repoburg 1.3.136 → 1.3.138

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/CODEMAP.md +3 -11
  2. package/README.md +4 -4
  3. package/backend/.env +2 -2
  4. package/backend/CODEMAP.md +2 -3
  5. package/backend/dist/src/core-entities/llm-call-log.entity.d.ts +1 -1
  6. package/backend/dist/src/core-entities/llm-call-log.entity.js +3 -3
  7. package/backend/dist/src/core-entities/llm-call-log.entity.js.map +1 -1
  8. package/backend/dist/src/core-entities/session.entity.js +2 -2
  9. package/backend/dist/src/core-entities/session.entity.js.map +1 -1
  10. package/backend/dist/src/core-entities/system-prompt.entity.js +1 -1
  11. package/backend/dist/src/core-entities/system-prompt.entity.js.map +1 -1
  12. package/backend/dist/src/llm-call-logs/dto/llm-call-log.dto.d.ts +1 -1
  13. package/backend/dist/src/llm-call-logs/dto/llm-call-log.dto.js +1 -1
  14. package/backend/dist/src/llm-call-logs/dto/llm-call-log.dto.js.map +1 -1
  15. package/backend/dist/src/llm-call-logs/llm-call-logs.controller.js +1 -1
  16. package/backend/dist/src/llm-orchestration/action-handlers/manage-sub-agents.handler.js +1 -1
  17. package/backend/dist/src/llm-orchestration/action-handlers/overwrite-file.handler.js +1 -0
  18. package/backend/dist/src/llm-orchestration/action-handlers/overwrite-file.handler.js.map +1 -1
  19. package/backend/dist/src/llm-orchestration/action-handlers/quick-edit.handler.js +3 -0
  20. package/backend/dist/src/llm-orchestration/action-handlers/quick-edit.handler.js.map +1 -1
  21. package/backend/dist/src/llm-provider/llm-provider.interface.d.ts +3 -9
  22. package/backend/dist/src/llm-provider/llm-provider.interface.js +2 -9
  23. package/backend/dist/src/llm-provider/llm-provider.interface.js.map +1 -1
  24. package/backend/dist/src/llm-provider/llm-provider.module.js +1 -2
  25. package/backend/dist/src/llm-provider/llm-provider.module.js.map +1 -1
  26. package/backend/dist/src/llm-provider/proxy-llm.provider.d.ts +1 -3
  27. package/backend/dist/src/llm-provider/proxy-llm.provider.js +4 -12
  28. package/backend/dist/src/llm-provider/proxy-llm.provider.js.map +1 -1
  29. package/backend/dist/src/llm-responses/dto/sync-conversation.dto.js +2 -2
  30. package/backend/dist/src/llm-responses/dto/sync-conversation.dto.js.map +1 -1
  31. package/backend/dist/src/sessions/dto/session.dto.d.ts +1 -1
  32. package/backend/dist/src/sessions/dto/session.dto.js +7 -7
  33. package/backend/dist/src/sessions/dto/session.dto.js.map +1 -1
  34. package/backend/dist/src/sessions/sessions.service.js +14 -5
  35. package/backend/dist/src/sessions/sessions.service.js.map +1 -1
  36. package/backend/dist/src/sub-agents/dto/create-sub-agent.dto.js +1 -1
  37. package/backend/dist/src/sub-agents/dto/create-sub-agent.dto.js.map +1 -1
  38. package/backend/dist/src/sub-agents/dto/update-sub-agent.dto.js +1 -1
  39. package/backend/dist/src/sub-agents/dto/update-sub-agent.dto.js.map +1 -1
  40. package/backend/dist/src/sub-agents/sub-agent.entity.js +1 -1
  41. package/backend/dist/src/sub-agents/sub-agent.entity.js.map +1 -1
  42. package/backend/dist/src/system-prompts/dto/system-prompt.dto.js +2 -2
  43. package/backend/dist/src/system-prompts/dto/system-prompt.dto.js.map +1 -1
  44. package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
  45. package/package.json +1 -2
  46. package/backend/dist/packages/gemini-core/examples/simple.d.ts +0 -1
  47. package/backend/dist/packages/gemini-core/examples/simple.js +0 -73
  48. package/backend/dist/packages/gemini-core/examples/simple.js.map +0 -1
  49. package/backend/dist/packages/gemini-core/index.d.ts +0 -3
  50. package/backend/dist/packages/gemini-core/index.js +0 -27
  51. package/backend/dist/packages/gemini-core/index.js.map +0 -1
  52. package/backend/dist/packages/gemini-core/src/code_assist/codeAssist.d.ts +0 -5
  53. package/backend/dist/packages/gemini-core/src/code_assist/codeAssist.js +0 -17
  54. package/backend/dist/packages/gemini-core/src/code_assist/codeAssist.js.map +0 -1
  55. package/backend/dist/packages/gemini-core/src/code_assist/converter.d.ts +0 -69
  56. package/backend/dist/packages/gemini-core/src/code_assist/converter.js +0 -129
  57. package/backend/dist/packages/gemini-core/src/code_assist/converter.js.map +0 -1
  58. package/backend/dist/packages/gemini-core/src/code_assist/oauth-credential-storage.d.ts +0 -8
  59. package/backend/dist/packages/gemini-core/src/code_assist/oauth-credential-storage.js +0 -84
  60. package/backend/dist/packages/gemini-core/src/code_assist/oauth-credential-storage.js.map +0 -1
  61. package/backend/dist/packages/gemini-core/src/code_assist/oauth2.d.ts +0 -12
  62. package/backend/dist/packages/gemini-core/src/code_assist/oauth2.js +0 -371
  63. package/backend/dist/packages/gemini-core/src/code_assist/oauth2.js.map +0 -1
  64. package/backend/dist/packages/gemini-core/src/code_assist/server.d.ts +0 -28
  65. package/backend/dist/packages/gemini-core/src/code_assist/server.js +0 -135
  66. package/backend/dist/packages/gemini-core/src/code_assist/server.js.map +0 -1
  67. package/backend/dist/packages/gemini-core/src/code_assist/setup.d.ts +0 -10
  68. package/backend/dist/packages/gemini-core/src/code_assist/setup.js +0 -94
  69. package/backend/dist/packages/gemini-core/src/code_assist/setup.js.map +0 -1
  70. package/backend/dist/packages/gemini-core/src/code_assist/types.d.ts +0 -106
  71. package/backend/dist/packages/gemini-core/src/code_assist/types.js +0 -28
  72. package/backend/dist/packages/gemini-core/src/code_assist/types.js.map +0 -1
  73. package/backend/dist/packages/gemini-core/src/config/config.d.ts +0 -22
  74. package/backend/dist/packages/gemini-core/src/config/config.js +0 -38
  75. package/backend/dist/packages/gemini-core/src/config/config.js.map +0 -1
  76. package/backend/dist/packages/gemini-core/src/config/models.d.ts +0 -6
  77. package/backend/dist/packages/gemini-core/src/config/models.js +0 -13
  78. package/backend/dist/packages/gemini-core/src/config/models.js.map +0 -1
  79. package/backend/dist/packages/gemini-core/src/config/storage.d.ts +0 -8
  80. package/backend/dist/packages/gemini-core/src/config/storage.js +0 -25
  81. package/backend/dist/packages/gemini-core/src/config/storage.js.map +0 -1
  82. package/backend/dist/packages/gemini-core/src/core/contentGenerator.d.ts +0 -23
  83. package/backend/dist/packages/gemini-core/src/core/contentGenerator.js +0 -63
  84. package/backend/dist/packages/gemini-core/src/core/contentGenerator.js.map +0 -1
  85. package/backend/dist/packages/gemini-core/src/core/geminiChat.d.ts +0 -34
  86. package/backend/dist/packages/gemini-core/src/core/geminiChat.js +0 -247
  87. package/backend/dist/packages/gemini-core/src/core/geminiChat.js.map +0 -1
  88. package/backend/dist/packages/gemini-core/src/core/geminiRequest.d.ts +0 -3
  89. package/backend/dist/packages/gemini-core/src/core/geminiRequest.js +0 -8
  90. package/backend/dist/packages/gemini-core/src/core/geminiRequest.js.map +0 -1
  91. package/backend/dist/packages/gemini-core/src/core/prompts.d.ts +0 -7
  92. package/backend/dist/packages/gemini-core/src/core/prompts.js +0 -69
  93. package/backend/dist/packages/gemini-core/src/core/prompts.js.map +0 -1
  94. package/backend/dist/packages/gemini-core/src/core/tokenLimits.d.ts +0 -5
  95. package/backend/dist/packages/gemini-core/src/core/tokenLimits.js +0 -25
  96. package/backend/dist/packages/gemini-core/src/core/tokenLimits.js.map +0 -1
  97. package/backend/dist/packages/gemini-core/src/index.d.ts +0 -18
  98. package/backend/dist/packages/gemini-core/src/index.js +0 -37
  99. package/backend/dist/packages/gemini-core/src/index.js.map +0 -1
  100. package/backend/dist/packages/gemini-core/src/mcp/token-storage/base-token-storage.d.ts +0 -14
  101. package/backend/dist/packages/gemini-core/src/mcp/token-storage/base-token-storage.js +0 -34
  102. package/backend/dist/packages/gemini-core/src/mcp/token-storage/base-token-storage.js.map +0 -1
  103. package/backend/dist/packages/gemini-core/src/mcp/token-storage/file-token-storage.d.ts +0 -19
  104. package/backend/dist/packages/gemini-core/src/mcp/token-storage/file-token-storage.js +0 -141
  105. package/backend/dist/packages/gemini-core/src/mcp/token-storage/file-token-storage.js.map +0 -1
  106. package/backend/dist/packages/gemini-core/src/mcp/token-storage/hybrid-token-storage.d.ts +0 -18
  107. package/backend/dist/packages/gemini-core/src/mcp/token-storage/hybrid-token-storage.js +0 -74
  108. package/backend/dist/packages/gemini-core/src/mcp/token-storage/hybrid-token-storage.js.map +0 -1
  109. package/backend/dist/packages/gemini-core/src/mcp/token-storage/index.d.ts +0 -6
  110. package/backend/dist/packages/gemini-core/src/mcp/token-storage/index.js +0 -24
  111. package/backend/dist/packages/gemini-core/src/mcp/token-storage/index.js.map +0 -1
  112. package/backend/dist/packages/gemini-core/src/mcp/token-storage/keychain-token-storage.d.ts +0 -26
  113. package/backend/dist/packages/gemini-core/src/mcp/token-storage/keychain-token-storage.js +0 -188
  114. package/backend/dist/packages/gemini-core/src/mcp/token-storage/keychain-token-storage.js.map +0 -1
  115. package/backend/dist/packages/gemini-core/src/mcp/token-storage/types.d.ts +0 -27
  116. package/backend/dist/packages/gemini-core/src/mcp/token-storage/types.js +0 -9
  117. package/backend/dist/packages/gemini-core/src/mcp/token-storage/types.js.map +0 -1
  118. package/backend/dist/packages/gemini-core/src/utils/errors.d.ts +0 -34
  119. package/backend/dist/packages/gemini-core/src/utils/errors.js +0 -103
  120. package/backend/dist/packages/gemini-core/src/utils/errors.js.map +0 -1
  121. package/backend/dist/packages/gemini-core/src/utils/partUtils.d.ts +0 -7
  122. package/backend/dist/packages/gemini-core/src/utils/partUtils.js +0 -108
  123. package/backend/dist/packages/gemini-core/src/utils/partUtils.js.map +0 -1
  124. package/backend/dist/packages/gemini-core/src/utils/quotaErrorDetection.d.ts +0 -16
  125. package/backend/dist/packages/gemini-core/src/utils/quotaErrorDetection.js +0 -60
  126. package/backend/dist/packages/gemini-core/src/utils/quotaErrorDetection.js.map +0 -1
  127. package/backend/dist/packages/gemini-core/src/utils/retry.d.ts +0 -14
  128. package/backend/dist/packages/gemini-core/src/utils/retry.js +0 -154
  129. package/backend/dist/packages/gemini-core/src/utils/retry.js.map +0 -1
  130. package/backend/dist/packages/gemini-core/src/utils/session.d.ts +0 -1
  131. package/backend/dist/packages/gemini-core/src/utils/session.js +0 -6
  132. package/backend/dist/packages/gemini-core/src/utils/session.js.map +0 -1
  133. package/backend/dist/packages/gemini-core/src/utils/userAccountManager.d.ts +0 -10
  134. package/backend/dist/packages/gemini-core/src/utils/userAccountManager.js +0 -106
  135. package/backend/dist/packages/gemini-core/src/utils/userAccountManager.js.map +0 -1
  136. package/backend/dist/src/gemini/gemini-llm.provider.d.ts +0 -12
  137. package/backend/dist/src/gemini/gemini-llm.provider.js +0 -126
  138. package/backend/dist/src/gemini/gemini-llm.provider.js.map +0 -1
  139. package/backend/dist/src/gemini/gemini.module.d.ts +0 -2
  140. package/backend/dist/src/gemini/gemini.module.js +0 -21
  141. package/backend/dist/src/gemini/gemini.module.js.map +0 -1
  142. package/backend/packages/gemini-core/CODEMAP.md +0 -30
  143. package/backend/packages/gemini-core/examples/simple.ts +0 -120
  144. package/backend/packages/gemini-core/index.ts +0 -15
  145. package/backend/packages/gemini-core/src/code_assist/codeAssist.ts +0 -37
  146. package/backend/packages/gemini-core/src/code_assist/converter.ts +0 -250
  147. package/backend/packages/gemini-core/src/code_assist/oauth-credential-storage.ts +0 -129
  148. package/backend/packages/gemini-core/src/code_assist/oauth2.ts +0 -541
  149. package/backend/packages/gemini-core/src/code_assist/server.ts +0 -236
  150. package/backend/packages/gemini-core/src/code_assist/setup.ts +0 -124
  151. package/backend/packages/gemini-core/src/code_assist/types.ts +0 -201
  152. package/backend/packages/gemini-core/src/config/config.ts +0 -75
  153. package/backend/packages/gemini-core/src/config/models.ts +0 -23
  154. package/backend/packages/gemini-core/src/config/storage.ts +0 -30
  155. package/backend/packages/gemini-core/src/core/contentGenerator.ts +0 -136
  156. package/backend/packages/gemini-core/src/core/geminiChat.ts +0 -385
  157. package/backend/packages/gemini-core/src/core/geminiRequest.ts +0 -19
  158. package/backend/packages/gemini-core/src/core/prompts.ts +0 -109
  159. package/backend/packages/gemini-core/src/core/tokenLimits.ts +0 -32
  160. package/backend/packages/gemini-core/src/index.ts +0 -35
  161. package/backend/packages/gemini-core/src/mcp/token-storage/base-token-storage.ts +0 -49
  162. package/backend/packages/gemini-core/src/mcp/token-storage/file-token-storage.ts +0 -184
  163. package/backend/packages/gemini-core/src/mcp/token-storage/hybrid-token-storage.ts +0 -97
  164. package/backend/packages/gemini-core/src/mcp/token-storage/index.ts +0 -14
  165. package/backend/packages/gemini-core/src/mcp/token-storage/keychain-token-storage.ts +0 -251
  166. package/backend/packages/gemini-core/src/mcp/token-storage/types.ts +0 -42
  167. package/backend/packages/gemini-core/src/utils/errors.ts +0 -112
  168. package/backend/packages/gemini-core/src/utils/partUtils.ts +0 -169
  169. package/backend/packages/gemini-core/src/utils/quotaErrorDetection.ts +0 -102
  170. package/backend/packages/gemini-core/src/utils/retry.ts +0 -265
  171. package/backend/packages/gemini-core/src/utils/session.ts +0 -9
  172. package/backend/packages/gemini-core/src/utils/userAccountManager.ts +0 -140
