sutra-agent 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sutra/__init__.py +157 -0
- sutra/claude_code.py +611 -0
- sutra/cli.py +350 -0
- sutra/codex.py +242 -0
- sutra/config.py +155 -0
- sutra/core.py +204 -0
- sutra/discovery.py +212 -0
- sutra/engine.py +657 -0
- sutra/hermes.py +275 -0
- sutra/hook.py +24 -0
- sutra/installer.py +143 -0
- sutra/jsonc.py +248 -0
- sutra/mcp.py +22 -0
- sutra/metrics.py +233 -0
- sutra/opencode.py +406 -0
- sutra/opencode_plugin.js +161 -0
- sutra/plugin.yaml +8 -0
- sutra/recovery.py +508 -0
- sutra/runner.py +34 -0
- sutra/scripts/_sutra.py +27 -0
- sutra/scripts/sutra-claude-code-hook.py +157 -0
- sutra/scripts/sutra-cli.py +165 -0
- sutra/scripts/sutra-mcp-server.py +177 -0
- sutra/store.py +543 -0
- sutra/utf8.py +41 -0
- sutra_agent-0.3.0.dist-info/METADATA +267 -0
- sutra_agent-0.3.0.dist-info/RECORD +31 -0
- sutra_agent-0.3.0.dist-info/WHEEL +5 -0
- sutra_agent-0.3.0.dist-info/entry_points.txt +4 -0
- sutra_agent-0.3.0.dist-info/licenses/LICENSE +21 -0
- sutra_agent-0.3.0.dist-info/top_level.txt +1 -0
sutra/__init__.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""SUTRA — deterministic compression of oversized tool output, with recovery.
|
|
2
|
+
|
|
3
|
+
Hermes spends most of its tokens re-sending context on every turn, and tool
|
|
4
|
+
output is the part that grows without bound. This plugin sits on the
|
|
5
|
+
``tool_execution`` middleware path and shrinks a tool result *after* it has run,
|
|
6
|
+
before the model ever sees it.
|
|
7
|
+
|
|
8
|
+
The compression itself lives in :mod:`core`, the host-agnostic seam shared with
|
|
9
|
+
the Claude Code hook, the CLI and the MCP server. This module is the Hermes
|
|
10
|
+
adapter: it registers the middleware and the recovery tool, and delegates every
|
|
11
|
+
decision below that to the seam.
|
|
12
|
+
|
|
13
|
+
What makes the shrinking safe is that the original is kept: every rewrite
|
|
14
|
+
quotes an id the agent can hand back to ``sutra_restore`` and get the exact
|
|
15
|
+
bytes that were left out. Correctness first, compression second.
|
|
16
|
+
|
|
17
|
+
Design rules, in priority order:
|
|
18
|
+
|
|
19
|
+
1. **Never invent content.** Only remove, collapse or elide, and always leave a
|
|
20
|
+
marker saying what was removed, how much, and how to get it back.
|
|
21
|
+
2. **Never change a payload's own format.** Diffs are returned byte-identical,
|
|
22
|
+
a JSON array stays an array, a traceback keeps its error end.
|
|
23
|
+
3. **Fail open.** Any unexpected shape, parse error or exception returns the
|
|
24
|
+
original result untouched.
|
|
25
|
+
4. **Measurable.** Every decision is counted, every rewrite appends a line to a
|
|
26
|
+
local savings log, and every retained original is a file the agent can ask
|
|
27
|
+
for by id.
|
|
28
|
+
|
|
29
|
+
Configuration comes from the environment so it can be tuned without a code
|
|
30
|
+
change:
|
|
31
|
+
|
|
32
|
+
SUTRA_ENABLED "0" disables compression entirely (default "1")
|
|
33
|
+
SUTRA_MAX_BYTES only results larger than this are rewritten (default 24576)
|
|
34
|
+
SUTRA_HEAD_BYTES bytes kept from the start (default 12288)
|
|
35
|
+
SUTRA_TAIL_BYTES bytes kept from the end (default 6144)
|
|
36
|
+
SUTRA_STATS_FILE where the saving log lives (default $HERMES_HOME/cache/sutra-stats.jsonl)
|
|
37
|
+
SUTRA_STATS_MAX_BYTES rotate the log at this size (default 5 MiB)
|
|
38
|
+
SUTRA_STORE "0" stops retaining originals (default "1")
|
|
39
|
+
SUTRA_STORE_DIR where originals live (default $HERMES_HOME/cache/sutra-store)
|
|
40
|
+
SUTRA_STORE_TTL_HOURS how long an original is kept (default 168 = 7 days)
|
|
41
|
+
SUTRA_STORE_MAX_BYTES total size of the store (default 128 MiB)
|
|
42
|
+
SUTRA_STORE_MAX_ENTRY_BYTES largest single original retained (default 32 MiB)
|
|
43
|
+
SUTRA_STORE_MAX_ENTRIES most originals retained (default 2000)
|
|
44
|
+
SUTRA_RECOVERY_MAX_BYTES response cap for sutra_restore (default 8192)
|
|
45
|
+
SUTRA_JSON_ITEMS retired; ignored (array elision changed the payload's type)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
from typing import Any
|
|
51
|
+
|
|
52
|
+
from . import config as _config
|
|
53
|
+
from . import metrics
|
|
54
|
+
from . import recovery
|
|
55
|
+
from . import store as _store
|
|
56
|
+
from .core import (
|
|
57
|
+
NO_COMPRESS_TOOLS,
|
|
58
|
+
Outcome,
|
|
59
|
+
outcome_json,
|
|
60
|
+
process_result,
|
|
61
|
+
process_text,
|
|
62
|
+
restore,
|
|
63
|
+
should_bypass,
|
|
64
|
+
)
|
|
65
|
+
from .engine import ( # re-exported: this was a single-module plugin before 0.2.0
|
|
66
|
+
_DEFAULT_MAX_BYTES as _DEFAULT_MAX_BYTES,
|
|
67
|
+
Classification,
|
|
68
|
+
Ref,
|
|
69
|
+
classify,
|
|
70
|
+
collapse_repeats,
|
|
71
|
+
compress,
|
|
72
|
+
config_from_env,
|
|
73
|
+
strip_ansi,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
__version__ = "0.3.0"
|
|
77
|
+
|
|
78
|
+
# Backward-compatible name: before the seam extraction this was the plugin's
|
|
79
|
+
# own function. It is now the seam's result-shaped entry point.
|
|
80
|
+
compress_result = process_result
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def configuration() -> _config.Config:
|
|
84
|
+
"""The resolved configuration every component reads from."""
|
|
85
|
+
return _config.resolve()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def enabled() -> bool:
|
|
89
|
+
return _config.resolve().enabled
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def stats_path() -> str:
|
|
93
|
+
return _config.resolve().stats_path
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def metrics_snapshot(store=None) -> dict[str, Any]:
|
|
97
|
+
"""One surface for compression, restores, cleanup, and the store's size."""
|
|
98
|
+
if store is None:
|
|
99
|
+
try:
|
|
100
|
+
store = _store.Store(_store.StoreConfig.from_config(_config.resolve()))
|
|
101
|
+
except Exception:
|
|
102
|
+
store = None
|
|
103
|
+
return metrics.snapshot(store)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def tool_execution(*, next_call, tool_name: str | None = None, args: Any = None,
|
|
107
|
+
session_id: str | None = None, **_context: Any) -> Any:
|
|
108
|
+
"""The middleware frame: run the tool, then compress what comes back.
|
|
109
|
+
|
|
110
|
+
The seam owns the bypass list, so a host never needs its own copy of it.
|
|
111
|
+
``next_call`` runs before the try: a downstream tool error must propagate
|
|
112
|
+
unchanged, not be mistaken for a compression failure.
|
|
113
|
+
"""
|
|
114
|
+
result = next_call(args)
|
|
115
|
+
try:
|
|
116
|
+
return process_result(result, tool_name=tool_name, session_id=session_id)
|
|
117
|
+
except Exception: # fail open: an exotic result shape is never worth a broken call
|
|
118
|
+
return result
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def register(ctx) -> None:
|
|
122
|
+
"""Hermes plugin entry point: middleware plus the recovery tool."""
|
|
123
|
+
ctx.register_middleware("tool_execution", tool_execution)
|
|
124
|
+
ctx.register_tool(
|
|
125
|
+
name=recovery.TOOL_NAME,
|
|
126
|
+
toolset="sutra",
|
|
127
|
+
schema=recovery.TOOL_SCHEMA,
|
|
128
|
+
handler=recovery.handle_tool_call,
|
|
129
|
+
description="Retrieve the omitted part of a tool result SUTRA compressed.",
|
|
130
|
+
emoji="🔎",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
__all__ = [
|
|
135
|
+
"Classification",
|
|
136
|
+
"NO_COMPRESS_TOOLS",
|
|
137
|
+
"Outcome",
|
|
138
|
+
"Ref",
|
|
139
|
+
"classify",
|
|
140
|
+
"collapse_repeats",
|
|
141
|
+
"compress",
|
|
142
|
+
"compress_result",
|
|
143
|
+
"config_from_env",
|
|
144
|
+
"configuration",
|
|
145
|
+
"enabled",
|
|
146
|
+
"metrics_snapshot",
|
|
147
|
+
"outcome_json",
|
|
148
|
+
"process_result",
|
|
149
|
+
"process_text",
|
|
150
|
+
"register",
|
|
151
|
+
"restore",
|
|
152
|
+
"should_bypass",
|
|
153
|
+
"stats_path",
|
|
154
|
+
"strip_ansi",
|
|
155
|
+
"tool_execution",
|
|
156
|
+
"__version__",
|
|
157
|
+
]
|