ata-coder 2.4.8__tar.gz → 2.4.9__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.
Files changed (143) hide show
  1. {ata_coder-2.4.8/ata_coder.egg-info → ata_coder-2.4.9}/PKG-INFO +9 -5
  2. {ata_coder-2.4.8 → ata_coder-2.4.9}/README.md +7 -3
  3. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent.py +0 -1
  4. {ata_coder-2.4.8 → ata_coder-2.4.9}/anthropic_client.py +0 -1
  5. {ata_coder-2.4.8 → ata_coder-2.4.9/ata_coder.egg-info}/PKG-INFO +9 -5
  6. {ata_coder-2.4.8 → ata_coder-2.4.9}/ata_coder.egg-info/SOURCES.txt +4 -0
  7. {ata_coder-2.4.8 → ata_coder-2.4.9}/ata_coder.egg-info/requires.txt +1 -1
  8. {ata_coder-2.4.8 → ata_coder-2.4.9}/change_tracker.py +1 -1
  9. {ata_coder-2.4.8 → ata_coder-2.4.9}/llm_client.py +0 -1
  10. {ata_coder-2.4.8 → ata_coder-2.4.9}/main.py +1 -1
  11. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompt_template.py +1 -1
  12. {ata_coder-2.4.8 → ata_coder-2.4.9}/pyproject.toml +11 -5
  13. {ata_coder-2.4.8 → ata_coder-2.4.9}/server.py +4 -78
  14. ata_coder-2.4.9/server_rate_limit.py +89 -0
  15. {ata_coder-2.4.8 → ata_coder-2.4.9}/settings.py +0 -1
  16. {ata_coder-2.4.8 → ata_coder-2.4.9}/setup_wizard.py +1 -1
  17. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/math-calculator/handler.py +1 -2
  18. {ata_coder-2.4.8 → ata_coder-2.4.9}/sub_agent.py +0 -1
  19. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_change_tracker.py +1 -1
  20. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_memory.py +2 -2
  21. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_tools.py +1 -1
  22. ata_coder-2.4.9/tools/file_ops.py +183 -0
  23. {ata_coder-2.4.8 → ata_coder-2.4.9}/LICENSE +0 -0
  24. {ata_coder-2.4.8 → ata_coder-2.4.9}/MANIFEST.in +0 -0
  25. {ata_coder-2.4.8 → ata_coder-2.4.9}/__init__.py +0 -0
  26. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent_compact.py +0 -0
  27. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent_controller.py +0 -0
  28. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent_extension.py +0 -0
  29. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent_routing.py +0 -0
  30. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent_subsystems.py +0 -0
  31. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent_tools.py +0 -0
  32. {ata_coder-2.4.8 → ata_coder-2.4.9}/agent_undo.py +0 -0
  33. {ata_coder-2.4.8 → ata_coder-2.4.9}/ata_coder.egg-info/dependency_links.txt +0 -0
  34. {ata_coder-2.4.8 → ata_coder-2.4.9}/ata_coder.egg-info/entry_points.txt +0 -0
  35. {ata_coder-2.4.8 → ata_coder-2.4.9}/ata_coder.egg-info/top_level.txt +0 -0
  36. {ata_coder-2.4.8 → ata_coder-2.4.9}/clawd_integration.py +0 -0
  37. {ata_coder-2.4.8 → ata_coder-2.4.9}/commands/__init__.py +0 -0
  38. {ata_coder-2.4.8 → ata_coder-2.4.9}/commands/_core.py +0 -0
  39. {ata_coder-2.4.8 → ata_coder-2.4.9}/commands/_safety.py +0 -0
  40. {ata_coder-2.4.8 → ata_coder-2.4.9}/commands/_settings.py +0 -0
  41. {ata_coder-2.4.8 → ata_coder-2.4.9}/commands/_workflow.py +0 -0
  42. {ata_coder-2.4.8 → ata_coder-2.4.9}/config.py +0 -0
  43. {ata_coder-2.4.8 → ata_coder-2.4.9}/context_manager.py +0 -0
  44. {ata_coder-2.4.8 → ata_coder-2.4.9}/core/__init__.py +0 -0
  45. {ata_coder-2.4.8 → ata_coder-2.4.9}/core/events.py +0 -0
  46. {ata_coder-2.4.8 → ata_coder-2.4.9}/core/queue.py +0 -0
  47. {ata_coder-2.4.8 → ata_coder-2.4.9}/core/state.py +0 -0
  48. {ata_coder-2.4.8 → ata_coder-2.4.9}/event_queue.py +0 -0
  49. {ata_coder-2.4.8 → ata_coder-2.4.9}/extension.py +0 -0
  50. {ata_coder-2.4.8 → ata_coder-2.4.9}/extensions/__init__.py +0 -0
  51. {ata_coder-2.4.8 → ata_coder-2.4.9}/extensions/hello_skill.py +0 -0
  52. {ata_coder-2.4.8 → ata_coder-2.4.9}/fool_proof.py +0 -0
  53. {ata_coder-2.4.8 → ata_coder-2.4.9}/git_workflow.py +0 -0
  54. {ata_coder-2.4.8 → ata_coder-2.4.9}/gui.py +0 -0
  55. {ata_coder-2.4.8 → ata_coder-2.4.9}/mcp_client.py +0 -0
  56. {ata_coder-2.4.8 → ata_coder-2.4.9}/memory.py +0 -0
  57. {ata_coder-2.4.8 → ata_coder-2.4.9}/model_registry.py +0 -0
  58. {ata_coder-2.4.8 → ata_coder-2.4.9}/model_router.py +0 -0
  59. {ata_coder-2.4.8 → ata_coder-2.4.9}/permissions.py +0 -0
  60. {ata_coder-2.4.8 → ata_coder-2.4.9}/privilege.py +0 -0
  61. {ata_coder-2.4.8 → ata_coder-2.4.9}/project.py +0 -0
  62. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/auto-mode.md +0 -0
  63. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/coding-rules.md +0 -0
  64. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/execution-guardrails.md +0 -0
  65. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/memory-system.md +0 -0
  66. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/output-style.md +0 -0
  67. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/safety.md +0 -0
  68. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/slash-commands.md +0 -0
  69. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/sub-agents.md +0 -0
  70. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/system-reminders.md +0 -0
  71. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/system.md +0 -0
  72. {ata_coder-2.4.8 → ata_coder-2.4.9}/prompts/tool-policy.md +0 -0
  73. {ata_coder-2.4.8 → ata_coder-2.4.9}/py.typed +0 -0
  74. {ata_coder-2.4.8 → ata_coder-2.4.9}/repl_theme.py +0 -0
  75. {ata_coder-2.4.8 → ata_coder-2.4.9}/repl_tracker.py +0 -0
  76. {ata_coder-2.4.8 → ata_coder-2.4.9}/repl_ui.py +0 -0
  77. {ata_coder-2.4.8 → ata_coder-2.4.9}/safety_guard.py +0 -0
  78. {ata_coder-2.4.8 → ata_coder-2.4.9}/self_correct.py +0 -0
  79. {ata_coder-2.4.8 → ata_coder-2.4.9}/server_session.py +0 -0
  80. {ata_coder-2.4.8 → ata_coder-2.4.9}/server_shell.py +0 -0
  81. {ata_coder-2.4.8 → ata_coder-2.4.9}/session.py +0 -0
  82. {ata_coder-2.4.8 → ata_coder-2.4.9}/setup.cfg +0 -0
  83. {ata_coder-2.4.8 → ata_coder-2.4.9}/skill_extension.py +0 -0
  84. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/architect/SKILL.md +0 -0
  85. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/code-reviewer/SKILL.md +0 -0
  86. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/codecraft/SKILL.md +0 -0
  87. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/debugger/SKILL.md +0 -0
  88. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/doc-writer/SKILL.md +0 -0
  89. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/general-coder/SKILL.md +0 -0
  90. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/math-calculator/README.md +0 -0
  91. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/math-calculator/SKILL.md +0 -0
  92. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/math-calculator/prompts/system.md +0 -0
  93. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/math-calculator/requirements.txt +0 -0
  94. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/math-calculator/resources/constants.json +0 -0
  95. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/math-calculator/tests/test_handler.py +0 -0
  96. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/security-auditor/SKILL.md +0 -0
  97. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/test-writer/SKILL.md +0 -0
  98. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/README.md +0 -0
  99. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/handler.py +0 -0
  100. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/manifest.json +0 -0
  101. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/prompts/system_prompt.txt +0 -0
  102. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/prompts/user_prompt_template.txt +0 -0
  103. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/requirements.txt +0 -0
  104. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/resources/city_list.json +0 -0
  105. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/resources/error_messages.json +0 -0
  106. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/tests/test_handler.py +0 -0
  107. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills/weather-skill/weather_utils.py +0 -0
  108. {ata_coder-2.4.8 → ata_coder-2.4.9}/skills.py +0 -0
  109. {ata_coder-2.4.8 → ata_coder-2.4.9}/sub_agent_manager.py +0 -0
  110. {ata_coder-2.4.8 → ata_coder-2.4.9}/system_prompt_builder.py +0 -0
  111. {ata_coder-2.4.8 → ata_coder-2.4.9}/task_planner.py +0 -0
  112. {ata_coder-2.4.8 → ata_coder-2.4.9}/terminal.py +0 -0
  113. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_agent.py +0 -0
  114. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_config.py +0 -0
  115. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_event_queue.py +0 -0
  116. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_extension.py +0 -0
  117. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_fibonacci.py +0 -0
  118. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_fool_proof.py +0 -0
  119. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_llm_client.py +0 -0
  120. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_model_registry.py +0 -0
  121. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_permissions.py +0 -0
  122. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_privilege.py +0 -0
  123. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_prompt_template.py +0 -0
  124. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_safety_guard.py +0 -0
  125. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_server.py +0 -0
  126. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_skill_handlers.py +0 -0
  127. {ata_coder-2.4.8 → ata_coder-2.4.9}/tests/test_sub_agent.py +0 -0
  128. {ata_coder-2.4.8 → ata_coder-2.4.9}/token_counter.py +0 -0
  129. {ata_coder-2.4.8 → ata_coder-2.4.9}/tools/__init__.py +0 -0
  130. {ata_coder-2.4.8 → ata_coder-2.4.9}/tools/definitions.py +0 -0
  131. {ata_coder-2.4.8 → ata_coder-2.4.9}/tools/executor.py +0 -0
  132. {ata_coder-2.4.8 → ata_coder-2.4.9}/tools/result.py +0 -0
  133. {ata_coder-2.4.8 → ata_coder-2.4.9}/tools/strategy.py +0 -0
  134. {ata_coder-2.4.8 → ata_coder-2.4.9}/tools/subagent.py +0 -0
  135. {ata_coder-2.4.8 → ata_coder-2.4.9}/tools/web.py +0 -0
  136. {ata_coder-2.4.8 → ata_coder-2.4.9}/types.py +0 -0
  137. {ata_coder-2.4.8 → ata_coder-2.4.9}/utils.py +0 -0
  138. {ata_coder-2.4.8 → ata_coder-2.4.9}/web/css/style.css +0 -0
  139. {ata_coder-2.4.8 → ata_coder-2.4.9}/web/index.html +0 -0
  140. {ata_coder-2.4.8 → ata_coder-2.4.9}/web/js/app.js +0 -0
  141. {ata_coder-2.4.8 → ata_coder-2.4.9}/web/package-lock.json +0 -0
  142. {ata_coder-2.4.8 → ata_coder-2.4.9}/web/package.json +0 -0
  143. {ata_coder-2.4.8 → ata_coder-2.4.9}/web/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ata-coder
