klaude-code 2.10.0__tar.gz → 2.10.2__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 (255) hide show
  1. {klaude_code-2.10.0 → klaude_code-2.10.2}/PKG-INFO +1 -1
  2. {klaude_code-2.10.0 → klaude_code-2.10.2}/pyproject.toml +1 -1
  3. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/app/runtime.py +4 -0
  4. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/main.py +10 -0
  5. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/assets/builtin_config.yaml +15 -14
  6. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/agent_profile.py +23 -6
  7. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-sub-agent-web.md +2 -2
  8. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/offload.py +4 -4
  9. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/web/web_fetch_tool.md +2 -1
  10. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/web/web_fetch_tool.py +1 -1
  11. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/turn.py +1 -2
  12. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_codex/client.py +0 -22
  13. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openrouter/client.py +4 -1
  14. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openrouter/input.py +6 -0
  15. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/templates/mermaid_viewer.html +85 -0
  16. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/metadata.py +5 -2
  17. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/markdown.py +3 -0
  18. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/theme.py +9 -8
  19. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/thinking.py +0 -35
  20. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/tools.py +3 -2
  21. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/user_input.py +2 -1
  22. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/display.py +1 -1
  23. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/key_bindings.py +96 -2
  24. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/prompt_toolkit.py +0 -1
  25. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/machine.py +46 -25
  26. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/renderer.py +8 -1
  27. klaude_code-2.10.0/src/klaude_code/llm/openai_codex/prompt_sync.py +0 -237
  28. {klaude_code-2.10.0 → klaude_code-2.10.2}/README.md +0 -0
  29. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/.DS_Store +0 -0
  30. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/__init__.py +0 -0
  31. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/app/__init__.py +0 -0
  32. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/AGENTS.md +0 -0
  33. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/__init__.py +0 -0
  34. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/antigravity/__init__.py +0 -0
  35. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/antigravity/exceptions.py +0 -0
  36. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/antigravity/oauth.py +0 -0
  37. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/antigravity/pkce.py +0 -0
  38. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/antigravity/token_manager.py +0 -0
  39. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/base.py +0 -0
  40. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/claude/__init__.py +0 -0
  41. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/claude/exceptions.py +0 -0
  42. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/claude/oauth.py +0 -0
  43. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/claude/token_manager.py +0 -0
  44. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/codex/__init__.py +0 -0
  45. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/codex/exceptions.py +0 -0
  46. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
  47. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/codex/oauth.py +0 -0
  48. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/codex/token_manager.py +0 -0
  49. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/auth/env.py +0 -0
  50. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/__init__.py +0 -0
  51. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/auth_cmd.py +0 -0
  52. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/config_cmd.py +0 -0
  53. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/cost_cmd.py +0 -0
  54. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/debug.py +0 -0
  55. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/list_model.py +0 -0
  56. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/cli/self_update.py +0 -0
  57. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/__init__.py +0 -0
  58. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/assets/__init__.py +0 -0
  59. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/builtin_config.py +0 -0
  60. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/config.py +0 -0
  61. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/model_matcher.py +0 -0
  62. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/sub_agent_model_helper.py +0 -0
  63. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/config/thinking.py +0 -0
  64. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/const.py +0 -0
  65. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/__init__.py +0 -0
  66. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/agent.py +0 -0
  67. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/bash_mode.py +0 -0
  68. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/compaction/AGENTS.md +0 -0
  69. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/compaction/__init__.py +0 -0
  70. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/compaction/compaction.py +0 -0
  71. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/compaction/overflow.py +0 -0
  72. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/compaction/prompts.py +0 -0
  73. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/executor.py +0 -0
  74. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/loaded_skills.py +0 -0
  75. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/manager/__init__.py +0 -0
  76. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/manager/llm_clients.py +0 -0
  77. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
  78. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
  79. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/memory.py +0 -0
  80. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-antigravity.md +0 -0
  81. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  82. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2-codex.md +0 -0
  83. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-codex-gpt-5-2.md +0 -0
  84. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-codex.md +0 -0
  85. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
  86. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-minimal.md +0 -0
  87. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-sub-agent-explore.md +0 -0
  88. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-sub-agent-image-gen.md +0 -0
  89. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/prompts/prompt-sub-agent.md +0 -0
  90. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/reminders.py +0 -0
  91. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/task.py +0 -0
  92. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/__init__.py +0 -0
  93. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/context.py +0 -0
  94. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/__init__.py +0 -0
  95. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/_utils.py +0 -0
  96. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  97. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  98. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
  99. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/diff_builder.py +0 -0
  100. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  101. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
  102. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  103. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/read_tool.py +0 -0
  104. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  105. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/file/write_tool.py +0 -0
  106. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/report_back_tool.py +0 -0
  107. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/shell/__init__.py +0 -0
  108. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  109. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
  110. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  111. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/sub_agent/__init__.py +0 -0
  112. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/sub_agent/image_gen.md +0 -0
  113. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/sub_agent/image_gen.py +0 -0
  114. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/sub_agent/task.md +0 -0
  115. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/sub_agent/task.py +0 -0
  116. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/todo/__init__.py +0 -0
  117. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  118. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
  119. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/todo/todo_write_tool_raw.md +0 -0
  120. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  121. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
  122. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/tool_abc.py +0 -0
  123. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/tool_registry.py +0 -0
  124. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/tool_runner.py +0 -0
  125. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/web/__init__.py +0 -0
  126. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  127. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
  128. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/web/web_search_tool.md +0 -0
  129. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/core/tool/web/web_search_tool.py +0 -0
  130. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/__init__.py +0 -0
  131. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  132. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/anthropic/client.py +0 -0
  133. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/anthropic/input.py +0 -0
  134. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/antigravity/__init__.py +0 -0
  135. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/antigravity/client.py +0 -0
  136. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/antigravity/input.py +0 -0
  137. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/bedrock_anthropic/__init__.py +0 -0
  138. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/bedrock_anthropic/client.py +0 -0
  139. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/claude/__init__.py +0 -0
  140. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/claude/client.py +0 -0
  141. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/client.py +0 -0
  142. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/google/__init__.py +0 -0
  143. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/google/client.py +0 -0
  144. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/google/input.py +0 -0
  145. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/image.py +0 -0
  146. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/input_common.py +0 -0
  147. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/json_stable.py +0 -0
  148. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_codex/__init__.py +0 -0
  149. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  150. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_compatible/client.py +0 -0
  151. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_compatible/input.py +0 -0
  152. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_compatible/stream.py +0 -0
  153. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_responses/__init__.py +0 -0
  154. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_responses/client.py +0 -0
  155. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openai_responses/input.py +0 -0
  156. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  157. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/openrouter/reasoning.py +0 -0
  158. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/partial_message.py +0 -0
  159. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/registry.py +0 -0
  160. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/stream_parts.py +0 -0
  161. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/llm/usage.py +0 -0
  162. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/log.py +0 -0
  163. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/__init__.py +0 -0
  164. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/commands.py +0 -0
  165. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/events.py +0 -0
  166. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/llm_param.py +0 -0
  167. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/message.py +0 -0
  168. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/model.py +0 -0
  169. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/op.py +0 -0
  170. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/op_handler.py +0 -0
  171. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/sub_agent/AGENTS.md +0 -0
  172. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/sub_agent/__init__.py +0 -0
  173. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/sub_agent/explore.py +0 -0
  174. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/sub_agent/image_gen.py +0 -0
  175. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/sub_agent/task.py +0 -0
  176. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/sub_agent/web.py +0 -0
  177. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/protocol/tools.py +0 -0
  178. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/__init__.py +0 -0
  179. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/codec.py +0 -0
  180. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/export.py +0 -0
  181. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/selector.py +0 -0
  182. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/session.py +0 -0
  183. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/store.py +0 -0
  184. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/session/templates/export_session.html +0 -0
  185. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/.DS_Store +0 -0
  186. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/__init__.py +0 -0
  187. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/assets/.DS_Store +0 -0
  188. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/assets/create-plan/SKILL.md +0 -0
  189. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/assets/deslop/SKILL.md +0 -0
  190. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/assets/handoff/SKILL.md +0 -0
  191. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/assets/skill-creator/SKILL.md +0 -0
  192. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/loader.py +0 -0
  193. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/manager.py +0 -0
  194. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/skill/system_skills.py +0 -0
  195. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/__init__.py +0 -0
  196. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/__init__.py +0 -0
  197. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/clear_cmd.py +0 -0
  198. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/command_abc.py +0 -0
  199. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/compact_cmd.py +0 -0
  200. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/continue_cmd.py +0 -0
  201. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/copy_cmd.py +0 -0
  202. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/debug_cmd.py +0 -0
  203. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/export_cmd.py +0 -0
  204. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/export_online_cmd.py +0 -0
  205. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/fork_session_cmd.py +0 -0
  206. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/model_cmd.py +0 -0
  207. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/model_picker.py +0 -0
  208. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/prompt-init.md +0 -0
  209. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/prompt_command.py +0 -0
  210. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/refresh_cmd.py +0 -0
  211. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/registry.py +0 -0
  212. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/resume_cmd.py +0 -0
  213. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/status_cmd.py +0 -0
  214. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/sub_agent_model_cmd.py +0 -0
  215. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/command/thinking_cmd.py +0 -0
  216. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/commands.py +0 -0
  217. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/__init__.py +0 -0
  218. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/bash_syntax.py +0 -0
  219. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/command_output.py +0 -0
  220. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/common.py +0 -0
  221. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/developer.py +0 -0
  222. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/diffs.py +0 -0
  223. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/errors.py +0 -0
  224. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/mermaid_viewer.py +0 -0
  225. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/__init__.py +0 -0
  226. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/cjk_wrap.py +0 -0
  227. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/code_panel.py +0 -0
  228. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/live.py +0 -0
  229. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/quote.py +0 -0
  230. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/rich/status.py +0 -0
  231. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/sub_agent.py +0 -0
  232. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/components/welcome.py +0 -0
  233. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/AGENTS.md +0 -0
  234. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/__init__.py +0 -0
  235. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/completers.py +0 -0
  236. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/drag_drop.py +0 -0
  237. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/images.py +0 -0
  238. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/input/paste.py +0 -0
  239. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/runner.py +0 -0
  240. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/terminal/__init__.py +0 -0
  241. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/terminal/color.py +0 -0
  242. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/terminal/control.py +0 -0
  243. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/terminal/image.py +0 -0
  244. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/terminal/notifier.py +0 -0
  245. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/terminal/progress_bar.py +0 -0
  246. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/tui/terminal/selector.py +0 -0
  247. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/__init__.py +0 -0
  248. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/common.py +0 -0
  249. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/core/__init__.py +0 -0
  250. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/core/display.py +0 -0
  251. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/core/input.py +0 -0
  252. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/debug_mode.py +0 -0
  253. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/terminal/__init__.py +0 -0
  254. {klaude_code-2.10.0 → klaude_code-2.10.2}/src/klaude_code/ui/terminal/title.py +0 -0
  255. {klaude_code-2.10.0 → klaude_code-2.10.2}/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.10.0
