You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement the parser that converts Claude Code CLI stream-json (NDJSON) into the neutral AgentStreamEvent type defined in D-7. This is the schema-translation layer — all Claude-specific knowledge lives here and nowhere else in the codebase.
ParserBuffer.swift — line buffering until \n, 10 MiB safeguard
ToolKindInference.swift — map Claude tool names (Read/Edit/Bash/mcp__*/...) to ToolKind
Parser contract
```swift
public actor ClaudeStreamJSONParser {
public init(sessionID: SessionID)
/// Feed bytes from TerminalSession.rawStdout. Emits events on the returned stream.
public func events() -> AsyncStream<AgentStreamEvent>
/// Feed raw bytes (may be partial; parser buffers until \\n).
public func ingest(_ data: Data)
/// Signal source closed (process exit). Flushes + emits terminated event if no result seen.
public func close()
}
```
Parsing rules
NDJSON: buffer until \n, parse each line with JSONDecoder.
Line limit: 10 MiB — drop with warning, continue from next \n.
Description
Implement the parser that converts Claude Code CLI stream-json (NDJSON) into the neutral
AgentStreamEventtype defined in D-7. This is the schema-translation layer — all Claude-specific knowledge lives here and nowhere else in the codebase.Spec:
docs/architecture/dialogue-events.md(full event catalog), Epic #250 §7.Scope
File layout
MacApp/Packages/AgentChat/Sources/AgentChat/Parser/ClaudeStreamJSONParser.swift— main actorClaudeStreamJSONDTO.swift— wire-level Codable DTOs (one file per group: SystemMessage, AssistantMessage, UserMessage, ResultMessage, StreamEvent)ParserBuffer.swift— line buffering until\n, 10 MiB safeguardToolKindInference.swift— map Claude tool names (Read/Edit/Bash/mcp__*/...) toToolKindParser contract
```swift
public actor ClaudeStreamJSONParser {
public init(sessionID: SessionID)
}
```
Parsing rules
\n, parse each line withJSONDecoder.\n.Codable; unknown event types → emitAgentStreamEvent.unknownEvent(raw: String).type:system / assistant / user / result / stream_event.system→ switchsubtype→ emit matching event.assistant→ parsemessage.content[], emitmessageCompletedwith usage/stopReason.user→ detecttool_resultvs echo, emittoolCallCompleted/userMessage.result→ emitsessionEnd(SessionResult), mark parser state as terminal.stream_event→ switch innerevent.type→ emit textDelta / thinkingStarted / toolCallStarted / etc.content_block_*interleaving (rare) — defensive support: track buffers perindex.Dispatch special cases
tool_use.name→ToolKindmapping table (source: events spec §4).mcp__<server>__<tool>names →ToolKind.otherwith metadata{server, tool}.EnterPlanMode/ExitPlanModeas system events, not tool cards.Acceptance Criteria
Tests/AgentChatTests/Parser/Fixtures/) — minimum 20 real JSONL samples covering: basic text response, interleaved tool_use + tool_result, thinking, api_retry, compact_boundary, rate_limit, subagent (parent_tool_use_id), MCP tool, failed tool, unknown event type.assistantevent; parser decodes known fields, ignores unknown, no exception.Relationships