From 77e96818bd2107f7dd5b23d0e589ff50e16f46c2 Mon Sep 17 00:00:00 2001 From: fancydirty Date: Sat, 15 Aug 2026 00:35:28 +0800 Subject: [PATCH 1/2] fix: preserve executeCode return values --- .../__tests__/code-mode-return.test.ts | 7 ++++++ packages/workshop-backend/src/overseer.ts | 23 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 packages/workshop-backend/__tests__/code-mode-return.test.ts diff --git a/packages/workshop-backend/__tests__/code-mode-return.test.ts b/packages/workshop-backend/__tests__/code-mode-return.test.ts new file mode 100644 index 000000000..4c88687aa --- /dev/null +++ b/packages/workshop-backend/__tests__/code-mode-return.test.ts @@ -0,0 +1,7 @@ +import { expect, it } from "vitest"; +import { appendCodeModeReturnValue, CODE_MODE_HARNESS } from "../src/overseer.js"; +it("preserves code mode return values", () => { + expect(CODE_MODE_HARNESS).toContain("return await agent"); + expect(appendCodeModeReturnValue("", 1n)).toBe("1"); + expect(appendCodeModeReturnValue("started", { ok: true })).toBe('started\n{"ok":true}'); +}); diff --git a/packages/workshop-backend/src/overseer.ts b/packages/workshop-backend/src/overseer.ts index e5fed5bb2..5f42085af 100644 --- a/packages/workshop-backend/src/overseer.ts +++ b/packages/workshop-backend/src/overseer.ts @@ -50,7 +50,7 @@ import { renderGadgetPdf } from "./browser-export"; const logger = createWorkshopLogger("workshop.overseer"); export const AGENT_RUNNING_ERROR_MESSAGE = "Agent is running, wait for it to finish."; -let CODE_MODE_HARNESS = +export const CODE_MODE_HARNESS = `import { WorkerEntrypoint, restore } from "cloudflare:workers"; import agent from "agent.js"; @@ -84,7 +84,7 @@ export default class extends WorkerEntrypoint { } } } - await agent(self, env, this.ctx); + return await agent(self, env, this.ctx); } } `; @@ -161,7 +161,19 @@ interface CodeModeEntrypoint extends WorkerEntrypoint { resolve: NativeRpcStub<(v: unknown) => void>, reject: NativeRpcStub<(e: unknown) => void> }>, - restoreForger?: NativeRpcStub): Promise; + restoreForger?: NativeRpcStub): Promise; +} + +/** Appends a code-mode module's return value to its console output. */ +export function appendCodeModeReturnValue(log: string, value: unknown): string { + if (value === undefined) return log; + let rendered: string; + try { + let json = value !== null && typeof value === "object" && + (Array.isArray(value) || Object.getPrototypeOf(value) === Object.prototype); + rendered = json ? JSON.stringify(value) : String(value); + } catch { rendered = "[unserializable return value]"; } + return `${log}${log && rendered ? "\n" : ""}${rendered}`; } interface RestoreForgerEntrypoint extends WorkerEntrypoint { @@ -5554,10 +5566,11 @@ class OverseerImpl implements AgentHooks { } let error: string | undefined; + let returnValue: unknown; try { // The forger is a transient stub argument, so the capability to forge persistent // gadget-restore stubs lives exactly as long as this run() call. - await entrypoint.run(selfStub, callbackResolvers, + returnValue = await entrypoint.run(selfStub, callbackResolvers, new RestoreForgerImpl(this, chatId, bindings)); } catch (err) { if (err instanceof Error && err.stack) { @@ -5587,7 +5600,7 @@ class OverseerImpl implements AgentHooks { log += `\n\nUncaught exception: ${error}`; } - return log; + return appendCodeModeReturnValue(log, returnValue); } finally { this.#codeModeOutputSubscribers.delete(executionId); this.#codeModeResolvers.delete(executionId); From 057339884485b52789c628f04ef6ee1c44ab4dd6 Mon Sep 17 00:00:00 2001 From: fancydirty Date: Sat, 15 Aug 2026 01:37:18 +0800 Subject: [PATCH 2/2] test: cover unserializable code mode returns --- .../workshop-backend/__tests__/code-mode-return.test.ts | 6 ++++-- packages/workshop-backend/src/overseer.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/workshop-backend/__tests__/code-mode-return.test.ts b/packages/workshop-backend/__tests__/code-mode-return.test.ts index 4c88687aa..f76083cd3 100644 --- a/packages/workshop-backend/__tests__/code-mode-return.test.ts +++ b/packages/workshop-backend/__tests__/code-mode-return.test.ts @@ -1,7 +1,9 @@ import { expect, it } from "vitest"; -import { appendCodeModeReturnValue, CODE_MODE_HARNESS } from "../src/overseer.js"; +import { appendCodeModeReturnValue } from "../src/overseer.js"; it("preserves code mode return values", () => { - expect(CODE_MODE_HARNESS).toContain("return await agent"); + const circular: Record = {}; + circular.self = circular; expect(appendCodeModeReturnValue("", 1n)).toBe("1"); expect(appendCodeModeReturnValue("started", { ok: true })).toBe('started\n{"ok":true}'); + expect(appendCodeModeReturnValue("", circular)).toBe("[unserializable return value]"); }); diff --git a/packages/workshop-backend/src/overseer.ts b/packages/workshop-backend/src/overseer.ts index 5f42085af..9f7bbc766 100644 --- a/packages/workshop-backend/src/overseer.ts +++ b/packages/workshop-backend/src/overseer.ts @@ -50,7 +50,7 @@ import { renderGadgetPdf } from "./browser-export"; const logger = createWorkshopLogger("workshop.overseer"); export const AGENT_RUNNING_ERROR_MESSAGE = "Agent is running, wait for it to finish."; -export const CODE_MODE_HARNESS = +let CODE_MODE_HARNESS = `import { WorkerEntrypoint, restore } from "cloudflare:workers"; import agent from "agent.js";