@@ -24,6 +24,8 @@ import {
2424import { mockFileContext , MockWebSocket } from './test-utils'
2525import * as toolExecutor from '../tools/tool-executor'
2626import * as requestContext from '../websockets/request-context'
27+ import * as agentRun from '../agent-run'
28+ import * as websocketAction from '../websockets/websocket-action'
2729
2830import type { AgentTemplate , StepGenerator } from '../templates/types'
2931import type { PublicAgentState } from '@codebuff/common/types/agent-template'
@@ -40,6 +42,8 @@ describe('runProgrammaticStep', () => {
4042 let mockParams : any
4143 let executeToolCallSpy : any
4244 let getRequestContextSpy : any
45+ let addAgentStepSpy : any
46+ let sendActionSpy : any
4347
4448 beforeAll ( ( ) => {
4549 // Mock logger
@@ -74,6 +78,17 @@ describe('runProgrammaticStep', () => {
7478 processedRepoId : 'test-repo-id' ,
7579 } ) )
7680
81+ // Mock addAgentStep
82+ addAgentStepSpy = spyOn ( agentRun , 'addAgentStep' ) . mockImplementation (
83+ async ( ) => 'test-step-id' ,
84+ )
85+
86+ // Mock sendAction
87+ sendActionSpy = spyOn (
88+ websocketAction ,
89+ 'sendAction' ,
90+ ) . mockImplementation ( ( ) => { } )
91+
7792 // Mock crypto.randomUUID
7893 spyOn ( crypto , 'randomUUID' ) . mockImplementation (
7994 ( ) =>
@@ -103,11 +118,14 @@ describe('runProgrammaticStep', () => {
103118 mockAgentState = {
104119 ...sessionState . mainAgentState ,
105120 agentId : 'test-agent-id' ,
121+ runId : 'test-run-id' as `${string } -${string } -${string } -${string } -${string } `,
106122 messageHistory : [
107123 { role : 'user' , content : 'Initial message' } ,
108124 { role : 'assistant' , content : 'Initial response' } ,
109125 ] ,
110126 output : undefined ,
127+ directCreditsUsed : 0 ,
128+ childRunIds : [ ] ,
111129 }
112130
113131 // Create mock params
@@ -124,6 +142,9 @@ describe('runProgrammaticStep', () => {
124142 assistantMessage : undefined ,
125143 assistantPrefix : undefined ,
126144 ws : new MockWebSocket ( ) as unknown as WebSocket ,
145+ localAgentTemplates : { } ,
146+ stepsComplete : false ,
147+ stepNumber : 1 ,
127148 }
128149 } )
129150
@@ -184,9 +205,10 @@ describe('runProgrammaticStep', () => {
184205 expect ( result1 . endTurn ) . toBe ( false )
185206
186207 // Second call should return early due to STEP_ALL state
187- const result2 = await runProgrammaticStep ( mockAgentState , mockParams )
208+ // Use the same agent state with the same runId
209+ const result2 = await runProgrammaticStep ( result1 . agentState , mockParams )
188210 expect ( result2 . endTurn ) . toBe ( false )
189- expect ( result2 . agentState ) . toEqual ( mockAgentState )
211+ expect ( result2 . agentState . agentId ) . toEqual ( result1 . agentState . agentId )
190212 } )
191213
192214 it ( 'should throw error when template has no handleStep' , async ( ) => {
@@ -215,12 +237,7 @@ describe('runProgrammaticStep', () => {
215237
216238 // Track chunks sent via sendSubagentChunk
217239 const sentChunks : string [ ] = [ ]
218- const originalSendAction =
219- require ( '../websockets/websocket-action' ) . sendAction
220- const sendActionSpy = spyOn (
221- require ( '../websockets/websocket-action' ) ,
222- 'sendAction' ,
223- ) . mockImplementation ( ( ws : any , action : any ) => {
240+ sendActionSpy . mockImplementation ( ( ws : any , action : any ) => {
224241 if ( action . type === 'subagent-response-chunk' ) {
225242 sentChunks . push ( action . chunk )
226243 }
@@ -619,7 +636,7 @@ describe('runProgrammaticStep', () => {
619636 // Verify STEP_ALL behavior
620637 expect ( executeToolCallSpy ) . not . toHaveBeenCalled ( ) // No tools should execute
621638 expect ( result2 . endTurn ) . toBe ( false ) // Should still not end turn
622- expect ( result2 . agentState ) . toEqual ( result1 . agentState ) // State should be unchanged
639+ expect ( result2 . agentState . agentId ) . toEqual ( result1 . agentState . agentId ) // State should be similar
623640 expect ( stepCount ) . toBe ( 1 ) // Generator should not have run again
624641
625642 // Third call - verify STEP_ALL state persists
@@ -629,7 +646,7 @@ describe('runProgrammaticStep', () => {
629646
630647 expect ( executeToolCallSpy ) . not . toHaveBeenCalled ( )
631648 expect ( result3 . endTurn ) . toBe ( false )
632- expect ( result3 . agentState ) . toEqual ( result1 . agentState )
649+ expect ( result3 . agentState . agentId ) . toEqual ( result1 . agentState . agentId )
633650 expect ( stepCount ) . toBe ( 1 ) // Generator should still not have run again
634651 } )
635652
@@ -1120,7 +1137,7 @@ describe('runProgrammaticStep', () => {
11201137 expect ( generatorCallCount ) . toBe ( 1 )
11211138
11221139 // Second call with stepsComplete=false should return early due to STEP_ALL
1123- const result2 = await runProgrammaticStep ( mockAgentState , {
1140+ const result2 = await runProgrammaticStep ( result1 . agentState , {
11241141 ...mockParams ,
11251142 stepsComplete : false ,
11261143 } )
@@ -1134,7 +1151,7 @@ describe('runProgrammaticStep', () => {
11341151 }
11351152 } )
11361153
1137- const result3 = await runProgrammaticStep ( mockAgentState , {
1154+ const result3 = await runProgrammaticStep ( result2 . agentState , {
11381155 ...mockParams ,
11391156 stepsComplete : true ,
11401157 } )
0 commit comments