feat: auto memory pipeline — host-driven Memento recall/reflect
Add automatic memory integration so EJClaw host directly calls Memento MCP for recall at session start and reflect on compact, removing reliance on agent voluntary tool use. Stage 1: New sessions get room memory briefing injected into system prompt (CLAUDE.md / AGENTS.md) via host-side MCP client. Stage 2: PreCompact hook automatically calls reflect + remember to persist session summaries as room-memory fragments. Also restores missing source files (token-rotation, codex-token-rotation, claude-usage exports) to fix full build from source. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
62
src/token-rotation.ts
Normal file
62
src/token-rotation.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
const HOST_CLAUDE_DIR = path.join(os.homedir(), '.claude');
|
||||
const CLAUDE_ACCOUNTS_DIR = path.join(os.homedir(), '.claude-accounts');
|
||||
|
||||
let activeIndex = 0;
|
||||
const rateLimitedAccounts = new Set<number>();
|
||||
|
||||
function listClaudeAccounts(): string[] {
|
||||
if (!fs.existsSync(CLAUDE_ACCOUNTS_DIR)) return [];
|
||||
|
||||
return fs
|
||||
.readdirSync(CLAUDE_ACCOUNTS_DIR, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory() && /^\d+$/.test(entry.name))
|
||||
.sort((a, b) => Number(a.name) - Number(b.name))
|
||||
.map((entry) => path.join(CLAUDE_ACCOUNTS_DIR, entry.name));
|
||||
}
|
||||
|
||||
function copyIfExists(src: string, dst: string): void {
|
||||
if (!fs.existsSync(src)) return;
|
||||
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
||||
fs.copyFileSync(src, dst);
|
||||
}
|
||||
|
||||
function syncHostClaudeAccount(accountDir: string): void {
|
||||
copyIfExists(
|
||||
path.join(accountDir, '.credentials.json'),
|
||||
path.join(HOST_CLAUDE_DIR, '.credentials.json'),
|
||||
);
|
||||
copyIfExists(path.join(accountDir, 'settings.json'), path.join(HOST_CLAUDE_DIR, 'settings.json'));
|
||||
}
|
||||
|
||||
export function getTokenCount(): number {
|
||||
const accounts = listClaudeAccounts();
|
||||
return accounts.length > 0 ? accounts.length : 1;
|
||||
}
|
||||
|
||||
export function rotateToken(_reason?: string): boolean {
|
||||
const accounts = listClaudeAccounts();
|
||||
if (accounts.length <= 1) return false;
|
||||
|
||||
rateLimitedAccounts.add(activeIndex);
|
||||
|
||||
const candidates = accounts.map((_, index) => index);
|
||||
const nextIndex =
|
||||
candidates.find(
|
||||
(index) => index !== activeIndex && !rateLimitedAccounts.has(index),
|
||||
) ??
|
||||
candidates.find((index) => index !== activeIndex);
|
||||
|
||||
if (nextIndex === undefined) return false;
|
||||
|
||||
syncHostClaudeAccount(accounts[nextIndex]);
|
||||
activeIndex = nextIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function markTokenHealthy(): void {
|
||||
rateLimitedAccounts.delete(activeIndex);
|
||||
}
|
||||
Reference in New Issue
Block a user