klaude-code 2.5.2__tar.gz → 2.5.3__tar.gz

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 (240) hide show
  1. {klaude_code-2.5.2 → klaude_code-2.5.3}/PKG-INFO +1 -1
  2. {klaude_code-2.5.2 → klaude_code-2.5.3}/pyproject.toml +1 -1
  3. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/auth_cmd.py +2 -13
  4. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/cost_cmd.py +10 -10
  5. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/main.py +40 -7
  6. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/session_cmd.py +2 -11
  7. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/assets/builtin_config.yaml +45 -24
  8. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/model_matcher.py +1 -1
  9. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/const.py +2 -1
  10. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/edit_tool.py +1 -1
  11. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/read_tool.py +2 -2
  12. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/write_tool.py +1 -1
  13. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/turn.py +19 -1
  14. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/anthropic/client.py +75 -50
  15. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/anthropic/input.py +20 -9
  16. klaude_code-2.5.3/src/klaude_code/llm/google/client.py +527 -0
  17. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/google/input.py +44 -36
  18. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openai_compatible/stream.py +109 -99
  19. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openrouter/reasoning.py +4 -29
  20. klaude_code-2.5.3/src/klaude_code/llm/partial_message.py +5 -0
  21. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/responses/client.py +99 -81
  22. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/responses/input.py +11 -25
  23. klaude_code-2.5.3/src/klaude_code/llm/stream_parts.py +94 -0
  24. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/log.py +57 -0
  25. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/fork_session_cmd.py +14 -23
  26. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/model_picker.py +2 -17
  27. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/resume_cmd.py +2 -18
  28. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/sub_agent_model_cmd.py +5 -19
  29. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/thinking_cmd.py +2 -14
  30. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/common.py +1 -1
  31. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/metadata.py +17 -16
  32. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/quote.py +36 -8
  33. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/theme.py +2 -0
  34. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/input/prompt_toolkit.py +3 -1
  35. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/machine.py +19 -1
  36. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/renderer.py +3 -3
  37. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/terminal/selector.py +174 -31
  38. klaude_code-2.5.2/src/klaude_code/llm/google/client.py +0 -452
  39. klaude_code-2.5.2/src/klaude_code/llm/partial_message.py +0 -35
  40. {klaude_code-2.5.2 → klaude_code-2.5.3}/README.md +0 -0
  41. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/.DS_Store +0 -0
  42. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/__init__.py +0 -0
  43. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/app/__init__.py +0 -0
  44. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/app/runtime.py +0 -0
  45. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/__init__.py +0 -0
  46. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/base.py +0 -0
  47. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/claude/__init__.py +0 -0
  48. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/claude/exceptions.py +0 -0
  49. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/claude/oauth.py +0 -0
  50. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/claude/token_manager.py +0 -0
  51. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/codex/__init__.py +0 -0
  52. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/codex/exceptions.py +0 -0
  53. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
  54. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/codex/oauth.py +0 -0
  55. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/auth/codex/token_manager.py +0 -0
  56. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/__init__.py +0 -0
  57. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/config_cmd.py +0 -0
  58. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/debug.py +0 -0
  59. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/list_model.py +0 -0
  60. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/cli/self_update.py +0 -0
  61. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/__init__.py +0 -0
  62. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/assets/__init__.py +0 -0
  63. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/builtin_config.py +0 -0
  64. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/config.py +0 -0
  65. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/sub_agent_model_helper.py +0 -0
  66. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/config/thinking.py +0 -0
  67. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/__init__.py +0 -0
  68. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/agent.py +0 -0
  69. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/agent_profile.py +0 -0
  70. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/executor.py +0 -0
  71. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/loaded_skills.py +0 -0
  72. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/manager/__init__.py +0 -0
  73. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/manager/llm_clients.py +0 -0
  74. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
  75. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
  76. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  77. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
  78. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
  79. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
  80. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
  81. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
  82. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
  83. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-sub-agent-image-gen.md +0 -0
  84. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +0 -0
  85. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
  86. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/reminders.py +0 -0
  87. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/task.py +0 -0
  88. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/__init__.py +0 -0
  89. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/context.py +0 -0
  90. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/__init__.py +0 -0
  91. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/_utils.py +0 -0
  92. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  93. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  94. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
  95. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
  96. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  97. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  98. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  99. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/offload.py +0 -0
  100. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/report_back_tool.py +0 -0
  101. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/shell/__init__.py +0 -0
  102. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  103. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
  104. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  105. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/sub_agent_tool.py +0 -0
  106. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/todo/__init__.py +0 -0
  107. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  108. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
  109. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
  110. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  111. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
  112. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/tool_abc.py +0 -0
  113. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/tool_registry.py +0 -0
  114. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/tool_runner.py +0 -0
  115. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/web/__init__.py +0 -0
  116. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  117. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
  118. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
  119. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
  120. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
  121. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
  122. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/__init__.py +0 -0
  123. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  124. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/bedrock/__init__.py +0 -0
  125. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/bedrock/client.py +0 -0
  126. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/claude/__init__.py +0 -0
  127. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/claude/client.py +0 -0
  128. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/client.py +0 -0
  129. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/codex/__init__.py +0 -0
  130. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/codex/client.py +0 -0
  131. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/google/__init__.py +0 -0
  132. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/image.py +0 -0
  133. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/input_common.py +0 -0
  134. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  135. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openai_compatible/client.py +0 -0
  136. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openai_compatible/input.py +0 -0
  137. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
  138. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  139. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openrouter/client.py +0 -0
  140. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/openrouter/input.py +0 -0
  141. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/registry.py +0 -0
  142. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/responses/__init__.py +0 -0
  143. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/llm/usage.py +0 -0
  144. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/__init__.py +0 -0
  145. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/commands.py +0 -0
  146. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/__init__.py +0 -0
  147. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/base.py +0 -0
  148. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/chat.py +0 -0
  149. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/lifecycle.py +0 -0
  150. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/metadata.py +0 -0
  151. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/streaming.py +0 -0
  152. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/system.py +0 -0
  153. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/events/tools.py +0 -0
  154. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/llm_param.py +0 -0
  155. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/message.py +0 -0
  156. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/model.py +0 -0
  157. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/op.py +0 -0
  158. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/op_handler.py +0 -0
  159. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/sub_agent/AGENTS.md +0 -0
  160. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/sub_agent/__init__.py +0 -0
  161. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
  162. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/sub_agent/image_gen.py +0 -0
  163. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/sub_agent/task.py +0 -0
  164. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/sub_agent/web.py +0 -0
  165. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/protocol/tools.py +0 -0
  166. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/__init__.py +0 -0
  167. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/codec.py +0 -0
  168. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/export.py +0 -0
  169. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/selector.py +0 -0
  170. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/session.py +0 -0
  171. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/store.py +0 -0
  172. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/templates/export_session.html +0 -0
  173. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/session/templates/mermaid_viewer.html +0 -0
  174. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/.DS_Store +0 -0
  175. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/__init__.py +0 -0
  176. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/assets/.DS_Store +0 -0
  177. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/assets/create-plan/SKILL.md +0 -0
  178. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
  179. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
  180. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
  181. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/loader.py +0 -0
  182. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/manager.py +0 -0
  183. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/skill/system_skills.py +0 -0
  184. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/__init__.py +0 -0
  185. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/__init__.py +0 -0
  186. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/clear_cmd.py +0 -0
  187. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/command_abc.py +0 -0
  188. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/copy_cmd.py +0 -0
  189. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/debug_cmd.py +0 -0
  190. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/export_cmd.py +0 -0
  191. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/export_online_cmd.py +0 -0
  192. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/model_cmd.py +0 -0
  193. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/prompt-init.md +0 -0
  194. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/prompt_command.py +0 -0
  195. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/refresh_cmd.py +0 -0
  196. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/registry.py +0 -0
  197. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/status_cmd.py +0 -0
  198. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/command/terminal_setup_cmd.py +0 -0
  199. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/commands.py +0 -0
  200. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/__init__.py +0 -0
  201. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/assistant.py +0 -0
  202. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/bash_syntax.py +0 -0
  203. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/command_output.py +0 -0
  204. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/developer.py +0 -0
  205. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/diffs.py +0 -0
  206. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/errors.py +0 -0
  207. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/mermaid_viewer.py +0 -0
  208. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/__init__.py +0 -0
  209. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/cjk_wrap.py +0 -0
  210. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/code_panel.py +0 -0
  211. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/live.py +0 -0
  212. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/markdown.py +0 -0
  213. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/searchable_text.py +0 -0
  214. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/rich/status.py +0 -0
  215. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/sub_agent.py +0 -0
  216. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/thinking.py +0 -0
  217. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/tools.py +0 -0
  218. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/user_input.py +0 -0
  219. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/components/welcome.py +0 -0
  220. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/display.py +0 -0
  221. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/input/__init__.py +0 -0
  222. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/input/clipboard.py +0 -0
  223. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/input/completers.py +0 -0
  224. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/input/key_bindings.py +0 -0
  225. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/runner.py +0 -0
  226. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/terminal/__init__.py +0 -0
  227. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/terminal/color.py +0 -0
  228. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/terminal/control.py +0 -0
  229. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/terminal/image.py +0 -0
  230. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/terminal/notifier.py +0 -0
  231. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/tui/terminal/progress_bar.py +0 -0
  232. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/__init__.py +0 -0
  233. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/common.py +0 -0
  234. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/core/__init__.py +0 -0
  235. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/core/display.py +0 -0
  236. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/core/input.py +0 -0
  237. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/debug_mode.py +0 -0
  238. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/terminal/__init__.py +0 -0
  239. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/ui/terminal/title.py +0 -0
  240. {klaude_code-2.5.2 → klaude_code-2.5.3}/src/klaude_code/update.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 2.5.2
