v0.9.0
Highlights
Bundles three improvement loops landed since 0.8.0 (37-iter context/compaction on 2026-05-25, 68-iter reliability/perf on 2026-05-24, 55-iter context-savings baseline).
Security
- DNS-rebinding window closed in SSRF guard:
webfetch.pyresolves once and pins the connection to that IP via a custom transport, so a hostile DNS server can't return a public IP to validation and a private IP (e.g. 169.254.169.254 IMDS) to the reconnect. paths.safe_join()promoted as the canonical fragment joiner; two raw joins that took user-controlled session_ids now flow through it.dispatch()ensurescontinue=trueso handlers returning{}can't become harness-blocking responses.- webfetch sidecar path-traversal fix: validates that
shrunk_pathresolves inside the cache roots before writing. - PIL decode-bomb cap:
MAX_IMAGE_PIXELSset to prevent multi-gigapixel decompression crashes.
Reliability
- Hook registry consolidated to single source of truth (
hook_registry.py); a startup_assert_hook_registry_aligned()raisesImportErrorif any event lacks a@hook_app.commanddecorator. Eliminates the recurring registry drift bug class. - Persistent hook wrapper at
data_dir/bin/tg-hook.cmdsurvivesuv tool install --reinstall; emits{"continue":true}and exits 0 when the venv is briefly absent. paths.ensure_dir()retry helper for the Windowsmkdir(parents=True, exist_ok=True)race whereis_dir()returns staleFalseafter a concurrent create.- Surrogate-escape crash fix in
post_bash(1,311 crashes/week in production). - Orphaned project GC: reclaims 2.3 GB on the audited install with a 30-min safety window.
- Session lock hardened: 5 s timeout, jittered poll backoff, fsynced PID write, rejects invalid PIDs.
- Worker SIGTERM-aware drain loop via
_GRACEFUL_SHUTDOWNevent. - Hook crash log at
hooks-stderr.log(100 KB cap,.prevrotation). - Concurrent dirty-queue write protected by
fcntl/msvcrtlocks. - Session CAS re-applies size caps after merge so a race can't inflate the JSON beyond limits.
- Hooks stderr log test isolation: 230 KB / 316 crash blocks of test garbage were polluting the production sink.
Token savings — manifest, hints, hot path
- Manifest format shortening:
L:X-Yinstead oflines X-Y, bash entries dropid=/shortenexit=toe=, lower_MAX_TODO_SUBJECT_CHARS(~71 tokens/manifest). - Active-skills section collapsed to a single line (
**Skills:** name1, name2 — recall via ...) (~160 tokens/6-skill manifest). - Manifest Delta line:
+sections that grew since last compact. - Bash entries grouped by exit class (Failed/Slow/Ok); suppresses TODOs that reference edited files.
- Manifest bold-label bundle:
### Edited:→**Edited:**,**Syms:**for inline labels. - Manifest SHA sidecar cache:
pre_compactwrites a sentinel and rebuilds only when the session SHA differs. - Cross-session grep dedup via
global.db::grep_patterns. - Adaptive
_MAX_BASH_ENTRIESscales withbash_historylength. - Clean-repo session brief collapses to
<branch> (clean)one-liner. status_linescap (50 +(+N more files)).- Single rev-list + adaptive git-log entry count; in-sync repos skip the git-log section.
- WebFetch HTML strip before caching (60-90% byte reduction).
- Process-local LRU on
session.load()(cap 4, mtime-keyed). - Pre-Read structured-file hint (CSV headers, JSON top-level keys, log entry count) ~70% smaller.
- Pre-Read index-only file suppression (lockfiles, source maps, build artifacts).
- AVIF image-shrink support (~15% smaller than WebP when libaom available).
- Hint fingerprint includes file path to prevent false positives across files.
- Session-level hint budget caps (files 5, bash 3, web 2, skills 4).
Performance
- Test suite 22% faster: eviction tests use
patch.object(session, "save")instead of 200-500 real disk writes. - Hook cold-start: 190 ms → 110 ms (~42% faster) via lazy imports in
hooks_session.py. - Compact-skip sentinel: <1 ms exit on fresh sessions when no edits since the last manifest.
- Skip git ops entirely when
cwdis not a repo (saves 60-100 ms per hook fire in non-repo dirs). - DB contention metric surfaced in
doctor. pytest-xdist --dist=loadscopefor module-scoped fixture reuse across tests.
DRY
- 16 git subprocess sites consolidated through
util.run_git()(always--no-optional-locks+ UTF-8 witherrors="replace"). cache_common.safe_cache_opcontext manager +store_blobfor atomic blob writes +short_content_hash()shared across bash/web/skill caches.paths.safe_join(),paths.hook_wrapper_path(),paths.normalize_key()promoted as canonical helpers.util.sanitize_surrogates,util.humanize_bytes,util.ellipsizecross-module helpers.- Session 6-item helper bundle (
safe_load,_merge_lists,_cap_dict,_bump_read_count,_session_path,_atomic_write).
Codex / opencode / openclaw compatibility
- Wire-format round-trip tests for every hook event across all four harnesses (Claude / Codex / opencode / openclaw); bridge TS event-table alignment regression test.
- PowerShell
bash_parsercoverage:Get-Content,gc,typeambiguity guard, equals-form flags. - Bash dispatch + golden-output tests (+151) across all 17 compression filters.
CI
- Workflow split into gating fast tier (
-m "not slow") + opt-in slow tier withcontinue-on-error: trueso racy multi-process stress tests don't block the build.
Suite at end of loop: 5051 pass / 28 skipped (started this release cycle at 4598; +453 tests added).
Full details in CHANGELOG.md.