klaude-code 2.7.0__py3-none-any.whl → 2.8.0__py3-none-any.whl

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 (69) hide show
  1. klaude_code/auth/AGENTS.md +325 -0
  2. klaude_code/auth/__init__.py +17 -1
  3. klaude_code/auth/antigravity/__init__.py +20 -0
  4. klaude_code/auth/antigravity/exceptions.py +17 -0
  5. klaude_code/auth/antigravity/oauth.py +320 -0
  6. klaude_code/auth/antigravity/pkce.py +25 -0
  7. klaude_code/auth/antigravity/token_manager.py +45 -0
  8. klaude_code/auth/base.py +4 -0
  9. klaude_code/auth/claude/oauth.py +29 -9
  10. klaude_code/auth/codex/exceptions.py +4 -0
  11. klaude_code/cli/auth_cmd.py +53 -3
  12. klaude_code/cli/cost_cmd.py +83 -160
  13. klaude_code/cli/list_model.py +50 -0
  14. klaude_code/cli/main.py +1 -1
  15. klaude_code/config/assets/builtin_config.yaml +108 -0
  16. klaude_code/config/builtin_config.py +5 -11
  17. klaude_code/config/config.py +24 -10
  18. klaude_code/const.py +1 -0
  19. klaude_code/core/agent.py +5 -1
  20. klaude_code/core/agent_profile.py +28 -32
  21. klaude_code/core/compaction/AGENTS.md +112 -0
  22. klaude_code/core/compaction/__init__.py +11 -0
  23. klaude_code/core/compaction/compaction.py +707 -0
  24. klaude_code/core/compaction/overflow.py +30 -0
  25. klaude_code/core/compaction/prompts.py +97 -0
  26. klaude_code/core/executor.py +103 -2
  27. klaude_code/core/manager/llm_clients.py +5 -0
  28. klaude_code/core/manager/llm_clients_builder.py +14 -2
  29. klaude_code/core/prompts/prompt-antigravity.md +80 -0
  30. klaude_code/core/prompts/prompt-codex-gpt-5-2.md +335 -0
  31. klaude_code/core/reminders.py +7 -2
  32. klaude_code/core/task.py +126 -0
  33. klaude_code/core/tool/todo/todo_write_tool.py +1 -1
  34. klaude_code/core/turn.py +3 -1
  35. klaude_code/llm/antigravity/__init__.py +3 -0
  36. klaude_code/llm/antigravity/client.py +558 -0
  37. klaude_code/llm/antigravity/input.py +261 -0
  38. klaude_code/llm/registry.py +1 -0
  39. klaude_code/protocol/events.py +18 -0
  40. klaude_code/protocol/llm_param.py +1 -0
  41. klaude_code/protocol/message.py +23 -1
  42. klaude_code/protocol/op.py +15 -1
  43. klaude_code/protocol/op_handler.py +5 -0
  44. klaude_code/session/session.py +36 -0
  45. klaude_code/skill/assets/create-plan/SKILL.md +6 -6
  46. klaude_code/tui/command/__init__.py +3 -0
  47. klaude_code/tui/command/compact_cmd.py +32 -0
  48. klaude_code/tui/command/fork_session_cmd.py +110 -14
  49. klaude_code/tui/command/model_picker.py +5 -1
  50. klaude_code/tui/command/thinking_cmd.py +1 -1
  51. klaude_code/tui/commands.py +6 -0
  52. klaude_code/tui/components/rich/markdown.py +57 -1
  53. klaude_code/tui/components/rich/theme.py +10 -2
  54. klaude_code/tui/components/tools.py +39 -25
  55. klaude_code/tui/components/user_input.py +1 -1
  56. klaude_code/tui/input/__init__.py +5 -2
  57. klaude_code/tui/input/drag_drop.py +6 -57
  58. klaude_code/tui/input/key_bindings.py +10 -0
  59. klaude_code/tui/input/prompt_toolkit.py +19 -6
  60. klaude_code/tui/machine.py +25 -0
  61. klaude_code/tui/renderer.py +67 -4
  62. klaude_code/tui/runner.py +18 -2
  63. klaude_code/tui/terminal/image.py +72 -10
  64. klaude_code/tui/terminal/selector.py +31 -7
  65. {klaude_code-2.7.0.dist-info → klaude_code-2.8.0.dist-info}/METADATA +1 -1
  66. {klaude_code-2.7.0.dist-info → klaude_code-2.8.0.dist-info}/RECORD +68 -52
  67. klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -117
  68. {klaude_code-2.7.0.dist-info → klaude_code-2.8.0.dist-info}/WHEEL +0 -0
  69. {klaude_code-2.7.0.dist-info → klaude_code-2.8.0.dist-info}/entry_points.txt +0 -0
