Adaptive context hygiene, AGENTS.md, cache hardening, GLM usage fix#8
Merged
Conversation
Age-based clearing treats a 5-minute-old read of the file being edited the same as a stale build log. Supersession is the semantically correct trigger: after a re-read, the older bytes aren't just bulky — they may be WRONG (the file changed). dedupeSupersededReads evicts them regardless of age: - a whole-file read supersedes every earlier read of that path - an identical-input read supersedes earlier identical reads - different ranges of the same path never supersede each other Cache discipline matches clearStaleToolResults: deterministic stub (bytes depend only on the path), monotonic (one transition, then byte-stable forever), minChars guard so small results aren't churned. Runs at turn start before age-based clearing, and inside compact(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dback The stale-result sweep now decides AT RUNTIME whether it pays, using the harness's own telemetry instead of assumptions: - cacheLive: did recent turns show real cache reads? If the endpoint ignores caching, there is nothing to protect — clear eagerly. - cacheLikelyCold: idle past the ~5min provider TTL (or fresh session) means the prefix is cold and the bust is free — clear eagerly. - otherwise run the break-even: 0.1 × savedTokens × observed avg requests/turn vs 1.25 × tail re-cache cost. Clear only when it pays. clearStaleToolResults gains dryRun planning (savedChars + tailChars); staleClearWorthIt is the pure, tested decision function. Supersession dedup stays always-on — it's correctness, not economics. The decision itself is explainable: TurnLens.hygiene records what was evicted and WHY (or why deferred), shown in /lens and the HTML report. MIST_ADAPTIVE_HYGIENE=0 restores unconditional clearing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… message_delta Wire-probed the z.ai endpoint: it sends ZEROED usage in message_start and the real numbers (input_tokens, cache_read_input_tokens) in message_delta at stream end — the opposite of Anthropic's API. We only read input from message_start, so every GLM request logged 0 input, /lens showed 'context now 0', and the real-token compaction trigger never saw a real reading (fell back to the estimate). message_delta now also accepts positive input/cache readings; Anthropic's API omits those fields there, so its values are never overwritten. Regression test uses the captured GLM wire shape. Live-verified on GLM: /lens now shows real input — and real cache activity (96% of input served from cache on the probe turn), so z.ai honors our cache_control breakpoints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The lens ledger lived only in engine memory, so a resumed session
answered /lens with 'no turns recorded yet' — the explainability
module forgot exactly the history you resumed to look at.
Sessions now persist one {"kind":"lens","turn":…} JSONL line per
completed turn (seq-guarded so errored turns that recorded nothing
don't duplicate), and resume rehydrates the engine ledger (capped at
the same 50 turns). rename() already preserves non-meta lines, so
titles and ledgers coexist. Verified live: run → quit → --continue →
/lens shows the pre-restart turn, cache numbers intact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mist now reads repository guidelines the way the ecosystem converged: Discovery (agents_md.ts): ~/.mist/AGENTS.md (user-global) → git-root → cwd, broad-first so deeper files override by recency; MIST.md wins over AGENTS.md per level; walk stops at the .git marker (dir OR worktree file); 32 KiB total budget — this text is resent every request forever, so the cap is the prefix budget. Cache contract: docs load ONCE and freeze into the system prompt. A mid-session edit is detected at turn start and appended at the TAIL as '[project-docs update] These AGENTS.md instructions replace ALL previously provided…' — the stale copy stays cached in the prefix, recency + the notice neutralize it, compaction garbage-collects later. Wire test asserts the system block stays byte-identical across the edit and the supersede message carries the new text. /init is prompt-sugar, not harness logic: submits a canned prompt and the model explores the actual repo and writes AGENTS.md with ordinary tools (hooks apply). Spec: 200-400 words, real commands, conventions inferred from git history, never overwrite an existing file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sting warm cache Supersession dedup was unconditional; clearing a read buried 200k tokens deep re-caches the entire tail to save a few k of stale text. The correctness argument doesn't require eagerness: the NEWER read is in context and recency outranks the stale copy, so clearing can wait. chooseSupersededToClear picks a DEPTH CUT, not all-or-nothing: bust cost is set by the earliest cleared block, so it evaluates every suffix of the candidate list and clears the best positive-net suffix. Deep candidates are deferred — the next cold cache (idle past TTL), inert endpoint, or compaction clears them for free. Same lens-driven signals as the stale-clear governor; decision recorded in TurnLens.hygiene (dedupedDeferred) and shown in /lens. MIST_ADAPTIVE_HYGIENE=0 restores unconditional clearing; compact() still dedupes everything (prefix dies anyway). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Supersession assumes the file IS its latest content (state semantics).
That's true for source/config/docs — and false for logs and run output,
where each read is a TIME SAMPLE of a changing stream: the error trace
read before a fix is unique evidence that a clean re-read afterwards
must not evict.
isObservationFile exempts *.log/out/err/trace, log dirs, and
*.jsonl/ndjson event streams from supersession ('log' inside a source
filename doesn't match; lock files are state and still supersede).
MIST_SUPERSEDE_SKIP adds a user regex to the exempt list.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two changes from auditing the official prompt-caching rules: 1. Sliding breakpoint window. We marked only system + the final block, so hits relied on the server's 20-block lookback finding the previous request's entry — a single large tool batch appends >20 blocks and outruns it, silently re-writing the whole conversation. Now the last block of each of the last THREE messages is marked (+ system = the 4-breakpoint API max). One request cycle appends 2 messages, so the previous request's breakpoint is always still explicitly marked: exact-hash hit, lookback never needed. 2. MIST_CACHE_TTL=1h — hold the tools+system prefix for an hour (2x write once, same 0.1x reads). Human think-time between turns routinely exceeds the 5-minute default TTL and cold-started every turn. Ordering constraint (1h before 5m) is satisfied by construction: system is the first breakpoint. Wire test asserts the window shape (last 3 marked, older unmarked, exactly 4 breakpoints) and TTL placement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Eight commits: a self-tuning context-hygiene layer driven by /lens telemetry, AGENTS.md support with cache-safe semantics, prompt-cache hardening per the official docs, and a GLM usage-parsing fix verified on the wire.
Adaptive context hygiene (029ab71, df83a00, 8ee6fd8, 94ddff8)
MIST_SUPERSEDE_SKIPextends the exempt list;MIST_ADAPTIVE_HYGIENE=0restores eager clearing.TurnLens.hygieneand shown in /lens — adaptation stays explainable.AGENTS.md + /init (099bb5a)
~/.mist/AGENTS.md→ git root → cwd, broad-first so deeper files override;MIST.mdwins per level; 32 KiB budget;.gitdir or worktree-file marker./initis prompt-sugar: submits a canned prompt; the model explores the actual repo and writes the file with ordinary tools (hooks apply). 200–400-word spec, never overwrites an existing file.Prompt-cache hardening (2ec1f4c)
MIST_CACHE_TTL=1h: hold the tools+system prefix an hour; human think-time routinely exceeds the 5-minute default TTL.GLM usage fix (49f7396)
Wire-probed z.ai: it sends zeroed usage in
message_startand the real numbers inmessage_delta— the opposite of Anthropic. GLM sessions logged0 input · context now 0, and the real-token compaction trigger silently fell back to estimates. Fixed with positive-reading-wins parsing; live-verified: /lens now shows real input and 96% cache-read on GLM — z.ai honors our breakpoints./lens resume persistence (577ee4f)
One
{"kind":"lens"}JSONL line per turn in the session store; resume rehydrates the ledger — /lens no longer answers "no turns recorded" after a restart, and adaptive-hygiene signals survive resumes.Test plan
cd ts && bun test— 123 tests, 0 fail (wire-format tests for breakpoint window/TTL/frozen AGENTS.md prefix; depth-cut economics; observation-file exemptions; store roundtrip)🤖 Generated with Claude Code