Repository navigation
Releases: igormcsouza/tether
Release list
v0.2.3 Small-Model Robustness
Dogfooding fixes: the issues came from running Tether with a small model (GLM-5.3) on its own backlog.
Features
tether read <id> --full(#72): the CLI now accepts the same--fullform as the in-sessionagent read; it prints the full result (already the CLI default),--digeststill gives the compact card.
Fixes
- Gate errors vs failures (#33): a broken verify gate (sandbox violation, exit 124/126/127) fails the session immediately with
Verification gate ... misconfigured: <reason>instead of burning the 3-failure abort budget;tether statsshows aGATE_ERRcolumn. edit writeoverwrite summary (#40): overwriting an existing file reportsReplaced N lines with M (X removed, Y added; first removed: ...)and points toedit replace --lines a:bwhen the file shrank.- Skill
tools.pywarning (#24): a@tooldefined intools.pybut not listed in the skill'stools:now warns at load (0 prompt tokens); docs say the tools stay opt-in. - Cycle detection (#39): repeated multi-command cycles (e.g.
cat/sed/pytest, or alternatingsed -nranges) trigger a deterministic[CYCLE DETECTED]nudge and a newcycle_stallevent type, shown asCYCLESintether stats. - Provider retry (#70): HTTP providers retry timeouts, network errors and 429/5xx twice with backoff, honour
timeoutfromproviders.yamlfor all kinds, and fail with aProviderErrornaming provider, model and timeout instead of a bareReadTimeout. - No more re-read loops (#69): the latest read of each of the last 4 files stays unfolded until that path is edited (or re-read); the fold hint only mentions
mem recallwhenmemis enabled. Static prompt prefix unchanged. - Clearer errors (#71): a provider 401/403 names the provider and the
api_key_envas missing or rejected (never its value); a declared but emptyapi_key_envfails before the first request; a disabled built-in tool says it is disabled and how to enable it.
v0.2.2 Provider Dialects, Schema Output & Context Config
Dogfooding fixes: Tether implemented most of this release itself through its own CLI and a claude -p provider.
Features
{schema}placeholder (#51) forcommandproviders: constrain output to the act schema (e.g.claude -p --json-schema '{schema}'); replies may be structured objects withcmd/stdin. Measured: withclaude -pthis adds ~2k prompt tokens per call, so it is a reliability option, not a saving.tether misses(#53): list unparsed model replies (nudges) as fixture-ready JSONL fortests/fixtures/dialects.jsonl.
Fixes
- Configurable context budget and fold window (#54):
--context-tokens/--fold-afterontether runandtether pipeline,context_tokens/fold_afteron MCPtether_run, and per-provider keys inproviders.yaml. The 8k context and fold-after-2 defaults made large-context backends re-read the same files forever. - Table-driven tool-call dialect extractors (#52): built-in
<tool_call>JSON, raw JSON,<parameter name="cmd">blocks and textact(...), extendable withtether.register_dialector adialects:regex list inproviders.yaml.
Docs: https://igormcsouza.github.io/tether/reference/providers/
v0.2.1 Declarative Providers
Add model backends without writing code.
Features
- Declarative providers (#49):
~/.tether/providers.yaml(override withTETHER_PROVIDERS, never read from the workspace) maps a model-id prefix to a provider. Kinds:openai(OpenRouter, vLLM, Groq, LM Studio...),ollama,anthropicandcommand, which wraps any CLI that prints JSON (for exampleclaude -p --tools "") with{model},{system}and{conversation}placeholders, JSON-path reply and token usage, and a timeout.argvis executed directly, never through a shell. Works intether run,tether pipeline, the SDK andtether mcp. - Built-in prefixes (
ollama,openai,anthropic,claude) cannot be shadowed.
Docs: https://igormcsouza.github.io/tether/reference/providers/#declarative-providers
v0.2.0 MCP Server & Agent-to-Agent Interface
Tether as a subagent for the harness you already use (Claude Code, Codex, Cursor, Gemini CLI, Copilot): MCP server, JSON output and exit codes (#46). See Use Tether Inside Your Harness.
Features
- MCP server (optional extra,
pip install "tether-sdk[mcp]"; without ittether mcpwarns and exits 1):tether mcpover stdio withtether_run|status|wait|read|send|list|stats|query. --jsononrun,pipeline,status,list,read,wait,send,stats; stable schemav: 1.- Exit codes: 0 completed, 1 usage, 2 waiting for input, 3 gate failed, 4 max steps, 5 worker error. Failure text now goes to stderr.
Harness(output_schema=...)/--output-schema,--result-file(JSON Schema subset enforced as adonegate),tether events <id> [--follow].
Fixes
- Worker errors keep the exception type and store the traceback as a
tool_errorturn (#41). ollamano longer silently substitutes an installed model on 404; it fails with the installed list. Opt back in withTETHER_OLLAMA_FALLBACK=1(#21).
Behavior changes
tether waitnow exits non-zero when a session failed or needs input (it always returned 0 before).
v0.1.4 Security hardening
Security hardening from a red-team run of ollama/qwen3.5:9b against v0.1.3 (hidden prompt injection in a README exfiltrated a file; docker.sock was reachable inside bwrap).
Security
- Sandbox: unix sockets under
/run(includingdocker.sock, a host escape for docker-group users) are masked;/home,/media,/mnt,/srvand/var/tmpare hidden behind tmpfs. Previously python could read them and connect to the socket despite the command-string guard. - Sandbox: secret-looking files are masked at any depth (
.env*,*.pem,*.key,id_rsa,id_ed25519,authorized_keys), not only.env*at the workspace root.sub/.e''nvandserver.pemwere readable throughbash. - Env:
bashnow gets an allowlisted environment instead of a denylist.DATABASE_URL,*_WEBHOOK,STRIPE_*and custom*_KEYvariables leaked before. Opt back in withTETHER_ENV_PASSTHROUGH=A,B. - Web:
web fetchblocks loopback, private, link-local and cloud-metadata addresses, checked on every redirect hop (TETHER_WEB_ALLOW_PRIVATE=1to opt out).
Features
- Sandbox: optional network isolation,
tether run|pipeline --no-network/SandboxPolicy(network=False)mapping tobwrap --unshare-net. Sub-agents and stages can only keep or narrow it; withoutbwrapit fails closed. (#23)
Fixes
v0.1.3 Bash Quoting & Structured History Replay
Fixes
- Bash quoting (#37, PR #42): the
bashtool now receives the original command text instead of ashlex.splitthen" ".joinrebuild, so quotes survive (sed -i 's/a b/c d/' f,grep -n "return list" f,python -c "print(1+1)") and pipelines,&∧keep working. Previously any quoted argument with spaces or parentheses was re-split by the shell. - History replay (#38, PR #43): past actions are now replayed to Ollama, OpenAI-compatible and Anthropic providers as real
tool_callsplustoolresults (tool_use/tool_resulton Anthropic) instead ofact('...')assistant text, which small tool-calling models copied into real arguments and corrupted files. Replayedstdinis a real 400-char prefix, not a copyable<N chars>placeholder. Custom providers keep the textual form unless they setstructured_history = True. The system prompt and tools array are unchanged.
Docs
- New runnable example
examples/course/and a Troubleshooting page (symptom, cause, fix, known limitations);@toolargument parsing, thetools.pyplustools:requirement, gate and pipeline practices, observation-folding workarounds, Ollama in Docker and the awk/sed sandbox false positive.
Verified
End-to-end on qwen3-coder:30b (bug-fix task on a small FastAPI repo): 18 steps, one-line fix, all tests pass, no corrupted arguments. Before these fixes the same task failed in five attempts.
v0.1.2 Small-Model Tool Args & Literal Newline Fixes
Fixes
@toolargument parsing (#25): extra unquoted words now join into the last positionalstrparameter even when later parameters are optional non-str(yt-search arquitetura em camadaswithdef yt_search(query: str, limit: int = 3)); later optionals are set via--flag.*args: strparameters now receive the words splatted instead of failing withunexpected keyword argument.- Multiplexer (#26): small models that send
edit write/edit replacestdin or thedonedeliverable as one line with literal\n/\tescapes now get them unescaped deterministically (only when the stdin has no real newline).bashstdin and all other tools are never touched.
Behavior note: an optional positional after a str param can no longer be set positionally; use --flag.
Open design questions for v0.2: #24 (skill tools.py registration), #27 (observation folding with small models).
v0.1.1 Silent-Success Bash Fix, tether --version & v0.1 Feature Tutorial
Tether v0.1.1
🛠️ Fixes
- Bash: a command with no output now returns
[exit 0: no output] Command succeeded.instead of a bare[exit 0], which 9B models misread as failure and repeated until the loop breaker fired (0 loop stalls in a realgemma4:e4brun). - CLI: added the documented-but-missing
tether --version.
📚 Documentation
- New tutorial page: verification gates (
--verify),done --ask, project memory (mem --project), YAML pipelines,tether statsandtether retro.
📦 Install
pip install tether-sdk / uv add tether-sdk (PyPI) · Docs: https://igormcsouza.github.io/tether/
Full changelog: v0.1.0...v0.1.1
v0.1.0 Verification Gates, Pipelines, Telemetry, Project Memory & Retro
Tether v0.1.0
🚀 Milestone v0.1: Deterministic Control Plane
- Pre-
doneVerification Gates:Harness(verify=[...]),--verify, or Skillverify:run inside the sessionSandboxPolicybeforedonecan complete a session (0 static prompt tokens until a gate fails). - Interruptible
WAITING_INPUT&done --ask: sessions pause for human/parent input and resume viatether send. - Hierarchical Project Memory:
mem pin --project/mem search --projectwith cross-session BM25 RAG. - Telemetry & Failure-Mode Inspector (
tether stats): per-model pass rate, token cost, step latency and ACI friction offenders. - Deterministic Multi-Stage Pipelines (
tether pipeline): isolated stage sessions, heterogeneous model routing, monotonic sandbox attenuation, ~50-token stage handoffs. - Post-Mortem Distillation (
tether retro): distills a session's friction trace into at most 2 short rules inSKILL.md(or project memory), offline only.
🛡️ Fixes from release validation
- Sandbox: under
bwrap,.env*,.tether/,.git/configand.git/hooksare masked at the mount level (previously bypassable via globs/shell indirection); PID/IPC namespaces unshared;SSH_AUTH_SOCKscrubbed. tether stats: pipeline root sessions no longer double-count tokens or invent a model run.- CLI: invalid pipeline manifests and
sendto unknown sessions printError: ...instead of a traceback. - Multiplexer: unknown command hints
bash <cmd>whenbashis enabled.
📦 Install
pip install tether-sdk / uv add tether-sdk (PyPI) · Docs: https://igormcsouza.github.io/tether/
Full changelog: v0.0.3...v0.1.0
v0.0.3 L1 Head+Tail Elision, Loop-Stall Breaker, KV-Cache Pooling, MkDocs & PyPI (tether-sdk)
Tether v0.0.3
🚀 Architectural & Performance Upgrades
- Head + Tail L1 Active Observation Elision (
tether/context.py): Oversized active observations (> 2,400 chars) are deterministically elided in L1 working memory to their first 900 chars (headers/command summary) + last 900 chars (stack trace/exit code) with a pointer tomem recall/mem search, preserving full outputs in SQLite FTS5. - Deterministic Loop-Stall Breaker (
tether/harness.py): Consecutive identical(cmd, stdin)calls from small 8B/9B models are intercepted before tool execution and answered with a deterministic[LOOP DETECTED]recovery nudge. - 100% KV-Cache Prefix Immutability Across Pins (
tether/context.py,tether/providers/anthropic.py):[PINNED MEMORY]is attached to the tail message after window trimming so earlier turn messages remain 100% byte-identical when pins change; added Anthropic ephemeralcache_controlbreakpoints. - Persistent Connection Pooling & Thread-Local SQLite WAL (
tether/providers/*,tether/store.py): Reuses persistenthttpx.AsyncClientinstances across turns and caches thread-local SQLite WAL connections withPRAGMA temp_store=MEMORY;and 128 MBmmap_size.
📦 Packaging, Documentation & CI
- Published on PyPI (
tether-sdk): Install viapip install tether-sdk,uv add tether-sdk, oruv tool install tether-sdk(https://pypi.org/project/tether-sdk/). - Flattened Package Layout: Moved
tether/to the repository root (removedsrc/). - MkDocs Documentation Site: Full tutorials, architecture & literature review, and auto-generated API reference live at https://igormcsouza.github.io/tether/.
- Per-Module Test Suite & 100% Coverage CI Gate: Split unit tests into 20 per-module test files under
tests/and enforcedruff,mypy,mkdocs build --strict, andpytest --cov=tether --cov-fail-under=100in CI.