@@ -6,12 +6,14 @@ provider_list:
6
6
  protocol: anthropic
7
7
  api_key: ${ANTHROPIC_API_KEY}
8
8
  model_list:
9
+
9
10
  - model_name: sonnet
10
11
  model_id: claude-sonnet-4-5-20250929
11
12
  context_limit: 200000
12
13
  provider_routing:
13
14
  sort: throughput
14
15
  cost: {input: 3, output: 15, cache_read: 0.3, cache_write: 3.75}
16
+
15
17
  - model_name: opus
16
18
  model_id: claude-opus-4-5-20251101
17
19
  context_limit: 200000
@@ -20,10 +22,13 @@ provider_list:
20
22
  type: enabled
21
23
  budget_tokens: 2048
22
24
  cost: {input: 5, output: 25, cache_read: 0.5, cache_write: 6.25}
25
+
26
+
23
27
  - provider_name: openai
24
28
  protocol: responses
25
29
  api_key: ${OPENAI_API_KEY}
26
30
  model_list:
31
+
27
32
  - model_name: gpt-5.2-high
28
33
  model_id: gpt-5.2
29
34
  max_tokens: 128000
@@ -33,6 +38,7 @@ provider_list:
33
38
  reasoning_effort: high
34
39
  reasoning_summary: detailed
35
40
  cost: {input: 1.75, output: 14, cache_read: 0.17}
41
+
36
42
  - model_name: gpt-5.2-medium
37
43
  model_id: gpt-5.2
38
44
  context_limit: 400000
@@ -41,6 +47,7 @@ provider_list:
41
47
  reasoning_effort: medium
42
48
  reasoning_summary: concise
43
49
  cost: {input: 1.75, output: 14, cache_read: 0.17}
50
+
44
51
  - model_name: gpt-5.2-low
45
52
  model_id: gpt-5.2
46
53
  context_limit: 400000
@@ -49,6 +56,7 @@ provider_list:
49
56
  reasoning_effort: low
50
57
  reasoning_summary: concise
51
58
  cost: {input: 1.75, output: 14, cache_read: 0.17}
59
+
52
60
  - model_name: gpt-5.2-fast
53
61
  model_id: gpt-5.2
54
62
  context_limit: 400000
@@ -56,6 +64,7 @@ provider_list:
56
64
  thinking:
57
65
  reasoning_effort: none
58
66
  cost: {input: 1.75, output: 14, cache_read: 0.17}
67
+
59
68
  - model_name: gpt-5.1-codex-max
60
69
  model_id: gpt-5.1-codex-max
61
70
  max_tokens: 128000
@@ -64,10 +73,13 @@ provider_list:
64
73
  reasoning_effort: medium
65
74
  reasoning_summary: detailed
66
75
  cost: {input: 1.25, output: 10, cache_read: 0.13}
76
+
77
+
67
78
  - provider_name: openrouter
68
79
  protocol: openrouter
69
80
  api_key: ${OPENROUTER_API_KEY}
70
81
  model_list:
82
+
71
83
  - model_name: gpt-5.2-high
72
84
  model_id: openai/gpt-5.2
73
85
  max_tokens: 128000
@@ -77,6 +89,7 @@ provider_list:
77
89
  reasoning_effort: high
78
90
  reasoning_summary: detailed
79
91
  cost: {input: 1.75, output: 14, cache_read: 0.17}
92
+
80
93
  - model_name: gpt-5.2-medium
81
94
  model_id: openai/gpt-5.2
82
95
  max_tokens: 128000
@@ -86,6 +99,7 @@ provider_list:
86
99
  reasoning_effort: medium
87
100
  reasoning_summary: concise
88
101
  cost: {input: 1.75, output: 14, cache_read: 0.17}
102
+
89
103
  - model_name: kimi
90
104
  model_id: moonshotai/kimi-k2-thinking
91
105
  context_limit: 262144
@@ -93,16 +107,19 @@ provider_list:
93
107
  only:
94
108
  - moonshotai/turbo
95
109
  cost: {input: 0.6, output: 2.5, cache_read: 0.15}
