@@ -6,7 +6,8 @@ import { fileURLToPath } from "node:url";
66import * as acp from "@agentclientprotocol/sdk" ;
77import type { Model } from "@earendil-works/pi-ai" ;
88import { fauxAssistantMessage , fauxToolCall , registerFauxProvider } from "@earendil-works/pi-ai" ;
9- import { afterEach , beforeEach , describe , expect , it } from "vitest" ;
9+ import { afterEach , beforeEach , describe , expect , it , vi } from "vitest" ;
10+ import { CredentialsStore } from "../auth/credentials.js" ;
1011import { runAcpServer } from "./server.js" ;
1112
1213const MOCK_MCP = fileURLToPath ( new URL ( "../mcp/__test__/mock-server.mjs" , import . meta. url ) ) ;
@@ -136,6 +137,50 @@ describe("runAcpServer", () => {
136137 await serverDone ;
137138 } ) ;
138139
140+ it ( "refreshes OAuth credentials before a long-lived server creates a new session" , async ( ) => {
141+ const store = new CredentialsStore ( ) ;
142+ store . save ( {
143+ accessToken : "access-current" ,
144+ refreshToken : "refresh-current" ,
145+ expiresAt : Date . now ( ) + 4 * 60_000 ,
146+ scopes : [ "inference" ] ,
147+ source : "codebase" ,
148+ } ) ;
149+ const fetchSpy = vi . spyOn ( globalThis , "fetch" ) . mockResolvedValueOnce (
150+ new Response ( JSON . stringify ( { access_token : "access-new" , expires_in : 3600 } ) , {
151+ status : 200 ,
152+ headers : { "Content-Type" : "application/json" } ,
153+ } ) ,
154+ ) ;
155+
156+ const toAgent = new PassThrough ( ) ;
157+ const fromAgent = new PassThrough ( ) ;
158+ const serverDone = runAcpServer ( {
159+ stdin : toAgent ,
160+ stdout : fromAgent ,
161+ configOverride : { model, apiKey : "faux-key" , source : "byok" } ,
162+ } ) ;
163+ const stream = acp . ndJsonStream ( Writable . toWeb ( toAgent ) , Readable . toWeb ( fromAgent ) as ReadableStream < Uint8Array > ) ;
164+
165+ await acp . client ( { name : "buzz-refresh-test" } ) . connectWith ( stream , async ( client ) => {
166+ await client . request ( acp . methods . agent . initialize , {
167+ protocolVersion : acp . PROTOCOL_VERSION ,
168+ clientInfo : { name : "buzz-acp" , version : "test" } ,
169+ } ) ;
170+ await client . request ( acp . methods . agent . session . new , {
171+ cwd,
172+ mcpServers : [ ] ,
173+ } ) ;
174+ } ) ;
175+
176+ const refreshCalls = fetchSpy . mock . calls . filter ( ( [ input ] ) => String ( input ) . endsWith ( "/api/oauth/token" ) ) ;
177+ expect ( refreshCalls ) . toHaveLength ( 1 ) ;
178+ expect ( store . load ( ) ?. accessToken ) . toBe ( "access-new" ) ;
179+
180+ toAgent . end ( ) ;
181+ await serverDone ;
182+ } ) ;
183+
139184 it ( "streams provider failures as visible ACP messages" , async ( ) => {
140185 faux . setResponses ( [
141186 fauxAssistantMessage ( [ ] , {
0 commit comments