Skip to content

Commit 14173c5

Browse files
committed
sdk: implement run, allow continuing from previous run
1 parent 0e35cad commit 14173c5

File tree

1 file changed

+103
-22
lines changed

1 file changed

+103
-22
lines changed

sdk/src/client.ts

Lines changed: 103 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@ import { CODEBUFF_BINARY } from './constants'
55
import { changeFile } from './tools/change-file'
66
import { WebSocketHandler } from './websocket-client'
77
import { 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'
1016
import { getFiles } from '../../npm-app/src/project-files'
1117

1218
export 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+
3344
export 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+
167241
function 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

Comments
 (0)