feat(session-anchors): implement S-2 session anchor persistence and auto-anchoring
This commit is contained in:
parent
8f4faaafa3
commit
a9e94fc154
7 changed files with 616 additions and 1 deletions
|
|
@ -28,6 +28,9 @@ import { tel } from './adapters/telemetry-bridge';
|
|||
import { recordActivity, recordToolDone, recordTokenSnapshot } from './stores/health.svelte';
|
||||
import { recordFileWrite, clearSessionWrites, setSessionWorktree } from './stores/conflicts.svelte';
|
||||
import { extractWritePaths, extractWorktreePath } from './utils/tool-files';
|
||||
import { hasAutoAnchored, markAutoAnchored, addAnchors, getAnchorSettings } from './stores/anchors.svelte';
|
||||
import { selectAutoAnchors, serializeAnchorsForInjection } from './utils/anchor-serializer';
|
||||
import type { SessionAnchor } from './types/anchors';
|
||||
|
||||
let unlistenMsg: (() => void) | null = null;
|
||||
let unlistenExit: (() => void) | null = null;
|
||||
|
|
@ -209,6 +212,18 @@ function handleAgentEvent(sessionId: string, event: Record<string, unknown>): vo
|
|||
break;
|
||||
}
|
||||
|
||||
case 'compaction': {
|
||||
// Auto-anchor on first compaction for this project
|
||||
const compactProjId = sessionProjectMap.get(sessionId);
|
||||
if (compactProjId && !hasAutoAnchored(compactProjId)) {
|
||||
const session = getAgentSession(sessionId);
|
||||
if (session) {
|
||||
triggerAutoAnchor(compactProjId, session.messages, session.prompt);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'cost': {
|
||||
const cost = msg.content as CostContent;
|
||||
updateAgentCost(sessionId, {
|
||||
|
|
@ -395,6 +410,48 @@ async function persistSessionForProject(sessionId: string): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
/** Auto-anchor first N turns on first compaction event for a project */
|
||||
function triggerAutoAnchor(
|
||||
projectId: string,
|
||||
messages: import('./adapters/claude-messages').AgentMessage[],
|
||||
sessionPrompt: string,
|
||||
): void {
|
||||
markAutoAnchored(projectId);
|
||||
|
||||
const settings = getAnchorSettings(projectId);
|
||||
const { turns, totalTokens } = selectAutoAnchors(
|
||||
messages,
|
||||
sessionPrompt,
|
||||
settings.anchorTurns,
|
||||
settings.anchorTokenBudget,
|
||||
);
|
||||
|
||||
if (turns.length === 0) return;
|
||||
|
||||
const nowSecs = Math.floor(Date.now() / 1000);
|
||||
const anchors: SessionAnchor[] = turns.map((turn, i) => {
|
||||
const content = serializeAnchorsForInjection([turn], settings.anchorTokenBudget);
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
projectId,
|
||||
messageId: `turn-${turn.index}`,
|
||||
anchorType: 'auto' as const,
|
||||
content: content,
|
||||
estimatedTokens: turn.estimatedTokens,
|
||||
turnIndex: turn.index,
|
||||
createdAt: nowSecs,
|
||||
};
|
||||
});
|
||||
|
||||
addAnchors(projectId, anchors);
|
||||
tel.info('auto_anchor_created', {
|
||||
projectId,
|
||||
anchorCount: anchors.length,
|
||||
totalTokens,
|
||||
});
|
||||
notify('info', `Anchored ${anchors.length} turns (${totalTokens} tokens) for context preservation`);
|
||||
}
|
||||
|
||||
export function stopAgentDispatcher(): void {
|
||||
if (unlistenMsg) {
|
||||
unlistenMsg();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue