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.
- axion/__init__.py +3 -0
- axion/api/__init__.py +0 -0
- axion/api/anthropic.py +460 -0
- axion/api/client.py +259 -0
- axion/api/error.py +161 -0
- axion/api/ollama.py +597 -0
- axion/api/openai_compat.py +805 -0
- axion/api/openai_responses.py +627 -0
- axion/api/prompt_cache.py +31 -0
- axion/api/sse.py +98 -0
- axion/api/types.py +451 -0
- axion/cli/__init__.py +0 -0
- axion/cli/init_cmd.py +50 -0
- axion/cli/input.py +290 -0
- axion/cli/main.py +2953 -0
- axion/cli/render.py +489 -0
- axion/cli/tui.py +766 -0
- axion/commands/__init__.py +0 -0
- axion/commands/handlers/__init__.py +0 -0
- axion/commands/handlers/agents.py +51 -0
- axion/commands/handlers/builtin_commands.py +367 -0
- axion/commands/handlers/mcp.py +59 -0
- axion/commands/handlers/models.py +75 -0
- axion/commands/handlers/plugins.py +55 -0
- axion/commands/handlers/skills.py +61 -0
- axion/commands/parsing.py +317 -0
- axion/commands/registry.py +166 -0
- axion/compat_harness/__init__.py +0 -0
- axion/compat_harness/extractor.py +145 -0
- axion/plugins/__init__.py +0 -0
- axion/plugins/hooks.py +22 -0
- axion/plugins/manager.py +391 -0
- axion/plugins/manifest.py +270 -0
- axion/runtime/__init__.py +0 -0
- axion/runtime/bash.py +388 -0
- axion/runtime/bootstrap.py +39 -0
- axion/runtime/claude_subscription.py +300 -0
- axion/runtime/compact.py +233 -0
- axion/runtime/config.py +397 -0
- axion/runtime/conversation.py +1073 -0
- axion/runtime/file_ops.py +613 -0
- axion/runtime/git.py +213 -0
- axion/runtime/hooks.py +235 -0
- axion/runtime/image.py +212 -0
- axion/runtime/lanes.py +282 -0
- axion/runtime/lsp.py +425 -0
- axion/runtime/mcp/__init__.py +0 -0
- axion/runtime/mcp/client.py +76 -0
- axion/runtime/mcp/lifecycle.py +96 -0
- axion/runtime/mcp/stdio.py +318 -0
- axion/runtime/mcp/tool_bridge.py +79 -0
- axion/runtime/memory.py +196 -0
- axion/runtime/oauth.py +329 -0
- axion/runtime/openai_subscription.py +346 -0
- axion/runtime/permissions.py +247 -0
- axion/runtime/plan_mode.py +96 -0
- axion/runtime/policy_engine.py +259 -0
- axion/runtime/prompt.py +586 -0
- axion/runtime/recovery.py +261 -0
- axion/runtime/remote.py +28 -0
- axion/runtime/sandbox.py +68 -0
- axion/runtime/scheduler.py +231 -0
- axion/runtime/session.py +365 -0
- axion/runtime/sharing.py +159 -0
- axion/runtime/skills.py +124 -0
- axion/runtime/tasks.py +258 -0
- axion/runtime/usage.py +241 -0
- axion/runtime/workers.py +186 -0
- axion/telemetry/__init__.py +0 -0
- axion/telemetry/events.py +67 -0
- axion/telemetry/profile.py +49 -0
- axion/telemetry/sink.py +60 -0
- axion/telemetry/tracer.py +95 -0
- axion/tools/__init__.py +0 -0
- axion/tools/lane_completion.py +33 -0
- axion/tools/registry.py +853 -0
- axion/tools/tool_search.py +226 -0
- axion_code-1.0.0.dist-info/METADATA +709 -0
- axion_code-1.0.0.dist-info/RECORD +82 -0
- axion_code-1.0.0.dist-info/WHEEL +4 -0
- axion_code-1.0.0.dist-info/entry_points.txt +2 -0
- axion_code-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""Git lane policy engine with condition combinators and chained actions.
|
|
2
|
+
|
|
3
|
+
Maps to: rust/crates/runtime/src/policy_engine.rs
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import enum
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
11
|
+
GreenLevel = int # 0-255
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
# Lane context
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
class ReviewStatus(enum.Enum):
|
|
19
|
+
PENDING = "pending"
|
|
20
|
+
APPROVED = "approved"
|
|
21
|
+
REJECTED = "rejected"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DiffScope(enum.Enum):
|
|
25
|
+
FULL = "full"
|
|
26
|
+
SCOPED = "scoped"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LaneBlocker(enum.Enum):
|
|
30
|
+
NONE = "none"
|
|
31
|
+
STARTUP = "startup"
|
|
32
|
+
EXTERNAL = "external"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class LaneContext:
|
|
37
|
+
"""Context for policy evaluation."""
|
|
38
|
+
|
|
39
|
+
lane_id: str
|
|
40
|
+
green_level: GreenLevel = 0
|
|
41
|
+
branch_freshness_ms: int = 0
|
|
42
|
+
blocker: LaneBlocker = LaneBlocker.NONE
|
|
43
|
+
review_status: ReviewStatus = ReviewStatus.PENDING
|
|
44
|
+
diff_scope: DiffScope = DiffScope.FULL
|
|
45
|
+
completed: bool = False
|
|
46
|
+
reconciled: bool = False
|
|
47
|
+
timed_out: bool = False
|
|
48
|
+
stale_branch: bool = False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Conditions (combinators matching Rust enum)
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
class PolicyCondition:
|
|
56
|
+
"""Base class for policy conditions."""
|
|
57
|
+
|
|
58
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ConditionAnd(PolicyCondition):
|
|
63
|
+
def __init__(self, conditions: list[PolicyCondition]) -> None:
|
|
64
|
+
self.conditions = conditions
|
|
65
|
+
|
|
66
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
67
|
+
return all(c.evaluate(ctx) for c in self.conditions)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ConditionOr(PolicyCondition):
|
|
71
|
+
def __init__(self, conditions: list[PolicyCondition]) -> None:
|
|
72
|
+
self.conditions = conditions
|
|
73
|
+
|
|
74
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
75
|
+
return any(c.evaluate(ctx) for c in self.conditions)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ConditionGreenAt(PolicyCondition):
|
|
79
|
+
def __init__(self, level: GreenLevel) -> None:
|
|
80
|
+
self.level = level
|
|
81
|
+
|
|
82
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
83
|
+
return ctx.green_level >= self.level
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ConditionStaleBranch(PolicyCondition):
|
|
87
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
88
|
+
return ctx.stale_branch
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ConditionStartupBlocked(PolicyCondition):
|
|
92
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
93
|
+
return ctx.blocker == LaneBlocker.STARTUP
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ConditionLaneCompleted(PolicyCondition):
|
|
97
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
98
|
+
return ctx.completed
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ConditionLaneReconciled(PolicyCondition):
|
|
102
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
103
|
+
return ctx.reconciled
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class ConditionReviewPassed(PolicyCondition):
|
|
107
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
108
|
+
return ctx.review_status == ReviewStatus.APPROVED
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class ConditionScopedDiff(PolicyCondition):
|
|
112
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
113
|
+
return ctx.diff_scope == DiffScope.SCOPED
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class ConditionTimedOut(PolicyCondition):
|
|
117
|
+
def __init__(self, duration_ms: int = 0) -> None:
|
|
118
|
+
self.duration_ms = duration_ms
|
|
119
|
+
|
|
120
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
121
|
+
return ctx.timed_out
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ConditionAlways(PolicyCondition):
|
|
125
|
+
def evaluate(self, ctx: LaneContext) -> bool:
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
# Actions
|
|
131
|
+
# ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
class PolicyAction(enum.Enum):
|
|
134
|
+
MERGE_TO_DEV = "merge_to_dev"
|
|
135
|
+
MERGE_FORWARD = "merge_forward"
|
|
136
|
+
RECOVER_ONCE = "recover_once"
|
|
137
|
+
ESCALATE = "escalate"
|
|
138
|
+
CLOSEOUT_LANE = "closeout_lane"
|
|
139
|
+
CLEANUP_SESSION = "cleanup_session"
|
|
140
|
+
RECONCILE = "reconcile"
|
|
141
|
+
NOTIFY = "notify"
|
|
142
|
+
BLOCK = "block"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass
|
|
146
|
+
class PolicyActionSpec:
|
|
147
|
+
"""An action with optional parameters."""
|
|
148
|
+
|
|
149
|
+
action: PolicyAction
|
|
150
|
+
reason: str = ""
|
|
151
|
+
channel: str = "" # For NOTIFY
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@dataclass
|
|
155
|
+
class ChainedAction:
|
|
156
|
+
"""Multiple actions executed in sequence."""
|
|
157
|
+
|
|
158
|
+
actions: list[PolicyActionSpec] = field(default_factory=list)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
# Rules
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
@dataclass
|
|
166
|
+
class PolicyRule:
|
|
167
|
+
"""A condition → action mapping with priority."""
|
|
168
|
+
|
|
169
|
+
name: str
|
|
170
|
+
condition: PolicyCondition
|
|
171
|
+
action: PolicyActionSpec | ChainedAction
|
|
172
|
+
priority: int = 0
|
|
173
|
+
description: str = ""
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ---------------------------------------------------------------------------
|
|
177
|
+
# Policy engine
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
class PolicyEngine:
|
|
181
|
+
"""Evaluates policy rules against lane context.
|
|
182
|
+
|
|
183
|
+
Maps to: rust/crates/runtime/src/policy_engine.rs::PolicyEngine
|
|
184
|
+
Supports condition combinators (And/Or), chained actions, and priority ordering.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
def __init__(self, rules: list[PolicyRule] | None = None) -> None:
|
|
188
|
+
self._rules = sorted(rules or [], key=lambda r: r.priority, reverse=True)
|
|
189
|
+
|
|
190
|
+
def add_rule(self, rule: PolicyRule) -> None:
|
|
191
|
+
self._rules.append(rule)
|
|
192
|
+
self._rules.sort(key=lambda r: r.priority, reverse=True)
|
|
193
|
+
|
|
194
|
+
def evaluate(self, context: LaneContext) -> list[PolicyActionSpec]:
|
|
195
|
+
"""Evaluate all rules and return applicable actions."""
|
|
196
|
+
actions: list[PolicyActionSpec] = []
|
|
197
|
+
|
|
198
|
+
for rule in self._rules:
|
|
199
|
+
if rule.condition.evaluate(context):
|
|
200
|
+
if isinstance(rule.action, ChainedAction):
|
|
201
|
+
actions.extend(rule.action.actions)
|
|
202
|
+
else:
|
|
203
|
+
actions.append(rule.action)
|
|
204
|
+
|
|
205
|
+
return actions
|
|
206
|
+
|
|
207
|
+
def evaluate_first(self, context: LaneContext) -> PolicyActionSpec | None:
|
|
208
|
+
"""Return the first matching action (highest priority)."""
|
|
209
|
+
for rule in self._rules:
|
|
210
|
+
if rule.condition.evaluate(context):
|
|
211
|
+
if isinstance(rule.action, ChainedAction):
|
|
212
|
+
return rule.action.actions[0] if rule.action.actions else None
|
|
213
|
+
return rule.action
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
@classmethod
|
|
217
|
+
def default_rules(cls) -> PolicyEngine:
|
|
218
|
+
"""Create engine with default git lane policies."""
|
|
219
|
+
return cls(rules=[
|
|
220
|
+
PolicyRule(
|
|
221
|
+
name="merge_approved",
|
|
222
|
+
condition=ConditionAnd([ConditionLaneCompleted(), ConditionReviewPassed()]),
|
|
223
|
+
action=PolicyActionSpec(action=PolicyAction.MERGE_TO_DEV),
|
|
224
|
+
priority=100,
|
|
225
|
+
description="Merge completed & approved lanes to dev",
|
|
226
|
+
),
|
|
227
|
+
PolicyRule(
|
|
228
|
+
name="block_startup",
|
|
229
|
+
condition=ConditionStartupBlocked(),
|
|
230
|
+
action=PolicyActionSpec(action=PolicyAction.BLOCK, reason="Startup blocker active"),
|
|
231
|
+
priority=90,
|
|
232
|
+
),
|
|
233
|
+
PolicyRule(
|
|
234
|
+
name="escalate_external",
|
|
235
|
+
condition=ConditionAnd([
|
|
236
|
+
ConditionOr([ConditionStaleBranch(), ConditionTimedOut()]),
|
|
237
|
+
]),
|
|
238
|
+
action=PolicyActionSpec(
|
|
239
|
+
action=PolicyAction.ESCALATE,
|
|
240
|
+
reason="Branch stale or timed out",
|
|
241
|
+
),
|
|
242
|
+
priority=80,
|
|
243
|
+
),
|
|
244
|
+
PolicyRule(
|
|
245
|
+
name="closeout_reconciled",
|
|
246
|
+
condition=ConditionAnd([ConditionLaneCompleted(), ConditionLaneReconciled()]),
|
|
247
|
+
action=ChainedAction(actions=[
|
|
248
|
+
PolicyActionSpec(action=PolicyAction.CLOSEOUT_LANE),
|
|
249
|
+
PolicyActionSpec(action=PolicyAction.CLEANUP_SESSION),
|
|
250
|
+
]),
|
|
251
|
+
priority=70,
|
|
252
|
+
),
|
|
253
|
+
PolicyRule(
|
|
254
|
+
name="recover_stale",
|
|
255
|
+
condition=ConditionStaleBranch(),
|
|
256
|
+
action=PolicyActionSpec(action=PolicyAction.RECOVER_ONCE, reason="Branch is stale"),
|
|
257
|
+
priority=50,
|
|
258
|
+
),
|
|
259
|
+
])
|