@@ -13,7 +13,7 @@ import {
1313import { PostHogAPIClient } from "../posthog-api.js" ;
1414import { SessionLogWriter } from "../session-log-writer.js" ;
1515import { TreeTracker } from "../tree-tracker.js" ;
16- import type { DeviceInfo , TreeSnapshotEvent } from "../types.js" ;
16+ import type { AgentMode , DeviceInfo , TreeSnapshotEvent } from "../types.js" ;
1717import { AsyncMutex } from "../utils/async-mutex.js" ;
1818import { getLlmGatewayUrl } from "../utils/gateway.js" ;
1919import { Logger } from "../utils/logger.js" ;
@@ -144,13 +144,23 @@ export class AgentServer {
144144 private server : ServerType | null = null ;
145145 private session : ActiveSession | null = null ;
146146 private app : Hono ;
147+ private posthogAPI : PostHogAPIClient ;
147148
148149 constructor ( config : AgentServerConfig ) {
149150 this . config = config ;
150151 this . logger = new Logger ( { debug : true , prefix : "[AgentServer]" } ) ;
152+ this . posthogAPI = new PostHogAPIClient ( {
153+ apiUrl : config . apiUrl ,
154+ projectId : config . projectId ,
155+ getApiKey : ( ) => config . apiKey ,
156+ } ) ;
151157 this . app = this . createApp ( ) ;
152158 }
153159
160+ private getEffectiveMode ( payload : JwtPayload ) : AgentMode {
161+ return payload . mode ?? this . config . mode ;
162+ }
163+
154164 private createApp ( ) : Hono {
155165 const app = new Hono ( ) ;
156166
@@ -309,7 +319,7 @@ export class AgentServer {
309319 }
310320
311321 async start ( ) : Promise < void > {
312- return new Promise ( ( resolve ) => {
322+ await new Promise < void > ( ( resolve ) => {
313323 this . server = serve (
314324 {
315325 fetch : this . app . fetch ,
@@ -321,6 +331,26 @@ export class AgentServer {
321331 } ,
322332 ) ;
323333 } ) ;
334+
335+ await this . autoInitializeSession ( ) ;
336+ }
337+
338+ private async autoInitializeSession ( ) : Promise < void > {
339+ const { taskId, runId, mode, projectId } = this . config ;
340+
341+ this . logger . info ( "Auto-initializing session" , { taskId, runId, mode } ) ;
342+
343+ // Create a synthetic payload from config (no JWT needed for auto-init)
344+ const payload : JwtPayload = {
345+ task_id : taskId ,
346+ run_id : runId ,
347+ team_id : projectId ,
348+ user_id : 0 , // System-initiated
349+ distinct_id : "agent-server" ,
350+ mode,
351+ } ;
352+
353+ await this . initializeSession ( payload , null ) ;
324354 }
325355
326356 async stop ( ) : Promise < void > {
@@ -409,7 +439,7 @@ export class AgentServer {
409439
410440 private async initializeSession (
411441 payload : JwtPayload ,
412- sseController : SseController ,
442+ sseController : SseController | null ,
413443 ) : Promise < void > {
414444 if ( this . session ) {
415445 await this . cleanupSession ( ) ;
@@ -506,6 +536,73 @@ export class AgentServer {
506536 } ;
507537
508538 this . logger . info ( "Session initialized successfully" ) ;
539+
540+ await this . sendInitialTaskMessage ( payload ) ;
541+ }
542+
543+ private async sendInitialTaskMessage ( payload : JwtPayload ) : Promise < void > {
544+ if ( ! this . session ) return ;
545+
546+ try {
547+ this . logger . info ( "Fetching task details" , { taskId : payload . task_id } ) ;
548+ const task = await this . posthogAPI . getTask ( payload . task_id ) ;
549+
550+ if ( ! task . description ) {
551+ this . logger . warn ( "Task has no description, skipping initial message" ) ;
552+ return ;
553+ }
554+
555+ this . logger . info ( "Sending initial task message" , {
556+ taskId : payload . task_id ,
557+ descriptionLength : task . description . length ,
558+ } ) ;
559+
560+ const result = await this . session . clientConnection . prompt ( {
561+ sessionId : payload . run_id ,
562+ prompt : [ { type : "text" , text : task . description } ] ,
563+ } ) ;
564+
565+ this . logger . info ( "Initial task message completed" , {
566+ stopReason : result . stopReason ,
567+ } ) ;
568+
569+ // Only auto-complete for background mode
570+ const mode = this . getEffectiveMode ( payload ) ;
571+ if ( mode === "background" ) {
572+ await this . signalTaskComplete ( payload , result . stopReason ) ;
573+ } else {
574+ this . logger . info ( "Interactive mode - staying open for conversation" ) ;
575+ }
576+ } catch ( error ) {
577+ this . logger . error ( "Failed to send initial task message" , error ) ;
578+ // Signal failure for background mode
579+ const mode = this . getEffectiveMode ( payload ) ;
580+ if ( mode === "background" ) {
581+ await this . signalTaskComplete ( payload , "error" ) ;
582+ }
583+ }
584+ }
585+
586+ private async signalTaskComplete (
587+ payload : JwtPayload ,
588+ stopReason : string ,
589+ ) : Promise < void > {
590+ const status =
591+ stopReason === "cancelled"
592+ ? "cancelled"
593+ : stopReason === "error"
594+ ? "failed"
595+ : "completed" ;
596+
597+ try {
598+ await this . posthogAPI . updateTaskRun ( payload . task_id , payload . run_id , {
599+ status,
600+ error_message : stopReason === "error" ? "Agent error" : undefined ,
601+ } ) ;
602+ this . logger . info ( "Task completion signaled" , { status, stopReason } ) ;
603+ } catch ( error ) {
604+ this . logger . error ( "Failed to signal task completion" , error ) ;
605+ }
509606 }
510607
511608 private configureEnvironment ( ) : void {
@@ -534,11 +631,21 @@ export class AgentServer {
534631 } ) ;
535632 }
536633
537- private createCloudClient ( _payload : JwtPayload ) {
634+ private createCloudClient ( payload : JwtPayload ) {
635+ const mode = this . getEffectiveMode ( payload ) ;
636+
538637 return {
539638 requestPermission : async ( params : {
540639 options : Array < { kind : string ; optionId : string } > ;
541640 } ) => {
641+ // Background mode: always auto-approve permissions
642+ // Interactive mode: also auto-approve for now (user can monitor via SSE)
643+ // Future: interactive mode could pause and wait for user approval via SSE
644+ this . logger . debug ( "Permission request" , {
645+ mode,
646+ options : params . options ,
647+ } ) ;
648+
542649 const allowOption = params . options . find (
543650 ( o ) => o . kind === "allow_once" || o . kind === "allow_always" ,
544651 ) ;
0 commit comments