@google/gemini-cli 0.21.2 → 0.22.0-preview.1

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 (167) hide show
  1. package/dist/package.json +3 -2
  2. package/dist/src/commands/extensions/disable.js.map +1 -1
  3. package/dist/src/commands/extensions/disable.test.js.map +1 -1
  4. package/dist/src/commands/extensions/enable.js.map +1 -1
  5. package/dist/src/commands/extensions/enable.test.js.map +1 -1
  6. package/dist/src/commands/extensions/examples/mcp-server/example.test.js +1 -1
  7. package/dist/src/commands/extensions/examples/mcp-server/example.test.js.map +1 -1
  8. package/dist/src/commands/extensions/examples/mcp-server/example.test.ts +4 -12
  9. package/dist/src/commands/extensions/link.test.js.map +1 -1
  10. package/dist/src/commands/extensions/list.test.js +0 -1
  11. package/dist/src/commands/extensions/list.test.js.map +1 -1
  12. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  13. package/dist/src/commands/extensions/uninstall.test.js.map +1 -1
  14. package/dist/src/commands/extensions/update.test.js.map +1 -1
  15. package/dist/src/config/config.js +2 -3
  16. package/dist/src/config/config.js.map +1 -1
  17. package/dist/src/config/config.test.js.map +1 -1
  18. package/dist/src/config/extension-manager.js +8 -9
  19. package/dist/src/config/extension-manager.js.map +1 -1
  20. package/dist/src/config/extension.test.js +4 -4
  21. package/dist/src/config/extension.test.js.map +1 -1
  22. package/dist/src/config/extensions/extensionSettings.js +1 -1
  23. package/dist/src/config/extensions/extensionSettings.js.map +1 -1
  24. package/dist/src/config/extensions/extensionSettings.test.js +27 -0
  25. package/dist/src/config/extensions/extensionSettings.test.js.map +1 -1
  26. package/dist/src/config/extensions/github_fetch.js.map +1 -1
  27. package/dist/src/config/settings-validation.d.ts +23 -0
  28. package/dist/src/config/settings-validation.js +249 -0
  29. package/dist/src/config/settings-validation.js.map +1 -0
  30. package/dist/src/config/settings-validation.test.js +322 -0
  31. package/dist/src/config/settings-validation.test.js.map +1 -0
  32. package/dist/src/config/settings.js +23 -2
  33. package/dist/src/config/settings.js.map +1 -1
  34. package/dist/src/config/settings.test.js +23 -0
  35. package/dist/src/config/settings.test.js.map +1 -1
  36. package/dist/src/config/settingsSchema.d.ts +9 -0
  37. package/dist/src/config/settingsSchema.js +9 -0
  38. package/dist/src/config/settingsSchema.js.map +1 -1
  39. package/dist/src/config/settingsSchema.test.js.map +1 -1
  40. package/dist/src/{utils/version.d.ts → config/settings_repro.test.d.ts} +1 -1
  41. package/dist/src/config/settings_repro.test.js +162 -0
  42. package/dist/src/config/settings_repro.test.js.map +1 -0
  43. package/dist/src/gemini.js +2 -3
  44. package/dist/src/gemini.js.map +1 -1
  45. package/dist/src/gemini.test.js +3 -5
  46. package/dist/src/gemini.test.js.map +1 -1
  47. package/dist/src/generated/git-commit.d.ts +2 -2
  48. package/dist/src/generated/git-commit.js +2 -2
  49. package/dist/src/generated/git-commit.js.map +1 -1
  50. package/dist/src/nonInteractiveCli.js +4 -4
  51. package/dist/src/nonInteractiveCli.js.map +1 -1
  52. package/dist/src/nonInteractiveCli.test.js +0 -2
  53. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  54. package/dist/src/services/FileCommandLoader.test.js.map +1 -1
  55. package/dist/src/ui/AppContainer.js +6 -0
  56. package/dist/src/ui/AppContainer.js.map +1 -1
  57. package/dist/src/ui/auth/ApiAuthDialog.js +36 -3
  58. package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -1
  59. package/dist/src/ui/auth/ApiAuthDialog.test.js +26 -2
  60. package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -1
  61. package/dist/src/ui/auth/AuthDialog.js +1 -2
  62. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  63. package/dist/src/ui/auth/AuthDialog.test.js +11 -2
  64. package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
  65. package/dist/src/ui/auth/useAuth.js +7 -4
  66. package/dist/src/ui/auth/useAuth.js.map +1 -1
  67. package/dist/src/ui/auth/useAuth.test.js +2 -2
  68. package/dist/src/ui/auth/useAuth.test.js.map +1 -1
  69. package/dist/src/ui/commands/aboutCommand.js +2 -3
  70. package/dist/src/ui/commands/aboutCommand.js.map +1 -1
  71. package/dist/src/ui/commands/aboutCommand.test.js +3 -6
  72. package/dist/src/ui/commands/aboutCommand.test.js.map +1 -1
  73. package/dist/src/ui/commands/bugCommand.js +2 -3
  74. package/dist/src/ui/commands/bugCommand.js.map +1 -1
  75. package/dist/src/ui/commands/bugCommand.test.js +3 -3
  76. package/dist/src/ui/commands/bugCommand.test.js.map +1 -1
  77. package/dist/src/ui/commands/chatCommand.test.js.map +1 -1
  78. package/dist/src/ui/commands/ideCommand.test.js.map +1 -1
  79. package/dist/src/ui/commands/initCommand.js +10 -50
  80. package/dist/src/ui/commands/initCommand.js.map +1 -1
  81. package/dist/src/ui/commands/mcpCommand.js +1 -2
  82. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  83. package/dist/src/ui/commands/restoreCommand.js +1 -3
  84. package/dist/src/ui/commands/restoreCommand.js.map +1 -1
  85. package/dist/src/ui/components/DialogManager.js +1 -1
  86. package/dist/src/ui/components/DialogManager.js.map +1 -1
  87. package/dist/src/ui/components/ModelStatsDisplay.js +1 -1
  88. package/dist/src/ui/components/ModelStatsDisplay.js.map +1 -1
  89. package/dist/src/ui/components/ModelStatsDisplay.test.js +7 -0
  90. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -1
  91. package/dist/src/ui/components/QuittingDisplay.test.js.map +1 -1
  92. package/dist/src/ui/components/SessionSummaryDisplay.test.js +1 -0
  93. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -1
  94. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  95. package/dist/src/ui/components/StatsDisplay.d.ts +1 -1
  96. package/dist/src/ui/components/StatsDisplay.js +11 -15
  97. package/dist/src/ui/components/StatsDisplay.js.map +1 -1
  98. package/dist/src/ui/components/StatsDisplay.test.js +41 -0
  99. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -1
  100. package/dist/src/ui/components/ToolStatsDisplay.js.map +1 -1
  101. package/dist/src/ui/components/messages/Todo.js.map +1 -1
  102. package/dist/src/ui/components/messages/ToolConfirmationMessage.js +20 -0
  103. package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  104. package/dist/src/ui/components/shared/VirtualizedList.js.map +1 -1
  105. package/dist/src/ui/components/shared/text-buffer.js +5 -3
  106. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  107. package/dist/src/ui/components/shared/text-buffer.test.js +25 -0
  108. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
  109. package/dist/src/ui/constants/tips.js +0 -1
  110. package/dist/src/ui/constants/tips.js.map +1 -1
  111. package/dist/src/ui/contexts/SessionContext.d.ts +1 -0
  112. package/dist/src/ui/contexts/SessionContext.js +2 -1
  113. package/dist/src/ui/contexts/SessionContext.js.map +1 -1
  114. package/dist/src/ui/contexts/SessionContext.test.js +3 -0
  115. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
  116. package/dist/src/ui/hooks/atCommandProcessor.d.ts +1 -1
  117. package/dist/src/ui/hooks/atCommandProcessor.js +25 -14
  118. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  119. package/dist/src/ui/hooks/atCommandProcessor.test.js +42 -33
  120. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  121. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +1 -1
  122. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  123. package/dist/src/ui/hooks/useGeminiStream.js +2 -1
  124. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  125. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
  126. package/dist/src/ui/hooks/useIncludeDirsTrust.test.js.map +1 -1
  127. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  128. package/dist/src/ui/hooks/useSelectionList.js.map +1 -1
  129. package/dist/src/ui/hooks/useToolScheduler.test.js +14 -4
  130. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  131. package/dist/src/ui/utils/clipboardUtils.d.ts +20 -0
  132. package/dist/src/ui/utils/clipboardUtils.js +82 -4
  133. package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
  134. package/dist/src/ui/utils/clipboardUtils.test.js +123 -1
  135. package/dist/src/ui/utils/clipboardUtils.test.js.map +1 -1
  136. package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
  137. package/dist/src/ui/utils/computeStats.js +2 -0
  138. package/dist/src/ui/utils/computeStats.js.map +1 -1
  139. package/dist/src/ui/utils/computeStats.test.js +9 -0
  140. package/dist/src/ui/utils/computeStats.test.js.map +1 -1
  141. package/dist/src/utils/deepMerge.js +7 -2
  142. package/dist/src/utils/deepMerge.js.map +1 -1
  143. package/dist/src/utils/deepMerge.test.js +21 -0
  144. package/dist/src/utils/deepMerge.test.js.map +1 -1
  145. package/dist/src/utils/envVarResolver.js.map +1 -1
  146. package/dist/src/utils/envVarResolver.test.js.map +1 -1
  147. package/dist/src/utils/errors.test.js +9 -1
  148. package/dist/src/utils/errors.test.js.map +1 -1
  149. package/dist/src/validateNonInterActiveAuth.js.map +1 -1
  150. package/dist/src/zed-integration/acp.test.js +8 -16
  151. package/dist/src/zed-integration/acp.test.js.map +1 -1
  152. package/dist/src/zed-integration/connection.js.map +1 -1
  153. package/dist/src/zed-integration/fileSystemService.d.ts +0 -1
  154. package/dist/src/zed-integration/fileSystemService.js +0 -3
  155. package/dist/src/zed-integration/fileSystemService.js.map +1 -1
  156. package/dist/src/zed-integration/fileSystemService.test.js +0 -8
  157. package/dist/src/zed-integration/fileSystemService.test.js.map +1 -1
  158. package/dist/src/zed-integration/zedIntegration.js +3 -2
  159. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  160. package/dist/tsconfig.tsbuildinfo +1 -1
  161. package/package.json +4 -3
  162. package/dist/google-gemini-cli-0.21.1.tgz +0 -0
  163. package/dist/src/utils/version.js +0 -15
  164. package/dist/src/utils/version.js.map +0 -1
  165. package/dist/src/utils/version.test.js +0 -39
  166. package/dist/src/utils/version.test.js.map +0 -1
  167. /package/dist/src/{utils/version.test.d.ts → config/settings-validation.test.d.ts} +0 -0