110
+
96
111
  - model_name: haiku
97
112
  model_id: anthropic/claude-haiku-4.5
98
113
  context_limit: 200000
99
114
  cost: {input: 1, output: 5, cache_read: 0.1, cache_write: 1.25}
115
+
100
116
  - model_name: sonnet
101
117
  model_id: anthropic/claude-4.5-sonnet
102
118
  context_limit: 200000
103
119
  provider_routing:
104
120
  sort: throughput
105
121
  cost: {input: 3, output: 15, cache_read: 0.3, cache_write: 3.75}
122
+
106
123
  - model_name: opus
107
124
  model_id: anthropic/claude-4.5-opus
108
125
  context_limit: 200000
@@ -110,18 +127,21 @@ provider_list:
110
127
  type: enabled
111
128
  budget_tokens: 2048
112
129
  cost: {input: 5, output: 25, cache_read: 0.5, cache_write: 6.25}
130
+
113
131
  - model_name: gemini-pro
114
132
  model_id: google/gemini-3-pro-preview
115
133
  context_limit: 1048576
116
134
  thinking:
117
135
  reasoning_effort: high
118
136
  cost: {input: 2, output: 12, cache_read: 0.2}
137
+
119
138
  - model_name: gemini-flash
120
139
  model_id: google/gemini-3-flash-preview
121
140
  context_limit: 1048576
122
141
  thinking:
123
142
  reasoning_effort: medium
124
143
  cost: {input: 0.5, output: 3, cache_read: 0.05}
144
+
125
145
  - model_name: nano-banana-pro
126
146
  model_id: google/gemini-3-pro-image-preview
127
147
  context_limit: 66000
@@ -129,6 +149,7 @@ provider_list:
129
149
  - image
130
150
  - text
131
151
  cost: {input: 2, output: 12, cache_read: 0.2, image: 120}
152
+
132
153
  - model_name: nano-banana
133
154
  model_id: google/gemini-2.5-flash-image
134
155
  context_limit: 33000
@@ -136,6 +157,7 @@ provider_list:
136
157
  - image
137
158
  - text
138
159
  cost: {input: 0.3, output: 2.5, cache_read: 0.03, image: 30}
160
+
139
161
  - model_name: grok
140
162
  model_id: x-ai/grok-4.1-fast
141
163
  context_limit: 2000000
@@ -143,10 +165,12 @@ provider_list:
143
165
  type: enabled
144
166
  budget_tokens: 2048
145
167
  cost: {input: 0.2, output: 0.5, cache_read: 0.05}
168
+
146
169
  - model_name: minimax
147
170
  model_id: minimax/minimax-m2.1
148
171
  context_limit: 204800
149
172
  cost: {input: 0.3, output: 1.2, cache_read: 0.03}
173
+
150
174
  - model_name: glm
151
175
  model_id: z-ai/glm-4.7
152
176
  context_limit: 200000
@@ -154,6 +178,7 @@ provider_list:
154
178
  only:
155
179
  - z-ai
156
180
  cost: {input: 0.44, output: 1.74, cache_read: 0.04}
181
+
157
182
  - model_name: seedream
158
183
  model_id: bytedance-seed/seedream-4.5
159
184
  context_limit: 4000
@@ -161,6 +186,7 @@ provider_list:
161
186
  modalities:
162
187
  - image
163
188
  - text
189
+
164
190
  - model_name: flux
165
191
  model_id: black-forest-labs/flux.2-max
166
192
  context_limit: 47000
@@ -168,22 +194,27 @@ provider_list:
168
194
  modalities:
169
195
  - image
170
196
  - text
197
+
198
+
171
199
  - provider_name: google
172
200
  protocol: google
173
201
  api_key: ${GOOGLE_API_KEY}
174
202
  model_list:
203
+
175
204
  - model_name: gemini-pro
176
205
  model_id: gemini-3-pro-preview
177
206
  context_limit: 1048576
178
207
  thinking:
179
208
  reasoning_effort: high
180
209
  cost: {input: 2, output: 12, cache_read: 0.2}
210
+
181
211
  - model_name: gemini-flash
182
212
  model_id: gemini-3-flash-preview
183
213
  context_limit: 1048576
184
214
  thinking:
185
215
  reasoning_effort: medium
186
216
  cost: {input: 0.5, output: 3, cache_read: 0.05}
217
+
187
218
  - model_name: nano-banana-pro
