Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cli/src/commands/gemini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const geminiCommand: CommandDefinition = {
startingMode?: 'local' | 'remote'
permissionMode?: GeminiPermissionMode
model?: string
resumeSessionId?: string
} = {}

for (let i = 0; i < commandArgs.length; i++) {
Expand All @@ -30,6 +31,12 @@ export const geminiCommand: CommandDefinition = {
}
} else if (arg === '--yolo') {
options.permissionMode = 'yolo'
} else if (arg === '--resume') {
const sessionId = commandArgs[++i]
if (!sessionId) {
throw new Error('Missing --resume value')
}
options.resumeSessionId = sessionId
} else if (arg === '--model') {
const model = commandArgs[++i]
if (!model) {
Expand Down
39 changes: 31 additions & 8 deletions cli/src/gemini/geminiRemoteLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class GeminiRemoteLauncher extends RemoteLauncherBase {
const backend = createGeminiBackend({
model: runtimeConfig.model,
token: runtimeConfig.token,
resumeSessionId: session.sessionId,
hookSettingsPath: this.hookSettingsPath,
cwd: session.path,
permissionMode: session.getPermissionMode() as string | undefined
Expand All @@ -69,10 +68,33 @@ class GeminiRemoteLauncher extends RemoteLauncherBase {

await backend.initialize();

const acpSessionId = await backend.newSession({
cwd: session.path,
mcpServers: toAcpMcpServers(mcpServers)
});
const resumeSessionId = session.sessionId;
const acpMcpServers = toAcpMcpServers(mcpServers);
let acpSessionId: string;
if (resumeSessionId) {
try {
acpSessionId = await backend.loadSession({
sessionId: resumeSessionId,
cwd: session.path,
mcpServers: acpMcpServers
});
} catch (error) {
logger.warn('[gemini-remote] resume failed, starting new session', error);
session.sendSessionEvent({
type: 'message',
message: 'Gemini resume failed; starting a new session.'
});
acpSessionId = await backend.newSession({
cwd: session.path,
mcpServers: acpMcpServers
});
}
} else {
acpSessionId = await backend.newSession({
cwd: session.path,
mcpServers: acpMcpServers
});
}
session.onSessionFound(acpSessionId);

this.permissionHandler = new GeminiPermissionHandler(
Expand Down Expand Up @@ -115,12 +137,13 @@ class GeminiRemoteLauncher extends RemoteLauncherBase {
this.handleAgentMessage(message);
});
} catch (error) {
logger.warn('[gemini-remote] prompt failed', error);
const errorMessage = error instanceof Error ? error.message : String(error);
logger.warn('[gemini-remote] prompt failed', { message: errorMessage });
session.sendSessionEvent({
type: 'message',
message: 'Gemini prompt failed. Check logs for details.'
message: `Gemini prompt failed: ${errorMessage}`
});
messageBuffer.addMessage('Gemini prompt failed', 'status');
messageBuffer.addMessage(`Gemini prompt failed: ${errorMessage}`, 'status');
} finally {
session.onThinkingChange(false);
await this.permissionHandler?.cancelAll('Prompt finished');
Expand Down
7 changes: 6 additions & 1 deletion cli/src/gemini/loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface GeminiLoopOptions {
model?: string;
hookSettingsPath?: string;
allowedTools?: string[];
resumeSessionId?: string;
onSessionReady?: (session: GeminiSession) => void;
}

Expand All @@ -31,7 +32,7 @@ export async function geminiLoop(opts: GeminiLoopOptions): Promise<void> {
api: opts.api,
client: opts.session,
path: opts.path,
sessionId: null,
sessionId: opts.resumeSessionId ?? null,
logPath,
messageQueue: opts.messageQueue,
onModeChange: opts.onModeChange,
Expand All @@ -41,6 +42,10 @@ export async function geminiLoop(opts: GeminiLoopOptions): Promise<void> {
permissionMode: opts.permissionMode ?? 'default'
});

if (opts.resumeSessionId) {
session.onSessionFound(opts.resumeSessionId);
}

await runLocalRemoteSession({
session,
startingMode: opts.startingMode,
Expand Down
22 changes: 22 additions & 0 deletions cli/src/gemini/runGemini.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,26 @@ describe('runGemini', () => {
expect(harness.bootstrapArgs[0]?.model).toBeUndefined();
expect(harness.geminiLoopArgs[0]?.model).toBe('gemini-2.5-pro');
});

it('passes resumeSessionId through to geminiLoop', async () => {
resolveGeminiRuntimeConfigMock.mockReturnValue({
model: 'gemini-2.5-pro',
modelSource: 'default'
});

await runGemini({ resumeSessionId: 'a6157ffa-f692-4b73-82d5-63d42177f4f9' });

expect(harness.geminiLoopArgs[0]?.resumeSessionId).toBe('a6157ffa-f692-4b73-82d5-63d42177f4f9');
});

it('does not set resumeSessionId when not provided', async () => {
resolveGeminiRuntimeConfigMock.mockReturnValue({
model: 'gemini-2.5-pro',
modelSource: 'default'
});

await runGemini({});

expect(harness.geminiLoopArgs[0]?.resumeSessionId).toBeUndefined();
});
});
2 changes: 2 additions & 0 deletions cli/src/gemini/runGemini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export async function runGemini(opts: {
startingMode?: 'local' | 'remote';
permissionMode?: PermissionMode;
model?: string;
resumeSessionId?: string;
} = {}): Promise<void> {
const workingDirectory = getInvokedCwd();
const startedBy = opts.startedBy ?? 'terminal';
Expand Down Expand Up @@ -149,6 +150,7 @@ export async function runGemini(opts: {
permissionMode: currentPermissionMode,
model: resolvedModel,
hookSettingsPath,
resumeSessionId: opts.resumeSessionId,
onModeChange: createModeChangeHandler(session),
onSessionReady: (instance) => {
sessionWrapperRef.current = instance;
Expand Down
Loading