alpiecode 10.0.2__tar.gz → 10.5.0__tar.gz

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 (59) hide show
  1. {alpiecode-10.0.2 → alpiecode-10.5.0}/PKG-INFO +1 -1
  2. {alpiecode-10.0.2 → alpiecode-10.5.0}/README.md +86 -12
  3. {alpiecode-10.0.2 → alpiecode-10.5.0}/pyproject.toml +1 -1
  4. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/alpiecode.egg-info/PKG-INFO +1 -1
  5. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/alpiecode.egg-info/SOURCES.txt +2 -13
  6. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/__init__.py +1 -1
  7. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/agent.py +2 -1
  8. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/backends/local_backend.py +37 -2
  9. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/backends/openai_backend.py +10 -3
  10. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/cache.py +39 -8
  11. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/cli.py +59 -2
  12. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/discovery.py +5 -0
  13. alpiecode-10.5.0/src/codeagent/dream_rsi.py +373 -0
  14. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/executor.py +8 -1
  15. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/local_model.py +83 -18
  16. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/memory.py +23 -4
  17. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/metrics.py +19 -1
  18. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/orchestrator.py +88 -5
  19. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/prompt.py +11 -3
  20. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/system1.py +23 -6
  21. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/tools.py +16 -2
  22. alpiecode-10.0.2/test/test_audit_fixes.py +0 -71
  23. alpiecode-10.0.2/test/test_benchmark_system1.py +0 -90
  24. alpiecode-10.0.2/test/test_cache_reads.py +0 -66
  25. alpiecode-10.0.2/test/test_chat_mode.py +0 -85
  26. alpiecode-10.0.2/test/test_claude_codex_capabilities.py +0 -111
  27. alpiecode-10.0.2/test/test_context_filter.py +0 -57
  28. alpiecode-10.0.2/test/test_metrics.py +0 -46
  29. alpiecode-10.0.2/test/test_offline_mode.py +0 -314
  30. alpiecode-10.0.2/test/test_phase1.py +0 -302
  31. alpiecode-10.0.2/test/test_phase2.py +0 -231
  32. alpiecode-10.0.2/test/test_phase3.py +0 -154
  33. alpiecode-10.0.2/test/test_system1.py +0 -84
  34. {alpiecode-10.0.2 → alpiecode-10.5.0}/setup.cfg +0 -0
  35. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/alpiecode/__init__.py +0 -0
  36. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/alpiecode.egg-info/dependency_links.txt +0 -0
  37. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/alpiecode.egg-info/entry_points.txt +0 -0
  38. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/alpiecode.egg-info/requires.txt +0 -0
  39. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/alpiecode.egg-info/top_level.txt +0 -0
  40. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/backends/__init__.py +0 -0
  41. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/backends/base.py +0 -0
  42. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/client.py +0 -0
  43. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/compaction.py +0 -0
  44. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/config.py +0 -0
  45. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/context.py +0 -0
  46. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/doctor.py +0 -0
  47. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/extension/alpiecode.vsix +0 -0
  48. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/git_ops.py +0 -0
  49. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/github.py +0 -0
  50. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/guardian.py +0 -0
  51. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/guardrails.py +0 -0
  52. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/ipython_ext.py +0 -0
  53. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/media.py +0 -0
  54. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/progress.py +0 -0
  55. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/rephraser.py +0 -0
  56. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/server.py +0 -0
  57. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/session.py +0 -0
  58. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/updater.py +0 -0
  59. {alpiecode-10.0.2 → alpiecode-10.5.0}/src/codeagent/vscode_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 10.0.2
3
+ Version: 10.5.0
4
4
  Summary: AlpieCode
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: huggingface_hub>=0.20.0
@@ -1,7 +1,7 @@
1
1
  # AlpieCode
2
2
 
