klaude-code 1.2.7__tar.gz → 1.2.8__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 (175) hide show
  1. {klaude_code-1.2.7 → klaude_code-1.2.8}/PKG-INFO +1 -1
  2. {klaude_code-1.2.7 → klaude_code-1.2.8}/pyproject.toml +1 -1
  3. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/task.py +0 -4
  4. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/turn.py +66 -41
  5. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/anthropic/client.py +2 -2
  6. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/codex/client.py +39 -26
  7. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openai_compatible/client.py +2 -2
  8. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/responses/client.py +35 -32
  9. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/events.py +1 -0
  10. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/model.py +0 -2
  11. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/session/session.py +5 -0
  12. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/renderer.py +2 -0
  13. {klaude_code-1.2.7 → klaude_code-1.2.8}/README.md +0 -0
  14. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/__init__.py +0 -0
  15. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/auth/__init__.py +0 -0
  16. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/auth/codex/__init__.py +0 -0
  17. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/auth/codex/exceptions.py +0 -0
  18. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/auth/codex/jwt_utils.py +0 -0
  19. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/auth/codex/oauth.py +0 -0
  20. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/auth/codex/token_manager.py +0 -0
  21. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/cli/__init__.py +0 -0
  22. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/cli/main.py +0 -0
  23. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/cli/runtime.py +0 -0
  24. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/cli/session_cmd.py +0 -0
  25. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/__init__.py +0 -0
  26. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/clear_cmd.py +0 -0
  27. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/command_abc.py +0 -0
  28. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/diff_cmd.py +0 -0
  29. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/export_cmd.py +0 -0
  30. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/help_cmd.py +0 -0
  31. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/model_cmd.py +0 -0
  32. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/prompt-dev-docs-update.md +0 -0
  33. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/prompt-dev-docs.md +0 -0
  34. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/prompt-init.md +0 -0
  35. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/prompt_command.py +0 -0
  36. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/refresh_cmd.py +0 -0
  37. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/registry.py +0 -0
  38. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/status_cmd.py +0 -0
  39. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/command/terminal_setup_cmd.py +0 -0
  40. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/config/__init__.py +0 -0
  41. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/config/config.py +0 -0
  42. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/config/list_model.py +0 -0
  43. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/config/select_model.py +0 -0
  44. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/const/__init__.py +0 -0
  45. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/__init__.py +0 -0
  46. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/agent.py +0 -0
  47. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/executor.py +0 -0
  48. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/manager/__init__.py +0 -0
  49. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/manager/agent_manager.py +0 -0
  50. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/manager/llm_clients.py +0 -0
  51. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/manager/llm_clients_builder.py +0 -0
  52. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/manager/sub_agent_manager.py +0 -0
  53. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompt.py +0 -0
  54. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-claude-code.md +0 -0
  55. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1-codex-max.md +0 -0
  56. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-codex-gpt-5-1.md +0 -0
  57. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-gemini.md +0 -0
  58. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-subagent-explore.md +0 -0
  59. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-subagent-oracle.md +0 -0
  60. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-subagent-webfetch.md +0 -0
  61. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/prompts/prompt-subagent.md +0 -0
  62. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/reminders.py +0 -0
  63. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/__init__.py +0 -0
  64. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/__init__.py +0 -0
  65. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/apply_patch.py +0 -0
  66. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/apply_patch_tool.md +0 -0
  67. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/apply_patch_tool.py +0 -0
  68. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/edit_tool.md +0 -0
  69. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/edit_tool.py +0 -0
  70. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/multi_edit_tool.md +0 -0
  71. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/multi_edit_tool.py +0 -0
  72. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/read_tool.md +0 -0
  73. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/read_tool.py +0 -0
  74. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/write_tool.md +0 -0
  75. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/file/write_tool.py +0 -0
  76. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/memory/__init__.py +0 -0
  77. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/memory/memory_tool.md +0 -0
  78. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/memory/memory_tool.py +0 -0
  79. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/memory/skill_loader.py +0 -0
  80. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/memory/skill_tool.md +0 -0
  81. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/memory/skill_tool.py +0 -0
  82. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/shell/__init__.py +0 -0
  83. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/shell/bash_tool.md +0 -0
  84. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/shell/bash_tool.py +0 -0
  85. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/shell/command_safety.py +0 -0
  86. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/sub_agent_tool.py +0 -0
  87. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/todo/__init__.py +0 -0
  88. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/todo/todo_write_tool.md +0 -0
  89. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/todo/todo_write_tool.py +0 -0
  90. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/todo/update_plan_tool.md +0 -0
  91. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/todo/update_plan_tool.py +0 -0
  92. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/tool_abc.py +0 -0
  93. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/tool_context.py +0 -0
  94. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/tool_registry.py +0 -0
  95. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/tool_runner.py +0 -0
  96. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/truncation.py +0 -0
  97. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/web/__init__.py +0 -0
  98. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/web/mermaid_tool.md +0 -0
  99. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/web/mermaid_tool.py +0 -0
  100. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/web/web_fetch_tool.md +0 -0
  101. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/core/tool/web/web_fetch_tool.py +0 -0
  102. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/__init__.py +0 -0
  103. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/anthropic/__init__.py +0 -0
  104. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/anthropic/input.py +0 -0
  105. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/client.py +0 -0
  106. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/codex/__init__.py +0 -0
  107. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/input_common.py +0 -0
  108. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openai_compatible/__init__.py +0 -0
  109. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openai_compatible/input.py +0 -0
  110. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -0
  111. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openrouter/__init__.py +0 -0
  112. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openrouter/client.py +0 -0
  113. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openrouter/input.py +0 -0
  114. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/openrouter/reasoning_handler.py +0 -0
  115. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/registry.py +0 -0
  116. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/responses/__init__.py +0 -0
  117. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/responses/input.py +0 -0
  118. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/llm/usage.py +0 -0
  119. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/__init__.py +0 -0
  120. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/commands.py +0 -0
  121. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/llm_param.py +0 -0
  122. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/op.py +0 -0
  123. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/op_handler.py +0 -0
  124. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/sub_agent.py +0 -0
  125. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/protocol/tools.py +0 -0
  126. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/session/__init__.py +0 -0
  127. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/session/export.py +0 -0
  128. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/session/selector.py +0 -0
  129. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/session/templates/export_session.html +0 -0
  130. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/trace/__init__.py +0 -0
  131. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/trace/log.py +0 -0
  132. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/__init__.py +0 -0
  133. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/core/__init__.py +0 -0
  134. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/core/display.py +0 -0
  135. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/core/input.py +0 -0
  136. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/core/stage_manager.py +0 -0
  137. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/__init__.py +0 -0
  138. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/debug/__init__.py +0 -0
  139. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/debug/display.py +0 -0
  140. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/exec/__init__.py +0 -0
  141. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/exec/display.py +0 -0
  142. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/__init__.py +0 -0
  143. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/clipboard.py +0 -0
  144. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/completers.py +0 -0
  145. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/display.py +0 -0
  146. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/event_handler.py +0 -0
  147. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/input_prompt_toolkit.py +0 -0
  148. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/modes/repl/key_bindings.py +0 -0
  149. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/__init__.py +0 -0
  150. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/assistant.py +0 -0
  151. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/common.py +0 -0
  152. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/developer.py +0 -0
  153. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/diffs.py +0 -0
  154. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/errors.py +0 -0
  155. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/metadata.py +0 -0
  156. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/sub_agent.py +0 -0
  157. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/thinking.py +0 -0
  158. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/tools.py +0 -0
  159. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/renderers/user_input.py +0 -0
  160. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/rich/__init__.py +0 -0
  161. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/rich/live.py +0 -0
  162. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/rich/markdown.py +0 -0
  163. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/rich/quote.py +0 -0
  164. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/rich/searchable_text.py +0 -0
  165. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/rich/status.py +0 -0
  166. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/rich/theme.py +0 -0
  167. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/terminal/__init__.py +0 -0
  168. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/terminal/color.py +0 -0
  169. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/terminal/control.py +0 -0
  170. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/terminal/notifier.py +0 -0
  171. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/terminal/progress_bar.py +0 -0
  172. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/utils/__init__.py +0 -0
  173. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/utils/common.py +0 -0
  174. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/ui/utils/debouncer.py +0 -0
  175. {klaude_code-1.2.7 → klaude_code-1.2.8}/src/klaude_code/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: klaude-code