@@ -1,136 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import type {
8
- CountTokensResponse,
9
- GenerateContentResponse,
10
- GenerateContentParameters,
11
- CountTokensParameters,
12
- EmbedContentResponse,
13
- EmbedContentParameters,
14
- } from '@google/genai';
15
- import { GoogleGenAI } from '@google/genai';
16
- import { createCodeAssistContentGenerator } from '../code_assist/codeAssist';
17
- import type { Config } from '../config/config';
18
-
19
- import type { UserTierId } from '../code_assist/types';
20
-
21
- /**
22
- * Interface abstracting the core functionalities for generating content and counting tokens.
23
- */
24
- export interface ContentGenerator {
25
- generateContent(
26
- request: GenerateContentParameters,
27
- userPromptId: string,
28
- ): Promise<GenerateContentResponse>;
29
-
30
- generateContentStream(
31
- request: GenerateContentParameters,
32
- userPromptId: string,
33
- ): Promise<AsyncGenerator<GenerateContentResponse>>;
34
-
35
- countTokens(request: CountTokensParameters): Promise<CountTokensResponse>;
36
-
37
- embedContent(request: EmbedContentParameters): Promise<EmbedContentResponse>;
38
-
39
- userTier?: UserTierId;
40
- }
41
-
42
- export enum AuthType {
43
- LOGIN_WITH_GOOGLE = 'oauth-personal',
44
- USE_GEMINI = 'gemini-api-key',
45
- USE_VERTEX_AI = 'vertex-ai',
46
- CLOUD_SHELL = 'cloud-shell',
47
- }
48
-
49
- export type ContentGeneratorConfig = {
50
- apiKey?: string;
51
- vertexai?: boolean;
52
- authType?: AuthType;
53
- };
54
-
55
- export function createContentGeneratorConfig(
56
- config: Config,
57
- authType: AuthType | undefined,
58
- ): ContentGeneratorConfig {
59
- const geminiApiKey = process.env['GEMINI_API_KEY'] || undefined;
60
- const googleApiKey = process.env['GOOGLE_API_KEY'] || undefined;
61
- const googleCloudProject = process.env['GOOGLE_CLOUD_PROJECT'] || undefined;
62
- const googleCloudLocation = process.env['GOOGLE_CLOUD_LOCATION'] || undefined;
63
-
64
- const contentGeneratorConfig: ContentGeneratorConfig = {
65
- authType,
66
- };
67
-
68
- // If we are using Google auth or we are in Cloud Shell, there is nothing else to validate for now
69
- if (
70
- authType === AuthType.LOGIN_WITH_GOOGLE ||
71
- authType === AuthType.CLOUD_SHELL
72
- ) {
73
- return contentGeneratorConfig;
74
- }
75
-
76
- if (authType === AuthType.USE_GEMINI && geminiApiKey) {
77
- contentGeneratorConfig.apiKey = geminiApiKey;
78
- contentGeneratorConfig.vertexai = false;
79
-
80
- return contentGeneratorConfig;
81
- }
82
-
83
- if (
84
- authType === AuthType.USE_VERTEX_AI &&
85
- (googleApiKey || (googleCloudProject && googleCloudLocation))
86
- ) {
87
- contentGeneratorConfig.apiKey = googleApiKey;
88
- contentGeneratorConfig.vertexai = true;
89
-
90
- return contentGeneratorConfig;
91
- }
92
-
93
- return contentGeneratorConfig;
94
- }
95
-
96
- export async function createContentGenerator(
97
- config: ContentGeneratorConfig,
98
- gcConfig: Config,
99
- sessionId?: string,
100
- ): Promise<ContentGenerator> {
101
- const version = process.env['CLI_VERSION'] || process.version;
102
- const userAgent = `GeminiCLI/${version} (${process.platform}; ${process.arch})`;
103
- const baseHeaders: Record<string, string> = {
104
- 'User-Agent': userAgent,
105
- };
106
-
107
- if (
108
- config.authType === AuthType.LOGIN_WITH_GOOGLE ||
109
- config.authType === AuthType.CLOUD_SHELL
110
- ) {
111
- const httpOptions = { headers: baseHeaders };
112
- return createCodeAssistContentGenerator(
113
- httpOptions,
114
- config.authType,
115
- gcConfig,
116
- sessionId,
117
- );
118
- }
119
-
120
- if (
121
- config.authType === AuthType.USE_GEMINI ||
122
- config.authType === AuthType.USE_VERTEX_AI
123
- ) {
124
- const httpOptions = { headers: baseHeaders };
125
-
126
- const googleGenAI = new GoogleGenAI({
127
- apiKey: config.apiKey === '' ? undefined : config.apiKey,
128
- vertexai: config.vertexai,
129
- httpOptions,
130
- });
131
- return googleGenAI.models;
132
- }
133
- throw new Error(
134
- `Error creating contentGenerator: Unsupported authType: ${config.authType}`,
135
- );
136
- }
@@ -1,385 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- // DISCLAIMER: This is a copied version of https://github.com/googleapis/js-genai/blob/main/src/chats.ts with the intention of working around a key bug
8
- // where function responses are not treated as "valid" responses: https://b.corp.google.com/issues/420354090
9
-
10
- import {
11
- GenerateContentResponse,
12
- type Content,
13
- type GenerateContentConfig,
14
- type SendMessageParameters,
15
- type Part,
16
- ApiError,
17
- } from '@google/genai';
18
- import { createUserContent } from '@google/genai';
19
- import { retryWithBackoff } from '../utils/retry';
20
- import type { Config } from '../config/config';
21
- import { getEffectiveModel } from '../config/models';
22
-
23
- export enum StreamEventType {
24
- /** A regular content chunk from the API. */
25
- CHUNK = 'chunk',
26
- /** A signal that a retry is about to happen. The UI should discard any partial
27
- * content from the attempt that just failed. */
28
- RETRY = 'retry',
29
- }
30
-
31
- export type StreamEvent =
32
- | { type: StreamEventType.CHUNK; value: GenerateContentResponse }
33
- | { type: StreamEventType.RETRY };
34
-
35
- /**
36
- * Options for retrying due to invalid content from the model.
37
- */
38
- interface ContentRetryOptions {
39
- /** Total number of attempts to make (1 initial + N retries). */
40
- maxAttempts: number;
41
- /** The base delay in milliseconds for linear backoff. */
42
- initialDelayMs: number;
43
- }
44
-
45
- const INVALID_CONTENT_RETRY_OPTIONS: ContentRetryOptions = {
46
- maxAttempts: 2, // 1 initial call + 1 retry
47
- initialDelayMs: 500,
48
- };
49
-
50
- /**
51
- * Returns true if the response is valid, false otherwise.
52
- */
53
- function isValidResponse(response: GenerateContentResponse): boolean {
54
- if (response.candidates === undefined || response.candidates.length === 0) {
55
- return false;
56
- }
57
- const content = response.candidates[0]?.content;
58
- if (content === undefined) {
59
- return false;
60
- }
61
- return isValidContent(content);
62
- }
63
-
64
- export function isValidNonThoughtTextPart(part: Part): boolean {
65
- return (
66
- typeof part.text === 'string' &&
67
- !part.thought &&
68
- // Technically, the model should never generate parts that have text and
69
- // any of these but we don't trust them so check anyways.
70
- !part.inlineData &&
71
- !part.fileData
72
- );
73
- }
74
-
75
- function isValidContent(content: Content): boolean {
76
- if (content.parts === undefined || content.parts.length === 0) {
77
- return false;
78
- }
79
- for (const part of content.parts) {
80
- if (part === undefined || Object.keys(part).length === 0) {
81
- return false;
82
- }
83
- if (!part.thought && part.text !== undefined && part.text === '') {
84
- return false;
85
- }
86
- }
87
- return true;
88
- }
89
-
90
- /**
91
- * Validates the history contains the correct roles.
92
- *
93
- * @throws Error if the history does not start with a user turn.
94
- * @throws Error if the history contains an invalid role.
95
- */
96
- function validateHistory(history: Content[]) {
97
- for (const content of history) {
98
- if (content.role !== 'user' && content.role !== 'model') {
99
- throw new Error(`Role must be user or model, but got ${content.role}.`);
100
- }
101
- }
102
- }
103
-
104
- /**
105
- * Extracts the curated (valid) history from a comprehensive history.
106
- *
107
- * @remarks
108
- * The model may sometimes generate invalid or empty contents(e.g., due to safety
109
- * filters or recitation). Extracting valid turns from the history
110
- * ensures that subsequent requests could be accepted by the model.
111
- */
112
- function extractCuratedHistory(comprehensiveHistory: Content[]): Content[] {
113
- if (comprehensiveHistory === undefined || comprehensiveHistory.length === 0) {
114
- return [];
115
- }
116
- const curatedHistory: Content[] = [];
117
- const length = comprehensiveHistory.length;
118
- let i = 0;
119
- while (i < length) {
120
- if (comprehensiveHistory[i].role === 'user') {
121
- curatedHistory.push(comprehensiveHistory[i]);
122
- i++;
123
- } else {
124
- const modelOutput: Content[] = [];
125
- let isValid = true;
126
- while (i < length && comprehensiveHistory[i].role === 'model') {
127
- modelOutput.push(comprehensiveHistory[i]);
128
- if (isValid && !isValidContent(comprehensiveHistory[i])) {
129
- isValid = false;
130
- }
131
- i++;
132
- }
133
- if (isValid) {
134
- curatedHistory.push(...modelOutput);
135
- }
136
- }
137
- }
138
- return curatedHistory;
139
- }
140
-
141
- /**
142
- * Custom error to signal that a stream completed with invalid content,
143
- * which should trigger a retry.
144
- */
145
- export class InvalidStreamError extends Error {
146
- readonly type: 'NO_FINISH_REASON' | 'NO_RESPONSE_TEXT';
147
-
148
- constructor(message: string, type: 'NO_FINISH_REASON' | 'NO_RESPONSE_TEXT') {
149
- super(message);
150
- this.name = 'InvalidStreamError';
151
- this.type = type;
152
- }
153
- }
154
-
155
- /**
156
- * Chat session that enables sending messages to the model with previous
157
- * conversation context.
158
- *
159
- * @remarks
160
- * The session maintains all the turns between user and model.
161
- */
162
- export class GeminiChat {
163
- // A promise to represent the current state of the message being sent to the
164
- // model.
165
- private sendPromise: Promise<void> = Promise.resolve();
166
-
167
- constructor(
168
- private readonly config: Config,
169
- private readonly generationConfig: GenerateContentConfig = {},
170
- private history: Content[] = [],
171
- ) {
172
- validateHistory(history);
173
- }
174
-
175
- setSystemInstruction(sysInstr: string) {
176
- this.generationConfig.systemInstruction = sysInstr;
177
- }
178
-
179
- async sendMessageStream(
180
- model: string,
181
- params: SendMessageParameters,
182
- prompt_id: string,
183
- ): Promise<AsyncGenerator<StreamEvent>> {
184
- await this.sendPromise;
185
-
186
- let streamDoneResolver: () => void;
187
- const streamDonePromise = new Promise<void>((resolve) => {
188
- streamDoneResolver = resolve;
189
- });
190
- this.sendPromise = streamDonePromise;
191
-
192
- const userContent = createUserContent(params.message);
193
-
194
- // Add user content to history ONCE before any attempts.
195
- this.history.push(userContent);
196
- const requestContents = this.getHistory(true);
197
-
198
- // eslint-disable-next-line @typescript-eslint/no-this-alias
199
- const self = this;
200
- return (async function* () {
201
- try {
202
- let lastError: unknown = new Error('Request failed after all retries.');
203
-
204
- for (
205
- let attempt = 0;
206
- attempt < INVALID_CONTENT_RETRY_OPTIONS.maxAttempts;
207
- attempt++
208
- ) {
209
- try {
210
- if (attempt > 0) {
211
- yield { type: StreamEventType.RETRY };
212
- }
213
-
214
- const stream = await self.makeApiCallAndProcessStream(
215
- model,
216
- requestContents,
217
- params,
218
- prompt_id,
219
- );
220
-
221
- for await (const chunk of stream) {
222
- yield { type: StreamEventType.CHUNK, value: chunk };
223
- }
224
-
225
- lastError = null;
226
- break;
227
- } catch (error) {
228
- lastError = error;
229
- const isContentError = error instanceof InvalidStreamError;
230
-
231
- if (isContentError) {
232
- // Check if we have more attempts left.
233
- if (attempt < INVALID_CONTENT_RETRY_OPTIONS.maxAttempts - 1) {
234
- await new Promise((res) =>
235
- setTimeout(
236
- res,
237
- INVALID_CONTENT_RETRY_OPTIONS.initialDelayMs *
238
- (attempt + 1),
239
- ),
240
- );
241
- continue;
242
- }
243
- }
244
- break;
245
- }
246
- }
247
-
248
- if (lastError) {
249
- if (self.history[self.history.length - 1] === userContent) {
250
- self.history.pop();
251
- }
252
- throw lastError;
253
- }
254
- } finally {
255
- streamDoneResolver!();
256
- }
257
- })();
258
- }
259
-
260
- private async makeApiCallAndProcessStream(
261
- model: string,
262
- requestContents: Content[],
263
- params: SendMessageParameters,
264
- prompt_id: string,
265
- ): Promise<AsyncGenerator<GenerateContentResponse>> {
266
- const apiCall = () => {
267
- const modelToUse = getEffectiveModel(model);
268
-
269
- return this.config.getContentGenerator().generateContentStream(
270
- {
271
- model: modelToUse,
272
- contents: requestContents,
273
- config: { ...this.generationConfig, ...params.config },
274
- },
275
- prompt_id,
276
- );
277
- };
278
-
279
- const streamResponse = await retryWithBackoff(apiCall, {
280
- shouldRetryOnError: (error: unknown) => {
281
- if (error instanceof ApiError && error.message) {
282
- if (error.status === 400) return false;
283
- if (isSchemaDepthError(error.message)) return false;
284
- if (error.status === 429) return true;
285
- if (error.status >= 500 && error.status < 600) return true;
286
- }
287
- return false;
288
- },
289
- authType: this.config.getContentGeneratorConfig()?.authType,
290
- });
291
-
292
- return this.processStreamResponse(model, streamResponse);
293
- }
294
-
295
- getHistory(curated: boolean = false): Content[] {
296
- const history = curated
297
- ? extractCuratedHistory(this.history)
298
- : this.history;
299
- // Deep copy the history to avoid mutating the history outside of the
300
- // chat session.
301
- return structuredClone(history);
302
- }
303
-
304
- clearHistory(): void {
305
- this.history = [];
306
- }
307
-
308
- addHistory(content: Content): void {
309
- this.history.push(content);
310
- }
311
-
312
- setHistory(history: Content[]): void {
313
- this.history = history;
314
- }
315
-
316
- private async *processStreamResponse(
317
- model: string,
318
- streamResponse: AsyncGenerator<GenerateContentResponse>,
319
- ): AsyncGenerator<GenerateContentResponse> {
320
- const modelResponseParts: Part[] = [];
321
-
322
- let hasFinishReason = false;
323
-
324
- for await (const chunk of streamResponse) {
325
- hasFinishReason =
326
- chunk?.candidates?.some((candidate) => candidate.finishReason) ?? false;
327
- if (isValidResponse(chunk)) {
328
- const content = chunk.candidates?.[0]?.content;
329
- if (content?.parts) {
330
- modelResponseParts.push(
331
- ...content.parts.filter((part) => !part.thought),
332
- );
333
- }
334
- }
335
-
336
- yield chunk; // Yield every chunk to the UI immediately.
337
- }
338
-
339
- // String thoughts and consolidate text parts.
340
- const consolidatedParts: Part[] = [];
341
- for (const part of modelResponseParts) {
342
- const lastPart = consolidatedParts[consolidatedParts.length - 1];
343
- if (
344
- lastPart?.text &&
345
- isValidNonThoughtTextPart(lastPart) &&
346
- isValidNonThoughtTextPart(part)
347
- ) {
348
- lastPart.text += part.text;
349
- } else {
350
- consolidatedParts.push(part);
351
- }
352
- }
353
-
354
- const responseText = consolidatedParts
355
- .filter((part) => part.text)
356
- .map((part) => part.text)
357
- .join('')
358
- .trim();
359
-
360
- if (!hasFinishReason || !responseText) {
361
- if (!hasFinishReason) {
362
- throw new InvalidStreamError(
363
- 'Model stream ended without a finish reason.',
364
- 'NO_FINISH_REASON',
365
- );
366
- } else {
367
- throw new InvalidStreamError(
368
- 'Model stream ended with empty response text.',
369
- 'NO_RESPONSE_TEXT',
370
- );
371
- }
372
- }
373
-
374
- this.history.push({ role: 'model', parts: consolidatedParts });
375
- }
376
- }
377
-
378
- /** Visible for Testing */
379
- export function isSchemaDepthError(errorMessage: string): boolean {
380
- return errorMessage.includes('maximum schema depth exceeded');
381
- }
382
-
383
- export function isInvalidArgumentError(errorMessage: string): boolean {
384
- return errorMessage.includes('Request contains an invalid argument');
385
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import { type PartListUnion } from '@google/genai';
8
- import { partToString } from '../utils/partUtils';
9
-
10
- /**
11
- * Represents a request to be sent to the Gemini API.
12
- * For now, it's an alias to PartListUnion as the primary content.
13
- * This can be expanded later to include other request parameters.
14
- */
15
- export type GeminiCodeRequest = PartListUnion;
16
-
17
- export function partListUnionToString(value: PartListUnion): string {
18
- return partToString(value, { verbose: true });
19
- }
@@ -1,109 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import path from 'node:path';
8
- import fs from 'node:fs';
9
- import os from 'node:os';
10
- import process from 'node:process';
11
-
12
- export const GEMINI_CONFIG_DIR = '.gemini';
13
-
14
- export function resolvePathFromEnv(envVar?: string): {
15
- isSwitch: boolean;
16
- value: string | null;
17
- isDisabled: boolean;
18
- } {
19
- // Handle the case where the environment variable is not set, empty, or just whitespace.
20
- const trimmedEnvVar = envVar?.trim();
21
- if (!trimmedEnvVar) {
22
- return { isSwitch: false, value: null, isDisabled: false };
23
- }
24
-
25
- const lowerEnvVar = trimmedEnvVar.toLowerCase();
26
- // Check if the input is a common boolean-like string.
27
- if (['0', 'false', '1', 'true'].includes(lowerEnvVar)) {
28
- // If so, identify it as a "switch" and return its value.
29
- const isDisabled = ['0', 'false'].includes(lowerEnvVar);
30
- return { isSwitch: true, value: lowerEnvVar, isDisabled };
31
- }
32
-
33
- // If it's not a switch, treat it as a potential file path.
34
- let customPath = trimmedEnvVar;
35
-
36
- // Safely expand the tilde (~) character to the user's home directory.
37
- if (customPath.startsWith('~/') || customPath === '~') {
38
- try {
39
- const home = os.homedir(); // This is the call that can throw an error.
40
- if (customPath === '~') {
41
- customPath = home;
42
- } else {
43
- customPath = path.join(home, customPath.slice(2));
44
- }
45
- } catch (error) {
46
- // If os.homedir() fails, we catch the error instead of crashing.
47
- console.warn(
48
- `Could not resolve home directory for path: ${trimmedEnvVar}`,
49
- error,
50
- );
51
- // Return null to indicate the path resolution failed.
52
- return { isSwitch: false, value: null, isDisabled: false };
53
- }
54
- }
55
-
56
- // Return it as a non-switch with the fully resolved absolute path.
57
- return {
58
- isSwitch: false,
59
- value: path.resolve(customPath),
60
- isDisabled: false,
61
- };
62
- }
63
-
64
- export function getCoreSystemPrompt(): string {
65
- // A flag to indicate whether the system prompt override is active.
66
- let systemMdEnabled = false;
67
- // The default path for the system prompt file. This can be overridden.
68
- let systemMdPath = path.resolve(path.join(GEMINI_CONFIG_DIR, 'system.md'));
69
- // Resolve the environment variable to get either a path or a switch value.
70
- const systemMdResolution = resolvePathFromEnv(
71
- process.env['GEMINI_SYSTEM_MD'],
72
- );
73
-
74
- // Proceed only if the environment variable is set and is not disabled.
75
- if (systemMdResolution.value && !systemMdResolution.isDisabled) {
76
- systemMdEnabled = true;
77
-
78
- // We update systemMdPath to this new custom path.
79
- if (!systemMdResolution.isSwitch) {
80
- systemMdPath = systemMdResolution.value;
81
- }
82
-
83
- // require file to exist when override is enabled
84
- if (!fs.existsSync(systemMdPath)) {
85
- throw new Error(`missing system prompt file '${systemMdPath}'`);
86
- }
87
- }
88
- const basePrompt = systemMdEnabled
89
- ? fs.readFileSync(systemMdPath, 'utf8')
90
- : `You are a helpful assistant.`;
91
-
92
- // if GEMINI_WRITE_SYSTEM_MD is set (and not 0|false), write base system prompt to file
93
- const writeSystemMdResolution = resolvePathFromEnv(
94
- process.env['GEMINI_WRITE_SYSTEM_MD'],
95
- );
96
-
97
- // Check if the feature is enabled. This proceeds only if the environment
98
- // variable is set and is not explicitly '0' or 'false'.
99
- if (writeSystemMdResolution.value && !writeSystemMdResolution.isDisabled) {
100
- const writePath = writeSystemMdResolution.isSwitch
101
- ? systemMdPath
102
- : writeSystemMdResolution.value;
103
-
104
- fs.mkdirSync(path.dirname(writePath), { recursive: true });
105
- fs.writeFileSync(writePath, basePrompt);
106
- }
107
-
108
- return basePrompt;
109
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2025 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- type Model = string;
8
- type TokenCount = number;
9
-
10
- export const DEFAULT_TOKEN_LIMIT = 1_048_576;
11
-
12
- export function tokenLimit(model: Model): TokenCount {
13
- // Add other models as they become relevant or if specified by config
14
- // Pulled from https://ai.google.dev/gemini-api/docs/models
15
- switch (model) {
16
- case 'gemini-1.5-pro':
17
- return 2_097_152;
18
- case 'gemini-1.5-flash':
19
- case 'gemini-2.5-pro-preview-05-06':
20
- case 'gemini-2.5-pro-preview-06-05':
21
- case 'gemini-2.5-pro':
22
- case 'gemini-2.5-flash-preview-05-20':
23
- case 'gemini-2.5-flash':
24
- case 'gemini-2.5-flash-lite':
25
- case 'gemini-2.0-flash':
26
- return 1_048_576;
27
- case 'gemini-2.0-flash-preview-image-generation':
28
- return 32_000;
29
- default:
30
- return DEFAULT_TOKEN_LIMIT;
31
- }
32
- }