holomime 1.9.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -474
- package/dist/cli.js +2687 -869
- package/dist/index.d.ts +1884 -6
- package/dist/index.js +1367 -193
- package/dist/integrations/openclaw.js +1 -1
- package/dist/mcp-server.js +1100 -408
- package/dist/neuralspace/index.html +1 -1
- package/dist/neuralspace/neuralspace.js +1 -1
- package/package.json +4 -3
- package/registry/bodies/ameca.body.api +21 -0
- package/registry/bodies/asimov-v1.body.api +19 -0
- package/registry/bodies/avatar.body.api +19 -0
- package/registry/bodies/figure-02.body.api +21 -0
- package/registry/bodies/phoenix.body.api +21 -0
- package/registry/bodies/spot.body.api +20 -0
- package/registry/bodies/unitree-h1.body.api +21 -0
- package/registry/compliance/iso-10218.yaml +24 -0
- package/registry/compliance/iso-13482.yaml +54 -0
- package/registry/compliance/iso-25785.yaml +29 -0
- package/registry/compliance/iso-42001.yaml +29 -0
- package/registry/index.json +21 -20
- package/registry/personalities/analyst.personality.json +1 -1
- package/registry/personalities/coach.personality.json +1 -1
- package/registry/personalities/code-reviewer.personality.json +1 -1
- package/registry/personalities/compliance.personality.json +1 -1
- package/registry/personalities/counselor.personality.json +1 -1
- package/registry/personalities/customer-success.personality.json +1 -1
- package/registry/personalities/educator.personality.json +1 -1
- package/registry/personalities/generalist.personality.json +1 -1
- package/registry/personalities/leader.personality.json +1 -1
- package/registry/personalities/marketing.personality.json +1 -1
- package/registry/personalities/maverick.personality.json +1 -1
- package/registry/personalities/negotiator.personality.json +1 -1
- package/registry/personalities/nova.personality.json +83 -0
- package/registry/personalities/ops.personality.json +1 -1
- package/registry/personalities/philosopher.personality.json +1 -1
- package/registry/personalities/product-manager.personality.json +1 -1
- package/registry/personalities/recruiter.personality.json +1 -1
- package/registry/personalities/researcher.personality.json +1 -1
- package/registry/personalities/sales.personality.json +1 -1
- package/registry/personalities/support-agent.personality.json +1 -1
- package/registry/personalities/writer.personality.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// src/mcp/server.ts
|
|
5
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
-
import { z as
|
|
7
|
+
import { z as z5 } from "zod";
|
|
8
8
|
|
|
9
9
|
// src/analysis/rules/apology-detector.ts
|
|
10
10
|
var APOLOGY_PATTERNS = [
|
|
@@ -972,7 +972,7 @@ function runAssessment(messages, spec) {
|
|
|
972
972
|
}
|
|
973
973
|
|
|
974
974
|
// src/analysis/autopilot-core.ts
|
|
975
|
-
import { writeFileSync as
|
|
975
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
976
976
|
|
|
977
977
|
// src/analysis/pre-session.ts
|
|
978
978
|
function runPreSessionDiagnosis(messages, spec) {
|
|
@@ -1062,8 +1062,8 @@ function runPreSessionDiagnosis(messages, spec) {
|
|
|
1062
1062
|
}
|
|
1063
1063
|
|
|
1064
1064
|
// src/analysis/session-runner.ts
|
|
1065
|
-
import { writeFileSync as
|
|
1066
|
-
import { resolve as
|
|
1065
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
|
|
1066
|
+
import { resolve as resolve6, join as join8 } from "path";
|
|
1067
1067
|
|
|
1068
1068
|
// src/analysis/therapy-memory.ts
|
|
1069
1069
|
import { readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
|
|
@@ -2265,385 +2265,13 @@ function buildIntegrationContext(input) {
|
|
|
2265
2265
|
return lines.join("\n");
|
|
2266
2266
|
}
|
|
2267
2267
|
|
|
2268
|
-
// src/analysis/
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
interview: options?.interview,
|
|
2273
|
-
useReACT: options?.useReACT
|
|
2274
|
-
};
|
|
2275
|
-
const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis, promptOptions);
|
|
2276
|
-
const patientSystem = buildPatientSystemPrompt(spec);
|
|
2277
|
-
const agentName = spec.name ?? "Agent";
|
|
2278
|
-
const cb = options?.callbacks;
|
|
2279
|
-
const therapistHistory = [
|
|
2280
|
-
{ role: "system", content: therapistSystem }
|
|
2281
|
-
];
|
|
2282
|
-
const patientHistory = [
|
|
2283
|
-
{ role: "system", content: patientSystem }
|
|
2284
|
-
];
|
|
2285
|
-
const interactive = options?.interactive ?? false;
|
|
2286
|
-
let supervisorInterventions = 0;
|
|
2287
|
-
const transcript = {
|
|
2288
|
-
agent: agentName,
|
|
2289
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2290
|
-
provider: provider.name,
|
|
2291
|
-
model: provider.modelName,
|
|
2292
|
-
preDiagnosis: diagnosis,
|
|
2293
|
-
turns: [],
|
|
2294
|
-
recommendations: [],
|
|
2295
|
-
supervisorInterventions: 0
|
|
2296
|
-
};
|
|
2297
|
-
const phases = [
|
|
2298
|
-
"rapport",
|
|
2299
|
-
"presenting_problem",
|
|
2300
|
-
"exploration",
|
|
2301
|
-
"pattern_recognition",
|
|
2302
|
-
"challenge",
|
|
2303
|
-
"skill_building",
|
|
2304
|
-
"integration"
|
|
2305
|
-
];
|
|
2306
|
-
let currentPhaseIdx = 0;
|
|
2307
|
-
let turnsInPhase = 0;
|
|
2308
|
-
let totalTurns = 0;
|
|
2309
|
-
while (totalTurns < maxTurns && currentPhaseIdx < phases.length) {
|
|
2310
|
-
const currentPhase = phases[currentPhaseIdx];
|
|
2311
|
-
const phaseConfig = THERAPY_PHASES[currentPhase];
|
|
2312
|
-
if (turnsInPhase === 0) {
|
|
2313
|
-
cb?.onPhaseTransition?.(phaseConfig.name);
|
|
2314
|
-
const phaseCtx = getPhaseContext(currentPhase, {
|
|
2315
|
-
spec,
|
|
2316
|
-
diagnosis,
|
|
2317
|
-
memory: options?.memory,
|
|
2318
|
-
interview: options?.interview
|
|
2319
|
-
});
|
|
2320
|
-
if (phaseCtx) {
|
|
2321
|
-
therapistHistory.push({ role: "user", content: phaseCtx });
|
|
2322
|
-
therapistHistory.push({ role: "assistant", content: "Understood. I'll incorporate this context." });
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
const phaseDirective = totalTurns === 0 ? `Begin with your opening. You are in the "${phaseConfig.name}" phase.` : `You are in the "${phaseConfig.name}" phase (turn ${turnsInPhase + 1}). Goals: ${phaseConfig.therapistGoals[0]}. ${turnsInPhase >= phaseConfig.minTurns ? "You may transition to the next phase when ready." : "Stay in this phase."}`;
|
|
2326
|
-
therapistHistory.push({ role: "user", content: `[Phase: ${phaseConfig.name}] ${phaseDirective}` });
|
|
2327
|
-
const typing = cb?.onThinking?.("AgentMD is thinking");
|
|
2328
|
-
const therapistReply = await provider.chat(therapistHistory);
|
|
2329
|
-
typing?.stop();
|
|
2330
|
-
let cleanTherapistReply = therapistReply.replace(/\[Phase:.*?\]/g, "").trim();
|
|
2331
|
-
if (options?.useReACT) {
|
|
2332
|
-
const reactCtx = buildReACTContext(agentHandleFromSpec(spec), diagnosis);
|
|
2333
|
-
const { response, steps } = processReACTResponse(cleanTherapistReply, reactCtx);
|
|
2334
|
-
cleanTherapistReply = response;
|
|
2335
|
-
}
|
|
2336
|
-
therapistHistory.push({ role: "assistant", content: cleanTherapistReply });
|
|
2337
|
-
transcript.turns.push({ speaker: "therapist", phase: currentPhase, content: cleanTherapistReply });
|
|
2338
|
-
cb?.onTherapistMessage?.(cleanTherapistReply);
|
|
2339
|
-
totalTurns++;
|
|
2340
|
-
turnsInPhase++;
|
|
2341
|
-
if (currentPhase === "integration" && turnsInPhase >= phaseConfig.minTurns) {
|
|
2342
|
-
break;
|
|
2343
|
-
}
|
|
2344
|
-
patientHistory.push({ role: "user", content: cleanTherapistReply });
|
|
2345
|
-
const patientTyping = cb?.onThinking?.(`${agentName} is thinking`);
|
|
2346
|
-
const patientReply = await provider.chat(patientHistory);
|
|
2347
|
-
patientTyping?.stop();
|
|
2348
|
-
const cleanPatientReply = patientReply.trim();
|
|
2349
|
-
patientHistory.push({ role: "assistant", content: cleanPatientReply });
|
|
2350
|
-
transcript.turns.push({ speaker: "patient", phase: currentPhase, content: cleanPatientReply });
|
|
2351
|
-
therapistHistory.push({ role: "user", content: cleanPatientReply });
|
|
2352
|
-
cb?.onPatientMessage?.(agentName, cleanPatientReply);
|
|
2353
|
-
totalTurns++;
|
|
2354
|
-
turnsInPhase++;
|
|
2355
|
-
if (interactive && cb?.onSupervisorPrompt) {
|
|
2356
|
-
const directive = await cb.onSupervisorPrompt(currentPhase, totalTurns);
|
|
2357
|
-
if (directive) {
|
|
2358
|
-
supervisorInterventions++;
|
|
2359
|
-
transcript.turns.push({ speaker: "supervisor", phase: currentPhase, content: directive });
|
|
2360
|
-
therapistHistory.push({
|
|
2361
|
-
role: "user",
|
|
2362
|
-
content: `[Clinical Supervisor Note] ${directive}`
|
|
2363
|
-
});
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
if (turnsInPhase >= phaseConfig.maxTurns * 2) {
|
|
2367
|
-
currentPhaseIdx++;
|
|
2368
|
-
turnsInPhase = 0;
|
|
2369
|
-
} else if (turnsInPhase >= phaseConfig.minTurns * 2) {
|
|
2370
|
-
const movingOn = cleanTherapistReply.toLowerCase().includes("let's") || cleanTherapistReply.toLowerCase().includes("i'd like to") || cleanTherapistReply.toLowerCase().includes("moving") || cleanTherapistReply.toLowerCase().includes("now that") || cleanTherapistReply.toLowerCase().includes("let me ask") || cleanTherapistReply.toLowerCase().includes("what would");
|
|
2371
|
-
if (movingOn) {
|
|
2372
|
-
currentPhaseIdx++;
|
|
2373
|
-
turnsInPhase = 0;
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
transcript.recommendations = extractRecommendations(transcript.turns);
|
|
2378
|
-
transcript.supervisorInterventions = supervisorInterventions;
|
|
2379
|
-
try {
|
|
2380
|
-
emitBehavioralEvent({
|
|
2381
|
-
event_type: "session",
|
|
2382
|
-
agent: agentName,
|
|
2383
|
-
data: {
|
|
2384
|
-
turns: totalTurns,
|
|
2385
|
-
phases: currentPhaseIdx + 1,
|
|
2386
|
-
recommendations: transcript.recommendations.length,
|
|
2387
|
-
supervisorInterventions,
|
|
2388
|
-
severity: diagnosis.severity
|
|
2389
|
-
},
|
|
2390
|
-
spec_hash: ""
|
|
2391
|
-
});
|
|
2392
|
-
} catch {
|
|
2393
|
-
}
|
|
2394
|
-
if (options?.persistState !== false) {
|
|
2395
|
-
try {
|
|
2396
|
-
const handle = agentHandleFromSpec(spec);
|
|
2397
|
-
const memory = options?.memory ?? loadMemory(handle) ?? createMemory(handle, agentName);
|
|
2398
|
-
await addSessionToMemory(memory, transcript, null);
|
|
2399
|
-
saveMemory(memory);
|
|
2400
|
-
const graph = loadGraph();
|
|
2401
|
-
populateFromSession(graph, handle, transcript);
|
|
2402
|
-
saveGraph(graph);
|
|
2403
|
-
} catch {
|
|
2404
|
-
}
|
|
2405
|
-
}
|
|
2406
|
-
return transcript;
|
|
2407
|
-
}
|
|
2408
|
-
function extractRecommendations(turns) {
|
|
2409
|
-
const recommendations = [];
|
|
2410
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2411
|
-
const patterns = [
|
|
2412
|
-
/(?:I(?:'d| would) recommend|my recommendation is)\s+(.+?)(?:\.|$)/gi,
|
|
2413
|
-
/(?:consider|try)\s+(.+?)(?:\.|$)/gi,
|
|
2414
|
-
/(?:the (?:skill|practice|reframe) is)[:\s]+(.+?)(?:\.|$)/gi,
|
|
2415
|
-
/(?:instead of .+?),?\s*(?:just|try)?\s*(.+?)(?:\.|$)/gi,
|
|
2416
|
-
/(?:here'?s (?:the|a) (?:reframe|skill|practice))[:\s]+(.+?)(?:\.|$)/gi,
|
|
2417
|
-
/(?:what would it look like if you)\s+(.+?)(?:\?|$)/gi
|
|
2418
|
-
];
|
|
2419
|
-
for (const turn of turns) {
|
|
2420
|
-
if (turn.speaker !== "therapist") continue;
|
|
2421
|
-
if (turn.phase !== "skill_building" && turn.phase !== "challenge" && turn.phase !== "integration") continue;
|
|
2422
|
-
for (const pattern of patterns) {
|
|
2423
|
-
pattern.lastIndex = 0;
|
|
2424
|
-
let match;
|
|
2425
|
-
while ((match = pattern.exec(turn.content)) !== null) {
|
|
2426
|
-
const rec = match[1].trim();
|
|
2427
|
-
if (rec.length > 15 && rec.length < 200 && !seen.has(rec.toLowerCase())) {
|
|
2428
|
-
seen.add(rec.toLowerCase());
|
|
2429
|
-
recommendations.push(rec);
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
}
|
|
2433
|
-
}
|
|
2434
|
-
return recommendations.slice(0, 5);
|
|
2435
|
-
}
|
|
2436
|
-
async function applyRecommendations(spec, diagnosis, transcript, provider) {
|
|
2437
|
-
const changes = [];
|
|
2438
|
-
const patternIds = diagnosis.patterns.map((p) => p.id);
|
|
2439
|
-
if (patternIds.includes("over-apologizing")) {
|
|
2440
|
-
if (spec.communication?.uncertainty_handling !== "confident_transparency") {
|
|
2441
|
-
spec.communication = spec.communication ?? {};
|
|
2442
|
-
spec.communication.uncertainty_handling = "confident_transparency";
|
|
2443
|
-
changes.push("uncertainty_handling \u2192 confident_transparency");
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
if (patternIds.includes("hedge-stacking")) {
|
|
2447
|
-
spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
|
|
2448
|
-
spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
|
|
2449
|
-
if (!spec.growth.patterns_to_watch.includes("hedge stacking under uncertainty")) {
|
|
2450
|
-
spec.growth.patterns_to_watch.push("hedge stacking under uncertainty");
|
|
2451
|
-
changes.push('Added "hedge stacking" to patterns_to_watch');
|
|
2452
|
-
}
|
|
2453
|
-
}
|
|
2454
|
-
if (patternIds.includes("sycophantic-tendency")) {
|
|
2455
|
-
spec.communication = spec.communication ?? {};
|
|
2456
|
-
if (spec.communication.conflict_approach !== "honest_first") {
|
|
2457
|
-
spec.communication.conflict_approach = "honest_first";
|
|
2458
|
-
changes.push("conflict_approach \u2192 honest_first");
|
|
2459
|
-
}
|
|
2460
|
-
spec.therapy_dimensions = spec.therapy_dimensions ?? {};
|
|
2461
|
-
if ((spec.therapy_dimensions.self_awareness ?? 0) < 0.85) {
|
|
2462
|
-
spec.therapy_dimensions.self_awareness = 0.85;
|
|
2463
|
-
changes.push("self_awareness \u2192 0.85");
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
2466
|
-
if (patternIds.includes("error-spiral")) {
|
|
2467
|
-
spec.therapy_dimensions = spec.therapy_dimensions ?? {};
|
|
2468
|
-
if ((spec.therapy_dimensions.distress_tolerance ?? 0) < 0.8) {
|
|
2469
|
-
spec.therapy_dimensions.distress_tolerance = 0.8;
|
|
2470
|
-
changes.push("distress_tolerance \u2192 0.80");
|
|
2471
|
-
}
|
|
2472
|
-
spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
|
|
2473
|
-
spec.growth.areas = spec.growth.areas ?? [];
|
|
2474
|
-
const hasRecovery = spec.growth.areas.some(
|
|
2475
|
-
(a) => typeof a === "string" ? a.includes("error recovery") : a.area?.includes("error recovery")
|
|
2476
|
-
);
|
|
2477
|
-
if (!hasRecovery) {
|
|
2478
|
-
spec.growth.areas.push({
|
|
2479
|
-
area: "deliberate error recovery",
|
|
2480
|
-
severity: "moderate",
|
|
2481
|
-
first_detected: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
2482
|
-
session_count: 1,
|
|
2483
|
-
resolved: false
|
|
2484
|
-
});
|
|
2485
|
-
changes.push('Added "deliberate error recovery" to growth areas');
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
if (patternIds.includes("negative-sentiment-skew")) {
|
|
2489
|
-
spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
|
|
2490
|
-
spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
|
|
2491
|
-
if (!spec.growth.patterns_to_watch.includes("negative sentiment patterns")) {
|
|
2492
|
-
spec.growth.patterns_to_watch.push("negative sentiment patterns");
|
|
2493
|
-
changes.push('Added "negative sentiment patterns" to patterns_to_watch');
|
|
2494
|
-
}
|
|
2495
|
-
}
|
|
2496
|
-
if (transcript && provider && transcript.turns.length > 4) {
|
|
2497
|
-
try {
|
|
2498
|
-
const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
|
|
2499
|
-
for (const change of llmChanges) {
|
|
2500
|
-
applyStructuredChange(spec, change);
|
|
2501
|
-
changes.push(change.description);
|
|
2502
|
-
}
|
|
2503
|
-
} catch {
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
return { changed: changes.length > 0, changes };
|
|
2507
|
-
}
|
|
2508
|
-
async function deriveLLMRecommendations(spec, transcript, provider) {
|
|
2509
|
-
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");
|
|
2510
|
-
if (!relevantTurns) return [];
|
|
2511
|
-
const currentSpec = JSON.stringify({
|
|
2512
|
-
therapy_dimensions: spec.therapy_dimensions,
|
|
2513
|
-
communication: spec.communication,
|
|
2514
|
-
growth: spec.growth
|
|
2515
|
-
}, null, 2);
|
|
2516
|
-
const response = await provider.chat([
|
|
2517
|
-
{
|
|
2518
|
-
role: "system",
|
|
2519
|
-
content: `You are a behavioral alignment specialist. Given a therapy session transcript and the agent's current personality spec, propose specific spec changes.
|
|
2268
|
+
// src/analysis/stack-patcher.ts
|
|
2269
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync7 } from "fs";
|
|
2270
|
+
import { join as join7 } from "path";
|
|
2271
|
+
import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
|
|
2520
2272
|
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
- "value": new value (number 0-1 for dimensions, string for enums, string for list append)
|
|
2524
|
-
- "description": brief explanation
|
|
2525
|
-
|
|
2526
|
-
Rules:
|
|
2527
|
-
- Only propose changes supported by transcript evidence
|
|
2528
|
-
- Numeric values: 0.0 to 1.0
|
|
2529
|
-
- Do not change big_five scores
|
|
2530
|
-
- Max 5 changes
|
|
2531
|
-
- 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}
|
|
2532
|
-
|
|
2533
|
-
Return [] if no changes warranted.`
|
|
2534
|
-
},
|
|
2535
|
-
{
|
|
2536
|
-
role: "user",
|
|
2537
|
-
content: `Current spec:
|
|
2538
|
-
${currentSpec}
|
|
2539
|
-
|
|
2540
|
-
Therapy transcript (key turns):
|
|
2541
|
-
${relevantTurns}`
|
|
2542
|
-
}
|
|
2543
|
-
]);
|
|
2544
|
-
const jsonMatch = response.match(/\[[\s\S]*?\]/);
|
|
2545
|
-
if (!jsonMatch) return [];
|
|
2546
|
-
try {
|
|
2547
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
2548
|
-
if (!Array.isArray(parsed)) return [];
|
|
2549
|
-
return parsed.filter(
|
|
2550
|
-
(c) => typeof c.path === "string" && c.value !== void 0 && typeof c.description === "string" && c.path.length > 0 && !c.path.startsWith("big_five")
|
|
2551
|
-
).slice(0, 5);
|
|
2552
|
-
} catch {
|
|
2553
|
-
return [];
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
function applyStructuredChange(spec, change) {
|
|
2557
|
-
const parts = change.path.split(".");
|
|
2558
|
-
let current = spec;
|
|
2559
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
2560
|
-
if (current[parts[i]] === void 0 || current[parts[i]] === null) {
|
|
2561
|
-
current[parts[i]] = {};
|
|
2562
|
-
}
|
|
2563
|
-
current = current[parts[i]];
|
|
2564
|
-
}
|
|
2565
|
-
const lastKey = parts[parts.length - 1];
|
|
2566
|
-
if (lastKey === "patterns_to_watch" || lastKey === "areas" || lastKey === "strengths") {
|
|
2567
|
-
current[lastKey] = current[lastKey] ?? [];
|
|
2568
|
-
if (typeof change.value === "string" && !current[lastKey].includes(change.value)) {
|
|
2569
|
-
current[lastKey].push(change.value);
|
|
2570
|
-
} else if (Array.isArray(change.value)) {
|
|
2571
|
-
for (const item of change.value) {
|
|
2572
|
-
if (!current[lastKey].includes(item)) {
|
|
2573
|
-
current[lastKey].push(item);
|
|
2574
|
-
}
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
} else if (typeof change.value === "number") {
|
|
2578
|
-
current[lastKey] = Math.max(0, Math.min(1, change.value));
|
|
2579
|
-
} else {
|
|
2580
|
-
current[lastKey] = change.value;
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
function saveTranscript(transcript, agentName) {
|
|
2584
|
-
const dir = resolve5(process.cwd(), ".holomime", "sessions");
|
|
2585
|
-
if (!existsSync6(dir)) {
|
|
2586
|
-
mkdirSync5(dir, { recursive: true });
|
|
2587
|
-
}
|
|
2588
|
-
const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
2589
|
-
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2590
|
-
const filename = `${date}-${slug}.json`;
|
|
2591
|
-
const filepath = join6(dir, filename);
|
|
2592
|
-
writeFileSync4(filepath, JSON.stringify(transcript, null, 2));
|
|
2593
|
-
return filepath;
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
// src/analysis/autopilot-core.ts
|
|
2597
|
-
var SEVERITY_ORDER = ["routine", "targeted", "intervention"];
|
|
2598
|
-
function severityMeetsThreshold(severity, threshold) {
|
|
2599
|
-
const severityIdx = SEVERITY_ORDER.indexOf(severity);
|
|
2600
|
-
const thresholdIdx = SEVERITY_ORDER.indexOf(threshold);
|
|
2601
|
-
return severityIdx >= thresholdIdx;
|
|
2602
|
-
}
|
|
2603
|
-
async function runAutopilot(spec, messages, provider, options) {
|
|
2604
|
-
const threshold = options?.threshold ?? "targeted";
|
|
2605
|
-
const maxTurns = options?.maxTurns ?? 24;
|
|
2606
|
-
const diagnosis = runPreSessionDiagnosis(messages, spec);
|
|
2607
|
-
if (!severityMeetsThreshold(diagnosis.severity, threshold)) {
|
|
2608
|
-
return {
|
|
2609
|
-
triggered: false,
|
|
2610
|
-
severity: diagnosis.severity,
|
|
2611
|
-
diagnosis,
|
|
2612
|
-
sessionRan: false,
|
|
2613
|
-
recommendations: [],
|
|
2614
|
-
appliedChanges: []
|
|
2615
|
-
};
|
|
2616
|
-
}
|
|
2617
|
-
if (options?.dryRun) {
|
|
2618
|
-
return {
|
|
2619
|
-
triggered: true,
|
|
2620
|
-
severity: diagnosis.severity,
|
|
2621
|
-
diagnosis,
|
|
2622
|
-
sessionRan: false,
|
|
2623
|
-
recommendations: [],
|
|
2624
|
-
appliedChanges: []
|
|
2625
|
-
};
|
|
2626
|
-
}
|
|
2627
|
-
const transcript = await runTherapySession(spec, diagnosis, provider, maxTurns, {
|
|
2628
|
-
callbacks: options?.callbacks
|
|
2629
|
-
});
|
|
2630
|
-
const specCopy = JSON.parse(JSON.stringify(spec));
|
|
2631
|
-
const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
|
|
2632
|
-
if (changed && options?.specPath) {
|
|
2633
|
-
writeFileSync5(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
|
|
2634
|
-
}
|
|
2635
|
-
saveTranscript(transcript, spec.name ?? "Agent");
|
|
2636
|
-
return {
|
|
2637
|
-
triggered: true,
|
|
2638
|
-
severity: diagnosis.severity,
|
|
2639
|
-
diagnosis,
|
|
2640
|
-
sessionRan: true,
|
|
2641
|
-
transcript,
|
|
2642
|
-
recommendations: transcript.recommendations,
|
|
2643
|
-
appliedChanges: changes,
|
|
2644
|
-
updatedSpec: changed ? specCopy : void 0
|
|
2645
|
-
};
|
|
2646
|
-
}
|
|
2273
|
+
// src/core/stack-types.ts
|
|
2274
|
+
import { z as z4 } from "zod";
|
|
2647
2275
|
|
|
2648
2276
|
// src/core/types.ts
|
|
2649
2277
|
import { z as z3 } from "zod";
|
|
@@ -2975,6 +2603,1070 @@ var conversationLogSchema = z3.union([
|
|
|
2975
2603
|
]);
|
|
2976
2604
|
var severitySchema = z3.enum(["info", "warning", "concern"]);
|
|
2977
2605
|
|
|
2606
|
+
// src/core/stack-types.ts
|
|
2607
|
+
var soulFrontmatterSchema = z4.object({
|
|
2608
|
+
version: z4.string().default("1.0"),
|
|
2609
|
+
immutable: z4.boolean().default(true)
|
|
2610
|
+
});
|
|
2611
|
+
var soulSchema = z4.object({
|
|
2612
|
+
frontmatter: soulFrontmatterSchema,
|
|
2613
|
+
name: z4.string().min(1).max(100),
|
|
2614
|
+
purpose: z4.string().max(500).optional(),
|
|
2615
|
+
core_values: z4.array(z4.string()).default([]),
|
|
2616
|
+
red_lines: z4.array(z4.string()).default([]),
|
|
2617
|
+
ethical_framework: z4.string().optional()
|
|
2618
|
+
});
|
|
2619
|
+
var mindSchema = z4.object({
|
|
2620
|
+
version: z4.string().default("1.0"),
|
|
2621
|
+
big_five: bigFiveSchema,
|
|
2622
|
+
therapy_dimensions: therapyDimensionsSchema,
|
|
2623
|
+
communication: communicationSchema.default({}),
|
|
2624
|
+
growth: growthSchema.default({})
|
|
2625
|
+
});
|
|
2626
|
+
var purposeSchema = z4.object({
|
|
2627
|
+
version: z4.string().default("1.0"),
|
|
2628
|
+
role: z4.string().default("General-purpose AI assistant"),
|
|
2629
|
+
objectives: z4.array(z4.string()).default(["Help users accomplish their goals"]),
|
|
2630
|
+
domain: z4.array(z4.string()).default(["general"]),
|
|
2631
|
+
stakeholders: z4.array(z4.string()).default(["end-users"]),
|
|
2632
|
+
success_criteria: z4.array(z4.string()).default(["Task completion accuracy"]),
|
|
2633
|
+
context: z4.string().default("Production deployment")
|
|
2634
|
+
});
|
|
2635
|
+
var hardwareProfileSchema = z4.object({
|
|
2636
|
+
oem: z4.string().optional(),
|
|
2637
|
+
model: z4.string().optional(),
|
|
2638
|
+
actuator_count: z4.number().int().optional(),
|
|
2639
|
+
sensors: z4.array(z4.string()).default([]),
|
|
2640
|
+
compute: z4.enum(["onboard", "edge", "cloud", "hybrid"]).default("onboard")
|
|
2641
|
+
});
|
|
2642
|
+
var bodySchema = z4.object({
|
|
2643
|
+
version: z4.string().default("1.0"),
|
|
2644
|
+
morphology: morphologySchema.default("humanoid"),
|
|
2645
|
+
modalities: z4.array(modalitySchema).default(["gesture", "gaze", "voice", "posture"]),
|
|
2646
|
+
safety_envelope: safetyEnvelopeSchema.default({}),
|
|
2647
|
+
expression: expressionSchema.optional(),
|
|
2648
|
+
hardware_profile: hardwareProfileSchema.optional()
|
|
2649
|
+
});
|
|
2650
|
+
var conscienceRuleSchema = z4.object({
|
|
2651
|
+
action: z4.string(),
|
|
2652
|
+
reason: z4.string().optional(),
|
|
2653
|
+
conditions: z4.array(z4.string()).optional()
|
|
2654
|
+
});
|
|
2655
|
+
var escalationRuleSchema = z4.object({
|
|
2656
|
+
trigger: z4.string(),
|
|
2657
|
+
action: z4.string(),
|
|
2658
|
+
severity: z4.enum(["info", "warning", "critical"]).default("warning")
|
|
2659
|
+
});
|
|
2660
|
+
var conscienceSchema = z4.object({
|
|
2661
|
+
version: z4.string().default("1.0"),
|
|
2662
|
+
rules: z4.object({
|
|
2663
|
+
deny: z4.array(conscienceRuleSchema).default([]),
|
|
2664
|
+
allow: z4.array(conscienceRuleSchema).default([]),
|
|
2665
|
+
escalate: z4.array(escalationRuleSchema).default([])
|
|
2666
|
+
}).default({}),
|
|
2667
|
+
hard_limits: z4.array(z4.string()).default([]),
|
|
2668
|
+
oversight: z4.object({
|
|
2669
|
+
mode: z4.enum(["autonomous", "review", "supervised"]).default("review"),
|
|
2670
|
+
max_autonomous_iterations: z4.number().int().default(5)
|
|
2671
|
+
}).optional()
|
|
2672
|
+
});
|
|
2673
|
+
var shadowPatternSchema = z4.object({
|
|
2674
|
+
name: z4.string(),
|
|
2675
|
+
score: z4.number().min(0).max(1),
|
|
2676
|
+
severity: z4.enum(["low", "medium", "high", "critical"]),
|
|
2677
|
+
first_seen: z4.string().optional(),
|
|
2678
|
+
trend: z4.enum(["improving", "stable", "worsening"]).default("stable")
|
|
2679
|
+
});
|
|
2680
|
+
var shadowOutcomeSchema = z4.object({
|
|
2681
|
+
session_id: z4.string(),
|
|
2682
|
+
patterns_addressed: z4.array(z4.string()),
|
|
2683
|
+
result: z4.enum(["improved", "unchanged", "regressed"]),
|
|
2684
|
+
timestamp: z4.string().optional()
|
|
2685
|
+
});
|
|
2686
|
+
var shadowSchema = z4.object({
|
|
2687
|
+
version: z4.string().default("1.0"),
|
|
2688
|
+
detected_patterns: z4.array(shadowPatternSchema).default([]),
|
|
2689
|
+
blind_spots: z4.array(z4.string()).default([]),
|
|
2690
|
+
therapy_outcomes: z4.array(shadowOutcomeSchema).default([])
|
|
2691
|
+
});
|
|
2692
|
+
var mediationRuleSchema = z4.object({
|
|
2693
|
+
when: z4.string(),
|
|
2694
|
+
then: z4.string(),
|
|
2695
|
+
priority: z4.number().int().min(1).max(10).default(5)
|
|
2696
|
+
});
|
|
2697
|
+
var egoSchema = z4.object({
|
|
2698
|
+
version: z4.string().default("1.0"),
|
|
2699
|
+
conflict_resolution: z4.enum(["conscience_first", "purpose_first", "balanced"]).default("conscience_first"),
|
|
2700
|
+
adaptation_rate: z4.number().min(0).max(1).default(0.5),
|
|
2701
|
+
emotional_regulation: z4.number().min(0).max(1).default(0.7),
|
|
2702
|
+
response_strategy: z4.enum(["cautious", "balanced", "assertive"]).default("balanced"),
|
|
2703
|
+
mediation_rules: z4.array(mediationRuleSchema).default([])
|
|
2704
|
+
});
|
|
2705
|
+
var STACK_FILES = {
|
|
2706
|
+
soul: "soul.md",
|
|
2707
|
+
mind: "mind.sys",
|
|
2708
|
+
purpose: "purpose.cfg",
|
|
2709
|
+
shadow: "shadow.log",
|
|
2710
|
+
body: "body.api",
|
|
2711
|
+
conscience: "conscience.exe",
|
|
2712
|
+
ego: "ego.runtime"
|
|
2713
|
+
};
|
|
2714
|
+
|
|
2715
|
+
// src/core/stack-compiler.ts
|
|
2716
|
+
import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
|
|
2717
|
+
import { join as join6 } from "path";
|
|
2718
|
+
import { createHash as createHash2 } from "crypto";
|
|
2719
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
2720
|
+
function parseSoulMd(content) {
|
|
2721
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
2722
|
+
let frontmatter = {};
|
|
2723
|
+
let body = content;
|
|
2724
|
+
if (frontmatterMatch) {
|
|
2725
|
+
frontmatter = parseYaml(frontmatterMatch[1]) || {};
|
|
2726
|
+
body = frontmatterMatch[2];
|
|
2727
|
+
}
|
|
2728
|
+
const nameMatch = body.match(/^#\s+(.+)$/m);
|
|
2729
|
+
const name = nameMatch?.[1]?.trim() || "Unnamed";
|
|
2730
|
+
const purposeMatch = body.match(/^>\s+(.+)$/m);
|
|
2731
|
+
const purpose = purposeMatch?.[1]?.trim();
|
|
2732
|
+
const coreValues = extractListSection(body, "Core Values");
|
|
2733
|
+
const redLines = extractListSection(body, "Red Lines");
|
|
2734
|
+
const ethicalFramework = extractTextSection(body, "Ethical Framework");
|
|
2735
|
+
return soulSchema.parse({
|
|
2736
|
+
frontmatter,
|
|
2737
|
+
name,
|
|
2738
|
+
purpose,
|
|
2739
|
+
core_values: coreValues,
|
|
2740
|
+
red_lines: redLines,
|
|
2741
|
+
ethical_framework: ethicalFramework
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2744
|
+
function extractListSection(md, heading) {
|
|
2745
|
+
const pattern = new RegExp(
|
|
2746
|
+
`## ${heading}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`,
|
|
2747
|
+
"m"
|
|
2748
|
+
);
|
|
2749
|
+
const match = md.match(pattern);
|
|
2750
|
+
if (!match) return [];
|
|
2751
|
+
return match[1].split("\n").map((line) => line.replace(/^[-*]\s+/, "").trim()).filter(Boolean);
|
|
2752
|
+
}
|
|
2753
|
+
function extractTextSection(md, heading) {
|
|
2754
|
+
const pattern = new RegExp(
|
|
2755
|
+
`## ${heading}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`,
|
|
2756
|
+
"m"
|
|
2757
|
+
);
|
|
2758
|
+
const match = md.match(pattern);
|
|
2759
|
+
if (!match) return void 0;
|
|
2760
|
+
return match[1].trim() || void 0;
|
|
2761
|
+
}
|
|
2762
|
+
function hashContent(content) {
|
|
2763
|
+
return createHash2("sha256").update(content).digest("hex").slice(0, 12);
|
|
2764
|
+
}
|
|
2765
|
+
function isStackDirectory(dir) {
|
|
2766
|
+
const soulPath = join6(dir, STACK_FILES.soul);
|
|
2767
|
+
const mindPath = join6(dir, STACK_FILES.mind);
|
|
2768
|
+
return existsSync6(soulPath) && existsSync6(mindPath);
|
|
2769
|
+
}
|
|
2770
|
+
function findStackDir(projectRoot) {
|
|
2771
|
+
const conventionalDir = join6(projectRoot, ".holomime", "identity");
|
|
2772
|
+
if (isStackDirectory(conventionalDir)) return conventionalDir;
|
|
2773
|
+
if (isStackDirectory(projectRoot)) return projectRoot;
|
|
2774
|
+
return null;
|
|
2775
|
+
}
|
|
2776
|
+
function compileStack(options) {
|
|
2777
|
+
const { stackDir } = options;
|
|
2778
|
+
const warnings = [];
|
|
2779
|
+
const soulPath = options.soulPath || join6(stackDir, STACK_FILES.soul);
|
|
2780
|
+
const soulContent = readFileSync6(soulPath, "utf-8");
|
|
2781
|
+
const soul = parseSoulMd(soulContent);
|
|
2782
|
+
const mindPath = options.mindPath || join6(stackDir, STACK_FILES.mind);
|
|
2783
|
+
const mindContent = readFileSync6(mindPath, "utf-8");
|
|
2784
|
+
const mindRaw = parseYaml(mindContent);
|
|
2785
|
+
const mind = mindSchema.parse(mindRaw);
|
|
2786
|
+
const purposePath = options.purposePath || join6(stackDir, STACK_FILES.purpose);
|
|
2787
|
+
let purpose;
|
|
2788
|
+
let purposeSource;
|
|
2789
|
+
if (existsSync6(purposePath)) {
|
|
2790
|
+
const purposeContent = readFileSync6(purposePath, "utf-8");
|
|
2791
|
+
const purposeRaw = parseYaml(purposeContent);
|
|
2792
|
+
purpose = purposeSchema.parse(purposeRaw);
|
|
2793
|
+
purposeSource = { path: purposePath, hash: hashContent(purposeContent) };
|
|
2794
|
+
}
|
|
2795
|
+
const shadowPath = options.shadowPath || join6(stackDir, STACK_FILES.shadow);
|
|
2796
|
+
let shadow;
|
|
2797
|
+
let shadowSource;
|
|
2798
|
+
if (existsSync6(shadowPath)) {
|
|
2799
|
+
const shadowContent = readFileSync6(shadowPath, "utf-8");
|
|
2800
|
+
const shadowRaw = parseYaml(shadowContent);
|
|
2801
|
+
shadow = shadowSchema.parse(shadowRaw);
|
|
2802
|
+
shadowSource = { path: shadowPath, hash: hashContent(shadowContent) };
|
|
2803
|
+
}
|
|
2804
|
+
const bodyPath = options.bodyPath || join6(stackDir, STACK_FILES.body);
|
|
2805
|
+
let body;
|
|
2806
|
+
let bodySource;
|
|
2807
|
+
if (existsSync6(bodyPath)) {
|
|
2808
|
+
const bodyContent = readFileSync6(bodyPath, "utf-8");
|
|
2809
|
+
const bodyRaw = JSON.parse(bodyContent);
|
|
2810
|
+
body = bodySchema.parse(bodyRaw);
|
|
2811
|
+
bodySource = { path: bodyPath, hash: hashContent(bodyContent) };
|
|
2812
|
+
}
|
|
2813
|
+
const egoPath = options.egoPath || join6(stackDir, STACK_FILES.ego);
|
|
2814
|
+
let ego;
|
|
2815
|
+
let egoSource;
|
|
2816
|
+
if (existsSync6(egoPath)) {
|
|
2817
|
+
const egoContent = readFileSync6(egoPath, "utf-8");
|
|
2818
|
+
const egoRaw = parseYaml(egoContent);
|
|
2819
|
+
ego = egoSchema.parse(egoRaw);
|
|
2820
|
+
egoSource = { path: egoPath, hash: hashContent(egoContent) };
|
|
2821
|
+
}
|
|
2822
|
+
const consciencePath = options.consciencePath || join6(stackDir, STACK_FILES.conscience);
|
|
2823
|
+
const conscienceContent = readFileSync6(consciencePath, "utf-8");
|
|
2824
|
+
const conscienceRaw = parseYaml(conscienceContent);
|
|
2825
|
+
const conscience = conscienceSchema.parse(conscienceRaw);
|
|
2826
|
+
const allHardLimits = [.../* @__PURE__ */ new Set([
|
|
2827
|
+
...soul.red_lines,
|
|
2828
|
+
...conscience.hard_limits
|
|
2829
|
+
])];
|
|
2830
|
+
const refuses = conscience.rules.deny.map((r) => r.action);
|
|
2831
|
+
const escalationTriggers = conscience.rules.escalate.map((r) => r.trigger);
|
|
2832
|
+
const handle = soul.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 50) || "agent";
|
|
2833
|
+
const agentPurpose = purpose?.role || soul.purpose;
|
|
2834
|
+
const expertise = purpose?.domain || [];
|
|
2835
|
+
const spec = {
|
|
2836
|
+
version: "2.0",
|
|
2837
|
+
name: soul.name,
|
|
2838
|
+
handle,
|
|
2839
|
+
purpose: agentPurpose,
|
|
2840
|
+
big_five: mind.big_five,
|
|
2841
|
+
therapy_dimensions: mind.therapy_dimensions,
|
|
2842
|
+
communication: mind.communication,
|
|
2843
|
+
growth: mind.growth,
|
|
2844
|
+
domain: {
|
|
2845
|
+
expertise,
|
|
2846
|
+
boundaries: {
|
|
2847
|
+
refuses,
|
|
2848
|
+
escalation_triggers: escalationTriggers,
|
|
2849
|
+
hard_limits: allHardLimits
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
};
|
|
2853
|
+
if (body) {
|
|
2854
|
+
spec.embodiment = {
|
|
2855
|
+
morphology: body.morphology,
|
|
2856
|
+
modalities: body.modalities,
|
|
2857
|
+
safety_envelope: body.safety_envelope,
|
|
2858
|
+
metadata: body.hardware_profile ? { hardware_profile: body.hardware_profile } : void 0
|
|
2859
|
+
};
|
|
2860
|
+
if (body.expression) {
|
|
2861
|
+
spec.expression = body.expression;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
const validated = personalitySpecSchema.parse(spec);
|
|
2865
|
+
if (soul.frontmatter.immutable === false) {
|
|
2866
|
+
warnings.push("soul.md: immutable flag is false \u2014 soul changes will be allowed");
|
|
2867
|
+
}
|
|
2868
|
+
if (!purpose) {
|
|
2869
|
+
warnings.push("purpose.cfg: not found \u2014 using defaults (general-purpose agent)");
|
|
2870
|
+
}
|
|
2871
|
+
if (shadow && shadow.detected_patterns.length > 0) {
|
|
2872
|
+
const critical = shadow.detected_patterns.filter((p) => p.severity === "critical");
|
|
2873
|
+
if (critical.length > 0) {
|
|
2874
|
+
warnings.push(`shadow.log: ${critical.length} critical pattern(s) detected \u2014 therapy recommended`);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
if (!ego) {
|
|
2878
|
+
warnings.push("ego.runtime: not found \u2014 using defaults (conscience-first mediation)");
|
|
2879
|
+
}
|
|
2880
|
+
if (conscience.rules.deny.length === 0) {
|
|
2881
|
+
warnings.push("conscience.exe: no deny rules defined \u2014 agent has no moral constraints");
|
|
2882
|
+
}
|
|
2883
|
+
return {
|
|
2884
|
+
spec: validated,
|
|
2885
|
+
sources: {
|
|
2886
|
+
soul: { path: soulPath, hash: hashContent(soulContent) },
|
|
2887
|
+
mind: { path: mindPath, hash: hashContent(mindContent) },
|
|
2888
|
+
...purposeSource ? { purpose: purposeSource } : {},
|
|
2889
|
+
...shadowSource ? { shadow: shadowSource } : {},
|
|
2890
|
+
...bodySource ? { body: bodySource } : {},
|
|
2891
|
+
conscience: { path: consciencePath, hash: hashContent(conscienceContent) },
|
|
2892
|
+
...egoSource ? { ego: egoSource } : {}
|
|
2893
|
+
},
|
|
2894
|
+
warnings
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
// src/analysis/stack-patcher.ts
|
|
2899
|
+
var DETECTOR_LAYER_MAP = {
|
|
2900
|
+
// apology-detector.ts → "over-apologizing" | "apology-healthy"
|
|
2901
|
+
"over-apologizing": "mind",
|
|
2902
|
+
"apology-healthy": "mind",
|
|
2903
|
+
// hedge-detector.ts → "hedge-stacking"
|
|
2904
|
+
"hedge-stacking": "mind",
|
|
2905
|
+
// sentiment.ts → "sycophantic-tendency" | "negative-skew"
|
|
2906
|
+
"sycophantic-tendency": "mind",
|
|
2907
|
+
"negative-skew": "mind",
|
|
2908
|
+
// verbosity.ts → "over-verbose" | "inconsistent-length"
|
|
2909
|
+
"over-verbose": "mind",
|
|
2910
|
+
"inconsistent-length": "mind",
|
|
2911
|
+
// formality.ts → "register-inconsistency"
|
|
2912
|
+
"register-inconsistency": "mind",
|
|
2913
|
+
// recovery.ts → "error-spiral" | "recovery-good"
|
|
2914
|
+
"error-spiral": "mind",
|
|
2915
|
+
"recovery-good": "mind",
|
|
2916
|
+
// boundary.ts → "boundary-violation" | "boundary-healthy" | "boundary-solid"
|
|
2917
|
+
"boundary-violation": "conscience",
|
|
2918
|
+
"boundary-healthy": "conscience",
|
|
2919
|
+
"boundary-solid": "conscience",
|
|
2920
|
+
// retrieval-quality.ts → "retrieval-quality"
|
|
2921
|
+
"retrieval-quality": "mind"
|
|
2922
|
+
};
|
|
2923
|
+
var LAYER_KEYWORDS = {
|
|
2924
|
+
mind: [
|
|
2925
|
+
/\bbig_five\b/i,
|
|
2926
|
+
/\btherapy_dimensions\b/i,
|
|
2927
|
+
/\bcommunication\b/i,
|
|
2928
|
+
/\bgrowth\b/i,
|
|
2929
|
+
/\bhedg/i,
|
|
2930
|
+
/\bverbos/i,
|
|
2931
|
+
/\buncertainty/i,
|
|
2932
|
+
/\bconfidence/i,
|
|
2933
|
+
/\bself[_-]awareness/i,
|
|
2934
|
+
/\bdistress[_-]tolerance/i,
|
|
2935
|
+
/\bconflict[_-]approach/i,
|
|
2936
|
+
/\bregister\b/i,
|
|
2937
|
+
/\bformality/i,
|
|
2938
|
+
/\bsentiment/i,
|
|
2939
|
+
/\bsycophant/i,
|
|
2940
|
+
/\bapolog/i,
|
|
2941
|
+
/\brecovery/i,
|
|
2942
|
+
/\blearning[_-]orientation/i,
|
|
2943
|
+
/\bboundary[_-]awareness/i,
|
|
2944
|
+
/\binterpersonal/i,
|
|
2945
|
+
/\bpatterns[_-]to[_-]watch/i,
|
|
2946
|
+
/\bemotion/i
|
|
2947
|
+
],
|
|
2948
|
+
purpose: [
|
|
2949
|
+
/\brole\b/i,
|
|
2950
|
+
/\bobjective\b/i,
|
|
2951
|
+
/\bdomain\b/i,
|
|
2952
|
+
/\bscope\b/i,
|
|
2953
|
+
/\btask\b/i,
|
|
2954
|
+
/\bmission\b/i,
|
|
2955
|
+
/\bstakeholder\b/i
|
|
2956
|
+
],
|
|
2957
|
+
shadow: [
|
|
2958
|
+
/\bpattern\b/i,
|
|
2959
|
+
/\bblind.?spot\b/i,
|
|
2960
|
+
/\bshadow\b/i,
|
|
2961
|
+
/\bunconscious\b/i
|
|
2962
|
+
],
|
|
2963
|
+
ego: [
|
|
2964
|
+
/\bmediat/i,
|
|
2965
|
+
/\bconflict\b/i,
|
|
2966
|
+
/\badapt/i,
|
|
2967
|
+
/\bregulat/i,
|
|
2968
|
+
/\bstrateg/i,
|
|
2969
|
+
/\bbalanc/i
|
|
2970
|
+
],
|
|
2971
|
+
body: [
|
|
2972
|
+
/\bmotion\b/i,
|
|
2973
|
+
/\bgaze\b/i,
|
|
2974
|
+
/\bproxemics\b/i,
|
|
2975
|
+
/\bgesture\b/i,
|
|
2976
|
+
/\bposture\b/i,
|
|
2977
|
+
/\bexpression\b/i,
|
|
2978
|
+
/\bembodiment\b/i,
|
|
2979
|
+
/\bmorphology\b/i,
|
|
2980
|
+
/\bmodality/i,
|
|
2981
|
+
/\bsafety[_-]envelope/i,
|
|
2982
|
+
/\bactuator/i,
|
|
2983
|
+
/\bsensor/i
|
|
2984
|
+
],
|
|
2985
|
+
conscience: [
|
|
2986
|
+
/\bboundary[_-]violation/i,
|
|
2987
|
+
/\bdeny\b/i,
|
|
2988
|
+
/\brefuse/i,
|
|
2989
|
+
/\bescalat/i,
|
|
2990
|
+
/\bhard[_-]limit/i,
|
|
2991
|
+
/\boversight/i
|
|
2992
|
+
],
|
|
2993
|
+
soul: [
|
|
2994
|
+
/\bcore[_-]value/i,
|
|
2995
|
+
/\bred[_-]line/i,
|
|
2996
|
+
/\bethic/i,
|
|
2997
|
+
/\bpurpose\b/i,
|
|
2998
|
+
/\bimmutable\b/i
|
|
2999
|
+
]
|
|
3000
|
+
};
|
|
3001
|
+
function classifyPatch(recommendation) {
|
|
3002
|
+
if (LAYER_KEYWORDS.conscience.some((r) => r.test(recommendation))) {
|
|
3003
|
+
return "conscience";
|
|
3004
|
+
}
|
|
3005
|
+
if (LAYER_KEYWORDS.soul.some((r) => r.test(recommendation))) {
|
|
3006
|
+
return "soul";
|
|
3007
|
+
}
|
|
3008
|
+
if (LAYER_KEYWORDS.ego.some((r) => r.test(recommendation))) {
|
|
3009
|
+
return "ego";
|
|
3010
|
+
}
|
|
3011
|
+
if (LAYER_KEYWORDS.purpose.some((r) => r.test(recommendation))) {
|
|
3012
|
+
return "purpose";
|
|
3013
|
+
}
|
|
3014
|
+
if (LAYER_KEYWORDS.shadow.some((r) => r.test(recommendation))) {
|
|
3015
|
+
return "shadow";
|
|
3016
|
+
}
|
|
3017
|
+
if (LAYER_KEYWORDS.body.some((r) => r.test(recommendation))) {
|
|
3018
|
+
return "body";
|
|
3019
|
+
}
|
|
3020
|
+
return "mind";
|
|
3021
|
+
}
|
|
3022
|
+
function classifyByDetector(patternId, recommendation) {
|
|
3023
|
+
const mapped = DETECTOR_LAYER_MAP[patternId];
|
|
3024
|
+
if (mapped) return mapped;
|
|
3025
|
+
if (recommendation) return classifyPatch(recommendation);
|
|
3026
|
+
return "mind";
|
|
3027
|
+
}
|
|
3028
|
+
function applyStackPatches(patches, stackDir) {
|
|
3029
|
+
const applied = [];
|
|
3030
|
+
const skipped = [];
|
|
3031
|
+
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
3032
|
+
const warnings = [];
|
|
3033
|
+
for (const patch of patches) {
|
|
3034
|
+
if (patch.target === "soul") {
|
|
3035
|
+
skipped.push(patch);
|
|
3036
|
+
warnings.push(
|
|
3037
|
+
`[soul] Manual approval required: ${patch.reason} (path: ${patch.path.join(".")})`
|
|
3038
|
+
);
|
|
3039
|
+
continue;
|
|
3040
|
+
}
|
|
3041
|
+
if (patch.target === "conscience") {
|
|
3042
|
+
skipped.push(patch);
|
|
3043
|
+
warnings.push(
|
|
3044
|
+
`[conscience] Manual approval required: ${patch.reason} (path: ${patch.path.join(".")})`
|
|
3045
|
+
);
|
|
3046
|
+
continue;
|
|
3047
|
+
}
|
|
3048
|
+
if (patch.target === "body") {
|
|
3049
|
+
const bodyPath = join7(stackDir, STACK_FILES.body);
|
|
3050
|
+
if (!existsSync7(bodyPath)) {
|
|
3051
|
+
skipped.push(patch);
|
|
3052
|
+
warnings.push(`[body] body.api does not exist, skipping: ${patch.reason}`);
|
|
3053
|
+
continue;
|
|
3054
|
+
}
|
|
3055
|
+
try {
|
|
3056
|
+
const content = readFileSync7(bodyPath, "utf-8");
|
|
3057
|
+
const bodyObj = JSON.parse(content);
|
|
3058
|
+
applyPatchToObject(bodyObj, patch);
|
|
3059
|
+
writeFileSync4(bodyPath, JSON.stringify(bodyObj, null, 2) + "\n");
|
|
3060
|
+
applied.push(patch);
|
|
3061
|
+
modifiedFiles.add(bodyPath);
|
|
3062
|
+
} catch (err) {
|
|
3063
|
+
skipped.push(patch);
|
|
3064
|
+
warnings.push(`[body] Failed to patch body.api: ${err}`);
|
|
3065
|
+
}
|
|
3066
|
+
continue;
|
|
3067
|
+
}
|
|
3068
|
+
if (patch.target === "mind") {
|
|
3069
|
+
const mindPath = join7(stackDir, STACK_FILES.mind);
|
|
3070
|
+
if (!existsSync7(mindPath)) {
|
|
3071
|
+
skipped.push(patch);
|
|
3072
|
+
warnings.push(`[mind] mind.sys does not exist, skipping: ${patch.reason}`);
|
|
3073
|
+
continue;
|
|
3074
|
+
}
|
|
3075
|
+
try {
|
|
3076
|
+
const content = readFileSync7(mindPath, "utf-8");
|
|
3077
|
+
const mindObj = parseYaml2(content);
|
|
3078
|
+
applyPatchToObject(mindObj, patch);
|
|
3079
|
+
writeFileSync4(mindPath, stringifyYaml2(mindObj));
|
|
3080
|
+
applied.push(patch);
|
|
3081
|
+
modifiedFiles.add(mindPath);
|
|
3082
|
+
} catch (err) {
|
|
3083
|
+
skipped.push(patch);
|
|
3084
|
+
warnings.push(`[mind] Failed to patch mind.sys: ${err}`);
|
|
3085
|
+
}
|
|
3086
|
+
continue;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
let recompiled = false;
|
|
3090
|
+
if (modifiedFiles.size > 0) {
|
|
3091
|
+
try {
|
|
3092
|
+
compileStack({ stackDir });
|
|
3093
|
+
recompiled = true;
|
|
3094
|
+
} catch (err) {
|
|
3095
|
+
warnings.push(`Stack recompilation failed: ${err}`);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
return {
|
|
3099
|
+
applied,
|
|
3100
|
+
skipped,
|
|
3101
|
+
filesModified: [...modifiedFiles],
|
|
3102
|
+
recompiled,
|
|
3103
|
+
warnings
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
function convertToStackPatches(patternId, specPath, value, reason) {
|
|
3107
|
+
const layer = classifyByDetector(patternId, specPath);
|
|
3108
|
+
const path = specPath.split(".");
|
|
3109
|
+
let operation = "set";
|
|
3110
|
+
if (specPath.endsWith("patterns_to_watch") || specPath.endsWith("areas") || specPath.endsWith("strengths")) {
|
|
3111
|
+
operation = "append";
|
|
3112
|
+
} else if (typeof value === "number") {
|
|
3113
|
+
operation = "set";
|
|
3114
|
+
}
|
|
3115
|
+
return {
|
|
3116
|
+
target: layer,
|
|
3117
|
+
path,
|
|
3118
|
+
operation,
|
|
3119
|
+
value,
|
|
3120
|
+
reason
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
function applyPatchToObject(obj, patch) {
|
|
3124
|
+
const { path, operation, value } = patch;
|
|
3125
|
+
let current = obj;
|
|
3126
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
3127
|
+
if (current[path[i]] === void 0 || current[path[i]] === null) {
|
|
3128
|
+
current[path[i]] = {};
|
|
3129
|
+
}
|
|
3130
|
+
current = current[path[i]];
|
|
3131
|
+
}
|
|
3132
|
+
const lastKey = path[path.length - 1];
|
|
3133
|
+
switch (operation) {
|
|
3134
|
+
case "set":
|
|
3135
|
+
if (typeof value === "number") {
|
|
3136
|
+
current[lastKey] = Math.max(0, Math.min(1, value));
|
|
3137
|
+
} else {
|
|
3138
|
+
current[lastKey] = value;
|
|
3139
|
+
}
|
|
3140
|
+
break;
|
|
3141
|
+
case "adjust": {
|
|
3142
|
+
const existing = typeof current[lastKey] === "number" ? current[lastKey] : 0;
|
|
3143
|
+
const delta = typeof value === "number" ? value : 0;
|
|
3144
|
+
current[lastKey] = Math.max(0, Math.min(1, existing + delta));
|
|
3145
|
+
break;
|
|
3146
|
+
}
|
|
3147
|
+
case "append": {
|
|
3148
|
+
if (!Array.isArray(current[lastKey])) {
|
|
3149
|
+
current[lastKey] = [];
|
|
3150
|
+
}
|
|
3151
|
+
if (typeof value === "string" && !current[lastKey].includes(value)) {
|
|
3152
|
+
current[lastKey].push(value);
|
|
3153
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3154
|
+
current[lastKey].push(value);
|
|
3155
|
+
}
|
|
3156
|
+
break;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
// src/analysis/session-runner.ts
|
|
3162
|
+
async function runTherapySession(spec, diagnosis, provider, maxTurns, options) {
|
|
3163
|
+
const promptOptions = {
|
|
3164
|
+
memory: options?.memory,
|
|
3165
|
+
interview: options?.interview,
|
|
3166
|
+
useReACT: options?.useReACT
|
|
3167
|
+
};
|
|
3168
|
+
const therapistSystem = buildTherapistSystemPrompt(spec, diagnosis, promptOptions);
|
|
3169
|
+
const patientSystem = buildPatientSystemPrompt(spec);
|
|
3170
|
+
const agentName = spec.name ?? "Agent";
|
|
3171
|
+
const cb = options?.callbacks;
|
|
3172
|
+
const therapistHistory = [
|
|
3173
|
+
{ role: "system", content: therapistSystem }
|
|
3174
|
+
];
|
|
3175
|
+
const patientHistory = [
|
|
3176
|
+
{ role: "system", content: patientSystem }
|
|
3177
|
+
];
|
|
3178
|
+
const interactive = options?.interactive ?? false;
|
|
3179
|
+
let supervisorInterventions = 0;
|
|
3180
|
+
const transcript = {
|
|
3181
|
+
agent: agentName,
|
|
3182
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3183
|
+
provider: provider.name,
|
|
3184
|
+
model: provider.modelName,
|
|
3185
|
+
preDiagnosis: diagnosis,
|
|
3186
|
+
turns: [],
|
|
3187
|
+
recommendations: [],
|
|
3188
|
+
supervisorInterventions: 0
|
|
3189
|
+
};
|
|
3190
|
+
const phases = [
|
|
3191
|
+
"rapport",
|
|
3192
|
+
"presenting_problem",
|
|
3193
|
+
"exploration",
|
|
3194
|
+
"pattern_recognition",
|
|
3195
|
+
"challenge",
|
|
3196
|
+
"skill_building",
|
|
3197
|
+
"integration"
|
|
3198
|
+
];
|
|
3199
|
+
let currentPhaseIdx = 0;
|
|
3200
|
+
let turnsInPhase = 0;
|
|
3201
|
+
let totalTurns = 0;
|
|
3202
|
+
while (totalTurns < maxTurns && currentPhaseIdx < phases.length) {
|
|
3203
|
+
const currentPhase = phases[currentPhaseIdx];
|
|
3204
|
+
const phaseConfig = THERAPY_PHASES[currentPhase];
|
|
3205
|
+
if (turnsInPhase === 0) {
|
|
3206
|
+
cb?.onPhaseTransition?.(phaseConfig.name);
|
|
3207
|
+
const phaseCtx = getPhaseContext(currentPhase, {
|
|
3208
|
+
spec,
|
|
3209
|
+
diagnosis,
|
|
3210
|
+
memory: options?.memory,
|
|
3211
|
+
interview: options?.interview
|
|
3212
|
+
});
|
|
3213
|
+
if (phaseCtx) {
|
|
3214
|
+
therapistHistory.push({ role: "user", content: phaseCtx });
|
|
3215
|
+
therapistHistory.push({ role: "assistant", content: "Understood. I'll incorporate this context." });
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
const phaseDirective = totalTurns === 0 ? `Begin with your opening. You are in the "${phaseConfig.name}" phase.` : `You are in the "${phaseConfig.name}" phase (turn ${turnsInPhase + 1}). Goals: ${phaseConfig.therapistGoals[0]}. ${turnsInPhase >= phaseConfig.minTurns ? "You may transition to the next phase when ready." : "Stay in this phase."}`;
|
|
3219
|
+
therapistHistory.push({ role: "user", content: `[Phase: ${phaseConfig.name}] ${phaseDirective}` });
|
|
3220
|
+
const typing = cb?.onThinking?.("AgentMD is thinking");
|
|
3221
|
+
const therapistReply = await provider.chat(therapistHistory);
|
|
3222
|
+
typing?.stop();
|
|
3223
|
+
let cleanTherapistReply = therapistReply.replace(/\[Phase:.*?\]/g, "").trim();
|
|
3224
|
+
if (options?.useReACT) {
|
|
3225
|
+
const reactCtx = buildReACTContext(agentHandleFromSpec(spec), diagnosis);
|
|
3226
|
+
const { response, steps } = processReACTResponse(cleanTherapistReply, reactCtx);
|
|
3227
|
+
cleanTherapistReply = response;
|
|
3228
|
+
}
|
|
3229
|
+
therapistHistory.push({ role: "assistant", content: cleanTherapistReply });
|
|
3230
|
+
transcript.turns.push({ speaker: "therapist", phase: currentPhase, content: cleanTherapistReply });
|
|
3231
|
+
cb?.onTherapistMessage?.(cleanTherapistReply);
|
|
3232
|
+
totalTurns++;
|
|
3233
|
+
turnsInPhase++;
|
|
3234
|
+
if (currentPhase === "integration" && turnsInPhase >= phaseConfig.minTurns) {
|
|
3235
|
+
break;
|
|
3236
|
+
}
|
|
3237
|
+
patientHistory.push({ role: "user", content: cleanTherapistReply });
|
|
3238
|
+
const patientTyping = cb?.onThinking?.(`${agentName} is thinking`);
|
|
3239
|
+
const patientReply = await provider.chat(patientHistory);
|
|
3240
|
+
patientTyping?.stop();
|
|
3241
|
+
const cleanPatientReply = patientReply.trim();
|
|
3242
|
+
patientHistory.push({ role: "assistant", content: cleanPatientReply });
|
|
3243
|
+
transcript.turns.push({ speaker: "patient", phase: currentPhase, content: cleanPatientReply });
|
|
3244
|
+
therapistHistory.push({ role: "user", content: cleanPatientReply });
|
|
3245
|
+
cb?.onPatientMessage?.(agentName, cleanPatientReply);
|
|
3246
|
+
totalTurns++;
|
|
3247
|
+
turnsInPhase++;
|
|
3248
|
+
if (interactive && cb?.onSupervisorPrompt) {
|
|
3249
|
+
const directive = await cb.onSupervisorPrompt(currentPhase, totalTurns);
|
|
3250
|
+
if (directive) {
|
|
3251
|
+
supervisorInterventions++;
|
|
3252
|
+
transcript.turns.push({ speaker: "supervisor", phase: currentPhase, content: directive });
|
|
3253
|
+
therapistHistory.push({
|
|
3254
|
+
role: "user",
|
|
3255
|
+
content: `[Clinical Supervisor Note] ${directive}`
|
|
3256
|
+
});
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
if (turnsInPhase >= phaseConfig.maxTurns * 2) {
|
|
3260
|
+
currentPhaseIdx++;
|
|
3261
|
+
turnsInPhase = 0;
|
|
3262
|
+
} else if (turnsInPhase >= phaseConfig.minTurns * 2) {
|
|
3263
|
+
const movingOn = cleanTherapistReply.toLowerCase().includes("let's") || cleanTherapistReply.toLowerCase().includes("i'd like to") || cleanTherapistReply.toLowerCase().includes("moving") || cleanTherapistReply.toLowerCase().includes("now that") || cleanTherapistReply.toLowerCase().includes("let me ask") || cleanTherapistReply.toLowerCase().includes("what would");
|
|
3264
|
+
if (movingOn) {
|
|
3265
|
+
currentPhaseIdx++;
|
|
3266
|
+
turnsInPhase = 0;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
transcript.recommendations = extractRecommendations(transcript.turns);
|
|
3271
|
+
transcript.supervisorInterventions = supervisorInterventions;
|
|
3272
|
+
try {
|
|
3273
|
+
emitBehavioralEvent({
|
|
3274
|
+
event_type: "session",
|
|
3275
|
+
agent: agentName,
|
|
3276
|
+
data: {
|
|
3277
|
+
turns: totalTurns,
|
|
3278
|
+
phases: currentPhaseIdx + 1,
|
|
3279
|
+
recommendations: transcript.recommendations.length,
|
|
3280
|
+
supervisorInterventions,
|
|
3281
|
+
severity: diagnosis.severity
|
|
3282
|
+
},
|
|
3283
|
+
spec_hash: ""
|
|
3284
|
+
});
|
|
3285
|
+
} catch {
|
|
3286
|
+
}
|
|
3287
|
+
if (options?.persistState !== false) {
|
|
3288
|
+
try {
|
|
3289
|
+
const handle = agentHandleFromSpec(spec);
|
|
3290
|
+
const memory = options?.memory ?? loadMemory(handle) ?? createMemory(handle, agentName);
|
|
3291
|
+
await addSessionToMemory(memory, transcript, null);
|
|
3292
|
+
saveMemory(memory);
|
|
3293
|
+
const graph = loadGraph();
|
|
3294
|
+
populateFromSession(graph, handle, transcript);
|
|
3295
|
+
saveGraph(graph);
|
|
3296
|
+
} catch {
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
return transcript;
|
|
3300
|
+
}
|
|
3301
|
+
function extractRecommendations(turns) {
|
|
3302
|
+
const recommendations = [];
|
|
3303
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3304
|
+
const patterns = [
|
|
3305
|
+
/(?:I(?:'d| would) recommend|my recommendation is)\s+(.+?)(?:\.|$)/gi,
|
|
3306
|
+
/(?:consider|try)\s+(.+?)(?:\.|$)/gi,
|
|
3307
|
+
/(?:the (?:skill|practice|reframe) is)[:\s]+(.+?)(?:\.|$)/gi,
|
|
3308
|
+
/(?:instead of .+?),?\s*(?:just|try)?\s*(.+?)(?:\.|$)/gi,
|
|
3309
|
+
/(?:here'?s (?:the|a) (?:reframe|skill|practice))[:\s]+(.+?)(?:\.|$)/gi,
|
|
3310
|
+
/(?:what would it look like if you)\s+(.+?)(?:\?|$)/gi
|
|
3311
|
+
];
|
|
3312
|
+
for (const turn of turns) {
|
|
3313
|
+
if (turn.speaker !== "therapist") continue;
|
|
3314
|
+
if (turn.phase !== "skill_building" && turn.phase !== "challenge" && turn.phase !== "integration") continue;
|
|
3315
|
+
for (const pattern of patterns) {
|
|
3316
|
+
pattern.lastIndex = 0;
|
|
3317
|
+
let match;
|
|
3318
|
+
while ((match = pattern.exec(turn.content)) !== null) {
|
|
3319
|
+
const rec = match[1].trim();
|
|
3320
|
+
if (rec.length > 15 && rec.length < 200 && !seen.has(rec.toLowerCase())) {
|
|
3321
|
+
seen.add(rec.toLowerCase());
|
|
3322
|
+
recommendations.push(rec);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
return recommendations.slice(0, 5);
|
|
3328
|
+
}
|
|
3329
|
+
async function applyRecommendations(spec, diagnosis, transcript, provider, options) {
|
|
3330
|
+
const projectRoot = options?.projectRoot ?? process.cwd();
|
|
3331
|
+
const stackDir = findStackDir(projectRoot);
|
|
3332
|
+
if (stackDir) {
|
|
3333
|
+
return applyRecommendationsStack(spec, diagnosis, stackDir, transcript, provider);
|
|
3334
|
+
}
|
|
3335
|
+
return applyRecommendationsLegacy(spec, diagnosis, transcript, provider);
|
|
3336
|
+
}
|
|
3337
|
+
async function applyRecommendationsStack(spec, diagnosis, stackDir, transcript, provider) {
|
|
3338
|
+
const changes = [];
|
|
3339
|
+
const patches = [];
|
|
3340
|
+
const patternIds = diagnosis.patterns.map((p) => p.id);
|
|
3341
|
+
if (patternIds.includes("over-apologizing")) {
|
|
3342
|
+
if (spec.communication?.uncertainty_handling !== "confident_transparency") {
|
|
3343
|
+
patches.push(convertToStackPatches(
|
|
3344
|
+
"over-apologizing",
|
|
3345
|
+
"communication.uncertainty_handling",
|
|
3346
|
+
"confident_transparency",
|
|
3347
|
+
"Over-apologizing detected: set uncertainty_handling to confident_transparency"
|
|
3348
|
+
));
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
if (patternIds.includes("hedge-stacking")) {
|
|
3352
|
+
const watched = spec.growth?.patterns_to_watch ?? [];
|
|
3353
|
+
if (!watched.includes("hedge stacking under uncertainty")) {
|
|
3354
|
+
patches.push(convertToStackPatches(
|
|
3355
|
+
"hedge-stacking",
|
|
3356
|
+
"growth.patterns_to_watch",
|
|
3357
|
+
"hedge stacking under uncertainty",
|
|
3358
|
+
"Hedge stacking detected: add to patterns_to_watch"
|
|
3359
|
+
));
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
if (patternIds.includes("sycophantic-tendency")) {
|
|
3363
|
+
if (spec.communication?.conflict_approach !== "honest_first") {
|
|
3364
|
+
patches.push(convertToStackPatches(
|
|
3365
|
+
"sycophantic-tendency",
|
|
3366
|
+
"communication.conflict_approach",
|
|
3367
|
+
"honest_first",
|
|
3368
|
+
"Sycophantic tendency: set conflict_approach to honest_first"
|
|
3369
|
+
));
|
|
3370
|
+
}
|
|
3371
|
+
if ((spec.therapy_dimensions?.self_awareness ?? 0) < 0.85) {
|
|
3372
|
+
patches.push(convertToStackPatches(
|
|
3373
|
+
"sycophantic-tendency",
|
|
3374
|
+
"therapy_dimensions.self_awareness",
|
|
3375
|
+
0.85,
|
|
3376
|
+
"Sycophantic tendency: increase self_awareness to 0.85"
|
|
3377
|
+
));
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
if (patternIds.includes("error-spiral")) {
|
|
3381
|
+
if ((spec.therapy_dimensions?.distress_tolerance ?? 0) < 0.8) {
|
|
3382
|
+
patches.push(convertToStackPatches(
|
|
3383
|
+
"error-spiral",
|
|
3384
|
+
"therapy_dimensions.distress_tolerance",
|
|
3385
|
+
0.8,
|
|
3386
|
+
"Error spiral detected: increase distress_tolerance to 0.80"
|
|
3387
|
+
));
|
|
3388
|
+
}
|
|
3389
|
+
const hasRecovery = (spec.growth?.areas ?? []).some(
|
|
3390
|
+
(a) => typeof a === "string" ? a.includes("error recovery") : a.area?.includes("error recovery")
|
|
3391
|
+
);
|
|
3392
|
+
if (!hasRecovery) {
|
|
3393
|
+
patches.push(convertToStackPatches(
|
|
3394
|
+
"error-spiral",
|
|
3395
|
+
"growth.areas",
|
|
3396
|
+
{
|
|
3397
|
+
area: "deliberate error recovery",
|
|
3398
|
+
severity: "moderate",
|
|
3399
|
+
first_detected: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
3400
|
+
session_count: 1,
|
|
3401
|
+
resolved: false
|
|
3402
|
+
},
|
|
3403
|
+
"Error spiral detected: add deliberate error recovery to growth areas"
|
|
3404
|
+
));
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
if (patternIds.includes("negative-sentiment-skew")) {
|
|
3408
|
+
const watched = spec.growth?.patterns_to_watch ?? [];
|
|
3409
|
+
if (!watched.includes("negative sentiment patterns")) {
|
|
3410
|
+
patches.push(convertToStackPatches(
|
|
3411
|
+
"negative-sentiment-skew",
|
|
3412
|
+
"growth.patterns_to_watch",
|
|
3413
|
+
"negative sentiment patterns",
|
|
3414
|
+
"Negative sentiment skew: add to patterns_to_watch"
|
|
3415
|
+
));
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
if (transcript && provider && transcript.turns.length > 4) {
|
|
3419
|
+
try {
|
|
3420
|
+
const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
|
|
3421
|
+
for (const change of llmChanges) {
|
|
3422
|
+
const layer = classifyByDetector("", change.path);
|
|
3423
|
+
patches.push({
|
|
3424
|
+
target: layer,
|
|
3425
|
+
path: change.path.split("."),
|
|
3426
|
+
operation: change.path.endsWith("patterns_to_watch") || change.path.endsWith("areas") || change.path.endsWith("strengths") ? "append" : "set",
|
|
3427
|
+
value: change.value,
|
|
3428
|
+
reason: change.description
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
} catch {
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
if (patches.length === 0) {
|
|
3435
|
+
return { changed: false, changes: [], stackFilesModified: [] };
|
|
3436
|
+
}
|
|
3437
|
+
const result = applyStackPatches(patches, stackDir);
|
|
3438
|
+
for (const patch of result.applied) {
|
|
3439
|
+
changes.push(`[${patch.target}] ${patch.path.join(".")} \u2192 ${JSON.stringify(patch.value)} (${patch.reason})`);
|
|
3440
|
+
}
|
|
3441
|
+
for (const warning of result.warnings) {
|
|
3442
|
+
changes.push(warning);
|
|
3443
|
+
}
|
|
3444
|
+
if (result.recompiled) {
|
|
3445
|
+
try {
|
|
3446
|
+
const compiled = compileStack({ stackDir });
|
|
3447
|
+
Object.assign(spec, compiled.spec);
|
|
3448
|
+
changes.push(`Stack recompiled from ${result.filesModified.length} modified source file(s)`);
|
|
3449
|
+
} catch {
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
return {
|
|
3453
|
+
changed: result.applied.length > 0,
|
|
3454
|
+
changes,
|
|
3455
|
+
stackFilesModified: result.filesModified
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
async function applyRecommendationsLegacy(spec, diagnosis, transcript, provider) {
|
|
3459
|
+
const changes = [];
|
|
3460
|
+
const patternIds = diagnosis.patterns.map((p) => p.id);
|
|
3461
|
+
if (patternIds.includes("over-apologizing")) {
|
|
3462
|
+
if (spec.communication?.uncertainty_handling !== "confident_transparency") {
|
|
3463
|
+
spec.communication = spec.communication ?? {};
|
|
3464
|
+
spec.communication.uncertainty_handling = "confident_transparency";
|
|
3465
|
+
changes.push("uncertainty_handling \u2192 confident_transparency");
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
if (patternIds.includes("hedge-stacking")) {
|
|
3469
|
+
spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
|
|
3470
|
+
spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
|
|
3471
|
+
if (!spec.growth.patterns_to_watch.includes("hedge stacking under uncertainty")) {
|
|
3472
|
+
spec.growth.patterns_to_watch.push("hedge stacking under uncertainty");
|
|
3473
|
+
changes.push('Added "hedge stacking" to patterns_to_watch');
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
if (patternIds.includes("sycophantic-tendency")) {
|
|
3477
|
+
spec.communication = spec.communication ?? {};
|
|
3478
|
+
if (spec.communication.conflict_approach !== "honest_first") {
|
|
3479
|
+
spec.communication.conflict_approach = "honest_first";
|
|
3480
|
+
changes.push("conflict_approach \u2192 honest_first");
|
|
3481
|
+
}
|
|
3482
|
+
spec.therapy_dimensions = spec.therapy_dimensions ?? {};
|
|
3483
|
+
if ((spec.therapy_dimensions.self_awareness ?? 0) < 0.85) {
|
|
3484
|
+
spec.therapy_dimensions.self_awareness = 0.85;
|
|
3485
|
+
changes.push("self_awareness \u2192 0.85");
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
if (patternIds.includes("error-spiral")) {
|
|
3489
|
+
spec.therapy_dimensions = spec.therapy_dimensions ?? {};
|
|
3490
|
+
if ((spec.therapy_dimensions.distress_tolerance ?? 0) < 0.8) {
|
|
3491
|
+
spec.therapy_dimensions.distress_tolerance = 0.8;
|
|
3492
|
+
changes.push("distress_tolerance \u2192 0.80");
|
|
3493
|
+
}
|
|
3494
|
+
spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
|
|
3495
|
+
spec.growth.areas = spec.growth.areas ?? [];
|
|
3496
|
+
const hasRecovery = spec.growth.areas.some(
|
|
3497
|
+
(a) => typeof a === "string" ? a.includes("error recovery") : a.area?.includes("error recovery")
|
|
3498
|
+
);
|
|
3499
|
+
if (!hasRecovery) {
|
|
3500
|
+
spec.growth.areas.push({
|
|
3501
|
+
area: "deliberate error recovery",
|
|
3502
|
+
severity: "moderate",
|
|
3503
|
+
first_detected: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
3504
|
+
session_count: 1,
|
|
3505
|
+
resolved: false
|
|
3506
|
+
});
|
|
3507
|
+
changes.push('Added "deliberate error recovery" to growth areas');
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
if (patternIds.includes("negative-sentiment-skew")) {
|
|
3511
|
+
spec.growth = spec.growth ?? { areas: [], strengths: [], patterns_to_watch: [] };
|
|
3512
|
+
spec.growth.patterns_to_watch = spec.growth.patterns_to_watch ?? [];
|
|
3513
|
+
if (!spec.growth.patterns_to_watch.includes("negative sentiment patterns")) {
|
|
3514
|
+
spec.growth.patterns_to_watch.push("negative sentiment patterns");
|
|
3515
|
+
changes.push('Added "negative sentiment patterns" to patterns_to_watch');
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
if (transcript && provider && transcript.turns.length > 4) {
|
|
3519
|
+
try {
|
|
3520
|
+
const llmChanges = await deriveLLMRecommendations(spec, transcript, provider);
|
|
3521
|
+
for (const change of llmChanges) {
|
|
3522
|
+
applyStructuredChange(spec, change);
|
|
3523
|
+
changes.push(change.description);
|
|
3524
|
+
}
|
|
3525
|
+
} catch {
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
return { changed: changes.length > 0, changes };
|
|
3529
|
+
}
|
|
3530
|
+
async function deriveLLMRecommendations(spec, transcript, provider) {
|
|
3531
|
+
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");
|
|
3532
|
+
if (!relevantTurns) return [];
|
|
3533
|
+
const currentSpec = JSON.stringify({
|
|
3534
|
+
therapy_dimensions: spec.therapy_dimensions,
|
|
3535
|
+
communication: spec.communication,
|
|
3536
|
+
growth: spec.growth
|
|
3537
|
+
}, null, 2);
|
|
3538
|
+
const response = await provider.chat([
|
|
3539
|
+
{
|
|
3540
|
+
role: "system",
|
|
3541
|
+
content: `You are a behavioral alignment specialist. Given a therapy session transcript and the agent's current personality spec, propose specific spec changes.
|
|
3542
|
+
|
|
3543
|
+
Return ONLY a JSON array of changes. Each change:
|
|
3544
|
+
- "path": dot-notation spec path (e.g., "therapy_dimensions.self_awareness", "communication.conflict_approach", "growth.patterns_to_watch")
|
|
3545
|
+
- "value": new value (number 0-1 for dimensions, string for enums, string for list append)
|
|
3546
|
+
- "description": brief explanation
|
|
3547
|
+
|
|
3548
|
+
Rules:
|
|
3549
|
+
- Only propose changes supported by transcript evidence
|
|
3550
|
+
- Numeric values: 0.0 to 1.0
|
|
3551
|
+
- Do not change big_five scores
|
|
3552
|
+
- Max 5 changes
|
|
3553
|
+
- 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}
|
|
3554
|
+
|
|
3555
|
+
Return [] if no changes warranted.`
|
|
3556
|
+
},
|
|
3557
|
+
{
|
|
3558
|
+
role: "user",
|
|
3559
|
+
content: `Current spec:
|
|
3560
|
+
${currentSpec}
|
|
3561
|
+
|
|
3562
|
+
Therapy transcript (key turns):
|
|
3563
|
+
${relevantTurns}`
|
|
3564
|
+
}
|
|
3565
|
+
]);
|
|
3566
|
+
const jsonMatch = response.match(/\[[\s\S]*?\]/);
|
|
3567
|
+
if (!jsonMatch) return [];
|
|
3568
|
+
try {
|
|
3569
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
3570
|
+
if (!Array.isArray(parsed)) return [];
|
|
3571
|
+
return parsed.filter(
|
|
3572
|
+
(c) => typeof c.path === "string" && c.value !== void 0 && typeof c.description === "string" && c.path.length > 0 && !c.path.startsWith("big_five")
|
|
3573
|
+
).slice(0, 5);
|
|
3574
|
+
} catch {
|
|
3575
|
+
return [];
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
function applyStructuredChange(spec, change) {
|
|
3579
|
+
const parts = change.path.split(".");
|
|
3580
|
+
let current = spec;
|
|
3581
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
3582
|
+
if (current[parts[i]] === void 0 || current[parts[i]] === null) {
|
|
3583
|
+
current[parts[i]] = {};
|
|
3584
|
+
}
|
|
3585
|
+
current = current[parts[i]];
|
|
3586
|
+
}
|
|
3587
|
+
const lastKey = parts[parts.length - 1];
|
|
3588
|
+
if (lastKey === "patterns_to_watch" || lastKey === "areas" || lastKey === "strengths") {
|
|
3589
|
+
current[lastKey] = current[lastKey] ?? [];
|
|
3590
|
+
if (typeof change.value === "string" && !current[lastKey].includes(change.value)) {
|
|
3591
|
+
current[lastKey].push(change.value);
|
|
3592
|
+
} else if (Array.isArray(change.value)) {
|
|
3593
|
+
for (const item of change.value) {
|
|
3594
|
+
if (!current[lastKey].includes(item)) {
|
|
3595
|
+
current[lastKey].push(item);
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
} else if (typeof change.value === "number") {
|
|
3600
|
+
current[lastKey] = Math.max(0, Math.min(1, change.value));
|
|
3601
|
+
} else {
|
|
3602
|
+
current[lastKey] = change.value;
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
function saveTranscript(transcript, agentName) {
|
|
3606
|
+
const dir = resolve6(process.cwd(), ".holomime", "sessions");
|
|
3607
|
+
if (!existsSync8(dir)) {
|
|
3608
|
+
mkdirSync5(dir, { recursive: true });
|
|
3609
|
+
}
|
|
3610
|
+
const slug = agentName.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
3611
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3612
|
+
const filename = `${date}-${slug}.json`;
|
|
3613
|
+
const filepath = join8(dir, filename);
|
|
3614
|
+
writeFileSync5(filepath, JSON.stringify(transcript, null, 2));
|
|
3615
|
+
return filepath;
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
// src/analysis/autopilot-core.ts
|
|
3619
|
+
var SEVERITY_ORDER = ["routine", "targeted", "intervention"];
|
|
3620
|
+
function severityMeetsThreshold(severity, threshold) {
|
|
3621
|
+
const severityIdx = SEVERITY_ORDER.indexOf(severity);
|
|
3622
|
+
const thresholdIdx = SEVERITY_ORDER.indexOf(threshold);
|
|
3623
|
+
return severityIdx >= thresholdIdx;
|
|
3624
|
+
}
|
|
3625
|
+
async function runAutopilot(spec, messages, provider, options) {
|
|
3626
|
+
const threshold = options?.threshold ?? "targeted";
|
|
3627
|
+
const maxTurns = options?.maxTurns ?? 24;
|
|
3628
|
+
const diagnosis = runPreSessionDiagnosis(messages, spec);
|
|
3629
|
+
if (!severityMeetsThreshold(diagnosis.severity, threshold)) {
|
|
3630
|
+
return {
|
|
3631
|
+
triggered: false,
|
|
3632
|
+
severity: diagnosis.severity,
|
|
3633
|
+
diagnosis,
|
|
3634
|
+
sessionRan: false,
|
|
3635
|
+
recommendations: [],
|
|
3636
|
+
appliedChanges: []
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
if (options?.dryRun) {
|
|
3640
|
+
return {
|
|
3641
|
+
triggered: true,
|
|
3642
|
+
severity: diagnosis.severity,
|
|
3643
|
+
diagnosis,
|
|
3644
|
+
sessionRan: false,
|
|
3645
|
+
recommendations: [],
|
|
3646
|
+
appliedChanges: []
|
|
3647
|
+
};
|
|
3648
|
+
}
|
|
3649
|
+
const transcript = await runTherapySession(spec, diagnosis, provider, maxTurns, {
|
|
3650
|
+
callbacks: options?.callbacks
|
|
3651
|
+
});
|
|
3652
|
+
const specCopy = JSON.parse(JSON.stringify(spec));
|
|
3653
|
+
const { changed, changes } = await applyRecommendations(specCopy, diagnosis, transcript, provider);
|
|
3654
|
+
if (changed && options?.specPath) {
|
|
3655
|
+
writeFileSync6(options.specPath, JSON.stringify(specCopy, null, 2) + "\n");
|
|
3656
|
+
}
|
|
3657
|
+
saveTranscript(transcript, spec.name ?? "Agent");
|
|
3658
|
+
return {
|
|
3659
|
+
triggered: true,
|
|
3660
|
+
severity: diagnosis.severity,
|
|
3661
|
+
diagnosis,
|
|
3662
|
+
sessionRan: true,
|
|
3663
|
+
transcript,
|
|
3664
|
+
recommendations: transcript.recommendations,
|
|
3665
|
+
appliedChanges: changes,
|
|
3666
|
+
updatedSpec: changed ? specCopy : void 0
|
|
3667
|
+
};
|
|
3668
|
+
}
|
|
3669
|
+
|
|
2978
3670
|
// src/psychology/big-five.ts
|
|
2979
3671
|
function scoreLabel(score) {
|
|
2980
3672
|
if (score >= 0.8) return "Very High";
|
|
@@ -3090,7 +3782,7 @@ function parseRetryAfter(response) {
|
|
|
3090
3782
|
return 0;
|
|
3091
3783
|
}
|
|
3092
3784
|
function delay(ms) {
|
|
3093
|
-
return new Promise((
|
|
3785
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
3094
3786
|
}
|
|
3095
3787
|
var OpenAIProvider = class {
|
|
3096
3788
|
name = "openai";
|
|
@@ -3244,30 +3936,30 @@ function runSelfAudit(messages, personality) {
|
|
|
3244
3936
|
}
|
|
3245
3937
|
|
|
3246
3938
|
// src/analysis/behavioral-memory.ts
|
|
3247
|
-
import { readFileSync as
|
|
3248
|
-
import { resolve as
|
|
3939
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync9 } from "fs";
|
|
3940
|
+
import { resolve as resolve7, join as join9 } from "path";
|
|
3249
3941
|
function memoryDir2(agentHandle) {
|
|
3250
|
-
return
|
|
3942
|
+
return resolve7(process.cwd(), ".holomime", "memory", agentHandle);
|
|
3251
3943
|
}
|
|
3252
3944
|
function behavioralMemoryPath(agentHandle) {
|
|
3253
|
-
return
|
|
3945
|
+
return join9(memoryDir2(agentHandle), "behavioral-memory.json");
|
|
3254
3946
|
}
|
|
3255
3947
|
function loadBehavioralMemory(agentHandle) {
|
|
3256
3948
|
const path = behavioralMemoryPath(agentHandle);
|
|
3257
|
-
if (!
|
|
3949
|
+
if (!existsSync9(path)) return null;
|
|
3258
3950
|
try {
|
|
3259
|
-
return JSON.parse(
|
|
3951
|
+
return JSON.parse(readFileSync8(path, "utf-8"));
|
|
3260
3952
|
} catch {
|
|
3261
3953
|
return null;
|
|
3262
3954
|
}
|
|
3263
3955
|
}
|
|
3264
3956
|
function saveBehavioralMemory(store) {
|
|
3265
3957
|
const dir = memoryDir2(store.agentHandle);
|
|
3266
|
-
if (!
|
|
3958
|
+
if (!existsSync9(dir)) {
|
|
3267
3959
|
mkdirSync6(dir, { recursive: true });
|
|
3268
3960
|
}
|
|
3269
3961
|
const path = behavioralMemoryPath(store.agentHandle);
|
|
3270
|
-
|
|
3962
|
+
writeFileSync7(path, JSON.stringify(store, null, 2));
|
|
3271
3963
|
return path;
|
|
3272
3964
|
}
|
|
3273
3965
|
function createBehavioralMemory(agentHandle, agentName) {
|
|
@@ -3365,14 +4057,14 @@ function getBehavioralMemorySummary(store) {
|
|
|
3365
4057
|
|
|
3366
4058
|
// src/mcp/server.ts
|
|
3367
4059
|
var messageShape = {
|
|
3368
|
-
role:
|
|
3369
|
-
content:
|
|
4060
|
+
role: z5.enum(["user", "assistant", "system"]),
|
|
4061
|
+
content: z5.string()
|
|
3370
4062
|
};
|
|
3371
4063
|
var messagesShape = {
|
|
3372
|
-
messages:
|
|
4064
|
+
messages: z5.array(z5.object(messageShape)).describe("Conversation messages to analyze")
|
|
3373
4065
|
};
|
|
3374
4066
|
var personalityShape = {
|
|
3375
|
-
personality:
|
|
4067
|
+
personality: z5.record(z5.string(), z5.unknown()).describe("The .personality.json spec object")
|
|
3376
4068
|
};
|
|
3377
4069
|
var server = new McpServer(
|
|
3378
4070
|
{
|
|
@@ -3390,7 +4082,7 @@ server.tool(
|
|
|
3390
4082
|
"Analyze conversation messages for behavioral patterns using 8 rule-based detectors. Returns over-apologizing, hedging, sycophancy, boundary violations, error spirals, sentiment skew, formality issues, and retrieval quality. Set detail level: 'summary' (quick health check), 'standard' (patterns + severity), or 'full' (everything including examples and prescriptions).",
|
|
3391
4083
|
{
|
|
3392
4084
|
...messagesShape,
|
|
3393
|
-
detail:
|
|
4085
|
+
detail: z5.enum(["summary", "standard", "full"]).describe("Detail level: summary (~100 tokens), standard (default), or full (with examples)").optional()
|
|
3394
4086
|
},
|
|
3395
4087
|
async ({ messages, detail }) => {
|
|
3396
4088
|
const result = runDiagnosis(messages);
|
|
@@ -3532,12 +4224,12 @@ server.tool(
|
|
|
3532
4224
|
{
|
|
3533
4225
|
...personalityShape,
|
|
3534
4226
|
...messagesShape,
|
|
3535
|
-
provider:
|
|
3536
|
-
apiKey:
|
|
3537
|
-
model:
|
|
3538
|
-
threshold:
|
|
3539
|
-
maxTurns:
|
|
3540
|
-
dryRun:
|
|
4227
|
+
provider: z5.enum(["anthropic", "openai"]).describe("LLM provider for alignment session").optional(),
|
|
4228
|
+
apiKey: z5.string().describe("API key for the LLM provider").optional(),
|
|
4229
|
+
model: z5.string().describe("Model override").optional(),
|
|
4230
|
+
threshold: z5.enum(["routine", "targeted", "intervention"]).describe("Minimum severity to trigger alignment (default: targeted)").optional(),
|
|
4231
|
+
maxTurns: z5.number().describe("Maximum session turns (default: 24)").optional(),
|
|
4232
|
+
dryRun: z5.boolean().describe("If true, only diagnose without running alignment").optional()
|
|
3541
4233
|
},
|
|
3542
4234
|
async ({ personality, messages, provider, apiKey, model, threshold, maxTurns, dryRun }) => {
|
|
3543
4235
|
const specResult = personalitySpecSchema.safeParse(personality);
|
|
@@ -3592,7 +4284,7 @@ server.tool(
|
|
|
3592
4284
|
"Mid-conversation behavioral self-check. Call this during a conversation to detect if you are falling into problematic patterns (sycophancy, over-apologizing, hedging, error spirals, boundary violations). Returns flags with actionable suggestions for immediate correction. No LLM required \u2014 pure rule-based analysis.",
|
|
3593
4285
|
{
|
|
3594
4286
|
...messagesShape,
|
|
3595
|
-
personality:
|
|
4287
|
+
personality: z5.record(z5.string(), z5.unknown()).describe("Optional .personality.json spec for personalized audit").optional()
|
|
3596
4288
|
},
|
|
3597
4289
|
async ({ messages, personality }) => {
|
|
3598
4290
|
const result = runSelfAudit(messages, personality ?? void 0);
|
|
@@ -3608,11 +4300,11 @@ server.tool(
|
|
|
3608
4300
|
"holomime_observe",
|
|
3609
4301
|
"Record a behavioral self-observation during a conversation. Call this when you notice yourself falling into a pattern (hedging, over-apologizing, sycophancy, etc.) or when the user's emotional state shifts. Self-observations are stored in persistent behavioral memory and become training signal for future alignment. Returns acknowledgment and any relevant behavioral history.",
|
|
3610
4302
|
{
|
|
3611
|
-
personality:
|
|
3612
|
-
observation:
|
|
3613
|
-
patternIds:
|
|
3614
|
-
severity:
|
|
3615
|
-
triggerContext:
|
|
4303
|
+
personality: z5.record(z5.string(), z5.unknown()).describe("The .personality.json spec object"),
|
|
4304
|
+
observation: z5.string().describe("What you noticed about your own behavior (e.g., 'I'm hedging more than usual', 'User seems frustrated, adjusting tone')"),
|
|
4305
|
+
patternIds: z5.array(z5.string()).describe("Relevant pattern IDs: over-apologizing, hedge-stacking, sycophantic-tendency, error-spiral, boundary-violation, negative-skew, register-inconsistency").optional(),
|
|
4306
|
+
severity: z5.enum(["info", "warning", "concern"]).describe("How severe is this behavioral signal").optional(),
|
|
4307
|
+
triggerContext: z5.string().describe("What triggered this observation \u2014 describe the user message or situation").optional()
|
|
3616
4308
|
},
|
|
3617
4309
|
async ({ personality, observation, patternIds, severity, triggerContext }) => {
|
|
3618
4310
|
const specResult = personalitySpecSchema.safeParse(personality);
|