mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-15 14:23:02 -07:00
fix(agent): implement AgentProtocol disposal to prevent memory leaks
This commit is contained in:
@@ -193,6 +193,7 @@ export async function runNonInteractive({
|
||||
|
||||
let errorToHandle: unknown | undefined;
|
||||
let scheduler: Scheduler | undefined;
|
||||
let session: LegacyAgentSession | undefined;
|
||||
let abortSession = () => {};
|
||||
try {
|
||||
consolePatcher.patch();
|
||||
@@ -296,7 +297,7 @@ export async function runNonInteractive({
|
||||
}
|
||||
|
||||
// Create LegacyAgentSession — owns the agentic loop
|
||||
const session = new LegacyAgentSession({
|
||||
session = new LegacyAgentSession({
|
||||
client: geminiClient,
|
||||
scheduler,
|
||||
config,
|
||||
@@ -305,7 +306,7 @@ export async function runNonInteractive({
|
||||
|
||||
// Wire Ctrl+C to session abort
|
||||
abortSession = () => {
|
||||
void session.abort();
|
||||
void session?.abort();
|
||||
};
|
||||
abortController.signal.addEventListener('abort', abortSession);
|
||||
if (abortController.signal.aborted) {
|
||||
@@ -640,6 +641,7 @@ export async function runNonInteractive({
|
||||
cleanupStdinCancellation();
|
||||
abortController.signal.removeEventListener('abort', abortSession);
|
||||
|
||||
session?.dispose();
|
||||
scheduler?.dispose();
|
||||
consolePatcher.cleanup();
|
||||
coreEvents.off(CoreEvent.UserFeedback, handleUserFeedback);
|
||||
|
||||
@@ -1180,6 +1180,10 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
[config, getPreferredEditor],
|
||||
);
|
||||
|
||||
useEffect(() => () => {
|
||||
streamAgent?.dispose?.();
|
||||
}, [streamAgent]);
|
||||
|
||||
const activeStream = streamAgent
|
||||
? // eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useAgentStream({
|
||||
|
||||
@@ -34,6 +34,10 @@ export class AgentSession implements AgentProtocol {
|
||||
return this._protocol.abort();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._protocol.dispose?.();
|
||||
}
|
||||
|
||||
get events(): readonly AgentEvent[] {
|
||||
return this._protocol.events;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ export class LegacyAgentProtocol implements AgentProtocol {
|
||||
private _agentEndEmitted = false;
|
||||
private _activeStreamId?: string;
|
||||
private _abortController = new AbortController();
|
||||
private _disposalController = new AbortController();
|
||||
private _nextStreamIdOverride?: string;
|
||||
private _lastToolStatuses = new Map<string, ToolEventStatus>();
|
||||
|
||||
@@ -106,10 +107,16 @@ export class LegacyAgentProtocol implements AgentProtocol {
|
||||
this._config.messageBus.subscribe(
|
||||
MessageBusType.TOOL_CALLS_UPDATE,
|
||||
this._handleToolCallsUpdate.bind(this),
|
||||
{ signal: this._disposalController.signal },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposalController.abort();
|
||||
void this.abort();
|
||||
}
|
||||
|
||||
get events(): readonly AgentEvent[] {
|
||||
return this._events;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@ export interface AgentProtocol extends Trajectory {
|
||||
*/
|
||||
abort(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Disposes of the protocol, cleaning up any long-lived resources.
|
||||
*/
|
||||
dispose?(): void;
|
||||
|
||||
/**
|
||||
* AgentProtocol implements the Trajectory interface and can retrieve existing events.
|
||||
*/
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import { writeFileSync, existsSync, cpSync, rmSync } from 'node:fs';
|
||||
import { writeFileSync, existsSync, cpSync } from 'node:fs';
|
||||
import { join, basename } from 'node:path';
|
||||
|
||||
if (!process.cwd().includes('packages')) {
|
||||
@@ -48,14 +48,7 @@ if (packageName === 'core') {
|
||||
const docsSource = join(process.cwd(), '..', '..', 'docs');
|
||||
const docsTarget = join(process.cwd(), 'dist', 'docs');
|
||||
if (existsSync(docsSource)) {
|
||||
if (existsSync(docsTarget)) {
|
||||
rmSync(docsTarget, { recursive: true, force: true });
|
||||
}
|
||||
cpSync(docsSource, docsTarget, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
force: true,
|
||||
});
|
||||
cpSync(docsSource, docsTarget, { recursive: true, dereference: true });
|
||||
console.log('Copied documentation to dist/docs');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user