Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/workshop-backend/__tests__/code-mode-return.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { expect, it } from "vitest";
import { appendCodeModeReturnValue } from "../src/overseer.js";
it("preserves code mode return values", () => {
const circular: Record<string, unknown> = {};
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]");
});
21 changes: 17 additions & 4 deletions packages/workshop-backend/src/overseer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default class extends WorkerEntrypoint {
}
}
}
await agent(self, env, this.ctx);
return await agent(self, env, this.ctx);
}
}
`;
Expand Down Expand Up @@ -161,7 +161,19 @@ interface CodeModeEntrypoint extends WorkerEntrypoint {
resolve: NativeRpcStub<(v: unknown) => void>,
reject: NativeRpcStub<(e: unknown) => void>
}>,
restoreForger?: NativeRpcStub<RestoreForgerImpl>): Promise<void>;
restoreForger?: NativeRpcStub<RestoreForgerImpl>): Promise<unknown>;
}

/** 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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
Loading