@@ -43,6 +43,7 @@ import {
4343import { GenericACPAgentClient } from "./generic-acp-agent.js" ;
4444import { transformPiModels } from "./pi/agent.js" ;
4545import type { AgentStreamEvent } from "../agent-sdk-types.js" ;
46+ import type { AgentCapabilityFlags , AgentPersistenceHandle } from "../agent-sdk-types.js" ;
4647import { createTestLogger } from "../../../test-utils/test-logger.js" ;
4748import { asInternals } from "../../test-utils/class-mocks.js" ;
4849import * as spawnUtils from "../../../utils/spawn.js" ;
@@ -2453,8 +2454,24 @@ describe("ACPAgentClient probe cleanup", () => {
24532454} ) ;
24542455
24552456describe ( "ACP session/load invariant — cwd and mcpServers always passed" , ( ) => {
2456- test ( "loadSession is always called with sessionId, cwd, and mcpServers even when mcpServers is empty" , async ( ) => {
2457- const loadSession = vi . fn ( ) . mockResolvedValue ( {
2457+ /**
2458+ * Shared factory: creates an ACPAgentSession subclass whose spawnProcess
2459+ * returns stubbed ACP internals so tests can inspect connection method calls
2460+ * without spawning real processes. Each call produces fresh vi.fn() stubs.
2461+ */
2462+ function makeTestSession ( args : {
2463+ capabilities ?: AgentCapabilityFlags ;
2464+ handle : AgentPersistenceHandle ;
2465+ loadSession ?: ReturnType < typeof vi . fn > ;
2466+ unstableResumeSession ?: ReturnType < typeof vi . fn > ;
2467+ } ) {
2468+ const loadSession = args . loadSession ?? vi . fn ( ) . mockResolvedValue ( {
2469+ sessionId : "session-1" ,
2470+ modes : null ,
2471+ models : null ,
2472+ configOptions : [ ] ,
2473+ } ) ;
2474+ const unstableResumeSession = args . unstableResumeSession ?? vi . fn ( ) . mockResolvedValue ( {
24582475 sessionId : "session-1" ,
24592476 modes : null ,
24602477 models : null ,
@@ -2468,17 +2485,16 @@ describe("ACP session/load invariant — cwd and mcpServers always passed", () =
24682485 connection : {
24692486 prompt : vi . fn ( ) ,
24702487 loadSession,
2471- } ,
2472- initialize : { agentCapabilities : { loadSession : true } } ,
2473- } as unknown as SpawnedACPProcess ;
2488+ unstable_resumeSession : unstableResumeSession ,
2489+ } as unknown as ClientSideConnection ,
2490+ initialize : { agentCapabilities : args . capabilities ?? { } } ,
2491+ } as SpawnedACPProcess ;
24742492 }
24752493 }
24762494
2495+ // Pass handle through the typed constructor option (no private-field casts).
24772496 const session = new TestSession (
2478- {
2479- provider : "claude-acp" ,
2480- cwd : "/tmp/paseo-acp-test" ,
2481- } ,
2497+ { provider : "claude-acp" , cwd : "/tmp/paseo-acp-test" } ,
24822498 {
24832499 provider : "claude-acp" ,
24842500 logger : createTestLogger ( ) ,
@@ -2491,14 +2507,20 @@ describe("ACP session/load invariant — cwd and mcpServers always passed", () =
24912507 supportsMcpServers : true ,
24922508 supportsReasoningStream : true ,
24932509 supportsToolInvocations : true ,
2510+ ...args . capabilities ,
24942511 } ,
2512+ handle : args . handle ,
24952513 } ,
24962514 ) ;
2497- // Provide the persistence handle that initializeResumedSession requires
2498- ( session as unknown as { initialHandle : unknown } ) . initialHandle = {
2499- sessionId : "session-1" ,
2500- provider : "claude-acp" ,
2501- } ;
2515+
2516+ return { session, loadSession, unstableResumeSession } ;
2517+ }
2518+
2519+ test ( "loadSession is always called with sessionId, cwd, and mcpServers even when mcpServers is empty" , async ( ) => {
2520+ const { session, loadSession } = makeTestSession ( {
2521+ capabilities : { loadSession : true , supportsMcpServers : true } ,
2522+ handle : { sessionId : "session-1" , provider : "claude-acp" } ,
2523+ } ) ;
25022524
25032525 await session . initializeResumedSession ( ) ;
25042526
@@ -2510,51 +2532,11 @@ describe("ACP session/load invariant — cwd and mcpServers always passed", () =
25102532 } ) ;
25112533
25122534 test ( "loadSession is always called with mcpServers even when supportsMcpServers is false" , async ( ) => {
2513- const loadSession = vi . fn ( ) . mockResolvedValue ( {
2514- sessionId : "session-1" ,
2515- modes : null ,
2516- models : null ,
2517- configOptions : [ ] ,
2535+ const { session, loadSession } = makeTestSession ( {
2536+ capabilities : { loadSession : true , supportsMcpServers : false } ,
2537+ handle : { sessionId : "session-1" , provider : "claude-acp" } ,
25182538 } ) ;
25192539
2520- class TestSession extends ACPAgentSession {
2521- protected override async spawnProcess ( ) : Promise < SpawnedACPProcess > {
2522- return {
2523- child : createProbeChildStub ( ) ,
2524- connection : {
2525- prompt : vi . fn ( ) ,
2526- loadSession,
2527- } ,
2528- initialize : { agentCapabilities : { loadSession : true } } ,
2529- } as unknown as SpawnedACPProcess ;
2530- }
2531- }
2532-
2533- const session = new TestSession (
2534- {
2535- provider : "claude-acp" ,
2536- cwd : "/tmp/paseo-acp-test" ,
2537- } ,
2538- {
2539- provider : "claude-acp" ,
2540- logger : createTestLogger ( ) ,
2541- defaultCommand : [ "claude" , "--acp" ] ,
2542- defaultModes : [ ] ,
2543- capabilities : {
2544- supportsStreaming : true ,
2545- supportsSessionPersistence : true ,
2546- supportsDynamicModes : true ,
2547- supportsMcpServers : false ,
2548- supportsReasoningStream : true ,
2549- supportsToolInvocations : true ,
2550- } ,
2551- } ,
2552- ) ;
2553- ( session as unknown as { initialHandle : unknown } ) . initialHandle = {
2554- sessionId : "session-1" ,
2555- provider : "claude-acp" ,
2556- } ;
2557-
25582540 await session . initializeResumedSession ( ) ;
25592541
25602542 // Even with supportsMcpServers=false, mcpServers: [] must still be passed
@@ -2566,53 +2548,11 @@ describe("ACP session/load invariant — cwd and mcpServers always passed", () =
25662548 } ) ;
25672549
25682550 test ( "unstable_resumeSession is always called with sessionId, cwd, and mcpServers" , async ( ) => {
2569- const unstableResumeSession = vi . fn ( ) . mockResolvedValue ( {
2570- sessionId : "session-1" ,
2571- modes : null ,
2572- models : null ,
2573- configOptions : [ ] ,
2551+ const { session, unstableResumeSession } = makeTestSession ( {
2552+ capabilities : { sessionCapabilities : { resume : { } } } ,
2553+ handle : { sessionId : "session-1" , provider : "claude-acp" } ,
25742554 } ) ;
25752555
2576- class TestSession extends ACPAgentSession {
2577- protected override async spawnProcess ( ) : Promise < SpawnedACPProcess > {
2578- return {
2579- child : createProbeChildStub ( ) ,
2580- connection : {
2581- prompt : vi . fn ( ) ,
2582- unstable_resumeSession : unstableResumeSession ,
2583- } ,
2584- initialize : {
2585- agentCapabilities : { sessionCapabilities : { resume : { } } } ,
2586- } ,
2587- } as unknown as SpawnedACPProcess ;
2588- }
2589- }
2590-
2591- const session = new TestSession (
2592- {
2593- provider : "claude-acp" ,
2594- cwd : "/tmp/paseo-acp-test" ,
2595- } ,
2596- {
2597- provider : "claude-acp" ,
2598- logger : createTestLogger ( ) ,
2599- defaultCommand : [ "claude" , "--acp" ] ,
2600- defaultModes : [ ] ,
2601- capabilities : {
2602- supportsStreaming : true ,
2603- supportsSessionPersistence : true ,
2604- supportsDynamicModes : true ,
2605- supportsMcpServers : true ,
2606- supportsReasoningStream : true ,
2607- supportsToolInvocations : true ,
2608- } ,
2609- } ,
2610- ) ;
2611- ( session as unknown as { initialHandle : unknown } ) . initialHandle = {
2612- sessionId : "session-1" ,
2613- provider : "claude-acp" ,
2614- } ;
2615-
26162556 await session . initializeResumedSession ( ) ;
26172557
26182558 expect ( unstableResumeSession ) . toHaveBeenCalledWith ( {
0 commit comments