@@ -170,6 +170,8 @@ function setup(
170170 const busy : boolean [ ] = [ ]
171171 /** Calls whose result the executor reported as reaching the model, in order. */
172172 const delivered : string [ ] = [ ]
173+ /** Calls Sim finished with without their real result reaching the model, in order. */
174+ const notDelivered : string [ ] = [ ]
173175 const executor = new DesktopExecutor ( {
174176 client : sim . client ,
175177 journal,
@@ -180,12 +182,23 @@ function setup(
180182 onBusyChange : ( value ) => busy . push ( value ) ,
181183 onApprovals : ( items ) => approvals . push ( items ) ,
182184 onResultDelivered : ( toolCallId ) => delivered . push ( toolCallId ) ,
185+ onResultNotDelivered : ( toolCallId ) => notDelivered . push ( toolCallId ) ,
183186 ...( options . maxHeldCalls ? { maxHeldCalls : options . maxHeldCalls } : { } ) ,
184187 ...( options . deliveryAwakeLimitMs !== undefined
185188 ? { deliveryAwakeLimitMs : options . deliveryAwakeLimitMs }
186189 : { } ) ,
187190 } )
188- return { sim, journal, runner, executor, onUnregistered, busy, approvals, delivered }
191+ return {
192+ sim,
193+ journal,
194+ runner,
195+ executor,
196+ onUnregistered,
197+ busy,
198+ approvals,
199+ delivered,
200+ notDelivered,
201+ }
189202}
190203
191204describe ( 'claiming' , ( ) => {
@@ -207,13 +220,13 @@ describe('claiming', () => {
207220 } )
208221
209222 it ( 'reports a result as reaching the model only once Sim takes it as the call own' , async ( ) => {
210- const { sim, journal, runner, executor, delivered } = setup ( )
223+ const { sim, journal, runner, executor, delivered, notDelivered } = setup ( )
211224 runner . immediate = DONE
212225 // Sim settled this call first: the result never reached the model.
213226 sim . completionOutcome = 'superseded'
214227 sim . inbox = [ callItem ( 'call-superseded' , 'chat-a' ) ]
215228 await executor . reconcile ( )
216- await vi . waitFor ( ( ) => expect ( sim . completions ) . toHaveLength ( 1 ) )
229+ await vi . waitFor ( ( ) => expect ( notDelivered ) . toEqual ( [ 'call-superseded' ] ) )
217230 expect ( delivered ) . toEqual ( [ ] )
218231
219232 // Taken by Sim, even with nothing written locally (no OS encryption, say).
@@ -222,10 +235,40 @@ describe('claiming', () => {
222235 sim . inbox = [ callItem ( 'call-recorded' , 'chat-a' ) ]
223236 await executor . reconcile ( )
224237 await vi . waitFor ( ( ) => expect ( delivered ) . toEqual ( [ 'call-recorded' ] ) )
238+ expect ( notDelivered ) . toEqual ( [ 'call-superseded' ] )
239+ } )
240+
241+ it ( 'reports a result Sim settled first, while a failed send waited to retry, as never reaching the model' , async ( ) => {
242+ const { sim, runner, executor, delivered, notDelivered } = setup ( )
243+ // The terminal handed back a command still going, and the first send of that result fails.
244+ runner . immediate = { status : 'success' , message : 'running' , data : { status : 'running' } }
245+ sim . completeErrors = [ new DeviceRequestError ( 503 , 'unavailable' ) ]
246+ sim . inbox = [ callItem ( 'call-1' , 'chat-a' , 'terminal' ) ]
247+ await executor . reconcile ( )
248+ await vi . waitFor ( ( ) => expect ( runner . started ) . toEqual ( [ 'call-1' ] ) )
249+
250+ // The user pressed Stop meanwhile, so Sim settled the call before the retry reached it.
251+ sim . completionOutcome = 'superseded'
252+ sim . inbox = [ { kind : 'cancel' , toolCallId : 'call-1' } ]
253+ await executor . reconcile ( )
254+
255+ await vi . waitFor ( ( ) => expect ( notDelivered ) . toEqual ( [ 'call-1' ] ) )
256+ expect ( delivered ) . toEqual ( [ ] )
257+ } )
258+
259+ it ( 'reports a result Sim refused as never reaching the model' , async ( ) => {
260+ const { sim, runner, executor, notDelivered } = setup ( )
261+ runner . immediate = DONE
262+ sim . completeErrors = [ new DeviceRequestError ( 403 , 'forbidden' ) ]
263+ sim . inbox = [ callItem ( 'call-refused' , 'chat-a' ) ]
264+
265+ await executor . reconcile ( )
266+
267+ await vi . waitFor ( ( ) => expect ( notDelivered ) . toEqual ( [ 'call-refused' ] ) )
225268 } )
226269
227270 it ( 'reports no result that stands in for what the action produced as reaching the model' , async ( ) => {
228- const { sim, runner, executor, delivered } = setup ( )
271+ const { sim, runner, executor, delivered, notDelivered } = setup ( )
229272 const standIns : DesktopToolCompletion [ ] = [
230273 { status : 'cancelled' , message : 'Stopped.' } ,
231274 { status : 'error' , message : 'Too large.' , data : { resultOmitted : true } } ,
@@ -243,6 +286,7 @@ describe('claiming', () => {
243286 sim . inbox = [ callItem ( 'real' , 'chat-a' ) ]
244287 await executor . reconcile ( )
245288 await vi . waitFor ( ( ) => expect ( delivered ) . toEqual ( [ 'real' ] ) )
289+ expect ( notDelivered ) . toEqual ( [ 'stand-in-0' , 'stand-in-1' , 'stand-in-2' , 'stand-in-3' ] )
246290 } )
247291
248292 it ( 'claims a whole backlog at once, before any of it runs' , async ( ) => {
0 commit comments