3
+ Version: 2.5.3
4
4
  Summary: Minimal code agent CLI
5
5
  Requires-Dist: anthropic>=0.66.0
6
6
  Requires-Dist: chardet>=5.2.0
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "klaude-code"
7
- version = "2.5.2"
7
+ version = "2.5.3"
8
8
  description = "Minimal code agent CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -4,20 +4,9 @@ import datetime
4
4
  import webbrowser
5
5
 
6
6
  import typer
7
- from prompt_toolkit.styles import Style
8
7
 
9
8
  from klaude_code.log import log
10
- from klaude_code.tui.terminal.selector import SelectItem, select_one
11
-
12
- _SELECT_STYLE = Style(
13
- [
14
- ("instruction", "ansibrightblack"),
15
- ("pointer", "ansigreen"),
16
- ("highlighted", "ansigreen"),
17
- ("text", "ansibrightblack"),
18
- ("question", "bold"),
19
- ]
20
- )
9
+ from klaude_code.tui.terminal.selector import DEFAULT_PICKER_STYLE, SelectItem, select_one
21
10
 
22
11
 
23
12
  def _select_provider() -> str | None:
@@ -30,7 +19,7 @@ def _select_provider() -> str | None:
30
19
  message="Select provider to login:",
31
20
  items=items,
