@@ -19,11 +19,13 @@ import {
1919import { getLlmGatewayUrl } from "@posthog/agent/posthog-api" ;
2020import type { OnLogCallback } from "@posthog/agent/types" ;
2121import { app } from "electron" ;
22- import { injectable , preDestroy } from "inversify" ;
22+ import { inject , injectable , preDestroy } from "inversify" ;
2323import type { ExecutionMode } from "@/shared/types.js" ;
2424import type { AcpMessage } from "../../../shared/types/session-events.js" ;
25+ import { MAIN_TOKENS } from "../../di/tokens.js" ;
2526import { logger } from "../../lib/logger.js" ;
2627import { TypedEventEmitter } from "../../lib/typed-event-emitter.js" ;
28+ import type { ProcessTrackingService } from "../process-tracking/service.js" ;
2729import {
2830 AgentServiceEvent ,
2931 type AgentServiceEvents ,
@@ -185,6 +187,7 @@ interface ManagedSession {
185187 config : SessionConfig ;
186188 interruptReason ?: InterruptReason ;
187189 needsRecreation : boolean ;
190+ recreationPromise ?: Promise < ManagedSession > ;
188191 promptPending : boolean ;
189192 pendingContext ?: string ;
190193 availableModels ?: Array < {
@@ -214,6 +217,15 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
214217 private sessions = new Map < string , ManagedSession > ( ) ;
215218 private currentToken : string | null = null ;
216219 private pendingPermissions = new Map < string , PendingPermission > ( ) ;
220+ private processTracking : ProcessTrackingService ;
221+
222+ constructor (
223+ @inject ( MAIN_TOKENS . ProcessTrackingService )
224+ processTracking : ProcessTrackingService ,
225+ ) {
226+ super ( ) ;
227+ this . processTracking = processTracking ;
228+ }
217229
218230 public updateToken ( newToken : string ) : void {
219231 this . currentToken = newToken ;
@@ -394,6 +406,9 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
394406 return existing ;
395407 }
396408
409+ // Kill any lingering processes from previous runs of this task
410+ this . processTracking . killByTaskId ( taskId ) ;
411+
397412 // Clean up any prior session for this taskRunId before creating a new one
398413 await this . cleanupSession ( taskRunId ) ;
399414 }
@@ -413,7 +428,26 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
413428 } ) ;
414429
415430 try {
416- const acpConnection = await agent . run ( taskId , taskRunId ) ;
431+ const acpConnection = await agent . run ( taskId , taskRunId , {
432+ processCallbacks : {
433+ onProcessSpawned : ( info ) => {
434+ this . processTracking . register (
435+ info . pid ,
436+ "agent" ,
437+ `agent:${ taskRunId } ` ,
438+ {
439+ taskRunId,
440+ taskId,
441+ command : info . command ,
442+ } ,
443+ taskId ,
444+ ) ;
445+ } ,
446+ onProcessExited : ( pid ) => {
447+ this . processTracking . unregister ( pid , "agent-exited" ) ;
448+ } ,
449+ } ,
450+ } ) ;
417451 const { clientStreams } = acpConnection ;
418452
419453 const connection = this . createClientConnection (
@@ -572,10 +606,18 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
572606
573607 // Recreate session if marked (token was refreshed while session was active)
574608 if ( session . needsRecreation ) {
575- log . info ( "Recreating session before prompt (token refreshed)" , {
576- sessionId,
577- } ) ;
578- session = await this . recreateSession ( sessionId ) ;
609+ if ( ! session . recreationPromise ) {
610+ log . info ( "Recreating session before prompt (token refreshed)" , {
611+ sessionId,
612+ } ) ;
613+ session . recreationPromise = this . recreateSession ( sessionId ) . finally (
614+ ( ) => {
615+ const s = this . sessions . get ( sessionId ) ;
616+ if ( s ) s . recreationPromise = undefined ;
617+ } ,
618+ ) ;
619+ }
620+ session = await session . recreationPromise ;
579621 }
580622
581623 // Prepend pending context if present
@@ -636,6 +678,14 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
636678 }
637679 }
638680
681+ async cancelSessionsByTaskId ( taskId : string ) : Promise < void > {
682+ for ( const [ taskRunId , session ] of this . sessions ) {
683+ if ( session . taskId === taskId ) {
684+ await this . cleanupSession ( taskRunId ) ;
685+ }
686+ }
687+ }
688+
639689 async cancelPrompt (
640690 sessionId : string ,
641691 reason ?: InterruptReason ,
0 commit comments