pari-cli 0.1.2__tar.gz → 0.1.4__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.
- pari_cli-0.1.4/PKG-INFO +111 -0
- pari_cli-0.1.4/README.md +68 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/__init__.py +1 -3
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/__main__.py +2 -0
- pari_cli-0.1.4/pari/agent/__init__.py +43 -0
- pari_cli-0.1.4/pari/agent/guards.py +35 -0
- pari_cli-0.1.4/pari/agent/lint.py +49 -0
- pari_cli-0.1.4/pari/agent/loop.py +389 -0
- pari_cli-0.1.4/pari/agent/reflection.py +75 -0
- pari_cli-0.1.4/pari/agent/runtime.py +248 -0
- pari_cli-0.1.4/pari/agent/subagent.py +58 -0
- pari_cli-0.1.4/pari/auth/__init__.py +13 -0
- pari_cli-0.1.4/pari/auth/account.py +731 -0
- pari_cli-0.1.4/pari/auth/interactive.py +524 -0
- pari_cli-0.1.4/pari/auth/login.py +207 -0
- pari_cli-0.1.4/pari/auth/menu.py +175 -0
- pari_cli-0.1.4/pari/auth/providers.py +82 -0
- pari_cli-0.1.4/pari/auth/store.py +43 -0
- pari_cli-0.1.4/pari/cli.py +328 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/config.py +2 -3
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/context/__init__.py +18 -1
- pari_cli-0.1.4/pari/context/cache.py +31 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/context/chunks.py +17 -4
- pari_cli-0.1.4/pari/context/compaction.py +58 -0
- pari_cli-0.1.4/pari/context/epoch.py +56 -0
- pari_cli-0.1.4/pari/context/warmer.py +96 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/edit/__init__.py +24 -1
- pari_cli-0.1.4/pari/edit/cascade.py +76 -0
- pari_cli-0.1.4/pari/edit/diff.py +19 -0
- pari_cli-0.1.4/pari/edit/dmp.py +46 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/edit/exact.py +28 -6
- pari_cli-0.1.4/pari/edit/git_osr.py +45 -0
- pari_cli-0.1.4/pari/edit/indenter.py +77 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/__init__.py +6 -3
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/judge.py +8 -4
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/logging.py +2 -2
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/metrics.py +2 -2
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/runner.py +4 -4
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/fs/__init__.py +11 -3
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/fs/atomic.py +4 -3
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/fs/path.py +7 -5
- pari_cli-0.1.4/pari/fs/queue.py +51 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/intelligence/__init__.py +18 -2
- pari_cli-0.1.4/pari/intelligence/ast.py +58 -0
- pari_cli-0.1.4/pari/intelligence/cache.py +66 -0
- pari_cli-0.1.4/pari/intelligence/ppr.py +67 -0
- pari_cli-0.1.4/pari/intelligence/repomap.py +128 -0
- pari_cli-0.1.4/pari/intelligence/search.py +90 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/output/__init__.py +13 -2
- pari_cli-0.1.4/pari/output/parser.py +18 -0
- pari_cli-0.1.4/pari/output/spooler.py +94 -0
- pari_cli-0.1.4/pari/output/truncation.py +29 -0
- pari_cli-0.1.4/pari/providers/__init__.py +66 -0
- pari_cli-0.1.4/pari/providers/anthropic.py +167 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/providers/base.py +11 -10
- pari_cli-0.1.4/pari/providers/deepseek.py +28 -0
- pari_cli-0.1.4/pari/providers/grok.py +29 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/providers/mock.py +5 -4
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/providers/ollama.py +11 -5
- pari_cli-0.1.4/pari/providers/openai.py +29 -0
- pari_cli-0.1.4/pari/providers/openai_compat.py +157 -0
- pari_cli-0.1.4/pari/providers/openrouter.py +29 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/routing/__init__.py +18 -4
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/routing/anti_verbosity.py +9 -2
- pari_cli-0.1.4/pari/routing/classifier.py +33 -0
- pari_cli-0.1.4/pari/routing/router.py +135 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/sandbox/__init__.py +14 -1
- pari_cli-0.1.4/pari/sandbox/base.py +386 -0
- pari_cli-0.1.4/pari/sandbox/bwrap.py +159 -0
- pari_cli-0.1.4/pari/sandbox/dispatcher.py +52 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/sandbox/e2b.py +11 -6
- pari_cli-0.1.4/pari/sandbox/process.py +54 -0
- pari_cli-0.1.4/pari/sandbox/seatbelt.py +30 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/session/__init__.py +21 -4
- pari_cli-0.1.4/pari/session/checkpoint.py +43 -0
- pari_cli-0.1.4/pari/session/dag.py +56 -0
- pari_cli-0.1.4/pari/session/events.py +92 -0
- pari_cli-0.1.4/pari/session/logger.py +149 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/session/undo.py +17 -24
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tools/__init__.py +4 -1
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tools/base.py +8 -6
- pari_cli-0.1.4/pari/tools/bash.py +82 -0
- pari_cli-0.1.4/pari/tools/fs.py +159 -0
- pari_cli-0.1.4/pari/tools/output_reader.py +75 -0
- pari_cli-0.1.4/pari/tools/registry.py +71 -0
- pari_cli-0.1.4/pari/tools/search.py +98 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tui/__init__.py +9 -4
- pari_cli-0.1.4/pari/tui/app.py +238 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tui/commands.py +6 -6
- pari_cli-0.1.4/pari/tui/completer.py +55 -0
- pari_cli-0.1.4/pari/tui/diff_view.py +22 -0
- pari_cli-0.1.4/pari/tui/keys.py +46 -0
- pari_cli-0.1.4/pari/tui/prompt.py +84 -0
- pari_cli-0.1.4/pari/tui/slash.py +126 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tui/status.py +34 -5
- pari_cli-0.1.4/pari/tui/style.py +27 -0
- pari_cli-0.1.4/pari_cli.egg-info/PKG-INFO +111 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/SOURCES.txt +19 -12
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/entry_points.txt +0 -1
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/requires.txt +4 -1
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pyproject.toml +52 -3
- pari_cli-0.1.2/PKG-INFO +0 -172
- pari_cli-0.1.2/README.md +0 -132
- pari_cli-0.1.2/pari/agent/__init__.py +0 -14
- pari_cli-0.1.2/pari/agent/loop.py +0 -35
- pari_cli-0.1.2/pari/agent/reflection.py +0 -39
- pari_cli-0.1.2/pari/agent/runtime.py +0 -91
- pari_cli-0.1.2/pari/agent/subagent.py +0 -34
- pari_cli-0.1.2/pari/cli.py +0 -132
- pari_cli-0.1.2/pari/context/cache.py +0 -15
- pari_cli-0.1.2/pari/context/compaction.py +0 -21
- pari_cli-0.1.2/pari/context/epoch.py +0 -32
- pari_cli-0.1.2/pari/context/warmer.py +0 -32
- pari_cli-0.1.2/pari/edit/cascade.py +0 -22
- pari_cli-0.1.2/pari/edit/diff.py +0 -8
- pari_cli-0.1.2/pari/edit/dmp.py +0 -15
- pari_cli-0.1.2/pari/edit/git_osr.py +0 -14
- pari_cli-0.1.2/pari/edit/indenter.py +0 -12
- pari_cli-0.1.2/pari/fs/queue.py +0 -22
- pari_cli-0.1.2/pari/intelligence/ast.py +0 -25
- pari_cli-0.1.2/pari/intelligence/cache.py +0 -21
- pari_cli-0.1.2/pari/intelligence/ppr.py +0 -21
- pari_cli-0.1.2/pari/intelligence/repomap.py +0 -21
- pari_cli-0.1.2/pari/intelligence/search.py +0 -33
- pari_cli-0.1.2/pari/output/parser.py +0 -14
- pari_cli-0.1.2/pari/output/spooler.py +0 -33
- pari_cli-0.1.2/pari/output/truncation.py +0 -20
- pari_cli-0.1.2/pari/providers/__init__.py +0 -24
- pari_cli-0.1.2/pari/providers/anthropic.py +0 -23
- pari_cli-0.1.2/pari/providers/deepseek.py +0 -24
- pari_cli-0.1.2/pari/providers/openai.py +0 -23
- pari_cli-0.1.2/pari/routing/classifier.py +0 -24
- pari_cli-0.1.2/pari/routing/router.py +0 -23
- pari_cli-0.1.2/pari/sandbox/base.py +0 -74
- pari_cli-0.1.2/pari/sandbox/bwrap.py +0 -39
- pari_cli-0.1.2/pari/sandbox/dispatcher.py +0 -31
- pari_cli-0.1.2/pari/sandbox/process.py +0 -38
- pari_cli-0.1.2/pari/sandbox/seatbelt.py +0 -32
- pari_cli-0.1.2/pari/session/checkpoint.py +0 -24
- pari_cli-0.1.2/pari/session/dag.py +0 -34
- pari_cli-0.1.2/pari/session/events.py +0 -50
- pari_cli-0.1.2/pari/session/logger.py +0 -41
- pari_cli-0.1.2/pari/tools/bash.py +0 -24
- pari_cli-0.1.2/pari/tools/fs.py +0 -53
- pari_cli-0.1.2/pari/tools/registry.py +0 -36
- pari_cli-0.1.2/pari/tools/search.py +0 -38
- pari_cli-0.1.2/pari/tui/app.py +0 -356
- pari_cli-0.1.2/pari/tui/diff_view.py +0 -17
- pari_cli-0.1.2/pari_cli.egg-info/PKG-INFO +0 -172
- pari_cli-0.1.2/tests/test_agent_runtime.py +0 -55
- pari_cli-0.1.2/tests/test_context.py +0 -38
- pari_cli-0.1.2/tests/test_edit.py +0 -53
- pari_cli-0.1.2/tests/test_fs.py +0 -48
- pari_cli-0.1.2/tests/test_output.py +0 -38
- pari_cli-0.1.2/tests/test_routing.py +0 -21
- pari_cli-0.1.2/tests/test_sandbox.py +0 -20
- pari_cli-0.1.2/tests/test_search.py +0 -26
- pari_cli-0.1.2/tests/test_session.py +0 -41
- pari_cli-0.1.2/tests/test_tools.py +0 -41
- pari_cli-0.1.2/tests/test_tui.py +0 -103
- {pari_cli-0.1.2 → pari_cli-0.1.4}/LICENSE +0 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/dependency_links.txt +0 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/top_level.txt +0 -0
- {pari_cli-0.1.2 → pari_cli-0.1.4}/setup.cfg +0 -0
pari_cli-0.1.4/PKG-INFO
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pari-cli
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: A resilient, sandbox-first terminal coding agent engineered for high pass rates and low token burn.
|
|
5
|
+
Author: PARI Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pari-cli.in
|
|
8
|
+
Project-URL: Documentation, https://pari-cli.in
|
|
9
|
+
Project-URL: Repository, https://github.com/MdTanwer/pari
|
|
10
|
+
Keywords: ai,coding-agent,terminal,llm,developer-tools
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: rich>=13.0.0
|
|
24
|
+
Requires-Dist: prompt_toolkit>=3.0.36
|
|
25
|
+
Requires-Dist: pydantic>=2.0.0
|
|
26
|
+
Requires-Dist: httpx>=0.24.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.12.10; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit>=4.3.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pycodestyle>=2.12.0; extra == "dev"
|
|
33
|
+
Provides-Extra: ast
|
|
34
|
+
Requires-Dist: tree-sitter>=0.21.0; extra == "ast"
|
|
35
|
+
Requires-Dist: tree-sitter-languages>=1.10.0; extra == "ast"
|
|
36
|
+
Requires-Dist: networkx>=3.0; extra == "ast"
|
|
37
|
+
Requires-Dist: grep-ast>=0.3.0; extra == "ast"
|
|
38
|
+
Provides-Extra: cloud
|
|
39
|
+
Requires-Dist: e2b>=0.14.0; extra == "cloud"
|
|
40
|
+
Provides-Extra: all
|
|
41
|
+
Requires-Dist: pari-cli[ast,cloud,dev]; extra == "all"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# PARI — Next-Generation Terminal Coding Agent
|
|
45
|
+
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
[](https://www.python.org/downloads/)
|
|
48
|
+
[](docs/architecture/overview.md)
|
|
49
|
+
[](docs/layers/layer-0-sandboxing.md)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## ⚡ The Core Thesis
|
|
54
|
+
|
|
55
|
+
$$\text{\bf Agent failure is a systems engineering failure, not a reasoning failure.}$$
|
|
56
|
+
|
|
57
|
+
The exact same frontier reasoning model (Claude 3.7 Sonnet, GPT-4o) demonstrates a **15% pass rate** in a naive loop, but achieves a **50–55% pass rate** inside an engineered harness. The 35% delta is not model intelligence; it is accounted for by **six fatal systems failure modes**:
|
|
58
|
+
|
|
59
|
+
1. **Unbounded stdout poisoning** $\rightarrow$ Solved by Layer 7 (50KB/2k-line disk spooler + balanced head/tail extraction).
|
|
60
|
+
2. **Mid-stream token truncation** $\rightarrow$ Solved by Layer 4 (Strict `stop_reason == "length"` rejection guard).
|
|
61
|
+
3. **File write race conditions** $\rightarrow$ Solved by Layer 6 (`FileMutationQueue` canonical realpath serialization).
|
|
62
|
+
4. **KV cache invalidation cascades** $\rightarrow$ Solved by Layer 2 (Immutable Baseline Epochs freezing Tiers 1–4).
|
|
63
|
+
5. **Blind file traversal loops** $\rightarrow$ Solved by Layer 3 (v1: Fast substring ripgrep & globbing / v2: Tree-sitter AST fallback).
|
|
64
|
+
6. **Unsandboxed host execution** $\rightarrow$ Solved by Layer 0 (Multi-platform kernel isolation: `bwrap`, `Seatbelt`, E2B MicroVMs).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 🚀 Installation & Quick Start
|
|
69
|
+
|
|
70
|
+
Official Website & Documentation: **[https://pari-cli.in](https://pari-cli.in)**
|
|
71
|
+
|
|
72
|
+
Choose your platform's installation command:
|
|
73
|
+
|
|
74
|
+
### 1. macOS & Linux (`curl`)
|
|
75
|
+
```bash
|
|
76
|
+
curl -fsSL https://pari-cli.in/install.sh | bash
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Windows (`irm` in PowerShell)
|
|
80
|
+
```powershell
|
|
81
|
+
irm https://www.pari-cli.in/install.ps1 | iex
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Via `pip` / `pipx` / `uv`
|
|
85
|
+
```bash
|
|
86
|
+
# Recommended (isolated CLI)
|
|
87
|
+
pipx install pari-cli
|
|
88
|
+
|
|
89
|
+
# Fast via uv
|
|
90
|
+
uv tool install pari-cli
|
|
91
|
+
|
|
92
|
+
# Standard pip
|
|
93
|
+
pip install pari-cli
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### 💻 Launch Interactive Session
|
|
99
|
+
```bash
|
|
100
|
+
# Export your API key
|
|
101
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
102
|
+
|
|
103
|
+
# Launch PARI anywhere in your project workspace
|
|
104
|
+
pari-cli
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## ⚖️ License
|
|
110
|
+
|
|
111
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
pari_cli-0.1.4/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# PARI — Next-Generation Terminal Coding Agent
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](docs/architecture/overview.md)
|
|
6
|
+
[](docs/layers/layer-0-sandboxing.md)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ⚡ The Core Thesis
|
|
11
|
+
|
|
12
|
+
$$\text{\bf Agent failure is a systems engineering failure, not a reasoning failure.}$$
|
|
13
|
+
|
|
14
|
+
The exact same frontier reasoning model (Claude 3.7 Sonnet, GPT-4o) demonstrates a **15% pass rate** in a naive loop, but achieves a **50–55% pass rate** inside an engineered harness. The 35% delta is not model intelligence; it is accounted for by **six fatal systems failure modes**:
|
|
15
|
+
|
|
16
|
+
1. **Unbounded stdout poisoning** $\rightarrow$ Solved by Layer 7 (50KB/2k-line disk spooler + balanced head/tail extraction).
|
|
17
|
+
2. **Mid-stream token truncation** $\rightarrow$ Solved by Layer 4 (Strict `stop_reason == "length"` rejection guard).
|
|
18
|
+
3. **File write race conditions** $\rightarrow$ Solved by Layer 6 (`FileMutationQueue` canonical realpath serialization).
|
|
19
|
+
4. **KV cache invalidation cascades** $\rightarrow$ Solved by Layer 2 (Immutable Baseline Epochs freezing Tiers 1–4).
|
|
20
|
+
5. **Blind file traversal loops** $\rightarrow$ Solved by Layer 3 (v1: Fast substring ripgrep & globbing / v2: Tree-sitter AST fallback).
|
|
21
|
+
6. **Unsandboxed host execution** $\rightarrow$ Solved by Layer 0 (Multi-platform kernel isolation: `bwrap`, `Seatbelt`, E2B MicroVMs).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Installation & Quick Start
|
|
26
|
+
|
|
27
|
+
Official Website & Documentation: **[https://pari-cli.in](https://pari-cli.in)**
|
|
28
|
+
|
|
29
|
+
Choose your platform's installation command:
|
|
30
|
+
|
|
31
|
+
### 1. macOS & Linux (`curl`)
|
|
32
|
+
```bash
|
|
33
|
+
curl -fsSL https://pari-cli.in/install.sh | bash
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Windows (`irm` in PowerShell)
|
|
37
|
+
```powershell
|
|
38
|
+
irm https://www.pari-cli.in/install.ps1 | iex
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 3. Via `pip` / `pipx` / `uv`
|
|
42
|
+
```bash
|
|
43
|
+
# Recommended (isolated CLI)
|
|
44
|
+
pipx install pari-cli
|
|
45
|
+
|
|
46
|
+
# Fast via uv
|
|
47
|
+
uv tool install pari-cli
|
|
48
|
+
|
|
49
|
+
# Standard pip
|
|
50
|
+
pip install pari-cli
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### 💻 Launch Interactive Session
|
|
56
|
+
```bash
|
|
57
|
+
# Export your API key
|
|
58
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
59
|
+
|
|
60
|
+
# Launch PARI anywhere in your project workspace
|
|
61
|
+
pari-cli
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## ⚖️ License
|
|
67
|
+
|
|
68
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""PARI Layer 4: The Execution Loop (Agent Runtime).
|
|
2
|
+
|
|
3
|
+
Architectural Purpose:
|
|
4
|
+
Implements the double-while execution loop with stop_reason == "length" truncation
|
|
5
|
+
guard,
|
|
6
|
+
bounded reflection retry limits, and lightweight asyncio fiber-based subagents.
|
|
7
|
+
|
|
8
|
+
Subsystems:
|
|
9
|
+
- loop: Core double-while agent loop with continuation handling
|
|
10
|
+
- runtime: Turn execution, tool orchestration, and state transitions
|
|
11
|
+
- reflection: Structured error injection with max 3 reflection retries
|
|
12
|
+
- subagent: Fiber-based read-only research subagents (closing Pi F2 RAM bloat)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from pari.agent.guards import (
|
|
16
|
+
DAEMON_IDLE_SECONDS,
|
|
17
|
+
LENGTH_TRUNCATION_NOTICE,
|
|
18
|
+
MAX_TURNS,
|
|
19
|
+
TURN_WARNING,
|
|
20
|
+
format_daemon_notice,
|
|
21
|
+
is_length_stop,
|
|
22
|
+
)
|
|
23
|
+
from pari.agent.loop import AgentExecutionLoop, TurnResult
|
|
24
|
+
from pari.agent.reflection import ReflectionManager, format_lint_error
|
|
25
|
+
from pari.agent.runtime import AgentRuntime
|
|
26
|
+
from pari.agent.subagent import FiberSubagentPool, SubagentTask
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"DAEMON_IDLE_SECONDS",
|
|
31
|
+
"LENGTH_TRUNCATION_NOTICE",
|
|
32
|
+
"MAX_TURNS",
|
|
33
|
+
"TURN_WARNING",
|
|
34
|
+
"AgentExecutionLoop",
|
|
35
|
+
"AgentRuntime",
|
|
36
|
+
"FiberSubagentPool",
|
|
37
|
+
"ReflectionManager",
|
|
38
|
+
"SubagentTask",
|
|
39
|
+
"TurnResult",
|
|
40
|
+
"format_daemon_notice",
|
|
41
|
+
"format_lint_error",
|
|
42
|
+
"is_length_stop",
|
|
43
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Constants and checks the execution loop applies before it runs a tool.
|
|
2
|
+
|
|
3
|
+
The loop itself decides when to stop. These values are the limits it checks.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
MAX_TURNS = 40
|
|
7
|
+
TURN_WARNING = 30
|
|
8
|
+
TURN_EXTENSION = 10
|
|
9
|
+
DAEMON_IDLE_SECONDS = 10
|
|
10
|
+
TURN_LIMIT_PROMPT = "[Turn limit reached. Continue (+10 turns) | Revert | Stop]?"
|
|
11
|
+
|
|
12
|
+
# Shown when stop_reason is "length". The loop must drop the pending tool
|
|
13
|
+
# calls before it appends this notice. A truncated edit must not touch disk.
|
|
14
|
+
LENGTH_TRUNCATION_NOTICE = (
|
|
15
|
+
"WARNING: Your previous tool call was truncated due to the "
|
|
16
|
+
"output token ceiling. No changes were written to disk. "
|
|
17
|
+
"Please break your modification into smaller, focused "
|
|
18
|
+
"search/replace blocks of <= 100 lines."
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def is_length_stop(stop_reason: str | None) -> bool:
|
|
23
|
+
"""True when the model hit max_tokens and the tool payload may be cut off."""
|
|
24
|
+
return stop_reason == "length"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def format_daemon_notice(command: str, idle_seconds: int = DAEMON_IDLE_SECONDS) -> str:
|
|
28
|
+
"""Notice for a command that kept streaming past the idle window."""
|
|
29
|
+
shown = command.strip() or "command"
|
|
30
|
+
return (
|
|
31
|
+
f"[DAEMON DETECTED]: Command '{shown}' did not exit after "
|
|
32
|
+
f"{idle_seconds}s. Process was backgrounded. "
|
|
33
|
+
"Stdout snapshot spooled to disk. "
|
|
34
|
+
"To manage background daemons, use background task APIs."
|
|
35
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Syntax check for a file the loop just wrote.
|
|
2
|
+
|
|
3
|
+
The check uses the standard library parser. It returns a short window around
|
|
4
|
+
the failing line so the next model turn does not receive a compiler log.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
|
|
9
|
+
from pari.agent.reflection import format_lint_error
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def lint_python(path: str, source: str) -> str | None:
|
|
13
|
+
"""Return a lint block when path is Python and source does not parse."""
|
|
14
|
+
if not path.endswith(".py"):
|
|
15
|
+
return None
|
|
16
|
+
try:
|
|
17
|
+
ast.parse(source)
|
|
18
|
+
except SyntaxError as exc:
|
|
19
|
+
line = exc.lineno or 1
|
|
20
|
+
return format_lint_error(
|
|
21
|
+
path,
|
|
22
|
+
line,
|
|
23
|
+
_enclosing(source, line),
|
|
24
|
+
_window(source, line, exc.msg),
|
|
25
|
+
)
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _enclosing(source: str, line: int) -> str:
|
|
30
|
+
rows = source.splitlines()
|
|
31
|
+
last = min(line, len(rows)) - 1
|
|
32
|
+
for index in range(last, -1, -1):
|
|
33
|
+
stripped = rows[index].lstrip()
|
|
34
|
+
if stripped.startswith(("def ", "async def ", "class ")):
|
|
35
|
+
return stripped
|
|
36
|
+
return "<module>"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _window(source: str, line: int, message: str) -> str:
|
|
40
|
+
rows = source.splitlines()
|
|
41
|
+
start = max(line - 5, 1)
|
|
42
|
+
end = min(line + 5, len(rows))
|
|
43
|
+
shown: list[str] = []
|
|
44
|
+
for number in range(start, end + 1):
|
|
45
|
+
text = rows[number - 1] if number <= len(rows) else ""
|
|
46
|
+
if number == line:
|
|
47
|
+
text = f"{text} # {message}"
|
|
48
|
+
shown.append(f"{number}: {text}")
|
|
49
|
+
return "\n".join(shown)
|