188
219
  model_id: gemini-3-pro-image-preview
189
220
  context_limit: 66000
@@ -191,6 +222,7 @@ provider_list:
191
222
  - image
192
223
  - text
193
224
  cost: {input: 2, output: 12, cache_read: 0.2, image: 120}
225
+
194
226
  - model_name: nano-banana
195
227
  model_id: gemini-2.5-flash-image
196
228
  context_limit: 33000
@@ -198,21 +230,27 @@ provider_list:
198
230
  - image
199
231
  - text
200
232
  cost: {input: 0.3, output: 2.5, cache_read: 0.03, image: 30}
233
+
234
+
201
235
  - provider_name: bedrock
202
236
  protocol: bedrock
203
237
  aws_access_key: ${AWS_ACCESS_KEY_ID}
204
238
  aws_secret_key: ${AWS_SECRET_ACCESS_KEY}
205
239
  aws_region: ${AWS_REGION}
206
240
  model_list:
241
+
207
242
  - model_name: sonnet
208
243
  model_id: us.anthropic.claude-sonnet-4-5-20250929-v1:0
209
244
  context_limit: 200000
210
245
  cost: {input: 3, output: 15, cache_read: 0.3, cache_write: 3.75}
246
+
247
+
211
248
  - provider_name: deepseek
212
249
  protocol: anthropic
213
250
  api_key: ${DEEPSEEK_API_KEY}
214
251
  base_url: https://api.deepseek.com/anthropic
215
252
  model_list:
253
+
216
254
  - model_name: deepseek
217
255
  model_id: deepseek-reasoner
218
256
  context_limit: 128000
@@ -220,11 +258,14 @@ provider_list:
220
258
  type: enabled
221
259
  budget_tokens: 2048
222
260
  cost: {input: 2, output: 3, cache_read: 0.2, currency: CNY}
261
+
262
+
223
263
  - provider_name: moonshot
224
264
  protocol: anthropic
225
265
  api_key: ${MOONSHOT_API_KEY}
226
266
  base_url: https://api.moonshot.cn/anthropic
227
267
  model_list:
268
+
228
269
  - model_name: kimi
229
270
  model_id: kimi-k2-thinking
230
271
  context_limit: 262144
@@ -232,13 +273,17 @@ provider_list:
232
273
  type: enabled
233
274
  budget_tokens: 8192
234
275
  cost: {input: 4, output: 16, cache_read: 1, currency: CNY}
276
+
277
+
235
278
  - provider_name: claude-max
236
279
  protocol: claude_oauth
237
280
  model_list:
281
+
238
282
  - model_name: sonnet
239
283
  model_id: claude-sonnet-4-5-20250929
240
284
  context_limit: 200000
241
285
  cost: {input: 3, output: 15, cache_read: 0.3, cache_write: 3.75}
286
+
242
287
  - model_name: opus
243
288
  model_id: claude-opus-4-5-20251101
244
289
  context_limit: 200000
@@ -246,17 +291,80 @@ provider_list:
246
291
  type: enabled
247
292
  budget_tokens: 2048
248
293
  cost: {input: 5, output: 25, cache_read: 0.5, cache_write: 6.25}
294
+
249
295
  - model_name: haiku
250
296
  model_id: claude-haiku-4-5-20251001
251
297
  context_limit: 200000
252
298
  cost: {input: 1, output: 5, cache_read: 0.1, cache_write: 1.25}
299
+
300
+
253
301
  - provider_name: codex
254
302
  protocol: codex_oauth
255
303
  model_list:
304
+
256
305
  - model_name: gpt-5.2-codex
257
306
  model_id: gpt-5.2-codex
258
307
  thinking:
259
308
  reasoning_effort: medium
309
+ reasoning_summary: auto
260
310
  context_limit: 400000
261
311
  max_tokens: 128000
262
312
  cost: {input: 1.75, output: 14, cache_read: 0.17}
