Files
dev/codex-rs/utils/stream-parser
zeekayandHanzo Dev 6cbbea2d03 merge: just-every/code v0.6.169 (e4fe007150) — keep Hanzo identity
Our history is just-every's with author emails rewritten, so the graphs are
disjoint and git finds no merge base. Supplied one by hand: d1f1f51a41, the
just-every commit matching our Oct-2025 fork point (22 files apart). 9618
upstream commits, 7004 files, +1.81M/-159k, 37 conflicts.

Resolution:
- Upstream code where our side was the Oct-2025 prototype of a feature they
  have since reimplemented 3-5x larger (agent_runs, browser_sessions,
  history_cell/{agent,browser}, the vt100 harness and its snapshots).
- Upstream's config.rs module split (builder/defaults/sources/validation)
  kept; zen5-coder and the provider-follows-model rule re-applied on top.
- Ours where the file IS the identity: README, package.json, postinstall,
  release notes pipeline, integration-zed.
- Hybrid where upstream improved a file we had branded: updates.rs (their
  release-repo migration, our hanzoai/dev), release.yml (their fail-fast npm
  auth check, our package name), chatwidget.rs (their turn handling, our
  HANZO_USER_KEY hint), exec human output (their 237 lines, our banner).

Superseded and dropped deliberately: our resume_command_name() coder-alias
hack — upstream's derive_resume_command_name() reads the invocation basename
generically, which yields "dev" for us.

LLM.md now records the real topology and the sync point; AGENTS.md and
CLAUDE.md are symlinks to it again. Single parent on purpose: adopting
jecode/main as a second parent would re-import 13,742 commits carrying the
upstream author emails a prior rewrite deliberately removed.

Version 0.2.188 -> 0.2.189.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 23:09:12 -07:00
..

codex-utils-stream-parser

Small, dependency-free utilities for parsing streamed text incrementally.

Disclaimer: This code is pretty complex and Codex did not manage to write it so before updating the code, make sure to deeply understand it and don't blindly trust Codex on it. Feel free to update the documentation as you modify the code

What it provides

  • StreamTextParser: trait for incremental parsers that consume string chunks
  • InlineHiddenTagParser<T>: generic parser that hides inline tags and extracts their contents
  • CitationStreamParser: convenience wrapper for <oai-mem-citation>...</oai-mem-citation>
  • strip_citations(...): one-shot helper for non-streamed strings
  • Utf8StreamParser<P>: adapter for raw &[u8] streams that may split UTF-8 code points

Why this exists

Some model outputs arrive as a stream and may contain hidden markup (for example <oai-mem-citation>...</oai-mem-citation>) split across chunk boundaries. Parsing each chunk independently is incorrect because tags can be split (<oai-mem- + citation>).

This crate keeps parser state across chunks, returns visible text safe to render immediately, and extracts hidden payloads separately.

Example: citation streaming

use codex_utils_stream_parser::CitationStreamParser;
use codex_utils_stream_parser::StreamTextParser;

let mut parser = CitationStreamParser::new();

let first = parser.push_str("Hello <oai-mem-");
assert_eq!(first.visible_text, "Hello ");
assert!(first.extracted.is_empty());

let second = parser.push_str("citation>doc A</oai-mem-citation> world");
assert_eq!(second.visible_text, " world");
assert_eq!(second.extracted, vec!["doc A".to_string()]);

let tail = parser.finish();
assert!(tail.visible_text.is_empty());
assert!(tail.extracted.is_empty());

Example: raw byte streaming with split UTF-8 code points

use codex_utils_stream_parser::CitationStreamParser;
use codex_utils_stream_parser::Utf8StreamParser;

# fn demo() -> Result<(), codex_utils_stream_parser::Utf8StreamParserError> {
let mut parser = Utf8StreamParser::new(CitationStreamParser::new());

// "é" split across chunks: 0xC3 + 0xA9
let first = parser.push_bytes(&[b'H', 0xC3])?;
assert_eq!(first.visible_text, "H");

let second = parser.push_bytes(&[0xA9, b'!'])?;
assert_eq!(second.visible_text, "é!");

let tail = parser.finish()?;
assert!(tail.visible_text.is_empty());
# Ok(())
# }

Example: custom hidden tags

use codex_utils_stream_parser::InlineHiddenTagParser;
use codex_utils_stream_parser::InlineTagSpec;
use codex_utils_stream_parser::StreamTextParser;

#[derive(Clone, Debug, PartialEq, Eq)]
enum Tag {
    Secret,
}

let mut parser = InlineHiddenTagParser::new(vec![InlineTagSpec {
    tag: Tag::Secret,
    open: "<secret>",
    close: "</secret>",
}]);

let out = parser.push_str("a<secret>x</secret>b");
assert_eq!(out.visible_text, "ab");
assert_eq!(out.extracted.len(), 1);
assert_eq!(out.extracted[0].content, "x");

Known limitations

  • Tags are matched literally and case-sensitively
  • No nested tag support
  • A stream can return empty objects.