audrey 0.17.0 → 0.21.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/CHANGELOG.md +15 -0
- package/README.md +180 -370
- package/dist/mcp-server/config.d.ts +44 -0
- package/dist/mcp-server/config.d.ts.map +1 -0
- package/dist/mcp-server/config.js +210 -0
- package/dist/mcp-server/config.js.map +1 -0
- package/dist/mcp-server/index.d.ts +181 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +1530 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/src/adaptive.d.ts +7 -0
- package/dist/src/adaptive.d.ts.map +1 -0
- package/dist/src/adaptive.js +49 -0
- package/dist/src/adaptive.js.map +1 -0
- package/dist/src/affect.d.ts +19 -0
- package/dist/src/affect.d.ts.map +1 -0
- package/dist/src/affect.js +72 -0
- package/dist/src/affect.js.map +1 -0
- package/dist/src/audrey.d.ts +144 -0
- package/dist/src/audrey.d.ts.map +1 -0
- package/dist/src/audrey.js +576 -0
- package/dist/src/audrey.js.map +1 -0
- package/dist/src/capsule.d.ts +68 -0
- package/dist/src/capsule.d.ts.map +1 -0
- package/dist/src/capsule.js +311 -0
- package/dist/src/capsule.js.map +1 -0
- package/dist/src/causal.d.ts +28 -0
- package/dist/src/causal.d.ts.map +1 -0
- package/dist/src/causal.js +65 -0
- package/dist/src/causal.js.map +1 -0
- package/dist/src/confidence.d.ts +12 -0
- package/dist/src/confidence.d.ts.map +1 -0
- package/dist/src/confidence.js +63 -0
- package/dist/src/confidence.js.map +1 -0
- package/dist/src/consolidate.d.ts +8 -0
- package/dist/src/consolidate.d.ts.map +1 -0
- package/dist/src/consolidate.js +218 -0
- package/dist/src/consolidate.js.map +1 -0
- package/dist/src/context.d.ts +3 -0
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +19 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/db.d.ts +12 -0
- package/dist/src/db.d.ts.map +1 -0
- package/dist/src/db.js +380 -0
- package/dist/src/db.js.map +1 -0
- package/dist/src/decay.d.ts +7 -0
- package/dist/src/decay.d.ts.map +1 -0
- package/dist/src/decay.js +68 -0
- package/dist/src/decay.js.map +1 -0
- package/dist/src/embedding.d.ts +57 -0
- package/dist/src/embedding.d.ts.map +1 -0
- package/dist/src/embedding.js +254 -0
- package/dist/src/embedding.js.map +1 -0
- package/dist/src/encode.d.ts +15 -0
- package/dist/src/encode.d.ts.map +1 -0
- package/dist/src/encode.js +36 -0
- package/dist/src/encode.js.map +1 -0
- package/dist/src/events.d.ts +69 -0
- package/dist/src/events.d.ts.map +1 -0
- package/dist/src/events.js +149 -0
- package/dist/src/events.js.map +1 -0
- package/dist/src/export.d.ts +3 -0
- package/dist/src/export.d.ts.map +1 -0
- package/dist/src/export.js +46 -0
- package/dist/src/export.js.map +1 -0
- package/dist/src/forget.d.ts +11 -0
- package/dist/src/forget.d.ts.map +1 -0
- package/dist/src/forget.js +105 -0
- package/dist/src/forget.js.map +1 -0
- package/dist/src/fts.d.ts +34 -0
- package/dist/src/fts.d.ts.map +1 -0
- package/dist/src/fts.js +117 -0
- package/dist/src/fts.js.map +1 -0
- package/dist/src/hybrid-recall.d.ts +37 -0
- package/dist/src/hybrid-recall.d.ts.map +1 -0
- package/dist/src/hybrid-recall.js +213 -0
- package/dist/src/hybrid-recall.js.map +1 -0
- package/dist/src/import.d.ts +4 -0
- package/dist/src/import.d.ts.map +1 -0
- package/dist/src/import.js +127 -0
- package/dist/src/import.js.map +1 -0
- package/dist/src/index.d.ts +26 -0
- package/dist/src/index.d.ts.map +1 -0
- package/{src → dist/src}/index.js +7 -13
- package/dist/src/index.js.map +1 -0
- package/dist/src/interference.d.ts +13 -0
- package/dist/src/interference.d.ts.map +1 -0
- package/dist/src/interference.js +45 -0
- package/dist/src/interference.js.map +1 -0
- package/dist/src/introspect.d.ts +4 -0
- package/dist/src/introspect.d.ts.map +1 -0
- package/dist/src/introspect.js +40 -0
- package/dist/src/introspect.js.map +1 -0
- package/dist/src/llm.d.ts +38 -0
- package/dist/src/llm.d.ts.map +1 -0
- package/dist/src/llm.js +167 -0
- package/dist/src/llm.js.map +1 -0
- package/dist/src/migrate.d.ts +6 -0
- package/dist/src/migrate.d.ts.map +1 -0
- package/dist/src/migrate.js +51 -0
- package/dist/src/migrate.js.map +1 -0
- package/dist/src/preflight.d.ts +51 -0
- package/dist/src/preflight.d.ts.map +1 -0
- package/dist/src/preflight.js +201 -0
- package/dist/src/preflight.js.map +1 -0
- package/dist/src/promote.d.ts +40 -0
- package/dist/src/promote.d.ts.map +1 -0
- package/dist/src/promote.js +200 -0
- package/dist/src/promote.js.map +1 -0
- package/dist/src/prompts.d.ts +16 -0
- package/dist/src/prompts.d.ts.map +1 -0
- package/{src → dist/src}/prompts.js +172 -203
- package/dist/src/prompts.js.map +1 -0
- package/dist/src/recall.d.ts +9 -0
- package/dist/src/recall.d.ts.map +1 -0
- package/dist/src/recall.js +432 -0
- package/dist/src/recall.js.map +1 -0
- package/dist/src/redact.d.ts +27 -0
- package/dist/src/redact.d.ts.map +1 -0
- package/dist/src/redact.js +228 -0
- package/dist/src/redact.js.map +1 -0
- package/dist/src/reflexes.d.ts +35 -0
- package/dist/src/reflexes.d.ts.map +1 -0
- package/dist/src/reflexes.js +87 -0
- package/dist/src/reflexes.js.map +1 -0
- package/dist/src/rollback.d.ts +8 -0
- package/dist/src/rollback.d.ts.map +1 -0
- package/dist/src/rollback.js +33 -0
- package/dist/src/rollback.js.map +1 -0
- package/dist/src/routes.d.ts +7 -0
- package/dist/src/routes.d.ts.map +1 -0
- package/dist/src/routes.js +303 -0
- package/dist/src/routes.js.map +1 -0
- package/dist/src/rules-compiler.d.ts +20 -0
- package/dist/src/rules-compiler.d.ts.map +1 -0
- package/dist/src/rules-compiler.js +143 -0
- package/dist/src/rules-compiler.js.map +1 -0
- package/dist/src/server.d.ts +12 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +22 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/tool-trace.d.ts +37 -0
- package/dist/src/tool-trace.d.ts.map +1 -0
- package/dist/src/tool-trace.js +142 -0
- package/dist/src/tool-trace.js.map +1 -0
- package/dist/src/types.d.ts +446 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +6 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/ulid.d.ts +3 -0
- package/dist/src/ulid.d.ts.map +1 -0
- package/dist/src/ulid.js +11 -0
- package/dist/src/ulid.js.map +1 -0
- package/dist/src/utils.d.ts +10 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +41 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/src/validate.d.ts +22 -0
- package/dist/src/validate.d.ts.map +1 -0
- package/dist/src/validate.js +109 -0
- package/dist/src/validate.js.map +1 -0
- package/docs/assets/audrey-feature-grid.jpg +0 -0
- package/docs/assets/audrey-logo.svg +45 -0
- package/docs/assets/audrey-wordmark.png +0 -0
- package/docs/audrey-for-dummies.md +670 -0
- package/docs/future-of-llm-memory.md +452 -0
- package/docs/mcp-hosts.md +206 -0
- package/docs/ollama-local-agents.md +128 -0
- package/docs/production-readiness.md +37 -5
- package/examples/fintech-ops-demo.js +1 -1
- package/examples/healthcare-ops-demo.js +1 -1
- package/examples/ollama-memory-agent.js +326 -0
- package/examples/stripe-demo.js +1 -1
- package/package.json +54 -14
- package/benchmarks/baselines.js +0 -169
- package/benchmarks/cases.js +0 -421
- package/benchmarks/reference-results.js +0 -70
- package/benchmarks/report.js +0 -255
- package/benchmarks/run.js +0 -514
- package/mcp-server/config.js +0 -133
- package/mcp-server/index.js +0 -1265
- package/mcp-server/serve.js +0 -482
- package/src/adaptive.js +0 -53
- package/src/affect.js +0 -64
- package/src/audrey.js +0 -642
- package/src/causal.js +0 -95
- package/src/confidence.js +0 -120
- package/src/consolidate.js +0 -281
- package/src/context.js +0 -15
- package/src/db.js +0 -391
- package/src/decay.js +0 -84
- package/src/embedding.js +0 -260
- package/src/encode.js +0 -69
- package/src/export.js +0 -67
- package/src/forget.js +0 -111
- package/src/fts.js +0 -134
- package/src/import.js +0 -273
- package/src/interference.js +0 -51
- package/src/introspect.js +0 -48
- package/src/llm.js +0 -249
- package/src/migrate.js +0 -58
- package/src/recall.js +0 -573
- package/src/rollback.js +0 -42
- package/src/ulid.js +0 -18
- package/src/utils.js +0 -63
- package/src/validate.js +0 -172
- package/types/index.d.ts +0 -434
|
@@ -0,0 +1,1530 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { homedir, platform, tmpdir } from 'node:os';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { Audrey } from '../src/index.js';
|
|
9
|
+
import { readStoredDimensions } from '../src/db.js';
|
|
10
|
+
import { VERSION, SERVER_NAME, MCP_ENTRYPOINT, buildAudreyConfig, buildInstallArgs, formatMcpHostConfig, resolveDataDir, resolveEmbeddingProvider, resolveLLMProvider, } from './config.js';
|
|
11
|
+
const VALID_SOURCES = {
|
|
12
|
+
'direct-observation': 'direct-observation',
|
|
13
|
+
'told-by-user': 'told-by-user',
|
|
14
|
+
'tool-result': 'tool-result',
|
|
15
|
+
'inference': 'inference',
|
|
16
|
+
'model-generated': 'model-generated',
|
|
17
|
+
};
|
|
18
|
+
const VALID_TYPES = {
|
|
19
|
+
'episodic': 'episodic',
|
|
20
|
+
'semantic': 'semantic',
|
|
21
|
+
'procedural': 'procedural',
|
|
22
|
+
};
|
|
23
|
+
export const MAX_MEMORY_CONTENT_LENGTH = 50_000;
|
|
24
|
+
const subcommand = process.argv[2];
|
|
25
|
+
function isNonEmptyText(value) {
|
|
26
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
27
|
+
}
|
|
28
|
+
export function validateMemoryContent(content) {
|
|
29
|
+
if (!isNonEmptyText(content)) {
|
|
30
|
+
throw new Error('content must be a non-empty string');
|
|
31
|
+
}
|
|
32
|
+
if (content.length > MAX_MEMORY_CONTENT_LENGTH) {
|
|
33
|
+
throw new Error(`content exceeds maximum length of ${MAX_MEMORY_CONTENT_LENGTH} characters`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function validateForgetSelection(id, query) {
|
|
37
|
+
if ((id && query) || (!id && !query)) {
|
|
38
|
+
throw new Error('Provide exactly one of id or query');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function initializeEmbeddingProvider(provider) {
|
|
42
|
+
if (provider && typeof provider.ready === 'function') {
|
|
43
|
+
await provider.ready();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export const memoryEncodeToolSchema = {
|
|
47
|
+
content: z.string()
|
|
48
|
+
.max(MAX_MEMORY_CONTENT_LENGTH)
|
|
49
|
+
.refine(isNonEmptyText, 'Content must not be empty')
|
|
50
|
+
.describe('The memory content to encode'),
|
|
51
|
+
source: z.enum(VALID_SOURCES).describe('Source type of the memory'),
|
|
52
|
+
tags: z.array(z.string()).optional().describe('Optional tags for categorization'),
|
|
53
|
+
salience: z.number().min(0).max(1).optional().describe('Importance weight 0-1'),
|
|
54
|
+
context: z.record(z.string(), z.string()).optional().describe('Situational context as key-value pairs (e.g., {task: "debugging", domain: "payments"})'),
|
|
55
|
+
affect: z.object({
|
|
56
|
+
valence: z.number().min(-1).max(1).describe('Emotional valence: -1 (very negative) to 1 (very positive)'),
|
|
57
|
+
arousal: z.number().min(0).max(1).optional().describe('Emotional arousal: 0 (calm) to 1 (highly activated)'),
|
|
58
|
+
label: z.string().optional().describe('Human-readable emotion label (e.g., "curiosity", "frustration", "relief")'),
|
|
59
|
+
}).optional().describe('Emotional affect - how this memory feels'),
|
|
60
|
+
private: z.boolean().optional().describe('If true, memory is only visible to the AI and excluded from public recall results'),
|
|
61
|
+
};
|
|
62
|
+
export const memoryRecallToolSchema = {
|
|
63
|
+
query: z.string().describe('Search query to match against memories'),
|
|
64
|
+
limit: z.number().min(1).max(50).optional().describe('Max results (default 10)'),
|
|
65
|
+
types: z.array(z.enum(VALID_TYPES)).optional().describe('Memory types to search'),
|
|
66
|
+
min_confidence: z.number().min(0).max(1).optional().describe('Minimum confidence threshold'),
|
|
67
|
+
tags: z.array(z.string()).optional().describe('Only return episodic memories with these tags'),
|
|
68
|
+
sources: z.array(z.enum(VALID_SOURCES)).optional().describe('Only return episodic memories from these sources'),
|
|
69
|
+
after: z.string().optional().describe('Only return memories created after this ISO date'),
|
|
70
|
+
before: z.string().optional().describe('Only return memories created before this ISO date'),
|
|
71
|
+
context: z.record(z.string(), z.string()).optional().describe('Retrieval context - memories encoded in matching context get boosted'),
|
|
72
|
+
mood: z.object({
|
|
73
|
+
valence: z.number().min(-1).max(1).describe('Current emotional valence: -1 (negative) to 1 (positive)'),
|
|
74
|
+
arousal: z.number().min(0).max(1).optional().describe('Current arousal: 0 (calm) to 1 (activated)'),
|
|
75
|
+
}).optional().describe('Current mood - boosts recall of memories encoded in similar emotional state'),
|
|
76
|
+
};
|
|
77
|
+
export const memoryImportToolSchema = {
|
|
78
|
+
snapshot: z.object({
|
|
79
|
+
version: z.string(),
|
|
80
|
+
episodes: z.array(z.any()),
|
|
81
|
+
semantics: z.array(z.any()).optional(),
|
|
82
|
+
procedures: z.array(z.any()).optional(),
|
|
83
|
+
causalLinks: z.array(z.any()).optional(),
|
|
84
|
+
contradictions: z.array(z.any()).optional(),
|
|
85
|
+
consolidationRuns: z.array(z.any()).optional(),
|
|
86
|
+
consolidationMetrics: z.array(z.any()).optional(),
|
|
87
|
+
config: z.record(z.string(), z.string()).optional(),
|
|
88
|
+
}).passthrough().describe('A snapshot from memory_export'),
|
|
89
|
+
};
|
|
90
|
+
export const memoryForgetToolSchema = {
|
|
91
|
+
id: z.string().optional().describe('ID of the memory to forget'),
|
|
92
|
+
query: z.string().optional().describe('Semantic query to find and forget the closest matching memory'),
|
|
93
|
+
min_similarity: z.number().min(0).max(1).optional().describe('Minimum similarity for query-based forget (default 0.9)'),
|
|
94
|
+
purge: z.boolean().optional().describe('Hard-delete the memory permanently (default false, soft-delete)'),
|
|
95
|
+
};
|
|
96
|
+
export const memoryPreflightToolSchema = {
|
|
97
|
+
action: z.string()
|
|
98
|
+
.refine(isNonEmptyText, 'Action must not be empty')
|
|
99
|
+
.describe('Natural-language description of the action the agent is about to take.'),
|
|
100
|
+
tool: z.string().optional().describe('Tool or command family about to be used, e.g. Bash, npm test, Edit, deploy.'),
|
|
101
|
+
session_id: z.string().optional().describe('Session identifier for grouping the optional preflight event.'),
|
|
102
|
+
cwd: z.string().optional().describe('Working directory for the action.'),
|
|
103
|
+
files: z.array(z.string()).optional().describe('File paths to fingerprint if record_event is true.'),
|
|
104
|
+
strict: z.boolean().optional().describe('If true, high-severity memory warnings produce decision=block instead of caution.'),
|
|
105
|
+
limit: z.number().int().min(1).max(50).optional().describe('Max recall results to consider before preflight categorization.'),
|
|
106
|
+
budget_chars: z.number().int().min(200).max(32000).optional().describe('Capsule budget in characters.'),
|
|
107
|
+
mode: z.enum(['balanced', 'conservative', 'aggressive']).optional().describe('Underlying capsule mode. Defaults to conservative.'),
|
|
108
|
+
failure_window_hours: z.number().int().min(1).max(8760).optional().describe('How far back to check failed tool events. Defaults to 168 hours.'),
|
|
109
|
+
include_status: z.boolean().optional().describe('Include memory health in the response and warning calculation. Defaults to true.'),
|
|
110
|
+
record_event: z.boolean().optional().describe('Record a redacted PreToolUse event for this preflight. Defaults to false.'),
|
|
111
|
+
include_capsule: z.boolean().optional().describe('If false, omit the embedded Memory Capsule from the response.'),
|
|
112
|
+
};
|
|
113
|
+
export const memoryReflexesToolSchema = {
|
|
114
|
+
...memoryPreflightToolSchema,
|
|
115
|
+
include_preflight: z.boolean().optional().describe('If true, include the full underlying preflight report.'),
|
|
116
|
+
};
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// CLI subcommands
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
async function serveHttp() {
|
|
121
|
+
const { startServer } = await import('../src/server.js');
|
|
122
|
+
const config = buildAudreyConfig();
|
|
123
|
+
const port = parseInt(process.env.AUDREY_PORT || '7437', 10);
|
|
124
|
+
const apiKey = process.env.AUDREY_API_KEY;
|
|
125
|
+
const server = await startServer({ port, config, apiKey });
|
|
126
|
+
console.error(`[audrey-http] v${VERSION} serving on port ${server.port}`);
|
|
127
|
+
if (apiKey) {
|
|
128
|
+
console.error('[audrey-http] API key authentication enabled');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function reembed() {
|
|
132
|
+
const dataDir = resolveDataDir(process.env);
|
|
133
|
+
const explicit = process.env['AUDREY_EMBEDDING_PROVIDER'];
|
|
134
|
+
const embedding = resolveEmbeddingProvider(process.env, explicit);
|
|
135
|
+
const storedDims = readStoredDimensions(dataDir);
|
|
136
|
+
const dimensionsChanged = storedDims !== null && storedDims !== embedding.dimensions;
|
|
137
|
+
console.log(`Re-embedding with ${embedding.provider} (${embedding.dimensions}d)...`);
|
|
138
|
+
if (dimensionsChanged) {
|
|
139
|
+
console.log(`Dimension change: ${storedDims}d -> ${embedding.dimensions}d (will drop and recreate vec tables)`);
|
|
140
|
+
}
|
|
141
|
+
const audrey = new Audrey({ dataDir, agent: 'reembed', embedding });
|
|
142
|
+
try {
|
|
143
|
+
await initializeEmbeddingProvider(audrey.embeddingProvider);
|
|
144
|
+
const { reembedAll } = await import('../src/migrate.js');
|
|
145
|
+
const counts = await reembedAll(audrey.db, audrey.embeddingProvider, { dropAndRecreate: dimensionsChanged });
|
|
146
|
+
console.log(`Done. Re-embedded: ${counts.episodes} episodes, ${counts.semantics} semantics, ${counts.procedures} procedures`);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
audrey.close();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function dream() {
|
|
153
|
+
const dataDir = resolveDataDir(process.env);
|
|
154
|
+
const explicit = process.env['AUDREY_EMBEDDING_PROVIDER'];
|
|
155
|
+
const embedding = resolveEmbeddingProvider(process.env, explicit);
|
|
156
|
+
const storedDims = readStoredDimensions(dataDir);
|
|
157
|
+
const config = {
|
|
158
|
+
dataDir,
|
|
159
|
+
agent: 'dream',
|
|
160
|
+
embedding,
|
|
161
|
+
};
|
|
162
|
+
const llm = resolveLLMProvider(process.env, process.env['AUDREY_LLM_PROVIDER']);
|
|
163
|
+
if (llm)
|
|
164
|
+
config.llm = llm;
|
|
165
|
+
const audrey = new Audrey(config);
|
|
166
|
+
try {
|
|
167
|
+
await initializeEmbeddingProvider(audrey.embeddingProvider);
|
|
168
|
+
const embeddingLabel = storedDims !== null && storedDims !== embedding.dimensions
|
|
169
|
+
? `${embedding.provider} (${embedding.dimensions}d; stored ${storedDims}d)`
|
|
170
|
+
: `${embedding.provider} (${embedding.dimensions}d)`;
|
|
171
|
+
console.log('[audrey] Starting dream cycle...');
|
|
172
|
+
console.log(`[audrey] Embedding: ${embeddingLabel}`);
|
|
173
|
+
const result = await audrey.dream();
|
|
174
|
+
const health = audrey.memoryStatus();
|
|
175
|
+
console.log(`[audrey] Consolidation: evaluated ${result.consolidation.episodesEvaluated} episodes, `
|
|
176
|
+
+ `found ${result.consolidation.clustersFound} clusters, extracted ${result.consolidation.principlesExtracted} principles `
|
|
177
|
+
+ `(${result.consolidation.semanticsCreated ?? 0} semantic, ${result.consolidation.proceduresCreated ?? 0} procedural)`);
|
|
178
|
+
console.log(`[audrey] Decay: evaluated ${result.decay.totalEvaluated} memories, `
|
|
179
|
+
+ `${result.decay.transitionedToDormant} transitioned to dormant`);
|
|
180
|
+
console.log(`[audrey] Final: ${result.stats.episodic} episodic, ${result.stats.semantic} semantic, ${result.stats.procedural} procedural `
|
|
181
|
+
+ `| ${health.healthy ? 'healthy' : 'unhealthy'}`);
|
|
182
|
+
console.log('[audrey] Dream complete.');
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
audrey.close();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function greeting() {
|
|
189
|
+
const dataDir = resolveDataDir(process.env);
|
|
190
|
+
const contextArg = process.argv[3] || undefined;
|
|
191
|
+
if (!existsSync(dataDir)) {
|
|
192
|
+
console.log('[audrey] No data yet - fresh start.');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const storedDimensions = readStoredDimensions(dataDir);
|
|
196
|
+
const resolvedEmbedding = resolveEmbeddingProvider(process.env, process.env['AUDREY_EMBEDDING_PROVIDER']);
|
|
197
|
+
const canUseResolvedEmbedding = Boolean(contextArg)
|
|
198
|
+
&& storedDimensions !== null
|
|
199
|
+
&& storedDimensions === resolvedEmbedding.dimensions;
|
|
200
|
+
const dimensions = storedDimensions || resolvedEmbedding.dimensions || 8;
|
|
201
|
+
const audrey = new Audrey({
|
|
202
|
+
dataDir,
|
|
203
|
+
agent: 'greeting',
|
|
204
|
+
embedding: canUseResolvedEmbedding
|
|
205
|
+
? resolvedEmbedding
|
|
206
|
+
: { provider: 'mock', dimensions },
|
|
207
|
+
});
|
|
208
|
+
try {
|
|
209
|
+
if (canUseResolvedEmbedding) {
|
|
210
|
+
await initializeEmbeddingProvider(audrey.embeddingProvider);
|
|
211
|
+
}
|
|
212
|
+
const result = await audrey.greeting({ context: canUseResolvedEmbedding ? contextArg : undefined });
|
|
213
|
+
const health = audrey.memoryStatus();
|
|
214
|
+
const lines = [];
|
|
215
|
+
lines.push(`[Audrey v${VERSION}] Memory briefing`);
|
|
216
|
+
lines.push('');
|
|
217
|
+
if (contextArg && !canUseResolvedEmbedding) {
|
|
218
|
+
lines.push(`Context recall skipped: stored index is ${storedDimensions ?? 'unknown'}d `
|
|
219
|
+
+ `but current embedding config resolves to ${resolvedEmbedding.dimensions}d.`);
|
|
220
|
+
lines.push('');
|
|
221
|
+
}
|
|
222
|
+
// Mood
|
|
223
|
+
if (result.mood && result.mood.samples > 0) {
|
|
224
|
+
const v = result.mood.valence;
|
|
225
|
+
const moodWord = v > 0.3 ? 'positive' : v < -0.3 ? 'negative' : 'neutral';
|
|
226
|
+
lines.push(`Mood: ${moodWord} (valence=${v.toFixed(2)}, `
|
|
227
|
+
+ `arousal=${result.mood.arousal.toFixed(2)}, `
|
|
228
|
+
+ `from ${result.mood.samples} recent memories)`);
|
|
229
|
+
}
|
|
230
|
+
// Health
|
|
231
|
+
const stats = audrey.introspect();
|
|
232
|
+
lines.push(`Memory: ${stats.episodic} episodic, ${stats.semantic} semantic, `
|
|
233
|
+
+ `${stats.procedural} procedural | ${health.healthy ? 'healthy' : 'needs attention'}`);
|
|
234
|
+
lines.push('');
|
|
235
|
+
// Principles (semantic memories)
|
|
236
|
+
if (result.principles?.length > 0) {
|
|
237
|
+
lines.push('Learned principles:');
|
|
238
|
+
for (const p of result.principles) {
|
|
239
|
+
lines.push(` - ${p.content}`);
|
|
240
|
+
}
|
|
241
|
+
lines.push('');
|
|
242
|
+
}
|
|
243
|
+
// Identity (private memories)
|
|
244
|
+
if (result.identity?.length > 0) {
|
|
245
|
+
lines.push('Identity:');
|
|
246
|
+
for (const m of result.identity) {
|
|
247
|
+
lines.push(` - ${m.content}`);
|
|
248
|
+
}
|
|
249
|
+
lines.push('');
|
|
250
|
+
}
|
|
251
|
+
// Recent memories
|
|
252
|
+
if (result.recent?.length > 0) {
|
|
253
|
+
lines.push('Recent memories:');
|
|
254
|
+
for (const r of result.recent) {
|
|
255
|
+
const age = timeSince(r.created_at);
|
|
256
|
+
lines.push(` - [${age}] ${r.content.slice(0, 200)}`);
|
|
257
|
+
}
|
|
258
|
+
lines.push('');
|
|
259
|
+
}
|
|
260
|
+
// Unresolved
|
|
261
|
+
if (result.unresolved?.length > 0) {
|
|
262
|
+
lines.push('Unresolved threads:');
|
|
263
|
+
for (const u of result.unresolved) {
|
|
264
|
+
lines.push(` - ${u.content.slice(0, 150)}`);
|
|
265
|
+
}
|
|
266
|
+
lines.push('');
|
|
267
|
+
}
|
|
268
|
+
// Contextual recall
|
|
269
|
+
if ((result.contextual?.length ?? 0) > 0) {
|
|
270
|
+
lines.push(`Context-relevant memories (query: "${contextArg}"):`);
|
|
271
|
+
for (const c of result.contextual) {
|
|
272
|
+
lines.push(` - [${c.type}] ${c.content.slice(0, 200)}`);
|
|
273
|
+
}
|
|
274
|
+
lines.push('');
|
|
275
|
+
}
|
|
276
|
+
console.log(lines.join('\n'));
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
audrey.close();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function timeSince(isoDate) {
|
|
283
|
+
const ms = Date.now() - new Date(isoDate).getTime();
|
|
284
|
+
const mins = Math.floor(ms / 60000);
|
|
285
|
+
if (mins < 60)
|
|
286
|
+
return `${mins}m ago`;
|
|
287
|
+
const hours = Math.floor(mins / 60);
|
|
288
|
+
if (hours < 24)
|
|
289
|
+
return `${hours}h ago`;
|
|
290
|
+
const days = Math.floor(hours / 24);
|
|
291
|
+
return `${days}d ago`;
|
|
292
|
+
}
|
|
293
|
+
async function reflect() {
|
|
294
|
+
const dataDir = resolveDataDir(process.env);
|
|
295
|
+
const explicit = process.env['AUDREY_EMBEDDING_PROVIDER'];
|
|
296
|
+
const embedding = resolveEmbeddingProvider(process.env, explicit);
|
|
297
|
+
const config = {
|
|
298
|
+
dataDir,
|
|
299
|
+
agent: 'reflect',
|
|
300
|
+
embedding,
|
|
301
|
+
};
|
|
302
|
+
const llm = resolveLLMProvider(process.env, process.env['AUDREY_LLM_PROVIDER']);
|
|
303
|
+
if (llm)
|
|
304
|
+
config.llm = llm;
|
|
305
|
+
const audrey = new Audrey(config);
|
|
306
|
+
try {
|
|
307
|
+
await initializeEmbeddingProvider(audrey.embeddingProvider);
|
|
308
|
+
// Read conversation turns from stdin if available
|
|
309
|
+
let turns = null;
|
|
310
|
+
if (!process.stdin.isTTY) {
|
|
311
|
+
const chunks = [];
|
|
312
|
+
for await (const chunk of process.stdin) {
|
|
313
|
+
chunks.push(chunk);
|
|
314
|
+
}
|
|
315
|
+
const raw = Buffer.concat(chunks).toString('utf-8').trim();
|
|
316
|
+
if (raw) {
|
|
317
|
+
try {
|
|
318
|
+
turns = JSON.parse(raw);
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
console.error('[audrey] Could not parse stdin as JSON turns, skipping reflect.');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (turns && Array.isArray(turns) && turns.length > 0) {
|
|
326
|
+
console.log(`[audrey] Reflecting on ${turns.length} conversation turns...`);
|
|
327
|
+
const reflectResult = await audrey.reflect(turns);
|
|
328
|
+
if (reflectResult.skipped) {
|
|
329
|
+
console.log(`[audrey] Reflect skipped: ${reflectResult.skipped}`);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
console.log(`[audrey] Reflected: encoded ${reflectResult.encoded} lasting memories.`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Always run dream cycle after reflect
|
|
336
|
+
console.log('[audrey] Starting dream cycle...');
|
|
337
|
+
const result = await audrey.dream();
|
|
338
|
+
console.log(`[audrey] Consolidation: ${result.consolidation.episodesEvaluated} episodes evaluated, `
|
|
339
|
+
+ `${result.consolidation.clustersFound} clusters, ${result.consolidation.principlesExtracted} principles`);
|
|
340
|
+
console.log(`[audrey] Decay: ${result.decay.totalEvaluated} evaluated, `
|
|
341
|
+
+ `${result.decay.transitionedToDormant} dormant`);
|
|
342
|
+
console.log(`[audrey] Status: ${result.stats.episodic} episodic, ${result.stats.semantic} semantic, `
|
|
343
|
+
+ `${result.stats.procedural} procedural`);
|
|
344
|
+
console.log('[audrey] Dream complete.');
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
audrey.close();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function parseInstallOptions(argv = process.argv) {
|
|
351
|
+
let host = 'claude-code';
|
|
352
|
+
let dryRun = false;
|
|
353
|
+
for (let i = 3; i < argv.length; i += 1) {
|
|
354
|
+
const arg = argv[i] ?? '';
|
|
355
|
+
if (arg === '--dry-run' || arg === '--print') {
|
|
356
|
+
dryRun = true;
|
|
357
|
+
}
|
|
358
|
+
else if (arg === '--host') {
|
|
359
|
+
host = argv[i + 1] || host;
|
|
360
|
+
i += 1;
|
|
361
|
+
}
|
|
362
|
+
else if (arg.startsWith('--host=')) {
|
|
363
|
+
host = arg.slice('--host='.length) || host;
|
|
364
|
+
}
|
|
365
|
+
else if (!arg.startsWith('-')) {
|
|
366
|
+
host = arg;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return { host, dryRun };
|
|
370
|
+
}
|
|
371
|
+
export function formatInstallGuide(host, env = process.env, dryRun = false) {
|
|
372
|
+
const normalizedHost = host || 'claude-code';
|
|
373
|
+
const title = dryRun || normalizedHost === 'claude-code'
|
|
374
|
+
? `Audrey install preview for ${normalizedHost}`
|
|
375
|
+
: `Audrey config-only install for ${normalizedHost}`;
|
|
376
|
+
const lines = [
|
|
377
|
+
title,
|
|
378
|
+
'',
|
|
379
|
+
'No host config files were modified.',
|
|
380
|
+
'',
|
|
381
|
+
'Generated MCP config:',
|
|
382
|
+
formatMcpHostConfig(normalizedHost, env),
|
|
383
|
+
'',
|
|
384
|
+
'Next steps:',
|
|
385
|
+
];
|
|
386
|
+
if (normalizedHost === 'claude-code') {
|
|
387
|
+
lines.push('- Run without --dry-run to register Audrey through Claude Code: npx audrey install --host claude-code');
|
|
388
|
+
lines.push('- Verify with: claude mcp list');
|
|
389
|
+
}
|
|
390
|
+
else if (normalizedHost === 'codex') {
|
|
391
|
+
lines.push('- Paste the TOML block into C:\\Users\\<you>\\.codex\\config.toml under the MCP server section.');
|
|
392
|
+
lines.push('- Restart Codex, then run: codex mcp list');
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
lines.push('- Paste the JSON block into your host MCP configuration.');
|
|
396
|
+
lines.push('- Restart the host and look for the audrey-memory MCP server.');
|
|
397
|
+
}
|
|
398
|
+
lines.push('- Run a local health check any time with: npx audrey doctor');
|
|
399
|
+
return lines.join('\n');
|
|
400
|
+
}
|
|
401
|
+
function installClaudeCode() {
|
|
402
|
+
try {
|
|
403
|
+
execFileSync('claude', ['--version'], { stdio: 'ignore' });
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
console.error('Error: claude CLI not found. Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code');
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
const dataDir = resolveDataDir(process.env);
|
|
410
|
+
const resolvedEmbedding = resolveEmbeddingProvider(process.env, process.env['AUDREY_EMBEDDING_PROVIDER']);
|
|
411
|
+
const resolvedLlm = resolveLLMProvider(process.env, process.env['AUDREY_LLM_PROVIDER']);
|
|
412
|
+
if (resolvedEmbedding.provider === 'gemini') {
|
|
413
|
+
console.log('Using Gemini embeddings (3072d)');
|
|
414
|
+
}
|
|
415
|
+
else if (resolvedEmbedding.provider === 'local') {
|
|
416
|
+
console.log(`Using local embeddings (384d, device=${resolvedEmbedding.device || 'gpu'})`);
|
|
417
|
+
}
|
|
418
|
+
else if (resolvedEmbedding.provider === 'openai') {
|
|
419
|
+
console.log('Using OpenAI embeddings (1536d)');
|
|
420
|
+
}
|
|
421
|
+
else if (resolvedEmbedding.provider === 'mock') {
|
|
422
|
+
console.log('Using mock embeddings');
|
|
423
|
+
}
|
|
424
|
+
if (resolvedLlm?.provider === 'anthropic') {
|
|
425
|
+
console.log('Using Anthropic for LLM-powered consolidation, contradiction detection, and reflection');
|
|
426
|
+
}
|
|
427
|
+
else if (resolvedLlm?.provider === 'openai') {
|
|
428
|
+
console.log('Using OpenAI for LLM-powered consolidation, contradiction detection, and reflection');
|
|
429
|
+
}
|
|
430
|
+
else if (resolvedLlm?.provider === 'mock') {
|
|
431
|
+
console.log('Using mock LLM provider');
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
console.log('No LLM provider configured - consolidation and contradiction detection will use heuristics');
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
execFileSync('claude', ['mcp', 'remove', SERVER_NAME], { stdio: 'ignore' });
|
|
438
|
+
}
|
|
439
|
+
catch {
|
|
440
|
+
// Not registered yet.
|
|
441
|
+
}
|
|
442
|
+
const args = buildInstallArgs(process.env);
|
|
443
|
+
try {
|
|
444
|
+
execFileSync('claude', args, { stdio: 'inherit' });
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
console.error('Failed to register MCP server. Is Claude Code installed and on your PATH?');
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
console.log(`
|
|
451
|
+
Audrey registered as "${SERVER_NAME}" with Claude Code.
|
|
452
|
+
|
|
453
|
+
19 MCP tools available in every session:
|
|
454
|
+
memory_encode - Store observations, facts, preferences
|
|
455
|
+
memory_recall - Search memories by semantic similarity
|
|
456
|
+
memory_consolidate - Extract principles from accumulated episodes
|
|
457
|
+
memory_dream - Full sleep cycle: consolidate + decay + stats
|
|
458
|
+
memory_introspect - Check memory system health
|
|
459
|
+
memory_resolve_truth - Resolve contradictions between claims
|
|
460
|
+
memory_export - Export all memories as JSON snapshot
|
|
461
|
+
memory_import - Import a snapshot into a fresh database
|
|
462
|
+
memory_forget - Forget a specific memory by ID or query
|
|
463
|
+
memory_decay - Apply forgetting curves, transition low-confidence to dormant
|
|
464
|
+
memory_status - Check brain health (episode/vec sync, dimensions)
|
|
465
|
+
memory_reflect - Form lasting memories from a conversation
|
|
466
|
+
memory_greeting - Wake up as yourself: load identity, context, mood
|
|
467
|
+
memory_observe_tool - Record redacted tool-use events
|
|
468
|
+
memory_recent_failures - Inspect recent failed tool events
|
|
469
|
+
memory_capsule - Return a ranked, evidence-backed memory packet
|
|
470
|
+
memory_preflight - Check memory before an agent acts
|
|
471
|
+
memory_reflexes - Convert preflight evidence into trigger-response reflexes
|
|
472
|
+
memory_promote - Promote repeated lessons into project rules
|
|
473
|
+
|
|
474
|
+
CLI subcommands:
|
|
475
|
+
npx audrey demo - Run a 60-second local proof with no network calls
|
|
476
|
+
npx audrey doctor - Diagnose runtime, store health, and host config readiness
|
|
477
|
+
npx audrey install - Register MCP server with Claude Code
|
|
478
|
+
npx audrey install --host codex --dry-run - Print safe host setup instructions
|
|
479
|
+
npx audrey mcp-config codex - Print Codex MCP TOML
|
|
480
|
+
npx audrey mcp-config generic - Print JSON config for other MCP hosts
|
|
481
|
+
npx audrey uninstall - Remove MCP server registration
|
|
482
|
+
npx audrey status - Show memory store health and stats
|
|
483
|
+
npx audrey status --json - Emit machine-readable health output
|
|
484
|
+
npx audrey status --json --fail-on-unhealthy - Exit non-zero on unhealthy status
|
|
485
|
+
npx audrey greeting - Output session briefing (for hooks)
|
|
486
|
+
npx audrey reflect - Reflect on conversation + dream cycle (for hooks)
|
|
487
|
+
npx audrey dream - Run consolidation + decay cycle
|
|
488
|
+
npx audrey reembed - Re-embed all memories with current provider
|
|
489
|
+
|
|
490
|
+
Data stored in: ${dataDir}
|
|
491
|
+
Verify: claude mcp list
|
|
492
|
+
`);
|
|
493
|
+
}
|
|
494
|
+
function install() {
|
|
495
|
+
const options = parseInstallOptions();
|
|
496
|
+
if (options.dryRun || options.host !== 'claude-code') {
|
|
497
|
+
try {
|
|
498
|
+
console.log(formatInstallGuide(options.host, process.env, options.dryRun));
|
|
499
|
+
}
|
|
500
|
+
catch (err) {
|
|
501
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
502
|
+
console.error(`[audrey] install failed: ${message}`);
|
|
503
|
+
process.exit(2);
|
|
504
|
+
}
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
installClaudeCode();
|
|
508
|
+
}
|
|
509
|
+
function uninstall() {
|
|
510
|
+
try {
|
|
511
|
+
execFileSync('claude', ['--version'], { stdio: 'ignore' });
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
console.error('Error: claude CLI not found.');
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
execFileSync('claude', ['mcp', 'remove', SERVER_NAME], { stdio: 'inherit' });
|
|
519
|
+
console.log(`Removed "${SERVER_NAME}" from Claude Code.`);
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
console.error(`Failed to remove "${SERVER_NAME}". It may not be registered.`);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function printMcpConfig() {
|
|
527
|
+
const host = process.argv[3] || 'generic';
|
|
528
|
+
try {
|
|
529
|
+
console.log(formatMcpHostConfig(host, process.env));
|
|
530
|
+
}
|
|
531
|
+
catch (err) {
|
|
532
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
533
|
+
console.error(`[audrey] mcp-config failed: ${message}`);
|
|
534
|
+
process.exit(2);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function sectionTitle(section) {
|
|
538
|
+
return section.replace(/_/g, ' ');
|
|
539
|
+
}
|
|
540
|
+
function createDemoDir() {
|
|
541
|
+
const preferredParent = process.env['AUDREY_DEMO_PARENT_DIR'] || tmpdir();
|
|
542
|
+
try {
|
|
543
|
+
return mkdtempSync(join(preferredParent, 'audrey-demo-'));
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
const fallbackParent = join(process.cwd(), '.audrey-demo-tmp');
|
|
547
|
+
mkdirSync(fallbackParent, { recursive: true });
|
|
548
|
+
return mkdtempSync(join(fallbackParent, 'run-'));
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
export async function runDemoCommand({ out = console.log, keep = process.argv.includes('--keep'), } = {}) {
|
|
552
|
+
const demoDir = createDemoDir();
|
|
553
|
+
const audrey = new Audrey({
|
|
554
|
+
dataDir: demoDir,
|
|
555
|
+
agent: 'audrey-demo',
|
|
556
|
+
embedding: { provider: 'mock', dimensions: 64 },
|
|
557
|
+
llm: { provider: 'mock' },
|
|
558
|
+
});
|
|
559
|
+
try {
|
|
560
|
+
out('Audrey 60-second memory demo');
|
|
561
|
+
out('');
|
|
562
|
+
out(`Memory store: ${demoDir}`);
|
|
563
|
+
out('Writing memories that could have come from Codex, Claude, or an Ollama agent...');
|
|
564
|
+
const ids = [];
|
|
565
|
+
ids.push(await audrey.encode({
|
|
566
|
+
content: 'Audrey should work across Codex, Claude Code, Claude Desktop, Cursor, and Ollama-backed local agents.',
|
|
567
|
+
source: 'direct-observation',
|
|
568
|
+
tags: ['must-follow', 'host-neutral', 'codex', 'ollama'],
|
|
569
|
+
}));
|
|
570
|
+
ids.push(await audrey.encode({
|
|
571
|
+
content: 'Before an agent starts work, ask Audrey for a Memory Capsule and include the capsule in the model context.',
|
|
572
|
+
source: 'direct-observation',
|
|
573
|
+
tags: ['procedure', 'memory-capsule', 'agent-loop'],
|
|
574
|
+
}));
|
|
575
|
+
ids.push(await audrey.encode({
|
|
576
|
+
content: 'If a host cannot auto-install Audrey, run npx audrey mcp-config codex '
|
|
577
|
+
+ 'or npx audrey mcp-config generic and paste the generated config.',
|
|
578
|
+
source: 'direct-observation',
|
|
579
|
+
tags: ['procedure', 'mcp', 'first-contact'],
|
|
580
|
+
}));
|
|
581
|
+
ids.push(await audrey.encode({
|
|
582
|
+
content: 'Repeated tool failures should become procedural warnings before the agent retries the same risky action.',
|
|
583
|
+
source: 'direct-observation',
|
|
584
|
+
tags: ['risk', 'procedure', 'tool-trace'],
|
|
585
|
+
}));
|
|
586
|
+
ids.push(await audrey.encode({
|
|
587
|
+
content: 'Memory Reflexes turn preflight evidence into trigger-response rules an agent can follow before tool use.',
|
|
588
|
+
source: 'direct-observation',
|
|
589
|
+
tags: ['procedure', 'memory-reflexes', 'agent-loop'],
|
|
590
|
+
}));
|
|
591
|
+
const event = audrey.observeTool({
|
|
592
|
+
event: 'PostToolUse',
|
|
593
|
+
tool: 'npm test',
|
|
594
|
+
outcome: 'failed',
|
|
595
|
+
errorSummary: 'Vitest can fail with spawn EPERM on locked-down Windows hosts; '
|
|
596
|
+
+ 'use build, typecheck, benchmarks, and direct dist smokes as the fallback evidence path.',
|
|
597
|
+
cwd: process.cwd(),
|
|
598
|
+
metadata: { demo: true, source: 'audrey demo' },
|
|
599
|
+
});
|
|
600
|
+
out(`Encoded ${ids.length} memories and 1 redacted tool trace (${event.event.id}).`);
|
|
601
|
+
out('');
|
|
602
|
+
const query = 'How should an agent use Audrey with Codex and Ollama?';
|
|
603
|
+
out(`Asking Audrey for a Memory Capsule: "${query}"`);
|
|
604
|
+
const capsule = await audrey.capsule(query, {
|
|
605
|
+
limit: 8,
|
|
606
|
+
budgetChars: 2400,
|
|
607
|
+
includeRisks: true,
|
|
608
|
+
includeContradictions: true,
|
|
609
|
+
});
|
|
610
|
+
out('');
|
|
611
|
+
out('Capsule highlights:');
|
|
612
|
+
let printed = 0;
|
|
613
|
+
for (const [name, entries] of Object.entries(capsule.sections)) {
|
|
614
|
+
if (!Array.isArray(entries) || entries.length === 0)
|
|
615
|
+
continue;
|
|
616
|
+
printed += 1;
|
|
617
|
+
out(`- ${sectionTitle(name)}:`);
|
|
618
|
+
for (const entry of entries.slice(0, 2)) {
|
|
619
|
+
out(` * ${entry.content}`);
|
|
620
|
+
out(` why: ${entry.reason}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (printed === 0) {
|
|
624
|
+
out('- No capsule sections were populated. That is unexpected for this demo.');
|
|
625
|
+
}
|
|
626
|
+
const reflexReport = await audrey.reflexes('run npm test before release', {
|
|
627
|
+
tool: 'npm test',
|
|
628
|
+
includePreflight: false,
|
|
629
|
+
});
|
|
630
|
+
out('');
|
|
631
|
+
out('Memory Reflex proof:');
|
|
632
|
+
const demoReflexes = [...reflexReport.reflexes].sort((a, b) => {
|
|
633
|
+
if (a.source === 'recent_failure' && b.source !== 'recent_failure')
|
|
634
|
+
return -1;
|
|
635
|
+
if (b.source === 'recent_failure' && a.source !== 'recent_failure')
|
|
636
|
+
return 1;
|
|
637
|
+
return 0;
|
|
638
|
+
});
|
|
639
|
+
for (const reflex of demoReflexes.slice(0, 3)) {
|
|
640
|
+
out(`- ${reflex.trigger}`);
|
|
641
|
+
out(` ${reflex.response_type}: ${reflex.response}`);
|
|
642
|
+
}
|
|
643
|
+
const recall = await audrey.recall('Codex Ollama Memory Capsule host install', { limit: 3 });
|
|
644
|
+
out('');
|
|
645
|
+
out('Recall proof:');
|
|
646
|
+
for (const memory of recall.slice(0, 3)) {
|
|
647
|
+
out(`- [${memory.type}] ${(memory.confidence * 100).toFixed(0)}% ${memory.content}`);
|
|
648
|
+
}
|
|
649
|
+
out('');
|
|
650
|
+
out('Next steps:');
|
|
651
|
+
out('- Diagnose your setup: npx audrey doctor');
|
|
652
|
+
out('- Codex: npx audrey mcp-config codex');
|
|
653
|
+
out('- Any stdio MCP host: npx audrey mcp-config generic');
|
|
654
|
+
out('- Ollama/local agents: npx audrey serve, then call /v1/reflexes, /v1/capsule, and /v1/recall as tools');
|
|
655
|
+
if (keep) {
|
|
656
|
+
out(`- Demo data kept at: ${demoDir}`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
finally {
|
|
660
|
+
audrey.close();
|
|
661
|
+
if (!keep) {
|
|
662
|
+
rmSync(demoDir, { recursive: true, force: true });
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
function cliHasFlag(flag, argv = process.argv) {
|
|
667
|
+
return Array.isArray(argv) && argv.includes(flag);
|
|
668
|
+
}
|
|
669
|
+
export function buildStatusReport({ dataDir = resolveDataDir(process.env), claudeJsonPath = join(homedir(), '.claude.json'), } = {}) {
|
|
670
|
+
let registered = false;
|
|
671
|
+
try {
|
|
672
|
+
const claudeConfig = JSON.parse(readFileSync(claudeJsonPath, 'utf-8'));
|
|
673
|
+
registered = SERVER_NAME in (claudeConfig.mcpServers || {});
|
|
674
|
+
}
|
|
675
|
+
catch {
|
|
676
|
+
// Ignore unreadable config.
|
|
677
|
+
}
|
|
678
|
+
const report = {
|
|
679
|
+
generatedAt: new Date().toISOString(),
|
|
680
|
+
registered,
|
|
681
|
+
dataDir,
|
|
682
|
+
exists: existsSync(dataDir),
|
|
683
|
+
storedDimensions: null,
|
|
684
|
+
stats: null,
|
|
685
|
+
health: null,
|
|
686
|
+
lastConsolidation: null,
|
|
687
|
+
error: null,
|
|
688
|
+
};
|
|
689
|
+
if (!report.exists) {
|
|
690
|
+
return report;
|
|
691
|
+
}
|
|
692
|
+
try {
|
|
693
|
+
report.storedDimensions = readStoredDimensions(dataDir);
|
|
694
|
+
const dimensions = report.storedDimensions || 8;
|
|
695
|
+
const audrey = new Audrey({
|
|
696
|
+
dataDir,
|
|
697
|
+
agent: 'status-check',
|
|
698
|
+
embedding: { provider: 'mock', dimensions },
|
|
699
|
+
});
|
|
700
|
+
report.stats = audrey.introspect();
|
|
701
|
+
report.health = audrey.memoryStatus();
|
|
702
|
+
report.lastConsolidation = audrey.db.prepare(`
|
|
703
|
+
SELECT completed_at FROM consolidation_runs
|
|
704
|
+
WHERE status = 'completed'
|
|
705
|
+
ORDER BY completed_at DESC
|
|
706
|
+
LIMIT 1
|
|
707
|
+
`).get()?.completed_at ?? 'never';
|
|
708
|
+
audrey.close();
|
|
709
|
+
}
|
|
710
|
+
catch (err) {
|
|
711
|
+
report.error = err.message || String(err);
|
|
712
|
+
}
|
|
713
|
+
return report;
|
|
714
|
+
}
|
|
715
|
+
export function formatStatusReport(report) {
|
|
716
|
+
const lines = [];
|
|
717
|
+
lines.push(`Registration: ${report.registered ? 'active' : 'not registered'}`);
|
|
718
|
+
if (!report.exists) {
|
|
719
|
+
lines.push(`Data directory: ${report.dataDir} (not yet created - will be created on first use)`);
|
|
720
|
+
return lines.join('\n');
|
|
721
|
+
}
|
|
722
|
+
if (report.error) {
|
|
723
|
+
lines.push(`Data directory: ${report.dataDir} (exists but could not read: ${report.error})`);
|
|
724
|
+
return lines.join('\n');
|
|
725
|
+
}
|
|
726
|
+
lines.push(`Data directory: ${report.dataDir}`);
|
|
727
|
+
lines.push(`Stored dimensions: ${report.storedDimensions ?? 'unknown'}`);
|
|
728
|
+
lines.push(`Memories: ${report.stats.episodic} episodic, ${report.stats.semantic} semantic, ${report.stats.procedural} procedural`);
|
|
729
|
+
lines.push(`Index sync: ${report.health.vec_episodes}/${report.health.searchable_episodes} episodic, `
|
|
730
|
+
+ `${report.health.vec_semantics}/${report.health.searchable_semantics} semantic, `
|
|
731
|
+
+ `${report.health.vec_procedures}/${report.health.searchable_procedures} procedural`);
|
|
732
|
+
lines.push(`Health: ${report.health.healthy ? 'healthy' : 'unhealthy'}`
|
|
733
|
+
+ `${report.health.reembed_recommended ? ' (re-embed recommended)' : ''}`);
|
|
734
|
+
lines.push(`Dormant: ${report.stats.dormant}`);
|
|
735
|
+
lines.push(`Causal links: ${report.stats.causalLinks}`);
|
|
736
|
+
lines.push(`Contradictions: ${report.stats.contradictions.open} open, ${report.stats.contradictions.resolved} resolved`);
|
|
737
|
+
lines.push(`Consolidation runs: ${report.stats.totalConsolidationRuns}`);
|
|
738
|
+
lines.push(`Last consolidation: ${report.lastConsolidation}`);
|
|
739
|
+
return lines.join('\n');
|
|
740
|
+
}
|
|
741
|
+
export function runStatusCommand({ argv = process.argv, dataDir = resolveDataDir(process.env), claudeJsonPath = join(homedir(), '.claude.json'), out = console.log, } = {}) {
|
|
742
|
+
const report = buildStatusReport({ dataDir, claudeJsonPath });
|
|
743
|
+
if (cliHasFlag('--json', argv)) {
|
|
744
|
+
out(JSON.stringify(report, null, 2));
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
out(formatStatusReport(report));
|
|
748
|
+
}
|
|
749
|
+
const exitCode = report.error
|
|
750
|
+
|| (cliHasFlag('--fail-on-unhealthy', argv) && report.exists && report.health && !report.health.healthy)
|
|
751
|
+
? 1
|
|
752
|
+
: 0;
|
|
753
|
+
return { report, exitCode };
|
|
754
|
+
}
|
|
755
|
+
function describeEmbedding(env) {
|
|
756
|
+
const embedding = resolveEmbeddingProvider(env, env['AUDREY_EMBEDDING_PROVIDER']);
|
|
757
|
+
if (embedding.provider === 'local') {
|
|
758
|
+
return `local (${embedding.dimensions}d, device=${embedding.device || 'gpu'})`;
|
|
759
|
+
}
|
|
760
|
+
return `${embedding.provider} (${embedding.dimensions}d)`;
|
|
761
|
+
}
|
|
762
|
+
function describeLlm(env) {
|
|
763
|
+
const llm = resolveLLMProvider(env, env['AUDREY_LLM_PROVIDER']);
|
|
764
|
+
return llm ? llm.provider : 'not configured (heuristic mode)';
|
|
765
|
+
}
|
|
766
|
+
function addDoctorCheck(checks, name, ok, severity, message, hint) {
|
|
767
|
+
checks.push({ name, ok, severity, message, ...(hint ? { hint } : {}) });
|
|
768
|
+
}
|
|
769
|
+
export function buildDoctorReport({ dataDir = resolveDataDir(process.env), claudeJsonPath = join(homedir(), '.claude.json'), env = process.env, nodeVersion = process.versions.node, } = {}) {
|
|
770
|
+
const checks = [];
|
|
771
|
+
const statusReport = buildStatusReport({ dataDir, claudeJsonPath });
|
|
772
|
+
const major = Number.parseInt(nodeVersion.split('.')[0] || '0', 10);
|
|
773
|
+
const entrypointExists = existsSync(MCP_ENTRYPOINT);
|
|
774
|
+
addDoctorCheck(checks, 'node-runtime', major >= 20, major >= 20 ? 'info' : 'error', `Node.js ${nodeVersion}`, major >= 20 ? undefined : 'Install Node.js 20 or newer.');
|
|
775
|
+
addDoctorCheck(checks, 'mcp-entrypoint', entrypointExists, entrypointExists ? 'info' : 'error', MCP_ENTRYPOINT, entrypointExists ? undefined : 'Run npm run build before launching Audrey from this checkout.');
|
|
776
|
+
let embedding = 'invalid';
|
|
777
|
+
try {
|
|
778
|
+
embedding = describeEmbedding(env);
|
|
779
|
+
addDoctorCheck(checks, 'embedding-provider', true, 'info', embedding);
|
|
780
|
+
}
|
|
781
|
+
catch (err) {
|
|
782
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
783
|
+
addDoctorCheck(checks, 'embedding-provider', false, 'error', message, 'Check AUDREY_EMBEDDING_PROVIDER.');
|
|
784
|
+
}
|
|
785
|
+
let llm = 'not configured (heuristic mode)';
|
|
786
|
+
try {
|
|
787
|
+
llm = describeLlm(env);
|
|
788
|
+
addDoctorCheck(checks, 'llm-provider', true, 'info', llm);
|
|
789
|
+
}
|
|
790
|
+
catch (err) {
|
|
791
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
792
|
+
addDoctorCheck(checks, 'llm-provider', false, 'error', message, 'Check AUDREY_LLM_PROVIDER.');
|
|
793
|
+
}
|
|
794
|
+
if (!statusReport.exists) {
|
|
795
|
+
addDoctorCheck(checks, 'memory-store', true, 'info', `${dataDir} is not created yet`, 'Run npx audrey demo or connect a host to create the store.');
|
|
796
|
+
}
|
|
797
|
+
else if (statusReport.error) {
|
|
798
|
+
addDoctorCheck(checks, 'memory-store', false, 'error', statusReport.error, 'Run npx audrey status --json for details.');
|
|
799
|
+
}
|
|
800
|
+
else if (!statusReport.health) {
|
|
801
|
+
addDoctorCheck(checks, 'memory-store', false, 'error', 'memory store health could not be read');
|
|
802
|
+
}
|
|
803
|
+
else if (statusReport.health && !statusReport.health.healthy) {
|
|
804
|
+
addDoctorCheck(checks, 'memory-store', false, 'error', 'memory vectors are out of sync', 'Run npx audrey reembed.');
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
addDoctorCheck(checks, 'memory-store', true, 'info', 'healthy');
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
formatMcpHostConfig('codex', env);
|
|
811
|
+
formatMcpHostConfig('generic', env);
|
|
812
|
+
addDoctorCheck(checks, 'host-config-generation', true, 'info', 'codex TOML and generic JSON can be generated');
|
|
813
|
+
}
|
|
814
|
+
catch (err) {
|
|
815
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
816
|
+
addDoctorCheck(checks, 'host-config-generation', false, 'error', message);
|
|
817
|
+
}
|
|
818
|
+
const ok = checks.every(check => check.ok || check.severity !== 'error');
|
|
819
|
+
return {
|
|
820
|
+
generatedAt: new Date().toISOString(),
|
|
821
|
+
version: VERSION,
|
|
822
|
+
node: nodeVersion,
|
|
823
|
+
platform: platform(),
|
|
824
|
+
entrypoint: MCP_ENTRYPOINT,
|
|
825
|
+
dataDir,
|
|
826
|
+
embedding,
|
|
827
|
+
llm,
|
|
828
|
+
status: statusReport,
|
|
829
|
+
checks,
|
|
830
|
+
ok,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
export function formatDoctorReport(report) {
|
|
834
|
+
const lines = [
|
|
835
|
+
`Audrey Doctor v${report.version}`,
|
|
836
|
+
`Runtime: Node.js ${report.node} on ${report.platform}`,
|
|
837
|
+
`MCP entrypoint: ${report.entrypoint}`,
|
|
838
|
+
`Data directory: ${report.dataDir}`,
|
|
839
|
+
`Embedding: ${report.embedding}`,
|
|
840
|
+
`LLM: ${report.llm}`,
|
|
841
|
+
`Store health: ${report.status.exists ? (report.status.health?.healthy ? 'healthy' : 'needs attention') : 'not initialized'}`,
|
|
842
|
+
'',
|
|
843
|
+
'Checks:',
|
|
844
|
+
];
|
|
845
|
+
for (const check of report.checks) {
|
|
846
|
+
const marker = check.ok ? 'OK' : check.severity.toUpperCase();
|
|
847
|
+
lines.push(`- [${marker}] ${check.name}: ${check.message}`);
|
|
848
|
+
if (check.hint)
|
|
849
|
+
lines.push(` hint: ${check.hint}`);
|
|
850
|
+
}
|
|
851
|
+
lines.push('');
|
|
852
|
+
lines.push(`Verdict: ${report.ok ? 'ready' : 'blocked'}`);
|
|
853
|
+
lines.push('');
|
|
854
|
+
lines.push('Next steps:');
|
|
855
|
+
lines.push('- Prove local behavior: npx audrey demo');
|
|
856
|
+
lines.push('- Preview host setup: npx audrey install --host codex --dry-run');
|
|
857
|
+
lines.push('- Emit automation JSON: npx audrey doctor --json');
|
|
858
|
+
return lines.join('\n');
|
|
859
|
+
}
|
|
860
|
+
export function runDoctorCommand({ argv = process.argv, dataDir = resolveDataDir(process.env), claudeJsonPath = join(homedir(), '.claude.json'), env = process.env, out = console.log, } = {}) {
|
|
861
|
+
const report = buildDoctorReport({ dataDir, claudeJsonPath, env });
|
|
862
|
+
out(cliHasFlag('--json', argv) ? JSON.stringify(report, null, 2) : formatDoctorReport(report));
|
|
863
|
+
return { report, exitCode: report.ok ? 0 : 1 };
|
|
864
|
+
}
|
|
865
|
+
function status() {
|
|
866
|
+
const { exitCode } = runStatusCommand();
|
|
867
|
+
if (exitCode !== 0) {
|
|
868
|
+
process.exitCode = exitCode;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function doctor() {
|
|
872
|
+
const { exitCode } = runDoctorCommand();
|
|
873
|
+
if (exitCode !== 0) {
|
|
874
|
+
process.exitCode = exitCode;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
function toolResult(data) {
|
|
878
|
+
return { content: [{ type: 'text', text: JSON.stringify(data) }] };
|
|
879
|
+
}
|
|
880
|
+
function toolError(err) {
|
|
881
|
+
return { isError: true, content: [{ type: 'text', text: `Error: ${err.message || String(err)}` }] };
|
|
882
|
+
}
|
|
883
|
+
export function registerShutdownHandlers(processRef, audrey, logger = console.error) {
|
|
884
|
+
let closed = false;
|
|
885
|
+
const shutdown = (message, exitCode = 0) => {
|
|
886
|
+
if (message) {
|
|
887
|
+
logger(message);
|
|
888
|
+
}
|
|
889
|
+
if (!closed) {
|
|
890
|
+
closed = true;
|
|
891
|
+
try {
|
|
892
|
+
audrey.close();
|
|
893
|
+
}
|
|
894
|
+
catch (err) {
|
|
895
|
+
logger(`[audrey-mcp] shutdown error: ${err.message || String(err)}`);
|
|
896
|
+
exitCode = exitCode === 0 ? 1 : exitCode;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (typeof processRef.exit === 'function') {
|
|
900
|
+
processRef.exit(exitCode);
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
processRef.once('SIGINT', () => shutdown('[audrey-mcp] received SIGINT, shutting down'));
|
|
904
|
+
processRef.once('SIGTERM', () => shutdown('[audrey-mcp] received SIGTERM, shutting down'));
|
|
905
|
+
processRef.once('SIGHUP', () => shutdown('[audrey-mcp] received SIGHUP, shutting down'));
|
|
906
|
+
processRef.once('uncaughtException', (err) => {
|
|
907
|
+
logger('[audrey-mcp] uncaught exception:', err);
|
|
908
|
+
shutdown(undefined, 1);
|
|
909
|
+
});
|
|
910
|
+
processRef.once('unhandledRejection', (reason) => {
|
|
911
|
+
logger('[audrey-mcp] unhandled rejection:', reason);
|
|
912
|
+
shutdown(undefined, 1);
|
|
913
|
+
});
|
|
914
|
+
return shutdown;
|
|
915
|
+
}
|
|
916
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
917
|
+
export function registerDreamTool(server, audrey) {
|
|
918
|
+
server.tool('memory_dream', {
|
|
919
|
+
min_cluster_size: z.number().optional().describe('Minimum episodes per cluster (default 3)'),
|
|
920
|
+
similarity_threshold: z.number().optional().describe('Similarity threshold for clustering (default 0.85)'),
|
|
921
|
+
dormant_threshold: z.number().min(0).max(1).optional().describe('Confidence below which memories go dormant (default 0.1)'),
|
|
922
|
+
}, async ({ min_cluster_size, similarity_threshold, dormant_threshold }) => {
|
|
923
|
+
try {
|
|
924
|
+
const result = await audrey.dream({
|
|
925
|
+
minClusterSize: min_cluster_size,
|
|
926
|
+
similarityThreshold: similarity_threshold,
|
|
927
|
+
dormantThreshold: dormant_threshold,
|
|
928
|
+
});
|
|
929
|
+
return toolResult(result);
|
|
930
|
+
}
|
|
931
|
+
catch (err) {
|
|
932
|
+
return toolError(err);
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
async function main() {
|
|
937
|
+
const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
|
|
938
|
+
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
939
|
+
const config = buildAudreyConfig();
|
|
940
|
+
const audrey = new Audrey(config);
|
|
941
|
+
const embLabel = config.embedding?.provider === 'mock'
|
|
942
|
+
? 'mock embeddings - set OPENAI_API_KEY for real semantic search'
|
|
943
|
+
: `${config.embedding?.provider} embeddings (${config.embedding?.dimensions}d)`;
|
|
944
|
+
console.error(`[audrey-mcp] v${VERSION} started - agent=${config.agent} dataDir=${config.dataDir} (${embLabel})`);
|
|
945
|
+
const server = new McpServer({
|
|
946
|
+
name: SERVER_NAME,
|
|
947
|
+
version: VERSION,
|
|
948
|
+
});
|
|
949
|
+
server.tool('memory_encode', memoryEncodeToolSchema, async ({ content, source, tags, salience, private: isPrivate, context, affect }) => {
|
|
950
|
+
try {
|
|
951
|
+
validateMemoryContent(content);
|
|
952
|
+
const id = await audrey.encode({ content, source, tags, salience, private: isPrivate, context, affect });
|
|
953
|
+
return toolResult({ id, content, source, private: isPrivate ?? false });
|
|
954
|
+
}
|
|
955
|
+
catch (err) {
|
|
956
|
+
return toolError(err);
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
server.tool('memory_recall', memoryRecallToolSchema, async ({ query, limit, types, min_confidence, tags, sources, after, before, context, mood, }) => {
|
|
960
|
+
try {
|
|
961
|
+
const results = await audrey.recall(query, {
|
|
962
|
+
limit: limit ?? 10,
|
|
963
|
+
types,
|
|
964
|
+
minConfidence: min_confidence,
|
|
965
|
+
tags,
|
|
966
|
+
sources,
|
|
967
|
+
after,
|
|
968
|
+
before,
|
|
969
|
+
context,
|
|
970
|
+
mood,
|
|
971
|
+
});
|
|
972
|
+
return toolResult(results);
|
|
973
|
+
}
|
|
974
|
+
catch (err) {
|
|
975
|
+
return toolError(err);
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
server.tool('memory_consolidate', {
|
|
979
|
+
min_cluster_size: z.number().optional().describe('Minimum episodes per cluster'),
|
|
980
|
+
similarity_threshold: z.number().optional().describe('Similarity threshold for clustering'),
|
|
981
|
+
}, async ({ min_cluster_size, similarity_threshold }) => {
|
|
982
|
+
try {
|
|
983
|
+
const consolidation = await audrey.consolidate({
|
|
984
|
+
minClusterSize: min_cluster_size,
|
|
985
|
+
similarityThreshold: similarity_threshold,
|
|
986
|
+
});
|
|
987
|
+
return toolResult(consolidation);
|
|
988
|
+
}
|
|
989
|
+
catch (err) {
|
|
990
|
+
return toolError(err);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
server.tool('memory_introspect', {}, async () => {
|
|
994
|
+
try {
|
|
995
|
+
return toolResult(audrey.introspect());
|
|
996
|
+
}
|
|
997
|
+
catch (err) {
|
|
998
|
+
return toolError(err);
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
server.tool('memory_resolve_truth', {
|
|
1002
|
+
contradiction_id: z.string().describe('ID of the contradiction to resolve'),
|
|
1003
|
+
}, async ({ contradiction_id }) => {
|
|
1004
|
+
try {
|
|
1005
|
+
return toolResult(await audrey.resolveTruth(contradiction_id));
|
|
1006
|
+
}
|
|
1007
|
+
catch (err) {
|
|
1008
|
+
return toolError(err);
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
1011
|
+
server.tool('memory_export', {}, async () => {
|
|
1012
|
+
try {
|
|
1013
|
+
return toolResult(audrey.export());
|
|
1014
|
+
}
|
|
1015
|
+
catch (err) {
|
|
1016
|
+
return toolError(err);
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
server.tool('memory_import', memoryImportToolSchema, async ({ snapshot }) => {
|
|
1020
|
+
try {
|
|
1021
|
+
await audrey.import(snapshot);
|
|
1022
|
+
return toolResult({ imported: true, stats: audrey.introspect() });
|
|
1023
|
+
}
|
|
1024
|
+
catch (err) {
|
|
1025
|
+
return toolError(err);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
server.tool('memory_forget', memoryForgetToolSchema, async ({ id, query, min_similarity, purge }) => {
|
|
1029
|
+
try {
|
|
1030
|
+
validateForgetSelection(id, query);
|
|
1031
|
+
let result;
|
|
1032
|
+
if (id) {
|
|
1033
|
+
result = audrey.forget(id, { purge: purge ?? false });
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
result = await audrey.forgetByQuery(query, {
|
|
1037
|
+
minSimilarity: min_similarity ?? 0.9,
|
|
1038
|
+
purge: purge ?? false,
|
|
1039
|
+
});
|
|
1040
|
+
if (!result) {
|
|
1041
|
+
return toolResult({ forgotten: false, reason: 'No memory found above similarity threshold' });
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return toolResult({ forgotten: true, ...result });
|
|
1045
|
+
}
|
|
1046
|
+
catch (err) {
|
|
1047
|
+
return toolError(err);
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
server.tool('memory_decay', {
|
|
1051
|
+
dormant_threshold: z.number().min(0).max(1).optional().describe('Confidence below which memories go dormant (default 0.1)'),
|
|
1052
|
+
}, async ({ dormant_threshold }) => {
|
|
1053
|
+
try {
|
|
1054
|
+
return toolResult(audrey.decay({ dormantThreshold: dormant_threshold }));
|
|
1055
|
+
}
|
|
1056
|
+
catch (err) {
|
|
1057
|
+
return toolError(err);
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
server.tool('memory_status', {}, async () => {
|
|
1061
|
+
try {
|
|
1062
|
+
return toolResult(audrey.memoryStatus());
|
|
1063
|
+
}
|
|
1064
|
+
catch (err) {
|
|
1065
|
+
return toolError(err);
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
server.tool('memory_reflect', {
|
|
1069
|
+
turns: z.array(z.object({
|
|
1070
|
+
role: z.string().describe('Message role: user or assistant'),
|
|
1071
|
+
content: z.string().describe('Message content'),
|
|
1072
|
+
})).describe('Conversation turns to reflect on. Call at end of meaningful conversations to form lasting memories.'),
|
|
1073
|
+
}, async ({ turns }) => {
|
|
1074
|
+
try {
|
|
1075
|
+
return toolResult(await audrey.reflect(turns));
|
|
1076
|
+
}
|
|
1077
|
+
catch (err) {
|
|
1078
|
+
return toolError(err);
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
registerDreamTool(server, audrey);
|
|
1082
|
+
server.tool('memory_greeting', {
|
|
1083
|
+
context: z.string().optional().describe('Optional hint about this session. When provided, Audrey also returns semantically relevant memories.'),
|
|
1084
|
+
}, async ({ context }) => {
|
|
1085
|
+
try {
|
|
1086
|
+
return toolResult(await audrey.greeting({ context }));
|
|
1087
|
+
}
|
|
1088
|
+
catch (err) {
|
|
1089
|
+
return toolError(err);
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
server.tool('memory_observe_tool', {
|
|
1093
|
+
event: z.string().describe('Hook event name (PreToolUse, PostToolUse, PostToolUseFailure, PreCompact, PostCompact, etc.)'),
|
|
1094
|
+
tool: z.string().describe('Tool name being observed (Bash, Edit, Write, etc.)'),
|
|
1095
|
+
session_id: z.string().optional().describe('Session identifier for grouping related events'),
|
|
1096
|
+
input: z.unknown().optional().describe('Tool input. Hashed and never stored raw; redacted metadata is only stored when retain_details is true.'),
|
|
1097
|
+
output: z.unknown().optional().describe('Tool output. Same redaction and storage policy as input.'),
|
|
1098
|
+
outcome: z.enum(['succeeded', 'failed', 'blocked', 'skipped', 'unknown']).optional().describe('Outcome classification'),
|
|
1099
|
+
error_summary: z.string().optional().describe('Short error description if the tool failed. Redacted and truncated to 2 KB.'),
|
|
1100
|
+
cwd: z.string().optional().describe('Working directory at the time of the tool call'),
|
|
1101
|
+
files: z.array(z.string()).optional().describe('File paths to fingerprint (size + mtime + content hash)'),
|
|
1102
|
+
metadata: z.record(z.string(), z.unknown()).optional().describe('Arbitrary structured metadata (redacted before storage)'),
|
|
1103
|
+
retain_details: z.boolean().optional().describe('If true, redacted input and output payloads are stored alongside hashes. Defaults to false.'),
|
|
1104
|
+
}, async ({ event, tool, session_id, input, output, outcome, error_summary, cwd, files, metadata, retain_details, }) => {
|
|
1105
|
+
try {
|
|
1106
|
+
const result = audrey.observeTool({
|
|
1107
|
+
event,
|
|
1108
|
+
tool,
|
|
1109
|
+
sessionId: session_id,
|
|
1110
|
+
input,
|
|
1111
|
+
output,
|
|
1112
|
+
outcome,
|
|
1113
|
+
errorSummary: error_summary,
|
|
1114
|
+
cwd,
|
|
1115
|
+
files,
|
|
1116
|
+
metadata,
|
|
1117
|
+
retainDetails: retain_details,
|
|
1118
|
+
});
|
|
1119
|
+
return toolResult({
|
|
1120
|
+
id: result.event.id,
|
|
1121
|
+
event_type: result.event.event_type,
|
|
1122
|
+
tool_name: result.event.tool_name,
|
|
1123
|
+
outcome: result.event.outcome,
|
|
1124
|
+
redaction_state: result.event.redaction_state,
|
|
1125
|
+
redactions: result.redactions,
|
|
1126
|
+
created_at: result.event.created_at,
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
catch (err) {
|
|
1130
|
+
return toolError(err);
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
server.tool('memory_recent_failures', {
|
|
1134
|
+
since: z.string().optional().describe('ISO timestamp lower bound (defaults to 7 days ago)'),
|
|
1135
|
+
limit: z.number().int().min(1).max(200).optional().describe('Max rows to return (defaults to 20)'),
|
|
1136
|
+
}, async ({ since, limit }) => {
|
|
1137
|
+
try {
|
|
1138
|
+
return toolResult(audrey.recentFailures({ since, limit }));
|
|
1139
|
+
}
|
|
1140
|
+
catch (err) {
|
|
1141
|
+
return toolError(err);
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
server.tool('memory_capsule', {
|
|
1145
|
+
query: z.string().describe('Natural-language query for the turn. Drives what gets surfaced.'),
|
|
1146
|
+
limit: z.number().int().min(1).max(50).optional().describe('Max recall results to consider before categorization.'),
|
|
1147
|
+
budget_chars: z.number().int().min(200).max(32000).optional().describe('Token budget in characters (defaults to AUDREY_CONTEXT_BUDGET_CHARS or 4000).'),
|
|
1148
|
+
mode: z.enum(['balanced', 'conservative', 'aggressive']).optional().describe('Capsule mode: conservative = fewer, higher-confidence entries; aggressive = broader sweep.'),
|
|
1149
|
+
recent_change_window_hours: z.number().int().min(1).max(720).optional().describe('How far back "recent_changes" looks (default 24h).'),
|
|
1150
|
+
include_risks: z.boolean().optional().describe('Include recent tool failures as risks (default true).'),
|
|
1151
|
+
include_contradictions: z.boolean().optional().describe('Include open contradictions (default true).'),
|
|
1152
|
+
}, async ({ query, limit, budget_chars, mode, recent_change_window_hours, include_risks, include_contradictions, }) => {
|
|
1153
|
+
try {
|
|
1154
|
+
const capsule = await audrey.capsule(query, {
|
|
1155
|
+
limit,
|
|
1156
|
+
budgetChars: budget_chars,
|
|
1157
|
+
mode,
|
|
1158
|
+
recentChangeWindowHours: recent_change_window_hours,
|
|
1159
|
+
includeRisks: include_risks,
|
|
1160
|
+
includeContradictions: include_contradictions,
|
|
1161
|
+
});
|
|
1162
|
+
return toolResult(capsule);
|
|
1163
|
+
}
|
|
1164
|
+
catch (err) {
|
|
1165
|
+
return toolError(err);
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1168
|
+
server.tool('memory_preflight', memoryPreflightToolSchema, async ({ action, tool, session_id, cwd, files, strict, limit, budget_chars, mode, failure_window_hours, include_status, record_event, include_capsule, }) => {
|
|
1169
|
+
try {
|
|
1170
|
+
const preflight = await audrey.preflight(action, {
|
|
1171
|
+
tool,
|
|
1172
|
+
sessionId: session_id,
|
|
1173
|
+
cwd,
|
|
1174
|
+
files,
|
|
1175
|
+
strict,
|
|
1176
|
+
limit,
|
|
1177
|
+
budgetChars: budget_chars,
|
|
1178
|
+
mode,
|
|
1179
|
+
recentFailureWindowHours: failure_window_hours,
|
|
1180
|
+
includeStatus: include_status,
|
|
1181
|
+
recordEvent: record_event,
|
|
1182
|
+
includeCapsule: include_capsule,
|
|
1183
|
+
});
|
|
1184
|
+
return toolResult(preflight);
|
|
1185
|
+
}
|
|
1186
|
+
catch (err) {
|
|
1187
|
+
return toolError(err);
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
server.tool('memory_reflexes', memoryReflexesToolSchema, async ({ action, tool, session_id, cwd, files, strict, limit, budget_chars, mode, failure_window_hours, include_status, record_event, include_capsule, include_preflight, }) => {
|
|
1191
|
+
try {
|
|
1192
|
+
const report = await audrey.reflexes(action, {
|
|
1193
|
+
tool,
|
|
1194
|
+
sessionId: session_id,
|
|
1195
|
+
cwd,
|
|
1196
|
+
files,
|
|
1197
|
+
strict,
|
|
1198
|
+
limit,
|
|
1199
|
+
budgetChars: budget_chars,
|
|
1200
|
+
mode,
|
|
1201
|
+
recentFailureWindowHours: failure_window_hours,
|
|
1202
|
+
includeStatus: include_status,
|
|
1203
|
+
recordEvent: record_event,
|
|
1204
|
+
includeCapsule: include_capsule,
|
|
1205
|
+
includePreflight: include_preflight,
|
|
1206
|
+
});
|
|
1207
|
+
return toolResult(report);
|
|
1208
|
+
}
|
|
1209
|
+
catch (err) {
|
|
1210
|
+
return toolError(err);
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
server.tool('memory_promote', {
|
|
1214
|
+
target: z.enum(['claude-rules']).optional().describe('Promotion target. Only claude-rules is implemented in PR 4 v1.'),
|
|
1215
|
+
min_confidence: z.number().min(0).max(1).optional().describe('Minimum memory confidence for promotion (default 0.7 for procedural, 0.8 for semantic).'),
|
|
1216
|
+
min_evidence: z.number().int().min(1).optional().describe('Minimum supporting episode count (default 2).'),
|
|
1217
|
+
limit: z.number().int().min(1).max(50).optional().describe('Max candidates to return/apply (default 20).'),
|
|
1218
|
+
dry_run: z.boolean().optional().describe('If true (default), return candidates without writing. Pair with yes=true to actually write.'),
|
|
1219
|
+
yes: z.boolean().optional().describe('Confirm write. Without this or dry_run=false the command stays in dry-run mode.'),
|
|
1220
|
+
project_dir: z.string().optional().describe('Absolute path to the project root where .claude/rules/ should be created. Defaults to process.cwd().'),
|
|
1221
|
+
}, async ({ target, min_confidence, min_evidence, limit, dry_run, yes, project_dir, }) => {
|
|
1222
|
+
try {
|
|
1223
|
+
const result = await audrey.promote({
|
|
1224
|
+
target,
|
|
1225
|
+
minConfidence: min_confidence,
|
|
1226
|
+
minEvidence: min_evidence,
|
|
1227
|
+
limit,
|
|
1228
|
+
dryRun: dry_run,
|
|
1229
|
+
yes,
|
|
1230
|
+
projectDir: project_dir,
|
|
1231
|
+
});
|
|
1232
|
+
return toolResult(result);
|
|
1233
|
+
}
|
|
1234
|
+
catch (err) {
|
|
1235
|
+
return toolError(err);
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
const transport = new StdioServerTransport();
|
|
1239
|
+
await server.connect(transport);
|
|
1240
|
+
console.error('[audrey-mcp] connected via stdio');
|
|
1241
|
+
registerShutdownHandlers(process, audrey);
|
|
1242
|
+
}
|
|
1243
|
+
function parseObserveToolArgs(argv) {
|
|
1244
|
+
const out = {};
|
|
1245
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1246
|
+
const token = argv[i];
|
|
1247
|
+
const next = () => argv[++i];
|
|
1248
|
+
if (token === '--event')
|
|
1249
|
+
out.event = next();
|
|
1250
|
+
else if (token === '--tool')
|
|
1251
|
+
out.tool = next();
|
|
1252
|
+
else if (token === '--session-id')
|
|
1253
|
+
out.sessionId = next();
|
|
1254
|
+
else if (token === '--outcome')
|
|
1255
|
+
out.outcome = next();
|
|
1256
|
+
else if (token === '--cwd')
|
|
1257
|
+
out.cwd = next();
|
|
1258
|
+
else if (token === '--error-summary')
|
|
1259
|
+
out.errorSummary = next();
|
|
1260
|
+
else if (token === '--files') {
|
|
1261
|
+
const list = next();
|
|
1262
|
+
if (list)
|
|
1263
|
+
out.files = list.split(',').map(s => s.trim()).filter(Boolean);
|
|
1264
|
+
}
|
|
1265
|
+
else if (token === '--input-json')
|
|
1266
|
+
out.inputJson = next();
|
|
1267
|
+
else if (token === '--output-json')
|
|
1268
|
+
out.outputJson = next();
|
|
1269
|
+
else if (token === '--metadata-json')
|
|
1270
|
+
out.metadataJson = next();
|
|
1271
|
+
else if (token === '--retain-details')
|
|
1272
|
+
out.retainDetails = true;
|
|
1273
|
+
}
|
|
1274
|
+
return out;
|
|
1275
|
+
}
|
|
1276
|
+
async function observeToolCli() {
|
|
1277
|
+
const args = parseObserveToolArgs(process.argv.slice(3));
|
|
1278
|
+
let stdinPayload = null;
|
|
1279
|
+
if (!process.stdin.isTTY) {
|
|
1280
|
+
const chunks = [];
|
|
1281
|
+
for await (const chunk of process.stdin)
|
|
1282
|
+
chunks.push(chunk);
|
|
1283
|
+
const raw = Buffer.concat(chunks).toString('utf-8').trim();
|
|
1284
|
+
if (raw) {
|
|
1285
|
+
try {
|
|
1286
|
+
stdinPayload = JSON.parse(raw);
|
|
1287
|
+
}
|
|
1288
|
+
catch {
|
|
1289
|
+
console.error('[audrey] observe-tool: stdin was not valid JSON, ignoring.');
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
// Auto-extract common fields from the Claude Code hook payload so the hook
|
|
1294
|
+
// config can be minimal: only --event needs to be specified on the command
|
|
1295
|
+
// line; tool_name / session_id / cwd / hook_event_name come from stdin.
|
|
1296
|
+
const effectiveEvent = args.event ?? stdinPayload?.hook_event_name;
|
|
1297
|
+
const effectiveTool = args.tool ?? stdinPayload?.tool_name;
|
|
1298
|
+
if (!effectiveEvent) {
|
|
1299
|
+
console.error('[audrey] observe-tool: --event is required (or provide hook_event_name in stdin JSON)');
|
|
1300
|
+
process.exit(2);
|
|
1301
|
+
}
|
|
1302
|
+
if (!effectiveTool) {
|
|
1303
|
+
console.error('[audrey] observe-tool: --tool is required (or provide tool_name in stdin JSON)');
|
|
1304
|
+
process.exit(2);
|
|
1305
|
+
}
|
|
1306
|
+
const parseMaybeJson = (text) => {
|
|
1307
|
+
if (text == null)
|
|
1308
|
+
return undefined;
|
|
1309
|
+
try {
|
|
1310
|
+
return JSON.parse(text);
|
|
1311
|
+
}
|
|
1312
|
+
catch {
|
|
1313
|
+
return text;
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
const inputPayload = args.inputJson !== undefined
|
|
1317
|
+
? parseMaybeJson(args.inputJson)
|
|
1318
|
+
: stdinPayload?.tool_input ?? stdinPayload?.input;
|
|
1319
|
+
const outputPayload = args.outputJson !== undefined
|
|
1320
|
+
? parseMaybeJson(args.outputJson)
|
|
1321
|
+
: stdinPayload?.tool_response ?? stdinPayload?.tool_output ?? stdinPayload?.output;
|
|
1322
|
+
const metadataPayload = args.metadataJson !== undefined
|
|
1323
|
+
? parseMaybeJson(args.metadataJson)
|
|
1324
|
+
: stdinPayload?.metadata;
|
|
1325
|
+
const sessionId = args.sessionId ?? stdinPayload?.session_id;
|
|
1326
|
+
const cwd = args.cwd ?? stdinPayload?.cwd;
|
|
1327
|
+
// Detect failure from Claude Code hook payload shape: tool_response often
|
|
1328
|
+
// includes a non-empty error or a success=false flag for failed tools.
|
|
1329
|
+
let outcome = args.outcome;
|
|
1330
|
+
let errorSummary = args.errorSummary ?? stdinPayload?.error_summary;
|
|
1331
|
+
if (outcome == null && effectiveEvent === 'PostToolUse') {
|
|
1332
|
+
const resp = stdinPayload?.tool_response ?? undefined;
|
|
1333
|
+
const errField = resp?.['error'] ?? resp?.['stderr'];
|
|
1334
|
+
const successField = resp?.['success'];
|
|
1335
|
+
if (typeof successField === 'boolean') {
|
|
1336
|
+
outcome = successField ? 'succeeded' : 'failed';
|
|
1337
|
+
}
|
|
1338
|
+
else if (errField && (typeof errField === 'string' ? errField.length > 0 : true)) {
|
|
1339
|
+
outcome = 'failed';
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
outcome = 'succeeded';
|
|
1343
|
+
}
|
|
1344
|
+
if (outcome === 'failed' && !errorSummary) {
|
|
1345
|
+
errorSummary = typeof errField === 'string' ? errField : JSON.stringify(errField ?? resp);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
const dataDir = resolveDataDir(process.env);
|
|
1349
|
+
const embedding = resolveEmbeddingProvider(process.env, process.env['AUDREY_EMBEDDING_PROVIDER']);
|
|
1350
|
+
const audrey = new Audrey({
|
|
1351
|
+
dataDir,
|
|
1352
|
+
agent: process.env['AUDREY_AGENT'] ?? 'observe-tool',
|
|
1353
|
+
embedding,
|
|
1354
|
+
});
|
|
1355
|
+
try {
|
|
1356
|
+
const result = audrey.observeTool({
|
|
1357
|
+
event: effectiveEvent,
|
|
1358
|
+
tool: effectiveTool,
|
|
1359
|
+
sessionId,
|
|
1360
|
+
input: inputPayload,
|
|
1361
|
+
output: outputPayload,
|
|
1362
|
+
outcome,
|
|
1363
|
+
errorSummary,
|
|
1364
|
+
cwd,
|
|
1365
|
+
files: args.files,
|
|
1366
|
+
metadata: (metadataPayload ?? undefined),
|
|
1367
|
+
retainDetails: args.retainDetails,
|
|
1368
|
+
});
|
|
1369
|
+
const summary = {
|
|
1370
|
+
id: result.event.id,
|
|
1371
|
+
event_type: result.event.event_type,
|
|
1372
|
+
tool_name: result.event.tool_name,
|
|
1373
|
+
outcome: result.event.outcome,
|
|
1374
|
+
redaction_state: result.event.redaction_state,
|
|
1375
|
+
redactions: result.redactions,
|
|
1376
|
+
};
|
|
1377
|
+
console.log(JSON.stringify(summary));
|
|
1378
|
+
}
|
|
1379
|
+
finally {
|
|
1380
|
+
audrey.close();
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
function parsePromoteArgs(argv) {
|
|
1384
|
+
const out = {};
|
|
1385
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1386
|
+
const token = argv[i];
|
|
1387
|
+
const next = () => argv[++i];
|
|
1388
|
+
if (token === '--target')
|
|
1389
|
+
out.target = next();
|
|
1390
|
+
else if (token === '--min-confidence')
|
|
1391
|
+
out.minConfidence = Number.parseFloat(next() ?? '');
|
|
1392
|
+
else if (token === '--min-evidence')
|
|
1393
|
+
out.minEvidence = Number.parseInt(next() ?? '', 10);
|
|
1394
|
+
else if (token === '--limit')
|
|
1395
|
+
out.limit = Number.parseInt(next() ?? '', 10);
|
|
1396
|
+
else if (token === '--dry-run')
|
|
1397
|
+
out.dryRun = true;
|
|
1398
|
+
else if (token === '--yes' || token === '-y')
|
|
1399
|
+
out.yes = true;
|
|
1400
|
+
else if (token === '--project-dir')
|
|
1401
|
+
out.projectDir = next();
|
|
1402
|
+
else if (token === '--json')
|
|
1403
|
+
out.json = true;
|
|
1404
|
+
}
|
|
1405
|
+
return out;
|
|
1406
|
+
}
|
|
1407
|
+
async function promoteCli() {
|
|
1408
|
+
const args = parsePromoteArgs(process.argv.slice(3));
|
|
1409
|
+
const dataDir = resolveDataDir(process.env);
|
|
1410
|
+
const embedding = resolveEmbeddingProvider(process.env, process.env['AUDREY_EMBEDDING_PROVIDER']);
|
|
1411
|
+
const audrey = new Audrey({
|
|
1412
|
+
dataDir,
|
|
1413
|
+
agent: process.env['AUDREY_AGENT'] ?? 'promote',
|
|
1414
|
+
embedding,
|
|
1415
|
+
});
|
|
1416
|
+
try {
|
|
1417
|
+
const result = await audrey.promote({
|
|
1418
|
+
target: args.target,
|
|
1419
|
+
minConfidence: args.minConfidence,
|
|
1420
|
+
minEvidence: args.minEvidence,
|
|
1421
|
+
limit: args.limit,
|
|
1422
|
+
dryRun: args.dryRun ?? !args.yes,
|
|
1423
|
+
yes: args.yes,
|
|
1424
|
+
projectDir: args.projectDir,
|
|
1425
|
+
});
|
|
1426
|
+
if (args.json) {
|
|
1427
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
const candidateLabel = `${result.candidates.length} candidate${result.candidates.length === 1 ? '' : 's'}`;
|
|
1431
|
+
const appliedLabel = `${result.applied.length} rule${result.applied.length === 1 ? '' : 's'}`;
|
|
1432
|
+
const header = result.dry_run
|
|
1433
|
+
? `[audrey] promote (dry-run) - ${candidateLabel} for target "${result.target}"`
|
|
1434
|
+
: `[audrey] promote - wrote ${appliedLabel} to ${result.project_dir}`;
|
|
1435
|
+
console.log(header);
|
|
1436
|
+
if (result.candidates.length === 0) {
|
|
1437
|
+
console.log(' (no candidates met the confidence/evidence thresholds)');
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
for (const c of result.candidates) {
|
|
1441
|
+
console.log('');
|
|
1442
|
+
console.log(` ${c.rendered_path} [score ${c.score.toFixed(1)}]`);
|
|
1443
|
+
const snippet = c.content.length > 120 ? c.content.slice(0, 117) + '...' : c.content;
|
|
1444
|
+
console.log(` memory: ${snippet}`);
|
|
1445
|
+
console.log(` why: ${c.reason}`);
|
|
1446
|
+
console.log(` confidence=${(c.confidence * 100).toFixed(1)}% `
|
|
1447
|
+
+ `evidence=${c.evidence_count} prevented_failures=${c.failure_prevented}`);
|
|
1448
|
+
}
|
|
1449
|
+
if (result.dry_run) {
|
|
1450
|
+
console.log('');
|
|
1451
|
+
console.log(' Re-run with --yes to write these rules to disk.');
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
finally {
|
|
1455
|
+
audrey.close();
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
const isDirectRun = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
1459
|
+
if (isDirectRun) {
|
|
1460
|
+
if (subcommand === 'install') {
|
|
1461
|
+
install();
|
|
1462
|
+
}
|
|
1463
|
+
else if (subcommand === 'uninstall') {
|
|
1464
|
+
uninstall();
|
|
1465
|
+
}
|
|
1466
|
+
else if (subcommand === 'mcp-config') {
|
|
1467
|
+
printMcpConfig();
|
|
1468
|
+
}
|
|
1469
|
+
else if (subcommand === 'demo') {
|
|
1470
|
+
runDemoCommand().catch(err => {
|
|
1471
|
+
console.error('[audrey] demo failed:', err);
|
|
1472
|
+
process.exit(1);
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
else if (subcommand === 'reembed') {
|
|
1476
|
+
reembed().catch(err => {
|
|
1477
|
+
console.error('[audrey] reembed failed:', err);
|
|
1478
|
+
process.exit(1);
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
else if (subcommand === 'dream') {
|
|
1482
|
+
dream().catch(err => {
|
|
1483
|
+
console.error('[audrey] dream failed:', err);
|
|
1484
|
+
process.exit(1);
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
else if (subcommand === 'greeting') {
|
|
1488
|
+
greeting().catch(err => {
|
|
1489
|
+
console.error('[audrey] greeting failed:', err);
|
|
1490
|
+
process.exit(1);
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
else if (subcommand === 'reflect') {
|
|
1494
|
+
reflect().catch(err => {
|
|
1495
|
+
console.error('[audrey] reflect failed:', err);
|
|
1496
|
+
process.exit(1);
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
else if (subcommand === 'serve') {
|
|
1500
|
+
serveHttp().catch(err => {
|
|
1501
|
+
console.error('[audrey] serve failed:', err);
|
|
1502
|
+
process.exit(1);
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
else if (subcommand === 'status') {
|
|
1506
|
+
status();
|
|
1507
|
+
}
|
|
1508
|
+
else if (subcommand === 'doctor') {
|
|
1509
|
+
doctor();
|
|
1510
|
+
}
|
|
1511
|
+
else if (subcommand === 'observe-tool') {
|
|
1512
|
+
observeToolCli().catch(err => {
|
|
1513
|
+
console.error('[audrey] observe-tool failed:', err);
|
|
1514
|
+
process.exit(1);
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
else if (subcommand === 'promote') {
|
|
1518
|
+
promoteCli().catch(err => {
|
|
1519
|
+
console.error('[audrey] promote failed:', err);
|
|
1520
|
+
process.exit(1);
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
else {
|
|
1524
|
+
main().catch(err => {
|
|
1525
|
+
console.error('[audrey-mcp] fatal:', err);
|
|
1526
|
+
process.exit(1);
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
//# sourceMappingURL=index.js.map
|