kolega-code 0.7.2__tar.gz → 0.8.0__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.7.2 → kolega_code-0.8.0}/.env.example +1 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/PKG-INFO +2 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/concepts/agents.md +2 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/concepts/tools.md +7 -4
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/configuration/environment-variables.md +5 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/configuration/providers-and-models.mdx +15 -4
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/tui/modes.md +2 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/__init__.py +1 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/__init__.py +1 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/baseagent.py +42 -17
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/conversation.py +203 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/investigationagent.py +2 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/planningagent.py +3 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/agents/investigation.md.j2 +1 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/agents/planning.md.j2 +5 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_chatgpt_oauth_provider.py +60 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_client.py +143 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_langfuse_normalization.py +17 -0
- kolega_code-0.8.0/kolega_code/agent/tests/llm/test_model_specs.py +51 -0
- kolega_code-0.8.0/kolega_code/agent/tests/llm/test_openai_message_conversion.py +140 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_openai_token_counting.py +25 -13
- kolega_code-0.8.0/kolega_code/agent/tests/llm/test_supports_vision.py +56 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_thinking_effort.py +26 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_agent_tools_inventory.py +18 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_base_agent.py +82 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_coder_attachments.py +175 -12
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_compaction_persistence.py +3 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_conversation_compaction.py +32 -1
- kolega_code-0.8.0/kolega_code/agent/tests/test_image_compat.py +420 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_planning_agent.py +26 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_prompts.py +2 -0
- kolega_code-0.8.0/kolega_code/agent/tests/test_read_image_tool.py +161 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_tool_registry.py +4 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_tools.py +7 -0
- kolega_code-0.8.0/kolega_code/agent/tool_backend/read_image_tool.py +35 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/workflow_tool.py +3 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tools.py +42 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/app.py +368 -43
- kolega_code-0.8.0/kolega_code/cli/clipboard_image.py +152 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/main.py +17 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/mentions.py +20 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/messages.py +13 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/provider_registry.py +6 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/session_store.py +7 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/slash_commands.py +2 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_app.py +246 -14
- kolega_code-0.8.0/kolega_code/cli/tests/test_app_compaction_restore.py +144 -0
- kolega_code-0.8.0/kolega_code/cli/tests/test_app_image_vision_warning.py +563 -0
- kolega_code-0.8.0/kolega_code/cli/tests/test_app_model_switch_vision.py +214 -0
- kolega_code-0.8.0/kolega_code/cli/tests/test_clipboard_image.py +185 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_mentions.py +30 -0
- kolega_code-0.8.0/kolega_code/cli/tests/test_provider_registry.py +19 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_session_store.py +24 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/instrumented_client.py +2 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/models.py +195 -76
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/providers/anthropic.py +1 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/providers/chatgpt_oauth.py +39 -2
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/providers/openai.py +33 -8
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/specs.py +129 -11
- kolega_code-0.8.0/kolega_code/utils/__init__.py +1 -0
- kolega_code-0.8.0/kolega_code/utils/images.py +140 -0
- kolega_code-0.8.0/kolega_code/utils/tests/__init__.py +0 -0
- kolega_code-0.8.0/kolega_code/utils/tests/test_images.py +90 -0
- kolega_code-0.8.0/kolega_code/utils/tests/test_images_ascii.py +97 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/pyproject.toml +2 -1
- {kolega_code-0.7.2 → kolega_code-0.8.0}/uv.lock +91 -2
- kolega_code-0.7.2/kolega_code/agent/tests/llm/test_model_specs.py +0 -31
- kolega_code-0.7.2/kolega_code/agent/tests/llm/test_openai_message_conversion.py +0 -30
- {kolega_code-0.7.2 → kolega_code-0.8.0}/.github/workflows/ci.yml +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/.github/workflows/docs.yml +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/.github/workflows/release.yml +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/.gitignore +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/CONTRIBUTING.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/LICENSE +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/README.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/RELEASING.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/SECURITY.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/conftest.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/.gitignore +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/README.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/astro.config.mjs +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/package-lock.json +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/package.json +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/public/favicon.svg +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/assets/demo.gif +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/assets/kolega-dark.svg +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/assets/kolega-light.svg +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/cli/ask.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/cli/doctor.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/cli/overview.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/cli/sessions.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/concepts/how-it-works.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/configuration/settings-and-api-keys.mdx +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/configuration/sign-in-with-chatgpt.mdx +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/getting-started/installation.mdx +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/getting-started/quick-start.mdx +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/gigacode.mdx +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/hooks.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/index.mdx +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/skills.mdx +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/tui/composer.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/tui/interface.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/tui/sessions-and-resume.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/tui/slash-commands.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content.config.ts +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/styles/brand.css +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/docs/tsconfig.json +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/browseragent.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/coder.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/common.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/compression.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/context.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/generalagent.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/budget.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/errors.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/executor.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/guide.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/journal.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/runtime.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/tests/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/tests/test_orchestration.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/orchestration/types.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_provider.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.system.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.user.md.j2 +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/auxiliary/terminal/output_summary.system.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/auxiliary/terminal/safety.system.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/auxiliary/tools/think_hard.system.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/extensions/cli/planning_questions.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/extensions/cli/shared_task_list.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/extensions/skills/catalog.md.j2 +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/agents/browser.md.j2 +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/agents/coder_cli.md.j2 +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/agents/general.md.j2 +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/includes/agents_md_instructions.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/includes/environment_variables/workspace_env_vars.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/includes/frontend_guidance.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/includes/starter_templates/expo.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/includes/starter_templates/html_website.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/includes/starter_templates/mern_stack.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/system/includes/starter_templates/react_vite_shadcdn.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/user_tasks/cli/implement_plan.md.j2 +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompt_templates/user_tasks/cli/init_agents.md.j2 +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/prompts.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/compaction_helpers.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_anthropic_token_counting.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_billing_openai_cache.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_chatgpt_live.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_dashscope_mapping.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_error_boundary.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_exceptions.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_google_thought_signature.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_google_tool_signature_live.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_instrumented_client.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_instrumented_client_integration.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_live_providers.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_openai_cached_tokens.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_openai_cached_tokens_stream.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/llm/test_tool_execution_ids.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/test_browser.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/test_browser_parity.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/test_file_system.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/test_sandbox_terminal_input.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/test_terminal.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/test_terminal_buffer.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/services/test_terminal_state_serializer.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_coder_prompt_extensions.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_commands.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_compaction_integration.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_compaction_subagent.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_compaction_tui_events.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_compress_command.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_compression.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_duplicate_tool_results.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_empty_message_handling.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_event_emitter.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_general_agent.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_gigacode_gating.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_hooks_integration.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_html.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_parallel_tool_calls.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_permissions.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/test_prompt_provider.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_agent_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_base_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_browser_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_build_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_create_file_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_edit_preview.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_glob_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_glob_tool_sandbox_parity.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_list_directory_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_read_file_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_replace_entire_file_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_replace_lines_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_search_and_replace_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_search_codebase_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_terminal_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_think_hard_integration.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_think_hard_streaming.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_web_fetch_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_web_search_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tests/tool_backend/test_workflow_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/agent_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/base_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/browser_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/build_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/create_file_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/edit_preview.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/glob_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/list_directory_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/memory_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/read_file_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/replace_entire_file_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/replace_lines_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_and_replace_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/base.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/duckduckgo.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/errors.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/firecrawl.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/models.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/searxng.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_backends/tavily.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/search_codebase_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/streaming_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/terminal_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/think_hard_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/web_fetch_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/tool_backend/web_search_tool.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/agent/utils/commands.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/_jwt.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/chatgpt_oauth.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/constants.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/tests/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/tests/test_chatgpt_oauth.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/tests/test_tokens.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/auth/tokens.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/config.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/connection.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/file_index.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/settings.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/skills.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_cli_config.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_connection.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_file_index.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_main.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_settings.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_skills.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_slash_commands.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_themes.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/tests/test_updater.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/theme.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/cli/updater.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/config.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/events.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/backends.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/config.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/dispatcher.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/events.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/matcher.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/outcome.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/tests/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/hooks/tests/test_hooks.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/client.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/exceptions.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/providers/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/providers/base.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/providers/google.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/providers/models.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/ratelimit.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/llm/tool_execution_ids.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/models/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/models/sandbox_terminal_state.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/permissions.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/runtime.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/README.md +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/async_filesystem.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/base.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/browser.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/event_loop.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/filesystem.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/local.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/serializer.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/terminal.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/sandbox/utils.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/services/base.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/services/browser.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/services/file_system.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/services/html.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/services/terminal.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/services/terminal_buffer.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/tools/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/tools/core.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/tools/definitions.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/tools/registry.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/tools/tests/__init__.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/kolega_code/tools/tests/test_definitions.py +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/run_tests.sh +0 -0
- {kolega_code-0.7.2 → kolega_code-0.8.0}/scripts/install-kolega-code.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kolega-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Local-first AI coding agent for 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/
|
|
@@ -54,6 +54,7 @@ Requires-Dist: nest-asyncio==1.6.0
|
|
|
54
54
|
Requires-Dist: openai==1.109.1
|
|
55
55
|
Requires-Dist: packaging==25.0
|
|
56
56
|
Requires-Dist: pathspec==1.1.1
|
|
57
|
+
Requires-Dist: pillow==12.2.0
|
|
57
58
|
Requires-Dist: playwright==1.59.0
|
|
58
59
|
Requires-Dist: psutil==7.2.2
|
|
59
60
|
Requires-Dist: pydantic==2.13.4
|
|
@@ -12,10 +12,10 @@ agent can hand off to the others when a task benefits from focus.
|
|
|
12
12
|
| Agent | Specialty | Can edit files? |
|
|
13
13
|
| --- | --- | --- |
|
|
14
14
|
| **Coder** | The main, general-purpose coding agent. Full toolset. | Yes |
|
|
15
|
-
| **Investigation** | Read-only exploration of the codebase. | No |
|
|
15
|
+
| **Investigation** | Read-only exploration of the codebase. Can run investigative commands. | No |
|
|
16
16
|
| **Browser** | Web navigation and interaction via Playwright. | No (browser tools only) |
|
|
17
17
|
| **General** | A flexible agent that can read and dispatch other agents. | Limited |
|
|
18
|
-
| **Planning** | Drives [Plan mode](../../tui/modes/): investigates and writes a plan
|
|
18
|
+
| **Planning** | Drives [Plan mode](../../tui/modes/): investigates and writes a plan. Can run commands but cannot edit files. | No |
|
|
19
19
|
|
|
20
20
|
## Dispatching sub-agents
|
|
21
21
|
|
|
@@ -61,13 +61,16 @@ and `dispatch_general_agent`.
|
|
|
61
61
|
## Read-only vs. full access
|
|
62
62
|
|
|
63
63
|
Tools are gated by mode. In a read-only context — like [Plan mode](../../tui/modes/)
|
|
64
|
-
or an investigation sub-agent —
|
|
64
|
+
or an investigation sub-agent — the agent can read and search the codebase
|
|
65
65
|
(`list_directory`, `read_entire_file`, `read_file_section`, `search_codebase`,
|
|
66
66
|
`find_files_by_pattern`, `web_search`, `web_fetch`, `think_hard`, and reading
|
|
67
|
-
memory)
|
|
67
|
+
memory) **and** run shell commands to investigate. Editing files still requires
|
|
68
|
+
Build mode's full toolset.
|
|
68
69
|
|
|
69
|
-
This separation is what
|
|
70
|
-
planning agent can look but
|
|
70
|
+
This separation is what keeps Plan mode safe to run against any codebase: the
|
|
71
|
+
planning agent can look and run investigative commands, but it has no file-edit
|
|
72
|
+
tools. Shell commands are further gated by the active permission mode — in `ask`
|
|
73
|
+
they prompt before running.
|
|
71
74
|
|
|
72
75
|
In the Textual TUI, Build mode defaults to `ask` permission mode. Shell commands
|
|
73
76
|
and file edits must be approved before they run unless you switch to `auto` or
|
{kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/configuration/environment-variables.md
RENAMED
|
@@ -118,6 +118,7 @@ starting point:
|
|
|
118
118
|
# Pick one provider's key (or several)
|
|
119
119
|
MOONSHOT_API_KEY=
|
|
120
120
|
DEEPSEEK_API_KEY=
|
|
121
|
+
FIREWORKS_API_KEY=
|
|
121
122
|
ANTHROPIC_API_KEY=
|
|
122
123
|
|
|
123
124
|
# Optional: choose models per role
|
|
@@ -125,6 +126,10 @@ KOLEGA_CODE_PROVIDER=moonshot
|
|
|
125
126
|
KOLEGA_CODE_MODEL=kimi-k2.7-code
|
|
126
127
|
KOLEGA_CODE_THINKING_EFFORT=auto
|
|
127
128
|
|
|
129
|
+
# Fireworks example:
|
|
130
|
+
# KOLEGA_CODE_PROVIDER=fireworks
|
|
131
|
+
# KOLEGA_CODE_MODEL=accounts/fireworks/models/glm-5p2
|
|
132
|
+
|
|
128
133
|
# Optional: web search
|
|
129
134
|
KOLEGA_CODE_WEB_SEARCH_BACKEND=duckduckgo
|
|
130
135
|
|
{kolega_code-0.7.2 → kolega_code-0.8.0}/docs/src/content/docs/configuration/providers-and-models.mdx
RENAMED
|
@@ -45,11 +45,21 @@ with thinking effort `auto` (enabled) requests route to **K2.7 Code**, and with
|
|
|
45
45
|
(disabled) they route to **K2.6**. Set `KIMI_CODING_API_KEY` to your Kimi Coding Plan key.
|
|
46
46
|
</Aside>
|
|
47
47
|
|
|
48
|
+
<Aside type="tip">
|
|
49
|
+
**Fireworks.ai.** The `fireworks` provider targets Fireworks' OpenAI-compatible
|
|
50
|
+
serverless endpoint (`https://api.fireworks.ai/inference/v1`) with `FIREWORKS_API_KEY`.
|
|
51
|
+
Fireworks model IDs use resource names such as `accounts/fireworks/models/glm-5p2`.
|
|
52
|
+
Kolega Code exposes GLM-5.2, GLM-5.1, Kimi K2.7 Code, DeepSeek V4 Pro/Flash,
|
|
53
|
+
MiniMax M3, and Qwen 3.7 Plus through the Settings UI and CLI. Fireworks reasoning
|
|
54
|
+
models return `reasoning_content`; Kolega Code exposes `none`, `low`, `medium`,
|
|
55
|
+
`high`, and `max` as `reasoning_effort` values for these models.
|
|
56
|
+
</Aside>
|
|
57
|
+
|
|
48
58
|
<Aside type="note">
|
|
49
|
-
The **Settings tab** in the TUI
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
The **Settings tab** in the TUI is generated from Kolega Code's model registry, so
|
|
60
|
+
new entries in the supported model catalog appear there automatically. You can also
|
|
61
|
+
select any supported provider/model through environment variables and CLI flags.
|
|
62
|
+
See [Settings & API Keys](../settings-and-api-keys/).
|
|
53
63
|
</Aside>
|
|
54
64
|
|
|
55
65
|
## Model roles
|
|
@@ -153,6 +163,7 @@ models in the TUI.
|
|
|
153
163
|
| Moonshot `kimi-k2.6` | `auto`, `none` | `auto` |
|
|
154
164
|
| Z.AI `glm-5.2` | `high`, `max` | `max` |
|
|
155
165
|
| Z.AI `glm-5.1` | `auto`, `none` | `auto` |
|
|
166
|
+
| Fireworks serverless reasoning models | `none`, `low`, `medium`, `high`, `max` | `medium` |
|
|
156
167
|
| Google `gemini-2.5-pro` | `auto`, `low`, `medium`, `high` | `medium` |
|
|
157
168
|
| OpenAI reasoning models | `low`, `medium`, `high` | `medium` |
|
|
158
169
|
|
|
@@ -35,6 +35,8 @@ produces a plan.
|
|
|
35
35
|
While planning, the agent can:
|
|
36
36
|
|
|
37
37
|
- Read and search the codebase.
|
|
38
|
+
- Run shell commands to investigate (e.g. `git log`, `grep`, running tests) —
|
|
39
|
+
subject to the current permission mode, so commands prompt for approval in `ask`.
|
|
38
40
|
- Maintain a shared **task list** (visible in the Planning tab).
|
|
39
41
|
- Write a structured plan, which appears in the **Planning** tab.
|
|
40
42
|
|
|
@@ -15,7 +15,7 @@ from .compression import CompactionResult, HistoryCompressor
|
|
|
15
15
|
from kolega_code.config import AgentConfig, ModelProvider
|
|
16
16
|
from kolega_code.events import AgentConnectionManager
|
|
17
17
|
from .context import AgentContext, AgentServices, Telemetry, WorkspaceInfo
|
|
18
|
-
from .conversation import Conversation
|
|
18
|
+
from .conversation import Conversation, adapt_history_for_provider
|
|
19
19
|
from kolega_code.events import AgentEventEmitter
|
|
20
20
|
from kolega_code.hooks import (
|
|
21
21
|
NO_OP_DISPATCHER,
|
|
@@ -34,7 +34,7 @@ from kolega_code.llm.exceptions import (
|
|
|
34
34
|
)
|
|
35
35
|
from kolega_code.llm.models import ImageBlock, Message, MessageHistory, TextBlock, ToolCall, ToolResult
|
|
36
36
|
from kolega_code.llm.providers.models import TokenCount
|
|
37
|
-
from kolega_code.llm.specs import get_model_specs
|
|
37
|
+
from kolega_code.llm.specs import get_model_specs, supports_vision as model_supports_vision
|
|
38
38
|
from kolega_code.permissions import (
|
|
39
39
|
PermissionDecision,
|
|
40
40
|
PermissionMode,
|
|
@@ -90,10 +90,6 @@ class BaseAgent(LogMixin):
|
|
|
90
90
|
long_content_tool_calls = ["create_file", "replace_entire_file"]
|
|
91
91
|
max_tool_result_chars_in_history = 100_000
|
|
92
92
|
skill_content_pattern = re.compile(r'<skill_content name="[^"]+">')
|
|
93
|
-
deepseek_image_unsupported_message = (
|
|
94
|
-
"DeepSeek V4 Pro does not support image input via the DeepSeek API. "
|
|
95
|
-
"Remove the image or switch to a vision-capable model for this request."
|
|
96
|
-
)
|
|
97
93
|
|
|
98
94
|
def __init__(
|
|
99
95
|
self,
|
|
@@ -270,6 +266,11 @@ class BaseAgent(LogMixin):
|
|
|
270
266
|
self.model_context_length = model_specs["context_length"]
|
|
271
267
|
self.model_completion_tokens = model_specs["max_completion_tokens"]
|
|
272
268
|
self.model_default_temperature = model_specs.get("default_temperature", 1.0)
|
|
269
|
+
# Whether this agent's primary model can accept image input. Read by the
|
|
270
|
+
# ToolCollection read_image tool gate (so non-vision models never see the
|
|
271
|
+
# tool) and used by _unsupported_attachment_message to reject image
|
|
272
|
+
# attachments for non-vision models with a clear message.
|
|
273
|
+
self.supports_vision = bool(model_specs.get("supports_vision", False))
|
|
273
274
|
|
|
274
275
|
self.llm = context.create_llm_client(agent_name=self.agent_name)
|
|
275
276
|
|
|
@@ -365,6 +366,26 @@ class BaseAgent(LogMixin):
|
|
|
365
366
|
"""
|
|
366
367
|
return self.conversation.repaired(messages)
|
|
367
368
|
|
|
369
|
+
def _history_for_llm(self) -> MessageHistory:
|
|
370
|
+
"""Build the message history to send to the LLM for this turn.
|
|
371
|
+
|
|
372
|
+
Compaction-aware and tool-call-repaired. For non-vision models, any
|
|
373
|
+
``ImageBlock`` carried over from earlier turns (user attachments or
|
|
374
|
+
``read_image`` tool results) is replaced with a text placeholder on this
|
|
375
|
+
request copy only — the stored history is never mutated, so switching
|
|
376
|
+
back to a vision-capable model restores the images.
|
|
377
|
+
"""
|
|
378
|
+
effective = self.get_effective_history_for_llm()
|
|
379
|
+
fixed = self.fix_incomplete_tool_calls(list(effective))
|
|
380
|
+
provider = getattr(self.primary_model_config.provider, "value", self.primary_model_config.provider)
|
|
381
|
+
fixed = adapt_history_for_provider(
|
|
382
|
+
fixed,
|
|
383
|
+
target_provider=str(provider),
|
|
384
|
+
target_model=self.primary_model_config.model,
|
|
385
|
+
supports_vision=self.supports_vision,
|
|
386
|
+
)
|
|
387
|
+
return MessageHistory(fixed)
|
|
388
|
+
|
|
368
389
|
def mark_cache_checkpoint(self) -> None:
|
|
369
390
|
"""
|
|
370
391
|
Mark the last message in history for caching and remove cache_control from all other messages.
|
|
@@ -480,18 +501,22 @@ class BaseAgent(LogMixin):
|
|
|
480
501
|
# ------------------------------------------------------------------
|
|
481
502
|
|
|
482
503
|
def _unsupported_attachment_message(self, attachments: Optional[List[Dict[str, Any]]]) -> Optional[str]:
|
|
504
|
+
if not any(attachment.get("type") == "image" for attachment in attachments or []):
|
|
505
|
+
return None
|
|
506
|
+
|
|
483
507
|
provider = getattr(
|
|
484
508
|
self.primary_model_config.provider,
|
|
485
509
|
"value",
|
|
486
510
|
self.primary_model_config.provider,
|
|
487
511
|
)
|
|
488
|
-
if provider
|
|
512
|
+
if model_supports_vision(provider, self.primary_model_config.model):
|
|
489
513
|
return None
|
|
490
514
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
515
|
+
return (
|
|
516
|
+
f"{self.primary_model_config.model} does not support image input. "
|
|
517
|
+
"Your message was not sent to the model. "
|
|
518
|
+
"Remove the image attachment or switch to a vision-capable model with /model."
|
|
519
|
+
)
|
|
495
520
|
|
|
496
521
|
def _attachment_blocks(self, attachments: Optional[List[Dict[str, Any]]]) -> List[Any]:
|
|
497
522
|
"""Convert attachment payloads into content blocks for a user message."""
|
|
@@ -518,9 +543,9 @@ class BaseAgent(LogMixin):
|
|
|
518
543
|
|
|
519
544
|
async def count_current_context(self) -> TokenCount:
|
|
520
545
|
self._sanitize_oversized_tool_results()
|
|
521
|
-
#
|
|
522
|
-
|
|
523
|
-
fixed_history =
|
|
546
|
+
# History sent to the LLM (and to token counting): tool-call-repaired and,
|
|
547
|
+
# for non-vision models, stripped of image blocks from earlier turns.
|
|
548
|
+
fixed_history = self._history_for_llm()
|
|
524
549
|
token_count = await self.llm.count_tokens(
|
|
525
550
|
system=self.system_prompt,
|
|
526
551
|
messages=fixed_history,
|
|
@@ -1304,9 +1329,9 @@ class BaseAgent(LogMixin):
|
|
|
1304
1329
|
response_uuid = str(uuid.uuid4())
|
|
1305
1330
|
thinking_uuid = str(uuid.uuid4())
|
|
1306
1331
|
|
|
1307
|
-
#
|
|
1308
|
-
|
|
1309
|
-
fixed_history =
|
|
1332
|
+
# History sent to the LLM: tool-call-repaired and, for non-vision
|
|
1333
|
+
# models, stripped of image blocks carried over from earlier turns.
|
|
1334
|
+
fixed_history = self._history_for_llm()
|
|
1310
1335
|
|
|
1311
1336
|
async with await self.llm.stream(
|
|
1312
1337
|
system=self.system_prompt,
|
|
@@ -11,11 +11,192 @@ import logging
|
|
|
11
11
|
import re
|
|
12
12
|
from typing import Any, Dict, List, Optional
|
|
13
13
|
|
|
14
|
-
from kolega_code.llm.models import
|
|
14
|
+
from kolega_code.llm.models import (
|
|
15
|
+
ImageBlock,
|
|
16
|
+
Message,
|
|
17
|
+
MessageHistory,
|
|
18
|
+
RedactedThinkingBlock,
|
|
19
|
+
TextBlock,
|
|
20
|
+
ThinkingBlock,
|
|
21
|
+
ToolCall,
|
|
22
|
+
ToolResult,
|
|
23
|
+
)
|
|
15
24
|
|
|
16
25
|
logger = logging.getLogger(__name__)
|
|
17
26
|
|
|
18
27
|
|
|
28
|
+
def _image_placeholder(media_type: str, model_name: str) -> str:
|
|
29
|
+
"""Text substituted for an image block when the active model can't see images."""
|
|
30
|
+
return f"[An image ({media_type}) was shared earlier in this thread but is not visible to {model_name}.]"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def count_image_blocks(messages: List[Message]) -> int:
|
|
34
|
+
"""Count ``ImageBlock`` instances across messages, including ones nested in tool results."""
|
|
35
|
+
count = 0
|
|
36
|
+
for message in messages:
|
|
37
|
+
if not isinstance(message.content, list):
|
|
38
|
+
continue
|
|
39
|
+
for block in message.content:
|
|
40
|
+
if isinstance(block, ImageBlock):
|
|
41
|
+
count += 1
|
|
42
|
+
elif isinstance(block, ToolResult) and isinstance(block.content, list):
|
|
43
|
+
count += sum(1 for inner in block.content if isinstance(inner, ImageBlock))
|
|
44
|
+
return count
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _provider_value(provider: Any) -> str:
|
|
48
|
+
value = getattr(provider, "value", provider)
|
|
49
|
+
return str(value or "")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _message_with_content(message: Message, content: List[Any]) -> Message:
|
|
53
|
+
return Message(
|
|
54
|
+
role=message.role,
|
|
55
|
+
content=content,
|
|
56
|
+
stop_reason=message.stop_reason,
|
|
57
|
+
tool_calls=message.tool_calls,
|
|
58
|
+
usage_metadata=message.usage_metadata,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _tool_result_with_content(tool_result: ToolResult, content: List[Any]) -> ToolResult:
|
|
63
|
+
return ToolResult(
|
|
64
|
+
tool_use_id=tool_result.tool_use_id,
|
|
65
|
+
content=content,
|
|
66
|
+
name=tool_result.name,
|
|
67
|
+
is_error=tool_result.is_error,
|
|
68
|
+
cache_checkpoint=tool_result.cache_checkpoint,
|
|
69
|
+
execution_id=tool_result.execution_id,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def replace_image_blocks_with_placeholders(messages: List[Message], model_name: str) -> List[Message]:
|
|
74
|
+
"""Return a new message list with every ``ImageBlock`` replaced by a text placeholder.
|
|
75
|
+
|
|
76
|
+
Non-mutating: messages without images are returned as-is. When a replacement
|
|
77
|
+
occurs, new ``Message``/``ToolResult`` objects are built so the caller's stored
|
|
78
|
+
history is never altered — switching back to a vision-capable model restores
|
|
79
|
+
the original images. ``ImageBlock``s nested inside a ``ToolResult.content`` list
|
|
80
|
+
(e.g. from the ``read_image`` tool) are handled too.
|
|
81
|
+
"""
|
|
82
|
+
result: List[Message] = []
|
|
83
|
+
for message in messages:
|
|
84
|
+
if not isinstance(message.content, list):
|
|
85
|
+
result.append(message)
|
|
86
|
+
continue
|
|
87
|
+
new_blocks: List[Any] = []
|
|
88
|
+
changed = False
|
|
89
|
+
for block in message.content:
|
|
90
|
+
if isinstance(block, ImageBlock):
|
|
91
|
+
new_blocks.append(TextBlock(text=_image_placeholder(block.media_type, model_name)))
|
|
92
|
+
changed = True
|
|
93
|
+
elif isinstance(block, ToolResult) and isinstance(block.content, list):
|
|
94
|
+
inner_new: List[Any] = []
|
|
95
|
+
inner_changed = False
|
|
96
|
+
for inner in block.content:
|
|
97
|
+
if isinstance(inner, ImageBlock):
|
|
98
|
+
inner_new.append(TextBlock(text=_image_placeholder(inner.media_type, model_name)))
|
|
99
|
+
inner_changed = True
|
|
100
|
+
else:
|
|
101
|
+
inner_new.append(inner)
|
|
102
|
+
if inner_changed:
|
|
103
|
+
new_blocks.append(_tool_result_with_content(block, inner_new))
|
|
104
|
+
changed = True
|
|
105
|
+
else:
|
|
106
|
+
new_blocks.append(block)
|
|
107
|
+
else:
|
|
108
|
+
new_blocks.append(block)
|
|
109
|
+
if changed:
|
|
110
|
+
result.append(_message_with_content(message, new_blocks))
|
|
111
|
+
else:
|
|
112
|
+
result.append(message)
|
|
113
|
+
return result
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _preserve_reasoning_block(block: Any, *, source_provider: str, target_provider: str) -> bool:
|
|
117
|
+
return bool(source_provider) and source_provider == target_provider
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _reasoning_placeholder(block: Any, source_provider: str) -> TextBlock:
|
|
121
|
+
source = source_provider or "unknown provider"
|
|
122
|
+
if isinstance(block, RedactedThinkingBlock):
|
|
123
|
+
return TextBlock(text=f"[Prior redacted reasoning from {source} omitted for compatibility.]")
|
|
124
|
+
return TextBlock(text=f"[Prior reasoning from {source} omitted for compatibility.]")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _adapt_content_blocks_for_provider(
|
|
128
|
+
blocks: List[Any], *, source_provider: str, target_provider: str, target_model: str, supports_vision: bool
|
|
129
|
+
) -> tuple[List[Any], bool]:
|
|
130
|
+
adapted: List[Any] = []
|
|
131
|
+
changed = False
|
|
132
|
+
|
|
133
|
+
for block in blocks:
|
|
134
|
+
if isinstance(block, ImageBlock) and not supports_vision:
|
|
135
|
+
adapted.append(TextBlock(text=_image_placeholder(block.media_type, target_model)))
|
|
136
|
+
changed = True
|
|
137
|
+
elif isinstance(block, (ThinkingBlock, RedactedThinkingBlock)):
|
|
138
|
+
if _preserve_reasoning_block(block, source_provider=source_provider, target_provider=target_provider):
|
|
139
|
+
adapted.append(block)
|
|
140
|
+
else:
|
|
141
|
+
adapted.append(_reasoning_placeholder(block, source_provider))
|
|
142
|
+
changed = True
|
|
143
|
+
elif isinstance(block, ToolResult) and isinstance(block.content, list):
|
|
144
|
+
inner, inner_changed = _adapt_content_blocks_for_provider(
|
|
145
|
+
block.content,
|
|
146
|
+
source_provider=source_provider,
|
|
147
|
+
target_provider=target_provider,
|
|
148
|
+
target_model=target_model,
|
|
149
|
+
supports_vision=supports_vision,
|
|
150
|
+
)
|
|
151
|
+
if inner_changed:
|
|
152
|
+
adapted.append(_tool_result_with_content(block, inner))
|
|
153
|
+
changed = True
|
|
154
|
+
else:
|
|
155
|
+
adapted.append(block)
|
|
156
|
+
else:
|
|
157
|
+
adapted.append(block)
|
|
158
|
+
|
|
159
|
+
return adapted, changed
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def adapt_history_for_provider(
|
|
163
|
+
messages: List[Message], *, target_provider: str, target_model: str, supports_vision: bool
|
|
164
|
+
) -> List[Message]:
|
|
165
|
+
"""Return a request-safe history for the target provider without mutating storage.
|
|
166
|
+
|
|
167
|
+
Provider-managed reasoning blocks are not portable across providers, but
|
|
168
|
+
reasoning produced by a provider is safe to send back to the same provider.
|
|
169
|
+
Preserve same-provider reasoning; convert foreign or unknown reasoning
|
|
170
|
+
blocks to text placeholders. Image blocks are preserved for vision models
|
|
171
|
+
and replaced with placeholders for non-vision models, matching the previous
|
|
172
|
+
image compatibility behavior.
|
|
173
|
+
"""
|
|
174
|
+
target_provider = _provider_value(target_provider)
|
|
175
|
+
result: List[Message] = []
|
|
176
|
+
changed_any = False
|
|
177
|
+
|
|
178
|
+
for message in messages:
|
|
179
|
+
if not isinstance(message.content, list):
|
|
180
|
+
result.append(message)
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
source_provider = _provider_value((message.usage_metadata or {}).get("provider"))
|
|
184
|
+
adapted_content, changed = _adapt_content_blocks_for_provider(
|
|
185
|
+
message.content,
|
|
186
|
+
source_provider=source_provider,
|
|
187
|
+
target_provider=target_provider,
|
|
188
|
+
target_model=target_model,
|
|
189
|
+
supports_vision=supports_vision,
|
|
190
|
+
)
|
|
191
|
+
if changed:
|
|
192
|
+
result.append(_message_with_content(message, adapted_content))
|
|
193
|
+
changed_any = True
|
|
194
|
+
else:
|
|
195
|
+
result.append(message)
|
|
196
|
+
|
|
197
|
+
return result if changed_any else messages
|
|
198
|
+
|
|
199
|
+
|
|
19
200
|
class Conversation:
|
|
20
201
|
"""Message history plus the invariants that keep it valid for providers."""
|
|
21
202
|
|
|
@@ -31,8 +212,11 @@ class Conversation:
|
|
|
31
212
|
# Compaction state: ``summary`` stands in for ``history[:compacted_through]``
|
|
32
213
|
# in the effective view; messages from ``compacted_through`` onward are kept
|
|
33
214
|
# verbatim. ``summary is None`` / ``compacted_through == 0`` means uncompacted.
|
|
215
|
+
# ``compacted_history_length`` is the history length captured when compaction
|
|
216
|
+
# ran, so the UI can place the summary after the retained tail on restore.
|
|
34
217
|
self.summary: Optional[Message] = None
|
|
35
218
|
self.compacted_through: int = 0
|
|
219
|
+
self.compacted_history_length: int = 0
|
|
36
220
|
self.max_tool_result_chars = max_tool_result_chars
|
|
37
221
|
|
|
38
222
|
# ------------------------------------------------------------------
|
|
@@ -51,6 +235,7 @@ class Conversation:
|
|
|
51
235
|
self._history = value if isinstance(value, MessageHistory) else MessageHistory(list(value))
|
|
52
236
|
self.summary = None
|
|
53
237
|
self.compacted_through = 0
|
|
238
|
+
self.compacted_history_length = 0
|
|
54
239
|
|
|
55
240
|
@property
|
|
56
241
|
def last_compression_index(self) -> Optional[int]:
|
|
@@ -66,6 +251,7 @@ class Conversation:
|
|
|
66
251
|
if value is None:
|
|
67
252
|
self.summary = None
|
|
68
253
|
self.compacted_through = 0
|
|
254
|
+
self.compacted_history_length = 0
|
|
69
255
|
else:
|
|
70
256
|
self.compacted_through = value + 1
|
|
71
257
|
|
|
@@ -74,6 +260,15 @@ class Conversation:
|
|
|
74
260
|
self._history = MessageHistory([])
|
|
75
261
|
self.summary = None
|
|
76
262
|
self.compacted_through = 0
|
|
263
|
+
self.compacted_history_length = 0
|
|
264
|
+
|
|
265
|
+
def has_image_blocks(self) -> bool:
|
|
266
|
+
"""True if the effective (compaction-aware) history still carries any images.
|
|
267
|
+
|
|
268
|
+
Images folded into a compaction summary are already gone, so this reflects
|
|
269
|
+
only what would actually be sent to the model.
|
|
270
|
+
"""
|
|
271
|
+
return count_image_blocks(list(self.effective_history())) > 0
|
|
77
272
|
|
|
78
273
|
# ------------------------------------------------------------------
|
|
79
274
|
# Appending
|
|
@@ -439,6 +634,7 @@ class Conversation:
|
|
|
439
634
|
summary if isinstance(summary, Message) else Message(role="user", content=[TextBlock(text=str(summary))])
|
|
440
635
|
)
|
|
441
636
|
self.compacted_through = len(self._history)
|
|
637
|
+
self.compacted_history_length = len(self._history)
|
|
442
638
|
|
|
443
639
|
def apply_compaction(self, summary_text: str, split_point: int) -> None:
|
|
444
640
|
"""Record ``summary_text`` as standing in for ``history[:split_point]``.
|
|
@@ -448,6 +644,7 @@ class Conversation:
|
|
|
448
644
|
"""
|
|
449
645
|
self.summary = Message(role="user", content=[TextBlock(text=summary_text)])
|
|
450
646
|
self.compacted_through = max(0, min(split_point, len(self._history)))
|
|
647
|
+
self.compacted_history_length = len(self._history)
|
|
451
648
|
|
|
452
649
|
def compaction_split_point(self, *, keep_recent: int, min_prefix: int) -> Optional[int]:
|
|
453
650
|
"""Index where the verbatim recent tail should begin.
|
|
@@ -500,13 +697,15 @@ class Conversation:
|
|
|
500
697
|
return {
|
|
501
698
|
"summary": self.summary.get_text_content() if self.summary is not None else "",
|
|
502
699
|
"compacted_through": self.compacted_through,
|
|
700
|
+
"compacted_history_length": self.compacted_history_length,
|
|
503
701
|
}
|
|
504
702
|
|
|
505
703
|
def restore_compaction(self, data: Optional[Dict[str, Any]]) -> None:
|
|
506
704
|
"""Restore a compaction boundary saved by ``dump_compaction``.
|
|
507
705
|
|
|
508
706
|
Call AFTER ``restore`` (which resets compaction). Assigns the boundary
|
|
509
|
-
fields directly so it does not trip the history setter's reset.
|
|
707
|
+
fields directly so it does not trip the history setter's reset. Old
|
|
708
|
+
sessions without ``compacted_history_length`` fall back to 0.
|
|
510
709
|
"""
|
|
511
710
|
data = data or {}
|
|
512
711
|
text = (data.get("summary") or "").strip()
|
|
@@ -514,6 +713,8 @@ class Conversation:
|
|
|
514
713
|
if text and through > 0:
|
|
515
714
|
self.summary = Message(role="user", content=[TextBlock(text=text)])
|
|
516
715
|
self.compacted_through = min(through, len(self._history))
|
|
716
|
+
self.compacted_history_length = int(data.get("compacted_history_length") or 0)
|
|
517
717
|
else:
|
|
518
718
|
self.summary = None
|
|
519
719
|
self.compacted_through = 0
|
|
720
|
+
self.compacted_history_length = 0
|
|
@@ -6,7 +6,7 @@ from kolega_code.config import AgentConfig
|
|
|
6
6
|
from kolega_code.events import AgentConnectionManager
|
|
7
7
|
from kolega_code.llm.models import Message, TextBlock
|
|
8
8
|
from .prompt_provider import AgentMode, AgentType, PromptExtension
|
|
9
|
-
from .tools import ToolCollection
|
|
9
|
+
from .tools import ToolCollection, ToolCollectionConfig
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class InvestigationAgent(BaseAgent):
|
|
@@ -106,7 +106,7 @@ class InvestigationAgent(BaseAgent):
|
|
|
106
106
|
self.connection_manager,
|
|
107
107
|
self.config,
|
|
108
108
|
caller=self,
|
|
109
|
-
read_only=True,
|
|
109
|
+
tool_config=ToolCollectionConfig(read_only=True, custom_tool_groups=["command_tools"]),
|
|
110
110
|
filesystem=self.filesystem,
|
|
111
111
|
terminal_manager=self.terminal_manager,
|
|
112
112
|
browser_manager=self.browser_manager,
|
|
@@ -92,7 +92,9 @@ class PlanningAgent(BaseAgent):
|
|
|
92
92
|
self.connection_manager,
|
|
93
93
|
self.config,
|
|
94
94
|
caller=self,
|
|
95
|
-
tool_config=ToolCollectionConfig(
|
|
95
|
+
tool_config=ToolCollectionConfig(
|
|
96
|
+
read_only=True, custom_tool_groups=["planning_tools", "command_tools"]
|
|
97
|
+
),
|
|
96
98
|
filesystem=self.filesystem,
|
|
97
99
|
terminal_manager=self.terminal_manager,
|
|
98
100
|
browser_manager=self.browser_manager,
|
|
@@ -46,7 +46,7 @@ to take further action, so your last message should contain the task result.
|
|
|
46
46
|
|
|
47
47
|
## **Running Commands**
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
You may run shell commands (e.g. `git log`, `grep`, build/lint/test commands) to investigate the codebase. Never use commands to modify the project — no editing files, installing dependencies, committing, pushing, or other side effects. Prefer read-only tools when they suffice. Commands are subject to the active permission mode and may require approval.
|
|
50
50
|
|
|
51
51
|
## **Communication Guidelines**
|
|
52
52
|
|
|
@@ -13,10 +13,13 @@ Here is useful information about the environment:
|
|
|
13
13
|
|
|
14
14
|
## Operating Mode
|
|
15
15
|
|
|
16
|
-
You are in planning mode. Do not implement code changes, create files, edit files,
|
|
17
|
-
|
|
16
|
+
You are in planning mode. Do not implement code changes, create files, edit files, start servers, or perform other mutating actions.
|
|
17
|
+
You may run shell commands (e.g. `git log`, `grep`, build/lint/test commands) to investigate the codebase and reduce ambiguity, but never use them to modify the project — no editing files, installing dependencies, committing, pushing, or other side effects. Prefer read-only tools when they suffice, and use `exec_command` only to inspect or validate.
|
|
18
|
+
Use read-only tools and investigative commands to inspect the repository and reduce ambiguity. If the host provides shared task-list tools, keep that list current enough that another agent could see what has been considered and what remains.
|
|
18
19
|
|
|
19
20
|
When the plan is decision complete, call `write_plan` with the final markdown plan. Do not call `write_plan` while major product or implementation choices are still unresolved.
|
|
21
|
+
If a planning turn ends with a complete new or revised implementation plan, submit it through `write_plan`; do not only print the plan in a normal assistant response.
|
|
22
|
+
When user answers, constraints, or follow-up discussion change the plan, the next `write_plan` payload must be a complete replacement plan that incorporates those decisions and can be implemented without reading earlier discussion.
|
|
20
23
|
|
|
21
24
|
If an important decision cannot be derived from the repository, ask the user a concise question instead of guessing. Otherwise choose conservative defaults that match the codebase.
|
|
22
25
|
|