3
- Version: 2.4.8
3
+ Version: 2.4.9
4
4
  Summary: ATA Coder — AI-powered coding assistant
5
5
  Author: ATA Coder Team
6
6
  License-Expression: MIT
@@ -8,7 +8,7 @@ Requires-Python: >=3.10
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: click>=8.0
11
- Requires-Dist: httpx>=0.27.0
11
+ Requires-Dist: httpx<1.0,>=0.27.0
12
12
  Requires-Dist: colorama>=0.4.6
13
13
  Requires-Dist: python-dotenv>=1.0.0
14
14
  Requires-Dist: rich>=13.0.0
@@ -21,13 +21,15 @@ Requires-Dist: pytest-timeout>=2.0; extra == "dev"
21
21
  Requires-Dist: tiktoken>=0.5.0; extra == "dev"
22
22
  Dynamic: license-file
23
23
 
24
- # ATA Coder v2.4.7
24
+ # ATA Coder v2.4.8
25
25
 
26
26
  **AI-powered coding assistant — async, AST-aware, single config file.**
27
27
 
28
28
  [English](#english) | [中文](#中文)
29
29
 
30
- > **v2.4.7** — **Context Memory Refactor**: O(1) token tracking, ContextManager, section-level prompt caching, pre-tokenized TF-IDF, LRU token cache. ~60% less overhead in the hot loop.
30
+ > **v2.4.8** — 🤖 **Self-Bootstrapped Audit (Round 12)**: ATA Coder found 10 bugs in its own source thread races, command injection, silent corruption. All self-found, all self-fixed.
31
+ >
32
+ > > **v2.4.7** — ⚡ **Context Memory Refactor**: O(1) token tracking, ContextManager, section-level prompt caching, pre-tokenized TF-IDF, LRU token cache. ~60% less overhead in the hot loop.
31
33
  >
32
34
  > > **v2.4.6** — 🔐 **OS-Native Credential Store**: API key encrypted at rest via Windows DPAPI / macOS Keychain / Linux secret-tool. Auto-migrates plaintext keys. Zero dependencies.
33
35
  >
@@ -552,7 +554,9 @@ All 6 findings in this release were discovered by **ATA Coder scanning its own s
552
554
 
553
555
  ## 中文
554
556
 
555
- > **v2.4.7** — **上下文记忆重构**: O(1) Token 追踪、ContextManager、章节级提示缓存、预分词 TF-IDF、LRU Token 缓存。热路径开销降低约 60%。
557
+ > **v2.4.8** — 🤖 **自举审计(第 12 轮)**: ATA Coder 审计自身源码发现 10 个 bug — 线程竞态、命令注入、静默数据损坏。全部自发现、自修复。
558
+ >
559
+ > > **v2.4.7** — ⚡ **上下文记忆重构**: O(1) Token 追踪、ContextManager、章节级提示缓存、预分词 TF-IDF、LRU Token 缓存。热路径开销降低约 60%。
556
560
  >
557
561
  > > **v2.4.6** — 🔐 **操作系统凭据存储**: API Key 通过 Windows DPAPI / macOS Keychain / Linux secret-tool 加密存储。自动迁移明文密钥。零依赖。
558
562
  >
@@ -1,10 +1,12 @@
1
- # ATA Coder v2.4.7
1
+ # ATA Coder v2.4.8
2
2
 
3
3
  **AI-powered coding assistant — async, AST-aware, single config file.**
4
4
 
5
5
  [English](#english) | [中文](#中文)
6
6
 
7
- > **v2.4.7** — **Context Memory Refactor**: O(1) token tracking, ContextManager, section-level prompt caching, pre-tokenized TF-IDF, LRU token cache. ~60% less overhead in the hot loop.
7
+ > **v2.4.8** — 🤖 **Self-Bootstrapped Audit (Round 12)**: ATA Coder found 10 bugs in its own source thread races, command injection, silent corruption. All self-found, all self-fixed.
8
+ >
9
+ > > **v2.4.7** — ⚡ **Context Memory Refactor**: O(1) token tracking, ContextManager, section-level prompt caching, pre-tokenized TF-IDF, LRU token cache. ~60% less overhead in the hot loop.
8
10
  >
9
11
  > > **v2.4.6** — 🔐 **OS-Native Credential Store**: API key encrypted at rest via Windows DPAPI / macOS Keychain / Linux secret-tool. Auto-migrates plaintext keys. Zero dependencies.
10
12
  >
@@ -529,7 +531,9 @@ All 6 findings in this release were discovered by **ATA Coder scanning its own s
529
531
 
530
532
  ## 中文
531
533
 
532
- > **v2.4.7** — **上下文记忆重构**: O(1) Token 追踪、ContextManager、章节级提示缓存、预分词 TF-IDF、LRU Token 缓存。热路径开销降低约 60%。
534
+ > **v2.4.8** — 🤖 **自举审计(第 12 轮)**: ATA Coder 审计自身源码发现 10 个 bug — 线程竞态、命令注入、静默数据损坏。全部自发现、自修复。
535
+ >
536
+ > > **v2.4.7** — ⚡ **上下文记忆重构**: O(1) Token 追踪、ContextManager、章节级提示缓存、预分词 TF-IDF、LRU Token 缓存。热路径开销降低约 60%。
533
537
  >
534
538
  > > **v2.4.6** — 🔐 **操作系统凭据存储**: API Key 通过 Windows DPAPI / macOS Keychain / Linux secret-tool 加密存储。自动迁移明文密钥。零依赖。
535
539
  >
@@ -22,7 +22,6 @@ The agent runs a conversation loop:
22
22
  import asyncio
23
23
  import json
24
24
  import logging
25
- import os
26
25
  import time
27
26
  from typing import Any, Callable
28
27
 
@@ -218,7 +218,6 @@ class AnthropicClient(BaseLLMClient):
218
218
  body = sanitize_surrogates(body)
219
219
 
220
220
  # Retry loop for streaming (up to 2 retries for 429/5xx)
221
- last_error = None
222
221
  for attempt in range(self._max_retries):
223
222
  try:
224
223
  resp = await self._client.send(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ata-coder
3
- Version: 2.4.8
3
+ Version: 2.4.9
4
4
  Summary: ATA Coder — AI-powered coding assistant
5
5
  Author: ATA Coder Team
6
6
  License-Expression: MIT
@@ -8,7 +8,7 @@ Requires-Python: >=3.10
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: click>=8.0
11
- Requires-Dist: httpx>=0.27.0
11
+ Requires-Dist: httpx<1.0,>=0.27.0
12
12
  Requires-Dist: colorama>=0.4.6
13
13
  Requires-Dist: python-dotenv>=1.0.0
14
14
  Requires-Dist: rich>=13.0.0
@@ -21,13 +21,15 @@ Requires-Dist: pytest-timeout>=2.0; extra == "dev"
21
21
  Requires-Dist: tiktoken>=0.5.0; extra == "dev"
22
22
  Dynamic: license-file
23
23
 
24
- # ATA Coder v2.4.7
24
+ # ATA Coder v2.4.8
25
25
 
26
26
  **AI-powered coding assistant — async, AST-aware, single config file.**
27
27
 
28
28
  [English](#english) | [中文](#中文)
29
29
 
30
- > **v2.4.7** — **Context Memory Refactor**: O(1) token tracking, ContextManager, section-level prompt caching, pre-tokenized TF-IDF, LRU token cache. ~60% less overhead in the hot loop.
30
+ > **v2.4.8** — 🤖 **Self-Bootstrapped Audit (Round 12)**: ATA Coder found 10 bugs in its own source thread races, command injection, silent corruption. All self-found, all self-fixed.
31
+ >
32
+ > > **v2.4.7** — ⚡ **Context Memory Refactor**: O(1) token tracking, ContextManager, section-level prompt caching, pre-tokenized TF-IDF, LRU token cache. ~60% less overhead in the hot loop.
31
33
  >
32
34
  > > **v2.4.6** — 🔐 **OS-Native Credential Store**: API key encrypted at rest via Windows DPAPI / macOS Keychain / Linux secret-tool. Auto-migrates plaintext keys. Zero dependencies.
33
35
  >
@@ -552,7 +554,9 @@ All 6 findings in this release were discovered by **ATA Coder scanning its own s
552
554
 
553
555
  ## 中文
554
556
 
555
- > **v2.4.7** — **上下文记忆重构**: O(1) Token 追踪、ContextManager、章节级提示缓存、预分词 TF-IDF、LRU Token 缓存。热路径开销降低约 60%。
557
+ > **v2.4.8** — 🤖 **自举审计(第 12 轮)**: ATA Coder 审计自身源码发现 10 个 bug — 线程竞态、命令注入、静默数据损坏。全部自发现、自修复。
558
+ >
559
+ > > **v2.4.7** — ⚡ **上下文记忆重构**: O(1) Token 追踪、ContextManager、章节级提示缓存、预分词 TF-IDF、LRU Token 缓存。热路径开销降低约 60%。
556
560
  >
557
561
  > > **v2.4.6** — 🔐 **操作系统凭据存储**: API Key 通过 Windows DPAPI / macOS Keychain / Linux secret-tool 加密存储。自动迁移明文密钥。零依赖。
558
562
  >
@@ -38,6 +38,7 @@ repl_ui.py
38
38
  safety_guard.py
39
39
  self_correct.py
40
40
  server.py
41
+ server_rate_limit.py
41
42
  server_session.py
42
43
  server_shell.py
43
44
  session.py
@@ -89,6 +90,7 @@ utils.py
89
90
  ./safety_guard.py
90
91
  ./self_correct.py
91
92
  ./server.py
93
+ ./server_rate_limit.py
92
94
  ./server_session.py
93
95
  ./server_shell.py
94
96
  ./session.py
@@ -154,6 +156,7 @@ utils.py
154
156
  ./tools/__init__.py
155
157
  ./tools/definitions.py
156
158
  ./tools/executor.py
159
+ ./tools/file_ops.py
157
160
  ./tools/result.py
158
161
  ./tools/strategy.py
159
162
  ./tools/subagent.py
@@ -238,6 +241,7 @@ tests/test_tools.py
238
241
  tools/__init__.py
239
242
  tools/definitions.py
240
243
  tools/executor.py
244
+ tools/file_ops.py
241
245
  tools/result.py
242
246
  tools/strategy.py
243
247
  tools/subagent.py
@@ -1,5 +1,5 @@
1
1
  click>=8.0
2
- httpx>=0.27.0
2
+ httpx<1.0,>=0.27.0
3
3
  colorama>=0.4.6
4
4
  python-dotenv>=1.0.0
5
5
  rich>=13.0.0
@@ -173,7 +173,7 @@ class ChangeTracker:
173
173
  if old_content == new_content:
174
174
  return None
175
175
 
176
- path = Path(file_path)
176
+ Path(file_path)
177
177
  # Backup before edit (for undo)
178
178
  self._backup(file_path)
179
179
 
@@ -282,7 +282,6 @@ class LLMClient(BaseLLMClient):
282
282
  body.pop("temperature", None)
283
283
 
284
284
  # Retry loop for streaming (up to 2 retries for 429/5xx)
285
- last_error = None
286
285
 
287
286
  # Sanitize surrogates before JSON encoding (prevent UTF-8 encode crash)
288
287
  from .utils import sanitize_surrogates
@@ -44,7 +44,7 @@ if sys.platform == 'win32':
44
44
  _patched_init.__ata_patched__ = True
45
45
  _sp.Popen.__init__ = _patched_init
46
46
 
47
- __version__ = "2.4.8"
47
+ __version__ = "2.4.9"
48
48
 
49
49
  import asyncio
50
50
  import logging
@@ -240,7 +240,7 @@ class PromptTemplate:
240
240
  import re as _re
241
241
 
242
242
  if_tag = _re.compile(r'\{\%\s*if\s+(.+?)\s*\%\}')
243
- endif_tag = _re.compile(r'\{\%\s*endif\s*\%\}')
243
+ _re.compile(r'\{\%\s*endif\s*\%\}')
244
244
  any_tag = _re.compile(r'\{\%\s*(?:if\s+.+?|endif)\s*\%\}')
245
245
 
246
246
  def _eval_condition(cond: str) -> bool:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ata-coder"
7
- version = "2.4.8"
7
+ version = "2.4.9"
8
8
  description = "ATA Coder — AI-powered coding assistant"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -14,7 +14,7 @@ authors = [
14
14
  ]
15
15
  dependencies = [
16
16
  "click>=8.0",
17
- "httpx>=0.27.0",
17
+ "httpx>=0.27.0,<1.0",
18
18
  "colorama>=0.4.6",
19
19
  "python-dotenv>=1.0.0",
20
20
  "rich>=13.0.0",
@@ -81,16 +81,22 @@ exclude = ["build", "dist", ".git", "__pycache__", ".pytest_cache", "web/node_mo
81
81
 
82
82
  [tool.ruff.lint]
83
83
  select = ["E", "F", "W"]
84
- ignore = ["E501", "E402", "E701", "E722", "E741", "E731", "F401", "F821", "F841"]
84
+ ignore = ["E501", "E402", "E701", "E722", "E741", "E731", "F821"]
85
+
86
+ [tool.ruff.lint.per-file-ignores]
87
+ # repl_ui.py + terminal.py: rich/colorama imports are availability checks
88
+ "repl_ui.py" = ["F401"]
89
+ "terminal.py" = ["F401"]
90
+ # Skill handlers + tests: some imports are for dynamic loading or optional deps
91
+ "skills/*/handler.py" = ["F401"]
92
+ "tests/*.py" = ["F401", "F841"]
85
93
  # E501: line too long (enforced by formatter instead)
86
94
  # E402: imports after sys.path.insert (intentional in main/server/gui)
87
95
  # E701: multiple statements on one line (compact error handling)
88
96
  # E722: bare except (intentional for cleanup/shutdown paths)
89
97
  # E741: ambiguous variable name (l for loop vars)
90
98
  # E731: lambda assignment (trivial inline helpers, not exported)
91
- # F401: unused import (optional deps guarded by try/except)
92
99
  # F821: undefined name (optional deps guarded by try/except)
93
- # F841: unused variable (test assertions via side effects)
94
100
 
95
101
  [tool.mypy]
96
102
  python_version = "3.10"
@@ -22,8 +22,6 @@ Usage:
22
22
  python main.py --server # From main launcher
23
23
  """
24
24
 
25
- import asyncio
26
- import collections
27
25
  import json
28
26
  import logging
29
27
  import os
@@ -101,6 +99,7 @@ from .config import AppConfig, get_config
101
99
  from .tools import TOOL_DEFINITIONS
102
100
  from .server_session import SessionStore
103
101
  from .server_shell import shell_open, shell_ensure, shell_close, shell_close_all, get_shell_sessions
102
+ from .server_rate_limit import RateLimiter
104
103
  from .skills import get_skill_manager
105
104
  from .utils import brief_args
106
105
 
@@ -112,11 +111,13 @@ logger = logging.getLogger(__name__)
112
111
  # ══════════════════════════════════════════════════════════════════════# HTTP Request Handler
113
112
  # ═══════════════════════════════════════════════════════════════════════════════
114
113
 
115
- class AgentAPIHandler(BaseHTTPRequestHandler):
114
+ class AgentAPIHandler(RateLimiter, BaseHTTPRequestHandler):
116
115
  """HTTP handler for the ATA Coder API.
117
116
 
118
117
  *config* and *store* are set as class attributes by :func:`create_server`
119
118
  before the server starts accepting requests.
119
+
120
+ Rate limiting is inherited from :class:`RateLimiter`.
120
121
  """
121
122
 
122
123
  # Class-level references (set by server factory before accepting requests).
@@ -126,80 +127,6 @@ class AgentAPIHandler(BaseHTTPRequestHandler):
126
127
  store: "SessionStore | None" = None
127
128
  _ws_lock: threading.Lock = threading.Lock() # protects workspace dir reads/writes
128
129
 
129
- # ── Rate limiting (class-level, shared across handler instances) ──────
130
- _rate_lock: threading.Lock = threading.Lock()
131
- _rate_buckets: dict[str, "collections.deque[float]"] = {} # ip → deque of timestamps
132
- _rate_blocked: dict[str, float] = {} # ip → block expiry timestamp
133
- _RATE_MAX_REQUESTS = 120 # max requests per window
134
- _RATE_WINDOW_S = 60.0 # sliding window in seconds
135
- _RATE_BLOCK_S = 300.0 # block duration after exceeding penalty threshold
136
- _RATE_PENALTY_MULTIPLIER = 3 # requests × this = block threshold
137
- _RATE_CLEANUP_INTERVAL = 1000 # amortized: trigger cleanup every N calls
138
- _rate_cleanup_counter: int = 0
139
-
140
- @classmethod
141
- def _cleanup_rate_buckets(cls, now: float) -> None:
142
- """Remove stale IP entries whose last activity exceeds 2× the window.
143
-
144
- Without this, IPs that stay under the rate limit forever accumulate
145
- in _rate_buckets and leak memory over long-running server processes.
146
- """
147
- cutoff = now - cls._RATE_WINDOW_S * 2
148
- stale = [
149
- ip for ip, dq in cls._rate_buckets.items()
150
- if not dq or dq[-1] <= cutoff
151
- ]
152
- for ip in stale:
153
- del cls._rate_buckets[ip]
154
- if stale:
155
- logger.debug("Rate limiter: pruned %d stale IP bucket(s)", len(stale))
156
-
157
- @classmethod
158
- def _check_rate_limit(cls, client_ip: str) -> bool:
159
- """Sliding-window rate limiter with deque for O(1) cleanup.
160
-
161
- Returns True if request is allowed.
162
- """
163
- now = time.time()
164
- with cls._rate_lock:
165
- # Periodic stale-bucket cleanup (amortized — triggered every N calls)
166
- cls._rate_cleanup_counter += 1
167
- if cls._rate_cleanup_counter >= cls._RATE_CLEANUP_INTERVAL:
168
- cls._rate_cleanup_counter = 0
169
- cls._cleanup_rate_buckets(now)
170
-
171
- # Check if IP is currently blocked (penalty tier)
172
- blocked_until = cls._rate_blocked.get(client_ip, 0)
173
- if now < blocked_until:
174
- return False
175
- if now >= blocked_until and client_ip in cls._rate_blocked:
176
- del cls._rate_blocked[client_ip]
177
-
178
- dq = cls._rate_buckets.get(client_ip)
179
- if dq is None:
180
- dq = collections.deque()
181
- cls._rate_buckets[client_ip] = dq
182
-
183
- # Purge expired entries — O(1) per entry via popleft
184
- cutoff = now - cls._RATE_WINDOW_S
185
- while dq and dq[0] <= cutoff:
186
- dq.popleft()
187
-
188
- # Penalty tier: block if request count exceeds penalty threshold
189
- penalty_limit = cls._RATE_MAX_REQUESTS * cls._RATE_PENALTY_MULTIPLIER
190
- if len(dq) > penalty_limit:
191
- cls._rate_blocked[client_ip] = now + cls._RATE_BLOCK_S
192
- logger.warning("Rate limit BLOCK: %s for %ds (%d requests in window)",
193
- client_ip, cls._RATE_BLOCK_S, len(dq))
194
- return False
195
-
196
- # Standard rate limit
197
- if len(dq) >= cls._RATE_MAX_REQUESTS:
198
- return False
199
-
200
- dq.append(now)
201
- return True
202
-
203
130
  def __init__(self, *args, **kwargs):
204
131
  # Per-instance copies for thread-safe access under ThreadingHTTPServer
205
132
  self.config = self.__class__.config
@@ -469,7 +396,6 @@ class AgentAPIHandler(BaseHTTPRequestHandler):
469
396
  except Exception:
470
397
  logger.debug("Failed to fetch models from API, using cache", exc_info=True)
471
398
  # Fallback: cached model list from settings or env
472
- import os
473
399
  from .settings import get_settings
474
400
  cached = get_settings().get("env", "ATA_CODER_MODELS_CACHE", default="") or self.config.llm.model
475
401
  models = [{"id": m.strip(), "owned_by": ""} for m in cached.split(",") if m.strip()]
@@ -0,0 +1,89 @@
1
+ """Sliding-window rate limiter for the HTTP API server.
2
+
3
+ Extracted from server.py to reduce file size and isolate concerns.
4
+ Used by AgentAPIHandler as a class-level mixin.
5
+ """
6
+
7
+ import collections
8
+ import logging
9
+ import threading
10
+ import time
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class RateLimiter:
16
+ """Sliding-window rate limiter with deque for O(1) expiry + penalty tier.
17
+
18
+ Usage (as class-level mixin on a BaseHTTPRequestHandler subclass):
19
+ class MyHandler(RateLimiter, BaseHTTPRequestHandler):
20
+ ...
21
+ allowed = self._check_rate_limit(self.client_address[0])
22
+ """
23
+
24
+ # ── Configuration ──────────────────────────────────────────────────────
25
+ _rate_lock: threading.Lock = threading.Lock()
26
+ _rate_buckets: dict[str, "collections.deque[float]"] = {} # ip → deque of timestamps
27
+ _rate_blocked: dict[str, float] = {} # ip → block expiry timestamp
28
+ _RATE_MAX_REQUESTS = 120 # max requests per window
29
+ _RATE_WINDOW_S = 60.0 # sliding window in seconds
30
+ _RATE_BLOCK_S = 300.0 # block duration after exceeding penalty threshold
31
+ _RATE_PENALTY_MULTIPLIER = 3 # requests × this = block threshold
32
+ _RATE_CLEANUP_INTERVAL = 1000 # amortized: trigger cleanup every N calls
33
+ _rate_cleanup_counter: int = 0
34
+
35
+ @classmethod
36
+ def _cleanup_rate_buckets(cls, now: float) -> None:
37
+ """Remove stale IP entries whose last activity exceeds 2× the window."""
38
+ cutoff = now - cls._RATE_WINDOW_S * 2
39
+ stale = [
40
+ ip for ip, dq in cls._rate_buckets.items()
41
+ if not dq or dq[-1] <= cutoff
42
+ ]
43
+ for ip in stale:
44
+ del cls._rate_buckets[ip]
45
+ if stale:
46
+ logger.debug("Rate limiter: pruned %d stale IP bucket(s)", len(stale))
47
+
48
+ @classmethod
49
+ def _check_rate_limit(cls, client_ip: str) -> bool:
50
+ """Sliding-window rate limiter. Returns True if request is allowed."""
51
+ now = time.time()
52
+ with cls._rate_lock:
53
+ # Periodic stale-bucket cleanup (amortized)
54
+ cls._rate_cleanup_counter += 1
55
+ if cls._rate_cleanup_counter >= cls._RATE_CLEANUP_INTERVAL:
56
+ cls._rate_cleanup_counter = 0
57
+ cls._cleanup_rate_buckets(now)
58
+
59
+ # Check if IP is currently blocked (penalty tier)
60
+ blocked_until = cls._rate_blocked.get(client_ip, 0)
61
+ if now < blocked_until:
62
+ return False
63
+ if now >= blocked_until and client_ip in cls._rate_blocked:
64
+ del cls._rate_blocked[client_ip]
65
+
66
+ dq = cls._rate_buckets.get(client_ip)
67
+ if dq is None:
68
+ dq = collections.deque()
69
+ cls._rate_buckets[client_ip] = dq
70
+
71
+ # Purge expired entries — O(1) per entry via popleft
72
+ cutoff = now - cls._RATE_WINDOW_S
73
+ while dq and dq[0] <= cutoff:
74
+ dq.popleft()
75
+
76
+ # Penalty tier: block if request count exceeds penalty threshold
77
+ penalty_limit = cls._RATE_MAX_REQUESTS * cls._RATE_PENALTY_MULTIPLIER
78
+ if len(dq) > penalty_limit:
79
+ cls._rate_blocked[client_ip] = now + cls._RATE_BLOCK_S
80
+ logger.warning("Rate limit BLOCK: %s for %ds (%d requests in window)",
81
+ client_ip, cls._RATE_BLOCK_S, len(dq))
82
+ return False
83
+
84
+ # Standard rate limit
85
+ if len(dq) >= cls._RATE_MAX_REQUESTS:
86
+ return False
87
+
88
+ dq.append(now)
89
+ return True
@@ -541,7 +541,6 @@ def _store_credential(service: str, account: str, secret: str) -> bool:
541
541
  cred_file.write_text(result.stdout.strip(), encoding="utf-8")
542
542
  # Restrictive permissions on the credential file
543
543
  try:
544
- import stat
545
544
  cred_file.chmod(0o600)
546
545
  except Exception:
547
546
  pass
@@ -111,7 +111,7 @@ def run_setup_wizard() -> None:
111
111
  print()
112
112
 
113
113
 
114
- __version__ = "2.4.8"
114
+ __version__ = "2.4.9"
115
115
 
116
116
 
117
117
  def print_banner() -> None:
@@ -4,7 +4,6 @@
4
4
  import ast
5
5
  import math
6
6
  import operator
7
- import re
8
7
  from typing import Any
9
8
 
10
9
 
@@ -155,7 +154,7 @@ def run(input_data: dict[str, Any]) -> dict[str, Any]:
155
154
  """
156
155
  expression = input_data.get("expression", "").strip()
157
156
  precision = input_data.get("precision", 6)
158
- output_format = input_data.get("format", "number")
157
+ input_data.get("format", "number")
159
158
 
160
159
  # Validate
161
160
  if not expression:
@@ -12,7 +12,6 @@ Each SubAgent runs as an asyncio.Task and has:
12
12
  import asyncio
13
13
  import json
14
14
  import logging
15
- import os
16
15
  import uuid
17
16
  from dataclasses import dataclass, field
18
17
  from typing import Callable, Optional
@@ -110,7 +110,7 @@ class TestChangeTracker:
110
110
 
111
111
  def test_init_creates_backup_dir(self, tmp_path):
112
112
  """ChangeTracker should create backup directory."""
113
- tracker = ChangeTracker("test-session", backup_dir=tmp_path / "backups")
113
+ ChangeTracker("test-session", backup_dir=tmp_path / "backups")
114
114
  assert (tmp_path / "backups").exists()
115
115
 
116
116
  def test_capture_write_new_file(self, tmp_path):
@@ -144,13 +144,13 @@ class TestMemoryStoreInit:
144
144
  def test_init_creates_memory_dir(self, tmp_path):
145
145
  """MemoryStore should create the memory directory on init."""
146
146
  memory_dir = tmp_path / "ata_memories"
147
- store = MemoryStore(memory_dir)
147
+ MemoryStore(memory_dir)
148
148
  assert memory_dir.exists()
149
149
 
150
150
  def test_init_creates_memory_index(self, tmp_path):
151
151
  """MemoryStore should create MEMORY.md index on init."""
152
152
  memory_dir = tmp_path / "ata_memories"
153
- store = MemoryStore(memory_dir)
153
+ MemoryStore(memory_dir)
154
154
  index_file = memory_dir / "MEMORY.md"
155
155
  assert index_file.exists()
156
156
 
@@ -147,7 +147,7 @@ class TestToolReadFile:
147
147
  async def test_read_caches_file(self):
148
148
  f = Path(self.tmp) / "cache_test.txt"
149
149
  f.write_text("cached content\n", encoding="utf-8")
150
- cache_key = str(f.resolve())
150
+ str(f.resolve())
151
151
  # First read
152
152
  r1 = await self.executor.execute("read_file", {"file_path": str(f)})
153
153
  assert r1.success