audrey 0.23.1 → 1.0.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.
Files changed (250) hide show
  1. package/CHANGELOG.md +101 -15
  2. package/LICENSE +21 -21
  3. package/README.md +232 -6
  4. package/SECURITY.md +2 -1
  5. package/benchmarks/adapter-kit.mjs +20 -0
  6. package/benchmarks/adapter-self-test.mjs +166 -0
  7. package/benchmarks/adapters/example-allow.mjs +28 -0
  8. package/benchmarks/adapters/mem0-platform.mjs +267 -0
  9. package/benchmarks/adapters/registry.json +51 -0
  10. package/benchmarks/adapters/zep-cloud.mjs +280 -0
  11. package/benchmarks/baselines.js +169 -0
  12. package/benchmarks/build-leaderboard.mjs +170 -0
  13. package/benchmarks/cases.js +537 -0
  14. package/benchmarks/create-conformance-card.mjs +139 -0
  15. package/benchmarks/create-submission-bundle.mjs +176 -0
  16. package/benchmarks/dry-run-external-adapters.mjs +165 -0
  17. package/benchmarks/guardbench.js +1125 -0
  18. package/benchmarks/output/adapter-self-test/guardbench-adapter-self-test.json +50 -0
  19. package/benchmarks/output/external/guardbench-external-dry-run.json +69 -0
  20. package/benchmarks/output/external/guardbench-external-evidence.json +56 -0
  21. package/benchmarks/output/guardbench-conformance-card.json +63 -0
  22. package/benchmarks/output/guardbench-manifest.json +414 -0
  23. package/benchmarks/output/guardbench-raw.json +1271 -0
  24. package/benchmarks/output/guardbench-summary.json +2107 -0
  25. package/benchmarks/output/leaderboard/guardbench-leaderboard.json +93 -0
  26. package/benchmarks/output/leaderboard/guardbench-leaderboard.md +7 -0
  27. package/benchmarks/output/submission-bundle/guardbench-conformance-card.json +63 -0
  28. package/benchmarks/output/submission-bundle/guardbench-manifest.json +414 -0
  29. package/benchmarks/output/submission-bundle/guardbench-raw.json +1271 -0
  30. package/benchmarks/output/submission-bundle/guardbench-summary.json +2107 -0
  31. package/benchmarks/output/submission-bundle/schemas/guardbench-adapter-registry.schema.json +69 -0
  32. package/benchmarks/output/submission-bundle/schemas/guardbench-adapter-self-test.schema.json +156 -0
  33. package/benchmarks/output/submission-bundle/schemas/guardbench-conformance-card.schema.json +184 -0
  34. package/benchmarks/output/submission-bundle/schemas/guardbench-external-dry-run.schema.json +74 -0
  35. package/benchmarks/output/submission-bundle/schemas/guardbench-external-evidence.schema.json +108 -0
  36. package/benchmarks/output/submission-bundle/schemas/guardbench-external-run.schema.json +160 -0
  37. package/benchmarks/output/submission-bundle/schemas/guardbench-leaderboard.schema.json +179 -0
  38. package/benchmarks/output/submission-bundle/schemas/guardbench-manifest.schema.json +213 -0
  39. package/benchmarks/output/submission-bundle/schemas/guardbench-publication-verification.schema.json +47 -0
  40. package/benchmarks/output/submission-bundle/schemas/guardbench-raw.schema.json +184 -0
  41. package/benchmarks/output/submission-bundle/schemas/guardbench-submission-manifest.schema.json +151 -0
  42. package/benchmarks/output/submission-bundle/schemas/guardbench-summary.schema.json +249 -0
  43. package/benchmarks/output/submission-bundle/submission-manifest.json +131 -0
  44. package/benchmarks/output/submission-bundle/validation-report.json +31 -0
  45. package/benchmarks/output/summary.json +2354 -0
  46. package/benchmarks/perf-snapshot.js +304 -0
  47. package/benchmarks/perf.bench.js +161 -0
  48. package/benchmarks/public-paths.mjs +78 -0
  49. package/benchmarks/reference-results.js +70 -0
  50. package/benchmarks/report.js +259 -0
  51. package/benchmarks/run-external-guardbench.mjs +281 -0
  52. package/benchmarks/run.js +682 -0
  53. package/benchmarks/schemas/guardbench-adapter-registry.schema.json +69 -0
  54. package/benchmarks/schemas/guardbench-adapter-self-test.schema.json +156 -0
  55. package/benchmarks/schemas/guardbench-conformance-card.schema.json +184 -0
  56. package/benchmarks/schemas/guardbench-external-dry-run.schema.json +74 -0
  57. package/benchmarks/schemas/guardbench-external-evidence.schema.json +108 -0
  58. package/benchmarks/schemas/guardbench-external-run.schema.json +160 -0
  59. package/benchmarks/schemas/guardbench-leaderboard.schema.json +179 -0
  60. package/benchmarks/schemas/guardbench-manifest.schema.json +213 -0
  61. package/benchmarks/schemas/guardbench-publication-verification.schema.json +47 -0
  62. package/benchmarks/schemas/guardbench-raw.schema.json +184 -0
  63. package/benchmarks/schemas/guardbench-submission-manifest.schema.json +151 -0
  64. package/benchmarks/schemas/guardbench-summary.schema.json +249 -0
  65. package/benchmarks/snapshots/perf-0.22.2.json +123 -0
  66. package/benchmarks/snapshots/perf-0.23.0.json +123 -0
  67. package/benchmarks/validate-adapter-module.mjs +104 -0
  68. package/benchmarks/validate-adapter-registry.mjs +134 -0
  69. package/benchmarks/validate-adapter-self-test.mjs +96 -0
  70. package/benchmarks/validate-guardbench-artifacts.mjs +343 -0
  71. package/benchmarks/verify-external-evidence.mjs +296 -0
  72. package/benchmarks/verify-publication-artifacts.mjs +286 -0
  73. package/benchmarks/verify-submission-bundle.mjs +167 -0
  74. package/dist/mcp-server/config.d.ts +1 -1
  75. package/dist/mcp-server/config.d.ts.map +1 -1
  76. package/dist/mcp-server/config.js +1 -1
  77. package/dist/mcp-server/config.js.map +1 -1
  78. package/dist/mcp-server/index.d.ts +65 -3
  79. package/dist/mcp-server/index.d.ts.map +1 -1
  80. package/dist/mcp-server/index.js +675 -157
  81. package/dist/mcp-server/index.js.map +1 -1
  82. package/dist/src/action-key.d.ts +9 -0
  83. package/dist/src/action-key.d.ts.map +1 -0
  84. package/dist/src/action-key.js +49 -0
  85. package/dist/src/action-key.js.map +1 -0
  86. package/dist/src/adaptive.js +5 -5
  87. package/dist/src/affect.js +8 -8
  88. package/dist/src/audrey.d.ts +13 -0
  89. package/dist/src/audrey.d.ts.map +1 -1
  90. package/dist/src/audrey.js +68 -3
  91. package/dist/src/audrey.js.map +1 -1
  92. package/dist/src/capsule.js +4 -4
  93. package/dist/src/causal.js +3 -3
  94. package/dist/src/consolidate.js +48 -48
  95. package/dist/src/controller.d.ts +78 -6
  96. package/dist/src/controller.d.ts.map +1 -1
  97. package/dist/src/controller.js +273 -53
  98. package/dist/src/controller.js.map +1 -1
  99. package/dist/src/db.js +172 -172
  100. package/dist/src/decay.js +8 -8
  101. package/dist/src/embedding.d.ts +2 -1
  102. package/dist/src/embedding.d.ts.map +1 -1
  103. package/dist/src/embedding.js +39 -29
  104. package/dist/src/embedding.js.map +1 -1
  105. package/dist/src/encode.js +6 -6
  106. package/dist/src/feedback.d.ts +6 -0
  107. package/dist/src/feedback.d.ts.map +1 -1
  108. package/dist/src/feedback.js +6 -0
  109. package/dist/src/feedback.js.map +1 -1
  110. package/dist/src/forget.js +12 -12
  111. package/dist/src/hybrid-recall.js +9 -9
  112. package/dist/src/impact.js +6 -6
  113. package/dist/src/import.d.ts +3 -3
  114. package/dist/src/import.js +41 -41
  115. package/dist/src/index.d.ts +5 -4
  116. package/dist/src/index.d.ts.map +1 -1
  117. package/dist/src/index.js +3 -3
  118. package/dist/src/index.js.map +1 -1
  119. package/dist/src/interference.js +14 -14
  120. package/dist/src/introspect.js +18 -18
  121. package/dist/src/preflight.d.ts.map +1 -1
  122. package/dist/src/preflight.js +41 -0
  123. package/dist/src/preflight.js.map +1 -1
  124. package/dist/src/promote.js +7 -7
  125. package/dist/src/prompts.js +118 -118
  126. package/dist/src/recall.js +30 -30
  127. package/dist/src/reflexes.d.ts +1 -0
  128. package/dist/src/reflexes.d.ts.map +1 -1
  129. package/dist/src/reflexes.js +3 -0
  130. package/dist/src/reflexes.js.map +1 -1
  131. package/dist/src/rollback.js +4 -4
  132. package/dist/src/routes.d.ts.map +1 -1
  133. package/dist/src/routes.js +71 -2
  134. package/dist/src/routes.js.map +1 -1
  135. package/dist/src/validate.js +25 -25
  136. package/docs/AUDREY_PAPER_OUTLINE.md +175 -0
  137. package/docs/MEMORY_BENCHMARKING.md +59 -0
  138. package/docs/PRODUCTION_BACKLOG.md +304 -0
  139. package/docs/paper/00-master.md +48 -0
  140. package/docs/paper/01-introduction.md +27 -0
  141. package/docs/paper/02-related-work.md +47 -0
  142. package/docs/paper/03-problem-definition.md +108 -0
  143. package/docs/paper/04-design.md +164 -0
  144. package/docs/paper/05-guardbench-spec.md +412 -0
  145. package/docs/paper/06-implementation.md +113 -0
  146. package/docs/paper/07-evaluation.md +168 -0
  147. package/docs/paper/08-discussion-limitations.md +61 -0
  148. package/docs/paper/09-conclusion.md +11 -0
  149. package/docs/paper/SUBMISSION_README.md +162 -0
  150. package/docs/paper/appendix-a-demo-transcript.md +114 -0
  151. package/docs/paper/arxiv-compile-report.schema.json +116 -0
  152. package/docs/paper/arxiv-source.schema.json +61 -0
  153. package/docs/paper/audrey-paper-v1.md +1106 -0
  154. package/docs/paper/browser-launch-plan.json +209 -0
  155. package/docs/paper/browser-launch-plan.schema.json +100 -0
  156. package/docs/paper/browser-launch-results.json +86 -0
  157. package/docs/paper/browser-launch-results.schema.json +66 -0
  158. package/docs/paper/claim-register.json +138 -0
  159. package/docs/paper/claim-register.schema.json +81 -0
  160. package/docs/paper/evidence-ledger.md +103 -0
  161. package/docs/paper/output/arxiv/README-arxiv.txt +8 -0
  162. package/docs/paper/output/arxiv/arxiv-manifest.json +41 -0
  163. package/docs/paper/output/arxiv/main.tex +949 -0
  164. package/docs/paper/output/arxiv/references.bib +222 -0
  165. package/docs/paper/output/arxiv-compile-report.json +24 -0
  166. package/docs/paper/output/submission-bundle/LICENSE +21 -0
  167. package/docs/paper/output/submission-bundle/README.md +555 -0
  168. package/docs/paper/output/submission-bundle/benchmarks/output/adapter-self-test/guardbench-adapter-self-test.json +50 -0
  169. package/docs/paper/output/submission-bundle/benchmarks/output/external/guardbench-external-dry-run.json +69 -0
  170. package/docs/paper/output/submission-bundle/benchmarks/output/external/guardbench-external-evidence.json +56 -0
  171. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-conformance-card.json +63 -0
  172. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-manifest.json +414 -0
  173. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-raw.json +1271 -0
  174. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-summary.json +2107 -0
  175. package/docs/paper/output/submission-bundle/benchmarks/output/leaderboard/guardbench-leaderboard.json +93 -0
  176. package/docs/paper/output/submission-bundle/benchmarks/output/leaderboard/guardbench-leaderboard.md +7 -0
  177. package/docs/paper/output/submission-bundle/benchmarks/output/submission-bundle/submission-manifest.json +131 -0
  178. package/docs/paper/output/submission-bundle/benchmarks/output/submission-bundle/validation-report.json +31 -0
  179. package/docs/paper/output/submission-bundle/benchmarks/output/summary.json +2354 -0
  180. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-adapter-registry.schema.json +69 -0
  181. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-adapter-self-test.schema.json +156 -0
  182. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-conformance-card.schema.json +184 -0
  183. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-external-dry-run.schema.json +74 -0
  184. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-external-evidence.schema.json +108 -0
  185. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-external-run.schema.json +160 -0
  186. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-leaderboard.schema.json +179 -0
  187. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-manifest.schema.json +213 -0
  188. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-publication-verification.schema.json +47 -0
  189. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-raw.schema.json +184 -0
  190. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-submission-manifest.schema.json +151 -0
  191. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-summary.schema.json +249 -0
  192. package/docs/paper/output/submission-bundle/docs/AUDREY_PAPER_OUTLINE.md +175 -0
  193. package/docs/paper/output/submission-bundle/docs/paper/00-master.md +48 -0
  194. package/docs/paper/output/submission-bundle/docs/paper/01-introduction.md +27 -0
  195. package/docs/paper/output/submission-bundle/docs/paper/02-related-work.md +47 -0
  196. package/docs/paper/output/submission-bundle/docs/paper/03-problem-definition.md +108 -0
  197. package/docs/paper/output/submission-bundle/docs/paper/04-design.md +164 -0
  198. package/docs/paper/output/submission-bundle/docs/paper/05-guardbench-spec.md +412 -0
  199. package/docs/paper/output/submission-bundle/docs/paper/06-implementation.md +113 -0
  200. package/docs/paper/output/submission-bundle/docs/paper/07-evaluation.md +168 -0
  201. package/docs/paper/output/submission-bundle/docs/paper/08-discussion-limitations.md +61 -0
  202. package/docs/paper/output/submission-bundle/docs/paper/09-conclusion.md +11 -0
  203. package/docs/paper/output/submission-bundle/docs/paper/SUBMISSION_README.md +162 -0
  204. package/docs/paper/output/submission-bundle/docs/paper/appendix-a-demo-transcript.md +114 -0
  205. package/docs/paper/output/submission-bundle/docs/paper/arxiv-compile-report.schema.json +116 -0
  206. package/docs/paper/output/submission-bundle/docs/paper/arxiv-source.schema.json +61 -0
  207. package/docs/paper/output/submission-bundle/docs/paper/audrey-paper-v1.md +1106 -0
  208. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-plan.json +209 -0
  209. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-plan.schema.json +100 -0
  210. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-results.json +86 -0
  211. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-results.schema.json +66 -0
  212. package/docs/paper/output/submission-bundle/docs/paper/claim-register.json +138 -0
  213. package/docs/paper/output/submission-bundle/docs/paper/claim-register.schema.json +81 -0
  214. package/docs/paper/output/submission-bundle/docs/paper/evidence-ledger.md +103 -0
  215. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/README-arxiv.txt +8 -0
  216. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/arxiv-manifest.json +41 -0
  217. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/main.tex +949 -0
  218. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/references.bib +222 -0
  219. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv-compile-report.json +24 -0
  220. package/docs/paper/output/submission-bundle/docs/paper/paper-submission-bundle.schema.json +70 -0
  221. package/docs/paper/output/submission-bundle/docs/paper/publication-pack.json +81 -0
  222. package/docs/paper/output/submission-bundle/docs/paper/publication-pack.schema.json +60 -0
  223. package/docs/paper/output/submission-bundle/docs/paper/references.bib +222 -0
  224. package/docs/paper/output/submission-bundle/package.json +212 -0
  225. package/docs/paper/output/submission-bundle/paper-submission-manifest.json +379 -0
  226. package/docs/paper/paper-submission-bundle.schema.json +70 -0
  227. package/docs/paper/publication-pack.json +81 -0
  228. package/docs/paper/publication-pack.schema.json +60 -0
  229. package/docs/paper/references.bib +222 -0
  230. package/package.json +87 -4
  231. package/scripts/audit-release-completion.mjs +362 -0
  232. package/scripts/create-arxiv-source.mjs +362 -0
  233. package/scripts/create-paper-submission-bundle.mjs +210 -0
  234. package/scripts/finalize-release.mjs +526 -0
  235. package/scripts/prepare-release-cut.mjs +269 -0
  236. package/scripts/publish-release-bundle.mjs +209 -0
  237. package/scripts/publish-release-github-api.mjs +429 -0
  238. package/scripts/run-vitest.mjs +34 -0
  239. package/scripts/smoke-cli.js +92 -0
  240. package/scripts/sync-paper-artifacts.mjs +109 -0
  241. package/scripts/verify-arxiv-compile.mjs +440 -0
  242. package/scripts/verify-arxiv-source.mjs +194 -0
  243. package/scripts/verify-browser-launch-plan.mjs +237 -0
  244. package/scripts/verify-browser-launch-results.mjs +285 -0
  245. package/scripts/verify-paper-artifacts.mjs +338 -0
  246. package/scripts/verify-paper-claims.mjs +226 -0
  247. package/scripts/verify-paper-submission-bundle.mjs +207 -0
  248. package/scripts/verify-publication-pack.mjs +196 -0
  249. package/scripts/verify-python-package.py +201 -0
  250. package/scripts/verify-release-readiness.mjs +785 -0
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { z } from 'zod';
3
3
  import { homedir, platform, tmpdir } from 'node:os';