3
3
  [![Python Version](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue.svg)](https://www.python.org/)
4
- [![Version](https://img.shields.io/badge/alpiecode-v9.0.1-emerald.svg)](https://github.com/169Pi/codeagent-poc)
4
+ [![Version](https://img.shields.io/badge/alpiecode-v10.0.2-emerald.svg)](https://github.com/169Pi/codeagent-poc)
5
5
  [![VS Code Extension](https://img.shields.io/badge/vscode%20extension-v8.0.8-blueviolet.svg)](https://marketplace.visualstudio.com/)
6
6
  [![FastAPI](https://img.shields.io/badge/FastAPI-0.100%2B-009688.svg)](https://fastapi.tiangolo.com/)
7
7
  [![Jupyter & Colab](https://img.shields.io/badge/Jupyter%20%7C%20Colab-Ready-f37726.svg)](https://colab.research.google.com/)
@@ -16,16 +16,18 @@
16
16
  ## 📑 Table of Contents
17
17
  1. [🌟 Overview & Capabilities](#-overview--capabilities)
18
18
  2. [🏗️ Core Architecture & Hybrid Dual-Engine](#️-core-architecture--hybrid-dual-engine)
19
- 3. [📦 Complete Installation Guide](#-complete-installation-guide)
20
- 4. [🚀 Backend API Server (`alpiecode serve`)](#-backend-api-server-alpiecode-serve)
21
- 5. [⌨️ Command-Line Interface (CLI) Full Reference](#️-command-line-interface-cli-full-reference)
22
- 6. [💻 VS Code Extension Deep-Dive](#-vs-code-extension-deep-dive)
23
- 7. [🐍 Python SDK Reference (Programmatic Usage)](#-python-sdk-reference-programmatic-usage)
24
- 8. [📓 Jupyter Notebooks, JupyterLab & Google Colab](#-jupyter-notebooks-jupyterlab--google-colab)
25
- 9. [🛠️ 15 Built-In Autonomous Agent Tools](#️-15-built-in-autonomous-agent-tools)
26
- 10. [🛡️ Safety Guardian, Git Checkpoints & Memory System](#️-safety-guardian-git-checkpoints--memory-system)
27
- 11. [⚙️ Configuration & Environment Variables](#️-configuration--environment-variables)
28
- 12. [❓ FAQ & Troubleshooting](#-faq--troubleshooting)
19
+ 3. [⚡ System 1 (Jev Architecture) & Performance Engine](#-system-1-jev-architecture--performance-engine)
20
+ 4. [📊 Performance & Optimization Benchmarks](#-performance--optimization-benchmarks)
21
+ 5. [📦 Complete Installation Guide](#-complete-installation-guide)
22
+ 6. [🚀 Backend API Server (`alpiecode serve`)](#-backend-api-server-alpiecode-serve)
23
+ 7. [⌨️ Command-Line Interface (CLI) Full Reference](#️-command-line-interface-cli-full-reference)
24
+ 8. [💻 VS Code Extension Deep-Dive](#-vs-code-extension-deep-dive)
25
+ 9. [🐍 Python SDK Reference (Programmatic Usage)](#-python-sdk-reference-programmatic-usage)
26
+ 10. [📓 Jupyter Notebooks, JupyterLab & Google Colab](#-jupyter-notebooks-jupyterlab--google-colab)
27
+ 11. [🛠️ 15 Built-In Autonomous Agent Tools](#️-15-built-in-autonomous-agent-tools)
28
+ 12. [🛡️ Safety Guardian, Git Checkpoints & Memory System](#️-safety-guardian-git-checkpoints--memory-system)
29
+ 13. [⚙️ Configuration & Environment Variables](#️-configuration--environment-variables)
30
+ 14. [❓ FAQ & Troubleshooting](#-faq--troubleshooting)
29
31
 
30
32
  ---
31
33
 
@@ -122,6 +124,78 @@ AlpieCode employs an intelligent **Hybrid Dual-Engine** routing system that pair
122
124
 
123
125
  ---
124
126
 
127
+ ## ⚡ System 1 (Jev Architecture) & Performance Engine
128
+
129
+ AlpieCode introduces a **Dual-Process Cognitive Architecture** inspired by TypeSafe AI's **Jev (System 1)** framework:
130
+ - **System 1 (Fast Reflex & Decision Plane)**: A sub-5ms non-autoregressive decision engine that handles routine routing, parameter validation, test parsing, loop prevention, and error triage.
131
+ - **System 2 (Deep Thought & Generative Engine)**: The heavy reasoning LLM (169Pi Cloud VLA / Claude / GPT-4o online, or local Qwen GGUF offline) dedicated purely to code synthesis and architectural reasoning.
132
+
133
+ ```
134
+ ┌──────────────────────────────────────────────┐
135
+ │ User Coding Task │
136
+ └──────────────────────┬───────────────────────┘
137
+ │
138
+ ┌────────────────────────────────────────▼──────────────────────────────────────────────────┐
139
+ │ SYSTEM 1: Jev Decision Plane (<5ms) │
140
+ │ • Tool Call Validation • Repeated Loop Detection • Test Result Triage │
141
+ │ • Surgical Diff Enforcement • Context Noise Filtering │
142
+ └────────────────────────────────────────┬──────────────────────────────────────────────────┘
143
+ │
144
+ ┌───────────────────┴───────────────────┐
145
+ ▼ ▼
146
+ ┌───────────────────────────┐ ┌───────────────────────────┐
147
+ │ Online Cloud Backend │ │ Offline Local Engine │
148
+ │ (169Pi / Claude / GPT) │ │ (llama.cpp Qwen GGUF) │
149
+ │ • Sub-12s Task Completion│ │ • Zero Context Overflow │
150
+ │ • >75% Token Savings │ │ • Zero Syntax Crash Loops│
151
+ └───────────────────────────┘ └───────────────────────────┘
152
+ ```
153
+
154
+ ### Key Capabilities
155
+
156
+ 1. **⚡ TypeSafe System 1 Decision Loop (`src/codeagent/system1.py`)**:
157
+ - **Loop & Oscillation Detection**: Tracks tool call signatures. If the agent fails an operation twice (e.g. repeated syntax errors), System 1 intercepts the turn and forces a targeted `edit_file` directive rather than an infinite full-file rewrite.
158
+ - **Automated Test Health Parser**: Fast regex engine parsing pytest, unittest, and npm test outputs in <1ms to determine test success without burning LLM inference.
159
+ - **Tool Parameter Validator**: Pre-execution validation preventing malformed or missing parameters.
160
+
161
+ 2. **🚀 Multi-Tier Caching System (`src/codeagent/cache.py`)**:
162
+ - **`FileReadCache`**: In-memory cache for repeated file reads keyed by `(path, mtime, size, line_range)`. Cuts read latency from 1.2ms to **<0.05ms** (24x speedup).
163
+ - **`TestResultCache`**: Disk-persisted cache in `~/.alpiecode/cache/test_cache.json` keyed by a workspace file fingerprint (`hash(mtimes)`). Unchanged test suites re-run in **<1ms** with zero redundant execution.
164
+ - **`ResponseCache`**: Disk-backed LRU cache for pure text explanations and Q&A.
165
+
166
+ 3. **🗜️ Context Noise Filtering (`src/codeagent/context.py`)**:
167
+ - Compresses 50+ lines of clean passing test noise down to a 2-line summary (`[Clean Test Output]`).
168
+ - On test failures, preserves critical tracebacks and failure frames while stripping passing test dots, achieving **>75% token reduction**.
169
+
170
+ 4. **📊 Real-Time Performance Observability (`src/codeagent/metrics.py`)**:
171
+ - At the conclusion of every task, AlpieCode renders an end-of-task summary:
172
+ ```text
173
+ ╭──────────────────────── 📊 Task Performance Summary ────────────────────────╮
174
+ │ ⏱️ Latency: 12.22s | 🧠 LLM Calls: 2 (5425ms) │
175
+ │ 🛠️ Tools Time: 153.5ms (1 calls) | 📦 Context Prep: 6253ms | ⚡ Cache: 1│
176
+ ╰─────────────────────────────────────────────────────────────────────────────╯
177
+ ```
178
+
179
+ 5. **📜 Agent Operational Guidelines (`agents.md`)**:
180
+ - Operational rules defining the **Investigate → Plan → Surgical Edit → Verify → Complete** cycle, enforcing surgical diffs for files >100 lines and zero stub implementations.
181
+
182
+ ---
183
+
184
+ ## 📊 Performance & Optimization Benchmarks
185
+
186
+ Comprehensive benchmarks comparing AlpieCode before vs. after the System 1 Jev engine and caching upgrade:
187
+
188
+ | Benchmark Metric | Baseline (v9.0) | With System 1 & Caching (v10.0.2) | Improvement |
189
+ | :--- | :---: | :---: | :---: |
190
+ | **Simple Test & Verify Task Latency** | 45.57s | **12.22s** | **3.7x faster (73% drop)** |
191
+ | **Repeated File Read Latency** | 1.20 ms | **<0.05 ms** | **24x faster** (In-Memory Cache) |
192
+ | **Unchanged Test Suite Re-run** | 3,500 ms | **<1.0 ms** | **Instant (Zero-Latency Cache)** |
193
+ | **Test Output Context Token Footprint**| ~3,500 tokens (500 lines) | **~80 tokens (4 lines)** | **>75% Context Compression** |
194
+ | **Syntax Error Retry Recovery** | Infinite full-file loop | **Loop intercepted on Turn 2** | Self-healing surgical diff |
195
+ | **Full Regression Test Suite** | 52 passed | **96 passed (100% green)** | Complete reliability |
196
+
197
+ ---
198
+
125
199
  ## 📦 Complete Installation Guide
126
200
 
127
201
  ### 1. Prerequisites
@@ -177,7 +251,7 @@ pip install dist/alpiecode-9.0.1-py3-none-any.whl
177
251
  #### Verify CLI Installation:
178
252
  ```bash
179
253
  alpiecode version
180
- # Output: alpiecode 9.0.1
254
+ # Output: alpiecode 10.0.2
181
255
 
182
256
  alpiecode doctor
183
257
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "alpiecode"
7
- version = "10.0.2"
7
+ version = "10.5.0"
8
8
  description = "AlpieCode"
9
9
  requires-python = ">=3.9"
10
10
  dependencies = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpiecode
3
- Version: 10.0.2
3
+ Version: 10.5.0
4
4
  Summary: AlpieCode
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: huggingface_hub>=0.20.0
@@ -17,6 +17,7 @@ src/codeagent/config.py
17
17
  src/codeagent/context.py
18
18
  src/codeagent/discovery.py
19
19
  src/codeagent/doctor.py
20
+ src/codeagent/dream_rsi.py
20
21
  src/codeagent/executor.py
21
22
  src/codeagent/git_ops.py
22
23
  src/codeagent/github.py
@@ -41,16 +42,4 @@ src/codeagent/backends/__init__.py
41
42
  src/codeagent/backends/base.py
42
43
  src/codeagent/backends/local_backend.py
43
44
  src/codeagent/backends/openai_backend.py
44
- src/codeagent/extension/alpiecode.vsix
45
- test/test_audit_fixes.py
46
- test/test_benchmark_system1.py
47
- test/test_cache_reads.py
48
- test/test_chat_mode.py
49
- test/test_claude_codex_capabilities.py
50
- test/test_context_filter.py
51
- test/test_metrics.py
52
- test/test_offline_mode.py
53
- test/test_phase1.py
54
- test/test_phase2.py
55
- test/test_phase3.py
56
- test/test_system1.py
45
+ src/codeagent/extension/alpiecode.vsix
@@ -17,7 +17,7 @@ Supports:
17
17
  from pathlib import Path
18
18
  from typing import Optional, Any, Dict
19
19
 
20
- __version__ = "10.0.2"
20
+ __version__ = "10.5.0"
21
21
 
22
22
  def run(task: str, workdir: str = ".", reasoning_level: str = "thinking", **kwargs):
23
23
  """Run an autonomous coding task programmatically."""
@@ -340,7 +340,8 @@ def run_agent(
340
340
  console.print(f"📺 URL: {url}", style="cyan")
341
341
 
342
342
  elif event.type == "adaptive_mode" and verbose and HAS_RICH:
343
- console.print("⚡ [dim]Adaptive mode: simple task detected, skipping deep reasoning for speed[/dim]")
343
+ msg = event.data.get("message") or "Adaptive mode: reasoning trace adjusted."
344
+ console.print(f"⚡ [dim]{msg}[/dim]")
344
345
 
345
346
  elif event.type == "status":
346
347
  phase = event.data.get("phase")
@@ -99,8 +99,17 @@ class LocalBackend:
99
99
  fn = tc.get("function", {})
100
100
  name = fn.get("name", "unknown")
101
101
  args = fn.get("arguments", "{}")
102
+ # Do not duplicate full file content in context history
103
+ if name in ("write_file", "edit_file"):
104
+ try:
105
+ import json as _json
106
+ a_dict = _json.loads(args) if isinstance(args, str) else args
107
+ p = a_dict.get("path") or a_dict.get("filename") or ""
108
+ args = f'{"path": "{p}"}'
109
+ except Exception:
110
+ args = "{...}"
102
111
  tc_descriptions.append(f"[Called tool: {name}({args})]")
103
- if tc_descriptions:
112
+ if tc_descriptions and "<tool_call>" not in content:
104
113
  content = (content + "\n" + "\n".join(tc_descriptions)).strip()
105
114
  # Do NOT include tool_calls key — GGUF template will crash on it
106
115
 
@@ -202,6 +211,9 @@ class LocalBackend:
202
211
  full_content = []
203
212
  full_reasoning = []
204
213
  tool_calls_dict = {}
214
+ # In non-thinking mode, swallow any accidental <think>...</think> generated by model
215
+ suppress_think = not enable_thinking
216
+ inside_suppressed_think = False
205
217
 
206
218
  for chunk in chunks:
207
219
  if not chunk or not chunk.get("choices"):
@@ -210,7 +222,7 @@ class LocalBackend:
210
222
  delta = choice.get("delta") or {}
211
223
 
212
224
  explicit_reasoning = delta.get("reasoning") or delta.get("reasoning_content")
213
- if explicit_reasoning:
225
+ if explicit_reasoning and enable_thinking:
214
226
  if not think_started:
215
227
  think_started = True
216
228
  yield ("thinking_start", {"time": start_time})
@@ -238,6 +250,25 @@ class LocalBackend:
238
250
 
239
251
  text = delta.get("content")
240
252
  if text:
253
+ if suppress_think:
254
+ # Silently swallow any think tags/blocks emitted in fast mode
255
+ if "<think>" in text:
256
+ inside_suppressed_think = True
257
+ text = text.replace("<think>", "")
258
+ if inside_suppressed_think:
259
+ if "<tool_call>" in text or "DONE:" in text or "<function=" in text:
260
+ inside_suppressed_think = False
261
+ elif "</think>" in text:
262
+ _, after = text.split("</think>", 1)
263
+ inside_suppressed_think = False
264
+ text = after
265
+ else:
266
+ text = ""
267
+ else:
268
+ text = text.replace("</think>", "")
269
+ if not text:
270
+ continue
271
+
241
272
  if in_think:
242
273
  if not think_started:
243
274
  think_started = True
@@ -286,6 +317,10 @@ class LocalBackend:
286
317
  tool_calls.append(ToolCall(id=item["id"], name=item["name"], arguments=args))
287
318
 
288
319
  final_content_str = "".join(full_content).strip()
320
+ if not enable_thinking and final_content_str:
321
+ import re
322
+ final_content_str = re.sub(r"^\s*<think>.*?</think>\s*", "", final_content_str, flags=re.DOTALL).strip()
323
+ final_content_str = re.sub(r"^\s*(?:</think>|<think>)*\s*", "", final_content_str).strip()
289
324
  final_reasoning_str = "".join(full_reasoning).strip()
290
325
 
291
326
  if not final_content_str and final_reasoning_str:
@@ -57,11 +57,15 @@ def _repair_and_parse_json_arguments(raw_args: str) -> dict:
57
57
  # Check unclosed quotes: count non-escaped double quotes
58
58
  quote_count = len(re.findall(r'(?<!\\)"', repaired))
59
59
  if quote_count % 2 != 0:
60
+ dangling_bs = len(repaired) - len(repaired.rstrip('\\'))
61
+ if dangling_bs % 2 != 0:
62
+ repaired = repaired[:-1]
60
63
  repaired += '"'
61
64
 
62
- # Balance unclosed brackets and braces
63
- open_curly = repaired.count("{") - repaired.count("}")
64
- open_square = repaired.count("[") - repaired.count("]")
65
+ # Balance unclosed brackets and braces outside string literals
66
+ stripped = re.sub(r'"(?:\\.|[^"\\])*"', '""', repaired)
67
+ open_curly = stripped.count("{") - stripped.count("}")
68
+ open_square = stripped.count("[") - stripped.count("]")
65
69
 
66
70
  if open_square > 0:
67
71
  repaired += "]" * open_square
@@ -91,6 +95,9 @@ def _repair_and_parse_json_arguments(raw_args: str) -> dict:
91
95
  extracted[k] = v_lit
92
96
 
93
97
  if extracted:
98
+ if "name" in extracted and "arguments" not in extracted:
99
+ args = {k: v for k, v in extracted.items() if k not in ("name", "id", "type")}
100
+ return {"name": extracted["name"], "arguments": args}
94
101
  return extracted
95
102
 
96
103
  logger.warning("Failed to parse tool call arguments: %s", raw_args[:200])
@@ -246,15 +246,32 @@ class FileReadCache:
246
246
 
247
247
 
248
248
  class TestResultCache:
249
- """Caches test command execution results when workspace files are unchanged."""
249
+ """Caches test command execution results when workspace files are unchanged.
250
+ Persists across CLI runs in ~/.alpiecode/cache/test_cache.json
251
+ """
250
252
  __test__ = False
251
253
 
252
254
  def __init__(self, max_entries: int = 100):
253
255
  self.max_entries = max_entries
256
+ self.disk_file = CACHE_DIR / "test_cache.json"
254
257
  self._cache: Dict[str, Dict[str, Any]] = {}
255
- self._workspace_versions: Dict[str, float] = {}
256
258
  self.hits = 0
257
259
  self.misses = 0
260
+ self._load_disk()
261
+
262
+ def _load_disk(self):
263
+ if self.disk_file.exists():
264
+ try:
265
+ self._cache = json.loads(self.disk_file.read_text(encoding="utf-8"))
266
+ except Exception:
267
+ self._cache = {}
268
+
269
+ def _save_disk(self):
270
+ try:
271
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
272
+ self.disk_file.write_text(json.dumps(self._cache, indent=2), encoding="utf-8")
273
+ except Exception:
274
+ pass
258
275
 
259
276
  def _is_test_cmd(self, cmd: str) -> bool:
260
277
  c = cmd.strip().lower()
@@ -266,10 +283,21 @@ class TestResultCache:
266
283
  or "go test" in c
267
284
  )
268
285
 
286
+ def _get_workspace_fingerprint(self, workdir: Path) -> str:
287
+ try:
288
+ mtimes = []
289
+ for ext in ("*.py", "*.js", "*.ts", "*.json"):
290
+ for p in workdir.glob(ext):
291
+ if p.is_file() and not p.name.startswith("."):
292
+ mtimes.append(f"{p.name}:{p.stat().st_mtime_ns}")
293
+ return hashlib.md5("".join(sorted(mtimes)).encode()).hexdigest()[:12]
294
+ except Exception:
295
+ return "0"
296
+
269
297
  def get(self, workdir: Path, cmd: str) -> Optional[str]:
270
298
  if not self._is_test_cmd(cmd):
271
299
  return None
272
- version = self._workspace_versions.get(str(workdir.resolve()), 0.0)
300
+ version = self._get_workspace_fingerprint(workdir)
273
301
  key = f"{workdir.resolve()}:{cmd.strip()}:{version}"
274
302
  entry = self._cache.get(key)
275
303
  if entry is not None:
@@ -281,17 +309,20 @@ class TestResultCache:
281
309
  def put(self, workdir: Path, cmd: str, result: str) -> None:
282
310
  if not self._is_test_cmd(cmd):
283
311
  return
284
- version = self._workspace_versions.get(str(workdir.resolve()), 0.0)
312
+ version = self._get_workspace_fingerprint(workdir)
285
313
  key = f"{workdir.resolve()}:{cmd.strip()}:{version}"
286
314
  self._cache[key] = {"content": result, "cached_at": time.time()}
315
+ self._save_disk()
287
316
 
288
317
  def invalidate(self, workdir: Optional[Path] = None) -> None:
289
318
  if workdir is None:
290
319
  self._cache.clear()
291
- self._workspace_versions.clear()
292
- return
293
- w_key = str(workdir.resolve())
294
- self._workspace_versions[w_key] = time.time()
320
+ else:
321
+ prefix = f"{workdir.resolve()}:"
322
+ keys_to_del = [k for k in self._cache if k.startswith(prefix)]
323
+ for k in keys_to_del:
324
+ self._cache.pop(k, None)
325
+ self._save_disk()
295
326
 
296
327
 
297
328
  _FILE_CACHE: Optional[FileReadCache] = None
@@ -29,7 +29,7 @@ def _show_banner():
29
29
 
30
30
 
31
31
  def _normalize_args():
32
- known_commands = {"init", "serve", "run", "chat", "plan", "diff", "undo", "doctor", "explain", "version", "-h", "--help", "-v", "--version"}
32
+ known_commands = {"init", "serve", "run", "chat", "plan", "diff", "undo", "doctor", "explain", "dream", "version", "-h", "--help", "-v", "--version"}
33
33
  subcommand = None
34
34
 
35
35
  # Check if a subcommand is present
@@ -87,6 +87,7 @@ def main():
87
87
  common.add_argument("--quiet", action="store_true", help="Suppress per-turn logging")
88
88
  common.add_argument("--debug", action="store_true", help="Show autonomous discovery and debug diagnostics")
89
89
  common.add_argument("--offline", "--local", dest="offline", action="store_true", help="Run using local offline GGUF model on GPU without internet")
90
+ common.add_argument("--ctx-size", type=int, default=None, help="Context size in tokens (default: 4096 offline, 262144 online)")
90
91
 
91
92
  from . import __version__
92
93
  parser = argparse.ArgumentParser(
@@ -130,6 +131,12 @@ def main():
130
131
  explain_p = sub.add_parser("explain", help="Explain a file, function, or codebase concept", parents=[common])
131
132
  explain_p.add_argument("target", help="File path or concept to explain")
132
133
 
134
+ # ?? dream ??
135
+ dream_p = sub.add_parser('dream', help='Dream-RSI: offline recursive self-improvement simulation')
136
+ dream_p.add_argument('--optimize', action='store_true', help='Run offline dreaming simulation to synthesize optimal search policy')
137
+ dream_p.add_argument('--stats', action='store_true', help='Show trajectory replay metrics and active policy status')
138
+ dream_p.add_argument('--clear', action='store_true', help='Clear historical trajectory replay pool')
139
+
133
140
  args = parser.parse_args()
134
141
 
135
142
  if not args.command:
@@ -144,6 +151,52 @@ def main():
144
151
  interactive_init()
145
152
  return
146
153
 
154
+ if args.command == 'dream':
155
+ import time
156
+ from .dream_rsi import TrajectoryStore, DreamOptimizer, load_active_dream_profile
157
+ store = TrajectoryStore()
158
+ optimizer = DreamOptimizer(store)
159
+ if getattr(args, 'clear', False):
160
+ count = store.clear_trajectories()
161
+ print(f'?? Cleared {count} historical trajectories from replay pool.')
162
+ return
163
+ if getattr(args, 'optimize', False):
164
+ print('?? Running Dream-RSI offline recursive self-improvement simulation...')
165
+ profile, res = optimizer.optimize()
166
+ if res.get('status') == 'no_trajectories':
167
+ print('?? No historical trajectories recorded yet. Run tasks with alpiecode run to populate the replay buffer.')
168
+ return
169
+ metrics = res.get('metrics', {})
170
+ print('\n? Dream-RSI Optimization Complete!')
171
+ print(f' ?? Trajectories Replayed: {metrics.get("total_trajectories", 0)}')
172
+ print(f' ?? Total Turns Saved: {metrics.get("total_turns_saved", 0)}')
173
+ print(f' ?? Loops Prevented: {metrics.get("total_loops_prevented", 0)}')
174
+ print(f' ?? Stalls Averted: {metrics.get("total_stalls_averted", 0)}')
175
+ print(f' ? Simulation Fitness: {metrics.get("average_score", 0.0)}')
176
+ print('\n?? Active Policy Updated:')
177
+ print(f' ? Syntax Loop Threshold: {profile.syntax_loop_threshold}')
178
+ print(f' ? General Loop Threshold: {profile.general_loop_threshold}')
179
+ print(f' ? Stall Trigger: {profile.stall_threshold}')
180
+ print(f' ? Single-File Enforcement: {profile.single_file_enforcement}')
181
+ return
182
+ trajectories = store.list_trajectories()
183
+ profile = load_active_dream_profile()
184
+ successes = sum(1 for t in trajectories if t.outcome == 'success')
185
+ failures = sum(1 for t in trajectories if t.outcome != 'success')
186
+ print('\n?? Dream-RSI Status & Replay Pool')
187
+ print(f' ?? Replay Buffer: {len(trajectories)} trajectories stored')
188
+ print(f' ? Successful Runs: {successes}')
189
+ print(f' ?? Failed/Stalled Runs: {failures}')
190
+ print('\n?? Active Policy Profile:')
191
+ print(f' ? Syntax Loop Threshold: {profile.syntax_loop_threshold}')
192
+ print(f' ? General Loop Threshold: {profile.general_loop_threshold}')
193
+ print(f' ? Stall Trigger: {profile.stall_threshold}')
194
+ print(f' ? Single-File Enforcement: {profile.single_file_enforcement}')
195
+ opt_time = time.ctime(profile.last_optimized_at) if profile.last_optimized_at else 'Never'
196
+ print(f' ? Last Optimized: {opt_time}')
197
+ print('\n?? Tip: Run alpiecode dream --optimize to run offline self-improvement.')
198
+ return
199
+
147
200
  if args.command == "doctor":
148
201
  from .doctor import run_doctor
149
202
  sys.exit(run_doctor())
@@ -174,10 +227,14 @@ def main():
174
227
 
175
228
  if getattr(args, "offline", False):
176
229
  cfg.base_url = "offline"
177
- cfg.n_ctx = 16384
230
+ # 4096 default fits 4.4GB model + KV cache 100% in 6GB VRAM with zero host-paging
178
231
  import os
232
+ cfg.n_ctx = int(os.environ.get("ALPIECODE_CTX_SIZE", 4096))
179
233
  os.environ["ALPIECODE_BASE_URL"] = "offline"
180
234
 
235
+ if getattr(args, "ctx_size", None):
236
+ cfg.n_ctx = args.ctx_size
237
+
181
238
  if getattr(args, "thinking", False):
182
239
  cfg.enable_thinking = True
183
240
  elif getattr(args, "no_thinking", False):
@@ -565,6 +565,11 @@ def gather_relevant_context(
565
565
  return None
566
566
 
567
567
  task_lower = task.lower().strip()
568
+
569
+ # Skip pre-reading repo files for standalone algorithms or scripts (e.g. palindrome, calculator)
570
+ standalone_kws = ["palindrome", "fibonacci", "quicksort", "bubblesort", "factorial", "hello world", "calculator", "reverse string", "reverse_string"]
571
+ if any(kw in task_lower for kw in standalone_kws):
572
+ return None
568
573
  sections: List[str] = []
569
574
  chars_used = 0
570
575