axion-code 1.0.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 (82) hide show
  1. axion/__init__.py +3 -0
  2. axion/api/__init__.py +0 -0
  3. axion/api/anthropic.py +460 -0
  4. axion/api/client.py +259 -0
  5. axion/api/error.py +161 -0
  6. axion/api/ollama.py +597 -0
  7. axion/api/openai_compat.py +805 -0
  8. axion/api/openai_responses.py +627 -0
  9. axion/api/prompt_cache.py +31 -0
  10. axion/api/sse.py +98 -0
  11. axion/api/types.py +451 -0
  12. axion/cli/__init__.py +0 -0
  13. axion/cli/init_cmd.py +50 -0
  14. axion/cli/input.py +290 -0
  15. axion/cli/main.py +2953 -0
  16. axion/cli/render.py +489 -0
  17. axion/cli/tui.py +766 -0
  18. axion/commands/__init__.py +0 -0
  19. axion/commands/handlers/__init__.py +0 -0
  20. axion/commands/handlers/agents.py +51 -0
  21. axion/commands/handlers/builtin_commands.py +367 -0
  22. axion/commands/handlers/mcp.py +59 -0
  23. axion/commands/handlers/models.py +75 -0
  24. axion/commands/handlers/plugins.py +55 -0
  25. axion/commands/handlers/skills.py +61 -0
  26. axion/commands/parsing.py +317 -0
  27. axion/commands/registry.py +166 -0
  28. axion/compat_harness/__init__.py +0 -0
  29. axion/compat_harness/extractor.py +145 -0
  30. axion/plugins/__init__.py +0 -0
  31. axion/plugins/hooks.py +22 -0
  32. axion/plugins/manager.py +391 -0
  33. axion/plugins/manifest.py +270 -0
  34. axion/runtime/__init__.py +0 -0
  35. axion/runtime/bash.py +388 -0
  36. axion/runtime/bootstrap.py +39 -0
  37. axion/runtime/claude_subscription.py +300 -0
  38. axion/runtime/compact.py +233 -0
  39. axion/runtime/config.py +397 -0
  40. axion/runtime/conversation.py +1073 -0
  41. axion/runtime/file_ops.py +613 -0
  42. axion/runtime/git.py +213 -0
  43. axion/runtime/hooks.py +235 -0
  44. axion/runtime/image.py +212 -0
  45. axion/runtime/lanes.py +282 -0
  46. axion/runtime/lsp.py +425 -0
  47. axion/runtime/mcp/__init__.py +0 -0
  48. axion/runtime/mcp/client.py +76 -0
  49. axion/runtime/mcp/lifecycle.py +96 -0
  50. axion/runtime/mcp/stdio.py +318 -0
  51. axion/runtime/mcp/tool_bridge.py +79 -0
  52. axion/runtime/memory.py +196 -0
  53. axion/runtime/oauth.py +329 -0
  54. axion/runtime/openai_subscription.py +346 -0
  55. axion/runtime/permissions.py +247 -0
  56. axion/runtime/plan_mode.py +96 -0
  57. axion/runtime/policy_engine.py +259 -0
  58. axion/runtime/prompt.py +586 -0
  59. axion/runtime/recovery.py +261 -0
  60. axion/runtime/remote.py +28 -0
  61. axion/runtime/sandbox.py +68 -0
  62. axion/runtime/scheduler.py +231 -0
  63. axion/runtime/session.py +365 -0
  64. axion/runtime/sharing.py +159 -0
  65. axion/runtime/skills.py +124 -0
  66. axion/runtime/tasks.py +258 -0
  67. axion/runtime/usage.py +241 -0
  68. axion/runtime/workers.py +186 -0
  69. axion/telemetry/__init__.py +0 -0
  70. axion/telemetry/events.py +67 -0
  71. axion/telemetry/profile.py +49 -0
  72. axion/telemetry/sink.py +60 -0
  73. axion/telemetry/tracer.py +95 -0
  74. axion/tools/__init__.py +0 -0
  75. axion/tools/lane_completion.py +33 -0
  76. axion/tools/registry.py +853 -0
  77. axion/tools/tool_search.py +226 -0
  78. axion_code-1.0.0.dist-info/METADATA +709 -0
  79. axion_code-1.0.0.dist-info/RECORD +82 -0
  80. axion_code-1.0.0.dist-info/WHEEL +4 -0
  81. axion_code-1.0.0.dist-info/entry_points.txt +2 -0
  82. axion_code-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,226 @@
