codexa 0.4.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 (189) hide show
  1. codexa-0.4.0.dist-info/METADATA +650 -0
  2. codexa-0.4.0.dist-info/RECORD +189 -0
  3. codexa-0.4.0.dist-info/WHEEL +5 -0
  4. codexa-0.4.0.dist-info/entry_points.txt +2 -0
  5. codexa-0.4.0.dist-info/licenses/LICENSE +21 -0
  6. codexa-0.4.0.dist-info/top_level.txt +1 -0
  7. semantic_code_intelligence/__init__.py +5 -0
  8. semantic_code_intelligence/analysis/__init__.py +21 -0
  9. semantic_code_intelligence/analysis/ai_features.py +351 -0
  10. semantic_code_intelligence/bridge/__init__.py +28 -0
  11. semantic_code_intelligence/bridge/context_provider.py +245 -0
  12. semantic_code_intelligence/bridge/protocol.py +167 -0
  13. semantic_code_intelligence/bridge/server.py +348 -0
  14. semantic_code_intelligence/bridge/vscode.py +271 -0
  15. semantic_code_intelligence/ci/__init__.py +13 -0
  16. semantic_code_intelligence/ci/hooks.py +98 -0
  17. semantic_code_intelligence/ci/hotspots.py +272 -0
  18. semantic_code_intelligence/ci/impact.py +246 -0
  19. semantic_code_intelligence/ci/metrics.py +591 -0
  20. semantic_code_intelligence/ci/pr.py +412 -0
  21. semantic_code_intelligence/ci/quality.py +557 -0
  22. semantic_code_intelligence/ci/templates.py +164 -0
  23. semantic_code_intelligence/ci/trace.py +224 -0
  24. semantic_code_intelligence/cli/__init__.py +0 -0
  25. semantic_code_intelligence/cli/commands/__init__.py +0 -0
  26. semantic_code_intelligence/cli/commands/ask_cmd.py +153 -0
  27. semantic_code_intelligence/cli/commands/benchmark_cmd.py +303 -0
  28. semantic_code_intelligence/cli/commands/chat_cmd.py +252 -0
  29. semantic_code_intelligence/cli/commands/ci_gen_cmd.py +74 -0
  30. semantic_code_intelligence/cli/commands/context_cmd.py +120 -0
  31. semantic_code_intelligence/cli/commands/cross_refactor_cmd.py +113 -0
  32. semantic_code_intelligence/cli/commands/deps_cmd.py +91 -0
  33. semantic_code_intelligence/cli/commands/docs_cmd.py +101 -0
  34. semantic_code_intelligence/cli/commands/doctor_cmd.py +147 -0
  35. semantic_code_intelligence/cli/commands/evolve_cmd.py +171 -0
  36. semantic_code_intelligence/cli/commands/explain_cmd.py +112 -0
  37. semantic_code_intelligence/cli/commands/gate_cmd.py +135 -0
  38. semantic_code_intelligence/cli/commands/grep_cmd.py +234 -0
  39. semantic_code_intelligence/cli/commands/hotspots_cmd.py +119 -0
  40. semantic_code_intelligence/cli/commands/impact_cmd.py +131 -0
  41. semantic_code_intelligence/cli/commands/index_cmd.py +138 -0
  42. semantic_code_intelligence/cli/commands/init_cmd.py +152 -0
  43. semantic_code_intelligence/cli/commands/investigate_cmd.py +163 -0
  44. semantic_code_intelligence/cli/commands/languages_cmd.py +101 -0
  45. semantic_code_intelligence/cli/commands/lsp_cmd.py +49 -0
  46. semantic_code_intelligence/cli/commands/mcp_cmd.py +50 -0
  47. semantic_code_intelligence/cli/commands/metrics_cmd.py +264 -0
  48. semantic_code_intelligence/cli/commands/models_cmd.py +157 -0
  49. semantic_code_intelligence/cli/commands/plugin_cmd.py +275 -0
  50. semantic_code_intelligence/cli/commands/pr_summary_cmd.py +178 -0
  51. semantic_code_intelligence/cli/commands/quality_cmd.py +208 -0
  52. semantic_code_intelligence/cli/commands/refactor_cmd.py +103 -0
  53. semantic_code_intelligence/cli/commands/review_cmd.py +88 -0
  54. semantic_code_intelligence/cli/commands/search_cmd.py +236 -0
  55. semantic_code_intelligence/cli/commands/serve_cmd.py +117 -0
  56. semantic_code_intelligence/cli/commands/suggest_cmd.py +100 -0
  57. semantic_code_intelligence/cli/commands/summary_cmd.py +78 -0
  58. semantic_code_intelligence/cli/commands/tool_cmd.py +282 -0
  59. semantic_code_intelligence/cli/commands/trace_cmd.py +123 -0
  60. semantic_code_intelligence/cli/commands/tui_cmd.py +58 -0
  61. semantic_code_intelligence/cli/commands/viz_cmd.py +127 -0
  62. semantic_code_intelligence/cli/commands/watch_cmd.py +72 -0
  63. semantic_code_intelligence/cli/commands/web_cmd.py +61 -0
  64. semantic_code_intelligence/cli/commands/workspace_cmd.py +250 -0
  65. semantic_code_intelligence/cli/main.py +65 -0
  66. semantic_code_intelligence/cli/router.py +92 -0
  67. semantic_code_intelligence/config/__init__.py +0 -0
  68. semantic_code_intelligence/config/settings.py +260 -0
  69. semantic_code_intelligence/context/__init__.py +19 -0
  70. semantic_code_intelligence/context/engine.py +429 -0
  71. semantic_code_intelligence/context/memory.py +253 -0
  72. semantic_code_intelligence/daemon/__init__.py +1 -0
  73. semantic_code_intelligence/daemon/watcher.py +515 -0
  74. semantic_code_intelligence/docs/__init__.py +1080 -0
  75. semantic_code_intelligence/embeddings/__init__.py +0 -0
  76. semantic_code_intelligence/embeddings/enhanced.py +131 -0
  77. semantic_code_intelligence/embeddings/generator.py +149 -0
  78. semantic_code_intelligence/embeddings/model_registry.py +100 -0
  79. semantic_code_intelligence/evolution/__init__.py +1 -0
  80. semantic_code_intelligence/evolution/budget_guard.py +111 -0
  81. semantic_code_intelligence/evolution/commit_manager.py +88 -0
  82. semantic_code_intelligence/evolution/context_builder.py +131 -0
  83. semantic_code_intelligence/evolution/engine.py +249 -0
  84. semantic_code_intelligence/evolution/patch_generator.py +229 -0
  85. semantic_code_intelligence/evolution/task_selector.py +214 -0
  86. semantic_code_intelligence/evolution/test_runner.py +111 -0
  87. semantic_code_intelligence/indexing/__init__.py +0 -0
  88. semantic_code_intelligence/indexing/chunker.py +174 -0
  89. semantic_code_intelligence/indexing/parallel.py +86 -0
  90. semantic_code_intelligence/indexing/scanner.py +146 -0
  91. semantic_code_intelligence/indexing/semantic_chunker.py +337 -0
  92. semantic_code_intelligence/llm/__init__.py +62 -0
  93. semantic_code_intelligence/llm/cache.py +219 -0
  94. semantic_code_intelligence/llm/cached_provider.py +145 -0
  95. semantic_code_intelligence/llm/conversation.py +190 -0
  96. semantic_code_intelligence/llm/cross_refactor.py +272 -0
  97. semantic_code_intelligence/llm/investigation.py +274 -0
  98. semantic_code_intelligence/llm/mock_provider.py +77 -0
  99. semantic_code_intelligence/llm/ollama_provider.py +122 -0
  100. semantic_code_intelligence/llm/openai_provider.py +100 -0
  101. semantic_code_intelligence/llm/provider.py +92 -0
  102. semantic_code_intelligence/llm/rate_limiter.py +164 -0
  103. semantic_code_intelligence/llm/reasoning.py +438 -0
  104. semantic_code_intelligence/llm/safety.py +110 -0
  105. semantic_code_intelligence/llm/streaming.py +251 -0
  106. semantic_code_intelligence/lsp/__init__.py +609 -0
  107. semantic_code_intelligence/mcp/__init__.py +393 -0
  108. semantic_code_intelligence/parsing/__init__.py +19 -0
  109. semantic_code_intelligence/parsing/parser.py +375 -0
  110. semantic_code_intelligence/plugins/__init__.py +255 -0
  111. semantic_code_intelligence/plugins/examples/__init__.py +1 -0
  112. semantic_code_intelligence/plugins/examples/code_quality.py +73 -0
  113. semantic_code_intelligence/plugins/examples/search_annotator.py +56 -0
  114. semantic_code_intelligence/scalability/__init__.py +205 -0
  115. semantic_code_intelligence/search/__init__.py +0 -0
  116. semantic_code_intelligence/search/formatter.py +123 -0
  117. semantic_code_intelligence/search/grep.py +361 -0
  118. semantic_code_intelligence/search/hybrid_search.py +170 -0
  119. semantic_code_intelligence/search/keyword_search.py +311 -0
  120. semantic_code_intelligence/search/section_expander.py +103 -0
  121. semantic_code_intelligence/services/__init__.py +0 -0
  122. semantic_code_intelligence/services/indexing_service.py +630 -0
  123. semantic_code_intelligence/services/search_service.py +269 -0
  124. semantic_code_intelligence/storage/__init__.py +0 -0
  125. semantic_code_intelligence/storage/chunk_hash_store.py +86 -0
  126. semantic_code_intelligence/storage/hash_store.py +66 -0
  127. semantic_code_intelligence/storage/index_manifest.py +85 -0
  128. semantic_code_intelligence/storage/index_stats.py +138 -0
  129. semantic_code_intelligence/storage/query_history.py +160 -0
  130. semantic_code_intelligence/storage/symbol_registry.py +209 -0
  131. semantic_code_intelligence/storage/vector_store.py +297 -0
  132. semantic_code_intelligence/tests/__init__.py +0 -0
  133. semantic_code_intelligence/tests/test_ai_features.py +351 -0
  134. semantic_code_intelligence/tests/test_chunker.py +119 -0
  135. semantic_code_intelligence/tests/test_cli.py +188 -0
  136. semantic_code_intelligence/tests/test_config.py +154 -0
  137. semantic_code_intelligence/tests/test_context.py +381 -0
  138. semantic_code_intelligence/tests/test_embeddings.py +73 -0
  139. semantic_code_intelligence/tests/test_endtoend.py +1142 -0
  140. semantic_code_intelligence/tests/test_enhanced_embeddings.py +92 -0
  141. semantic_code_intelligence/tests/test_hash_store.py +79 -0
  142. semantic_code_intelligence/tests/test_logging.py +55 -0
  143. semantic_code_intelligence/tests/test_new_cli.py +138 -0
  144. semantic_code_intelligence/tests/test_parser.py +495 -0
  145. semantic_code_intelligence/tests/test_phase10.py +355 -0
  146. semantic_code_intelligence/tests/test_phase11.py +593 -0
  147. semantic_code_intelligence/tests/test_phase12.py +375 -0
  148. semantic_code_intelligence/tests/test_phase13.py +663 -0
  149. semantic_code_intelligence/tests/test_phase14.py +568 -0
  150. semantic_code_intelligence/tests/test_phase15.py +814 -0
  151. semantic_code_intelligence/tests/test_phase16.py +792 -0
  152. semantic_code_intelligence/tests/test_phase17.py +815 -0
  153. semantic_code_intelligence/tests/test_phase18.py +934 -0
  154. semantic_code_intelligence/tests/test_phase19.py +986 -0
  155. semantic_code_intelligence/tests/test_phase20.py +2753 -0
  156. semantic_code_intelligence/tests/test_phase20b.py +2058 -0
  157. semantic_code_intelligence/tests/test_phase20c.py +962 -0
  158. semantic_code_intelligence/tests/test_phase21.py +428 -0
  159. semantic_code_intelligence/tests/test_phase22.py +799 -0
  160. semantic_code_intelligence/tests/test_phase23.py +783 -0
  161. semantic_code_intelligence/tests/test_phase24.py +715 -0
  162. semantic_code_intelligence/tests/test_phase25.py +496 -0
  163. semantic_code_intelligence/tests/test_phase26.py +251 -0
  164. semantic_code_intelligence/tests/test_phase27.py +531 -0
  165. semantic_code_intelligence/tests/test_phase8.py +592 -0
  166. semantic_code_intelligence/tests/test_phase9.py +643 -0
  167. semantic_code_intelligence/tests/test_plugins.py +293 -0
  168. semantic_code_intelligence/tests/test_priority_features.py +727 -0
  169. semantic_code_intelligence/tests/test_router.py +41 -0
  170. semantic_code_intelligence/tests/test_scalability.py +138 -0
  171. semantic_code_intelligence/tests/test_scanner.py +125 -0
  172. semantic_code_intelligence/tests/test_search.py +160 -0
  173. semantic_code_intelligence/tests/test_semantic_chunker.py +255 -0
  174. semantic_code_intelligence/tests/test_tools.py +182 -0
  175. semantic_code_intelligence/tests/test_vector_store.py +151 -0
  176. semantic_code_intelligence/tests/test_watcher.py +211 -0
  177. semantic_code_intelligence/tools/__init__.py +442 -0
  178. semantic_code_intelligence/tools/executor.py +232 -0
  179. semantic_code_intelligence/tools/protocol.py +200 -0
  180. semantic_code_intelligence/tui/__init__.py +454 -0
  181. semantic_code_intelligence/utils/__init__.py +0 -0
  182. semantic_code_intelligence/utils/logging.py +112 -0
  183. semantic_code_intelligence/version.py +3 -0
  184. semantic_code_intelligence/web/__init__.py +11 -0
  185. semantic_code_intelligence/web/api.py +289 -0
  186. semantic_code_intelligence/web/server.py +397 -0
  187. semantic_code_intelligence/web/ui.py +659 -0
  188. semantic_code_intelligence/web/visualize.py +226 -0
  189. semantic_code_intelligence/workspace/__init__.py +427 -0
