Implemented 2025-11-25 Streamable HTTP polling semantics for POST SSE responses: retain event IDs and retry intervals, wait as instructed, and reconnect with GET plus Last-Event-ID until the originating JSON-RPC response arrives.
Extended the shared SSE parser to expose valid id/retry fields and control-only events so reconnecting consumers do not need to reparse raw lines.
Fixes#8264
Long write/edit streams made the TUI stutter or freeze for seconds at a
time (ui.loop-blocked warnings, keystrokes starving while the single JS
thread rebuilt previews). Two compounding quadratic paths:
1. parseStreamingJsonThrottled re-parsed the entire accumulated args
buffer on a FIXED 256-byte cadence. The comment claimed this bounded
mid-stream work to O(N), but a constant growth gate still parses an
N-byte buffer N/256 times at O(N) each: O(N^2) with a smaller
constant. The gate now scales geometrically (max(256, len/32)), so
parse points form a geometric progression: O(log N) parses, O(N log N)
total, with mid-stream snapshots staying within ~3% of the stream.
Small buffers keep the exact fixed cadence as before.
2. write.ts formatStreamingContent normalized + split('\n') the WHOLE
accumulated content on every 30Hz reveal tick (renderCall also ran a
full-payload normalize first): O(N) per tick, O(N^2) per stream, per
concurrent writer. The collapsed tail-window path now tracks the
newline count incrementally (append-only resume keyed on the
component's persistent render-state object via WeakMap) and extracts
only the tail window with a backward scan: O(delta + preview lines)
per tick, byte-identical output to the split-based reference. The
expanded (Ctrl+O) path is unchanged in output and skips its
split+join round-trip.
Tests: geometric-gate bounds + freshness + small-buffer cadence in
parse-streaming-json-throttled.test.ts; append-growth/reference-
window/CRLF/trailing-newline/restart battery in
write-streaming-incremental.test.ts. Full write/tool-render battery
(56 tests) and ai streaming-args tests (117 tests) pass.
- The two-child fs.watch/poll choreography deadlocked under parallel test load
(runner killed a dangling probe); the PID-namespace pruning test still covers
the multiprocess audit/rotation file contract.
- mnemopi provider parity 'diagnose, validate, graph' does ~6.7s of real
work under bun --parallel=8 on loaded runners; raised its per-test
timeout to 30s (default 5s flaked twice in three CI runs).
- utils LRUCache updateAgeOnGet drove a 30ms TTL with real 20ms sleeps
(10ms margin); now drives performance.now() via a mocked clock, so the
contract is asserted deterministically with no wall-clock wait.
- A blank run at EOF now breaks the list without consuming the blank,
matching real marked: '- item\n\n' lexes as a tight list plus a space
token instead of a loose list whose raw includes the blank.
- Completes the 17.2.10 mid-document fix; same-marker continuation and
indented item content across blanks are unaffected.
- Added list/blank boundary token-shape tests (verified against marked
v15) since the tui incremental tests compare the lexer to itself.
- Implemented in-house, zero-dependency utility modules in `pi-utils` covering DOM manipulation, markdown parsing, templating, browser automation helpers, and terminal buffers.
- Migrated packages across the repository to consume the new internal utilities and `omptype` schema validators instead of external dependencies.
- Removed multiple external runtime and development dependencies including Zod, Marked, LRU cache, Turndown, and Puppeteer browser packages.
Registered live session resume commands with postmortem handling so a
fatal rejection or exception identifies every recoverable agent before
cleanup. Escaped terminal control characters in recovery output.
Account-reset hint evaluated before short retry hints; account-scoped caps rotate on status 403 or undefined (Devin statusless trailer); statusless concurrency caps marked transient; transient same-model retries use the concurrency backoff.
Refuted: quota-worded concurrency caps were already excluded from rotation before the usage-limit text match.
(cherry picked from commit f2b9a18d715ddbcb6ae703670f2212da36bb2826)
- Added native `FileLock` bindings supporting cross-process advisory locking on Linux, Unix, and Windows.
- Replaced directory-based file locking and custom stale-lock reclamation with OS-backed native locks.
- Updated TypeScript declarations, native bindings, and package documentation for the new API.
- Added comprehensive unit tests and fixtures validating single-owner constraints and process death handoff.
- Moved the coding-agent lock-directory primitive to @oh-my-pi/pi-utils/file-lock
and migrated settings, MCP config-writer, and security store imports.
- Replaced the stats aggregator's parallel ~200-line token/breaker lock protocol
with the shared primitive: dead owners reclaimed immediately, live-but-wedged
owners after STATS_SYNC_LOCK_STALE_MS, unstamped acquisitions after the new
acquireStaleMs grace (10s).
- Shared primitive now treats EPERM kill probes as live owners.
- Rewrote the stats lock-reclamation regressions against the shared protocol
and moved the file-lock contract test into pi-utils.
- waitForLogEntry raced winston's async flush and JSON.parsed a
partially written line, failing the error-serialization tests on
loaded CI runners; unparseable lines now wait for the next poll.
On Windows process.env/Bun.env lookups are case-insensitive, so the
"env var name, else literal" resolvers turned a literal /login key like
`public` (OpenCode Zen's free key) into the built-in PUBLIC=C:\Users\Public,
sending `Authorization: Bearer C:\Users\Public` and 401ing every request.
Added `$envExact` in pi-utils, which trusts an env lookup only when an
exact-case key is enumerated (the only case-preserving signal on Windows;
the getter and hasOwnProperty/getOwnPropertyDescriptor traps are all
case-insensitive there). Wired it into all three resolvers:
resolve-config-value.ts, model-registry.ts, and auth-storage.ts.
Fixes#7361
- Raised probe-spawning logger tests to 30s timeouts; bun's 5s default
SIGTERMed probe children (exit 143) on shared-core CI runners, matching
the precedent documented in logger-contract.test.ts.
- Allowed clippy::unused_async_trait_impl on KillCommand::execute with an
unknown_lints guard for the pinned CI nightly that predates the lint.
Scoped test-runtime detection to explicit runner markers and Bun test entrypoints, so application NODE_ENV/BUN_ENV values no longer make ProcessTerminal headless.
Added subprocess regression coverage and propagated the private marker to test children.
Fixes#7261
- Reformatted the logger burst test per biome (the type-check job gates on
check:tools, which failed on the previous hotfix's formatting).
- Raised the native/unit bucket's chunk watchdog to 1200 s: the mupdf PDF
extraction chunk runs ~7 min per attempt on burstable runners under a
full fan-out and the 600 s default SIGKILLed both tries in release run
30519992654; the watchdog targets wedged children, not slow chunks.
- The hosted disk-cache prune swept ~/.cache/omp-bazel-repo file-by-file;
extracted repository contents keep upstream-archive mtimes (months old),
so a restored archive lost most of rules_rust while bazel still trusted
the entry's recorded_inputs — both darwin release legs failed with
'BUILD file not found' in release run 30519253683. Prune only the
action disk cache, whose files carry bazel-written mtimes.
- Gave the logger burst-order contract an explicit 30 s budget: two probe
children measure ~4.4 s unloaded and bun's 5 s default test timeout
SIGTERMed them (exit 143) on shared-core runners.
- filterChildShellEnv now also filters Bun-autoloaded .env.{NODE_ENV||development}
entries, closing the .env.production/.env.development leak into child shells.
- parseEnvLine skips backslash-escaped quotes when locating the closing
delimiter, restoring baseline/Bun-literal handling of values like JSON="{\"a\":1}"
that the new parser truncated.
- Adds a parseEnvFile regression test for escaped quotes.