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.
Files changed (164) hide show
  1. pari_cli-0.1.4/PKG-INFO +111 -0
  2. pari_cli-0.1.4/README.md +68 -0
  3. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/__init__.py +1 -3
  4. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/__main__.py +2 -0
  5. pari_cli-0.1.4/pari/agent/__init__.py +43 -0
  6. pari_cli-0.1.4/pari/agent/guards.py +35 -0
  7. pari_cli-0.1.4/pari/agent/lint.py +49 -0
  8. pari_cli-0.1.4/pari/agent/loop.py +389 -0
  9. pari_cli-0.1.4/pari/agent/reflection.py +75 -0
  10. pari_cli-0.1.4/pari/agent/runtime.py +248 -0
  11. pari_cli-0.1.4/pari/agent/subagent.py +58 -0
  12. pari_cli-0.1.4/pari/auth/__init__.py +13 -0
  13. pari_cli-0.1.4/pari/auth/account.py +731 -0
  14. pari_cli-0.1.4/pari/auth/interactive.py +524 -0
  15. pari_cli-0.1.4/pari/auth/login.py +207 -0
  16. pari_cli-0.1.4/pari/auth/menu.py +175 -0
  17. pari_cli-0.1.4/pari/auth/providers.py +82 -0
  18. pari_cli-0.1.4/pari/auth/store.py +43 -0
  19. pari_cli-0.1.4/pari/cli.py +328 -0
  20. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/config.py +2 -3
  21. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/context/__init__.py +18 -1
  22. pari_cli-0.1.4/pari/context/cache.py +31 -0
  23. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/context/chunks.py +17 -4
  24. pari_cli-0.1.4/pari/context/compaction.py +58 -0
  25. pari_cli-0.1.4/pari/context/epoch.py +56 -0
  26. pari_cli-0.1.4/pari/context/warmer.py +96 -0
  27. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/edit/__init__.py +24 -1
  28. pari_cli-0.1.4/pari/edit/cascade.py +76 -0
  29. pari_cli-0.1.4/pari/edit/diff.py +19 -0
  30. pari_cli-0.1.4/pari/edit/dmp.py +46 -0
  31. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/edit/exact.py +28 -6
  32. pari_cli-0.1.4/pari/edit/git_osr.py +45 -0
  33. pari_cli-0.1.4/pari/edit/indenter.py +77 -0
  34. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/__init__.py +6 -3
  35. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/judge.py +8 -4
  36. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/logging.py +2 -2
  37. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/metrics.py +2 -2
  38. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/eval/runner.py +4 -4
  39. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/fs/__init__.py +11 -3
  40. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/fs/atomic.py +4 -3
  41. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/fs/path.py +7 -5
  42. pari_cli-0.1.4/pari/fs/queue.py +51 -0
  43. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/intelligence/__init__.py +18 -2
  44. pari_cli-0.1.4/pari/intelligence/ast.py +58 -0
  45. pari_cli-0.1.4/pari/intelligence/cache.py +66 -0
  46. pari_cli-0.1.4/pari/intelligence/ppr.py +67 -0
  47. pari_cli-0.1.4/pari/intelligence/repomap.py +128 -0
  48. pari_cli-0.1.4/pari/intelligence/search.py +90 -0
  49. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/output/__init__.py +13 -2
  50. pari_cli-0.1.4/pari/output/parser.py +18 -0
  51. pari_cli-0.1.4/pari/output/spooler.py +94 -0
  52. pari_cli-0.1.4/pari/output/truncation.py +29 -0
  53. pari_cli-0.1.4/pari/providers/__init__.py +66 -0
  54. pari_cli-0.1.4/pari/providers/anthropic.py +167 -0
  55. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/providers/base.py +11 -10
  56. pari_cli-0.1.4/pari/providers/deepseek.py +28 -0
  57. pari_cli-0.1.4/pari/providers/grok.py +29 -0
  58. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/providers/mock.py +5 -4
  59. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/providers/ollama.py +11 -5
  60. pari_cli-0.1.4/pari/providers/openai.py +29 -0
  61. pari_cli-0.1.4/pari/providers/openai_compat.py +157 -0
  62. pari_cli-0.1.4/pari/providers/openrouter.py +29 -0
  63. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/routing/__init__.py +18 -4
  64. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/routing/anti_verbosity.py +9 -2
  65. pari_cli-0.1.4/pari/routing/classifier.py +33 -0
  66. pari_cli-0.1.4/pari/routing/router.py +135 -0
  67. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/sandbox/__init__.py +14 -1
  68. pari_cli-0.1.4/pari/sandbox/base.py +386 -0
  69. pari_cli-0.1.4/pari/sandbox/bwrap.py +159 -0
  70. pari_cli-0.1.4/pari/sandbox/dispatcher.py +52 -0
  71. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/sandbox/e2b.py +11 -6
  72. pari_cli-0.1.4/pari/sandbox/process.py +54 -0
  73. pari_cli-0.1.4/pari/sandbox/seatbelt.py +30 -0
  74. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/session/__init__.py +21 -4
  75. pari_cli-0.1.4/pari/session/checkpoint.py +43 -0
  76. pari_cli-0.1.4/pari/session/dag.py +56 -0
  77. pari_cli-0.1.4/pari/session/events.py +92 -0
  78. pari_cli-0.1.4/pari/session/logger.py +149 -0
  79. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/session/undo.py +17 -24
  80. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tools/__init__.py +4 -1
  81. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tools/base.py +8 -6
  82. pari_cli-0.1.4/pari/tools/bash.py +82 -0
  83. pari_cli-0.1.4/pari/tools/fs.py +159 -0
  84. pari_cli-0.1.4/pari/tools/output_reader.py +75 -0
  85. pari_cli-0.1.4/pari/tools/registry.py +71 -0
  86. pari_cli-0.1.4/pari/tools/search.py +98 -0
  87. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tui/__init__.py +9 -4
  88. pari_cli-0.1.4/pari/tui/app.py +238 -0
  89. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tui/commands.py +6 -6
  90. pari_cli-0.1.4/pari/tui/completer.py +55 -0
  91. pari_cli-0.1.4/pari/tui/diff_view.py +22 -0
  92. pari_cli-0.1.4/pari/tui/keys.py +46 -0
  93. pari_cli-0.1.4/pari/tui/prompt.py +84 -0
  94. pari_cli-0.1.4/pari/tui/slash.py +126 -0
  95. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari/tui/status.py +34 -5
  96. pari_cli-0.1.4/pari/tui/style.py +27 -0
  97. pari_cli-0.1.4/pari_cli.egg-info/PKG-INFO +111 -0
  98. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/SOURCES.txt +19 -12
  99. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/entry_points.txt +0 -1
  100. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/requires.txt +4 -1
  101. {pari_cli-0.1.2 → pari_cli-0.1.4}/pyproject.toml +52 -3
  102. pari_cli-0.1.2/PKG-INFO +0 -172
  103. pari_cli-0.1.2/README.md +0 -132
  104. pari_cli-0.1.2/pari/agent/__init__.py +0 -14
  105. pari_cli-0.1.2/pari/agent/loop.py +0 -35
  106. pari_cli-0.1.2/pari/agent/reflection.py +0 -39
  107. pari_cli-0.1.2/pari/agent/runtime.py +0 -91
  108. pari_cli-0.1.2/pari/agent/subagent.py +0 -34
  109. pari_cli-0.1.2/pari/cli.py +0 -132
  110. pari_cli-0.1.2/pari/context/cache.py +0 -15
  111. pari_cli-0.1.2/pari/context/compaction.py +0 -21
  112. pari_cli-0.1.2/pari/context/epoch.py +0 -32
  113. pari_cli-0.1.2/pari/context/warmer.py +0 -32
  114. pari_cli-0.1.2/pari/edit/cascade.py +0 -22
  115. pari_cli-0.1.2/pari/edit/diff.py +0 -8
  116. pari_cli-0.1.2/pari/edit/dmp.py +0 -15
  117. pari_cli-0.1.2/pari/edit/git_osr.py +0 -14
  118. pari_cli-0.1.2/pari/edit/indenter.py +0 -12
  119. pari_cli-0.1.2/pari/fs/queue.py +0 -22
  120. pari_cli-0.1.2/pari/intelligence/ast.py +0 -25
  121. pari_cli-0.1.2/pari/intelligence/cache.py +0 -21
  122. pari_cli-0.1.2/pari/intelligence/ppr.py +0 -21
  123. pari_cli-0.1.2/pari/intelligence/repomap.py +0 -21
  124. pari_cli-0.1.2/pari/intelligence/search.py +0 -33
  125. pari_cli-0.1.2/pari/output/parser.py +0 -14
  126. pari_cli-0.1.2/pari/output/spooler.py +0 -33
  127. pari_cli-0.1.2/pari/output/truncation.py +0 -20
  128. pari_cli-0.1.2/pari/providers/__init__.py +0 -24
  129. pari_cli-0.1.2/pari/providers/anthropic.py +0 -23
  130. pari_cli-0.1.2/pari/providers/deepseek.py +0 -24
  131. pari_cli-0.1.2/pari/providers/openai.py +0 -23
  132. pari_cli-0.1.2/pari/routing/classifier.py +0 -24
  133. pari_cli-0.1.2/pari/routing/router.py +0 -23
  134. pari_cli-0.1.2/pari/sandbox/base.py +0 -74
  135. pari_cli-0.1.2/pari/sandbox/bwrap.py +0 -39
  136. pari_cli-0.1.2/pari/sandbox/dispatcher.py +0 -31
  137. pari_cli-0.1.2/pari/sandbox/process.py +0 -38
  138. pari_cli-0.1.2/pari/sandbox/seatbelt.py +0 -32
  139. pari_cli-0.1.2/pari/session/checkpoint.py +0 -24
  140. pari_cli-0.1.2/pari/session/dag.py +0 -34
  141. pari_cli-0.1.2/pari/session/events.py +0 -50
  142. pari_cli-0.1.2/pari/session/logger.py +0 -41
  143. pari_cli-0.1.2/pari/tools/bash.py +0 -24
  144. pari_cli-0.1.2/pari/tools/fs.py +0 -53
  145. pari_cli-0.1.2/pari/tools/registry.py +0 -36
  146. pari_cli-0.1.2/pari/tools/search.py +0 -38
  147. pari_cli-0.1.2/pari/tui/app.py +0 -356
  148. pari_cli-0.1.2/pari/tui/diff_view.py +0 -17
  149. pari_cli-0.1.2/pari_cli.egg-info/PKG-INFO +0 -172
  150. pari_cli-0.1.2/tests/test_agent_runtime.py +0 -55
  151. pari_cli-0.1.2/tests/test_context.py +0 -38
  152. pari_cli-0.1.2/tests/test_edit.py +0 -53
  153. pari_cli-0.1.2/tests/test_fs.py +0 -48
  154. pari_cli-0.1.2/tests/test_output.py +0 -38
  155. pari_cli-0.1.2/tests/test_routing.py +0 -21
  156. pari_cli-0.1.2/tests/test_sandbox.py +0 -20
  157. pari_cli-0.1.2/tests/test_search.py +0 -26
  158. pari_cli-0.1.2/tests/test_session.py +0 -41
  159. pari_cli-0.1.2/tests/test_tools.py +0 -41
  160. pari_cli-0.1.2/tests/test_tui.py +0 -103
  161. {pari_cli-0.1.2 → pari_cli-0.1.4}/LICENSE +0 -0
  162. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/dependency_links.txt +0 -0
  163. {pari_cli-0.1.2 → pari_cli-0.1.4}/pari_cli.egg-info/top_level.txt +0 -0
  164. {pari_cli-0.1.2 → pari_cli-0.1.4}/setup.cfg +0 -0
@@ -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: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
47
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
48
+ [![Architecture: 11-Layer Stack](https://img.shields.io/badge/architecture-11--layer-green.svg)](docs/architecture/overview.md)
49
+ [![Security: Kernel Sandboxed](https://img.shields.io/badge/sandbox-bwrap%20%7C%20Seatbelt%20%7C%20E2B-orange.svg)](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.
@@ -0,0 +1,68 @@
1
+ # PARI — Next-Generation Terminal Coding Agent
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
5
+ [![Architecture: 11-Layer Stack](https://img.shields.io/badge/architecture-11--layer-green.svg)](docs/architecture/overview.md)
6
+ [![Security: Kernel Sandboxed](https://img.shields.io/badge/sandbox-bwrap%20%7C%20Seatbelt%20%7C%20E2B-orange.svg)](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.
@@ -1,5 +1,3 @@
1
1
  """PARI — High-performance terminal coding agent."""
2
2
 
3
- __version__ = "0.1.2"
4
-
5
-
3
+ __version__ = "0.1.4"
@@ -6,7 +6,9 @@ Allows running PARI via:
6
6
  """
7
7
 
8
8
  import sys
9
+
9
10
  from pari.cli import main
10
11
 
12
+
11
13
  if __name__ == "__main__":
12
14
  sys.exit(main())
@@ -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)