Compare commits

...

4 Commits

Author SHA1 Message Date
Alex Newman bd1fe5995f chore: bump version to 9.0.11
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 13:50:21 -05:00
Glucksberg 6791069bca fix: isolate observer sessions to prevent polluting claude --resume list (#837)
Observer sessions created by claude-mem were appearing in the main Claude Code
session picker (`claude --resume`), cluttering the list with internal plugin
sessions that users never intend to resume.

In one user's case: 74 observer sessions out of ~220 total (34% noise).

## Solution

Set `CLAUDE_CONFIG_DIR` to `~/.claude-mem/observer-config/` when spawning
observer Claude processes. This stores observer session files in a separate
location, isolating them from user sessions.

## Changes

1. Added `OBSERVER_CONFIG_DIR` to paths.ts
2. Modified `createPidCapturingSpawn()` in ProcessRegistry.ts to inject
   `CLAUDE_CONFIG_DIR` environment variable

Observer sessions now write their `.jsonl` files to:
`~/.claude-mem/observer-config/projects/*/`

Instead of the user's:
`~/.claude/projects/*/`

Fixes #832

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 13:47:29 -05:00
Alexander Knigge 3e6add90de fix: prevent stale memory_session_id resume crash after worker restart (Issue #817) (#839)
When the worker restarts, the SDK context is lost but the database still contains
memory_session_id values from the previous worker instance. The existing guard
(lastPromptNumber > 1) doesn't protect against this because lastPromptNumber is
also loaded from the database.

This fix:
- Clears memory_session_id when initializing a session from DB (not from cache)
- Adds warning log when discarding stale session IDs
- Lets SDK agent capture fresh memory_session_id on first response

The key insight: if a session is not in memory, we're in a new worker instance,
and any database memory_session_id is definitely stale.

Fixes #817
Related to #825

Co-authored-by: bigphoot <bigphoot@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 02:40:19 -05:00
Alex Newman d3331d1e22 docs: update CHANGELOG.md for v9.0.10 2026-01-26 15:52:15 -05:00
11 changed files with 159 additions and 119 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
"plugins": [
{
"name": "claude-mem",
"version": "9.0.10",
"version": "9.0.11",
"source": "./plugin",
"description": "Persistent memory system for Claude Code - context compression across sessions"
}
+19 -11
View File
@@ -2,6 +2,25 @@
All notable changes to claude-mem.
## [v9.0.10] - 2026-01-26
## Bug Fix
**Fixed path format mismatch causing folder CLAUDE.md files to show "No recent activity" (#794)** - Thanks @bigph00t!
The folder-level CLAUDE.md generation was failing to find observations due to a path format mismatch between how API queries used absolute paths and how the database stored relative paths. The `isDirectChild()` function's simple prefix match always returned false in these cases.
**Root cause:** PR #809 (v9.0.9) only masked this bug by skipping file creation when "no activity" was detected. Since ALL folders were affected, this prevented file creation entirely. This PR provides the actual fix.
**Changes:**
- Added new shared module `src/shared/path-utils.ts` with robust path normalization and matching utilities
- Updated `SessionSearch.ts`, `regenerate-claude-md.ts`, and `claude-md-utils.ts` to use shared path utilities
- Added comprehensive test coverage (61 new tests) for path matching edge cases
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## [v9.0.9] - 2026-01-26
## Bug Fixes
@@ -1295,14 +1314,3 @@ This represents a major reliability improvement for Windows users, eliminating c
- Enhanced SDKAgent response handling and message processing
## [v7.3.5] - 2025-12-17
## What's Changed
* fix(windows): solve zombie port problem with wrapper architecture by @ToxMox in https://github.com/thedotmack/claude-mem/pull/372
* chore: bump version to 7.3.5 by @thedotmack in https://github.com/thedotmack/claude-mem/pull/375
## New Contributors
* @ToxMox made their first contribution in https://github.com/thedotmack/claude-mem/pull/372
**Full Changelog**: https://github.com/thedotmack/claude-mem/compare/v7.3.4...v7.3.5
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claude-mem",
"version": "9.0.10",
"version": "9.0.11",
"description": "Memory compression system for Claude Code - persist context across sessions",
"keywords": [
"claude",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claude-mem",
"version": "9.0.10",
"version": "9.0.11",
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
"author": {
"name": "Alex Newman"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claude-mem-plugin",
"version": "9.0.10",
"version": "9.0.11",
"private": true,
"description": "Runtime dependencies for claude-mem bundled hooks",
"type": "module",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+15 -1
View File
@@ -19,6 +19,7 @@
import { spawn, exec, ChildProcess } from 'child_process';
import { promisify } from 'util';
import { logger } from '../../utils/logger.js';
import { OBSERVER_CONFIG_DIR, ensureDir } from '../../shared/paths.js';
const execAsync = promisify(exec);
@@ -187,8 +188,15 @@ export async function reapOrphanedProcesses(activeSessionIds: Set<number>): Prom
*
* The SDK's spawnClaudeCodeProcess option allows us to intercept subprocess
* creation and capture the PID before the SDK hides it.
*
* IMPORTANT (Issue #832): We set CLAUDE_CONFIG_DIR to isolate observer sessions.
* This prevents observer sessions from appearing in `claude --resume` list,
* which was causing 34%+ of resume entries to be internal plugin sessions.
*/
export function createPidCapturingSpawn(sessionDbId: number) {
// Ensure observer config directory exists
ensureDir(OBSERVER_CONFIG_DIR);
return (spawnOptions: {
command: string;
args: string[];
@@ -196,9 +204,15 @@ export function createPidCapturingSpawn(sessionDbId: number) {
env?: NodeJS.ProcessEnv;
signal?: AbortSignal;
}) => {
// Inject CLAUDE_CONFIG_DIR to isolate observer sessions (Issue #832)
const isolatedEnv = {
...spawnOptions.env,
CLAUDE_CONFIG_DIR: OBSERVER_CONFIG_DIR
};
const child = spawn(spawnOptions.command, spawnOptions.args, {
cwd: spawnOptions.cwd,
env: spawnOptions.env,
env: isolatedEnv,
stdio: ['pipe', 'pipe', 'pipe'],
signal: spawnOptions.signal, // CRITICAL: Pass signal for AbortController integration
windowsHide: true
+18 -4
View File
@@ -106,6 +106,15 @@ export class SessionManager {
memory_session_id: dbSession.memory_session_id
});
// Log warning if we're discarding a stale memory_session_id (Issue #817)
if (dbSession.memory_session_id) {
logger.warn('SESSION', `Discarding stale memory_session_id from previous worker instance (Issue #817)`, {
sessionDbId,
staleMemorySessionId: dbSession.memory_session_id,
reason: 'SDK context lost on worker restart - will capture new ID'
});
}
// Use currentUserPrompt if provided, otherwise fall back to database (first prompt)
const userPrompt = currentUserPrompt || dbSession.user_prompt;
@@ -124,11 +133,15 @@ export class SessionManager {
}
// Create active session
// Load memorySessionId from database if previously captured (enables resume across restarts)
// CRITICAL: Do NOT load memorySessionId from database here (Issue #817)
// When creating a new in-memory session, any database memory_session_id is STALE
// because the SDK context was lost when the worker restarted. The SDK agent will
// capture a new memorySessionId on the first response and persist it.
// Loading stale memory_session_id causes "No conversation found" crashes on resume.
session = {
sessionDbId,
contentSessionId: dbSession.content_session_id,
memorySessionId: dbSession.memory_session_id || null,
memorySessionId: null, // Always start fresh - SDK will capture new ID
project: dbSession.project,
userPrompt,
pendingMessages: [],
@@ -143,10 +156,11 @@ export class SessionManager {
currentProvider: null // Will be set when generator starts
};
logger.debug('SESSION', 'Creating new session object', {
logger.debug('SESSION', 'Creating new session object (memorySessionId cleared to prevent stale resume)', {
sessionDbId,
contentSessionId: dbSession.content_session_id,
memorySessionId: dbSession.memory_session_id || '(none - fresh session)',
dbMemorySessionId: dbSession.memory_session_id || '(none in DB)',
memorySessionId: '(cleared - will capture fresh from SDK)',
lastPromptNumber: promptNumber || this.dbManager.getSessionStore().getPromptNumberFromUserPrompts(dbSession.content_session_id)
});
+4
View File
@@ -38,6 +38,10 @@ export const USER_SETTINGS_PATH = join(DATA_DIR, 'settings.json');
export const DB_PATH = join(DATA_DIR, 'claude-mem.db');
export const VECTOR_DB_DIR = join(DATA_DIR, 'vector-db');
// Isolated config directory for observer sessions (Issue #832)
// This prevents observer sessions from appearing in `claude --resume` list
export const OBSERVER_CONFIG_DIR = join(DATA_DIR, 'observer-config');
// Claude integration paths
export const CLAUDE_SETTINGS_PATH = join(CLAUDE_CONFIG_DIR, 'settings.json');
export const CLAUDE_COMMANDS_DIR = join(CLAUDE_CONFIG_DIR, 'commands');