Skip to content

fix(streams): release consumed completion payloads - #91

Merged
Kinfe123 merged 2 commits into
mainfrom
fix/managed-completion-retention
Oct 29, 2026
Merged

Kinfe123 merged 2 commits into
mainfrom
fix/managed-completion-retention

Conversation

@Kinfe123

Copy link
Copy Markdown
Collaborator

Problem

Streamfold 0.1.14 retains every completed SDK payload while a managed readStructured iterator stays alive, even when the consumer discards each result and maxActiveStreams is 1. Long agent sessions consequently retain the text, parsed value, and snapshot of all previous calls.

Reproduce with the added managed-memory.test.js: consume 200 Vercel start/delta/end calls through readStructured, keep only weak references to their final values, pause before EOF, and force GC in a separate Node process. All 200 values stay alive before this change, versus only the current value for a custom adapter.

Root cause

Built-in integrations keep a completion history for the manual finish() API. Managed readers consumed the lifecycle updates but kept that same history until the integration was released.

Change

Register each built-in integration's history in an internal weak map. Managed readers clear it after obtaining a batch, after EOF finalization, and during cleanup. Returned batches and consumer-owned results remain intact. Manual integrations keep their full history. Completed call IDs remain recorded for duplicate suppression; this does not claim constant memory for arbitrarily many IDs.

Custom integration objects are unchanged, and no public option or method is added.

Verification

  • The new subprocess regression failed before the fix with SDK reader retained 200 payloads and passes after it. It uses actual adapters, public values, weak references, and explicit GC, not internal history inspection. It also verifies all 200 manual completions remain available across repeated finish() calls.
  • The same 200-call reproduction on Apple M1 / Node 24.22.1 retained 200 SDK payloads before and 1 after; the custom-adapter control retained 1 in both runs. Each call contains a repeated text field, uses a single delta, and runs with maxActiveStreams: 1. These are retained-object counts, not heap-byte or latency claims.
  • Focused memory, managed-reader, cancellation, and integration suites passed. Existing ID-reuse coverage now also replays a completion before reusing the ID.
  • pnpm release:check: 179 JavaScript tests and 11 Rust tests passed, plus types, syntax, Rust formatting, clippy, and package dry-run.
  • Chromium smoke test, pnpm bench, and pnpm bench:sdk passed. No CPU speedup is claimed.

Public surface

No public API, exports, dependencies, or types change. API guidance and changelog describe managed retention and the unchanged manual history contract.

@Kinfe123

Copy link
Copy Markdown
Collaborator Author

lgtm, releases consumed payloads while preserving manual history.

@Kinfe123
Kinfe123 merged commit 11b1294 into main Oct 29, 2026
6 checks passed
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