3
- Version: 1.2.7
3
+ Version: 1.2.8
4
4
  Summary: Add your description here
5
5
  Requires-Dist: anthropic>=0.66.0
6
6
  Requires-Dist: openai>=1.102.0
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "klaude-code"
7
- version = "1.2.7"
7
+ version = "1.2.8"
8
8
  description = "Add your description here"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -79,10 +79,6 @@ class MetadataAccumulator:
79
79
  accumulated.model_name = turn_metadata.model_name
80
80
  if turn_metadata.response_id:
81
81
  accumulated.response_id = turn_metadata.response_id
82
- if turn_metadata.status is not None:
83
- accumulated.status = turn_metadata.status
84
- if turn_metadata.error_reason is not None:
85
- accumulated.error_reason = turn_metadata.error_reason
86
82
 
87
83
  def finalize(self, task_duration_s: float) -> model.ResponseMetadataItem:
88
84
  """Return the final accumulated metadata with computed throughput and duration."""
@@ -38,6 +38,16 @@ class TurnExecutionContext:
38
38
  todo_context: TodoContext
39
39
 
40
40
 
41
+ @dataclass
42
+ class TurnResult:
43
+ """Aggregated state produced while executing a turn."""
44
+
45
+ reasoning_items: list[model.ReasoningTextItem | model.ReasoningEncryptedItem]
46
+ assistant_message: model.AssistantMessageItem | None
47
+ tool_calls: list[model.ToolCallItem]
48
+ stream_error: model.StreamErrorItem | None
49
+
50
+
41
51
  def build_events_from_tool_executor_event(session_id: str, event: ToolExecutorEvent) -> list[events.Event]:
42
52
  """Translate internal tool executor events into public protocol events."""
43
53
 
@@ -113,12 +123,34 @@ class TurnExecutor:
113
123
 
114
124
  yield events.TurnStartEvent(session_id=ctx.session_id)
115
125
 
116
- turn_reasoning_items: list[model.ReasoningTextItem | model.ReasoningEncryptedItem] = []
117
- turn_assistant_message: model.AssistantMessageItem | None = None
118
- turn_tool_calls: list[model.ToolCallItem] = []
119
- response_failed = False
120
- error_message: str | None = None
126
+ turn_result = TurnResult(
127
+ reasoning_items=[],
128
+ assistant_message=None,
129
+ tool_calls=[],
130
+ stream_error=None,
131
+ )
132
+
133
+ async for event in self._consume_llm_stream(turn_result):
134
+ yield event
135
+
136
+ if turn_result.stream_error is not None:
137
+ ctx.append_history([turn_result.stream_error])
138
+ yield events.TurnEndEvent(session_id=ctx.session_id)
139
+ raise TurnError(turn_result.stream_error.error)
140
+
141
+ self._append_success_history(turn_result)
142
+ self._has_tool_call = bool(turn_result.tool_calls)
143
+
144
+ if turn_result.tool_calls:
145
+ async for ui_event in self._run_tool_executor(turn_result.tool_calls):
146
+ yield ui_event
147
+
148
+ yield events.TurnEndEvent(session_id=ctx.session_id)
121
149
 
