@@ -15,12 +15,16 @@ import {
1515 type RunResultPackageResponse ,
1616} from '@open-design/contracts' ;
1717import {
18+ buildRunCreatedV4Aliases ,
19+ buildRunFinishedV4Aliases ,
1820 deriveConfigureGlobals ,
1921 modelIdForTracking ,
2022 sessionModeToTracking ,
2123 type TrackingDesignSystemSource ,
2224 type TrackingDesignSystemKind ,
2325 type TrackingDesignSystemEditSurface ,
26+ type RunTaskLineageProps ,
27+ type TrackingRunRecoveryActionType ,
2428} from '@open-design/contracts/analytics' ;
2529import type { OdNativeEvent } from '@open-design/agui-adapter' ;
2630import { newInsertId , readAnalyticsContext } from '../analytics.js' ;
@@ -87,7 +91,10 @@ import {
8791} from '../run-analytics-observability.js' ;
8892import {
8993 diffRunArtifacts ,
94+ primaryArtifactChangeForRun ,
9095 snapshotProjectArtifacts ,
96+ supportingAssetFilesChangedForRun ,
97+ type RunArtifactDiff ,
9198 type RunArtifactBaseline ,
9299} from '../run-artifact-fs.js' ;
93100import {
@@ -342,6 +349,7 @@ interface ChatRun {
342349 artifactsModified ?: number ;
343350 designSystemCreated : boolean ;
344351 previewModuleCount : number ;
352+ diff ?: RunArtifactDiff ;
345353 } ;
346354 designSystemId ?: string | null ;
347355 designSystemRequestedId ?: string | null ;
@@ -1875,6 +1883,46 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
18751883 const hintProjectTurnIndex = typeof analyticsHints . projectTurnIndex === 'number'
18761884 ? analyticsHints . projectTurnIndex
18771885 : undefined ;
1886+ const taskExecutionId = typeof analyticsHints . taskExecutionId === 'string'
1887+ && analyticsHints . taskExecutionId . length > 0
1888+ ? analyticsHints . taskExecutionId
1889+ : run . clientRequestId ?? run . id ;
1890+ const initialRunId = typeof analyticsHints . initialRunId === 'string'
1891+ && analyticsHints . initialRunId . length > 0
1892+ ? analyticsHints . initialRunId
1893+ : run . id ;
1894+ const taskRunIndex = typeof analyticsHints . taskRunIndex === 'number'
1895+ && Number . isInteger ( analyticsHints . taskRunIndex )
1896+ && analyticsHints . taskRunIndex >= 0
1897+ ? analyticsHints . taskRunIndex
1898+ : 0 ;
1899+ const recoveryActionTypes : ReadonlySet < TrackingRunRecoveryActionType > = new Set ( [
1900+ 'manual_retry' ,
1901+ 'resume_run' ,
1902+ 'authorize_and_retry' ,
1903+ 'switch_model_retry' ,
1904+ 'switch_runtime_retry' ,
1905+ 'question_answer' ,
1906+ ] ) ;
1907+ const recoveryActionType = typeof analyticsHints . recoveryActionType === 'string'
1908+ && recoveryActionTypes . has (
1909+ analyticsHints . recoveryActionType as TrackingRunRecoveryActionType ,
1910+ )
1911+ ? analyticsHints . recoveryActionType as TrackingRunRecoveryActionType
1912+ : undefined ;
1913+ const taskLineage : RunTaskLineageProps = {
1914+ task_execution_id : taskExecutionId ,
1915+ initial_run_id : initialRunId ,
1916+ task_run_index : taskRunIndex ,
1917+ ...( typeof analyticsHints . sourceRunId === 'string' && analyticsHints . sourceRunId . length > 0
1918+ ? { source_run_id : analyticsHints . sourceRunId }
1919+ : { } ) ,
1920+ ...( recoveryActionType ? { recovery_action_type : recoveryActionType } : { } ) ,
1921+ ...( typeof analyticsHints . recoveryActionInstanceId === 'string'
1922+ && analyticsHints . recoveryActionInstanceId . length > 0
1923+ ? { recovery_action_instance_id : analyticsHints . recoveryActionInstanceId }
1924+ : { } ) ,
1925+ } ;
18781926 const conversationTurnIndex = run . conversationId
18791927 ? conversationTurnIndexForRun ( db , run . conversationId , run . id )
18801928 : null ;
@@ -2088,6 +2136,7 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
20882136 }
20892137 : { } ) ,
20902138 } ;
2139+ Object . assign ( baseProps , buildRunCreatedV4Aliases ( baseProps , taskLineage ) ) ;
20912140 design . runs . setAnalyticsRecovery ?.( run , {
20922141 context : analyticsContext ,
20932142 properties : baseProps ,
@@ -2170,7 +2219,9 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
21702219 // in the rollout `last_token_usage`, read here best-effort.
21712220 const firstCallUsage = await ( async ( ) : Promise < {
21722221 first_call_input_tokens ?: number ;
2222+ first_call_input_tokens_effective ?: number ;
21732223 first_call_cache_read_input_tokens ?: number ;
2224+ first_call_cache_creation_input_tokens ?: number ;
21742225 first_call_cache_hit_ratio ?: number ;
21752226 } | null > => {
21762227 if ( run . agentId === 'codex' ) {
@@ -2187,20 +2238,39 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
21872238 'codex' ,
21882239 ) ,
21892240 ) . CODEX_HOME ;
2190- return await readCodexRolloutFirstCall ( { codexHome, sessionId } ) ;
2241+ const codexUsage = await readCodexRolloutFirstCall ( { codexHome, sessionId } ) ;
2242+ return codexUsage
2243+ ? {
2244+ ...codexUsage ,
2245+ first_call_input_tokens_effective :
2246+ codexUsage . first_call_input_tokens ,
2247+ }
2248+ : null ;
21912249 } catch {
21922250 return null ;
21932251 }
21942252 }
21952253 if ( usageAnalytics . first_call_input_tokens === undefined ) return null ;
21962254 return {
21972255 first_call_input_tokens : usageAnalytics . first_call_input_tokens ,
2256+ ...( usageAnalytics . first_call_input_tokens_effective !== undefined
2257+ ? {
2258+ first_call_input_tokens_effective :
2259+ usageAnalytics . first_call_input_tokens_effective ,
2260+ }
2261+ : { } ) ,
21982262 ...( usageAnalytics . first_call_cache_read_input_tokens !== undefined
21992263 ? {
22002264 first_call_cache_read_input_tokens :
22012265 usageAnalytics . first_call_cache_read_input_tokens ,
22022266 }
22032267 : { } ) ,
2268+ ...( usageAnalytics . first_call_cache_creation_input_tokens !== undefined
2269+ ? {
2270+ first_call_cache_creation_input_tokens :
2271+ usageAnalytics . first_call_cache_creation_input_tokens ,
2272+ }
2273+ : { } ) ,
22042274 ...( usageAnalytics . first_call_cache_hit_ratio !== undefined
22052275 ? { first_call_cache_hit_ratio : usageAnalytics . first_call_cache_hit_ratio }
22062276 : { } ) ,
@@ -2225,13 +2295,15 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
22252295 let artifactsModified : number | undefined ;
22262296 let designSystemCreated : boolean ;
22272297 let previewModuleCount : number ;
2298+ let artifactDiff : RunArtifactDiff | undefined ;
22282299 const artifactOutcome = run . artifactOutcome ;
22292300 if ( artifactOutcome ) {
22302301 artifactCount = artifactOutcome . artifactCount ;
22312302 artifactsCreated = artifactOutcome . artifactsCreated ;
22322303 artifactsModified = artifactOutcome . artifactsModified ;
22332304 designSystemCreated = artifactOutcome . designSystemCreated ;
22342305 previewModuleCount = artifactOutcome . previewModuleCount ;
2306+ artifactDiff = artifactOutcome . diff ;
22352307 } else {
22362308 const artifactBaseline = runArtifactBaselines . take ( run . id ) ;
22372309 if ( artifactBaseline && ! artifactBaseline . contended ) {
@@ -2245,6 +2317,7 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
22452317 diff = null ;
22462318 }
22472319 if ( diff ) {
2320+ artifactDiff = diff ;
22482321 artifactCount = diff . touched ;
22492322 artifactsCreated = diff . created ;
22502323 artifactsModified = diff . modified ;
@@ -2309,7 +2382,23 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
23092382 insertId : `${ runInsertId } -${ retryEvent . event } -${ index } ` ,
23102383 } ) ;
23112384 }
2312- const finishedProperties = {
2385+ const clarificationRequested = runAskedUserQuestion ( run . events ) ;
2386+ const interactionMode = typeof reqBody . sessionMode === 'string'
2387+ ? sessionModeToTracking ( reqBody . sessionMode )
2388+ : undefined ;
2389+ const primaryArtifactChange = artifactDiff
2390+ ? primaryArtifactChangeForRun ( {
2391+ diff : artifactDiff ,
2392+ projectKind : runProjectKind ,
2393+ hadExistingArtifacts : hintHasExistingArtifact === true ,
2394+ ...( interactionMode ? { interactionMode } : { } ) ,
2395+ clarificationRequested,
2396+ } )
2397+ : undefined ;
2398+ const supportingAssetFilesChanged = artifactDiff
2399+ ? supportingAssetFilesChangedForRun ( artifactDiff , runProjectKind )
2400+ : undefined ;
2401+ const finishedProperties : Record < string , unknown > = {
23132402 ...baseProps ,
23142403 design_system_id : run . designSystemId ?? undefined ,
23152404 design_system_digest : run . designSystemDigest ?? undefined ,
@@ -2345,7 +2434,7 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
23452434 : { } ) ,
23462435 ...( artifactsCreated !== undefined ? { artifacts_created : artifactsCreated } : { } ) ,
23472436 ...( artifactsModified !== undefined ? { artifacts_modified : artifactsModified } : { } ) ,
2348- asked_user_question : runAskedUserQuestion ( run . events ) ,
2437+ asked_user_question : clarificationRequested ,
23492438 retry_attempt_count : run . retryAttemptCount ?? 0 ,
23502439 retry_final_result : run . retryFinalResult ?? 'not_attempted' ,
23512440 ...( agentCliVersion
@@ -2444,6 +2533,55 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
24442533 tool_name_count : toolAnalytics . tool_name_count ,
24452534 tool_names : toolAnalytics . tool_names_csv ,
24462535 } ;
2536+ Object . assign (
2537+ finishedProperties ,
2538+ buildRunFinishedV4Aliases ( finishedProperties , taskLineage , {
2539+ inputAccountingMode : usageAnalytics . input_accounting_mode ,
2540+ ...( firstCallUsage
2541+ ? {
2542+ firstModelCall : {
2543+ ...( firstCallUsage . first_call_input_tokens !== undefined
2544+ ? { provider_input_tokens : firstCallUsage . first_call_input_tokens }
2545+ : { } ) ,
2546+ ...( firstCallUsage . first_call_input_tokens_effective !== undefined
2547+ ? { effective_input_tokens : firstCallUsage . first_call_input_tokens_effective }
2548+ : { } ) ,
2549+ ...( firstCallUsage . first_call_cache_read_input_tokens !== undefined
2550+ ? { cache_read_tokens : firstCallUsage . first_call_cache_read_input_tokens }
2551+ : { } ) ,
2552+ ...( firstCallUsage . first_call_cache_creation_input_tokens !== undefined
2553+ ? { cache_write_tokens : firstCallUsage . first_call_cache_creation_input_tokens }
2554+ : { } ) ,
2555+ } ,
2556+ }
2557+ : { } ) ,
2558+ ...( primaryArtifactChange
2559+ ? { primaryArtifactChange }
2560+ : { } ) ,
2561+ ...( artifactDiff
2562+ ? {
2563+ artifactFiles : {
2564+ changed_file_count : artifactDiff . contentTouched ,
2565+ created_file_count : artifactDiff . contentCreated ,
2566+ modified_file_count : artifactDiff . contentModified ,
2567+ ...( supportingAssetFilesChanged !== undefined
2568+ ? {
2569+ supporting_asset_files_changed_count :
2570+ supportingAssetFilesChanged ,
2571+ }
2572+ : { } ) ,
2573+ } ,
2574+ }
2575+ : { } ) ,
2576+ ...( isDesignSystemRun
2577+ ? {
2578+ designSystemChangeType : designSystemCreated
2579+ ? hintHasExistingArtifact === true ? 'modified' : 'created'
2580+ : 'none' ,
2581+ }
2582+ : { } ) ,
2583+ } ) ,
2584+ ) ;
24472585 // Refresh local recovery snapshot so crash recovery matches PostHog
24482586 // `run_finished` (usage/timing/tools), not only run_created baseProps.
24492587 // Keep the base insertId here: reconcileDurableRunTerminals appends
0 commit comments