11import { createHash } from "crypto" ;
22import type { ActHandler } from "../handlers/actHandler" ;
33import type { LLMClient } from "../llm/LLMClient" ;
4- import type { ActResult , Logger } from "../types/public" ;
4+ import type { Action , ActResult , Logger } from "../types/public" ;
55import type { Page } from "../understudy/page" ;
66import { CacheStorage } from "./CacheStorage" ;
77import { safeGetPageUrl } from "./utils" ;
@@ -100,7 +100,7 @@ export class ActCache {
100100 } ,
101101 } ) ;
102102
103- return await this . replayCachedActions ( entry , page , timeout ) ;
103+ return await this . replayCachedActions ( context , entry , page , timeout ) ;
104104 }
105105
106106 async store ( context : ActCacheContext , result : ActResult ) : Promise < void > {
@@ -157,6 +157,7 @@ export class ActCache {
157157 }
158158
159159 private async replayCachedActions (
160+ context : ActCacheContext ,
160161 entry : CachedActEntry ,
161162 page : Page ,
162163 timeout ?: number ,
@@ -206,6 +207,19 @@ export class ActCache {
206207 actionResults [ actionResults . length - 1 ] ?. actionDescription ||
207208 entry . actions [ entry . actions . length - 1 ] ?. description ||
208209 entry . instruction ;
210+
211+ if (
212+ success &&
213+ actions . length > 0 &&
214+ this . haveActionsChanged ( entry . actions , actions )
215+ ) {
216+ await this . refreshCacheEntry ( context , {
217+ ...entry ,
218+ actions,
219+ message,
220+ actionDescription,
221+ } ) ;
222+ }
209223 return {
210224 success,
211225 message,
@@ -217,6 +231,78 @@ export class ActCache {
217231 return await this . runWithTimeout ( execute , timeout ) ;
218232 }
219233
234+ private haveActionsChanged ( original : Action [ ] , updated : Action [ ] ) : boolean {
235+ if ( original . length !== updated . length ) {
236+ return true ;
237+ }
238+
239+ for ( let i = 0 ; i < original . length ; i += 1 ) {
240+ const orig = original [ i ] ;
241+ const next = updated [ i ] ;
242+ if ( ! next ) {
243+ return true ;
244+ }
245+
246+ if ( orig . selector !== next . selector ) {
247+ return true ;
248+ }
249+
250+ if ( orig . description !== next . description ) {
251+ return true ;
252+ }
253+
254+ if ( ( orig . method ?? "" ) !== ( next . method ?? "" ) ) {
255+ return true ;
256+ }
257+
258+ const origArgs = orig . arguments ?? [ ] ;
259+ const nextArgs = next . arguments ?? [ ] ;
260+ if ( origArgs . length !== nextArgs . length ) {
261+ return true ;
262+ }
263+
264+ for ( let j = 0 ; j < origArgs . length ; j += 1 ) {
265+ if ( origArgs [ j ] !== nextArgs [ j ] ) {
266+ return true ;
267+ }
268+ }
269+ }
270+
271+ return false ;
272+ }
273+
274+ private async refreshCacheEntry (
275+ context : ActCacheContext ,
276+ entry : CachedActEntry ,
277+ ) : Promise < void > {
278+ const { error, path } = await this . storage . writeJson (
279+ `${ context . cacheKey } .json` ,
280+ entry ,
281+ ) ;
282+
283+ if ( error && path ) {
284+ this . logger ( {
285+ category : "cache" ,
286+ message : "failed to update act cache entry after self-heal" ,
287+ level : 0 ,
288+ auxiliary : {
289+ error : { value : String ( error ) , type : "string" } ,
290+ } ,
291+ } ) ;
292+ return ;
293+ }
294+
295+ this . logger ( {
296+ category : "cache" ,
297+ message : "act cache entry updated after self-heal" ,
298+ level : 2 ,
299+ auxiliary : {
300+ instruction : { value : context . instruction , type : "string" } ,
301+ url : { value : context . pageUrl , type : "string" } ,
302+ } ,
303+ } ) ;
304+ }
305+
220306 private async runWithTimeout < T > (
221307 run : ( ) => Promise < T > ,
222308 timeout ?: number ,
0 commit comments