The self-referential inspect tool surfaces Session's public API in its
transcript; the construction-boundary field added in bc8512d17 now
appears there. Keyless refresh; only the affected scenario re-recorded.
Resolutions: regenerate the conflicted generated docs (cordis services
catalog, event-producer-consumer, module-graph); take master's
packages/README pair and re-insert the telemetry row on both sides;
re-record the README and session-doc translation pairs.
The local PTY readiness poll held its inferred_idle fallback for exactly
one pollIntervalMs after a prompt marker, so a bash foreground handoff
that lands on the silence boundary only wins the exact stdin_read
attribution when the kernel publishes it inside that single poll. On a
slow or loaded host it does not, and the attribution flips.
handoffGraceMs replaces the hardcoded one-poll window as a validated,
deployment-owned config field defaulting to 500ms, rejected at load when
it cannot contain one readiness poll. Real-shell tests that interrupt a
send now assert the session is usable again rather than which readiness
tier observed the handoff, because no fixed grace removes the race.
Two review findings on the turndown swap, both verified empirically:
- Unclosed-tag nesting makes the synchronous turndown/domino walk
superlinear (measured: depth 512 ~0.15s, 2k ~2s, 20k ~5s), during
which the cooperative fetchTimeoutMs timer cannot fire. renderBody
now preflights nesting depth with a linear tag scan and passes
bodies past 512 levels through raw; the try/catch stays for markup
the scan cannot see (comment-hidden tags), simulated in tests via a
converter throw.
- Markdown escaping can expand converted HTML ~2x (100k underscores
render as 200k chars), so provider body caps no longer bounded the
model-visible result. formatFetchOutput now caps the complete output
(header + body + footer) under new fetchMaxOutputChars config
(default 200000 = 2x the local provider's default body cap), reusing
the truncation notice.
README EN+ZH, config catalog, Agent Note EN+ZH updated; the new
web-fetch fixture is migrated to the packed layout master now
requires; tool-web coverage stays 100% per-file.
The simplified request path no longer anchors an unchanged request/header on
resume (agent.ts logs a header only when it differs from the folded baseline),
so resume-turn logs drop that event and later seqs shift down. Refresh the
keyless session-log fixtures to match; no model scripts changed.
Implements the turndown Agent Note from the NIH dependency audit (full
variant, not the minimal entities-only fallback): dsh-tool-web's fetch
rendering now converts HTML through turndown + @joplin/turndown-plugin-gfm
(atx headings, fenced code, dash bullets, GFM tables/strikethrough) over
the real domino DOM, with script/style/noscript removed wholesale. The
hand-rolled ~86-line regex converter html.ts and its entity tables are
deleted; renderBody wraps the conversion in try/catch falling back to
the raw HTML body, because turndown's recursive DOM walk overflows with
a RangeError on pathological nesting (measured: 4k levels on the main
thread, 8k in a worker) where the regex version could never throw.
Closure weight, measured: tool-web IS in the single-exe runtime closure,
and the exe asset globs would pack ~7.9 MB of the three new packages —
but ~6 MB of that is domino's test corpus, with runtime lib/ at ~550 KB
against a ~174 MB artifact (<0.5% either way), so the swap wins.
Per testing policy the previously-missing keyless web_fetch snapshot
ships in the same change: the acp-agent `web-fetch` scenario boots a new
web.cordis.yml overlay (web seam + real dsh-web-fetch-local provider +
tool-web fetch-only + a loopback HTTP fixture server on a fixed port
serving deterministic HTML with entities, a GFM table, and nesting), so
recording and keyless replay both drive the real HTTP fetch and real
conversion end to end; the scenario pins the new `web` header class.
The Agent Note moves proposed -> implemented and is rewritten per the
lifecycle contract (Decision/Consequences/Testing, closure verdict and
alternatives recorded); tool-web and acp-agent READMEs updated in both
languages and pairs re-recorded.
- proposed/testing -> implemented/testing with the lifecycle rewrite
(Proposal->Decision in present tense, Acceptance criteria + Risks
folded into Consequences); zh counterpart mirrored and both pairs
re-recorded.
- the rejected NIH-audit roll-up pair now links the implemented/ path.
- loader-smoke README: captured output is bounded by execa's default
100 MB maxBuffer, no longer unbounded.
- acp-snapshot README: harness.ts now also imports vitest (vi.waitFor),
so the vitest-run-only constraint names both modules.
- jsonrpc keyless smoke: raise the invalid-env case's subprocess
deadline to 25s (the 9s pick starved a cold tsx boot on slow NFS).