@@ -0,0 +1,249 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { z } from 'zod';
7
+ import { getSettingsSchema, SETTINGS_SCHEMA_DEFINITIONS, } from './settingsSchema.js';
8
+ // Helper to build Zod schema from the JSON-schema-like definitions
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ function buildZodSchemaFromJsonSchema(def) {
11
+ if (def.anyOf) {
12
+ return z.union(
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ def.anyOf.map((d) => buildZodSchemaFromJsonSchema(d)));
15
+ }
16
+ if (def.type === 'string') {
17
+ if (def.enum)
18
+ return z.enum(def.enum);
19
+ return z.string();
20
+ }
21
+ if (def.type === 'number')
22
+ return z.number();
23
+ if (def.type === 'boolean')
24
+ return z.boolean();
25
+ if (def.type === 'array') {
26
+ if (def.items) {
27
+ return z.array(buildZodSchemaFromJsonSchema(def.items));
28
+ }
29
+ return z.array(z.unknown());
30
+ }
31
+ if (def.type === 'object') {
32
+ let schema;
33
+ if (def.properties) {
34
+ const shape = {};
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ for (const [key, propDef] of Object.entries(def.properties)) {
37
+ let propSchema = buildZodSchemaFromJsonSchema(propDef);
38
+ if (def.required &&
39
+ Array.isArray(def.required) &&
40
+ def.required.includes(key)) {
41
+ // keep it required
42
+ }
43
+ else {
44
+ propSchema = propSchema.optional();
45
+ }
46
+ shape[key] = propSchema;
47
+ }
48
+ schema = z.object(shape).passthrough();
49
+ }
50
+ else {
51
+ schema = z.object({}).passthrough();
52
+ }
53
+ if (def.additionalProperties === false) {
54
+ schema = schema.strict();
55
+ }
56
+ else if (typeof def.additionalProperties === 'object') {
57
+ schema = schema.catchall(buildZodSchemaFromJsonSchema(def.additionalProperties));
58
+ }
59
+ return schema;
60
+ }
61
+ return z.unknown();
62
+ }
63
+ /**
64
+ * Builds a Zod enum schema from options array
65
+ */
66
+ function buildEnumSchema(options) {
67
+ if (!options || options.length === 0) {
68
+ throw new Error(`Enum type must have options defined. Check your settings schema definition.`);
69
+ }
70
+ const values = options.map((opt) => opt.value);
71
+ if (values.every((v) => typeof v === 'string')) {
72
+ return z.enum(values);
73
+ }
74
+ else if (values.every((v) => typeof v === 'number')) {
75
+ return z.union(values.map((v) => z.literal(v)));
76
+ }
77
+ else {
78
+ return z.union(values.map((v) => z.literal(v)));
79
+ }
80
+ }
81
+ /**
82
+ * Builds a Zod object shape from properties record
83
+ */
84
+ function buildObjectShapeFromProperties(properties) {
85
+ const shape = {};
86
+ for (const [key, childDef] of Object.entries(properties)) {
87
+ shape[key] = buildZodSchemaFromDefinition(childDef);
88
+ }
89
+ return shape;
90
+ }
91
+ /**
92
+ * Builds a Zod schema for primitive types (string, number, boolean)
93
+ */
94
+ function buildPrimitiveSchema(type) {
95
+ switch (type) {
96
+ case 'string':
97
+ return z.string();
98
+ case 'number':
99
+ return z.number();
100
+ case 'boolean':
101
+ return z.boolean();
102
+ default:
103
+ return z.unknown();
104
+ }
105
+ }
106
+ const REF_SCHEMAS = {};
107
+ // Initialize REF_SCHEMAS
108
+ for (const [name, def] of Object.entries(SETTINGS_SCHEMA_DEFINITIONS)) {
109
+ REF_SCHEMAS[name] = buildZodSchemaFromJsonSchema(def);
110
+ }
111
+ /**
112
+ * Recursively builds a Zod schema from a SettingDefinition
113
+ */
114
+ function buildZodSchemaFromDefinition(definition) {
115
+ let baseSchema;
116
+ // Special handling for TelemetrySettings which can be boolean or object
117
+ if (definition.ref === 'TelemetrySettings') {
118
+ const objectSchema = REF_SCHEMAS['TelemetrySettings'];
119
+ if (objectSchema) {
120
+ return z.union([z.boolean(), objectSchema]).optional();
121
+ }
122
+ }
123
+ // Handle refs using registry
124
+ if (definition.ref && definition.ref in REF_SCHEMAS) {
125
+ return REF_SCHEMAS[definition.ref].optional();
126
+ }
127
+ switch (definition.type) {
128
+ case 'string':
129
+ case 'number':
130
+ case 'boolean':
131
+ baseSchema = buildPrimitiveSchema(definition.type);
132
+ break;
133
+ case 'enum': {
134
+ baseSchema = buildEnumSchema(definition.options);
135
+ break;
136
+ }
137
+ case 'array':
138
+ if (definition.items) {
139
+ const itemSchema = buildZodSchemaFromCollection(definition.items);
140
+ baseSchema = z.array(itemSchema);
141
+ }
142
+ else {
143
+ baseSchema = z.array(z.unknown());
144
+ }
145
+ break;
146
+ case 'object':
147
+ if (definition.properties) {
148
+ const shape = buildObjectShapeFromProperties(definition.properties);
149
+ baseSchema = z.object(shape).passthrough();
150
+ if (definition.additionalProperties) {
151
+ const additionalSchema = buildZodSchemaFromCollection(definition.additionalProperties);
152
+ baseSchema = z.object(shape).catchall(additionalSchema);
153
+ }
154
+ }
155
+ else if (definition.additionalProperties) {
156
+ const valueSchema = buildZodSchemaFromCollection(definition.additionalProperties);
157
+ baseSchema = z.record(z.string(), valueSchema);
158
+ }
159
+ else {
160
+ baseSchema = z.record(z.string(), z.unknown());
161
+ }
162
+ break;
163
+ default:
164
+ baseSchema = z.unknown();
165
+ }
166
+ // Make all fields optional since settings are partial
167
+ return baseSchema.optional();
168
+ }
169
+ /**
170
+ * Builds a Zod schema from a SettingCollectionDefinition
171
+ */
172
+ function buildZodSchemaFromCollection(collection) {
173
+ if (collection.ref && collection.ref in REF_SCHEMAS) {
174
+ return REF_SCHEMAS[collection.ref];
175
+ }
176
+ switch (collection.type) {
177
+ case 'string':
178
+ case 'number':
179
+ case 'boolean':
180
+ return buildPrimitiveSchema(collection.type);
181
+ case 'enum': {
182
+ return buildEnumSchema(collection.options);
183
+ }
184
+ case 'array':
185
+ if (collection.properties) {
186
+ const shape = buildObjectShapeFromProperties(collection.properties);
187
+ return z.array(z.object(shape));
188
+ }
189
+ return z.array(z.unknown());
190
+ case 'object':
191
+ if (collection.properties) {
192
+ const shape = buildObjectShapeFromProperties(collection.properties);
193
+ return z.object(shape).passthrough();
194
+ }
195
+ return z.record(z.string(), z.unknown());
196
+ default:
197
+ return z.unknown();
198
+ }
199
+ }
200
+ /**
201
+ * Builds the complete Zod schema for Settings from SETTINGS_SCHEMA
202
+ */
203
+ function buildSettingsZodSchema() {
204
+ const schema = getSettingsSchema();
205
+ const shape = {};
206
+ for (const [key, definition] of Object.entries(schema)) {
207
+ shape[key] = buildZodSchemaFromDefinition(definition);
208
+ }
209
+ return z.object(shape).passthrough();
210
+ }
211
+ export const settingsZodSchema = buildSettingsZodSchema();
212
+ /**
213
+ * Validates settings data against the Zod schema
214
+ */
215
+ export function validateSettings(data) {
216
+ const result = settingsZodSchema.safeParse(data);
217
+ return result;
218
+ }
219
+ /**
220
+ * Format a Zod error into a helpful error message
221
+ */
222
+ export function formatValidationError(error, filePath) {
223
+ const lines = [];
224
+ lines.push(`Invalid configuration in ${filePath}:`);
225
+ lines.push('');
226
+ const MAX_ERRORS_TO_DISPLAY = 5;
227
+ const displayedIssues = error.issues.slice(0, MAX_ERRORS_TO_DISPLAY);
228
+ for (const issue of displayedIssues) {
229
+ const path = issue.path.reduce((acc, curr) => typeof curr === 'number'
230
+ ? `${acc}[${curr}]`
231
+ : `${acc ? acc + '.' : ''}${curr}`, '');
232
+ lines.push(`Error in: ${path || '(root)'}`);
233
+ lines.push(` ${issue.message}`);
234
+ if (issue.code === 'invalid_type') {
235
+ const expected = issue.expected;
236
+ const received = issue.received;
237
+ lines.push(`Expected: ${expected}, but received: ${received}`);
238
+ }
239
+ lines.push('');
240
+ }
241
+ if (error.issues.length > MAX_ERRORS_TO_DISPLAY) {
242
+ lines.push(`...and ${error.issues.length - MAX_ERRORS_TO_DISPLAY} more errors.`);
243
+ lines.push('');
244
+ }
245
+ lines.push('Please fix the configuration and try again.');
246
+ lines.push('See: https://github.com/google-gemini/gemini-cli/blob/main/docs/get-started/configuration.md');
247
+ return lines.join('\n');
248
+ }
249
+ //# sourceMappingURL=settings-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-validation.js","sourceRoot":"","sources":["../../../src/config/settings-validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,iBAAiB,EAGjB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAE7B,mEAAmE;AACnE,8DAA8D;AAC9D,SAAS,4BAA4B,CAAC,GAAQ;IAC5C,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,CAAC,CAAC,KAAK;QACZ,8DAA8D;QAC9D,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,CAC3D,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,GAAG,CAAC,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAA6B,CAAC,CAAC;QAC/D,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;IACpB,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7C,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IAE/C,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACzB,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,CAAC,KAAK,CAAC,4BAA4B,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,MAAM,CAAC;QACX,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnB,MAAM,KAAK,GAAiC,EAAE,CAAC;YAC/C,8DAA8D;YAC9D,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAQ,EAAE,CAAC;gBACnE,IAAI,UAAU,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC;gBACvD,IACE,GAAG,CAAC,QAAQ;oBACZ,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;oBAC3B,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAC1B,CAAC;oBACD,mBAAmB;gBACrB,CAAC;qBAAM,CAAC;oBACN,UAAU,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;gBACrC,CAAC;gBACD,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;YAC1B,CAAC;YACD,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACtC,CAAC;QAED,IAAI,GAAG,CAAC,oBAAoB,KAAK,KAAK,EAAE,CAAC;YACvC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QAC3B,CAAC;aAAM,IAAI,OAAO,GAAG,CAAC,oBAAoB,KAAK,QAAQ,EAAE,CAAC;YACxD,MAAM,GAAG,MAAM,CAAC,QAAQ,CACtB,4BAA4B,CAAC,GAAG,CAAC,oBAAoB,CAAC,CACvD,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CACtB,OAA2E;IAE3E,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,6EAA6E,CAC9E,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,CAAC,IAAI,CAAC,MAA+B,CAAC,CAAC;IACjD,CAAC;SAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,CAAC,KAAK,CACZ,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAI7B,CACF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,CAAC,KAAK,CACZ,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAI7B,CACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,8BAA8B,CACrC,UAA6C;IAE7C,MAAM,KAAK,GAAiC,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACzD,KAAK,CAAC,GAAG,CAAC,GAAG,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAC3B,IAAqC;IAErC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,QAAQ;YACX,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;QACrB;YACE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED,MAAM,WAAW,GAAiC,EAAE,CAAC;AAErD,yBAAyB;AACzB,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,2BAA2B,CAAC,EAAE,CAAC;IACtE,WAAW,CAAC,IAAI,CAAC,GAAG,4BAA4B,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,4BAA4B,CACnC,UAA6B;IAE7B,IAAI,UAAwB,CAAC;IAE7B,wEAAwE;IACxE,IAAI,UAAU,CAAC,GAAG,KAAK,mBAAmB,EAAE,CAAC;QAC3C,MAAM,YAAY,GAAG,WAAW,CAAC,mBAAmB,CAAC,CAAC;QACtD,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,IAAI,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,IAAI,WAAW,EAAE,CAAC;QACpD,OAAO,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IAChD,CAAC;IAED,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,UAAU,GAAG,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACnD,MAAM;QAER,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,UAAU,GAAG,eAAe,CAAC,UAAU,CAAC,OAAQ,CAAC,CAAC;YAClD,MAAM;QACR,CAAC;QAED,KAAK,OAAO;YACV,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;gBACrB,MAAM,UAAU,GAAG,4BAA4B,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAClE,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YACpC,CAAC;YACD,MAAM;QAER,KAAK,QAAQ;YACX,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,8BAA8B,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;gBACpE,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;gBAE3C,IAAI,UAAU,CAAC,oBAAoB,EAAE,CAAC;oBACpC,MAAM,gBAAgB,GAAG,4BAA4B,CACnD,UAAU,CAAC,oBAAoB,CAChC,CAAC;oBACF,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;gBAC1D,CAAC;YACH,CAAC;iBAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,CAAC;gBAC3C,MAAM,WAAW,GAAG,4BAA4B,CAC9C,UAAU,CAAC,oBAAoB,CAChC,CAAC;gBACF,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YACjD,CAAC;YACD,MAAM;QAER;YACE,UAAU,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED,sDAAsD;IACtD,OAAO,UAAU,CAAC,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAS,4BAA4B,CACnC,UAAuC;IAEvC,IAAI,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,IAAI,WAAW,EAAE,CAAC;QACpD,OAAO,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,oBAAoB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAE/C,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,OAAO,eAAe,CAAC,UAAU,CAAC,OAAQ,CAAC,CAAC;QAC9C,CAAC;QAED,KAAK,OAAO;YACV,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,8BAA8B,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;gBACpE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAClC,CAAC;YACD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAE9B,KAAK,QAAQ;YACX,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,8BAA8B,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;gBACpE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YACvC,CAAC;YACD,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAE3C;YACE,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB;IAC7B,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAiC,EAAE,CAAC;IAE/C,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,KAAK,CAAC,GAAG,CAAC,GAAG,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,EAAE,CAAC;AAE1D;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAa;IAK5C,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAiB,EACjB,QAAgB;IAEhB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,4BAA4B,QAAQ,GAAG,CAAC,CAAC;IACpD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,MAAM,qBAAqB,GAAG,CAAC,CAAC;IAChC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAErE,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAC5B,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CACZ,OAAO,IAAI,KAAK,QAAQ;YACtB,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,GAAG;YACnB,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,EACtC,EAAE,CACH,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAEnC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAChC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,aAAa,QAAQ,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAChD,KAAK,CAAC,IAAI,CACR,UAAU,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,qBAAqB,eAAe,CACrE,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAC1D,KAAK,CAAC,IAAI,CACR,8FAA8F,CAC/F,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,322 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /// <reference types="vitest/globals" />
7
+ import { describe, it, expect } from 'vitest';
8
+ import { validateSettings, formatValidationError, settingsZodSchema, } from './settings-validation.js';
9
+ import { z } from 'zod';
10
+ describe('settings-validation', () => {
11
+ describe('validateSettings', () => {
12
+ it('should accept valid settings with correct model.name as string', () => {
13
+ const validSettings = {
14
+ model: {
15
+ name: 'gemini-2.0-flash-exp',
16
+ maxSessionTurns: 10,
17
+ },
18
+ ui: {
19
+ theme: 'dark',
20
+ },
21
+ };
22
+ const result = validateSettings(validSettings);
23
+ expect(result.success).toBe(true);
24
+ });
25
+ it('should reject model.name as object instead of string', () => {
26
+ const invalidSettings = {
27
+ model: {
28
+ name: {
29
+ skipNextSpeakerCheck: true,
30
+ },
31
+ },
32
+ };
33
+ const result = validateSettings(invalidSettings);
34
+ expect(result.success).toBe(false);
35
+ expect(result.error).toBeDefined();
36
+ if (result.error) {
37
+ const issues = result.error.issues;
38
+ expect(issues.length).toBeGreaterThan(0);
39
+ expect(issues[0]?.path).toEqual(['model', 'name']);
40
+ expect(issues[0]?.code).toBe('invalid_type');
41
+ }
42
+ });
43
+ it('should accept valid model.summarizeToolOutput structure', () => {
44
+ const validSettings = {
45
+ model: {
46
+ summarizeToolOutput: {
47
+ run_shell_command: {
48
+ tokenBudget: 500,
49
+ },
50
+ },
51
+ },
52
+ };
53
+ const result = validateSettings(validSettings);
54
+ expect(result.success).toBe(true);
55
+ });
56
+ it('should reject invalid model.summarizeToolOutput structure', () => {
57
+ const invalidSettings = {
58
+ model: {
59
+ summarizeToolOutput: {
60
+ run_shell_command: {
61
+ tokenBudget: 500,
62
+ },
63
+ },
64
+ },
65
+ };
66
+ // First test with valid structure
67
+ let result = validateSettings(invalidSettings);
68
+ expect(result.success).toBe(true);
69
+ // Now test with wrong type (string instead of object)
70
+ const actuallyInvalidSettings = {
71
+ model: {
72
+ summarizeToolOutput: 'invalid',
73
+ },
74
+ };
75
+ result = validateSettings(actuallyInvalidSettings);
76
+ expect(result.success).toBe(false);
77
+ if (result.error) {
78
+ expect(result.error.issues.length).toBeGreaterThan(0);
79
+ }
80
+ });
81
+ it('should accept empty settings object', () => {
82
+ const emptySettings = {};
83
+ const result = validateSettings(emptySettings);
84
+ expect(result.success).toBe(true);
85
+ });
86
+ it('should accept unknown top-level keys (for migration compatibility)', () => {
87
+ const settingsWithUnknownKey = {
88
+ unknownKey: 'some value',
89
+ };
90
+ const result = validateSettings(settingsWithUnknownKey);
91
+ expect(result.success).toBe(true);
92
+ // Unknown keys are allowed via .passthrough() for migration scenarios
93
+ });
94
+ it('should accept nested valid settings', () => {
95
+ const validSettings = {
96
+ ui: {
97
+ theme: 'dark',
98
+ hideWindowTitle: true,
99
+ footer: {
100
+ hideCWD: false,
101
+ hideModelInfo: true,
102
+ },
103
+ },
104
+ tools: {
105
+ sandbox: 'inherit',
106
+ autoAccept: false,
107
+ },
108
+ };
109
+ const result = validateSettings(validSettings);
110
+ expect(result.success).toBe(true);
111
+ });
112
+ it('should validate array types correctly', () => {
113
+ const validSettings = {
114
+ tools: {
115
+ allowed: ['git', 'npm'],
116
+ exclude: ['dangerous-tool'],
117
+ },
118
+ context: {
119
+ includeDirectories: ['/path/1', '/path/2'],
120
+ },
121
+ };
122
+ const result = validateSettings(validSettings);
123
+ expect(result.success).toBe(true);
124
+ });
125
+ it('should reject invalid types in arrays', () => {
126
+ const invalidSettings = {
127
+ tools: {
128
+ allowed: ['git', 123],
129
+ },
130
+ };
131
+ const result = validateSettings(invalidSettings);
132
+ expect(result.success).toBe(false);
133
+ });
134
+ it('should validate boolean fields correctly', () => {
135
+ const validSettings = {
136
+ general: {
137
+ vimMode: true,
138
+ disableAutoUpdate: false,
139
+ },
140
+ };
141
+ const result = validateSettings(validSettings);
142
+ expect(result.success).toBe(true);
143
+ });
144
+ it('should reject non-boolean values for boolean fields', () => {
145
+ const invalidSettings = {
146
+ general: {
147
+ vimMode: 'yes',
148
+ },
149
+ };
150
+ const result = validateSettings(invalidSettings);
151
+ expect(result.success).toBe(false);
152
+ });
153
+ it('should validate number fields correctly', () => {
154
+ const validSettings = {
155
+ model: {
156
+ maxSessionTurns: 50,
157
+ compressionThreshold: 0.2,
158
+ },
159
+ };
160
+ const result = validateSettings(validSettings);
161
+ expect(result.success).toBe(true);
162
+ });
163
+ it('should validate complex nested mcpServers configuration', () => {
164
+ const invalidSettings = {
165
+ mcpServers: {
166
+ 'my-server': {
167
+ command: 123, // Should be string
168
+ args: ['arg1'],
169
+ env: {
170
+ VAR: 'value',
171
+ },
172
+ },
173
+ },
174
+ };
175
+ const result = validateSettings(invalidSettings);
176
+ expect(result.success).toBe(false);
177
+ if (result.error) {
178
+ expect(result.error.issues.length).toBeGreaterThan(0);
179
+ // Path should be mcpServers.my-server.command
180
+ const issue = result.error.issues.find((i) => i.path.includes('command'));
181
+ expect(issue).toBeDefined();
182
+ expect(issue?.code).toBe('invalid_type');
183
+ }
184
+ });
185
+ it('should validate complex nested customThemes configuration', () => {
186
+ const invalidSettings = {
187
+ ui: {
188
+ customThemes: {
189
+ 'my-theme': {
190
+ type: 'custom',
191
+ // Missing 'name' property which is required
192
+ text: {
193
+ primary: '#ffffff',
194
+ },
195
+ },
196
+ },
197
+ },
198
+ };
199
+ const result = validateSettings(invalidSettings);
200
+ expect(result.success).toBe(false);
201
+ if (result.error) {
202
+ expect(result.error.issues.length).toBeGreaterThan(0);
203
+ // Should complain about missing 'name'
204
+ const issue = result.error.issues.find((i) => i.code === 'invalid_type' && i.message.includes('Required'));
205
+ expect(issue).toBeDefined();
206
+ }
207
+ });
208
+ });
209
+ describe('formatValidationError', () => {
210
+ it('should format error with file path and helpful message for model.name', () => {
211
+ const invalidSettings = {
212
+ model: {
213
+ name: {
214
+ skipNextSpeakerCheck: true,
215
+ },
216
+ },
217
+ };
218
+ const result = validateSettings(invalidSettings);
219
+ expect(result.success).toBe(false);
220
+ if (result.error) {
221
+ const formatted = formatValidationError(result.error, '/path/to/settings.json');
222
+ expect(formatted).toContain('/path/to/settings.json');
223
+ expect(formatted).toContain('model.name');
224
+ expect(formatted).toContain('Expected: string, but received: object');
225
+ expect(formatted).toContain('Please fix the configuration and try again.');
226
+ expect(formatted).toContain('https://github.com/google-gemini/gemini-cli');
227
+ }
228
+ });
229
+ it('should format error for model.summarizeToolOutput', () => {
230
+ const invalidSettings = {
231
+ model: {
232
+ summarizeToolOutput: 'wrong type',
233
+ },
234
+ };
235
+ const result = validateSettings(invalidSettings);
236
+ expect(result.success).toBe(false);
237
+ if (result.error) {
238
+ const formatted = formatValidationError(result.error, '~/.gemini/settings.json');
239
+ expect(formatted).toContain('~/.gemini/settings.json');
240
+ expect(formatted).toContain('model.summarizeToolOutput');
241
+ }
242
+ });
243
+ it('should include link to documentation', () => {
244
+ const invalidSettings = {
245
+ model: {
246
+ name: { invalid: 'object' }, // model.name should be a string
247
+ },
248
+ };
249
+ const result = validateSettings(invalidSettings);
250
+ expect(result.success).toBe(false);
251
+ if (result.error) {
252
+ const formatted = formatValidationError(result.error, 'test.json');
253
+ expect(formatted).toContain('https://github.com/google-gemini/gemini-cli');
254
+ expect(formatted).toContain('configuration.md');
255
+ }
256
+ });
257
+ it('should list all validation errors', () => {
258
+ const invalidSettings = {
259
+ model: {
260
+ name: { invalid: 'object' },
261
+ maxSessionTurns: 'not a number',
262
+ },
263
+ };
264
+ const result = validateSettings(invalidSettings);
265
+ expect(result.success).toBe(false);
266
+ if (result.error) {
267
+ const formatted = formatValidationError(result.error, 'test.json');
268
+ // Should have multiple errors listed
269
+ expect(formatted.match(/Error in:/g)?.length).toBeGreaterThan(1);
270
+ }
271
+ });
272
+ it('should format array paths correctly (e.g. tools.allowed[0])', () => {
273
+ const invalidSettings = {
274
+ tools: {
275
+ allowed: ['git', 123], // 123 is invalid, expected string
276
+ },
277
+ };
278
+ const result = validateSettings(invalidSettings);
279
+ expect(result.success).toBe(false);
280
+ if (result.error) {
281
+ const formatted = formatValidationError(result.error, 'test.json');
282
+ expect(formatted).toContain('tools.allowed[1]');
283
+ }
284
+ });
285
+ it('should limit the number of displayed errors', () => {
286
+ const invalidSettings = {
287
+ tools: {
288
+ // Create 6 invalid items to trigger the limit
289
+ allowed: [1, 2, 3, 4, 5, 6],
290
+ },
291
+ };
292
+ const result = validateSettings(invalidSettings);
293
+ expect(result.success).toBe(false);
294
+ if (result.error) {
295
+ const formatted = formatValidationError(result.error, 'test.json');
296
+ // Should see the first 5
297
+ expect(formatted).toContain('tools.allowed[0]');
298
+ expect(formatted).toContain('tools.allowed[4]');
299
+ // Should NOT see the 6th
300
+ expect(formatted).not.toContain('tools.allowed[5]');
301
+ // Should see the summary
302
+ expect(formatted).toContain('...and 1 more errors.');
303
+ }
304
+ });
305
+ });
306
+ describe('settingsZodSchema', () => {
307
+ it('should be a valid Zod object schema', () => {
308
+ expect(settingsZodSchema).toBeInstanceOf(z.ZodObject);
309
+ });
310
+ it('should have optional fields', () => {
311
+ // All top-level fields should be optional
312
+ const shape = settingsZodSchema.shape;
313
+ expect(shape['model']).toBeDefined();
314
+ expect(shape['ui']).toBeDefined();
315
+ expect(shape['tools']).toBeDefined();
316
+ // Test that empty object is valid (all fields optional)
317
+ const result = settingsZodSchema.safeParse({});
318
+ expect(result.success).toBe(true);
319
+ });
320
+ });
321
+ });
322
+ //# sourceMappingURL=settings-validation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-validation.test.js","sourceRoot":"","sources":["../../../src/config/settings-validation.test.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,7 @@ import { getSettingsSchema, } from './settingsSchema.js';
17
17
  import { resolveEnvVarsInObject } from '../utils/envVarResolver.js';
