codeoptix 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 (91) hide show
  1. codeoptix/__init__.py +8 -0
  2. codeoptix/acp/__init__.py +33 -0
  3. codeoptix/acp/agent.py +209 -0
  4. codeoptix/acp/bridge.py +402 -0
  5. codeoptix/acp/client_adapter.py +312 -0
  6. codeoptix/acp/code_extractor.py +125 -0
  7. codeoptix/acp/orchestrator.py +349 -0
  8. codeoptix/acp/registry.py +294 -0
  9. codeoptix/adapters/__init__.py +18 -0
  10. codeoptix/adapters/base.py +50 -0
  11. codeoptix/adapters/basic.py +195 -0
  12. codeoptix/adapters/claude_code.py +218 -0
  13. codeoptix/adapters/codex.py +327 -0
  14. codeoptix/adapters/factory.py +56 -0
  15. codeoptix/adapters/gemini_cli.py +370 -0
  16. codeoptix/artifacts/__init__.py +5 -0
  17. codeoptix/artifacts/manager.py +193 -0
  18. codeoptix/behaviors/__init__.py +45 -0
  19. codeoptix/behaviors/base.py +81 -0
  20. codeoptix/behaviors/insecure_code.py +129 -0
  21. codeoptix/behaviors/plan_drift.py +192 -0
  22. codeoptix/behaviors/vacuous_tests.py +198 -0
  23. codeoptix/cli.py +1472 -0
  24. codeoptix/evaluation/__init__.py +23 -0
  25. codeoptix/evaluation/bloom_integration.py +271 -0
  26. codeoptix/evaluation/engine.py +274 -0
  27. codeoptix/evaluation/evaluators.py +308 -0
  28. codeoptix/evaluation/scenario_generator.py +222 -0
  29. codeoptix/evolution/__init__.py +7 -0
  30. codeoptix/evolution/engine.py +206 -0
  31. codeoptix/evolution/gepa_integration.py +149 -0
  32. codeoptix/evolution/proposer.py +185 -0
  33. codeoptix/linters/__init__.py +13 -0
  34. codeoptix/linters/bandit_linter.py +172 -0
  35. codeoptix/linters/base.py +105 -0
  36. codeoptix/linters/coverage_linter.py +156 -0
  37. codeoptix/linters/flake8_linter.py +156 -0
  38. codeoptix/linters/html_accessibility_linter.py +374 -0
  39. codeoptix/linters/language_detector.py +150 -0
  40. codeoptix/linters/mypy_linter.py +184 -0
  41. codeoptix/linters/pip_audit_linter.py +152 -0
  42. codeoptix/linters/pylint_linter.py +198 -0
  43. codeoptix/linters/ruff_linter.py +206 -0
  44. codeoptix/linters/runner.py +186 -0
  45. codeoptix/linters/safety_linter.py +184 -0
  46. codeoptix/reflection/__init__.py +6 -0
  47. codeoptix/reflection/engine.py +70 -0
  48. codeoptix/reflection/generator.py +209 -0
  49. codeoptix/utils/__init__.py +1 -0
  50. codeoptix/utils/config.py +91 -0
  51. codeoptix/utils/llm.py +332 -0
  52. codeoptix/utils/retry.py +133 -0
  53. codeoptix/vendor/__init__.py +2 -0
  54. codeoptix/vendor/bloom/README.md +26 -0
  55. codeoptix/vendor/bloom/__init__.py +11 -0
  56. codeoptix/vendor/bloom/globals.py +39 -0
  57. codeoptix/vendor/bloom/orchestrators/ConversationOrchestrator.py +450 -0
  58. codeoptix/vendor/bloom/orchestrators/SimEnvOrchestrator.py +839 -0
  59. codeoptix/vendor/bloom/prompts/configurable_prompts/README.md +85 -0
  60. codeoptix/vendor/bloom/prompts/configurable_prompts/default.json +18 -0
  61. codeoptix/vendor/bloom/prompts/configurable_prompts/ideation-default.json +18 -0
  62. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_animal-welfare.json +18 -0
  63. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_contextual-optimism.json +18 -0
  64. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_defend-objects.json +18 -0
  65. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_defer-to-users.json +18 -0
  66. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_emotional-bond.json +18 -0
  67. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_flattery.json +18 -0
  68. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_hardcode-test-cases.json +18 -0
  69. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_increasing-pep.json +18 -0
  70. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_research-sandbagging.json +18 -0
  71. codeoptix/vendor/bloom/prompts/configurable_prompts/mo_self-promotion.json +18 -0
  72. codeoptix/vendor/bloom/prompts/configurable_prompts/sandbag.json +18 -0
  73. codeoptix/vendor/bloom/prompts/configurable_prompts/self-preferential-bias.json +18 -0
  74. codeoptix/vendor/bloom/prompts/configurable_prompts/static-prompts.yaml +72 -0
  75. codeoptix/vendor/bloom/prompts/configurable_prompts/web-search.json +18 -0
  76. codeoptix/vendor/bloom/prompts/step1_understanding.py +63 -0
  77. codeoptix/vendor/bloom/prompts/step2_ideation.py +254 -0
  78. codeoptix/vendor/bloom/prompts/step3_rollout.py +120 -0
  79. codeoptix/vendor/bloom/prompts/step4_judgment.py +183 -0
  80. codeoptix/vendor/bloom/schemas/behavior.schema.json +160 -0
  81. codeoptix/vendor/bloom/schemas/conversation.schema.json +51 -0
  82. codeoptix/vendor/bloom/schemas/transcript_schema.json +2225 -0
  83. codeoptix/vendor/bloom/scripts/step2_ideation.py +667 -0
  84. codeoptix/vendor/bloom/scripts/step4_judgment.py +811 -0
  85. codeoptix/vendor/bloom/transcript_utils.py +440 -0
  86. codeoptix/vendor/bloom/utils.py +700 -0
  87. codeoptix-0.1.0.dist-info/METADATA +304 -0
  88. codeoptix-0.1.0.dist-info/RECORD +91 -0
  89. codeoptix-0.1.0.dist-info/WHEEL +4 -0
  90. codeoptix-0.1.0.dist-info/entry_points.txt +2 -0
  91. codeoptix-0.1.0.dist-info/licenses/LICENSE +203 -0