313
+
314
+ - model_name: gpt-5.2-high
315
+ model_id: gpt-5.2
316
+ max_tokens: 128000
317
+ context_limit: 400000
318
+ verbosity: high
319
+ thinking:
320
+ reasoning_effort: high
321
+ reasoning_summary: detailed
322
+ cost: {input: 1.75, output: 14, cache_read: 0.17}
323
+
324
+
325
+ - provider_name: antigravity
326
+ protocol: antigravity
327
+ model_list:
328
+ - model_name: opus
329
+ model_id: claude-opus-4-5-thinking
330
+ context_limit: 200000
331
+ max_tokens: 64000
332
+ thinking:
333
+ type: enabled
334
+ budget_tokens: 10240
335
+ - model_name: sonnet
336
+ model_id: claude-sonnet-4-5
337
+ context_limit: 200000
338
+ max_tokens: 64000
339
+ - model_name: sonnet-thinking
340
+ model_id: claude-sonnet-4-5-thinking
341
+ context_limit: 200000
342
+ max_tokens: 64000
343
+ thinking:
344
+ type: enabled
345
+ budget_tokens: 10240
346
+ - model_name: gemini-pro-high
347
+ model_id: gemini-3-pro-high
348
+ context_limit: 1048576
349
+ max_tokens: 65535
350
+ thinking:
351
+ reasoning_effort: high
352
+ - model_name: gemini-pro-low
353
+ model_id: gemini-3-pro-low
354
+ context_limit: 1048576
355
+ max_tokens: 65535
356
+ thinking:
357
+ reasoning_effort: low
358
+ - model_name: gemini-flash
359
+ model_id: gemini-3-flash
360
+ context_limit: 1048576
361
+ max_tokens: 65535
362
+ thinking:
363
+ reasoning_effort: medium
364
+ - model_name: gpt-oss
365
+ model_id: gpt-oss-120b-medium
366
+ context_limit: 131072
367
+ max_tokens: 32768
368
+
369
+
370
+ compact_model: gemini-flash
@@ -13,7 +13,7 @@ from typing import TYPE_CHECKING, Any
13
13
  import yaml
14
14
 
15
15
  if TYPE_CHECKING:
16
- from klaude_code.config.config import ProviderConfig
16
+ from klaude_code.config.config import Config
17
17
 
18
18
 
19
19
  @dataclass(frozen=True)
@@ -48,16 +48,10 @@ def _load_builtin_yaml() -> dict[str, Any]:
48
48
  return data
49
49
 
50
50
 
51
- def get_builtin_provider_configs() -> list["ProviderConfig"]:
52
- """Load built-in provider configurations from YAML asset."""
51
+ def get_builtin_config() -> "Config":
52
+ """Load built-in configuration from YAML asset."""
53
53
  # Import here to avoid circular import
54
- from klaude_code.config.config import ProviderConfig
54
+ from klaude_code.config.config import Config
55
55
 
56
56
  data = _load_builtin_yaml()
57
- return [ProviderConfig.model_validate(p) for p in data.get("provider_list", [])]
58
-
59
-
60
- def get_builtin_sub_agent_models() -> dict[str, str]:
61
- """Load built-in sub agent model mappings from YAML asset."""
62
- data = _load_builtin_yaml()
63
- return data.get("sub_agent_models", {})
57
+ return Config.model_validate(data)
@@ -11,8 +11,7 @@ from pydantic import BaseModel, Field, ValidationError, model_validator
11
11
  from klaude_code.auth.env import get_auth_env
12
12
  from klaude_code.config.builtin_config import (
13
13
  SUPPORTED_API_KEYS,
14
- get_builtin_provider_configs,
15
- get_builtin_sub_agent_models,
14
+ get_builtin_config,
16
15
  )
17
16
  from klaude_code.log import log
18
17
  from klaude_code.protocol import llm_param
@@ -108,6 +107,15 @@ class ProviderConfig(llm_param.LLMConfigProviderParameter):
108
107
  # Consider available if logged in. Token refresh happens on-demand.
109
108
  return state is None
110
109
 
110
+ if self.protocol == LLMClientProtocol.ANTIGRAVITY:
111
+ # Antigravity uses OAuth authentication, not API key
112
+ from klaude_code.auth.antigravity.token_manager import AntigravityTokenManager
113
+
114
+ token_manager = AntigravityTokenManager()
115
+ state = token_manager.get_state()
116
+ # Consider available if logged in. Token refresh happens on-demand.
117
+ return state is None
118
+
111
119
  if self.protocol == LLMClientProtocol.BEDROCK:
112
120
  # Bedrock uses AWS credentials, not API key. Region is always required.
113
121
  _, resolved_profile = parse_env_var_syntax(self.aws_profile)
@@ -161,6 +169,7 @@ class UserConfig(BaseModel):
161
169
  """User configuration (what gets saved to disk)."""
162
170
 
163
171
  main_model: str | None = None
