From 60cdb6565dfaa9a8e4424b8e5b2c8473a53f252b Mon Sep 17 00:00:00 2001 From: Max Peterson Date: Fri, 21 Aug 2026 12:22:50 -0700 Subject: [PATCH 1/5] log executeCode return values --- packages/gatekeeper-scheduler/src/types.d.ts | 8 ++++---- packages/workshop-backend/src/agent.ts | 2 ++ packages/workshop-backend/src/overseer.ts | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/gatekeeper-scheduler/src/types.d.ts b/packages/gatekeeper-scheduler/src/types.d.ts index 0c9623f26..a41c1de1b 100644 --- a/packages/gatekeeper-scheduler/src/types.d.ts +++ b/packages/gatekeeper-scheduler/src/types.d.ts @@ -280,9 +280,9 @@ export interface ScheduledTaskHook { * runs through the restored Gadget and does not need the Scheduler binding. * * Every successful registration call creates a distinct hook; registration is not idempotent. - * `executeCode` ignores the exported function's return value, so print the schedule ID with - * `console.log()`. Empty console output is not evidence that registration failed, and the disabled - * hook appearing in Connections confirms success. Do not retry solely because no ID was printed. + * Return or print the schedule ID so it appears in the `executeCode` output. Empty output is not + * evidence that registration failed, and the disabled hook appearing in Connections confirms + * success. Do not retry solely because no ID was printed. * * @example * // server.js @@ -308,7 +308,7 @@ export interface ScheduledTaskHook { * callback, * { title: "Daily brief", description: "Prepare the morning activity summary." }, * ); - * console.log("Schedule registered:", scheduleId); + * return scheduleId; */ export interface ScheduleSession { /** diff --git a/packages/workshop-backend/src/agent.ts b/packages/workshop-backend/src/agent.ts index 95cd61999..b7f8df173 100644 --- a/packages/workshop-backend/src/agent.ts +++ b/packages/workshop-backend/src/agent.ts @@ -831,6 +831,8 @@ Note that this differs from the \`env\` a Gadget's own code sees: a Gadget's ser When the user asks you to just do a task that can be done with these bindings, you should use executeCode to perform the task, instead of adding code to a gadget to do it. The function also receives a \`self\` parameter which is a magic object that points back to this chat thread. Calling any method on \`self\`, like \`self.foo(123)\`, delivers a callback message to this chat and activates you to respond. \`self\` can be passed over RPC (e.g. to a subscription method) and stored in a Durable Object's KV storage for long-term callbacks. When an agent callback is received, it appears in your env under a name like \`PARAMS_1\`, with \`.args\` (the callback arguments), \`.resolve(value)\` (to return a value to the caller), and \`.reject(error)\` (to reject with an error). + +A non-\`undefined\` value returned by the function is included as the final line of console output, so only return values that should be visible in this output. `.trim(); let LIST_CONNECTABLE_RESOURCES_TOOL_DESCRIPTION = ` diff --git a/packages/workshop-backend/src/overseer.ts b/packages/workshop-backend/src/overseer.ts index 3b0723f3e..b1936f9a4 100644 --- a/packages/workshop-backend/src/overseer.ts +++ b/packages/workshop-backend/src/overseer.ts @@ -99,7 +99,8 @@ export default class extends WorkerEntrypoint { } } } - await agent(self, env, this.ctx); + let result = await agent(self, env, this.ctx); + if (result !== undefined) console.log(result); } } `; From 8aa8e99f4cc5ef0d4eb6064e413ea46e70572965 Mon Sep 17 00:00:00 2001 From: Max Peterson Date: Tue, 25 Aug 2026 12:06:39 -0700 Subject: [PATCH 2/5] drop executeCode return value line from system prompt --- packages/workshop-backend/src/agent.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/workshop-backend/src/agent.ts b/packages/workshop-backend/src/agent.ts index b7f8df173..95cd61999 100644 --- a/packages/workshop-backend/src/agent.ts +++ b/packages/workshop-backend/src/agent.ts @@ -831,8 +831,6 @@ Note that this differs from the \`env\` a Gadget's own code sees: a Gadget's ser When the user asks you to just do a task that can be done with these bindings, you should use executeCode to perform the task, instead of adding code to a gadget to do it. The function also receives a \`self\` parameter which is a magic object that points back to this chat thread. Calling any method on \`self\`, like \`self.foo(123)\`, delivers a callback message to this chat and activates you to respond. \`self\` can be passed over RPC (e.g. to a subscription method) and stored in a Durable Object's KV storage for long-term callbacks. When an agent callback is received, it appears in your env under a name like \`PARAMS_1\`, with \`.args\` (the callback arguments), \`.resolve(value)\` (to return a value to the caller), and \`.reject(error)\` (to reject with an error). - -A non-\`undefined\` value returned by the function is included as the final line of console output, so only return values that should be visible in this output. `.trim(); let LIST_CONNECTABLE_RESOURCES_TOOL_DESCRIPTION = ` From d9541415632ffc3c2670823ea7b9ca6a0e6d20ec Mon Sep 17 00:00:00 2001 From: Max Peterson Date: Tue, 25 Aug 2026 12:22:26 -0700 Subject: [PATCH 3/5] prefix auto-logged executeCode return values --- packages/workshop-backend/src/overseer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/workshop-backend/src/overseer.ts b/packages/workshop-backend/src/overseer.ts index b1936f9a4..830f328f4 100644 --- a/packages/workshop-backend/src/overseer.ts +++ b/packages/workshop-backend/src/overseer.ts @@ -100,7 +100,7 @@ export default class extends WorkerEntrypoint { } } let result = await agent(self, env, this.ctx); - if (result !== undefined) console.log(result); + if (result !== undefined) console.log("Return value:", result); } } `; From ebc13c495aa1cf761bc8ff3cdb8108db6fbdf569 Mon Sep 17 00:00:00 2001 From: Max Peterson Date: Mon, 31 Aug 2026 10:42:50 -0700 Subject: [PATCH 4/5] add note to the agent when executeCode succeeds with no output - help kimi and other non-sota models understand that no output doesn't mean something when wrong, just that the function ran without logging or returning anything --- packages/gatekeeper-scheduler/src/types.d.ts | 3 --- packages/workshop-backend/src/overseer.ts | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/gatekeeper-scheduler/src/types.d.ts b/packages/gatekeeper-scheduler/src/types.d.ts index a41c1de1b..02035baaf 100644 --- a/packages/gatekeeper-scheduler/src/types.d.ts +++ b/packages/gatekeeper-scheduler/src/types.d.ts @@ -280,9 +280,6 @@ export interface ScheduledTaskHook { * runs through the restored Gadget and does not need the Scheduler binding. * * Every successful registration call creates a distinct hook; registration is not idempotent. - * Return or print the schedule ID so it appears in the `executeCode` output. Empty output is not - * evidence that registration failed, and the disabled hook appearing in Connections confirms - * success. Do not retry solely because no ID was printed. * * @example * // server.js diff --git a/packages/workshop-backend/src/overseer.ts b/packages/workshop-backend/src/overseer.ts index 830f328f4..0fcc4f28a 100644 --- a/packages/workshop-backend/src/overseer.ts +++ b/packages/workshop-backend/src/overseer.ts @@ -7263,6 +7263,8 @@ class OverseerImpl implements AgentHooks { if (error) { log += `\n\nUncaught exception: ${error}`; + } else if (log === "") { + log = "(function succeeded with no output)"; } return log; From b866e1ec93bbb889edb51f2c5c60c3cd24c1ee4b Mon Sep 17 00:00:00 2001 From: Max Peterson Date: Mon, 31 Aug 2026 11:22:28 -0700 Subject: [PATCH 5/5] address ai code review comments - report errors that serialize to "" as uncaught exceptions instead of reporting success --- packages/gatekeeper-scheduler/src/types.d.ts | 2 +- packages/workshop-backend/src/overseer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gatekeeper-scheduler/src/types.d.ts b/packages/gatekeeper-scheduler/src/types.d.ts index 02035baaf..0c4c52077 100644 --- a/packages/gatekeeper-scheduler/src/types.d.ts +++ b/packages/gatekeeper-scheduler/src/types.d.ts @@ -305,7 +305,7 @@ export interface ScheduledTaskHook { * callback, * { title: "Daily brief", description: "Prepare the morning activity summary." }, * ); - * return scheduleId; + * console.log("Schedule registered:", scheduleId); */ export interface ScheduleSession { /** diff --git a/packages/workshop-backend/src/overseer.ts b/packages/workshop-backend/src/overseer.ts index 0fcc4f28a..06c2cf0f2 100644 --- a/packages/workshop-backend/src/overseer.ts +++ b/packages/workshop-backend/src/overseer.ts @@ -7261,7 +7261,7 @@ class OverseerImpl implements AgentHooks { }).join(" "); }).join("\n"); - if (error) { + if (error !== undefined) { log += `\n\nUncaught exception: ${error}`; } else if (log === "") { log = "(function succeeded with no output)";