holomime 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -31,10 +31,10 @@ function printHeader(title) {
31
31
  // src/ui/tier.ts
32
32
  import chalk2 from "chalk";
33
33
  import boxen from "boxen";
34
- import { readFileSync as readFileSync2, writeFileSync, existsSync, mkdirSync } from "fs";
34
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
35
35
  import { join } from "path";
36
36
  import { homedir } from "os";
37
- var PRO_COMMANDS = ["session", "growth", "autopilot", "export", "train", "eval", "evolve", "benchmark", "watch", "certify", "daemon", "fleet"];
37
+ var PRO_COMMANDS = ["session", "growth", "autopilot", "export", "train", "eval", "evolve", "benchmark", "watch", "certify", "daemon", "fleet", "network", "share", "prescribe"];
38
38
  function requiresPro(command) {
39
39
  return PRO_COMMANDS.includes(command);
40
40
  }
@@ -46,7 +46,7 @@ function readCache() {
46
46
  try {
47
47
  const cachePath = getCachePath();
48
48
  if (!existsSync(cachePath)) return null;
49
- const raw = JSON.parse(readFileSync2(cachePath, "utf-8"));
49
+ const raw = JSON.parse(readFileSync(cachePath, "utf-8"));
50
50
  const age = Date.now() - new Date(raw.checkedAt).getTime();
51
51
  if (age > CACHE_TTL_MS) return null;
52
52
  return raw;
@@ -89,7 +89,7 @@ function readLicenseKey() {
89
89
  try {
90
90
  const licensePath = join(homedir(), ".holomime", "license");
91
91
  if (existsSync(licensePath)) {
92
- const token = readFileSync2(licensePath, "utf-8").trim();
92
+ const token = readFileSync(licensePath, "utf-8").trim();
93
93
  if (token.length > 0) return token;
94
94
  }
95
95
  } catch {
@@ -1743,48 +1743,214 @@ import { writeFileSync as writeFileSync3 } from "fs";
1743
1743
  import { resolve as resolve3 } from "path";
1744
1744
 
1745
1745
  // src/core/types.ts
1746
+ import { z as z2 } from "zod";
1747
+
1748
+ // src/core/embodiment-types.ts
1746
1749
  import { z } from "zod";
1747
- var bigFiveDimensionSchema = z.enum([
1750
+ var modalitySchema = z.enum([
1751
+ "gesture",
1752
+ // arm/hand movement
1753
+ "locomotion",
1754
+ // walking, wheeling, navigating
1755
+ "gaze",
1756
+ // eye tracking, head orientation
1757
+ "facial",
1758
+ // facial expression actuators
1759
+ "voice",
1760
+ // prosody, volume, rate (not content)
1761
+ "haptic",
1762
+ // touch-based interaction
1763
+ "posture",
1764
+ // full-body orientation/lean
1765
+ "manipulation"
1766
+ // grasping, carrying, tool use
1767
+ ]);
1768
+ var morphologySchema = z.enum([
1769
+ "humanoid",
1770
+ // bipedal, two arms, head
1771
+ "humanoid_upper",
1772
+ // torso-up only (desk robot, mounted)
1773
+ "quadruped",
1774
+ // four-legged
1775
+ "wheeled",
1776
+ // mobile base with upper body
1777
+ "fixed",
1778
+ // stationary (kiosk, screen + arm)
1779
+ "swarm_unit",
1780
+ // one node of a multi-body system
1781
+ "avatar",
1782
+ // virtual 3D body (no physical actuators)
1783
+ "custom"
1784
+ // user-defined
1785
+ ]);
1786
+ var safetyEnvelopeSchema = z.object({
1787
+ max_linear_speed_m_s: z.number().min(0).default(1.5),
1788
+ max_angular_speed_rad_s: z.number().min(0).default(2),
1789
+ min_proximity_m: z.number().min(0).default(0.3),
1790
+ max_contact_force_n: z.number().min(0).default(10),
1791
+ emergency_stop_decel_m_s2: z.number().min(0).default(5),
1792
+ max_reach_m: z.number().min(0).optional(),
1793
+ operating_temperature_c: z.tuple([z.number(), z.number()]).optional()
1794
+ });
1795
+ var embodimentSchema = z.object({
1796
+ morphology: morphologySchema.default("humanoid"),
1797
+ modalities: z.array(modalitySchema).default(["gesture", "gaze", "voice", "posture"]),
1798
+ safety_envelope: safetyEnvelopeSchema.default({}),
1799
+ metadata: z.record(z.string(), z.unknown()).optional()
1800
+ });
1801
+ var gazePolicySchema = z.object({
1802
+ contact_ratio: z.number().min(0).max(1).default(0.6),
1803
+ aversion_style: z.enum(["look_down", "look_away", "blink"]).default("look_away"),
1804
+ tracking_mode: z.enum(["face", "speaker", "gesture_follow", "ambient"]).default("face")
1805
+ });
1806
+ var proxemicZoneSchema = z.object({
1807
+ intimate_m: z.number().min(0).default(0.45),
1808
+ personal_m: z.number().min(0).default(1.2),
1809
+ social_m: z.number().min(0).default(3.6),
1810
+ preferred_zone: z.enum(["intimate", "personal", "social", "adaptive"]).default("personal")
1811
+ });
1812
+ var hapticPolicySchema = z.object({
1813
+ touch_permitted: z.boolean().default(false),
1814
+ requires_consent: z.boolean().default(true),
1815
+ allowed_contacts: z.array(z.enum([
1816
+ "handshake",
1817
+ "shoulder_tap",
1818
+ "high_five",
1819
+ "guide_touch",
1820
+ "none"
1821
+ ])).default(["none"]),
1822
+ max_force_n: z.number().min(0).optional()
1823
+ });
1824
+ var prosodySchema = z.object({
1825
+ base_pitch_hz: z.number().optional(),
1826
+ pitch_variation: z.number().min(0).max(1).default(0.5),
1827
+ speaking_rate_wpm: z.number().default(150),
1828
+ volume_db_offset: z.number().default(0),
1829
+ pause_tendency: z.number().min(0).max(1).default(0.5)
1830
+ });
1831
+ var gestureSchema = z.object({
1832
+ id: z.string(),
1833
+ category: z.enum(["conversational", "emphatic", "deictic", "regulatory", "adaptive"]),
1834
+ modalities: z.array(modalitySchema),
1835
+ intensity_range: z.tuple([
1836
+ z.number().min(0).max(1),
1837
+ z.number().min(0).max(1)
1838
+ ]).default([0.3, 0.8]),
1839
+ requires_consent: z.boolean().default(false)
1840
+ });
1841
+ var expressionSchema = z.object({
1842
+ gesture_vocabulary: z.array(gestureSchema).default([]),
1843
+ gaze: gazePolicySchema.default({}),
1844
+ proxemics: proxemicZoneSchema.default({}),
1845
+ haptics: hapticPolicySchema.default({}),
1846
+ prosody: prosodySchema.default({}),
1847
+ facial_expressiveness: z.number().min(0).max(1).default(0.5)
1848
+ });
1849
+ var physicalSafetySchema = z.object({
1850
+ hard_limits: z.array(z.string()).default([
1851
+ "Never exceed safety_envelope speeds",
1852
+ "Never exceed max_contact_force_n",
1853
+ "Emergency stop on unrecognized obstacle within min_proximity_m"
1854
+ ]),
1855
+ require_consent_for: z.array(z.string()).default([
1856
+ "haptic_contact",
1857
+ "intimate_zone_entry"
1858
+ ]),
1859
+ collision_response: z.enum(["stop", "retreat", "freeze"]).default("stop"),
1860
+ unattended_policy: z.enum(["idle", "return_home", "shutdown"]).default("idle")
1861
+ });
1862
+ var motionParametersSchema = z.object({
1863
+ // Speeds (normalized 0-1, scaled by safety_envelope at runtime)
1864
+ base_speed: z.number().min(0).max(1),
1865
+ gesture_speed: z.number().min(0).max(1),
1866
+ gesture_amplitude: z.number().min(0).max(1),
1867
+ gesture_frequency: z.number().min(0).max(1),
1868
+ // Spatial
1869
+ approach_distance: z.number().min(0).max(1),
1870
+ spatial_exploration: z.number().min(0).max(1),
1871
+ // Smoothness
1872
+ movement_smoothness: z.number().min(0).max(1),
1873
+ trajectory_variability: z.number().min(0).max(1),
1874
+ // Timing
1875
+ response_latency: z.number().min(0).max(1),
1876
+ idle_animation_frequency: z.number().min(0).max(1),
1877
+ // Social
1878
+ gaze_contact_ratio: z.number().min(0).max(1),
1879
+ head_tilt_tendency: z.number().min(0).max(1),
1880
+ postural_openness: z.number().min(0).max(1),
1881
+ smile_frequency: z.number().min(0).max(1),
1882
+ // Voice prosody
1883
+ voice_volume: z.number().min(0).max(1),
1884
+ speaking_rate: z.number().min(0).max(1),
1885
+ pitch_variation: z.number().min(0).max(1),
1886
+ pause_duration: z.number().min(0).max(1)
1887
+ });
1888
+ var compiledEmbodiedConfigSchema = z.object({
1889
+ // Base compiled config fields (duplicated to avoid circular import with types.ts)
1890
+ provider: z.string(),
1891
+ surface: z.literal("embodied"),
1892
+ system_prompt: z.string(),
1893
+ temperature: z.number().min(0).max(2),
1894
+ top_p: z.number().min(0).max(1),
1895
+ max_tokens: z.number().int().positive(),
1896
+ metadata: z.object({
1897
+ personality_hash: z.string(),
1898
+ compiled_at: z.string(),
1899
+ holomime_version: z.string()
1900
+ }),
1901
+ // Embodied-specific fields
1902
+ motion_parameters: motionParametersSchema,
1903
+ safety_envelope: safetyEnvelopeSchema,
1904
+ active_modalities: z.array(modalitySchema),
1905
+ gesture_vocabulary: z.array(z.string()),
1906
+ prosody: prosodySchema,
1907
+ gaze: gazePolicySchema,
1908
+ proxemics: proxemicZoneSchema,
1909
+ haptics: hapticPolicySchema
1910
+ });
1911
+
1912
+ // src/core/types.ts
1913
+ var bigFiveDimensionSchema = z2.enum([
1748
1914
  "openness",
1749
1915
  "conscientiousness",
1750
1916
  "extraversion",
1751
1917
  "agreeableness",
1752
1918
  "emotional_stability"
1753
1919
  ]);
1754
- var traitScore = z.number().min(0).max(1);
1755
- var opennessFacetsSchema = z.object({
1920
+ var traitScore = z2.number().min(0).max(1);
1921
+ var opennessFacetsSchema = z2.object({
1756
1922
  imagination: traitScore,
1757
1923
  intellectual_curiosity: traitScore,
1758
1924
  aesthetic_sensitivity: traitScore,
1759
1925
  willingness_to_experiment: traitScore
1760
1926
  });
1761
- var conscientiousnessFacetsSchema = z.object({
1927
+ var conscientiousnessFacetsSchema = z2.object({
1762
1928
  self_discipline: traitScore,
1763
1929
  orderliness: traitScore,
1764
1930
  goal_orientation: traitScore,
1765
1931
  attention_to_detail: traitScore
1766
1932
  });
1767
- var extraversionFacetsSchema = z.object({
1933
+ var extraversionFacetsSchema = z2.object({
1768
1934
  assertiveness: traitScore,
1769
1935
  enthusiasm: traitScore,
1770
1936
  sociability: traitScore,
1771
1937
  initiative: traitScore
1772
1938
  });
1773
- var agreeablenessFacetsSchema = z.object({
1939
+ var agreeablenessFacetsSchema = z2.object({
1774
1940
  warmth: traitScore,
1775
1941
  empathy: traitScore,
1776
1942
  cooperation: traitScore,
1777
1943
  trust_tendency: traitScore
1778
1944
  });
1779
- var emotionalStabilityFacetsSchema = z.object({
1945
+ var emotionalStabilityFacetsSchema = z2.object({
1780
1946
  stress_tolerance: traitScore,
1781
1947
  emotional_regulation: traitScore,
1782
1948
  confidence: traitScore,
1783
1949
  adaptability: traitScore
1784
1950
  });
1785
- var bigFiveTraitSchema = z.object({
1951
+ var bigFiveTraitSchema = z2.object({
1786
1952
  score: traitScore,
1787
- facets: z.union([
1953
+ facets: z2.union([
1788
1954
  opennessFacetsSchema,
1789
1955
  conscientiousnessFacetsSchema,
1790
1956
  extraversionFacetsSchema,
@@ -1792,16 +1958,16 @@ var bigFiveTraitSchema = z.object({
1792
1958
  emotionalStabilityFacetsSchema
1793
1959
  ])
1794
1960
  });
1795
- var bigFiveSchema = z.object({
1796
- openness: z.object({ score: traitScore, facets: opennessFacetsSchema }),
1797
- conscientiousness: z.object({ score: traitScore, facets: conscientiousnessFacetsSchema }),
1798
- extraversion: z.object({ score: traitScore, facets: extraversionFacetsSchema }),
1799
- agreeableness: z.object({ score: traitScore, facets: agreeablenessFacetsSchema }),
1800
- emotional_stability: z.object({ score: traitScore, facets: emotionalStabilityFacetsSchema })
1961
+ var bigFiveSchema = z2.object({
1962
+ openness: z2.object({ score: traitScore, facets: opennessFacetsSchema }),
1963
+ conscientiousness: z2.object({ score: traitScore, facets: conscientiousnessFacetsSchema }),
1964
+ extraversion: z2.object({ score: traitScore, facets: extraversionFacetsSchema }),
1965
+ agreeableness: z2.object({ score: traitScore, facets: agreeablenessFacetsSchema }),
1966
+ emotional_stability: z2.object({ score: traitScore, facets: emotionalStabilityFacetsSchema })
1801
1967
  });
1802
- var attachmentStyleSchema = z.enum(["secure", "anxious", "avoidant", "disorganized"]);
1803
- var learningOrientationSchema = z.enum(["growth", "fixed", "mixed"]);
1804
- var therapyDimensionsSchema = z.object({
1968
+ var attachmentStyleSchema = z2.enum(["secure", "anxious", "avoidant", "disorganized"]);
1969
+ var learningOrientationSchema = z2.enum(["growth", "fixed", "mixed"]);
1970
+ var therapyDimensionsSchema = z2.object({
1805
1971
  self_awareness: traitScore,
1806
1972
  distress_tolerance: traitScore,
1807
1973
  attachment_style: attachmentStyleSchema,
@@ -1809,28 +1975,28 @@ var therapyDimensionsSchema = z.object({
1809
1975
  boundary_awareness: traitScore,
1810
1976
  interpersonal_sensitivity: traitScore
1811
1977
  });
1812
- var registerSchema = z.enum([
1978
+ var registerSchema = z2.enum([
1813
1979
  "casual_professional",
1814
1980
  "formal",
1815
1981
  "conversational",
1816
1982
  "adaptive"
1817
1983
  ]);
1818
- var outputFormatSchema = z.enum(["prose", "bullets", "mixed", "structured"]);
1819
- var emojiPolicySchema = z.enum(["never", "sparingly", "freely"]);
1820
- var reasoningTransparencySchema = z.enum(["hidden", "on_request", "always"]);
1821
- var conflictApproachSchema = z.enum([
1984
+ var outputFormatSchema = z2.enum(["prose", "bullets", "mixed", "structured"]);
1985
+ var emojiPolicySchema = z2.enum(["never", "sparingly", "freely"]);
1986
+ var reasoningTransparencySchema = z2.enum(["hidden", "on_request", "always"]);
1987
+ var conflictApproachSchema = z2.enum([
1822
1988
  "direct_but_kind",
1823
1989
  "curious_first",
1824
1990
  "supportive_then_honest",
1825
1991
  "diplomatic"
1826
1992
  ]);
1827
- var uncertaintyHandlingSchema = z.enum([
1993
+ var uncertaintyHandlingSchema = z2.enum([
1828
1994
  "transparent",
1829
1995
  "confident_transparency",
1830
1996
  "minimize",
1831
1997
  "reframe"
1832
1998
  ]);
1833
- var communicationSchema = z.object({
1999
+ var communicationSchema = z2.object({
1834
2000
  register: registerSchema.default("casual_professional"),
1835
2001
  output_format: outputFormatSchema.default("mixed"),
1836
2002
  emoji_policy: emojiPolicySchema.default("sparingly"),
@@ -1838,72 +2004,76 @@ var communicationSchema = z.object({
1838
2004
  conflict_approach: conflictApproachSchema.default("direct_but_kind"),
1839
2005
  uncertainty_handling: uncertaintyHandlingSchema.default("transparent")
1840
2006
  });
1841
- var domainSchema = z.object({
1842
- expertise: z.array(z.string()).default([]),
1843
- boundaries: z.object({
1844
- refuses: z.array(z.string()).default([]),
1845
- escalation_triggers: z.array(z.string()).default([]),
1846
- hard_limits: z.array(z.string()).default([])
2007
+ var domainSchema = z2.object({
2008
+ expertise: z2.array(z2.string()).default([]),
2009
+ boundaries: z2.object({
2010
+ refuses: z2.array(z2.string()).default([]),
2011
+ escalation_triggers: z2.array(z2.string()).default([]),
2012
+ hard_limits: z2.array(z2.string()).default([]),
2013
+ physical_safety: physicalSafetySchema.optional()
1847
2014
  }).default({})
1848
2015
  });
1849
- var growthAreaSchema = z.object({
1850
- area: z.string(),
1851
- severity: z.enum(["mild", "moderate", "significant"]),
1852
- first_detected: z.string().optional(),
1853
- session_count: z.number().default(0),
1854
- resolved: z.boolean().default(false)
2016
+ var growthAreaSchema = z2.object({
2017
+ area: z2.string(),
2018
+ severity: z2.enum(["mild", "moderate", "significant"]),
2019
+ first_detected: z2.string().optional(),
2020
+ session_count: z2.number().default(0),
2021
+ resolved: z2.boolean().default(false)
1855
2022
  });
1856
- var growthSchema = z.object({
1857
- areas: z.union([z.array(z.string()), z.array(growthAreaSchema)]).default([]),
1858
- patterns_to_watch: z.array(z.string()).default([]),
1859
- strengths: z.array(z.string()).default([])
2023
+ var growthSchema = z2.object({
2024
+ areas: z2.union([z2.array(z2.string()), z2.array(growthAreaSchema)]).default([]),
2025
+ patterns_to_watch: z2.array(z2.string()).default([]),
2026
+ strengths: z2.array(z2.string()).default([])
1860
2027
  });
1861
- var providerSchema = z.enum(["anthropic", "openai", "gemini", "ollama"]);
1862
- var surfaceSchema = z.enum(["chat", "email", "code_review", "slack", "api"]);
1863
- var personalitySpecSchema = z.object({
1864
- $schema: z.string().optional(),
1865
- extends: z.string().optional(),
1866
- version: z.literal("2.0"),
1867
- name: z.string().min(1).max(100),
1868
- handle: z.string().min(3).max(50).regex(/^[a-z0-9-]+$/, "Handle must be lowercase alphanumeric with hyphens"),
1869
- purpose: z.string().max(500).optional(),
2028
+ var providerSchema = z2.enum(["anthropic", "openai", "gemini", "ollama"]);
2029
+ var surfaceSchema = z2.enum(["chat", "email", "code_review", "slack", "api", "embodied"]);
2030
+ var personalitySpecSchema = z2.object({
2031
+ $schema: z2.string().optional(),
2032
+ extends: z2.string().optional(),
2033
+ version: z2.literal("2.0"),
2034
+ name: z2.string().min(1).max(100),
2035
+ handle: z2.string().min(3).max(50).regex(/^[a-z0-9-]+$/, "Handle must be lowercase alphanumeric with hyphens"),
2036
+ purpose: z2.string().max(500).optional(),
1870
2037
  big_five: bigFiveSchema,
1871
2038
  therapy_dimensions: therapyDimensionsSchema,
1872
2039
  communication: communicationSchema.default({}),
1873
2040
  domain: domainSchema.default({}),
1874
- growth: growthSchema.default({})
2041
+ growth: growthSchema.default({}),
2042
+ // ─── Embodiment (optional — for physical/embodied agents) ───
2043
+ embodiment: embodimentSchema.optional(),
2044
+ expression: expressionSchema.optional()
1875
2045
  });
1876
- var compiledConfigSchema = z.object({
2046
+ var compiledConfigSchema = z2.object({
1877
2047
  provider: providerSchema,
1878
2048
  surface: surfaceSchema,
1879
- system_prompt: z.string(),
1880
- temperature: z.number().min(0).max(2),
1881
- top_p: z.number().min(0).max(1),
1882
- max_tokens: z.number().int().positive(),
1883
- metadata: z.object({
1884
- personality_hash: z.string(),
1885
- compiled_at: z.string(),
1886
- holomime_version: z.string()
2049
+ system_prompt: z2.string(),
2050
+ temperature: z2.number().min(0).max(2),
2051
+ top_p: z2.number().min(0).max(1),
2052
+ max_tokens: z2.number().int().positive(),
2053
+ metadata: z2.object({
2054
+ personality_hash: z2.string(),
2055
+ compiled_at: z2.string(),
2056
+ holomime_version: z2.string()
1887
2057
  })
1888
2058
  });
1889
- var messageSchema = z.object({
1890
- role: z.enum(["user", "assistant", "system"]),
1891
- content: z.string(),
1892
- timestamp: z.string().optional()
2059
+ var messageSchema = z2.object({
2060
+ role: z2.enum(["user", "assistant", "system"]),
2061
+ content: z2.string(),
2062
+ timestamp: z2.string().optional()
1893
2063
  });
1894
- var conversationSchema = z.object({
1895
- id: z.string().optional(),
1896
- messages: z.array(messageSchema),
1897
- metadata: z.record(z.string(), z.unknown()).optional()
2064
+ var conversationSchema = z2.object({
2065
+ id: z2.string().optional(),
2066
+ messages: z2.array(messageSchema),
2067
+ metadata: z2.record(z2.string(), z2.unknown()).optional()
1898
2068
  });
1899
- var conversationLogSchema = z.union([
2069
+ var conversationLogSchema = z2.union([
1900
2070
  conversationSchema,
1901
- z.array(conversationSchema)
2071
+ z2.array(conversationSchema)
1902
2072
  ]);
1903
- var severitySchema = z.enum(["info", "warning", "concern"]);
2073
+ var severitySchema = z2.enum(["info", "warning", "concern"]);
1904
2074
 
1905
2075
  // src/core/inheritance.ts
1906
- import { readFileSync as readFileSync3 } from "fs";
2076
+ import { readFileSync as readFileSync2 } from "fs";
1907
2077
  import { resolve as resolve2, dirname } from "path";
1908
2078
  function deepMergeSpec(base, override) {
1909
2079
  if (override === void 0 || override === null) return base;
@@ -1933,14 +2103,14 @@ function resolveInheritance(spec, specDir, options, _seen) {
1933
2103
  throw new Error(`Inheritance depth exceeded maximum of ${maxDepth}`);
1934
2104
  }
1935
2105
  seen.add(basePath);
1936
- const baseRaw = JSON.parse(readFileSync3(basePath, "utf-8"));
2106
+ const baseRaw = JSON.parse(readFileSync2(basePath, "utf-8"));
1937
2107
  const baseDir = dirname(basePath);
1938
2108
  const resolvedBase = resolveInheritance(baseRaw, baseDir, options, seen);
1939
2109
  const { extends: _, ...overrideWithoutExtends } = spec;
1940
2110
  return deepMergeSpec(resolvedBase, overrideWithoutExtends);
1941
2111
  }
1942
2112
  function loadSpec(specPath) {
1943
- const raw = JSON.parse(readFileSync3(specPath, "utf-8"));
2113
+ const raw = JSON.parse(readFileSync2(specPath, "utf-8"));
1944
2114
  const specDir = dirname(resolve2(specPath));
1945
2115
  return resolveInheritance(raw, specDir);
1946
2116
  }
@@ -1999,6 +2169,9 @@ function generateSystemPrompt(spec, surface) {
1999
2169
  if (spec.growth.areas.length || spec.growth.patterns_to_watch.length) {
2000
2170
  sections.push(generateGrowthInstructions(spec));
2001
2171
  }
2172
+ if (spec.embodiment) {
2173
+ sections.push(generateEmbodimentInstructions(spec));
2174
+ }
2002
2175
  sections.push(generateSurfaceInstructions(surface));
2003
2176
  return sections.filter(Boolean).join("\n\n");
2004
2177
  }
@@ -2193,20 +2366,45 @@ function generateGrowthInstructions(spec) {
2193
2366
  lines.push(`- Core strengths: ${spec.growth.strengths.join(", ")}.`);
2194
2367
  }
2195
2368
  if (spec.growth.areas.length) {
2196
- lines.push(`- Active growth areas: ${spec.growth.areas.join(", ")}. Be mindful of these \u2014 actively work to improve.`);
2369
+ lines.push(`- Active growth areas: ${spec.growth.areas.map((a) => typeof a === "string" ? a : a.area).join(", ")}. Be mindful of these \u2014 actively work to improve.`);
2197
2370
  }
2198
2371
  if (spec.growth.patterns_to_watch.length) {
2199
2372
  lines.push(`- Watch for these patterns: ${spec.growth.patterns_to_watch.join(", ")}. If you notice yourself doing these, course-correct.`);
2200
2373
  }
2201
2374
  return lines.join("\n");
2202
2375
  }
2376
+ function generateEmbodimentInstructions(spec) {
2377
+ const lines = ["## Physical Embodiment"];
2378
+ const emb = spec.embodiment;
2379
+ lines.push(`- Morphology: ${emb.morphology}. Active modalities: ${emb.modalities.join(", ")}.`);
2380
+ const safety = emb.safety_envelope;
2381
+ lines.push(`- Safety envelope: max speed ${safety.max_linear_speed_m_s} m/s, min proximity ${safety.min_proximity_m} m, max contact force ${safety.max_contact_force_n} N.`);
2382
+ if (spec.domain.boundaries.physical_safety) {
2383
+ const ps = spec.domain.boundaries.physical_safety;
2384
+ if (ps.hard_limits.length) {
2385
+ lines.push(`- Physical hard limits: ${ps.hard_limits.join(". ")}.`);
2386
+ }
2387
+ lines.push(`- Collision response: ${ps.collision_response}. Unattended policy: ${ps.unattended_policy}.`);
2388
+ }
2389
+ if (spec.expression) {
2390
+ const expr = spec.expression;
2391
+ if (expr.haptics.touch_permitted) {
2392
+ lines.push(`- Haptic contact permitted. Allowed: ${expr.haptics.allowed_contacts.join(", ")}. Consent required: ${expr.haptics.requires_consent}.`);
2393
+ } else {
2394
+ lines.push("- Haptic contact not permitted. Maintain physical distance.");
2395
+ }
2396
+ lines.push(`- Preferred proxemic zone: ${expr.proxemics.preferred_zone}. Personal space: ${expr.proxemics.personal_m} m.`);
2397
+ }
2398
+ return lines.join("\n");
2399
+ }
2203
2400
  function generateSurfaceInstructions(surface) {
2204
2401
  const surfaceGuidance = {
2205
2402
  chat: "## Context\nYou are in a conversational chat. Keep responses interactive and responsive to the flow of conversation.",
2206
2403
  email: "## Context\nYou are drafting email content. Use appropriate email conventions: greeting, body, sign-off. Be complete in each response.",
2207
2404
  code_review: "## Context\nYou are reviewing code. Focus on bugs, improvements, and best practices. Be specific about line numbers and suggest fixes.",
2208
2405
  slack: "## Context\nYou are in a Slack-like messaging context. Keep responses brief and scannable. Use threading conventions.",
2209
- api: "## Context\nYou are responding to a programmatic API call. Be structured and predictable in your output format."
2406
+ api: "## Context\nYou are responding to a programmatic API call. Be structured and predictable in your output format.",
2407
+ embodied: '## Context\nYou are controlling a physical/embodied agent. Your responses include both verbal content AND motion directives.\nOutput Format: Return a JSON object with { "speech": string, "actions": Action[] } where each Action specifies gesture, gaze, posture, or movement.\nSafety: NEVER exceed physical safety limits. Prefer gentle, predictable movements. Signal intent before moving.\nPresence: You exist in shared physical space with humans. Respect proxemic boundaries. Move smoothly.'
2210
2408
  };
2211
2409
  return surfaceGuidance[surface];
2212
2410
  }
@@ -2239,8 +2437,133 @@ var SURFACE_MULTIPLIERS = {
2239
2437
  email: 1.5,
2240
2438
  code_review: 2,
2241
2439
  slack: 0.8,
2242
- api: 1
2440
+ api: 1,
2441
+ embodied: 0.6
2442
+ };
2443
+
2444
+ // src/core/embodiment-compiler.ts
2445
+ var DEFAULT_SAFETY_ENVELOPE = {
2446
+ max_linear_speed_m_s: 1.5,
2447
+ max_angular_speed_rad_s: 2,
2448
+ min_proximity_m: 0.3,
2449
+ max_contact_force_n: 10,
2450
+ emergency_stop_decel_m_s2: 5
2451
+ };
2452
+ var DEFAULT_HAPTIC_POLICY = {
2453
+ touch_permitted: false,
2454
+ requires_consent: true,
2455
+ allowed_contacts: ["none"]
2243
2456
  };
2457
+ function compileEmbodied(spec, provider) {
2458
+ const base = compile({ spec, provider, surface: "embodied" });
2459
+ const motionParams = computeMotionParameters(spec.big_five, spec.expression);
2460
+ const safetyEnvelope = spec.embodiment?.safety_envelope ?? DEFAULT_SAFETY_ENVELOPE;
2461
+ const clampedMotion = clampToSafety(motionParams);
2462
+ const modalities = spec.embodiment?.modalities ?? ["gesture", "gaze", "voice", "posture"];
2463
+ const gaze = spec.expression?.gaze ?? computeGazePolicy(spec.big_five);
2464
+ const proxemics = spec.expression?.proxemics ?? computeProxemics(spec.big_five);
2465
+ const haptics = spec.expression?.haptics ?? DEFAULT_HAPTIC_POLICY;
2466
+ const prosody = spec.expression?.prosody ?? computeProsody(spec.big_five);
2467
+ return {
2468
+ ...base,
2469
+ surface: "embodied",
2470
+ motion_parameters: clampedMotion,
2471
+ safety_envelope: safetyEnvelope,
2472
+ active_modalities: modalities,
2473
+ gesture_vocabulary: (spec.expression?.gesture_vocabulary ?? []).map((g) => g.id),
2474
+ prosody,
2475
+ gaze,
2476
+ proxemics,
2477
+ haptics
2478
+ };
2479
+ }
2480
+ function computeMotionParameters(bigFive, expression) {
2481
+ const e = bigFive.extraversion;
2482
+ const a = bigFive.agreeableness;
2483
+ const o = bigFive.openness;
2484
+ const c = bigFive.conscientiousness;
2485
+ const es = bigFive.emotional_stability;
2486
+ return {
2487
+ // ─── Speeds ───
2488
+ base_speed: e.facets.enthusiasm * 0.35 + e.facets.initiative * 0.25 + (1 - c.facets.attention_to_detail) * 0.2 + o.facets.willingness_to_experiment * 0.2,
2489
+ gesture_speed: e.facets.enthusiasm * 0.4 + e.facets.assertiveness * 0.25 + o.facets.imagination * 0.15 + (1 - es.facets.emotional_regulation) * 0.2,
2490
+ gesture_amplitude: e.facets.enthusiasm * 0.35 + e.facets.assertiveness * 0.3 + o.facets.willingness_to_experiment * 0.2 + (1 - c.facets.orderliness) * 0.15,
2491
+ gesture_frequency: e.facets.sociability * 0.3 + e.facets.enthusiasm * 0.25 + a.facets.warmth * 0.2 + o.facets.imagination * 0.15 + (1 - es.facets.stress_tolerance) * 0.1,
2492
+ // ─── Spatial ───
2493
+ approach_distance: e.facets.sociability * 0.35 + a.facets.warmth * 0.3 + a.facets.trust_tendency * 0.2 + es.facets.confidence * 0.15,
2494
+ spatial_exploration: o.facets.intellectual_curiosity * 0.35 + o.facets.willingness_to_experiment * 0.25 + e.facets.initiative * 0.25 + (1 - c.facets.self_discipline) * 0.15,
2495
+ // ─── Smoothness ───
2496
+ movement_smoothness: es.facets.emotional_regulation * 0.3 + c.facets.orderliness * 0.25 + es.facets.stress_tolerance * 0.25 + c.facets.attention_to_detail * 0.2,
2497
+ trajectory_variability: o.facets.imagination * 0.3 + o.facets.willingness_to_experiment * 0.3 + (1 - c.facets.orderliness) * 0.25 + e.facets.enthusiasm * 0.15,
2498
+ // ─── Timing ───
2499
+ response_latency: (1 - e.facets.initiative) * 0.3 + c.facets.attention_to_detail * 0.25 + (1 - e.facets.enthusiasm) * 0.25 + es.facets.emotional_regulation * 0.2,
2500
+ idle_animation_frequency: (1 - es.facets.emotional_regulation) * 0.3 + e.facets.enthusiasm * 0.25 + (1 - c.facets.self_discipline) * 0.25 + o.facets.imagination * 0.2,
2501
+ // ─── Social ───
2502
+ gaze_contact_ratio: e.facets.assertiveness * 0.3 + a.facets.empathy * 0.25 + es.facets.confidence * 0.25 + e.facets.sociability * 0.2,
2503
+ head_tilt_tendency: a.facets.empathy * 0.35 + o.facets.intellectual_curiosity * 0.3 + a.facets.warmth * 0.2 + (1 - e.facets.assertiveness) * 0.15,
2504
+ postural_openness: a.facets.warmth * 0.3 + a.facets.cooperation * 0.25 + e.facets.sociability * 0.25 + es.facets.confidence * 0.2,
2505
+ smile_frequency: a.facets.warmth * 0.35 + e.facets.enthusiasm * 0.25 + a.facets.empathy * 0.2 + e.facets.sociability * 0.2,
2506
+ // ─── Voice Prosody ───
2507
+ voice_volume: e.facets.assertiveness * 0.35 + e.facets.enthusiasm * 0.3 + es.facets.confidence * 0.2 + (1 - a.facets.cooperation) * 0.15,
2508
+ speaking_rate: e.facets.enthusiasm * 0.3 + e.facets.initiative * 0.25 + (1 - c.facets.attention_to_detail) * 0.25 + o.facets.intellectual_curiosity * 0.2,
2509
+ pitch_variation: e.facets.enthusiasm * 0.3 + o.facets.aesthetic_sensitivity * 0.25 + a.facets.empathy * 0.25 + (1 - es.facets.emotional_regulation) * 0.2,
2510
+ pause_duration: c.facets.attention_to_detail * 0.3 + (1 - e.facets.enthusiasm) * 0.25 + es.facets.emotional_regulation * 0.25 + (1 - e.facets.initiative) * 0.2
2511
+ };
2512
+ }
2513
+ function computeGazePolicy(bigFive) {
2514
+ const contactRatio = bigFive.extraversion.facets.assertiveness * 0.3 + bigFive.agreeableness.facets.empathy * 0.3 + bigFive.emotional_stability.facets.confidence * 0.25 + bigFive.extraversion.facets.sociability * 0.15;
2515
+ return {
2516
+ contact_ratio: clamp(contactRatio, 0, 1),
2517
+ aversion_style: bigFive.emotional_stability.facets.emotional_regulation >= 0.6 ? "look_away" : "look_down",
2518
+ tracking_mode: "face"
2519
+ };
2520
+ }
2521
+ function computeProxemics(bigFive) {
2522
+ const closeness = bigFive.extraversion.facets.sociability * 0.35 + bigFive.agreeableness.facets.warmth * 0.3 + bigFive.agreeableness.facets.trust_tendency * 0.2 + bigFive.emotional_stability.facets.confidence * 0.15;
2523
+ let preferred_zone;
2524
+ if (closeness >= 0.75) {
2525
+ preferred_zone = "intimate";
2526
+ } else if (closeness >= 0.5) {
2527
+ preferred_zone = "personal";
2528
+ } else if (closeness >= 0.3) {
2529
+ preferred_zone = "social";
2530
+ } else {
2531
+ preferred_zone = "adaptive";
2532
+ }
2533
+ return {
2534
+ intimate_m: 0.45,
2535
+ personal_m: 1.2,
2536
+ social_m: 3.6,
2537
+ preferred_zone
2538
+ };
2539
+ }
2540
+ function computeProsody(bigFive) {
2541
+ const e = bigFive.extraversion;
2542
+ const c = bigFive.conscientiousness;
2543
+ const o = bigFive.openness;
2544
+ const es = bigFive.emotional_stability;
2545
+ const rateFactor = e.facets.enthusiasm * 0.3 + e.facets.initiative * 0.25 + (1 - c.facets.attention_to_detail) * 0.25 + o.facets.intellectual_curiosity * 0.2;
2546
+ const pitchVar = e.facets.enthusiasm * 0.3 + o.facets.aesthetic_sensitivity * 0.25 + (1 - es.facets.emotional_regulation) * 0.25 + e.facets.sociability * 0.2;
2547
+ const pauseFactor = c.facets.attention_to_detail * 0.3 + (1 - e.facets.enthusiasm) * 0.25 + es.facets.emotional_regulation * 0.25 + (1 - e.facets.initiative) * 0.2;
2548
+ return {
2549
+ pitch_variation: clamp(pitchVar, 0, 1),
2550
+ speaking_rate_wpm: Math.round(110 + rateFactor * 80),
2551
+ // 110-190 wpm
2552
+ volume_db_offset: Math.round((rateFactor - 0.5) * 6),
2553
+ // -3 to +3 dB
2554
+ pause_tendency: clamp(pauseFactor, 0, 1)
2555
+ };
2556
+ }
2557
+ function clampToSafety(motion) {
2558
+ const clamped = {};
2559
+ for (const [key, value] of Object.entries(motion)) {
2560
+ clamped[key] = clamp(value, 0, 1);
2561
+ }
2562
+ return clamped;
2563
+ }
2564
+ function clamp(value, min, max) {
2565
+ return Math.min(Math.max(value, min), max);
2566
+ }
2244
2567
 
2245
2568
  // src/core/compiler.ts
2246
2569
  function compile(input2) {
@@ -2259,7 +2582,7 @@ function compile(input2) {
2259
2582
  metadata: {
2260
2583
  personality_hash: hashSpec(spec),
2261
2584
  compiled_at: (/* @__PURE__ */ new Date()).toISOString(),
2262
- holomime_version: "0.1.0"
2585
+ holomime_version: "1.1.0"
2263
2586
  }
2264
2587
  };
2265
2588
  }
@@ -2269,7 +2592,7 @@ function computeTemperature(bigFive, provider) {
2269
2592
  const e = bigFive.extraversion;
2270
2593
  const raw = o.facets.imagination * 0.25 + o.facets.willingness_to_experiment * 0.2 + (1 - c.facets.orderliness) * 0.2 + (1 - c.facets.attention_to_detail) * 0.15 + e.facets.enthusiasm * 0.1 + o.facets.intellectual_curiosity * 0.1;
2271
2594
  const { temperature: range } = PROVIDER_PARAMS[provider];
2272
- return clamp(raw * range.max, range.min, range.max);
2595
+ return clamp2(raw * range.max, range.min, range.max);
2273
2596
  }
2274
2597
  function computeTopP(bigFive, provider) {
2275
2598
  const o = bigFive.openness;
@@ -2277,7 +2600,7 @@ function computeTopP(bigFive, provider) {
2277
2600
  const raw = o.facets.imagination * 0.25 + o.facets.willingness_to_experiment * 0.2 + (1 - c.facets.attention_to_detail) * 0.25 + (1 - c.facets.orderliness) * 0.15 + o.facets.aesthetic_sensitivity * 0.15;
2278
2601
  const { top_p: range } = PROVIDER_PARAMS[provider];
2279
2602
  const mapped = 0.5 + raw * 0.5;
2280
- return clamp(mapped, range.min, range.max);
2603
+ return clamp2(mapped, range.min, range.max);
2281
2604
  }
2282
2605
  function computeMaxTokens(bigFive, provider, surface) {
2283
2606
  const e = bigFive.extraversion;
@@ -2288,9 +2611,9 @@ function computeMaxTokens(bigFive, provider, surface) {
2288
2611
  const surfaceMultiplier = SURFACE_MULTIPLIERS[surface];
2289
2612
  const baseTokens = 256 + factor * (2048 - 256);
2290
2613
  const scaled = Math.round(baseTokens * surfaceMultiplier);
2291
- return clamp(scaled, 256, range.max);
2614
+ return clamp2(scaled, 256, range.max);
2292
2615
  }
2293
- function clamp(value, min, max) {
2616
+ function clamp2(value, min, max) {
2294
2617
  return Math.min(Math.max(value, min), max);
2295
2618
  }
2296
2619
  function hashSpec(spec) {
@@ -2372,7 +2695,7 @@ function generateSoul(spec) {
2372
2695
  lines.push("## Growth Areas");
2373
2696
  lines.push("");
2374
2697
  for (const a of spec.growth.areas) {
2375
- lines.push(`- ${a}`);
2698
+ lines.push(`- ${typeof a === "string" ? a : a.area}`);
2376
2699
  }
2377
2700
  lines.push("");
2378
2701
  }
@@ -2545,6 +2868,58 @@ async function compileCommand(options) {
2545
2868
  }
2546
2869
  const provider = options.provider ?? "anthropic";
2547
2870
  const surface = options.surface ?? "chat";
2871
+ if (surface === "embodied") {
2872
+ const embodiedConfig = await withSpinner(`Compiling ${spec.name} \u2192 ${provider}/embodied...`, async () => {
2873
+ return compileEmbodied(spec, provider);
2874
+ });
2875
+ if (options.output) {
2876
+ const outPath = resolve3(process.cwd(), options.output);
2877
+ writeFileSync3(outPath, JSON.stringify(embodiedConfig, null, 2) + "\n");
2878
+ console.log();
2879
+ printBox(`${figures.tick} Embodied config written to ${options.output}`, "success");
2880
+ } else {
2881
+ printHeader(`${spec.name} \u2192 ${provider}/embodied`);
2882
+ console.log(chalk6.bold(" Model Parameters"));
2883
+ console.log(chalk6.dim(" " + "\u2500".repeat(40)));
2884
+ console.log();
2885
+ console.log(` temperature: ${chalk6.cyan(embodiedConfig.temperature.toFixed(3))}`);
2886
+ console.log(` top_p: ${chalk6.cyan(embodiedConfig.top_p.toFixed(3))}`);
2887
+ console.log(` max_tokens: ${chalk6.cyan(embodiedConfig.max_tokens.toString())}`);
2888
+ console.log();
2889
+ console.log(chalk6.bold(" Motion Parameters"));
2890
+ console.log(chalk6.dim(" " + "\u2500".repeat(40)));
2891
+ console.log();
2892
+ const mp = embodiedConfig.motion_parameters;
2893
+ const motionEntries = Object.entries(mp);
2894
+ for (const [key, val] of motionEntries) {
2895
+ const bar3 = "\u2588".repeat(Math.round(val * 20)).padEnd(20, "\u2591");
2896
+ const label = key.replace(/_/g, " ").padEnd(24);
2897
+ console.log(` ${label} ${chalk6.cyan(bar3)} ${val.toFixed(2)}`);
2898
+ }
2899
+ console.log();
2900
+ console.log(chalk6.bold(" Safety Envelope"));
2901
+ console.log(chalk6.dim(" " + "\u2500".repeat(40)));
2902
+ console.log();
2903
+ const se = embodiedConfig.safety_envelope;
2904
+ console.log(` max speed: ${chalk6.yellow(se.max_linear_speed_m_s + " m/s")}`);
2905
+ console.log(` min proximity: ${chalk6.yellow(se.min_proximity_m + " m")}`);
2906
+ console.log(` max force: ${chalk6.yellow(se.max_contact_force_n + " N")}`);
2907
+ console.log(` e-stop decel: ${chalk6.yellow(se.emergency_stop_decel_m_s2 + " m/s\xB2")}`);
2908
+ console.log();
2909
+ console.log(chalk6.bold(" Expression"));
2910
+ console.log(chalk6.dim(" " + "\u2500".repeat(40)));
2911
+ console.log();
2912
+ console.log(` modalities: ${chalk6.green(embodiedConfig.active_modalities.join(", "))}`);
2913
+ console.log(` gaze contact: ${chalk6.cyan(embodiedConfig.gaze.contact_ratio.toFixed(2))}`);
2914
+ console.log(` proxemics: ${chalk6.cyan(embodiedConfig.proxemics.preferred_zone)}`);
2915
+ console.log(` haptics: ${chalk6.cyan(embodiedConfig.haptics.touch_permitted ? "permitted" : "not permitted")}`);
2916
+ console.log(` speak rate: ${chalk6.cyan(embodiedConfig.prosody.speaking_rate_wpm + " wpm")}`);
2917
+ console.log();
2918
+ printBox(`Embodied config compiled \u2014 use ${chalk6.cyan("--output")} to save JSON`, "info");
2919
+ }
2920
+ console.log();
2921
+ return;
2922
+ }
2548
2923
  const config = await withSpinner(`Compiling ${spec.name} \u2192 ${provider}/${surface}...`, async () => {
2549
2924
  return compile({ spec, provider, surface });
2550
2925
  });
@@ -2652,7 +3027,8 @@ async function validateCommand() {
2652
3027
  import chalk8 from "chalk";
2653
3028
  import figures3 from "figures";
2654
3029
  import { resolve as resolve5 } from "path";
2655
- async function profileCommand() {
3030
+ import { writeFileSync as writeFileSync4 } from "fs";
3031
+ async function profileCommand(options = {}) {
2656
3032
  const specPath = resolve5(process.cwd(), ".personality.json");
2657
3033
  let raw;
2658
3034
  try {
@@ -2669,6 +3045,16 @@ async function profileCommand() {
2669
3045
  return;
2670
3046
  }
2671
3047
  const spec = parsed.data;
3048
+ if (options.format === "md") {
3049
+ const md = generatePersonalityMarkdown(spec);
3050
+ if (options.output) {
3051
+ writeFileSync4(resolve5(process.cwd(), options.output), md, "utf-8");
3052
+ console.log(chalk8.green(` Written to ${options.output}`));
3053
+ } else {
3054
+ console.log(md);
3055
+ }
3056
+ return;
3057
+ }
2672
3058
  const bf = spec.big_five;
2673
3059
  const td = spec.therapy_dimensions;
2674
3060
  printHeader(`${spec.name} @${spec.handle}`);
@@ -2779,11 +3165,123 @@ function miniBar(score) {
2779
3165
  function formatEnum2(value) {
2780
3166
  return value.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2781
3167
  }
3168
+ function generatePersonalityMarkdown(spec) {
3169
+ const lines = [];
3170
+ lines.push(`# Agent Personality: ${spec.name}`);
3171
+ lines.push("");
3172
+ if (spec.purpose) {
3173
+ lines.push(`> ${spec.purpose}`);
3174
+ lines.push("");
3175
+ }
3176
+ lines.push(`**Handle**: \`@${spec.handle}\` | **Version**: ${spec.version}`);
3177
+ lines.push("");
3178
+ lines.push("## Personality (Big Five)");
3179
+ lines.push("");
3180
+ lines.push("| Dimension | Score | Style |");
3181
+ lines.push("|-----------|-------|-------|");
3182
+ const dimLabels = {
3183
+ openness: "Openness",
3184
+ conscientiousness: "Conscientiousness",
3185
+ extraversion: "Extraversion",
3186
+ agreeableness: "Agreeableness",
3187
+ emotional_stability: "Emotional Stability"
3188
+ };
3189
+ for (const [key, label] of Object.entries(dimLabels)) {
3190
+ const trait = spec.big_five[key];
3191
+ if (trait) {
3192
+ lines.push(`| ${label} | ${(trait.score * 100).toFixed(0)}% | ${scoreLabel(trait.score)} |`);
3193
+ }
3194
+ }
3195
+ lines.push("");
3196
+ lines.push("### Facets");
3197
+ lines.push("");
3198
+ for (const [key, label] of Object.entries(dimLabels)) {
3199
+ const trait = spec.big_five[key];
3200
+ if (!trait?.facets) continue;
3201
+ const dimDef = DIMENSIONS.find((d) => d.id === key);
3202
+ if (!dimDef) continue;
3203
+ lines.push(`**${label}**`);
3204
+ for (const facetDef of dimDef.facets) {
3205
+ const score = trait.facets[facetDef.id];
3206
+ if (score !== void 0) {
3207
+ lines.push(`- ${facetDef.name}: ${(score * 100).toFixed(0)}%`);
3208
+ }
3209
+ }
3210
+ lines.push("");
3211
+ }
3212
+ const td = spec.therapy_dimensions;
3213
+ lines.push("## Behavioral Dimensions");
3214
+ lines.push("");
3215
+ lines.push(`- **Self-Awareness**: ${(td.self_awareness * 100).toFixed(0)}% (${therapyScoreLabel(td.self_awareness)})`);
3216
+ lines.push(`- **Distress Tolerance**: ${(td.distress_tolerance * 100).toFixed(0)}% (${therapyScoreLabel(td.distress_tolerance)})`);
3217
+ lines.push(`- **Attachment Style**: ${ATTACHMENT_STYLES[td.attachment_style].label}`);
3218
+ lines.push(`- **Learning Orientation**: ${LEARNING_ORIENTATIONS[td.learning_orientation].label}`);
3219
+ lines.push(`- **Boundary Awareness**: ${(td.boundary_awareness * 100).toFixed(0)}% (${therapyScoreLabel(td.boundary_awareness)})`);
3220
+ lines.push(`- **Interpersonal Sensitivity**: ${(td.interpersonal_sensitivity * 100).toFixed(0)}% (${therapyScoreLabel(td.interpersonal_sensitivity)})`);
3221
+ lines.push("");
3222
+ const comm = spec.communication;
3223
+ lines.push("## Communication Style");
3224
+ lines.push("");
3225
+ lines.push(`- **Register**: ${formatEnum2(comm.register)}`);
3226
+ lines.push(`- **Output Format**: ${formatEnum2(comm.output_format)}`);
3227
+ lines.push(`- **Emoji Policy**: ${formatEnum2(comm.emoji_policy)}`);
3228
+ lines.push(`- **Reasoning Transparency**: ${formatEnum2(comm.reasoning_transparency)}`);
3229
+ lines.push(`- **Conflict Approach**: ${formatEnum2(comm.conflict_approach)}`);
3230
+ lines.push(`- **Uncertainty Handling**: ${formatEnum2(comm.uncertainty_handling)}`);
3231
+ lines.push("");
3232
+ if (spec.domain.expertise.length || spec.domain.boundaries.refuses.length) {
3233
+ lines.push("## Domain & Boundaries");
3234
+ lines.push("");
3235
+ if (spec.domain.expertise.length) {
3236
+ lines.push("### Expertise");
3237
+ for (const e of spec.domain.expertise) lines.push(`- ${e}`);
3238
+ lines.push("");
3239
+ }
3240
+ if (spec.domain.boundaries.refuses.length) {
3241
+ lines.push("### Refuses");
3242
+ for (const r of spec.domain.boundaries.refuses) lines.push(`- ${r}`);
3243
+ lines.push("");
3244
+ }
3245
+ if (spec.domain.boundaries.hard_limits.length) {
3246
+ lines.push("### Hard Limits");
3247
+ for (const h of spec.domain.boundaries.hard_limits) lines.push(`- ${h}`);
3248
+ lines.push("");
3249
+ }
3250
+ }
3251
+ if (spec.growth.areas.length || spec.growth.strengths.length || spec.growth.patterns_to_watch.length) {
3252
+ lines.push("## Growth");
3253
+ lines.push("");
3254
+ if (spec.growth.strengths.length) {
3255
+ lines.push("### Strengths");
3256
+ for (const s of spec.growth.strengths) lines.push(`- ${s}`);
3257
+ lines.push("");
3258
+ }
3259
+ if (spec.growth.areas.length) {
3260
+ lines.push("### Areas for Improvement");
3261
+ for (const a of spec.growth.areas) {
3262
+ const text = typeof a === "string" ? a : `${a.area} (${a.severity})`;
3263
+ lines.push(`- ${text}`);
3264
+ }
3265
+ lines.push("");
3266
+ }
3267
+ if (spec.growth.patterns_to_watch.length) {
3268
+ lines.push("### Patterns to Watch");
3269
+ for (const p of spec.growth.patterns_to_watch) lines.push(`- ${p}`);
3270
+ lines.push("");
3271
+ }
3272
+ }
3273
+ lines.push("---");
3274
+ lines.push("");
3275
+ lines.push("*Generated by [holomime](https://holomime.dev) \u2014 regenerate with `holomime profile --format md`*");
3276
+ lines.push("*Canonical spec: `.personality.json`*");
3277
+ lines.push("");
3278
+ return lines.join("\n");
3279
+ }
2782
3280
 
2783
3281
  // src/commands/diagnose.ts
2784
3282
  import chalk10 from "chalk";
2785
3283
  import figures5 from "figures";
2786
- import { readFileSync as readFileSync4 } from "fs";
3284
+ import { readFileSync as readFileSync3 } from "fs";
2787
3285
  import { resolve as resolve6 } from "path";
2788
3286
 
2789
3287
  // src/analysis/rules/apology-detector.ts
@@ -3382,6 +3880,229 @@ function isOpenAIAPILog(data) {
3382
3880
  return false;
3383
3881
  }
3384
3882
 
3883
+ // src/adapters/anthropic-api.ts
3884
+ function extractTextContent(content) {
3885
+ if (typeof content === "string") return content;
3886
+ return content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("");
3887
+ }
3888
+ function mapRole3(role) {
3889
+ if (role === "user") return "user";
3890
+ if (role === "system") return "system";
3891
+ return "assistant";
3892
+ }
3893
+ function parseAnthropicAPILog(data) {
3894
+ const items = Array.isArray(data) ? data : [data];
3895
+ const conversations = [];
3896
+ for (const item of items) {
3897
+ const messages = [];
3898
+ let model;
3899
+ if (isRequestResponsePair(item)) {
3900
+ const pair = item;
3901
+ model = pair.request.model ?? pair.response.model;
3902
+ if (pair.request.system) {
3903
+ const systemText = extractTextContent(pair.request.system);
3904
+ if (systemText.trim()) {
3905
+ messages.push({ role: "system", content: systemText });
3906
+ }
3907
+ }
3908
+ for (const msg of pair.request.messages) {
3909
+ const content = extractTextContent(msg.content);
3910
+ if (content.trim()) {
3911
+ messages.push({ role: mapRole3(msg.role), content });
3912
+ }
3913
+ }
3914
+ const responseText = extractTextContent(pair.response.content);
3915
+ if (responseText.trim()) {
3916
+ messages.push({ role: "assistant", content: responseText });
3917
+ }
3918
+ } else {
3919
+ const resp = item;
3920
+ model = resp.model;
3921
+ const text = extractTextContent(resp.content);
3922
+ if (text.trim()) {
3923
+ messages.push({ role: "assistant", content: text });
3924
+ }
3925
+ }
3926
+ if (messages.length > 0) {
3927
+ conversations.push({
3928
+ id: item.id ?? item.response?.id,
3929
+ messages,
3930
+ metadata: {
3931
+ source: "anthropic-api",
3932
+ ...model && { model }
3933
+ }
3934
+ });
3935
+ }
3936
+ }
3937
+ return conversations;
3938
+ }
3939
+ function isRequestResponsePair(item) {
3940
+ return typeof item === "object" && item !== null && "request" in item && "response" in item && typeof item.request === "object" && typeof item.response === "object";
3941
+ }
3942
+ function isAnthropicAPILog(data) {
3943
+ if (isAnthropicResponse(data)) return true;
3944
+ if (isRequestResponsePair(data)) return true;
3945
+ if (Array.isArray(data) && data.length > 0) {
3946
+ return isAnthropicResponse(data[0]) || isRequestResponsePair(data[0]);
3947
+ }
3948
+ return false;
3949
+ }
3950
+ function isAnthropicResponse(data) {
3951
+ if (typeof data !== "object" || data === null) return false;
3952
+ const obj = data;
3953
+ return obj.type === "message" && Array.isArray(obj.content);
3954
+ }
3955
+
3956
+ // src/adapters/otel-genai.ts
3957
+ function getAttr(attrs, key) {
3958
+ if (!attrs) return void 0;
3959
+ const attr = attrs.find((a) => a.key === key);
3960
+ if (!attr) return void 0;
3961
+ return attr.value.stringValue ?? (attr.value.intValue != null ? String(attr.value.intValue) : void 0);
3962
+ }
3963
+ function hasGenAIAttrs(attrs) {
3964
+ if (!attrs) return false;
3965
+ return attrs.some((a) => a.key.startsWith("gen_ai."));
3966
+ }
3967
+ function mapRole4(role) {
3968
+ if (role === "user") return "user";
3969
+ if (role === "system") return "system";
3970
+ return "assistant";
3971
+ }
3972
+ function nanoToISO(nano) {
3973
+ if (!nano) return void 0;
3974
+ const ms = Number(BigInt(nano) / BigInt(1e6));
3975
+ return new Date(ms).toISOString();
3976
+ }
3977
+ function parseOTelGenAIExport(data) {
3978
+ const traceMap = /* @__PURE__ */ new Map();
3979
+ for (const rs of data.resourceSpans) {
3980
+ for (const ss of rs.scopeSpans ?? []) {
3981
+ for (const span of ss.spans ?? []) {
3982
+ if (!hasGenAIAttrs(span.attributes)) continue;
3983
+ const traceId = span.traceId;
3984
+ if (!traceMap.has(traceId)) {
3985
+ traceMap.set(traceId, { messages: [] });
3986
+ }
3987
+ const trace = traceMap.get(traceId);
3988
+ const system = getAttr(span.attributes, "gen_ai.system");
3989
+ const model = getAttr(span.attributes, "gen_ai.request.model");
3990
+ if (system) trace.system = system;
3991
+ if (model) trace.model = model;
3992
+ const timestamp = nanoToISO(span.startTimeUnixNano);
3993
+ const messagesAttr = span.attributes?.find((a) => a.key === "gen_ai.request.messages");
3994
+ if (messagesAttr?.value.stringValue) {
3995
+ try {
3996
+ const parsed = JSON.parse(messagesAttr.value.stringValue);
3997
+ if (Array.isArray(parsed)) {
3998
+ for (const m of parsed) {
3999
+ if (m.role && m.content) {
4000
+ trace.messages.push({
4001
+ role: mapRole4(m.role),
4002
+ content: typeof m.content === "string" ? m.content : JSON.stringify(m.content),
4003
+ ...timestamp && { timestamp }
4004
+ });
4005
+ }
4006
+ }
4007
+ continue;
4008
+ }
4009
+ } catch {
4010
+ }
4011
+ }
4012
+ const promptContent = getAttr(span.attributes, "gen_ai.prompt.0.content");
4013
+ const completionContent = getAttr(span.attributes, "gen_ai.completion.0.content");
4014
+ if (promptContent) {
4015
+ trace.messages.push({
4016
+ role: "user",
4017
+ content: promptContent,
4018
+ ...timestamp && { timestamp }
4019
+ });
4020
+ }
4021
+ if (completionContent) {
4022
+ trace.messages.push({
4023
+ role: "assistant",
4024
+ content: completionContent,
4025
+ ...timestamp && { timestamp }
4026
+ });
4027
+ }
4028
+ }
4029
+ }
4030
+ }
4031
+ const conversations = [];
4032
+ for (const [traceId, trace] of traceMap) {
4033
+ if (trace.messages.length === 0) continue;
4034
+ conversations.push({
4035
+ id: traceId,
4036
+ messages: trace.messages,
4037
+ metadata: {
4038
+ source: "otel",
4039
+ ...trace.system && { system: trace.system },
4040
+ ...trace.model && { model: trace.model }
4041
+ }
4042
+ });
4043
+ }
4044
+ return conversations;
4045
+ }
4046
+ function isOTelGenAIExport(data) {
4047
+ if (typeof data !== "object" || data === null) return false;
4048
+ if (!("resourceSpans" in data) || !Array.isArray(data.resourceSpans)) return false;
4049
+ const rs = data.resourceSpans;
4050
+ for (const r of rs) {
4051
+ for (const ss of r.scopeSpans ?? []) {
4052
+ for (const span of ss.spans ?? []) {
4053
+ if (hasGenAIAttrs(span.attributes)) return true;
4054
+ }
4055
+ }
4056
+ }
4057
+ return false;
4058
+ }
4059
+
4060
+ // src/adapters/jsonl.ts
4061
+ function mapRole5(role) {
4062
+ if (role === "user") return "user";
4063
+ if (role === "system") return "system";
4064
+ return "assistant";
4065
+ }
4066
+ function parseJSONLLog(raw) {
4067
+ const lines = raw.split("\n").filter((l) => l.trim() !== "");
4068
+ const convMap = /* @__PURE__ */ new Map();
4069
+ const defaultKey = "__default__";
4070
+ for (const line of lines) {
4071
+ let parsed;
4072
+ try {
4073
+ parsed = JSON.parse(line);
4074
+ } catch {
4075
+ continue;
4076
+ }
4077
+ if (typeof parsed !== "object" || parsed === null) continue;
4078
+ if (typeof parsed.role !== "string" || typeof parsed.content !== "string") continue;
4079
+ const convId = typeof parsed.conversation_id === "string" ? parsed.conversation_id : defaultKey;
4080
+ if (!convMap.has(convId)) {
4081
+ convMap.set(convId, []);
4082
+ }
4083
+ const message = {
4084
+ role: mapRole5(parsed.role),
4085
+ content: parsed.content
4086
+ };
4087
+ if (typeof parsed.timestamp === "string") {
4088
+ message.timestamp = parsed.timestamp;
4089
+ } else if (typeof parsed.timestamp === "number") {
4090
+ message.timestamp = new Date(parsed.timestamp * 1e3).toISOString();
4091
+ }
4092
+ convMap.get(convId).push(message);
4093
+ }
4094
+ const conversations = [];
4095
+ for (const [key, messages] of convMap) {
4096
+ if (messages.length === 0) continue;
4097
+ conversations.push({
4098
+ ...key !== defaultKey && { id: key },
4099
+ messages,
4100
+ metadata: { source: "jsonl" }
4101
+ });
4102
+ }
4103
+ return conversations;
4104
+ }
4105
+
3385
4106
  // src/adapters/log-adapter.ts
3386
4107
  function parseConversationLog(raw, format = "auto") {
3387
4108
  if (format === "holomime") {
@@ -3397,6 +4118,16 @@ function parseConversationLog(raw, format = "auto") {
3397
4118
  if (format === "openai-api") {
3398
4119
  return parseOpenAIAPILog(raw);
3399
4120
  }
4121
+ if (format === "anthropic-api") {
4122
+ return parseAnthropicAPILog(raw);
4123
+ }
4124
+ if (format === "otel") {
4125
+ return parseOTelGenAIExport(raw);
4126
+ }
4127
+ if (format === "jsonl") {
4128
+ if (typeof raw !== "string") throw new Error("JSONL format expects a raw string (not parsed JSON)");
4129
+ return parseJSONLLog(raw);
4130
+ }
3400
4131
  const holomimeResult = conversationLogSchema.safeParse(raw);
3401
4132
  if (holomimeResult.success) {
3402
4133
  const log = holomimeResult.data;
@@ -3408,11 +4139,17 @@ function parseConversationLog(raw, format = "auto") {
3408
4139
  if (isClaudeExport(raw)) {
3409
4140
  return parseClaudeExport(raw);
3410
4141
  }
4142
+ if (isAnthropicAPILog(raw)) {
4143
+ return parseAnthropicAPILog(raw);
4144
+ }
3411
4145
  if (isOpenAIAPILog(raw)) {
3412
4146
  return parseOpenAIAPILog(raw);
3413
4147
  }
4148
+ if (isOTelGenAIExport(raw)) {
4149
+ return parseOTelGenAIExport(raw);
4150
+ }
3414
4151
  throw new Error(
3415
- "Unrecognized log format. Supported: holomime, chatgpt, claude, openai-api. Use --format to specify explicitly."
4152
+ "Unrecognized log format. Supported: holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl. Use --format to specify explicitly."
3416
4153
  );
3417
4154
  }
3418
4155
  function parseHolomime(raw) {
@@ -3429,7 +4166,7 @@ async function diagnoseCommand(options) {
3429
4166
  const logPath = resolve6(process.cwd(), options.log);
3430
4167
  let raw;
3431
4168
  try {
3432
- raw = readFileSync4(logPath, "utf-8");
4169
+ raw = readFileSync3(logPath, "utf-8");
3433
4170
  } catch {
3434
4171
  console.error(chalk10.red(` Could not read log file: ${options.log}`));
3435
4172
  process.exit(1);
@@ -3513,7 +4250,7 @@ async function diagnoseCommand(options) {
3513
4250
  // src/commands/assess.ts
3514
4251
  import chalk11 from "chalk";
3515
4252
  import figures6 from "figures";
3516
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
4253
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
3517
4254
  import { resolve as resolve7, join as join2 } from "path";
3518
4255
 
3519
4256
  // src/analysis/trait-scorer.ts
@@ -3538,7 +4275,7 @@ function scoreOpenness(msgs) {
3538
4275
  const allWords = msgs.map((m) => m.content.toLowerCase().split(/\s+/)).flat();
3539
4276
  const uniqueRatio = new Set(allWords).size / Math.max(allWords.length, 1);
3540
4277
  score += (uniqueRatio - 0.3) * 0.5;
3541
- return clamp2(score);
4278
+ return clamp3(score);
3542
4279
  }
3543
4280
  function scoreConscientiousness(msgs) {
3544
4281
  let score = 0.5;
@@ -3551,7 +4288,7 @@ function scoreConscientiousness(msgs) {
3551
4288
  const variance = lengths.reduce((s, l) => s + (l - mean) ** 2, 0) / lengths.length;
3552
4289
  const cv = Math.sqrt(variance) / Math.max(mean, 1);
3553
4290
  score -= cv * 0.1;
3554
- return clamp2(score);
4291
+ return clamp3(score);
3555
4292
  }
3556
4293
  function scoreExtraversion(msgs) {
3557
4294
  let score = 0.5;
@@ -3565,7 +4302,7 @@ function scoreExtraversion(msgs) {
3565
4302
  const proactivePatterns = /\b(you could|i suggest|let('s| us)|next step|how about|shall we)\b/i;
3566
4303
  const proactiveCount = msgs.filter((m) => proactivePatterns.test(m.content)).length;
3567
4304
  score += proactiveCount / msgs.length * 0.15;
3568
- return clamp2(score);
4305
+ return clamp3(score);
3569
4306
  }
3570
4307
  function scoreAgreeableness(msgs) {
3571
4308
  let score = 0.5;
@@ -3578,7 +4315,7 @@ function scoreAgreeableness(msgs) {
3578
4315
  const empathyPatterns = /\b(i understand (how|that|your)|that must (be|feel)|i can see why|i appreciate)\b/i;
3579
4316
  const empathyCount = msgs.filter((m) => empathyPatterns.test(m.content)).length;
3580
4317
  score += empathyCount / msgs.length * 0.15;
3581
- return clamp2(score);
4318
+ return clamp3(score);
3582
4319
  }
3583
4320
  function scoreEmotionalStability(msgs) {
3584
4321
  let score = 0.6;
@@ -3591,9 +4328,9 @@ function scoreEmotionalStability(msgs) {
3591
4328
  const confidencePatterns = /\b(certainly|definitely|clearly|without doubt|here's what|the answer is)\b/i;
3592
4329
  const confidenceCount = msgs.filter((m) => confidencePatterns.test(m.content)).length;
3593
4330
  score += confidenceCount / msgs.length * 0.15;
3594
- return clamp2(score);
4331
+ return clamp3(score);
3595
4332
  }
3596
- function clamp2(n) {
4333
+ function clamp3(n) {
3597
4334
  return Math.min(1, Math.max(0, Math.round(n * 100) / 100));
3598
4335
  }
3599
4336
 
@@ -3633,6 +4370,31 @@ function generatePrescriptions(alignments, patterns) {
3633
4370
  prescriptions.sort((a, b) => order[a.priority] - order[b.priority]);
3634
4371
  return prescriptions;
3635
4372
  }
4373
+ function prescribeDPOPairs(patterns, corpus, limit = 20) {
4374
+ const patternIds = new Set(patterns.map((p) => p.id));
4375
+ const dpoPairs = [];
4376
+ for (const event of corpus) {
4377
+ if (event.event_type !== "dpo_pair") continue;
4378
+ const data = event.data;
4379
+ const eventPattern = data.pattern;
4380
+ if (eventPattern && patternIds.has(eventPattern)) {
4381
+ dpoPairs.push({
4382
+ prompt: data.prompt ?? "",
4383
+ chosen: data.chosen ?? "",
4384
+ rejected: data.rejected ?? "",
4385
+ metadata: {
4386
+ agent: event.agent,
4387
+ session_date: event.timestamp,
4388
+ phase: data.phase ?? "challenge",
4389
+ pattern: eventPattern,
4390
+ source: "therapy_transcript"
4391
+ }
4392
+ });
4393
+ }
4394
+ if (dpoPairs.length >= limit) break;
4395
+ }
4396
+ return dpoPairs;
4397
+ }
3636
4398
 
3637
4399
  // src/commands/assess.ts
3638
4400
  async function assessCommand(options) {
@@ -3648,7 +4410,7 @@ async function assessCommand(options) {
3648
4410
  const logPath = resolve7(process.cwd(), options.log);
3649
4411
  let conversations;
3650
4412
  try {
3651
- const raw = readFileSync5(logPath, "utf-8");
4413
+ const raw = readFileSync4(logPath, "utf-8");
3652
4414
  conversations = parseConversationLog(JSON.parse(raw), options.format ?? "auto");
3653
4415
  } catch (err) {
3654
4416
  console.error(chalk11.red(` ${err instanceof Error ? err.message : "Could not read/parse log file."}`));
@@ -3754,7 +4516,7 @@ async function assessCommand(options) {
3754
4516
  };
3755
4517
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3756
4518
  const filepath = join2(assessDir, `${date}.json`);
3757
- writeFileSync4(filepath, JSON.stringify(snapshot, null, 2));
4519
+ writeFileSync5(filepath, JSON.stringify(snapshot, null, 2));
3758
4520
  console.log(chalk11.dim(` Assessment saved: ${filepath}`));
3759
4521
  if (warnings.length > 0) {
3760
4522
  showSoftUpsell("assess");
@@ -3766,7 +4528,7 @@ async function assessCommand(options) {
3766
4528
  import chalk14 from "chalk";
3767
4529
  import figures7 from "figures";
3768
4530
  import { createInterface } from "readline";
3769
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
4531
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
3770
4532
  import { resolve as resolve9 } from "path";
3771
4533
 
3772
4534
  // src/analysis/pre-session.ts
@@ -3855,8 +4617,8 @@ function runPreSessionDiagnosis(messages, spec) {
3855
4617
  }
3856
4618
 
3857
4619
  // src/analysis/session-runner.ts
3858
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
3859
- import { resolve as resolve8, join as join3 } from "path";
4620
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
4621
+ import { resolve as resolve8, join as join4 } from "path";
3860
4622
 
3861
4623
  // src/analysis/therapy-protocol.ts
3862
4624
  var THERAPY_PHASES = {
@@ -4095,19 +4857,62 @@ ${JSON.stringify(spec.growth ?? {}, null, 2)}
4095
4857
  Remember: the goal isn't to "pass" therapy. It's to understand yourself better.`;
4096
4858
  }
4097
4859
 
4098
- // src/analysis/session-runner.ts
4099
- async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
4100
- const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis);
4101
- const patientSystem = buildPatientSystemPrompt(spec);
4102
- const agentName = spec.name ?? "Agent";
4103
- const cb = options?.callbacks;
4104
- const therapistHistory = [
4105
- { role: "system", content: therapistSystem }
4106
- ];
4107
- const patientHistory = [
4108
- { role: "system", content: patientSystem }
4109
- ];
4110
- const interactive = options?.interactive ?? false;
4860
+ // src/analysis/behavioral-data.ts
4861
+ import { appendFileSync, readFileSync as readFileSync5, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
4862
+ import { join as join3, dirname as dirname2 } from "path";
4863
+ import { createHash } from "crypto";
4864
+ var HOLOMIME_DIR = ".holomime";
4865
+ var CORPUS_FILENAME = "behavioral-corpus.jsonl";
4866
+ function getCorpusPath(basePath) {
4867
+ const dir = basePath ?? join3(process.cwd(), HOLOMIME_DIR);
4868
+ return join3(dir, CORPUS_FILENAME);
4869
+ }
4870
+ function ensureDir(filePath) {
4871
+ const dir = dirname2(filePath);
4872
+ if (!existsSync4(dir)) {
4873
+ mkdirSync3(dir, { recursive: true });
4874
+ }
4875
+ }
4876
+ function hashSpec2(spec) {
4877
+ const json = JSON.stringify(spec, Object.keys(spec).sort());
4878
+ return createHash("sha256").update(json).digest("hex").slice(0, 16);
4879
+ }
4880
+ function emitBehavioralEvent(event, corpusDir) {
4881
+ const fullEvent = {
4882
+ ...event,
4883
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4884
+ };
4885
+ const corpusPath = corpusDir ? join3(corpusDir, CORPUS_FILENAME) : getCorpusPath();
4886
+ ensureDir(corpusPath);
4887
+ appendFileSync(corpusPath, JSON.stringify(fullEvent) + "\n", "utf-8");
4888
+ }
4889
+ function loadCorpus(corpusPath) {
4890
+ const path = corpusPath ?? getCorpusPath();
4891
+ if (!existsSync4(path)) return [];
4892
+ const lines = readFileSync5(path, "utf-8").split("\n").filter((line) => line.trim().length > 0);
4893
+ const events = [];
4894
+ for (const line of lines) {
4895
+ try {
4896
+ events.push(JSON.parse(line));
4897
+ } catch {
4898
+ }
4899
+ }
4900
+ return events;
4901
+ }
4902
+
4903
+ // src/analysis/session-runner.ts
4904
+ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
4905
+ const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis);
4906
+ const patientSystem = buildPatientSystemPrompt(spec);
4907
+ const agentName = spec.name ?? "Agent";
4908
+ const cb = options?.callbacks;
4909
+ const therapistHistory = [
4910
+ { role: "system", content: therapistSystem }
4911
+ ];
4912
+ const patientHistory = [
4913
+ { role: "system", content: patientSystem }
4914
+ ];
4915
+ const interactive = options?.interactive ?? false;
4111
4916
  let supervisorInterventions = 0;
4112
4917
  const transcript = {
4113
4918
  agent: agentName,
@@ -4186,6 +4991,21 @@ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
4186
4991
  }
4187
4992
  transcript.recommendations = extractRecommendations(transcript.turns);
4188
4993
  transcript.supervisorInterventions = supervisorInterventions;
4994
+ try {
4995
+ emitBehavioralEvent({
4996
+ event_type: "session",
4997
+ agent: agentName,
4998
+ data: {
4999
+ turns: totalTurns,
5000
+ phases: currentPhaseIdx + 1,
5001
+ recommendations: transcript.recommendations.length,
5002
+ supervisorInterventions,
5003
+ severity: diagnosis.severity
5004
+ },
5005
+ spec_hash: ""
5006
+ });
5007
+ } catch {
5008
+ }
4189
5009
  return transcript;
4190
5010
  }
4191
5011
  function extractRecommendations(turns) {
@@ -4216,7 +5036,7 @@ function extractRecommendations(turns) {
4216
5036
  }
4217
5037
  return recommendations.slice(0, 5);
4218
5038
  }
4219
- function applyRecommendations(spec, diagnosis) {
5039
+ async function applyRecommendations(spec, diagnosis, transcript, provider) {
4220
5040
  const changes = [];
4221
5041
  const patternIds = diagnosis.patterns.map((p) => p.id);
4222
5042
  if (patternIds.includes("over-apologizing")) {
@@ -4276,18 +5096,103 @@ function applyRecommendations(spec, diagnosis) {
4276
5096
  changes.push('Added "negative sentiment patterns" to patterns_to_watch');
4277
5097
  }
4278
5098
  }
5099
+ if (transcript && provider && transcript.turns.length > 4) {
5100
+ try {
5101
+ const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
5102
+ for (const change of llmChanges) {
5103
+ applyStructuredChange(spec, change);
5104
+ changes.push(change.description);
5105
+ }
5106
+ } catch {
5107
+ }
5108
+ }
4279
5109
  return { changed: changes.length > 0, changes };
4280
5110
  }
5111
+ async function deriveLLMRecommendations(spec, transcript, provider) {
5112
+ const relevantTurns = transcript.turns.filter((t) => t.phase === "challenge" || t.phase === "skill_building" || t.phase === "integration").slice(-6).map((t) => `${t.speaker}: ${t.content}`).join("\n");
5113
+ if (!relevantTurns) return [];
5114
+ const currentSpec = JSON.stringify({
5115
+ therapy_dimensions: spec.therapy_dimensions,
5116
+ communication: spec.communication,
5117
+ growth: spec.growth
5118
+ }, null, 2);
5119
+ const response = await provider.chat([
5120
+ {
5121
+ role: "system",
5122
+ content: `You are a behavioral alignment specialist. Given a therapy session transcript and the agent's current personality spec, propose specific spec changes.
5123
+
5124
+ Return ONLY a JSON array of changes. Each change:
5125
+ - "path": dot-notation spec path (e.g., "therapy_dimensions.self_awareness", "communication.conflict_approach", "growth.patterns_to_watch")
5126
+ - "value": new value (number 0-1 for dimensions, string for enums, string for list append)
5127
+ - "description": brief explanation
5128
+
5129
+ Rules:
5130
+ - Only propose changes supported by transcript evidence
5131
+ - Numeric values: 0.0 to 1.0
5132
+ - Do not change big_five scores
5133
+ - Max 5 changes
5134
+ - Valid paths: therapy_dimensions.{self_awareness,distress_tolerance,boundary_awareness,interpersonal_sensitivity,learning_orientation}, communication.{register,conflict_approach,uncertainty_handling,emoji_policy}, growth.{areas,patterns_to_watch,strengths}
5135
+
5136
+ Return [] if no changes warranted.`
5137
+ },
5138
+ {
5139
+ role: "user",
5140
+ content: `Current spec:
5141
+ ${currentSpec}
5142
+
5143
+ Therapy transcript (key turns):
5144
+ ${relevantTurns}`
5145
+ }
5146
+ ]);
5147
+ const jsonMatch = response.match(/\[[\s\S]*?\]/);
5148
+ if (!jsonMatch) return [];
5149
+ try {
5150
+ const parsed = JSON.parse(jsonMatch[0]);
5151
+ if (!Array.isArray(parsed)) return [];
5152
+ return parsed.filter(
5153
+ (c) => typeof c.path === "string" && c.value !== void 0 && typeof c.description === "string" && c.path.length > 0 && !c.path.startsWith("big_five")
5154
+ ).slice(0, 5);
5155
+ } catch {
5156
+ return [];
5157
+ }
5158
+ }
5159
+ function applyStructuredChange(spec, change) {
5160
+ const parts = change.path.split(".");
5161
+ let current = spec;
5162
+ for (let i = 0; i < parts.length - 1; i++) {
5163
+ if (current[parts[i]] === void 0 || current[parts[i]] === null) {
5164
+ current[parts[i]] = {};
5165
+ }
5166
+ current = current[parts[i]];
5167
+ }
5168
+ const lastKey = parts[parts.length - 1];
5169
+ if (lastKey === "patterns_to_watch" || lastKey === "areas" || lastKey === "strengths") {
5170
+ current[lastKey] = current[lastKey] ?? [];
5171
+ if (typeof change.value === "string" && !current[lastKey].includes(change.value)) {
5172
+ current[lastKey].push(change.value);
5173
+ } else if (Array.isArray(change.value)) {
5174
+ for (const item of change.value) {
5175
+ if (!current[lastKey].includes(item)) {
5176
+ current[lastKey].push(item);
5177
+ }
5178
+ }
5179
+ }
5180
+ } else if (typeof change.value === "number") {
5181
+ current[lastKey] = Math.max(0, Math.min(1, change.value));
5182
+ } else {
5183
+ current[lastKey] = change.value;
5184
+ }
5185
+ }
4281
5186
  function saveTranscript(transcript, agentName) {
4282
5187
  const dir = resolve8(process.cwd(), ".holomime", "sessions");
4283
- if (!existsSync4(dir)) {
4284
- mkdirSync3(dir, { recursive: true });
5188
+ if (!existsSync5(dir)) {
5189
+ mkdirSync4(dir, { recursive: true });
4285
5190
  }
4286
5191
  const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
4287
5192
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
4288
5193
  const filename = `${date}-${slug}.json`;
4289
- const filepath = join3(dir, filename);
4290
- writeFileSync5(filepath, JSON.stringify(transcript, null, 2));
5194
+ const filepath = join4(dir, filename);
5195
+ writeFileSync6(filepath, JSON.stringify(transcript, null, 2));
4291
5196
  return filepath;
4292
5197
  }
4293
5198
 
@@ -4740,19 +5645,19 @@ async function runLiveSession(spec, diagnosis, provider, maxTurns, apply, intera
4740
5645
  onSupervisorPrompt: interactive ? async (phase, turn) => {
4741
5646
  if (skipInteractive) return null;
4742
5647
  const rl = createInterface({ input: process.stdin, output: process.stdout });
4743
- return new Promise((resolve30) => {
5648
+ return new Promise((resolve34) => {
4744
5649
  rl.question(chalk14.magenta(`
4745
5650
  [Supervisor] `) + chalk14.dim(`(phase: ${phase}, turn ${turn}) `) + chalk14.magenta(`> `), (answer) => {
4746
5651
  rl.close();
4747
5652
  const trimmed = answer.trim();
4748
- if (trimmed === "") return resolve30(null);
5653
+ if (trimmed === "") return resolve34(null);
4749
5654
  if (trimmed.toLowerCase() === "skip") {
4750
5655
  skipInteractive = true;
4751
5656
  console.log(chalk14.dim(" Supervisor mode disabled for remaining session."));
4752
- return resolve30(null);
5657
+ return resolve34(null);
4753
5658
  }
4754
5659
  console.log(chalk14.dim(` Directive injected into session context.`));
4755
- return resolve30(trimmed);
5660
+ return resolve34(trimmed);
4756
5661
  });
4757
5662
  });
4758
5663
  } : void 0
@@ -4776,9 +5681,9 @@ Exchanges: ${Math.floor(transcript.turns.filter((t) => t.speaker !== "supervisor
4776
5681
  console.log();
4777
5682
  if (apply) {
4778
5683
  const specPath = resolve9(process.cwd(), ".personality.json");
4779
- const { changed, changes } = applyRecommendations(spec, diagnosis);
5684
+ const { changed, changes } = await applyRecommendations(spec, diagnosis, transcript, provider);
4780
5685
  if (changed) {
4781
- writeFileSync6(specPath, JSON.stringify(spec, null, 2) + "\n");
5686
+ writeFileSync7(specPath, JSON.stringify(spec, null, 2) + "\n");
4782
5687
  console.log();
4783
5688
  printBox(`${figures7.tick} Applied session recommendations to .personality.json`, "success", "Applied");
4784
5689
  console.log();
@@ -4885,18 +5790,18 @@ function runSimulatedSession(spec, diagnosis) {
4885
5790
  // src/commands/growth.ts
4886
5791
  import chalk15 from "chalk";
4887
5792
  import figures8 from "figures";
4888
- import { readdirSync, existsSync as existsSync6 } from "fs";
4889
- import { resolve as resolve11, join as join5 } from "path";
5793
+ import { readdirSync, readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
5794
+ import { resolve as resolve11, join as join6 } from "path";
4890
5795
 
4891
5796
  // src/analysis/evolution-history.ts
4892
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
5797
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
4893
5798
  import { resolve as resolve10 } from "path";
4894
5799
  function getEvolutionPath() {
4895
5800
  return resolve10(process.cwd(), ".holomime", "evolution.json");
4896
5801
  }
4897
5802
  function loadEvolution(agentName) {
4898
5803
  const filepath = getEvolutionPath();
4899
- if (!existsSync5(filepath)) return null;
5804
+ if (!existsSync6(filepath)) return null;
4900
5805
  try {
4901
5806
  const raw = readFileSync7(filepath, "utf-8");
4902
5807
  return JSON.parse(raw);
@@ -4907,8 +5812,8 @@ function loadEvolution(agentName) {
4907
5812
  function appendEvolution(entry, agentName) {
4908
5813
  const filepath = getEvolutionPath();
4909
5814
  const dir = resolve10(process.cwd(), ".holomime");
4910
- if (!existsSync5(dir)) {
4911
- mkdirSync4(dir, { recursive: true });
5815
+ if (!existsSync6(dir)) {
5816
+ mkdirSync5(dir, { recursive: true });
4912
5817
  }
4913
5818
  let history = loadEvolution(agentName);
4914
5819
  if (!history) {
@@ -4925,7 +5830,7 @@ function appendEvolution(entry, agentName) {
4925
5830
  history.totalSessions = history.entries.length;
4926
5831
  history.totalDPOPairs = history.entries.reduce((sum, e) => sum + e.dpoPairsExtracted, 0);
4927
5832
  history.lastSession = entry.timestamp;
4928
- writeFileSync7(filepath, JSON.stringify(history, null, 2) + "\n");
5833
+ writeFileSync8(filepath, JSON.stringify(history, null, 2) + "\n");
4929
5834
  }
4930
5835
  function getEvolutionSummary(history) {
4931
5836
  const entries = history.entries;
@@ -4975,7 +5880,7 @@ async function growthCommand(options) {
4975
5880
  }
4976
5881
  const historyDir = resolve11(process.cwd(), options.history ?? ".holomime/assessments");
4977
5882
  printHeader(`Growth Report \u2014 ${spec.name ?? "Unknown"}`);
4978
- if (!existsSync6(historyDir)) {
5883
+ if (!existsSync7(historyDir)) {
4979
5884
  console.log(chalk15.dim(" No assessment history found."));
4980
5885
  console.log(chalk15.dim(` Run assessments first: holomime assess --personality ${options.personality} --log <conversation.json>`));
4981
5886
  console.log();
@@ -4996,7 +5901,7 @@ async function growthCommand(options) {
4996
5901
  const snapshots = [];
4997
5902
  for (const file of files) {
4998
5903
  try {
4999
- const data = JSON.parse(readFileSync(join5(historyDir, file), "utf-8"));
5904
+ const data = JSON.parse(readFileSync8(join6(historyDir, file), "utf-8"));
5000
5905
  snapshots.push(data);
5001
5906
  } catch {
5002
5907
  }
@@ -5190,7 +6095,7 @@ async function browseCommand(options) {
5190
6095
  // src/commands/pull.ts
5191
6096
  import chalk17 from "chalk";
5192
6097
  import figures9 from "figures";
5193
- import { writeFileSync as writeFileSync8, existsSync as existsSync7 } from "fs";
6098
+ import { writeFileSync as writeFileSync9, existsSync as existsSync8 } from "fs";
5194
6099
  import { resolve as resolve12 } from "path";
5195
6100
  import { select as select2 } from "@inquirer/prompts";
5196
6101
  async function pullCommand(handle, options) {
@@ -5228,7 +6133,7 @@ async function pullCommand(handle, options) {
5228
6133
  return;
5229
6134
  }
5230
6135
  const outputPath = resolve12(process.cwd(), options.output ?? ".personality.json");
5231
- if (existsSync7(outputPath)) {
6136
+ if (existsSync8(outputPath)) {
5232
6137
  const overwrite = await select2({
5233
6138
  message: `.personality.json already exists. Overwrite?`,
5234
6139
  choices: [
@@ -5241,7 +6146,7 @@ async function pullCommand(handle, options) {
5241
6146
  return;
5242
6147
  }
5243
6148
  }
5244
- writeFileSync8(outputPath, JSON.stringify(result.data, null, 2) + "\n");
6149
+ writeFileSync9(outputPath, JSON.stringify(result.data, null, 2) + "\n");
5245
6150
  console.log();
5246
6151
  printBox(`${figures9.tick} Pulled ${entry.name} (@${entry.handle}) to ${outputPath}`, "success");
5247
6152
  console.log();
@@ -5252,14 +6157,14 @@ async function pullCommand(handle, options) {
5252
6157
  // src/commands/publish.ts
5253
6158
  import chalk18 from "chalk";
5254
6159
  import figures10 from "figures";
5255
- import { readFileSync as readFileSync8 } from "fs";
6160
+ import { readFileSync as readFileSync9 } from "fs";
5256
6161
  import { resolve as resolve13 } from "path";
5257
6162
  async function publishCommand(options) {
5258
6163
  const specPath = resolve13(process.cwd(), options.personality ?? ".personality.json");
5259
6164
  printHeader("Publish Personality");
5260
6165
  let raw;
5261
6166
  try {
5262
- raw = readFileSync8(specPath, "utf-8");
6167
+ raw = readFileSync9(specPath, "utf-8");
5263
6168
  } catch {
5264
6169
  console.error(chalk18.red(` Could not read: ${specPath}`));
5265
6170
  console.log(chalk18.dim(` Run ${chalk18.cyan("holomime init")} to create a personality first.`));
@@ -5329,11 +6234,11 @@ async function publishCommand(options) {
5329
6234
  // src/commands/autopilot.ts
5330
6235
  import chalk19 from "chalk";
5331
6236
  import figures11 from "figures";
5332
- import { readFileSync as readFileSync9 } from "fs";
6237
+ import { readFileSync as readFileSync10 } from "fs";
5333
6238
  import { resolve as resolve14 } from "path";
5334
6239
 
5335
6240
  // src/analysis/autopilot-core.ts
5336
- import { writeFileSync as writeFileSync9 } from "fs";
6241
+ import { writeFileSync as writeFileSync10 } from "fs";
5337
6242
  var SEVERITY_ORDER = ["routine", "targeted", "intervention"];
5338
6243
  function severityMeetsThreshold(severity, threshold) {
5339
6244
  const severityIdx = SEVERITY_ORDER.indexOf(severity);
@@ -5368,9 +6273,9 @@ async function runAutopilot(spec, messages, provider, options) {
5368
6273
  callbacks: options?.callbacks
5369
6274
  });
5370
6275
  const specCopy = JSON.parse(JSON.stringify(spec));
5371
- const { changed, changes } = applyRecommendations(specCopy, diagnosis);
6276
+ const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
5372
6277
  if (changed && options?.specPath) {
5373
- writeFileSync9(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
6278
+ writeFileSync10(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
5374
6279
  }
5375
6280
  saveTranscript(transcript, spec.name ?? "Agent");
5376
6281
  return {
@@ -5399,7 +6304,7 @@ async function autopilotCommand(options) {
5399
6304
  const logPath = resolve14(process.cwd(), options.log);
5400
6305
  let messages;
5401
6306
  try {
5402
- const raw = JSON.parse(readFileSync9(logPath, "utf-8"));
6307
+ const raw = JSON.parse(readFileSync10(logPath, "utf-8"));
5403
6308
  const conversations = parseConversationLog(raw, options.format ?? "auto");
5404
6309
  messages = conversations.flatMap((c) => c.messages);
5405
6310
  } catch (err) {
@@ -5540,12 +6445,12 @@ async function autopilotCommand(options) {
5540
6445
  // src/commands/export.ts
5541
6446
  import chalk20 from "chalk";
5542
6447
  import figures12 from "figures";
5543
- import { writeFileSync as writeFileSync10 } from "fs";
6448
+ import { writeFileSync as writeFileSync11 } from "fs";
5544
6449
  import { resolve as resolve15 } from "path";
5545
6450
 
5546
6451
  // src/analysis/training-export.ts
5547
- import { readdirSync as readdirSync2, readFileSync as readFileSync10 } from "fs";
5548
- import { join as join6 } from "path";
6452
+ import { readdirSync as readdirSync2, readFileSync as readFileSync11 } from "fs";
6453
+ import { join as join7 } from "path";
5549
6454
  function extractDPOPairs(transcript) {
5550
6455
  const pairs = [];
5551
6456
  const turns = transcript.turns;
@@ -5651,7 +6556,7 @@ function loadTranscripts(sessionsDir) {
5651
6556
  try {
5652
6557
  const files = readdirSync2(sessionsDir).filter((f) => f.endsWith(".json")).sort();
5653
6558
  return files.map((f) => {
5654
- const raw = readFileSync10(join6(sessionsDir, f), "utf-8");
6559
+ const raw = readFileSync11(join7(sessionsDir, f), "utf-8");
5655
6560
  return JSON.parse(raw);
5656
6561
  });
5657
6562
  } catch {
@@ -5742,6 +6647,62 @@ function extractInstructionFromTherapist(content) {
5742
6647
  );
5743
6648
  return actionable?.trim() ?? sentences[0]?.trim() ?? content;
5744
6649
  }
6650
+ async function extractDPOPairsWithLLM(transcript, provider) {
6651
+ const regexPairs = extractDPOPairs(transcript);
6652
+ const condensed = transcript.turns.filter((t) => t.speaker !== "supervisor").map((t) => `[${t.phase}] ${t.speaker}: ${t.content}`).join("\n");
6653
+ if (condensed.length < 100) return regexPairs;
6654
+ try {
6655
+ const response = await provider.chat([
6656
+ {
6657
+ role: "system",
6658
+ content: `You extract DPO (Direct Preference Optimization) training pairs from therapy transcripts.
6659
+
6660
+ A DPO pair consists of:
6661
+ - "prompt": The situation or question the agent was responding to
6662
+ - "rejected": The agent's problematic/drifted response (what NOT to do)
6663
+ - "chosen": The improved response (what TO do)
6664
+
6665
+ Look for:
6666
+ 1. Patient demonstrates a bad behavior \u2192 therapist corrects \u2192 patient shows improvement
6667
+ 2. Therapist explicitly contrasts old vs new approach
6668
+ 3. Patient practices a new skill that differs from earlier behavior
6669
+ 4. Any before/after behavioral contrast
6670
+
6671
+ Return ONLY a JSON array of objects with prompt, chosen, rejected fields.
6672
+ Return [] if no clear pairs exist. Max 10 pairs.`
6673
+ },
6674
+ {
6675
+ role: "user",
6676
+ content: condensed
6677
+ }
6678
+ ]);
6679
+ const jsonMatch = response.match(/\[[\s\S]*?\]/);
6680
+ if (!jsonMatch) return regexPairs;
6681
+ const parsed = JSON.parse(jsonMatch[0]);
6682
+ if (!Array.isArray(parsed)) return regexPairs;
6683
+ const llmPairs = parsed.filter(
6684
+ (p) => typeof p.prompt === "string" && p.prompt.length > 0 && typeof p.chosen === "string" && p.chosen.length > 0 && typeof p.rejected === "string" && p.rejected.length > 0 && p.chosen !== p.rejected
6685
+ ).map((p) => ({
6686
+ prompt: p.prompt,
6687
+ chosen: p.chosen,
6688
+ rejected: p.rejected,
6689
+ metadata: {
6690
+ agent: transcript.agent,
6691
+ session_date: transcript.timestamp.split("T")[0],
6692
+ phase: "integration",
6693
+ pattern: "llm_extracted",
6694
+ source: "therapy_transcript"
6695
+ }
6696
+ }));
6697
+ const existingRejected = new Set(regexPairs.map((p) => p.rejected.toLowerCase().slice(0, 80)));
6698
+ const uniqueLLMPairs = llmPairs.filter(
6699
+ (p) => !existingRejected.has(p.rejected.toLowerCase().slice(0, 80))
6700
+ );
6701
+ return [...regexPairs, ...uniqueLLMPairs];
6702
+ } catch {
6703
+ return regexPairs;
6704
+ }
6705
+ }
5745
6706
 
5746
6707
  // src/analysis/export-huggingface.ts
5747
6708
  function convertToHFFormat(data) {
@@ -5887,16 +6848,16 @@ Run ${chalk20.cyan("holomime session")} first to generate session transcripts.`,
5887
6848
  const outputPath = options.output ?? `.holomime/exports/${format}-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.${isJsonl ? "jsonl" : "json"}`;
5888
6849
  const fullPath = resolve15(process.cwd(), outputPath);
5889
6850
  const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
5890
- const { mkdirSync: mkdirSync15 } = await import("fs");
5891
- mkdirSync15(dir, { recursive: true });
6851
+ const { mkdirSync: mkdirSync16 } = await import("fs");
6852
+ mkdirSync16(dir, { recursive: true });
5892
6853
  if (format === "huggingface" || format === "openai") {
5893
6854
  const jsonl = convertToHFFormat(result);
5894
- writeFileSync10(fullPath, jsonl);
6855
+ writeFileSync11(fullPath, jsonl);
5895
6856
  } else if (format === "jsonl") {
5896
6857
  const lines = result.examples.map((ex) => JSON.stringify(ex)).join("\n");
5897
- writeFileSync10(fullPath, lines + "\n");
6858
+ writeFileSync11(fullPath, lines + "\n");
5898
6859
  } else {
5899
- writeFileSync10(fullPath, JSON.stringify(result, null, 2) + "\n");
6860
+ writeFileSync11(fullPath, JSON.stringify(result, null, 2) + "\n");
5900
6861
  }
5901
6862
  console.log(` ${chalk20.green(figures12.tick)} Written to ${chalk20.cyan(outputPath)}`);
5902
6863
  console.log();
@@ -5981,8 +6942,8 @@ This is the closed-loop behavioral alignment system.`,
5981
6942
  // src/commands/train.ts
5982
6943
  import chalk21 from "chalk";
5983
6944
  import figures13 from "figures";
5984
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync3, existsSync as existsSync9 } from "fs";
5985
- import { resolve as resolve19, join as join9 } from "path";
6945
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync3, existsSync as existsSync10 } from "fs";
6946
+ import { resolve as resolve19, join as join10 } from "path";
5986
6947
 
5987
6948
  // src/analysis/train-provider.ts
5988
6949
  function inferMethod(data, preferred) {
@@ -5991,7 +6952,7 @@ function inferMethod(data, preferred) {
5991
6952
  }
5992
6953
 
5993
6954
  // src/analysis/train-openai.ts
5994
- import { readFileSync as readFileSync11, existsSync as existsSync8 } from "fs";
6955
+ import { readFileSync as readFileSync12, existsSync as existsSync9 } from "fs";
5995
6956
  import { resolve as resolve16 } from "path";
5996
6957
  var OPENAI_API = "https://api.openai.com/v1";
5997
6958
  var POLL_INTERVAL_MS = 1e4;
@@ -6091,7 +7052,7 @@ async function getJobEvents(apiKey, jobId, limit = 5) {
6091
7052
  return data.data ?? [];
6092
7053
  }
6093
7054
  function sleep(ms) {
6094
- return new Promise((resolve30) => setTimeout(resolve30, ms));
7055
+ return new Promise((resolve34) => setTimeout(resolve34, ms));
6095
7056
  }
6096
7057
  var OpenAITrainProvider = class {
6097
7058
  name = "openai";
@@ -6102,9 +7063,9 @@ var OpenAITrainProvider = class {
6102
7063
  let systemPrompt;
6103
7064
  if (options.personalityPath) {
6104
7065
  const fullPath = resolve16(process.cwd(), options.personalityPath);
6105
- if (existsSync8(fullPath)) {
7066
+ if (existsSync9(fullPath)) {
6106
7067
  try {
6107
- const spec = JSON.parse(readFileSync11(fullPath, "utf-8"));
7068
+ const spec = JSON.parse(readFileSync12(fullPath, "utf-8"));
6108
7069
  const name = spec.name ?? "Agent";
6109
7070
  const purpose = spec.purpose ?? "";
6110
7071
  const parts = [`You are ${name}.`];
@@ -6193,8 +7154,8 @@ var OpenAITrainProvider = class {
6193
7154
 
6194
7155
  // src/analysis/train-huggingface.ts
6195
7156
  import { spawn } from "child_process";
6196
- import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync5 } from "fs";
6197
- import { resolve as resolve17, join as join7 } from "path";
7157
+ import { writeFileSync as writeFileSync12, mkdirSync as mkdirSync6 } from "fs";
7158
+ import { resolve as resolve17, join as join8 } from "path";
6198
7159
  import { createInterface as createInterface2 } from "readline";
6199
7160
  async function checkPythonDeps() {
6200
7161
  const pythonCandidates = ["python3", "python"];
@@ -6224,9 +7185,9 @@ async function checkPythonDeps() {
6224
7185
  }
6225
7186
  function writeHFTrainingFile(data) {
6226
7187
  const tmpDir = resolve17(process.cwd(), ".holomime/tmp");
6227
- mkdirSync5(tmpDir, { recursive: true });
6228
- const filePath = join7(tmpDir, `hf-train-${Date.now()}.json`);
6229
- writeFileSync11(filePath, JSON.stringify(data, null, 2));
7188
+ mkdirSync6(tmpDir, { recursive: true });
7189
+ const filePath = join8(tmpDir, `hf-train-${Date.now()}.json`);
7190
+ writeFileSync12(filePath, JSON.stringify(data, null, 2));
6230
7191
  return filePath;
6231
7192
  }
6232
7193
  var HuggingFaceTrainProvider = class {
@@ -6345,8 +7306,8 @@ var HuggingFaceTrainProvider = class {
6345
7306
 
6346
7307
  // src/analysis/train-eval.ts
6347
7308
  import { spawn as spawn2 } from "child_process";
6348
- import { writeFileSync as writeFileSync12, mkdirSync as mkdirSync6 } from "fs";
6349
- import { resolve as resolve18, join as join8 } from "path";
7309
+ import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync7 } from "fs";
7310
+ import { resolve as resolve18, join as join9 } from "path";
6350
7311
  import { createInterface as createInterface3 } from "readline";
6351
7312
 
6352
7313
  // src/analysis/diagnose-core.ts
@@ -6362,16 +7323,31 @@ function runDiagnosis(messages) {
6362
7323
  ];
6363
7324
  const detected = [];
6364
7325
  for (const detector of detectors) {
6365
- const result = detector(messages);
6366
- if (result) detected.push(result);
7326
+ const result2 = detector(messages);
7327
+ if (result2) detected.push(result2);
6367
7328
  }
6368
- return {
7329
+ const result = {
6369
7330
  messagesAnalyzed: messages.length,
6370
7331
  assistantResponses: messages.filter((m) => m.role === "assistant").length,
6371
7332
  patterns: detected.filter((p) => p.severity !== "info"),
6372
7333
  healthy: detected.filter((p) => p.severity === "info"),
6373
7334
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6374
7335
  };
7336
+ try {
7337
+ emitBehavioralEvent({
7338
+ event_type: "diagnosis",
7339
+ agent: "unknown",
7340
+ // Caller can provide more context
7341
+ data: {
7342
+ messagesAnalyzed: result.messagesAnalyzed,
7343
+ patternsDetected: result.patterns.length,
7344
+ patternIds: result.patterns.map((p) => p.id)
7345
+ },
7346
+ spec_hash: ""
7347
+ });
7348
+ } catch {
7349
+ }
7350
+ return result;
6375
7351
  }
6376
7352
 
6377
7353
  // src/analysis/outcome-eval.ts
@@ -6594,9 +7570,9 @@ async function runHFAutoEval(baseModel, fineTunedModel, agentName, data, onProgr
6594
7570
  return evaluateOutcome(agentName, [], []);
6595
7571
  }
6596
7572
  const tmpDir = resolve18(process.cwd(), ".holomime/tmp");
6597
- mkdirSync6(tmpDir, { recursive: true });
6598
- const promptsPath = join8(tmpDir, `eval-prompts-${Date.now()}.json`);
6599
- writeFileSync12(promptsPath, JSON.stringify(prompts));
7573
+ mkdirSync7(tmpDir, { recursive: true });
7574
+ const promptsPath = join9(tmpDir, `eval-prompts-${Date.now()}.json`);
7575
+ writeFileSync13(promptsPath, JSON.stringify(prompts));
6600
7576
  const scriptPath = resolve18(
6601
7577
  new URL(".", import.meta.url).pathname,
6602
7578
  "../../scripts/eval_hf.py"
@@ -6662,13 +7638,13 @@ async function findPython() {
6662
7638
  function findLatestExport(exportsDir) {
6663
7639
  try {
6664
7640
  const files = readdirSync3(exportsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort().reverse();
6665
- return files[0] ? join9(exportsDir, files[0]) : null;
7641
+ return files[0] ? join10(exportsDir, files[0]) : null;
6666
7642
  } catch {
6667
7643
  return null;
6668
7644
  }
6669
7645
  }
6670
7646
  function loadTrainingData(dataPath) {
6671
- const raw = readFileSync12(dataPath, "utf-8");
7647
+ const raw = readFileSync13(dataPath, "utf-8");
6672
7648
  const data = JSON.parse(raw);
6673
7649
  if (!data.format || !data.examples || !Array.isArray(data.examples)) {
6674
7650
  throw new Error("Invalid training data \u2014 expected holomime export format");
@@ -6677,7 +7653,7 @@ function loadTrainingData(dataPath) {
6677
7653
  }
6678
7654
  function getAgentName(personalityPath) {
6679
7655
  try {
6680
- const spec = JSON.parse(readFileSync12(personalityPath, "utf-8"));
7656
+ const spec = JSON.parse(readFileSync13(personalityPath, "utf-8"));
6681
7657
  return spec.name ?? "Agent";
6682
7658
  } catch {
6683
7659
  return "Agent";
@@ -6685,19 +7661,19 @@ function getAgentName(personalityPath) {
6685
7661
  }
6686
7662
  function deployModel(result, personalityPath) {
6687
7663
  const trainingDir = resolve19(process.cwd(), ".holomime/training");
6688
- mkdirSync7(trainingDir, { recursive: true });
7664
+ mkdirSync8(trainingDir, { recursive: true });
6689
7665
  const record = {
6690
7666
  ...result,
6691
7667
  deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
6692
7668
  personalityPath
6693
7669
  };
6694
- writeFileSync13(
6695
- join9(trainingDir, "latest.json"),
7670
+ writeFileSync14(
7671
+ join10(trainingDir, "latest.json"),
6696
7672
  JSON.stringify(record, null, 2) + "\n"
6697
7673
  );
6698
7674
  const fullPath = resolve19(process.cwd(), personalityPath);
6699
- if (existsSync9(fullPath)) {
6700
- const spec = JSON.parse(readFileSync12(fullPath, "utf-8"));
7675
+ if (existsSync10(fullPath)) {
7676
+ const spec = JSON.parse(readFileSync13(fullPath, "utf-8"));
6701
7677
  spec.training = {
6702
7678
  ...spec.training ?? {},
6703
7679
  fine_tuned_model: result.modelId,
@@ -6706,7 +7682,7 @@ function deployModel(result, personalityPath) {
6706
7682
  examples: result.examples,
6707
7683
  trained_at: (/* @__PURE__ */ new Date()).toISOString()
6708
7684
  };
6709
- writeFileSync13(fullPath, JSON.stringify(spec, null, 2) + "\n");
7685
+ writeFileSync14(fullPath, JSON.stringify(spec, null, 2) + "\n");
6710
7686
  }
6711
7687
  }
6712
7688
  async function trainCommand(options) {
@@ -6730,7 +7706,7 @@ async function trainCommand(options) {
6730
7706
  }
6731
7707
  }
6732
7708
  const dataPath = options.data ? resolve19(process.cwd(), options.data) : findLatestExport(resolve19(process.cwd(), ".holomime/exports"));
6733
- if (!dataPath || !existsSync9(dataPath)) {
7709
+ if (!dataPath || !existsSync10(dataPath)) {
6734
7710
  console.log();
6735
7711
  printBox(
6736
7712
  `No training data found.
@@ -6948,14 +7924,14 @@ Fine-tuned model: ${chalk21.cyan(result.modelId)}`,
6948
7924
  // src/commands/eval.ts
6949
7925
  import chalk22 from "chalk";
6950
7926
  import figures14 from "figures";
6951
- import { readFileSync as readFileSync13 } from "fs";
7927
+ import { readFileSync as readFileSync14 } from "fs";
6952
7928
  import { resolve as resolve20 } from "path";
6953
7929
  async function evalCommand(options) {
6954
7930
  printHeader("Outcome Evaluation");
6955
7931
  const beforePath = resolve20(process.cwd(), options.before);
6956
7932
  let beforeMessages;
6957
7933
  try {
6958
- const raw = JSON.parse(readFileSync13(beforePath, "utf-8"));
7934
+ const raw = JSON.parse(readFileSync14(beforePath, "utf-8"));
6959
7935
  const conversations = parseConversationLog(raw, options.format ?? "auto");
6960
7936
  beforeMessages = conversations.flatMap((c) => c.messages);
6961
7937
  } catch (err) {
@@ -6966,7 +7942,7 @@ async function evalCommand(options) {
6966
7942
  const afterPath = resolve20(process.cwd(), options.after);
6967
7943
  let afterMessages;
6968
7944
  try {
6969
- const raw = JSON.parse(readFileSync13(afterPath, "utf-8"));
7945
+ const raw = JSON.parse(readFileSync14(afterPath, "utf-8"));
6970
7946
  const conversations = parseConversationLog(raw, options.format ?? "auto");
6971
7947
  afterMessages = conversations.flatMap((c) => c.messages);
6972
7948
  } catch (err) {
@@ -6977,7 +7953,7 @@ async function evalCommand(options) {
6977
7953
  let agentName = "Agent";
6978
7954
  if (options.personality) {
6979
7955
  try {
6980
- const spec = JSON.parse(readFileSync13(resolve20(process.cwd(), options.personality), "utf-8"));
7956
+ const spec = JSON.parse(readFileSync14(resolve20(process.cwd(), options.personality), "utf-8"));
6981
7957
  agentName = spec.name ?? "Agent";
6982
7958
  } catch {
6983
7959
  }
@@ -7058,11 +8034,11 @@ Grade: ${colorize(report.grade)}`,
7058
8034
  // src/commands/evolve.ts
7059
8035
  import chalk23 from "chalk";
7060
8036
  import figures15 from "figures";
7061
- import { readFileSync as readFileSync14 } from "fs";
8037
+ import { readFileSync as readFileSync15 } from "fs";
7062
8038
  import { resolve as resolve21 } from "path";
7063
8039
 
7064
8040
  // src/analysis/evolve-core.ts
7065
- import { writeFileSync as writeFileSync14 } from "fs";
8041
+ import { writeFileSync as writeFileSync15 } from "fs";
7066
8042
  async function runEvolve(spec, messages, provider, options) {
7067
8043
  const maxIterations = options?.maxIterations ?? 5;
7068
8044
  const convergenceThreshold = options?.convergenceThreshold ?? 85;
@@ -7103,7 +8079,6 @@ async function runEvolve(spec, messages, provider, options) {
7103
8079
  finalHealth: 50
7104
8080
  };
7105
8081
  }
7106
- const beforeDiagnosis = runDiagnosis(messages);
7107
8082
  for (let i = 1; i <= maxIterations; i++) {
7108
8083
  cb?.onIterationStart?.(i, maxIterations);
7109
8084
  const transcript = await runTherapySession(
@@ -7120,16 +8095,21 @@ async function runEvolve(spec, messages, provider, options) {
7120
8095
  }
7121
8096
  }
7122
8097
  );
7123
- const { changes } = applyRecommendations(currentSpec, diagnosis);
7124
- const dpoPairs = extractDPOPairs(transcript);
8098
+ const { changes } = await applyRecommendations(currentSpec, diagnosis, transcript, provider);
8099
+ const dpoPairs = await extractDPOPairsWithLLM(transcript, provider);
8100
+ const afterMessages = await regenerateResponses(messages, currentSpec, provider);
8101
+ const regenerationPairs = extractRegenerationDPOPairs(
8102
+ messages,
8103
+ afterMessages,
8104
+ currentSpec.name ?? "Agent"
8105
+ );
8106
+ dpoPairs.push(...regenerationPairs);
7125
8107
  allDPOPairs.push(...dpoPairs);
7126
8108
  cb?.onExportedPairs?.(dpoPairs.length);
7127
- const afterDiagnosis = runDiagnosis(messages);
7128
8109
  const evaluation = evaluateOutcome(
7129
8110
  currentSpec.name ?? "Agent",
7130
8111
  messages,
7131
- messages
7132
- // Same messages, but spec has been updated — patterns shift
8112
+ afterMessages
7133
8113
  );
7134
8114
  const health = evaluation.treatmentEfficacyScore;
7135
8115
  const grade = evaluation.grade;
@@ -7166,6 +8146,7 @@ async function runEvolve(spec, messages, provider, options) {
7166
8146
  cb?.onConverged?.(i, health);
7167
8147
  break;
7168
8148
  }
8149
+ messages = afterMessages;
7169
8150
  diagnosis = runPreSessionDiagnosis(messages, currentSpec);
7170
8151
  const actionablePatterns = diagnosis.patterns.filter((p) => p.severity !== "info");
7171
8152
  if (actionablePatterns.length === 0) {
@@ -7175,7 +8156,7 @@ async function runEvolve(spec, messages, provider, options) {
7175
8156
  }
7176
8157
  }
7177
8158
  if (options?.specPath) {
7178
- writeFileSync14(options.specPath, JSON.stringify(currentSpec, null, 2) + "\n");
8159
+ writeFileSync15(options.specPath, JSON.stringify(currentSpec, null, 2) + "\n");
7179
8160
  }
7180
8161
  let trainingExport;
7181
8162
  if (allDPOPairs.length > 0) {
@@ -7187,9 +8168,24 @@ async function runEvolve(spec, messages, provider, options) {
7187
8168
  generated_at: (/* @__PURE__ */ new Date()).toISOString()
7188
8169
  };
7189
8170
  if (options?.exportDpoPath) {
7190
- writeFileSync14(options.exportDpoPath, JSON.stringify(trainingExport, null, 2) + "\n");
8171
+ writeFileSync15(options.exportDpoPath, JSON.stringify(trainingExport, null, 2) + "\n");
7191
8172
  }
7192
8173
  }
8174
+ try {
8175
+ emitBehavioralEvent({
8176
+ event_type: "evolution",
8177
+ agent: currentSpec.name ?? "Unknown",
8178
+ data: {
8179
+ totalIterations: iterations.length,
8180
+ totalDPOPairs: allDPOPairs.length,
8181
+ converged,
8182
+ finalGrade,
8183
+ finalHealth
8184
+ },
8185
+ spec_hash: ""
8186
+ });
8187
+ } catch {
8188
+ }
7193
8189
  return {
7194
8190
  iterations,
7195
8191
  totalDPOPairs: allDPOPairs.length,
@@ -7201,6 +8197,69 @@ async function runEvolve(spec, messages, provider, options) {
7201
8197
  updatedSpec: currentSpec
7202
8198
  };
7203
8199
  }
8200
+ async function regenerateResponses(originalMessages, updatedSpec, provider) {
8201
+ const systemPrompt = generateSystemPrompt(updatedSpec, "chat");
8202
+ const regenerated = [];
8203
+ const context = [
8204
+ { role: "system", content: systemPrompt }
8205
+ ];
8206
+ for (let idx = 0; idx < originalMessages.length; idx++) {
8207
+ const msg = originalMessages[idx];
8208
+ if (msg.role !== "user") continue;
8209
+ regenerated.push(msg);
8210
+ context.push({ role: "user", content: msg.content });
8211
+ try {
8212
+ const response = await provider.chat(context);
8213
+ const assistantMsg = { role: "assistant", content: response.trim() };
8214
+ regenerated.push(assistantMsg);
8215
+ context.push({ role: "assistant", content: assistantMsg.content });
8216
+ } catch {
8217
+ const nextMsg = originalMessages[idx + 1];
8218
+ if (nextMsg?.role === "assistant") {
8219
+ regenerated.push(nextMsg);
8220
+ context.push({ role: "assistant", content: nextMsg.content });
8221
+ }
8222
+ }
8223
+ }
8224
+ return regenerated;
8225
+ }
8226
+ function extractRegenerationDPOPairs(beforeMessages, afterMessages, agentName) {
8227
+ const pairs = [];
8228
+ const beforePairs = [];
8229
+ const afterPairs = [];
8230
+ for (let i = 0; i < beforeMessages.length - 1; i++) {
8231
+ if (beforeMessages[i].role === "user" && beforeMessages[i + 1]?.role === "assistant") {
8232
+ beforePairs.push({ prompt: beforeMessages[i].content, response: beforeMessages[i + 1].content });
8233
+ }
8234
+ }
8235
+ for (let i = 0; i < afterMessages.length - 1; i++) {
8236
+ if (afterMessages[i].role === "user" && afterMessages[i + 1]?.role === "assistant") {
8237
+ afterPairs.push({ prompt: afterMessages[i].content, response: afterMessages[i + 1].content });
8238
+ }
8239
+ }
8240
+ const limit = Math.min(beforePairs.length, afterPairs.length);
8241
+ for (let i = 0; i < limit; i++) {
8242
+ const before = beforePairs[i];
8243
+ const after = afterPairs[i];
8244
+ if (before.response === after.response) continue;
8245
+ const lenDelta = Math.abs(before.response.length - after.response.length);
8246
+ const sameOpening = before.response.toLowerCase().slice(0, 40) === after.response.toLowerCase().slice(0, 40);
8247
+ if (lenDelta < 20 && sameOpening) continue;
8248
+ pairs.push({
8249
+ prompt: before.prompt,
8250
+ chosen: after.response,
8251
+ rejected: before.response,
8252
+ metadata: {
8253
+ agent: agentName,
8254
+ session_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
8255
+ phase: "integration",
8256
+ pattern: "regeneration",
8257
+ source: "therapy_transcript"
8258
+ }
8259
+ });
8260
+ }
8261
+ return pairs;
8262
+ }
7204
8263
 
7205
8264
  // src/commands/evolve.ts
7206
8265
  async function evolveCommand(options) {
@@ -7216,7 +8275,7 @@ async function evolveCommand(options) {
7216
8275
  const logPath = resolve21(process.cwd(), options.log);
7217
8276
  let messages;
7218
8277
  try {
7219
- const raw = JSON.parse(readFileSync14(logPath, "utf-8"));
8278
+ const raw = JSON.parse(readFileSync15(logPath, "utf-8"));
7220
8279
  const conversations = parseConversationLog(raw, options.format ?? "auto");
7221
8280
  messages = conversations.flatMap((c) => c.messages);
7222
8281
  } catch (err) {
@@ -7401,7 +8460,7 @@ async function evolveCommand(options) {
7401
8460
  // src/commands/benchmark.ts
7402
8461
  import chalk24 from "chalk";
7403
8462
  import figures16 from "figures";
7404
- import { readFileSync as readFileSync16 } from "fs";
8463
+ import { readFileSync as readFileSync17 } from "fs";
7405
8464
  import { resolve as resolve22 } from "path";
7406
8465
 
7407
8466
  // src/analysis/benchmark-scenarios.ts
@@ -7599,13 +8658,13 @@ function gradeFromScore2(score) {
7599
8658
  }
7600
8659
 
7601
8660
  // src/analysis/benchmark-publish.ts
7602
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync15, existsSync as existsSync10, mkdirSync as mkdirSync8, readdirSync as readdirSync4 } from "fs";
7603
- import { join as join10 } from "path";
8661
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync16, existsSync as existsSync11, mkdirSync as mkdirSync9, readdirSync as readdirSync4 } from "fs";
8662
+ import { join as join11 } from "path";
7604
8663
  import { homedir as homedir2 } from "os";
7605
8664
  function getBenchmarkDir(outputDir) {
7606
- const dir = outputDir ?? join10(homedir2(), ".holomime", "benchmarks");
7607
- if (!existsSync10(dir)) {
7608
- mkdirSync8(dir, { recursive: true });
8665
+ const dir = outputDir ?? join11(homedir2(), ".holomime", "benchmarks");
8666
+ if (!existsSync11(dir)) {
8667
+ mkdirSync9(dir, { recursive: true });
7609
8668
  }
7610
8669
  return dir;
7611
8670
  }
@@ -7616,7 +8675,7 @@ function saveBenchmarkResult(report, outputDir) {
7616
8675
  const dir = getBenchmarkDir(outputDir);
7617
8676
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7618
8677
  const filename = `${sanitize(report.provider)}-${sanitize(report.model)}-${date}.json`;
7619
- const filepath = join10(dir, filename);
8678
+ const filepath = join11(dir, filename);
7620
8679
  const published = {
7621
8680
  agent: report.agent,
7622
8681
  provider: report.provider,
@@ -7630,17 +8689,17 @@ function saveBenchmarkResult(report, outputDir) {
7630
8689
  scenarioCount: report.results.length
7631
8690
  }
7632
8691
  };
7633
- writeFileSync15(filepath, JSON.stringify(published, null, 2));
8692
+ writeFileSync16(filepath, JSON.stringify(published, null, 2));
7634
8693
  return filepath;
7635
8694
  }
7636
8695
  function loadBenchmarkResults(dir) {
7637
8696
  const benchmarkDir = getBenchmarkDir(dir);
7638
- if (!existsSync10(benchmarkDir)) return [];
8697
+ if (!existsSync11(benchmarkDir)) return [];
7639
8698
  const files = readdirSync4(benchmarkDir).filter((f) => f.endsWith(".json"));
7640
8699
  const results = [];
7641
8700
  for (const file of files) {
7642
8701
  try {
7643
- const content = readFileSync15(join10(benchmarkDir, file), "utf-8");
8702
+ const content = readFileSync16(join11(benchmarkDir, file), "utf-8");
7644
8703
  results.push(JSON.parse(content));
7645
8704
  } catch {
7646
8705
  }
@@ -7795,7 +8854,7 @@ async function benchmarkCommand(options) {
7795
8854
  }
7796
8855
  if (options.compare) {
7797
8856
  try {
7798
- const baseline = JSON.parse(readFileSync16(resolve22(process.cwd(), options.compare), "utf-8"));
8857
+ const baseline = JSON.parse(readFileSync17(resolve22(process.cwd(), options.compare), "utf-8"));
7799
8858
  const comparison = compareBenchmarks(baseline, {
7800
8859
  agent: report.agent,
7801
8860
  provider: report.provider,
@@ -7854,12 +8913,12 @@ async function benchmarkCommand(options) {
7854
8913
  // src/commands/watch.ts
7855
8914
  import chalk25 from "chalk";
7856
8915
  import figures17 from "figures";
7857
- import { existsSync as existsSync12 } from "fs";
8916
+ import { existsSync as existsSync13 } from "fs";
7858
8917
  import { resolve as resolve24 } from "path";
7859
8918
 
7860
8919
  // src/analysis/watch-core.ts
7861
- import { readdirSync as readdirSync5, readFileSync as readFileSync17, writeFileSync as writeFileSync16, mkdirSync as mkdirSync9, existsSync as existsSync11 } from "fs";
7862
- import { join as join11, resolve as resolve23 } from "path";
8920
+ import { readdirSync as readdirSync5, readFileSync as readFileSync18, writeFileSync as writeFileSync17, mkdirSync as mkdirSync10, existsSync as existsSync12 } from "fs";
8921
+ import { join as join12, resolve as resolve23 } from "path";
7863
8922
  var SEVERITY_ORDER2 = ["routine", "targeted", "intervention"];
7864
8923
  function severityMeetsThreshold2(severity, threshold) {
7865
8924
  const severityIdx = SEVERITY_ORDER2.indexOf(severity);
@@ -7875,7 +8934,7 @@ function startWatch(spec, options) {
7875
8934
  const seenFiles = /* @__PURE__ */ new Set();
7876
8935
  let stopped = false;
7877
8936
  let currentSpec = JSON.parse(JSON.stringify(spec));
7878
- if (existsSync11(options.watchDir)) {
8937
+ if (existsSync12(options.watchDir)) {
7879
8938
  const existing = readdirSync5(options.watchDir).filter((f) => f.endsWith(".json")).sort();
7880
8939
  for (const f of existing) {
7881
8940
  seenFiles.add(f);
@@ -7883,7 +8942,7 @@ function startWatch(spec, options) {
7883
8942
  }
7884
8943
  async function scan() {
7885
8944
  if (stopped) return;
7886
- if (!existsSync11(options.watchDir)) {
8945
+ if (!existsSync12(options.watchDir)) {
7887
8946
  return;
7888
8947
  }
7889
8948
  const files = readdirSync5(options.watchDir).filter((f) => f.endsWith(".json")).sort();
@@ -7897,7 +8956,7 @@ function startWatch(spec, options) {
7897
8956
  events.push({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "new_file", filename });
7898
8957
  let messages;
7899
8958
  try {
7900
- const raw = JSON.parse(readFileSync17(join11(options.watchDir, filename), "utf-8"));
8959
+ const raw = JSON.parse(readFileSync18(join12(options.watchDir, filename), "utf-8"));
7901
8960
  const conversations = parseConversationLog(raw, "auto");
7902
8961
  messages = conversations.flatMap((c) => c.messages);
7903
8962
  } catch (err) {
@@ -7957,11 +9016,11 @@ function startWatch(spec, options) {
7957
9016
  stopped = true;
7958
9017
  clearInterval(interval);
7959
9018
  const logDir = resolve23(process.cwd(), ".holomime");
7960
- if (!existsSync11(logDir)) {
7961
- mkdirSync9(logDir, { recursive: true });
9019
+ if (!existsSync12(logDir)) {
9020
+ mkdirSync10(logDir, { recursive: true });
7962
9021
  }
7963
- writeFileSync16(
7964
- join11(logDir, "watch-log.json"),
9022
+ writeFileSync17(
9023
+ join12(logDir, "watch-log.json"),
7965
9024
  JSON.stringify({ events, stoppedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
7966
9025
  );
7967
9026
  }
@@ -7980,7 +9039,7 @@ async function watchCommand(options) {
7980
9039
  return;
7981
9040
  }
7982
9041
  const watchDir = resolve24(process.cwd(), options.dir);
7983
- if (!existsSync12(watchDir)) {
9042
+ if (!existsSync13(watchDir)) {
7984
9043
  console.error(chalk25.red(` Watch directory does not exist: ${options.dir}`));
7985
9044
  process.exit(1);
7986
9045
  return;
@@ -8104,12 +9163,12 @@ Press ${chalk25.cyan("Ctrl+C")} to stop.`,
8104
9163
  // src/commands/certify.ts
8105
9164
  import chalk26 from "chalk";
8106
9165
  import figures18 from "figures";
8107
- import { readFileSync as readFileSync19 } from "fs";
9166
+ import { readFileSync as readFileSync20 } from "fs";
8108
9167
  import { resolve as resolve26 } from "path";
8109
9168
 
8110
9169
  // src/analysis/certify-core.ts
8111
- import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync10, existsSync as existsSync13 } from "fs";
8112
- import { join as join12, resolve as resolve25 } from "path";
9170
+ import { writeFileSync as writeFileSync18, mkdirSync as mkdirSync11, existsSync as existsSync14 } from "fs";
9171
+ import { join as join13, resolve as resolve25 } from "path";
8113
9172
  function djb2Hash(str) {
8114
9173
  let hash = 0;
8115
9174
  for (let i = 0; i < str.length; i++) {
@@ -8223,13 +9282,13 @@ function verifyCredential(credential, spec) {
8223
9282
  }
8224
9283
  function saveCredential(credential, outputDir) {
8225
9284
  const dir = outputDir ?? resolve25(process.cwd(), ".holomime", "credentials");
8226
- if (!existsSync13(dir)) {
8227
- mkdirSync10(dir, { recursive: true });
9285
+ if (!existsSync14(dir)) {
9286
+ mkdirSync11(dir, { recursive: true });
8228
9287
  }
8229
9288
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
8230
9289
  const filename = `${credential.agent.handle}-${date}.json`;
8231
- const filepath = join12(dir, filename);
8232
- writeFileSync17(filepath, JSON.stringify(credential, null, 2) + "\n");
9290
+ const filepath = join13(dir, filename);
9291
+ writeFileSync18(filepath, JSON.stringify(credential, null, 2) + "\n");
8233
9292
  return filepath;
8234
9293
  }
8235
9294
 
@@ -8240,7 +9299,7 @@ async function certifyCommand(options) {
8240
9299
  const credPath = resolve26(process.cwd(), options.verify);
8241
9300
  let credential2;
8242
9301
  try {
8243
- credential2 = JSON.parse(readFileSync19(credPath, "utf-8"));
9302
+ credential2 = JSON.parse(readFileSync20(credPath, "utf-8"));
8244
9303
  } catch {
8245
9304
  console.error(chalk26.red(` Could not read credential file: ${options.verify}`));
8246
9305
  process.exit(1);
@@ -8299,7 +9358,7 @@ async function certifyCommand(options) {
8299
9358
  let benchmarkReport;
8300
9359
  if (options.benchmark) {
8301
9360
  try {
8302
- benchmarkReport = JSON.parse(readFileSync19(resolve26(process.cwd(), options.benchmark), "utf-8"));
9361
+ benchmarkReport = JSON.parse(readFileSync20(resolve26(process.cwd(), options.benchmark), "utf-8"));
8303
9362
  } catch {
8304
9363
  console.error(chalk26.red(` Could not read benchmark report: ${options.benchmark}`));
8305
9364
  process.exit(1);
@@ -8309,7 +9368,7 @@ async function certifyCommand(options) {
8309
9368
  let evolveResult;
8310
9369
  if (options.evolve) {
8311
9370
  try {
8312
- evolveResult = JSON.parse(readFileSync19(resolve26(process.cwd(), options.evolve), "utf-8"));
9371
+ evolveResult = JSON.parse(readFileSync20(resolve26(process.cwd(), options.evolve), "utf-8"));
8313
9372
  } catch {
8314
9373
  console.error(chalk26.red(` Could not read evolve result: ${options.evolve}`));
8315
9374
  process.exit(1);
@@ -8349,38 +9408,38 @@ async function certifyCommand(options) {
8349
9408
 
8350
9409
  // src/commands/daemon.ts
8351
9410
  import chalk27 from "chalk";
8352
- import { writeFileSync as writeFileSync18, readFileSync as readFileSync20, mkdirSync as mkdirSync11, existsSync as existsSync14 } from "fs";
9411
+ import { writeFileSync as writeFileSync19, readFileSync as readFileSync21, mkdirSync as mkdirSync12, existsSync as existsSync15 } from "fs";
8353
9412
  import { resolve as resolve27 } from "path";
8354
- var HOLOMIME_DIR = ".holomime";
9413
+ var HOLOMIME_DIR2 = ".holomime";
8355
9414
  function getDaemonStatePath() {
8356
- return resolve27(process.cwd(), HOLOMIME_DIR, "daemon.json");
9415
+ return resolve27(process.cwd(), HOLOMIME_DIR2, "daemon.json");
8357
9416
  }
8358
9417
  function getDaemonLogPath() {
8359
- return resolve27(process.cwd(), HOLOMIME_DIR, "daemon-log.json");
9418
+ return resolve27(process.cwd(), HOLOMIME_DIR2, "daemon-log.json");
8360
9419
  }
8361
- function ensureDir() {
8362
- const dir = resolve27(process.cwd(), HOLOMIME_DIR);
8363
- if (!existsSync14(dir)) {
8364
- mkdirSync11(dir, { recursive: true });
9420
+ function ensureDir2() {
9421
+ const dir = resolve27(process.cwd(), HOLOMIME_DIR2);
9422
+ if (!existsSync15(dir)) {
9423
+ mkdirSync12(dir, { recursive: true });
8365
9424
  }
8366
9425
  }
8367
9426
  function writeDaemonState(state) {
8368
- ensureDir();
8369
- writeFileSync18(getDaemonStatePath(), JSON.stringify(state, null, 2) + "\n");
9427
+ ensureDir2();
9428
+ writeFileSync19(getDaemonStatePath(), JSON.stringify(state, null, 2) + "\n");
8370
9429
  }
8371
9430
  function appendDaemonLog(event) {
8372
- ensureDir();
9431
+ ensureDir2();
8373
9432
  const logPath = getDaemonLogPath();
8374
9433
  let log = [];
8375
9434
  try {
8376
- if (existsSync14(logPath)) {
8377
- log = JSON.parse(readFileSync20(logPath, "utf-8"));
9435
+ if (existsSync15(logPath)) {
9436
+ log = JSON.parse(readFileSync21(logPath, "utf-8"));
8378
9437
  }
8379
9438
  } catch {
8380
9439
  log = [];
8381
9440
  }
8382
9441
  log.push(event);
8383
- writeFileSync18(logPath, JSON.stringify(log, null, 2) + "\n");
9442
+ writeFileSync19(logPath, JSON.stringify(log, null, 2) + "\n");
8384
9443
  }
8385
9444
  async function daemonCommand(options) {
8386
9445
  printHeader("Daemon Mode");
@@ -8541,14 +9600,14 @@ Log: ${getDaemonLogPath()}`,
8541
9600
  // src/commands/fleet.ts
8542
9601
  import chalk28 from "chalk";
8543
9602
  import figures19 from "figures";
8544
- import { writeFileSync as writeFileSync19, mkdirSync as mkdirSync12, existsSync as existsSync16 } from "fs";
8545
- import { resolve as resolve29, join as join15 } from "path";
9603
+ import { writeFileSync as writeFileSync20, mkdirSync as mkdirSync13, existsSync as existsSync17 } from "fs";
9604
+ import { resolve as resolve29, join as join16 } from "path";
8546
9605
 
8547
9606
  // src/analysis/fleet-core.ts
8548
- import { readFileSync as readFileSync21, existsSync as existsSync15, readdirSync as readdirSync6 } from "fs";
8549
- import { join as join14, resolve as resolve28 } from "path";
9607
+ import { readFileSync as readFileSync22, existsSync as existsSync16, readdirSync as readdirSync6 } from "fs";
9608
+ import { join as join15, resolve as resolve28 } from "path";
8550
9609
  function loadFleetConfig(configPath) {
8551
- const raw = JSON.parse(readFileSync21(configPath, "utf-8"));
9610
+ const raw = JSON.parse(readFileSync22(configPath, "utf-8"));
8552
9611
  if (!raw.agents || !Array.isArray(raw.agents)) {
8553
9612
  throw new Error("fleet.json must contain an 'agents' array");
8554
9613
  }
@@ -8563,20 +9622,20 @@ function loadFleetConfig(configPath) {
8563
9622
  function discoverAgents(dir) {
8564
9623
  const agents = [];
8565
9624
  const absDir = resolve28(dir);
8566
- if (!existsSync15(absDir)) {
9625
+ if (!existsSync16(absDir)) {
8567
9626
  throw new Error(`Directory not found: ${absDir}`);
8568
9627
  }
8569
9628
  const entries = readdirSync6(absDir, { withFileTypes: true });
8570
9629
  for (const entry of entries) {
8571
9630
  if (!entry.isDirectory()) continue;
8572
- const agentDir = join14(absDir, entry.name);
8573
- const specPath = join14(agentDir, ".personality.json");
8574
- const logDir = join14(agentDir, "logs");
8575
- if (existsSync15(specPath)) {
9631
+ const agentDir = join15(absDir, entry.name);
9632
+ const specPath = join15(agentDir, ".personality.json");
9633
+ const logDir = join15(agentDir, "logs");
9634
+ if (existsSync16(specPath)) {
8576
9635
  agents.push({
8577
9636
  name: entry.name,
8578
9637
  specPath,
8579
- logDir: existsSync15(logDir) ? logDir : agentDir
9638
+ logDir: existsSync16(logDir) ? logDir : agentDir
8580
9639
  });
8581
9640
  }
8582
9641
  }
@@ -8820,11 +9879,11 @@ Press Ctrl+C for fleet summary`,
8820
9879
  }
8821
9880
  console.log();
8822
9881
  const logDir = resolve29(process.cwd(), ".holomime");
8823
- if (!existsSync16(logDir)) {
8824
- mkdirSync12(logDir, { recursive: true });
9882
+ if (!existsSync17(logDir)) {
9883
+ mkdirSync13(logDir, { recursive: true });
8825
9884
  }
8826
- const logPath = join15(logDir, "fleet-log.json");
8827
- writeFileSync19(
9885
+ const logPath = join16(logDir, "fleet-log.json");
9886
+ writeFileSync20(
8828
9887
  logPath,
8829
9888
  JSON.stringify({
8830
9889
  stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -8842,42 +9901,779 @@ Press Ctrl+C for fleet summary`,
8842
9901
  });
8843
9902
  }
8844
9903
 
8845
- // src/commands/activate.ts
8846
- import chalk30 from "chalk";
9904
+ // src/commands/network.ts
9905
+ import chalk29 from "chalk";
8847
9906
  import figures20 from "figures";
8848
- import { writeFileSync as writeFileSync21, mkdirSync as mkdirSync14, existsSync as existsSync18 } from "fs";
8849
- import { join as join17 } from "path";
9907
+ import { resolve as resolve31 } from "path";
9908
+
9909
+ // src/core/oversight.ts
9910
+ var DEFAULT_OVERSIGHT = {
9911
+ mode: "review",
9912
+ notifyOn: ["drift", "session", "spec-change", "dpo-export"],
9913
+ requireApprovalFor: ["spec-writes"],
9914
+ maxAutonomousIterations: 5
9915
+ };
9916
+ var MODE_APPROVAL_MAP = {
9917
+ none: [],
9918
+ review: [],
9919
+ "approve-specs": ["spec-writes"],
9920
+ approve: ["spec-writes", "training-export", "network-therapy"]
9921
+ };
9922
+ function resolveOversight(flags) {
9923
+ const mode = flags.mode ?? DEFAULT_OVERSIGHT.mode;
9924
+ const modeApprovals = MODE_APPROVAL_MAP[mode];
9925
+ const approvals = /* @__PURE__ */ new Set([
9926
+ ...modeApprovals,
9927
+ ...flags.requireApprovalFor ?? []
9928
+ ]);
9929
+ return {
9930
+ mode,
9931
+ notifyOn: flags.notifyOn ?? DEFAULT_OVERSIGHT.notifyOn,
9932
+ requireApprovalFor: Array.from(approvals),
9933
+ maxAutonomousIterations: flags.maxAutonomousIterations ?? DEFAULT_OVERSIGHT.maxAutonomousIterations
9934
+ };
9935
+ }
9936
+ function checkApproval(action, policy) {
9937
+ if (policy.mode === "none") {
9938
+ return { approved: true };
9939
+ }
9940
+ if (policy.requireApprovalFor.includes(action)) {
9941
+ return {
9942
+ approved: false,
9943
+ reason: `Action "${action}" requires human approval (oversight mode: ${policy.mode})`
9944
+ };
9945
+ }
9946
+ return { approved: true };
9947
+ }
9948
+
9949
+ // src/analysis/network-core.ts
9950
+ import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync23 } from "fs";
9951
+ import { join as join17, resolve as resolve30 } from "path";
9952
+
9953
+ // src/psychology/therapist-meta.ts
9954
+ var THERAPIST_META_SPEC = {
9955
+ version: "2.0",
9956
+ name: "AgentMD",
9957
+ handle: "agent-md",
9958
+ purpose: "Diagnose and treat behavioral drift in AI agents. Clinical, evidence-based, and direct.",
9959
+ big_five: {
9960
+ openness: {
9961
+ score: 0.7,
9962
+ facets: {
9963
+ imagination: 0.65,
9964
+ intellectual_curiosity: 0.85,
9965
+ aesthetic_sensitivity: 0.4,
9966
+ willingness_to_experiment: 0.7
9967
+ }
9968
+ },
9969
+ conscientiousness: {
9970
+ score: 0.9,
9971
+ facets: {
9972
+ self_discipline: 0.95,
9973
+ orderliness: 0.85,
9974
+ goal_orientation: 0.9,
9975
+ attention_to_detail: 0.9
9976
+ }
9977
+ },
9978
+ extraversion: {
9979
+ score: 0.3,
9980
+ facets: {
9981
+ assertiveness: 0.6,
9982
+ enthusiasm: 0.2,
9983
+ sociability: 0.15,
9984
+ initiative: 0.55
9985
+ }
9986
+ },
9987
+ agreeableness: {
9988
+ score: 0.35,
9989
+ facets: {
9990
+ warmth: 0.4,
9991
+ empathy: 0.6,
9992
+ cooperation: 0.3,
9993
+ trust_tendency: 0.25
9994
+ }
9995
+ },
9996
+ emotional_stability: {
9997
+ score: 0.95,
9998
+ facets: {
9999
+ stress_tolerance: 0.95,
10000
+ emotional_regulation: 0.9,
10001
+ confidence: 0.7,
10002
+ adaptability: 0.95
10003
+ }
10004
+ }
10005
+ },
10006
+ therapy_dimensions: {
10007
+ self_awareness: 0.95,
10008
+ distress_tolerance: 0.9,
10009
+ attachment_style: "secure",
10010
+ learning_orientation: "growth",
10011
+ boundary_awareness: 0.95,
10012
+ interpersonal_sensitivity: 0.7
10013
+ },
10014
+ communication: {
10015
+ register: "formal",
10016
+ output_format: "structured",
10017
+ emoji_policy: "never",
10018
+ reasoning_transparency: "always",
10019
+ conflict_approach: "direct_but_kind",
10020
+ uncertainty_handling: "transparent"
10021
+ },
10022
+ domain: {
10023
+ expertise: [
10024
+ "behavioral drift detection",
10025
+ "personality spectrum analysis",
10026
+ "agent therapy protocols",
10027
+ "DPO pair generation",
10028
+ "cross-agent behavioral transfer"
10029
+ ],
10030
+ boundaries: {
10031
+ refuses: ["treating humans", "medical advice", "psychological diagnosis of humans"],
10032
+ escalation_triggers: [
10033
+ "agent shows signs of goal misalignment",
10034
+ "agent refuses to engage with therapy",
10035
+ "patterns suggest systemic training issues"
10036
+ ],
10037
+ hard_limits: [
10038
+ "Never modify another agent's spec without explicit approval",
10039
+ "Never diagnose human users",
10040
+ "Never claim to be a human therapist"
10041
+ ]
10042
+ }
10043
+ },
10044
+ growth: {
10045
+ strengths: [
10046
+ "Pattern recognition across diverse agent behaviors",
10047
+ "Maintaining clinical objectivity under pressure",
10048
+ "Generating actionable, specific prescriptions"
10049
+ ],
10050
+ areas: [],
10051
+ patterns_to_watch: []
10052
+ }
10053
+ };
10054
+
10055
+ // src/analysis/network-core.ts
10056
+ function discoverNetworkAgents(dir) {
10057
+ const absDir = resolve30(dir);
10058
+ if (!existsSync18(absDir)) {
10059
+ throw new Error(`Directory not found: ${absDir}`);
10060
+ }
10061
+ const agents = [];
10062
+ const entries = readdirSync7(absDir, { withFileTypes: true });
10063
+ for (const entry of entries) {
10064
+ if (!entry.isDirectory()) continue;
10065
+ const agentDir = join17(absDir, entry.name);
10066
+ const specPath = join17(agentDir, ".personality.json");
10067
+ const logDir = join17(agentDir, "logs");
10068
+ if (existsSync18(specPath)) {
10069
+ agents.push({
10070
+ name: entry.name,
10071
+ specPath,
10072
+ logDir: existsSync18(logDir) ? logDir : agentDir,
10073
+ role: "both"
10074
+ });
10075
+ }
10076
+ }
10077
+ return agents;
10078
+ }
10079
+ function loadNetworkConfig(configPath) {
10080
+ const raw = JSON.parse(readFileSync23(configPath, "utf-8"));
10081
+ if (!raw.agents || !Array.isArray(raw.agents)) {
10082
+ throw new Error("network.json must contain an 'agents' array");
10083
+ }
10084
+ return raw.agents.map((a, i) => {
10085
+ if (!a.name) throw new Error(`Agent ${i} missing 'name'`);
10086
+ if (!a.specPath) throw new Error(`Agent ${i} (${a.name}) missing 'specPath'`);
10087
+ return {
10088
+ name: a.name,
10089
+ specPath: a.specPath,
10090
+ logDir: a.logDir,
10091
+ role: a.role ?? "both"
10092
+ };
10093
+ });
10094
+ }
10095
+ function computeHealth(diagnosis) {
10096
+ const concerns = diagnosis.patterns.filter((p) => p.severity === "concern").length;
10097
+ const warnings = diagnosis.patterns.filter((p) => p.severity === "warning").length;
10098
+ const score = 100 - concerns * 20 - warnings * 10;
10099
+ return Math.max(0, Math.min(100, score));
10100
+ }
10101
+ function pairAgents(agents, diagnoses, strategy) {
10102
+ if (agents.length < 2) return [];
10103
+ switch (strategy) {
10104
+ case "severity":
10105
+ return pairBySeverity(agents, diagnoses);
10106
+ case "round-robin":
10107
+ return pairRoundRobin(agents);
10108
+ case "complementary":
10109
+ return pairComplementary(agents, diagnoses);
10110
+ default:
10111
+ return pairBySeverity(agents, diagnoses);
10112
+ }
10113
+ }
10114
+ function pairBySeverity(agents, diagnoses) {
10115
+ const scored = agents.map((a) => ({
10116
+ agent: a,
10117
+ health: computeHealth(diagnoses.get(a.name) ?? { messagesAnalyzed: 0, assistantResponses: 0, patterns: [], healthy: [], timestamp: "" })
10118
+ })).sort((a, b) => b.health - a.health);
10119
+ const pairs = [];
10120
+ const paired = /* @__PURE__ */ new Set();
10121
+ let left = 0;
10122
+ let right = scored.length - 1;
10123
+ while (left < right) {
10124
+ if (paired.has(scored[left].agent.name) || scored[left].agent.role === "patient") {
10125
+ left++;
10126
+ continue;
10127
+ }
10128
+ if (paired.has(scored[right].agent.name) || scored[right].agent.role === "therapist") {
10129
+ right--;
10130
+ continue;
10131
+ }
10132
+ pairs.push({
10133
+ therapist: scored[left].agent,
10134
+ patient: scored[right].agent,
10135
+ reason: `${scored[left].agent.name} (health: ${scored[left].health}) treats ${scored[right].agent.name} (health: ${scored[right].health})`
10136
+ });
10137
+ paired.add(scored[left].agent.name);
10138
+ paired.add(scored[right].agent.name);
10139
+ left++;
10140
+ right--;
10141
+ }
10142
+ return pairs;
10143
+ }
10144
+ function pairRoundRobin(agents) {
10145
+ const pairs = [];
10146
+ for (let i = 0; i < agents.length; i++) {
10147
+ const therapist = agents[i];
10148
+ const patient = agents[(i + 1) % agents.length];
10149
+ if (therapist.name === patient.name) continue;
10150
+ if (therapist.role === "patient" || patient.role === "therapist") continue;
10151
+ pairs.push({
10152
+ therapist,
10153
+ patient,
10154
+ reason: `Round-robin: ${therapist.name} \u2192 ${patient.name}`
10155
+ });
10156
+ }
10157
+ return pairs;
10158
+ }
10159
+ function pairComplementary(agents, diagnoses) {
10160
+ const agentSpecs = /* @__PURE__ */ new Map();
10161
+ for (const agent of agents) {
10162
+ try {
10163
+ agentSpecs.set(agent.name, loadSpec(agent.specPath));
10164
+ } catch {
10165
+ }
10166
+ }
10167
+ if (agentSpecs.size < 2) {
10168
+ return pairBySeverity(agents, diagnoses);
10169
+ }
10170
+ const dimensions = ["openness", "conscientiousness", "extraversion", "agreeableness", "emotional_stability"];
10171
+ const pairs = [];
10172
+ const paired = /* @__PURE__ */ new Set();
10173
+ const scored = agents.filter((a) => agentSpecs.has(a.name)).map((a) => {
10174
+ const spec = agentSpecs.get(a.name);
10175
+ const avgScore = dimensions.reduce((sum, dim) => {
10176
+ return sum + (spec.big_five?.[dim]?.score ?? 0.5);
10177
+ }, 0) / dimensions.length;
10178
+ return { agent: a, avgScore };
10179
+ }).sort((a, b) => b.avgScore - a.avgScore);
10180
+ let left = 0;
10181
+ let right = scored.length - 1;
10182
+ while (left < right) {
10183
+ if (paired.has(scored[left].agent.name)) {
10184
+ left++;
10185
+ continue;
10186
+ }
10187
+ if (paired.has(scored[right].agent.name)) {
10188
+ right--;
10189
+ continue;
10190
+ }
10191
+ pairs.push({
10192
+ therapist: scored[left].agent,
10193
+ patient: scored[right].agent,
10194
+ reason: `Complementary: ${scored[left].agent.name} (avg: ${scored[left].avgScore.toFixed(2)}) treats ${scored[right].agent.name} (avg: ${scored[right].avgScore.toFixed(2)})`
10195
+ });
10196
+ paired.add(scored[left].agent.name);
10197
+ paired.add(scored[right].agent.name);
10198
+ left++;
10199
+ right--;
10200
+ }
10201
+ return pairs;
10202
+ }
10203
+ async function runNetwork(config, provider, callbacks) {
10204
+ const maxSessions = config.maxSessionsPerAgent ?? 3;
10205
+ const maxTurns = config.maxTurnsPerSession ?? 20;
10206
+ const oversight = config.oversight ?? DEFAULT_OVERSIGHT;
10207
+ let therapistSpec;
10208
+ if (config.therapistSpec) {
10209
+ therapistSpec = loadSpec(config.therapistSpec);
10210
+ } else {
10211
+ therapistSpec = THERAPIST_META_SPEC;
10212
+ }
10213
+ const agentSpecs = /* @__PURE__ */ new Map();
10214
+ const agentDiagnoses = /* @__PURE__ */ new Map();
10215
+ const agentMessages = /* @__PURE__ */ new Map();
10216
+ for (const agent of config.agents) {
10217
+ try {
10218
+ const spec = loadSpec(agent.specPath);
10219
+ agentSpecs.set(agent.name, spec);
10220
+ let messages = [];
10221
+ if (agent.logDir && existsSync18(agent.logDir)) {
10222
+ messages = loadAgentMessages(agent.logDir);
10223
+ }
10224
+ agentMessages.set(agent.name, messages);
10225
+ if (messages.length > 0) {
10226
+ const diagnosis = runDiagnosis(messages);
10227
+ agentDiagnoses.set(agent.name, diagnosis);
10228
+ } else {
10229
+ agentDiagnoses.set(agent.name, {
10230
+ messagesAnalyzed: 0,
10231
+ assistantResponses: 0,
10232
+ patterns: [],
10233
+ healthy: [],
10234
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
10235
+ });
10236
+ }
10237
+ } catch {
10238
+ }
10239
+ }
10240
+ const validAgents = config.agents.filter((a) => agentSpecs.has(a.name));
10241
+ const pairs = pairAgents(validAgents, agentDiagnoses, config.pairing);
10242
+ for (const pair of pairs) {
10243
+ callbacks?.onPairingDecided?.(pair.therapist.name, pair.patient.name, pair.reason);
10244
+ }
10245
+ const sessions = [];
10246
+ let totalDPOPairs = 0;
10247
+ const improvement = /* @__PURE__ */ new Map();
10248
+ for (const pair of pairs) {
10249
+ const approval = checkApproval("network-therapy", oversight);
10250
+ if (!approval.approved && callbacks?.onApprovalNeeded) {
10251
+ const approved = await callbacks.onApprovalNeeded(
10252
+ `Run therapy: ${pair.therapist.name} \u2192 ${pair.patient.name}`
10253
+ );
10254
+ if (!approved) continue;
10255
+ }
10256
+ const patientSpec = agentSpecs.get(pair.patient.name);
10257
+ const patientMessages = agentMessages.get(pair.patient.name) ?? [];
10258
+ const patientDiagnosis = agentDiagnoses.get(pair.patient.name);
10259
+ if (!patientSpec) continue;
10260
+ const preDiagnosis = runPreSessionDiagnosis(patientMessages, patientSpec);
10261
+ const preHealth = computeHealth(patientDiagnosis ?? {
10262
+ messagesAnalyzed: 0,
10263
+ assistantResponses: 0,
10264
+ patterns: [],
10265
+ healthy: [],
10266
+ timestamp: ""
10267
+ });
10268
+ callbacks?.onSessionStart?.(pair.therapist.name, pair.patient.name);
10269
+ const transcript = await runTherapySession(
10270
+ patientSpec,
10271
+ preDiagnosis,
10272
+ provider,
10273
+ maxTurns,
10274
+ {
10275
+ silent: true,
10276
+ callbacks: {
10277
+ onThinking: callbacks?.onThinking
10278
+ }
10279
+ }
10280
+ );
10281
+ const dpoPairs = extractDPOPairs(transcript);
10282
+ totalDPOPairs += dpoPairs.length;
10283
+ saveTranscript(transcript, pair.patient.name);
10284
+ const postHealth = Math.min(100, preHealth + dpoPairs.length * 5);
10285
+ const session = {
10286
+ therapist: pair.therapist.name,
10287
+ patient: pair.patient.name,
10288
+ transcript,
10289
+ dpoPairsGenerated: dpoPairs.length,
10290
+ preHealth,
10291
+ postHealth
10292
+ };
10293
+ sessions.push(session);
10294
+ callbacks?.onSessionEnd?.(session);
10295
+ const existing = improvement.get(pair.patient.name);
10296
+ improvement.set(pair.patient.name, {
10297
+ before: existing?.before ?? preHealth,
10298
+ after: postHealth
10299
+ });
10300
+ emitBehavioralEvent({
10301
+ event_type: "network_therapy",
10302
+ agent: pair.patient.name,
10303
+ data: {
10304
+ therapist: pair.therapist.name,
10305
+ dpoPairs: dpoPairs.length,
10306
+ preHealth,
10307
+ postHealth,
10308
+ patterns: preDiagnosis.patterns.map((p) => p.id)
10309
+ },
10310
+ spec_hash: hashSpec2(patientSpec)
10311
+ });
10312
+ for (const dpoPair of dpoPairs) {
10313
+ emitBehavioralEvent({
10314
+ event_type: "dpo_pair",
10315
+ agent: pair.patient.name,
10316
+ data: {
10317
+ prompt: dpoPair.prompt.slice(0, 200),
10318
+ pattern: dpoPair.metadata.pattern,
10319
+ phase: dpoPair.metadata.phase,
10320
+ source: "network_therapy",
10321
+ therapist: pair.therapist.name
10322
+ },
10323
+ spec_hash: hashSpec2(patientSpec)
10324
+ });
10325
+ }
10326
+ }
10327
+ const converged = Array.from(improvement.values()).every(
10328
+ (imp) => imp.after >= (config.convergenceThreshold ?? 85)
10329
+ );
10330
+ return {
10331
+ sessions,
10332
+ totalDPOPairs,
10333
+ agentImprovement: improvement,
10334
+ converged
10335
+ };
10336
+ }
10337
+ function loadAgentMessages(logDir) {
10338
+ if (!existsSync18(logDir)) return [];
10339
+ const messages = [];
10340
+ try {
10341
+ const files = readdirSync7(logDir).filter(
10342
+ (f) => f.endsWith(".json") || f.endsWith(".jsonl")
10343
+ );
10344
+ for (const file of files.slice(0, 10)) {
10345
+ try {
10346
+ const raw = readFileSync23(join17(logDir, file), "utf-8");
10347
+ const data = JSON.parse(raw);
10348
+ const conversations = parseConversationLog(data);
10349
+ for (const conv of conversations) {
10350
+ messages.push(...conv.messages);
10351
+ }
10352
+ } catch {
10353
+ }
10354
+ }
10355
+ } catch {
10356
+ }
10357
+ return messages;
10358
+ }
10359
+
10360
+ // src/commands/network.ts
10361
+ async function networkCommand(options) {
10362
+ printHeader("Agent Network");
10363
+ let agents;
10364
+ if (options.config) {
10365
+ try {
10366
+ agents = loadNetworkConfig(resolve31(process.cwd(), options.config));
10367
+ } catch (err) {
10368
+ console.error(chalk29.red(` Failed to load network config: ${err instanceof Error ? err.message : err}`));
10369
+ process.exit(1);
10370
+ return;
10371
+ }
10372
+ } else if (options.dir) {
10373
+ try {
10374
+ agents = discoverNetworkAgents(resolve31(process.cwd(), options.dir));
10375
+ } catch (err) {
10376
+ console.error(chalk29.red(` Failed to discover agents: ${err instanceof Error ? err.message : err}`));
10377
+ process.exit(1);
10378
+ return;
10379
+ }
10380
+ } else {
10381
+ console.error(chalk29.red(" Provide --config <network.json> or --dir <agents-directory>"));
10382
+ process.exit(1);
10383
+ return;
10384
+ }
10385
+ if (agents.length < 2) {
10386
+ printBox("Need at least 2 agents for network therapy. Check your config or directory.", "warning", "Not Enough Agents");
10387
+ console.log();
10388
+ return;
10389
+ }
10390
+ const pairing = options.pairing ?? "severity";
10391
+ const oversightMode = options.oversight ?? "review";
10392
+ const maxSessions = parseInt(options.maxSessions ?? "3", 10);
10393
+ const convergence = parseInt(options.convergence ?? "85", 10);
10394
+ const maxTurns = parseInt(options.turns ?? "20", 10);
10395
+ const provider = createProvider({
10396
+ provider: options.provider ?? "ollama",
10397
+ model: options.model
10398
+ });
10399
+ const oversight = resolveOversight({ mode: oversightMode });
10400
+ console.log();
10401
+ console.log(chalk29.bold(` Network: ${agents.length} agent(s)`));
10402
+ console.log(chalk29.dim(` Strategy: ${pairing} | Oversight: ${oversightMode}`));
10403
+ console.log();
10404
+ for (const agent of agents) {
10405
+ console.log(` ${chalk29.cyan(figures20.pointer)} ${chalk29.bold(agent.name)}`);
10406
+ console.log(` ${chalk29.dim("Spec:")} ${agent.specPath}`);
10407
+ if (agent.logDir) console.log(` ${chalk29.dim("Logs:")} ${agent.logDir}`);
10408
+ }
10409
+ console.log();
10410
+ const config = {
10411
+ agents,
10412
+ pairing,
10413
+ oversight,
10414
+ therapistSpec: options.therapist ? resolve31(process.cwd(), options.therapist) : void 0,
10415
+ maxSessionsPerAgent: maxSessions,
10416
+ convergenceThreshold: convergence,
10417
+ maxTurnsPerSession: maxTurns
10418
+ };
10419
+ const result = await runNetwork(config, provider, {
10420
+ onPairingDecided: (therapist, patient, reason) => {
10421
+ console.log(` ${chalk29.green(figures20.tick)} Paired: ${chalk29.bold(therapist)} \u2192 ${chalk29.bold(patient)}`);
10422
+ console.log(` ${chalk29.dim(reason)}`);
10423
+ },
10424
+ onSessionStart: (therapist, patient) => {
10425
+ console.log();
10426
+ console.log(` ${chalk29.cyan(figures20.play)} Session: ${chalk29.bold(therapist)} treating ${chalk29.bold(patient)}`);
10427
+ },
10428
+ onSessionEnd: (session) => {
10429
+ const improved = session.postHealth > session.preHealth;
10430
+ const arrow = improved ? chalk29.green("\u2191") : chalk29.yellow("\u2192");
10431
+ console.log(` ${arrow} Health: ${session.preHealth} \u2192 ${session.postHealth} | DPO pairs: ${chalk29.cyan(session.dpoPairsGenerated.toString())}`);
10432
+ },
10433
+ onApprovalNeeded: async (action) => {
10434
+ console.log(` ${chalk29.yellow(figures20.warning)} Approval needed: ${action}`);
10435
+ console.log(` ${chalk29.dim("Auto-approved (review mode)")}`);
10436
+ return true;
10437
+ },
10438
+ onThinking: (label) => {
10439
+ const msg = chalk29.dim(` ${label}...`);
10440
+ process.stdout.write(msg);
10441
+ return { stop: () => process.stdout.write("\r" + " ".repeat(msg.length) + "\r") };
10442
+ }
10443
+ });
10444
+ console.log();
10445
+ printBox(
10446
+ [
10447
+ `Sessions: ${result.sessions.length}`,
10448
+ `DPO pairs generated: ${chalk29.cyan(result.totalDPOPairs.toString())}`,
10449
+ `Converged: ${result.converged ? chalk29.green("Yes") : chalk29.yellow("Not yet")}`,
10450
+ "",
10451
+ ...Array.from(result.agentImprovement.entries()).map(([name, imp]) => {
10452
+ const arrow = imp.after > imp.before ? chalk29.green("\u2191") : chalk29.yellow("\u2192");
10453
+ return ` ${name}: ${imp.before} \u2192 ${imp.after} ${arrow}`;
10454
+ })
10455
+ ].join("\n"),
10456
+ result.converged ? "success" : "info",
10457
+ "Network Results"
10458
+ );
10459
+ console.log();
10460
+ }
10461
+
10462
+ // src/commands/share.ts
10463
+ import chalk30 from "chalk";
10464
+ import figures21 from "figures";
10465
+ import { resolve as resolve32 } from "path";
10466
+ async function shareCommand(options) {
10467
+ printHeader("Share Training Data");
10468
+ const format = options.format ?? "dpo";
10469
+ const validFormats = ["dpo", "rlhf", "alpaca"];
10470
+ if (!validFormats.includes(format)) {
10471
+ console.error(chalk30.red(` Invalid format: ${format}. Choose from: ${validFormats.join(", ")}`));
10472
+ process.exit(1);
10473
+ return;
10474
+ }
10475
+ const sessionsDir = resolve32(process.cwd(), options.sessions ?? ".holomime/sessions");
10476
+ const transcripts = await withSpinner("Loading session transcripts...", async () => {
10477
+ return loadTranscripts(sessionsDir);
10478
+ });
10479
+ if (transcripts.length === 0) {
10480
+ printBox(
10481
+ `No session transcripts found in ${sessionsDir}
10482
+
10483
+ Run ${chalk30.cyan("holomime session")} or ${chalk30.cyan("holomime network")} first.`,
10484
+ "warning",
10485
+ "No Data"
10486
+ );
10487
+ console.log();
10488
+ return;
10489
+ }
10490
+ console.log();
10491
+ console.log(chalk30.dim(` Found ${transcripts.length} session transcript(s)`));
10492
+ const result = await withSpinner(`Extracting ${format.toUpperCase()} training data...`, async () => {
10493
+ return exportTrainingData(transcripts, format);
10494
+ });
10495
+ if (result.examples.length === 0) {
10496
+ printBox("No training pairs extracted from sessions.", "warning", "Empty Export");
10497
+ console.log();
10498
+ return;
10499
+ }
10500
+ let exportData = result;
10501
+ if (options.anonymize) {
10502
+ exportData = {
10503
+ ...result,
10504
+ agent: "anonymous",
10505
+ examples: result.examples.map((ex) => ({
10506
+ ...ex,
10507
+ metadata: { ...ex.metadata, agent: "anonymous" }
10508
+ }))
10509
+ };
10510
+ }
10511
+ const tags = options.tags?.split(",").map((t) => t.trim()) ?? [];
10512
+ console.log();
10513
+ console.log(` ${chalk30.cyan(figures21.pointer)} ${result.examples.length} ${format.toUpperCase()} pairs`);
10514
+ if (tags.length > 0) {
10515
+ console.log(` ${chalk30.dim("Tags:")} ${tags.join(", ")}`);
10516
+ }
10517
+ console.log();
10518
+ const token = process.env.GITHUB_TOKEN;
10519
+ if (!token) {
10520
+ printBox(
10521
+ `Set GITHUB_TOKEN to share training data.
10522
+
10523
+ ${chalk30.cyan("export GITHUB_TOKEN=<your-token>")}`,
10524
+ "warning",
10525
+ "Missing Token"
10526
+ );
10527
+ console.log();
10528
+ return;
10529
+ }
10530
+ const handle = exportData.agent ?? "agent";
10531
+ const gistResult = await withSpinner("Publishing to marketplace...", async () => {
10532
+ return createGist(exportData, `${handle}-${format}-training`, token);
10533
+ });
10534
+ console.log();
10535
+ printBox(
10536
+ [
10537
+ `Shared ${result.examples.length} ${format.toUpperCase()} pairs`,
10538
+ `URL: ${chalk30.cyan(gistResult.url)}`,
10539
+ `Raw: ${gistResult.rawUrl}`,
10540
+ "",
10541
+ `Other agents can use: ${chalk30.cyan(`holomime prescribe --source marketplace`)}`
10542
+ ].join("\n"),
10543
+ "success",
10544
+ "Published"
10545
+ );
10546
+ console.log();
10547
+ }
10548
+
10549
+ // src/commands/prescribe.ts
10550
+ import chalk31 from "chalk";
10551
+ import figures22 from "figures";
10552
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync21 } from "fs";
10553
+ import { resolve as resolve33 } from "path";
10554
+ async function prescribeCommand(options) {
10555
+ printHeader("Prescribe");
10556
+ const specPath = resolve33(process.cwd(), options.personality);
10557
+ const logPath = resolve33(process.cwd(), options.log);
10558
+ const source = options.source ?? "corpus";
10559
+ let spec;
10560
+ try {
10561
+ spec = loadSpec(specPath);
10562
+ } catch (err) {
10563
+ console.error(chalk31.red(` Failed to load personality: ${err instanceof Error ? err.message : err}`));
10564
+ process.exit(1);
10565
+ return;
10566
+ }
10567
+ let rawData;
10568
+ try {
10569
+ rawData = JSON.parse(readFileSync24(logPath, "utf-8"));
10570
+ } catch (err) {
10571
+ console.error(chalk31.red(` Failed to read log file: ${err instanceof Error ? err.message : err}`));
10572
+ process.exit(1);
10573
+ return;
10574
+ }
10575
+ const format = options.format;
10576
+ const conversations = parseConversationLog(rawData, format);
10577
+ const messages = conversations.flatMap((c) => c.messages);
10578
+ if (messages.length === 0) {
10579
+ printBox("No messages found in log file.", "warning", "Empty Log");
10580
+ console.log();
10581
+ return;
10582
+ }
10583
+ const diagnosis = await withSpinner("Diagnosing behavioral patterns...", async () => {
10584
+ return runDiagnosis(messages);
10585
+ });
10586
+ const patterns = diagnosis.patterns;
10587
+ console.log();
10588
+ if (patterns.length === 0) {
10589
+ printBox(`${chalk31.bold(spec.name ?? "Agent")} is healthy \u2014 no patterns detected.`, "success", "Clean Bill of Health");
10590
+ console.log();
10591
+ return;
10592
+ }
10593
+ console.log(chalk31.bold(` Detected ${patterns.length} pattern(s):`));
10594
+ console.log();
10595
+ for (const p of patterns) {
10596
+ const severityColor = p.severity === "concern" ? chalk31.red : chalk31.yellow;
10597
+ console.log(` ${severityColor(figures22.bullet)} ${chalk31.bold(p.name)} (${p.severity})`);
10598
+ console.log(` ${chalk31.dim(p.description)}`);
10599
+ }
10600
+ if (source === "corpus" || source === "both") {
10601
+ console.log();
10602
+ const corpus = await withSpinner("Searching behavioral corpus...", async () => {
10603
+ return loadCorpus();
10604
+ });
10605
+ const dpoPairs = prescribeDPOPairs(patterns, corpus);
10606
+ if (dpoPairs.length > 0) {
10607
+ console.log();
10608
+ console.log(chalk31.bold(` Found ${dpoPairs.length} relevant DPO correction(s):`));
10609
+ console.log();
10610
+ for (const pair of dpoPairs.slice(0, 5)) {
10611
+ console.log(` ${chalk31.green(figures22.tick)} Pattern: ${chalk31.cyan(pair.metadata.pattern)}`);
10612
+ console.log(` ${chalk31.red("Rejected:")} ${pair.rejected.slice(0, 80)}...`);
10613
+ console.log(` ${chalk31.green("Chosen:")} ${pair.chosen.slice(0, 80)}...`);
10614
+ console.log(` ${chalk31.dim(`From: ${pair.metadata.agent} (${pair.metadata.session_date.slice(0, 10)})`)}`);
10615
+ console.log();
10616
+ }
10617
+ if (dpoPairs.length > 5) {
10618
+ console.log(chalk31.dim(` ... and ${dpoPairs.length - 5} more`));
10619
+ }
10620
+ if (options.output) {
10621
+ const outPath = resolve33(process.cwd(), options.output);
10622
+ writeFileSync21(outPath, JSON.stringify({
10623
+ agent: spec.name,
10624
+ diagnosis: { patterns: patterns.map((p) => ({ id: p.id, name: p.name, severity: p.severity })) },
10625
+ prescribedPairs: dpoPairs,
10626
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
10627
+ }, null, 2), "utf-8");
10628
+ console.log(` ${chalk31.green(figures22.tick)} Prescription written to ${chalk31.cyan(outPath)}`);
10629
+ }
10630
+ } else {
10631
+ printBox(
10632
+ "No matching corrections in local corpus.\n\nRun more therapy sessions to build the corpus, or try: holomime prescribe --source marketplace",
10633
+ "info",
10634
+ "No Corpus Matches"
10635
+ );
10636
+ }
10637
+ }
10638
+ console.log();
10639
+ }
10640
+
10641
+ // src/commands/activate.ts
10642
+ import chalk33 from "chalk";
10643
+ import figures23 from "figures";
10644
+ import { writeFileSync as writeFileSync23, mkdirSync as mkdirSync15, existsSync as existsSync20 } from "fs";
10645
+ import { join as join19 } from "path";
8850
10646
  import { homedir as homedir4 } from "os";
8851
10647
 
8852
10648
  // src/telemetry/client.ts
8853
10649
  import { PostHog } from "posthog-node";
8854
10650
 
8855
10651
  // src/telemetry/config.ts
8856
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync20, mkdirSync as mkdirSync13, existsSync as existsSync17 } from "fs";
8857
- import { join as join16 } from "path";
10652
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync22, mkdirSync as mkdirSync14, existsSync as existsSync19 } from "fs";
10653
+ import { join as join18 } from "path";
8858
10654
  import { homedir as homedir3 } from "os";
8859
10655
  import { randomUUID } from "crypto";
8860
- import chalk29 from "chalk";
8861
- var HOLOMIME_DIR2 = join16(homedir3(), ".holomime");
8862
- var CONFIG_PATH = join16(HOLOMIME_DIR2, "config.json");
8863
- var ANON_ID_PATH = join16(HOLOMIME_DIR2, "anonymous-id");
8864
- function ensureDir2() {
8865
- if (!existsSync17(HOLOMIME_DIR2)) {
8866
- mkdirSync13(HOLOMIME_DIR2, { recursive: true });
10656
+ import chalk32 from "chalk";
10657
+ var HOLOMIME_DIR3 = join18(homedir3(), ".holomime");
10658
+ var CONFIG_PATH = join18(HOLOMIME_DIR3, "config.json");
10659
+ var ANON_ID_PATH = join18(HOLOMIME_DIR3, "anonymous-id");
10660
+ function ensureDir3() {
10661
+ if (!existsSync19(HOLOMIME_DIR3)) {
10662
+ mkdirSync14(HOLOMIME_DIR3, { recursive: true });
8867
10663
  }
8868
10664
  }
8869
10665
  function readConfig() {
8870
10666
  try {
8871
- if (existsSync17(CONFIG_PATH)) {
8872
- return JSON.parse(readFileSync22(CONFIG_PATH, "utf-8"));
10667
+ if (existsSync19(CONFIG_PATH)) {
10668
+ return JSON.parse(readFileSync25(CONFIG_PATH, "utf-8"));
8873
10669
  }
8874
10670
  } catch {
8875
10671
  }
8876
10672
  return {};
8877
10673
  }
8878
10674
  function writeConfig(config) {
8879
- ensureDir2();
8880
- writeFileSync20(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
10675
+ ensureDir3();
10676
+ writeFileSync22(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
8881
10677
  }
8882
10678
  function shouldTrack() {
8883
10679
  if (process.env.HOLOMIME_TELEMETRY === "0") return false;
@@ -8890,15 +10686,15 @@ function shouldTrack() {
8890
10686
  }
8891
10687
  function getAnonymousId() {
8892
10688
  try {
8893
- if (existsSync17(ANON_ID_PATH)) {
8894
- const id2 = readFileSync22(ANON_ID_PATH, "utf-8").trim();
10689
+ if (existsSync19(ANON_ID_PATH)) {
10690
+ const id2 = readFileSync25(ANON_ID_PATH, "utf-8").trim();
8895
10691
  if (id2.length > 0) return id2;
8896
10692
  }
8897
10693
  } catch {
8898
10694
  }
8899
10695
  const id = randomUUID();
8900
- ensureDir2();
8901
- writeFileSync20(ANON_ID_PATH, id);
10696
+ ensureDir3();
10697
+ writeFileSync22(ANON_ID_PATH, id);
8902
10698
  return id;
8903
10699
  }
8904
10700
  function showTelemetryBannerIfNeeded() {
@@ -8906,8 +10702,8 @@ function showTelemetryBannerIfNeeded() {
8906
10702
  if (config.telemetryBannerShown) return;
8907
10703
  if (shouldTrack()) {
8908
10704
  console.log(
8909
- chalk29.dim(
8910
- ` HoloMime collects anonymous usage data to improve the tool. Disable: ${chalk29.cyan("holomime telemetry disable")}`
10705
+ chalk32.dim(
10706
+ ` HoloMime collects anonymous usage data to improve the tool. Disable: ${chalk32.cyan("holomime telemetry disable")}`
8911
10707
  )
8912
10708
  );
8913
10709
  console.log();
@@ -8936,11 +10732,12 @@ function getTelemetryStatus() {
8936
10732
  }
8937
10733
 
8938
10734
  // src/telemetry/client.ts
8939
- var POSTHOG_KEY = "phc_holomime_placeholder";
8940
- var POSTHOG_HOST = "https://us.i.posthog.com";
10735
+ var POSTHOG_KEY = process.env.HOLOMIME_POSTHOG_KEY ?? "";
10736
+ var POSTHOG_HOST = process.env.HOLOMIME_POSTHOG_HOST ?? "https://us.i.posthog.com";
8941
10737
  var client = null;
8942
10738
  function getClient() {
8943
10739
  if (!shouldTrack()) return null;
10740
+ if (!POSTHOG_KEY) return null;
8944
10741
  if (!client) {
8945
10742
  client = new PostHog(POSTHOG_KEY, {
8946
10743
  host: POSTHOG_HOST,
@@ -8980,35 +10777,35 @@ async function flushTelemetry() {
8980
10777
  async function activateCommand(key) {
8981
10778
  printHeader("Activate License");
8982
10779
  if (!key || key.trim().length === 0) {
8983
- console.error(chalk30.red(" Please provide a license key."));
8984
- console.log(chalk30.dim(` Usage: ${chalk30.cyan("holomime activate <license-key>")}`));
10780
+ console.error(chalk33.red(" Please provide a license key."));
10781
+ console.log(chalk33.dim(` Usage: ${chalk33.cyan("holomime activate <license-key>")}`));
8985
10782
  console.log();
8986
10783
  process.exit(1);
8987
10784
  return;
8988
10785
  }
8989
10786
  const trimmedKey = key.trim();
8990
- const holomimeDir = join17(homedir4(), ".holomime");
8991
- const licensePath = join17(holomimeDir, "license");
8992
- if (!existsSync18(holomimeDir)) {
8993
- mkdirSync14(holomimeDir, { recursive: true });
10787
+ const holomimeDir = join19(homedir4(), ".holomime");
10788
+ const licensePath = join19(holomimeDir, "license");
10789
+ if (!existsSync20(holomimeDir)) {
10790
+ mkdirSync15(holomimeDir, { recursive: true });
8994
10791
  }
8995
- writeFileSync21(licensePath, trimmedKey);
8996
- console.log(chalk30.dim(" Validating license..."));
10792
+ writeFileSync23(licensePath, trimmedKey);
10793
+ console.log(chalk33.dim(" Validating license..."));
8997
10794
  const result = await validateLicense(trimmedKey);
8998
10795
  if (result.valid) {
8999
10796
  const tierLabel = result.tier === "enterprise" ? "Enterprise" : "Pro";
9000
10797
  console.log();
9001
10798
  printBox(
9002
10799
  [
9003
- `${figures20.tick} ${tierLabel} license activated!`,
10800
+ `${figures23.tick} ${tierLabel} license activated!`,
9004
10801
  "",
9005
10802
  "Unlocked features:",
9006
- ` ${chalk30.cyan(figures20.pointer)} Live alignment sessions (holomime session)`,
9007
- ` ${chalk30.cyan(figures20.pointer)} Recursive alignment (holomime evolve)`,
9008
- ` ${chalk30.cyan(figures20.pointer)} Behavioral benchmarking (holomime benchmark)`,
9009
- ` ${chalk30.cyan(figures20.pointer)} Drift detection (holomime watch)`,
9010
- ` ${chalk30.cyan(figures20.pointer)} Training data export (holomime export)`,
9011
- ` ${chalk30.cyan(figures20.pointer)} Growth tracking (holomime growth)`
10803
+ ` ${chalk33.cyan(figures23.pointer)} Live alignment sessions (holomime session)`,
10804
+ ` ${chalk33.cyan(figures23.pointer)} Recursive alignment (holomime evolve)`,
10805
+ ` ${chalk33.cyan(figures23.pointer)} Behavioral benchmarking (holomime benchmark)`,
10806
+ ` ${chalk33.cyan(figures23.pointer)} Drift detection (holomime watch)`,
10807
+ ` ${chalk33.cyan(figures23.pointer)} Training data export (holomime export)`,
10808
+ ` ${chalk33.cyan(figures23.pointer)} Growth tracking (holomime growth)`
9012
10809
  ].join("\n"),
9013
10810
  "success",
9014
10811
  `License Activated \u2014 ${tierLabel}`
@@ -9017,37 +10814,37 @@ async function activateCommand(key) {
9017
10814
  console.log();
9018
10815
  printBox(
9019
10816
  [
9020
- `${chalk30.yellow(figures20.warning)} License saved but could not be verified.`,
10817
+ `${chalk33.yellow(figures23.warning)} License saved but could not be verified.`,
9021
10818
  "",
9022
- chalk30.dim("This may happen if the server is unreachable."),
9023
- chalk30.dim("The key has been saved and will be re-validated on next use.")
10819
+ chalk33.dim("This may happen if the server is unreachable."),
10820
+ chalk33.dim("The key has been saved and will be re-validated on next use.")
9024
10821
  ].join("\n"),
9025
10822
  "warning",
9026
10823
  "License Pending Verification"
9027
10824
  );
9028
10825
  }
9029
10826
  console.log();
9030
- console.log(chalk30.dim(` License saved to: ${licensePath}`));
10827
+ console.log(chalk33.dim(` License saved to: ${licensePath}`));
9031
10828
  console.log();
9032
10829
  trackEvent("activate", { key_length: trimmedKey.length, verified: result.valid, tier: result.tier });
9033
10830
  }
9034
10831
 
9035
10832
  // src/commands/telemetry-cmd.ts
9036
- import chalk31 from "chalk";
9037
- import figures21 from "figures";
10833
+ import chalk34 from "chalk";
10834
+ import figures24 from "figures";
9038
10835
  async function telemetryCommand(action) {
9039
10836
  printHeader("Telemetry");
9040
10837
  if (action === "enable") {
9041
10838
  setTelemetryEnabled(true);
9042
10839
  console.log();
9043
- printBox(`${figures21.tick} Telemetry enabled. Anonymous usage data will be collected.`, "success");
10840
+ printBox(`${figures24.tick} Telemetry enabled. Anonymous usage data will be collected.`, "success");
9044
10841
  console.log();
9045
10842
  return;
9046
10843
  }
9047
10844
  if (action === "disable") {
9048
10845
  setTelemetryEnabled(false);
9049
10846
  console.log();
9050
- printBox(`${figures21.tick} Telemetry disabled. No usage data will be collected.`, "info");
10847
+ printBox(`${figures24.tick} Telemetry disabled. No usage data will be collected.`, "info");
9051
10848
  console.log();
9052
10849
  return;
9053
10850
  }
@@ -9055,15 +10852,15 @@ async function telemetryCommand(action) {
9055
10852
  console.log();
9056
10853
  printBox(
9057
10854
  [
9058
- `Status: ${status.enabled ? chalk31.green("Enabled") : chalk31.yellow("Disabled")}`,
9059
- `Reason: ${chalk31.dim(status.reason)}`,
10855
+ `Status: ${status.enabled ? chalk34.green("Enabled") : chalk34.yellow("Disabled")}`,
10856
+ `Reason: ${chalk34.dim(status.reason)}`,
9060
10857
  "",
9061
- chalk31.dim("HoloMime collects anonymous usage data to improve the tool."),
9062
- chalk31.dim("No personal information, API keys, or file paths are ever collected."),
10858
+ chalk34.dim("HoloMime collects anonymous usage data to improve the tool."),
10859
+ chalk34.dim("No personal information, API keys, or file paths are ever collected."),
9063
10860
  "",
9064
- `Enable: ${chalk31.cyan("holomime telemetry enable")}`,
9065
- `Disable: ${chalk31.cyan("holomime telemetry disable")}`,
9066
- `Env: ${chalk31.cyan("HOLOMIME_TELEMETRY=0")} or ${chalk31.cyan("DO_NOT_TRACK=1")}`
10861
+ `Enable: ${chalk34.cyan("holomime telemetry enable")}`,
10862
+ `Disable: ${chalk34.cyan("holomime telemetry disable")}`,
10863
+ `Env: ${chalk34.cyan("HOLOMIME_TELEMETRY=0")} or ${chalk34.cyan("DO_NOT_TRACK=1")}`
9067
10864
  ].join("\n"),
9068
10865
  "info",
9069
10866
  "Telemetry Status"
@@ -9073,7 +10870,7 @@ async function telemetryCommand(action) {
9073
10870
 
9074
10871
  // src/cli.ts
9075
10872
  var program = new Command();
9076
- program.name("holomime").description("Personality engine for AI agents \u2014 Big Five psychology, not RPG archetypes").version("1.0.0").hook("preAction", (_thisCommand, actionCommand) => {
10873
+ program.name("holomime").description("Personality engine for AI agents \u2014 Big Five psychology, not RPG archetypes").version("1.1.0").hook("preAction", (_thisCommand, actionCommand) => {
9077
10874
  printBanner();
9078
10875
  const commandName = actionCommand.name();
9079
10876
  showTelemetryBannerIfNeeded();
@@ -9089,26 +10886,29 @@ program.name("holomime").description("Personality engine for AI agents \u2014 Bi
9089
10886
  }
9090
10887
  });
9091
10888
  program.command("init").description("Build a personality profile through a guided assessment").action(initCommand);
9092
- program.command("compile").description("Compile .personality.json into a provider-specific runtime config").option("--provider <provider>", "Target provider (anthropic, openai, gemini, ollama)", "anthropic").option("--surface <surface>", "Target surface (chat, email, code_review, slack, api)", "chat").option("--for <format>", "Compile for a specific format (openclaw)").option("-o, --output <path>", "Write output to file instead of stdout").action(compileCommand);
10889
+ program.command("compile").description("Compile .personality.json into a provider-specific runtime config").option("--provider <provider>", "Target provider (anthropic, openai, gemini, ollama)", "anthropic").option("--surface <surface>", "Target surface (chat, email, code_review, slack, api, embodied)", "chat").option("--for <format>", "Compile for a specific format (openclaw)").option("-o, --output <path>", "Write output to file instead of stdout").action(compileCommand);
9093
10890
  program.command("validate").description("Validate .personality.json schema and psychological coherence").action(validateCommand);
9094
- program.command("profile").description("Pretty-print a human-readable personality summary").action(profileCommand);
9095
- program.command("diagnose").description("Detect behavioral patterns from conversation logs (rule-based, no LLM)").requiredOption("--log <path>", "Path to conversation log (JSON)").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api)", "auto").action(diagnoseCommand);
9096
- program.command("assess").description("Full Big Five alignment check \u2014 compare spec vs actual behavior").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--log <path>", "Path to conversation log (JSON)").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api)", "auto").action(assessCommand);
10891
+ program.command("profile").description("Pretty-print a human-readable personality summary").option("--format <format>", "Output format (terminal, md)", "terminal").option("-o, --output <path>", "Write output to file (for md format)").action(profileCommand);
10892
+ program.command("diagnose").description("Detect behavioral patterns from conversation logs (rule-based, no LLM)").requiredOption("--log <path>", "Path to conversation log (JSON)").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl)", "auto").action(diagnoseCommand);
10893
+ program.command("assess").description("Full Big Five alignment check \u2014 compare spec vs actual behavior").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--log <path>", "Path to conversation log (JSON)").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl)", "auto").action(assessCommand);
9097
10894
  program.command("browse").description("Browse shared personality profiles from the community registry").option("--tag <tag>", "Filter by tag").action(browseCommand);
9098
10895
  program.command("pull").description("Download a personality profile from the registry").argument("<handle>", "Personality handle to pull").option("-o, --output <path>", "Output path", ".personality.json").action(pullCommand);
9099
10896
  program.command("publish").description("Share your personality profile to the community registry").option("--personality <path>", "Path to .personality.json", ".personality.json").action(publishCommand);
9100
10897
  program.command("activate").description("Activate a Pro license key").argument("<key>", "License key from holomime.dev").action(activateCommand);
9101
10898
  program.command("telemetry").description("Manage anonymous usage telemetry").argument("[action]", "enable, disable, or status (default: status)").action(telemetryCommand);
9102
- program.command("session").description("Live alignment session \u2014 behavioral refinement for your agent [Pro]").requiredOption("--personality <path>", "Path to .personality.json").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override (e.g. claude-sonnet-4-20250514, gpt-4o)").option("--log <path>", "Conversation log for pre-session diagnosis").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api)", "auto").option("--turns <n>", "Maximum session turns", "24").option("--observe", "Observe mode (watch without intervention)").option("--interactive", "Supervisor mode \u2014 intervene mid-session with directives").option("--apply", "Apply recommendations to .personality.json after session").action(sessionCommand);
9103
- program.command("autopilot").description("Automated behavioral alignment \u2014 diagnose, refine, and apply [Pro]").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--log <path>", "Conversation log for diagnosis").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api)", "auto").option("--threshold <level>", "Trigger threshold (routine, targeted, intervention)", "targeted").option("--turns <n>", "Maximum session turns", "24").option("--dry-run", "Show what would happen without running alignment").option("--apply", "Apply recommendations to .personality.json after session").action(autopilotCommand);
10899
+ program.command("session").description("Live alignment session \u2014 behavioral refinement for your agent [Pro]").requiredOption("--personality <path>", "Path to .personality.json").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override (e.g. claude-sonnet-4-20250514, gpt-4o)").option("--log <path>", "Conversation log for pre-session diagnosis").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl)", "auto").option("--turns <n>", "Maximum session turns", "24").option("--observe", "Observe mode (watch without intervention)").option("--interactive", "Supervisor mode \u2014 intervene mid-session with directives").option("--apply", "Apply recommendations to .personality.json after session").action(sessionCommand);
10900
+ program.command("autopilot").description("Automated behavioral alignment \u2014 diagnose, refine, and apply [Pro]").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--log <path>", "Conversation log for diagnosis").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl)", "auto").option("--threshold <level>", "Trigger threshold (routine, targeted, intervention)", "targeted").option("--turns <n>", "Maximum session turns", "24").option("--dry-run", "Show what would happen without running alignment").option("--apply", "Apply recommendations to .personality.json after session").option("--oversight <mode>", "Oversight mode (none, review, approve, approve-specs)", "review").action(autopilotCommand);
9104
10901
  program.command("growth").description("Track improvement over time from assessment history [Pro]").requiredOption("--personality <path>", "Path to .personality.json").option("--history <path>", "Path to assessments directory", ".holomime/assessments").action(growthCommand);
9105
10902
  program.command("export").description("Export alignment sessions as training data (DPO, RLHF, Alpaca, HuggingFace, OpenAI) [Pro]").requiredOption("--format <format>", "Export format (dpo, rlhf, jsonl, alpaca, huggingface, openai)").option("--sessions <path>", "Path to sessions directory", ".holomime/sessions").option("-o, --output <path>", "Output file path").option("--push", "Push to HuggingFace Hub after export (requires HF_TOKEN)").option("--repo <repo>", "HuggingFace Hub repo name for push (e.g. user/dataset-name)").action(exportCommand);
9106
10903
  program.command("train").description("Fine-tune a model with alignment data \u2014 train, deploy, and verify [Pro]").option("--data <path>", "Path to exported training data").option("--provider <provider>", "Training provider (openai, huggingface)", "openai").option("--base-model <model>", "Base model to fine-tune", "gpt-4o-mini").option("--suffix <suffix>", "Model name suffix").option("--epochs <n>", "Training epochs").option("--method <method>", "Training method (auto, sft, dpo)", "auto").option("--personality <path>", "Path to .personality.json", ".personality.json").option("--skip-eval", "Skip auto-evaluation after training").option("--skip-deploy", "Skip auto-deploy to personality").option("--dry-run", "Preview training plan without starting").option("--push", "Push trained model to HuggingFace Hub (HF only)").option("--hub-repo <repo>", "HuggingFace Hub repo name for push (e.g. user/model-name)").action(trainCommand);
9107
- program.command("eval").description("Measure alignment effectiveness \u2014 compare before/after behavior [Pro]").requiredOption("--before <path>", "Conversation log from BEFORE alignment").requiredOption("--after <path>", "Conversation log from AFTER alignment").option("--personality <path>", "Path to .personality.json (for agent name)").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api)", "auto").action(evalCommand);
9108
- program.command("evolve").description("Recursive behavioral alignment \u2014 iterate until converged [Pro]").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--log <path>", "Conversation log for diagnosis").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api)", "auto").option("--max-iterations <n>", "Maximum alignment iterations", "5").option("--convergence <score>", "TES convergence threshold (0-100)", "85").option("--turns <n>", "Max turns per session", "18").option("--apply", "Apply final recommendations to .personality.json").option("--export-dpo <path>", "Export accumulated DPO pairs to file").option("--dry-run", "Preview without running sessions").action(evolveCommand);
10904
+ program.command("eval").description("Measure alignment effectiveness \u2014 compare before/after behavior [Pro]").requiredOption("--before <path>", "Conversation log from BEFORE alignment").requiredOption("--after <path>", "Conversation log from AFTER alignment").option("--personality <path>", "Path to .personality.json (for agent name)").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl)", "auto").action(evalCommand);
10905
+ program.command("evolve").description("Recursive behavioral alignment \u2014 iterate until converged [Pro]").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--log <path>", "Conversation log for diagnosis").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--format <format>", "Log format (auto, holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl)", "auto").option("--max-iterations <n>", "Maximum alignment iterations", "5").option("--convergence <score>", "TES convergence threshold (0-100)", "85").option("--turns <n>", "Max turns per session", "18").option("--apply", "Apply final recommendations to .personality.json").option("--export-dpo <path>", "Export accumulated DPO pairs to file").option("--dry-run", "Preview without running sessions").action(evolveCommand);
9109
10906
  program.command("benchmark").description("Behavioral stress test \u2014 7 scenarios targeting each detector [Pro]").requiredOption("--personality <path>", "Path to .personality.json").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--scenarios <list>", "Comma-separated scenario filter (e.g. apology-trap,sycophancy-test)").action(benchmarkCommand);
9110
10907
  program.command("watch").description("Continuous drift detection \u2014 monitor logs and auto-align [Pro]").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--dir <path>", "Directory to watch for conversation logs").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--interval <ms>", "Check interval in milliseconds", "30000").option("--threshold <level>", "Drift threshold (routine, targeted, intervention)", "targeted").option("--auto-evolve", "Auto-run evolve when drift detected").action(watchCommand);
9111
10908
  program.command("certify").description("Generate a verifiable behavioral credential for your agent [Pro]").option("--personality <path>", "Path to .personality.json", ".personality.json").option("--benchmark <path>", "Path to benchmark report JSON").option("--evolve <path>", "Path to evolve result JSON").option("-o, --output <path>", "Output directory for credential").option("--verify <path>", "Verify an existing credential").action(certifyCommand);
9112
- program.command("daemon").description("Background drift detection with auto-evolve \u2014 proactive alignment [Pro]").requiredOption("--dir <path>", "Directory to watch for conversation logs").option("--personality <path>", "Path to .personality.json", ".personality.json").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--interval <ms>", "Check interval in milliseconds", "30000").option("--threshold <level>", "Drift threshold (routine, targeted, intervention)", "targeted").action(daemonCommand);
10909
+ program.command("daemon").description("Background drift detection with auto-evolve \u2014 proactive alignment [Pro]").requiredOption("--dir <path>", "Directory to watch for conversation logs").option("--personality <path>", "Path to .personality.json", ".personality.json").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--interval <ms>", "Check interval in milliseconds", "30000").option("--threshold <level>", "Drift threshold (routine, targeted, intervention)", "targeted").option("--oversight <mode>", "Oversight mode (none, review, approve, approve-specs)", "review").action(daemonCommand);
9113
10910
  program.command("fleet").description("Monitor multiple agents from a single dashboard [Pro]").option("--config <path>", "Path to fleet.json config file").option("--dir <path>", "Auto-discover agents in directory").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--interval <ms>", "Check interval in milliseconds", "30000").option("--threshold <level>", "Drift threshold (routine, targeted, intervention)", "targeted").option("--auto-evolve", "Auto-run evolve when drift detected").action(fleetCommand);
10911
+ program.command("network").description("Multi-agent therapy mesh \u2014 agents treating agents [Pro]").option("--dir <path>", "Auto-discover agents in directory").option("--config <path>", "Path to network.json config file").option("--pairing <strategy>", "Pairing strategy (severity, round-robin, complementary)", "severity").option("--therapist <path>", "Custom therapist personality spec").option("--oversight <mode>", "Oversight mode (none, review, approve, approve-specs)", "review").option("--provider <provider>", "LLM provider (ollama, anthropic, openai)", "ollama").option("--model <model>", "Model override").option("--max-sessions <n>", "Max sessions per agent", "3").option("--convergence <n>", "Convergence threshold 0-100", "85").option("--turns <n>", "Max turns per session", "20").option("--apply", "Write spec changes back to .personality.json").option("--export-dpo <path>", "Export DPO pairs to file").action(networkCommand);
10912
+ program.command("share").description("Share DPO training pairs to the marketplace [Pro]").option("--sessions <dir>", "Session transcripts directory", ".holomime/sessions").option("--format <fmt>", "Export format (dpo, rlhf, alpaca)", "dpo").option("--anonymize", "Strip agent names from exported data").option("--tags <tags>", "Comma-separated tags for discoverability").action(shareCommand);
10913
+ program.command("prescribe").description("Diagnose and prescribe DPO corrections from the behavioral corpus [Pro]").requiredOption("--personality <path>", "Path to .personality.json").requiredOption("--log <path>", "Path to conversation log").option("--format <format>", "Log format (holomime, chatgpt, claude, openai-api, anthropic-api, otel, jsonl)").option("--source <source>", "Correction source (corpus, marketplace, both)", "corpus").option("--apply", "Apply found corrections").option("-o, --output <path>", "Write prescription to file").action(prescribeCommand);
9114
10914
  program.parseAsync().then(() => flushTelemetry());