@@ -2,12 +2,12 @@ import { describe, expect, test } from "bun:test";
22import { deliverAgentMessage } from "./deliver-agent-message.js" ;
33
44describe ( "deliverAgentMessage" , ( ) => {
5- test ( "surfaces a not-delivered notice instead of throwing when the agent is mid-rebuild" , ( ) => {
5+ test ( "surfaces a not-delivered notice instead of throwing when the agent is mid-rebuild" , async ( ) => {
66 const notices : string [ ] = [ ] ;
77 const fatal = new Error ( "agent rebuild failed: provider unreachable" ) ;
88 let delivered = false ;
99
10- deliverAgentMessage ( {
10+ await deliverAgentMessage ( {
1111 getFatalBuildError : ( ) => fatal ,
1212 deliverToLiveAgent : ( ) => {
1313 delivered = true ;
@@ -24,10 +24,10 @@ describe("deliverAgentMessage", () => {
2424 expect ( notices [ 0 ] ) . toContain ( "provider unreachable" ) ;
2525 } ) ;
2626
27- test ( "surfaces a not-delivered notice when the live agent throws on delivery" , ( ) => {
27+ test ( "surfaces a not-delivered notice when the live agent throws on delivery" , async ( ) => {
2828 const notices : string [ ] = [ ] ;
2929
30- deliverAgentMessage ( {
30+ await deliverAgentMessage ( {
3131 getFatalBuildError : ( ) => null ,
3232 deliverToLiveAgent : ( ) => {
3333 throw new Error ( "agent is closed" ) ;
@@ -40,11 +40,11 @@ describe("deliverAgentMessage", () => {
4040 expect ( notices [ 0 ] ) . toContain ( "agent is closed" ) ;
4141 } ) ;
4242
43- test ( "delivers normally and stays silent when the agent is healthy" , ( ) => {
43+ test ( "delivers normally and stays silent when the agent is healthy" , async ( ) => {
4444 const notices : string [ ] = [ ] ;
4545 let delivered = false ;
4646
47- deliverAgentMessage ( {
47+ await deliverAgentMessage ( {
4848 getFatalBuildError : ( ) => null ,
4949 deliverToLiveAgent : ( ) => {
5050 delivered = true ;
@@ -55,4 +55,53 @@ describe("deliverAgentMessage", () => {
5555 expect ( delivered ) . toBe ( true ) ;
5656 expect ( notices ) . toHaveLength ( 0 ) ;
5757 } ) ;
58+
59+ test ( "holds delivery until ready settles so the first request cannot race startup work" , async ( ) => {
60+ // Models the Codex instructions refresh: the request body prefix pins the
61+ // instructions text, so delivery — and therefore request building — must
62+ // wait for the in-flight refresh instead of letting it swap the value
63+ // between turn 1 and turn 2.
64+ let instructions = "cached" ;
65+ let observedAtDelivery = "" ;
66+ let resolveReady ! : ( ) => void ;
67+ const ready = new Promise < void > ( ( resolve ) => {
68+ resolveReady = resolve ;
69+ } ) ;
70+
71+ const pending = deliverAgentMessage ( {
72+ getFatalBuildError : ( ) => null ,
73+ ready,
74+ deliverToLiveAgent : ( ) => {
75+ observedAtDelivery = instructions ;
76+ } ,
77+ onDeliverFailure : ( ) => undefined ,
78+ } ) ;
79+
80+ // Refresh still in flight: nothing delivered yet.
81+ await Promise . resolve ( ) ;
82+ expect ( observedAtDelivery ) . toBe ( "" ) ;
83+
84+ instructions = "refreshed" ;
85+ resolveReady ( ) ;
86+ await pending ;
87+
88+ // The first request observed the post-refresh value, so a later-settling
89+ // refresh can never change the prefix of an already-started session.
90+ expect ( observedAtDelivery ) . toBe ( "refreshed" ) ;
91+ } ) ;
92+
93+ test ( "delivers immediately when ready is already settled" , async ( ) => {
94+ let delivered = false ;
95+
96+ await deliverAgentMessage ( {
97+ getFatalBuildError : ( ) => null ,
98+ ready : Promise . resolve ( ) ,
99+ deliverToLiveAgent : ( ) => {
100+ delivered = true ;
101+ } ,
102+ onDeliverFailure : ( ) => undefined ,
103+ } ) ;
104+
105+ expect ( delivered ) . toBe ( true ) ;
106+ } ) ;
58107} ) ;
0 commit comments