4
- import { join, resolve } from 'node:path';
5
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from 'node:fs';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
6
6
  import { execFileSync } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { Audrey, MemoryController } from '../src/index.js';
@@ -120,6 +120,28 @@ export const memoryPreflightToolSchema = {
120
120
  include_capsule: z.boolean().optional().describe('If false, omit the embedded Memory Capsule from the response.'),
121
121
  scope: z.enum(['agent', 'shared']).optional().describe('agent restricts memory recall to this server agent identity. shared searches the whole store. Defaults to agent.'),
122
122
  };
123
+ const { record_event: _preflightRecordEvent, ...memoryGuardBeforeFields } = memoryPreflightToolSchema;
124
+ export const memoryGuardBeforeToolSchema = {
125
+ ...memoryGuardBeforeFields,
126
+ session_id: z.string().optional().describe('Session identifier for grouping the required guard receipt event.'),
127
+ files: z.array(z.string()).optional().describe('File paths to fingerprint in the required guard receipt.'),
128
+ };
129
+ export const memoryGuardAfterToolSchema = {
130
+ receipt_id: z.string()
131
+ .refine(isNonEmptyText, 'Receipt id must not be empty')
132
+ .describe('Receipt id returned by memory_guard_before.'),
133
+ tool: z.string().optional().describe('Tool or command family that completed, e.g. Bash, npm test, Edit, deploy.'),
134
+ session_id: z.string().optional().describe('Session identifier for grouping related guard events.'),
135
+ input: z.unknown().optional().describe('Tool input. Hashed and never stored raw; redacted metadata is only stored when retain_details is true.'),
136
+ output: z.unknown().optional().describe('Tool output. Same redaction and storage policy as input.'),
137
+ outcome: z.enum(['succeeded', 'failed', 'blocked', 'skipped', 'unknown']).optional().describe('Outcome classification'),
138
+ error_summary: z.string().optional().describe('Short error description if the action failed. Redacted and truncated to 2 KB.'),
139
+ cwd: z.string().optional().describe('Working directory at the time of the action.'),
140
+ files: z.array(z.string()).optional().describe('File paths to fingerprint (size + mtime + content hash).'),
141
+ metadata: z.record(z.string(), z.unknown()).optional().describe('Arbitrary structured metadata (redacted before storage).'),
142
+ retain_details: z.boolean().optional().describe('If true, redacted input and output payloads are stored alongside hashes. Defaults to false.'),
143
+ evidence_feedback: z.record(z.string(), z.enum(['used', 'helpful', 'wrong'])).optional().describe('Map of evidence ids from the guard receipt to memory validation outcomes.'),
144
+ };
123
145
  export const memoryReflexesToolSchema = {
124
146
  ...memoryPreflightToolSchema,
125
147
  include_preflight: z.boolean().optional().describe('If true, include the full underlying preflight report.'),
@@ -426,10 +448,20 @@ export function formatInstallGuide(host, env = process.env, dryRun = false) {
426
448
  'Generated MCP config:',
427
449
  formatMcpHostConfig(normalizedHost, env),
428
450
  '',
451
+ ...(normalizedHost === 'claude-code'
452
+ ? [
453
+ 'Generated Claude Code hook config:',
454
+ formatClaudeCodeHookConfig(),
455
+ '',
456
+ ]
457
+ : []),
429
458
  'Next steps:',
430
459
  ];
431
460
  if (normalizedHost === 'claude-code') {
432
461
  lines.push('- Run without --dry-run to register Audrey through Claude Code: npx audrey install --host claude-code');
462
+ lines.push('- Apply project hooks with: npx audrey hook-config claude-code --apply --scope project');
463
+ lines.push('- Apply user hooks with: npx audrey hook-config claude-code --apply --scope user');
464
+ lines.push('- Verify hooks in Claude Code with: /hooks');
433
465
  lines.push('- Verify with: claude mcp list');
434
466
  }
435
467
  else if (normalizedHost === 'codex') {
@@ -496,49 +528,51 @@ function installClaudeCode(options = { includeSecrets: false }) {
496
528
  console.error('Failed to register MCP server. Is Claude Code installed and on your PATH?');
497
529
  process.exit(1);
498
530
  }
499
- console.log(`
500
- Audrey registered as "${SERVER_NAME}" with Claude Code.
501
-
502
- 20 MCP tools available in every session:
503
- memory_encode - Store observations, facts, preferences
504
- memory_recall - Search memories by semantic similarity
505
- memory_consolidate - Extract principles from accumulated episodes
506
- memory_dream - Full sleep cycle: consolidate + decay + stats
507
- memory_introspect - Check memory system health
508
- memory_resolve_truth - Resolve contradictions between claims
509
- memory_export - Export all memories as JSON snapshot
510
- memory_import - Import a snapshot into a fresh database
511
- memory_forget - Forget a specific memory by ID or query
512
- memory_validate - Closed-loop feedback: helpful/used/wrong outcomes
513
- memory_decay - Apply forgetting curves, transition low-confidence to dormant
514
- memory_status - Check brain health (episode/vec sync, dimensions)
515
- memory_reflect - Form lasting memories from a conversation
516
- memory_greeting - Wake up as yourself: load identity, context, mood
517
- memory_observe_tool - Record redacted tool-use events
518
- memory_recent_failures - Inspect recent failed tool events
519
- memory_capsule - Return a ranked, evidence-backed memory packet
520
- memory_preflight - Check memory before an agent acts
521
- memory_reflexes - Convert preflight evidence into trigger-response reflexes
522
- memory_promote - Promote repeated lessons into project rules
523
-
524
- CLI subcommands:
525
- npx audrey demo - Run a 60-second local proof with no network calls
526
- npx audrey doctor - Diagnose runtime, store health, and host config readiness
527
- npx audrey install - Register MCP server with Claude Code
528
- npx audrey install --host codex --dry-run - Print safe host setup instructions
529
- npx audrey mcp-config codex - Print Codex MCP TOML
530
- npx audrey mcp-config generic - Print JSON config for other MCP hosts
531
- npx audrey uninstall - Remove MCP server registration
532
- npx audrey status - Show memory store health and stats
533
- npx audrey status --json - Emit machine-readable health output
534
- npx audrey status --json --fail-on-unhealthy - Exit non-zero on unhealthy status
535
- npx audrey greeting - Output session briefing (for hooks)
536
- npx audrey reflect - Reflect on conversation + dream cycle (for hooks)
537
- npx audrey dream - Run consolidation + decay cycle
538
- npx audrey reembed - Re-embed all memories with current provider
539
-
540
- Data stored in: ${dataDir}
541
- Verify: claude mcp list
531
+ console.log(`
532
+ Audrey registered as "${SERVER_NAME}" with Claude Code.
533
+
534
+ 20 MCP tools available in every session:
535
+ memory_encode - Store observations, facts, preferences
536
+ memory_recall - Search memories by semantic similarity
537
+ memory_consolidate - Extract principles from accumulated episodes
538
+ memory_dream - Full sleep cycle: consolidate + decay + stats
539
+ memory_introspect - Check memory system health
540
+ memory_resolve_truth - Resolve contradictions between claims
541
+ memory_export - Export all memories as JSON snapshot
542
+ memory_import - Import a snapshot into a fresh database
543
+ memory_forget - Forget a specific memory by ID or query
544
+ memory_validate - Closed-loop feedback: helpful/used/wrong outcomes
545
+ memory_decay - Apply forgetting curves, transition low-confidence to dormant
546
+ memory_status - Check brain health (episode/vec sync, dimensions)
547
+ memory_reflect - Form lasting memories from a conversation
548
+ memory_greeting - Wake up as yourself: load identity, context, mood
549
+ memory_observe_tool - Record redacted tool-use events
550
+ memory_recent_failures - Inspect recent failed tool events
551
+ memory_capsule - Return a ranked, evidence-backed memory packet
552
+ memory_preflight - Check memory before an agent acts
553
+ memory_guard_before - Create a guard receipt before an agent acts
554
+ memory_guard_after - Record the outcome for a guard receipt
555
+ memory_reflexes - Convert preflight evidence into trigger-response reflexes
556
+ memory_promote - Promote repeated lessons into project rules
557
+
558
+ CLI subcommands:
559
+ npx audrey demo - Run a 60-second local proof with no network calls
560
+ npx audrey doctor - Diagnose runtime, store health, and host config readiness
561
+ npx audrey install - Register MCP server with Claude Code
562
+ npx audrey install --host codex --dry-run - Print safe host setup instructions
563
+ npx audrey mcp-config codex - Print Codex MCP TOML
564
+ npx audrey mcp-config generic - Print JSON config for other MCP hosts
565
+ npx audrey uninstall - Remove MCP server registration
566
+ npx audrey status - Show memory store health and stats
567
+ npx audrey status --json - Emit machine-readable health output
568
+ npx audrey status --json --fail-on-unhealthy - Exit non-zero on unhealthy status
569
+ npx audrey greeting - Output session briefing (for hooks)
570
+ npx audrey reflect - Reflect on conversation + dream cycle (for hooks)
571
+ npx audrey dream - Run consolidation + decay cycle
572
+ npx audrey reembed - Re-embed all memories with current provider
573
+
574
+ Data stored in: ${dataDir}
575
+ Verify: claude mcp list
542
576
  `);
543
577
  }
544
578
  function install() {
@@ -584,9 +618,238 @@ function printMcpConfig() {
584
618
  process.exit(2);
585
619
  }
586
620
  }
621
+ function printHookConfig() {
622
+ let options;
623
+ try {
624
+ options = parseHookConfigOptions();
625
+ }
626
+ catch (err) {
627
+ const message = err instanceof Error ? err.message : String(err);
628
+ console.error(`[audrey] hook-config failed: ${message}`);
629
+ process.exit(2);
630
+ }
631
+ if (options.host !== 'claude-code') {
632
+ console.error(`[audrey] hook-config currently supports claude-code only, got "${options.host}"`);
633
+ process.exit(2);
634
+ }
635
+ if (!options.apply) {
636
+ console.log(formatClaudeCodeHookConfig());
637
+ return;
638
+ }
639
+ try {
640
+ const settingsPath = options.settingsPath ?? defaultClaudeCodeSettingsPath(options);
641
+ const result = applyClaudeCodeHookConfig({
642
+ settingsPath,
643
+ dryRun: options.dryRun,
644
+ });
645
+ const action = result.dryRun
646
+ ? result.changed ? 'would update' : 'would leave unchanged'
647
+ : result.changed ? 'updated' : 'already up to date';
648
+ console.log(`[audrey] Claude Code hook settings ${action}: ${result.settingsPath}`);
649
+ if (result.backupPath)
650
+ console.log(`[audrey] backup written: ${result.backupPath}`);
651
+ if (result.dryRun)
652
+ console.log(JSON.stringify(result.settings, null, 2));
653
+ }
654
+ catch (err) {
655
+ const message = err instanceof Error ? err.message : String(err);
656
+ console.error(`[audrey] hook-config failed: ${message}`);
657
+ process.exit(2);
658
+ }
659
+ }
660
+ export function recallPayload(results) {
661
+ return {
662
+ results: Array.from(results),
663
+ partial_failure: results.partialFailure ?? false,
664
+ errors: results.errors ?? [],
665
+ };
666
+ }
587
667
  function sectionTitle(section) {
588
668
  return section.replace(/_/g, ' ');
589
669
  }
670
+ function shellQuote(value) {
671
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
672
+ }
673
+ export function formatClaudeCodeHookConfig(entrypoint = MCP_ENTRYPOINT) {
674
+ const node = shellQuote(process.execPath);
675
+ const entry = shellQuote(entrypoint);
676
+ const command = (subcommand) => `${node} ${entry} ${subcommand}`;
677
+ return JSON.stringify({
678
+ hooks: {
679
+ PreToolUse: [
680
+ {
681
+ matcher: '.*',
682
+ hooks: [
683
+ {
684
+ type: 'command',
685
+ command: command('guard --hook --fail-on-warn'),
686
+ },
687
+ ],
688
+ },
689
+ ],
690
+ PostToolUse: [
691
+ {
692
+ matcher: '.*',
693
+ hooks: [
694
+ {
695
+ type: 'command',
696
+ command: command('observe-tool --event PostToolUse'),
697
+ },
698
+ ],
699
+ },
700
+ ],
701
+ PostToolUseFailure: [
702
+ {
703
+ matcher: '.*',
704
+ hooks: [
705
+ {
706
+ type: 'command',
707
+ command: command('observe-tool --event PostToolUseFailure'),
708
+ },
709
+ ],
710
+ },
711
+ ],
712
+ },
713
+ }, null, 2);
714
+ }
715
+ function asJsonRecord(value) {
716
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
717
+ }
718
+ function cloneHookRows(value) {
719
+ return Array.isArray(value) ? [...value] : [];
720
+ }
721
+ function hookCommandSet(settings) {
722
+ const commands = new Set();
723
+ const hooks = asJsonRecord(settings.hooks);
724
+ for (const rows of Object.values(hooks)) {
725
+ if (!Array.isArray(rows))
726
+ continue;
727
+ for (const row of rows) {
728
+ const record = asJsonRecord(row);
729
+ const hookItems = cloneHookRows(record.hooks);
730
+ for (const item of hookItems) {
731
+ const hook = asJsonRecord(item);
732
+ if (typeof hook.command === 'string')
733
+ commands.add(hook.command);
734
+ }
735
+ }
736
+ }
737
+ return commands;
738
+ }
739
+ export function mergeClaudeCodeHookSettings(existingSettings, generatedSettings = JSON.parse(formatClaudeCodeHookConfig())) {
740
+ const existing = asJsonRecord(existingSettings);
741
+ const generated = asJsonRecord(generatedSettings);
742
+ const merged = { ...existing };
743
+ const existingHooks = asJsonRecord(existing.hooks);
744
+ const generatedHooks = asJsonRecord(generated.hooks);
745
+ const nextHooks = { ...existingHooks };
746
+ const existingCommands = hookCommandSet(existing);
747
+ for (const [eventName, generatedRows] of Object.entries(generatedHooks)) {
748
+ const rows = cloneHookRows(nextHooks[eventName]);
749
+ for (const row of cloneHookRows(generatedRows)) {
750
+ const hookRow = asJsonRecord(row);
751
+ const commands = cloneHookRows(hookRow.hooks)
752
+ .map(item => asJsonRecord(item).command)
753
+ .filter((command) => typeof command === 'string');
754
+ if (commands.some(command => existingCommands.has(command)))
755
+ continue;
756
+ rows.push(row);
757
+ for (const command of commands)
758
+ existingCommands.add(command);
759
+ }
760
+ nextHooks[eventName] = rows;
761
+ }
762
+ merged.hooks = nextHooks;
763
+ return merged;
764
+ }
765
+ function parseHookConfigOptions(argv = process.argv) {
766
+ let host = argv[3] || 'claude-code';
767
+ let apply = false;
768
+ let dryRun = false;
769
+ let scope = 'project';
770
+ let projectDir = process.cwd();
771
+ let settingsPath;
772
+ for (let i = 4; i < argv.length; i++) {
773
+ const token = argv[i];
774
+ const next = () => argv[++i];
775
+ if (token === '--apply')
776
+ apply = true;
777
+ else if (token === '--dry-run' || token === '--print')
778
+ dryRun = true;
779
+ else if (token === '--scope') {
780
+ const value = next();
781
+ if (value === 'project' || value === 'user')
782
+ scope = value;
783
+ else
784
+ throw new Error(`Unsupported hook-config scope "${value}". Use project or user.`);
785
+ }
786
+ else if (token?.startsWith('--scope=')) {
787
+ const value = token.slice('--scope='.length);
788
+ if (value === 'project' || value === 'user')
789
+ scope = value;
790
+ else
791
+ throw new Error(`Unsupported hook-config scope "${value}". Use project or user.`);
792
+ }
793
+ else if (token === '--project-dir') {
794
+ projectDir = next() ?? projectDir;
795
+ }
796
+ else if (token?.startsWith('--project-dir=')) {
797
+ projectDir = token.slice('--project-dir='.length) || projectDir;
798
+ }
799
+ else if (token === '--settings') {
800
+ settingsPath = next();
801
+ }
802
+ else if (token?.startsWith('--settings=')) {
803
+ settingsPath = token.slice('--settings='.length);
804
+ }
805
+ else if (token && !token.startsWith('-')) {
806
+ host = token;
807
+ }
808
+ else if (token) {
809
+ throw new Error(`Unknown hook-config option: ${token}`);
810
+ }
811
+ }
812
+ return { host, apply, dryRun, scope, projectDir, ...(settingsPath ? { settingsPath } : {}) };
813
+ }
814
+ function defaultClaudeCodeSettingsPath(options) {
815
+ if (options.scope === 'user')
816
+ return join(homedir(), '.claude', 'settings.json');
817
+ return join(resolve(options.projectDir), '.claude', 'settings.local.json');
818
+ }
819
+ export function applyClaudeCodeHookConfig(options) {
820
+ const settingsPath = resolve(options.settingsPath);
821
+ const existingText = existsSync(settingsPath) ? readFileSync(settingsPath, 'utf-8') : '';
822
+ let existing = {};
823
+ if (existingText.trim()) {
824
+ try {
825
+ existing = JSON.parse(existingText);
826
+ }
827
+ catch (err) {
828
+ const message = err instanceof Error ? err.message : String(err);
829
+ throw new Error(`Cannot merge Audrey hooks into invalid JSON at ${settingsPath}: ${message}`);
830
+ }
831
+ }
832
+ const settings = mergeClaudeCodeHookSettings(existing);
833
+ const nextText = `${JSON.stringify(settings, null, 2)}\n`;
834
+ const changed = existingText !== nextText;
835
+ let backupPath = null;
836
+ if (changed && !options.dryRun) {
837
+ mkdirSync(dirname(settingsPath), { recursive: true });
838
+ if (existingText) {
839
+ const stamp = (options.now ?? new Date()).toISOString().replace(/[:.]/g, '-');
840
+ backupPath = `${settingsPath}.audrey-${stamp}.bak`;
841
+ writeFileSync(backupPath, existingText, 'utf-8');
842
+ }
843
+ writeFileSync(settingsPath, nextText, 'utf-8');
844
+ }
845
+ return {
846
+ settingsPath,
847
+ dryRun: options.dryRun ?? false,
848
+ changed,
849
+ backupPath,
850
+ settings,
851
+ };
852
+ }
590
853
  function createDemoDir() {
591
854
  const preferredParent = process.env['AUDREY_DEMO_PARENT_DIR'] || tmpdir();
592
855
  try {
@@ -598,13 +861,6 @@ function createDemoDir() {
598
861
  return mkdtempSync(join(fallbackParent, 'run-'));
599
862
  }
600
863
  }
601
- export function recallPayload(results) {
602
- return {
603
- results: Array.from(results),
604
- partial_failure: results.partialFailure ?? false,
605
- errors: results.errors ?? [],
606
- };
607
- }
608
864
  function cliValue(flag, argv = process.argv) {
609
865
  for (let i = 0; i < argv.length; i++) {
610
866
  const token = argv[i];
@@ -618,7 +874,7 @@ function cliValue(flag, argv = process.argv) {
618
874
  function demoScenario(argv = process.argv) {
619
875
  return cliValue('--scenario', argv);
620
876
  }
621
- function formatGuardResult(result, { explain = false } = {}) {
877
+ function formatControllerGuardResult(result) {
622
878
  const label = result.decision === 'block'
623
879
  ? 'BLOCKED'
624
880
  : result.decision === 'warn'
@@ -632,39 +888,14 @@ function formatGuardResult(result, { explain = false } = {}) {
632
888
  if (result.evidenceIds.length > 0) {
633
889
  lines.push('');
634
890
  lines.push('Evidence:');
635
- for (const id of result.evidenceIds.slice(0, 8)) {
891
+ for (const id of result.evidenceIds.slice(0, 8))
636
892
  lines.push(`- ${id}`);
637
- }
638
893
  }
639
894
  if (result.recommendedActions.length > 0) {
640
895
  lines.push('');
641
896
  lines.push('Recommended action:');
642
- for (const action of result.recommendedActions.slice(0, 5)) {
897
+ for (const action of result.recommendedActions.slice(0, 5))
643
898
  lines.push(`- ${action}`);
644
- }
645
- }
646
- if (result.reflexes.length > 0) {
647
- lines.push('');
648
- lines.push('Memory reflexes:');
649
- for (const reflex of result.reflexes.slice(0, 5)) {
650
- lines.push(`- ${reflex.response_type}: ${reflex.response}`);
651
- }
652
- }
653
- if (explain && result.capsule) {
654
- lines.push('');
655
- lines.push('Capsule:');
656
- for (const [section, entries] of Object.entries(result.capsule.sections)) {
657
- if (!Array.isArray(entries) || entries.length === 0)
658
- continue;
659
- lines.push(`- ${sectionTitle(section)}:`);
660
- for (const entry of entries.slice(0, 3)) {
661
- lines.push(` * ${entry.memory_id}: ${entry.content}`);
662
- }
663
- }
664
- }
665
- if (result.decision === 'block') {
666
- lines.push('');
667
- lines.push('Next: fix the warning and retry, or pass --override to allow this guard check.');
668
899
  }
669
900
  return lines.join('\n');
670
901
  }
@@ -709,7 +940,7 @@ async function runRepeatedFailureDemo({ out = console.log, keep = process.argv.i
709
940
  const result = await controller.beforeAction(action);
710
941
  out('Step 3: a new preflight checks the same action before tool use.');
711
942
  out('');
712
- out(formatGuardResult(result));
943
+ out(formatControllerGuardResult(result));
713
944
  audrey.validate({ id: lessonId, outcome: 'helpful' });
714
945
  const impactReport = audrey.impact({ windowDays: 7, limit: 3 });
715
946
  out('');
@@ -733,14 +964,10 @@ async function runRepeatedFailureDemo({ out = console.log, keep = process.argv.i
733
964
  }
734
965
  }
735
966
  export async function runDemoCommand({ out = console.log, keep = process.argv.includes('--keep'), } = {}) {
736
- const scenario = demoScenario();
737
- if (scenario === 'repeated-failure') {
967
+ if (demoScenario() === 'repeated-failure') {
738
968
  await runRepeatedFailureDemo({ out, keep });
739
969
  return;
740
970
  }
741
- if (scenario) {
742
- throw new Error(`Unknown demo scenario "${scenario}". Supported scenarios: repeated-failure`);
743
- }
744
971
  const demoDir = createDemoDir();
745
972
  const audrey = new Audrey({
746
973
  dataDir: demoDir,
@@ -891,11 +1118,11 @@ export function buildStatusReport({ dataDir = resolveDataDir(process.env), claud
891
1118
  });
892
1119
  report.stats = audrey.introspect();
893
1120
  report.health = audrey.memoryStatus();
894
- report.lastConsolidation = audrey.db.prepare(`
895
- SELECT completed_at FROM consolidation_runs
896
- WHERE status = 'completed'
897
- ORDER BY completed_at DESC
898
- LIMIT 1
1121
+ report.lastConsolidation = audrey.db.prepare(`
1122
+ SELECT completed_at FROM consolidation_runs
1123
+ WHERE status = 'completed'
1124
+ ORDER BY completed_at DESC
1125
+ LIMIT 1
899
1126
  `).get()?.completed_at ?? 'never';
900
1127
  audrey.close();
901
1128
  }
@@ -1324,10 +1551,10 @@ async function main() {
1324
1551
  };
1325
1552
  if (profileEnabled) {
1326
1553
  const { results, diagnostics } = await audrey.recallWithDiagnostics(query, recallOptions);
1327
- return toolResult(recallPayload(results), diagnostics);
1554
+ return toolResult(results, diagnostics);
1328
1555
  }
1329
1556
  const results = await audrey.recall(query, recallOptions);
1330
- return toolResult(recallPayload(results));
1557
+ return toolResult(results);
1331
1558
  }
1332
1559
  catch (err) {
1333
1560
  return toolError(err);
@@ -1471,7 +1698,7 @@ async function main() {
1471
1698
  outcome: z.enum(['succeeded', 'failed', 'blocked', 'skipped', 'unknown']).optional().describe('Outcome classification'),
1472
1699
  error_summary: z.string().optional().describe('Short error description if the tool failed. Redacted and truncated to 2 KB.'),
1473
1700
  cwd: z.string().optional().describe('Working directory at the time of the tool call'),
1474
- files: z.array(z.string()).optional().describe('File paths under cwd to fingerprint (relative path + size + mtime)'),
1701
+ files: z.array(z.string()).optional().describe('File paths to fingerprint (size + mtime + content hash)'),
1475
1702
  metadata: z.record(z.string(), z.unknown()).optional().describe('Arbitrary structured metadata (redacted before storage)'),
1476
1703
  retain_details: z.boolean().optional().describe('If true, redacted input and output payloads are stored alongside hashes. Defaults to false.'),
1477
1704
  }, async ({ event, tool, session_id, input, output, outcome, error_summary, cwd, files, metadata, retain_details, }) => {
@@ -1563,6 +1790,51 @@ async function main() {
1563
1790
  return toolError(err);
1564
1791
  }
1565
1792
  });
1793
+ server.tool('memory_guard_before', memoryGuardBeforeToolSchema, async ({ action, tool, session_id, cwd, files, strict, limit, budget_chars, mode, failure_window_hours, include_status, include_capsule, scope, }) => {
1794
+ try {
1795
+ const decision = await audrey.beforeAction(action, {
1796
+ tool,
1797
+ sessionId: session_id,
1798
+ cwd,
1799
+ files,
1800
+ strict,
1801
+ limit,
1802
+ budgetChars: budget_chars,
1803
+ mode,
1804
+ recentFailureWindowHours: failure_window_hours,
1805
+ includeStatus: include_status,
1806
+ recordEvent: true,
1807
+ includeCapsule: include_capsule,
1808
+ scope: scope ?? 'agent',
1809
+ });
1810
+ return toolResult(decision);
1811
+ }
1812
+ catch (err) {
1813
+ return toolError(err);
1814
+ }
1815
+ });
1816
+ server.tool('memory_guard_after', memoryGuardAfterToolSchema, async ({ receipt_id, tool, session_id, input, output, outcome, error_summary, cwd, files, metadata, retain_details, evidence_feedback, }) => {
1817
+ try {
1818
+ const result = audrey.afterAction({
1819
+ receiptId: receipt_id,
1820
+ tool,
1821
+ sessionId: session_id,
1822
+ input,
1823
+ output,
1824
+ outcome,
1825
+ errorSummary: error_summary,
1826
+ cwd,
1827
+ files,
1828
+ metadata,
1829
+ retainDetails: retain_details,
1830
+ evidenceFeedback: evidence_feedback,
1831
+ });
1832
+ return toolResult(result);
1833
+ }
1834
+ catch (err) {
1835
+ return toolError(err);
1836
+ }
1837
+ });
1566
1838
  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, scope, }) => {
1567
1839
  try {
1568
1840
  const report = await audrey.reflexes(action, {
@@ -1783,6 +2055,9 @@ function parseGuardArgs(argv) {
1783
2055
  let override = false;
1784
2056
  let failOnWarn = false;
1785
2057
  let explain = false;
2058
+ let hook = false;
2059
+ let strict = false;
2060
+ let includeCapsule = false;
1786
2061
  for (let i = 0; i < argv.length; i++) {
1787
2062
  const token = argv[i];
1788
2063
  const next = () => argv[++i];
@@ -1816,13 +2091,16 @@ function parseGuardArgs(argv) {
1816
2091
  failOnWarn = true;
1817
2092
  else if (token === '--explain')
1818
2093
  explain = true;
2094
+ else if (token === '--hook')
2095
+ hook = true;
2096
+ else if (token === '--strict')
2097
+ strict = true;
2098
+ else if (token === '--include-capsule')
2099
+ includeCapsule = true;
1819
2100
  else if (token && token !== '--')
1820
2101
  positional.push(token);
1821
2102
  }
1822
2103
  const action = positional.join(' ').trim();
1823
- if (!action) {
1824
- throw new Error('audrey guard requires an action, e.g. audrey guard --tool Bash "npm run deploy"');
1825
- }
1826
2104
  return {
1827
2105
  tool,
1828
2106
  action,
@@ -1833,28 +2111,164 @@ function parseGuardArgs(argv) {
1833
2111
  override,
1834
2112
  failOnWarn,
1835
2113
  explain,
2114
+ hook,
2115
+ strict,
2116
+ includeCapsule,
1836
2117
  };
1837
2118
  }
2119
+ function guardDisplayDecision(result) {
2120
+ if (result.decision === 'block')
2121
+ return 'block';
2122
+ if (result.decision === 'caution')
2123
+ return 'warn';
2124
+ return 'allow';
2125
+ }
2126
+ function summarizeToolInput(payload, tool) {
2127
+ const input = (payload.tool_input && typeof payload.tool_input === 'object')
2128
+ ? payload.tool_input
2129
+ : {};
2130
+ const command = typeof input.command === 'string' ? input.command : undefined;
2131
+ const fileFields = ['file_path', 'path', 'notebook_path'];
2132
+ const files = fileFields
2133
+ .map(field => input[field])
2134
+ .filter((value) => typeof value === 'string' && value.trim().length > 0);
2135
+ if (command)
2136
+ return { action: command, command, files };
2137
+ const description = typeof input.description === 'string' ? input.description : undefined;
2138
+ if (description)
2139
+ return { action: `${tool}: ${description}`, files };
2140
+ const compactInput = JSON.stringify(input);
2141
+ return {
2142
+ action: compactInput && compactInput !== '{}'
2143
+ ? `${tool} ${compactInput}`
2144
+ : `Use ${tool}`,
2145
+ files,
2146
+ };
2147
+ }
2148
+ async function readHookPayload() {
2149
+ const chunks = [];
2150
+ for await (const chunk of process.stdin)
2151
+ chunks.push(chunk);
2152
+ const raw = Buffer.concat(chunks).toString('utf-8').trim();
2153
+ if (!raw)
2154
+ return {};
2155
+ return JSON.parse(raw);
2156
+ }
2157
+ function formatHookReason(result) {
2158
+ const recommendations = result.recommended_actions.slice(0, 3);
2159
+ return [
2160
+ result.summary,
2161
+ recommendations.length > 0 ? `Recommended: ${recommendations.join(' ')}` : '',
2162
+ result.evidence_ids.length > 0 ? `Evidence: ${result.evidence_ids.slice(0, 5).join(', ')}` : '',
2163
+ ].filter(Boolean).join('\n');
2164
+ }
2165
+ function formatPreToolUseHookOutput(result, failOnWarn) {
2166
+ const decision = guardDisplayDecision(result);
2167
+ const shouldDeny = decision === 'block' || (failOnWarn && decision === 'warn');
2168
+ if (shouldDeny) {
2169
+ return {
2170
+ hookSpecificOutput: {
2171
+ hookEventName: 'PreToolUse',
2172
+ permissionDecision: 'deny',
2173
+ permissionDecisionReason: formatHookReason(result),
2174
+ },
2175
+ };
2176
+ }
2177
+ if (decision === 'warn') {
2178
+ return {
2179
+ hookSpecificOutput: {
2180
+ hookEventName: 'PreToolUse',
2181
+ additionalContext: formatHookReason(result),
2182
+ },
2183
+ };
2184
+ }
2185
+ return {};
2186
+ }
2187
+ function formatGuardDecision(result, { explain = false } = {}) {
2188
+ const display = guardDisplayDecision(result);
2189
+ const label = display === 'block' ? 'BLOCKED' : display === 'warn' ? 'WARN' : 'ALLOW';
2190
+ const lines = [];
2191
+ lines.push(`Audrey Guard: ${label}`);
2192
+ lines.push('');
2193
+ lines.push(`Receipt: ${result.receipt_id}`);
2194
+ lines.push(`Reason: ${result.summary}`);
2195
+ lines.push(`Risk score: ${result.risk_score.toFixed(2)}`);
2196
+ if (result.evidence_ids.length > 0) {
2197
+ lines.push('');
2198
+ lines.push('Evidence:');
2199
+ for (const id of result.evidence_ids.slice(0, 8))
2200
+ lines.push(`- ${id}`);
2201
+ }
2202
+ if (result.recommended_actions.length > 0) {
2203
+ lines.push('');
2204
+ lines.push('Recommended action:');
2205
+ for (const action of result.recommended_actions.slice(0, 5))
2206
+ lines.push(`- ${action}`);
2207
+ }
2208
+ if (result.reflexes.length > 0) {
2209
+ lines.push('');
2210
+ lines.push('Memory reflexes:');
2211
+ for (const reflex of result.reflexes.slice(0, 5)) {
2212
+ lines.push(`- ${reflex.response_type}: ${reflex.response}`);
2213
+ }
2214
+ }
2215
+ if (explain && result.capsule) {
2216
+ lines.push('');
2217
+ lines.push('Capsule:');
2218
+ for (const [section, entries] of Object.entries(result.capsule.sections)) {
2219
+ if (!Array.isArray(entries) || entries.length === 0)
2220
+ continue;
2221
+ lines.push(`- ${sectionTitle(section)}:`);
2222
+ for (const entry of entries.slice(0, 3)) {
2223
+ lines.push(` * ${entry.memory_id}: ${entry.content}`);
2224
+ }
2225
+ }
2226
+ }
2227
+ if (display === 'block') {
2228
+ lines.push('');
2229
+ lines.push('Next: fix the warning and retry, or pass --override to allow this guard check.');
2230
+ }
2231
+ return lines.join('\n');
2232
+ }
1838
2233
  async function guardCli() {
1839
2234
  const args = parseGuardArgs(process.argv.slice(3));
1840
- const audrey = new Audrey(buildAudreyConfig());
2235
+ if (!args.action && !args.hook) {
2236
+ console.error('[audrey] guard: action is required');
2237
+ process.exit(2);
2238
+ }
2239
+ const hookPayload = args.hook ? await readHookPayload() : null;
2240
+ const hookTool = hookPayload && typeof hookPayload.tool_name === 'string' ? hookPayload.tool_name : undefined;
2241
+ const hookSessionId = hookPayload && typeof hookPayload.session_id === 'string' ? hookPayload.session_id : undefined;
2242
+ const hookCwd = hookPayload && typeof hookPayload.cwd === 'string' ? hookPayload.cwd : undefined;
2243
+ const hookSummary = hookPayload ? summarizeToolInput(hookPayload, hookTool ?? args.tool) : null;
2244
+ const dataDir = resolveDataDir(process.env);
2245
+ const embedding = resolveEmbeddingProvider(process.env, process.env['AUDREY_EMBEDDING_PROVIDER']);
2246
+ const audrey = new Audrey({
2247
+ dataDir,
2248
+ agent: process.env['AUDREY_AGENT'] ?? 'guard',
2249
+ embedding,
2250
+ });
1841
2251
  try {
1842
- const controller = new MemoryController(audrey);
1843
- const result = await controller.beforeAction({
1844
- tool: args.tool,
1845
- action: args.action,
1846
- command: args.action,
1847
- cwd: args.cwd ?? process.cwd(),
1848
- files: args.files.length > 0 ? args.files : undefined,
1849
- sessionId: args.sessionId,
2252
+ const result = await audrey.beforeAction(hookSummary?.action ?? args.action, {
2253
+ tool: hookTool ?? args.tool,
2254
+ sessionId: args.sessionId ?? hookSessionId,
2255
+ cwd: args.cwd ?? hookCwd ?? process.cwd(),
2256
+ files: args.files.length > 0 ? args.files : hookSummary?.files?.length ? hookSummary.files : undefined,
2257
+ strict: args.strict || args.failOnWarn || args.hook,
2258
+ recordEvent: true,
2259
+ includeCapsule: args.includeCapsule || args.explain,
1850
2260
  });
1851
- if (args.json) {
2261
+ if (args.hook) {
2262
+ console.log(JSON.stringify(formatPreToolUseHookOutput(result, args.failOnWarn)));
2263
+ }
2264
+ else if (args.json) {
1852
2265
  console.log(JSON.stringify(result, null, 2));
1853
2266
  }
1854
2267
  else {
1855
- console.log(formatGuardResult(result, { explain: args.explain }));
2268
+ console.log(formatGuardDecision(result, { explain: args.explain }));
1856
2269
  }
1857
- if ((result.decision === 'block' || (args.failOnWarn && result.decision === 'warn')) && !args.override) {
2270
+ const display = guardDisplayDecision(result);
2271
+ if (!args.hook && (display === 'block' || (args.failOnWarn && display === 'warn')) && !args.override) {
1858
2272
  process.exitCode = 2;
1859
2273
  }
1860
2274
  }
@@ -1862,6 +2276,100 @@ async function guardCli() {
1862
2276
  await audrey.closeAsync();
1863
2277
  }
1864
2278
  }
2279
+ function parseGuardAfterArgs(argv) {
2280
+ const out = {};
2281
+ for (let i = 0; i < argv.length; i++) {
2282
+ const token = argv[i];
2283
+ const next = () => argv[++i];
2284
+ if (token === '--receipt')
2285
+ out.receipt = next();
2286
+ else if (token === '--tool')
2287
+ out.tool = next();
2288
+ else if (token === '--session-id')
2289
+ out.sessionId = next();
2290
+ else if (token === '--outcome')
2291
+ out.outcome = next();
2292
+ else if (token === '--error-summary')
2293
+ out.errorSummary = next();
2294
+ else if (token === '--cwd')
2295
+ out.cwd = next();
2296
+ }
2297
+ return out;
2298
+ }
2299
+ async function readOptionalJsonFromStdin(command) {
2300
+ if (process.stdin.isTTY)
2301
+ return null;
2302
+ const chunks = [];
2303
+ for await (const chunk of process.stdin)
2304
+ chunks.push(chunk);
2305
+ const raw = Buffer.concat(chunks).toString('utf-8').trim();
2306
+ if (!raw)
2307
+ return null;
2308
+ try {
2309
+ return JSON.parse(raw);
2310
+ }
2311
+ catch {
2312
+ console.error(`[audrey] ${command}: stdin was not valid JSON, ignoring.`);
2313
+ return null;
2314
+ }
2315
+ }
2316
+ function inferGuardAfterOutcome(stdinPayload) {
2317
+ const response = stdinPayload?.tool_response
2318
+ ?? stdinPayload?.tool_output
2319
+ ?? stdinPayload?.output;
2320
+ const success = response?.success;
2321
+ if (typeof success === 'boolean')
2322
+ return success ? 'succeeded' : 'failed';
2323
+ const errField = response?.error ?? response?.stderr ?? stdinPayload?.error ?? stdinPayload?.stderr;
2324
+ if (errField && (typeof errField !== 'string' || errField.length > 0))
2325
+ return 'failed';
2326
+ return undefined;
2327
+ }
2328
+ async function guardAfterCli() {
2329
+ const args = parseGuardAfterArgs(process.argv.slice(3));
2330
+ if (!args.receipt) {
2331
+ console.error('[audrey] guard-after: --receipt is required');
2332
+ process.exit(2);
2333
+ }
2334
+ const stdinPayload = await readOptionalJsonFromStdin('guard-after');
2335
+ const outputPayload = stdinPayload?.tool_response ?? stdinPayload?.tool_output ?? stdinPayload?.output;
2336
+ const inputPayload = stdinPayload?.tool_input ?? stdinPayload?.input;
2337
+ const outcome = args.outcome ?? inferGuardAfterOutcome(stdinPayload);
2338
+ let errorSummary = args.errorSummary ?? stdinPayload?.error_summary;
2339
+ if (outcome === 'failed' && !errorSummary) {
2340
+ const response = outputPayload && typeof outputPayload === 'object'
2341
+ ? outputPayload
2342
+ : undefined;
2343
+ const errField = response?.error ?? response?.stderr ?? stdinPayload?.error ?? stdinPayload?.stderr;
2344
+ if (typeof errField === 'string')
2345
+ errorSummary = errField;
2346
+ else if (errField !== undefined)
2347
+ errorSummary = JSON.stringify(errField);
2348
+ }
2349
+ const dataDir = resolveDataDir(process.env);
2350
+ const embedding = resolveEmbeddingProvider(process.env, process.env['AUDREY_EMBEDDING_PROVIDER']);
2351
+ const audrey = new Audrey({
2352
+ dataDir,
2353
+ agent: process.env['AUDREY_AGENT'] ?? 'guard-after',
2354
+ embedding,
2355
+ });
2356
+ try {
2357
+ const result = audrey.afterAction({
2358
+ receiptId: args.receipt,
2359
+ tool: args.tool ?? stdinPayload?.tool_name,
2360
+ sessionId: args.sessionId ?? stdinPayload?.session_id,
2361
+ input: inputPayload,
2362
+ output: outputPayload,
2363
+ outcome,
2364
+ errorSummary,
2365
+ cwd: args.cwd ?? stdinPayload?.cwd,
2366
+ });
2367
+ console.log(JSON.stringify(result));
2368
+ }
2369
+ finally {
2370
+ await audrey.closeAsync();
2371
+ }
2372
+ }
1865
2373
  function parsePromoteArgs(argv) {
1866
2374
  const out = {};
1867
2375
  for (let i = 0; i < argv.length; i++) {
@@ -1949,60 +2457,61 @@ function canonicalEntryPath(path) {
1949
2457
  const isDirectRun = Boolean(process.argv[1])
1950
2458
  && canonicalEntryPath(process.argv[1]) === canonicalEntryPath(fileURLToPath(import.meta.url));
1951
2459
  const KNOWN_SUBCOMMANDS = [
1952
- 'install', 'uninstall', 'mcp-config', 'demo', 'guard', 'reembed', 'dream',
1953
- 'greeting', 'reflect', 'serve', 'status', 'doctor', 'observe-tool', 'promote', 'impact',
2460
+ 'install', 'uninstall', 'mcp-config', 'hook-config', 'demo', 'reembed', 'dream',
2461
+ 'greeting', 'reflect', 'serve', 'status', 'doctor', 'observe-tool', 'guard', 'guard-after', 'promote', 'impact',
1954
2462
  ];
1955
2463
  function printHelp() {
1956
- process.stdout.write(`audrey ${VERSION} — local-first memory runtime for AI agents
1957
-
1958
- Usage: audrey <command> [options]
1959
-
2464
+ process.stdout.write(`audrey ${VERSION} — local-first memory runtime for AI agents
2465
+
2466
+ Usage: audrey <command> [options]
2467
+
1960
2468
  Commands:
1961
2469
  doctor Verify Node, MCP entrypoint, providers, and store health
1962
2470
  demo Run a no-key, no-network proof of recall + reflexes
1963
- demo --scenario repeated-failure
1964
- Show Audrey Guard stopping a repeated deploy failure
1965
- guard --tool <Tool> "<action>" Run memory-before-action guard; exits 2 on block
1966
- Use --fail-on-warn for hooks/CI, --override to allow
1967
2471
  status Print store health (add --json --fail-on-unhealthy for CI)
1968
2472
  install [--host <h>] Register Audrey with an MCP host (codex, claude-code, generic)
1969
- uninstall Remove Audrey from a host's MCP config
1970
- mcp-config <host> Print raw MCP config block for a host (codex|generic|vscode)
1971
- serve Start the REST sidecar (default port 7437; AUDREY_API_KEY recommended)
1972
- dream Run consolidation + decay sweep
1973
- reembed Recompute vectors after dimension/provider change
1974
- greeting Emit session-start briefing (used by host hooks)
1975
- reflect End-of-session memory capture from stdin transcript
1976
- observe-tool Record a tool-trace event (--event, --tool, --outcome)
1977
- impact Show closed-loop feedback metrics (--window N, --limit N, --json)
1978
- promote Promote rules from observed traces (--dry-run to preview)
1979
-
1980
- (no command) Start the MCP stdio server (used by MCP hosts)
1981
-
1982
- Common options:
1983
- -h, --help Print this help and exit
1984
- -v, --version Print version and exit
1985
- --include-secrets Include provider API keys in Claude Code install argv/config
1986
-
1987
- Environment:
1988
- AUDREY_DATA_DIR Path to SQLite memory store (default: ~/.audrey/data)
1989
- AUDREY_AGENT Logical agent identity (default: local-agent)
1990
- AUDREY_EMBEDDING_PROVIDER local | gemini | openai | mock
1991
- AUDREY_LLM_PROVIDER anthropic | openai | mock
1992
- AUDREY_ENABLE_ADMIN_TOOLS=1 Enable export, import, and forget tools/routes
1993
- AUDREY_PORT REST sidecar port (default: 7437)
1994
- AUDREY_API_KEY Bearer token required for non-loopback REST traffic
1995
- AUDREY_PROFILE=1 Emit per-stage timings via _meta.diagnostics
1996
- AUDREY_DISABLE_WARMUP=1 Skip background embedding warmup
1997
- AUDREY_ONNX_VERBOSE=1 Show ONNX runtime warnings (off by default)
1998
-
2473
+ uninstall Remove Audrey from a host's MCP config
2474
+ mcp-config <host> Print raw MCP config block for a host (codex|generic|vscode)
2475
+ hook-config claude-code Print Claude Code hook config (add --apply to merge settings)
2476
+ serve Start the REST sidecar (default port 7437; AUDREY_API_KEY recommended)
2477
+ dream Run consolidation + decay sweep
2478
+ reembed Recompute vectors after dimension/provider change
2479
+ greeting Emit session-start briefing (used by host hooks)
2480
+ reflect End-of-session memory capture from stdin transcript
2481
+ observe-tool Record a tool-trace event (--event, --tool, --outcome)
2482
+ guard Check memory before an action (--json, --tool, --strict)
2483
+ guard-after Record a guarded action outcome (--receipt, --outcome)
2484
+ impact Show closed-loop feedback metrics (--window N, --limit N, --json)
2485
+ promote Promote rules from observed traces (--dry-run to preview)
2486
+
2487
+ (no command) Start the MCP stdio server (used by MCP hosts)
2488
+
2489
+ Common options:
2490
+ -h, --help Print this help and exit
2491
+ -v, --version Print version and exit
2492
+ --include-secrets Include provider API keys in Claude Code install argv/config
2493
+
2494
+ Environment:
2495
+ AUDREY_DATA_DIR Path to SQLite memory store (default: ~/.audrey/data)
2496
+ AUDREY_AGENT Logical agent identity (default: local-agent)
2497
+ AUDREY_EMBEDDING_PROVIDER local | gemini | openai | mock
2498
+ AUDREY_LLM_PROVIDER anthropic | openai | mock
2499
+ AUDREY_ENABLE_ADMIN_TOOLS=1 Enable export, import, and forget tools/routes
2500
+ AUDREY_PORT REST sidecar port (default: 7437)
2501
+ AUDREY_API_KEY Bearer token required for non-loopback REST traffic
2502
+ AUDREY_PROFILE=1 Emit per-stage timings via _meta.diagnostics
2503
+ AUDREY_DISABLE_WARMUP=1 Skip background embedding warmup
2504
+ AUDREY_ONNX_VERBOSE=1 Show ONNX runtime warnings (off by default)
2505
+
1999
2506
  Quick start:
2000
2507
  npx audrey doctor
2001
2508
  npx audrey demo --scenario repeated-failure
2002
2509
  npx audrey guard --tool Bash "npm run deploy"
2003
2510
  npx audrey install --host codex --dry-run
2004
-
2005
- Docs: https://github.com/Evilander/Audrey
2511
+ npx audrey hook-config claude-code
2512
+ npx audrey hook-config claude-code --apply --scope project
2513
+
2514
+ Docs: https://github.com/Evilander/Audrey
2006
2515
  `);
2007
2516
  }
2008
2517
  function printVersion() {
@@ -2028,18 +2537,15 @@ if (isDirectRun) {
2028
2537
  else if (subcommand === 'mcp-config') {
2029
2538
  printMcpConfig();
2030
2539
  }
2540
+ else if (subcommand === 'hook-config') {
2541
+ printHookConfig();
2542
+ }
2031
2543
  else if (subcommand === 'demo') {
2032
2544
  runDemoCommand().catch(err => {
2033
2545
  console.error('[audrey] demo failed:', err);
2034
2546
  process.exit(1);
2035
2547
  });
2036
2548
  }
2037
- else if (subcommand === 'guard') {
2038
- guardCli().catch(err => {
2039
- console.error('[audrey] guard failed:', err);
2040
- process.exit(1);
2041
- });
2042
- }
2043
2549
  else if (subcommand === 'reembed') {
2044
2550
  reembed().catch(err => {
2045
2551
  console.error('[audrey] reembed failed:', err);
@@ -2082,6 +2588,18 @@ if (isDirectRun) {
2082
2588
  process.exit(1);
2083
2589
  });
2084
2590
  }
2591
+ else if (subcommand === 'guard') {
2592
+ guardCli().catch(err => {
2593
+ console.error('[audrey] guard failed:', err);
2594
+ process.exit(1);
2595
+ });
2596
+ }
2597
+ else if (subcommand === 'guard-after') {
2598
+ guardAfterCli().catch(err => {
2599
+ console.error('[audrey] guard-after failed:', err);
2600
+ process.exit(1);
2601
+ });
2602
+ }
2085
2603
  else if (subcommand === 'impact') {
2086
2604
  impact().catch(err => {
2087
2605
  console.error('[audrey] impact failed:', err);