NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #3675 most downloaded on pub.dev
File-descriptor-level (dup2) capture and redirection of stdout/stderr, including native/FFI and child-process output invisible to Dart streams. POSIX only.
Last release 2 months ago
20 Jul 2026
Too new to tell
only 2 dated releases
Nearly every release is documented
notes for 2 of 2 stable releases
Nothing withdrawn
no release was ever pulled
2 months old
2 releases · first in 2026
One column per month.
api: trim the sink hierarchy; symmetric adopt; conflict-semantics har…
api: trim the sink hierarchy; symmetric adopt; conflict-semantics har…
Pre-publish consolidation: correctness fixes at the fd layer plus a final API finalization pass (breaking — 0.4.0 was never published).
Correctness
stop() now restores the saved descriptors' original file-status flags.
The mirror-to-original path sets O_NONBLOCK on the saved fds, and the
flag lives on the open file description — shared with the fd 1/2 handed
back on stop (and with the parent shell when the target is the tty) — so
post-stop writes could EAGAIN, and the leak outlived the process.maxLineBytes is now enforced on newline-terminated lines too: a long line
arriving within one read chunk previously bypassed the cap entirely. Runs
are split byte-exactly into cap-sized pieces (an exactly-cap-sized line
stays whole); the README's split guarantee is now unconditionally true.write()).
pause()/resume()/startProcess/adopt throw StateError on a
degraded session; readerError + closed streams remain the signal.mirrorDroppedBytes counter (they were silent; they are a separate channel
from the line counters, which they deliberately do not pollute).droppedLines/droppedBytes now count exactly the lines missing from
history: reader-side backpressure drops PLUS history-ring evictions
(evictions were previously uncounted, making the documented totals wrong).close() is no longer retried on EINTR (on Linux/macOS the fd is
deallocated regardless, so the retry was a double-close that could hit an
unrelated reallocated fd).stop()'s drain-timeout path no longer closes fds a wedged-but-alive
reader may still be blocked in read()/poll() on (deliberate bounded
leak instead of fd-reuse byte theft), and the backstop
Isolate.kill(immediate) is now issued only on that path — it can no
longer race a healthy reader's cleanup.startProcess()/adopt() after stop() now throw StateError instead of
silently discarding the child's output.classify callback no longer burns reader send credits (which
permanently stalled the live feed after ~8 throws): the line is treated as
unclassified and the first error is surfaced once on readerError.open(2) is bound through VarArgs like the other variadic syscalls.StdioRedirect.stop() releases the process-wide slot in a finally, so a
(nearly impossible) restore failure can no longer wedge the package with
no way to start a fresh session.Breaking API changes
StdioCapture → Stdio, so the entry points read
as Stdio.capture / Stdio.start / Stdio.redirectToFile (matching the
package name).Captured is now Captured<T> with a value field: Stdio.capture<T>()
returns the body's result alongside the transcript, and stop() returns
Future<Captured<void>>. Captured also gained droppedLines /
droppedBytes (lines missing from lines).Stdio.capture() accepts historyLines / maxLineBytes / mirrorToFile
/ classify, forwarded to start().startProcess() returns CapturedProcess (the process plus a drained
future that completes on full delivery); adopt() now returns
CapturedProcess too (it was the bare drain future — ambiguous at the
call site) and throws a clear StateError when the child's streams were
already claimed (adopted twice / listened before adopt).mirrorToSavedFds: → mirrorToOriginal: (introduced unpublished
after 0.3.0), with real documentation and, new in this release, drop
accounting (mirrorDroppedBytes) and end-to-end tests.terminal is now typed StdoutTerminalSink — an IOSink with the
terminal accessors and a concrete Stdout — and the redundant
terminalStdout is gone (it was a second mutable-encoding handle to the
same fd). terminalStderr (introduced unpublished after 0.3.0) stays, as
the saved fd 2 counterpart. The abstract TerminalSink interface is gone
too: it had no consumers and both implementations are final, so
FdTerminalSink is now the documented base type (nullable-safe
columns/rows; StdoutTerminalSink adds the throwing Stdout-contract
terminalColumns/terminalLines view of the same data).CapturedLine gained seq, a monotonic per-session sequence number
stamped as each line enters the transcript — the robust history→stream
stitch across awaits (the constructor now requires it).CapturedLine.text strips one trailing \r (CRLF output reads clean);
bytes stays byte-exact. StdoutTerminalSink.writeln honors
lineTerminator (payload \ns still pass through untranslated).StdioException moved to its own library and is now exported (the 0.4.0
pre-release changelog wrongly called the non-export deliberate: callers
could neither catch it by type nor read readerError usefully).Stdio.anyActive static — advisory "is any capture/redirect holding
fd 1/2" probe for coordination; the uncoordinated second start() still
throws StateError.platforms: linux, macos declared explicitly (POSIX-only).Docs
stop()/capture() return the retained transcript
(the last historyLines lines; see droppedLines) — the docs no longer
claim everything is guaranteed present, and note that a throwing capture()
body loses the transcript (use start()/stop() for post-mortems).StateError
on every entry point, redirectToFile moving BOTH descriptors, the
FIFO-mirror caveat, readerError carrying the stringified isolate error,
the gap-free history→subscribe recipe (and seq stitching), and
the sink flush/close no-op semantics on the sink type itself.dart run stdio:verify is mentioned as the platform self-check.pause() / resume() / isPaused: temporarily point fd 1/2 back at the real terminal without tearing the session down — the terminal-handoff primitive fo
pause() / resume() / isPaused: temporarily point fd 1/2 back at the
real terminal without tearing the session down — the terminal-handoff
primitive for TUIs (spawn $EDITOR/a pager with inheritStdio mid-session;
the child inherits the real descriptors). Buffered writers are flushed at
both edges so bytes land on the side of the boundary they were written on.
stop() remains safe while paused.Your coding agent can read these notes before it upgrades. Set up the MCP server →