From 9731b82818a2a199a8d826ae3e406c61572c2b6f Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 24 Mar 2026 23:47:13 +0200 Subject: reload-runtime: fix reload loop by calling ctx.reload() directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tool previously used pi.sendUserMessage("/reload-runtime", { deliverAs: "followUp" }), which injected a user message that started a new AI turn and could cause the agent to call reload_runtime again — resulting in a loop. Now the tool calls ctx.reload() directly via a captured lastCtx, avoiding any follow-up message. Also tightened the tool description to discourage repeated calls in the same turn. Co-Authored-By: Claude Sonnet 4.6 --- pi/agent/extensions/reload-runtime/index.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'pi') diff --git a/pi/agent/extensions/reload-runtime/index.ts b/pi/agent/extensions/reload-runtime/index.ts index ebe6b7f..aceeddc 100644 --- a/pi/agent/extensions/reload-runtime/index.ts +++ b/pi/agent/extensions/reload-runtime/index.ts @@ -1,10 +1,19 @@ -import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; +import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent"; import { Type } from "@sinclair/typebox"; export default function (pi: ExtensionAPI) { + // Capture ctx so the tool can call ctx.reload() directly, avoiding a + // follow-up user message that would re-trigger the AI and cause a reload loop. + let lastCtx: ExtensionContext | undefined; + + pi.on("session_start", async (_event, ctx) => { + lastCtx = ctx; + }); + pi.registerCommand("reload-runtime", { description: "Reload extensions, skills, prompts, and themes", handler: async (_args, ctx) => { + lastCtx = ctx; await ctx.reload(); return; }, @@ -13,9 +22,20 @@ export default function (pi: ExtensionAPI) { pi.registerTool({ name: "reload_runtime", label: "Reload Runtime", - description: "Reload extensions, skills, prompts, and themes", + // Explicit single-use guidance prevents the AI from calling this in a loop. + description: "Reload extensions, skills, prompts, and themes. Call this once after editing extension files. Do not call it again in the same turn.", parameters: Type.Object({}), async execute() { + if (lastCtx) { + // Direct reload via ctx avoids injecting a follow-up user message, + // which would start a new AI turn and risk a reload loop. + await lastCtx.reload(); + return { + content: [{ type: "text", text: "Runtime reloaded." }], + details: {}, + }; + } + // Fallback if ctx is not yet available (should not happen in practice). pi.sendUserMessage("/reload-runtime", { deliverAs: "followUp" }); return { content: [{ type: "text", text: "Queued /reload-runtime as a follow-up command." }], -- cgit v1.2.3