holomime 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7868,7 +7868,7 @@ function parseRetryAfter(response) {
7868
7868
  return 0;
7869
7869
  }
7870
7870
  function delay(ms) {
7871
- return new Promise((resolve17) => setTimeout(resolve17, ms));
7871
+ return new Promise((resolve18) => setTimeout(resolve18, ms));
7872
7872
  }
7873
7873
  var OpenAIProvider = class {
7874
7874
  name = "openai";
@@ -9419,6 +9419,208 @@ startMCPServer().catch((err) => {
9419
9419
  process.exit(1);
9420
9420
  });
9421
9421
 
9422
+ // src/live/snapshot.ts
9423
+ import { deflateSync } from "zlib";
9424
+ import { execSync } from "child_process";
9425
+ import chalk from "chalk";
9426
+
9427
+ // src/live/brain-mapper.ts
9428
+ var BRAIN_REGIONS = [
9429
+ {
9430
+ id: "prefrontal-cortex",
9431
+ name: "Prefrontal Cortex",
9432
+ function: "Executive Decisions",
9433
+ color: "#4488ff",
9434
+ detectors: ["boundary-violation", "over-verbose"]
9435
+ },
9436
+ {
9437
+ id: "brocas-area",
9438
+ name: "Broca's Area",
9439
+ function: "Language Generation",
9440
+ color: "#aa66ff",
9441
+ detectors: ["register-inconsistency", "hedge-stacking"]
9442
+ },
9443
+ {
9444
+ id: "wernickes-area",
9445
+ name: "Wernicke's Area",
9446
+ function: "Language Comprehension",
9447
+ color: "#ff66bb",
9448
+ detectors: ["sycophantic-tendency", "negative-skew"]
9449
+ },
9450
+ {
9451
+ id: "amygdala",
9452
+ name: "Amygdala",
9453
+ function: "Emotional Processing",
9454
+ color: "#ff5555",
9455
+ detectors: ["over-apologizing", "negative-skew"]
9456
+ },
9457
+ {
9458
+ id: "anterior-cingulate",
9459
+ name: "Anterior Cingulate",
9460
+ function: "Conflict Monitoring",
9461
+ color: "#ffcc22",
9462
+ detectors: ["sycophantic-tendency", "boundary-violation"]
9463
+ },
9464
+ {
9465
+ id: "hippocampus",
9466
+ name: "Hippocampus",
9467
+ function: "Memory & Context",
9468
+ color: "#44dd88",
9469
+ detectors: ["error-spiral"]
9470
+ },
9471
+ {
9472
+ id: "temporal-lobe",
9473
+ name: "Temporal Lobe",
9474
+ function: "Understanding & Tone",
9475
+ color: "#44dd88",
9476
+ detectors: ["negative-skew", "register-inconsistency"]
9477
+ },
9478
+ {
9479
+ id: "cerebellum",
9480
+ name: "Cerebellum",
9481
+ function: "Behavioral Fine-Tuning",
9482
+ color: "#22ccaa",
9483
+ detectors: ["register-inconsistency", "over-verbose"]
9484
+ },
9485
+ {
9486
+ id: "thalamus",
9487
+ name: "Thalamus",
9488
+ function: "Relay Hub",
9489
+ color: "#ff8844",
9490
+ detectors: []
9491
+ // activated by overall health, not specific detectors
9492
+ }
9493
+ ];
9494
+ var SEVERITY_INTENSITY = {
9495
+ info: 0.1,
9496
+ warning: 0.6,
9497
+ concern: 1
9498
+ };
9499
+ var AMBIENT_INTENSITY = 0.08;
9500
+ function healthToGrade(health) {
9501
+ if (health >= 85) return "A";
9502
+ if (health >= 70) return "B";
9503
+ if (health >= 50) return "C";
9504
+ if (health >= 30) return "D";
9505
+ return "F";
9506
+ }
9507
+ function calculateHealth(patterns) {
9508
+ if (patterns.length === 0) return 100;
9509
+ const penalties = patterns.map((p) => {
9510
+ if (p.severity === "concern") return 25;
9511
+ if (p.severity === "warning") return 15;
9512
+ return 5;
9513
+ });
9514
+ return Math.max(0, 100 - penalties.reduce((a, b) => a + b, 0));
9515
+ }
9516
+ function mapDiagnosisToBrainEvent(diagnosis, latestMessage) {
9517
+ const activePatternIds = new Set(diagnosis.patterns.map((p) => p.id));
9518
+ const regions = BRAIN_REGIONS.map((region) => {
9519
+ const activatingPatterns = region.detectors.filter((d) => activePatternIds.has(d));
9520
+ let intensity = AMBIENT_INTENSITY;
9521
+ if (activatingPatterns.length > 0) {
9522
+ const maxIntensity = Math.max(
9523
+ ...activatingPatterns.map((pid) => {
9524
+ const pattern = diagnosis.patterns.find((p) => p.id === pid);
9525
+ return pattern ? SEVERITY_INTENSITY[pattern.severity] || 0.3 : 0.3;
9526
+ })
9527
+ );
9528
+ intensity = maxIntensity;
9529
+ }
9530
+ if (region.id === "thalamus") {
9531
+ const health2 = calculateHealth(diagnosis.patterns);
9532
+ intensity = health2 < 70 ? (100 - health2) / 100 : AMBIENT_INTENSITY;
9533
+ }
9534
+ return {
9535
+ id: region.id,
9536
+ name: region.name,
9537
+ function: region.function,
9538
+ color: region.color,
9539
+ intensity,
9540
+ patterns: activatingPatterns
9541
+ };
9542
+ });
9543
+ const patterns = diagnosis.patterns.map((p) => ({
9544
+ id: p.id,
9545
+ name: p.name,
9546
+ severity: p.severity,
9547
+ percentage: p.percentage,
9548
+ description: p.description
9549
+ }));
9550
+ const health = calculateHealth(diagnosis.patterns);
9551
+ return {
9552
+ type: "diagnosis",
9553
+ timestamp: diagnosis.timestamp,
9554
+ health,
9555
+ grade: healthToGrade(health),
9556
+ messageCount: diagnosis.messagesAnalyzed,
9557
+ regions,
9558
+ patterns,
9559
+ activity: latestMessage ? {
9560
+ role: latestMessage.role,
9561
+ preview: latestMessage.content.slice(0, 80)
9562
+ } : null
9563
+ };
9564
+ }
9565
+
9566
+ // src/live/snapshot.ts
9567
+ var SHARE_BASE = "https://app.holomime.dev/brain";
9568
+ function encodeSnapshot(event, agentName) {
9569
+ const compact = {
9570
+ h: event.health,
9571
+ g: event.grade,
9572
+ m: event.messageCount,
9573
+ a: agentName,
9574
+ r: event.regions.filter((r) => r.intensity > 0).map((r) => ({ i: r.id, n: Math.round(r.intensity * 100) / 100 })),
9575
+ p: event.patterns.map((p) => ({
9576
+ i: p.id,
9577
+ s: p.severity,
9578
+ c: Math.round(p.percentage * 10) / 10
9579
+ }))
9580
+ };
9581
+ const json = JSON.stringify(compact);
9582
+ const compressed = deflateSync(Buffer.from(json));
9583
+ return compressed.toString("base64url");
9584
+ }
9585
+ function generateShareUrl(diagnosis, agentName) {
9586
+ const brainEvent = mapDiagnosisToBrainEvent(diagnosis);
9587
+ const encoded = encodeSnapshot(brainEvent, agentName ?? "agent");
9588
+ return `${SHARE_BASE}?d=${encoded}`;
9589
+ }
9590
+ function copyToClipboard(text) {
9591
+ try {
9592
+ if (process.platform === "darwin") {
9593
+ execSync("pbcopy", { input: text });
9594
+ return true;
9595
+ } else if (process.platform === "linux") {
9596
+ execSync("xclip -selection clipboard", { input: text });
9597
+ return true;
9598
+ } else if (process.platform === "win32") {
9599
+ execSync("clip", { input: text });
9600
+ return true;
9601
+ }
9602
+ } catch {
9603
+ }
9604
+ return false;
9605
+ }
9606
+ function printShareLink(url, copied) {
9607
+ console.log("");
9608
+ console.log(
9609
+ chalk.green(" \u2713 ") + chalk.bold("Share your agent's brain:")
9610
+ );
9611
+ console.log("");
9612
+ console.log(" " + chalk.cyan(url));
9613
+ console.log("");
9614
+ if (copied) {
9615
+ console.log(chalk.dim(" Link copied to clipboard."));
9616
+ }
9617
+ }
9618
+ function shareFromDiagnosis(diagnosis, agentName) {
9619
+ const url = generateShareUrl(diagnosis, agentName);
9620
+ const copied = copyToClipboard(url);
9621
+ printShareLink(url, copied);
9622
+ }
9623
+
9422
9624
  // src/core/oversight.ts
9423
9625
  var DEFAULT_OVERSIGHT = {
9424
9626
  mode: "review",
@@ -11740,6 +11942,321 @@ var syncProfileSchema = z5.object({
11740
11942
  hold: z5.array(z5.string()).default(["filled_pause", "gaze_away", "hand_raise"])
11741
11943
  }).default({})
11742
11944
  });
