Skip to content

streamWithNativeTools: same hardcoded meta.type = 'text' bug as runNativeToolsYielding #20

Description

@Anarchid

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions