@@ -75,6 +75,7 @@ import {
7575} from "./cloud-prompt" ;
7676import { TaskRunEventStreamSender } from "./event-stream-sender" ;
7777import { type JwtPayload , JwtValidationError , validateJwt } from "./jwt" ;
78+ import { resolveRtkSavings } from "./rtk-savings" ;
7879import {
7980 handoffLocalGitStateSchema ,
8081 jsonRpcRequestSchema ,
@@ -304,6 +305,7 @@ export class AgentServer {
304305 private app : Hono ;
305306 private posthogAPI : PostHogAPIClient ;
306307 private eventStreamSender : TaskRunEventStreamSender | null = null ;
308+ private rtkSavingsEmitted = false ;
307309 private questionRelayedToSlack = false ;
308310 private adapterEmittedTurnComplete = false ;
309311 private detectedPrUrl : string | null = null ;
@@ -2838,6 +2840,11 @@ ${signedCommitInstructions}
28382840 error : errorMessage ?? "Agent error" ,
28392841 } ) ;
28402842
2843+ // Emit before the finally below stops the stream — failed runs still used
2844+ // RTK-compressed commands and must be counted. cleanupSession's emit runs
2845+ // after this stop on the error path, so it can't rely on that one.
2846+ await this . emitRtkSavings ( ) ;
2847+
28412848 try {
28422849 await this . posthogAPI . updateTaskRun ( payload . task_id , payload . run_id , {
28432850 status,
@@ -3365,6 +3372,7 @@ ${signedCommitInstructions}
33653372 }
33663373
33673374 if ( completeEventStream ) {
3375+ await this . emitRtkSavings ( ) ;
33683376 await this . eventStreamSender ?. stop ( ) ;
33693377 }
33703378
@@ -3373,6 +3381,47 @@ ${signedCommitInstructions}
33733381 this . session = null ;
33743382 }
33753383
3384+ /**
3385+ * Emit RTK's output-compression token savings for this run as a telemetry
3386+ * event, so PostHog can report how much context RTK saved. Best-effort and
3387+ * cloud-only (no-op when the event stream isn't configured). Fires at most
3388+ * once per run: it's called from both terminal seams (the error path stops
3389+ * the stream in signalTaskComplete, before cleanupSession runs) and must land
3390+ * before the stream is stopped, since enqueue is a no-op once stopped.
3391+ */
3392+ private async emitRtkSavings ( ) : Promise < void > {
3393+ if ( ! this . eventStreamSender || this . rtkSavingsEmitted ) return ;
3394+ this . rtkSavingsEmitted = true ;
3395+
3396+ try {
3397+ const savings = await resolveRtkSavings ( ) ;
3398+ if ( ! savings ) return ;
3399+
3400+ this . eventStreamSender . enqueue ( {
3401+ type : "notification" ,
3402+ timestamp : new Date ( ) . toISOString ( ) ,
3403+ notification : {
3404+ jsonrpc : "2.0" ,
3405+ method : POSTHOG_NOTIFICATIONS . RTK_SAVINGS ,
3406+ // snake_case: these land as PostHog event properties.
3407+ params : {
3408+ task_id : this . config . taskId ,
3409+ run_id : this . config . runId ,
3410+ team_id : this . config . projectId ,
3411+ total_commands : savings . totalCommands ,
3412+ input_tokens : savings . inputTokens ,
3413+ output_tokens : savings . outputTokens ,
3414+ tokens_saved : savings . tokensSaved ,
3415+ avg_savings_pct : savings . avgSavingsPct ,
3416+ } ,
3417+ } ,
3418+ } ) ;
3419+ this . logger . debug ( "Emitted rtk savings" , { ...savings } ) ;
3420+ } catch ( error ) {
3421+ this . logger . debug ( "Failed to emit rtk savings" , { error } ) ;
3422+ }
3423+ }
3424+
33763425 private async captureCheckpointState (
33773426 localGitState ?: HandoffLocalGitState ,
33783427 ) : Promise < void > {
0 commit comments