kolega-code 0.1.0__py3-none-any.whl
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/__init__.py +151 -0
- kolega_code/agent/__init__.py +42 -0
- kolega_code/agent/baseagent.py +998 -0
- kolega_code/agent/browseragent.py +123 -0
- kolega_code/agent/coder.py +157 -0
- kolega_code/agent/common.py +41 -0
- kolega_code/agent/compression.py +81 -0
- kolega_code/agent/context.py +112 -0
- kolega_code/agent/conversation.py +408 -0
- kolega_code/agent/generalagent.py +146 -0
- kolega_code/agent/investigationagent.py +123 -0
- kolega_code/agent/planningagent.py +187 -0
- kolega_code/agent/prompt_provider.py +196 -0
- kolega_code/agent/prompt_templates/agents/browser.j2 +102 -0
- kolega_code/agent/prompt_templates/agents/coder_cli_mode.j2 +127 -0
- kolega_code/agent/prompt_templates/agents/general.j2 +68 -0
- kolega_code/agent/prompt_templates/agents/investigation.j2 +72 -0
- kolega_code/agent/prompt_templates/common/frontend_guidance.md +36 -0
- kolega_code/agent/prompt_templates/common/kolega_md_instructions.md +14 -0
- kolega_code/agent/prompt_templates/environment_variables/workspace_env_vars.md +11 -0
- kolega_code/agent/prompt_templates/template_guidance/expo-template.md +379 -0
- kolega_code/agent/prompt_templates/template_guidance/html-website-template.md +3 -0
- kolega_code/agent/prompt_templates/template_guidance/mern-stack-template.md +3 -0
- kolega_code/agent/prompt_templates/template_guidance/react-vite-shadcdn-template.md +182 -0
- kolega_code/agent/prompts.py +192 -0
- kolega_code/agent/tests/__init__.py +0 -0
- kolega_code/agent/tests/llm/__init__.py +0 -0
- kolega_code/agent/tests/llm/test_anthropic_token_counting.py +633 -0
- kolega_code/agent/tests/llm/test_billing_openai_cache.py +74 -0
- kolega_code/agent/tests/llm/test_client.py +773 -0
- kolega_code/agent/tests/llm/test_dashscope_mapping.py +32 -0
- kolega_code/agent/tests/llm/test_error_boundary.py +322 -0
- kolega_code/agent/tests/llm/test_exceptions.py +249 -0
- kolega_code/agent/tests/llm/test_instrumented_client.py +536 -0
- kolega_code/agent/tests/llm/test_instrumented_client_integration.py +547 -0
- kolega_code/agent/tests/llm/test_langfuse_normalization.py +39 -0
- kolega_code/agent/tests/llm/test_model_specs.py +17 -0
- kolega_code/agent/tests/llm/test_openai_cached_tokens.py +58 -0
- kolega_code/agent/tests/llm/test_openai_cached_tokens_stream.py +74 -0
- kolega_code/agent/tests/llm/test_openai_message_conversion.py +30 -0
- kolega_code/agent/tests/llm/test_openai_token_counting.py +687 -0
- kolega_code/agent/tests/llm/test_tool_execution_ids.py +193 -0
- kolega_code/agent/tests/services/__init__.py +1 -0
- kolega_code/agent/tests/services/test_browser.py +447 -0
- kolega_code/agent/tests/services/test_browser_parity.py +353 -0
- kolega_code/agent/tests/services/test_file_system.py +699 -0
- kolega_code/agent/tests/services/test_sandbox_terminal_input.py +98 -0
- kolega_code/agent/tests/services/test_terminal.py +154 -0
- kolega_code/agent/tests/services/test_terminal_command_tracking.py +385 -0
- kolega_code/agent/tests/services/test_terminal_state_serializer.py +262 -0
- kolega_code/agent/tests/test_agent_tools_inventory.py +267 -0
- kolega_code/agent/tests/test_base_agent.py +1942 -0
- kolega_code/agent/tests/test_coder_attachments.py +330 -0
- kolega_code/agent/tests/test_coder_prompt_extensions.py +61 -0
- kolega_code/agent/tests/test_commands.py +179 -0
- kolega_code/agent/tests/test_duplicate_tool_results.py +556 -0
- kolega_code/agent/tests/test_empty_message_handling.py +48 -0
- kolega_code/agent/tests/test_general_agent.py +242 -0
- kolega_code/agent/tests/test_html.py +320 -0
- kolega_code/agent/tests/test_parallel_tool_calls.py +291 -0
- kolega_code/agent/tests/test_planning_agent.py +227 -0
- kolega_code/agent/tests/test_prompt_provider.py +271 -0
- kolega_code/agent/tests/test_tool_registry.py +102 -0
- kolega_code/agent/tests/test_tools.py +549 -0
- kolega_code/agent/tests/tool_backend/__init__.py +0 -0
- kolega_code/agent/tests/tool_backend/test_agent_tool.py +356 -0
- kolega_code/agent/tests/tool_backend/test_base_tool.py +147 -0
- kolega_code/agent/tests/tool_backend/test_browser_tool.py +335 -0
- kolega_code/agent/tests/tool_backend/test_build_tool.py +93 -0
- kolega_code/agent/tests/tool_backend/test_create_file_tool.py +115 -0
- kolega_code/agent/tests/tool_backend/test_glob_tool.py +196 -0
- kolega_code/agent/tests/tool_backend/test_glob_tool_sandbox_parity.py +230 -0
- kolega_code/agent/tests/tool_backend/test_list_directory_tool.py +292 -0
- kolega_code/agent/tests/tool_backend/test_read_file_tool.py +173 -0
- kolega_code/agent/tests/tool_backend/test_replace_entire_file_tool.py +115 -0
- kolega_code/agent/tests/tool_backend/test_replace_lines_tool.py +141 -0
- kolega_code/agent/tests/tool_backend/test_search_and_replace_tool.py +174 -0
- kolega_code/agent/tests/tool_backend/test_search_codebase_tool.py +228 -0
- kolega_code/agent/tests/tool_backend/test_terminal_tool.py +482 -0
- kolega_code/agent/tests/tool_backend/test_think_hard_integration.py +189 -0
- kolega_code/agent/tests/tool_backend/test_think_hard_streaming.py +445 -0
- kolega_code/agent/tests/tool_backend/test_web_fetch_tool.py +194 -0
- kolega_code/agent/tool_backend/agent_tool.py +414 -0
- kolega_code/agent/tool_backend/apply_edit_tool.py +98 -0
- kolega_code/agent/tool_backend/apply_patch_tool.py +514 -0
- kolega_code/agent/tool_backend/base_tool.py +217 -0
- kolega_code/agent/tool_backend/browser_tool.py +271 -0
- kolega_code/agent/tool_backend/build_tool.py +93 -0
- kolega_code/agent/tool_backend/create_file_tool.py +52 -0
- kolega_code/agent/tool_backend/glob_tool.py +323 -0
- kolega_code/agent/tool_backend/list_directory_tool.py +300 -0
- kolega_code/agent/tool_backend/memory_tool.py +79 -0
- kolega_code/agent/tool_backend/read_file_tool.py +119 -0
- kolega_code/agent/tool_backend/replace_entire_file_tool.py +40 -0
- kolega_code/agent/tool_backend/replace_lines_tool.py +97 -0
- kolega_code/agent/tool_backend/search_and_replace_tool.py +146 -0
- kolega_code/agent/tool_backend/search_codebase_tool.py +377 -0
- kolega_code/agent/tool_backend/streaming_tool.py +47 -0
- kolega_code/agent/tool_backend/terminal_tool.py +643 -0
- kolega_code/agent/tool_backend/think_hard_tool.py +211 -0
- kolega_code/agent/tool_backend/web_fetch_tool.py +205 -0
- kolega_code/agent/tools.py +1704 -0
- kolega_code/agent/utils/commands.py +94 -0
- kolega_code/cli/__init__.py +1 -0
- kolega_code/cli/app.py +2756 -0
- kolega_code/cli/config.py +280 -0
- kolega_code/cli/connection.py +49 -0
- kolega_code/cli/file_index.py +147 -0
- kolega_code/cli/main.py +564 -0
- kolega_code/cli/mentions.py +155 -0
- kolega_code/cli/messages.py +89 -0
- kolega_code/cli/provider_registry.py +96 -0
- kolega_code/cli/session_store.py +207 -0
- kolega_code/cli/settings.py +87 -0
- kolega_code/cli/skills.py +409 -0
- kolega_code/cli/slash_commands.py +108 -0
- kolega_code/cli/tests/__init__.py +1 -0
- kolega_code/cli/tests/test_app.py +4251 -0
- kolega_code/cli/tests/test_cli_config.py +171 -0
- kolega_code/cli/tests/test_connection.py +26 -0
- kolega_code/cli/tests/test_file_index.py +103 -0
- kolega_code/cli/tests/test_main.py +455 -0
- kolega_code/cli/tests/test_mentions.py +108 -0
- kolega_code/cli/tests/test_session_store.py +67 -0
- kolega_code/cli/tests/test_settings.py +62 -0
- kolega_code/cli/tests/test_skills.py +157 -0
- kolega_code/cli/tests/test_slash_commands.py +88 -0
- kolega_code/cli/theme.py +180 -0
- kolega_code/config.py +154 -0
- kolega_code/events.py +202 -0
- kolega_code/llm/client.py +300 -0
- kolega_code/llm/exceptions.py +285 -0
- kolega_code/llm/instrumented_client.py +520 -0
- kolega_code/llm/models.py +1368 -0
- kolega_code/llm/providers/__init__.py +0 -0
- kolega_code/llm/providers/anthropic.py +387 -0
- kolega_code/llm/providers/base.py +71 -0
- kolega_code/llm/providers/google.py +157 -0
- kolega_code/llm/providers/models.py +37 -0
- kolega_code/llm/providers/openai.py +363 -0
- kolega_code/llm/ratelimit.py +40 -0
- kolega_code/llm/specs.py +67 -0
- kolega_code/llm/tool_execution_ids.py +18 -0
- kolega_code/models/__init__.py +9 -0
- kolega_code/models/sandbox_terminal_state.py +47 -0
- kolega_code/runtime.py +50 -0
- kolega_code/sandbox/README.md +200 -0
- kolega_code/sandbox/__init__.py +21 -0
- kolega_code/sandbox/async_filesystem.py +475 -0
- kolega_code/sandbox/base.py +297 -0
- kolega_code/sandbox/browser.py +25 -0
- kolega_code/sandbox/event_loop.py +43 -0
- kolega_code/sandbox/filesystem.py +341 -0
- kolega_code/sandbox/local.py +118 -0
- kolega_code/sandbox/serializer.py +175 -0
- kolega_code/sandbox/terminal.py +868 -0
- kolega_code/sandbox/utils.py +216 -0
- kolega_code/services/base.py +255 -0
- kolega_code/services/browser.py +444 -0
- kolega_code/services/file_system.py +749 -0
- kolega_code/services/html.py +221 -0
- kolega_code/services/terminal.py +903 -0
- kolega_code/tools/__init__.py +22 -0
- kolega_code/tools/core.py +33 -0
- kolega_code/tools/definitions.py +81 -0
- kolega_code/tools/registry.py +73 -0
- kolega_code-0.1.0.dist-info/METADATA +157 -0
- kolega_code-0.1.0.dist-info/RECORD +171 -0
- kolega_code-0.1.0.dist-info/WHEEL +4 -0
- kolega_code-0.1.0.dist-info/entry_points.txt +2 -0
- kolega_code-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from .baseagent import BaseAgent
|
|
5
|
+
from kolega_code.config import AgentConfig
|
|
6
|
+
from kolega_code.events import AgentConnectionManager
|
|
7
|
+
from kolega_code.llm.models import Message, TextBlock
|
|
8
|
+
from .prompt_provider import AgentType, PromptExtension
|
|
9
|
+
from .tools import ToolCollection
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BrowserAgent(BaseAgent):
|
|
13
|
+
"""
|
|
14
|
+
An AI coding agent that operates within a workspace to assist with programming tasks.
|
|
15
|
+
|
|
16
|
+
The agent has access to the project filesystem and can perform coding operations
|
|
17
|
+
like reading, analyzing, and modifying code files.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
agent_name = "browser-agent"
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
project_path: str | Path,
|
|
25
|
+
workspace_id: str,
|
|
26
|
+
thread_id: str,
|
|
27
|
+
connection_manager: AgentConnectionManager,
|
|
28
|
+
config: AgentConfig,
|
|
29
|
+
sub_agent: bool = True,
|
|
30
|
+
filesystem=None,
|
|
31
|
+
terminal_manager=None,
|
|
32
|
+
browser_manager=None,
|
|
33
|
+
langfuse_client=None,
|
|
34
|
+
user_id: Optional[str] = None,
|
|
35
|
+
user_email: Optional[str] = None,
|
|
36
|
+
project_template_slug: Optional[str] = None,
|
|
37
|
+
protected_files: Optional[List[str]] = None,
|
|
38
|
+
agent_mode: Optional["AgentMode"] = None,
|
|
39
|
+
workspace_env_var_descriptions: Optional[Dict[str, str]] = None,
|
|
40
|
+
workspace_memories: Optional[List[str]] = None,
|
|
41
|
+
prompt_extensions: Optional[List[PromptExtension]] = None,
|
|
42
|
+
tool_extensions: Optional[List[Any]] = None,
|
|
43
|
+
usage_recorder: Optional[Any] = None,
|
|
44
|
+
sub_agent_recorder: Optional[Any] = None,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Initialize a new BrowserAgent instance.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
project_path: File system path to the project root directory
|
|
51
|
+
workspace_id: Identifier for the workspace
|
|
52
|
+
thread_id: Identifier for the thread
|
|
53
|
+
connection_manager: Manager for handling agent connections
|
|
54
|
+
config: Agent configuration
|
|
55
|
+
sub_agent: Whether this agent is a sub-agent of another agent
|
|
56
|
+
filesystem: Optional filesystem implementation
|
|
57
|
+
terminal_manager: Optional terminal manager implementation
|
|
58
|
+
browser_manager: Optional browser manager implementation
|
|
59
|
+
langfuse_client: Optional Langfuse client for LLM observability
|
|
60
|
+
user_id: Optional ID of user who created this job
|
|
61
|
+
user_email: Optional email of user who created this job
|
|
62
|
+
project_template_slug: Optional slug of the project template being used
|
|
63
|
+
protected_files: Optional list of file basenames protected from edits in vibe mode
|
|
64
|
+
agent_mode: Optional agent mode (not used for BrowserAgent)
|
|
65
|
+
workspace_env_var_descriptions: Optional mapping of workspace environment variable descriptions
|
|
66
|
+
workspace_memories: Optional list of workspace memories to inject into prompts
|
|
67
|
+
prompt_extensions: Host-provided prompt sections for app-specific context
|
|
68
|
+
tool_extensions: Host-provided tool providers for app-specific tools
|
|
69
|
+
usage_recorder: Optional callback for recording normalized LLM usage
|
|
70
|
+
sub_agent_recorder: Optional callback for persisting sub-agent conversation state
|
|
71
|
+
"""
|
|
72
|
+
super().__init__(
|
|
73
|
+
project_path,
|
|
74
|
+
workspace_id,
|
|
75
|
+
thread_id,
|
|
76
|
+
connection_manager,
|
|
77
|
+
config,
|
|
78
|
+
sub_agent=sub_agent,
|
|
79
|
+
filesystem=filesystem,
|
|
80
|
+
terminal_manager=terminal_manager,
|
|
81
|
+
browser_manager=browser_manager,
|
|
82
|
+
langfuse_client=langfuse_client,
|
|
83
|
+
user_id=user_id,
|
|
84
|
+
user_email=user_email,
|
|
85
|
+
project_template_slug=project_template_slug,
|
|
86
|
+
protected_files=protected_files,
|
|
87
|
+
agent_mode=agent_mode,
|
|
88
|
+
workspace_env_var_descriptions=workspace_env_var_descriptions,
|
|
89
|
+
workspace_memories=workspace_memories,
|
|
90
|
+
prompt_extensions=prompt_extensions,
|
|
91
|
+
tool_extensions=tool_extensions,
|
|
92
|
+
usage_recorder=usage_recorder,
|
|
93
|
+
sub_agent_recorder=sub_agent_recorder,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.tool_collection = ToolCollection(
|
|
97
|
+
self.project_path,
|
|
98
|
+
self.workspace_id,
|
|
99
|
+
self.thread_id,
|
|
100
|
+
self.connection_manager,
|
|
101
|
+
self.config,
|
|
102
|
+
caller=self,
|
|
103
|
+
browser_only=True,
|
|
104
|
+
filesystem=self.filesystem,
|
|
105
|
+
terminal_manager=self.terminal_manager,
|
|
106
|
+
browser_manager=self.browser_manager,
|
|
107
|
+
langfuse_client=self.langfuse_client,
|
|
108
|
+
tool_extensions=self.tool_extensions,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
self._initialize_system_prompt()
|
|
112
|
+
|
|
113
|
+
def _initialize_system_prompt(self):
|
|
114
|
+
"""Initialize system prompt using PromptProvider."""
|
|
115
|
+
# Generate prompt using the shared prompt provider
|
|
116
|
+
prompt_text = self.prompt_provider.get_system_prompt(
|
|
117
|
+
agent_type=AgentType.BROWSER,
|
|
118
|
+
mode=self.agent_mode,
|
|
119
|
+
template_slug=self.project_template_slug,
|
|
120
|
+
prompt_extensions=self.prompt_extensions,
|
|
121
|
+
context=self.build_prompt_context(),
|
|
122
|
+
)
|
|
123
|
+
self.system_prompt = Message(role="system", content=[TextBlock(text=prompt_text)])
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from .baseagent import BaseAgent
|
|
5
|
+
from .common import LogMixin
|
|
6
|
+
from kolega_code.config import AgentConfig
|
|
7
|
+
from kolega_code.events import AgentConnectionManager
|
|
8
|
+
from kolega_code.llm.models import Message, TextBlock
|
|
9
|
+
from .prompt_provider import AgentType, AgentMode, PromptExtension, PromptProvider
|
|
10
|
+
from .tools import ToolCollection, ToolCollectionConfig
|
|
11
|
+
from .utils.commands import CommandProcessor
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@CommandProcessor.process_commands
|
|
15
|
+
class CoderAgent(BaseAgent, LogMixin):
|
|
16
|
+
"""
|
|
17
|
+
An AI coding agent that operates within a workspace to assist with programming tasks.
|
|
18
|
+
|
|
19
|
+
The agent has access to the project filesystem and can perform coding operations
|
|
20
|
+
like reading, analyzing, and modifying code files.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
agent_name = "coder"
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
project_path: str | Path,
|
|
28
|
+
workspace_id: str,
|
|
29
|
+
thread_id: str,
|
|
30
|
+
connection_manager: AgentConnectionManager,
|
|
31
|
+
config: AgentConfig,
|
|
32
|
+
sub_agent: bool = False,
|
|
33
|
+
filesystem=None,
|
|
34
|
+
terminal_manager=None,
|
|
35
|
+
browser_manager=None,
|
|
36
|
+
langfuse_client=None,
|
|
37
|
+
user_id: Optional[str] = None,
|
|
38
|
+
user_email: Optional[str] = None,
|
|
39
|
+
project_template_slug: Optional[str] = None,
|
|
40
|
+
protected_files: Optional[List[str]] = None,
|
|
41
|
+
agent_mode: Optional[AgentMode] = None,
|
|
42
|
+
workspace_env_var_descriptions: Optional[Dict[str, str]] = None,
|
|
43
|
+
workspace_memories: Optional[List[str]] = None,
|
|
44
|
+
prompt_provider: Optional[PromptProvider] = None,
|
|
45
|
+
prompt_extensions: Optional[List[PromptExtension]] = None,
|
|
46
|
+
tool_extensions: Optional[List[Any]] = None,
|
|
47
|
+
usage_recorder: Optional[Any] = None,
|
|
48
|
+
sub_agent_recorder: Optional[Any] = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""
|
|
51
|
+
Initialize a new CoderAgent instance.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
project_path: File system path to the project root directory
|
|
55
|
+
workspace_id: Identifier for the workspace
|
|
56
|
+
thread_id: Identifier for the thread
|
|
57
|
+
connection_manager: Manager for handling agent connections
|
|
58
|
+
config: Agent configuration settings
|
|
59
|
+
sub_agent: Whether this is a sub-agent (default: False)
|
|
60
|
+
filesystem: File system implementation (optional)
|
|
61
|
+
terminal_manager: Terminal manager implementation (optional)
|
|
62
|
+
browser_manager: Browser manager implementation (optional)
|
|
63
|
+
langfuse_client: Optional Langfuse client for LLM observability
|
|
64
|
+
user_id: Optional ID of user who created this job
|
|
65
|
+
user_email: Optional email of user who created this job
|
|
66
|
+
project_template_slug: Optional slug of the project template being used
|
|
67
|
+
protected_files: Optional list of file basenames protected from edits in vibe mode
|
|
68
|
+
agent_mode: Optional agent mode (CLI, VIBE, CODE, or FIX)
|
|
69
|
+
workspace_env_var_descriptions: Optional mapping of workspace environment variable descriptions
|
|
70
|
+
workspace_memories: Optional list of workspace memories to inject into prompts
|
|
71
|
+
prompt_provider: Optional host-configured prompt provider
|
|
72
|
+
prompt_extensions: Host-provided prompt sections for app-specific context
|
|
73
|
+
tool_extensions: Host-provided tool providers for app-specific tools
|
|
74
|
+
usage_recorder: Optional callback for recording normalized LLM usage
|
|
75
|
+
sub_agent_recorder: Optional callback for persisting sub-agent conversation state
|
|
76
|
+
"""
|
|
77
|
+
# Call parent constructor
|
|
78
|
+
super().__init__(
|
|
79
|
+
project_path,
|
|
80
|
+
workspace_id,
|
|
81
|
+
thread_id,
|
|
82
|
+
connection_manager,
|
|
83
|
+
config,
|
|
84
|
+
sub_agent,
|
|
85
|
+
filesystem=filesystem,
|
|
86
|
+
terminal_manager=terminal_manager,
|
|
87
|
+
browser_manager=browser_manager,
|
|
88
|
+
langfuse_client=langfuse_client,
|
|
89
|
+
user_id=user_id,
|
|
90
|
+
user_email=user_email,
|
|
91
|
+
project_template_slug=project_template_slug,
|
|
92
|
+
protected_files=protected_files,
|
|
93
|
+
agent_mode=agent_mode,
|
|
94
|
+
workspace_env_var_descriptions=workspace_env_var_descriptions,
|
|
95
|
+
workspace_memories=workspace_memories,
|
|
96
|
+
prompt_provider=prompt_provider,
|
|
97
|
+
prompt_extensions=prompt_extensions,
|
|
98
|
+
tool_extensions=tool_extensions,
|
|
99
|
+
usage_recorder=usage_recorder,
|
|
100
|
+
sub_agent_recorder=sub_agent_recorder,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Configure tool collection with custom coder agent tools
|
|
104
|
+
tool_exclusions = [
|
|
105
|
+
"read_memory",
|
|
106
|
+
"write_memory",
|
|
107
|
+
"execute_terminal_command",
|
|
108
|
+
"replace_lines",
|
|
109
|
+
"apply_patch",
|
|
110
|
+
"edit_file",
|
|
111
|
+
"get_tool_list",
|
|
112
|
+
"log_error",
|
|
113
|
+
"log_info",
|
|
114
|
+
"run_command", # Disabled: unreliable completion detection, use run_command_tracked instead
|
|
115
|
+
# Exclude task-specific dispatch tools since coder shouldn't call itself or other agents
|
|
116
|
+
"dispatch_coding_agent",
|
|
117
|
+
]
|
|
118
|
+
mode_value = self.agent_mode.value if isinstance(self.agent_mode, AgentMode) else self.agent_mode
|
|
119
|
+
if mode_value == AgentMode.CLI.value:
|
|
120
|
+
tool_exclusions.extend(["build_backend", "build_frontend"])
|
|
121
|
+
if sub_agent:
|
|
122
|
+
# A dispatched coder must not fan out into further sub-agents
|
|
123
|
+
tool_exclusions.append("dispatch_general_agent")
|
|
124
|
+
|
|
125
|
+
tool_config = ToolCollectionConfig(
|
|
126
|
+
custom_tool_groups=["coder_agent_tools"],
|
|
127
|
+
tool_exclusions=tool_exclusions,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
self.tool_collection = ToolCollection(
|
|
131
|
+
self.project_path,
|
|
132
|
+
self.workspace_id,
|
|
133
|
+
self.thread_id,
|
|
134
|
+
self.connection_manager,
|
|
135
|
+
self.config,
|
|
136
|
+
caller=self,
|
|
137
|
+
tool_config=tool_config,
|
|
138
|
+
filesystem=self.filesystem,
|
|
139
|
+
terminal_manager=self.terminal_manager,
|
|
140
|
+
browser_manager=self.browser_manager,
|
|
141
|
+
langfuse_client=self.langfuse_client,
|
|
142
|
+
tool_extensions=self.tool_extensions,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
self._initialize_system_prompt()
|
|
146
|
+
|
|
147
|
+
def _initialize_system_prompt(self):
|
|
148
|
+
"""Initialize system prompt using PromptProvider."""
|
|
149
|
+
# Generate prompt using the shared prompt provider
|
|
150
|
+
prompt_text = self.prompt_provider.get_system_prompt(
|
|
151
|
+
agent_type=AgentType.CODER,
|
|
152
|
+
mode=self.agent_mode,
|
|
153
|
+
template_slug=self.project_template_slug,
|
|
154
|
+
prompt_extensions=self.prompt_extensions,
|
|
155
|
+
context=self.build_prompt_context(),
|
|
156
|
+
)
|
|
157
|
+
self.system_prompt = Message(role="system", content=[TextBlock(text=prompt_text)])
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from kolega_code.events import AgentEvent
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LogMixin:
|
|
5
|
+
"""
|
|
6
|
+
A mixin class providing logging functionality to agents.
|
|
7
|
+
|
|
8
|
+
This mixin expects the implementing class to have a connection_manager attribute,
|
|
9
|
+
a workspace_id attribute, and a thread_id attribute.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
async def log_info(self, message: str, sender: str = "agent") -> None:
|
|
13
|
+
"""
|
|
14
|
+
Log an informational message to the logs panel.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
message: The information message to log
|
|
18
|
+
"""
|
|
19
|
+
log_event = AgentEvent(event_type="log_message", sender=sender, content={"text": message, "level": "info"})
|
|
20
|
+
|
|
21
|
+
await self.connection_manager.broadcast_event(log_event, self.workspace_id, self.thread_id)
|
|
22
|
+
|
|
23
|
+
async def log_error(self, message: str, sender: str = "agent") -> None:
|
|
24
|
+
"""
|
|
25
|
+
Log an error message to the logs panel.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
message: The error message to log
|
|
29
|
+
"""
|
|
30
|
+
log_event = AgentEvent(event_type="log_message", sender=sender, content={"text": message, "level": "error"})
|
|
31
|
+
await self.connection_manager.broadcast_event(log_event, self.workspace_id, self.thread_id)
|
|
32
|
+
|
|
33
|
+
async def log_warning(self, message: str, sender: str = "agent") -> None:
|
|
34
|
+
"""
|
|
35
|
+
Log a warning message to the logs panel.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
message: The warning message to log
|
|
39
|
+
"""
|
|
40
|
+
log_event = AgentEvent(event_type="log_message", sender=sender, content={"text": message, "level": "warning"})
|
|
41
|
+
await self.connection_manager.broadcast_event(log_event, self.workspace_id, self.thread_id)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""History compression: summarize a conversation when it outgrows the context window."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Awaitable, Callable, Optional
|
|
5
|
+
|
|
6
|
+
from .conversation import Conversation
|
|
7
|
+
from kolega_code.llm.models import Message, MessageHistory, TextBlock
|
|
8
|
+
from .prompts import (
|
|
9
|
+
COMPRESSION_SUMMARY_SYSTEM_PROMPT,
|
|
10
|
+
COMPRESSION_SUMMARY_USER_PROMPT_TEMPLATE,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
LogCallback = Callable[[str], Awaitable[None]]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class HistoryCompressor:
|
|
19
|
+
"""Summarizes a conversation non-destructively when it crosses the budget threshold."""
|
|
20
|
+
|
|
21
|
+
MIN_MESSAGES_TO_COMPRESS = 5
|
|
22
|
+
|
|
23
|
+
def __init__(self, threshold: float = 0.8) -> None:
|
|
24
|
+
# Fraction of the model context window above which compression kicks in
|
|
25
|
+
self.threshold = threshold
|
|
26
|
+
|
|
27
|
+
def over_budget(self, input_tokens: int, model_context_length: int) -> bool:
|
|
28
|
+
return input_tokens > model_context_length * self.threshold
|
|
29
|
+
|
|
30
|
+
async def summarize(
|
|
31
|
+
self,
|
|
32
|
+
conversation: Conversation,
|
|
33
|
+
*,
|
|
34
|
+
llm,
|
|
35
|
+
model: str,
|
|
36
|
+
max_completion_tokens: int,
|
|
37
|
+
temperature: float,
|
|
38
|
+
thinking,
|
|
39
|
+
on_info: Optional[LogCallback] = None,
|
|
40
|
+
on_error: Optional[LogCallback] = None,
|
|
41
|
+
) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Non-destructively summarize the conversation and mark a compression boundary.
|
|
44
|
+
|
|
45
|
+
Returns True if a summary was recorded.
|
|
46
|
+
"""
|
|
47
|
+
if not conversation.history or len(conversation.history) < self.MIN_MESSAGES_TO_COMPRESS:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
if on_info:
|
|
51
|
+
await on_info("Compressing message history...")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
conversation_markdown = conversation.history.get_markdown_conversation()
|
|
55
|
+
user_prompt_filled = COMPRESSION_SUMMARY_USER_PROMPT_TEMPLATE.replace("{HISTORY}", conversation_markdown)
|
|
56
|
+
|
|
57
|
+
messages = MessageHistory([Message(role="user", content=[TextBlock(text=user_prompt_filled)])])
|
|
58
|
+
system_message = Message(role="system", content=[TextBlock(text=COMPRESSION_SUMMARY_SYSTEM_PROMPT)])
|
|
59
|
+
|
|
60
|
+
response = await llm.generate(
|
|
61
|
+
messages=messages,
|
|
62
|
+
system=system_message,
|
|
63
|
+
temperature=temperature,
|
|
64
|
+
model=model,
|
|
65
|
+
max_completion_tokens=max_completion_tokens,
|
|
66
|
+
thinking=thinking,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
summary = response.get_text_content()
|
|
70
|
+
conversation.record_compression(Message(role="user", content=[TextBlock(text=summary)]))
|
|
71
|
+
|
|
72
|
+
if on_info:
|
|
73
|
+
await on_info("Message history compressed (non-destructive).")
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
if on_error:
|
|
78
|
+
await on_error(f"Failed to compress message history: {str(e)}")
|
|
79
|
+
else:
|
|
80
|
+
logger.error("Failed to compress message history: %s", e)
|
|
81
|
+
return False
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""AgentContext: everything an agent needs, grouped by concern.
|
|
2
|
+
|
|
3
|
+
Replaces the long flat constructor signature on BaseAgent. Hosts build one
|
|
4
|
+
AgentContext and hand it to any agent class; the legacy keyword signature
|
|
5
|
+
remains supported on BaseAgent and converts to an AgentContext internally.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from langfuse import Langfuse
|
|
13
|
+
|
|
14
|
+
from kolega_code.config import AgentConfig
|
|
15
|
+
from kolega_code.events import AgentConnectionManager
|
|
16
|
+
from kolega_code.llm.client import LLMClient
|
|
17
|
+
from kolega_code.llm.instrumented_client import InstrumentedLLMClient
|
|
18
|
+
from .prompt_provider import AgentMode, PromptExtension, PromptProvider
|
|
19
|
+
from kolega_code.services.base import BrowserManager, TerminalManager
|
|
20
|
+
from kolega_code.services.browser import PlaywrightBrowserManager
|
|
21
|
+
from kolega_code.services.file_system import FileSystem, LocalFileSystem
|
|
22
|
+
from kolega_code.services.terminal import LocalTerminalManager
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class WorkspaceInfo:
|
|
27
|
+
"""Identity and content of the workspace the agent operates in."""
|
|
28
|
+
|
|
29
|
+
project_path: Path
|
|
30
|
+
workspace_id: str
|
|
31
|
+
thread_id: str
|
|
32
|
+
project_template_slug: Optional[str] = None
|
|
33
|
+
protected_files: List[str] = field(default_factory=list)
|
|
34
|
+
env_var_descriptions: Dict[str, str] = field(default_factory=dict)
|
|
35
|
+
memories: List[str] = field(default_factory=list)
|
|
36
|
+
|
|
37
|
+
def __post_init__(self) -> None:
|
|
38
|
+
if isinstance(self.project_path, str):
|
|
39
|
+
self.project_path = Path(self.project_path)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class AgentServices:
|
|
44
|
+
"""The environment abstractions the agent works through."""
|
|
45
|
+
|
|
46
|
+
filesystem: FileSystem
|
|
47
|
+
terminal_manager: TerminalManager
|
|
48
|
+
browser_manager: BrowserManager
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def local(cls, workspace: WorkspaceInfo, connection_manager: AgentConnectionManager) -> "AgentServices":
|
|
52
|
+
"""Default local-machine services rooted at the workspace project path."""
|
|
53
|
+
return cls(
|
|
54
|
+
filesystem=LocalFileSystem(root_path=workspace.project_path),
|
|
55
|
+
terminal_manager=LocalTerminalManager(workspace.workspace_id, workspace.thread_id, connection_manager),
|
|
56
|
+
browser_manager=PlaywrightBrowserManager(),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class Telemetry:
|
|
62
|
+
"""Observability and usage-recording hooks provided by the host."""
|
|
63
|
+
|
|
64
|
+
langfuse_client: Optional[Langfuse] = None
|
|
65
|
+
user_id: Optional[str] = None
|
|
66
|
+
user_email: Optional[str] = None
|
|
67
|
+
usage_recorder: Optional[Any] = None
|
|
68
|
+
sub_agent_recorder: Optional[Any] = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class AgentContext:
|
|
73
|
+
"""Everything an agent needs to run, grouped by concern."""
|
|
74
|
+
|
|
75
|
+
workspace: WorkspaceInfo
|
|
76
|
+
config: AgentConfig
|
|
77
|
+
connection_manager: AgentConnectionManager
|
|
78
|
+
services: AgentServices
|
|
79
|
+
telemetry: Telemetry = field(default_factory=Telemetry)
|
|
80
|
+
agent_mode: Optional[AgentMode] = None
|
|
81
|
+
prompt_provider: Optional[PromptProvider] = None
|
|
82
|
+
prompt_extensions: List[PromptExtension] = field(default_factory=list)
|
|
83
|
+
tool_extensions: List[Any] = field(default_factory=list)
|
|
84
|
+
|
|
85
|
+
def create_llm_client(self, agent_name: str) -> LLMClient:
|
|
86
|
+
"""Create the LLM client, instrumented when a Langfuse client is available."""
|
|
87
|
+
model_config = self.config.long_context_config
|
|
88
|
+
|
|
89
|
+
if self.telemetry.langfuse_client:
|
|
90
|
+
return InstrumentedLLMClient(
|
|
91
|
+
provider=model_config.provider,
|
|
92
|
+
api_key=self.config.get_api_key(model_config.provider),
|
|
93
|
+
max_retries=model_config.rate_limits.max_retries,
|
|
94
|
+
requests_per_minute=model_config.rate_limits.requests_per_minute,
|
|
95
|
+
tokens_per_minute=model_config.rate_limits.tokens_per_minute,
|
|
96
|
+
langfuse_client=self.telemetry.langfuse_client,
|
|
97
|
+
workspace_id=self.workspace.workspace_id,
|
|
98
|
+
thread_id=self.workspace.thread_id,
|
|
99
|
+
agent_type=agent_name,
|
|
100
|
+
environment=self.config.environment,
|
|
101
|
+
user_id=self.telemetry.user_id,
|
|
102
|
+
user_email=self.telemetry.user_email,
|
|
103
|
+
usage_recorder=self.telemetry.usage_recorder,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return LLMClient(
|
|
107
|
+
provider=model_config.provider,
|
|
108
|
+
api_key=self.config.get_api_key(model_config.provider),
|
|
109
|
+
max_retries=model_config.rate_limits.max_retries,
|
|
110
|
+
requests_per_minute=model_config.rate_limits.requests_per_minute,
|
|
111
|
+
tokens_per_minute=model_config.rate_limits.tokens_per_minute,
|
|
112
|
+
)
|