@@ -95,6 +95,7 @@ async function runUntilCompleted(params: {
9595describe ( "Code Mode" , ( ) => {
9696 afterEach ( ( ) => {
9797 __testing . activeRuns . clear ( ) ;
98+ __testing . resumingRunIds . clear ( ) ;
9899 } ) ;
99100
100101 it ( "resolves object config defaults" , ( ) => {
@@ -299,6 +300,60 @@ describe("Code Mode", () => {
299300 ) . rejects . toThrow ( "different session" ) ;
300301 } ) ;
301302
303+ it ( "rejects concurrent waits for the same suspended run" , async ( ) => {
304+ const catalogRef = createToolSearchCatalogRef ( ) ;
305+ const config = {
306+ tools : {
307+ codeMode : {
308+ enabled : true ,
309+ timeoutMs : 100 ,
310+ } ,
311+ } ,
312+ } as never ;
313+ const ctx = {
314+ config,
315+ runtimeConfig : config ,
316+ sessionId : "session-code-mode" ,
317+ sessionKey : "agent:main:main" ,
318+ runId : "run-code-mode" ,
319+ catalogRef,
320+ } ;
321+ const codeModeTools = createCodeModeTools ( ctx ) ;
322+ applyCodeModeCatalog ( {
323+ tools : [
324+ ...codeModeTools ,
325+ pluginToolWithExecute (
326+ "fake_slow" ,
327+ "Slow helper" ,
328+ async ( ) => await new Promise < never > ( ( ) => undefined ) ,
329+ ) ,
330+ ] ,
331+ config,
332+ sessionId : "session-code-mode" ,
333+ sessionKey : "agent:main:main" ,
334+ runId : "run-code-mode" ,
335+ catalogRef,
336+ } ) ;
337+
338+ const first = resultDetails (
339+ await codeModeTools [ 0 ] . execute ( "code-call-concurrent-wait" , {
340+ code : "await tools.fake_slow({}); return 'done';" ,
341+ } ) ,
342+ ) ;
343+ expect ( first . status ) . toBe ( "waiting" ) ;
344+
345+ const firstWait = codeModeTools [ 1 ] . execute ( "code-wait-concurrent-a" , {
346+ runId : first . runId ,
347+ } ) ;
348+ await expect (
349+ codeModeTools [ 1 ] . execute ( "code-wait-concurrent-b" , { runId : first . runId } ) ,
350+ ) . rejects . toThrow ( "already being resumed" ) ;
351+ const stillWaiting = resultDetails ( await firstWait ) ;
352+
353+ expect ( stillWaiting . status ) . toBe ( "waiting" ) ;
354+ expect ( stillWaiting . runId ) . toBe ( first . runId ) ;
355+ } ) ;
356+
302357 it ( "reports only unsettled pending tool calls when wait times out" , async ( ) => {
303358 const catalogRef = createToolSearchCatalogRef ( ) ;
304359 const config = {
@@ -379,6 +434,54 @@ describe("Code Mode", () => {
379434 expect ( __testing . getTypescriptRuntimePromise ( ) ) . toBeNull ( ) ;
380435 } ) ;
381436
437+ it ( "allows identifiers and strings that contain import without module access" , async ( ) => {
438+ const { config, catalogRef, tools : codeModeTools } = createCodeModeHarness ( ) ;
439+ applyCodeModeCatalog ( {
440+ tools : [ ...codeModeTools , pluginTool ( "fake_noop" , "Noop" ) ] ,
441+ config,
442+ sessionId : "session-code-mode" ,
443+ sessionKey : "agent:main:main" ,
444+ runId : "run-code-mode" ,
445+ catalogRef,
446+ } ) ;
447+
448+ const details = await runUntilCompleted ( {
449+ execTool : codeModeTools [ 0 ] ,
450+ waitTool : codeModeTools [ 1 ] ,
451+ code : `
452+ const important = 41;
453+ const message = "import docs later";
454+ return important + (message.includes("import") ? 1 : 0);
455+ ` ,
456+ } ) ;
457+
458+ expect ( details . status ) . toBe ( "completed" ) ;
459+ expect ( details . value ) . toBe ( 42 ) ;
460+ } ) ;
461+
462+ it ( "fails pending promises that have no host bridge work" , async ( ) => {
463+ const { config, catalogRef, tools : codeModeTools } = createCodeModeHarness ( ) ;
464+ applyCodeModeCatalog ( {
465+ tools : [ ...codeModeTools , pluginTool ( "fake_noop" , "Noop" ) ] ,
466+ config,
467+ sessionId : "session-code-mode" ,
468+ sessionKey : "agent:main:main" ,
469+ runId : "run-code-mode" ,
470+ catalogRef,
471+ } ) ;
472+
473+ const beforeRunCount = __testing . activeRuns . size ;
474+ const details = resultDetails (
475+ await codeModeTools [ 0 ] . execute ( "code-call-empty-wait" , {
476+ code : "await new Promise(() => undefined); return 'never';" ,
477+ } ) ,
478+ ) ;
479+
480+ expect ( details . status ) . toBe ( "failed" ) ;
481+ expect ( String ( details . error ) ) . toContain ( "pending without host work" ) ;
482+ expect ( __testing . activeRuns . size ) . toBe ( beforeRunCount ) ;
483+ } ) ;
484+
382485 it ( "clamps omitted code-mode catalog search limits to maxSearchLimit" , async ( ) => {
383486 const catalogRef = createToolSearchCatalogRef ( ) ;
384487 const config = {
@@ -449,7 +552,12 @@ describe("Code Mode", () => {
449552 expect ( details . value ) . toEqual ( { value : 42 } ) ;
450553 } ) ;
451554
452- it ( "rejects module access" , async ( ) => {
555+ it . each ( [
556+ "const fs = require('node:fs'); return fs;" ,
557+ "return import('node:fs');" ,
558+ "return import.meta.url;" ,
559+ "return `${import('node:fs')}`;" ,
560+ ] ) ( "rejects module access: %s" , async ( code ) => {
453561 const { config, catalogRef, tools : codeModeTools } = createCodeModeHarness ( ) ;
454562 applyCodeModeCatalog ( {
455563 tools : [ ...codeModeTools , pluginTool ( "fake_noop" , "Noop" ) ] ,
@@ -462,7 +570,7 @@ describe("Code Mode", () => {
462570
463571 const details = resultDetails (
464572 await codeModeTools [ 0 ] . execute ( "code-call-import" , {
465- code : "const fs = require('node:fs'); return fs;" ,
573+ code,
466574 } ) ,
467575 ) ;
468576
@@ -585,5 +693,6 @@ describe("Code Mode", () => {
585693
586694 await expect ( heartbeat ) . resolves . toBe ( "main-event-loop-alive" ) ;
587695 expect ( details . status ) . toBe ( "failed" ) ;
696+ expect ( String ( details . error ) ) . toContain ( "timeout exceeded" ) ;
588697 } ) ;
589698} ) ;
0 commit comments