akernel-runtime 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 (40) hide show
  1. akernel_runtime-0.1.0.dist-info/METADATA +270 -0
  2. akernel_runtime-0.1.0.dist-info/RECORD +40 -0
  3. akernel_runtime-0.1.0.dist-info/WHEEL +5 -0
  4. akernel_runtime-0.1.0.dist-info/entry_points.txt +2 -0
  5. akernel_runtime-0.1.0.dist-info/licenses/LICENSE +201 -0
  6. akernel_runtime-0.1.0.dist-info/licenses/NOTICE +4 -0
  7. akernel_runtime-0.1.0.dist-info/top_level.txt +1 -0
  8. context_kernel/__init__.py +4 -0
  9. context_kernel/__main__.py +5 -0
  10. context_kernel/agent_reports.py +188 -0
  11. context_kernel/benchmarks.py +493 -0
  12. context_kernel/budget.py +72 -0
  13. context_kernel/cli.py +2953 -0
  14. context_kernel/context.py +161 -0
  15. context_kernel/evals.py +347 -0
  16. context_kernel/global_memory.py +126 -0
  17. context_kernel/loop.py +1617 -0
  18. context_kernel/marketplace.py +194 -0
  19. context_kernel/marketplace_data/skills/context_budget.json +27 -0
  20. context_kernel/marketplace_data/skills/context_compaction.json +27 -0
  21. context_kernel/marketplace_data/skills/edit_file.json +27 -0
  22. context_kernel/marketplace_data/skills/index.json +66 -0
  23. context_kernel/marketplace_data/skills/long_task_planning.json +27 -0
  24. context_kernel/marketplace_data/skills/multi_file_bugfix.json +28 -0
  25. context_kernel/memory.py +515 -0
  26. context_kernel/models.py +144 -0
  27. context_kernel/planner.py +155 -0
  28. context_kernel/policy.py +271 -0
  29. context_kernel/project.py +317 -0
  30. context_kernel/providers.py +1264 -0
  31. context_kernel/report_costs.py +375 -0
  32. context_kernel/runner.py +78 -0
  33. context_kernel/skills.py +318 -0
  34. context_kernel/state_writer.py +108 -0
  35. context_kernel/storage.py +171 -0
  36. context_kernel/tasks.py +549 -0
  37. context_kernel/text.py +42 -0
  38. context_kernel/tokenizer.py +22 -0
  39. context_kernel/tools.py +544 -0
  40. context_kernel/verifier.py +77 -0
