@@ -3,15 +3,42 @@ import {
33 isTerminalSessionStatus ,
44 type ProviderSessionEvent ,
55} from "@openagentpack/sdk" ;
6- import { sanitizeSessionEvent , sanitizeSessionEvents } from "@openagentpack/sdk/session-events" ;
6+ import { sanitizeSessionEvents } from "@openagentpack/sdk/session-events" ;
77
88/** Skip user echo + thinking noise in live rendering (mirrors OpenAgentPack CLI). */
99function shouldRenderLiveEvent ( event : ProviderSessionEvent ) : boolean {
1010 return event . type !== "thinking" && ! ( event . type === "message" && event . role === "user" ) ;
1111}
1212
13- function writeJsonLine ( value : unknown ) : void {
14- process . stdout . write ( `${ JSON . stringify ( value ) } \n` ) ;
13+ function renderTerminalStatus ( status : string , json : boolean ) : void {
14+ if ( json ) return ;
15+ process . stderr . write ( `\n[session ${ status } ]\n` ) ;
16+ }
17+
18+ /**
19+ * Consume an SSE stream. Text mode renders live (assistant text → stdout,
20+ * diagnostics → stderr). JSON mode collects every event and emits exactly one
21+ * JSON document at the end — `--output json` guarantees a single valid JSON
22+ * result on stdout (mirrors `text chat --stream --output json`).
23+ */
24+ export async function streamAndRenderEvents (
25+ events : AsyncIterable < ProviderSessionEvent > ,
26+ json : boolean ,
27+ ) : Promise < void > {
28+ const collected : ProviderSessionEvent [ ] = [ ] ;
29+ for await ( const event of events ) {
30+ if ( json ) collected . push ( event ) ;
31+ else renderEvent ( event ) ;
32+ if ( event . type === "status" && isTerminalSessionStatus ( event . status ) ) {
33+ renderTerminalStatus ( event . status ?? "" , json ) ;
34+ break ;
35+ }
36+ }
37+ if ( json ) {
38+ process . stdout . write (
39+ `${ JSON . stringify ( { events : sanitizeSessionEvents ( collected ) } , null , 2 ) } \n` ,
40+ ) ;
41+ }
1542}
1643
1744/** Assistant text → stdout (data channel); everything else → stderr (diagnostics). */
@@ -32,26 +59,6 @@ function renderEvent(event: ProviderSessionEvent): void {
3259 }
3360}
3461
35- function renderTerminalStatus ( status : string , json : boolean ) : void {
36- if ( json ) return ;
37- process . stderr . write ( `\n[session ${ status } ]\n` ) ;
38- }
39-
40- /** Consume an SSE stream, rendering live (text) or as JSONL (json). */
41- export async function streamAndRenderEvents (
42- events : AsyncIterable < ProviderSessionEvent > ,
43- json : boolean ,
44- ) : Promise < void > {
45- for await ( const event of events ) {
46- if ( json ) writeJsonLine ( sanitizeSessionEvent ( event ) ) ;
47- else renderEvent ( event ) ;
48- if ( event . type === "status" && isTerminalSessionStatus ( event . status ) ) {
49- renderTerminalStatus ( event . status ?? "" , json ) ;
50- break ;
51- }
52- }
53- }
54-
5562/** Render a polled (non-streaming) collected result. */
5663export function renderCollectedEvents ( result : CollectedSessionEvents , json : boolean ) : void {
5764 if ( json ) {
0 commit comments