150
+ async def _consume_llm_stream(self, turn_result: TurnResult) -> AsyncGenerator[events.Event, None]:
151
+ """Stream events from LLM and update turn_result in place."""
152
+
153
+ ctx = self._context
122
154
  async for response_item in ctx.llm_client.call(
123
155
  llm_param.LLMCallParameter(
124
156
  input=ctx.get_conversation_history(),
@@ -136,16 +168,16 @@ class TurnExecutor:
136
168
  )
137
169
  match response_item:
138
170
  case model.StartItem():
139
- pass
171
+ continue
140
172
  case model.ReasoningTextItem() as item:
141
- turn_reasoning_items.append(item)
173
+ turn_result.reasoning_items.append(item)
142
174
  yield events.ThinkingEvent(
143
175
  content=item.content,
144
176
  response_id=item.response_id,
145
177
  session_id=ctx.session_id,
146
178
  )
147
179
  case model.ReasoningEncryptedItem() as item:
148
- turn_reasoning_items.append(item)
180
+ turn_result.reasoning_items.append(item)
149
181
  case model.AssistantMessageDelta() as item:
150
182
  yield events.AssistantMessageDeltaEvent(
151
183
  content=item.content,
@@ -153,7 +185,7 @@ class TurnExecutor:
153
185
  session_id=ctx.session_id,
154
186
  )
155
187
  case model.AssistantMessageItem() as item:
156
- turn_assistant_message = item
188
+ turn_result.assistant_message = item
157
189
  yield events.AssistantMessageEvent(
158
190
  content=item.content or "",
159
191
  response_id=item.response_id,
@@ -164,13 +196,8 @@ class TurnExecutor:
164
196
  session_id=ctx.session_id,
165
197
  metadata=item,
166
198
  )
167
- status = item.status
168
- if status is not None and status != "completed":
169
- response_failed = True
170
- error_message = f"Response status: {status}"
171
199
  case model.StreamErrorItem() as item:
172
- response_failed = True
173
- error_message = item.error
200
+ turn_result.stream_error = item
174
201
  log_debug(
175
202
  "[StreamError]",
176
203
  item.error,
@@ -186,35 +213,33 @@ class TurnExecutor:
186
213
  arguments="",
187
214
  )
188
215
  case model.ToolCallItem() as item:
189
- turn_tool_calls.append(item)
216
+ turn_result.tool_calls.append(item)
190
217
  case _:
191
- pass
218
+ continue
192
219
 
193
- if response_failed:
194
- yield events.TurnEndEvent(session_id=ctx.session_id)
195
- raise TurnError(error_message or "Turn failed")
196
-
197
- # Append to history only on success
198
- if turn_reasoning_items:
199
- ctx.append_history(turn_reasoning_items)
200
- if turn_assistant_message:
201
- ctx.append_history([turn_assistant_message])
202
- if turn_tool_calls:
203
- ctx.append_history(turn_tool_calls)
204
- self._has_tool_call = True
205
-
206
- # Execute tools
207
- if turn_tool_calls:
208
- with tool_context(ctx.file_tracker, ctx.todo_context):
209
- executor = ToolExecutor(
210
- registry=ctx.tool_registry,
211
- append_history=ctx.append_history,
212
- )
213
- self._tool_executor = executor
220
+ def _append_success_history(self, turn_result: TurnResult) -> None:
221
+ """Persist successful turn artifacts to conversation history."""
222
+ ctx = self._context
223
+ if turn_result.reasoning_items:
224
+ ctx.append_history(turn_result.reasoning_items)
225
+ if turn_result.assistant_message:
226
+ ctx.append_history([turn_result.assistant_message])
227
+ if turn_result.tool_calls:
228
+ ctx.append_history(turn_result.tool_calls)
214
229
 
215
- async for exec_event in executor.run_tools(turn_tool_calls):
230
+ async def _run_tool_executor(self, tool_calls: list[model.ToolCallItem]) -> AsyncGenerator[events.Event, None]:
231
+ """Run tools for the turn and translate executor events to UI events."""
232
+
233
+ ctx = self._context
234
+ with tool_context(ctx.file_tracker, ctx.todo_context):
235
+ executor = ToolExecutor(
236
+ registry=ctx.tool_registry,
237
+ append_history=ctx.append_history,
238
+ )
239
+ self._tool_executor = executor
240
+ try:
241
+ async for exec_event in executor.run_tools(tool_calls):
216
242
  for ui_event in build_events_from_tool_executor_event(ctx.session_id, exec_event):
217
243
  yield ui_event
244
+ finally:
218
245
  self._tool_executor = None
219
-
220
- yield events.TurnEndEvent(session_id=ctx.session_id)
@@ -5,7 +5,7 @@ from typing import override
5
5
 
6
6
  import anthropic
7
7
  import httpx
8
- from anthropic import RateLimitError
8
+ from anthropic import APIError
9
9
  from anthropic.types.beta.beta_input_json_delta import BetaInputJSONDelta
10
10
  from anthropic.types.beta.beta_raw_content_block_delta_event import BetaRawContentBlockDeltaEvent
11
11
  from anthropic.types.beta.beta_raw_content_block_start_event import BetaRawContentBlockStartEvent
@@ -217,5 +217,5 @@ class AnthropicClient(LLMClientABC):
217
217
  )
218
218
  case _:
219
219
  pass
220
- except RateLimitError as e:
220
+ except (APIError, httpx.HTTPError) as e:
221
221
  yield model.StreamErrorItem(error=f"{e.__class__.__name__} {str(e)}")
@@ -5,6 +5,7 @@ from collections.abc import AsyncGenerator
5
5
  from typing import override
6
6
 
7
7
  import httpx
8
+ import openai
8
9
  from openai import AsyncOpenAI
9
10
 
10
11
  from klaude_code.auth.codex.exceptions import CodexNotLoggedInError
@@ -20,9 +21,9 @@ from klaude_code.protocol import llm_param, model
20
21
  # Codex API configuration
21
22
  CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex"
22
23
  CODEX_HEADERS = {
23
- "OpenAI-Beta": "responses=experimental",
24
24
  "originator": "codex_cli_rs",
25
- "User-Agent": "GitHubCopilotChat/0.32.4",
25
+ # Mocked Codex-style user agent string
26
+ "User-Agent": "codex_cli_rs/0.0.0-klaude",
26
27
  }