18
18
  import { customDeepMerge } from '../utils/deepMerge.js';
19
19
  import { updateSettingsFilePreservingFormat } from '../utils/commentJson.js';
20
+ import { validateSettings, formatValidationError, } from './settings-validation.js';
20
21
  import { SettingPaths } from './settingPaths.js';
21
22
  function getMergeStrategyForPath(path) {
22
23
  let current = undefined;
@@ -184,7 +185,7 @@ export function needsMigration(settings) {
184
185
  if (v1Key === v2Path || !(v1Key in settings)) {
185
186
  return false;
186
187
  }
187
- // If a key exists that is both a V1 key and a V2 container (like 'model'),
188
+ // If a key exists that is a V1 key and a V2 container (like 'model'),
188
189
  // we need to check the type. If it's an object, it's a V2 container and not
189
190
  // a V1 key that needs migration.
190
191
  if (KNOWN_V2_CONTAINERS.has(v1Key) &&
@@ -204,6 +205,16 @@ function migrateSettingsToV2(flatSettings) {
204
205
  const flatKeys = new Set(Object.keys(flatSettings));
205
206
  for (const [oldKey, newPath] of Object.entries(MIGRATION_MAP)) {
206
207
  if (flatKeys.has(oldKey)) {
208
+ // If the key exists and is a V2 container (like 'model'), and the value is an object,
209
+ // it is likely already migrated or partially migrated. We should not move it
210
+ // to the mapped V2 path (e.g. 'model' -> 'model.name').
211
+ // Instead, let it fall through to the "Carry over" section to be merged.
212
+ if (KNOWN_V2_CONTAINERS.has(oldKey) &&
213
+ typeof flatSettings[oldKey] === 'object' &&
214
+ flatSettings[oldKey] !== null &&
215
+ !Array.isArray(flatSettings[oldKey])) {
216
+ continue;
217
+ }
207
218
  setNestedProperty(v2Settings, newPath, flatSettings[oldKey]);
208
219
  flatKeys.delete(oldKey);
209
220
  }
@@ -224,7 +235,7 @@ function migrateSettingsToV2(flatSettings) {
224
235
  newValue !== null &&
225
236
  !Array.isArray(newValue)) {
226
237
  const pathAwareGetStrategy = (path) => getMergeStrategyForPath([remainingKey, ...path]);
227
- v2Settings[remainingKey] = customDeepMerge(pathAwareGetStrategy, {}, newValue, existingValue);
238
+ v2Settings[remainingKey] = customDeepMerge(pathAwareGetStrategy, {}, existingValue, newValue);
228
239
  }
229
240
  else {
230
241
  v2Settings[remainingKey] = newValue;
@@ -481,10 +492,20 @@ export function loadSettings(workspaceDir = process.cwd()) {
481
492
  settingsObject = migratedSettings;
482
493
  }
483
494
  }
495
+ // Validate settings structure with Zod after migration
496
+ const validationResult = validateSettings(settingsObject);
497
+ if (!validationResult.success && validationResult.error) {
498
+ const errorMessage = formatValidationError(validationResult.error, filePath);
499
+ throw new FatalConfigError(errorMessage);
500
+ }
484
501
  return { settings: settingsObject, rawJson: content };
485
502
  }
486
503
  }
487
504
  catch (error) {
505
+ // Preserve FatalConfigError with formatted validation messages
506
+ if (error instanceof FatalConfigError) {
507
+ throw error;
508
+ }
488
509
  settingsErrors.push({
489
510
  message: getErrorMessage(error),
490
511
  path: filePath,