Update Untether
Untether publishes releases to PyPI. To upgrade to the latest version:
Untether publishes releases to PyPI. To upgrade to the latest version:
=== “uv (recommended)”
```sh
uv tool upgrade untether
```
=== “pipx”
```sh
pipx upgrade untether
```
Check your current version:
untether --version
After upgrading, restart the service if running as a systemd unit:
systemctl --user restart untether
Agent CLIs are separate Untether wraps agent CLIs (Claude Code, Codex, OpenCode, Pi, Gemini CLI, Amp) as subprocesses. Updating Untether does not update the agent CLIs. Update them separately:
```sh
npm update -g @anthropic-ai/claude-code
npm update -g @openai/codex
npm update -g opencode-ai
npm update -g @mariozechner/pi-coding-agent
npm update -g @google/gemini-cli
npm update -g @sourcegraph/amp
```
Upgrading to v0.35.4
See the v0.35.4 changelog entry for the full list. Behaviour changes that may affect operators:
- Voice transcription is now SSRF-validated. If
voice_transcription_base_urlpoints at a loopback or private-network endpoint (e.g. a local Whisper server athttp://localhost:8000/v1), transcription is now refused unless you allowlist it — addvoice_transcription_url_allowlist = ["127.0.0.0/8"]to[transports.telegram]. The default public path (api.openai.com) is unaffected. (#381) - Webhooks with
auth = "none"are refused on non-loopback hosts. An unauthenticated webhook bound to a public interface is now dropped at startup and on hot-reload (polling, commands, and crons keep running); loopback binds are still allowed. To keep an unauthenticated webhook on a public host, set[triggers] allow_unauthenticated_webhooks = true. (#382) - The pre-spawn RAM guard is now concurrency-aware. The block threshold rises with the number of runs already in flight (
prespawn_ram_per_run_reserve_mb, default 750), and an optional hard ceiling (max_concurrent_engine_runs, default0= unlimited) caps concurrent engine subprocesses. On small VPS hosts this stops the OOM killer SIGKILLing a live session — see the sizing note under config → watchdog. (#589) - Empty-resume recovery (Claude). A resume that returns an empty 0-turn result now auto-recovers on a fresh session instead of silently doing nothing, and post-result force-killed sessions are quarantined proactively. No config needed; opt out via
[auto_continue] empty_resume_fresh = false. (#631, #632) - The Claude plan-mode progressive cooldown was retired. The upstream
ExitPlanModere-issue loop it worked around is fixed (CLI 2.1.215); “Pause & Outline Plan” now holds the session open on a text-based outline gate. No action needed. (#570)
Upgrading to v0.35.2
See the v0.35.2 changelog entry for the full change list. Behaviour changes that may affect operators upgrading from v0.35.1 or earlier:
- Claude/Pi subprocess env is now allowlisted. Arbitrary process env no longer leaks to agent CLIs. If a plugin or MCP server depends on a specific variable, confirm it’s on the allowlist — see Env allowlist (Claude/Pi). (#198, #361)
CLAUDE_STREAM_IDLE_TIMEOUT_MSdefault raised to300000(5 min). The old 60 s default killed long-thinking runs. Set the var explicitly to restore the old value. (#342)[security] env_audit = trueby default. Any leaked env var logsclaude.env_audit.leaked_varWARNING and subprocesses spawn underenv -i. Set tofalseinuntether.tomlto restore legacy behaviour. (#361)run_oncecrons persist fired state torun_once_fired.json(sibling tountether.toml). They no longer re-fire on reload or restart. Delete the file to re-arm. (#317)- Webhook port bind failure no longer crashes the bot. Check logs for
triggers.server.bind_failed. Remediation:ss -tlnp | grep <port>to find the conflicting process, then setport = <N>in[triggers]. (#320) - Engine subprocess cleanup walks the process tree. Orphaned
workerdprocesses (seen at 37 GB RSS in pre-0.35.2 incidents) are now signalled alongside the parent. (#275)