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.
- {kolega_code-0.11.0 → kolega_code-0.11.1}/CHANGELOG.md +13 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/PKG-INFO +1 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/coverage.svg +1 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/__init__.py +1 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/__init__.py +1 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/baseagent.py +11 -2
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/app.py +23 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/file_index.py +26 -4
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/theme.py +8 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/agent_runtime.py +4 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/state.py +18 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/transcript.py +110 -2
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/widgets.py +9 -2
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/client.py +6 -2
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/exceptions.py +45 -7
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/instrumented_client.py +16 -6
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/anthropic.py +105 -11
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/chatgpt_oauth.py +6 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/google.py +6 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/openai.py +157 -68
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/responses_common.py +4 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/thinking.py +6 -5
- kolega_code-0.11.1/kolega_code/llm/timeouts.py +37 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/pyproject.toml +1 -1
- kolega_code-0.11.1/scripts/bench_hotpaths.py +162 -0
- kolega_code-0.11.1/tests/agent/llm/test_anthropic_local_token_counting_incremental.py +107 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_dashscope_mapping.py +5 -4
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_error_boundary.py +9 -7
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_instrumented_client.py +4 -2
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_langfuse_normalization.py +5 -2
- kolega_code-0.11.1/tests/agent/llm/test_openai_token_counting_incremental.py +136 -0
- kolega_code-0.11.1/tests/agent/llm/test_streaming_timeouts.py +109 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_thinking_effort.py +15 -5
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/_app_test_utils.py +6 -1
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_mention_dropdown.py +37 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_file_index.py +33 -3
- kolega_code-0.11.1/tests/cli/test_transcript_streaming_render.py +119 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/uv.lock +2 -2
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.env.example +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.githooks/pre-commit +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/workflows/ci.yml +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/workflows/docs.yml +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.github/workflows/release.yml +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.gitignore +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/.pre-commit-config.yaml +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/CONTRIBUTING.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/LICENSE +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/README.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/RELEASING.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/SECURITY.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/.gitignore +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/README.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/astro.config.mjs +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/package-lock.json +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/package.json +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/public/favicon.svg +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/demo.gif +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/kolega-dark.svg +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/assets/kolega-light.svg +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/ask.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/doctor.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/overview.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/cli/sessions.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/concepts/agents.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/concepts/how-it-works.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/concepts/tools.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/environment-variables.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/prompt-overrides.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/providers-and-models.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/settings-and-api-keys.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/configuration/sign-in-with-chatgpt.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/getting-started/installation.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/getting-started/quick-start.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/gigacode.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/hooks.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/index.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/skills.mdx +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/composer.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/interface.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/modes.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/sessions-and-resume.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content/docs/tui/slash-commands.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/content.config.ts +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/src/styles/brand.css +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/docs/tsconfig.json +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/browseragent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/coder.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/common.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/compression.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/context.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/conversation.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/errors.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/generalagent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/investigationagent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/budget.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/errors.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/executor.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/guide.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/journal.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/runtime.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/orchestration/types.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/planningagent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_dump.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_overrides.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_provider.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.system.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.user.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/terminal/output_summary.system.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/terminal/safety.system.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/auxiliary/tools/think_hard.system.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/extensions/cli/planning_questions.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/extensions/cli/shared_task_list.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/extensions/skills/catalog.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/browser.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/coder_cli.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/general.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/investigation.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/agents/planning.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/agents_md_instructions.md +0 -0
- {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
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/frontend_guidance.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/expo.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/html_website.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/mern_stack.md +0 -0
- {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
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/user_tasks/cli/implement_plan.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompt_templates/user_tasks/cli/init_agents.md.j2 +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/prompts.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/agent_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/base_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/browser_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/build_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/create_file_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/edit_preview.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/glob_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/list_directory_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/memory_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/read_file_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/read_image_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/replace_entire_file_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/replace_lines_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_and_replace_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/base.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/duckduckgo.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/errors.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/firecrawl.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/models.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/searxng.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_backends/tavily.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/search_codebase_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/streaming_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/terminal_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/think_hard_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/web_fetch_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/web_search_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tool_backend/workflow_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/tools.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/agent/utils/commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/_jwt.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/chatgpt_oauth.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/constants.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/auth/tokens.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/clipboard_image.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/config.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/connection.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/main.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/mentions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/messages.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/provider_registry.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/session_store.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/settings.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/skills.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/slash_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/changes_screen.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/command_handlers.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/constants.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/prompt_flows.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/session_diff.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/settings_panel.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/status_dashboard.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/styles.tcss +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/sub_agent_screen.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/tui/terminal_display.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/cli/updater.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/config.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/events.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/backends.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/config.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/dispatcher.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/events.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/matcher.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/hooks/outcome.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/models.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/base.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/models.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/providers/openai_responses.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/ratelimit.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/accessors.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/anthropic.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/dashscope.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/deepseek.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/fireworks.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/google.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/kimi_coding.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/moonshot.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/ollama_cloud.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/openai.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/openai_chatgpt.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/together.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/xai.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/catalog/zai.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/specs/types.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/llm/tool_execution_ids.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/local_state.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/models/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/models/sandbox_terminal_state.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/permissions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/runtime.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/README.md +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/async_filesystem.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/base.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/browser.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/event_loop.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/filesystem.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/local.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/serializer.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/terminal.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/sandbox/utils.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/base.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/browser.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/file_system.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/html.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/terminal.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/services/terminal_buffer.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/core.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/definitions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/tools/registry.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/utils/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/kolega_code/utils/images.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/run_tests.sh +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/scripts/check-dev-hooks.sh +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/scripts/install-kolega-code.sh +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/scripts/setup-dev.sh +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/compaction_helpers.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/conftest.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/_token_counting_utils.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_anthropic_token_counting.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_billing_openai_cache.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_live.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_auth_config.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_conversion.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_provider.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_provider_generate.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_chatgpt_oauth_streaming.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_client.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_client_live.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_client_runtime.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_exceptions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_google_thought_signature.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_google_tool_signature_live.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_instrumented_client_integration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_instrumented_client_stream.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_live_providers.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_message_conversion.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_model_specs.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_cached_tokens.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_cached_tokens_stream.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_message_conversion.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_responses_provider.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_openai_token_counting.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_provider_usage_mapping.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_supports_vision.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/llm/test_tool_execution_ids.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/orchestration/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/orchestration/test_orchestration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_console_logs.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_content.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_integration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_browser_parity.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_file_system.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_file_system_integration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_file_system_path.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_local_file_system.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_sandbox_terminal_input.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_terminal.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_terminal_buffer.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/services/test_terminal_state_serializer.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_agent_tools_inventory.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_anthropic_history_validation.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_compaction.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_effective_history.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_history_serialization.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_prompt_context.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_role_overrides.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_runtime.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_tool_call_repair.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_tool_call_repair_integration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_base_agent_tool_execution.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_coder_attachments.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_coder_prompt_extensions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_integration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_persistence.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_subagent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compaction_tui_events.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compress_command.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_compression.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_conversation_compaction.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_duplicate_tool_results.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_empty_message_handling.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_event_emitter.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_general_agent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_gigacode_gating.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_history_adapter_thinking.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_history_image_blocks.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_hooks_integration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_html.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_image_compat.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_parallel_tool_calls.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_permissions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_planning_agent.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompt_dump.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompt_overrides.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompt_provider.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_prompts.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_read_image_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_builtin_dispatch.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_config.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_extensions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_initialization.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_collection_memory.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tool_registry.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/test_tools.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_agent_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_base_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_browser_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_build_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_create_file_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_edit_preview.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_glob_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_glob_tool_sandbox_parity.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_list_directory_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_read_file_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_replace_entire_file_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_replace_lines_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_search_and_replace_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_search_codebase_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_terminal_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_integration.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming_content.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming_errors.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_think_hard_streaming_updates.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_web_fetch_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_web_search_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/agent/tool_backend/test_workflow_tool.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/auth/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/auth/test_chatgpt_oauth.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/auth/test_tokens.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_agent_wiring.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_bootstrap_settings.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_changes_inspector.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_compaction_restore.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_composer_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_composer_input.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_conversation_rendering.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_focus_behavior.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_image_vision_warning.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_misc_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_model_effort_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_model_switch_vision.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_modes_planning.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_navigation_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_permission_prompts.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_persistence_history.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_plan_lifecycle.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_planning_questions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_planning_restore.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_entries.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_errors.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_scroll.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_selection.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_status.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_streaming.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_terminal_logs.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_rendering_tools.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_skill_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_slash_dropdown.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_status_modes.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_app_sub_agents_workflows.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_cli_config.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_clipboard_image.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_connection.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_main.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_mentions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_provider_registry.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_session_diff.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_session_store.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_settings.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_skills.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_slash_commands.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_terminal_display.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_themes.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_tui_stylesheet.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/cli/test_updater.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/conftest.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/hooks/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/hooks/test_hooks.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/tools/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/tools/test_definitions.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/utils/__init__.py +0 -0
- {kolega_code-0.11.0 → kolega_code-0.11.1}/tests/utils/test_images.py +0 -0
- {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 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 80.
|
|
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>
|
|
@@ -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
|
-
#
|
|
1261
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
|
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
|
|
103
|
-
"""Raised when the
|
|
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
|
|
407
|
+
return LLMConnectionError(message=f"Connection error: {str(error)}", provider=provider)
|
|
372
408
|
elif aiohttp and isinstance(error, aiohttp.ClientError):
|
|
373
|
-
return
|
|
374
|
-
elif httpx and isinstance(error, httpx.
|
|
375
|
-
|
|
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", "
|
|
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 [
|
|
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", "
|
|
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", "
|
|
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),
|