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.
Files changed (171) hide show
  1. kolega_code/__init__.py +151 -0
  2. kolega_code/agent/__init__.py +42 -0
  3. kolega_code/agent/baseagent.py +998 -0
  4. kolega_code/agent/browseragent.py +123 -0
  5. kolega_code/agent/coder.py +157 -0
  6. kolega_code/agent/common.py +41 -0
  7. kolega_code/agent/compression.py +81 -0
  8. kolega_code/agent/context.py +112 -0
  9. kolega_code/agent/conversation.py +408 -0
  10. kolega_code/agent/generalagent.py +146 -0
  11. kolega_code/agent/investigationagent.py +123 -0
  12. kolega_code/agent/planningagent.py +187 -0
  13. kolega_code/agent/prompt_provider.py +196 -0
  14. kolega_code/agent/prompt_templates/agents/browser.j2 +102 -0
  15. kolega_code/agent/prompt_templates/agents/coder_cli_mode.j2 +127 -0
  16. kolega_code/agent/prompt_templates/agents/general.j2 +68 -0
  17. kolega_code/agent/prompt_templates/agents/investigation.j2 +72 -0
  18. kolega_code/agent/prompt_templates/common/frontend_guidance.md +36 -0
  19. kolega_code/agent/prompt_templates/common/kolega_md_instructions.md +14 -0
  20. kolega_code/agent/prompt_templates/environment_variables/workspace_env_vars.md +11 -0
  21. kolega_code/agent/prompt_templates/template_guidance/expo-template.md +379 -0
  22. kolega_code/agent/prompt_templates/template_guidance/html-website-template.md +3 -0
  23. kolega_code/agent/prompt_templates/template_guidance/mern-stack-template.md +3 -0
  24. kolega_code/agent/prompt_templates/template_guidance/react-vite-shadcdn-template.md +182 -0
  25. kolega_code/agent/prompts.py +192 -0
  26. kolega_code/agent/tests/__init__.py +0 -0
  27. kolega_code/agent/tests/llm/__init__.py +0 -0
  28. kolega_code/agent/tests/llm/test_anthropic_token_counting.py +633 -0
  29. kolega_code/agent/tests/llm/test_billing_openai_cache.py +74 -0
  30. kolega_code/agent/tests/llm/test_client.py +773 -0
  31. kolega_code/agent/tests/llm/test_dashscope_mapping.py +32 -0
  32. kolega_code/agent/tests/llm/test_error_boundary.py +322 -0
  33. kolega_code/agent/tests/llm/test_exceptions.py +249 -0
  34. kolega_code/agent/tests/llm/test_instrumented_client.py +536 -0
  35. kolega_code/agent/tests/llm/test_instrumented_client_integration.py +547 -0
  36. kolega_code/agent/tests/llm/test_langfuse_normalization.py +39 -0
  37. kolega_code/agent/tests/llm/test_model_specs.py +17 -0
  38. kolega_code/agent/tests/llm/test_openai_cached_tokens.py +58 -0
  39. kolega_code/agent/tests/llm/test_openai_cached_tokens_stream.py +74 -0
  40. kolega_code/agent/tests/llm/test_openai_message_conversion.py +30 -0
  41. kolega_code/agent/tests/llm/test_openai_token_counting.py +687 -0
  42. kolega_code/agent/tests/llm/test_tool_execution_ids.py +193 -0
  43. kolega_code/agent/tests/services/__init__.py +1 -0
  44. kolega_code/agent/tests/services/test_browser.py +447 -0
  45. kolega_code/agent/tests/services/test_browser_parity.py +353 -0
  46. kolega_code/agent/tests/services/test_file_system.py +699 -0
  47. kolega_code/agent/tests/services/test_sandbox_terminal_input.py +98 -0
  48. kolega_code/agent/tests/services/test_terminal.py +154 -0
  49. kolega_code/agent/tests/services/test_terminal_command_tracking.py +385 -0
  50. kolega_code/agent/tests/services/test_terminal_state_serializer.py +262 -0
  51. kolega_code/agent/tests/test_agent_tools_inventory.py +267 -0
  52. kolega_code/agent/tests/test_base_agent.py +1942 -0
  53. kolega_code/agent/tests/test_coder_attachments.py +330 -0
  54. kolega_code/agent/tests/test_coder_prompt_extensions.py +61 -0
  55. kolega_code/agent/tests/test_commands.py +179 -0
  56. kolega_code/agent/tests/test_duplicate_tool_results.py +556 -0
  57. kolega_code/agent/tests/test_empty_message_handling.py +48 -0
  58. kolega_code/agent/tests/test_general_agent.py +242 -0
  59. kolega_code/agent/tests/test_html.py +320 -0
  60. kolega_code/agent/tests/test_parallel_tool_calls.py +291 -0
  61. kolega_code/agent/tests/test_planning_agent.py +227 -0
  62. kolega_code/agent/tests/test_prompt_provider.py +271 -0
  63. kolega_code/agent/tests/test_tool_registry.py +102 -0
  64. kolega_code/agent/tests/test_tools.py +549 -0
  65. kolega_code/agent/tests/tool_backend/__init__.py +0 -0
  66. kolega_code/agent/tests/tool_backend/test_agent_tool.py +356 -0
  67. kolega_code/agent/tests/tool_backend/test_base_tool.py +147 -0
  68. kolega_code/agent/tests/tool_backend/test_browser_tool.py +335 -0
  69. kolega_code/agent/tests/tool_backend/test_build_tool.py +93 -0
  70. kolega_code/agent/tests/tool_backend/test_create_file_tool.py +115 -0
  71. kolega_code/agent/tests/tool_backend/test_glob_tool.py +196 -0
  72. kolega_code/agent/tests/tool_backend/test_glob_tool_sandbox_parity.py +230 -0
  73. kolega_code/agent/tests/tool_backend/test_list_directory_tool.py +292 -0
  74. kolega_code/agent/tests/tool_backend/test_read_file_tool.py +173 -0
  75. kolega_code/agent/tests/tool_backend/test_replace_entire_file_tool.py +115 -0
  76. kolega_code/agent/tests/tool_backend/test_replace_lines_tool.py +141 -0
  77. kolega_code/agent/tests/tool_backend/test_search_and_replace_tool.py +174 -0
  78. kolega_code/agent/tests/tool_backend/test_search_codebase_tool.py +228 -0
  79. kolega_code/agent/tests/tool_backend/test_terminal_tool.py +482 -0
  80. kolega_code/agent/tests/tool_backend/test_think_hard_integration.py +189 -0
  81. kolega_code/agent/tests/tool_backend/test_think_hard_streaming.py +445 -0
  82. kolega_code/agent/tests/tool_backend/test_web_fetch_tool.py +194 -0
  83. kolega_code/agent/tool_backend/agent_tool.py +414 -0
  84. kolega_code/agent/tool_backend/apply_edit_tool.py +98 -0
  85. kolega_code/agent/tool_backend/apply_patch_tool.py +514 -0
  86. kolega_code/agent/tool_backend/base_tool.py +217 -0
  87. kolega_code/agent/tool_backend/browser_tool.py +271 -0
  88. kolega_code/agent/tool_backend/build_tool.py +93 -0
  89. kolega_code/agent/tool_backend/create_file_tool.py +52 -0
  90. kolega_code/agent/tool_backend/glob_tool.py +323 -0
  91. kolega_code/agent/tool_backend/list_directory_tool.py +300 -0
  92. kolega_code/agent/tool_backend/memory_tool.py +79 -0
  93. kolega_code/agent/tool_backend/read_file_tool.py +119 -0
  94. kolega_code/agent/tool_backend/replace_entire_file_tool.py +40 -0
  95. kolega_code/agent/tool_backend/replace_lines_tool.py +97 -0
  96. kolega_code/agent/tool_backend/search_and_replace_tool.py +146 -0
  97. kolega_code/agent/tool_backend/search_codebase_tool.py +377 -0
  98. kolega_code/agent/tool_backend/streaming_tool.py +47 -0
  99. kolega_code/agent/tool_backend/terminal_tool.py +643 -0
  100. kolega_code/agent/tool_backend/think_hard_tool.py +211 -0
  101. kolega_code/agent/tool_backend/web_fetch_tool.py +205 -0
  102. kolega_code/agent/tools.py +1704 -0
  103. kolega_code/agent/utils/commands.py +94 -0
  104. kolega_code/cli/__init__.py +1 -0
  105. kolega_code/cli/app.py +2756 -0
  106. kolega_code/cli/config.py +280 -0
  107. kolega_code/cli/connection.py +49 -0
  108. kolega_code/cli/file_index.py +147 -0
  109. kolega_code/cli/main.py +564 -0
  110. kolega_code/cli/mentions.py +155 -0
  111. kolega_code/cli/messages.py +89 -0
  112. kolega_code/cli/provider_registry.py +96 -0
  113. kolega_code/cli/session_store.py +207 -0
  114. kolega_code/cli/settings.py +87 -0
  115. kolega_code/cli/skills.py +409 -0
  116. kolega_code/cli/slash_commands.py +108 -0
  117. kolega_code/cli/tests/__init__.py +1 -0
  118. kolega_code/cli/tests/test_app.py +4251 -0
  119. kolega_code/cli/tests/test_cli_config.py +171 -0
  120. kolega_code/cli/tests/test_connection.py +26 -0
  121. kolega_code/cli/tests/test_file_index.py +103 -0
  122. kolega_code/cli/tests/test_main.py +455 -0
  123. kolega_code/cli/tests/test_mentions.py +108 -0
  124. kolega_code/cli/tests/test_session_store.py +67 -0
  125. kolega_code/cli/tests/test_settings.py +62 -0
  126. kolega_code/cli/tests/test_skills.py +157 -0
  127. kolega_code/cli/tests/test_slash_commands.py +88 -0
  128. kolega_code/cli/theme.py +180 -0
  129. kolega_code/config.py +154 -0
  130. kolega_code/events.py +202 -0
  131. kolega_code/llm/client.py +300 -0
  132. kolega_code/llm/exceptions.py +285 -0
  133. kolega_code/llm/instrumented_client.py +520 -0
  134. kolega_code/llm/models.py +1368 -0
  135. kolega_code/llm/providers/__init__.py +0 -0
  136. kolega_code/llm/providers/anthropic.py +387 -0
  137. kolega_code/llm/providers/base.py +71 -0
  138. kolega_code/llm/providers/google.py +157 -0
  139. kolega_code/llm/providers/models.py +37 -0
  140. kolega_code/llm/providers/openai.py +363 -0
  141. kolega_code/llm/ratelimit.py +40 -0
  142. kolega_code/llm/specs.py +67 -0
  143. kolega_code/llm/tool_execution_ids.py +18 -0
  144. kolega_code/models/__init__.py +9 -0
  145. kolega_code/models/sandbox_terminal_state.py +47 -0
  146. kolega_code/runtime.py +50 -0
  147. kolega_code/sandbox/README.md +200 -0
  148. kolega_code/sandbox/__init__.py +21 -0
  149. kolega_code/sandbox/async_filesystem.py +475 -0
  150. kolega_code/sandbox/base.py +297 -0
  151. kolega_code/sandbox/browser.py +25 -0
  152. kolega_code/sandbox/event_loop.py +43 -0
  153. kolega_code/sandbox/filesystem.py +341 -0
  154. kolega_code/sandbox/local.py +118 -0
  155. kolega_code/sandbox/serializer.py +175 -0
  156. kolega_code/sandbox/terminal.py +868 -0
  157. kolega_code/sandbox/utils.py +216 -0
  158. kolega_code/services/base.py +255 -0
  159. kolega_code/services/browser.py +444 -0
  160. kolega_code/services/file_system.py +749 -0
  161. kolega_code/services/html.py +221 -0
  162. kolega_code/services/terminal.py +903 -0
  163. kolega_code/tools/__init__.py +22 -0
  164. kolega_code/tools/core.py +33 -0
  165. kolega_code/tools/definitions.py +81 -0
  166. kolega_code/tools/registry.py +73 -0
  167. kolega_code-0.1.0.dist-info/METADATA +157 -0
  168. kolega_code-0.1.0.dist-info/RECORD +171 -0
  169. kolega_code-0.1.0.dist-info/WHEEL +4 -0
  170. kolega_code-0.1.0.dist-info/entry_points.txt +2 -0
  171. kolega_code-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,297 @@