32
21
  pointer="→",
33
- style=_SELECT_STYLE,
22
+ style=DEFAULT_PICKER_STYLE,
34
23
  use_search_filter=False,
35
24
  )
36
25
 
@@ -183,14 +183,14 @@ def render_cost_table(daily_stats: dict[str, DailyStats]) -> Table:
183
183
  box=ASCII_HORIZONAL,
184
184
  )
185
185
 
186
- table.add_column("Date", style="cyan", no_wrap=True)
187
- table.add_column("Model", no_wrap=True)
188
- table.add_column("Input", justify="right", no_wrap=True)
189
- table.add_column("Output", justify="right", no_wrap=True)
190
- table.add_column("Cache", justify="right", no_wrap=True)
191
- table.add_column("Total", justify="right", no_wrap=True)
192
- table.add_column("USD", justify="right", no_wrap=True)
193
- table.add_column("CNY", justify="right", no_wrap=True)
186
+ table.add_column("Date", style="cyan")
187
+ table.add_column("Model", overflow="ellipsis")
188
+ table.add_column("Input", justify="right")
189
+ table.add_column("Output", justify="right")
190
+ table.add_column("Cache", justify="right")
191
+ table.add_column("Total", justify="right")
192
+ table.add_column("USD", justify="right")
193
+ table.add_column("CNY", justify="right")
194
194
 
195
195
  # Sort dates
196
196
  sorted_dates = sorted(daily_stats.keys())
@@ -222,7 +222,7 @@ def render_cost_table(daily_stats: dict[str, DailyStats]) -> Table:
222
222
 
