Skip to content

v0.42.6

Choose a tag to compare

@github-actions github-actions released this 19 Sep 02:59
· 4082 commits to master since this release

Behavior change

  • Agent ctx_reduce drops no longer cause a cache bust; they ride the next one. A queued drop used to apply on the first scheduler execute pass, which rewrote the prompt prefix to reclaim a few thousand tokens minutes before a history fold would have done the same for free. Now the scheduler's execute decision alone never originates a bust: queued drops and every automatic reclaim lane (age sweep, supersession, caveman, reasoning clearing) land only on a pass that is already busting for another reason — a history fold or refresh, /ctx-flush, or the force band at 85%+. The ctx_reduce tool description says so. Same rule on OpenCode, Pi/OMP and the Rust module. The e2e fixtures that assumed "execute applies my drop" were changed to drive a real bust; no invariant was weakened.

Fixes

  • OpenCode sessions were labelled as OpenCode 2 after 0.42.4. OpenCode 1.18.30 calls both the v1 server() entry and the v2 setup() entry on the published plugin object. setup() treated that as an OpenCode 2 host and locked the harness to opencode2, so every session-scoped row that seat wrote (tags, session metadata, project bindings, compartments, notes, and the rest) was stored under the wrong label. Dashboard subagent filtering, per-harness session delete, and doctor counts then looked at the OpenCode label and missed those rows. setup() is now inert unless the host actually provides an OpenCode 2 session hook, and server() locks opencode first so a wrong-order call fails at boot instead of mis-tagging. The inert branch logs to the plugin log only; an earlier build wrote it to the console, where the TUI painted it onto the prompt line.

  • Existing mislabelled rows are rewritten on first boot. Schema version 85 relabels opencode2 → opencode in every table that stores a harness. No released Magic Context has run on a real OpenCode 2 host against the shared database, so those rows are the v1 mislabel above. Where the same session already had both labels (session project bindings are the common case), the newer row is kept. The fresh-install schema is unchanged.

  • Auto-update no longer disables itself after the first update (#466). After a successful update from a bare package spec, the updater pins the config to the exact version it installed (deliberate since 0.38.0: OpenCode's loader can delete an unpinned active package mid-session), and the next check then read that pin as a user opt-out and skipped forever. The updater now records the spec it wrote; a pin equal to it is treated as the updater's own and the next update proceeds, while any other exact pin is still respected. The two branches log differently, doctor prints an "auto-update: stalled — pinned by updater / by you" line, and the README documents the opt-outs (auto_update: false, or a pin to any other version).

  • ctx_note has one id field (#460). note_id and note_ids collapsed into note_ids: exactly one id for update, one to fifty for dismiss, ignored by write and read. Providers that send every declared property no longer fail a write on the old mutual-exclusion rule. Same surface on OpenCode, Pi/OMP and the Rust facade; the Rust facade also now honours batch dismiss, which the TypeScript adapter had never forwarded.

  • Filler arguments on ctx_expand, ctx_memory, ctx_search and ctx_reduce are ignored, not rejected. Same class as #460 on the other four tools, on every lane.

  • Rust transform mode recovers from a silent engine request instead of refusing the turn. A transform request that goes quiet while the engine reports healthy is re-sent once (requests are content-addressed, so a duplicate cannot apply twice); if a turn was still refused, the session is resumed automatically with one "engine reconnected, continue" prompt once the engine answers, unless the user has already sent a newer message. Never on a provider-proven emergency. Each request now leaves a receive/complete trace in session.status so a stall is attributable. On a slow host the recovery watcher could read the refused turn's own still-finalizing assistant row as newer history and cancel the resume; an unfinished assistant child of the refused prompt now counts as the refused step.

  • Historian chunks no longer chain across consecutive tool arcs (#467). The rule that keeps a completed tool arc whole in one chunk could keep admitting the next arc, and the next, once the chunk budget landed inside one, so a tool-loop marathon could hand the historian a 230k-token chunk that exceeded its model. The head escape now admits one connected component and stops; the producer-window guard keeps a 3% estimator margin (the report overflowed by one token) and logs its numbers on refusal; a component that cannot fit even alone is split at its largest tool-result boundary with the existing truncation marker instead of being refused on every pass. Same rule on OpenCode, Pi/OMP and the Rust module. An explicit limit.context in opencode.json wins over the provider catalog value.

  • Entering rust mode costs one prompt-cache rewrite, not two. The first pass after a session flips to transform_mode: "rust" rendered its messages without their tag prefixes and the next pass added them, rewriting the prefix twice. The full tag surface now renders on the same pass that commits the new render configuration. Module-side; ships with the ck-mc bounce.

  • Rust mode tolerates a compartment whose boundary message no longer exists. One such row parked a 1,500-message session on every pass ("engine is reconnecting"). The seed now resolves the boundary from its neighbours and logs the repair; the historian refuses to publish a boundary that disappeared during its run; doctor lists dangling boundaries.

  • Rust-mode memory ids are host ids in every mode (#468, iceteaSA). Under transform_mode: "rust" the ctx_memory tool and the <project-memory> block used the module's own id space, which overlaps the host's — an id remembered before a mode flip could silently read or mutate a different memory. Ids are now translated at the tool boundary in both directions, a write returns the host id, the memory block renders host ids, an unmapped id fails with a translation message instead of a scoping one, and every error path renders host ids too. Requires the matching ck-mc build (store migration 54); each rust-mode session pays one prompt-cache rewrite when the block switches to host ids.

  • Dreamer last_run_at advances from run start (#463, TreedsSlop). A compartment that landed while a task was running was older than the completion-time cutoff and skipped by the next slot; the watermark is now the run's start time, so nothing lands in the gap.

  • Pi resolves embedding routing before registration (#462, Qiiks). An unreachable Synapse could register a Synapse identity over an empty connection file, shadow_embedding was ignored on Pi, and a fallback was memoised for the process. Pi now goes through the same routing resolver as OpenCode; a retired shadow lane is unregistered on both harnesses without disposing a provider the primary lane still uses.

  • Dashboard Hide subagents now follows OpenCode's own parent pointer. Task-spawned child sessions are hidden when session.parent_id is set in OpenCode's store, even if Magic Context has no metadata row for that child, and the metadata flag is honoured whether it was stored as opencode or opencode2.

  • protected_tokens below its minimum now says what it is. A value like 20 (the old protected_tags count kept under the new key) produces a specific warning naming the token floor instead of the generic invalid-value message.

  • Pi and Rust count aged-out tool outputs as reclaimable. Tool outputs appended after the hygiene baseline were excluded from the reclaimable mass forever, so Channel 1 and Channel 2 nudges could stall once baseline refreshes became rarer under the ride-only rule. OpenCode had the same gap; all three lanes now agree.

  • Cache-bust analyzers join decision rows correctly on Pi, and dropped_tokens is a per-tag sum on Pi. The Pi analyzer labelled every bust no_mc_pass_row because it never loaded the decision rows; the Pi telemetry column was a constant zero.

OpenCode 2

  • Historian and text-only Dreamer runs use their configured cheaper models without changing the user's session. session.generate is pinned to the session model, so Magic Context reuses one visible root session per project and role, sends the exact calibrated prompt and generation options through a fail-closed context hook, records provider usage from the persisted assistant row, retires failed roots, and exposes doctor list-hidden-sessions as a read-only inventory. OpenCode does not yet let plugins remove these roots (opencode#49389, #49549, #49550 filed), so cleanup remains manual.

  • Pinned to @opencode/* 2.0.5. 2.0.5 removed plugin.awaitActivation; the hermetic runner now waits on plugin.updated and reads plugin.list state, failing fast with the host's error when the plugin reports failed. context.model.list() returns { data } on 2.0.5; the pre-fold guard that iterated it threw inside the context hook and silently interrupted every turn on 2.0.5 — fixed, both shapes accepted.

  • Per-model tool descriptions on OpenCode 2. The context draft carries the tool map per request, so prompt_surface.models now routes the ctx_* descriptions per model as well as the guidance; on OpenCode 1.x descriptions still follow default.

Documentation

  • cache_ttl accepts provider/* wildcards (documented; the generator previously dropped union-typed rows and rendered paired provider/* mentions as emphasis).
  • Timestamp column units (epoch ms) and their writers are documented in ARCHITECTURE.md; ARCHITECTURE.md/STRUCTURE.md refreshed for the ride-only rule, rust-mode recovery, the OpenCode 2 child carrier and filler tolerance.