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/README.md +160 -97
- package/dist/cli.js +2125 -325
- package/dist/index.d.ts +1770 -173
- package/dist/index.js +1726 -158
- package/dist/mcp-server.js +404 -93
- package/package.json +21 -6
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
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
1755
|
-
var opennessFacetsSchema =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1951
|
+
var bigFiveTraitSchema = z2.object({
|
|
1786
1952
|
score: traitScore,
|
|
1787
|
-
facets:
|
|
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 =
|
|
1796
|
-
openness:
|
|
1797
|
-
conscientiousness:
|
|
1798
|
-
extraversion:
|
|
1799
|
-
agreeableness:
|
|
1800
|
-
emotional_stability:
|
|
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 =
|
|
1803
|
-
var learningOrientationSchema =
|
|
1804
|
-
var therapyDimensionsSchema =
|
|
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 =
|
|
1978
|
+
var registerSchema = z2.enum([
|
|
1813
1979
|
"casual_professional",
|
|
1814
1980
|
"formal",
|
|
1815
1981
|
"conversational",
|
|
1816
1982
|
"adaptive"
|
|
1817
1983
|
]);
|
|
1818
|
-
var outputFormatSchema =
|
|
1819
|
-
var emojiPolicySchema =
|
|
1820
|
-
var reasoningTransparencySchema =
|
|
1821
|
-
var conflictApproachSchema =
|
|
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 =
|
|
1993
|
+
var uncertaintyHandlingSchema = z2.enum([
|
|
1828
1994
|
"transparent",
|
|
1829
1995
|
"confident_transparency",
|
|
1830
1996
|
"minimize",
|
|
1831
1997
|
"reframe"
|
|
1832
1998
|
]);
|
|
1833
|
-
var communicationSchema =
|
|
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 =
|
|
1842
|
-
expertise:
|
|
1843
|
-
boundaries:
|
|
1844
|
-
refuses:
|
|
1845
|
-
escalation_triggers:
|
|
1846
|
-
hard_limits:
|
|
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 =
|
|
1850
|
-
area:
|
|
1851
|
-
severity:
|
|
1852
|
-
first_detected:
|
|
1853
|
-
session_count:
|
|
1854
|
-
resolved:
|
|
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 =
|
|
1857
|
-
areas:
|
|
1858
|
-
patterns_to_watch:
|
|
1859
|
-
strengths:
|
|
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 =
|
|
1862
|
-
var surfaceSchema =
|
|
1863
|
-
var personalitySpecSchema =
|
|
1864
|
-
$schema:
|
|
1865
|
-
extends:
|
|
1866
|
-
version:
|
|
1867
|
-
name:
|
|
1868
|
-
handle:
|
|
1869
|
-
purpose:
|
|
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 =
|
|
2046
|
+
var compiledConfigSchema = z2.object({
|
|
1877
2047
|
provider: providerSchema,
|
|
1878
2048
|
surface: surfaceSchema,
|
|
1879
|
-
system_prompt:
|
|
1880
|
-
temperature:
|
|
1881
|
-
top_p:
|
|
1882
|
-
max_tokens:
|
|
1883
|
-
metadata:
|
|
1884
|
-
personality_hash:
|
|
1885
|
-
compiled_at:
|
|
1886
|
-
holomime_version:
|
|
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 =
|
|
1890
|
-
role:
|
|
1891
|
-
content:
|
|
1892
|
-
timestamp:
|
|
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 =
|
|
1895
|
-
id:
|
|
1896
|
-
messages:
|
|
1897
|
-
metadata:
|
|
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 =
|
|
2069
|
+
var conversationLogSchema = z2.union([
|
|
1900
2070
|
conversationSchema,
|
|
1901
|
-
|
|
2071
|
+
z2.array(conversationSchema)
|
|
1902
2072
|
]);
|
|
1903
|
-
var severitySchema =
|
|
2073
|
+
var severitySchema = z2.enum(["info", "warning", "concern"]);
|
|
1904
2074
|
|
|
1905
2075
|
// src/core/inheritance.ts
|
|
1906
|
-
import { readFileSync as
|
|
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(
|
|
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(
|
|
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: "
|
|
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
|
|
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
|
|
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
|
|
2614
|
+
return clamp2(scaled, 256, range.max);
|
|
2292
2615
|
}
|
|
2293
|
-
function
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4331
|
+
return clamp3(score);
|
|
3595
4332
|
}
|
|
3596
|
-
function
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
3859
|
-
import { resolve as resolve8, join as
|
|
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/
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
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 (!
|
|
4284
|
-
|
|
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 =
|
|
4290
|
-
|
|
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((
|
|
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
|
|
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
|
|
5657
|
+
return resolve34(null);
|
|
4753
5658
|
}
|
|
4754
5659
|
console.log(chalk14.dim(` Directive injected into session context.`));
|
|
4755
|
-
return
|
|
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
|
-
|
|
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
|
|
4889
|
-
import { resolve as resolve11, join as
|
|
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
|
|
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 (!
|
|
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 (!
|
|
4911
|
-
|
|
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
|
-
|
|
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 (!
|
|
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(
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
5548
|
-
import { join as
|
|
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 =
|
|
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:
|
|
5891
|
-
|
|
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
|
-
|
|
6855
|
+
writeFileSync11(fullPath, jsonl);
|
|
5895
6856
|
} else if (format === "jsonl") {
|
|
5896
6857
|
const lines = result.examples.map((ex) => JSON.stringify(ex)).join("\n");
|
|
5897
|
-
|
|
6858
|
+
writeFileSync11(fullPath, lines + "\n");
|
|
5898
6859
|
} else {
|
|
5899
|
-
|
|
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
|
|
5985
|
-
import { resolve as resolve19, join as
|
|
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
|
|
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((
|
|
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 (
|
|
7066
|
+
if (existsSync9(fullPath)) {
|
|
6106
7067
|
try {
|
|
6107
|
-
const spec = JSON.parse(
|
|
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
|
|
6197
|
-
import { resolve as resolve17, join as
|
|
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
|
-
|
|
6228
|
-
const filePath =
|
|
6229
|
-
|
|
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
|
|
6349
|
-
import { resolve as resolve18, join as
|
|
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
|
|
6366
|
-
if (
|
|
7326
|
+
const result2 = detector(messages);
|
|
7327
|
+
if (result2) detected.push(result2);
|
|
6367
7328
|
}
|
|
6368
|
-
|
|
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
|
-
|
|
6598
|
-
const promptsPath =
|
|
6599
|
-
|
|
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] ?
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
6695
|
-
|
|
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 (
|
|
6700
|
-
const spec = JSON.parse(
|
|
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
|
-
|
|
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 || !
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
7603
|
-
import { join as
|
|
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 ??
|
|
7607
|
-
if (!
|
|
7608
|
-
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
7862
|
-
import { join as
|
|
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 (
|
|
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 (!
|
|
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(
|
|
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 (!
|
|
7961
|
-
|
|
9019
|
+
if (!existsSync12(logDir)) {
|
|
9020
|
+
mkdirSync10(logDir, { recursive: true });
|
|
7962
9021
|
}
|
|
7963
|
-
|
|
7964
|
-
|
|
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 (!
|
|
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
|
|
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
|
|
8112
|
-
import { join as
|
|
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 (!
|
|
8227
|
-
|
|
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 =
|
|
8232
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
9413
|
+
var HOLOMIME_DIR2 = ".holomime";
|
|
8355
9414
|
function getDaemonStatePath() {
|
|
8356
|
-
return resolve27(process.cwd(),
|
|
9415
|
+
return resolve27(process.cwd(), HOLOMIME_DIR2, "daemon.json");
|
|
8357
9416
|
}
|
|
8358
9417
|
function getDaemonLogPath() {
|
|
8359
|
-
return resolve27(process.cwd(),
|
|
9418
|
+
return resolve27(process.cwd(), HOLOMIME_DIR2, "daemon-log.json");
|
|
8360
9419
|
}
|
|
8361
|
-
function
|
|
8362
|
-
const dir = resolve27(process.cwd(),
|
|
8363
|
-
if (!
|
|
8364
|
-
|
|
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
|
-
|
|
8369
|
-
|
|
9427
|
+
ensureDir2();
|
|
9428
|
+
writeFileSync19(getDaemonStatePath(), JSON.stringify(state, null, 2) + "\n");
|
|
8370
9429
|
}
|
|
8371
9430
|
function appendDaemonLog(event) {
|
|
8372
|
-
|
|
9431
|
+
ensureDir2();
|
|
8373
9432
|
const logPath = getDaemonLogPath();
|
|
8374
9433
|
let log = [];
|
|
8375
9434
|
try {
|
|
8376
|
-
if (
|
|
8377
|
-
log = JSON.parse(
|
|
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
|
-
|
|
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
|
|
8545
|
-
import { resolve as resolve29, join as
|
|
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
|
|
8549
|
-
import { join as
|
|
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(
|
|
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 (!
|
|
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 =
|
|
8573
|
-
const specPath =
|
|
8574
|
-
const logDir =
|
|
8575
|
-
if (
|
|
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:
|
|
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 (!
|
|
8824
|
-
|
|
9882
|
+
if (!existsSync17(logDir)) {
|
|
9883
|
+
mkdirSync13(logDir, { recursive: true });
|
|
8825
9884
|
}
|
|
8826
|
-
const logPath =
|
|
8827
|
-
|
|
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/
|
|
8846
|
-
import
|
|
9904
|
+
// src/commands/network.ts
|
|
9905
|
+
import chalk29 from "chalk";
|
|
8847
9906
|
import figures20 from "figures";
|
|
8848
|
-
import {
|
|
8849
|
-
|
|
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
|
|
8857
|
-
import { join as
|
|
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
|
|
8861
|
-
var
|
|
8862
|
-
var CONFIG_PATH =
|
|
8863
|
-
var ANON_ID_PATH =
|
|
8864
|
-
function
|
|
8865
|
-
if (!
|
|
8866
|
-
|
|
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 (
|
|
8872
|
-
return JSON.parse(
|
|
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
|
-
|
|
8880
|
-
|
|
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 (
|
|
8894
|
-
const id2 =
|
|
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
|
-
|
|
8901
|
-
|
|
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
|
-
|
|
8910
|
-
` HoloMime collects anonymous usage data to improve the tool. 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 = "
|
|
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(
|
|
8984
|
-
console.log(
|
|
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 =
|
|
8991
|
-
const licensePath =
|
|
8992
|
-
if (!
|
|
8993
|
-
|
|
10787
|
+
const holomimeDir = join19(homedir4(), ".holomime");
|
|
10788
|
+
const licensePath = join19(holomimeDir, "license");
|
|
10789
|
+
if (!existsSync20(holomimeDir)) {
|
|
10790
|
+
mkdirSync15(holomimeDir, { recursive: true });
|
|
8994
10791
|
}
|
|
8995
|
-
|
|
8996
|
-
console.log(
|
|
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
|
-
`${
|
|
10800
|
+
`${figures23.tick} ${tierLabel} license activated!`,
|
|
9004
10801
|
"",
|
|
9005
10802
|
"Unlocked features:",
|
|
9006
|
-
` ${
|
|
9007
|
-
` ${
|
|
9008
|
-
` ${
|
|
9009
|
-
` ${
|
|
9010
|
-
` ${
|
|
9011
|
-
` ${
|
|
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
|
-
`${
|
|
10817
|
+
`${chalk33.yellow(figures23.warning)} License saved but could not be verified.`,
|
|
9021
10818
|
"",
|
|
9022
|
-
|
|
9023
|
-
|
|
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(
|
|
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
|
|
9037
|
-
import
|
|
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(`${
|
|
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(`${
|
|
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 ?
|
|
9059
|
-
`Reason: ${
|
|
10855
|
+
`Status: ${status.enabled ? chalk34.green("Enabled") : chalk34.yellow("Disabled")}`,
|
|
10856
|
+
`Reason: ${chalk34.dim(status.reason)}`,
|
|
9060
10857
|
"",
|
|
9061
|
-
|
|
9062
|
-
|
|
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: ${
|
|
9065
|
-
`Disable: ${
|
|
9066
|
-
`Env: ${
|
|
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.
|
|
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());
|