mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-20 10:10:56 -07:00
fix(core): dynamic session ID injection to resolve resume bugs (#24972)
This commit is contained in:
committed by
GitHub
parent
80764c8bb5
commit
d06dba3538
@@ -182,6 +182,7 @@ class SubAgentInvocation extends BaseToolInvocation<AgentInputs, ToolResult> {
|
||||
{
|
||||
operation: GeminiCliOperation.AgentCall,
|
||||
logPrompts: this.context.config.getTelemetryLogPromptsEnabled(),
|
||||
sessionId: this.context.config.getSessionId(),
|
||||
attributes: {
|
||||
[GEN_AI_AGENT_NAME]: this.definition.name,
|
||||
[GEN_AI_AGENT_DESCRIPTION]: this.definition.description,
|
||||
|
||||
@@ -74,6 +74,7 @@ describe('LoggingContentGenerator', () => {
|
||||
}),
|
||||
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(true),
|
||||
refreshUserQuotaIfStale: vi.fn().mockResolvedValue(undefined),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Config;
|
||||
loggingContentGenerator = new LoggingContentGenerator(wrapped, config);
|
||||
vi.useFakeTimers();
|
||||
|
||||
@@ -350,6 +350,7 @@ export class LoggingContentGenerator implements ContentGenerator {
|
||||
{
|
||||
operation: GeminiCliOperation.LLMCall,
|
||||
logPrompts: this.config.getTelemetryLogPromptsEnabled(),
|
||||
sessionId: this.config.getSessionId(),
|
||||
attributes: {
|
||||
[GEN_AI_REQUEST_MODEL]: req.model,
|
||||
[GEN_AI_PROMPT_NAME]: userPromptId,
|
||||
@@ -440,6 +441,7 @@ export class LoggingContentGenerator implements ContentGenerator {
|
||||
{
|
||||
operation: GeminiCliOperation.LLMCall,
|
||||
logPrompts: this.config.getTelemetryLogPromptsEnabled(),
|
||||
sessionId: this.config.getSessionId(),
|
||||
attributes: {
|
||||
[GEN_AI_REQUEST_MODEL]: req.model,
|
||||
[GEN_AI_PROMPT_NAME]: userPromptId,
|
||||
@@ -594,6 +596,7 @@ export class LoggingContentGenerator implements ContentGenerator {
|
||||
{
|
||||
operation: GeminiCliOperation.LLMCall,
|
||||
logPrompts: this.config.getTelemetryLogPromptsEnabled(),
|
||||
sessionId: this.config.getSessionId(),
|
||||
attributes: {
|
||||
[GEN_AI_REQUEST_MODEL]: req.model,
|
||||
},
|
||||
|
||||
@@ -252,7 +252,7 @@ export * from './telemetry/index.js';
|
||||
export * from './telemetry/billingEvents.js';
|
||||
export { logBillingEvent } from './telemetry/loggers.js';
|
||||
export * from './telemetry/constants.js';
|
||||
export { sessionId, createSessionId } from './utils/session.js';
|
||||
export { createSessionId } from './utils/session.js';
|
||||
export * from './utils/compatibility.js';
|
||||
export * from './utils/browser.js';
|
||||
export { Storage } from './config/storage.js';
|
||||
|
||||
@@ -51,8 +51,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
|
||||
@@ -79,8 +79,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
|
||||
@@ -161,8 +161,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
|
||||
@@ -226,8 +226,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
const toolCall = {
|
||||
request: { name: 'test-tool', args: {}, isClientInitiated: true },
|
||||
tool: { name: 'test-tool' },
|
||||
@@ -243,8 +243,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -273,8 +273,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -307,6 +307,7 @@ describe('policy.ts', () => {
|
||||
isTrustedFolder: vi.fn().mockReturnValue(false),
|
||||
getWorkspacePoliciesDir: vi.fn().mockReturnValue(undefined),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
@@ -339,8 +340,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -379,8 +380,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -420,8 +421,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -447,8 +448,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -473,8 +474,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -499,8 +500,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -540,8 +541,8 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
mockConfig as Config;
|
||||
const mockMessageBus = {
|
||||
@@ -583,6 +584,7 @@ describe('policy.ts', () => {
|
||||
isTrustedFolder: vi.fn().mockReturnValue(false),
|
||||
getWorkspacePoliciesDir: vi.fn().mockReturnValue(undefined),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config =
|
||||
@@ -628,6 +630,7 @@ describe('policy.ts', () => {
|
||||
.fn()
|
||||
.mockReturnValue('/mock/project/policies'),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
const mockMessageBus = {
|
||||
publish: vi.fn(),
|
||||
@@ -659,6 +662,7 @@ describe('policy.ts', () => {
|
||||
.fn()
|
||||
.mockReturnValue('/mock/project/policies'),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
const mockMessageBus = {
|
||||
publish: vi.fn(),
|
||||
@@ -689,6 +693,7 @@ describe('policy.ts', () => {
|
||||
getWorkspacePoliciesDir: vi.fn().mockReturnValue(undefined),
|
||||
getTargetDir: vi.fn().mockReturnValue('/mock/dir'),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
const mockMessageBus = {
|
||||
publish: vi.fn(),
|
||||
@@ -727,6 +732,7 @@ describe('policy.ts', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
const mockMessageBus = {
|
||||
publish: vi.fn(),
|
||||
@@ -766,6 +772,7 @@ describe('policy.ts', () => {
|
||||
it('should return default denial message when no rule provided', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Config;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config = mockConfig;
|
||||
@@ -779,6 +786,7 @@ describe('policy.ts', () => {
|
||||
it('should return custom deny message if provided', () => {
|
||||
const mockConfig = {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Config;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config = mockConfig;
|
||||
@@ -840,7 +848,6 @@ describe('Plan Mode Denial Consistency', () => {
|
||||
publish: vi.fn(),
|
||||
subscribe: vi.fn(),
|
||||
} as unknown as Mocked<MessageBus>;
|
||||
|
||||
mockConfig = {
|
||||
getPolicyEngine: vi.fn().mockReturnValue(mockPolicyEngine),
|
||||
toolRegistry: mockToolRegistry,
|
||||
@@ -852,6 +859,7 @@ describe('Plan Mode Denial Consistency', () => {
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.PLAN), // Key: Plan Mode
|
||||
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
|
||||
setApprovalMode: vi.fn(),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
getUsageStatisticsEnabled: vi.fn().mockReturnValue(false),
|
||||
} as unknown as Mocked<Config>;
|
||||
(mockConfig as unknown as { config: Config }).config = mockConfig as Config;
|
||||
@@ -933,6 +941,7 @@ describe('Plan Mode Denial Consistency', () => {
|
||||
getApprovalMode: vi.fn().mockReturnValue(currentMode),
|
||||
isTrustedFolder: vi.fn().mockReturnValue(false),
|
||||
getWorkspacePoliciesDir: vi.fn().mockReturnValue(undefined),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
const mockMessageBus = {
|
||||
|
||||
@@ -177,6 +177,7 @@ describe('Scheduler (Orchestrator)', () => {
|
||||
setApprovalMode: vi.fn(),
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config = mockConfig as Config;
|
||||
@@ -1423,6 +1424,7 @@ describe('Scheduler MCP Progress', () => {
|
||||
setApprovalMode: vi.fn(),
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config = mockConfig as Config;
|
||||
|
||||
@@ -197,6 +197,7 @@ export class Scheduler {
|
||||
{
|
||||
operation: GeminiCliOperation.ScheduleToolCalls,
|
||||
logPrompts: this.context.config.getTelemetryLogPromptsEnabled(),
|
||||
sessionId: this.context.config.getSessionId(),
|
||||
},
|
||||
async ({ metadata: spanMetadata }) => {
|
||||
const requests = Array.isArray(request) ? request : [request];
|
||||
|
||||
@@ -218,6 +218,7 @@ describe('Scheduler Parallel Execution', () => {
|
||||
setApprovalMode: vi.fn(),
|
||||
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
|
||||
getTelemetryLogPromptsEnabled: vi.fn().mockReturnValue(false),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Mocked<Config>;
|
||||
|
||||
(mockConfig as unknown as { config: Config }).config = mockConfig as Config;
|
||||
|
||||
@@ -84,6 +84,7 @@ export class ToolExecutor {
|
||||
{
|
||||
operation: GeminiCliOperation.ToolCall,
|
||||
logPrompts: this.config.getTelemetryLogPromptsEnabled(),
|
||||
sessionId: this.config.getSessionId(),
|
||||
attributes: {
|
||||
[GEN_AI_TOOL_NAME]: toolName,
|
||||
[GEN_AI_TOOL_CALL_ID]: callId,
|
||||
|
||||
@@ -110,7 +110,7 @@ describe('runInDevTraceSpan', () => {
|
||||
const fn = vi.fn(async () => 'result');
|
||||
|
||||
const result = await runInDevTraceSpan(
|
||||
{ operation: GeminiCliOperation.LLMCall },
|
||||
{ operation: GeminiCliOperation.LLMCall, sessionId: 'test-session-id' },
|
||||
fn,
|
||||
);
|
||||
|
||||
@@ -125,7 +125,7 @@ describe('runInDevTraceSpan', () => {
|
||||
|
||||
it('should set default attributes on the span metadata', async () => {
|
||||
await runInDevTraceSpan(
|
||||
{ operation: GeminiCliOperation.LLMCall },
|
||||
{ operation: GeminiCliOperation.LLMCall, sessionId: 'test-session-id' },
|
||||
async ({ metadata }) => {
|
||||
expect(metadata.attributes[GEN_AI_OPERATION_NAME]).toBe(
|
||||
GeminiCliOperation.LLMCall,
|
||||
@@ -143,7 +143,7 @@ describe('runInDevTraceSpan', () => {
|
||||
|
||||
it('should set span attributes from metadata on completion', async () => {
|
||||
await runInDevTraceSpan(
|
||||
{ operation: GeminiCliOperation.LLMCall },
|
||||
{ operation: GeminiCliOperation.LLMCall, sessionId: 'test-session-id' },
|
||||
async ({ metadata }) => {
|
||||
metadata.input = { query: 'hello' };
|
||||
metadata.output = { response: 'world' };
|
||||
@@ -169,9 +169,12 @@ describe('runInDevTraceSpan', () => {
|
||||
it('should handle errors in the wrapped function', async () => {
|
||||
const error = new Error('test error');
|
||||
await expect(
|
||||
runInDevTraceSpan({ operation: GeminiCliOperation.LLMCall }, async () => {
|
||||
throw error;
|
||||
}),
|
||||
runInDevTraceSpan(
|
||||
{ operation: GeminiCliOperation.LLMCall, sessionId: 'test-session-id' },
|
||||
async () => {
|
||||
throw error;
|
||||
},
|
||||
),
|
||||
).rejects.toThrow(error);
|
||||
|
||||
expect(mockSpan.setStatus).toHaveBeenCalledWith({
|
||||
@@ -189,7 +192,7 @@ describe('runInDevTraceSpan', () => {
|
||||
}
|
||||
|
||||
const resultStream = await runInDevTraceSpan(
|
||||
{ operation: GeminiCliOperation.LLMCall },
|
||||
{ operation: GeminiCliOperation.LLMCall, sessionId: 'test-session-id' },
|
||||
async () => testStream(),
|
||||
);
|
||||
|
||||
@@ -212,7 +215,7 @@ describe('runInDevTraceSpan', () => {
|
||||
}
|
||||
|
||||
const resultStream = await runInDevTraceSpan(
|
||||
{ operation: GeminiCliOperation.LLMCall },
|
||||
{ operation: GeminiCliOperation.LLMCall, sessionId: 'test-session-id' },
|
||||
async () => errorStream(),
|
||||
);
|
||||
|
||||
@@ -231,7 +234,7 @@ describe('runInDevTraceSpan', () => {
|
||||
});
|
||||
|
||||
await runInDevTraceSpan(
|
||||
{ operation: GeminiCliOperation.LLMCall },
|
||||
{ operation: GeminiCliOperation.LLMCall, sessionId: 'test-session-id' },
|
||||
async ({ metadata }) => {
|
||||
metadata.input = 'trigger error';
|
||||
},
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
SERVICE_DESCRIPTION,
|
||||
SERVICE_NAME,
|
||||
} from './constants.js';
|
||||
import { sessionId } from '../utils/session.js';
|
||||
|
||||
import { truncateString } from '../utils/textUtils.js';
|
||||
|
||||
@@ -96,10 +95,14 @@ export interface SpanMetadata {
|
||||
* @returns The result of the function.
|
||||
*/
|
||||
export async function runInDevTraceSpan<R>(
|
||||
opts: SpanOptions & { operation: GeminiCliOperation; logPrompts?: boolean },
|
||||
opts: SpanOptions & {
|
||||
operation: GeminiCliOperation;
|
||||
logPrompts?: boolean;
|
||||
sessionId: string;
|
||||
},
|
||||
fn: ({ metadata }: { metadata: SpanMetadata }) => Promise<R>,
|
||||
): Promise<R> {
|
||||
const { operation, logPrompts, ...restOfSpanOpts } = opts;
|
||||
const { operation, logPrompts, sessionId, ...restOfSpanOpts } = opts;
|
||||
|
||||
const tracer = trace.getTracer(TRACER_NAME, TRACER_VERSION);
|
||||
return tracer.startActiveSpan(operation, restOfSpanOpts, async (span) => {
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
export const sessionId = randomUUID();
|
||||
|
||||
export function createSessionId(): string {
|
||||
return randomUUID();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user