@@ -7,11 +7,13 @@ describe("pi_provider.cjs", () => {
77 let module ;
88 let originalEnv ;
99 let originalFetch ;
10+ let originalExitCode ;
1011 let stderrOutput ;
1112
1213 beforeEach ( async ( ) => {
1314 originalEnv = { ...process . env } ;
1415 originalFetch = global . fetch ;
16+ originalExitCode = process . exitCode ;
1517 stderrOutput = [ ] ;
1618 vi . spyOn ( process . stderr , "write" ) . mockImplementation ( msg => {
1719 stderrOutput . push ( String ( msg ) ) ;
@@ -23,6 +25,7 @@ describe("pi_provider.cjs", () => {
2325 afterEach ( ( ) => {
2426 process . env = originalEnv ;
2527 global . fetch = originalFetch ;
28+ process . exitCode = originalExitCode ;
2629 vi . restoreAllMocks ( ) ;
2730 } ) ;
2831
@@ -116,6 +119,127 @@ describe("pi_provider.cjs", () => {
116119 expect ( stderrOutput . some ( line => line . includes ( "provider_response provider=copilot model=claude-sonnet-4 status=503 method=POST url=http://api-proxy:10002/v1/chat/completions response_headers=content-type,x-request-id" ) ) ) . toBe ( true ) ;
117120 } ) ;
118121
122+ it ( "resolves native Anthropic requests to the Messages API" , ( ) => {
123+ expect (
124+ module . resolveProviderRequestTarget ( {
125+ api : "anthropic-messages" ,
126+ baseUrl : "http://api-proxy:10001" ,
127+ } )
128+ ) . toEqual ( {
129+ api : "anthropic-messages" ,
130+ method : "POST" ,
131+ url : "http://api-proxy:10001/v1/messages" ,
132+ } ) ;
133+ } ) ;
134+
135+ it ( "keeps legacy Anthropic requests on the compatibility endpoint" , ( ) => {
136+ expect (
137+ module . resolveProviderRequestTarget ( {
138+ api : "anthropic" ,
139+ baseUrl : "http://anthropic.example.test" ,
140+ } )
141+ ) . toEqual ( {
142+ api : "anthropic" ,
143+ method : "POST" ,
144+ url : "http://anthropic.example.test/messages" ,
145+ } ) ;
146+ } ) ;
147+
148+ it ( "fails the run when every provider request fails" , async ( ) => {
149+ const tempDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "pi-provider-" ) ) ;
150+ process . env . GH_AW_SAFE_OUTPUTS = path . join ( tempDir , "outputs.jsonl" ) ;
151+ process . env . GH_AW_SAFEOUTPUTS_CLI = "true" ;
152+
153+ const handlers = { } ;
154+ const pi = {
155+ registerProvider : vi . fn ( ) ,
156+ on : vi . fn ( ( event , handler ) => {
157+ handlers [ event ] = handler ;
158+ } ) ,
159+ } ;
160+ const ctx = {
161+ model : {
162+ provider : "aw-gateway" ,
163+ id : "claude-sonnet-5" ,
164+ api : "anthropic-messages" ,
165+ baseUrl : "http://api-proxy:10001" ,
166+ } ,
167+ } ;
168+
169+ module . default ( pi ) ;
170+ await handlers . before_provider_request ( { type : "before_provider_request" , payload : { } } , ctx ) ;
171+ await handlers . after_provider_response ( { type : "after_provider_response" , status : 404 , headers : { } } , ctx ) ;
172+ await handlers . agent_end ( ) ;
173+
174+ expect ( process . exitCode ) . toBe ( 1 ) ;
175+ expect ( stderrOutput . some ( line => line . includes ( "report_incomplete emitted via safeoutputs CLI" ) ) ) . toBe ( true ) ;
176+ } ) ;
177+
178+ it ( "does not fail the run when a provider request succeeds after a failure" , async ( ) => {
179+ const handlers = { } ;
180+ const pi = {
181+ registerProvider : vi . fn ( ) ,
182+ on : vi . fn ( ( event , handler ) => {
183+ handlers [ event ] = handler ;
184+ } ) ,
185+ } ;
186+ const ctx = {
187+ model : {
188+ provider : "aw-gateway" ,
189+ id : "claude-sonnet-5" ,
190+ api : "anthropic-messages" ,
191+ baseUrl : "http://api-proxy:10001" ,
192+ } ,
193+ } ;
194+
195+ module . default ( pi ) ;
196+ await handlers . before_provider_request ( { type : "before_provider_request" , payload : { } } , ctx ) ;
197+ await handlers . after_provider_response ( { type : "after_provider_response" , status : 503 , headers : { } } , ctx ) ;
198+ await handlers . before_provider_request ( { type : "before_provider_request" , payload : { } } , ctx ) ;
199+ await handlers . after_provider_response ( { type : "after_provider_response" , status : 200 , headers : { } } , ctx ) ;
200+ await handlers . agent_end ( ) ;
201+
202+ expect ( process . exitCode ) . toBe ( originalExitCode ) ;
203+ } ) ;
204+
205+ it ( "fails the run when a successful response ends with a stream error" , async ( ) => {
206+ const tempDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "pi-provider-" ) ) ;
207+ process . env . GH_AW_SAFE_OUTPUTS = path . join ( tempDir , "outputs.jsonl" ) ;
208+ process . env . GH_AW_SAFEOUTPUTS_CLI = "true" ;
209+
210+ const handlers = { } ;
211+ const pi = {
212+ registerProvider : vi . fn ( ) ,
213+ on : vi . fn ( ( event , handler ) => {
214+ handlers [ event ] = handler ;
215+ } ) ,
216+ } ;
217+ const ctx = {
218+ model : {
219+ provider : "aw-gateway" ,
220+ id : "claude-sonnet-5" ,
221+ api : "anthropic-messages" ,
222+ baseUrl : "http://api-proxy:10001" ,
223+ } ,
224+ } ;
225+
226+ module . default ( pi ) ;
227+ await handlers . before_provider_request ( { } , ctx ) ;
228+ await handlers . after_provider_response ( { status : 200 , headers : { } } , ctx ) ;
229+ await handlers . message_end ( {
230+ message : {
231+ role : "assistant" ,
232+ provider : "aw-gateway" ,
233+ model : "claude-sonnet-5" ,
234+ stopReason : "error" ,
235+ errorMessage : "stream interrupted" ,
236+ } ,
237+ } ) ;
238+ await handlers . agent_end ( ) ;
239+
240+ expect ( process . exitCode ) . toBe ( 1 ) ;
241+ } ) ;
242+
119243 // Triggers the message_end infrastructure-error handler with a given stand-in
120244 // GH_AW_SAFEOUTPUTS_CLI override ('true' simulates a successful CLI call, 'false'
121245 // simulates a failed one) and returns the handlers/stderr output for assertions.
@@ -154,6 +278,7 @@ describe("pi_provider.cjs", () => {
154278 errorMessage : "Connection error." ,
155279 } ,
156280 } ) ;
281+ await handlers . agent_end ( ) ;
157282 }
158283
159284 it ( "logs assistant inference errors with the last request target" , async ( ) => {
0 commit comments