27
28
 
28
29
 
@@ -87,30 +88,42 @@ class CodexClient(LLMClientABC):
87
88
  inputs = convert_history_to_input(param.input, param.model)
88
89
  tools = convert_tool_schema(param.tools)
89
90
 
90
- stream = await call_with_logged_payload(
91
- self.client.responses.create,
92
- model=str(param.model),
93
- tool_choice="auto",
94
- parallel_tool_calls=True,
95
- include=[
96
- "reasoning.encrypted_content",
97
- ],
98
- store=False, # Always False for Codex
99
- stream=True,
100
- input=inputs,
101
- instructions=param.system,
102
- tools=tools,
103
- text={
104
- "verbosity": param.verbosity,
105
- },
106
- prompt_cache_key=param.session_id or "",
107
- reasoning={
108
- "effort": param.thinking.reasoning_effort,
109
- "summary": param.thinking.reasoning_summary,
110
- }
111
- if param.thinking and param.thinking.reasoning_effort
112
- else None,
113
- )
91
+ session_id = param.session_id or ""
92
+ # Must send conversation_id/session_id headers to improve ChatGPT backend prompt cache hit rate.
93
+ extra_headers: dict[str, str] = {}
94
+ if session_id:
95
+ extra_headers["conversation_id"] = session_id
96
+ extra_headers["session_id"] = session_id
97
+
98
+ try:
99
+ stream = await call_with_logged_payload(
100
+ self.client.responses.create,
101
+ model=str(param.model),
102
+ tool_choice="auto",
103
+ parallel_tool_calls=True,
104
+ include=[
105
+ "reasoning.encrypted_content",
106
+ ],
107
+ store=False, # Always False for Codex
108
+ stream=True,
109
+ input=inputs,
110
+ instructions=param.system,
111
+ tools=tools,
112
+ text={
113
+ "verbosity": param.verbosity,
114
+ },
115
+ prompt_cache_key=session_id,
116
+ reasoning={
117
+ "effort": param.thinking.reasoning_effort,
118
+ "summary": param.thinking.reasoning_summary,
119
+ }
120
+ if param.thinking and param.thinking.reasoning_effort
121
+ else None,
122
+ extra_headers=extra_headers,
123
+ )
124
+ except (openai.OpenAIError, httpx.HTTPError) as e:
125
+ yield model.StreamErrorItem(error=f"{e.__class__.__name__} {str(e)}")
126
+ return
114
127
 
115
128
  async for item in parse_responses_stream(stream, param, self._config.cost, request_start_time):
116
129
  yield item
@@ -4,7 +4,7 @@ from typing import Literal, override
4
4
 
5
5
  import httpx
6
6
  import openai
7
- from openai import APIError, RateLimitError
7
+
8
8
 
9
9
  from klaude_code.llm.client import LLMClientABC, call_with_logged_payload
10
10
  from klaude_code.llm.input_common import apply_config_defaults
@@ -193,7 +193,7 @@ class OpenAICompatibleClient(LLMClientABC):
193
193
  name=tc.function.name,
194
194
  )
195
195
  accumulated_tool_calls.add(delta.tool_calls)
196
- except (RateLimitError, APIError) as e:
196
+ except (openai.OpenAIError, httpx.HTTPError) as e:
197
197
  yield model.StreamErrorItem(error=f"{e.__class__.__name__} {str(e)}")
198
198
 
199
199
  # Finalize
@@ -4,7 +4,8 @@ from collections.abc import AsyncGenerator
4
4
  from typing import TYPE_CHECKING, override
5
5
 
6
6
  import httpx
7
- from openai import AsyncAzureOpenAI, AsyncOpenAI, RateLimitError
7
+ import openai
8
+ from openai import AsyncAzureOpenAI, AsyncOpenAI
8
9
  from openai.types import responses
9
10
 
10
11
  from klaude_code.llm.client import LLMClientABC, call_with_logged_payload
@@ -138,8 +139,6 @@ async def parse_responses_stream(
138
139
  usage=usage,
139
140
  response_id=response_id,
140
141
  model_name=str(param.model),
141
- status=event.response.status,
142
- error_reason=error_reason,
143
142
  )
144
143
  if event.response.status != "completed":
145
144
  error_message = f"LLM response finished with status '{event.response.status}'"