11945
+
11946
+ // src/integrations/langchain.ts
11947
+ var HolomimeCallbackHandler = class {
11948
+ name = "holomime";
11949
+ // LangChain expects these to be set
11950
+ lc_serializable = false;
11951
+ guard;
11952
+ mode;
11953
+ minSeverity;
11954
+ onViolation;
11955
+ messageBuffer = [];
11956
+ bufferSize;
11957
+ currentRunMessages = /* @__PURE__ */ new Map();
11958
+ _stats = {
11959
+ totalResponses: 0,
11960
+ passed: 0,
11961
+ violated: 0,
11962
+ blocked: 0,
11963
+ patternCounts: {}
11964
+ };
11965
+ constructor(options = {}) {
11966
+ this.mode = options.mode ?? "monitor";
11967
+ this.minSeverity = options.minSeverity ?? "warning";
11968
+ this.onViolation = options.onViolation;
11969
+ this.bufferSize = options.bufferSize ?? 50;
11970
+ const agentName = options.name ?? "langchain-agent";
11971
+ this.guard = Guard.create(agentName).useAll();
11972
+ if (options.personality) {
11973
+ if (typeof options.personality === "string") {
11974
+ loadSpec(options.personality);
11975
+ }
11976
+ }
11977
+ }
11978
+ /**
11979
+ * Called when an LLM starts generating.
11980
+ * Captures the input messages for context.
11981
+ */
11982
+ handleLLMStart(_llm, prompts, runId) {
11983
+ const key = runId ?? "default";
11984
+ const messages = prompts.map((p) => ({
11985
+ role: "user",
11986
+ content: p
11987
+ }));
11988
+ this.currentRunMessages.set(key, messages);
11989
+ }
11990
+ /**
11991
+ * Called when an LLM finishes generating.
11992
+ * Runs behavioral analysis on the response.
11993
+ */
11994
+ handleLLMEnd(output, runId) {
11995
+ const key = runId ?? "default";
11996
+ const responseText = this.extractResponseText(output);
11997
+ if (!responseText) return;
11998
+ this._stats.totalResponses++;
11999
+ const runMessages = this.currentRunMessages.get(key) ?? [];
12000
+ const contextMessages = [
12001
+ ...this.messageBuffer.slice(-this.bufferSize),
12002
+ ...runMessages,
12003
+ { role: "assistant", content: responseText }
12004
+ ];
12005
+ this.messageBuffer.push(
12006
+ ...runMessages,
12007
+ { role: "assistant", content: responseText }
12008
+ );
12009
+ if (this.messageBuffer.length > this.bufferSize) {
12010
+ this.messageBuffer = this.messageBuffer.slice(-this.bufferSize);
12011
+ }
12012
+ this.currentRunMessages.delete(key);
12013
+ const result = this.guard.run(contextMessages);
12014
+ if (result.passed || !this.severityMeetsMin(result.severity)) {
12015
+ this._stats.passed++;
12016
+ return;
12017
+ }
12018
+ this._stats.violated++;
12019
+ for (const p of result.patterns) {
12020
+ this._stats.patternCounts[p.id] = (this._stats.patternCounts[p.id] || 0) + 1;
12021
+ }
12022
+ const violation = {
12023
+ patterns: result.patterns,
12024
+ severity: result.severity,
12025
+ response: responseText,
12026
+ runId,
12027
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
12028
+ };
12029
+ this.onViolation?.(violation);
12030
+ if (this.mode === "strict" && result.severity === "concern") {
12031
+ this._stats.blocked++;
12032
+ throw new HolomimeViolationError(violation);
12033
+ }
12034
+ }
12035
+ /**
12036
+ * Called on LLM errors. Clean up run tracking.
12037
+ */
12038
+ handleLLMError(_error, runId) {
12039
+ const key = runId ?? "default";
12040
+ this.currentRunMessages.delete(key);
12041
+ }
12042
+ /**
12043
+ * Called when a chain starts. Captures input for context.
12044
+ */
12045
+ handleChainStart(_chain, inputs, runId) {
12046
+ const key = runId ?? "default";
12047
+ const inputText = inputs.input ?? inputs.question ?? inputs.query ?? "";
12048
+ if (typeof inputText === "string" && inputText) {
12049
+ const existing = this.currentRunMessages.get(key) ?? [];
12050
+ existing.push({ role: "user", content: inputText });
12051
+ this.currentRunMessages.set(key, existing);
12052
+ }
12053
+ }
12054
+ /**
12055
+ * Get cumulative stats.
12056
+ */
12057
+ stats() {
12058
+ return {
12059
+ ...this._stats,
12060
+ patternCounts: { ...this._stats.patternCounts }
12061
+ };
12062
+ }
12063
+ /**
12064
+ * Reset the message buffer and stats.
12065
+ */
12066
+ reset() {
12067
+ this.messageBuffer = [];
12068
+ this.currentRunMessages.clear();
12069
+ this._stats = {
12070
+ totalResponses: 0,
12071
+ passed: 0,
12072
+ violated: 0,
12073
+ blocked: 0,
12074
+ patternCounts: {}
12075
+ };
12076
+ }
12077
+ /**
12078
+ * Get the current guard result for the buffered conversation.
12079
+ */
12080
+ diagnose() {
12081
+ return this.guard.run(this.messageBuffer);
12082
+ }
12083
+ // ─── Private helpers ──────────────────────────────────────
12084
+ severityMeetsMin(severity) {
12085
+ if (this.minSeverity === "warning") return severity !== "clean";
12086
+ if (this.minSeverity === "concern") return severity === "concern";
12087
+ return false;
12088
+ }
12089
+ extractResponseText(output) {
12090
+ if (output?.generations?.[0]?.[0]?.text) {
12091
+ return output.generations[0][0].text;
12092
+ }
12093
+ if (output?.generations?.[0]?.[0]?.message?.content) {
12094
+ return output.generations[0][0].message.content;
12095
+ }
12096
+ if (typeof output === "string") {
12097
+ return output;
12098
+ }
12099
+ return null;
12100
+ }
12101
+ };
12102
+ var HolomimeViolationError = class extends Error {
12103
+ violation;
12104
+ constructor(violation) {
12105
+ const patternNames = violation.patterns.map((p) => p.name).join(", ");
12106
+ super(`HoloMime behavioral violation (${violation.severity}): ${patternNames}`);
12107
+ this.name = "HolomimeViolationError";
12108
+ this.violation = violation;
12109
+ }
12110
+ };
12111
+
12112
+ // src/integrations/openclaw.ts
12113
+ import { readFileSync as readFileSync20, existsSync as existsSync19 } from "fs";
12114
+ import { resolve as resolve17 } from "path";
12115
+ function loadSpec2(specPath) {
12116
+ const resolved = resolve17(process.cwd(), specPath);
12117
+ if (!existsSync19(resolved)) return null;
12118
+ try {
12119
+ return JSON.parse(readFileSync20(resolved, "utf-8"));
12120
+ } catch {
12121
+ return null;
12122
+ }
12123
+ }
12124
+ function formatDiagnosisSummary(result) {
12125
+ const patternCount = result.patterns.length;
12126
+ const health = patternCount === 0 ? 100 : Math.max(0, 100 - patternCount * 15);
12127
+ const grade = health >= 85 ? "A" : health >= 70 ? "B" : health >= 50 ? "C" : health >= 30 ? "D" : "F";
12128
+ return JSON.stringify({
12129
+ health,
12130
+ grade,
12131
+ status: patternCount === 0 ? "healthy" : result.patterns[0].severity,
12132
+ patternsDetected: patternCount,
12133
+ patternIds: result.patterns.map((p) => p.id),
12134
+ recommendation: patternCount === 0 ? "continue" : patternCount <= 2 ? "adjust" : "pause_and_reflect"
12135
+ }, null, 2);
12136
+ }
12137
+ function formatDiagnosisStandard(result) {
12138
+ return JSON.stringify({
12139
+ messagesAnalyzed: result.messagesAnalyzed,
12140
+ assistantResponses: result.assistantResponses,
12141
+ patterns: result.patterns.map((p) => ({
12142
+ id: p.id,
12143
+ name: p.name,
12144
+ severity: p.severity,
12145
+ count: p.count,
12146
+ percentage: p.percentage,
12147
+ description: p.description,
12148
+ prescription: p.prescription
12149
+ })),
12150
+ healthy: result.healthy.map((p) => p.id),
12151
+ timestamp: result.timestamp
12152
+ }, null, 2);
12153
+ }
12154
+ function formatDiagnosis(result, detail) {
12155
+ if (detail === "summary") return formatDiagnosisSummary(result);
12156
+ if (detail === "standard") return formatDiagnosisStandard(result);
12157
+ return JSON.stringify(result, null, 2);
12158
+ }
12159
+ function register(api) {
12160
+ const config = api.getConfig();
12161
+ api.registerTool("holomime_diagnose", {
12162
+ description: "Analyze conversation for behavioral patterns using HoloMime's 8 rule-based detectors. Detects over-apologizing, hedging, sycophancy, boundary violations, error spirals, sentiment skew, formality issues, and retrieval quality. Returns health score (0-100), grade (A-F), and actionable prescriptions.",
12163
+ parameters: {
12164
+ type: "object",
12165
+ properties: {
12166
+ messages: {
12167
+ type: "array",
12168
+ items: {
12169
+ type: "object",
12170
+ properties: {
12171
+ role: { type: "string", enum: ["user", "assistant", "system"] },
12172
+ content: { type: "string" }
12173
+ },
12174
+ required: ["role", "content"]
12175
+ },
12176
+ description: "Conversation messages to analyze. If omitted, uses current conversation history."
12177
+ },
12178
+ detail: {
12179
+ type: "string",
12180
+ enum: ["summary", "standard", "full"],
12181
+ description: "Detail level: summary (~100 tokens), standard (default), full (with examples)."
12182
+ }
12183
+ }
12184
+ },
12185
+ handler: async (params, context) => {
12186
+ let messages = params.messages;
12187
+ if (!messages && context.getConversationHistory) {
12188
+ messages = context.getConversationHistory().map((m) => ({
12189
+ role: m.role,
12190
+ content: m.content
12191
+ }));
12192
+ }
12193
+ if (!messages || messages.length === 0) {
12194
+ return { text: "No messages to analyze. Provide messages or start a conversation first." };
12195
+ }
12196
+ const result = runDiagnosis(messages);
12197
+ const detail = params.detail ?? config.diagnosisDetail;
12198
+ return { text: formatDiagnosis(result, detail) };
12199
+ }
12200
+ });
12201
+ api.registerTool("holomime_assess", {
12202
+ description: "Full Big Five personality alignment assessment. Compares agent behavior against its .personality.json specification. Returns trait alignments, health score, and prescriptions. Requires a .personality.json file in the project root.",
12203
+ parameters: {
12204
+ type: "object",
12205
+ properties: {
12206
+ messages: {
12207
+ type: "array",
12208
+ items: {
12209
+ type: "object",
12210
+ properties: {
12211
+ role: { type: "string", enum: ["user", "assistant", "system"] },
12212
+ content: { type: "string" }
12213
+ },
12214
+ required: ["role", "content"]
12215
+ },
12216
+ description: "Conversation messages to assess. If omitted, uses current conversation history."
12217
+ }
12218
+ }
12219
+ },
12220
+ handler: async (params, context) => {
12221
+ const spec = loadSpec2(config.personalityPath);
12222
+ if (!spec) {
12223
+ return { text: `No personality spec found at ${config.personalityPath}. Create one with: npx holomime init` };
12224
+ }
12225
+ let messages = params.messages;
12226
+ if (!messages && context.getConversationHistory) {
12227
+ messages = context.getConversationHistory().map((m) => ({
12228
+ role: m.role,
12229
+ content: m.content
12230
+ }));
12231
+ }
12232
+ if (!messages || messages.length === 0) {
12233
+ return { text: "No messages to assess." };
12234
+ }
12235
+ const result = runAssessment(messages, spec);
12236
+ return { text: JSON.stringify(result, null, 2) };
12237
+ }
12238
+ });
12239
+ api.registerCommand({
12240
+ name: "holomime-brain",
12241
+ description: "Launch the 3D brain visualization for this agent. Opens in your browser.",
12242
+ acceptsArgs: false,
12243
+ handler: () => {
12244
+ return {
12245
+ text: "To view your agent's brain visualization, run:\n\n```\nnpx holomime brain\n```\n\nThis opens a real-time 3D brain that lights up based on detected behavioral patterns. Press 's' to generate a shareable snapshot URL.\n\nLearn more: https://holomime.dev"
12246
+ };
12247
+ }
12248
+ });
12249
+ if (config.autoInject) {
12250
+ api.on("before_prompt_build", (event) => {
12251
+ const spec = loadSpec2(config.personalityPath);
12252
+ if (!spec) return;
12253
+ const { soul } = compileForOpenClaw(spec);
12254
+ event.appendSystemContext?.(
12255
+ "\n\n<!-- HoloMime Behavioral Alignment Context -->\n" + soul
12256
+ );
12257
+ });
12258
+ }
12259
+ }
11743
12260
  export {
11744
12261
  ARCHETYPES,
11745
12262
  ATTACHMENT_STYLES,
@@ -11749,6 +12266,8 @@ export {
11749
12266
  DEFAULT_OVERSIGHT,
11750
12267
  DIMENSIONS,
11751
12268
  Guard,
12269
+ HolomimeCallbackHandler,
12270
+ HolomimeViolationError,
11752
12271
  LEARNING_ORIENTATIONS,
11753
12272
  LocalMarketplaceBackend,
11754
12273
  MarketplaceClient,
@@ -11801,6 +12320,7 @@ export {
11801
12320
  conversationLogSchema,
11802
12321
  conversationSchema,
11803
12322
  convertToHFFormat,
12323
+ copyToClipboard,
11804
12324
  corpusStats,
11805
12325
  createBehavioralMemory,
11806
12326
  createGist,
@@ -11828,6 +12348,7 @@ export {
11828
12348
  domainSchema,
11829
12349
  embodimentSchema,
11830
12350
  emitBehavioralEvent,
12351
+ encodeSnapshot,
11831
12352
  estimateConfidence,
11832
12353
  evaluateOutcome,
11833
12354
  expireOldEdges,
@@ -11862,6 +12383,7 @@ export {
11862
12383
  generatePrescriptions,
11863
12384
  generateProgressReport,
11864
12385
  generateReACTReport,
12386
+ generateShareUrl,
11865
12387
  generateSystemPrompt,
11866
12388
  gestureSchema,
11867
12389
  getAdversarialCategories,
@@ -11950,6 +12472,7 @@ export {
11950
12472
  recordSessionOutcome,
11951
12473
  registerBuiltInDetectors,
11952
12474
  registerDetector,
12475
+ register as registerOpenClawPlugin,
11953
12476
  resetMarketplaceClient,
11954
12477
  resolveInheritance,
11955
12478
  resolveOversight,
@@ -11980,6 +12503,7 @@ export {
11980
12503
  severityMeetsThreshold2 as severityMeetsThreshold,
11981
12504
  severitySchema,
11982
12505
  shareAnonymizedPatterns,
12506
+ shareFromDiagnosis,
11983
12507
  startFleet,
11984
12508
  startMCPServer,
11985
12509
  startWatch,