ata-coder 2.4.3__tar.gz → 2.4.4__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.
- {ata_coder-2.4.3/ata_coder.egg-info → ata_coder-2.4.4}/PKG-INFO +15 -1
- {ata_coder-2.4.3 → ata_coder-2.4.4}/README.md +14 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent_compact.py +11 -4
- {ata_coder-2.4.3 → ata_coder-2.4.4/ata_coder.egg-info}/PKG-INFO +15 -1
- {ata_coder-2.4.3 → ata_coder-2.4.4}/config.py +7 -2
- {ata_coder-2.4.3 → ata_coder-2.4.4}/git_workflow.py +9 -7
- {ata_coder-2.4.3 → ata_coder-2.4.4}/main.py +1 -1
- {ata_coder-2.4.3 → ata_coder-2.4.4}/mcp_client.py +25 -3
- {ata_coder-2.4.3 → ata_coder-2.4.4}/pyproject.toml +1 -1
- {ata_coder-2.4.3 → ata_coder-2.4.4}/safety_guard.py +47 -32
- {ata_coder-2.4.3 → ata_coder-2.4.4}/server.py +128 -35
- {ata_coder-2.4.3 → ata_coder-2.4.4}/server_session.py +233 -178
- {ata_coder-2.4.3 → ata_coder-2.4.4}/server_shell.py +8 -2
- {ata_coder-2.4.3 → ata_coder-2.4.4}/settings.py +139 -2
- {ata_coder-2.4.3 → ata_coder-2.4.4}/setup_wizard.py +1 -1
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tools/executor.py +6 -3
- {ata_coder-2.4.3 → ata_coder-2.4.4}/utils.py +7 -4
- {ata_coder-2.4.3 → ata_coder-2.4.4}/LICENSE +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/MANIFEST.in +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/__init__.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent_controller.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent_extension.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent_routing.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent_subsystems.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent_tools.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/agent_undo.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/anthropic_client.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/ata_coder.egg-info/SOURCES.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/ata_coder.egg-info/dependency_links.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/ata_coder.egg-info/entry_points.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/ata_coder.egg-info/requires.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/ata_coder.egg-info/top_level.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/change_tracker.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/clawd_integration.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/commands/__init__.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/commands/_core.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/commands/_safety.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/commands/_settings.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/commands/_workflow.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/core/__init__.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/core/events.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/core/queue.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/core/state.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/event_queue.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/extension.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/extensions/__init__.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/extensions/hello_skill.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/fool_proof.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/gui.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/llm_client.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/memory.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/model_registry.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/model_router.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/permissions.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/privilege.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/project.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompt_template.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/auto-mode.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/coding-rules.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/execution-guardrails.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/memory-system.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/output-style.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/safety.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/slash-commands.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/sub-agents.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/system-reminders.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/system.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/prompts/tool-policy.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/repl_theme.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/repl_tracker.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/repl_ui.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/self_correct.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/session.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/setup.cfg +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skill_extension.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/architect/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/code-reviewer/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/codecraft/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/debugger/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/doc-writer/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/general-coder/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/math-calculator/README.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/math-calculator/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/math-calculator/handler.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/math-calculator/prompts/system.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/math-calculator/requirements.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/math-calculator/resources/constants.json +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/math-calculator/tests/test_handler.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/security-auditor/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/test-writer/SKILL.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/README.md +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/handler.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/manifest.json +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/prompts/system_prompt.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/prompts/user_prompt_template.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/requirements.txt +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/resources/city_list.json +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/resources/error_messages.json +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/tests/test_handler.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills/weather-skill/weather_utils.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/skills.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/sub_agent.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/sub_agent_manager.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/system_prompt_builder.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/task_planner.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/terminal.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_agent.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_change_tracker.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_config.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_event_queue.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_extension.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_fibonacci.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_fool_proof.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_llm_client.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_memory.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_model_registry.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_permissions.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_privilege.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_prompt_template.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_safety_guard.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_server.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_skill_handlers.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_sub_agent.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tests/test_tools.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/token_counter.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tools/__init__.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tools/definitions.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tools/result.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tools/strategy.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tools/subagent.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/tools/web.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/types.py +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/web/css/style.css +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/web/index.html +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/web/js/app.js +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/web/package-lock.json +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/web/package.json +0 -0
- {ata_coder-2.4.3 → ata_coder-2.4.4}/web/tsconfig.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ata-coder
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
4
4
|
Summary: ATA Coder — AI-powered coding assistant
|
|
5
5
|
Author: ATA Coder Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -51,6 +51,10 @@ Dynamic: license-file
|
|
|
51
51
|
|
|
52
52
|
ATA Coder is a CLI AI coding assistant compatible with OpenAI and Anthropic APIs. It runs on a **single-threaded asyncio event loop** — no threads, no race conditions, low memory. Features deterministic AST-based code editing, sub-agent pool, MCP support, and a built-in HTTP API server.
|
|
53
53
|
|
|
54
|
+
```bash
|
|
55
|
+
pip install ata-coder
|
|
56
|
+
```
|
|
57
|
+
|
|
54
58
|
### Architecture (v2.3.3)
|
|
55
59
|
|
|
56
60
|
```
|
|
@@ -95,7 +99,12 @@ asyncio Event Loop (single-threaded)
|
|
|
95
99
|
### Quick Start
|
|
96
100
|
|
|
97
101
|
```bash
|
|
102
|
+
# Install from PyPI (recommended)
|
|
103
|
+
pip install ata-coder
|
|
104
|
+
|
|
105
|
+
# Or install from source (development)
|
|
98
106
|
pip install -e .
|
|
107
|
+
|
|
99
108
|
ata # Interactive REPL
|
|
100
109
|
ata run "Add type hints" # Single task
|
|
101
110
|
ata server --port 8080 # HTTP API server
|
|
@@ -558,7 +567,12 @@ asyncio 事件循环(单线程)
|
|
|
558
567
|
### 快速开始
|
|
559
568
|
|
|
560
569
|
```bash
|
|
570
|
+
# 从 PyPI 安装(推荐)
|
|
571
|
+
pip install ata-coder
|
|
572
|
+
|
|
573
|
+
# 或从源码安装(开发模式)
|
|
561
574
|
pip install -e .
|
|
575
|
+
|
|
562
576
|
ata # 交互模式
|
|
563
577
|
ata run "添加类型注解" # 单任务
|
|
564
578
|
ata server --port 8080 # API 服务
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
|
|
29
29
|
ATA Coder is a CLI AI coding assistant compatible with OpenAI and Anthropic APIs. It runs on a **single-threaded asyncio event loop** — no threads, no race conditions, low memory. Features deterministic AST-based code editing, sub-agent pool, MCP support, and a built-in HTTP API server.
|
|
30
30
|
|
|
31
|
+
```bash
|
|
32
|
+
pip install ata-coder
|
|
33
|
+
```
|
|
34
|
+
|
|
31
35
|
### Architecture (v2.3.3)
|
|
32
36
|
|
|
33
37
|
```
|
|
@@ -72,7 +76,12 @@ asyncio Event Loop (single-threaded)
|
|
|
72
76
|
### Quick Start
|
|
73
77
|
|
|
74
78
|
```bash
|
|
79
|
+
# Install from PyPI (recommended)
|
|
80
|
+
pip install ata-coder
|
|
81
|
+
|
|
82
|
+
# Or install from source (development)
|
|
75
83
|
pip install -e .
|
|
84
|
+
|
|
76
85
|
ata # Interactive REPL
|
|
77
86
|
ata run "Add type hints" # Single task
|
|
78
87
|
ata server --port 8080 # HTTP API server
|
|
@@ -535,7 +544,12 @@ asyncio 事件循环(单线程)
|
|
|
535
544
|
### 快速开始
|
|
536
545
|
|
|
537
546
|
```bash
|
|
547
|
+
# 从 PyPI 安装(推荐)
|
|
548
|
+
pip install ata-coder
|
|
549
|
+
|
|
550
|
+
# 或从源码安装(开发模式)
|
|
538
551
|
pip install -e .
|
|
552
|
+
|
|
539
553
|
ata # 交互模式
|
|
540
554
|
ata run "添加类型注解" # 单任务
|
|
541
555
|
ata server --port 8080 # API 服务
|
|
@@ -4,6 +4,7 @@ import json
|
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
6
|
from .types import Message
|
|
7
|
+
from .token_counter import TokenCounter
|
|
7
8
|
from .clawd_integration import get_clawd
|
|
8
9
|
from .model_router import get_subagent_model
|
|
9
10
|
logger = logging.getLogger(__name__)
|
|
@@ -35,7 +36,11 @@ class CompactionMixin:
|
|
|
35
36
|
recent_tokens = 0
|
|
36
37
|
for msg in reversed(all_but_system):
|
|
37
38
|
msg_tokens = self._estimate_message_tokens(msg)
|
|
38
|
-
if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET
|
|
39
|
+
if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET:
|
|
40
|
+
if not recent:
|
|
41
|
+
# Single huge message — include it anyway but stop after
|
|
42
|
+
recent.insert(0, msg)
|
|
43
|
+
recent_tokens += msg_tokens
|
|
39
44
|
break
|
|
40
45
|
recent.insert(0, msg)
|
|
41
46
|
recent_tokens += msg_tokens
|
|
@@ -79,8 +84,11 @@ class CompactionMixin:
|
|
|
79
84
|
recent_tokens = 0
|
|
80
85
|
for msg in reversed(all_but_system):
|
|
81
86
|
msg_tokens = self._estimate_message_tokens(msg)
|
|
82
|
-
if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET
|
|
83
|
-
|
|
87
|
+
if recent_tokens + msg_tokens > self.RECENT_TOKEN_BUDGET:
|
|
88
|
+
if not recent:
|
|
89
|
+
# Single huge message — include it anyway but stop after
|
|
90
|
+
recent.insert(0, msg)
|
|
91
|
+
recent_tokens += msg_tokens
|
|
84
92
|
break
|
|
85
93
|
recent.insert(0, msg)
|
|
86
94
|
recent_tokens += msg_tokens
|
|
@@ -120,7 +128,6 @@ class CompactionMixin:
|
|
|
120
128
|
|
|
121
129
|
def _estimate_message_tokens(self, msg: Message) -> int:
|
|
122
130
|
"""Rough token estimate for a single message (via TokenCounter)."""
|
|
123
|
-
from .token_counter import TokenCounter
|
|
124
131
|
model = getattr(self.llm.config, 'model', '')
|
|
125
132
|
return TokenCounter.for_model(model).count_tokens([msg])
|
|
126
133
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ata-coder
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
4
4
|
Summary: ATA Coder — AI-powered coding assistant
|
|
5
5
|
Author: ATA Coder Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -51,6 +51,10 @@ Dynamic: license-file
|
|
|
51
51
|
|
|
52
52
|
ATA Coder is a CLI AI coding assistant compatible with OpenAI and Anthropic APIs. It runs on a **single-threaded asyncio event loop** — no threads, no race conditions, low memory. Features deterministic AST-based code editing, sub-agent pool, MCP support, and a built-in HTTP API server.
|
|
53
53
|
|
|
54
|
+
```bash
|
|
55
|
+
pip install ata-coder
|
|
56
|
+
```
|
|
57
|
+
|
|
54
58
|
### Architecture (v2.3.3)
|
|
55
59
|
|
|
56
60
|
```
|
|
@@ -95,7 +99,12 @@ asyncio Event Loop (single-threaded)
|
|
|
95
99
|
### Quick Start
|
|
96
100
|
|
|
97
101
|
```bash
|
|
102
|
+
# Install from PyPI (recommended)
|
|
103
|
+
pip install ata-coder
|
|
104
|
+
|
|
105
|
+
# Or install from source (development)
|
|
98
106
|
pip install -e .
|
|
107
|
+
|
|
99
108
|
ata # Interactive REPL
|
|
100
109
|
ata run "Add type hints" # Single task
|
|
101
110
|
ata server --port 8080 # HTTP API server
|
|
@@ -558,7 +567,12 @@ asyncio 事件循环(单线程)
|
|
|
558
567
|
### 快速开始
|
|
559
568
|
|
|
560
569
|
```bash
|
|
570
|
+
# 从 PyPI 安装(推荐)
|
|
571
|
+
pip install ata-coder
|
|
572
|
+
|
|
573
|
+
# 或从源码安装(开发模式)
|
|
561
574
|
pip install -e .
|
|
575
|
+
|
|
562
576
|
ata # 交互模式
|
|
563
577
|
ata run "添加类型注解" # 单任务
|
|
564
578
|
ata server --port 8080 # API 服务
|
|
@@ -176,7 +176,7 @@ class AppConfig:
|
|
|
176
176
|
|
|
177
177
|
llm: LLMConfig = field(default_factory=LLMConfig)
|
|
178
178
|
agent: AgentConfig = field(default_factory=AgentConfig)
|
|
179
|
-
effort: str = field(default_factory=lambda: _from_settings("effort_level"
|
|
179
|
+
effort: str = field(default_factory=lambda: (_from_settings("effort_level") or "medium"))
|
|
180
180
|
|
|
181
181
|
@classmethod
|
|
182
182
|
def load(cls) -> "AppConfig":
|
|
@@ -246,7 +246,12 @@ def _safe_temperature() -> float:
|
|
|
246
246
|
|
|
247
247
|
|
|
248
248
|
def _settings_api_key() -> str:
|
|
249
|
-
|
|
249
|
+
"""Resolve API key with tiered fallback (OS keychain → env vars → settings.json)."""
|
|
250
|
+
try:
|
|
251
|
+
from .settings import resolve_api_key
|
|
252
|
+
return resolve_api_key()
|
|
253
|
+
except ImportError:
|
|
254
|
+
return _from_settings("api_key", "")
|
|
250
255
|
|
|
251
256
|
|
|
252
257
|
def _settings_base_url() -> str:
|
|
@@ -134,13 +134,15 @@ class GitWorkflow:
|
|
|
134
134
|
status.modified += 1
|
|
135
135
|
status.clean = (status.staged == 0 and status.modified == 0 and status.untracked == 0)
|
|
136
136
|
|
|
137
|
-
# Ahead/behind
|
|
138
|
-
|
|
139
|
-
if
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
# Ahead/behind — only if upstream is configured
|
|
138
|
+
has_upstream, _, _ = _run_git(["rev-parse", "--abbrev-ref", "@{u}"], self.cwd, timeout=10)
|
|
139
|
+
if has_upstream == 0:
|
|
140
|
+
_, ahead_str, _ = _run_git(["rev-list", "--count", "@{u}..HEAD"], self.cwd, timeout=10)
|
|
141
|
+
if ahead_str and ahead_str.isdigit():
|
|
142
|
+
status.ahead = int(ahead_str)
|
|
143
|
+
_, behind_str, _ = _run_git(["rev-list", "--count", "HEAD..@{u}"], self.cwd, timeout=10)
|
|
144
|
+
if behind_str and behind_str.isdigit():
|
|
145
|
+
status.behind = int(behind_str)
|
|
144
146
|
|
|
145
147
|
# Last commit
|
|
146
148
|
_, last, _ = _run_git(["log", "-1", "--format=%h %s"], self.cwd)
|
|
@@ -995,6 +995,25 @@ class MCPClient:
|
|
|
995
995
|
"""Register a callback for health check failures."""
|
|
996
996
|
self._on_health_fail = callback
|
|
997
997
|
|
|
998
|
+
async def reconnect_server(self, name: str) -> bool:
|
|
999
|
+
"""Attempt to reconnect a failed MCP server. Returns True on success."""
|
|
1000
|
+
conn = self._connections.get(name)
|
|
1001
|
+
if not conn:
|
|
1002
|
+
return False
|
|
1003
|
+
logger.info("[%s] Attempting reconnection...", name)
|
|
1004
|
+
try:
|
|
1005
|
+
await conn.stop()
|
|
1006
|
+
except Exception:
|
|
1007
|
+
pass
|
|
1008
|
+
try:
|
|
1009
|
+
await conn.start()
|
|
1010
|
+
self._register_server_tools(name, conn)
|
|
1011
|
+
logger.info("[%s] Reconnected successfully", name)
|
|
1012
|
+
return True
|
|
1013
|
+
except Exception as e:
|
|
1014
|
+
logger.warning("[%s] Reconnection failed: %s", name, e)
|
|
1015
|
+
return False
|
|
1016
|
+
|
|
998
1017
|
def start_health_monitor(self, interval: float = 60.0) -> None:
|
|
999
1018
|
"""Start periodic health checks (ping every N seconds)."""
|
|
1000
1019
|
if self._health_running:
|
|
@@ -1021,16 +1040,19 @@ class MCPClient:
|
|
|
1021
1040
|
break
|
|
1022
1041
|
for name, conn in list(self._connections.items()):
|
|
1023
1042
|
try:
|
|
1024
|
-
|
|
1025
|
-
|
|
1043
|
+
alive = await conn.ping(timeout=10)
|
|
1044
|
+
if not alive:
|
|
1045
|
+
logger.warning("[%s] Health check failed — attempting reconnect", name)
|
|
1026
1046
|
if self._on_health_fail:
|
|
1027
1047
|
self._on_health_fail(name)
|
|
1048
|
+
await self.reconnect_server(name)
|
|
1028
1049
|
except asyncio.CancelledError:
|
|
1029
1050
|
raise
|
|
1030
1051
|
except Exception as e:
|
|
1031
|
-
logger.warning("[%s] Health check error: %s", name, e)
|
|
1052
|
+
logger.warning("[%s] Health check error: %s — attempting reconnect", name, e)
|
|
1032
1053
|
if self._on_health_fail:
|
|
1033
1054
|
self._on_health_fail(name)
|
|
1055
|
+
await self.reconnect_server(name)
|
|
1034
1056
|
|
|
1035
1057
|
# ── Properties ──────────────────────────────────────────────────────────
|
|
1036
1058
|
|
|
@@ -98,63 +98,78 @@ PROTECTED_PATHS = [
|
|
|
98
98
|
|
|
99
99
|
# Destructive shell command patterns
|
|
100
100
|
DESTRUCTIVE_PATTERNS = [
|
|
101
|
-
# System destruction
|
|
102
|
-
(r"
|
|
103
|
-
(r"
|
|
104
|
-
(r"
|
|
101
|
+
# System destruction — cover common bypasses (/*, / *, wildcards, etc.)
|
|
102
|
+
(r"\brm\s+.*-r\w*\s*-?f\w*\s+/(?:\s|\*|$)", RiskLevel.CRITICAL, "Recursive delete of root filesystem"),
|
|
103
|
+
(r"\brm\s+.*-r\w*\s*-?f\w*\s+/\s*\*", RiskLevel.CRITICAL, "Recursive delete of root filesystem (/*)"),
|
|
104
|
+
(r"\brm\s+.*-r\w*\s*-?f\w*\s+/\s+\*", RiskLevel.CRITICAL, "Recursive delete of root filesystem (/ *)"),
|
|
105
|
+
(r"\brm\s+.*-r\w*\s*-?f\w*\s+~", RiskLevel.CRITICAL, "Recursive delete of home directory"),
|
|
106
|
+
(r"\brm\s+.*-r\w*\s*-?f\w*\s+\$HOME", RiskLevel.CRITICAL, "Recursive delete of home directory"),
|
|
107
|
+
(r"\bfind\s+/.*-delete\b", RiskLevel.CRITICAL, "Recursive delete of root via find"),
|
|
108
|
+
(r"\bfind\s+/\s+.*-delete\b", RiskLevel.CRITICAL, "Recursive delete of root via find"),
|
|
109
|
+
(r"\bfind\s+/.*-exec\s+rm\b", RiskLevel.CRITICAL, "Recursive delete via find -exec rm"),
|
|
110
|
+
(r"\bfind\s+/\s+.*-exec\s+rm\b", RiskLevel.CRITICAL, "Recursive delete of root via find -exec rm"),
|
|
105
111
|
(r"mkfs\.", RiskLevel.CRITICAL, "Filesystem format"),
|
|
106
|
-
(r"
|
|
107
|
-
(r"
|
|
112
|
+
(r"\bdd\s+if=", RiskLevel.CRITICAL, "Raw disk write (dd)"),
|
|
113
|
+
(r"\bdd\s+of=", RiskLevel.CRITICAL, "Raw disk write (dd of=)"),
|
|
108
114
|
(r">\s*/dev/sd", RiskLevel.CRITICAL, "Direct disk write"),
|
|
109
115
|
(r">\s*/dev/nvme", RiskLevel.CRITICAL, "Direct NVMe write"),
|
|
110
|
-
(r"
|
|
111
|
-
(r"shred\s+", RiskLevel.DANGER, "Secure file deletion"),
|
|
116
|
+
(r"\bshred\s+", RiskLevel.DANGER, "Secure file deletion"),
|
|
112
117
|
(r"\$\(.*\)", RiskLevel.CAUTION, "Command substitution detected"),
|
|
113
118
|
(r"`[^`]+`", RiskLevel.CAUTION, "Backtick command substitution detected"),
|
|
114
119
|
(r"chmod\s+777\s+/", RiskLevel.CRITICAL, "World-writable root"),
|
|
115
120
|
(r"chmod\s+-R\s+777\s+/", RiskLevel.CRITICAL, "World-writable root recursive"),
|
|
116
121
|
|
|
117
122
|
# System control
|
|
118
|
-
(r"
|
|
119
|
-
(r"
|
|
120
|
-
(r"
|
|
121
|
-
(r"
|
|
122
|
-
(r"
|
|
123
|
-
(r"
|
|
123
|
+
(r"\bshutdown\b", RiskLevel.DANGER, "System shutdown"),
|
|
124
|
+
(r"\breboot\b", RiskLevel.DANGER, "System reboot"),
|
|
125
|
+
(r"\bsystemctl\s+stop\b", RiskLevel.DANGER, "Stop system service"),
|
|
126
|
+
(r"\bsystemctl\s+disable\b", RiskLevel.DANGER, "Disable system service"),
|
|
127
|
+
(r"\bkillall\b", RiskLevel.DANGER, "Kill all processes"),
|
|
128
|
+
(r"\bpkill\b", RiskLevel.DANGER, "Kill processes by pattern"),
|
|
124
129
|
|
|
125
130
|
# Git danger
|
|
126
|
-
(r"
|
|
127
|
-
(r"
|
|
128
|
-
(r"
|
|
129
|
-
(r"
|
|
131
|
+
(r"\bgit\s+push\s+--force\b", RiskLevel.DANGER, "Force push"),
|
|
132
|
+
(r"\bgit\s+push\s+-f\b", RiskLevel.DANGER, "Force push"),
|
|
133
|
+
(r"\bgit\s+reset\s+--hard\b", RiskLevel.DANGER, "Hard reset — loses changes"),
|
|
134
|
+
(r"\bgit\s+clean\s+-fdx\b", RiskLevel.DANGER, "Remove all untracked files"),
|
|
130
135
|
|
|
131
136
|
# Network danger
|
|
132
|
-
(r"
|
|
133
|
-
(r"
|
|
134
|
-
(r"
|
|
137
|
+
(r"\bcurl\b.*\|\s*(ba)?sh\b", RiskLevel.DANGER, "Pipe curl to shell"),
|
|
138
|
+
(r"\bwget\b.*\|\s*(ba)?sh\b", RiskLevel.DANGER, "Pipe wget to shell"),
|
|
139
|
+
(r"\bnc\s+-l\b", RiskLevel.CAUTION, "Open network listener"),
|
|
135
140
|
|
|
136
141
|
# Fork bomb
|
|
137
142
|
(r":\(\)\s*\{", RiskLevel.CRITICAL, "Fork bomb pattern"),
|
|
138
143
|
|
|
139
144
|
# Database danger
|
|
140
|
-
(r"
|
|
141
|
-
(r"
|
|
142
|
-
(r"
|
|
143
|
-
(r"
|
|
145
|
+
(r"\bDROP\s+(TABLE|DATABASE)\b", RiskLevel.DANGER, "SQL DROP operation"),
|
|
146
|
+
(r"\bTRUNCATE\s+(TABLE\s+)?", RiskLevel.DANGER, "SQL TRUNCATE operation"),
|
|
147
|
+
(r"\bDELETE\s+FROM\s+\w+\s+WHERE\b", RiskLevel.CAUTION, "SQL DELETE with condition"),
|
|
148
|
+
(r"\bDELETE\s+FROM\s+\w+\s*;", RiskLevel.DANGER, "SQL DELETE without WHERE"),
|
|
144
149
|
|
|
145
150
|
# Package manager danger
|
|
146
|
-
(r"(pip|npm|gem|cargo)\s+(uninstall|remove)", RiskLevel.CAUTION, "Package removal"),
|
|
151
|
+
(r"\b(pip|npm|gem|cargo)\s+(uninstall|remove)\b", RiskLevel.CAUTION, "Package removal"),
|
|
147
152
|
|
|
148
153
|
# Permission changes
|
|
149
|
-
(r"
|
|
150
|
-
(r"
|
|
154
|
+
(r"\bchmod\s+777\b", RiskLevel.CAUTION, "Make file world-writable"),
|
|
155
|
+
(r"\bchown\s+root\b", RiskLevel.DANGER, "Change owner to root"),
|
|
151
156
|
|
|
152
157
|
# Encoded / obfuscated commands (common bypass techniques)
|
|
153
|
-
(r"
|
|
158
|
+
(r"\bbase64\s+(-d|--decode)\b", RiskLevel.CAUTION, "Base64 decode — possible obfuscated command"),
|
|
154
159
|
(r"\bIEX\s*\([^)]*\)", RiskLevel.DANGER, "PowerShell Invoke-Expression (IEX) — remote code execution risk"),
|
|
155
|
-
(r"
|
|
156
|
-
(r"
|
|
157
|
-
(r"
|
|
160
|
+
(r"\bInvoke-Expression\b", RiskLevel.DANGER, "PowerShell Invoke-Expression — remote code execution risk"),
|
|
161
|
+
(r"\bInvoke-WebRequest\b", RiskLevel.CAUTION, "PowerShell Invoke-WebRequest — fetches remote content"),
|
|
162
|
+
(r"\bInvoke-RestMethod\b", RiskLevel.CAUTION, "PowerShell Invoke-RestMethod — fetches remote content"),
|
|
163
|
+
# Additional PowerShell bypass techniques
|
|
164
|
+
(r"\bStart-Process\s+.*-Verb\s+RunAs\b", RiskLevel.DANGER, "PowerShell elevated execution"),
|
|
165
|
+
(r"\[\s*System\.Net\.WebClient\s*\]", RiskLevel.DANGER, "PowerShell WebClient — remote download"),
|
|
166
|
+
(r"\[\s*System\.Reflection\.Assembly\s*\]", RiskLevel.DANGER, "PowerShell reflection assembly load"),
|
|
167
|
+
|
|
168
|
+
# Additional bypass techniques
|
|
169
|
+
(r"\beval\s+.*\$", RiskLevel.CAUTION, "Eval with variable — possible code injection"),
|
|
170
|
+
(r"\bxargs\s+.*\brm\b", RiskLevel.DANGER, "xargs with rm — mass delete"),
|
|
171
|
+
(r">\s*/etc/", RiskLevel.CRITICAL, "Write to /etc/"),
|
|
172
|
+
(r">\s*/boot/", RiskLevel.CRITICAL, "Write to /boot/"),
|
|
158
173
|
]
|
|
159
174
|
|
|
160
175
|
# Suspicious file extensions (writing these is unusual for a code agent)
|