kolega-code 0.11.0__tar.gz → 0.11.1__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 (436) hide show
  1. {kolega_code-0.11.0 → kolega_code-0.11.1}/CHANGELOG.md +13 -0
  2. {kolega_code-0.11.0 → kolega_code-0.11.1}/PKG-INFO +1 -1
  3. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/coverage.svg +1 -1
  4. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/__init__.py +1 -1
  5. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/__init__.py +1 -1
  6. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/baseagent.py +11 -2
  7. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/app.py +23 -1
  8. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/file_index.py +26 -4
  9. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/theme.py +8 -0
  10. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/agent_runtime.py +4 -1
  11. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/state.py +18 -0
  12. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/transcript.py +110 -2
  13. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/widgets.py +9 -2
  14. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/client.py +6 -2
  15. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/exceptions.py +45 -7
  16. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/instrumented_client.py +16 -6
  17. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/anthropic.py +105 -11
  18. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/chatgpt_oauth.py +6 -1
  19. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/google.py +6 -0
  20. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/openai.py +157 -68
  21. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/responses_common.py +4 -1
  22. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/thinking.py +6 -5
  23. kolega_code-0.11.1/kolega_code/llm/timeouts.py +37 -0
  24. {kolega_code-0.11.0 → kolega_code-0.11.1}/pyproject.toml +1 -1
  25. kolega_code-0.11.1/scripts/bench_hotpaths.py +162 -0
  26. kolega_code-0.11.1/tests/agent/llm/test_anthropic_local_token_counting_incremental.py +107 -0
  27. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_dashscope_mapping.py +5 -4
  28. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_error_boundary.py +9 -7
  29. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_instrumented_client.py +4 -2
  30. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_langfuse_normalization.py +5 -2
  31. kolega_code-0.11.1/tests/agent/llm/test_openai_token_counting_incremental.py +136 -0
  32. kolega_code-0.11.1/tests/agent/llm/test_streaming_timeouts.py +109 -0
  33. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_thinking_effort.py +15 -5
  34. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/_app_test_utils.py +6 -1
  35. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_mention_dropdown.py +37 -0
  36. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_file_index.py +33 -3
  37. kolega_code-0.11.1/tests/cli/test_transcript_streaming_render.py +119 -0
  38. {kolega_code-0.11.0 → kolega_code-0.11.1}/uv.lock +2 -2
  39. {kolega_code-0.11.0 → kolega_code-0.11.1}/.env.example +0 -0
  40. {kolega_code-0.11.0 → kolega_code-0.11.1}/.githooks/pre-commit +0 -0
  41. {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  42. {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  43. {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  44. {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/workflows/ci.yml +0 -0
  45. {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/workflows/docs.yml +0 -0
  46. {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/workflows/release.yml +0 -0
  47. {kolega_code-0.11.0 → kolega_code-0.11.1}/.gitignore +0 -0
  48. {kolega_code-0.11.0 → kolega_code-0.11.1}/.pre-commit-config.yaml +0 -0
  49. {kolega_code-0.11.0 → kolega_code-0.11.1}/CONTRIBUTING.md +0 -0
  50. {kolega_code-0.11.0 → kolega_code-0.11.1}/LICENSE +0 -0
  51. {kolega_code-0.11.0 → kolega_code-0.11.1}/README.md +0 -0
  52. {kolega_code-0.11.0 → kolega_code-0.11.1}/RELEASING.md +0 -0
  53. {kolega_code-0.11.0 → kolega_code-0.11.1}/SECURITY.md +0 -0
  54. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/.gitignore +0 -0
  55. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/README.md +0 -0
  56. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/astro.config.mjs +0 -0
  57. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/package-lock.json +0 -0
  58. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/package.json +0 -0
  59. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/public/favicon.svg +0 -0
  60. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/demo.gif +0 -0
  61. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/kolega-dark.svg +0 -0
  62. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/kolega-light.svg +0 -0
  63. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/ask.md +0 -0
  64. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/doctor.md +0 -0
  65. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/overview.md +0 -0
  66. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/sessions.md +0 -0
  67. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/concepts/agents.md +0 -0
  68. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/concepts/how-it-works.md +0 -0
  69. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/concepts/tools.md +0 -0
  70. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/environment-variables.md +0 -0
  71. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/prompt-overrides.md +0 -0
  72. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/providers-and-models.mdx +0 -0
  73. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/settings-and-api-keys.mdx +0 -0
  74. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/sign-in-with-chatgpt.mdx +0 -0
  75. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/getting-started/installation.mdx +0 -0
  76. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/getting-started/quick-start.mdx +0 -0
  77. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/gigacode.mdx +0 -0
  78. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/hooks.md +0 -0
  79. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/index.mdx +0 -0
  80. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/skills.mdx +0 -0
  81. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/composer.md +0 -0
  82. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/interface.md +0 -0
  83. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/modes.md +0 -0
  84. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/sessions-and-resume.md +0 -0
  85. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/slash-commands.md +0 -0
  86. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content.config.ts +0 -0
  87. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/styles/brand.css +0 -0
  88. {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/tsconfig.json +0 -0
  89. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/browseragent.py +0 -0
  90. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/coder.py +0 -0
  91. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/common.py +0 -0
  92. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/compression.py +0 -0
  93. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/context.py +0 -0
  94. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/conversation.py +0 -0
  95. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/errors.py +0 -0
  96. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/generalagent.py +0 -0
  97. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/investigationagent.py +0 -0
  98. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/__init__.py +0 -0
  99. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/budget.py +0 -0
  100. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/errors.py +0 -0
  101. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/executor.py +0 -0
  102. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/guide.py +0 -0
  103. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/journal.py +0 -0
  104. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/runtime.py +0 -0
  105. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/types.py +0 -0
  106. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/planningagent.py +0 -0
  107. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_dump.py +0 -0
  108. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_overrides.py +0 -0
  109. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_provider.py +0 -0
  110. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.system.md +0 -0
  111. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.user.md.j2 +0 -0
  112. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/terminal/output_summary.system.md +0 -0
  113. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/terminal/safety.system.md +0 -0
  114. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/tools/think_hard.system.md +0 -0
  115. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/extensions/cli/planning_questions.md +0 -0
  116. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/extensions/cli/shared_task_list.md +0 -0
  117. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/extensions/skills/catalog.md.j2 +0 -0
  118. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/browser.md.j2 +0 -0
  119. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/coder_cli.md.j2 +0 -0
  120. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/general.md.j2 +0 -0
  121. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/investigation.md.j2 +0 -0
  122. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/planning.md.j2 +0 -0
  123. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/agents_md_instructions.md +0 -0
  124. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/environment_variables/workspace_env_vars.md +0 -0
  125. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/frontend_guidance.md +0 -0
  126. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/expo.md +0 -0
  127. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/html_website.md +0 -0
  128. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/mern_stack.md +0 -0
  129. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/react_vite_shadcdn.md +0 -0
  130. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/user_tasks/cli/implement_plan.md.j2 +0 -0
  131. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/user_tasks/cli/init_agents.md.j2 +0 -0
  132. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompts.py +0 -0
  133. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/agent_tool.py +0 -0
  134. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/base_tool.py +0 -0
  135. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/browser_tool.py +0 -0
  136. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/build_tool.py +0 -0
  137. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/create_file_tool.py +0 -0
  138. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/edit_preview.py +0 -0
  139. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/glob_tool.py +0 -0
  140. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/list_directory_tool.py +0 -0
  141. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/memory_tool.py +0 -0
  142. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/read_file_tool.py +0 -0
  143. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/read_image_tool.py +0 -0
  144. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/replace_entire_file_tool.py +0 -0
  145. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/replace_lines_tool.py +0 -0
  146. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_and_replace_tool.py +0 -0
  147. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/__init__.py +0 -0
  148. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/base.py +0 -0
  149. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/duckduckgo.py +0 -0
  150. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/errors.py +0 -0
  151. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/firecrawl.py +0 -0
  152. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/models.py +0 -0
  153. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/searxng.py +0 -0
  154. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/tavily.py +0 -0
  155. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_codebase_tool.py +0 -0
  156. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/streaming_tool.py +0 -0
  157. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/terminal_tool.py +0 -0
  158. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/think_hard_tool.py +0 -0
  159. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/web_fetch_tool.py +0 -0
  160. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/web_search_tool.py +0 -0
  161. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/workflow_tool.py +0 -0
  162. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tools.py +0 -0
  163. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/utils/commands.py +0 -0
  164. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/__init__.py +0 -0
  165. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/_jwt.py +0 -0
  166. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/chatgpt_oauth.py +0 -0
  167. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/constants.py +0 -0
  168. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/tokens.py +0 -0
  169. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/__init__.py +0 -0
  170. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/clipboard_image.py +0 -0
  171. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/config.py +0 -0
  172. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/connection.py +0 -0
  173. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/main.py +0 -0
  174. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/mentions.py +0 -0
  175. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/messages.py +0 -0
  176. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/provider_registry.py +0 -0
  177. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/session_store.py +0 -0
  178. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/settings.py +0 -0
  179. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/skills.py +0 -0
  180. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/slash_commands.py +0 -0
  181. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/__init__.py +0 -0
  182. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/changes_screen.py +0 -0
  183. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/command_handlers.py +0 -0
  184. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/constants.py +0 -0
  185. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/prompt_flows.py +0 -0
  186. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/session_diff.py +0 -0
  187. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/settings_panel.py +0 -0
  188. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/status_dashboard.py +0 -0
  189. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/styles.tcss +0 -0
  190. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/sub_agent_screen.py +0 -0
  191. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/terminal_display.py +0 -0
  192. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/updater.py +0 -0
  193. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/config.py +0 -0
  194. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/events.py +0 -0
  195. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/__init__.py +0 -0
  196. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/backends.py +0 -0
  197. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/config.py +0 -0
  198. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/dispatcher.py +0 -0
  199. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/events.py +0 -0
  200. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/matcher.py +0 -0
  201. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/outcome.py +0 -0
  202. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/models.py +0 -0
  203. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/__init__.py +0 -0
  204. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/base.py +0 -0
  205. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/models.py +0 -0
  206. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/openai_responses.py +0 -0
  207. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/ratelimit.py +0 -0
  208. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/__init__.py +0 -0
  209. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/accessors.py +0 -0
  210. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/__init__.py +0 -0
  211. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/anthropic.py +0 -0
  212. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/dashscope.py +0 -0
  213. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/deepseek.py +0 -0
  214. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/fireworks.py +0 -0
  215. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/google.py +0 -0
  216. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/kimi_coding.py +0 -0
  217. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/moonshot.py +0 -0
  218. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/ollama_cloud.py +0 -0
  219. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/openai.py +0 -0
  220. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/openai_chatgpt.py +0 -0
  221. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/together.py +0 -0
  222. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/xai.py +0 -0
  223. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/zai.py +0 -0
  224. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/types.py +0 -0
  225. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/tool_execution_ids.py +0 -0
  226. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/local_state.py +0 -0
  227. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/models/__init__.py +0 -0
  228. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/models/sandbox_terminal_state.py +0 -0
  229. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/permissions.py +0 -0
  230. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/runtime.py +0 -0
  231. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/README.md +0 -0
  232. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/__init__.py +0 -0
  233. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/async_filesystem.py +0 -0
  234. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/base.py +0 -0
  235. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/browser.py +0 -0
  236. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/event_loop.py +0 -0
  237. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/filesystem.py +0 -0
  238. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/local.py +0 -0
  239. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/serializer.py +0 -0
  240. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/terminal.py +0 -0
  241. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/utils.py +0 -0
  242. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/base.py +0 -0
  243. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/browser.py +0 -0
  244. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/file_system.py +0 -0
  245. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/html.py +0 -0
  246. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/terminal.py +0 -0
  247. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/terminal_buffer.py +0 -0
  248. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/__init__.py +0 -0
  249. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/core.py +0 -0
  250. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/definitions.py +0 -0
  251. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/registry.py +0 -0
  252. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/utils/__init__.py +0 -0
  253. {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/utils/images.py +0 -0
  254. {kolega_code-0.11.0 → kolega_code-0.11.1}/run_tests.sh +0 -0
  255. {kolega_code-0.11.0 → kolega_code-0.11.1}/scripts/check-dev-hooks.sh +0 -0
  256. {kolega_code-0.11.0 → kolega_code-0.11.1}/scripts/install-kolega-code.sh +0 -0
  257. {kolega_code-0.11.0 → kolega_code-0.11.1}/scripts/setup-dev.sh +0 -0
  258. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/__init__.py +0 -0
  259. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/__init__.py +0 -0
  260. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/compaction_helpers.py +0 -0
  261. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/conftest.py +0 -0
  262. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/__init__.py +0 -0
  263. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/_token_counting_utils.py +0 -0
  264. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_anthropic_token_counting.py +0 -0
  265. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_billing_openai_cache.py +0 -0
  266. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_live.py +0 -0
  267. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_auth_config.py +0 -0
  268. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_conversion.py +0 -0
  269. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_provider.py +0 -0
  270. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_provider_generate.py +0 -0
  271. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_streaming.py +0 -0
  272. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_client.py +0 -0
  273. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_client_live.py +0 -0
  274. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_client_runtime.py +0 -0
  275. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_exceptions.py +0 -0
  276. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_google_thought_signature.py +0 -0
  277. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_google_tool_signature_live.py +0 -0
  278. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_instrumented_client_integration.py +0 -0
  279. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_instrumented_client_stream.py +0 -0
  280. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_live_providers.py +0 -0
  281. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_message_conversion.py +0 -0
  282. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_model_specs.py +0 -0
  283. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_cached_tokens.py +0 -0
  284. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_cached_tokens_stream.py +0 -0
  285. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_message_conversion.py +0 -0
  286. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_responses_provider.py +0 -0
  287. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_token_counting.py +0 -0
  288. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_provider_usage_mapping.py +0 -0
  289. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_supports_vision.py +0 -0
  290. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_tool_execution_ids.py +0 -0
  291. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/orchestration/__init__.py +0 -0
  292. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/orchestration/test_orchestration.py +0 -0
  293. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/__init__.py +0 -0
  294. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser.py +0 -0
  295. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_console_logs.py +0 -0
  296. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_content.py +0 -0
  297. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_integration.py +0 -0
  298. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_parity.py +0 -0
  299. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_file_system.py +0 -0
  300. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_file_system_integration.py +0 -0
  301. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_file_system_path.py +0 -0
  302. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_local_file_system.py +0 -0
  303. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_sandbox_terminal_input.py +0 -0
  304. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_terminal.py +0 -0
  305. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_terminal_buffer.py +0 -0
  306. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_terminal_state_serializer.py +0 -0
  307. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_agent_tools_inventory.py +0 -0
  308. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent.py +0 -0
  309. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_anthropic_history_validation.py +0 -0
  310. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_compaction.py +0 -0
  311. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_effective_history.py +0 -0
  312. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_history_serialization.py +0 -0
  313. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_prompt_context.py +0 -0
  314. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_role_overrides.py +0 -0
  315. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_runtime.py +0 -0
  316. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_tool_call_repair.py +0 -0
  317. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_tool_call_repair_integration.py +0 -0
  318. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_tool_execution.py +0 -0
  319. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_coder_attachments.py +0 -0
  320. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_coder_prompt_extensions.py +0 -0
  321. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_commands.py +0 -0
  322. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_integration.py +0 -0
  323. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_persistence.py +0 -0
  324. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_subagent.py +0 -0
  325. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_tui_events.py +0 -0
  326. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compress_command.py +0 -0
  327. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compression.py +0 -0
  328. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_conversation_compaction.py +0 -0
  329. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_duplicate_tool_results.py +0 -0
  330. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_empty_message_handling.py +0 -0
  331. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_event_emitter.py +0 -0
  332. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_general_agent.py +0 -0
  333. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_gigacode_gating.py +0 -0
  334. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_history_adapter_thinking.py +0 -0
  335. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_history_image_blocks.py +0 -0
  336. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_hooks_integration.py +0 -0
  337. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_html.py +0 -0
  338. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_image_compat.py +0 -0
  339. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_parallel_tool_calls.py +0 -0
  340. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_permissions.py +0 -0
  341. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_planning_agent.py +0 -0
  342. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompt_dump.py +0 -0
  343. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompt_overrides.py +0 -0
  344. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompt_provider.py +0 -0
  345. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompts.py +0 -0
  346. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_read_image_tool.py +0 -0
  347. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_builtin_dispatch.py +0 -0
  348. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_config.py +0 -0
  349. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_extensions.py +0 -0
  350. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_initialization.py +0 -0
  351. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_memory.py +0 -0
  352. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_registry.py +0 -0
  353. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tools.py +0 -0
  354. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/__init__.py +0 -0
  355. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_agent_tool.py +0 -0
  356. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_base_tool.py +0 -0
  357. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_browser_tool.py +0 -0
  358. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_build_tool.py +0 -0
  359. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_create_file_tool.py +0 -0
  360. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_edit_preview.py +0 -0
  361. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_glob_tool.py +0 -0
  362. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_glob_tool_sandbox_parity.py +0 -0
  363. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_list_directory_tool.py +0 -0
  364. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_read_file_tool.py +0 -0
  365. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_replace_entire_file_tool.py +0 -0
  366. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_replace_lines_tool.py +0 -0
  367. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_search_and_replace_tool.py +0 -0
  368. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_search_codebase_tool.py +0 -0
  369. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_terminal_tool.py +0 -0
  370. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_integration.py +0 -0
  371. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming.py +0 -0
  372. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming_content.py +0 -0
  373. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming_errors.py +0 -0
  374. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming_updates.py +0 -0
  375. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_web_fetch_tool.py +0 -0
  376. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_web_search_tool.py +0 -0
  377. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_workflow_tool.py +0 -0
  378. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/auth/__init__.py +0 -0
  379. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/auth/test_chatgpt_oauth.py +0 -0
  380. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/auth/test_tokens.py +0 -0
  381. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/__init__.py +0 -0
  382. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app.py +0 -0
  383. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_agent_wiring.py +0 -0
  384. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_bootstrap_settings.py +0 -0
  385. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_changes_inspector.py +0 -0
  386. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_compaction_restore.py +0 -0
  387. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_composer_commands.py +0 -0
  388. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_composer_input.py +0 -0
  389. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_conversation_rendering.py +0 -0
  390. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_focus_behavior.py +0 -0
  391. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_image_vision_warning.py +0 -0
  392. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_misc_commands.py +0 -0
  393. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_model_effort_commands.py +0 -0
  394. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_model_switch_vision.py +0 -0
  395. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_modes_planning.py +0 -0
  396. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_navigation_commands.py +0 -0
  397. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_permission_prompts.py +0 -0
  398. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_persistence_history.py +0 -0
  399. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_plan_lifecycle.py +0 -0
  400. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_planning_questions.py +0 -0
  401. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_planning_restore.py +0 -0
  402. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_entries.py +0 -0
  403. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_errors.py +0 -0
  404. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_scroll.py +0 -0
  405. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_selection.py +0 -0
  406. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_status.py +0 -0
  407. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_streaming.py +0 -0
  408. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_terminal_logs.py +0 -0
  409. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_tools.py +0 -0
  410. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_skill_commands.py +0 -0
  411. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_slash_dropdown.py +0 -0
  412. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_status_modes.py +0 -0
  413. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_sub_agents_workflows.py +0 -0
  414. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_cli_config.py +0 -0
  415. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_clipboard_image.py +0 -0
  416. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_connection.py +0 -0
  417. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_main.py +0 -0
  418. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_mentions.py +0 -0
  419. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_provider_registry.py +0 -0
  420. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_session_diff.py +0 -0
  421. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_session_store.py +0 -0
  422. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_settings.py +0 -0
  423. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_skills.py +0 -0
  424. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_slash_commands.py +0 -0
  425. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_terminal_display.py +0 -0
  426. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_themes.py +0 -0
  427. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_tui_stylesheet.py +0 -0
  428. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_updater.py +0 -0
  429. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/conftest.py +0 -0
  430. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/hooks/__init__.py +0 -0
  431. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/hooks/test_hooks.py +0 -0
  432. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/tools/__init__.py +0 -0
  433. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/tools/test_definitions.py +0 -0
  434. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/utils/__init__.py +0 -0
  435. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/utils/test_images.py +0 -0
  436. {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/utils/test_images_ascii.py +0 -0
@@ -6,6 +6,19 @@ This project uses GitHub Releases for detailed generated release notes. This fil
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.11.1 - 2026-06-26
10
+
11
+ ### Added
12
+
13
+ - Made images `@`-mentionable in the TUI and moved file-index walking off the
14
+ event loop.
15
+
16
+ ### Fixed
17
+
18
+ - Routed DeepSeek through the OpenAI-compatible `/v1` endpoint.
19
+ - Bounded LLM streaming timeouts and added retry handling for transport errors.
20
+ - Prevented DeepSeek stream freezes in the TUI.
21
+
9
22
  ## 0.11.0 - 2026-06-26
10
23
 
11
24
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kolega-code
3
- Version: 0.11.0
3
+ Version: 0.11.1
4
4
  Summary: Multi-agent coding in the terminal
5
5
  Project-URL: Homepage, https://kolega-ai.github.io/kolega-code/
6
6
  Project-URL: Documentation, https://kolega-ai.github.io/kolega-code/
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 80.66%"><title>coverage: 80.66%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#67ac09"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="865" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">80.66%</text><text x="865" y="150" fill-opacity=".3" textLength="430">80.66%</text></g><text x="865" y="140" textLength="430">80.66%</text></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 80.82%"><title>coverage: 80.82%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#67ac09"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="865" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">80.82%</text><text x="865" y="150" fill-opacity=".3" textLength="430">80.82%</text></g><text x="865" y="140" textLength="430">80.82%</text></g></g></svg>
@@ -151,4 +151,4 @@ __all__ = [
151
151
  "parse_git_status_output",
152
152
  ]
153
153
 
154
- __version__ = "0.11.0"
154
+ __version__ = "0.11.1"
@@ -44,4 +44,4 @@ __all__ = [
44
44
  "ToolExtension",
45
45
  ]
46
46
 
47
- __version__ = "0.11.0"
47
+ __version__ = "0.11.1"
@@ -28,6 +28,7 @@ from kolega_code.hooks import (
28
28
  LifecycleEvent,
29
29
  )
30
30
  from kolega_code.llm.exceptions import (
31
+ LLMConnectionError,
31
32
  LLMError,
32
33
  LLMInternalServerError,
33
34
  LLMRateLimitError,
@@ -1257,8 +1258,10 @@ class BaseAgent(LogMixin):
1257
1258
  retry_after = self._parse_retry_after(error)
1258
1259
  error = map_to_llm_error(error, provider=self.primary_model_config.provider.value)
1259
1260
 
1260
- # LLMInternalServerError covers 500/529 overloaded plus connection/transport errors.
1261
- if isinstance(error, (LLMRateLimitError, LLMInternalServerError)):
1261
+ # Retry transient failures: rate limits, provider 5xx/overload, and transport-layer
1262
+ # failures (LLMConnectionError, incl. its LLMTimeout subclass — e.g. a stalled
1263
+ # streaming read hitting the per-request timeout, or a dropped connection).
1264
+ if isinstance(error, (LLMRateLimitError, LLMInternalServerError, LLMConnectionError)):
1262
1265
  cap = self.primary_model_config.rate_limits.loop_max_retries
1263
1266
  self._consecutive_llm_retries += 1
1264
1267
  if self._consecutive_llm_retries > cap:
@@ -1277,6 +1280,12 @@ class BaseAgent(LogMixin):
1277
1280
  f"Transient LLM error ({error}); retry {self._consecutive_llm_retries}/{cap} in {delay:.1f}s.",
1278
1281
  sender=self.agent_name,
1279
1282
  )
1283
+ # Surface the retry in the UI so a stalled stream (which now fails fast on the
1284
+ # per-request timeout instead of hanging) reads as "retrying", not a freeze.
1285
+ await self.emitter.llm_status(
1286
+ "info",
1287
+ f"Connection issue — retrying ({self._consecutive_llm_retries}/{cap}) in {delay:.0f}s…",
1288
+ )
1280
1289
  await asyncio.sleep(delay)
1281
1290
  await self.log_info("Resuming after backoff.", sender=self.agent_name)
1282
1291
  # Don't re-raise - the turn loop re-issues the request.
@@ -158,6 +158,7 @@ class KolegaCodeApp(
158
158
  self.settings: CliSettings = CliSettings()
159
159
  self.skill_catalog: SkillCatalog = discover_skills(self.project_path)
160
160
  self.file_index = WorkspaceFileIndex(self.project_path)
161
+ self._file_index_refreshing = False
161
162
  self.browser_visible = browser_visible
162
163
  self.sidebar_visible = True
163
164
  self.check_for_updates = check_for_updates
@@ -419,6 +420,9 @@ class KolegaCodeApp(
419
420
  self._update_detach_button()
420
421
  if self.check_for_updates:
421
422
  self.run_worker(self._check_for_update_on_startup(), name="kolega-update-check", group="updates")
423
+ # Warm the @-mention file index off the event loop so the first mention has
424
+ # results without blocking the UI on os.walk (slow on iCloud/large trees).
425
+ self._maybe_refresh_file_index()
422
426
  self._schedule_conversation_bottom_anchor()
423
427
  self.run_worker(self._consume_events(), name="kolega-events", group="events")
424
428
  if self.config is not None:
@@ -832,12 +836,30 @@ class KolegaCodeApp(
832
836
  if active is None:
833
837
  dropdown.close()
834
838
  return
835
- entries = self.file_index.search(active[0], limit=8)
839
+ # Search the cached snapshot only — never block the keystroke on os.walk. A
840
+ # stale/empty cache triggers a background refresh that re-runs this when done.
841
+ self._maybe_refresh_file_index()
842
+ entries = self.file_index.cached_search(active[0], limit=8)
836
843
  if not entries:
837
844
  dropdown.close()
838
845
  return
839
846
  dropdown.open_with([tui_widgets.file_completion_item(entry) for entry in entries])
840
847
 
848
+ def _maybe_refresh_file_index(self) -> None:
849
+ """Refresh the file index on a worker thread if it's stale and not already running."""
850
+ if self._file_index_refreshing or not self.file_index.is_stale():
851
+ return
852
+ self._file_index_refreshing = True
853
+ self.run_worker(self._refresh_file_index(), name="kolega-file-index", group="file-index")
854
+
855
+ async def _refresh_file_index(self) -> None:
856
+ try:
857
+ await asyncio.to_thread(self.file_index.refresh)
858
+ finally:
859
+ self._file_index_refreshing = False
860
+ # Surface freshly-walked results if an @-mention is still being typed.
861
+ self._refresh_completion_dropdown()
862
+
841
863
  def on_descendant_focus(self, event: events.DescendantFocus) -> None:
842
864
  # Fires after screen.focused settles. Catches AUTO_FOCUS landing on the
843
865
  # conversation transcript (resume/resize) and any other stray focus while a
@@ -11,6 +11,7 @@ from typing import List, Optional
11
11
  import pathspec
12
12
 
13
13
  from kolega_code.agent.tool_backend.glob_tool import GlobTool
14
+ from kolega_code.utils.images import image_media_type
14
15
 
15
16
 
16
17
  @dataclass(frozen=True)
@@ -31,11 +32,16 @@ class WorkspaceFileIndex:
31
32
  self._refreshed_at: Optional[float] = None
32
33
 
33
34
  def entries(self) -> List[IndexEntry]:
34
- now = time.monotonic()
35
- if self._refreshed_at is None or now - self._refreshed_at > self.TTL_SECONDS:
35
+ if self.is_stale():
36
36
  self.refresh()
37
37
  return self._entries
38
38
 
39
+ def is_stale(self) -> bool:
40
+ """Whether the cache is empty or older than the TTL (a refresh is due)."""
41
+ if self._refreshed_at is None:
42
+ return True
43
+ return time.monotonic() - self._refreshed_at > self.TTL_SECONDS
44
+
39
45
  def refresh(self) -> None:
40
46
  gitignore = self._load_gitignore_spec()
41
47
  entries: List[IndexEntry] = []
@@ -54,7 +60,10 @@ class WorkspaceFileIndex:
54
60
  dirnames[:] = kept_dirs
55
61
 
56
62
  for name in sorted(filenames):
57
- if Path(name).suffix.lower() in GlobTool.BINARY_EXTENSIONS:
63
+ # Exclude binaries, but keep attachable images: @-mentioning an image
64
+ # attaches it (build_file_attachments handles it via the same
65
+ # image_media_type), so it belongs in completions for vision models.
66
+ if Path(name).suffix.lower() in GlobTool.BINARY_EXTENSIONS and image_media_type(name) is None:
58
67
  continue
59
68
  rel = self._posix(rel_dir / name)
60
69
  if gitignore is not None and gitignore.match_file(rel):
@@ -69,8 +78,21 @@ class WorkspaceFileIndex:
69
78
  self._refreshed_at = time.monotonic()
70
79
 
71
80
  def search(self, query: str, limit: int = 8) -> List[IndexEntry]:
81
+ """Search, refreshing the cache first if stale (may block on os.walk)."""
82
+ return self._rank(self.entries(), query, limit)
83
+
84
+ def cached_search(self, query: str, limit: int = 8) -> List[IndexEntry]:
85
+ """Search the current cached entries WITHOUT refreshing.
86
+
87
+ The UI uses this so a keystroke never blocks on ``os.walk``; a background
88
+ refresh (see ``app._refresh_file_index``) updates the cache out-of-band.
89
+ """
90
+ return self._rank(self._entries, query, limit)
91
+
92
+ @staticmethod
93
+ def _rank(entries: List[IndexEntry], query: str, limit: int) -> List[IndexEntry]:
72
94
  scored = []
73
- for entry in self.entries():
95
+ for entry in entries:
74
96
  score = fuzzy_score(query, entry.path)
75
97
  if score is not None:
76
98
  scored.append((score, entry))
@@ -88,6 +88,14 @@ CONTEXT_BAR_WIDTH = 18
88
88
  INSET_WIDTH = 2
89
89
  MARKDOWN_CODE_THEME = "monokai"
90
90
  RENDER_COALESCE_INTERVAL = 0.05
91
+ # Coalesce less aggressively as the live streaming entry grows: each flush still costs
92
+ # Textual an O(height) re-measure of an auto-height widget, so for very large reasoning
93
+ # streams we trade a little update latency for far fewer full re-measures. Sizes are
94
+ # characters of the live entry; see transcript._invalidate_conversation.
95
+ RENDER_COALESCE_INTERVAL_MEDIUM = 0.12
96
+ RENDER_COALESCE_INTERVAL_LARGE = 0.25
97
+ RENDER_COALESCE_MEDIUM_CHARS = 40_000
98
+ RENDER_COALESCE_LARGE_CHARS = 200_000
91
99
 
92
100
 
93
101
  @lru_cache(maxsize=None)
@@ -214,7 +214,10 @@ class AgentRuntimeMixin:
214
214
  if queued.entry not in self.conversation_entries:
215
215
  self._add_conversation_entry(queued.entry)
216
216
  else:
217
- self._render_conversation()
217
+ # The entry is already mounted (added when queued); just re-render that one
218
+ # widget for the queued->user transition instead of rebuilding the whole
219
+ # transcript, which on a long thread is a synchronous O(entries) hitch.
220
+ self._invalidate_conversation(queued.entry)
218
221
  self._refresh_queued_messages_panel()
219
222
  self._clear_composer_hint()
220
223
  self.agent_worker = self.run_worker(
@@ -76,6 +76,24 @@ class ConversationEntry:
76
76
  full_content: str = "" # untruncated tool output for expand-on-demand (capped)
77
77
  edit_preview: Optional[dict] = None # UI-only structured diff/head preview for edit tools (not persisted)
78
78
  entry_id: str = field(default_factory=_next_entry_id) # UI-only widget key, not persisted
79
+ # Streaming buffers (UI-only, excluded from equality/repr). Deltas accumulate in
80
+ # stream_parts and are joined into content once per render flush, not per chunk, to
81
+ # avoid O(n^2) attribute concatenation. render_cache holds the incrementally-built
82
+ # inset renderable so each flush appends only the new text rather than re-splitting
83
+ # the whole buffer. See transcript._apply_stream_chunk / _streaming_inset_renderable.
84
+ stream_parts: list[str] = field(default_factory=list, compare=False, repr=False)
85
+ render_cache: object = field(default=None, compare=False, repr=False)
86
+
87
+ def materialize(self) -> str:
88
+ """Fold any deferred stream deltas into ``content`` and return it.
89
+
90
+ Streaming appends land in ``stream_parts`` (O(1)); they are joined into
91
+ ``content`` once here — on each render flush and when the segment completes —
92
+ rather than on every chunk, so growth stays O(n) instead of O(n^2)."""
93
+ if self.stream_parts:
94
+ self.content += "".join(self.stream_parts)
95
+ self.stream_parts.clear()
96
+ return self.content
79
97
 
80
98
 
81
99
  @dataclass
@@ -31,6 +31,29 @@ from .sub_agent_screen import SubAgentEntryWidget
31
31
  from .widgets import ConversationEntryWidget, JumpToBottomBar, ToolEntryWidget
32
32
 
33
33
 
34
+ class _InsetRenderState:
35
+ """Incremental render cache for a streaming inset body (assistant/thinking).
36
+
37
+ Holds the Rich ``Text`` built so far plus enough state to append only newly
38
+ arrived characters on each flush, instead of re-splitting the whole growing
39
+ buffer. The output matches ``_format_inset_text`` for ``\\n``-delimited content
40
+ (the common case); completion does a canonical full reformat as a safety net.
41
+ """
42
+
43
+ __slots__ = ("text", "length", "started", "open_has_content", "deferred_newline", "style")
44
+
45
+ def __init__(self, style):
46
+ self.text = Text()
47
+ self.length = 0 # chars of entry.content already folded into `text`
48
+ self.started = False # has the first line's inset bar been emitted?
49
+ self.open_has_content = False # has the current (last) line emitted its " " + text?
50
+ # One pending line break: a "\n" defers its new line until either more content or
51
+ # another "\n" arrives, so a *trailing* terminator leaves no empty inset line
52
+ # (matching str.splitlines, which drops only the final terminator).
53
+ self.deferred_newline = False
54
+ self.style = style
55
+
56
+
34
57
  class TranscriptRenderingMixin:
35
58
  def _restore_conversation_history(self, history: list[dict]) -> None:
36
59
  self.conversation_entries = []
@@ -238,8 +261,14 @@ class TranscriptRenderingMixin:
238
261
  self.conversation_entries.append(entry)
239
262
  self._stream_entries[cache_key] = entry
240
263
 
241
- entry.content += content
264
+ # Defer concatenation to the next render flush (see ConversationEntryWidget.
265
+ # refresh_content): per-chunk `content += content` is O(n^2) over the stream.
266
+ # On completion, materialize now so entry.content is current the moment the
267
+ # segment ends (one O(n) join), without waiting for the flush.
268
+ entry.stream_parts.append(content)
242
269
  entry.complete = complete
270
+ if complete:
271
+ entry.materialize()
243
272
  self._invalidate_conversation(entry)
244
273
 
245
274
  def _begin_turn_progress(self) -> None:
@@ -1112,9 +1141,10 @@ class TranscriptRenderingMixin:
1112
1141
  if self._render_pending:
1113
1142
  return
1114
1143
  self._render_pending = True
1144
+ interval = self._render_coalesce_interval(entry)
1115
1145
  try:
1116
1146
  self.set_timer(
1117
- theme.RENDER_COALESCE_INTERVAL,
1147
+ interval,
1118
1148
  self._flush_conversation_render,
1119
1149
  name="conversation-render",
1120
1150
  )
@@ -1122,6 +1152,23 @@ class TranscriptRenderingMixin:
1122
1152
  # Timers are unavailable before the app is running; render directly.
1123
1153
  self._flush_conversation_render()
1124
1154
 
1155
+ def _render_coalesce_interval(self, entry: Optional[ConversationEntry]) -> float:
1156
+ """Back off the flush cadence for very large live entries.
1157
+
1158
+ Each flush makes Textual re-measure the auto-height streaming widget (O(height)),
1159
+ so for big reasoning streams fewer, larger flushes beat ~20/sec full re-measures.
1160
+ Length lags one flush (deltas materialize at flush time), which is fine: the entry
1161
+ only grows, so the cadence steps up within one interval of crossing a threshold.
1162
+ """
1163
+ if entry is None:
1164
+ return theme.RENDER_COALESCE_INTERVAL
1165
+ size = len(entry.content)
1166
+ if size >= theme.RENDER_COALESCE_LARGE_CHARS:
1167
+ return theme.RENDER_COALESCE_INTERVAL_LARGE
1168
+ if size >= theme.RENDER_COALESCE_MEDIUM_CHARS:
1169
+ return theme.RENDER_COALESCE_INTERVAL_MEDIUM
1170
+ return theme.RENDER_COALESCE_INTERVAL
1171
+
1125
1172
  def _flush_conversation_render(self) -> None:
1126
1173
  if not self._render_pending:
1127
1174
  return
@@ -1280,11 +1327,15 @@ class TranscriptRenderingMixin:
1280
1327
  header = theme.role_header(Glyph.AGENT, "Agent", Color.AGENT, state=streaming)
1281
1328
  if entry.complete and entry.content.strip():
1282
1329
  return self._markdown_entry(header, entry.content)
1330
+ if not entry.complete:
1331
+ return self._streaming_inset_renderable(entry, header)
1283
1332
  return self._entry_renderable(header, entry.content)
1284
1333
  if entry.kind == "thinking":
1285
1334
  header = theme.role_header(
1286
1335
  Glyph.STATUS, "Thinking", Color.THINKING, label_style="dim italic", state=streaming
1287
1336
  )
1337
+ if not entry.complete:
1338
+ return self._streaming_inset_renderable(entry, header, body_style="italic dim")
1288
1339
  return self._entry_renderable(header, entry.content, body_style="italic dim")
1289
1340
  if entry.kind == "progress":
1290
1341
  color = Color.ERROR if entry.tone == "error" else Color.WARNING
@@ -1336,6 +1387,63 @@ class TranscriptRenderingMixin:
1336
1387
  return header_text
1337
1388
  return Group(header_text, self._format_inset_text(body, style=body_style))
1338
1389
 
1390
+ def _streaming_inset_renderable(self, entry, header: str, *, body_style: Optional[str] = None) -> Group:
1391
+ """Inset body for a still-streaming entry, built incrementally (O(delta) per flush).
1392
+
1393
+ Caches the rendered Text on the entry so each flush only appends the newly
1394
+ arrived characters instead of re-splitting and re-rendering the whole buffer
1395
+ (the O(n^2) cliff). The header is small and rebuilt each call. On completion the
1396
+ dispatcher routes to the canonical _entry_renderable/_markdown_entry instead.
1397
+ """
1398
+ state = entry.render_cache
1399
+ content = entry.content
1400
+ if not isinstance(state, _InsetRenderState) or state.style != body_style or state.length > len(content):
1401
+ # First render for this entry, a body-style change, or a defensive shrink:
1402
+ # fold the whole current buffer once.
1403
+ state = _InsetRenderState(body_style)
1404
+ entry.render_cache = state
1405
+ self._extend_inset(state, content)
1406
+ elif state.length < len(content):
1407
+ self._extend_inset(state, content[state.length :])
1408
+ return Group(Text.from_markup(header), state.text)
1409
+
1410
+ def _extend_inset(self, state: "_InsetRenderState", delta: str) -> None:
1411
+ """Append ``delta`` to the cached inset Text, matching _format_inset_text for "\\n".
1412
+
1413
+ A pending newline is flushed (its new line materialized) as soon as another
1414
+ newline *or* content follows it; only a still-pending terminator at the very end
1415
+ is left unrendered, so the result equals ``_format_inset_text`` over the buffer.
1416
+ """
1417
+ if not delta and state.started:
1418
+ return
1419
+ bar = f" {theme.g(Glyph.INSET_BAR)}"
1420
+ text = state.text
1421
+
1422
+ def flush_deferred() -> None:
1423
+ if state.deferred_newline:
1424
+ text.append("\n")
1425
+ text.append(bar, style="dim")
1426
+ state.deferred_newline = False
1427
+ state.open_has_content = False
1428
+
1429
+ if not state.started:
1430
+ text.append(bar, style="dim")
1431
+ state.started = True
1432
+ state.open_has_content = False
1433
+
1434
+ for index, segment in enumerate(delta.split("\n")):
1435
+ if index > 0:
1436
+ # A "\n" boundary confirms any prior deferred line, then defers its own.
1437
+ flush_deferred()
1438
+ state.deferred_newline = True
1439
+ if segment:
1440
+ flush_deferred()
1441
+ if not state.open_has_content:
1442
+ text.append(" ")
1443
+ state.open_has_content = True
1444
+ text.append(segment, style=state.style)
1445
+ state.length += len(delta)
1446
+
1339
1447
  def _markdown_entry(self, header: str, content: str) -> Group:
1340
1448
  return Group(
1341
1449
  Text.from_markup(header),
@@ -40,6 +40,10 @@ class ConversationEntryWidget(Static):
40
40
  self.refresh_content()
41
41
 
42
42
  def refresh_content(self) -> None:
43
+ # Join any streamed deltas into content once here (per flush) rather than on
44
+ # every chunk, so growth stays O(n) instead of O(n^2) attribute concatenation.
45
+ self.entry.materialize()
46
+
43
47
  kind_class = f"entry-{self.entry.kind}"
44
48
  if kind_class != self._kind_class:
45
49
  if self._kind_class:
@@ -55,13 +59,16 @@ class ConversationEntryWidget(Static):
55
59
  self.update(self._formatted)
56
60
 
57
61
  def _entry_snapshot(self) -> tuple[object, ...]:
62
+ # Use content lengths, not the full strings: streaming only appends, so length
63
+ # is a strictly-monotonic change signal and the comparison stays O(1) instead of
64
+ # an O(n) string compare on every coalesced flush.
58
65
  return (
59
66
  self.entry.kind,
60
- self.entry.content,
67
+ len(self.entry.content),
61
68
  self.entry.complete,
62
69
  self.entry.tool_name,
63
70
  self.entry.tone,
64
- self.entry.full_content,
71
+ len(self.entry.full_content),
65
72
  repr(self.entry.edit_preview),
66
73
  )
67
74
 
@@ -132,7 +132,11 @@ class LLMClient:
132
132
  "xai": OpenAIProvider,
133
133
  "dashscope": OpenAIProvider,
134
134
  "moonshot": AnthropicProvider,
135
- "deepseek": AnthropicProvider,
135
+ # DeepSeek's OpenAI-compatible /v1 endpoint streams reasoning_content
136
+ # continuously; its Anthropic-compatible endpoint goes silent during
137
+ # reasoning, so an idle-connection drop hangs streaming turns. Route
138
+ # through the OpenAI Chat path (matches opencode/openclaw, which work).
139
+ "deepseek": OpenAIProvider,
136
140
  "zai": AnthropicProvider,
137
141
  "kimi_coding": AnthropicProvider,
138
142
  "ollama_cloud": OpenAIProvider,
@@ -148,7 +152,7 @@ class LLMClient:
148
152
  "xai": "https://api.x.ai/v1",
149
153
  "dashscope": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
150
154
  "moonshot": "https://api.moonshot.ai/anthropic",
151
- "deepseek": "https://api.deepseek.com/anthropic",
155
+ "deepseek": "https://api.deepseek.com/v1",
152
156
  "zai": "https://api.z.ai/api/anthropic",
153
157
  "kimi_coding": "https://api.kimi.com/coding",
154
158
  "ollama_cloud": "https://ollama.com/v1",
@@ -39,11 +39,17 @@ import asyncio
39
39
 
40
40
  from anthropic import (
41
41
  AnthropicError,
42
+ APIConnectionError as AnthropicAPIConnectionError,
42
43
  APIStatusError as AnthropicAPIStatusError,
44
+ APITimeoutError as AnthropicAPITimeoutError,
43
45
  InternalServerError as AnthropicInternalServerError,
44
46
  )
45
47
  from google.genai.errors import APIError as GoogleAPIError
46
- from openai import OpenAIError
48
+ from openai import (
49
+ APIConnectionError as OpenAIAPIConnectionError,
50
+ APITimeoutError as OpenAIAPITimeoutError,
51
+ OpenAIError,
52
+ )
47
53
 
48
54
  try:
49
55
  import aiohttp
@@ -99,8 +105,15 @@ class LLMNotFoundError(LLMError):
99
105
  """Raised when the requested resource is not found."""
100
106
 
101
107
 
102
- class LLMTimeout(LLMError):
103
- """Raised when the request to the LLM service times out."""
108
+ class LLMConnectionError(LLMError):
109
+ """Raised when the transport layer fails a connection that couldn't be established,
110
+ was dropped/reset, or stalled. Distinct from LLMInternalServerError (a provider 5xx):
111
+ nothing reached the model, or the connection died mid-stream. Transient and retryable."""
112
+
113
+
114
+ class LLMTimeout(LLMConnectionError):
115
+ """Raised when a request to the LLM service times out (a stalled connection). A
116
+ specific kind of LLMConnectionError, e.g. a streaming read exceeding its timeout."""
104
117
 
105
118
 
106
119
  class LLMUnprocessableEntityError(LLMError):
@@ -191,6 +204,9 @@ def llm_error_message(error: LLMError, model: str | None = None) -> str:
191
204
  if isinstance(error, LLMTimeout):
192
205
  return f"{provider_model} timed out while processing this request. Please try again."
193
206
 
207
+ if isinstance(error, LLMConnectionError):
208
+ return f"{provider_model} could not be reached (connection error). Please try again."
209
+
194
210
  if isinstance(error, LLMContentPolicyViolationError):
195
211
  return f"{provider_model} blocked this request due to the provider's content policy."
196
212
 
@@ -248,6 +264,14 @@ def map_openai_errors(error: OpenAIError) -> LLMError:
248
264
  elif error.status_code == 500:
249
265
  return LLMInternalServerError(message=f"OpenAI APIError: {str(error)}", provider=ModelProvider.OPENAI.value)
250
266
 
267
+ # Transport failures (a stalled streaming read hitting the per-request timeout, a
268
+ # dropped/reset connection) are retryable. APITimeoutError subclasses APIConnectionError,
269
+ # so check the timeout case first for the more specific error/message.
270
+ if isinstance(error, OpenAIAPITimeoutError):
271
+ return LLMTimeout(message=f"OpenAI APIError: {str(error)}", provider=ModelProvider.OPENAI.value)
272
+ if isinstance(error, OpenAIAPIConnectionError):
273
+ return LLMConnectionError(message=f"OpenAI APIError: {str(error)}", provider=ModelProvider.OPENAI.value)
274
+
251
275
  return LLMError(message=f"OpenAI APIError: {str(error)}", provider=ModelProvider.OPENAI.value)
252
276
 
253
277
 
@@ -333,6 +357,14 @@ def map_anthropic_errors(error: AnthropicError, provider: str | None = None) ->
333
357
  if error.status_code == 529:
334
358
  return LLMInternalServerError(message=f"AnthropicError: {str(error)}", provider=provider)
335
359
 
360
+ # Transport failures (a stalled streaming read hitting the per-request timeout, a
361
+ # dropped/reset connection) are retryable. APITimeoutError subclasses APIConnectionError,
362
+ # so check the timeout case first for the more specific error/message.
363
+ if isinstance(error, AnthropicAPITimeoutError):
364
+ return LLMTimeout(message=f"AnthropicError: {str(error)}", provider=provider)
365
+ if isinstance(error, AnthropicAPIConnectionError):
366
+ return LLMConnectionError(message=f"AnthropicError: {str(error)}", provider=provider)
367
+
336
368
  return LLMError(message=f"AnthropicError: {str(error)}", provider=provider)
337
369
 
338
370
 
@@ -365,14 +397,20 @@ def map_to_llm_error(error: Exception, provider: str = None) -> LLMError:
365
397
  # Map common Python exceptions
366
398
  if isinstance(error, ValueError):
367
399
  return LLMInvalidRequestError(message=f"Invalid parameter: {str(error)}", provider=provider)
400
+ elif httpx and isinstance(error, httpx.TimeoutException):
401
+ # Read/connect/pool/write timeouts (e.g. a stalled streaming read). Check before
402
+ # the broader TransportError below; LLMTimeout is an LLMConnectionError subclass.
403
+ return LLMTimeout(message=f"Request timeout: {str(error)}", provider=provider)
368
404
  elif isinstance(error, (TimeoutError, asyncio.TimeoutError)):
369
405
  return LLMTimeout(message=f"Request timeout: {str(error)}", provider=provider)
370
406
  elif isinstance(error, ConnectionError):
371
- return LLMInternalServerError(message=f"Connection error: {str(error)}", provider=provider)
407
+ return LLMConnectionError(message=f"Connection error: {str(error)}", provider=provider)
372
408
  elif aiohttp and isinstance(error, aiohttp.ClientError):
373
- return LLMInternalServerError(message=f"HTTP client error: {str(error)}", provider=provider)
374
- elif httpx and isinstance(error, httpx.RemoteProtocolError):
375
- return LLMInternalServerError(message=f"HTTPX protocol error: {str(error)}", provider=provider)
409
+ return LLMConnectionError(message=f"HTTP client error: {str(error)}", provider=provider)
410
+ elif httpx and isinstance(error, httpx.TransportError):
411
+ # Connect/read/protocol/reset errors — transient transport failures (e.g. a dropped
412
+ # streaming connection), so retryable.
413
+ return LLMConnectionError(message=f"HTTPX transport error: {str(error)}", provider=provider)
376
414
  elif isinstance(error, KeyError):
377
415
  return LLMInvalidRequestError(message=f"Missing required parameter: {str(error)}", provider=provider)
378
416
  elif isinstance(error, TypeError):
@@ -89,12 +89,22 @@ class InstrumentedLLMClient(LLMClient):
89
89
 
90
90
  provider = usage_metadata.get("provider", self.provider_name)
91
91
 
92
- if provider in ["anthropic", "moonshot", "deepseek", "kimi_coding"]:
92
+ if provider in ["anthropic", "moonshot", "kimi_coding"]:
93
93
  input_tokens = usage_metadata.get("input_tokens", 0)
94
94
  output_tokens = usage_metadata.get("output_tokens", 0)
95
95
  cache_read_tokens = usage_metadata.get("cache_read_input_tokens", 0)
96
96
  cache_write_tokens = usage_metadata.get("cache_write_input_tokens", 0)
97
- elif provider in ["openai", "openai_chatgpt", "together", "groq", "fireworks", "llama", "xai", "dashscope"]:
97
+ elif provider in [
98
+ "openai",
99
+ "openai_chatgpt",
100
+ "together",
101
+ "groq",
102
+ "fireworks",
103
+ "llama",
104
+ "xai",
105
+ "dashscope",
106
+ "deepseek",
107
+ ]:
98
108
  input_tokens = usage_metadata.get("prompt_tokens", 0)
99
109
  output_tokens = usage_metadata.get("completion_tokens", 0)
100
110
  cache_read_tokens = usage_metadata.get("cache_read_input_tokens", 0)
@@ -246,7 +256,7 @@ class InstrumentedLLMClient(LLMClient):
246
256
  normalized_usage = None
247
257
  if usage_details:
248
258
  provider = usage_details.get("provider", self.provider_name)
249
- if provider in ["anthropic", "moonshot", "deepseek", "kimi_coding"]:
259
+ if provider in ["anthropic", "moonshot", "kimi_coding"]:
250
260
  normalized_usage = {
251
261
  "input": usage_details.get("input_tokens", 0),
252
262
  "output": usage_details.get("output_tokens", 0),
@@ -254,7 +264,7 @@ class InstrumentedLLMClient(LLMClient):
254
264
  "cache_read_input_tokens": usage_details.get("cache_read_input_tokens", 0),
255
265
  "cache_creation_input_tokens": usage_details.get("cache_write_input_tokens", 0),
256
266
  }
257
- elif provider in ["openai", "openai_chatgpt", "fireworks"]:
267
+ elif provider in ["openai", "openai_chatgpt", "fireworks", "deepseek"]:
258
268
  normalized_usage = {
259
269
  "input": usage_details.get("prompt_tokens", 0),
260
270
  "output": usage_details.get("completion_tokens", 0),
@@ -495,7 +505,7 @@ class MinimalLangfuseStreamWrapper:
495
505
  usage_metadata = message.usage_metadata
496
506
  provider = usage_metadata.get("provider", "")
497
507
 
498
- if provider in ["anthropic", "moonshot", "deepseek", "kimi_coding"]:
508
+ if provider in ["anthropic", "moonshot", "kimi_coding"]:
499
509
  return {
500
510
  "input": usage_metadata.get("input_tokens", 0),
501
511
  "output": usage_metadata.get("output_tokens", 0),
@@ -503,7 +513,7 @@ class MinimalLangfuseStreamWrapper:
503
513
  "cache_read_input_tokens": usage_metadata.get("cache_read_input_tokens", 0),
504
514
  "cache_creation_input_tokens": usage_metadata.get("cache_write_input_tokens", 0),
505
515
  }
506
- elif provider in ["openai", "openai_chatgpt", "fireworks"]:
516
+ elif provider in ["openai", "openai_chatgpt", "fireworks", "deepseek"]:
507
517
  return {
508
518
  "input": usage_metadata.get("prompt_tokens", 0),
509
519
  "output": usage_metadata.get("completion_tokens", 0),