fix: refresh role rules after compaction
This commit is contained in:
128
src/compact-refresh.ts
Normal file
128
src/compact-refresh.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { DATA_DIR } from './config.js';
|
||||
import type { PairedRoomRole } from './types.js';
|
||||
|
||||
export interface CompactRefreshFlag {
|
||||
sessionFolder: string;
|
||||
sessionId: string;
|
||||
compactedAt: string;
|
||||
trigger?: string | null;
|
||||
}
|
||||
|
||||
export interface AppliedCompactRefresh {
|
||||
flag: CompactRefreshFlag;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
const COMPACT_REFRESH_DIR = path.join(DATA_DIR, 'compact-refresh');
|
||||
|
||||
const COMPACT_REFRESH_PROMPT = `[EJClaw compact refresh]
|
||||
The previous run compacted this same SDK session. Continue following the current AGENTS.md/CLAUDE.md role rules exactly: required first-line status, paired-room role boundaries, output-only task context, concise Korean responses, and verification before completion. This is not new user scope.
|
||||
[/EJClaw compact refresh]`;
|
||||
|
||||
function getFlagPath(sessionFolder: string): string {
|
||||
return path.join(
|
||||
COMPACT_REFRESH_DIR,
|
||||
`${encodeURIComponent(sessionFolder)}.json`,
|
||||
);
|
||||
}
|
||||
|
||||
function isRefreshableRole(role: PairedRoomRole): boolean {
|
||||
return role === 'owner' || role === 'reviewer';
|
||||
}
|
||||
|
||||
function isSessionCommand(prompt: string): boolean {
|
||||
return prompt.trim() === '/compact';
|
||||
}
|
||||
|
||||
export function readCompactRefreshFlag(
|
||||
sessionFolder: string,
|
||||
): CompactRefreshFlag | null {
|
||||
const flagPath = getFlagPath(sessionFolder);
|
||||
if (!fs.existsSync(flagPath)) return null;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(
|
||||
fs.readFileSync(flagPath, 'utf-8'),
|
||||
) as Partial<CompactRefreshFlag>;
|
||||
if (
|
||||
parsed.sessionFolder !== sessionFolder ||
|
||||
typeof parsed.sessionId !== 'string' ||
|
||||
parsed.sessionId.length === 0 ||
|
||||
typeof parsed.compactedAt !== 'string'
|
||||
) {
|
||||
fs.rmSync(flagPath, { force: true });
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
sessionFolder,
|
||||
sessionId: parsed.sessionId,
|
||||
compactedAt: parsed.compactedAt,
|
||||
trigger:
|
||||
typeof parsed.trigger === 'string' && parsed.trigger.length > 0
|
||||
? parsed.trigger
|
||||
: null,
|
||||
};
|
||||
} catch {
|
||||
fs.rmSync(flagPath, { force: true });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function markCompactRefreshNeeded(args: {
|
||||
sessionFolder: string;
|
||||
sessionId: string;
|
||||
trigger?: string | null;
|
||||
}): CompactRefreshFlag {
|
||||
const flag: CompactRefreshFlag = {
|
||||
sessionFolder: args.sessionFolder,
|
||||
sessionId: args.sessionId,
|
||||
compactedAt: new Date().toISOString(),
|
||||
trigger: args.trigger ?? null,
|
||||
};
|
||||
fs.mkdirSync(COMPACT_REFRESH_DIR, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
getFlagPath(args.sessionFolder),
|
||||
`${JSON.stringify(flag)}\n`,
|
||||
);
|
||||
return flag;
|
||||
}
|
||||
|
||||
export function clearCompactRefreshIfUnchanged(args: {
|
||||
sessionFolder: string;
|
||||
flag: CompactRefreshFlag;
|
||||
}): void {
|
||||
const current = readCompactRefreshFlag(args.sessionFolder);
|
||||
if (
|
||||
current &&
|
||||
current.sessionId === args.flag.sessionId &&
|
||||
current.compactedAt === args.flag.compactedAt
|
||||
) {
|
||||
fs.rmSync(getFlagPath(args.sessionFolder), { force: true });
|
||||
}
|
||||
}
|
||||
|
||||
export function maybeApplyCompactRefresh(args: {
|
||||
sessionFolder: string;
|
||||
sessionId?: string;
|
||||
role: PairedRoomRole;
|
||||
prompt: string;
|
||||
}): AppliedCompactRefresh | null {
|
||||
if (!args.sessionId) return null;
|
||||
if (!isRefreshableRole(args.role)) return null;
|
||||
if (isSessionCommand(args.prompt)) return null;
|
||||
|
||||
const flag = readCompactRefreshFlag(args.sessionFolder);
|
||||
if (!flag) return null;
|
||||
if (flag.sessionId !== args.sessionId) {
|
||||
fs.rmSync(getFlagPath(args.sessionFolder), { force: true });
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
flag,
|
||||
prompt: `${COMPACT_REFRESH_PROMPT}\n\n${args.prompt}`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user