Skip to content

perf(core): bound EventBroadcaster and stop accumulating tool yields - #69

Open
LukasParke wants to merge 3 commits into
mattapperson:mainfrom
LukasParke:lukeparke/dev-819-bound-eventbroadcaster
Open

perf(core): bound EventBroadcaster and stop accumulating tool yields#69
LukasParke wants to merge 3 commits into
mattapperson:mainfrom
LukasParke:lukeparke/dev-819-bound-eventbroadcaster

Conversation

@LukasParke

Copy link
Copy Markdown
Collaborator

Fixes DEV-819.

Port the isolate-memory behaviors from openrouter-web#33659 into Noetic so DEV-798 does not reintroduce unbounded stream retention.

What

  • EventBroadcaster now trims behind the slowest live consumer. The 10k cap stays as a pre-consumer / stuck-consumer backstop.
  • Late joiners after the first consumer attach at the watermark, not event zero.
  • consumeToolGenerator and the OpenRouter adapter no longer accumulate every generator yield. ui.progress is invoked with the latest event only.

Why

@openrouter/agent 0.9.0 still needs a bun patch because replay buffers and generator-tool yield arrays retain O(total streamed bytes) for the life of a run. That is the cfw-api/fusion Worker exceededMemory failure mode. Long-term home is Noetic, not the bun patch.

Breaking

ui.progress now receives only the latest yield (single-element array). Tools that need history must accumulate it themselves. The array signature is unchanged.

Test plan

  • packages/core typecheck
  • event-broadcaster.test.ts including watermark + late-joiner cases
  • tool-ui.test.ts generator progress receives [latest] only

Trim replay behind the slowest live consumer so retained events stay
proportional to unread backlog. Generator-tool UI progress now receives
only the latest yield.

Fixes DEV-819.

BREAKING CHANGE: `ui.progress` is invoked with the latest yield only, not
the full history. Tools that need history must accumulate it themselves.

Signed-off-by: Luke Parke <5702154+LukasParke@users.noreply.github.com>
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

@LukasParke is attempting to deploy a commit to the Matt Apperson's projects Team on Vercel.

A member of the Team first needs to authorize it.

…nerator yields (DEV-819)

Broadcaster: buffer never exceeds maxBufferSize under 50x publish volume
(structural invariant per emit), drop-oldest retains newest window, buffer
stays at zero behind a caught-up consumer, unsubscribe frees per-subscriber
state (post-depart emits discarded, no stale replay), lagging consumer is
not stranded by eviction.

Generator tools: first progress event is observable while the generator is
still suspended (gate-based, no timers; deadlocks against accumulation),
ui.progress retains only the latest yield across 100k yields, one
tool_progress event per yield in order, and a mid-stream throw delivers
partial yields in order then surfaces as NoeticError step_failed with the
original cause.

Verified red against the pre-fix sources (8890d5f~1): 2 generator tests
and 2 broadcaster tests fail; green against 8890d5f.
…oin replay (DEV-819)

The bounded EventBroadcaster trims its buffer to the consumed watermark,
so re-attaching after collect() replays nothing. Update the two late-join
assertions to derive text/items from the already-collected events instead:
- textFromEvents() mirrors filterTextStream over the event list
- eventsToBroadcaster() re-wraps events for buildItemStream

Full core suite: 1453 pass / 0 fail; tsc --noEmit and biome clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant