@@ -3292,6 +3292,7 @@ describe("AgentServer pending user attachments", () => {
32923292 } ) ;
32933293
32943294 afterEach ( async ( ) => {
3295+ vi . useRealTimers ( ) ;
32953296 await server ?. stop ( ) ;
32963297 server = undefined ;
32973298 await rm ( tempDir , { recursive : true , force : true } ) ;
@@ -3313,6 +3314,7 @@ describe("AgentServer pending user attachments", () => {
33133314 } ;
33143315
33153316 it ( "appends an explicit notice when a pending attachment never reaches the manifest" , async ( ) => {
3317+ vi . useFakeTimers ( ) ;
33163318 const internals = buildInternals ( ) ;
33173319 // Refetch still can't see the attachment (truly absent, not just lagging).
33183320 const getTaskRun = vi . fn ( async ( ) =>
@@ -3323,17 +3325,18 @@ describe("AgentServer pending user attachments", () => {
33233325 ) ;
33243326 internals . posthogAPI . getTaskRun = getTaskRun ;
33253327
3326- const result = await internals . getPendingUserPrompt (
3328+ const resultPromise = internals . getPendingUserPrompt (
33273329 createTaskRun ( {
33283330 state : { pending_user_artifact_ids : [ "missing-attachment" ] } ,
33293331 artifacts : [ ] ,
33303332 } ) ,
33313333 ) ;
3334+ await vi . runAllTimersAsync ( ) ;
3335+ const result = await resultPromise ;
33323336
3333- // Refetched once to recover a lagging manifest, then — still missing —
3334- // surfaced an explicit notice instead of returning null (which would let the
3335- // caller fall back to the misleading "Attached files: …" description).
3336- expect ( getTaskRun ) . toHaveBeenCalledTimes ( 1 ) ;
3337+ // Retried to recover a lagging manifest, then surfaced an explicit notice
3338+ // instead of falling back to the misleading attachment summary.
3339+ expect ( getTaskRun ) . toHaveBeenCalledTimes ( 4 ) ;
33373340 expect ( result ) . not . toBeNull ( ) ;
33383341 expect ( result ?. prompt ) . toHaveLength ( 1 ) ;
33393342 const [ block ] = result ?. prompt ?? [ ] ;
@@ -3387,6 +3390,62 @@ describe("AgentServer pending user attachments", () => {
33873390 expect ( hasNotice ) . toBe ( false ) ;
33883391 } ) ;
33893392
3393+ it ( "recovers a pending attachment that only lands in a later manifest refetch" , async ( ) => {
3394+ vi . useFakeTimers ( ) ;
3395+ const internals = buildInternals ( ) ;
3396+ internals . posthogAPI . getTaskRun = vi
3397+ . fn ( )
3398+ . mockResolvedValueOnce (
3399+ createTaskRun ( {
3400+ state : { pending_user_artifact_ids : [ "att-1" ] } ,
3401+ artifacts : [ ] ,
3402+ } ) ,
3403+ )
3404+ . mockResolvedValueOnce (
3405+ createTaskRun ( {
3406+ state : { pending_user_artifact_ids : [ "att-1" ] } ,
3407+ artifacts : [ ] ,
3408+ } ) ,
3409+ )
3410+ . mockResolvedValue (
3411+ createTaskRun ( {
3412+ state : { pending_user_artifact_ids : [ "att-1" ] } ,
3413+ artifacts : [
3414+ {
3415+ id : "att-1" ,
3416+ name : "pasted-text.txt" ,
3417+ type : "user_attachment" ,
3418+ storage_path : "tasks/artifacts/pasted-text.txt" ,
3419+ content_type : "text/plain" ,
3420+ } ,
3421+ ] ,
3422+ } ) ,
3423+ ) ;
3424+ internals . posthogAPI . downloadArtifact = vi . fn ( async ( ) =>
3425+ exactArrayBuffer ( new TextEncoder ( ) . encode ( "pasted body" ) ) ,
3426+ ) ;
3427+
3428+ const resultPromise = internals . getPendingUserPrompt (
3429+ createTaskRun ( {
3430+ state : { pending_user_artifact_ids : [ "att-1" ] } ,
3431+ artifacts : [ ] ,
3432+ } ) ,
3433+ ) ;
3434+ await vi . runAllTimersAsync ( ) ;
3435+ const result = await resultPromise ;
3436+
3437+ expect ( internals . posthogAPI . getTaskRun ) . toHaveBeenCalledTimes ( 3 ) ;
3438+ expect ( result ?. prompt . some ( ( block ) => block . type === "resource_link" ) ) . toBe (
3439+ true ,
3440+ ) ;
3441+ expect (
3442+ result ?. prompt . some (
3443+ ( block ) =>
3444+ block . type === "text" && block . text . includes ( "could not be loaded" ) ,
3445+ ) ,
3446+ ) . toBe ( false ) ;
3447+ } ) ;
3448+
33903449 it ( "returns null without refetching when no pending artifacts were declared" , async ( ) => {
33913450 const internals = buildInternals ( ) ;
33923451 const getTaskRun = vi . fn ( ) ;
@@ -3401,6 +3460,7 @@ describe("AgentServer pending user attachments", () => {
34013460 } ) ;
34023461
34033462 it ( "warns once (not twice) about a missing artifact across the speculative and post-refetch resolves" , async ( ) => {
3463+ vi . useFakeTimers ( ) ;
34043464 const internals = buildInternals ( ) ;
34053465 // A non-empty manifest that never lists the requested id — so getArtifactsById
34063466 // reaches its per-id "missing" warning on both the pre- and post-refetch calls
@@ -3425,12 +3485,14 @@ describe("AgentServer pending user attachments", () => {
34253485 . spyOn ( loggerHost . logger , "warn" )
34263486 . mockImplementation ( ( ) => { } ) ;
34273487
3428- await internals . getPendingUserPrompt (
3488+ const resultPromise = internals . getPendingUserPrompt (
34293489 createTaskRun ( {
34303490 state : { pending_user_artifact_ids : [ "missing-attachment" ] } ,
34313491 artifacts : decoyManifest ,
34323492 } ) ,
34333493 ) ;
3494+ await vi . runAllTimersAsync ( ) ;
3495+ await resultPromise ;
34343496
34353497 // The speculative pre-refetch resolve stays quiet (a miss there is expected);
34363498 // only the post-refetch resolve emits the per-id "missing" warning.
0 commit comments