mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-15 22:33:05 -07:00
refactor(context): implement commit-on-render strategy for GC backstop
This commit is contained in:
@@ -47,8 +47,6 @@ export class ContextManager {
|
||||
};
|
||||
};
|
||||
|
||||
private lastAppliedNodes?: readonly ConcreteNode[];
|
||||
|
||||
private hasPerformedHotStart = false;
|
||||
|
||||
constructor(
|
||||
@@ -80,6 +78,21 @@ export class ContextManager {
|
||||
this.evaluateTriggers(event.newNodes);
|
||||
});
|
||||
this.eventBus.onProcessorResult((event) => {
|
||||
// Defensive: Verify all targets are still present in the buffer.
|
||||
// If a synchronous render or a previous async task already removed them,
|
||||
// this result is stale and should be dropped.
|
||||
const currentIds = new Set(this.buffer.nodes.map((n) => n.id));
|
||||
const allTargetsPresent = event.targets.every((t) =>
|
||||
currentIds.has(t.id),
|
||||
);
|
||||
|
||||
if (!allTargetsPresent) {
|
||||
debugLogger.log(
|
||||
`[ContextManager] Dropping stale processor result from ${event.processorId}. One or more targets were already removed.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer = this.buffer.applyProcessorResult(
|
||||
event.processorId,
|
||||
event.targets,
|
||||
@@ -371,7 +384,16 @@ export class ContextManager {
|
||||
processedNodes,
|
||||
} = renderResult;
|
||||
|
||||
this.lastAppliedNodes = processedNodes;
|
||||
if (didApplyManagement) {
|
||||
// Commit the GC backstop results back to the master buffer.
|
||||
// We filter out preview nodes because they are ephemeral and will be
|
||||
// added to history naturally by the client after the turn completes.
|
||||
this.buffer = this.buffer.applyProcessorResult(
|
||||
'sync_backstop',
|
||||
this.buffer.nodes,
|
||||
processedNodes.filter((n) => !previewNodeIds.has(n.id)),
|
||||
);
|
||||
}
|
||||
|
||||
// Structural validation in debug mode
|
||||
checkContextInvariants(this.buffer.nodes, 'RenderHistory');
|
||||
@@ -444,9 +466,7 @@ export class ContextManager {
|
||||
}
|
||||
|
||||
exportState(): ContextEngineState {
|
||||
return SnapshotStateHelper.exportState(
|
||||
this.lastAppliedNodes || this.buffer.nodes,
|
||||
);
|
||||
return SnapshotStateHelper.exportState(this.buffer.nodes);
|
||||
}
|
||||
|
||||
restoreState(state: ContextEngineState): void {
|
||||
|
||||
@@ -23,6 +23,7 @@ export function fromGraph(nodes: readonly ConcreteNode[]): Content[] {
|
||||
|
||||
for (const node of nodes) {
|
||||
const turnId = node.turnId;
|
||||
const partWithId = { ...node.payload, _synthId: node.id };
|
||||
|
||||
// We start a new turn if:
|
||||
// 1. We don't have a current turn.
|
||||
@@ -35,12 +36,12 @@ export function fromGraph(nodes: readonly ConcreteNode[]): Content[] {
|
||||
) {
|
||||
currentTurn = {
|
||||
role: node.role,
|
||||
parts: [node.payload],
|
||||
parts: [partWithId],
|
||||
_turnId: turnId,
|
||||
};
|
||||
history.push(currentTurn);
|
||||
} else {
|
||||
currentTurn.parts = [...(currentTurn.parts || []), node.payload];
|
||||
currentTurn.parts = [...(currentTurn.parts || []), partWithId];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user