@@ -159,7 +158,7 @@ async def parse_responses_stream(
159
158
  style="red",
160
159
  debug_type=DebugType.LLM_STREAM,
161
160
  )
162
- except RateLimitError as e:
161
+ except (openai.OpenAIError, httpx.HTTPError) as e:
163
162
  yield model.StreamErrorItem(error=f"{e.__class__.__name__} {str(e)}")
164
163
 
165
164
 
@@ -198,34 +197,38 @@ class ResponsesClient(LLMClientABC):
198
197
  inputs = convert_history_to_input(param.input, param.model)
199
198
  tools = convert_tool_schema(param.tools)
200
199
 
201
- stream = await call_with_logged_payload(
202
- self.client.responses.create,
203
- model=str(param.model),
204
- tool_choice="auto",
205
- parallel_tool_calls=True,
206
- include=[
207
- "reasoning.encrypted_content",
208
- ],
209
- store=param.store,
210
- previous_response_id=param.previous_response_id,
211
- stream=True,
212
- temperature=param.temperature,
213
- max_output_tokens=param.max_tokens,
214
- input=inputs,
215
- instructions=param.system,
216
- tools=tools,
217
- text={
218
- "verbosity": param.verbosity,
219
- },
220
- prompt_cache_key=param.session_id or "",
221
- reasoning={
222
- "effort": param.thinking.reasoning_effort,
223
- "summary": param.thinking.reasoning_summary,
224
- }
225
- if param.thinking and param.thinking.reasoning_effort
226
- else None,
227
- extra_headers={"extra": json.dumps({"session_id": param.session_id})},
228
- )
200
+ try:
201
+ stream = await call_with_logged_payload(
202
+ self.client.responses.create,
203
+ model=str(param.model),
204
+ tool_choice="auto",
205
+ parallel_tool_calls=True,
206
+ include=[
207
+ "reasoning.encrypted_content",
208
+ ],
209
+ store=param.store,
210
+ previous_response_id=param.previous_response_id,
211
+ stream=True,
212
+ temperature=param.temperature,
213
+ max_output_tokens=param.max_tokens,
214
+ input=inputs,
215
+ instructions=param.system,
216
+ tools=tools,
217
+ text={
218
+ "verbosity": param.verbosity,
219
+ },
220
+ prompt_cache_key=param.session_id or "",
221
+ reasoning={
222
+ "effort": param.thinking.reasoning_effort,
223
+ "summary": param.thinking.reasoning_summary,
224
+ }
225
+ if param.thinking and param.thinking.reasoning_effort
226
+ else None,
227
+ extra_headers={"extra": json.dumps({"session_id": param.session_id})},
228
+ )
229
+ except (openai.OpenAIError, httpx.HTTPError) as e:
230
+ yield model.StreamErrorItem(error=f"{e.__class__.__name__} {str(e)}")
231
+ return
229
232
 
230
233
  async for item in parse_responses_stream(stream, param, self._config.cost, request_start_time):
231
234
  yield item
@@ -130,6 +130,7 @@ HistoryItemEvent = (
130
130
  | ResponseMetadataEvent
131
131
  | InterruptEvent
132
132
  | DeveloperMessageEvent
133
+ | ErrorEvent
133
134
  )
134
135
 
135
136
 
@@ -260,8 +260,6 @@ class ResponseMetadataItem(BaseModel):
260
260
  model_name: str = ""
261
261
  provider: str | None = None # OpenRouter's provider name
262
262
  task_duration_s: float | None = None
263
- status: str | None = None
264
- error_reason: str | None = None
265
263
  created_at: datetime = Field(default_factory=datetime.now)
266
264
 
267
265
 
@@ -309,6 +309,11 @@ class Session(BaseModel):
309
309
  session_id=self.id,
310
310
  item=dm,
311
311
  )
312
+ case model.StreamErrorItem() as se:
313
+ yield events.ErrorEvent(
314
+ error_message=se.error,
315
+ can_retry=False,
316
+ )
312
317
  case _:
313
318
  continue
314
319
  prev_item = it
@@ -190,6 +190,8 @@ class REPLRenderer:
190
190
  case events.InterruptEvent():
191
191
  self.print()
192
192
  self.print(r_user_input.render_interrupt())
193
+ case events.ErrorEvent() as e:
194
+ self.display_error(e)
193
195
 
194
196
  def display_developer_message(self, e: events.DeveloperMessageEvent) -> None:
195
197
  if not r_developer.need_render_developer_message(e):
File without changes