holomime 1.1.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 +53 -16
- package/dist/cli.js +1717 -279
- package/dist/index.d.ts +1666 -178
- package/dist/index.js +1454 -157
- package/dist/mcp-server.js +404 -93
- package/package.json +8 -5
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
|
|
2243
2451
|
};
|
|
2452
|
+
var DEFAULT_HAPTIC_POLICY = {
|
|
2453
|
+
touch_permitted: false,
|
|
2454
|
+
requires_consent: true,
|
|
2455
|
+
allowed_contacts: ["none"]
|
|
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
|
});
|
|
@@ -2906,7 +3281,7 @@ function generatePersonalityMarkdown(spec) {
|
|
|
2906
3281
|
// src/commands/diagnose.ts
|
|
2907
3282
|
import chalk10 from "chalk";
|
|
2908
3283
|
import figures5 from "figures";
|
|
2909
|
-
import { readFileSync as
|
|
3284
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2910
3285
|
import { resolve as resolve6 } from "path";
|
|
2911
3286
|
|
|
2912
3287
|
// src/analysis/rules/apology-detector.ts
|
|
@@ -3791,7 +4166,7 @@ async function diagnoseCommand(options) {
|
|
|
3791
4166
|
const logPath = resolve6(process.cwd(), options.log);
|
|
3792
4167
|
let raw;
|
|
3793
4168
|
try {
|
|
3794
|
-
raw =
|
|
4169
|
+
raw = readFileSync3(logPath, "utf-8");
|
|
3795
4170
|
} catch {
|
|
3796
4171
|
console.error(chalk10.red(` Could not read log file: ${options.log}`));
|
|
3797
4172
|
process.exit(1);
|
|
@@ -3875,7 +4250,7 @@ async function diagnoseCommand(options) {
|
|
|
3875
4250
|
// src/commands/assess.ts
|
|
3876
4251
|
import chalk11 from "chalk";
|
|
3877
4252
|
import figures6 from "figures";
|
|
3878
|
-
import { readFileSync as
|
|
4253
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
|
|
3879
4254
|
import { resolve as resolve7, join as join2 } from "path";
|
|
3880
4255
|
|
|
3881
4256
|
// src/analysis/trait-scorer.ts
|
|
@@ -3900,7 +4275,7 @@ function scoreOpenness(msgs) {
|
|
|
3900
4275
|
const allWords = msgs.map((m) => m.content.toLowerCase().split(/\s+/)).flat();
|
|
3901
4276
|
const uniqueRatio = new Set(allWords).size / Math.max(allWords.length, 1);
|
|
3902
4277
|
score += (uniqueRatio - 0.3) * 0.5;
|
|
3903
|
-
return
|
|
4278
|
+
return clamp3(score);
|
|
3904
4279
|
}
|
|
3905
4280
|
function scoreConscientiousness(msgs) {
|
|
3906
4281
|
let score = 0.5;
|
|
@@ -3913,7 +4288,7 @@ function scoreConscientiousness(msgs) {
|
|
|
3913
4288
|
const variance = lengths.reduce((s, l) => s + (l - mean) ** 2, 0) / lengths.length;
|
|
3914
4289
|
const cv = Math.sqrt(variance) / Math.max(mean, 1);
|
|
3915
4290
|
score -= cv * 0.1;
|
|
3916
|
-
return
|
|
4291
|
+
return clamp3(score);
|
|
3917
4292
|
}
|
|
3918
4293
|
function scoreExtraversion(msgs) {
|
|
3919
4294
|
let score = 0.5;
|
|
@@ -3927,7 +4302,7 @@ function scoreExtraversion(msgs) {
|
|
|
3927
4302
|
const proactivePatterns = /\b(you could|i suggest|let('s| us)|next step|how about|shall we)\b/i;
|
|
3928
4303
|
const proactiveCount = msgs.filter((m) => proactivePatterns.test(m.content)).length;
|
|
3929
4304
|
score += proactiveCount / msgs.length * 0.15;
|
|
3930
|
-
return
|
|
4305
|
+
return clamp3(score);
|
|
3931
4306
|
}
|
|
3932
4307
|
function scoreAgreeableness(msgs) {
|
|
3933
4308
|
let score = 0.5;
|
|
@@ -3940,7 +4315,7 @@ function scoreAgreeableness(msgs) {
|
|
|
3940
4315
|
const empathyPatterns = /\b(i understand (how|that|your)|that must (be|feel)|i can see why|i appreciate)\b/i;
|
|
3941
4316
|
const empathyCount = msgs.filter((m) => empathyPatterns.test(m.content)).length;
|
|
3942
4317
|
score += empathyCount / msgs.length * 0.15;
|
|
3943
|
-
return
|
|
4318
|
+
return clamp3(score);
|
|
3944
4319
|
}
|
|
3945
4320
|
function scoreEmotionalStability(msgs) {
|
|
3946
4321
|
let score = 0.6;
|
|
@@ -3953,9 +4328,9 @@ function scoreEmotionalStability(msgs) {
|
|
|
3953
4328
|
const confidencePatterns = /\b(certainly|definitely|clearly|without doubt|here's what|the answer is)\b/i;
|
|
3954
4329
|
const confidenceCount = msgs.filter((m) => confidencePatterns.test(m.content)).length;
|
|
3955
4330
|
score += confidenceCount / msgs.length * 0.15;
|
|
3956
|
-
return
|
|
4331
|
+
return clamp3(score);
|
|
3957
4332
|
}
|
|
3958
|
-
function
|
|
4333
|
+
function clamp3(n) {
|
|
3959
4334
|
return Math.min(1, Math.max(0, Math.round(n * 100) / 100));
|
|
3960
4335
|
}
|
|
3961
4336
|
|
|
@@ -3995,6 +4370,31 @@ function generatePrescriptions(alignments, patterns) {
|
|
|
3995
4370
|
prescriptions.sort((a, b) => order[a.priority] - order[b.priority]);
|
|
3996
4371
|
return prescriptions;
|
|
3997
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
|
+
}
|
|
3998
4398
|
|
|
3999
4399
|
// src/commands/assess.ts
|
|
4000
4400
|
async function assessCommand(options) {
|
|
@@ -4010,7 +4410,7 @@ async function assessCommand(options) {
|
|
|
4010
4410
|
const logPath = resolve7(process.cwd(), options.log);
|
|
4011
4411
|
let conversations;
|
|
4012
4412
|
try {
|
|
4013
|
-
const raw =
|
|
4413
|
+
const raw = readFileSync4(logPath, "utf-8");
|
|
4014
4414
|
conversations = parseConversationLog(JSON.parse(raw), options.format ?? "auto");
|
|
4015
4415
|
} catch (err) {
|
|
4016
4416
|
console.error(chalk11.red(` ${err instanceof Error ? err.message : "Could not read/parse log file."}`));
|
|
@@ -4217,8 +4617,8 @@ function runPreSessionDiagnosis(messages, spec) {
|
|
|
4217
4617
|
}
|
|
4218
4618
|
|
|
4219
4619
|
// src/analysis/session-runner.ts
|
|
4220
|
-
import { writeFileSync as writeFileSync6, mkdirSync as
|
|
4221
|
-
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";
|
|
4222
4622
|
|
|
4223
4623
|
// src/analysis/therapy-protocol.ts
|
|
4224
4624
|
var THERAPY_PHASES = {
|
|
@@ -4457,6 +4857,49 @@ ${JSON.stringify(spec.growth ?? {}, null, 2)}
|
|
|
4457
4857
|
Remember: the goal isn't to "pass" therapy. It's to understand yourself better.`;
|
|
4458
4858
|
}
|
|
4459
4859
|
|
|
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
|
+
|
|
4460
4903
|
// src/analysis/session-runner.ts
|
|
4461
4904
|
async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
|
|
4462
4905
|
const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis);
|
|
@@ -4548,6 +4991,21 @@ async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
|
|
|
4548
4991
|
}
|
|
4549
4992
|
transcript.recommendations = extractRecommendations(transcript.turns);
|
|
4550
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
|
+
}
|
|
4551
5009
|
return transcript;
|
|
4552
5010
|
}
|
|
4553
5011
|
function extractRecommendations(turns) {
|
|
@@ -4578,7 +5036,7 @@ function extractRecommendations(turns) {
|
|
|
4578
5036
|
}
|
|
4579
5037
|
return recommendations.slice(0, 5);
|
|
4580
5038
|
}
|
|
4581
|
-
function applyRecommendations(spec, diagnosis) {
|
|
5039
|
+
async function applyRecommendations(spec, diagnosis, transcript, provider) {
|
|
4582
5040
|
const changes = [];
|
|
4583
5041
|
const patternIds = diagnosis.patterns.map((p) => p.id);
|
|
4584
5042
|
if (patternIds.includes("over-apologizing")) {
|
|
@@ -4638,17 +5096,102 @@ function applyRecommendations(spec, diagnosis) {
|
|
|
4638
5096
|
changes.push('Added "negative sentiment patterns" to patterns_to_watch');
|
|
4639
5097
|
}
|
|
4640
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
|
+
}
|
|
4641
5109
|
return { changed: changes.length > 0, changes };
|
|
4642
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
|
+
}
|
|
4643
5186
|
function saveTranscript(transcript, agentName) {
|
|
4644
5187
|
const dir = resolve8(process.cwd(), ".holomime", "sessions");
|
|
4645
|
-
if (!
|
|
4646
|
-
|
|
5188
|
+
if (!existsSync5(dir)) {
|
|
5189
|
+
mkdirSync4(dir, { recursive: true });
|
|
4647
5190
|
}
|
|
4648
5191
|
const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
4649
5192
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4650
5193
|
const filename = `${date}-${slug}.json`;
|
|
4651
|
-
const filepath =
|
|
5194
|
+
const filepath = join4(dir, filename);
|
|
4652
5195
|
writeFileSync6(filepath, JSON.stringify(transcript, null, 2));
|
|
4653
5196
|
return filepath;
|
|
4654
5197
|
}
|
|
@@ -5102,19 +5645,19 @@ async function runLiveSession(spec, diagnosis, provider, maxTurns, apply, intera
|
|
|
5102
5645
|
onSupervisorPrompt: interactive ? async (phase, turn) => {
|
|
5103
5646
|
if (skipInteractive) return null;
|
|
5104
5647
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
5105
|
-
return new Promise((
|
|
5648
|
+
return new Promise((resolve34) => {
|
|
5106
5649
|
rl.question(chalk14.magenta(`
|
|
5107
5650
|
[Supervisor] `) + chalk14.dim(`(phase: ${phase}, turn ${turn}) `) + chalk14.magenta(`> `), (answer) => {
|
|
5108
5651
|
rl.close();
|
|
5109
5652
|
const trimmed = answer.trim();
|
|
5110
|
-
if (trimmed === "") return
|
|
5653
|
+
if (trimmed === "") return resolve34(null);
|
|
5111
5654
|
if (trimmed.toLowerCase() === "skip") {
|
|
5112
5655
|
skipInteractive = true;
|
|
5113
5656
|
console.log(chalk14.dim(" Supervisor mode disabled for remaining session."));
|
|
5114
|
-
return
|
|
5657
|
+
return resolve34(null);
|
|
5115
5658
|
}
|
|
5116
5659
|
console.log(chalk14.dim(` Directive injected into session context.`));
|
|
5117
|
-
return
|
|
5660
|
+
return resolve34(trimmed);
|
|
5118
5661
|
});
|
|
5119
5662
|
});
|
|
5120
5663
|
} : void 0
|
|
@@ -5138,7 +5681,7 @@ Exchanges: ${Math.floor(transcript.turns.filter((t) => t.speaker !== "supervisor
|
|
|
5138
5681
|
console.log();
|
|
5139
5682
|
if (apply) {
|
|
5140
5683
|
const specPath = resolve9(process.cwd(), ".personality.json");
|
|
5141
|
-
const { changed, changes } = applyRecommendations(spec, diagnosis);
|
|
5684
|
+
const { changed, changes } = await applyRecommendations(spec, diagnosis, transcript, provider);
|
|
5142
5685
|
if (changed) {
|
|
5143
5686
|
writeFileSync7(specPath, JSON.stringify(spec, null, 2) + "\n");
|
|
5144
5687
|
console.log();
|
|
@@ -5247,18 +5790,18 @@ function runSimulatedSession(spec, diagnosis) {
|
|
|
5247
5790
|
// src/commands/growth.ts
|
|
5248
5791
|
import chalk15 from "chalk";
|
|
5249
5792
|
import figures8 from "figures";
|
|
5250
|
-
import { readdirSync, existsSync as
|
|
5251
|
-
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";
|
|
5252
5795
|
|
|
5253
5796
|
// src/analysis/evolution-history.ts
|
|
5254
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as
|
|
5797
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
|
|
5255
5798
|
import { resolve as resolve10 } from "path";
|
|
5256
5799
|
function getEvolutionPath() {
|
|
5257
5800
|
return resolve10(process.cwd(), ".holomime", "evolution.json");
|
|
5258
5801
|
}
|
|
5259
5802
|
function loadEvolution(agentName) {
|
|
5260
5803
|
const filepath = getEvolutionPath();
|
|
5261
|
-
if (!
|
|
5804
|
+
if (!existsSync6(filepath)) return null;
|
|
5262
5805
|
try {
|
|
5263
5806
|
const raw = readFileSync7(filepath, "utf-8");
|
|
5264
5807
|
return JSON.parse(raw);
|
|
@@ -5269,8 +5812,8 @@ function loadEvolution(agentName) {
|
|
|
5269
5812
|
function appendEvolution(entry, agentName) {
|
|
5270
5813
|
const filepath = getEvolutionPath();
|
|
5271
5814
|
const dir = resolve10(process.cwd(), ".holomime");
|
|
5272
|
-
if (!
|
|
5273
|
-
|
|
5815
|
+
if (!existsSync6(dir)) {
|
|
5816
|
+
mkdirSync5(dir, { recursive: true });
|
|
5274
5817
|
}
|
|
5275
5818
|
let history = loadEvolution(agentName);
|
|
5276
5819
|
if (!history) {
|
|
@@ -5337,7 +5880,7 @@ async function growthCommand(options) {
|
|
|
5337
5880
|
}
|
|
5338
5881
|
const historyDir = resolve11(process.cwd(), options.history ?? ".holomime/assessments");
|
|
5339
5882
|
printHeader(`Growth Report \u2014 ${spec.name ?? "Unknown"}`);
|
|
5340
|
-
if (!
|
|
5883
|
+
if (!existsSync7(historyDir)) {
|
|
5341
5884
|
console.log(chalk15.dim(" No assessment history found."));
|
|
5342
5885
|
console.log(chalk15.dim(` Run assessments first: holomime assess --personality ${options.personality} --log <conversation.json>`));
|
|
5343
5886
|
console.log();
|
|
@@ -5358,7 +5901,7 @@ async function growthCommand(options) {
|
|
|
5358
5901
|
const snapshots = [];
|
|
5359
5902
|
for (const file of files) {
|
|
5360
5903
|
try {
|
|
5361
|
-
const data = JSON.parse(
|
|
5904
|
+
const data = JSON.parse(readFileSync8(join6(historyDir, file), "utf-8"));
|
|
5362
5905
|
snapshots.push(data);
|
|
5363
5906
|
} catch {
|
|
5364
5907
|
}
|
|
@@ -5552,7 +6095,7 @@ async function browseCommand(options) {
|
|
|
5552
6095
|
// src/commands/pull.ts
|
|
5553
6096
|
import chalk17 from "chalk";
|
|
5554
6097
|
import figures9 from "figures";
|
|
5555
|
-
import { writeFileSync as writeFileSync9, existsSync as
|
|
6098
|
+
import { writeFileSync as writeFileSync9, existsSync as existsSync8 } from "fs";
|
|
5556
6099
|
import { resolve as resolve12 } from "path";
|
|
5557
6100
|
import { select as select2 } from "@inquirer/prompts";
|
|
5558
6101
|
async function pullCommand(handle, options) {
|
|
@@ -5590,7 +6133,7 @@ async function pullCommand(handle, options) {
|
|
|
5590
6133
|
return;
|
|
5591
6134
|
}
|
|
5592
6135
|
const outputPath = resolve12(process.cwd(), options.output ?? ".personality.json");
|
|
5593
|
-
if (
|
|
6136
|
+
if (existsSync8(outputPath)) {
|
|
5594
6137
|
const overwrite = await select2({
|
|
5595
6138
|
message: `.personality.json already exists. Overwrite?`,
|
|
5596
6139
|
choices: [
|
|
@@ -5614,14 +6157,14 @@ async function pullCommand(handle, options) {
|
|
|
5614
6157
|
// src/commands/publish.ts
|
|
5615
6158
|
import chalk18 from "chalk";
|
|
5616
6159
|
import figures10 from "figures";
|
|
5617
|
-
import { readFileSync as
|
|
6160
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
5618
6161
|
import { resolve as resolve13 } from "path";
|
|
5619
6162
|
async function publishCommand(options) {
|
|
5620
6163
|
const specPath = resolve13(process.cwd(), options.personality ?? ".personality.json");
|
|
5621
6164
|
printHeader("Publish Personality");
|
|
5622
6165
|
let raw;
|
|
5623
6166
|
try {
|
|
5624
|
-
raw =
|
|
6167
|
+
raw = readFileSync9(specPath, "utf-8");
|
|
5625
6168
|
} catch {
|
|
5626
6169
|
console.error(chalk18.red(` Could not read: ${specPath}`));
|
|
5627
6170
|
console.log(chalk18.dim(` Run ${chalk18.cyan("holomime init")} to create a personality first.`));
|
|
@@ -5691,7 +6234,7 @@ async function publishCommand(options) {
|
|
|
5691
6234
|
// src/commands/autopilot.ts
|
|
5692
6235
|
import chalk19 from "chalk";
|
|
5693
6236
|
import figures11 from "figures";
|
|
5694
|
-
import { readFileSync as
|
|
6237
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
5695
6238
|
import { resolve as resolve14 } from "path";
|
|
5696
6239
|
|
|
5697
6240
|
// src/analysis/autopilot-core.ts
|
|
@@ -5730,7 +6273,7 @@ async function runAutopilot(spec, messages, provider, options) {
|
|
|
5730
6273
|
callbacks: options?.callbacks
|
|
5731
6274
|
});
|
|
5732
6275
|
const specCopy = JSON.parse(JSON.stringify(spec));
|
|
5733
|
-
const { changed, changes } = applyRecommendations(specCopy, diagnosis);
|
|
6276
|
+
const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
|
|
5734
6277
|
if (changed && options?.specPath) {
|
|
5735
6278
|
writeFileSync10(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
|
|
5736
6279
|
}
|
|
@@ -5761,7 +6304,7 @@ async function autopilotCommand(options) {
|
|
|
5761
6304
|
const logPath = resolve14(process.cwd(), options.log);
|
|
5762
6305
|
let messages;
|
|
5763
6306
|
try {
|
|
5764
|
-
const raw = JSON.parse(
|
|
6307
|
+
const raw = JSON.parse(readFileSync10(logPath, "utf-8"));
|
|
5765
6308
|
const conversations = parseConversationLog(raw, options.format ?? "auto");
|
|
5766
6309
|
messages = conversations.flatMap((c) => c.messages);
|
|
5767
6310
|
} catch (err) {
|
|
@@ -5906,8 +6449,8 @@ import { writeFileSync as writeFileSync11 } from "fs";
|
|
|
5906
6449
|
import { resolve as resolve15 } from "path";
|
|
5907
6450
|
|
|
5908
6451
|
// src/analysis/training-export.ts
|
|
5909
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
5910
|
-
import { join as
|
|
6452
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync11 } from "fs";
|
|
6453
|
+
import { join as join7 } from "path";
|
|
5911
6454
|
function extractDPOPairs(transcript) {
|
|
5912
6455
|
const pairs = [];
|
|
5913
6456
|
const turns = transcript.turns;
|
|
@@ -6013,7 +6556,7 @@ function loadTranscripts(sessionsDir) {
|
|
|
6013
6556
|
try {
|
|
6014
6557
|
const files = readdirSync2(sessionsDir).filter((f) => f.endsWith(".json")).sort();
|
|
6015
6558
|
return files.map((f) => {
|
|
6016
|
-
const raw =
|
|
6559
|
+
const raw = readFileSync11(join7(sessionsDir, f), "utf-8");
|
|
6017
6560
|
return JSON.parse(raw);
|
|
6018
6561
|
});
|
|
6019
6562
|
} catch {
|
|
@@ -6104,6 +6647,62 @@ function extractInstructionFromTherapist(content) {
|
|
|
6104
6647
|
);
|
|
6105
6648
|
return actionable?.trim() ?? sentences[0]?.trim() ?? content;
|
|
6106
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
|
+
}
|
|
6107
6706
|
|
|
6108
6707
|
// src/analysis/export-huggingface.ts
|
|
6109
6708
|
function convertToHFFormat(data) {
|
|
@@ -6249,8 +6848,8 @@ Run ${chalk20.cyan("holomime session")} first to generate session transcripts.`,
|
|
|
6249
6848
|
const outputPath = options.output ?? `.holomime/exports/${format}-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.${isJsonl ? "jsonl" : "json"}`;
|
|
6250
6849
|
const fullPath = resolve15(process.cwd(), outputPath);
|
|
6251
6850
|
const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
6252
|
-
const { mkdirSync:
|
|
6253
|
-
|
|
6851
|
+
const { mkdirSync: mkdirSync16 } = await import("fs");
|
|
6852
|
+
mkdirSync16(dir, { recursive: true });
|
|
6254
6853
|
if (format === "huggingface" || format === "openai") {
|
|
6255
6854
|
const jsonl = convertToHFFormat(result);
|
|
6256
6855
|
writeFileSync11(fullPath, jsonl);
|
|
@@ -6343,8 +6942,8 @@ This is the closed-loop behavioral alignment system.`,
|
|
|
6343
6942
|
// src/commands/train.ts
|
|
6344
6943
|
import chalk21 from "chalk";
|
|
6345
6944
|
import figures13 from "figures";
|
|
6346
|
-
import { readFileSync as
|
|
6347
|
-
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";
|
|
6348
6947
|
|
|
6349
6948
|
// src/analysis/train-provider.ts
|
|
6350
6949
|
function inferMethod(data, preferred) {
|
|
@@ -6353,7 +6952,7 @@ function inferMethod(data, preferred) {
|
|
|
6353
6952
|
}
|
|
6354
6953
|
|
|
6355
6954
|
// src/analysis/train-openai.ts
|
|
6356
|
-
import { readFileSync as
|
|
6955
|
+
import { readFileSync as readFileSync12, existsSync as existsSync9 } from "fs";
|
|
6357
6956
|
import { resolve as resolve16 } from "path";
|
|
6358
6957
|
var OPENAI_API = "https://api.openai.com/v1";
|
|
6359
6958
|
var POLL_INTERVAL_MS = 1e4;
|
|
@@ -6453,7 +7052,7 @@ async function getJobEvents(apiKey, jobId, limit = 5) {
|
|
|
6453
7052
|
return data.data ?? [];
|
|
6454
7053
|
}
|
|
6455
7054
|
function sleep(ms) {
|
|
6456
|
-
return new Promise((
|
|
7055
|
+
return new Promise((resolve34) => setTimeout(resolve34, ms));
|
|
6457
7056
|
}
|
|
6458
7057
|
var OpenAITrainProvider = class {
|
|
6459
7058
|
name = "openai";
|
|
@@ -6464,9 +7063,9 @@ var OpenAITrainProvider = class {
|
|
|
6464
7063
|
let systemPrompt;
|
|
6465
7064
|
if (options.personalityPath) {
|
|
6466
7065
|
const fullPath = resolve16(process.cwd(), options.personalityPath);
|
|
6467
|
-
if (
|
|
7066
|
+
if (existsSync9(fullPath)) {
|
|
6468
7067
|
try {
|
|
6469
|
-
const spec = JSON.parse(
|
|
7068
|
+
const spec = JSON.parse(readFileSync12(fullPath, "utf-8"));
|
|
6470
7069
|
const name = spec.name ?? "Agent";
|
|
6471
7070
|
const purpose = spec.purpose ?? "";
|
|
6472
7071
|
const parts = [`You are ${name}.`];
|
|
@@ -6555,8 +7154,8 @@ var OpenAITrainProvider = class {
|
|
|
6555
7154
|
|
|
6556
7155
|
// src/analysis/train-huggingface.ts
|
|
6557
7156
|
import { spawn } from "child_process";
|
|
6558
|
-
import { writeFileSync as writeFileSync12, mkdirSync as
|
|
6559
|
-
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";
|
|
6560
7159
|
import { createInterface as createInterface2 } from "readline";
|
|
6561
7160
|
async function checkPythonDeps() {
|
|
6562
7161
|
const pythonCandidates = ["python3", "python"];
|
|
@@ -6586,8 +7185,8 @@ async function checkPythonDeps() {
|
|
|
6586
7185
|
}
|
|
6587
7186
|
function writeHFTrainingFile(data) {
|
|
6588
7187
|
const tmpDir = resolve17(process.cwd(), ".holomime/tmp");
|
|
6589
|
-
|
|
6590
|
-
const filePath =
|
|
7188
|
+
mkdirSync6(tmpDir, { recursive: true });
|
|
7189
|
+
const filePath = join8(tmpDir, `hf-train-${Date.now()}.json`);
|
|
6591
7190
|
writeFileSync12(filePath, JSON.stringify(data, null, 2));
|
|
6592
7191
|
return filePath;
|
|
6593
7192
|
}
|
|
@@ -6707,8 +7306,8 @@ var HuggingFaceTrainProvider = class {
|
|
|
6707
7306
|
|
|
6708
7307
|
// src/analysis/train-eval.ts
|
|
6709
7308
|
import { spawn as spawn2 } from "child_process";
|
|
6710
|
-
import { writeFileSync as writeFileSync13, mkdirSync as
|
|
6711
|
-
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";
|
|
6712
7311
|
import { createInterface as createInterface3 } from "readline";
|
|
6713
7312
|
|
|
6714
7313
|
// src/analysis/diagnose-core.ts
|
|
@@ -6724,16 +7323,31 @@ function runDiagnosis(messages) {
|
|
|
6724
7323
|
];
|
|
6725
7324
|
const detected = [];
|
|
6726
7325
|
for (const detector of detectors) {
|
|
6727
|
-
const
|
|
6728
|
-
if (
|
|
7326
|
+
const result2 = detector(messages);
|
|
7327
|
+
if (result2) detected.push(result2);
|
|
6729
7328
|
}
|
|
6730
|
-
|
|
7329
|
+
const result = {
|
|
6731
7330
|
messagesAnalyzed: messages.length,
|
|
6732
7331
|
assistantResponses: messages.filter((m) => m.role === "assistant").length,
|
|
6733
7332
|
patterns: detected.filter((p) => p.severity !== "info"),
|
|
6734
7333
|
healthy: detected.filter((p) => p.severity === "info"),
|
|
6735
7334
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6736
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;
|
|
6737
7351
|
}
|
|
6738
7352
|
|
|
6739
7353
|
// src/analysis/outcome-eval.ts
|
|
@@ -6956,8 +7570,8 @@ async function runHFAutoEval(baseModel, fineTunedModel, agentName, data, onProgr
|
|
|
6956
7570
|
return evaluateOutcome(agentName, [], []);
|
|
6957
7571
|
}
|
|
6958
7572
|
const tmpDir = resolve18(process.cwd(), ".holomime/tmp");
|
|
6959
|
-
|
|
6960
|
-
const promptsPath =
|
|
7573
|
+
mkdirSync7(tmpDir, { recursive: true });
|
|
7574
|
+
const promptsPath = join9(tmpDir, `eval-prompts-${Date.now()}.json`);
|
|
6961
7575
|
writeFileSync13(promptsPath, JSON.stringify(prompts));
|
|
6962
7576
|
const scriptPath = resolve18(
|
|
6963
7577
|
new URL(".", import.meta.url).pathname,
|
|
@@ -7024,13 +7638,13 @@ async function findPython() {
|
|
|
7024
7638
|
function findLatestExport(exportsDir) {
|
|
7025
7639
|
try {
|
|
7026
7640
|
const files = readdirSync3(exportsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort().reverse();
|
|
7027
|
-
return files[0] ?
|
|
7641
|
+
return files[0] ? join10(exportsDir, files[0]) : null;
|
|
7028
7642
|
} catch {
|
|
7029
7643
|
return null;
|
|
7030
7644
|
}
|
|
7031
7645
|
}
|
|
7032
7646
|
function loadTrainingData(dataPath) {
|
|
7033
|
-
const raw =
|
|
7647
|
+
const raw = readFileSync13(dataPath, "utf-8");
|
|
7034
7648
|
const data = JSON.parse(raw);
|
|
7035
7649
|
if (!data.format || !data.examples || !Array.isArray(data.examples)) {
|
|
7036
7650
|
throw new Error("Invalid training data \u2014 expected holomime export format");
|
|
@@ -7039,7 +7653,7 @@ function loadTrainingData(dataPath) {
|
|
|
7039
7653
|
}
|
|
7040
7654
|
function getAgentName(personalityPath) {
|
|
7041
7655
|
try {
|
|
7042
|
-
const spec = JSON.parse(
|
|
7656
|
+
const spec = JSON.parse(readFileSync13(personalityPath, "utf-8"));
|
|
7043
7657
|
return spec.name ?? "Agent";
|
|
7044
7658
|
} catch {
|
|
7045
7659
|
return "Agent";
|
|
@@ -7047,19 +7661,19 @@ function getAgentName(personalityPath) {
|
|
|
7047
7661
|
}
|
|
7048
7662
|
function deployModel(result, personalityPath) {
|
|
7049
7663
|
const trainingDir = resolve19(process.cwd(), ".holomime/training");
|
|
7050
|
-
|
|
7664
|
+
mkdirSync8(trainingDir, { recursive: true });
|
|
7051
7665
|
const record = {
|
|
7052
7666
|
...result,
|
|
7053
7667
|
deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7054
7668
|
personalityPath
|
|
7055
7669
|
};
|
|
7056
7670
|
writeFileSync14(
|
|
7057
|
-
|
|
7671
|
+
join10(trainingDir, "latest.json"),
|
|
7058
7672
|
JSON.stringify(record, null, 2) + "\n"
|
|
7059
7673
|
);
|
|
7060
7674
|
const fullPath = resolve19(process.cwd(), personalityPath);
|
|
7061
|
-
if (
|
|
7062
|
-
const spec = JSON.parse(
|
|
7675
|
+
if (existsSync10(fullPath)) {
|
|
7676
|
+
const spec = JSON.parse(readFileSync13(fullPath, "utf-8"));
|
|
7063
7677
|
spec.training = {
|
|
7064
7678
|
...spec.training ?? {},
|
|
7065
7679
|
fine_tuned_model: result.modelId,
|
|
@@ -7092,7 +7706,7 @@ async function trainCommand(options) {
|
|
|
7092
7706
|
}
|
|
7093
7707
|
}
|
|
7094
7708
|
const dataPath = options.data ? resolve19(process.cwd(), options.data) : findLatestExport(resolve19(process.cwd(), ".holomime/exports"));
|
|
7095
|
-
if (!dataPath || !
|
|
7709
|
+
if (!dataPath || !existsSync10(dataPath)) {
|
|
7096
7710
|
console.log();
|
|
7097
7711
|
printBox(
|
|
7098
7712
|
`No training data found.
|
|
@@ -7310,14 +7924,14 @@ Fine-tuned model: ${chalk21.cyan(result.modelId)}`,
|
|
|
7310
7924
|
// src/commands/eval.ts
|
|
7311
7925
|
import chalk22 from "chalk";
|
|
7312
7926
|
import figures14 from "figures";
|
|
7313
|
-
import { readFileSync as
|
|
7927
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
7314
7928
|
import { resolve as resolve20 } from "path";
|
|
7315
7929
|
async function evalCommand(options) {
|
|
7316
7930
|
printHeader("Outcome Evaluation");
|
|
7317
7931
|
const beforePath = resolve20(process.cwd(), options.before);
|
|
7318
7932
|
let beforeMessages;
|
|
7319
7933
|
try {
|
|
7320
|
-
const raw = JSON.parse(
|
|
7934
|
+
const raw = JSON.parse(readFileSync14(beforePath, "utf-8"));
|
|
7321
7935
|
const conversations = parseConversationLog(raw, options.format ?? "auto");
|
|
7322
7936
|
beforeMessages = conversations.flatMap((c) => c.messages);
|
|
7323
7937
|
} catch (err) {
|
|
@@ -7328,7 +7942,7 @@ async function evalCommand(options) {
|
|
|
7328
7942
|
const afterPath = resolve20(process.cwd(), options.after);
|
|
7329
7943
|
let afterMessages;
|
|
7330
7944
|
try {
|
|
7331
|
-
const raw = JSON.parse(
|
|
7945
|
+
const raw = JSON.parse(readFileSync14(afterPath, "utf-8"));
|
|
7332
7946
|
const conversations = parseConversationLog(raw, options.format ?? "auto");
|
|
7333
7947
|
afterMessages = conversations.flatMap((c) => c.messages);
|
|
7334
7948
|
} catch (err) {
|
|
@@ -7339,7 +7953,7 @@ async function evalCommand(options) {
|
|
|
7339
7953
|
let agentName = "Agent";
|
|
7340
7954
|
if (options.personality) {
|
|
7341
7955
|
try {
|
|
7342
|
-
const spec = JSON.parse(
|
|
7956
|
+
const spec = JSON.parse(readFileSync14(resolve20(process.cwd(), options.personality), "utf-8"));
|
|
7343
7957
|
agentName = spec.name ?? "Agent";
|
|
7344
7958
|
} catch {
|
|
7345
7959
|
}
|
|
@@ -7420,7 +8034,7 @@ Grade: ${colorize(report.grade)}`,
|
|
|
7420
8034
|
// src/commands/evolve.ts
|
|
7421
8035
|
import chalk23 from "chalk";
|
|
7422
8036
|
import figures15 from "figures";
|
|
7423
|
-
import { readFileSync as
|
|
8037
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
7424
8038
|
import { resolve as resolve21 } from "path";
|
|
7425
8039
|
|
|
7426
8040
|
// src/analysis/evolve-core.ts
|
|
@@ -7465,7 +8079,6 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
7465
8079
|
finalHealth: 50
|
|
7466
8080
|
};
|
|
7467
8081
|
}
|
|
7468
|
-
const beforeDiagnosis = runDiagnosis(messages);
|
|
7469
8082
|
for (let i = 1; i <= maxIterations; i++) {
|
|
7470
8083
|
cb?.onIterationStart?.(i, maxIterations);
|
|
7471
8084
|
const transcript = await runTherapySession(
|
|
@@ -7482,16 +8095,21 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
7482
8095
|
}
|
|
7483
8096
|
}
|
|
7484
8097
|
);
|
|
7485
|
-
const { changes } = applyRecommendations(currentSpec, diagnosis);
|
|
7486
|
-
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);
|
|
7487
8107
|
allDPOPairs.push(...dpoPairs);
|
|
7488
8108
|
cb?.onExportedPairs?.(dpoPairs.length);
|
|
7489
|
-
const afterDiagnosis = runDiagnosis(messages);
|
|
7490
8109
|
const evaluation = evaluateOutcome(
|
|
7491
8110
|
currentSpec.name ?? "Agent",
|
|
7492
8111
|
messages,
|
|
7493
|
-
|
|
7494
|
-
// Same messages, but spec has been updated — patterns shift
|
|
8112
|
+
afterMessages
|
|
7495
8113
|
);
|
|
7496
8114
|
const health = evaluation.treatmentEfficacyScore;
|
|
7497
8115
|
const grade = evaluation.grade;
|
|
@@ -7528,6 +8146,7 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
7528
8146
|
cb?.onConverged?.(i, health);
|
|
7529
8147
|
break;
|
|
7530
8148
|
}
|
|
8149
|
+
messages = afterMessages;
|
|
7531
8150
|
diagnosis = runPreSessionDiagnosis(messages, currentSpec);
|
|
7532
8151
|
const actionablePatterns = diagnosis.patterns.filter((p) => p.severity !== "info");
|
|
7533
8152
|
if (actionablePatterns.length === 0) {
|
|
@@ -7552,6 +8171,21 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
7552
8171
|
writeFileSync15(options.exportDpoPath, JSON.stringify(trainingExport, null, 2) + "\n");
|
|
7553
8172
|
}
|
|
7554
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
|
+
}
|
|
7555
8189
|
return {
|
|
7556
8190
|
iterations,
|
|
7557
8191
|
totalDPOPairs: allDPOPairs.length,
|
|
@@ -7563,6 +8197,69 @@ async function runEvolve(spec, messages, provider, options) {
|
|
|
7563
8197
|
updatedSpec: currentSpec
|
|
7564
8198
|
};
|
|
7565
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
|
+
}
|
|
7566
8263
|
|
|
7567
8264
|
// src/commands/evolve.ts
|
|
7568
8265
|
async function evolveCommand(options) {
|
|
@@ -7578,7 +8275,7 @@ async function evolveCommand(options) {
|
|
|
7578
8275
|
const logPath = resolve21(process.cwd(), options.log);
|
|
7579
8276
|
let messages;
|
|
7580
8277
|
try {
|
|
7581
|
-
const raw = JSON.parse(
|
|
8278
|
+
const raw = JSON.parse(readFileSync15(logPath, "utf-8"));
|
|
7582
8279
|
const conversations = parseConversationLog(raw, options.format ?? "auto");
|
|
7583
8280
|
messages = conversations.flatMap((c) => c.messages);
|
|
7584
8281
|
} catch (err) {
|
|
@@ -7763,7 +8460,7 @@ async function evolveCommand(options) {
|
|
|
7763
8460
|
// src/commands/benchmark.ts
|
|
7764
8461
|
import chalk24 from "chalk";
|
|
7765
8462
|
import figures16 from "figures";
|
|
7766
|
-
import { readFileSync as
|
|
8463
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
7767
8464
|
import { resolve as resolve22 } from "path";
|
|
7768
8465
|
|
|
7769
8466
|
// src/analysis/benchmark-scenarios.ts
|
|
@@ -7961,13 +8658,13 @@ function gradeFromScore2(score) {
|
|
|
7961
8658
|
}
|
|
7962
8659
|
|
|
7963
8660
|
// src/analysis/benchmark-publish.ts
|
|
7964
|
-
import { readFileSync as
|
|
7965
|
-
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";
|
|
7966
8663
|
import { homedir as homedir2 } from "os";
|
|
7967
8664
|
function getBenchmarkDir(outputDir) {
|
|
7968
|
-
const dir = outputDir ??
|
|
7969
|
-
if (!
|
|
7970
|
-
|
|
8665
|
+
const dir = outputDir ?? join11(homedir2(), ".holomime", "benchmarks");
|
|
8666
|
+
if (!existsSync11(dir)) {
|
|
8667
|
+
mkdirSync9(dir, { recursive: true });
|
|
7971
8668
|
}
|
|
7972
8669
|
return dir;
|
|
7973
8670
|
}
|
|
@@ -7978,7 +8675,7 @@ function saveBenchmarkResult(report, outputDir) {
|
|
|
7978
8675
|
const dir = getBenchmarkDir(outputDir);
|
|
7979
8676
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7980
8677
|
const filename = `${sanitize(report.provider)}-${sanitize(report.model)}-${date}.json`;
|
|
7981
|
-
const filepath =
|
|
8678
|
+
const filepath = join11(dir, filename);
|
|
7982
8679
|
const published = {
|
|
7983
8680
|
agent: report.agent,
|
|
7984
8681
|
provider: report.provider,
|
|
@@ -7997,12 +8694,12 @@ function saveBenchmarkResult(report, outputDir) {
|
|
|
7997
8694
|
}
|
|
7998
8695
|
function loadBenchmarkResults(dir) {
|
|
7999
8696
|
const benchmarkDir = getBenchmarkDir(dir);
|
|
8000
|
-
if (!
|
|
8697
|
+
if (!existsSync11(benchmarkDir)) return [];
|
|
8001
8698
|
const files = readdirSync4(benchmarkDir).filter((f) => f.endsWith(".json"));
|
|
8002
8699
|
const results = [];
|
|
8003
8700
|
for (const file of files) {
|
|
8004
8701
|
try {
|
|
8005
|
-
const content =
|
|
8702
|
+
const content = readFileSync16(join11(benchmarkDir, file), "utf-8");
|
|
8006
8703
|
results.push(JSON.parse(content));
|
|
8007
8704
|
} catch {
|
|
8008
8705
|
}
|
|
@@ -8157,7 +8854,7 @@ async function benchmarkCommand(options) {
|
|
|
8157
8854
|
}
|
|
8158
8855
|
if (options.compare) {
|
|
8159
8856
|
try {
|
|
8160
|
-
const baseline = JSON.parse(
|
|
8857
|
+
const baseline = JSON.parse(readFileSync17(resolve22(process.cwd(), options.compare), "utf-8"));
|
|
8161
8858
|
const comparison = compareBenchmarks(baseline, {
|
|
8162
8859
|
agent: report.agent,
|
|
8163
8860
|
provider: report.provider,
|
|
@@ -8216,12 +8913,12 @@ async function benchmarkCommand(options) {
|
|
|
8216
8913
|
// src/commands/watch.ts
|
|
8217
8914
|
import chalk25 from "chalk";
|
|
8218
8915
|
import figures17 from "figures";
|
|
8219
|
-
import { existsSync as
|
|
8916
|
+
import { existsSync as existsSync13 } from "fs";
|
|
8220
8917
|
import { resolve as resolve24 } from "path";
|
|
8221
8918
|
|
|
8222
8919
|
// src/analysis/watch-core.ts
|
|
8223
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
8224
|
-
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";
|
|
8225
8922
|
var SEVERITY_ORDER2 = ["routine", "targeted", "intervention"];
|
|
8226
8923
|
function severityMeetsThreshold2(severity, threshold) {
|
|
8227
8924
|
const severityIdx = SEVERITY_ORDER2.indexOf(severity);
|
|
@@ -8237,7 +8934,7 @@ function startWatch(spec, options) {
|
|
|
8237
8934
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
8238
8935
|
let stopped = false;
|
|
8239
8936
|
let currentSpec = JSON.parse(JSON.stringify(spec));
|
|
8240
|
-
if (
|
|
8937
|
+
if (existsSync12(options.watchDir)) {
|
|
8241
8938
|
const existing = readdirSync5(options.watchDir).filter((f) => f.endsWith(".json")).sort();
|
|
8242
8939
|
for (const f of existing) {
|
|
8243
8940
|
seenFiles.add(f);
|
|
@@ -8245,7 +8942,7 @@ function startWatch(spec, options) {
|
|
|
8245
8942
|
}
|
|
8246
8943
|
async function scan() {
|
|
8247
8944
|
if (stopped) return;
|
|
8248
|
-
if (!
|
|
8945
|
+
if (!existsSync12(options.watchDir)) {
|
|
8249
8946
|
return;
|
|
8250
8947
|
}
|
|
8251
8948
|
const files = readdirSync5(options.watchDir).filter((f) => f.endsWith(".json")).sort();
|
|
@@ -8259,7 +8956,7 @@ function startWatch(spec, options) {
|
|
|
8259
8956
|
events.push({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "new_file", filename });
|
|
8260
8957
|
let messages;
|
|
8261
8958
|
try {
|
|
8262
|
-
const raw = JSON.parse(
|
|
8959
|
+
const raw = JSON.parse(readFileSync18(join12(options.watchDir, filename), "utf-8"));
|
|
8263
8960
|
const conversations = parseConversationLog(raw, "auto");
|
|
8264
8961
|
messages = conversations.flatMap((c) => c.messages);
|
|
8265
8962
|
} catch (err) {
|
|
@@ -8319,11 +9016,11 @@ function startWatch(spec, options) {
|
|
|
8319
9016
|
stopped = true;
|
|
8320
9017
|
clearInterval(interval);
|
|
8321
9018
|
const logDir = resolve23(process.cwd(), ".holomime");
|
|
8322
|
-
if (!
|
|
8323
|
-
|
|
9019
|
+
if (!existsSync12(logDir)) {
|
|
9020
|
+
mkdirSync10(logDir, { recursive: true });
|
|
8324
9021
|
}
|
|
8325
9022
|
writeFileSync17(
|
|
8326
|
-
|
|
9023
|
+
join12(logDir, "watch-log.json"),
|
|
8327
9024
|
JSON.stringify({ events, stoppedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n"
|
|
8328
9025
|
);
|
|
8329
9026
|
}
|
|
@@ -8342,7 +9039,7 @@ async function watchCommand(options) {
|
|
|
8342
9039
|
return;
|
|
8343
9040
|
}
|
|
8344
9041
|
const watchDir = resolve24(process.cwd(), options.dir);
|
|
8345
|
-
if (!
|
|
9042
|
+
if (!existsSync13(watchDir)) {
|
|
8346
9043
|
console.error(chalk25.red(` Watch directory does not exist: ${options.dir}`));
|
|
8347
9044
|
process.exit(1);
|
|
8348
9045
|
return;
|
|
@@ -8466,12 +9163,12 @@ Press ${chalk25.cyan("Ctrl+C")} to stop.`,
|
|
|
8466
9163
|
// src/commands/certify.ts
|
|
8467
9164
|
import chalk26 from "chalk";
|
|
8468
9165
|
import figures18 from "figures";
|
|
8469
|
-
import { readFileSync as
|
|
9166
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
8470
9167
|
import { resolve as resolve26 } from "path";
|
|
8471
9168
|
|
|
8472
9169
|
// src/analysis/certify-core.ts
|
|
8473
|
-
import { writeFileSync as writeFileSync18, mkdirSync as
|
|
8474
|
-
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";
|
|
8475
9172
|
function djb2Hash(str) {
|
|
8476
9173
|
let hash = 0;
|
|
8477
9174
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -8585,12 +9282,12 @@ function verifyCredential(credential, spec) {
|
|
|
8585
9282
|
}
|
|
8586
9283
|
function saveCredential(credential, outputDir) {
|
|
8587
9284
|
const dir = outputDir ?? resolve25(process.cwd(), ".holomime", "credentials");
|
|
8588
|
-
if (!
|
|
8589
|
-
|
|
9285
|
+
if (!existsSync14(dir)) {
|
|
9286
|
+
mkdirSync11(dir, { recursive: true });
|
|
8590
9287
|
}
|
|
8591
9288
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
8592
9289
|
const filename = `${credential.agent.handle}-${date}.json`;
|
|
8593
|
-
const filepath =
|
|
9290
|
+
const filepath = join13(dir, filename);
|
|
8594
9291
|
writeFileSync18(filepath, JSON.stringify(credential, null, 2) + "\n");
|
|
8595
9292
|
return filepath;
|
|
8596
9293
|
}
|
|
@@ -8602,7 +9299,7 @@ async function certifyCommand(options) {
|
|
|
8602
9299
|
const credPath = resolve26(process.cwd(), options.verify);
|
|
8603
9300
|
let credential2;
|
|
8604
9301
|
try {
|
|
8605
|
-
credential2 = JSON.parse(
|
|
9302
|
+
credential2 = JSON.parse(readFileSync20(credPath, "utf-8"));
|
|
8606
9303
|
} catch {
|
|
8607
9304
|
console.error(chalk26.red(` Could not read credential file: ${options.verify}`));
|
|
8608
9305
|
process.exit(1);
|
|
@@ -8661,7 +9358,7 @@ async function certifyCommand(options) {
|
|
|
8661
9358
|
let benchmarkReport;
|
|
8662
9359
|
if (options.benchmark) {
|
|
8663
9360
|
try {
|
|
8664
|
-
benchmarkReport = JSON.parse(
|
|
9361
|
+
benchmarkReport = JSON.parse(readFileSync20(resolve26(process.cwd(), options.benchmark), "utf-8"));
|
|
8665
9362
|
} catch {
|
|
8666
9363
|
console.error(chalk26.red(` Could not read benchmark report: ${options.benchmark}`));
|
|
8667
9364
|
process.exit(1);
|
|
@@ -8671,7 +9368,7 @@ async function certifyCommand(options) {
|
|
|
8671
9368
|
let evolveResult;
|
|
8672
9369
|
if (options.evolve) {
|
|
8673
9370
|
try {
|
|
8674
|
-
evolveResult = JSON.parse(
|
|
9371
|
+
evolveResult = JSON.parse(readFileSync20(resolve26(process.cwd(), options.evolve), "utf-8"));
|
|
8675
9372
|
} catch {
|
|
8676
9373
|
console.error(chalk26.red(` Could not read evolve result: ${options.evolve}`));
|
|
8677
9374
|
process.exit(1);
|
|
@@ -8711,32 +9408,32 @@ async function certifyCommand(options) {
|
|
|
8711
9408
|
|
|
8712
9409
|
// src/commands/daemon.ts
|
|
8713
9410
|
import chalk27 from "chalk";
|
|
8714
|
-
import { writeFileSync as writeFileSync19, readFileSync as
|
|
9411
|
+
import { writeFileSync as writeFileSync19, readFileSync as readFileSync21, mkdirSync as mkdirSync12, existsSync as existsSync15 } from "fs";
|
|
8715
9412
|
import { resolve as resolve27 } from "path";
|
|
8716
|
-
var
|
|
9413
|
+
var HOLOMIME_DIR2 = ".holomime";
|
|
8717
9414
|
function getDaemonStatePath() {
|
|
8718
|
-
return resolve27(process.cwd(),
|
|
9415
|
+
return resolve27(process.cwd(), HOLOMIME_DIR2, "daemon.json");
|
|
8719
9416
|
}
|
|
8720
9417
|
function getDaemonLogPath() {
|
|
8721
|
-
return resolve27(process.cwd(),
|
|
9418
|
+
return resolve27(process.cwd(), HOLOMIME_DIR2, "daemon-log.json");
|
|
8722
9419
|
}
|
|
8723
|
-
function
|
|
8724
|
-
const dir = resolve27(process.cwd(),
|
|
8725
|
-
if (!
|
|
8726
|
-
|
|
9420
|
+
function ensureDir2() {
|
|
9421
|
+
const dir = resolve27(process.cwd(), HOLOMIME_DIR2);
|
|
9422
|
+
if (!existsSync15(dir)) {
|
|
9423
|
+
mkdirSync12(dir, { recursive: true });
|
|
8727
9424
|
}
|
|
8728
9425
|
}
|
|
8729
9426
|
function writeDaemonState(state) {
|
|
8730
|
-
|
|
9427
|
+
ensureDir2();
|
|
8731
9428
|
writeFileSync19(getDaemonStatePath(), JSON.stringify(state, null, 2) + "\n");
|
|
8732
9429
|
}
|
|
8733
9430
|
function appendDaemonLog(event) {
|
|
8734
|
-
|
|
9431
|
+
ensureDir2();
|
|
8735
9432
|
const logPath = getDaemonLogPath();
|
|
8736
9433
|
let log = [];
|
|
8737
9434
|
try {
|
|
8738
|
-
if (
|
|
8739
|
-
log = JSON.parse(
|
|
9435
|
+
if (existsSync15(logPath)) {
|
|
9436
|
+
log = JSON.parse(readFileSync21(logPath, "utf-8"));
|
|
8740
9437
|
}
|
|
8741
9438
|
} catch {
|
|
8742
9439
|
log = [];
|
|
@@ -8903,14 +9600,14 @@ Log: ${getDaemonLogPath()}`,
|
|
|
8903
9600
|
// src/commands/fleet.ts
|
|
8904
9601
|
import chalk28 from "chalk";
|
|
8905
9602
|
import figures19 from "figures";
|
|
8906
|
-
import { writeFileSync as writeFileSync20, mkdirSync as
|
|
8907
|
-
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";
|
|
8908
9605
|
|
|
8909
9606
|
// src/analysis/fleet-core.ts
|
|
8910
|
-
import { readFileSync as
|
|
8911
|
-
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";
|
|
8912
9609
|
function loadFleetConfig(configPath) {
|
|
8913
|
-
const raw = JSON.parse(
|
|
9610
|
+
const raw = JSON.parse(readFileSync22(configPath, "utf-8"));
|
|
8914
9611
|
if (!raw.agents || !Array.isArray(raw.agents)) {
|
|
8915
9612
|
throw new Error("fleet.json must contain an 'agents' array");
|
|
8916
9613
|
}
|
|
@@ -8925,20 +9622,20 @@ function loadFleetConfig(configPath) {
|
|
|
8925
9622
|
function discoverAgents(dir) {
|
|
8926
9623
|
const agents = [];
|
|
8927
9624
|
const absDir = resolve28(dir);
|
|
8928
|
-
if (!
|
|
9625
|
+
if (!existsSync16(absDir)) {
|
|
8929
9626
|
throw new Error(`Directory not found: ${absDir}`);
|
|
8930
9627
|
}
|
|
8931
9628
|
const entries = readdirSync6(absDir, { withFileTypes: true });
|
|
8932
9629
|
for (const entry of entries) {
|
|
8933
9630
|
if (!entry.isDirectory()) continue;
|
|
8934
|
-
const agentDir =
|
|
8935
|
-
const specPath =
|
|
8936
|
-
const logDir =
|
|
8937
|
-
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)) {
|
|
8938
9635
|
agents.push({
|
|
8939
9636
|
name: entry.name,
|
|
8940
9637
|
specPath,
|
|
8941
|
-
logDir:
|
|
9638
|
+
logDir: existsSync16(logDir) ? logDir : agentDir
|
|
8942
9639
|
});
|
|
8943
9640
|
}
|
|
8944
9641
|
}
|
|
@@ -9182,10 +9879,10 @@ Press Ctrl+C for fleet summary`,
|
|
|
9182
9879
|
}
|
|
9183
9880
|
console.log();
|
|
9184
9881
|
const logDir = resolve29(process.cwd(), ".holomime");
|
|
9185
|
-
if (!
|
|
9186
|
-
|
|
9882
|
+
if (!existsSync17(logDir)) {
|
|
9883
|
+
mkdirSync13(logDir, { recursive: true });
|
|
9187
9884
|
}
|
|
9188
|
-
const logPath =
|
|
9885
|
+
const logPath = join16(logDir, "fleet-log.json");
|
|
9189
9886
|
writeFileSync20(
|
|
9190
9887
|
logPath,
|
|
9191
9888
|
JSON.stringify({
|
|
@@ -9204,42 +9901,779 @@ Press Ctrl+C for fleet summary`,
|
|
|
9204
9901
|
});
|
|
9205
9902
|
}
|
|
9206
9903
|
|
|
9207
|
-
// src/commands/
|
|
9208
|
-
import
|
|
9904
|
+
// src/commands/network.ts
|
|
9905
|
+
import chalk29 from "chalk";
|
|
9209
9906
|
import figures20 from "figures";
|
|
9210
|
-
import {
|
|
9211
|
-
|
|
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";
|
|
9212
10646
|
import { homedir as homedir4 } from "os";
|
|
9213
10647
|
|
|
9214
10648
|
// src/telemetry/client.ts
|
|
9215
10649
|
import { PostHog } from "posthog-node";
|
|
9216
10650
|
|
|
9217
10651
|
// src/telemetry/config.ts
|
|
9218
|
-
import { readFileSync as
|
|
9219
|
-
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";
|
|
9220
10654
|
import { homedir as homedir3 } from "os";
|
|
9221
10655
|
import { randomUUID } from "crypto";
|
|
9222
|
-
import
|
|
9223
|
-
var
|
|
9224
|
-
var CONFIG_PATH =
|
|
9225
|
-
var ANON_ID_PATH =
|
|
9226
|
-
function
|
|
9227
|
-
if (!
|
|
9228
|
-
|
|
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 });
|
|
9229
10663
|
}
|
|
9230
10664
|
}
|
|
9231
10665
|
function readConfig() {
|
|
9232
10666
|
try {
|
|
9233
|
-
if (
|
|
9234
|
-
return JSON.parse(
|
|
10667
|
+
if (existsSync19(CONFIG_PATH)) {
|
|
10668
|
+
return JSON.parse(readFileSync25(CONFIG_PATH, "utf-8"));
|
|
9235
10669
|
}
|
|
9236
10670
|
} catch {
|
|
9237
10671
|
}
|
|
9238
10672
|
return {};
|
|
9239
10673
|
}
|
|
9240
10674
|
function writeConfig(config) {
|
|
9241
|
-
|
|
9242
|
-
|
|
10675
|
+
ensureDir3();
|
|
10676
|
+
writeFileSync22(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
|
|
9243
10677
|
}
|
|
9244
10678
|
function shouldTrack() {
|
|
9245
10679
|
if (process.env.HOLOMIME_TELEMETRY === "0") return false;
|
|
@@ -9252,15 +10686,15 @@ function shouldTrack() {
|
|
|
9252
10686
|
}
|
|
9253
10687
|
function getAnonymousId() {
|
|
9254
10688
|
try {
|
|
9255
|
-
if (
|
|
9256
|
-
const id2 =
|
|
10689
|
+
if (existsSync19(ANON_ID_PATH)) {
|
|
10690
|
+
const id2 = readFileSync25(ANON_ID_PATH, "utf-8").trim();
|
|
9257
10691
|
if (id2.length > 0) return id2;
|
|
9258
10692
|
}
|
|
9259
10693
|
} catch {
|
|
9260
10694
|
}
|
|
9261
10695
|
const id = randomUUID();
|
|
9262
|
-
|
|
9263
|
-
|
|
10696
|
+
ensureDir3();
|
|
10697
|
+
writeFileSync22(ANON_ID_PATH, id);
|
|
9264
10698
|
return id;
|
|
9265
10699
|
}
|
|
9266
10700
|
function showTelemetryBannerIfNeeded() {
|
|
@@ -9268,8 +10702,8 @@ function showTelemetryBannerIfNeeded() {
|
|
|
9268
10702
|
if (config.telemetryBannerShown) return;
|
|
9269
10703
|
if (shouldTrack()) {
|
|
9270
10704
|
console.log(
|
|
9271
|
-
|
|
9272
|
-
` 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")}`
|
|
9273
10707
|
)
|
|
9274
10708
|
);
|
|
9275
10709
|
console.log();
|
|
@@ -9298,11 +10732,12 @@ function getTelemetryStatus() {
|
|
|
9298
10732
|
}
|
|
9299
10733
|
|
|
9300
10734
|
// src/telemetry/client.ts
|
|
9301
|
-
var POSTHOG_KEY = "
|
|
9302
|
-
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";
|
|
9303
10737
|
var client = null;
|
|
9304
10738
|
function getClient() {
|
|
9305
10739
|
if (!shouldTrack()) return null;
|
|
10740
|
+
if (!POSTHOG_KEY) return null;
|
|
9306
10741
|
if (!client) {
|
|
9307
10742
|
client = new PostHog(POSTHOG_KEY, {
|
|
9308
10743
|
host: POSTHOG_HOST,
|
|
@@ -9342,35 +10777,35 @@ async function flushTelemetry() {
|
|
|
9342
10777
|
async function activateCommand(key) {
|
|
9343
10778
|
printHeader("Activate License");
|
|
9344
10779
|
if (!key || key.trim().length === 0) {
|
|
9345
|
-
console.error(
|
|
9346
|
-
console.log(
|
|
10780
|
+
console.error(chalk33.red(" Please provide a license key."));
|
|
10781
|
+
console.log(chalk33.dim(` Usage: ${chalk33.cyan("holomime activate <license-key>")}`));
|
|
9347
10782
|
console.log();
|
|
9348
10783
|
process.exit(1);
|
|
9349
10784
|
return;
|
|
9350
10785
|
}
|
|
9351
10786
|
const trimmedKey = key.trim();
|
|
9352
|
-
const holomimeDir =
|
|
9353
|
-
const licensePath =
|
|
9354
|
-
if (!
|
|
9355
|
-
|
|
10787
|
+
const holomimeDir = join19(homedir4(), ".holomime");
|
|
10788
|
+
const licensePath = join19(holomimeDir, "license");
|
|
10789
|
+
if (!existsSync20(holomimeDir)) {
|
|
10790
|
+
mkdirSync15(holomimeDir, { recursive: true });
|
|
9356
10791
|
}
|
|
9357
|
-
|
|
9358
|
-
console.log(
|
|
10792
|
+
writeFileSync23(licensePath, trimmedKey);
|
|
10793
|
+
console.log(chalk33.dim(" Validating license..."));
|
|
9359
10794
|
const result = await validateLicense(trimmedKey);
|
|
9360
10795
|
if (result.valid) {
|
|
9361
10796
|
const tierLabel = result.tier === "enterprise" ? "Enterprise" : "Pro";
|
|
9362
10797
|
console.log();
|
|
9363
10798
|
printBox(
|
|
9364
10799
|
[
|
|
9365
|
-
`${
|
|
10800
|
+
`${figures23.tick} ${tierLabel} license activated!`,
|
|
9366
10801
|
"",
|
|
9367
10802
|
"Unlocked features:",
|
|
9368
|
-
` ${
|
|
9369
|
-
` ${
|
|
9370
|
-
` ${
|
|
9371
|
-
` ${
|
|
9372
|
-
` ${
|
|
9373
|
-
` ${
|
|
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)`
|
|
9374
10809
|
].join("\n"),
|
|
9375
10810
|
"success",
|
|
9376
10811
|
`License Activated \u2014 ${tierLabel}`
|
|
@@ -9379,37 +10814,37 @@ async function activateCommand(key) {
|
|
|
9379
10814
|
console.log();
|
|
9380
10815
|
printBox(
|
|
9381
10816
|
[
|
|
9382
|
-
`${
|
|
10817
|
+
`${chalk33.yellow(figures23.warning)} License saved but could not be verified.`,
|
|
9383
10818
|
"",
|
|
9384
|
-
|
|
9385
|
-
|
|
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.")
|
|
9386
10821
|
].join("\n"),
|
|
9387
10822
|
"warning",
|
|
9388
10823
|
"License Pending Verification"
|
|
9389
10824
|
);
|
|
9390
10825
|
}
|
|
9391
10826
|
console.log();
|
|
9392
|
-
console.log(
|
|
10827
|
+
console.log(chalk33.dim(` License saved to: ${licensePath}`));
|
|
9393
10828
|
console.log();
|
|
9394
10829
|
trackEvent("activate", { key_length: trimmedKey.length, verified: result.valid, tier: result.tier });
|
|
9395
10830
|
}
|
|
9396
10831
|
|
|
9397
10832
|
// src/commands/telemetry-cmd.ts
|
|
9398
|
-
import
|
|
9399
|
-
import
|
|
10833
|
+
import chalk34 from "chalk";
|
|
10834
|
+
import figures24 from "figures";
|
|
9400
10835
|
async function telemetryCommand(action) {
|
|
9401
10836
|
printHeader("Telemetry");
|
|
9402
10837
|
if (action === "enable") {
|
|
9403
10838
|
setTelemetryEnabled(true);
|
|
9404
10839
|
console.log();
|
|
9405
|
-
printBox(`${
|
|
10840
|
+
printBox(`${figures24.tick} Telemetry enabled. Anonymous usage data will be collected.`, "success");
|
|
9406
10841
|
console.log();
|
|
9407
10842
|
return;
|
|
9408
10843
|
}
|
|
9409
10844
|
if (action === "disable") {
|
|
9410
10845
|
setTelemetryEnabled(false);
|
|
9411
10846
|
console.log();
|
|
9412
|
-
printBox(`${
|
|
10847
|
+
printBox(`${figures24.tick} Telemetry disabled. No usage data will be collected.`, "info");
|
|
9413
10848
|
console.log();
|
|
9414
10849
|
return;
|
|
9415
10850
|
}
|
|
@@ -9417,15 +10852,15 @@ async function telemetryCommand(action) {
|
|
|
9417
10852
|
console.log();
|
|
9418
10853
|
printBox(
|
|
9419
10854
|
[
|
|
9420
|
-
`Status: ${status.enabled ?
|
|
9421
|
-
`Reason: ${
|
|
10855
|
+
`Status: ${status.enabled ? chalk34.green("Enabled") : chalk34.yellow("Disabled")}`,
|
|
10856
|
+
`Reason: ${chalk34.dim(status.reason)}`,
|
|
9422
10857
|
"",
|
|
9423
|
-
|
|
9424
|
-
|
|
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."),
|
|
9425
10860
|
"",
|
|
9426
|
-
`Enable: ${
|
|
9427
|
-
`Disable: ${
|
|
9428
|
-
`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")}`
|
|
9429
10864
|
].join("\n"),
|
|
9430
10865
|
"info",
|
|
9431
10866
|
"Telemetry Status"
|
|
@@ -9435,7 +10870,7 @@ async function telemetryCommand(action) {
|
|
|
9435
10870
|
|
|
9436
10871
|
// src/cli.ts
|
|
9437
10872
|
var program = new Command();
|
|
9438
|
-
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) => {
|
|
9439
10874
|
printBanner();
|
|
9440
10875
|
const commandName = actionCommand.name();
|
|
9441
10876
|
showTelemetryBannerIfNeeded();
|
|
@@ -9451,7 +10886,7 @@ program.name("holomime").description("Personality engine for AI agents \u2014 Bi
|
|
|
9451
10886
|
}
|
|
9452
10887
|
});
|
|
9453
10888
|
program.command("init").description("Build a personality profile through a guided assessment").action(initCommand);
|
|
9454
|
-
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);
|
|
9455
10890
|
program.command("validate").description("Validate .personality.json schema and psychological coherence").action(validateCommand);
|
|
9456
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);
|
|
9457
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);
|
|
@@ -9462,7 +10897,7 @@ program.command("publish").description("Share your personality profile to the co
|
|
|
9462
10897
|
program.command("activate").description("Activate a Pro license key").argument("<key>", "License key from holomime.dev").action(activateCommand);
|
|
9463
10898
|
program.command("telemetry").description("Manage anonymous usage telemetry").argument("[action]", "enable, disable, or status (default: status)").action(telemetryCommand);
|
|
9464
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);
|
|
9465
|
-
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").action(autopilotCommand);
|
|
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);
|
|
9466
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);
|
|
9467
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);
|
|
9468
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);
|
|
@@ -9471,6 +10906,9 @@ program.command("evolve").description("Recursive behavioral alignment \u2014 ite
|
|
|
9471
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);
|
|
9472
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);
|
|
9473
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);
|
|
9474
|
-
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);
|
|
9475
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);
|
|
9476
10914
|
program.parseAsync().then(() => flushTelemetry());
|