@@ -5,8 +5,14 @@ import { CODEBUFF_BINARY } from './constants'
55import { changeFile } from './tools/change-file'
66import { WebSocketHandler } from './websocket-client'
77import { API_KEY_ENV_VAR } from '../../common/src/constants'
8- import type { ServerAction } from '../../common/src/actions'
9- import { getInitialSessionState } from '../../common/src/types/session-state'
8+ import {
9+ PromptResponseSchema ,
10+ type ServerAction ,
11+ } from '../../common/src/actions'
12+ import {
13+ getInitialSessionState ,
14+ SessionState ,
15+ } from '../../common/src/types/session-state'
1016import { getFiles } from '../../npm-app/src/project-files'
1117
1218export type ClientToolName =
@@ -30,11 +36,20 @@ export type CodebuffClientOptions = {
3036 }
3137}
3238
39+ type RunState = {
40+ sessionState : SessionState
41+ toolResults : Extract < ServerAction , { type : 'prompt-response' } > [ 'toolResults' ]
42+ }
43+
3344export class CodebuffClient {
45+ public cwd : string
3446 private readonly websocketHandler : WebSocketHandler
3547 private readonly overrideTools : CodebuffClientOptions [ 'overrideTools' ]
3648 private readonly fingerprintId = `codebuff-sdk-${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) } `
37- public cwd : string
49+ private readonly promptIdToResolveResponse : Record <
50+ string ,
51+ { resolve : ( response : any ) => void ; reject : ( error : any ) => void }
52+ > = { }
3853
3954 constructor ( { cwd, onError, overrideTools } : CodebuffClientOptions ) {
4055 // TODO: download binary automatically
@@ -68,45 +83,103 @@ export class CodebuffClient {
6883 onResponseChunk : async ( ) => { } ,
6984 onSubagentResponseChunk : async ( ) => { } ,
7085
71- onPromptResponse : async ( ) => { } ,
86+ onPromptResponse : this . handlePromptResponse . bind ( this ) ,
7287 } )
7388 }
7489
75- public async runNewChat ( {
90+ /**
91+ * Run an agent.
92+ *
93+ * Pass an agent id, a prompt, and an event handler, plus options.
94+ *
95+ * Returns the state of the run, which can be passed to a subsequent run to continue the run.
96+ *
97+ * @param agent - The agent to run, e.g. 'base' or 'codebuff/file-picker@0.0.1'
98+ * @param prompt - The user prompt, e.g. 'Add a console.log to the index file'
99+ * @param params - (Optional) The parameters to pass to the agent.
100+ * @param handleEvent - A function to handle events.
101+ *
102+ * @param previousState - (Optional) Continue a previous run with the return value of a previous run.
103+ *
104+ * @param allFiles - (Optional) All the files in the project, in an object of file path to file content. Improves codebuff's ability to locate files.
105+ * @param knowledgeFiles - (Optional) The knowledge files to pass to the agent.
106+ * @param agentTemplates - (Optional) The agent templates to pass to the agent.
107+ * @param maxAgentSteps - (Optional) The maximum number of agent steps the main agent can run before stopping.
108+ */
109+ public async run ( {
76110 agent,
77111 prompt,
78112 params,
79113 handleEvent,
114+ previousState,
80115 allFiles,
81116 knowledgeFiles,
82- agentTemplates,
117+ agentConfig,
118+ maxAgentSteps,
83119 } : {
84120 agent : string
85121 prompt : string
86122 params ?: Record < string , any >
87123 handleEvent : ( event : any ) => void
124+ previousState ?: RunState
88125 allFiles ?: Record < string , string >
89126 knowledgeFiles ?: Record < string , string >
90- agentTemplates ?: Record < string , any >
91- } ) : Promise < {
92- agentId : string
93- } > {
127+ agentConfig ?: Record < string , any >
128+ maxAgentSteps ?: number
129+ } ) : Promise < RunState > {
130+ const promptId = Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 )
131+ const sessionState =
132+ previousState ?. sessionState ??
133+ initialSessionState ( this . cwd , {
134+ knowledgeFiles,
135+ agentConfig,
136+ allFiles,
137+ maxAgentSteps,
138+ } )
139+ const toolResults = previousState ?. toolResults ?? [ ]
94140 this . websocketHandler . sendInput ( {
95- promptId : Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) ,
141+ promptId,
96142 prompt,
97143 promptParams : params ,
98144 fingerprintId : this . fingerprintId ,
99145 costMode : 'normal' ,
100- sessionState : initialSessionState ( this . cwd , {
101- knowledgeFiles,
102- agentTemplates,
103- allFiles,
104- } ) ,
105- toolResults : [ ] ,
146+ sessionState,
147+ toolResults,
106148 agentId : agent ,
107149 } )
108150
109- return new Promise ( ( resolve ) => { } )
151+ return new Promise < RunState > ( ( resolve , reject ) => {
152+ this . promptIdToResolveResponse [ promptId ] = { resolve, reject }
153+ } )
154+ }
155+
156+ private async handlePromptResponse (
157+ action : Extract < ServerAction , { type : 'prompt-response' } > ,
158+ ) {
159+ const promiseActions =
160+ this . promptIdToResolveResponse [ action ?. promptId ?? '' ]
161+
162+ const parsedAction = PromptResponseSchema . safeParse ( action )
163+ if ( ! parsedAction . success ) {
164+ const message = [
165+ 'Received invalid prompt response from server:' ,
166+ JSON . stringify ( parsedAction . error . errors ) ,
167+ 'If this issues persists, please contact support@codebuff.com' ,
168+ ] . join ( '\n' )
169+ if ( promiseActions ) {
170+ promiseActions . reject ( new Error ( message ) )
171+ }
172+ return
173+ }
174+
175+ if ( promiseActions ) {
176+ const { sessionState, toolResults } = action
177+ const state : RunState = {
178+ sessionState,
179+ toolResults,
180+ }
181+ promiseActions . resolve ( state )
182+ }
110183 }
111184
112185 private async readFiles ( filePath : string [ ] ) {
@@ -164,25 +237,27 @@ export class CodebuffClient {
164237 }
165238 }
166239}
240+
167241function initialSessionState (
168242 cwd : string ,
169243 options : {
170244 allFiles ?: Record < string , string >
171245 knowledgeFiles ?: Record < string , string >
172- agentTemplates ?: Record < string , any >
246+ agentConfig ?: Record < string , any >
247+ maxAgentSteps ?: number
173248 } ,
174249) {
175- const { knowledgeFiles = { } , agentTemplates = { } } = options
250+ const { knowledgeFiles = { } , agentConfig = { } } = options
176251
177- return getInitialSessionState ( {
252+ const initialState = getInitialSessionState ( {
178253 projectRoot : cwd ,
179254 cwd,
180255 fileTree : [ ] ,
181256 fileTokenScores : { } ,
182257 tokenCallers : { } ,
183258 knowledgeFiles,
184259 userKnowledgeFiles : { } ,
185- agentTemplates,
260+ agentTemplates : agentConfig ,
186261 gitChanges : {
187262 status : '' ,
188263 diff : '' ,
@@ -200,4 +275,10 @@ function initialSessionState(
200275 cpus : 16 ,
201276 } ,
202277 } )
278+
279+ if ( options . maxAgentSteps ) {
280+ initialState . mainAgentState . stepsRemaining = options . maxAgentSteps
281+ }
282+
283+ return initialState
203284}
0 commit comments