11#!/usr/bin/env node
22
3- import { spawn } from "node:child_process ";
3+ import "dotenv/config ";
44import { existsSync , readFileSync , writeFileSync } from "node:fs" ;
55import { dirname , join } from "node:path" ;
66import * as readline from "node:readline/promises" ;
7- import { Readable , Writable } from "node:stream" ;
87import { fileURLToPath } from "node:url" ;
98
109import {
@@ -20,8 +19,10 @@ import {
2019 type WriteTextFileRequest ,
2120 type WriteTextFileResponse ,
2221} from "@agentclientprotocol/sdk" ;
22+ import { Agent } from "./src/agent.js" ;
2323import { PostHogAPIClient } from "./src/posthog-api.js" ;
2424import type { SessionPersistenceConfig } from "./src/session-store.js" ;
25+ import { Logger } from "./src/utils/logger.js" ;
2526
2627// PostHog configuration - set via env vars
2728const POSTHOG_CONFIG = {
@@ -30,6 +31,8 @@ const POSTHOG_CONFIG = {
3031 projectId : parseInt ( process . env . POSTHOG_PROJECT_ID || "0" , 10 ) ,
3132} ;
3233
34+ const logger = new Logger ( { debug : true , prefix : "[example-client]" } ) ;
35+
3336// Simple file-based storage for session -> persistence mapping
3437const SESSION_STORE_PATH = join (
3538 dirname ( fileURLToPath ( import . meta. url ) ) ,
@@ -111,6 +114,9 @@ class ExampleClient implements Client {
111114 console . log ( `[${ update . content . type } ]` ) ;
112115 }
113116 break ;
117+ case "user_message_chunk" :
118+ // Skip rendering user messages live - the user already sees what they typed
119+ break ;
114120 case "tool_call" :
115121 console . log ( `\n🔧 ${ update . title } (${ update . status } )` ) ;
116122 break ;
@@ -120,10 +126,13 @@ class ExampleClient implements Client {
120126 ) ;
121127 break ;
122128 case "plan" :
123- case "agent_thought_chunk" :
124- case "user_message_chunk" :
125129 console . log ( `[${ update . sessionUpdate } ]` ) ;
126130 break ;
131+ case "agent_thought_chunk" :
132+ if ( update . content . type === "text" ) {
133+ process . stdout . write ( `💭 ${ update . content . text } ` ) ;
134+ }
135+ break ;
127136 default :
128137 break ;
129138 }
@@ -142,7 +151,7 @@ class ExampleClient implements Client {
142151 case "user_message_chunk" :
143152 if ( update . content . type === "text" ) {
144153 process . stdout . write (
145- `\n${ dim } 💬 You: ${ update . content . text } ${ reset } ` ,
154+ `\n${ dim } 💬 You: ${ update . content . text } ${ reset } \n ` ,
146155 ) ;
147156 }
148157 break ;
@@ -219,10 +228,6 @@ async function prompt(message: string): Promise<string> {
219228}
220229
221230async function main ( ) {
222- const __filename = fileURLToPath ( import . meta. url ) ;
223- const __dirname = dirname ( __filename ) ;
224- const agentPath = join ( __dirname , "agent.ts" ) ;
225-
226231 // Check for session ID argument: npx tsx example-client.ts [sessionId]
227232 const existingSessionId = process . argv [ 2 ] ;
228233
@@ -286,28 +291,33 @@ async function main() {
286291 console . log ( " Starting fresh without persistence...\n" ) ;
287292 }
288293
289- // Spawn the agent as a subprocess using tsx
290- // Pass PostHog config as env vars so agent can create its own SessionStore
291- const agentProcess = spawn ( "npx" , [ "tsx" , agentPath ] , {
292- stdio : [ "pipe" , "pipe" , "inherit" ] ,
293- env : {
294- ...process . env ,
295- POSTHOG_API_URL : POSTHOG_CONFIG . apiUrl ,
296- POSTHOG_API_KEY : POSTHOG_CONFIG . apiKey ,
297- POSTHOG_PROJECT_ID : String ( POSTHOG_CONFIG . projectId ) ,
294+ // Create Agent and get in-process ACP connection
295+ const agent = new Agent ( {
296+ workingDirectory : process . cwd ( ) ,
297+ debug : true ,
298+ onLog : ( level , scope , message , data ) => {
299+ logger . log ( level , message , data , scope ) ;
298300 } ,
301+ ...( POSTHOG_CONFIG . apiUrl && { posthogApiUrl : POSTHOG_CONFIG . apiUrl } ) ,
302+ ...( POSTHOG_CONFIG . apiKey && { posthogApiKey : POSTHOG_CONFIG . apiKey } ) ,
303+ ...( POSTHOG_CONFIG . projectId && { posthogProjectId : POSTHOG_CONFIG . projectId } ) ,
299304 } ) ;
300305
301- // Create streams to communicate with the agent
302- const input = Writable . toWeb ( agentProcess . stdin ! ) ;
303- const output = Readable . toWeb (
304- agentProcess . stdout ! ,
305- ) as unknown as ReadableStream < Uint8Array > ;
306+ if ( ! persistence ) {
307+ logger . error ( "PostHog configuration required for runTaskV2" ) ;
308+ process . exit ( 1 ) ;
309+ }
310+
311+ const { clientStreams } = await agent . runTaskV2 (
312+ persistence . taskId ,
313+ persistence . runId ,
314+ { skipGitBranch : true } ,
315+ ) ;
306316
307- // Create the client connection
317+ // Create the client connection using the in-memory streams
308318 const client = new ExampleClient ( ) ;
309- const stream = ndJsonStream ( input , output ) ;
310- const connection = new ClientSideConnection ( ( _agent ) => client , stream ) ;
319+ const clientStream = ndJsonStream ( clientStreams . writable , clientStreams . readable ) ;
320+ const connection = new ClientSideConnection ( ( _agent ) => client , clientStream ) ;
311321
312322 try {
313323 // Initialize the connection
@@ -414,7 +424,6 @@ async function main() {
414424 } catch ( error ) {
415425 console . error ( "[Client] Error:" , error ) ;
416426 } finally {
417- agentProcess . kill ( ) ;
418427 process . exit ( 0 ) ;
419428 }
420429}
0 commit comments