172
+ compact_model: str | None = None
164
173
  sub_agent_models: dict[str, str] = Field(default_factory=dict)
165
174
  theme: str | None = None
166
175
  provider_list: list[UserProviderConfig] = Field(default_factory=lambda: [])
@@ -183,6 +192,7 @@ class Config(BaseModel):
183
192
  """Merged configuration (builtin + user) for runtime use."""
184
193
 
185
194
  main_model: str | None = None
195
+ compact_model: str | None = None
186
196
  sub_agent_models: dict[str, str] = Field(default_factory=dict)
187
197
  theme: str | None = None
188
198
  provider_list: list[ProviderConfig] = Field(default_factory=lambda: [])
@@ -286,6 +296,7 @@ class Config(BaseModel):
286
296
  not in {
287
297
  llm_param.LLMClientProtocol.CODEX_OAUTH,
288
298
  llm_param.LLMClientProtocol.CLAUDE_OAUTH,
299
+ llm_param.LLMClientProtocol.ANTIGRAVITY,
289
300
  llm_param.LLMClientProtocol.BEDROCK,
290
301
  }
291
302
  and not api_key
@@ -313,6 +324,7 @@ class Config(BaseModel):
313
324
  not in {
314
325
  llm_param.LLMClientProtocol.CODEX_OAUTH,
315
326
  llm_param.LLMClientProtocol.CLAUDE_OAUTH,
327
+ llm_param.LLMClientProtocol.ANTIGRAVITY,
316
328
  llm_param.LLMClientProtocol.BEDROCK,
317
329
  }
318
330
  and not api_key
@@ -389,6 +401,7 @@ class Config(BaseModel):
389
401
 
390
402
  # Sync user-modifiable fields from merged config to user config
391
403
  user_config.main_model = self.main_model
404
+ user_config.compact_model = self.compact_model
392
405
  user_config.sub_agent_models = self.sub_agent_models
393
406
  user_config.theme = self.theme
394
407
  # Note: provider_list is NOT synced - user providers are already in user_config
@@ -407,6 +420,7 @@ def get_example_config() -> UserConfig:
407
420
  """Generate example config for user reference (will be commented out)."""
