diff --git a/packages/coding-agent/src/modes/noninteractive-dispose.test.ts b/packages/coding-agent/src/modes/noninteractive-dispose.test.ts index 2893586e2..bc5c4ebe7 100644 --- a/packages/coding-agent/src/modes/noninteractive-dispose.test.ts +++ b/packages/coding-agent/src/modes/noninteractive-dispose.test.ts @@ -35,6 +35,8 @@ describe("print-mode error exit disposes the session before exit", () => { const session = { extensionRunner: undefined, subscribe: () => {}, + settings: { get: () => false }, + sessionManager: { buildSessionContext: () => ({ messages: [] }), getEntries: () => [] }, state: { messages: [errorMsg] }, getLastAssistantMessage: () => errorMsg, prepareForHeadlessAdvisorDrain: () => {}, diff --git a/packages/coding-agent/src/modes/print-mode.ts b/packages/coding-agent/src/modes/print-mode.ts index a25a19208..3da757e7d 100644 --- a/packages/coding-agent/src/modes/print-mode.ts +++ b/packages/coding-agent/src/modes/print-mode.ts @@ -115,14 +115,12 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti // the first prompt; persisting the mode_change also lets a later interactive // attachment restore and review the generated plan. let abortAfterPlanProposal = false; - const hasConversationContext = session.sessionManager.buildSessionContext().messages.length > 0; - const hasExplicitMode = session.sessionManager.getEntries().some(entry => entry.type === "mode_change"); - if ( - !hasConversationContext && - !hasExplicitMode && + const planDefaultArmed = session.settings.get("plan.defaultOnStartup") && - session.settings.get("plan.enabled") - ) { + session.settings.get("plan.enabled") && + session.sessionManager.buildSessionContext().messages.length === 0 && + !session.sessionManager.getEntries().some(entry => entry.type === "mode_change"); + if (planDefaultArmed) { const planFilePath = session.getPlanReferencePath() || "local://PLAN.md"; const previousTools = session.getEnabledToolNames(); const planTools = session.hasBuiltInTool("write") ? [...new Set([...previousTools, "write"])] : previousTools; diff --git a/packages/coding-agent/test/rpc-stdin-lock.test.ts b/packages/coding-agent/test/rpc-stdin-lock.test.ts index 46b610532..a184e5d3e 100644 --- a/packages/coding-agent/test/rpc-stdin-lock.test.ts +++ b/packages/coding-agent/test/rpc-stdin-lock.test.ts @@ -46,7 +46,12 @@ async function expectRpcModeOwnsStdin(mode: "rpc" | "rpc-ui"): Promise { } const stderr = await stderrPromise; - expect(stderr).not.toContain("ReadableStream is locked"); + // The adversarial fixture is EXPECTED to fail loading — RPC claimed stdin + // first — and its surfaced load notice (#4954) mentions the locked stream. + // Any OTHER "ReadableStream is locked" line means RPC lost stdin ownership. + for (const line of stderr.split("\n").filter(l => l.includes("ReadableStream is locked"))) { + expect(line).toContain("Failed to load extension"); + } expect(stateResponse?.success).toBe(true); } diff --git a/packages/coding-agent/test/silent-abort-print-mode.test.ts b/packages/coding-agent/test/silent-abort-print-mode.test.ts index 75e59c960..32cae2d3f 100644 --- a/packages/coding-agent/test/silent-abort-print-mode.test.ts +++ b/packages/coding-agent/test/silent-abort-print-mode.test.ts @@ -45,8 +45,11 @@ function createMockSession( return { state: { messages }, getLastAssistantMessage: () => messages.findLast(message => message.role === "assistant"), + settings: { get: () => false }, sessionManager: { getHeader: () => undefined, + buildSessionContext: () => ({ messages: [] }), + getEntries: () => [], }, extensionRunner: undefined, subscribe: () => () => {},