@@ -186,16 +186,201 @@ describe('resolveInstance', () => {
186186 } ) ;
187187
188188 it ( 'selects the instance matching BOTH cwd and port when a port is supplied' , ( ) => {
189- const a = record ( '/Users/x/projects/foo' , 'ready' , { pid : 100 , port : 6006 } ) ;
190- const b = record ( '/Users/x/projects/foo' , 'ready' , { pid : 200 , port : 6007 } ) ;
191- const result = resolveInstance ( [ a , b ] , '/Users/x/projects/foo' , 6007 ) ;
189+ const a = record ( '/Users/x/projects/foo' , 'ready' , {
190+ agent : 'claude-preview' ,
191+ pid : 100 ,
192+ port : 6006 ,
193+ } ) ;
194+ const b = record ( '/Users/x/projects/foo' , 'ready' , { agent : 'codex' , pid : 200 , port : 6007 } ) ;
195+ const result = resolveInstance ( [ a , b ] , '/Users/x/projects/foo' , 6007 , 'claude' ) ;
192196 expect ( result . kind ) . toBe ( 'instance' ) ;
193197 if ( result . kind === 'instance' ) {
194198 expect ( result . record ) . toBe ( b ) ;
195199 expect ( result . matches ) . toEqual ( [ b ] ) ;
196200 }
197201 } ) ;
198202
203+ it ( 'prefers Claude preview records for Claude CLI invocations' , ( ) => {
204+ const genericClaude = record ( '/Users/x/projects/foo' , 'ready' , {
205+ agent : 'claude' ,
206+ startedAt : '2026-06-09T11:00:00.000Z' ,
207+ } ) ;
208+ const claudePreview = record ( '/Users/x/projects/foo' , 'ready' , {
209+ agent : 'claude-preview' ,
210+ startedAt : '2026-06-09T10:00:00.000Z' ,
211+ } ) ;
212+ const newerCodex = record ( '/Users/x/projects/foo' , 'ready' , {
213+ agent : 'codex' ,
214+ startedAt : '2026-06-09T12:00:00.000Z' ,
215+ } ) ;
216+ const result = resolveInstance (
217+ [ genericClaude , claudePreview , newerCodex ] ,
218+ '/Users/x/projects/foo' ,
219+ undefined ,
220+ 'claude'
221+ ) ;
222+
223+ expect ( result . kind ) . toBe ( 'instance' ) ;
224+ if ( result . kind === 'instance' ) {
225+ expect ( result . record ) . toBe ( claudePreview ) ;
226+ expect ( result . matches ) . toEqual ( [ claudePreview ] ) ;
227+ }
228+ } ) ;
229+
230+ it ( 'falls back to generic Claude records when no Claude preview record matches' , ( ) => {
231+ const genericClaude = record ( '/Users/x/projects/foo' , 'ready' , {
232+ agent : 'claude' ,
233+ startedAt : '2026-06-09T10:00:00.000Z' ,
234+ } ) ;
235+ const newerCodex = record ( '/Users/x/projects/foo' , 'ready' , {
236+ agent : 'codex' ,
237+ startedAt : '2026-06-09T11:00:00.000Z' ,
238+ } ) ;
239+ const result = resolveInstance (
240+ [ genericClaude , newerCodex ] ,
241+ '/Users/x/projects/foo' ,
242+ undefined ,
243+ 'claude'
244+ ) ;
245+
246+ expect ( result . kind ) . toBe ( 'instance' ) ;
247+ if ( result . kind === 'instance' ) {
248+ expect ( result . record ) . toBe ( genericClaude ) ;
249+ expect ( result . matches ) . toEqual ( [ genericClaude ] ) ;
250+ }
251+ } ) ;
252+
253+ it ( 'stays in the preferred agent bucket even when another bucket is ready' , ( ) => {
254+ const startingPreview = record ( '/Users/x/projects/foo' , 'starting' , {
255+ agent : 'claude-preview' ,
256+ startedAt : '2026-06-09T11:00:00.000Z' ,
257+ } ) ;
258+ const readyCodex = record ( '/Users/x/projects/foo' , 'ready' , {
259+ agent : 'codex' ,
260+ startedAt : '2026-06-09T10:00:00.000Z' ,
261+ } ) ;
262+ const result = resolveInstance (
263+ [ startingPreview , readyCodex ] ,
264+ '/Users/x/projects/foo' ,
265+ undefined ,
266+ 'claude'
267+ ) ;
268+
269+ expect ( result . kind ) . toBe ( 'intercept' ) ;
270+ if ( result . kind === 'intercept' ) {
271+ expect ( result . reason ) . toBe ( 'mcp-starting' ) ;
272+ expect ( result . matches ) . toEqual ( [ startingPreview ] ) ;
273+ }
274+ } ) ;
275+
276+ it ( 'reports errors from the preferred agent bucket instead of switching buckets' , ( ) => {
277+ const erroredPreview = record ( '/Users/x/projects/foo' , 'error' , {
278+ agent : 'claude-preview' ,
279+ startedAt : '2026-06-09T11:00:00.000Z' ,
280+ } ) ;
281+ const readyClaude = record ( '/Users/x/projects/foo' , 'ready' , {
282+ agent : 'claude' ,
283+ startedAt : '2026-06-09T10:00:00.000Z' ,
284+ } ) ;
285+ const result = resolveInstance (
286+ [ erroredPreview , readyClaude ] ,
287+ '/Users/x/projects/foo' ,
288+ undefined ,
289+ 'claude'
290+ ) ;
291+
292+ expect ( result . kind ) . toBe ( 'intercept' ) ;
293+ if ( result . kind === 'intercept' ) {
294+ expect ( result . reason ) . toBe ( 'mcp-error' ) ;
295+ expect ( result . matches ) . toEqual ( [ erroredPreview ] ) ;
296+ }
297+ } ) ;
298+
299+ it ( 'prefers records matching the current non-Claude agent' , ( ) => {
300+ const codex = record ( '/Users/x/projects/foo' , 'ready' , {
301+ agent : 'codex' ,
302+ startedAt : '2026-06-09T10:00:00.000Z' ,
303+ } ) ;
304+ const newerCursor = record ( '/Users/x/projects/foo' , 'ready' , {
305+ agent : 'cursor' ,
306+ startedAt : '2026-06-09T11:00:00.000Z' ,
307+ } ) ;
308+ const result = resolveInstance (
309+ [ codex , newerCursor ] ,
310+ '/Users/x/projects/foo' ,
311+ undefined ,
312+ 'codex'
313+ ) ;
314+
315+ expect ( result . kind ) . toBe ( 'instance' ) ;
316+ if ( result . kind === 'instance' ) {
317+ expect ( result . record ) . toBe ( codex ) ;
318+ expect ( result . matches ) . toEqual ( [ codex ] ) ;
319+ }
320+ } ) ;
321+
322+ it ( 'chooses the latest-started ready instance inside the selected agent bucket' , ( ) => {
323+ const olderPreview = record ( '/Users/x/projects/foo' , 'ready' , {
324+ agent : 'claude-preview' ,
325+ startedAt : '2026-06-09T10:00:00.000Z' ,
326+ } ) ;
327+ const newerPreview = record ( '/Users/x/projects/foo' , 'ready' , {
328+ agent : 'claude-preview' ,
329+ startedAt : '2026-06-09T11:00:00.000Z' ,
330+ } ) ;
331+ const newestCodex = record ( '/Users/x/projects/foo' , 'ready' , {
332+ agent : 'codex' ,
333+ startedAt : '2026-06-09T12:00:00.000Z' ,
334+ } ) ;
335+ const result = resolveInstance (
336+ [ olderPreview , newerPreview , newestCodex ] ,
337+ '/Users/x/projects/foo' ,
338+ undefined ,
339+ 'claude'
340+ ) ;
341+
342+ expect ( result . kind ) . toBe ( 'instance' ) ;
343+ if ( result . kind === 'instance' ) {
344+ expect ( result . record ) . toBe ( newerPreview ) ;
345+ expect ( result . matches ) . toEqual ( [ newerPreview , olderPreview ] ) ;
346+ }
347+ } ) ;
348+
349+ it ( 'falls back to latest-started behavior when no record matches the current agent' , ( ) => {
350+ const older = record ( '/Users/x/projects/foo' , 'ready' , {
351+ startedAt : '2026-06-09T10:00:00.000Z' ,
352+ } ) ;
353+ const newer = record ( '/Users/x/projects/foo' , 'ready' , {
354+ agent : 'cursor' ,
355+ startedAt : '2026-06-09T11:00:00.000Z' ,
356+ } ) ;
357+ const result = resolveInstance ( [ older , newer ] , '/Users/x/projects/foo' , undefined , 'codex' ) ;
358+
359+ expect ( result . kind ) . toBe ( 'instance' ) ;
360+ if ( result . kind === 'instance' ) {
361+ expect ( result . record ) . toBe ( newer ) ;
362+ expect ( result . matches ) . toEqual ( [ newer , older ] ) ;
363+ }
364+ } ) ;
365+
366+ it ( 'falls back to latest-started behavior when no current agent is detected' , ( ) => {
367+ const olderPreview = record ( '/Users/x/projects/foo' , 'ready' , {
368+ agent : 'claude-preview' ,
369+ startedAt : '2026-06-09T10:00:00.000Z' ,
370+ } ) ;
371+ const newerCodex = record ( '/Users/x/projects/foo' , 'ready' , {
372+ agent : 'codex' ,
373+ startedAt : '2026-06-09T11:00:00.000Z' ,
374+ } ) ;
375+ const result = resolveInstance ( [ olderPreview , newerCodex ] , '/Users/x/projects/foo' ) ;
376+
377+ expect ( result . kind ) . toBe ( 'instance' ) ;
378+ if ( result . kind === 'instance' ) {
379+ expect ( result . record ) . toBe ( newerCodex ) ;
380+ expect ( result . matches ) . toEqual ( [ newerCodex , olderPreview ] ) ;
381+ }
382+ } ) ;
383+
199384 it ( 'ignores port when it is not supplied (routes by cwd alone)' , ( ) => {
200385 const a = record ( '/Users/x/projects/foo' , 'ready' , { pid : 100 , port : 6006 } ) ;
201386 const b = record ( '/Users/x/projects/foo' , 'ready' , { pid : 200 , port : 6007 } ) ;
0 commit comments