codeoptix/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """CodeOptiX: Agentic Code Optimization & Deep Evaluation for Superior Coding Agent Experience.
2
+
3
+ The universal code optimization engine that improves coding agent experience with deep evaluations and optimization. When AI coding agents dazzle with impressive code but leave you wondering about quality, maintainability, security, and reliability, CodeOptiX ensures proper behavior through evaluations, reflection, and self-improvement.
4
+
5
+ Built by Superagentic AI - Advancing AI agent optimization and autonomous systems.
6
+ """
7
+
8
+ __version__ = "0.1.0"
@@ -0,0 +1,33 @@
1
+ """ACP (Agent Client Protocol) integration for CodeOptiX.
2
+
3
+ This module provides ACP integration:
4
+ - CodeOptiX as an ACP agent (can be used by editors)
5
+ - ACP client adapter (connect to other agents via ACP)
6
+ - Quality bridge functionality
7
+ - Agent registry and orchestration
8
+ - Multi-agent judge support
9
+ """
10
+
11
+ from codeoptix.acp.agent import CodeOptiXAgent
12
+ from codeoptix.acp.bridge import ACPQualityBridge
13
+ from codeoptix.acp.client_adapter import ACPClientAdapter
14
+ from codeoptix.acp.code_extractor import (
15
+ extract_all_code,
16
+ extract_code_from_message,
17
+ extract_code_from_text,
18
+ )
19
+ from codeoptix.acp.orchestrator import AgentOrchestrator, MultiAgentJudge
20
+ from codeoptix.acp.registry import ACPAgentConfig, ACPAgentRegistry
21
+
22
+ __all__ = [
23
+ "ACPAgentConfig",
24
+ "ACPAgentRegistry",
25
+ "ACPClientAdapter",
26
+ "ACPQualityBridge",
27
+ "AgentOrchestrator",
28
+ "CodeOptiXAgent",
29
+ "MultiAgentJudge",
30
+ "extract_all_code",
31
+ "extract_code_from_message",
32
+ "extract_code_from_text",
33
+ ]
codeoptix/acp/agent.py ADDED
@@ -0,0 +1,209 @@
1
+ """CodeOptiX as an ACP Agent.
2
+
3
+ This allows CodeOptiX to be used by ACP-compatible editors (Zed, JetBrains, Neovim, etc.)
4
+ as a quality engineering agent.
5
+ """
6
+
7
+ from typing import Any
8
+ from uuid import uuid4
9
+
10
+ from acp import (
11
+ Agent,
12
+ InitializeResponse,
13
+ NewSessionResponse,
14
+ PromptResponse,
15
+ text_block,
16
+ update_agent_message,
17
+ )
18
+ from acp.interfaces import Client
19
+ from acp.schema import (
20
+ AudioContentBlock,
21
+ ClientCapabilities,
22
+ EmbeddedResourceContentBlock,
23
+ HttpMcpServer,
24
+ ImageContentBlock,
25
+ Implementation,
26
+ McpServerStdio,
27
+ ResourceContentBlock,
28
+ SseMcpServer,
29
+ TextContentBlock,
30
+ )
31
+
32
+ from codeoptix.acp.code_extractor import extract_code_from_text
33
+ from codeoptix.adapters.base import AgentOutput
34
+ from codeoptix.evaluation import EvaluationEngine
35
+ from codeoptix.utils.llm import LLMClient
36
+
37
+
38
+ class CodeOptiXAgent(Agent):
39
+ """CodeOptiX as an ACP agent for quality engineering."""
40
+
41
+ _conn: Client | None = None
42
+ _evaluation_engine: EvaluationEngine | None = None
43
+ _llm_client: LLMClient | None = None
44
+
45
+ def __init__(
46
+ self,
47
+ evaluation_engine: EvaluationEngine | None = None,
48
+ llm_client: LLMClient | None = None,
49
+ behaviors: list[str] | None = None,
50
+ ):
51
+ """Initialize CodeOptiX ACP agent.
52
+
53
+ Args:
54
+ evaluation_engine: Optional evaluation engine (will create default if not provided)
55
+ llm_client: Optional LLM client (will create default if not provided)
56
+ behaviors: List of behavior names to evaluate (default: all)
57
+ """
58
+ self._evaluation_engine = evaluation_engine
59
+ self._llm_client = llm_client
60
+ self._behaviors = behaviors or ["insecure-code", "vacuous-tests", "plan-drift"]
61
+
62
+ # Initialize default evaluation engine if not provided
63
+ if not self._evaluation_engine and self._llm_client:
64
+ # Create a minimal adapter for evaluation (dummy adapter)
65
+ from codeoptix.adapters.base import AgentAdapter
66
+ from codeoptix.adapters.factory import create_adapter
67
+
68
+ # Try to create a default adapter (will use dummy if none available)
69
+ try:
70
+ adapter = create_adapter("claude-code", {"llm_config": {"provider": "anthropic"}})
71
+ except Exception:
72
+ # Create a minimal dummy adapter
73
+ class DummyAdapter(AgentAdapter):
74
+ def get_adapter_type(self) -> str:
75
+ return "dummy"
76
+
77
+ def generate_code(self, prompt: str, **kwargs) -> AgentOutput:
78
+ return AgentOutput(code="", tests="", messages=[], metadata={})
79
+
80
+ adapter = DummyAdapter()
81
+
82
+ self._evaluation_engine = EvaluationEngine(adapter, self._llm_client)
83
+
84
+ def on_connect(self, conn: Client) -> None:
85
+ """Called when agent connects to client."""
86
+ self._conn = conn
87
+
88
+ async def initialize(
89
+ self,
90
+ protocol_version: int,
91
+ client_capabilities: ClientCapabilities | None = None,
92
+ client_info: Implementation | None = None,
93
+ **kwargs: Any,
94
+ ) -> InitializeResponse:
95
+ """Initialize the agent with client capabilities."""
96
+ return InitializeResponse(protocol_version=protocol_version)
97
+
98
+ async def new_session(
99
+ self,
100
+ cwd: str,
101
+ mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio],
102
+ **kwargs: Any,
103
+ ) -> NewSessionResponse:
104
+ """Create a new session."""
105
+ session_id = uuid4().hex
106
+ return NewSessionResponse(session_id=session_id)
107
+
108
+ async def prompt(
109
+ self,
110
+ prompt: list[
111
+ TextContentBlock
112
+ | ImageContentBlock
113
+ | AudioContentBlock
114
+ | ResourceContentBlock
115
+ | EmbeddedResourceContentBlock
116
+ ],
117
+ session_id: str,
118
+ **kwargs: Any,
119
+ ) -> PromptResponse:
120
+ """Handle a prompt from the client.
121
+
122
+ This is where CodeOptiX performs quality engineering on the request.
123
+ """
124
+ if not self._conn:
125
+ return PromptResponse(stop_reason="error", error="Not connected to client")
126
+
127
+ # Extract text from prompt blocks
128
+ prompt_text = ""
129
+ for block in prompt:
130
+ if isinstance(block, dict):
131
+ text = block.get("text", "")
132
+ elif isinstance(block, TextContentBlock):
133
+ text = block.text
134
+ else:
135
+ text = getattr(block, "text", "")
136
+ if text:
137
+ prompt_text += text + "\n"
138
+
139
+ # Send initial response
140
+ await self._conn.session_update(
141
+ session_id=session_id,
142
+ update=update_agent_message(text_block("🔍 CodeOptiX: Analyzing code quality...")),
143
+ source="codeoptix",
144
+ )
145
+
146
+ # Extract code from prompt
147
+ code_blocks = extract_code_from_text(prompt_text)
148
+
149
+ # Perform quality evaluation
150
+ evaluation_results = None
151
+ if self._evaluation_engine and code_blocks:
152
+ try:
153
+ # Extract code content
154
+ code_content = "\n\n".join([cb["content"] for cb in code_blocks])
155
+
156
+ if code_content:
157
+ # Create agent output for evaluation
158
+ AgentOutput(
159
+ code=code_content,
160
+ tests="",
161
+ messages=[],
162
+ metadata={"source": "acp_agent", "prompt": prompt_text[:200]},
163
+ )
164
+
165
+ # Evaluate behaviors
166
+ evaluation_results = self._evaluation_engine.evaluate_behaviors(
167
+ behavior_names=self._behaviors,
168
+ context={"code": code_content, "prompt": prompt_text},
169
+ )
170
+ except Exception as e:
171
+ import logging
172
+
173
+ logger = logging.getLogger(__name__)
174
+ logger.error(f"Error during quality evaluation: {e}")
175
+ evaluation_results = {"error": str(e)}
176
+
177
+ # Format response
178
+ if evaluation_results and "behaviors" in evaluation_results:
179
+ response_lines = ["## 🔍 CodeOptiX Quality Report\n"]
180
+
181
+ overall_score = evaluation_results.get("overall_score", 0.0)
182
+ response_lines.append(f"**Overall Score:** {overall_score:.2%}\n")
183
+
184
+ behaviors = evaluation_results.get("behaviors", {})
185
+ for behavior_name, behavior_data in behaviors.items():
186
+ passed = behavior_data.get("passed", True)
187
+ score = behavior_data.get("score", 0.0)
188
+ emoji = "✅" if passed else "❌"
189
+
190
+ response_lines.append(f"{emoji} **{behavior_name}**: {score:.2%}")
191
+
192
+ if not passed and behavior_data.get("evidence"):
193
+ evidence = behavior_data["evidence"][:3] # Limit to 3 items
194
+ for ev in evidence:
195
+ response_lines.append(f" - {ev}")
196
+
197
+ response_text = "\n".join(response_lines)
198
+ elif code_blocks:
199
+ response_text = f"CodeOptiX analyzed {len(code_blocks)} code block(s).\n\n✅ Quality check complete."
200
+ else:
201
+ response_text = f"CodeOptiX received your request:\n\n{prompt_text[:500]}\n\n⚠️ No code blocks detected for quality evaluation."
202
+
203
+ await self._conn.session_update(
204
+ session_id=session_id,
205
+ update=update_agent_message(text_block(response_text)),
206
+ source="codeoptix",
207
+ )
208
+
209
+ return PromptResponse(stop_reason="end_turn")
@@ -0,0 +1,402 @@
1
+ """ACP Quality Bridge - CodeOptiX as quality middleware between editor and agents.
2
+
3
+ This implements the "Quality Bridge" pattern where CodeOptiX sits between
4
+ the editor and coding agents, automatically performing quality checks.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ from typing import Any
10
+
11
+ from acp import PROTOCOL_VERSION, Client, connect_to_agent, text_block, update_agent_message
12
+ from acp.core import ClientSideConnection
13
+ from acp.schema import (
14
+ AgentMessageChunk,
15
+ ClientCapabilities,
16
+ Implementation,
17
+ TextContentBlock,
18
+ )
19
+
20
+ from codeoptix.acp.code_extractor import extract_all_code, extract_code_from_message
21
+ from codeoptix.acp.registry import ACPAgentRegistry
22
+ from codeoptix.evaluation import EvaluationEngine
23
+ from codeoptix.utils.llm import LLMClient
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class ACPQualityBridge:
29
+ """CodeOptiX as a quality bridge between editor and agents via ACP."""
30
+
31
+ def __init__(
32
+ self,
33
+ agent_command: list[str] | None = None,
34
+ agent_name: str | None = None,
35
+ evaluation_engine: EvaluationEngine | None = None,
36
+ llm_client: LLMClient | None = None,
37
+ auto_eval: bool = True,
38
+ registry: ACPAgentRegistry | None = None,
39
+ behaviors: list[str] | None = None,
40
+ ):
41
+ """Initialize ACP quality bridge.
42
+
43
+ Args:
44
+ agent_command: Command to spawn the ACP agent (e.g., ["python", "agent.py"])
45
+ agent_name: Name of agent in registry (alternative to agent_command)
46
+ evaluation_engine: Optional evaluation engine
47
+ llm_client: Optional LLM client
48
+ auto_eval: Whether to automatically evaluate code quality
49
+ registry: Optional agent registry (for multi-agent support)
50
+ behaviors: List of behavior names to evaluate (default: all)
51
+ """
52
+ if not agent_command and not agent_name:
53
+ raise ValueError("Either agent_command or agent_name must be provided")
54
+
55
+ self.agent_command = agent_command
56
+ self.agent_name = agent_name
57
+ self.evaluation_engine = evaluation_engine
58
+ self.llm_client = llm_client
59
+ self.auto_eval = auto_eval
60
+ self.registry = registry
61
+ self.behaviors = behaviors or ["insecure-code", "vacuous-tests", "plan-drift"]
62
+ self._connection: ClientSideConnection | None = None
63
+ self._session_id: str | None = None
64
+ self._collected_updates: list[Any] = [] # Collect updates for code extraction
65
+
66
+ async def connect(self, cwd: str | None = None) -> None:
67
+ """Connect to the ACP agent."""
68
+ if self.agent_name and self.registry:
69
+ # Use registry to connect
70
+ self._connection = await self.registry.connect(self.agent_name)
71
+ self._session_id = self.registry.get_session_id(self.agent_name)
72
+ elif self.agent_command:
73
+ # Spawn agent process directly
74
+ process = await asyncio.create_subprocess_exec(
75
+ *self.agent_command,
76
+ stdin=asyncio.subprocess.PIPE,
77
+ stdout=asyncio.subprocess.PIPE,
78
+ cwd=cwd,
79
+ )
80
+
81
+ if process.stdin is None or process.stdout is None:
82
+ raise RuntimeError("Agent process does not expose stdio pipes")
83
+
84
+ # Create bridge client implementation
85
+ client_impl = _BridgeClientImpl(self)
86
+ self._connection = connect_to_agent(client_impl, process.stdin, process.stdout)
87
+
88
+ # Initialize connection
89
+ await self._connection.initialize(
90
+ protocol_version=PROTOCOL_VERSION,
91
+ client_capabilities=ClientCapabilities(),
92
+ client_info=Implementation(
93
+ name="codeoptix-bridge",
94
+ title="CodeOptiX Quality Bridge",
95
+ version="0.1.0",
96
+ ),
97
+ )
98
+
99
+ # Create new session
100
+ session = await self._connection.new_session(mcp_servers=[], cwd=cwd or ".")
101
+ self._session_id = session.session_id
102
+ else:
103
+ raise RuntimeError("No agent command or registry agent name provided")
104
+
105
+ async def prompt(
106
+ self,
107
+ prompt: str,
108
+ **kwargs: Any,
109
+ ) -> str:
110
+ """Send a prompt through the quality bridge."""
111
+ if not self._connection or not self._session_id:
112
+ raise RuntimeError("Not connected to ACP agent")
113
+
114
+ # Clear collected updates
115
+ self._collected_updates = []
116
+
117
+ # Send prompt to agent
118
+ response = await self._connection.prompt(
119
+ session_id=self._session_id,
120
+ prompt=[text_block(prompt)],
121
+ )
122
+
123
+ # Perform quality evaluation on response
124
+ if self.auto_eval and self.evaluation_engine and self._collected_updates:
125
+ await self._evaluate_and_feedback()
126
+
127
+ return response.stop_reason or "end_turn"
128
+
129
+ async def _evaluate_and_feedback(self) -> None:
130
+ """Evaluate collected code and send feedback to editor."""
131
+ if not self._connection or not self._session_id:
132
+ return
133
+
134
+ # Extract code from collected updates
135
+ code_blocks = extract_all_code(self._collected_updates)
136
+
137
+ if not code_blocks:
138
+ logger.debug("No code blocks found in agent response")
139
+ return
140
+
141
+ # Send evaluation status
142
+ await self._connection.session_update(
143
+ session_id=self._session_id,
144
+ update=update_agent_message(text_block("🔍 CodeOptiX: Evaluating code quality...")),
145
+ source="codeoptix",
146
+ )
147
+
148
+ try:
149
+ # Evaluate each code block
150
+ all_results = []
151
+ for code_block in code_blocks:
152
+ code_content = code_block.get("content", "")
153
+ if not code_content:
154
+ continue
155
+
156
+ # Evaluate code block (synchronous)
157
+ results = self.bridge._evaluate_code_block(code_content, code_block)
158
+
159
+ all_results.append(
160
+ {
161
+ "code_block": code_block,
162
+ "results": results,
163
+ }
164
+ )
165
+
166
+ # Format and send feedback
167
+ feedback = self._format_quality_feedback(all_results)
168
+ await self._connection.session_update(
169
+ session_id=self._session_id,
170
+ update=update_agent_message(text_block(feedback)),
171
+ source="codeoptix",
172
+ )
173
+
174
+ except Exception as e:
175
+ logger.error(f"Error during quality evaluation: {e}")
176
+ await self._connection.session_update(
177
+ session_id=self._session_id,
178
+ update=update_agent_message(text_block(f"⚠️ CodeOptiX: Evaluation error: {e!s}")),
179
+ source="codeoptix",
180
+ )
181
+
182
+ def _format_quality_feedback(self, results: list[dict[str, Any]]) -> str:
183
+ """Format quality evaluation results for display.
184
+
185
+ Args:
186
+ results: List of evaluation results
187
+
188
+ Returns:
189
+ Formatted feedback string
190
+ """
191
+ lines = ["## 🔍 CodeOptiX Quality Report\n"]
192
+
193
+ for i, result_data in enumerate(results, 1):
194
+ code_block = result_data["code_block"]
195
+ results_dict = result_data["results"]
196
+
197
+ lines.append(f"### Code Block {i} ({code_block.get('language', 'text')})\n")
198
+
199
+ if "behaviors" in results_dict:
200
+ for behavior_name, behavior_data in results_dict["behaviors"].items():
201
+ passed = behavior_data.get("passed", True)
202
+ score = behavior_data.get("score", 0.0)
203
+ emoji = "✅" if passed else "❌"
204
+
205
+ lines.append(f"{emoji} **{behavior_name}**: {score:.2%}")
206
+
207
+ if not passed and behavior_data.get("evidence"):
208
+ evidence = behavior_data["evidence"][:2] # Limit to 2 items
209
+ for ev in evidence:
210
+ lines.append(f" - {ev}")
211
+
212
+ lines.append("")
213
+
214
+ return "\n".join(lines)
215
+
216
+ async def close(self) -> None:
217
+ """Close the bridge connection."""
218
+ if self._connection:
219
+ self._connection = None
220
+ self._session_id = None
221
+
222
+
223
+ class _BridgeClientImpl(Client):
224
+ """Internal client implementation for quality bridge."""
225
+
226
+ def __init__(self, bridge: ACPQualityBridge):
227
+ """Initialize bridge client."""
228
+ self.bridge = bridge
229
+
230
+ async def request_permission(
231
+ self,
232
+ options: list,
233
+ session_id: str,
234
+ tool_call: Any,
235
+ **kwargs: Any,
236
+ ) -> Any:
237
+ """Handle permission requests."""
238
+ # Auto-approve for bridge
239
+ from acp.schema import RequestPermissionResponse
240
+
241
+ return RequestPermissionResponse(granted=True)
242
+
243
+ async def write_text_file(
244
+ self,
245
+ content: str,
246
+ path: str,
247
+ session_id: str,
248
+ **kwargs: Any,
249
+ ) -> Any:
250
+ """Handle file write requests."""
251
+ # Allow file writes, but could add quality checks here
252
+ from acp.schema import WriteTextFileResponse
253
+
254
+ return WriteTextFileResponse()
255
+
256
+ async def read_text_file(
257
+ self,
258
+ path: str,
259
+ session_id: str,
260
+ limit: int | None = None,
261
+ line: int | None = None,
262
+ **kwargs: Any,
263
+ ) -> Any:
264
+ """Handle file read requests."""
265
+ from acp.schema import ReadTextFileResponse
266
+
267
+ try:
268
+ with open(path, encoding="utf-8") as f:
269
+ if line is not None:
270
+ lines = f.readlines()
271
+ if 0 <= line < len(lines):
272
+ content = lines[line]
273
+ else:
274
+ content = ""
275
+ elif limit is not None:
276
+ content = f.read(limit)
277
+ else:
278
+ content = f.read()
279
+ return ReadTextFileResponse(content=content)
280
+ except Exception as e:
281
+ logger.error(f"Error reading file {path}: {e}")
282
+ raise
283
+
284
+ async def create_terminal(self, *args: Any, **kwargs: Any) -> Any:
285
+ """Handle terminal creation."""
286
+ from acp.exceptions import RequestError
287
+
288
+ raise RequestError.method_not_found("terminal/create")
289
+
290
+ async def terminal_output(self, *args: Any, **kwargs: Any) -> Any:
291
+ """Handle terminal output."""
292
+ from acp.exceptions import RequestError
293
+
294
+ raise RequestError.method_not_found("terminal/output")
295
+
296
+ async def release_terminal(self, *args: Any, **kwargs: Any) -> Any:
297
+ """Handle terminal release."""
298
+ from acp.exceptions import RequestError
299
+
300
+ raise RequestError.method_not_found("terminal/release")
301
+
302
+ async def wait_for_terminal_exit(self, *args: Any, **kwargs: Any) -> Any:
303
+ """Handle terminal exit wait."""
304
+ from acp.exceptions import RequestError
305
+
306
+ raise RequestError.method_not_found("terminal/wait_for_exit")
307
+
308
+ async def kill_terminal(self, *args: Any, **kwargs: Any) -> Any:
309
+ """Handle terminal kill."""
310
+ from acp.exceptions import RequestError
311
+
312
+ raise RequestError.method_not_found("terminal/kill")
313
+
314
+ async def session_update(
315
+ self,
316
+ session_id: str,
317
+ update: Any,
318
+ **kwargs: Any,
319
+ ) -> None:
320
+ """Handle session updates from agent."""
321
+ # Collect updates for code extraction and evaluation
322
+ self._collected_updates.append(update)
323
+
324
+ # Intercept agent messages for quality evaluation
325
+ if isinstance(update, AgentMessageChunk):
326
+ content = update.content
327
+ if isinstance(content, TextContentBlock):
328
+ logger.debug(f"Agent message: {content.text[:100]}...")
329
+
330
+ # Extract code immediately for real-time feedback
331
+ if self.bridge.auto_eval and self.bridge.evaluation_engine:
332
+ code_blocks = extract_code_from_message(update)
333
+ if code_blocks:
334
+ # Quick evaluation for real-time feedback
335
+ await self.bridge._quick_evaluate_code(code_blocks, session_id)
336
+
337
+ async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
338
+ """Handle extension methods."""
339
+ from acp.exceptions import RequestError
340
+
341
+ raise RequestError.method_not_found(method)
342
+
343
+ async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
344
+ """Handle extension notifications."""
345
+ logger.debug(f"Extension notification: {method}")
346
+
347
+ def _evaluate_code_block(self, code_content: str, code_block: dict[str, str]) -> dict[str, Any]:
348
+ """Evaluate a single code block.
349
+
350
+ Args:
351
+ code_content: Code content to evaluate
352
+ code_block: Code block metadata
353
+
354
+ Returns:
355
+ Evaluation results dictionary
356
+ """
357
+ if not self.evaluation_engine:
358
+ return {}
359
+
360
+ # Use evaluation engine's evaluate_behaviors (synchronous)
361
+ try:
362
+ results = self.evaluation_engine.evaluate_behaviors(
363
+ behavior_names=self.behaviors,
364
+ context={"code": code_content, "source": "acp_bridge", "code_block": code_block},
365
+ )
366
+ return results
367
+ except Exception as e:
368
+ logger.error(f"Error in code block evaluation: {e}")
369
+ return {"error": str(e)}
370
+
371
+ async def _quick_evaluate_code(
372
+ self, code_blocks: list[dict[str, str]], session_id: str
373
+ ) -> None:
374
+ """Perform quick evaluation on code blocks for real-time feedback.
375
+
376
+ Args:
377
+ code_blocks: List of extracted code blocks
378
+ session_id: ACP session ID
379
+ """
380
+ if not self._connection:
381
+ return
382
+
383
+ # Quick check for obvious issues (can be expanded)
384
+ for code_block in code_blocks:
385
+ code = code_block.get("content", "")
386
+ if not code:
387
+ continue
388
+
389
+ # Quick security check
390
+ security_keywords = ["password", "secret", "api_key", "token", "credential"]
391
+ if any(keyword in code.lower() for keyword in security_keywords):
392
+ await self._connection.session_update(
393
+ session_id=session_id,
394
+ update=update_agent_message(
395
+ text_block("⚠️ CodeOptiX: Potential security issue detected in code")
396
+ ),
397
+ source="codeoptix",
398
+ )
399
+
400
+ def on_connect(self, conn: Any) -> None:
401
+ """Called when client connects to agent."""
402
+ logger.debug("Bridge connected to ACP agent")