408
421
  return UserConfig(
409
422
  main_model="opus",
423
+ compact_model="gemini-flash",
410
424
  sub_agent_models={"explore": "haiku", "webagent": "sonnet", "task": "sonnet"},
411
425
  provider_list=[
412
426
  UserProviderConfig(
@@ -434,11 +448,7 @@ def get_example_config() -> UserConfig:
434
448
 
435
449
  def _get_builtin_config() -> Config:
436
450
  """Load built-in provider configurations."""
437
- # Re-validate to ensure compatibility with current ProviderConfig class
438
- # (needed for tests that may monkeypatch the class)
439
- providers = [ProviderConfig.model_validate(p.model_dump()) for p in get_builtin_provider_configs()]
440
- sub_agent_models = get_builtin_sub_agent_models()
441
- return Config(provider_list=providers, sub_agent_models=sub_agent_models)
451
+ return get_builtin_config()
442
452
 
443
453
 
444
454
  def _merge_model(builtin: ModelConfig, user: ModelConfig) -> ModelConfig:
@@ -485,7 +495,7 @@ def _merge_provider(builtin: ProviderConfig, user: UserProviderConfig) -> Provid
485
495
  if value is not None:
486
496
  merged_data[key] = value
487
497
 
488
- merged_data["model_list"] = list(merged_models.values())
498
+ merged_data["model_list"] = [m.model_dump() for m in merged_models.values()]
489
499
  return ProviderConfig.model_validate(merged_data)
490
500
 
491
501
 
@@ -530,11 +540,14 @@ def _merge_configs(user_config: UserConfig | None, builtin_config: Config) -> Co
530
540
  # Merge sub_agent_models
531
541
  merged_sub_agent_models = {**builtin_config.sub_agent_models, **user_config.sub_agent_models}
532
542
 
543
+ # Re-validate providers to ensure compatibility (tests may monkeypatch the class)
544
+ revalidated_providers = [ProviderConfig.model_validate(p.model_dump()) for p in merged_providers.values()]
533
545
  merged = Config(
534
546
  main_model=user_config.main_model or builtin_config.main_model,
547
+ compact_model=user_config.compact_model or builtin_config.compact_model,
535
548
  sub_agent_models=merged_sub_agent_models,
536
549
  theme=user_config.theme or builtin_config.theme,
537
- provider_list=list(merged_providers.values()),
550
+ provider_list=revalidated_providers,
538
551
  )
539
552
  # Keep reference to user config for saving
540
553
  merged.set_user_config(user_config)
@@ -555,7 +568,7 @@ def _load_user_config() -> UserConfig | None:
555
568
  try:
556
569
  return UserConfig.model_validate(config_dict)
557
570
  except ValidationError as e:
558
- log(f"Invalid config file: {config_path}", style="red bold")
571
+ log(f"Invalid config file: {config_path}", style="red")
559
572
  log(str(e), style="red")
560
573
  raise ValueError(f"Invalid config file: {config_path}") from e
561
574
 
@@ -591,6 +604,7 @@ def create_example_config() -> bool:
591
604
  def _load_config_uncached() -> Config:
592
605
  """Load and merge builtin + user config. Always returns a valid Config."""
593
606
  builtin_config = _get_builtin_config()
607
+
594
608
  user_config = _load_user_config()
595
609
 
596
610
  return _merge_configs(user_config, builtin_config)
klaude_code/const.py CHANGED
@@ -170,6 +170,7 @@ STATUS_HINT_TEXT = " (esc to interrupt)" # Status hint text shown after spinner
170
170
  STATUS_WAITING_TEXT = "Loading …"
171
171
  STATUS_THINKING_TEXT = "Thinking …"
172
172
  STATUS_COMPOSING_TEXT = "Composing"
173
+ STATUS_COMPACTING_TEXT = "Compacting"
173
174
 
174
175
  # Backwards-compatible alias for the default spinner status text.
175
176
  STATUS_DEFAULT_TEXT = STATUS_WAITING_TEXT
klaude_code/core/agent.py CHANGED
@@ -18,9 +18,11 @@ class Agent:
18
18
  self,
19
19
  session: Session,
20
20
  profile: AgentProfile,
21
+ compact_llm_client: LLMClientABC | None = None,
21
22
  ):
22
23
  self.session: Session = session
23
24
  self.profile: AgentProfile = profile
25
+ self.compact_llm_client: LLMClientABC | None = compact_llm_client
24
26
  self._current_task: TaskExecutor | None = None
25
27
  if not self.session.model_name:
26
28
  self.session.model_name = profile.llm_client.model_name
@@ -43,18 +45,20 @@ class Agent:
43
45
  ) -> AsyncGenerator[events.Event]:
44
46
  session_ctx = SessionContext(
45
47
  session_id=self.session.id,
46
- get_conversation_history=lambda: self.session.conversation_history,
48
+ get_conversation_history=self.session.get_llm_history,
47
49
  append_history=self.session.append_history,
48
50
  file_tracker=self.session.file_tracker,
49
51
  todo_context=build_todo_context(self.session),
50
52
  run_subtask=run_subtask,
51
53
  )
52
54
  context = TaskExecutionContext(
55
+ session=self.session,
53
56
  session_ctx=session_ctx,
54
57
  profile=self.profile,
55
58
  tool_registry=get_registry(),
56
59
  process_reminder=self._process_reminder,
57
60
  sub_agent_state=self.session.sub_agent_state,
61
+ compact_llm_client=self.compact_llm_client,
58
62
  )
59
63
 
60
64
  task = TaskExecutor(context)
@@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, Protocol
12
12
  if TYPE_CHECKING:
13
13
  from klaude_code.config.config import Config
14
14
 
15
+ from klaude_code.auth.codex.exceptions import CodexUnsupportedModelError
15
16
  from klaude_code.config.sub_agent_model_helper import SubAgentModelHelper
16
17
  from klaude_code.core.reminders import (
17
18
  at_file_reader_reminder,
@@ -53,15 +54,15 @@ COMMAND_DESCRIPTIONS: dict[str, str] = {
53
54
  }
54
55
 
55
56
 
56
- # Mapping from logical prompt keys to resource file paths under the core/prompt directory.
57
- PROMPT_FILES: dict[str, str] = {
58
- "main_codex": "prompts/prompt-codex.md",
59
- "main_gpt_5_1_codex_max": "prompts/prompt-codex-gpt-5-1-codex-max.md",
60
- "main_gpt_5_2_codex": "prompts/prompt-codex-gpt-5-2-codex.md",
61
- "main": "prompts/prompt-claude-code.md",
62
- "main_gemini": "prompts/prompt-gemini.md", # https://ai.google.dev/gemini-api/docs/prompting-strategies?hl=zh-cn#agentic-si-template
57
+ # Prompts for codex_oauth protocol - must be used exactly as-is without any additions.
58
+ CODEX_OAUTH_PROMPTS: dict[str, str] = {
59
+ "gpt-5.2-codex": "prompts/prompt-codex-gpt-5-2-codex.md",
60
+ "gpt-5.2": "prompts/prompt-codex-gpt-5-2.md",
63
61
  }
64
62
 
63
+ # Prompt for antigravity protocol - used exactly as-is without any additions.
64
+ ANTIGRAVITY_PROMPT_PATH = "prompts/prompt-antigravity.md"
65
+
65
66
 
66
67
  STRUCTURED_OUTPUT_PROMPT_FOR_SUB_AGENT = """\
67
68
 
@@ -93,31 +94,20 @@ def _load_prompt_by_path(prompt_path: str) -> str:
93
94
  return files(__package__).joinpath(prompt_path).read_text(encoding="utf-8").strip()
94
95
 
95
96
 
96
- def _load_base_prompt(file_key: str) -> str:
97
- """Load and cache the base prompt content from file."""
98
-
99
- try:
100
- prompt_path = PROMPT_FILES[file_key]
101
- except KeyError as exc:
102
- raise ValueError(f"Unknown prompt key: {file_key}") from exc
103
-
104
- return _load_prompt_by_path(prompt_path)
105
-
106
-
107
- def _get_file_key(model_name: str, protocol: llm_param.LLMClientProtocol) -> str:
108
- """Determine which prompt file to use based on model."""
97
+ def _load_prompt_by_model(model_name: str) -> str:
98
+ """Load base prompt content based on model name."""
109
99
 
110
100
  match model_name:
111
101
  case name if "gpt-5.2-codex" in name:
112
- return "main_gpt_5_2_codex"
113
- case name if "gpt-5.1-codex-max" in name:
114
- return "main_gpt_5_1_codex_max"
102
+ return _load_prompt_by_path("prompts/prompt-codex-gpt-5-2-codex.md")
103
+ case name if "gpt-5.2" in name:
104
+ return _load_prompt_by_path("prompts/prompt-codex-gpt-5-2.md")
115
105
  case name if "gpt-5" in name:
116
- return "main_codex"
106
+ return _load_prompt_by_path("prompts/prompt-codex.md")
117
107
  case name if "gemini" in name:
118
- return "main_gemini"
108
+ return _load_prompt_by_path("prompts/prompt-gemini.md")
119
109
  case _:
120
- return "main"
110
+ return _load_prompt_by_path("prompts/prompt-claude-code.md")
121
111
 
122
112
 
123
113
  def _build_env_info(model_name: str) -> str:
@@ -175,16 +165,22 @@ def load_system_prompt(
175
165
  ) -> str:
176
166
  """Get system prompt content for the given model and sub-agent type."""
177
167
 
168
+ # For codex_oauth protocol, use exact prompts without any additions.
169
+ if protocol == llm_param.LLMClientProtocol.CODEX_OAUTH:
170
+ for model_key, prompt_path in CODEX_OAUTH_PROMPTS.items():
171
+ if model_key in model_name:
172
+ return _load_prompt_by_path(prompt_path)
173
+ raise CodexUnsupportedModelError(f"codex_oauth protocol does not support model: {model_name}")
174
+
175
+ # For antigravity protocol, use exact prompt without any additions.
176
+ if protocol == llm_param.LLMClientProtocol.ANTIGRAVITY:
177
+ return _load_prompt_by_path(ANTIGRAVITY_PROMPT_PATH)
178
+
178
179
  if sub_agent_type is not None:
179
180
  profile = get_sub_agent_profile(sub_agent_type)
180
181
  base_prompt = _load_prompt_by_path(profile.prompt_file)
181
182
  else:
182
- file_key = _get_file_key(model_name, protocol)
183
- base_prompt = _load_base_prompt(file_key)
184
-
185
- if protocol == llm_param.LLMClientProtocol.CODEX_OAUTH:
186
- # Do not append environment info or skills info for Codex protocol.
187
- return base_prompt
183
+ base_prompt = _load_prompt_by_model(model_name)
188
184
 
189
185
  skills_prompt = ""
190
186
  sub_agent_prompt = ""