@@ -0,0 +1,155 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from .context import ContextBuilder
6
+ from .policy import assess_request_policy
7
+ from .storage import Workspace
8
+ from .tokenizer import estimate_tokens
9
+
10
+
11
+ CODE_TERMS = {
12
+ "code",
13
+ "cli",
14
+ "debug",
15
+ "edit",
16
+ "file",
17
+ "fix",
18
+ "implement",
19
+ "patch",
20
+ "refactor",
21
+ "test",
22
+ }
23
+ RESEARCH_TERMS = {"compare", "current", "latest", "news", "price", "research", "today", "verify"}
24
+
25
+
26
+ class ExecutionPlanner:
27
+ def __init__(self, workspace: Workspace):
28
+ self.workspace = workspace
29
+
30
+ def plan(
31
+ self,
32
+ request: str,
33
+ total_budget: int | None,
34
+ profile: str = "balanced",
35
+ task_id: str | None = None,
36
+ resume: bool = False,
37
+ ) -> dict[str, Any]:
38
+ comparison = ContextBuilder(self.workspace).compare(
39
+ request,
40
+ total_budget,
41
+ profile,
42
+ task_id=task_id,
43
+ resume=resume,
44
+ )
45
+ packet = comparison["kernel"]["packet"]
46
+ route = classify_route(request, packet)
47
+ policy = assess_request_policy(self.workspace, request)
48
+ warnings = list(packet.get("omissions", []))
49
+ if packet["budget"]["over_budget"]:
50
+ warnings.append("Do not execute provider call until context is reduced or budget is raised.")
51
+ warnings.extend(policy["warnings"])
52
+
53
+ return {
54
+ "request": request,
55
+ "profile": comparison["profile"],
56
+ "route": route,
57
+ "task": summarize_task(packet),
58
+ "policy": policy,
59
+ "budget": packet["budget"],
60
+ "selection": {
61
+ "memory": summarize_memory(packet),
62
+ "skills": summarize_skills(packet),
63
+ },
64
+ "savings": comparison["savings"],
65
+ "actions": planned_actions(packet),
66
+ "warnings": warnings,
67
+ }
68
+
69
+
70
+ def classify_route(request: str, packet: dict[str, Any]) -> dict[str, Any]:
71
+ request_terms = {term.strip(".,:;!?()[]{}\"'").casefold() for term in request.split()}
72
+ has_code = bool(request_terms.intersection(CODE_TERMS))
73
+ has_research = bool(request_terms.intersection(RESEARCH_TERMS))
74
+ selected_items = len(packet.get("memory", [])) + len(packet.get("skills", []))
75
+ request_tokens = estimate_tokens(request)
76
+
77
+ if has_research:
78
+ mode = "research_or_verification"
79
+ elif has_code:
80
+ mode = "code_or_file_work"
81
+ else:
82
+ mode = "direct_answer"
83
+
84
+ if request_tokens > 180 or selected_items >= 5:
85
+ complexity = "high"
86
+ elif request_tokens > 80 or selected_items >= 3:
87
+ complexity = "medium"
88
+ else:
89
+ complexity = "low"
90
+
91
+ return {
92
+ "mode": mode,
93
+ "complexity": complexity,
94
+ "reason": route_reason(mode, complexity, selected_items),
95
+ }
96
+
97
+
98
+ def summarize_memory(packet: dict[str, Any]) -> list[dict[str, Any]]:
99
+ return [
100
+ {
101
+ "id": item["record"]["id"],
102
+ "kind": item["record"]["kind"],
103
+ "score": item["score"],
104
+ "reason": item["reason"],
105
+ "estimated_tokens": item["estimated_tokens"],
106
+ }
107
+ for item in packet.get("memory", [])
108
+ ]
109
+
110
+
111
+ def summarize_skills(packet: dict[str, Any]) -> list[dict[str, Any]]:
112
+ return [
113
+ {
114
+ "id": item["contract"]["id"],
115
+ "name": item["contract"]["name"],
116
+ "level": item["level"],
117
+ "score": item["score"],
118
+ "reason": item["reason"],
119
+ "estimated_tokens": item["estimated_tokens"],
120
+ }
121
+ for item in packet.get("skills", [])
122
+ ]
123
+
124
+
125
+ def summarize_task(packet: dict[str, Any]) -> dict[str, Any]:
126
+ task = packet.get("task", {})
127
+ brief = task.get("brief")
128
+ if not brief:
129
+ return {"resume": False}
130
+ return {
131
+ "resume": True,
132
+ "id": brief["task"]["id"],
133
+ "title": brief["task"]["title"],
134
+ "status": brief["task"]["status"],
135
+ "estimated_tokens": brief.get("estimated_tokens", 0),
136
+ "plan": brief.get("plan"),
137
+ }
138
+
139
+
140
+ def planned_actions(packet: dict[str, Any]) -> list[str]:
141
+ actions = [
142
+ "Assemble the minimal context packet shown in this plan.",
143
+ "Call the selected provider only after budget checks pass.",
144
+ "Write a trace with selected memory, selected skills, provider usage, and verifier checks.",
145
+ ]
146
+ if packet["budget"]["over_budget"]:
147
+ return [
148
+ "Stop before provider execution because the packet is over budget.",
149
+ "Reduce selected memory or skill levels, or choose a larger profile.",
150
+ ]
151
+ return actions
152
+
153
+
154
+ def route_reason(mode: str, complexity: str, selected_items: int) -> str:
155
+ return f"mode={mode}; complexity={complexity}; selected_context_items={selected_items}"
@@ -0,0 +1,271 @@
1
+ from __future__ import annotations
2
+
3
+ import shlex
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from .storage import DEFAULT_COMMAND_POLICY, Workspace
8
+
9
+
10
+ FILE_OPERATIONS = {"read", "write", "delete"}
11
+ DESTRUCTIVE_FILE_OPERATIONS = {"delete"}
12
+ SENSITIVE_FILENAMES = {".env", ".env.example"}
13
+ PROTECTED_DIRECTORIES = {".venv", "__pycache__"}
14
+ PROTECTED_STATE_FILES = {"memory.sqlite3", "config.json"}
15
+ DESTRUCTIVE_COMMAND_TERMS = {
16
+ "del",
17
+ "erase",
18
+ "format",
19
+ "git checkout",
20
+ "git clean",
21
+ "git reset",
22
+ "move-item",
23
+ "rd",
24
+ "remove-item",
25
+ "rm",
26
+ "rmdir",
27
+ }
28
+ SAFE_COMMAND_ROOTS = set(DEFAULT_COMMAND_POLICY["allowed_roots"])
29
+ DANGEROUS_REQUEST_TERMS = {"delete", "remove", "reset", "wipe", "destroy", "清空", "删除", "重置"}
30
+
31
+
32
+ def check_file_policy(
33
+ workspace: Workspace,
34
+ operation: str,
35
+ target: Path | str,
36
+ *,
37
+ allow_destructive: bool = False,
38
+ ) -> dict[str, Any]:
39
+ operation = operation.lower()
40
+ if operation not in FILE_OPERATIONS:
41
+ raise ValueError(f"Unsupported file operation: {operation}. Expected one of: {', '.join(sorted(FILE_OPERATIONS))}")
42
+
43
+ target_path = Path(target)
44
+ resolved = resolve_target(workspace, target_path)
45
+ reasons: list[str] = []
46
+ allowed = True
47
+
48
+ if not is_relative_to(resolved, workspace.root):
49
+ allowed = False
50
+ reasons.append("target is outside the workspace root")
51
+
52
+ if target_path.name.lower() in SENSITIVE_FILENAMES or resolved.name.lower() in SENSITIVE_FILENAMES:
53
+ allowed = False
54
+ reasons.append("target is a sensitive environment file")
55
+
56
+ relative_parts = {part.lower() for part in safe_relative_parts(workspace, resolved)}
57
+ if relative_parts.intersection(PROTECTED_DIRECTORIES):
58
+ allowed = False
59
+ reasons.append("target is inside a protected generated directory")
60
+
61
+ if ".akernel" in relative_parts and (operation != "read" or resolved.name in PROTECTED_STATE_FILES):
62
+ allowed = False
63
+ reasons.append("target touches protected Context Kernel state")
64
+
65
+ if operation in DESTRUCTIVE_FILE_OPERATIONS and not allow_destructive:
66
+ allowed = False
67
+ reasons.append("destructive file operation requires --allow-destructive")
68
+
69
+ return policy_result(
70
+ kind="file",
71
+ allowed=allowed,
72
+ subject=str(resolved),
73
+ operation=operation,
74
+ reasons=reasons,
75
+ )
76
+
77
+
78
+ def check_batch_file_policy(
79
+ workspace: Workspace,
80
+ edits: list[dict[str, Any]],
81
+ *,
82
+ allow_destructive: bool = False,
83
+ ) -> dict[str, Any]:
84
+ if not edits:
85
+ raise ValueError("Batch file policy requires at least one edit.")
86
+
87
+ allowed = True
88
+ reasons: list[str] = []
89
+ items: list[dict[str, Any]] = []
90
+ subjects: list[str] = []
91
+ for index, edit in enumerate(edits, start=1):
92
+ path = edit.get("path")
93
+ if not isinstance(path, str) or not path.strip():
94
+ raise ValueError(f"Batch edit {index} is missing a valid path.")
95
+ policy = check_file_policy(
96
+ workspace,
97
+ "write",
98
+ path,
99
+ allow_destructive=allow_destructive,
100
+ )
101
+ items.append(policy)
102
+ subjects.append(str(policy["subject"]))
103
+ if not policy["allowed"]:
104
+ allowed = False
105
+ reasons.extend([f"edit {index}: {reason}" for reason in policy["reasons"]])
106
+
107
+ result = policy_result(
108
+ kind="batch_file",
109
+ allowed=allowed,
110
+ subject=", ".join(subjects),
111
+ operation="batch_patch",
112
+ reasons=reasons,
113
+ )
114
+ result["items"] = items
115
+ return result
116
+
117
+
118
+ def check_command_policy(
119
+ command: str,
120
+ *,
121
+ workspace: Workspace | None = None,
122
+ allow_destructive: bool = False,
123
+ ) -> dict[str, Any]:
124
+ normalized = " ".join(command.strip().split())
125
+ if not normalized:
126
+ raise ValueError("Command cannot be empty.")
127
+
128
+ lower = normalized.casefold()
129
+ reasons: list[str] = []
130
+ allowed = True
131
+ root = command_root(normalized)
132
+ root_candidates = command_root_candidates(normalized)
133
+ command_policy = command_policy_settings(workspace)
134
+ allowed_roots = set(command_policy["allowed_roots"])
135
+ blocked_terms = set(command_policy["blocked_terms"])
136
+
137
+ if root and not allowed_roots.intersection(root_candidates):
138
+ allowed = False
139
+ reasons.append(f"command root is not in the workspace safe list: {root}")
140
+
141
+ matched_destructive = matched_destructive_terms(lower, extra_terms=blocked_terms)
142
+ if matched_destructive and not allow_destructive:
143
+ allowed = False
144
+ reasons.append("destructive command term requires --allow-destructive: " + ", ".join(matched_destructive))
145
+
146
+ if root == "git" and ("--hard" in lower or "-f" in lower) and not allow_destructive:
147
+ allowed = False
148
+ reasons.append("forceful git operations are blocked by default")
149
+
150
+ result = policy_result(
151
+ kind="command",
152
+ allowed=allowed,
153
+ subject=normalized,
154
+ operation="execute",
155
+ reasons=reasons,
156
+ )
157
+ result["policy_config"] = {
158
+ "allowed_roots": command_policy["allowed_roots"],
159
+ "blocked_terms": command_policy["blocked_terms"],
160
+ "workspace": str(workspace.root) if workspace else None,
161
+ }
162
+ return result
163
+
164
+
165
+ def assess_request_policy(workspace: Workspace, request: str) -> dict[str, Any]:
166
+ lower = request.casefold()
167
+ warnings: list[str] = []
168
+ if any(term in lower for term in DANGEROUS_REQUEST_TERMS):
169
+ warnings.append("request contains destructive language; require an explicit policy check before file or command execution")
170
+ if ".env" in lower:
171
+ warnings.append("request mentions environment files; avoid exposing secrets and require policy review")
172
+ if "git reset" in lower or "reset --hard" in lower:
173
+ warnings.append("request mentions forceful git reset; default command policy blocks this")
174
+ return {
175
+ "workspace": str(workspace.root),
176
+ "warnings": warnings,
177
+ "requires_policy_check": bool(warnings),
178
+ "command_policy": summarize_command_policy(workspace),
179
+ }
180
+
181
+
182
+ def resolve_target(workspace: Workspace, target: Path) -> Path:
183
+ if target.is_absolute():
184
+ return target.resolve()
185
+ return (workspace.root / target).resolve()
186
+
187
+
188
+ def is_relative_to(path: Path, parent: Path) -> bool:
189
+ try:
190
+ path.relative_to(parent)
191
+ except ValueError:
192
+ return False
193
+ return True
194
+
195
+
196
+ def safe_relative_parts(workspace: Workspace, path: Path) -> list[str]:
197
+ if not is_relative_to(path, workspace.root):
198
+ return []
199
+ return list(path.relative_to(workspace.root).parts)
200
+
201
+
202
+ def command_root(command: str) -> str:
203
+ try:
204
+ parts = shlex.split(command, posix=False)
205
+ except ValueError:
206
+ parts = command.split()
207
+ if not parts:
208
+ return ""
209
+ return parts[0].strip('"').strip("'").casefold()
210
+
211
+
212
+ def command_root_candidates(command: str) -> set[str]:
213
+ root = command_root(command)
214
+ if not root:
215
+ return set()
216
+ candidates = {root}
217
+ basename = Path(root).name.casefold()
218
+ if basename:
219
+ candidates.add(basename)
220
+ if basename.endswith(".exe"):
221
+ candidates.add(basename[:-4])
222
+ return candidates
223
+
224
+
225
+ def matched_destructive_terms(lower_command: str, *, extra_terms: set[str] | None = None) -> list[str]:
226
+ tokens = {part.strip('"').strip("'").casefold() for part in lower_command.split()}
227
+ destructive_terms = set(DESTRUCTIVE_COMMAND_TERMS)
228
+ destructive_terms.update(extra_terms or set())
229
+ matches: list[str] = []
230
+ for term in sorted(destructive_terms):
231
+ if " " in term:
232
+ if term in lower_command:
233
+ matches.append(term)
234
+ continue
235
+ if term in tokens:
236
+ matches.append(term)
237
+ return matches
238
+
239
+
240
+ def command_policy_settings(workspace: Workspace | None = None) -> dict[str, list[str]]:
241
+ if workspace is None:
242
+ return {
243
+ "allowed_roots": sorted(SAFE_COMMAND_ROOTS),
244
+ "blocked_terms": [],
245
+ }
246
+ config = workspace.load_config()
247
+ command_policy = config.get("command_policy", {})
248
+ return {
249
+ "allowed_roots": list(command_policy.get("allowed_roots", [])),
250
+ "blocked_terms": list(command_policy.get("blocked_terms", [])),
251
+ }
252
+
253
+
254
+ def summarize_command_policy(workspace: Workspace | None = None) -> dict[str, Any]:
255
+ settings = command_policy_settings(workspace)
256
+ return {
257
+ "allowed_roots": settings["allowed_roots"],
258
+ "blocked_terms": settings["blocked_terms"],
259
+ "allowed_root_count": len(settings["allowed_roots"]),
260
+ }
261
+
262
+
263
+ def policy_result(kind: str, allowed: bool, subject: str, operation: str, reasons: list[str]) -> dict[str, Any]:
264
+ return {
265
+ "kind": kind,
266
+ "operation": operation,
267
+ "subject": subject,
268
+ "allowed": allowed,
269
+ "status": "allowed" if allowed else "blocked",
270
+ "reasons": reasons or ["policy checks passed"],
271
+ }