1
+ """ToolSearch - deferred tool schema loading.
2
+
3
+ When tools are too numerous to include in every request, ToolSearch
4
+ lets the model discover tools by keyword, then fetch their full schemas
5
+ on demand. This keeps the context window lean while still exposing a
6
+ large tool surface.
7
+
8
+ Usage by the model:
9
+ 1. Call ToolSearch with a query like "notebook" or "select:Read,Edit"
10
+ 2. Get back matching tool names + descriptions
11
+ 3. Call ToolSearch with "select:ToolName" to fetch full schema
12
+ 4. Now the tool can be invoked normally
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass, field
18
+ from typing import Any
19
+
20
+ from axion.tools.registry import GlobalToolRegistry, ToolSpec, get_tool_registry
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Types
24
+ # ---------------------------------------------------------------------------
25
+
26
+ @dataclass
27
+ class ToolSearchResult:
28
+ """A single tool match from a search."""
29
+
30
+ name: str
31
+ description: str
32
+ score: float = 0.0
33
+
34
+
35
+ @dataclass
36
+ class ToolSearchOutput:
37
+ """Output from a ToolSearch query."""
38
+
39
+ query: str
40
+ results: list[ToolSearchResult] = field(default_factory=list)
41
+ schemas: list[dict[str, Any]] = field(default_factory=list)
42
+ message: str = ""
43
+
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Deferred tool registry
47
+ # ---------------------------------------------------------------------------
48
+
49
+ class DeferredToolRegistry:
50
+ """Manages tools that are loaded on demand.
51
+
52
+ Tools can be registered as "deferred" — only their name and description
53
+ are included in the system prompt. Their full schema is fetched via
54
+ ToolSearch when the model needs them.
55
+ """
56
+
57
+ def __init__(self, full_registry: GlobalToolRegistry | None = None) -> None:
58
+ self._full_registry = full_registry or get_tool_registry()
59
+ self._deferred: dict[str, ToolSpec] = {}
60
+ self._active: set[str] = set() # Tools whose schemas are active
61
+
62
+ def defer_tool(self, name: str) -> None:
63
+ """Mark a tool as deferred (schema not included by default)."""
64
+ tool = self._full_registry.get(name)
65
+ if tool:
66
+ self._deferred[name] = tool.spec
67
+ self._active.discard(name)
68
+
69
+ def activate_tool(self, name: str) -> ToolSpec | None:
70
+ """Activate a deferred tool (include its schema)."""
71
+ if name in self._deferred:
72
+ self._active.add(name)
73
+ return self._deferred[name]
74
+ # Also check full registry
75
+ tool = self._full_registry.get(name)
76
+ if tool:
77
+ self._active.add(name)
78
+ return tool.spec
79
+ return None
80
+
81
+ def deferred_tool_names(self) -> list[str]:
82
+ """Get names of tools that are deferred but not yet active."""
83
+ return [n for n in self._deferred if n not in self._active]
84
+
85
+ def active_tool_names(self) -> list[str]:
86
+ """Get names of tools whose schemas are currently active."""
87
+ return list(self._active)
88
+
89
+ def is_active(self, name: str) -> bool:
90
+ return name in self._active
91
+
92
+
93
+ # ---------------------------------------------------------------------------
94
+ # Search implementation
95
+ # ---------------------------------------------------------------------------
96
+
97
+ def tool_search(
98
+ query: str,
99
+ registry: GlobalToolRegistry | None = None,
100
+ max_results: int = 5,
101
+ ) -> ToolSearchOutput:
102
+ """Search for tools by query.
103
+
104
+ Query formats:
105
+ - "select:Read,Edit,Grep" — fetch exact tools by name (returns full schemas)
106
+ - "notebook jupyter" — keyword search, returns top matches by relevance
107
+ - "+slack send" — require "slack" in name, rank by remaining terms
108
+ """
109
+ reg = registry or get_tool_registry()
110
+ all_tools = reg.all_tools()
111
+
112
+ # Parse query
113
+ query = query.strip()
114
+
115
+ # Direct selection: "select:Tool1,Tool2"
116
+ if query.lower().startswith("select:"):
117
+ names = [n.strip() for n in query[7:].split(",") if n.strip()]
118
+ return _select_tools(names, reg)
119
+
120
+ # Required name prefix: "+prefix keyword"
121
+ required_prefix = None
122
+ search_terms = query.lower().split()
123
+ if search_terms and search_terms[0].startswith("+"):
124
+ required_prefix = search_terms[0][1:]
125
+ search_terms = search_terms[1:]
126
+
127
+ # Keyword search
128
+ results: list[ToolSearchResult] = []
129
+
130
+ for tool_def in all_tools:
131
+ spec = tool_def.spec
132
+ name_lower = spec.name.lower()
133
+ desc_lower = spec.description.lower()
134
+
135
+ # Required prefix filter
136
+ if required_prefix and required_prefix not in name_lower:
137
+ continue
138
+
139
+ # Score by keyword matches
140
+ score = 0.0
141
+ for term in search_terms:
142
+ if term in name_lower:
143
+ score += 2.0 # Name match worth more
144
+ if term in desc_lower:
145
+ score += 1.0
146
+
147
+ # Also match if query is substring of name
148
+ if query.lower() in name_lower:
149
+ score += 3.0
150
+
151
+ if score > 0:
152
+ results.append(ToolSearchResult(
153
+ name=spec.name,
154
+ description=spec.description[:200],
155
+ score=score,
156
+ ))
157
+
158
+ # Sort by score descending
159
+ results.sort(key=lambda r: r.score, reverse=True)
160
+ results = results[:max_results]
161
+
162
+ return ToolSearchOutput(
163
+ query=query,
164
+ results=results,
165
+ message=f"Found {len(results)} tool(s) matching '{query}'",
166
+ )
167
+
168
+
169
+ def _select_tools(
170
+ names: list[str], registry: GlobalToolRegistry
171
+ ) -> ToolSearchOutput:
172
+ """Fetch full schemas for specific tools by name."""
173
+ schemas: list[dict[str, Any]] = []
174
+ not_found: list[str] = []
175
+
176
+ for name in names:
177
+ tool = registry.get(name)
178
+ if tool:
179
+ schemas.append({
180
+ "name": tool.spec.name,
181
+ "description": tool.spec.description,
182
+ "input_schema": tool.spec.input_schema,
183
+ })
184
+ else:
185
+ not_found.append(name)
186
+
187
+ msg = f"Loaded {len(schemas)} tool schema(s)"
188
+ if not_found:
189
+ msg += f". Not found: {', '.join(not_found)}"
190
+
191
+ return ToolSearchOutput(
192
+ query=f"select:{','.join(names)}",
193
+ schemas=schemas,
194
+ message=msg,
195
+ )
196
+
197
+
198
+ # ---------------------------------------------------------------------------
199
+ # Tool spec for ToolSearch itself
200
+ # ---------------------------------------------------------------------------
201
+
202
+ TOOL_SEARCH_SPEC = ToolSpec(
203
+ name="ToolSearch",
204
+ description=(
205
+ "Fetches full schema definitions for deferred tools so they can be called. "
206
+ "Use 'select:Read,Edit' for exact selection, or keywords to search."
207
+ ),
208
+ input_schema={
209
+ "type": "object",
210
+ "properties": {
211
+ "query": {
212
+ "type": "string",
213
+ "description": (
214
+ "Query to find tools. Use 'select:<name>' for direct selection, "
215
+ "or keywords to search."
216
+ ),
217
+ },
218
+ "max_results": {
219
+ "type": "number",
220
+ "description": "Maximum results (default 5)",
221
+ },
222
+ },
223
+ "required": ["query"],
224
+ },
225
+ required_permission="read-only",
226
+ )