claude-recall 0.20.6 → 0.20.8

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.
@@ -8,7 +8,7 @@ source: claude-recall
8
8
 
9
9
  # Corrections
10
10
 
11
- Auto-generated from 14 memories. Last updated: 2026-04-06.
11
+ Auto-generated from 16 memories. Last updated: 2026-04-06.
12
12
 
13
13
  ## Rules
14
14
 
@@ -24,6 +24,8 @@ Auto-generated from 14 memories. Last updated: 2026-04-06.
24
24
  - CORRECTION: Memory with complex metadata
25
25
  - CORRECTION: Memory with complex metadata
26
26
  - CORRECTION: Memory with complex metadata
27
+ - CORRECTION: Memory with complex metadata
28
+ - CORRECTION: Memory with complex metadata
27
29
  - CORRECTION: License copyright should include user's name instead of 'Claude Recall Contributors'
28
30
  - CORRECTION: License copyright should list your name instead of 'Claude Recall Contributors'
29
31
 
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "topicId": "corrections",
3
- "sourceHash": "c77b49a2f99b869465aeb4705a4c3de1f922381d5b49ed8d78c661477f8668fa",
4
- "memoryCount": 14,
5
- "generatedAt": "2026-04-06T16:14:29.343Z",
3
+ "sourceHash": "1313608fb812bd1bed85d9854e8958179f7584619e0160604c56bd40ec8cc5b0",
4
+ "memoryCount": 16,
5
+ "generatedAt": "2026-04-06T17:24:01.505Z",
6
6
  "memoryKeys": [
7
+ "memory_1775496241485_1yyc5ht88",
8
+ "memory_1775494279035_j6uj5lzxo",
7
9
  "memory_1775492069326_vksvzmt3f",
8
10
  "memory_1775491767369_sepsjmg8y",
9
11
  "memory_1775169786543_43p8to1hu",
@@ -8,10 +8,20 @@ source: claude-recall
8
8
 
9
9
  # Preferences
10
10
 
11
- Auto-generated from 69 memories. Last updated: 2026-04-06.
11
+ Auto-generated from 79 memories. Last updated: 2026-04-06.
12
12
 
13
13
  ## Rules
14
14
 
15
+ - Session test preference 1775496241643
16
+ - Test preference 1775496241514-2
17
+ - Test preference 1775496241514-1
18
+ - Test preference 1775496241514-0
19
+ - Test memory content
20
+ - Session test preference 1775494279149
21
+ - Test preference 1775494279061-2
22
+ - Test preference 1775494279061-1
23
+ - Test preference 1775494279061-0
24
+ - Test memory content
15
25
  - Session test preference 1775492069465
16
26
  - Test preference 1775492069353-2
17
27
  - Test preference 1775492069353-1
@@ -1,9 +1,19 @@
1
1
  {
2
2
  "topicId": "preferences",
3
- "sourceHash": "84b3e678b896e5e6761dcfc6d49ee47c0027d4c7808f6ef6f8f3e00aa39ee76c",
4
- "memoryCount": 69,
5
- "generatedAt": "2026-04-06T16:14:29.485Z",
3
+ "sourceHash": "1cad20b547cd1594370facfdae6b4ae8700516e9be712d8b28459462398c9276",
4
+ "memoryCount": 79,
5
+ "generatedAt": "2026-04-06T17:24:01.663Z",
6
6
  "memoryKeys": [
7
+ "memory_1775496241645_xtz7cwan4",
8
+ "memory_1775496241576_j7sxxuge3",
9
+ "memory_1775496241542_rg3hj24ud",
10
+ "memory_1775496241516_y3inz8rlf",
11
+ "memory_1775496241411_vwdz6igrm",
12
+ "memory_1775494279150_n4pq7zy11",
13
+ "memory_1775494279108_6hbe8qoit",
14
+ "memory_1775494279088_nv8hjdm7s",
15
+ "memory_1775494279062_jx4wrwn6s",
16
+ "memory_1775494278982_fsc491z41",
7
17
  "memory_1775492069467_5cturlg0a",
8
18
  "memory_1775492069400_icg4tjivf",
9
19
  "memory_1775492069377_goix7nu9v",
package/README.md CHANGED
@@ -81,10 +81,12 @@ Once installed, Claude Recall works automatically in the background:
81
81
  1. **Session start** — active rules are loaded before the first action. In Claude Code, this happens via the `search_enforcer` hook; in Pi, rules are injected into the system prompt automatically
82
82
  2. **As you work** — every prompt is classified for corrections and preferences. Natural statements like *"we use tabs here"* or *"no, put tests in `__tests__/`"* are detected and stored
83
83
  3. **Tool outcomes** — results from all tools (Bash, Edit, Write, and more) are captured. Failures are stored as memories; Bash failures are paired with successful fixes
84
- 4. **End of session** — session episodes are created, candidate lessons extracted from failures, and a promotion cycle graduates validated patterns into active rules
84
+ 4. **End of session** — session episodes are created, candidate lessons extracted from failures, and a promotion cycle graduates validated patterns into active rules. A session extraction pass sends the last 50 transcript entries to Haiku to extract durable project knowledge from long coding sessions
85
85
  5. **Reask detection** — frustration signals ("still broken", "that didn't work") are recorded as outcome events
86
86
  6. **Before context compression** — aggressive memory sweep captures important context before the window shrinks
87
- 7. **Rules sync** (Claude Code only) — top 30 rules are exported as typed `.md` files to Claude Code's native memory directory
87
+ 7. **After context compression** (Claude Code only) — rules are automatically re-injected into context so they're not lost when the window shrinks
88
+ 8. **Multi-agent outcomes** — subagent completions (completed/failed/killed) are captured from task notifications and recorded as outcome events
89
+ 9. **Rules sync** (Claude Code only) — top 30 rules are exported as typed `.md` files to Claude Code's native memory directory
88
90
 
89
91
  Classification uses Claude Haiku (via `ANTHROPIC_API_KEY`) with silent regex fallback. No configuration needed.
90
92
 
@@ -693,8 +693,20 @@ async function main() {
693
693
  // This avoids registry lookups on every hook invocation.
694
694
  const cliScript = path.join(packageDir, 'dist', 'cli', 'claude-recall-cli.js');
695
695
  const hookCmd = `node ${cliScript} hook run`;
696
- settings.hooksVersion = '10.0.0'; // v10 = add PostToolUseFailure for explicit error capture
696
+ settings.hooksVersion = '11.0.0'; // v11 = add SessionStart(compact) for post-compaction rule reload
697
697
  settings.hooks = {
698
+ SessionStart: [
699
+ {
700
+ matcher: "compact",
701
+ hooks: [
702
+ {
703
+ type: "command",
704
+ command: `${hookCmd} post-compact-reload`,
705
+ timeout: 10
706
+ }
707
+ ]
708
+ }
709
+ ],
698
710
  PostToolUse: [
699
711
  {
700
712
  hooks: [
@@ -85,6 +85,11 @@ class HookCommands {
85
85
  await handleToolFailure(input);
86
86
  break;
87
87
  }
88
+ case 'post-compact-reload': {
89
+ const { handlePostCompactReload } = await Promise.resolve().then(() => __importStar(require('../../hooks/post-compact-reload')));
90
+ await handlePostCompactReload(input);
91
+ break;
92
+ }
88
93
  case 'bash-failure-watcher': {
89
94
  // Backward compat alias — routes to tool-outcome-watcher
90
95
  const { handleBashFailureWatcher } = await Promise.resolve().then(() => __importStar(require('../../hooks/tool-outcome-watcher')));
@@ -64,6 +64,6 @@ async function handleCorrectionDetector(input) {
64
64
  ? result.extract.substring(0, 60) + '...'
65
65
  : result.extract;
66
66
  // Output hook message that Claude sees
67
- console.log(`<user-prompt-submit-hook>[Memory] Auto-captured ${result.type}: ${summary}</user-prompt-submit-hook>`);
67
+ console.log(`<user-prompt-submit-hook>📌 Recall: auto-captured ${result.type} ${summary}</user-prompt-submit-hook>`);
68
68
  (0, shared_1.hookLog)('correction-detector', `Captured ${result.type}: ${result.extract.substring(0, 80)}`);
69
69
  }
@@ -107,6 +107,9 @@ async function handleMemoryStop(input) {
107
107
  stored++;
108
108
  (0, shared_1.hookLog)('memory-stop', `Captured ${result.type}: ${result.extract.substring(0, 80)}`);
109
109
  }
110
+ if (stored > 0) {
111
+ console.log(`📝 Recall: captured ${stored} memories from this session`);
112
+ }
110
113
  (0, shared_1.hookLog)('memory-stop', `Session end: stored ${stored} memories from ${entries.length} entries`);
111
114
  // Session extraction: learn from long coding sessions (reads wider window)
112
115
  try {
@@ -114,8 +117,24 @@ async function handleMemoryStop(input) {
114
117
  const sessionEntries = (0, shared_1.readTranscriptTail)(transcriptPath, 50);
115
118
  if (sessionEntries.length >= 10) {
116
119
  const conversationEntries = buildConversationEntries(sessionEntries);
120
+ // Record failed subagent outcomes
121
+ for (const entry of conversationEntries) {
122
+ if (entry.toolName === 'Agent' && entry.isError) {
123
+ try {
124
+ outcomeStorage.createOutcomeEvent({
125
+ event_type: 'agent_failure',
126
+ actor: 'tool',
127
+ action_summary: entry.text,
128
+ next_state_summary: entry.text,
129
+ tags: ['agent', 'subagent'],
130
+ });
131
+ }
132
+ catch { /* non-critical */ }
133
+ }
134
+ }
117
135
  const extracted = await (0, event_processors_1.extractSessionLearnings)(conversationEntries, input?.session_id ?? '', projectId, 5);
118
136
  if (extracted > 0) {
137
+ console.log(`🔍 Recall: extracted ${extracted} learnings from session analysis`);
119
138
  (0, shared_1.hookLog)('memory-stop', `Session extraction: stored ${extracted} learnings`);
120
139
  stored += extracted;
121
140
  }
@@ -143,6 +162,9 @@ async function handleMemoryStop(input) {
143
162
  const { PromotionEngine } = await Promise.resolve().then(() => __importStar(require('../services/promotion-engine')));
144
163
  const result = PromotionEngine.getInstance().runCycle(projectId);
145
164
  if (result.promoted > 0 || result.archived > 0) {
165
+ if (result.promoted > 0) {
166
+ console.log(`⬆️ Recall: ${result.promoted} lesson(s) promoted to active rules`);
167
+ }
146
168
  (0, shared_1.hookLog)('memory-stop', `Promotion: ${result.promoted} promoted, ${result.archived} archived`);
147
169
  }
148
170
  }
@@ -417,7 +439,21 @@ function buildConversationEntries(entries) {
417
439
  else {
418
440
  const text = (0, shared_1.extractTextFromEntry)(entry);
419
441
  if (text && text.length > 5) {
420
- result.push({ role: 'assistant', text: text.substring(0, 300) });
442
+ // Detect subagent task notifications
443
+ const notifMatch = text.match(/<task-notification>[\s\S]*?<\/task-notification>/);
444
+ if (notifMatch) {
445
+ const status = notifMatch[0].match(/<status>(.*?)<\/status>/)?.[1] ?? 'unknown';
446
+ const summary = notifMatch[0].match(/<summary>(.*?)<\/summary>/)?.[1] ?? '';
447
+ result.push({
448
+ role: 'tool_result',
449
+ text: `Agent ${status}: ${summary}`.substring(0, 300),
450
+ toolName: 'Agent',
451
+ isError: status === 'failed' || status === 'killed',
452
+ });
453
+ }
454
+ else {
455
+ result.push({ role: 'assistant', text: text.substring(0, 300) });
456
+ }
421
457
  }
422
458
  }
423
459
  }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * post-compact-reload hook — fires on SessionStart with source "compact".
4
+ *
5
+ * After context compaction, recall rules loaded earlier in the session are
6
+ * gone from the model's context. This hook re-injects them by outputting
7
+ * the active rules to stdout, which CC injects as a system message.
8
+ *
9
+ * Input: { session_id, hook_event_name: "SessionStart", source: "compact" }
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.handlePostCompactReload = handlePostCompactReload;
13
+ const shared_1 = require("./shared");
14
+ const memory_1 = require("../services/memory");
15
+ const config_1 = require("../services/config");
16
+ const DIRECTIVE = 'These rules were re-loaded after context compaction.\n' +
17
+ 'Continue applying them. Cite at the point of application: (applied from memory: <rule>)';
18
+ function extractVal(value) {
19
+ if (typeof value === 'string')
20
+ return value;
21
+ if (typeof value === 'object' && value !== null) {
22
+ return value.content || value.value || JSON.stringify(value);
23
+ }
24
+ return String(value ?? '');
25
+ }
26
+ function formatRules(rules) {
27
+ const sections = [];
28
+ if (rules.preferences.length > 0) {
29
+ sections.push('## Preferences\n' + rules.preferences.map(m => `- ${extractVal(m.value)}`).join('\n'));
30
+ }
31
+ if (rules.corrections.length > 0) {
32
+ sections.push('## Corrections\n' + rules.corrections.map(m => `- ${extractVal(m.value)}`).join('\n'));
33
+ }
34
+ if (rules.failures.length > 0) {
35
+ sections.push('## Failures\n' + rules.failures.map(m => `- ${extractVal(m.value)}`).join('\n'));
36
+ }
37
+ if (rules.devops.length > 0) {
38
+ sections.push('## DevOps Rules\n' + rules.devops.map(m => `- ${extractVal(m.value)}`).join('\n'));
39
+ }
40
+ return sections.join('\n\n');
41
+ }
42
+ async function handlePostCompactReload(input) {
43
+ try {
44
+ const projectId = config_1.ConfigService.getInstance().getProjectId();
45
+ const rules = memory_1.MemoryService.getInstance().loadActiveRules(projectId);
46
+ const totalRules = rules.preferences.length + rules.corrections.length +
47
+ rules.failures.length + rules.devops.length;
48
+ if (totalRules === 0)
49
+ return;
50
+ const body = formatRules(rules);
51
+ console.log(`🔄 Recall: ${totalRules} rules re-loaded after context compaction\n\n${DIRECTIVE}\n\n---\n\n${body}`);
52
+ (0, shared_1.hookLog)('post-compact-reload', `Re-injected ${totalRules} rules after compaction`);
53
+ }
54
+ catch (err) {
55
+ (0, shared_1.hookLog)('post-compact-reload', `Error: ${err.message}`);
56
+ }
57
+ }
@@ -93,6 +93,9 @@ async function handlePrecompactPreserve(input) {
93
93
  stored++;
94
94
  (0, shared_1.hookLog)('precompact', `Captured ${result.type}: ${result.extract.substring(0, 80)}`);
95
95
  }
96
+ if (stored > 0) {
97
+ console.log(`💾 Recall: preserved ${stored} memories before context compression`);
98
+ }
96
99
  (0, shared_1.hookLog)('precompact', `PreCompact sweep: stored ${stored} memories from ${entries.length} entries`);
97
100
  // Reset search enforcer hook-state so Claude is forced to re-load rules
98
101
  // after context compression. Without this, the enforcer thinks rules are
@@ -137,21 +137,47 @@ function default_1(pi) {
137
137
  }
138
138
  });
139
139
  // --- Event: detect corrections from user input ---
140
- pi.on('input', (event, _ctx) => {
140
+ pi.on('input', (event, ctx) => {
141
141
  collectedUserTexts.push(event.text);
142
- (0, event_processors_1.processUserInput)(event.text, sessionId).catch(() => { });
142
+ (0, event_processors_1.processUserInput)(event.text, sessionId).then(msg => {
143
+ if (msg && ctx.hasUI) {
144
+ try {
145
+ ctx.ui.notify(`📌 ${msg}`, 'info');
146
+ }
147
+ catch { /* non-critical */ }
148
+ }
149
+ }).catch(() => { });
143
150
  return { action: 'continue' };
144
151
  });
145
152
  // --- Event: session end — episode + promotion + session extraction ---
146
- pi.on('session_shutdown', (_event, _ctx) => {
147
- (0, event_processors_1.processSessionEnd)(collectedUserTexts, sessionId, projectId).catch(() => { });
153
+ pi.on('session_shutdown', (_event, ctx) => {
154
+ (0, event_processors_1.processSessionEnd)(collectedUserTexts, sessionId, projectId).then(result => {
155
+ if (ctx.hasUI) {
156
+ try {
157
+ if (result.stored > 0) {
158
+ ctx.ui.notify(`📝 Recall: captured ${result.stored} memories from this session`, 'info');
159
+ }
160
+ if (result.promoted > 0) {
161
+ ctx.ui.notify(`⬆️ Recall: ${result.promoted} lesson(s) promoted to active rules`, 'info');
162
+ }
163
+ }
164
+ catch { /* non-critical */ }
165
+ }
166
+ }).catch(() => { });
148
167
  // Session extraction: learn from long coding sessions
149
168
  const allEntries = [
150
169
  ...collectedUserTexts.map(t => ({ role: 'user', text: t })),
151
170
  ...collectedToolResults,
152
171
  ];
153
172
  if (allEntries.length >= 10) {
154
- (0, event_processors_1.extractSessionLearnings)(allEntries, sessionId, projectId, 5).catch(() => { });
173
+ (0, event_processors_1.extractSessionLearnings)(allEntries, sessionId, projectId, 5).then(extracted => {
174
+ if (extracted > 0 && ctx.hasUI) {
175
+ try {
176
+ ctx.ui.notify(`🔍 Recall: extracted ${extracted} learnings from session`, 'info');
177
+ }
178
+ catch { /* non-critical */ }
179
+ }
180
+ }).catch(() => { });
155
181
  }
156
182
  });
157
183
  // --- Event: pre-compaction — aggressive capture ---
@@ -0,0 +1,114 @@
1
+ # Claude Code Agent Harness — Architecture Reference
2
+
3
+ Reference notes on Claude Code's internal agent harness architecture, based on source code analysis (v1.0.x, March 2026). Useful for understanding integration points and designing Claude Recall features.
4
+
5
+ ## Core Orchestration
6
+
7
+ **Query loop** — main while-loop that calls the API, executes tools, handles continuations, retries, and abort. Key behaviors:
8
+ - Infinite loop with explicit terminal conditions (end_turn, max_tokens, budget exceeded, turn limit)
9
+ - Pre-API hooks, post-sampling hooks, stop hooks at each stage
10
+ - Memory prefetch started non-blocking before API call
11
+ - Skill discovery prefetch in parallel with streaming
12
+
13
+ **Tool execution pipeline** — partitions tool calls into batches:
14
+ - Read-only tools (Read, Grep, Glob) run concurrently (max 10)
15
+ - Write tools (Bash, Edit, Write) run serially
16
+ - Each tool goes through: permission check → execute → yield result → apply context modifiers
17
+ - Tool result size budget enforced per turn
18
+
19
+ ## Permission System
20
+
21
+ Three modes: `default` (interactive), `auto` (ML classifier), `bypass`.
22
+
23
+ Pipeline per tool call:
24
+ 1. Rules-based check (always-allow/deny/ask lists)
25
+ 2. Bash classifier (ML, 2s timeout for speculative decisions)
26
+ 3. Hook execution (PreToolUse hooks)
27
+ 4. Interactive UI prompt (if needed)
28
+ 5. Decision persisted to ToolPermissionContext
29
+
30
+ Risk classification: LOW/MEDIUM/HIGH per tool action. Denial tracking state for threshold-based fallback.
31
+
32
+ ## Multi-Agent
33
+
34
+ **Coordinator mode** — multi-agent orchestration with parallel worker phases and shared scratchpad.
35
+
36
+ **Agent tool** — forks subagents with inherited context:
37
+ - Child shares parent's prompt cache (byte-identical prefix = free context)
38
+ - Hard turn limit per agent
39
+ - Task notifications via XML: `<task-notification><status>completed</status>...</task-notification>`
40
+ - Agent types: worker (async), teammate (in-process, visible UI), fork (implicit context inheritance)
41
+
42
+ **Worker constraints:**
43
+ - SIMPLE mode: Bash, Read, Edit only
44
+ - Normal mode: full tool list including MCP
45
+
46
+ ## State Management
47
+
48
+ **AppState** (Zustand store, 200+ fields):
49
+ - messages, tasks, agents, permissions, MCP clients, models, plugins, settings
50
+ - Observable via selectors
51
+ - Task registry: `{ [taskId]: TaskState }` with status tracking
52
+
53
+ **Task types:** local_bash, local_agent, remote_agent, in_process_teammate, local_workflow, monitor_mcp, dream
54
+
55
+ **Session persistence:** transcript JSONL written per session, resumable.
56
+
57
+ ## Context Management / Compaction
58
+
59
+ Four compaction strategies (in order of aggressiveness):
60
+ 1. **Microcompact** — cache-editing on every turn (efficient, preserves cache)
61
+ 2. **Snip compact** — truncate oldest history (feature-gated)
62
+ 3. **Context collapse** — deduplicate/compress (feature-gated)
63
+ 4. **Autocompact** — full summarization when token threshold crossed
64
+
65
+ Token tracking: per-message estimates, cache creation/read tokens, danger zone threshold, cumulative budget.
66
+
67
+ Pre/post compact hooks fire at each stage.
68
+
69
+ ## Safety & Guardrails
70
+
71
+ - Cyber risk instruction (defensive security, CTF rules)
72
+ - Secret scanner (gitleaks patterns, credential redaction)
73
+ - Path traversal prevention
74
+ - Command injection protection (fixed in security audit)
75
+ - Crypto: `crypto.randomUUID()` / `crypto.randomBytes()` throughout (no weak RNG)
76
+
77
+ ## Memory System
78
+
79
+ **extractMemories** — forked sub-agent after each query loop:
80
+ - Reads last ~N messages, decides what to extract
81
+ - 5-turn budget, read-then-write strategy
82
+ - Mutual exclusivity: skips if main agent already wrote to memory
83
+ - Shares parent's prompt cache
84
+
85
+ **autoDream** — background consolidation:
86
+ - Three-gate trigger: 24h since last + 5 sessions + no parallel consolidation
87
+ - Four phases: orient → gather → consolidate → prune
88
+ - PID-based locking, 1h stale window
89
+
90
+ **Memory retrieval** — Sonnet sidequery selects up to 5 relevant memories per query from the memory directory.
91
+
92
+ ## Integration Points for Claude Recall
93
+
94
+ ### Currently Used
95
+ - PostToolUse / PostToolUseFailure hooks (tool outcome capture)
96
+ - UserPromptSubmit hook (correction detection)
97
+ - Stop hook (session-end processing)
98
+ - PreCompact hook (pre-compaction capture)
99
+ - MCP server (tool registration)
100
+ - Skills directory (behavioral guidance)
101
+
102
+ ### Available but Unused
103
+ - Permission decision hooks (observe denied tools as learning signals)
104
+ - Task notification parsing (multi-agent session outcomes)
105
+ - Agent fork interception (inject memory context into subagents)
106
+ - Compaction triggers (pre-compact hooks with message access)
107
+ - Memory prefetch integration (inject recall results alongside native memory)
108
+
109
+ ### Architectural Constraints
110
+ - Hooks run as external processes (no shared memory, no prompt cache)
111
+ - MCP tools are request-response (no streaming, no multi-turn)
112
+ - Cannot fork sub-agents from hooks
113
+ - Cannot modify the query loop or tool pipeline directly
114
+ - Feature flags (GrowthBook `tengu_*`) control many code paths — not accessible externally
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.20.6",
3
+ "version": "0.20.8",
4
4
  "description": "Persistent memory for Claude Code and Pi with native Skills integration, automatic capture, failure learning, and project scoping",
5
5
  "main": "dist/index.js",
6
6
  "bin": {