223
223
  table.add_row(
224
224
  format_date_display(date_str) if first_row else "",
225
- f"- {model_name}",
225
+ f"{model_name}",
226
226
  format_tokens(stats.input_tokens),
227
227
  format_tokens(stats.output_tokens),
228
228
  format_tokens(stats.cached_tokens),
@@ -276,7 +276,7 @@ def render_cost_table(daily_stats: dict[str, DailyStats]) -> Table:
276
276
  usd_str, cny_str = format_cost_dual(stats.cost_usd, stats.cost_cny)
277
277
  table.add_row(
278
278
  "",
279
- f"- {model_name}",
279
+ f"{model_name}",
280
280
  format_tokens(stats.input_tokens),
281
281
  format_tokens(stats.output_tokens),
282
282
  format_tokens(stats.cached_tokens),
@@ -13,13 +13,46 @@ from klaude_code.session import Session
13
13
  from klaude_code.tui.command.resume_cmd import select_session_sync
14
14
  from klaude_code.ui.terminal.title import update_terminal_title
15
15
 
16
- ENV_HELP = """\
17
- Environment Variables:
18
-
19
- KLAUDE_READ_GLOBAL_LINE_CAP Max lines to read (default: 2000)
20
-
21
- KLAUDE_READ_MAX_CHARS Max total chars to read (default: 50000)
22
- """
16
+ ENV_HELP_LINES = [
17
+ "Environment Variables:",
18
+ "",
19
+ "Provider API keys (built-in config):",
20
+ " ANTHROPIC_API_KEY Anthropic API key",
21
+ " OPENAI_API_KEY OpenAI API key",
22
+ " OPENROUTER_API_KEY OpenRouter API key",
23
+ " GOOGLE_API_KEY Google API key (Gemini)",
24
+ " DEEPSEEK_API_KEY DeepSeek API key",
25
+ " MOONSHOT_API_KEY Moonshot API key (Kimi)",
26
+ "",
27
+ "AWS credentials (Bedrock):",
28
+ " AWS_ACCESS_KEY_ID AWS access key id",
29
+ " AWS_SECRET_ACCESS_KEY AWS secret access key",
30
+ " AWS_REGION AWS region",
31
+ "",
32
+ "Tool limits (Read):",
33
+ " KLAUDE_READ_GLOBAL_LINE_CAP Max lines to read (default: 2000)",
34
+ " KLAUDE_READ_MAX_CHARS Max total chars to read (default: 50000)",
35
+ " KLAUDE_READ_MAX_IMAGE_BYTES Max image bytes to read (default: 4MB)",
36
+ " KLAUDE_IMAGE_OUTPUT_MAX_BYTES Max decoded image bytes (default: 64MB)",
37
+ "",
38
+ "Notifications / testing:",
39
+ " KLAUDE_NOTIFY Set to 0/off/false/disable(d) to disable task notifications",
40
+ " KLAUDE_TEST_SIGNAL In tmux, emit `tmux wait-for -S <channel>` on task completion",
41
+ " TMUX Auto-detected; required for KLAUDE_TEST_SIGNAL",
42
+ "",
43
+ "Editor / terminal integration:",
44
+ " EDITOR Preferred editor for `klaude config`",
45
+ " TERM Terminal identification (auto-detected)",
46
+ " TERM_PROGRAM Terminal identification (auto-detected)",
47
+ " WT_SESSION Terminal hint (auto-detected)",
48
+ " VTE_VERSION Terminal hint (auto-detected)",
49
+ " GHOSTTY_RESOURCES_DIR Ghostty detection (auto-detected)",
50
+ "",
51
+ "Compatibility:",
52
+ " ANTHROPIC_AUTH_TOKEN Reserved by anthropic SDK; temporarily unset during client init",
53
+ ]
54
+
55
+ ENV_HELP = "\n\n".join(ENV_HELP_LINES)
23
56
 
24
57
  app = typer.Typer(
25
58
  add_completion=False,
@@ -9,9 +9,7 @@ from klaude_code.session import Session
9
9
  def _session_confirm(sessions: list[Session.SessionMetaBrief], message: str) -> bool:
10
10
  """Show session list and confirm deletion using prompt_toolkit."""
11
11
 
12
- from prompt_toolkit.styles import Style
13
-
14
- from klaude_code.tui.terminal.selector import SelectItem, select_one
12
+ from klaude_code.tui.terminal.selector import DEFAULT_PICKER_STYLE, SelectItem, select_one
15
13
 
16
14
  def _fmt(ts: float) -> str:
17
15
  try:
@@ -37,14 +35,7 @@ def _session_confirm(sessions: list[Session.SessionMetaBrief], message: str) ->
37
35
  message=message,
38
36
  items=items,
39
37
  pointer="→",
40
- style=Style(
41
- [
42
- ("question", "bold"),
43
- ("pointer", "ansigreen"),
44
- ("highlighted", "ansigreen"),
45
- ("text", ""),
46
- ]
47
- ),
38
+ style=DEFAULT_PICKER_STYLE,
48
39
  use_search_filter=False,
49
40
  )
50
41
  return bool(result)
@@ -1,4 +1,3 @@
1
- ---
2
1
  # Built-in provider and model configurations
3
2
  # Users can start using klaude by simply setting environment variables
4
3
  # (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.) without manual configuration.
@@ -25,32 +24,58 @@ provider_list:
25
24
  protocol: responses
26
25
  api_key: ${OPENAI_API_KEY}
27
26
  model_list:
28
- - model_name: gpt-5.2
27
+ - model_name: gpt-5.2-high
29
28
  model_id: gpt-5.2
30
29
  max_tokens: 128000
31
30
  context_limit: 400000
32
31
  verbosity: high
33
32
  thinking:
34
33
  reasoning_effort: high
34
+ reasoning_summary: detailed
35
+ cost: {input: 1.75, output: 14, cache_read: 0.17}
36
+ - model_name: gpt-5.2-medium
37
+ model_id: gpt-5.2
38
+ context_limit: 400000
39
+ verbosity: high
40
+ thinking:
41
+ reasoning_effort: medium
42
+ reasoning_summary: concise
43
+ cost: {input: 1.75, output: 14, cache_read: 0.17}
44
+ - model_name: gpt-5.2-low
45
+ model_id: gpt-5.2
46
+ context_limit: 400000
47
+ verbosity: low
48
+ thinking:
49
+ reasoning_effort: low
50
+ reasoning_summary: concise
51
+ cost: {input: 1.75, output: 14, cache_read: 0.17}
52
+ - model_name: gpt-5.2-fast
53
+ model_id: gpt-5.2
54
+ context_limit: 400000
55
+ verbosity: low
56
+ thinking:
57
+ reasoning_effort: none
35
58
  cost: {input: 1.75, output: 14, cache_read: 0.17}
36
- - provider_name: openrouter
37
- protocol: openrouter
38
- api_key: ${OPENROUTER_API_KEY}
39
- model_list:
40
59
  - model_name: gpt-5.1-codex-max
41
- model_id: openai/gpt-5.1-codex-max
60
+ model_id: gpt-5.1-codex-max
42
61
  max_tokens: 128000
43
62
  context_limit: 400000
44
63
  thinking:
45
64
  reasoning_effort: medium
65
+ reasoning_summary: detailed
46
66
  cost: {input: 1.25, output: 10, cache_read: 0.13}
47
- - model_name: gpt-5.2
67
+ - provider_name: openrouter
68
+ protocol: openrouter
69
+ api_key: ${OPENROUTER_API_KEY}
70
+ model_list:
71
+ - model_name: gpt-5.2-high
48
72
  model_id: openai/gpt-5.2
49
73
  max_tokens: 128000
50
74
  context_limit: 400000
51
75
  verbosity: high
52
76
  thinking:
53
77
  reasoning_effort: high
78
+ reasoning_summary: detailed
54
79
  cost: {input: 1.75, output: 14, cache_read: 0.17}
55
80
  - model_name: gpt-5.2-medium
56
81
  model_id: openai/gpt-5.2
@@ -59,22 +84,7 @@ provider_list:
59
84
  verbosity: high
60
85
  thinking:
61
86
  reasoning_effort: medium
62
- cost: {input: 1.75, output: 14, cache_read: 0.17}
63
- - model_name: gpt-5.2-low
64
- model_id: openai/gpt-5.2
65
- max_tokens: 128000
66
- context_limit: 400000
67
- verbosity: low
68
- thinking:
69
- reasoning_effort: low
70
- cost: {input: 1.75, output: 14, cache_read: 0.17}
71
- - model_name: gpt-5.2-fast
72
- model_id: openai/gpt-5.2
73
- max_tokens: 128000
74
- context_limit: 400000
75
- verbosity: low
76
- thinking:
77
- reasoning_effort: none
87
+ reasoning_summary: concise
78
88
  cost: {input: 1.75, output: 14, cache_read: 0.17}
79
89
  - model_name: kimi
80
90
  model_id: moonshotai/kimi-k2-thinking
@@ -165,10 +175,14 @@ provider_list:
165
175
  - model_name: gemini-pro
166
176
  model_id: gemini-3-pro-preview
167
177
  context_limit: 1048576
178
+ thinking:
179
+ reasoning_effort: high
168
180
  cost: {input: 2, output: 12, cache_read: 0.2}
169
181
  - model_name: gemini-flash
170
182
  model_id: gemini-3-flash-preview
171
183
  context_limit: 1048576
184
+ thinking:
185
+ reasoning_effort: medium
172
186
  cost: {input: 0.5, output: 3, cache_read: 0.05}
173
187
  - model_name: nano-banana-pro
174
188
  model_id: gemini-3-pro-image-preview
@@ -177,6 +191,13 @@ provider_list:
177
191
  - image
178
192
  - text
179
193
  cost: {input: 2, output: 12, cache_read: 0.2, image: 120}
194
+ - model_name: nano-banana
195
+ model_id: gemini-2.5-flash-image
196
+ context_limit: 33000
197
+ modalities:
198
+ - image
199
+ - text
200
+ cost: {input: 0.3, output: 2.5, cache_read: 0.03, image: 30}
180
201
  - provider_name: bedrock
181
202
  protocol: bedrock
182
203
  aws_access_key: ${AWS_ACCESS_KEY_ID}
@@ -102,6 +102,7 @@ def match_model_from_config(preferred: str | None = None) -> ModelMatchResult:
102
102
  )
103
103
 
104
104
  # Normalized matching (e.g. gpt52 == gpt-5.2, gpt52 in gpt-5.2-2025-...)
105
+ # Only match selector/model_name exactly; model_id is checked via substring match below
105
106
  preferred_norm = _normalize_model_key(preferred)
106
107
  normalized_matches: list[ModelEntry] = []
107
108
  if preferred_norm:
@@ -110,7 +111,6 @@ def match_model_from_config(preferred: str | None = None) -> ModelMatchResult:
110
111
  for m in models
111
112
  if preferred_norm == _normalize_model_key(m.selector)
112
113
  or preferred_norm == _normalize_model_key(m.model_name)
113
- or preferred_norm == _normalize_model_key(m.model_id or "")
114
114
  ]
