@@ -34,14 +34,14 @@ beforeEach(() => {
3434
3535describe ( 'AgentCoreComputeStrategy' , ( ) => {
3636 test ( 'type is agentcore' , ( ) => {
37- const strategy = new AgentCoreComputeStrategy ( { runtimeArn : defaultRuntimeArn } ) ;
37+ const strategy = new AgentCoreComputeStrategy ( ) ;
3838 expect ( strategy . type ) . toBe ( 'agentcore' ) ;
3939 } ) ;
4040
4141 describe ( 'startSession' , ( ) => {
4242 test ( 'invokes agent runtime and returns SessionHandle' , async ( ) => {
4343 mockSend . mockResolvedValueOnce ( { } ) ;
44- const strategy = new AgentCoreComputeStrategy ( { runtimeArn : defaultRuntimeArn } ) ;
44+ const strategy = new AgentCoreComputeStrategy ( ) ;
4545
4646 const handle = await strategy . startSession ( {
4747 taskId : 'TASK001' ,
@@ -55,39 +55,67 @@ describe('AgentCoreComputeStrategy', () => {
5555 expect ( mockSend ) . toHaveBeenCalledTimes ( 1 ) ;
5656 } ) ;
5757
58- test ( 'uses blueprint runtime_arn override ' , async ( ) => {
58+ test ( 'uses runtime_arn from blueprintConfig (single source of truth) ' , async ( ) => {
5959 mockSend . mockResolvedValueOnce ( { } ) ;
60- const strategy = new AgentCoreComputeStrategy ( { runtimeArn : defaultRuntimeArn } ) ;
61- const overrideArn = 'arn:aws:bedrock-agentcore:us-east-1:123:runtime/custom' ;
60+ const strategy = new AgentCoreComputeStrategy ( ) ;
61+ const runtimeArn = 'arn:aws:bedrock-agentcore:us-east-1:123:runtime/custom' ;
6262
6363 const handle = await strategy . startSession ( {
6464 taskId : 'TASK001' ,
6565 payload : { repo_url : 'org/repo' , task_id : 'TASK001' } ,
66- blueprintConfig : { compute_type : 'agentcore' , runtime_arn : overrideArn } ,
66+ blueprintConfig : { compute_type : 'agentcore' , runtime_arn : runtimeArn } ,
6767 } ) ;
6868
69- expect ( handle . metadata . runtimeArn ) . toBe ( overrideArn ) ;
69+ expect ( handle . metadata . runtimeArn ) . toBe ( runtimeArn ) ;
7070 const invokeCall = mockSend . mock . calls [ 0 ] [ 0 ] ;
71- expect ( invokeCall . input . agentRuntimeArn ) . toBe ( overrideArn ) ;
71+ expect ( invokeCall . input . agentRuntimeArn ) . toBe ( runtimeArn ) ;
72+ } ) ;
73+
74+ test ( 'reuses shared BedrockAgentCoreClient across instances' , async ( ) => {
75+ const { BedrockAgentCoreClient } = require ( '@aws-sdk/client-bedrock-agentcore' ) ;
76+ // The lazy singleton may already be initialized from prior tests.
77+ // Record the current call count, then verify no additional constructor calls happen.
78+ const callsBefore = BedrockAgentCoreClient . mock . calls . length ;
79+
80+ mockSend . mockResolvedValue ( { } ) ;
81+ const strategy1 = new AgentCoreComputeStrategy ( ) ;
82+ const strategy2 = new AgentCoreComputeStrategy ( ) ;
83+
84+ await strategy1 . startSession ( {
85+ taskId : 'T1' ,
86+ payload : { } ,
87+ blueprintConfig : { compute_type : 'agentcore' , runtime_arn : defaultRuntimeArn } ,
88+ } ) ;
89+ await strategy2 . startSession ( {
90+ taskId : 'T2' ,
91+ payload : { } ,
92+ blueprintConfig : { compute_type : 'agentcore' , runtime_arn : defaultRuntimeArn } ,
93+ } ) ;
94+
95+ // Lazy singleton: at most one constructor call total across all strategy instances
96+ const callsAfter = BedrockAgentCoreClient . mock . calls . length ;
97+ expect ( callsAfter - callsBefore ) . toBeLessThanOrEqual ( 1 ) ;
98+ expect ( mockSend ) . toHaveBeenCalledTimes ( 2 ) ;
7299 } ) ;
73100 } ) ;
74101
75102 describe ( 'pollSession' , ( ) => {
76- test ( 'returns running status' , async ( ) => {
77- const strategy = new AgentCoreComputeStrategy ( { runtimeArn : defaultRuntimeArn } ) ;
78- const result = await strategy . pollSession ( {
79- sessionId : 'test-session' ,
80- strategyType : 'agentcore' ,
81- metadata : { runtimeArn : defaultRuntimeArn } ,
82- } ) ;
83- expect ( result . status ) . toBe ( 'running' ) ;
103+ test ( 'throws because AgentCore polling is done at orchestrator level' , async ( ) => {
104+ const strategy = new AgentCoreComputeStrategy ( ) ;
105+ await expect (
106+ strategy . pollSession ( {
107+ sessionId : 'test-session' ,
108+ strategyType : 'agentcore' ,
109+ metadata : { runtimeArn : defaultRuntimeArn } ,
110+ } ) ,
111+ ) . rejects . toThrow ( 'pollSession is not implemented for AgentCore' ) ;
84112 } ) ;
85113 } ) ;
86114
87115 describe ( 'stopSession' , ( ) => {
88116 test ( 'sends StopRuntimeSessionCommand' , async ( ) => {
89117 mockSend . mockResolvedValueOnce ( { } ) ;
90- const strategy = new AgentCoreComputeStrategy ( { runtimeArn : defaultRuntimeArn } ) ;
118+ const strategy = new AgentCoreComputeStrategy ( ) ;
91119
92120 await strategy . stopSession ( {
93121 sessionId : 'test-session' ,
@@ -101,9 +129,54 @@ describe('AgentCoreComputeStrategy', () => {
101129 expect ( call . input . runtimeSessionId ) . toBe ( 'test-session' ) ;
102130 } ) ;
103131
104- test ( 'does not throw when stop fails' , async ( ) => {
105- mockSend . mockRejectedValueOnce ( new Error ( 'Access denied' ) ) ;
106- const strategy = new AgentCoreComputeStrategy ( { runtimeArn : defaultRuntimeArn } ) ;
132+ test ( 'logs info for ResourceNotFoundException (session already gone)' , async ( ) => {
133+ const err = new Error ( 'Not found' ) ;
134+ err . name = 'ResourceNotFoundException' ;
135+ mockSend . mockRejectedValueOnce ( err ) ;
136+ const strategy = new AgentCoreComputeStrategy ( ) ;
137+
138+ await expect (
139+ strategy . stopSession ( {
140+ sessionId : 'test-session' ,
141+ strategyType : 'agentcore' ,
142+ metadata : { runtimeArn : defaultRuntimeArn } ,
143+ } ) ,
144+ ) . resolves . toBeUndefined ( ) ;
145+ } ) ;
146+
147+ test ( 'logs error for ThrottlingException' , async ( ) => {
148+ const err = new Error ( 'Rate exceeded' ) ;
149+ err . name = 'ThrottlingException' ;
150+ mockSend . mockRejectedValueOnce ( err ) ;
151+ const strategy = new AgentCoreComputeStrategy ( ) ;
152+
153+ await expect (
154+ strategy . stopSession ( {
155+ sessionId : 'test-session' ,
156+ strategyType : 'agentcore' ,
157+ metadata : { runtimeArn : defaultRuntimeArn } ,
158+ } ) ,
159+ ) . resolves . toBeUndefined ( ) ;
160+ } ) ;
161+
162+ test ( 'logs error for AccessDeniedException' , async ( ) => {
163+ const err = new Error ( 'Access denied' ) ;
164+ err . name = 'AccessDeniedException' ;
165+ mockSend . mockRejectedValueOnce ( err ) ;
166+ const strategy = new AgentCoreComputeStrategy ( ) ;
167+
168+ await expect (
169+ strategy . stopSession ( {
170+ sessionId : 'test-session' ,
171+ strategyType : 'agentcore' ,
172+ metadata : { runtimeArn : defaultRuntimeArn } ,
173+ } ) ,
174+ ) . resolves . toBeUndefined ( ) ;
175+ } ) ;
176+
177+ test ( 'logs warn for unknown errors (best-effort)' , async ( ) => {
178+ mockSend . mockRejectedValueOnce ( new Error ( 'Network error' ) ) ;
179+ const strategy = new AgentCoreComputeStrategy ( ) ;
107180
108181 await expect (
109182 strategy . stopSession ( {
@@ -115,7 +188,7 @@ describe('AgentCoreComputeStrategy', () => {
115188 } ) ;
116189
117190 test ( 'skips stop when no runtimeArn in metadata' , async ( ) => {
118- const strategy = new AgentCoreComputeStrategy ( { runtimeArn : defaultRuntimeArn } ) ;
191+ const strategy = new AgentCoreComputeStrategy ( ) ;
119192
120193 await strategy . stopSession ( {
121194 sessionId : 'test-session' ,
0 commit comments