3
+ Version: 2.10.2
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.10.0"
7
+ version = "2.10.2"
8
8
  description = "Minimal code agent CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -12,6 +12,7 @@ from klaude_code.core.agent import Agent
12
12
  from klaude_code.core.agent_profile import (
13
13
  DefaultModelProfileProvider,
14
14
  VanillaModelProfileProvider,
15
+ WebModelProfileProvider,
15
16
  )
16
17
  from klaude_code.core.executor import Executor
17
18
  from klaude_code.core.manager import build_llm_clients
@@ -27,6 +28,7 @@ class AppInitConfig:
27
28
  model: str | None
28
29
  debug: bool
29
30
  vanilla: bool
31
+ web: bool = False
30
32
  debug_filters: set[DebugType] | None = None
31
33
 
32
34
 
@@ -74,6 +76,8 @@ async def initialize_app_components(
74
76
 
75
77
  if init_config.vanilla:
76
78
  model_profile_provider = VanillaModelProfileProvider()
79
+ elif init_config.web:
80
+ model_profile_provider = WebModelProfileProvider(config=config)
77
81
  else:
78
82
  model_profile_provider = DefaultModelProfileProvider(config=config)
79
83
 
@@ -200,6 +200,11 @@ def main_callback(
200
200
  help="Image generation mode (alias for --model banana)",
201
201
  rich_help_panel="LLM",
202
202
  ),
203
+ web: bool = typer.Option(
204
+ False,
205
+ "--web",
206
+ help="Enable web tools (WebFetch, WebSearch) for the main agent",
207
+ ),
203
208
  version: bool = typer.Option(
204
209
  False,
205
210
  "--version",
@@ -218,6 +223,10 @@ def main_callback(
218
223
  log(("Error: --banana cannot be combined with --vanilla", "red"))
219
224
  raise typer.Exit(2)
220
225
 
226
+ if vanilla and web:
227
+ log(("Error: --web cannot be combined with --vanilla", "red"))
228
+ raise typer.Exit(2)
229
+
221
230
  resume_by_id_value = resume_by_id.strip() if resume_by_id is not None else None
222
231
  if resume_by_id_value == "":
223
232
  log(("Error: --resume <id> cannot be empty", "red"))
@@ -347,6 +356,7 @@ def main_callback(
347
356
  model=chosen_model,
348
357
  debug=debug_enabled,
349
358
  vanilla=vanilla,
359
+ web=web,
350
360
  debug_filters=debug_filters,
351
361
  )
352
362
 
@@ -57,22 +57,14 @@ provider_list:
57
57
  reasoning_summary: concise
58
58
  cost: {input: 1.75, output: 14, cache_read: 0.17}
59
59
 
60
- - model_name: gpt-5.2-fast
61
- model_id: gpt-5.2
62
- context_limit: 400000
63
- verbosity: low
60
+ - model_name: gpt-5.2-codex
61
+ model_id: gpt-5.2-codex
64
62
  thinking:
65
- reasoning_effort: none
66
- cost: {input: 1.75, output: 14, cache_read: 0.17}
67
-
68
- - model_name: gpt-5.1-codex-max
69
- model_id: gpt-5.1-codex-max
70
- max_tokens: 128000
63
+ reasoning_effort: high
64
+ reasoning_summary: auto
71
65
  context_limit: 400000
72
- thinking:
73
- reasoning_effort: medium
74
- reasoning_summary: concise
75
- cost: {input: 1.25, output: 10, cache_read: 0.13}
66
+ max_tokens: 128000
67
+ cost: {input: 1.75, output: 14, cache_read: 0.17}
76
68
 
77
69
 
78
70
  - provider_name: openrouter
@@ -80,6 +72,15 @@ provider_list:
80
72
  api_key: ${OPENROUTER_API_KEY}
81
73
  model_list:
82
74
 
75
+ - model_name: gpt-5.2-codex
76
+ model_id: gpt-5.2-codex
77
+ thinking:
78
+ reasoning_effort: high
79
+ reasoning_summary: auto
80
+ context_limit: 400000
81
+ max_tokens: 128000
82
+ cost: {input: 1.75, output: 14, cache_read: 0.17}
83
+
83
84
  - model_name: gpt-5.2-high
84
85
  model_id: openai/gpt-5.2
85
86
  max_tokens: 128000
@@ -130,12 +130,6 @@ def load_system_prompt(
130
130
  ) -> str:
131
131
  """Get system prompt content for the given model and sub-agent type."""
132
132
 
133
- # For codex_oauth protocol, use dynamic prompts from GitHub (no additions).
134
- if protocol == llm_param.LLMClientProtocol.CODEX_OAUTH:
135
- from klaude_code.llm.openai_codex.prompt_sync import get_codex_instructions
136
-
137
- return get_codex_instructions(model_name)
138
-
139
133
  # For antigravity protocol, use exact prompt without any additions.
140
134
  if protocol == llm_param.LLMClientProtocol.ANTIGRAVITY:
141
135
  return _load_prompt_by_path(ANTIGRAVITY_PROMPT_PATH)
@@ -305,3 +299,26 @@ class VanillaModelProfileProvider(ModelProfileProvider):
305
299
  if output_schema:
306
300
  return with_structured_output(profile, output_schema)
307
301
  return profile
302
+
303
+
304
+ class WebModelProfileProvider(DefaultModelProfileProvider):
305
+ """Provider that adds web tools to the main agent."""
306
+
307
+ def build_profile(
308
+ self,
309
+ llm_client: LLMClientABC,
310
+ sub_agent_type: tools.SubAgentType | None = None,
311
+ *,
312
+ output_schema: dict[str, Any] | None = None,
313
+ ) -> AgentProfile:
314
+ profile = super().build_profile(llm_client, sub_agent_type, output_schema=output_schema)
315
+ # Only add web tools for main agent (not sub-agents)
316
+ if sub_agent_type is None:
317
+ web_tools = get_tool_schemas([tools.WEB_FETCH, tools.WEB_SEARCH])
318
+ return AgentProfile(
319
+ llm_client=profile.llm_client,
320
+ system_prompt=profile.system_prompt,
321
+ tools=[*profile.tools, *web_tools],
322
+ reminders=profile.reminders,
323
+ )
324
+ return profile
@@ -17,7 +17,7 @@ You are a web research subagent that searches and fetches web content to provide
17
17
  - HTML pages are automatically converted to Markdown
18
18
  - JSON responses are auto-formatted with indentation
19
19
  - Other text content returned as-is
20
- - **Content is always saved to a local file** - path shown in `[Web content saved to ...]` at output start
20
+ - **Content is always saved to a local file** - path shown in `[Full content saved to ...]` at output start
21
21
 
22
22
  ## Tool Usage Strategy
23
23
 
@@ -54,7 +54,7 @@ Balance efficiency with thoroughness. For open-ended questions (e.g., "recommend
54
54
  ## Response Guidelines
55
55
 
56
56
  - Only your last message is returned to the main agent
57
- - Include the file path from `[Web content saved to ...]` so the main agent can access full content
57
+ - Include the file path from `[Full content saved to ...]` so the main agent can access full content
58
58
  - **DO NOT copy full web page content** - the main agent can read the saved files directly
59
59
  - Provide a concise summary/analysis of key findings
60
60
  - Lead with the most recent info for evolving topics
@@ -227,11 +227,11 @@ class HeadTailOffloadStrategy(OffloadStrategy):
227
227
  if self._should_offload(needs_truncation):
228
228
  offloaded_path = self._save_to_file(output, tool_call)
229
229
 
230
- # Prefer line-based truncation if line limit exceeded
231
- if needs_line_truncation:
232
- truncated_output, hidden = self._truncate_by_lines(output, lines, offloaded_path)
233
- else:
230
+ # Prefer char-based truncation if char limit exceeded (stricter limit)
231
+ if needs_char_truncation:
234
232
  truncated_output, hidden = self._truncate_by_chars(output, offloaded_path)
233
+ else:
234
+ truncated_output, hidden = self._truncate_by_lines(output, lines, offloaded_path)
235
235
 
236
236
  return OffloadResult(
237
237
  output=truncated_output,
@@ -1,8 +1,9 @@
1
1
  Fetch content from a URL and return it in a readable format.
2
2
 
3
3
  The tool automatically processes the response based on Content-Type:
4
+
4
5
  - HTML pages are converted to Markdown for easier reading
5
6
  - JSON responses are formatted with indentation
6
7
  - Markdown and other text content is returned as-is
7
8
 
8
- Content is always saved to a local file. The file path is shown at the start of the output in `[Web content saved to ...]` format. For large content that gets truncated, you can read the saved file directly.
9
+ Content is always saved to a local file. The file path is shown at the start of the output in `[Full content saved to ...]` format. For large content that gets truncated, you can read the saved file directly.
@@ -235,7 +235,7 @@ class WebFetchTool(ToolABC):
235
235
  text = _decode_content(data, charset)
236
236
  processed = _process_content(content_type, text)
237
237
  saved_path = _save_text_content(url, processed)
238
- output = f"[Web content saved to {saved_path}]\n\n{processed}" if saved_path else processed
238
+ output = f"[Full content saved to {saved_path}]\n\n{processed}" if saved_path else processed
239
239
 
240
240
  return message.ToolResultMessage(
241
241
  status="success",
@@ -196,8 +196,7 @@ class TurnExecutor:
196
196
  ):
197
197
  # Discard partial message if it only contains thinking parts
198
198
  has_non_thinking = any(
199
- not isinstance(part, message.ThinkingTextPart)
200
- for part in self._turn_result.assistant_message.parts
199
+ not isinstance(part, message.ThinkingTextPart) for part in self._turn_result.assistant_message.parts
201
200
  )
202
201
  if has_non_thinking:
203
202
  session_ctx.append_history([self._turn_result.assistant_message])
@@ -146,28 +146,6 @@ class CodexClient(LLMClientABC):
146
146
  )
147
147
  except (openai.OpenAIError, httpx.HTTPError) as e:
148
148
  error_message = f"{e.__class__.__name__} {e!s}"
149
-
150
- # Check for invalid instruction error and invalidate prompt cache
151
- if _is_invalid_instruction_error(e) and param.model_id:
152
- _invalidate_prompt_cache_for_model(param.model_id)
153
-
154
149
  return error_llm_stream(metadata_tracker, error=error_message)
155
150
 
156
151
  return ResponsesLLMStream(stream, param=param, metadata_tracker=metadata_tracker)
157
-
158
-
159
- def _is_invalid_instruction_error(e: Exception) -> bool:
160
- """Check if the error is related to invalid instructions."""
161
- error_str = str(e).lower()
162
- return "invalid instruction" in error_str or "invalid_instruction" in error_str
163
-
164
-
165
- def _invalidate_prompt_cache_for_model(model_id: str) -> None:
166
- """Invalidate the cached prompt for a model to force refresh."""
167
- from klaude_code.llm.openai_codex.prompt_sync import invalidate_cache
168
-
169
- log_debug(
170
- f"Invalidating prompt cache for model {model_id} due to invalid instruction error",
171
- debug_type=DebugType.GENERAL,
172
- )
173
- invalidate_cache(model_id)
@@ -17,7 +17,7 @@ from klaude_code.llm.client import LLMClientABC, LLMStreamABC
17
17
  from klaude_code.llm.input_common import apply_config_defaults
18
18
  from klaude_code.llm.openai_compatible.input import convert_tool_schema
19
19
  from klaude_code.llm.openai_compatible.stream import OpenAILLMStream
20
- from klaude_code.llm.openrouter.input import convert_history_to_input, is_claude_model
20
+ from klaude_code.llm.openrouter.input import convert_history_to_input, is_claude_model, is_xai_model
21
21
  from klaude_code.llm.openrouter.reasoning import ReasoningStreamHandler
22
22
  from klaude_code.llm.registry import register
23
23
  from klaude_code.llm.usage import MetadataTracker, error_llm_stream
@@ -70,6 +70,9 @@ def build_payload(
70
70
  f"{ANTHROPIC_BETA_FINE_GRAINED_TOOL_STREAMING},{ANTHROPIC_BETA_INTERLEAVED_THINKING}"
71
71
  )
72
72
 
73
+ if is_xai_model(param.model_id):
74
+ extra_body["plugins"] = [{"id": "web", "engine": "native"}]
75
+
73
76
  payload: CompletionCreateParamsStreaming = {
74
77
  "model": str(param.model_id),
75
78
  "tool_choice": "auto",
@@ -34,6 +34,12 @@ def is_gemini_model(model_name: str | None) -> bool:
34
34
  return model_name is not None and model_name.startswith("google/gemini")
35
35
 
36
36
 
37
+ def is_xai_model(model_name: str | None) -> bool:
38
+ """Return True if the model name represents an xAI model."""
39
+
40
+ return model_name is not None and model_name.startswith("x-ai/")
41
+
42
+
37
43
  def _assistant_message_to_openrouter(
38
44
  msg: message.AssistantMessage, model_name: str | None
39
45
  ) -> chat.ChatCompletionMessageParam:
@@ -539,6 +539,21 @@ __KLAUDE_CODE__</textarea
539
539
  </svg>
540
540
  <span>SVG</span>
541
541
  </button>
542
+ <button class="tool-btn" id="btn-download-png" title="Download PNG">
543
+ <svg
544
+ width="16"
545
+ height="16"
546
+ viewBox="0 0 24 24"
547
+ fill="none"
548
+ stroke="currentColor"
549
+ stroke-width="2"
550
+ >
551
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
552
+ <polyline points="7 10 12 15 17 10"></polyline>
553
+ <line x1="12" y1="15" x2="12" y2="3"></line>
554
+ </svg>
555
+ <span>PNG</span>
556
+ </button>
542
557
  </div>
543
558
  </div>
544
559
 
@@ -570,6 +585,7 @@ __KLAUDE_CODE__</textarea
570
585
  zoomOut: document.getElementById("btn-zoom-out"),
571
586
  reset: document.getElementById("btn-reset"),
572
587
  download: document.getElementById("btn-download"),
588
+ downloadPng: document.getElementById("btn-download-png"),
573
589
  copy: document.getElementById("btn-copy-code"),
574
590
  collapse: document.getElementById("btn-collapse"),
575
591
  expand: document.getElementById("btn-expand"),
@@ -894,6 +910,75 @@ __KLAUDE_CODE__</textarea
894
910
  URL.revokeObjectURL(url);
895
911
  };
896
912
 
913
+ els.btns.downloadPng.onclick = async () => {
914
+ const svg = els.canvas.querySelector("svg");
915
+ if (!svg) return;
916
+
917
+ const clone = svg.cloneNode(true);
918
+
919
+ const bbox = svg.getBBox();
920
+ const width = bbox.width + 40;
921
+ const height = bbox.height + 40;
922
+
923
+ clone.setAttribute("width", width);
924
+ clone.setAttribute("height", height);
925
+ clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
926
+ clone.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
927
+
928
+ // Remove foreignObject elements (they cause tainted canvas)
929
+ clone.querySelectorAll("foreignObject").forEach((fo) => {
930
+ const text = fo.textContent || "";
931
+ const parent = fo.parentNode;
932
+ if (parent) {
933
+ const textEl = document.createElementNS("http://www.w3.org/2000/svg", "text");
934
+ textEl.textContent = text;
935
+ textEl.setAttribute("font-family", "sans-serif");
936
+ textEl.setAttribute("font-size", "14");
937
+ parent.replaceChild(textEl, fo);
938
+ }
939
+ });
940
+
941
+ // Add white background rect
942
+ const bgRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
943
+ bgRect.setAttribute("width", "100%");
944
+ bgRect.setAttribute("height", "100%");
945
+ bgRect.setAttribute("fill", "white");
946
+ clone.insertBefore(bgRect, clone.firstChild);
947
+
948
+ const svgData = new XMLSerializer().serializeToString(clone);
949
+ const svgBase64 = btoa(unescape(encodeURIComponent(svgData)));
950
+ const dataUrl = "data:image/svg+xml;base64," + svgBase64;
951
+
952
+ const img = new Image();
953
+ img.onload = () => {
954
+ const scale = 2;
955
+ const canvas = document.createElement("canvas");
956
+ canvas.width = width * scale;
957
+ canvas.height = height * scale;
958
+
959
+ const ctx = canvas.getContext("2d");
960
+ ctx.fillStyle = "white";
961
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
962
+ ctx.scale(scale, scale);
963
+ ctx.drawImage(img, 0, 0);
964
+
965
+ canvas.toBlob((blob) => {
966
+ const pngUrl = URL.createObjectURL(blob);
967
+ const a = document.createElement("a");
968
+ a.href = pngUrl;
969
+ a.download = "diagram.png";
970
+ document.body.appendChild(a);
971
+ a.click();
972
+ a.remove();
973
+ URL.revokeObjectURL(pngUrl);
974
+ }, "image/png");
975
+ };
976
+ img.onerror = (e) => {
977
+ console.error("PNG export failed:", e);
978
+ };
979
+ img.src = dataUrl;
980
+ };
981
+
897
982
  els.btns.copy.onclick = async () => {
898
983
  try {
899
984
  await navigator.clipboard.writeText(els.textarea.value);
@@ -47,13 +47,16 @@ def _render_task_metadata_block(
47
47
  if metadata.usage is not None:
48
48
  # Tokens: ↑37k ◎5k ↓907 ∿45k ⌗ 100
49
49
  token_text = Text()
50
+ input_tokens = max(metadata.usage.input_tokens - metadata.usage.cached_tokens, 0)
51
+ output_tokens = max(metadata.usage.output_tokens - metadata.usage.reasoning_tokens, 0)
52
+
50
53
  token_text.append("↑", style=ThemeKey.METADATA)
51
- token_text.append(format_number(metadata.usage.input_tokens), style=ThemeKey.METADATA)
54
+ token_text.append(format_number(input_tokens), style=ThemeKey.METADATA)
52
55
  if metadata.usage.cached_tokens > 0:
53
56
  token_text.append(" ◎", style=ThemeKey.METADATA)
54
57
  token_text.append(format_number(metadata.usage.cached_tokens), style=ThemeKey.METADATA)
55
58
  token_text.append(" ↓", style=ThemeKey.METADATA)
56
- token_text.append(format_number(metadata.usage.output_tokens), style=ThemeKey.METADATA)
59
+ token_text.append(format_number(output_tokens), style=ThemeKey.METADATA)
57
60
  if metadata.usage.reasoning_tokens > 0:
58
61
  token_text.append(" ∿", style=ThemeKey.METADATA)
59
62
  token_text.append(format_number(metadata.usage.reasoning_tokens), style=ThemeKey.METADATA)
@@ -609,6 +609,9 @@ class MarkdownStream:
609
609
 
610
610
  live_text_to_set: Text | None = None
611
611
  if not final and MARKDOWN_STREAM_LIVE_REPAINT_ENABLED and self._live_sink is not None:
612
+ # Only update live area after we have rendered at least one stable block
613
+ if not self._stable_rendered_lines:
614
+ return
612
615
  # When nothing is stable yet, we still want to show incremental output.
613
616
  # Apply the mark only for the first (all-live) frame so it stays anchored
614
617
  # to the first visible line of the full message.
@@ -270,10 +270,10 @@ def get_theme(theme: str | None = None) -> Themes:
270
270
  # ASSISTANT
271
271
  ThemeKey.ASSISTANT_MESSAGE_MARK.value: "bold",
272
272
  # METADATA
273
- ThemeKey.METADATA.value: palette.blue,
274
- ThemeKey.METADATA_DIM.value: "dim " + palette.blue,
275
- ThemeKey.METADATA_BOLD.value: "bold " + palette.blue,
276
- ThemeKey.METADATA_ITALIC.value: "italic " + palette.blue,
273
+ ThemeKey.METADATA.value: palette.grey1,
274
+ ThemeKey.METADATA_DIM.value: "dim " + palette.grey1,
275
+ ThemeKey.METADATA_BOLD.value: "bold " + palette.grey1,
276
+ ThemeKey.METADATA_ITALIC.value: "italic " + palette.grey1,
277
277
  # STATUS
278
278
  ThemeKey.STATUS_SPINNER.value: palette.blue,
279
279
  ThemeKey.STATUS_TEXT.value: palette.blue,
@@ -351,8 +351,8 @@ def get_theme(theme: str | None = None) -> Themes:
351
351
  # it is used while rendering assistant output.
352
352
  "markdown.thinking": "italic " + palette.grey2,
353
353
  "markdown.thinking.tag": palette.grey2,
354
- "markdown.code.border": palette.grey3,
355
- "markdown.code.fence": palette.grey3,
354
+ "markdown.code.border": palette.grey2,
355
+ "markdown.code.fence": palette.grey2,
356
356
  "markdown.code.fence.title": palette.grey1,
357
357
  # Used by ThinkingMarkdown when rendering `<thinking>` blocks.
358
358
  "markdown.code.block": palette.grey1,
@@ -379,8 +379,9 @@ def get_theme(theme: str | None = None) -> Themes:
379
379
  "markdown.strong": "italic " + palette.grey1,
380
380
  "markdown.code": palette.grey1 + " italic on " + palette.code_background,
381
381
  "markdown.code.block": palette.grey2,
382
- "markdown.code.fence": palette.grey3,
383
- "markdown.code.border": palette.grey3,
382
+ "markdown.code.fence": palette.grey2,
383
+ "markdown.code.fence.title": palette.grey1,
384
+ "markdown.code.border": palette.grey2,
384
385
  "markdown.thinking.tag": palette.grey2 + " dim",
385
386
  "markdown.h1": "bold reverse",
386
387
  "markdown.h1.border": palette.grey3,
@@ -26,38 +26,3 @@ def normalize_thinking_content(content: str) -> str:
26
26
  text = text.replace("**\n\n", "** \n")
27
27
 
28
28
  return text
29
-
30
-
31
- def extract_last_bold_header(text: str) -> str | None:
32
- """Extract the latest complete bold header ("**…**") from text.
33
-
34
- We treat a bold segment as a "header" only if it appears at the beginning
35
- of a line (ignoring leading whitespace). This avoids picking up incidental
36
- emphasis inside paragraphs.
37
-
38
- Returns None if no complete bold segment is available yet.
39
- """
40
-
41
- last: str | None = None
42
- i = 0
43
- while True:
44
- start = text.find("**", i)
45
- if start < 0:
46
- break
47
-
48
- line_start = text.rfind("\n", 0, start) + 1
49
- if text[line_start:start].strip():
50
- i = start + 2
51
- continue
52
-
53
- end = text.find("**", start + 2)
54
- if end < 0:
55
- break
56
-
57
- inner = " ".join(text[start + 2 : end].split())
58
- if inner and "\n" not in inner:
59
- last = inner
60
-
61
- i = end + 2
62
-
63
- return last
@@ -4,6 +4,7 @@ from typing import Any, cast
4
4
 
5
5
  from rich import box
6
6
  from rich.console import Group, RenderableType
7
+ from rich.padding import Padding
7
8
  from rich.panel import Panel
8
9
  from rich.style import Style
9
10
  from rich.text import Text
@@ -166,7 +167,6 @@ def render_bash_tool_call(arguments: str) -> RenderableType:
166
167
  if isinstance(command, str) and command.strip():
167
168
  cmd_str = command.strip()
168
169
  highlighted = highlight_bash_command(cmd_str)
169
- highlighted.stylize(ThemeKey.CODE_BACKGROUND)
170
170
 
171
171
  display_line_count = len(highlighted.plain.splitlines())
172
172
 
@@ -189,7 +189,8 @@ def render_bash_tool_call(arguments: str) -> RenderableType:
189
189
  highlighted.append(f" {timeout_ms // 1000}s", style=ThemeKey.TOOL_TIMEOUT)
190
190
  else:
191
191
  highlighted.append(f" {timeout_ms}ms", style=ThemeKey.TOOL_TIMEOUT)
192
- return _render_tool_call_tree(mark=MARK_BASH, tool_name=tool_name, details=highlighted)
192
+ padded = Padding(highlighted, pad=0, style=ThemeKey.CODE_BACKGROUND, expand=False)
193
+ return _render_tool_call_tree(mark=MARK_BASH, tool_name=tool_name, details=padded)
193
194
  else:
194
195
  summary = Text("", ThemeKey.TOOL_PARAM)
195
196
  if isinstance(timeout_ms, int):
@@ -25,7 +25,7 @@ def render_at_and_skill_patterns(
25
25
  available_skill_names: set[str] | None = None,
26
26
  ) -> Text:
27
27
  """Render text with highlighted @file and $skill patterns."""
28
- result = Text(text, style=other_style)
28
+ result = Text(text, style=other_style, overflow="fold")
29
29
  for match in INLINE_RENDER_PATTERN.finditer(text):
30
30
  skill_name = match.group(1)
31
31
  if skill_name is None:
@@ -79,6 +79,7 @@ def render_user_input(content: str) -> RenderableType:
79
79
  render_at_and_skill_patterns(splits[1], available_skill_names=available_skill_names)
80
80
  if len(splits) > 1
81
81
  else Text(""),
82
+ overflow="fold",
82
83
  )
83
84
  renderables.append(line_text)
84
85
  continue
@@ -100,4 +100,4 @@ class TUIDisplay(DisplayABC):
100
100
  with contextlib.suppress(Exception):
101
101
  self._renderer.spinner_stop()
102
102
  with contextlib.suppress(Exception):
103
- self._renderer.stop_bottom_live()
103
+ self._renderer.stop_bottom_live()