@@ -25,6 +25,7 @@ import { app } from "electron";
2525import { inject , injectable , preDestroy } from "inversify" ;
2626import { MAIN_TOKENS } from "../../di/tokens.js" ;
2727import { logger } from "../../lib/logger.js" ;
28+ import { createMainTimingCollector } from "../../lib/timing.js" ;
2829import { TypedEventEmitter } from "../../lib/typed-event-emitter.js" ;
2930import type { FsService } from "../fs/service.js" ;
3031import { getCurrentUserId , getPostHogClient } from "../posthog-analytics.js" ;
@@ -178,6 +179,8 @@ interface SessionConfig {
178179 additionalDirectories ?: string [ ] ;
179180 /** Permission mode to use for the session */
180181 permissionMode ?: string ;
182+ /** Dev-only: timestamp from renderer when user submitted the task */
183+ submittedAt ?: number ;
181184}
182185
183186interface ManagedSession {
@@ -418,6 +421,8 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
418421 isReconnect : boolean ,
419422 isRetry = false ,
420423 ) : Promise < ManagedSession | null > {
424+ const tc = createMainTimingCollector ( log ) ;
425+
421426 const {
422427 taskId,
423428 taskRunId,
@@ -427,8 +432,13 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
427432 adapter,
428433 additionalDirectories,
429434 permissionMode,
435+ submittedAt,
430436 } = config ;
431437
438+ if ( submittedAt ) {
439+ tc . record ( "ipcTransit" , Date . now ( ) - submittedAt ) ;
440+ }
441+
432442 // Preview sessions don't need a real repo — use a temp directory
433443 const repoPath = taskId === "__preview__" ? tmpdir ( ) : rawRepoPath ;
434444
@@ -439,23 +449,31 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
439449 }
440450
441451 // Kill any lingering processes from previous runs of this task
442- this . processTracking . killByTaskId ( taskId ) ;
452+ tc . timeSync ( "killProcesses" , ( ) =>
453+ this . processTracking . killByTaskId ( taskId ) ,
454+ ) ;
443455
444456 // Clean up any prior session for this taskRunId before creating a new one
445- await this . cleanupSession ( taskRunId ) ;
457+ await tc . time ( "cleanup" , ( ) => this . cleanupSession ( taskRunId ) ) ;
446458 }
447459
448460 const channel = `agent-event:${ taskRunId } ` ;
449- const mockNodeDir = this . setupMockNodeEnvironment ( taskRunId ) ;
450- this . setupEnvironment ( credentials , mockNodeDir ) ;
461+ const mockNodeDir = tc . timeSync ( "mockNode" , ( ) =>
462+ this . setupMockNodeEnvironment ( taskRunId ) ,
463+ ) ;
464+ tc . timeSync ( "setupEnv" , ( ) =>
465+ this . setupEnvironment ( credentials , mockNodeDir ) ,
466+ ) ;
451467
452468 // Preview sessions don't persist logs — no real task exists
453469 const isPreview = taskId === "__preview__" ;
454470
455471 // OTEL log pipeline or legacy S3 writer if FF false
456472 const useOtelPipeline = isPreview
457473 ? false
458- : await this . isFeatureFlagEnabled ( "twig-agent-logs-pipeline" ) ;
474+ : await tc . time ( "featureFlag" , ( ) =>
475+ this . isFeatureFlagEnabled ( "twig-agent-logs-pipeline" ) ,
476+ ) ;
459477
460478 log . info ( "Agent log transport" , {
461479 transport : isPreview ? "none" : useOtelPipeline ? "otel" : "s3" ,
@@ -482,78 +500,83 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
482500 } ) ;
483501
484502 try {
485- const acpConnection = await agent . run ( taskId , taskRunId , {
486- adapter,
487- codexBinaryPath : adapter === "codex" ? getCodexBinaryPath ( ) : undefined ,
488- processCallbacks : {
489- onProcessSpawned : ( info ) => {
490- this . processTracking . register (
491- info . pid ,
492- "agent" ,
493- `agent:${ taskRunId } ` ,
494- {
495- taskRunId,
503+ const acpConnection = await tc . time ( "agentRun" , ( ) =>
504+ agent . run ( taskId , taskRunId , {
505+ adapter,
506+ codexBinaryPath :
507+ adapter === "codex" ? getCodexBinaryPath ( ) : undefined ,
508+ processCallbacks : {
509+ onProcessSpawned : ( info ) => {
510+ this . processTracking . register (
511+ info . pid ,
512+ "agent" ,
513+ `agent:${ taskRunId } ` ,
514+ {
515+ taskRunId,
516+ taskId,
517+ command : info . command ,
518+ } ,
496519 taskId ,
497- command : info . command ,
498- } ,
499- taskId ,
500- ) ;
501- } ,
502- onProcessExited : ( pid ) => {
503- this . processTracking . unregister ( pid , "agent-exited" ) ;
520+ ) ;
521+ } ,
522+ onProcessExited : ( pid ) => {
523+ this . processTracking . unregister ( pid , "agent-exited" ) ;
524+ } ,
504525 } ,
505- } ,
506- } ) ;
526+ } ) ,
527+ ) ;
507528 const { clientStreams } = acpConnection ;
508529
509- const connection = this . createClientConnection (
510- taskRunId ,
511- channel ,
512- clientStreams ,
530+ const connection = tc . timeSync ( "clientConnection" , ( ) =>
531+ this . createClientConnection ( taskRunId , channel , clientStreams ) ,
513532 ) ;
514533
515- await connection . initialize ( {
516- protocolVersion : PROTOCOL_VERSION ,
517- clientCapabilities : {
518- fs : {
519- readTextFile : true ,
520- writeTextFile : true ,
534+ await tc . time ( "initialize" , ( ) =>
535+ connection . initialize ( {
536+ protocolVersion : PROTOCOL_VERSION ,
537+ clientCapabilities : {
538+ fs : {
539+ readTextFile : true ,
540+ writeTextFile : true ,
541+ } ,
542+ terminal : true ,
521543 } ,
522- terminal : true ,
523- } ,
524- } ) ;
544+ } ) ,
545+ ) ;
525546
526- const mcpServers =
527- adapter === "codex" ? [ ] : this . buildMcpServers ( credentials ) ;
547+ const mcpServers = tc . timeSync ( "buildMcp" , ( ) =>
548+ adapter === "codex" ? [ ] : this . buildMcpServers ( credentials ) ,
549+ ) ;
528550
529551 let configOptions : SessionConfigOption [ ] | undefined ;
530552 let agentSessionId : string ;
531553
532554 if ( isReconnect && adapter === "codex" && config . sessionId ) {
533- const loadResponse = await connection . loadSession ( {
534- sessionId : config . sessionId ,
535- cwd : repoPath ,
536- mcpServers,
537- } ) ;
555+ const loadResponse = await tc . time ( "loadSession" , ( ) =>
556+ connection . loadSession ( {
557+ sessionId : config . sessionId ! ,
558+ cwd : repoPath ,
559+ mcpServers,
560+ } ) ,
561+ ) ;
538562 configOptions = loadResponse . configOptions ?? undefined ;
539563 agentSessionId = config . sessionId ;
540564 } else if ( isReconnect && adapter !== "codex" ) {
541565 if ( ! config . sessionId ) {
542566 throw new Error ( "Cannot resume session without sessionId" ) ;
543567 }
544568 const systemPrompt = this . buildPostHogSystemPrompt ( credentials ) ;
545- const resumeResponse = await connection . extMethod (
546- "_posthog/session/resume" ,
547- {
548- sessionId : config . sessionId ,
569+ const resumeResponse = await tc . time ( "resumeSession" , ( ) =>
570+ connection . extMethod ( "_posthog/session/resume" , {
571+ sessionId : config . sessionId ! ,
549572 cwd : repoPath ,
550573 mcpServers,
551574 _meta : {
552575 ...( logUrl && {
553576 persistence : { taskId, runId : taskRunId , logUrl } ,
554577 } ) ,
555578 taskRunId,
556- sessionId : config . sessionId ,
579+ sessionId : config . sessionId ! ,
557580 systemPrompt,
558581 ...( permissionMode && { permissionMode } ) ,
559582 ...( additionalDirectories ?. length && {
@@ -562,7 +585,7 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
562585 } ,
563586 } ) ,
564587 } ,
565- } ,
588+ } ) ,
566589 ) ;
567590 const resumeMeta = resumeResponse ?. _meta as
568591 | {
@@ -573,26 +596,31 @@ export class AgentService extends TypedEventEmitter<AgentServiceEvents> {
573596 agentSessionId = config . sessionId ;
574597 } else {
575598 const systemPrompt = this . buildPostHogSystemPrompt ( credentials ) ;
576- const newSessionResponse = await connection . newSession ( {
577- cwd : repoPath ,
578- mcpServers,
579- _meta : {
580- taskRunId,
581- systemPrompt,
582- ...( permissionMode && { permissionMode } ) ,
583- ...( additionalDirectories ?. length && {
584- claudeCode : {
585- options : { additionalDirectories } ,
586- } ,
587- } ) ,
588- } ,
589- } ) ;
599+ const newSessionResponse = await tc . time ( "newSession" , ( ) =>
600+ connection . newSession ( {
601+ cwd : repoPath ,
602+ mcpServers,
603+ _meta : {
604+ taskRunId,
605+ systemPrompt,
606+ ...( permissionMode && { permissionMode } ) ,
607+ ...( additionalDirectories ?. length && {
608+ claudeCode : {
609+ options : { additionalDirectories } ,
610+ } ,
611+ } ) ,
612+ } ,
613+ } ) ,
614+ ) ;
590615 configOptions = newSessionResponse . configOptions ?? undefined ;
591616 agentSessionId = newSessionResponse . sessionId ;
592617 }
593618
594619 config . sessionId = agentSessionId ;
595620
621+ const sessionType = isReconnect ? "reconnect" : "create" ;
622+ tc . summarize ( `getOrCreateSession(${ sessionType } )` ) ;
623+
596624 const session : ManagedSession = {
597625 taskRunId,
598626 taskId,
@@ -1300,6 +1328,7 @@ For git operations while detached:
13001328 : undefined ,
13011329 permissionMode :
13021330 "permissionMode" in params ? params . permissionMode : undefined ,
1331+ submittedAt : "submittedAt" in params ? params . submittedAt : undefined ,
13031332 } ;
13041333 }
13051334
0 commit comments