




















@@ -95,6 +95,7 @@ async function runUntilCompleted(params: {
9595describe("Code Mode", () => {
9696afterEach(() => {
9797__testing.activeRuns.clear();
98+__testing.resumingRunIds.clear();
9899});
99100100101it("resolves object config defaults", () => {
@@ -299,6 +300,60 @@ describe("Code Mode", () => {
299300).rejects.toThrow("different session");
300301});
301302303+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+302357it("reports only unsettled pending tool calls when wait times out", async () => {
303358const catalogRef = createToolSearchCatalogRef();
304359const config = {
@@ -379,6 +434,54 @@ describe("Code Mode", () => {
379434expect(__testing.getTypescriptRuntimePromise()).toBeNull();
380435});
381436437+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+382485it("clamps omitted code-mode catalog search limits to maxSearchLimit", async () => {
383486const catalogRef = createToolSearchCatalogRef();
384487const config = {
@@ -449,7 +552,12 @@ describe("Code Mode", () => {
449552expect(details.value).toEqual({ value: 42 });
450553});
451554452-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) => {
453561const { config, catalogRef, tools: codeModeTools } = createCodeModeHarness();
454562applyCodeModeCatalog({
455563tools: [...codeModeTools, pluginTool("fake_noop", "Noop")],
@@ -462,7 +570,7 @@ describe("Code Mode", () => {
462570463571const details = resultDetails(
464572await 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", () => {
585693586694await expect(heartbeat).resolves.toBe("main-event-loop-alive");
587695expect(details.status).toBe("failed");
696+expect(String(details.error)).toContain("timeout exceeded");
588697});
589698});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。