Context
antra-tess/membrane#19 fixed the native-tools yielding stream path (`runNativeToolsYielding`) which was hardcoding `meta.type = 'text'` on every chunk and never emitting `'block'` lifecycle events. That fix unblocked extended-thinking rendering for streaming TUIs.
The same TODO and the same bug pattern still exists in `streamWithNativeTools` (`src/membrane.ts` around the old line 778), which is the non-yielding path used by `Membrane.complete()`:
```ts
// For native mode, emit text chunks with basic metadata
// TODO: Use native API content_block events for richer metadata
const meta: ChunkMeta = {
type: 'text', // ← always 'text', even for thinking_delta chunks
visible: true,
blockIndex,
};
onChunk?.(chunk, meta);
```
Impact
Callers of `Membrane.complete()` that pass an `onChunk` callback with the intention of distinguishing thinking from text receive incorrectly tagged metadata. In practice this affects:
- The autobiographical context-manager strategy's compression model, which uses `complete()`. Its output is stored as a summary blob and doesn't render through any TUI, so the immediate user impact is nil — but it means any future caller that wires `complete()` to a UI will hit the same bug.
- `onContentBlock` is passed through as `onContentBlockUpdate`, but it's a `@deprecated` callback that the agent-framework doesn't use. The newer `onBlock` callback (added for streaming) is not invoked from this path.
Suggested fix
Mirror the shape applied in `runNativeToolsYielding`:
- Track `currentBlockType: MembraneBlockType` and `seenBlockIndices: Set` across the `streamOnce` call.
- Wire `onContentBlock` (internal, to the provider's callback) to update `currentBlockType` on first sighting and call `options.onBlock` with a `block_start` event; emit `block_complete` on the second sighting with the finalised block payload.
- Set `meta.type = currentBlockType` and `meta.visible = currentBlockType === 'text'` on each `onChunk` invocation.
The patch is mechanical and the test surface is small — no existing test exercises the metadata field on `complete()`, so a new unit test would be welcome alongside.
🤖 Filed via Claude Code
Context
antra-tess/membrane#19 fixed the native-tools yielding stream path (`runNativeToolsYielding`) which was hardcoding `meta.type = 'text'` on every chunk and never emitting `'block'` lifecycle events. That fix unblocked extended-thinking rendering for streaming TUIs.
The same TODO and the same bug pattern still exists in `streamWithNativeTools` (`src/membrane.ts` around the old line 778), which is the non-yielding path used by `Membrane.complete()`:
```ts
// For native mode, emit text chunks with basic metadata
// TODO: Use native API content_block events for richer metadata
const meta: ChunkMeta = {
type: 'text', // ← always 'text', even for thinking_delta chunks
visible: true,
blockIndex,
};
onChunk?.(chunk, meta);
```
Impact
Callers of `Membrane.complete()` that pass an `onChunk` callback with the intention of distinguishing thinking from text receive incorrectly tagged metadata. In practice this affects:
Suggested fix
Mirror the shape applied in `runNativeToolsYielding`:
The patch is mechanical and the test surface is small — no existing test exercises the metadata field on `complete()`, so a new unit test would be welcome alongside.
🤖 Filed via Claude Code