sutra-agent 0.3.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 (37) hide show
  1. sutra_agent-0.3.0/LICENSE +21 -0
  2. sutra_agent-0.3.0/MANIFEST.in +9 -0
  3. sutra_agent-0.3.0/PKG-INFO +267 -0
  4. sutra_agent-0.3.0/README.md +243 -0
  5. sutra_agent-0.3.0/__init__.py +157 -0
  6. sutra_agent-0.3.0/claude_code.py +611 -0
  7. sutra_agent-0.3.0/cli.py +350 -0
  8. sutra_agent-0.3.0/codex.py +242 -0
  9. sutra_agent-0.3.0/config.py +155 -0
  10. sutra_agent-0.3.0/core.py +204 -0
  11. sutra_agent-0.3.0/discovery.py +212 -0
  12. sutra_agent-0.3.0/engine.py +657 -0
  13. sutra_agent-0.3.0/hermes.py +275 -0
  14. sutra_agent-0.3.0/hook.py +24 -0
  15. sutra_agent-0.3.0/installer.py +143 -0
  16. sutra_agent-0.3.0/jsonc.py +248 -0
  17. sutra_agent-0.3.0/mcp.py +22 -0
  18. sutra_agent-0.3.0/metrics.py +233 -0
  19. sutra_agent-0.3.0/opencode.py +406 -0
  20. sutra_agent-0.3.0/opencode_plugin.js +161 -0
  21. sutra_agent-0.3.0/plugin.yaml +8 -0
  22. sutra_agent-0.3.0/pyproject.toml +61 -0
  23. sutra_agent-0.3.0/recovery.py +508 -0
  24. sutra_agent-0.3.0/runner.py +34 -0
  25. sutra_agent-0.3.0/scripts/_sutra.py +27 -0
  26. sutra_agent-0.3.0/scripts/check_version.py +44 -0
  27. sutra_agent-0.3.0/scripts/sutra-claude-code-hook.py +157 -0
  28. sutra_agent-0.3.0/scripts/sutra-cli.py +165 -0
  29. sutra_agent-0.3.0/scripts/sutra-mcp-server.py +177 -0
  30. sutra_agent-0.3.0/setup.cfg +4 -0
  31. sutra_agent-0.3.0/store.py +543 -0
  32. sutra_agent-0.3.0/sutra_agent.egg-info/PKG-INFO +267 -0
  33. sutra_agent-0.3.0/sutra_agent.egg-info/SOURCES.txt +60 -0
  34. sutra_agent-0.3.0/sutra_agent.egg-info/dependency_links.txt +1 -0
  35. sutra_agent-0.3.0/sutra_agent.egg-info/entry_points.txt +4 -0
  36. sutra_agent-0.3.0/sutra_agent.egg-info/top_level.txt +1 -0
  37. sutra_agent-0.3.0/utf8.py +41 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 aler9
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ # The wheel carries the runtime package; the sdist carries the same files plus
2
+ # packaging metadata. The development test suite is not installed with the
3
+ # package (its conftest is what makes the checkout layout importable), so
4
+ # shipping half of it in the sdist is worse than shipping none.
5
+ prune tests
6
+
7
+ # Release tooling is useful to anyone rebuilding from the sdist, but it is not
8
+ # runtime code: the wheel excludes it through the explicit package-data list.
9
+ include scripts/check_version.py
@@ -0,0 +1,267 @@
1
+ Metadata-Version: 2.4
2
+ Name: sutra-agent
3
+ Version: 0.3.0
4
+ Summary: Deterministic tool-output reduction for AI agents, preserving the evidence that matters.
5
+ Author: Vishal Gurung
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://sutra-alive.vercel.app/
8
+ Project-URL: Repository, https://github.com/g0GobliN/sutra
9
+ Project-URL: Documentation, https://github.com/g0GobliN/sutra/blob/main/docs/README.md
10
+ Project-URL: Changelog, https://github.com/g0GobliN/sutra/blob/main/CHANGELOG.md
11
+ Project-URL: Issues, https://github.com/g0GobliN/sutra/issues
12
+ Keywords: ai-agents,context-compression,tool-output,mcp
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Dynamic: license-file
24
+
25
+ <p align="center">
26
+ <img src="https://raw.githubusercontent.com/g0GobliN/sutra/main/public/logo/sutraThumb.png" alt="SUTRA" width="520">
27
+ </p>
28
+
29
+ # SUTRA
30
+
31
+ **Less output. More signal.**
32
+
33
+ SUTRA is a deterministic tool-output optimization and recovery layer for AI coding agents. It processes eligible tool results after a command runs and before the model reads them, so a large log costs less context on every following turn — without a model paraphrasing it.
34
+
35
+ One core. Multiple agents. Recoverable output. No compression model calls.
36
+
37
+ > **Eight-fixture benchmark:** payload size reduced by **54.3%**, o200k tokenizer counts by **55.2%**; **8/8** planted clues recovered; **0** diffs touched. The fixtures are generated tool-output shapes and the token figure is not a provider bill — [method](#evidence-and-benchmarks).
38
+
39
+ [Why](#the-problem-tool-output-is-re-read-all-session) · [How it works](#how-sutra-handles-a-tool-result) · [Architecture](#architecture) · [Comparison](#how-sutra-compares-with-caveman-and-ponytail) · [Evidence](#evidence-and-benchmarks) · [Install](#install) · [Integrations](#supported-integrations) · [Limitations](#limitations)
40
+
41
+ ## The problem: tool output is re-read all session
42
+
43
+ An agent runs `pytest -q`, receives 12,000 lines, and needs one failure. The useful part may be twenty lines, but the tool result stays in context and is re-sent on every subsequent turn. Prompt compression can shave an instruction once; tool output grows with every command an agent runs.
44
+
45
+ ```text
46
+ turn 1 ── tool output 60.9 kB ──► model
47
+ turn 2 ── tool output 60.9 kB ──► model (re-sent)
48
+ turn 3 ── tool output 60.9 kB ──► model (re-sent)
49
+ ... context grows with the session
50
+ ```
51
+
52
+ The agent rarely needs the whole result: it needs the failing line, the stack frame, the row that matters — and a route back to everything else.
53
+
54
+ ## How SUTRA handles a tool result
55
+
56
+ 1. **The tool runs normally.** SUTRA does not wrap, delay or re-execute it.
57
+ 2. **The host adapter hands the result to the shared core.** The core decides whether the result is eligible: size, payload shape, evidence tools, diffs.
58
+ 3. **The core rewrites only what is safe to remove.** ANSI escapes, repeated lines and oversized middles are elided; diffs stay byte-identical, JSON keeps its shape, a traceback keeps its error end.
59
+ 4. **The original is retained locally** when storage is enabled, with a bounded lifetime.
60
+ 5. **The model sees a smaller result with a marker** naming what was removed, how much, and the exact `sutra_restore` call that returns it.
61
+ 6. **Nothing is generated.** No summariser, no model call, no interpretation, no invented content.
62
+
63
+ If anything is unexpected — a malformed payload, a failed store write, an internal exception — SUTRA fails open and the host receives the original result unchanged.
64
+
65
+ ## What makes SUTRA different
66
+
67
+ - **Deterministic, not probabilistic.** The same bytes produce the same rewrite and the same marker. No compression model decides what matters.
68
+ - **Recoverable by construction.** An id is written only after the original is on disk. A marker never cites bytes that were not kept.
69
+ - **Agent-agnostic core.** Hermes, Claude Code, OpenCode, Codex, MCP and the CLI all call the same eligibility, compression, storage and recovery code.
70
+ - **Host-appropriate integration.** Automatic replacement only where the host documents a result-replacing hook; explicit tools or MCP registration where it does not.
71
+ - **Safety boundaries that hold.** Diffs and patches are never rewritten, JSON is never byte-sliced, traceback tails are never dropped.
72
+ - **Fail-open at every adapter.** A broken host payload or a missing interpreter leaves normal agent execution alone.
73
+
74
+ | Capability | What it means in practice |
75
+ |---|---|
76
+ | Deterministic rules, no compression model | Logs are never interpreted or paraphrased by another model |
77
+ | Local original retention | Anything SUTRA omitted can be retrieved later, by id |
78
+ | Native host adapters | No manual compression step on hosts with a result hook |
79
+ | Diff and shape protection | Code changes are never silently rewritten; JSON stays JSON |
80
+ | Fail-open design | A malformed result leaves the agent running normally |
81
+ | Shared core | The same rules, markers and recovery across every integration |
82
+
83
+ ## Architecture
84
+
85
+ ```text
86
+ AI coding agent
87
+ │ runs a tool
88
+ ▼
89
+ tool execution
90
+ │ raw result
91
+ ▼
92
+ host adapter
93
+ ├─ Hermes: tool_execution middleware automatic
94
+ ├─ Claude Code: PostToolUse hook automatic
95
+ ├─ OpenCode: tool.execute.after plugin automatic
96
+ ├─ Codex: MCP registration only explicit calls
97
+ └─ other clients: CLI or MCP server explicit calls
98
+ │
99
+ ▼
100
+ shared SUTRA core
101
+ ├─ eligibility: size, payload shape, evidence tools, diffs
102
+ ├─ deterministic rewrite + a marker naming what was removed
103
+ └─ local retention (TTL, quota, bounded reads)
104
+ │
105
+ ▼
106
+ model reads the smaller result
107
+ │ marker quotes: sutra_restore(id="…", mode="…")
108
+ ▼
109
+ original retrieved from the local store when needed
110
+ ```
111
+
112
+ The recovery path is separate from the compression path: compression can run with storage disabled, but then the marker says the original was not retained and the model can only re-run the command narrower. Recovery reads the local store by validated id and bounded mode.
113
+
114
+ ## How SUTRA compares with Caveman and Ponytail
115
+
116
+ SUTRA and [Caveman](https://github.com/JuliusBrussee/caveman) approach context efficiency from different architectural directions. Both address unnecessary context consumption, but SUTRA specialises in deterministic, recoverable tool-output processing at the integration layer. [Ponytail](https://github.com/DietrichGebert/ponytail) pulls a third lever: how much code the agent writes at all.
117
+
118
+ | Dimension | SUTRA | Caveman | Ponytail |
119
+ |---|---|---|---|
120
+ | Primary focus | Deterministic tool-output processing | Agent brevity plus the inputs it reads | How much code the agent writes |
121
+ | Where it acts | Between a tool result and the model, through host adapters | Skill plus CLI/proxy around the agent | Skill/rules before code is generated |
122
+ | What it transforms | Logs, JSON documents, tracebacks; diffs untouched | Replies, tool-readable files, web pages; routing and blocks in 4.0 | The agent's plan and implementation choices |
123
+ | Original recovery | Local bounded store, id marker, `sutra_restore` | Docs state originals stay on disk and can be pulled back | Not a compression layer; no recovery surface claimed |
124
+ | Model calls for compression | None — deterministic rules | No separate compression model for the voice; file compressors are tooling | None; the effect depends on the host model following its rules |
125
+ | Prompt handling | Does not touch prompts or replies | Docs state prompts are never rewritten | A prompt/rules layer by design |
126
+ | Agent support | Four first-class adapters plus MCP/CLI; automatic only where a result hook exists | 30+ agents via skill/installer | Many agents via skill/plugin |
127
+
128
+ The closest overlap is Caveman's "read less" path, which also shrinks what the model reads and keeps originals retrievable. SUTRA's difference is scope and guarantee: one deterministic, byte-level transform on the tool-result path, one local recovery protocol, and no automatic claim on a host that lacks a result hook.
129
+
130
+ Caveman's README reports 33.2% fewer input tokens across whole sessions (older engine), 8.5% fewer output tokens in a JetBrains paired test, and one HTML case 9.9% worse; Ponytail reports 45% fewer tokens and 53% less code on its 39-task benchmark. Those are their own measurements on their own workloads. Sources: [Caveman README](https://github.com/JuliusBrussee/caveman), [Caveman product page](https://caveman.so/products/caveman), [Ponytail README](https://github.com/DietrichGebert/ponytail). Details may change.
131
+
132
+ ## Evidence and benchmarks
133
+
134
+ ![Horizontal bar chart: bytes before and after SUTRA on eight payload shapes. Total 1.63 MB → 742.7 kB, 54.3% saved; the patch and JSON-array rows are unchanged on purpose.](https://raw.githubusercontent.com/g0GobliN/sutra/main/bench/savings.svg)
135
+
136
+ | Payload | Rule | Bytes before → after | o200k tokens before → after | Saved |
137
+ |---|---|---:|---:|---:|
138
+ | 12,000-line command output | `head-tail` | 60.9 kB → 18.6 kB | 35,000 → 10,149 | 71.0% |
139
+ | JSON tool envelope | `json-fields` | 85.9 kB → 19.2 kB | 26,012 → 5,670 | 78.2% |
140
+ | Test run, 2,000 cases | `head-tail` | 99.8 kB → 18.6 kB | 29,998 → 5,529 | 81.6% |
141
+ | 3,000-frame traceback | `traceback` | 359.4 kB → 10.3 kB | 126,015 → 3,680 | 97.1% |
142
+ | 432 kB patch | `diff-kept` | 432.1 kB → 432.1 kB | 120,039 → 120,039 | 0.0% |
143
+ | 900-row SQL result | `head-tail` | 46.6 kB → 18.5 kB | 18,906 → 7,543 | 60.1% |
144
+ | 8,000-line build log | `head-tail` | 334.9 kB → 18.6 kB | 127,005 → 6,905 | 94.6% |
145
+ | 8,000-item JSON array | `json-kept` | 206.9 kB → 206.9 kB | 103,002 → 103,002 | 0.0% |
146
+ | **Total** | | **1.63 MB → 742.7 kB (54.3%)** | **585,977 → 262,517 (55.2%)** | |
147
+
148
+ The patch row saves nothing on purpose: a compressor that shrinks diffs can hide a code change. The JSON-array row saves nothing because keeping the array an array leaves nothing safe to remove. Five buried clues needed one `sutra_restore` call (about 700 B each; the single-line JSON envelope cost 8.9 kB); three were already visible.
149
+
150
+ What the numbers are — and are not:
151
+
152
+ - Payload bytes and `tiktoken o200k_base` tokenizer counts on the payload text.
153
+ - Not provider input tokens, not prompt-cache effects, not an invoice, and not a live-agent task-success score. No model was called for any number here.
154
+ - 8/8 proves the clue is retained and retrievable; it does not prove an agent will ask for it.
155
+
156
+ Method, fixtures, per-phase timings, memory, recovery costs and reproduction: [docs/benchmark.md](https://github.com/g0GobliN/sutra/blob/main/docs/benchmark.md). The frozen live-model A/B protocol is in [docs/ab-benchmark.md](https://github.com/g0GobliN/sutra/blob/main/docs/ab-benchmark.md); it has not been run.
157
+
158
+ ## Safety, recovery and trust
159
+
160
+ Every rewrite leaves a marker in place of the omitted bytes:
161
+
162
+ ```text
163
+ … [sutra: partial · 133,522 bytes elided (large output) · id=su_3e72c41e… ·
164
+ original 151,920 B retained 168h ·
165
+ retrieve: sutra_restore(id="su_3e72c41e…", mode="bytes", start=12259, end=145781)] …
166
+ ```
167
+
168
+ The marker answers four questions: is this complete, what is missing and why, is the original still available, and exactly how to get it. `sutra_restore` supports exact byte ranges, line ranges, literal search and `full` when it fits, with paging that reproduces the original slice byte for byte.
169
+
170
+ Safety properties you can rely on:
171
+
172
+ - **Diffs and patches are byte-identical.** Always.
173
+ - **JSON keeps its shape.** Documents are decoded and re-encoded, never byte-sliced; an array stays an array.
174
+ - **Tracebacks keep their error end.** Only the middle is elided.
175
+ - **An id means the bytes exist.** If storage is off, full or the write failed, the marker says "not retained" and quotes no id.
176
+ - **Fail-open.** Any unexpected shape, parse error or internal exception leaves the original result untouched.
177
+ - **Bounded storage.** Defaults: 32 MiB per entry, 128 MiB total, 2,000 entries, 7-day TTL, 8 KiB recovery response (64 KiB hard cap). The store is local and **not encrypted**; it is protected by filesystem permissions, and tool output can contain secrets. Set `SUTRA_STORE=0` to opt out.
178
+
179
+ Read the full threat model in [SECURITY.md](https://github.com/g0GobliN/sutra/blob/main/SECURITY.md), the guarantees in [docs/contract.md](https://github.com/g0GobliN/sutra/blob/main/docs/contract.md), and the recovery protocol in [docs/recovery.md](https://github.com/g0GobliN/sutra/blob/main/docs/recovery.md).
180
+
181
+ ## Install
182
+
183
+ The distribution is `sutra-agent`; the `sutra` name on PyPI belongs to an unrelated package.
184
+
185
+ ```bash
186
+ python3 -m venv ~/.sutra-venv
187
+ ~/.sutra-venv/bin/pip install sutra-agent
188
+ ~/.sutra-venv/bin/sutra --version # sutra 0.3.0
189
+
190
+ ~/.sutra-venv/bin/sutra detect # read-only: what is installed
191
+ ~/.sutra-venv/bin/sutra install --dry-run
192
+ ~/.sutra-venv/bin/sutra install # configure every detected client
193
+ ~/.sutra-venv/bin/sutra doctor # prove the installed paths
194
+ ```
195
+
196
+ If the index you use does not serve 0.3.0 yet, install the checkout instead:
197
+
198
+ ```bash
199
+ git clone https://github.com/g0GobliN/sutra
200
+ ~/.sutra-venv/bin/pip install ./sutra
201
+ ```
202
+
203
+ `sutra install` auto-detects Hermes, Claude Code, OpenCode and Codex. Each change is backed up first and can be removed with `sutra uninstall`; `--dry-run` prints the change and writes nothing. Restart the host after installing a plugin or hook.
204
+
205
+ ```bash
206
+ ~/.sutra-venv/bin/sutra uninstall # remove only SUTRA-owned entries
207
+ ```
208
+
209
+ If you only want one client, use `--client hermes|claude|opencode|codex`. The installer's behaviour and evidence are recorded in [docs/automatic-install.md](https://github.com/g0GobliN/sutra/blob/main/docs/automatic-install.md).
210
+
211
+ ## Supported integrations
212
+
213
+ | Host | Mode | Verified against | What to expect |
214
+ |---|---|---|---|
215
+ | **Hermes Agent** | Automatic — native `tool_execution` middleware | 0.22.5 | Plugin validated and enabled by the installer; restart the gateway; enable the `sutra` toolset for recovery |
216
+ | **Claude Code** | Automatic — `PostToolUse` hook + MCP recovery | 2.1.268 | Result replaced before the model reads it; start a new session; hook and MCP must share a store |
217
+ | **OpenCode** | Automatic — generated `tool.execute.after` plugin + MCP recovery | 1.19.5 | Oversized string results replaced in place; restart OpenCode |
218
+ | **Codex CLI** | **Explicit MCP only** | 0.166.0 | `sutra_compress`/`sutra_restore` are callable; Codex has no supported post-tool result replacement, so nothing is automatic |
219
+ | **Any MCP client** | Explicit | protocol `2024-11-05` | Register `python -m sutra.mcp`; MCP registration is not interception |
220
+ | **Any subprocess-capable agent** | Explicit CLI | Python 3.11–3.13 | `sutra compress` / `sutra restore` as JSON; one process per call |
221
+ | **Cursor, Windsurf/Cascade** | No adapter | — | No generic built-in-tool result hook; MCP registration only, by hand |
222
+
223
+ The authoritative, per-client matrix — mechanisms, install paths, evidence and known limits — is [docs/integrations.md](https://github.com/g0GobliN/sutra/blob/main/docs/integrations.md). The host-API audit is [docs/universal-integration.md](https://github.com/g0GobliN/sutra/blob/main/docs/universal-integration.md).
224
+
225
+ ## Configuration
226
+
227
+ Common settings, read on every call:
228
+
229
+ | Variable | Default | Meaning |
230
+ |---|---|---|
231
+ | `SUTRA_ENABLED` | `1` | `0` disables compression and retention |
232
+ | `SUTRA_MAX_BYTES` | `24576` | results at or below this are untouched |
233
+ | `SUTRA_HEAD_BYTES` / `SUTRA_TAIL_BYTES` | `12288` / `6144` | bytes kept from the start and end |
234
+ | `SUTRA_STORE` | `1` | `0` keeps compression but stops retaining originals |
235
+ | `SUTRA_STORE_DIR` | `$HERMES_HOME/cache/sutra-store` | where originals live |
236
+ | `SUTRA_STORE_TTL_HOURS` | `168` | retention window |
237
+ | `SUTRA_RECOVERY_MAX_BYTES` | `8192` | default response cap (64 KiB hard max) |
238
+
239
+ The complete table, including store quotas, stats-log rotation and retrieval bounds, is in [docs/configuration.md](https://github.com/g0GobliN/sutra/blob/main/docs/configuration.md).
240
+
241
+ ## Limitations
242
+
243
+ - **Automatic replacement depends on the host.** Codex, Cursor and Windsurf have no supported generic result-replacement hook; they get explicit tools only.
244
+ - **JSON re-encoding normalises formatting.** Indentation and whitespace inside a re-encoded document are not preserved, and duplicate keys collapse to the last value. The original bytes remain recoverable, so nothing is lost, but the visible copy is not byte-identical.
245
+ - **Large JSON arrays with no large string fields are not compressed at all** — the price of keeping their shape.
246
+ - **Retrieval is bounded.** One entry ≤32 MiB, one response ≤8 KiB by default, at most 20 literal matches; a single line longer than the response cap is returned clipped.
247
+ - **The store is not encrypted.** It is local, permission-protected and expiring, but any process running as you can read it.
248
+ - **The recovery tool may need enabling.** In Hermes, `sutra_restore` is gated by the plugin toolset; without it the marker degrades to "re-run narrower".
249
+ - **No live-model A/B has been run.** No task-success, invoice or provider-token claim is made.
250
+ - **Windows is untested.** CI covers Linux and macOS.
251
+
252
+ ## Development and contributing
253
+
254
+ ```bash
255
+ python3 -m venv .venv && .venv/bin/pip install pytest ruff
256
+ .venv/bin/python -m pytest tests -q
257
+ .venv/bin/python -m pytest spikes -q
258
+ .venv/bin/ruff check .
259
+ ```
260
+
261
+ Read [CONTRIBUTING.md](https://github.com/g0GobliN/sutra/blob/main/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/g0GobliN/sutra/blob/main/CODE_OF_CONDUCT.md) before opening a pull request: one rule per change, a failing test first, and proof that the elided bytes are still recoverable. Bug reports are most useful with the smallest fixture, the result shape, the byte size and the `SUTRA_*` settings in force. Security issues go through a private advisory, not a public issue — see [SECURITY.md](https://github.com/g0GobliN/sutra/blob/main/SECURITY.md).
262
+
263
+ Browse the documentation index at [docs/README.md](https://github.com/g0GobliN/sutra/blob/main/docs/README.md). Releases are prepared and published only by authorized maintainers, following [docs/release.md](https://github.com/g0GobliN/sutra/blob/main/docs/release.md).
264
+
265
+ ## Licence
266
+
267
+ MIT.
@@ -0,0 +1,243 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/g0GobliN/sutra/main/public/logo/sutraThumb.png" alt="SUTRA" width="520">
3
+ </p>
4
+
5
+ # SUTRA
6
+
7
+ **Less output. More signal.**
8
+
9
+ SUTRA is a deterministic tool-output optimization and recovery layer for AI coding agents. It processes eligible tool results after a command runs and before the model reads them, so a large log costs less context on every following turn — without a model paraphrasing it.
10
+
11
+ One core. Multiple agents. Recoverable output. No compression model calls.
12
+
13
+ > **Eight-fixture benchmark:** payload size reduced by **54.3%**, o200k tokenizer counts by **55.2%**; **8/8** planted clues recovered; **0** diffs touched. The fixtures are generated tool-output shapes and the token figure is not a provider bill — [method](#evidence-and-benchmarks).
14
+
15
+ [Why](#the-problem-tool-output-is-re-read-all-session) · [How it works](#how-sutra-handles-a-tool-result) · [Architecture](#architecture) · [Comparison](#how-sutra-compares-with-caveman-and-ponytail) · [Evidence](#evidence-and-benchmarks) · [Install](#install) · [Integrations](#supported-integrations) · [Limitations](#limitations)
16
+
17
+ ## The problem: tool output is re-read all session
18
+
19
+ An agent runs `pytest -q`, receives 12,000 lines, and needs one failure. The useful part may be twenty lines, but the tool result stays in context and is re-sent on every subsequent turn. Prompt compression can shave an instruction once; tool output grows with every command an agent runs.
20
+
21
+ ```text
22
+ turn 1 ── tool output 60.9 kB ──► model
23
+ turn 2 ── tool output 60.9 kB ──► model (re-sent)
24
+ turn 3 ── tool output 60.9 kB ──► model (re-sent)
25
+ ... context grows with the session
26
+ ```
27
+
28
+ The agent rarely needs the whole result: it needs the failing line, the stack frame, the row that matters — and a route back to everything else.
29
+
30
+ ## How SUTRA handles a tool result
31
+
32
+ 1. **The tool runs normally.** SUTRA does not wrap, delay or re-execute it.
33
+ 2. **The host adapter hands the result to the shared core.** The core decides whether the result is eligible: size, payload shape, evidence tools, diffs.
34
+ 3. **The core rewrites only what is safe to remove.** ANSI escapes, repeated lines and oversized middles are elided; diffs stay byte-identical, JSON keeps its shape, a traceback keeps its error end.
35
+ 4. **The original is retained locally** when storage is enabled, with a bounded lifetime.
36
+ 5. **The model sees a smaller result with a marker** naming what was removed, how much, and the exact `sutra_restore` call that returns it.
37
+ 6. **Nothing is generated.** No summariser, no model call, no interpretation, no invented content.
38
+
39
+ If anything is unexpected — a malformed payload, a failed store write, an internal exception — SUTRA fails open and the host receives the original result unchanged.
40
+
41
+ ## What makes SUTRA different
42
+
43
+ - **Deterministic, not probabilistic.** The same bytes produce the same rewrite and the same marker. No compression model decides what matters.
44
+ - **Recoverable by construction.** An id is written only after the original is on disk. A marker never cites bytes that were not kept.
45
+ - **Agent-agnostic core.** Hermes, Claude Code, OpenCode, Codex, MCP and the CLI all call the same eligibility, compression, storage and recovery code.
46
+ - **Host-appropriate integration.** Automatic replacement only where the host documents a result-replacing hook; explicit tools or MCP registration where it does not.
47
+ - **Safety boundaries that hold.** Diffs and patches are never rewritten, JSON is never byte-sliced, traceback tails are never dropped.
48
+ - **Fail-open at every adapter.** A broken host payload or a missing interpreter leaves normal agent execution alone.
49
+
50
+ | Capability | What it means in practice |
51
+ |---|---|
52
+ | Deterministic rules, no compression model | Logs are never interpreted or paraphrased by another model |
53
+ | Local original retention | Anything SUTRA omitted can be retrieved later, by id |
54
+ | Native host adapters | No manual compression step on hosts with a result hook |
55
+ | Diff and shape protection | Code changes are never silently rewritten; JSON stays JSON |
56
+ | Fail-open design | A malformed result leaves the agent running normally |
57
+ | Shared core | The same rules, markers and recovery across every integration |
58
+
59
+ ## Architecture
60
+
61
+ ```text
62
+ AI coding agent
63
+ │ runs a tool
64
+ ▼
65
+ tool execution
66
+ │ raw result
67
+ ▼
68
+ host adapter
69
+ ├─ Hermes: tool_execution middleware automatic
70
+ ├─ Claude Code: PostToolUse hook automatic
71
+ ├─ OpenCode: tool.execute.after plugin automatic
72
+ ├─ Codex: MCP registration only explicit calls
73
+ └─ other clients: CLI or MCP server explicit calls
74
+ │
75
+ ▼
76
+ shared SUTRA core
77
+ ├─ eligibility: size, payload shape, evidence tools, diffs
78
+ ├─ deterministic rewrite + a marker naming what was removed
79
+ └─ local retention (TTL, quota, bounded reads)
80
+ │
81
+ ▼
82
+ model reads the smaller result
83
+ │ marker quotes: sutra_restore(id="…", mode="…")
84
+ ▼
85
+ original retrieved from the local store when needed
86
+ ```
87
+
88
+ The recovery path is separate from the compression path: compression can run with storage disabled, but then the marker says the original was not retained and the model can only re-run the command narrower. Recovery reads the local store by validated id and bounded mode.
89
+
90
+ ## How SUTRA compares with Caveman and Ponytail
91
+
92
+ SUTRA and [Caveman](https://github.com/JuliusBrussee/caveman) approach context efficiency from different architectural directions. Both address unnecessary context consumption, but SUTRA specialises in deterministic, recoverable tool-output processing at the integration layer. [Ponytail](https://github.com/DietrichGebert/ponytail) pulls a third lever: how much code the agent writes at all.
93
+
94
+ | Dimension | SUTRA | Caveman | Ponytail |
95
+ |---|---|---|---|
96
+ | Primary focus | Deterministic tool-output processing | Agent brevity plus the inputs it reads | How much code the agent writes |
97
+ | Where it acts | Between a tool result and the model, through host adapters | Skill plus CLI/proxy around the agent | Skill/rules before code is generated |
98
+ | What it transforms | Logs, JSON documents, tracebacks; diffs untouched | Replies, tool-readable files, web pages; routing and blocks in 4.0 | The agent's plan and implementation choices |
99
+ | Original recovery | Local bounded store, id marker, `sutra_restore` | Docs state originals stay on disk and can be pulled back | Not a compression layer; no recovery surface claimed |
100
+ | Model calls for compression | None — deterministic rules | No separate compression model for the voice; file compressors are tooling | None; the effect depends on the host model following its rules |
101
+ | Prompt handling | Does not touch prompts or replies | Docs state prompts are never rewritten | A prompt/rules layer by design |
102
+ | Agent support | Four first-class adapters plus MCP/CLI; automatic only where a result hook exists | 30+ agents via skill/installer | Many agents via skill/plugin |
103
+
104
+ The closest overlap is Caveman's "read less" path, which also shrinks what the model reads and keeps originals retrievable. SUTRA's difference is scope and guarantee: one deterministic, byte-level transform on the tool-result path, one local recovery protocol, and no automatic claim on a host that lacks a result hook.
105
+
106
+ Caveman's README reports 33.2% fewer input tokens across whole sessions (older engine), 8.5% fewer output tokens in a JetBrains paired test, and one HTML case 9.9% worse; Ponytail reports 45% fewer tokens and 53% less code on its 39-task benchmark. Those are their own measurements on their own workloads. Sources: [Caveman README](https://github.com/JuliusBrussee/caveman), [Caveman product page](https://caveman.so/products/caveman), [Ponytail README](https://github.com/DietrichGebert/ponytail). Details may change.
107
+
108
+ ## Evidence and benchmarks
109
+
110
+ ![Horizontal bar chart: bytes before and after SUTRA on eight payload shapes. Total 1.63 MB → 742.7 kB, 54.3% saved; the patch and JSON-array rows are unchanged on purpose.](https://raw.githubusercontent.com/g0GobliN/sutra/main/bench/savings.svg)
111
+
112
+ | Payload | Rule | Bytes before → after | o200k tokens before → after | Saved |
113
+ |---|---|---:|---:|---:|
114
+ | 12,000-line command output | `head-tail` | 60.9 kB → 18.6 kB | 35,000 → 10,149 | 71.0% |
115
+ | JSON tool envelope | `json-fields` | 85.9 kB → 19.2 kB | 26,012 → 5,670 | 78.2% |
116
+ | Test run, 2,000 cases | `head-tail` | 99.8 kB → 18.6 kB | 29,998 → 5,529 | 81.6% |
117
+ | 3,000-frame traceback | `traceback` | 359.4 kB → 10.3 kB | 126,015 → 3,680 | 97.1% |
118
+ | 432 kB patch | `diff-kept` | 432.1 kB → 432.1 kB | 120,039 → 120,039 | 0.0% |
119
+ | 900-row SQL result | `head-tail` | 46.6 kB → 18.5 kB | 18,906 → 7,543 | 60.1% |
120
+ | 8,000-line build log | `head-tail` | 334.9 kB → 18.6 kB | 127,005 → 6,905 | 94.6% |
121
+ | 8,000-item JSON array | `json-kept` | 206.9 kB → 206.9 kB | 103,002 → 103,002 | 0.0% |
122
+ | **Total** | | **1.63 MB → 742.7 kB (54.3%)** | **585,977 → 262,517 (55.2%)** | |
123
+
124
+ The patch row saves nothing on purpose: a compressor that shrinks diffs can hide a code change. The JSON-array row saves nothing because keeping the array an array leaves nothing safe to remove. Five buried clues needed one `sutra_restore` call (about 700 B each; the single-line JSON envelope cost 8.9 kB); three were already visible.
125
+
126
+ What the numbers are — and are not:
127
+
128
+ - Payload bytes and `tiktoken o200k_base` tokenizer counts on the payload text.
129
+ - Not provider input tokens, not prompt-cache effects, not an invoice, and not a live-agent task-success score. No model was called for any number here.
130
+ - 8/8 proves the clue is retained and retrievable; it does not prove an agent will ask for it.
131
+
132
+ Method, fixtures, per-phase timings, memory, recovery costs and reproduction: [docs/benchmark.md](https://github.com/g0GobliN/sutra/blob/main/docs/benchmark.md). The frozen live-model A/B protocol is in [docs/ab-benchmark.md](https://github.com/g0GobliN/sutra/blob/main/docs/ab-benchmark.md); it has not been run.
133
+
134
+ ## Safety, recovery and trust
135
+
136
+ Every rewrite leaves a marker in place of the omitted bytes:
137
+
138
+ ```text
139
+ … [sutra: partial · 133,522 bytes elided (large output) · id=su_3e72c41e… ·
140
+ original 151,920 B retained 168h ·
141
+ retrieve: sutra_restore(id="su_3e72c41e…", mode="bytes", start=12259, end=145781)] …
142
+ ```
143
+
144
+ The marker answers four questions: is this complete, what is missing and why, is the original still available, and exactly how to get it. `sutra_restore` supports exact byte ranges, line ranges, literal search and `full` when it fits, with paging that reproduces the original slice byte for byte.
145
+
146
+ Safety properties you can rely on:
147
+
148
+ - **Diffs and patches are byte-identical.** Always.
149
+ - **JSON keeps its shape.** Documents are decoded and re-encoded, never byte-sliced; an array stays an array.
150
+ - **Tracebacks keep their error end.** Only the middle is elided.
151
+ - **An id means the bytes exist.** If storage is off, full or the write failed, the marker says "not retained" and quotes no id.
152
+ - **Fail-open.** Any unexpected shape, parse error or internal exception leaves the original result untouched.
153
+ - **Bounded storage.** Defaults: 32 MiB per entry, 128 MiB total, 2,000 entries, 7-day TTL, 8 KiB recovery response (64 KiB hard cap). The store is local and **not encrypted**; it is protected by filesystem permissions, and tool output can contain secrets. Set `SUTRA_STORE=0` to opt out.
154
+
155
+ Read the full threat model in [SECURITY.md](https://github.com/g0GobliN/sutra/blob/main/SECURITY.md), the guarantees in [docs/contract.md](https://github.com/g0GobliN/sutra/blob/main/docs/contract.md), and the recovery protocol in [docs/recovery.md](https://github.com/g0GobliN/sutra/blob/main/docs/recovery.md).
156
+
157
+ ## Install
158
+
159
+ The distribution is `sutra-agent`; the `sutra` name on PyPI belongs to an unrelated package.
160
+
161
+ ```bash
162
+ python3 -m venv ~/.sutra-venv
163
+ ~/.sutra-venv/bin/pip install sutra-agent
164
+ ~/.sutra-venv/bin/sutra --version # sutra 0.3.0
165
+
166
+ ~/.sutra-venv/bin/sutra detect # read-only: what is installed
167
+ ~/.sutra-venv/bin/sutra install --dry-run
168
+ ~/.sutra-venv/bin/sutra install # configure every detected client
169
+ ~/.sutra-venv/bin/sutra doctor # prove the installed paths
170
+ ```
171
+
172
+ If the index you use does not serve 0.3.0 yet, install the checkout instead:
173
+
174
+ ```bash
175
+ git clone https://github.com/g0GobliN/sutra
176
+ ~/.sutra-venv/bin/pip install ./sutra
177
+ ```
178
+
179
+ `sutra install` auto-detects Hermes, Claude Code, OpenCode and Codex. Each change is backed up first and can be removed with `sutra uninstall`; `--dry-run` prints the change and writes nothing. Restart the host after installing a plugin or hook.
180
+
181
+ ```bash
182
+ ~/.sutra-venv/bin/sutra uninstall # remove only SUTRA-owned entries
183
+ ```
184
+
185
+ If you only want one client, use `--client hermes|claude|opencode|codex`. The installer's behaviour and evidence are recorded in [docs/automatic-install.md](https://github.com/g0GobliN/sutra/blob/main/docs/automatic-install.md).
186
+
187
+ ## Supported integrations
188
+
189
+ | Host | Mode | Verified against | What to expect |
190
+ |---|---|---|---|
191
+ | **Hermes Agent** | Automatic — native `tool_execution` middleware | 0.22.5 | Plugin validated and enabled by the installer; restart the gateway; enable the `sutra` toolset for recovery |
192
+ | **Claude Code** | Automatic — `PostToolUse` hook + MCP recovery | 2.1.268 | Result replaced before the model reads it; start a new session; hook and MCP must share a store |
193
+ | **OpenCode** | Automatic — generated `tool.execute.after` plugin + MCP recovery | 1.19.5 | Oversized string results replaced in place; restart OpenCode |
194
+ | **Codex CLI** | **Explicit MCP only** | 0.166.0 | `sutra_compress`/`sutra_restore` are callable; Codex has no supported post-tool result replacement, so nothing is automatic |
195
+ | **Any MCP client** | Explicit | protocol `2024-11-05` | Register `python -m sutra.mcp`; MCP registration is not interception |
196
+ | **Any subprocess-capable agent** | Explicit CLI | Python 3.11–3.13 | `sutra compress` / `sutra restore` as JSON; one process per call |
197
+ | **Cursor, Windsurf/Cascade** | No adapter | — | No generic built-in-tool result hook; MCP registration only, by hand |
198
+
199
+ The authoritative, per-client matrix — mechanisms, install paths, evidence and known limits — is [docs/integrations.md](https://github.com/g0GobliN/sutra/blob/main/docs/integrations.md). The host-API audit is [docs/universal-integration.md](https://github.com/g0GobliN/sutra/blob/main/docs/universal-integration.md).
200
+
201
+ ## Configuration
202
+
203
+ Common settings, read on every call:
204
+
205
+ | Variable | Default | Meaning |
206
+ |---|---|---|
207
+ | `SUTRA_ENABLED` | `1` | `0` disables compression and retention |
208
+ | `SUTRA_MAX_BYTES` | `24576` | results at or below this are untouched |
209
+ | `SUTRA_HEAD_BYTES` / `SUTRA_TAIL_BYTES` | `12288` / `6144` | bytes kept from the start and end |
210
+ | `SUTRA_STORE` | `1` | `0` keeps compression but stops retaining originals |
211
+ | `SUTRA_STORE_DIR` | `$HERMES_HOME/cache/sutra-store` | where originals live |
212
+ | `SUTRA_STORE_TTL_HOURS` | `168` | retention window |
213
+ | `SUTRA_RECOVERY_MAX_BYTES` | `8192` | default response cap (64 KiB hard max) |
214
+
215
+ The complete table, including store quotas, stats-log rotation and retrieval bounds, is in [docs/configuration.md](https://github.com/g0GobliN/sutra/blob/main/docs/configuration.md).
216
+
217
+ ## Limitations
218
+
219
+ - **Automatic replacement depends on the host.** Codex, Cursor and Windsurf have no supported generic result-replacement hook; they get explicit tools only.
220
+ - **JSON re-encoding normalises formatting.** Indentation and whitespace inside a re-encoded document are not preserved, and duplicate keys collapse to the last value. The original bytes remain recoverable, so nothing is lost, but the visible copy is not byte-identical.
221
+ - **Large JSON arrays with no large string fields are not compressed at all** — the price of keeping their shape.
222
+ - **Retrieval is bounded.** One entry ≤32 MiB, one response ≤8 KiB by default, at most 20 literal matches; a single line longer than the response cap is returned clipped.
223
+ - **The store is not encrypted.** It is local, permission-protected and expiring, but any process running as you can read it.
224
+ - **The recovery tool may need enabling.** In Hermes, `sutra_restore` is gated by the plugin toolset; without it the marker degrades to "re-run narrower".
225
+ - **No live-model A/B has been run.** No task-success, invoice or provider-token claim is made.
226
+ - **Windows is untested.** CI covers Linux and macOS.
227
+
228
+ ## Development and contributing
229
+
230
+ ```bash
231
+ python3 -m venv .venv && .venv/bin/pip install pytest ruff
232
+ .venv/bin/python -m pytest tests -q
233
+ .venv/bin/python -m pytest spikes -q
234
+ .venv/bin/ruff check .
235
+ ```
236
+
237
+ Read [CONTRIBUTING.md](https://github.com/g0GobliN/sutra/blob/main/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/g0GobliN/sutra/blob/main/CODE_OF_CONDUCT.md) before opening a pull request: one rule per change, a failing test first, and proof that the elided bytes are still recoverable. Bug reports are most useful with the smallest fixture, the result shape, the byte size and the `SUTRA_*` settings in force. Security issues go through a private advisory, not a public issue — see [SECURITY.md](https://github.com/g0GobliN/sutra/blob/main/SECURITY.md).
238
+
239
+ Browse the documentation index at [docs/README.md](https://github.com/g0GobliN/sutra/blob/main/docs/README.md). Releases are prepared and published only by authorized maintainers, following [docs/release.md](https://github.com/g0GobliN/sutra/blob/main/docs/release.md).
240
+
241
+ ## Licence
242
+
243
+ MIT.