1
+ """Base interfaces for sandbox management."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, Dict, List, Optional, Protocol, runtime_checkable
6
+
7
+ from ..services.base import TerminalManager, BrowserManager
8
+ from ..services.file_system import FileSystem
9
+
10
+
11
+ @runtime_checkable
12
+ class SandboxHandle(Protocol):
13
+ """
14
+ The duck-typed interface the generic sandbox services expect from a
15
+ provider's sandbox object (e.g. E2B's AsyncSandbox).
16
+
17
+ SandboxFileSystem, SandboxTerminalManager, and friends only touch these
18
+ members; a provider object satisfying this protocol can back them all.
19
+ """
20
+
21
+ @property
22
+ def commands(self) -> Any:
23
+ """Command runner exposing run(...), and send_stdin(pid, data) where supported."""
24
+ ...
25
+
26
+ @property
27
+ def files(self) -> Any:
28
+ """File API exposing read/write/list/exists operations."""
29
+ ...
30
+
31
+ def get_host(self, port: int) -> str:
32
+ """Return the externally reachable host for a port inside the sandbox."""
33
+ ...
34
+
35
+
36
+ @dataclass
37
+ class SandboxConfig:
38
+ """Configuration for sandbox creation."""
39
+
40
+ git_url: str
41
+ branch: str = "main"
42
+ commit_hash: Optional[str] = None
43
+ manifest_path: str = ".kolega-manifest.yaml"
44
+ resources: Dict[str, Any] = field(default_factory=dict)
45
+ environment_vars: Dict[str, str] = field(default_factory=dict)
46
+ auth_method: str = "group_token" # group_token, pat, or ssh
47
+ network_access_mode: str = "deny_all" # deny_all | allow_all | custom
48
+ network_allowed_hosts: List[str] = field(default_factory=list)
49
+ # Pool mode flags
50
+ skip_git: bool = False
51
+ skip_s3_mount: bool = False
52
+ skip_project_setup: bool = False
53
+ skip_integration_env_sync: bool = False # Skip user-specific environment variable sync
54
+
55
+
56
+ @dataclass
57
+ class ProjectManifest:
58
+ """Project configuration manifest."""
59
+
60
+ name: str
61
+ runtime: str # e.g., "node:18", "python:3.11"
62
+ install_commands: Optional[List[str]] = None
63
+ post_setup_commands: Optional[List[str]] = None
64
+ dev_server_command: Optional[str] = None
65
+ dev_server_commands: Optional[List[str]] = None
66
+ test_commands: Optional[List[str]] = None
67
+ build_command: Optional[str] = None
68
+ backend_build_command: Optional[str] = None
69
+ frontend_build_command: Optional[str] = None
70
+ environment_setup: Optional[List[str]] = None
71
+
72
+
73
+ class SandboxManager(ABC):
74
+ """Abstract base class for sandbox managers."""
75
+
76
+ @abstractmethod
77
+ async def create_sandbox(
78
+ self,
79
+ workspace_id: str,
80
+ thread_id: str,
81
+ config: Optional[SandboxConfig] = None,
82
+ workspace: Optional[Any] = None,
83
+ connection_manager: Optional[Any] = None,
84
+ ) -> str:
85
+ """
86
+ Create a new sandbox.
87
+
88
+ Args:
89
+ workspace_id: ID of the workspace
90
+ thread_id: Thread ID for terminal output streaming
91
+ config: Sandbox configuration
92
+ workspace: Optional workspace object for additional context
93
+ connection_manager: Optional connection manager for dispatching events
94
+
95
+ Returns:
96
+ Sandbox ID
97
+ """
98
+ pass
99
+
100
+ @abstractmethod
101
+ async def destroy_sandbox(self, sandbox_id: str) -> None:
102
+ """
103
+ Destroy a sandbox and clean up resources.
104
+
105
+ Args:
106
+ sandbox_id: ID of the sandbox to destroy
107
+
108
+ Raises:
109
+ ValueError: If sandbox doesn't exist
110
+ """
111
+
112
+ @abstractmethod
113
+ async def get_sandbox_status(self, sandbox_id: str) -> Dict[str, Any]:
114
+ """
115
+ Get current status of a sandbox.
116
+
117
+ Args:
118
+ sandbox_id: ID of the sandbox
119
+
120
+ Returns:
121
+ Dictionary with status information
122
+
123
+ Raises:
124
+ ValueError: If sandbox doesn't exist
125
+ """
126
+
127
+ @abstractmethod
128
+ async def commit_changes(self, sandbox_id: str, message: str, files: Optional[List[str]] = None) -> str:
129
+ """
130
+ Commit changes in sandbox and return commit hash.
131
+
132
+ Args:
133
+ sandbox_id: ID of the sandbox
134
+ message: Commit message
135
+ files: Optional list of files to commit (None = all changes)
136
+
137
+ Returns:
138
+ Git commit hash
139
+
140
+ Raises:
141
+ ValueError: If sandbox doesn't exist
142
+ Exception: If commit fails
143
+ """
144
+
145
+ @abstractmethod
146
+ async def push_changes(self, sandbox_id: str) -> bool:
147
+ """
148
+ Push committed changes to remote repository.
149
+
150
+ Args:
151
+ sandbox_id: ID of the sandbox
152
+
153
+ Returns:
154
+ True if push succeeded, False otherwise
155
+
156
+ Raises:
157
+ ValueError: If sandbox doesn't exist
158
+ """
159
+
160
+ @abstractmethod
161
+ def get_filesystem(self, sandbox_id: str) -> FileSystem:
162
+ """
163
+ Get filesystem interface for sandbox.
164
+
165
+ Args:
166
+ sandbox_id: ID of the sandbox
167
+
168
+ Returns:
169
+ FileSystem implementation for the sandbox
170
+
171
+ Raises:
172
+ ValueError: If sandbox doesn't exist
173
+ """
174
+
175
+ @abstractmethod
176
+ def get_terminal_manager(self, sandbox_id: str) -> TerminalManager:
177
+ """
178
+ Get terminal manager for sandbox.
179
+
180
+ Args:
181
+ sandbox_id: ID of the sandbox
182
+
183
+ Returns:
184
+ TerminalManager implementation for the sandbox
185
+
186
+ Raises:
187
+ ValueError: If sandbox doesn't exist
188
+ """
189
+
190
+ @abstractmethod
191
+ def get_browser_manager(self, sandbox_id: str) -> BrowserManager:
192
+ """
193
+ Get browser manager for sandbox.
194
+
195
+ Args:
196
+ sandbox_id: ID of the sandbox
197
+
198
+ Returns:
199
+ BrowserManager implementation for the sandbox
200
+
201
+ Raises:
202
+ ValueError: If sandbox doesn't exist
203
+ """
204
+
205
+ @abstractmethod
206
+ async def get_host(self, sandbox_id: str, port: int) -> str:
207
+ """
208
+ Get the hostname for accessing services on the given port.
209
+
210
+ Args:
211
+ sandbox_id: ID of the sandbox
212
+ port: The port number to access
213
+
214
+ Returns:
215
+ The hostname (e.g., 'localhost' or 'xxxx.e2b.dev')
216
+ """
217
+ pass
218
+
219
+ @abstractmethod
220
+ async def pause_sandbox(self, sandbox_id: str) -> str:
221
+ """
222
+ Pause sandbox and return persistent sandbox ID for resuming later.
223
+
224
+ Args:
225
+ sandbox_id: ID of the sandbox to pause
226
+
227
+ Returns:
228
+ Persistent sandbox ID that can be used to resume
229
+
230
+ Raises:
231
+ ValueError: If sandbox doesn't exist
232
+ """
233
+ pass
234
+
235
+ @abstractmethod
236
+ async def resume_sandbox(self, persistent_sandbox_id: str, workspace_id: str, thread_id: str) -> str:
237
+ """
238
+ Resume a paused sandbox from persistent ID.
239
+
240
+ Args:
241
+ persistent_sandbox_id: The persistent ID returned from pause_sandbox
242
+ workspace_id: ID of the workspace
243
+ thread_id: Thread ID for terminal output streaming
244
+
245
+ Returns:
246
+ New sandbox ID for the resumed sandbox
247
+
248
+ Raises:
249
+ Exception: If sandbox cannot be resumed
250
+ """
251
+ pass
252
+
253
+ @abstractmethod
254
+ def has_sandbox(self, sandbox_id: str) -> bool:
255
+ """
256
+ Check if a sandbox is currently active in memory.
257
+
258
+ Args:
259
+ sandbox_id: ID of the sandbox to check
260
+
261
+ Returns:
262
+ True if sandbox is active, False otherwise
263
+ """
264
+ pass
265
+
266
+ @abstractmethod
267
+ async def adopt_sandbox(self, sandbox_id: str, workspace_id: str, thread_id: str) -> str:
268
+ """
269
+ Adopt an existing sandbox into this manager instance.
270
+
271
+ Args:
272
+ sandbox_id: The sandbox ID to adopt
273
+ workspace_id: ID of the workspace
274
+ thread_id: Thread ID for terminal output streaming
275
+
276
+ Returns:
277
+ The sandbox ID (same as input)
278
+
279
+ Raises:
280
+ Exception: If sandbox cannot be connected to
281
+ """
282
+ pass
283
+
284
+ @abstractmethod
285
+ async def sync_sandbox_env_vars(self, sandbox_id: str, workspace_id: str, sandbox: Optional[Any] = None, skip_integration_env_sync: bool = False) -> None:
286
+ """
287
+ Sync current environment variables to an existing sandbox.
288
+
289
+ Args:
290
+ sandbox_id: ID of the sandbox
291
+ workspace_id: ID of the workspace (used to fetch env vars from database)
292
+ sandbox: Optional sandbox instance (avoids reconnection if provided)
293
+
294
+ Raises:
295
+ Exception: If sandbox cannot be connected to or env vars cannot be synced
296
+ """
297
+ pass
@@ -0,0 +1,25 @@
1
+ """Browser manager implementation for sandbox environments."""
2
+
3
+ from typing import Any
4
+
5
+ from ..services.browser import PlaywrightBrowserManager
6
+
7
+
8
+ class SandboxBrowserManager(PlaywrightBrowserManager):
9
+ """
10
+ Browser manager for sandbox environments using Browserless.
11
+
12
+ This class extends PlaywrightBrowserManager with Browserless support enabled,
13
+ providing remote browser capabilities for sandbox environments.
14
+ """
15
+
16
+ def __init__(self, sandbox: Any = None):
17
+ """
18
+ Initialize sandbox browser manager with Browserless support.
19
+
20
+ Args:
21
+ sandbox: The sandbox instance (optional, kept for compatibility)
22
+ """
23
+ # Initialize parent class with Browserless backend
24
+ super().__init__(browser_backend="browserless")
25
+ self.sandbox = sandbox
@@ -0,0 +1,43 @@
1
+ """Utility functions for event loop cleanup in task workers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import logging
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def cleanup_event_loop(loop: asyncio.AbstractEventLoop) -> None:
12
+ """Clean up a manually managed event loop."""
13
+ try:
14
+ tasks_to_cancel = asyncio.all_tasks(loop)
15
+ if tasks_to_cancel:
16
+ for task in tasks_to_cancel:
17
+ task.cancel()
18
+ try:
19
+ loop.run_until_complete(asyncio.gather(*tasks_to_cancel, return_exceptions=True))
20
+ except RuntimeError as error:
21
+ logger.warning("Could not wait for task cancellation: %s", error)
22
+
23
+ try:
24
+ loop.run_until_complete(loop.shutdown_asyncgens())
25
+ except RuntimeError as error:
26
+ logger.warning("Could not shut down async generators: %s", error)
27
+
28
+ try:
29
+ loop.run_until_complete(asyncio.sleep(0))
30
+ except RuntimeError as error:
31
+ logger.warning("Could not process scheduled callbacks: %s", error)
32
+
33
+ pending = asyncio.all_tasks(loop)
34
+ if pending:
35
+ try:
36
+ loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
37
+ except RuntimeError as error:
38
+ logger.warning("Could not wait for remaining tasks: %s", error)
39
+
40
+ finally:
41
+ if not loop.is_closed():
42
+ loop.close()
43
+