1- import { Buffer } from ' node:buffer'
2- import { randomUUID } from ' node:crypto'
1+ import { Buffer } from " node:buffer"
2+ import { randomUUID } from " node:crypto"
33
44const FNV1A_64_OFFSET_BASIS = 0xcbf29ce484222325n
55const FNV1A_64_PRIME = 0x100000001b3n
66const DEFAULT_SESSION_STATE_TTL_MS = 24 * 60 * 60 * 1000
77const DEFAULT_MAX_SESSION_STATES = 256
88
99const AGY_REQUEST_FIELD_ORDER = [
10- ' contents' ,
11- ' systemInstruction' ,
12- ' tools' ,
13- ' toolConfig' ,
14- ' labels' ,
15- ' generationConfig' ,
16- ' sessionId' ,
10+ " contents" ,
11+ " systemInstruction" ,
12+ " tools" ,
13+ " toolConfig" ,
14+ " labels" ,
15+ " generationConfig" ,
16+ " sessionId" ,
1717] as const
1818
1919const AGY_MODEL_ENUM_BY_WIRE_MODEL : Readonly < Record < string , string > > = {
20- ' gemini-3.5-flash-extra-low' : ' MODEL_PLACEHOLDER_M187' ,
21- ' gemini-3.5-flash-low' : ' MODEL_PLACEHOLDER_M20' ,
22- ' gemini-3-flash-agent' : ' MODEL_PLACEHOLDER_M84' ,
23- ' gemini-3.6-flash-low' : ' MODEL_PLACEHOLDER_M266' ,
24- ' gemini-3.6-flash-medium' : ' MODEL_PLACEHOLDER_M265' ,
25- ' gemini-3.6-flash-high' : ' MODEL_PLACEHOLDER_M264' ,
26- ' gemini-3.1-pro-low' : ' MODEL_PLACEHOLDER_M36' ,
27- ' gemini-pro-agent' : ' MODEL_PLACEHOLDER_M16' ,
28- ' claude-sonnet-4-6' : ' MODEL_PLACEHOLDER_M35' ,
29- ' claude-opus-4-6-thinking' : ' MODEL_PLACEHOLDER_M26' ,
30- ' gemini-3.1-flash-image' : ' MODEL_PLACEHOLDER_M21' ,
31- ' gpt-oss-120b-medium' : ' MODEL_OPENAI_GPT_OSS_120B_MEDIUM' ,
20+ " gemini-3.5-flash-extra-low" : " MODEL_PLACEHOLDER_M187" ,
21+ " gemini-3.5-flash-low" : " MODEL_PLACEHOLDER_M20" ,
22+ " gemini-3-flash-agent" : " MODEL_PLACEHOLDER_M84" ,
23+ " gemini-3.6-flash-low" : " MODEL_PLACEHOLDER_M266" ,
24+ " gemini-3.6-flash-medium" : " MODEL_PLACEHOLDER_M265" ,
25+ " gemini-3.6-flash-high" : " MODEL_PLACEHOLDER_M264" ,
26+ " gemini-3.1-pro-low" : " MODEL_PLACEHOLDER_M36" ,
27+ " gemini-pro-agent" : " MODEL_PLACEHOLDER_M16" ,
28+ " claude-sonnet-4-6" : " MODEL_PLACEHOLDER_M35" ,
29+ " claude-opus-4-6-thinking" : " MODEL_PLACEHOLDER_M26" ,
30+ " gemini-3.1-flash-image" : " MODEL_PLACEHOLDER_M21" ,
31+ " gpt-oss-120b-medium" : " MODEL_OPENAI_GPT_OSS_120B_MEDIUM" ,
3232}
3333
3434export interface AgyRequestSessionContext {
@@ -37,6 +37,7 @@ export interface AgyRequestSessionContext {
3737 numericSessionId : string
3838 usedClaude ?: boolean
3939 usedNonGeminiModel ?: boolean
40+ lastExecutionId ?: string
4041}
4142
4243interface StoredAgyRequestSession {
@@ -57,12 +58,13 @@ export interface AgyRequestScope {
5758}
5859
5960export interface AgyRequestLabels {
61+ last_execution_id ?: string
6062 last_step_index : string
6163 model_enum ?: string
6264 trajectory_id : string
63- used_claude : ' true' | ' false'
64- used_claude_conservative : ' true' | ' false'
65- used_non_gemini_model : ' true' | ' false'
65+ used_claude : " true" | " false"
66+ used_claude_conservative : " true" | " false"
67+ used_non_gemini_model : " true" | " false"
6668}
6769
6870export interface AgyAgentRequestMetadata {
@@ -72,9 +74,13 @@ export interface AgyAgentRequestMetadata {
7274 lastStepIndex : number
7375}
7476
77+ export interface AgyAgentRequestMetadataOptions {
78+ stepCountMode ?: "parts" | "contents"
79+ }
80+
7581export function fnv1a64Signed ( input : string ) : string {
7682 let hash = FNV1A_64_OFFSET_BASIS
77- for ( const byte of Buffer . from ( input , ' utf8' ) ) {
83+ for ( const byte of Buffer . from ( input , " utf8" ) ) {
7884 hash ^= BigInt ( byte )
7985 hash = BigInt . asUintN ( 64 , hash * FNV1A_64_PRIME )
8086 }
@@ -99,10 +105,7 @@ export class AgyRequestSessionStore {
99105 private readonly maxEntries : number
100106 private readonly now : ( ) => number
101107
102- constructor (
103- workspaceUri : string ,
104- options : AgyRequestSessionStoreOptions = { } ,
105- ) {
108+ constructor ( workspaceUri : string , options : AgyRequestSessionStoreOptions = { } ) {
106109 this . workspaceUri = workspaceUri
107110 this . ttlMs = options . ttlMs ?? DEFAULT_SESSION_STATE_TTL_MS
108111 this . maxEntries = options . maxEntries ?? DEFAULT_MAX_SESSION_STATES
@@ -131,14 +134,18 @@ export class AgyRequestSessionStore {
131134 beginRequest ( key : string ) : AgyRequestScope {
132135 const session = this . getOrCreate ( key )
133136 const stored = this . entries . get ( key ) !
134- const timestamp = Math . max (
135- stored . lastAccessedAt ,
136- stored . lastRequestTimestamp + 1 ,
137- )
137+ const timestamp = Math . max ( stored . lastAccessedAt , stored . lastRequestTimestamp + 1 )
138138 stored . lastRequestTimestamp = timestamp
139139 return { session, timestamp }
140140 }
141141
142+ completeExecution ( key : string ) : void {
143+ const stored = this . entries . get ( key )
144+ if ( stored ) {
145+ stored . context . lastExecutionId = randomUUID ( )
146+ }
147+ }
148+
142149 has ( key : string ) : boolean {
143150 return this . entries . has ( key )
144151 }
@@ -147,10 +154,6 @@ export class AgyRequestSessionStore {
147154 this . entries . delete ( key )
148155 }
149156
150- clear ( ) : void {
151- this . entries . clear ( )
152- }
153-
154157 get size ( ) : number {
155158 return this . entries . size
156159 }
@@ -163,10 +166,7 @@ export class AgyRequestSessionStore {
163166 }
164167 }
165168
166- while (
167- this . entries . size >= this . maxEntries &&
168- ! this . entries . has ( preservedKey )
169- ) {
169+ while ( this . entries . size >= this . maxEntries && ! this . entries . has ( preservedKey ) ) {
170170 let oldestKey : string | null = null
171171 let oldestAccess = Number . POSITIVE_INFINITY
172172 for ( const [ key , value ] of this . entries ) {
@@ -187,9 +187,7 @@ export function getAgyModelEnum(model: string): string | undefined {
187187 return AGY_MODEL_ENUM_BY_WIRE_MODEL [ model . toLowerCase ( ) ]
188188}
189189
190- export function orderAgyRequestPayloadInPlace (
191- payload : Record < string , unknown > ,
192- ) : void {
190+ export function orderAgyRequestPayloadInPlace ( payload : Record < string , unknown > ) : void {
193191 const ordered : Record < string , unknown > = { }
194192 const remaining = new Set ( Object . keys ( payload ) )
195193
@@ -209,21 +207,19 @@ export function orderAgyRequestPayloadInPlace(
209207 Object . assign ( payload , ordered )
210208}
211209
212- export function countAgyRequestSteps ( payload : Record < string , unknown > ) : number {
210+ export function countAgyRequestSteps (
211+ payload : Record < string , unknown > ,
212+ mode : "parts" | "contents" = "parts" ,
213+ ) : number {
213214 const contents = payload . contents
214- if ( ! Array . isArray ( contents ) ) {
215- return 1
216- }
215+ if ( ! Array . isArray ( contents ) ) return 1
216+ if ( mode === "contents" ) return Math . max ( 1 , contents . length )
217217
218218 let partCount = 0
219219 for ( const content of contents ) {
220- if ( ! content || typeof content !== 'object' || Array . isArray ( content ) ) {
221- continue
222- }
220+ if ( ! content || typeof content !== "object" || Array . isArray ( content ) ) continue
223221 const parts = ( content as Record < string , unknown > ) . parts
224- if ( Array . isArray ( parts ) ) {
225- partCount += parts . length
226- }
222+ if ( Array . isArray ( parts ) ) partCount += parts . length
227223 }
228224 return Math . max ( 1 , partCount )
229225}
@@ -233,21 +229,23 @@ export function buildAgyAgentRequestMetadata(
233229 payload : Record < string , unknown > ,
234230 model : string ,
235231 timestamp = Date . now ( ) ,
232+ options : AgyAgentRequestMetadataOptions = { } ,
236233) : AgyAgentRequestMetadata {
237- const lastStepIndex = countAgyRequestSteps ( payload )
238- const isClaude = model . toLowerCase ( ) . startsWith ( 'claude-' )
239- const isNonGemini = isClaude || model . toLowerCase ( ) . startsWith ( 'gpt-' )
234+ const lastStepIndex = countAgyRequestSteps ( payload , options . stepCountMode )
235+ + ( session . lastExecutionId ? 1 : 0 )
236+ const isClaude = model . toLowerCase ( ) . startsWith ( "claude-" )
237+ const isNonGemini = isClaude || model . toLowerCase ( ) . startsWith ( "gpt-" )
240238 session . usedClaude = session . usedClaude === true || isClaude
241- session . usedNonGeminiModel =
242- session . usedNonGeminiModel === true || isNonGemini
239+ session . usedNonGeminiModel = session . usedNonGeminiModel === true || isNonGemini
243240 const modelEnum = getAgyModelEnum ( model )
244241 const labels : AgyRequestLabels = {
242+ ...( session . lastExecutionId ? { last_execution_id : session . lastExecutionId } : { } ) ,
245243 last_step_index : String ( lastStepIndex ) ,
246244 ...( modelEnum ? { model_enum : modelEnum } : { } ) ,
247245 trajectory_id : session . trajectoryId ,
248- used_claude : session . usedClaude ? ' true' : ' false' ,
249- used_claude_conservative : session . usedClaude ? ' true' : ' false' ,
250- used_non_gemini_model : session . usedNonGeminiModel ? ' true' : ' false' ,
246+ used_claude : session . usedClaude ? " true" : " false" ,
247+ used_claude_conservative : session . usedClaude ? " true" : " false" ,
248+ used_non_gemini_model : session . usedNonGeminiModel ? " true" : " false" ,
251249 }
252250
253251 return {
0 commit comments