@@ -0,0 +1,245 @@
1
+ """Context provider — structured context generation for external AI pipelines.
2
+
3
+ The ``ContextProvider`` is the heart of CodexA's cooperation model. It
4
+ wraps the existing semantic search, symbol analysis, and dependency tools
5
+ into a single entry point that external AI assistants can call to enrich
6
+ their prompts with deep repository knowledge.
7
+
8
+ All public methods return plain ``dict`` objects ready for JSON
9
+ serialisation, so they can be consumed by any agent that speaks JSON —
10
+ no CodexA-specific types leak across the boundary.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import time
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from semantic_code_intelligence.analysis.ai_features import (
20
+ explain_file,
21
+ explain_symbol,
22
+ generate_ai_context,
23
+ summarize_repository,
24
+ )
25
+ from semantic_code_intelligence.context.engine import (
26
+ CallGraph,
27
+ ContextBuilder,
28
+ DependencyMap,
29
+ )
30
+ from semantic_code_intelligence.llm.safety import SafetyValidator
31
+ from semantic_code_intelligence.services.search_service import search_codebase
32
+ from semantic_code_intelligence.utils.logging import get_logger
33
+
34
+ logger = get_logger("bridge.context")
35
+
36
+
37
+ class ContextProvider:
38
+ """Generates structured context for consumption by external AI agents.
39
+
40
+ Usage::
41
+
42
+ provider = ContextProvider(Path("/my/project"))
43
+ ctx = provider.context_for_symbol("authenticate")
44
+ # ctx is a plain dict ready to be injected into an LLM prompt
45
+
46
+ All expensive work (scanning, parsing) is done lazily on first access.
47
+ """
48
+
49
+ def __init__(self, project_root: Path) -> None:
50
+ self._root = project_root.resolve()
51
+ self._builder: ContextBuilder | None = None
52
+ self._indexed = False
53
+ self._validator = SafetyValidator()
54
+
55
+ # --- lazy indexing -------------------------------------------------
56
+
57
+ def _ensure_indexed(self) -> ContextBuilder:
58
+ if self._builder is not None and self._indexed:
59
+ return self._builder
60
+
61
+ from semantic_code_intelligence.config.settings import load_config
62
+ from semantic_code_intelligence.indexing.scanner import scan_repository
63
+
64
+ self._builder = ContextBuilder()
65
+ config = load_config(self._root)
66
+ scanned = scan_repository(self._root, config.index)
67
+ for sf in scanned:
68
+ full = str(self._root / sf.relative_path)
69
+ try:
70
+ self._builder.index_file(full)
71
+ except Exception:
72
+ logger.debug("Skip %s", full)
73
+ self._indexed = True
74
+ return self._builder
75
+
76
+ # --- public context generators ----------------------------------
77
+
78
+ def context_for_query(
79
+ self,
80
+ query: str,
81
+ *,
82
+ top_k: int = 5,
83
+ threshold: float = 0.2,
84
+ include_repo_summary: bool = False,
85
+ ) -> dict[str, Any]:
86
+ """Return semantic-search context for a natural-language query.
87
+
88
+ Designed to be injected as additional context into an LLM prompt
89
+ by an external AI assistant.
90
+ """
91
+ snippets: list[dict[str, Any]] = []
92
+ try:
93
+ results = search_codebase(query, self._root, top_k=top_k, threshold=threshold)
94
+ snippets = [r.to_dict() for r in results]
95
+ except Exception:
96
+ logger.debug("Search unavailable — returning empty snippets.")
97
+
98
+ resp: dict[str, Any] = {
99
+ "query": query,
100
+ "snippet_count": len(snippets),
101
+ "snippets": snippets,
102
+ }
103
+
104
+ if include_repo_summary:
105
+ builder = self._ensure_indexed()
106
+ resp["repo_summary"] = summarize_repository(builder).to_dict()
107
+
108
+ return resp
109
+
110
+ def context_for_symbol(
111
+ self,
112
+ symbol_name: str,
113
+ *,
114
+ file_path: str | None = None,
115
+ include_call_graph: bool = True,
116
+ include_dependencies: bool = True,
117
+ ) -> dict[str, Any]:
118
+ """Return rich context around a symbol for external AI consumption.
119
+
120
+ Includes the symbol's definition, related symbols, callers/callees,
121
+ and dependency information.
122
+ """
123
+ builder = self._ensure_indexed()
124
+
125
+ if file_path:
126
+ builder.index_file(file_path)
127
+
128
+ matches = builder.find_symbol(symbol_name)
129
+ if not matches:
130
+ return {"symbol_name": symbol_name, "found": False}
131
+
132
+ explanations = [explain_symbol(s, builder).to_dict() for s in matches[:5]]
133
+ context_windows = [
134
+ builder.build_context(s).to_dict() for s in matches[:5]
135
+ ]
136
+
137
+ result: dict[str, Any] = {
138
+ "symbol_name": symbol_name,
139
+ "found": True,
140
+ "match_count": len(matches),
141
+ "explanations": explanations,
142
+ "context_windows": context_windows,
143
+ }
144
+
145
+ if include_call_graph:
146
+ all_syms = builder.get_all_symbols()
147
+ graph = CallGraph()
148
+ graph.build(all_syms)
149
+ callers = [e.to_dict() for e in graph.callers_of(symbol_name)]
150
+ callees: list[dict[str, Any]] = []
151
+ for edge in graph.edges:
152
+ if edge.caller.endswith(f":{symbol_name}"):
153
+ callees.append(edge.to_dict())
154
+ result["call_graph"] = {"callers": callers, "callees": callees}
155
+
156
+ if include_dependencies:
157
+ for s in matches[:1]:
158
+ dep_map = DependencyMap()
159
+ if s.file_path in builder._file_contents:
160
+ dep_map.add_file(s.file_path, builder._file_contents[s.file_path])
161
+ result["dependencies"] = dep_map.to_dict()
162
+
163
+ return result
164
+
165
+ def context_for_file(self, file_path: str) -> dict[str, Any]:
166
+ """Return structured context for an entire file."""
167
+ builder = self._ensure_indexed()
168
+ builder.index_file(file_path)
169
+
170
+ symbols = builder.get_symbols(file_path)
171
+ explanations = explain_file(file_path)
172
+
173
+ dep_map = DependencyMap()
174
+ if file_path in builder._file_contents:
175
+ dep_map.add_file(file_path, builder._file_contents[file_path])
176
+
177
+ return {
178
+ "file_path": file_path,
179
+ "symbol_count": len(symbols),
180
+ "symbols": [s.to_dict() for s in symbols],
181
+ "explanations": [e.to_dict() for e in explanations],
182
+ "dependencies": dep_map.to_dict(),
183
+ }
184
+
185
+ def context_for_repo(self) -> dict[str, Any]:
186
+ """Return a repository-level summary."""
187
+ builder = self._ensure_indexed()
188
+ summary = summarize_repository(builder)
189
+ return summary.to_dict()
190
+
191
+ def validate_code(self, code: str) -> dict[str, Any]:
192
+ """Validate code (e.g. AI-generated) for safety issues."""
193
+ report = self._validator.validate(code)
194
+ return report.to_dict()
195
+
196
+ def get_dependencies(self, file_path: str) -> dict[str, Any]:
197
+ """Return file-level dependency map."""
198
+ builder = self._ensure_indexed()
199
+ # Resolve relative paths against project root
200
+ resolved = Path(file_path)
201
+ if not resolved.is_absolute():
202
+ resolved = self._root / resolved
203
+ resolved_str = str(resolved)
204
+ builder.index_file(resolved_str)
205
+ dep_map = DependencyMap()
206
+ if resolved_str in builder._file_contents:
207
+ dep_map.add_file(resolved_str, builder._file_contents[resolved_str])
208
+ return {"file_path": file_path, "dependencies": dep_map.to_dict()}
209
+
210
+ def get_call_graph(self, symbol_name: str) -> dict[str, Any]:
211
+ """Return callers and callees for a symbol."""
212
+ builder = self._ensure_indexed()
213
+ all_syms = builder.get_all_symbols()
214
+ graph = CallGraph()
215
+ graph.build(all_syms)
216
+ callers = [e.to_dict() for e in graph.callers_of(symbol_name)]
217
+ callees: list[dict[str, Any]] = []
218
+ for edge in graph.edges:
219
+ if edge.caller.endswith(f":{symbol_name}"):
220
+ callees.append(edge.to_dict())
221
+ edges = callers + callees
222
+ return {
223
+ "symbol_name": symbol_name,
224
+ "callers": callers,
225
+ "callees": callees,
226
+ "edges": edges,
227
+ }
228
+
229
+ def find_references(self, symbol_name: str) -> dict[str, Any]:
230
+ """Find all references to a symbol."""
231
+ builder = self._ensure_indexed()
232
+ all_syms = builder.get_all_symbols()
233
+ refs: list[dict[str, Any]] = []
234
+ for sym in all_syms:
235
+ if sym.name == symbol_name:
236
+ refs.append(sym.to_dict())
237
+ elif symbol_name in sym.body:
238
+ refs.append({
239
+ "referencing_symbol": sym.name,
240
+ "kind": sym.kind,
241
+ "file_path": sym.file_path,
242
+ "start_line": sym.start_line,
243
+ "end_line": sym.end_line,
244
+ })
245
+ return {"symbol_name": symbol_name, "reference_count": len(refs), "references": refs}
@@ -0,0 +1,167 @@
1
+ """Agent cooperation protocol — request/response types for external AI systems.
2
+
3
+ Defines a model-neutral JSON protocol that any IDE AI assistant (Copilot,
4
+ Cursor, Continue, etc.) can use to request context from CodexA and receive
5
+ structured responses.
6
+
7
+ The protocol is intentionally simple and stateless — every request carries
8
+ the information needed to produce a response. This keeps the bridge
9
+ lightweight and easy to integrate with any tool that speaks JSON.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import time
16
+ from dataclasses import dataclass, field
17
+ from enum import Enum
18
+ from typing import Any
19
+
20
+
21
+ # ---------------------------------------------------------------------------
22
+ # Enums
23
+ # ---------------------------------------------------------------------------
24
+
25
+ class RequestKind(str, Enum):
26
+ """Types of requests the bridge can handle."""
27
+
28
+ SEMANTIC_SEARCH = "semantic_search"
29
+ EXPLAIN_SYMBOL = "explain_symbol"
30
+ EXPLAIN_FILE = "explain_file"
31
+ GET_CONTEXT = "get_context"
32
+ GET_DEPENDENCIES = "get_dependencies"
33
+ GET_CALL_GRAPH = "get_call_graph"
34
+ SUMMARIZE_REPO = "summarize_repo"
35
+ FIND_REFERENCES = "find_references"
36
+ VALIDATE_CODE = "validate_code"
37
+ LIST_CAPABILITIES = "list_capabilities"
38
+
39
+ # Phase 19 — AI Agent Tooling Protocol
40
+ INVOKE_TOOL = "invoke_tool"
41
+ LIST_TOOLS = "list_tools"
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Request / Response
46
+ # ---------------------------------------------------------------------------
47
+
48
+ @dataclass
49
+ class AgentRequest:
50
+ """Incoming request from an external AI agent or IDE extension.
51
+
52
+ Attributes:
53
+ kind: The type of operation requested.
54
+ params: Operation-specific parameters (key-value).
55
+ request_id: Optional caller-assigned ID for correlation.
56
+ source: Optional identifier for the calling agent (e.g. "copilot").
57
+ """
58
+
59
+ kind: str
60
+ params: dict[str, Any] = field(default_factory=dict)
61
+ request_id: str = ""
62
+ source: str = ""
63
+
64
+ def to_dict(self) -> dict[str, Any]:
65
+ """Serialize the request to a plain dictionary."""
66
+ return {
67
+ "kind": self.kind,
68
+ "params": self.params,
69
+ "request_id": self.request_id,
70
+ "source": self.source,
71
+ }
72
+
73
+ def to_json(self) -> str:
74
+ """Serialize the request to a JSON string."""
75
+ return json.dumps(self.to_dict())
76
+
77
+ @classmethod
78
+ def from_dict(cls, data: dict[str, Any]) -> "AgentRequest":
79
+ """Create an AgentRequest from a dictionary."""
80
+ return cls(
81
+ kind=data.get("kind", ""),
82
+ params=data.get("params", {}),
83
+ request_id=data.get("request_id", ""),
84
+ source=data.get("source", ""),
85
+ )
86
+
87
+ @classmethod
88
+ def from_json(cls, text: str) -> "AgentRequest":
89
+ """Create an AgentRequest by parsing a JSON string."""
90
+ return cls.from_dict(json.loads(text))
91
+
92
+
93
+ @dataclass
94
+ class AgentResponse:
95
+ """Outgoing response to an external AI agent or IDE extension.
96
+
97
+ Attributes:
98
+ success: Whether the request was handled without error.
99
+ data: The structured response payload.
100
+ error: Human-readable error message if success is False.
101
+ request_id: Echoes the caller's request_id for correlation.
102
+ elapsed_ms: Time taken to process the request.
103
+ """
104
+
105
+ success: bool
106
+ data: dict[str, Any] = field(default_factory=dict)
107
+ error: str = ""
108
+ request_id: str = ""
109
+ elapsed_ms: float = 0.0
110
+
111
+ def to_dict(self) -> dict[str, Any]:
112
+ """Serialize the response to a plain dictionary."""
113
+ result: dict[str, Any] = {
114
+ "success": self.success,
115
+ "request_id": self.request_id,
116
+ "elapsed_ms": round(self.elapsed_ms, 2),
117
+ }
118
+ if self.success:
119
+ result["data"] = self.data
120
+ else:
121
+ result["error"] = self.error
122
+ return result
123
+
124
+ def to_json(self, indent: int | None = None) -> str:
125
+ """Serialize the response to a JSON string."""
126
+ return json.dumps(self.to_dict(), indent=indent)
127
+
128
+
129
+ # ---------------------------------------------------------------------------
130
+ # Capabilities manifest
131
+ # ---------------------------------------------------------------------------
132
+
133
+ @dataclass
134
+ class BridgeCapabilities:
135
+ """Advertises what CodexA can do to external consumers.
136
+
137
+ Returned in response to ``list_capabilities`` requests and also
138
+ served at ``GET /`` by the bridge server.
139
+ """
140
+
141
+ version: str = "0.9.0"
142
+ name: str = "CodexA Bridge"
143
+ description: str = (
144
+ "Semantic code intelligence provider — context, search, "
145
+ "explanation, dependency analysis, and safety validation "
146
+ "for external AI coding assistants."
147
+ )
148
+ supported_requests: list[str] = field(
149
+ default_factory=lambda: [k.value for k in RequestKind]
150
+ )
151
+ tools: list[dict[str, Any]] = field(default_factory=list)
152
+
153
+ def to_dict(self) -> dict[str, Any]:
154
+ """Serialize the capabilities manifest to a plain dictionary."""
155
+ result: dict[str, Any] = {
156
+ "version": self.version,
157
+ "name": self.name,
158
+ "description": self.description,
159
+ "supported_requests": self.supported_requests,
160
+ }
161
+ if self.tools:
162
+ result["tools"] = self.tools
163
+ return result
164
+
165
+ def to_json(self, indent: int = 2) -> str:
166
+ """Serialize the capabilities manifest to a JSON string."""
167
+ return json.dumps(self.to_dict(), indent=indent)