mirror of
https://github.com/thedotmack/claude-mem.git
synced 2026-09-20 04:23:02 +08:00
Merge branch 'pr2942' into mainline
# Conflicts: # src/services/worker/ClaudeProvider.ts # src/supervisor/process-registry.ts
This commit is contained in:
@@ -32,6 +32,10 @@ npm run build-and-sync # Build, sync to marketplace, restart worker
|
||||
|
||||
No need to edit the changelog ever, it's generated automatically.
|
||||
|
||||
## Local Status Notes
|
||||
|
||||
- 2026-06-15: Issue #2909 is intentionally split. PR #2919 covers session-isolation/read-path behavior, while target 29 covers the observer `.jsonl` accumulation half by disabling session persistence for observer tool-use SDK queries.
|
||||
|
||||
## Daily Maintenance
|
||||
|
||||
Run a daily version check across all package manifests and upgrade every dependency to its latest version — including major version bumps. Staying on the latest is the goal; do not skip majors.
|
||||
|
||||
@@ -174,6 +174,7 @@ export class ClaudeProvider {
|
||||
|
||||
async startSession(session: ActiveSession, worker?: WorkerRef): Promise<void> {
|
||||
const cwdTracker = { lastCwd: undefined as string | undefined };
|
||||
const observerExtraArgs = ['--no-session-persistence'];
|
||||
|
||||
// Find and validate Claude executable (shared utility, closes #2222)
|
||||
let claudePath: string;
|
||||
@@ -199,8 +200,16 @@ export class ClaudeProvider {
|
||||
const activeResponseContext = { current: snapshotResponseContext(session) };
|
||||
const messageGenerator = this.createMessageGenerator(session, cwdTracker, activeResponseContext);
|
||||
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
const shouldResume = hasRealMemorySessionId && session.lastPromptNumber > 1 && !session.forceInit;
|
||||
if (session.memorySessionId) {
|
||||
// Observer spawns intentionally opt out of Claude transcript persistence.
|
||||
// A carried session_id from an earlier no-persist spawn is therefore not
|
||||
// safe to feed back into `resume` on a later fresh process.
|
||||
this.dbManager.getSessionStore().updateMemorySessionId(session.sessionDbId, null);
|
||||
session.memorySessionId = null;
|
||||
}
|
||||
|
||||
const hasRealMemorySessionId = false;
|
||||
const shouldResume = false;
|
||||
|
||||
if (session.forceInit) {
|
||||
logger.info('SDK', 'forceInit flag set, starting fresh SDK session', {
|
||||
@@ -256,7 +265,7 @@ export class ClaudeProvider {
|
||||
pathToClaudeCodeExecutable: claudePath,
|
||||
abortController: session.abortController,
|
||||
...(shouldResume && session.memorySessionId ? { resume: session.memorySessionId } : {}),
|
||||
spawnClaudeCodeProcess: createSdkSpawnFactory(session.sessionDbId, slotReservation),
|
||||
spawnClaudeCodeProcess: createSdkSpawnFactory(session.sessionDbId, slotReservation, observerExtraArgs),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -618,11 +618,36 @@ export interface SpawnedSdkProcess {
|
||||
export interface SpawnSdkOptions {
|
||||
command: string;
|
||||
args: string[];
|
||||
extraArgs?: string[];
|
||||
cwd?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export function normalizeSpawnSdkArgs(args: string[], extraArgs: string[] = []): string[] {
|
||||
const filteredArgs: string[] = [];
|
||||
for (const arg of args) {
|
||||
if (arg === '') {
|
||||
// The SDK encodes optional flag/value pairs as `--flag ''` when the
|
||||
// value is absent. Strip the whole pair, but only when the preceding
|
||||
// token is a long option so positional args are left untouched.
|
||||
if (filteredArgs.length > 0 && filteredArgs[filteredArgs.length - 1].startsWith('--')) {
|
||||
filteredArgs.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
filteredArgs.push(arg);
|
||||
}
|
||||
|
||||
for (const extraArg of extraArgs) {
|
||||
if (extraArg !== '') {
|
||||
filteredArgs.push(extraArg);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredArgs;
|
||||
}
|
||||
|
||||
export function spawnSdkProcess(
|
||||
sessionDbId: number,
|
||||
options: SpawnSdkOptions
|
||||
@@ -631,17 +656,7 @@ export function spawnSdkProcess(
|
||||
|
||||
const useCmdWrapper = process.platform === 'win32' && options.command.endsWith('.cmd');
|
||||
const env = sanitizeEnv(options.env ?? process.env);
|
||||
|
||||
const filteredArgs: string[] = [];
|
||||
for (const arg of options.args) {
|
||||
if (arg === '') {
|
||||
if (filteredArgs.length > 0 && filteredArgs[filteredArgs.length - 1].startsWith('--')) {
|
||||
filteredArgs.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
filteredArgs.push(arg);
|
||||
}
|
||||
const filteredArgs = normalizeSpawnSdkArgs(options.args, options.extraArgs);
|
||||
|
||||
const isWin = process.platform === 'win32';
|
||||
const child = useCmdWrapper
|
||||
@@ -763,7 +778,7 @@ function sigtermDuplicateSdkProcess(record: ManagedProcessRecord, sessionDbId: n
|
||||
});
|
||||
}
|
||||
|
||||
export function createSdkSpawnFactory(sessionDbId: number, slotReservation?: SlotReservation) {
|
||||
export function createSdkSpawnFactory(sessionDbId: number, slotReservation?: SlotReservation, extraArgs: string[] = []) {
|
||||
return (spawnOptions: SpawnSdkOptions): SpawnedSdkProcess => {
|
||||
const registry = getProcessRegistry();
|
||||
|
||||
@@ -788,7 +803,10 @@ export function createSdkSpawnFactory(sessionDbId: number, slotReservation?: Slo
|
||||
|
||||
let result: ReturnType<typeof spawnSdkProcess>;
|
||||
try {
|
||||
result = spawnSdkProcess(sessionDbId, spawnOptions);
|
||||
result = spawnSdkProcess(sessionDbId, {
|
||||
...spawnOptions,
|
||||
extraArgs: [...(spawnOptions.extraArgs ?? []), ...extraArgs],
|
||||
});
|
||||
} finally {
|
||||
// The waitForSlot() reservation is consumed here: on success the
|
||||
// process is now a registry record (registered inside spawnSdkProcess)
|
||||
|
||||
@@ -4,7 +4,9 @@ describe('ClaudeProvider Resume Parameter Logic', () => {
|
||||
function shouldPassResumeParameter(session: {
|
||||
memorySessionId: string | null;
|
||||
lastPromptNumber: number;
|
||||
disableSessionPersistence?: boolean;
|
||||
}): boolean {
|
||||
if (session.disableSessionPersistence) return false;
|
||||
const hasRealMemorySessionId = !!session.memorySessionId;
|
||||
return hasRealMemorySessionId && session.lastPromptNumber > 1;
|
||||
}
|
||||
@@ -126,5 +128,17 @@ describe('ClaudeProvider Resume Parameter Logic', () => {
|
||||
|
||||
expect(shouldResume).toBe(true);
|
||||
});
|
||||
|
||||
it('should NOT resume when session persistence is disabled for observer spawns', () => {
|
||||
const session = {
|
||||
memorySessionId: '5439891b-7d4b-4ee3-8662-c000f66bc199',
|
||||
lastPromptNumber: 2,
|
||||
disableSessionPersistence: true,
|
||||
};
|
||||
|
||||
const shouldResume = shouldPassResumeParameter(session);
|
||||
|
||||
expect(shouldResume).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from 'bun:test';
|
||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import path from 'path';
|
||||
import { createProcessRegistry, isPidAlive } from '../../src/supervisor/process-registry.js';
|
||||
import { createProcessRegistry, isPidAlive, normalizeSpawnSdkArgs } from '../../src/supervisor/process-registry.js';
|
||||
|
||||
function makeTempDir(): string {
|
||||
return path.join(tmpdir(), `claude-mem-supervisor-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
||||
@@ -361,6 +361,29 @@ describe('supervisor ProcessRegistry', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeSpawnSdkArgs', () => {
|
||||
it('appends explicit extra args after SDK args', () => {
|
||||
expect(normalizeSpawnSdkArgs(['--print', 'json'], ['--no-session-persistence'])).toEqual([
|
||||
'--print',
|
||||
'json',
|
||||
'--no-session-persistence',
|
||||
]);
|
||||
});
|
||||
|
||||
it('strips empty placeholder flags before appending extra args', () => {
|
||||
expect(normalizeSpawnSdkArgs([
|
||||
'--append-system-prompt',
|
||||
'',
|
||||
'--resume',
|
||||
'session-123',
|
||||
], ['--no-session-persistence'])).toEqual([
|
||||
'--resume',
|
||||
'session-123',
|
||||
'--no-session-persistence',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reapSession', () => {
|
||||
it('unregisters dead processes for the given session', async () => {
|
||||
const tempDir = makeTempDir();
|
||||
|
||||
Reference in New Issue
Block a user