Seeding additionalDirectories at launch (via --add-dir or the
workspace.additionalDirectories setting) called #rewriteAtomically on a
brand-new session manager, which materialized a header-only JSONL and a
fresh breadcrumb before any assistant output. Launching and exiting with
configured roots therefore created an empty resumable session that
--continue picked over the previous conversation.
Gated all three workspace-directory mutators behind the existing
#shouldHaveSessionFile() lazy-persistence gate (one shared helper), and
made setAdditionalDirectories a no-op when the normalized list is
unchanged so resuming large sessions no longer rewrites the whole JSONL
on every startup. Roots set before the gate is crossed land in the
header with the first durable write; added a regression test.
Gated the read.renderMarkdown opt-in at read time (details tagging) instead
of inside the renderer. The renderer gate silently flipped every
protocol-supplied text/markdown read (skill://, pr://, issue://, history://,
rule://, omp://, agent://, vault://, local://, memory://, ssh://) from the
formatted markdown cell to the raw code cell when the setting was off, which
regressed default TUI behavior. Local file tagging now happens only when the
setting is enabled, so the default render path is byte-identical to the
pre-setting behavior while opt-in previews still work end-to-end.
Also inlined the tautological isMarkdownContentPath wrapper, pinned the
widened prose-summary bypass (.mdx stays verbatim when prose summaries are
off) with a test, and documented it under Changed in the changelog.
Tested the shell-control guard against the raw command: whitespace
normalization collapsed newlines/CR before the guard ran, so
'git status\nrm file.txt' rode a 'git *' allow rule while bash executed
both lines. Honored tool-owned allow/prompt policies in yolo mode so
per-command prompt rules were no longer silently discarded under the
default approvalMode. Added precision regression tests through the real
matcher (separators, subshells, redirects, env prefixes, path/quoting
variants) that fail on the unfixed head.
Forced retains returned early with an empty incremental slice when no new
messages arrived since the last successful auto-retain, so a user-visible
/memory enqueue rebuild sent nothing and could not recover a deleted or
unmaterialized upstream document. Also removed the public modifier from the
test fake per the root AGENTS.md class-privacy rule and added a forced-retain
resend test.
- Credential-shaped token redaction now requires explicit opt-in via `configureCredentialRedaction` and is disabled by default, so user-supplied credential-like strings reach providers unmodified unless the host enables redaction.
- Wired the `secrets.enabled` ("Hide Secrets") setting to `configureCredentialRedaction` in the coding-agent so the pattern redaction follows the existing secret-obfuscation toggle.
Review follow-ups (Codex on #6362, round 5):
- #computeSnapcompactRescueMaxFrames now subtracts the kept tail AFTER the
archive (plus the existing fixed-context reserves) so the budget mirrors
what #compactionCreatedHeadroom will measure, and returns 0 when not even
one frame fits — the rescue bails instead of appending a rebuild that can
never create headroom (and would wedge prepareCompaction behind its
last-entry guard once elide fixes the real tail).
- Dead-end warnings now stamp the branch's LATEST compaction entry: the
post-pass path no longer badges the entry the rescue just superseded, and
the no-preparation path badges the rebuilt entry when the rescue appended
without creating headroom.
Claude-Session: https://claude.ai/code/session_014rh4JyWFkxgMhgFaEf8VBY
Review follow-up (Codex on #6362, round 4): rebuilding a non-tail archive
appends the replacement compaction at the leaf, so the branch tail becomes a
compaction entry that prepareCompaction's last-entry guard can never
summarize past — even after elide shrinks the oversized kept tool result
that was the real culprit. The rescue now estimates the kept tail AFTER the
latest archive and bails when it alone exceeds the recovery band, leaving
that shape to the elide/image tiers.
Claude-Session: https://claude.ai/code/session_014rh4JyWFkxgMhgFaEf8VBY
Review follow-ups (Codex on #6362):
- #rescueSnapcompactFrameOverflow now returns the CompactionResult and emits
session_compact for the rebuilt entry, so extensions see the entry that is
actually active instead of (only) the one the rescue superseded.
- The no-preparation auto_compaction_end now carries that result instead of
{result: undefined, skipped: true} when the rescue rewrote history — the
TUI rebuilds the transcript on result, so a successful rescue is no longer
presented as a benign no-op.
Claude-Session: https://claude.ai/code/session_014rh4JyWFkxgMhgFaEf8VBY
Two fixes on top of the paged transport:
- Near-limit v2 framing no longer materializes the full base64 transport:
chunk lines are generated lazily from a single serialization, the 64 MiB
reassembly ceiling is enforced via Buffer.byteLength before any
full-payload allocation, and RPC stdout writes drain with backpressure
one physical line at a time. Peak RSS for a 63 MiB response drops
~686 MB -> ~521 MB; a rejected 80 MiB response drops ~507 MB -> ~259 MB
(parity with the v1 path).
- get_messages_page errors now carry a machine-readable code
(session_busy | stale_cursor). Both bundled clients' high-level
getMessages() drains discard partial pages and fall back to the legacy
snapshot on either code — previously a cursor invalidated by a
background mutation (e.g. an appended bash message) threw instead of
falling back. Direct page calls remain strict.
Review follow-up (Codex on #6362): the rescue's replaceMessages() rebuild
drops the transient plan-reference message, so clear #planReferenceSent
(#1246) and reset advisor runtimes / todo phases exactly like the regular
compaction append path.
Claude-Session: https://claude.ai/code/session_014rh4JyWFkxgMhgFaEf8VBY
A budget-aborted keep-alive subagent's job row (job id == agent id) settles
failed and is retained ~5 min; executeCancel short-circuited to
already_completed for that window, leaving the zombie registration
unkillable exactly when the user wants it dead. Fall through to
cancelAgentRegistration for settled rows; keep already_completed when no
lingering registration exists.
Also stop wiring AgentLifecycleManager.global() onto SDK sessions created
with a caller-supplied agentRegistry: the global lifecycle releases through
AgentRegistry.global(), so it would report a cancel while releasing an
unrelated global ref. Without a lifecycle, cancel falls back to
dispose + unregister on the session's own registry.
Addresses both Codex P2 review findings on #6319.
Collect TSImportEqualsDeclaration/TSExternalModuleReference targets so
legacy .ts/.cts extensions using `import x = require("pkg")` get their
bare dependencies pinned like plain require() calls. Fold of the #6256
follow-up (comicchang/oh-my-pi@1e54b68) requested on #6324.
Servers may allow the unauthenticated MCP handshake yet protect individual
tool calls via _meta["mcp/www_authenticate"]. The 'reauthorization is not
required' guard would silently abort the tool-challenge reauth path.
The getDiscoverableProviders() guard skipped awaiting runtimeDiscoveryPromise
when no config-discovery providers exist, so a cold deferred selector backed
only by runtime model managers (extension fetchDynamicModels) with implicit
local discovery disabled still resolved against the offline cache. Awaiting
unconditionally is free when no runtime managers are registered
(refreshRuntimeProviders early-returns); the full refresh fallback stays
gated on discoverable providers.
Route loadSessionMessagesReadOnly through transcript mode (collapsed to the
latest compaction) so history:// transcripts of on-disk sessions retain
failed/aborted assistant tails that the provider-context builder now drops.