115
115
  if len(normalized_matches) == 1:
116
116
  return ModelMatchResult(
@@ -27,6 +27,7 @@ def _get_int_env(name: str, default: int) -> int:
27
27
  # =============================================================================
28
28
 
29
29
  MAX_FAILED_TURN_RETRIES = 10 # Maximum retry attempts for failed turns
30
+ RETRY_PRESERVE_PARTIAL_MESSAGE = True # Preserve partial message on stream error for retry prefill
30
31
  LLM_HTTP_TIMEOUT_TOTAL = 300.0 # HTTP timeout for LLM API requests (seconds)
31
32
  LLM_HTTP_TIMEOUT_CONNECT = 15.0 # HTTP connect timeout (seconds)
32
33
  LLM_HTTP_TIMEOUT_READ = 285.0 # HTTP read timeout (seconds)
@@ -157,7 +158,7 @@ MARKDOWN_RIGHT_MARGIN = 2 # Right margin (columns) for markdown rendering
157
158
  STATUS_HINT_TEXT = " (esc to interrupt)" # Status hint text shown after spinner
158
159
 
159
160
  # Spinner status texts
160
- STATUS_WAITING_TEXT = "Connecting …"
161
+ STATUS_WAITING_TEXT = "Loading …"
161
162
  STATUS_THINKING_TEXT = "Thinking …"
162
163
  STATUS_COMPOSING_TEXT = "Composing"
163
164
 
@@ -98,7 +98,7 @@ class EditTool(ToolABC):
98
98
  if is_directory(file_path):
99
99
  return message.ToolResultMessage(
100
100
  status="error",
101
- output_text="<tool_use_error>Illegal operation on a directory. edit</tool_use_error>",
101
+ output_text="<tool_use_error>Illegal operation on a directory: edit</tool_use_error>",
102
102
  )
103
103
 
104
104
  if args.old_string == "":
@@ -210,7 +210,7 @@ class ReadTool(ToolABC):
210
210
  if is_directory(file_path):
211
211
  return message.ToolResultMessage(
212
212
  status="error",
213
- output_text="<tool_use_error>Illegal operation on a directory. read</tool_use_error>",
213
+ output_text="<tool_use_error>Illegal operation on a directory: read</tool_use_error>",
214
214
  )
215
215
  if not file_exists(file_path):
216
216
  return message.ToolResultMessage(
@@ -308,7 +308,7 @@ class ReadTool(ToolABC):
308
308
  except IsADirectoryError:
309
309
  return message.ToolResultMessage(
310
310
  status="error",
311
- output_text="<tool_use_error>Illegal operation on a directory. read</tool_use_error>",
311
+ output_text="<tool_use_error>Illegal operation on a directory: read</tool_use_error>",
312
312
  )
313
313
 
314
314
  if offset > max(read_result.total_lines, 0):
@@ -57,7 +57,7 @@ class WriteTool(ToolABC):
57
57
  if is_directory(file_path):
58
58
  return message.ToolResultMessage(
59
59
  status="error",
60
- output_text="<tool_use_error>Illegal operation on a directory. write</tool_use_error>",
60
+ output_text="<tool_use_error>Illegal operation on a directory: write</tool_use_error>",
61
61
  )
62
62
 
63
63
  file_tracker = context.file_tracker
@@ -4,7 +4,7 @@ from collections.abc import AsyncGenerator
4
4
  from dataclasses import dataclass, field
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from klaude_code.const import SUPPORTED_IMAGE_SIZES
7
+ from klaude_code.const import RETRY_PRESERVE_PARTIAL_MESSAGE, SUPPORTED_IMAGE_SIZES
8
8
  from klaude_code.core.tool import ToolABC
9
9
  from klaude_code.core.tool.context import SubAgentResumeClaims, ToolContext
10
10
 
@@ -24,6 +24,12 @@ from klaude_code.llm.client import LLMStreamABC
24
24
  from klaude_code.log import DebugType, log_debug
25
25
  from klaude_code.protocol import events, llm_param, message, model, tools
26
26
 
27
+ # Protocols that support prefill (continuing from partial assistant message)
28
+ _PREFILL_SUPPORTED_PROTOCOLS = frozenset({
29
+ "anthropic",
30
+ "claude_oauth",
31
+ })
32
+
27
33
 
28
34
  class TurnError(Exception):
29
35
  """Raised when a turn fails and should be retried."""
@@ -176,6 +182,18 @@ class TurnExecutor:
176
182
  yield event
177
183
 
178
184
  if self._turn_result.stream_error is not None:
185
+ # Save accumulated content for potential prefill on retry (only for supported protocols)
186
+ protocol = ctx.llm_client.get_llm_config().protocol
187
+ supports_prefill = protocol.value in _PREFILL_SUPPORTED_PROTOCOLS
188
+ if (
189
+ RETRY_PRESERVE_PARTIAL_MESSAGE
190
+ and supports_prefill
191
+ and self._turn_result.assistant_message is not None
192
+ and self._turn_result.assistant_message.parts
193
+ ):
194
+ session_ctx.append_history([self._turn_result.assistant_message])
195
+ # Add continuation prompt to avoid Anthropic thinking block requirement
196
+ session_ctx.append_history([message.UserMessage(parts=[message.TextPart(text="continue")])])
179
197
  session_ctx.append_history([self._turn_result.stream_error])
180
198
  yield events.TurnEndEvent(session_id=session_ctx.session_id)
181
199
  raise TurnError(self._turn_result.stream_error.error)
@@ -33,8 +33,13 @@ from klaude_code.const import (
33
33
  from klaude_code.llm.anthropic.input import convert_history_to_input, convert_system_to_input, convert_tool_schema
34
34
  from klaude_code.llm.client import LLMClientABC, LLMStreamABC
35
35
  from klaude_code.llm.input_common import apply_config_defaults
36
- from klaude_code.llm.partial_message import degrade_thinking_to_text
37
36
  from klaude_code.llm.registry import register
37
+ from klaude_code.llm.stream_parts import (
38
+ append_text_part,
39
+ append_thinking_text_part,
40
+ build_partial_message,
41
+ build_partial_parts,
42
+ )
38
43
  from klaude_code.llm.usage import MetadataTracker, error_llm_stream
39
44
  from klaude_code.log import DebugType, log_debug
40
45
  from klaude_code.protocol import llm_param, message, model
@@ -64,11 +69,10 @@ class AnthropicStreamStateManager:
64
69
 
65
70
  def __init__(self, model_id: str) -> None:
66
71
  self.model_id = model_id
67
- self.accumulated_thinking: list[str] = []
68
- self.accumulated_content: list[str] = []
69
- self.parts: list[message.Part] = []
72
+ self.assistant_parts: list[message.Part] = []
70
73
  self.response_id: str | None = None
71
- self.pending_signature: str | None = None
74
+ self._pending_signature: str | None = None
75
+ self._pending_signature_thinking_index: int | None = None
72
76
  self.stop_reason: model.StopReason | None = None
73
77
 
74
78
  # Tool call state
@@ -80,34 +84,59 @@ class AnthropicStreamStateManager:
80
84
  self.input_token: int = 0
81
85
  self.cached_token: int = 0
82
86
 
83
- def flush_thinking(self) -> None:
84
- """Flush accumulated thinking content into parts."""
85
- if not self.accumulated_thinking:
87
+ def append_thinking_text(self, text: str) -> None:
88
+ """Append thinking text, merging with the previous ThinkingTextPart when possible."""
89
+ index = append_thinking_text_part(self.assistant_parts, text, model_id=self.model_id)
90
+ if index is not None:
91
+ self._pending_signature_thinking_index = index
92
+
93
+ def append_text(self, text: str) -> None:
94
+ """Append assistant text, merging with the previous TextPart when possible."""
95
+ append_text_part(self.assistant_parts, text)
96
+
97
+ def set_pending_signature(self, signature: str) -> None:
98
+ if signature:
99
+ self._pending_signature = signature
100
+
101
+ def flush_pending_signature(self) -> None:
102
+ """Attach any pending signature to the most recent thinking segment.
103
+
104
+ Anthropic's signature is semantically tied to its thinking content. The
105
+ signature delta may arrive slightly after thinking text, so we insert the
106
+ signature part adjacent to the thinking part it signs.
107
+ """
108
+
109
+ if not self._pending_signature:
110
+ return
111
+ if self._pending_signature_thinking_index is None:
112
+ # No thinking part seen for this signature; drop it.
113
+ self._pending_signature = None
86
114
  return
87
- full_thinking = "".join(self.accumulated_thinking)
88
- self.parts.append(message.ThinkingTextPart(text=full_thinking, model_id=self.model_id))
89
- if self.pending_signature:
90
- self.parts.append(
91
- message.ThinkingSignaturePart(
92
- signature=self.pending_signature,
93
- model_id=self.model_id,
94
- format="anthropic",
95
- )
96
- )
97
- self.accumulated_thinking.clear()
98
- self.pending_signature = None
99
115
 
100
- def flush_content(self) -> None:
101
- """Flush accumulated content into parts."""
102
- if not self.accumulated_content:
116
+ insert_at = self._pending_signature_thinking_index + 1
117
+ # Avoid inserting duplicates if flush is called multiple times.
118
+ if insert_at < len(self.assistant_parts) and isinstance(
119
+ self.assistant_parts[insert_at], message.ThinkingSignaturePart
120
+ ):
121
+ self._pending_signature = None
103
122
  return
104
- self.parts.append(message.TextPart(text="".join(self.accumulated_content)))
105
- self.accumulated_content.clear()
123
+
124
+ self.assistant_parts.insert(
125
+ insert_at,
126
+ message.ThinkingSignaturePart(
127
+ signature=self._pending_signature,
128
+ model_id=self.model_id,
129
+ format="anthropic",
130
+ ),
131
+ )
132
+
133
+ self._pending_signature = None
134
+ self._pending_signature_thinking_index = None
106
135
 
107
136
  def flush_tool_call(self) -> None:
108
137
  """Flush current tool call into parts."""
109
138
  if self.current_tool_name and self.current_tool_call_id:
110
- self.parts.append(
139
+ self.assistant_parts.append(
111
140
  message.ToolCallPart(
112
141
  call_id=self.current_tool_call_id,
113
142
  tool_name=self.current_tool_name,
@@ -119,11 +148,17 @@ class AnthropicStreamStateManager:
119
148
  self.current_tool_inputs = None
120
149
 
121
150
  def flush_all(self) -> list[message.Part]:
122
- """Flush all accumulated content in order and return parts."""
123
- self.flush_thinking()
124
- self.flush_content()
151
+ """Flush all pending content in order and return parts."""
152
+ self.flush_pending_signature()
125
153
  self.flush_tool_call()
126
- return list(self.parts)
154
+ return list(self.assistant_parts)
155
+
156
+ def get_partial_parts(self) -> list[message.Part]:
157
+ """Get accumulated parts excluding tool calls, with thinking degraded.
158
+
159
+ Filters out ToolCallPart and applies degrade_thinking_to_text.
160
+ """
161
+ return build_partial_parts(self.assistant_parts)
127
162
 
128
163
  def get_partial_message(self) -> message.AssistantMessage | None:
129
164
  """Build a partial AssistantMessage from accumulated state.
@@ -131,16 +166,7 @@ class AnthropicStreamStateManager:
131
166
  Flushes all accumulated content and returns the message with
132
167
  stop_reason="aborted". Returns None if no content has been accumulated.
133
168
  """
134
- self.flush_thinking()
135
- self.flush_content()
136
- parts = degrade_thinking_to_text(list(self.parts))
137
- if not parts:
138
- return None
139
- return message.AssistantMessage(
140
- parts=parts,
141
- response_id=self.response_id,
142
- stop_reason="aborted",
143
- )
169
+ return build_partial_message(self.assistant_parts, response_id=self.response_id)
144
170
 
145
171
 
146
172
  def build_payload(
@@ -226,17 +252,18 @@ async def parse_anthropic_stream(
226
252
  case BetaThinkingDelta() as delta:
227
253
  if delta.thinking:
228
254
  metadata_tracker.record_token()
229
- state.accumulated_thinking.append(delta.thinking)
255
+ state.append_thinking_text(delta.thinking)
230
256
  yield message.ThinkingTextDelta(
231
257
  content=delta.thinking,
232
258
  response_id=state.response_id,
233
259
  )
234
260
  case BetaSignatureDelta() as delta:
235
- state.pending_signature = delta.signature
261
+ state.set_pending_signature(delta.signature)
236
262
  case BetaTextDelta() as delta:
237
263
  if delta.text:
238
264
  metadata_tracker.record_token()
239
- state.accumulated_content.append(delta.text)
265
+ state.flush_pending_signature()
266
+ state.append_text(delta.text)
240
267
  yield message.AssistantTextDelta(
241
268
  content=delta.text,
242
269
  response_id=state.response_id,
@@ -251,6 +278,7 @@ async def parse_anthropic_stream(
251
278
  match event.content_block:
252
279
  case BetaToolUseBlock() as block:
253
280
  metadata_tracker.record_token()
281
+ state.flush_pending_signature()
254
282
  yield message.ToolCallStartDelta(
255
283
  response_id=state.response_id,
256
284
  call_id=block.id,
@@ -262,12 +290,7 @@ async def parse_anthropic_stream(
262
290
  case _:
263
291
  pass
264
292
  case BetaRawContentBlockStopEvent():
265
- if state.accumulated_thinking:
266
- metadata_tracker.record_token()
267
- state.flush_thinking()
268
- if state.accumulated_content:
269
- metadata_tracker.record_token()
270
- state.flush_content()
293
+ state.flush_pending_signature()
271
294
  if state.current_tool_name and state.current_tool_call_id:
272
295
  metadata_tracker.record_token()
273
296
  state.flush_tool_call()
@@ -337,8 +360,10 @@ class AnthropicLLMStream(LLMStreamABC):
337
360
  self._metadata_tracker.set_model_name(str(self._param.model_id))
338
361
  self._metadata_tracker.set_response_id(self._state.response_id)
339
362
  metadata = self._metadata_tracker.finalize()
363
+ # Use accumulated parts for potential prefill on retry
364
+ self._state.flush_all()
340
365
  yield message.AssistantMessage(
341
- parts=[],
366
+ parts=self._state.get_partial_parts(),
342
367
  response_id=self._state.response_id,
343
368
  usage=metadata,
344
369
  stop_reason="error",