From eb281b63d4f86dde323a3ecfaa21c42520cc67c5 Mon Sep 17 00:00:00 2001 From: Amos Date: Sat, 27 Jun 2026 02:05:12 +0300 Subject: [PATCH] removed autoExit schema field from subagent_message --- README.md | 3 +-- pi-extension/subagents/index.ts | 19 ++++++++----------- test/test.ts | 22 +++++++++------------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 5dea6a9..9c9557c 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ The message is delivered into the child's TUI editor (newlines are flattened to subagent_message({ sessionId: "019f05b2-f1c3", message: "Now write the tests too" }); ``` -Resuming is fire-and-forget async: the relaunched session's result is delivered later as a steer message, exactly like a fresh `subagent` spawn. +Resuming is fire-and-forget async: the relaunched session always runs its follow-up task autonomously and its result is delivered later as a steer message, exactly like a fresh `subagent` spawn. (There is no per-call behavior knob — resume is always autonomous, matching this result-delivery model.) > **Guard:** a `sessionId` that maps to a still-running subagent is rejected — resuming would launch a second process mutating the same session file. Steer it by `name` instead. There is no hard-abort tool; to forcibly stop a subagent, use its pane directly. @@ -197,7 +197,6 @@ Resuming is fire-and-forget async: the relaunched session's result is delivered - `name` — exact display name of a running subagent to steer (mutually exclusive with `sessionId`) - `sessionId` — id (or id prefix) of a finished session to resume (mutually exclusive with `name`) - `message` (required) — the instruction or next task to deliver -- `autoExit` (resume only) — whether the resumed session auto-exits after its next response. Defaults to `true`; set `false` for an interactive handoff. --- diff --git a/pi-extension/subagents/index.ts b/pi-extension/subagents/index.ts index b127b24..8e06f69 100644 --- a/pi-extension/subagents/index.ts +++ b/pi-extension/subagents/index.ts @@ -997,9 +997,12 @@ function startStatusRefresh(pi: ExtensionAPI) { (globalThis as any)[STATUS_INTERVAL_KEY] = statusInterval; } -function resolveResumeLaunchBehavior(params: { autoExit?: boolean }): { autoExit: boolean; interactive: boolean } { - const autoExit = params.autoExit ?? true; - return { autoExit, interactive: !autoExit }; +// Resuming a finished session is always autonomous: the relaunched agent runs +// its follow-up task to completion and the harness delivers the result as a +// steer message (fire-and-forget). An interactive resume would park the pane +// waiting for the user, contradicting that result-delivery model. +function resolveResumeLaunchBehavior(): { autoExit: boolean; interactive: boolean } { + return { autoExit: true, interactive: false }; } export const __test__ = { @@ -1889,12 +1892,6 @@ export default function subagentsExtension(pi: ExtensionAPI) { description: "The message to deliver: a follow-up instruction for a running subagent, or the next task for a resumed session.", }), - autoExit: Type.Optional( - Type.Boolean({ - description: - "Resume only: whether the resumed session should automatically exit after completing its response. Defaults to true for autonomous follow-up work; set false for interactive resumed sessions.", - }), - ), }), renderCall(args, theme) { @@ -1958,8 +1955,8 @@ export default function subagentsExtension(pi: ExtensionAPI) { // ── Resume a finished session ── const requestedId = params.sessionId!.trim(); const message = params.message; - const name = params.name ?? "Resume"; - const { autoExit, interactive } = resolveResumeLaunchBehavior(params); + const name = "Resume"; + const { autoExit, interactive } = resolveResumeLaunchBehavior(); const startTime = Date.now(); const id = Math.random().toString(16).slice(2, 10); diff --git a/test/test.ts b/test/test.ts index 096e799..038c435 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1474,17 +1474,13 @@ describe("commands", () => { }); describe("tool registration", () => { - it("defaults resumed subagents to auto-exit and non-interactive tracking", () => { + it("always resumes subagents as autonomous (auto-exit, non-interactive tracking)", () => { const testApi = (subagentsModule as any).__test__; - assert.deepEqual(testApi.resolveResumeLaunchBehavior({}), { + assert.deepEqual(testApi.resolveResumeLaunchBehavior(), { autoExit: true, interactive: false, }); - assert.deepEqual(testApi.resolveResumeLaunchBehavior({ autoExit: false }), { - autoExit: false, - interactive: true, - }); }); @@ -1561,7 +1557,7 @@ describe("tool registration", () => { assert.match(output, /\(unnamed\)/); }); - it("registers subagent_message with name, sessionId, message, and an autoExit override", () => { + it("registers subagent_message with only name, sessionId, and required message (no autoExit knob)", () => { const { api, registeredTools } = createMockExtensionApi(); (subagentsModule as any).default(api); @@ -1569,14 +1565,14 @@ describe("tool registration", () => { assert.ok(messageTool, "expected subagent_message tool to be registered"); const props = messageTool.parameters.properties; - assert.ok(props.name, "expected name param"); - assert.ok(props.sessionId, "expected sessionId param"); + assert.deepEqual( + Object.keys(props).sort(), + ["message", "name", "sessionId"], + "only name/sessionId/message should remain", + ); assert.equal(props.message.type, "string"); assert.equal(messageTool.parameters.required?.includes("message"), true); - - const autoExitSchema = props.autoExit; - assert.equal(autoExitSchema.type, "boolean"); - assert.match(autoExitSchema.description, /Defaults to true/); + assert.equal(props.autoExit, undefined, "autoExit knob should be removed"); }); it("no longer registers subagent_interrupt or subagent_resume", () => {