feat(commands,audit): add /context command and proactive compaction audit events
This commit is contained in:
@@ -927,6 +927,10 @@ describe('AgentOrchestrator', () => {
|
||||
deleteConfig: vi.fn(),
|
||||
};
|
||||
|
||||
const sessionCheckpoint = vi.fn();
|
||||
const previousAuditLogger = auditLogger;
|
||||
initAuditLogger({ sessionCheckpoint } as unknown as AuditLogger);
|
||||
|
||||
const orchestrator = new AgentOrchestrator({
|
||||
modelRouter: mockRouter,
|
||||
systemPrompt: 'You are helpful.',
|
||||
@@ -958,17 +962,23 @@ describe('AgentOrchestrator', () => {
|
||||
},
|
||||
});
|
||||
|
||||
await orchestrator.process('ping');
|
||||
const alert = orchestrator.consumeContextAlert();
|
||||
expect(alert?.level).toBe('checkpoint');
|
||||
expect(alert?.actions.checkpointSaved).toBe(true);
|
||||
expect(alert?.actions.checkpointNamespace).toContain('session/checkpoints');
|
||||
expect(orchestrator.consumeContextAlert()).toBeUndefined();
|
||||
try {
|
||||
await orchestrator.process('ping');
|
||||
const alert = orchestrator.consumeContextAlert();
|
||||
expect(alert?.level).toBe('checkpoint');
|
||||
expect(alert?.actions.checkpointSaved).toBe(true);
|
||||
expect(alert?.actions.checkpointNamespace).toContain('session/checkpoints');
|
||||
expect(orchestrator.consumeContextAlert()).toBeUndefined();
|
||||
|
||||
const stored = memoryStore.read('session/checkpoints/ws/context-test');
|
||||
expect(stored.length).toBeGreaterThan(0);
|
||||
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
const stored = memoryStore.read('session/checkpoints/ws/context-test');
|
||||
expect(stored.length).toBeGreaterThan(0);
|
||||
expect(sessionCheckpoint).toHaveBeenCalledWith(expect.objectContaining({
|
||||
session_id: 'ws:context-test',
|
||||
}));
|
||||
} finally {
|
||||
initAuditLogger(previousAuditLogger as unknown as AuditLogger);
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('auto-compacts proactively at critical threshold and emits alert', async () => {
|
||||
@@ -1006,6 +1016,11 @@ describe('AgentOrchestrator', () => {
|
||||
deleteConfig: vi.fn(),
|
||||
};
|
||||
|
||||
const sessionAutoCompact = vi.fn();
|
||||
const sessionCompact = vi.fn();
|
||||
const previousAuditLogger = auditLogger;
|
||||
initAuditLogger({ sessionAutoCompact, sessionCompact } as unknown as AuditLogger);
|
||||
|
||||
const orchestrator = new AgentOrchestrator({
|
||||
modelRouter: compactRouter,
|
||||
systemPrompt: 'You are helpful.',
|
||||
@@ -1036,10 +1051,18 @@ describe('AgentOrchestrator', () => {
|
||||
},
|
||||
});
|
||||
|
||||
await orchestrator.process('continue');
|
||||
const alert = orchestrator.consumeContextAlert();
|
||||
expect(alert?.actions.autoCompacted).toBe(true);
|
||||
expect(history.length).toBeLessThan(6);
|
||||
try {
|
||||
await orchestrator.process('continue');
|
||||
const alert = orchestrator.consumeContextAlert();
|
||||
expect(alert?.actions.autoCompacted).toBe(true);
|
||||
expect(history.length).toBeLessThan(6);
|
||||
expect(sessionAutoCompact).toHaveBeenCalledWith(expect.objectContaining({
|
||||
session_id: 'ws:auto-compact',
|
||||
compacted: true,
|
||||
}));
|
||||
} finally {
|
||||
initAuditLogger(previousAuditLogger as unknown as AuditLogger);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -573,6 +573,7 @@ export class AgentOrchestrator {
|
||||
}
|
||||
|
||||
if (level === 'critical') {
|
||||
const beforeAuto = budget;
|
||||
try {
|
||||
const result = await this.compact();
|
||||
autoCompacted = Boolean(result && result.compactedCount > 0);
|
||||
@@ -580,6 +581,16 @@ export class AgentOrchestrator {
|
||||
console.warn('[Flynn:compact] Proactive auto-compaction failed:', error);
|
||||
}
|
||||
budget = this.getContextBudget();
|
||||
if (this._session) {
|
||||
auditLogger?.sessionAutoCompact({
|
||||
session_id: this._session.id,
|
||||
usage_pct_before: beforeAuto.usagePct,
|
||||
usage_pct_after: budget.usagePct,
|
||||
compacted: autoCompacted,
|
||||
tokens_before: beforeAuto.estimatedTokens,
|
||||
tokens_after: budget.estimatedTokens,
|
||||
});
|
||||
}
|
||||
level = this._resolveContextAlertLevel(budget.usagePct);
|
||||
}
|
||||
|
||||
@@ -691,6 +702,12 @@ export class AgentOrchestrator {
|
||||
const namespace = `${namespaceBase}/${this._sanitizeSessionId(this._session.id)}`;
|
||||
const block = `## ${new Date().toISOString()}\n\n${summary}\n\n`;
|
||||
this._memoryStore.write(namespace, block, 'append');
|
||||
auditLogger?.sessionCheckpoint({
|
||||
session_id: this._session.id,
|
||||
namespace,
|
||||
chars_written: block.length,
|
||||
usage_pct: this.getContextBudget().usagePct,
|
||||
});
|
||||
this._lastCheckpointAt = Date.now();
|
||||
return { saved: true, namespace };
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user