Skip to content

Commit b18d40c

Browse files
add back selection
1 parent df062fd commit b18d40c

5 files changed

Lines changed: 63 additions & 45 deletions

File tree

packages/mcp/src/index.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import escapeStringRegexp from 'escape-string-regexp';
88
import { z } from 'zod';
99
import { askCodebase, getFileSource, listCommits, listRepos, search } from './client.js';
1010
import { env, numberSchema } from './env.js';
11-
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
12-
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
11+
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
12+
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
1313

1414
const dedent = _dedent.withOptions({ alignValues: true });
1515

@@ -253,26 +253,16 @@ server.tool(
253253
254254
This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
255255
`,
256-
{
257-
question: z.string().describe("The question to ask about the codebase."),
258-
},
259-
async ({
260-
question,
261-
}) => {
262-
const response = await askCodebase({
263-
question,
264-
});
256+
askCodebaseRequestSchema.shape,
257+
async (request: AskCodebaseRequest) => {
258+
const response = await askCodebase(request);
265259

266260
// Format the response with the answer and a link to the chat
267261
const formattedResponse = dedent`
268262
${response.answer}
269263
270264
---
271265
**View full research session:** ${response.chatUrl}
272-
273-
**Sources referenced:** ${response.sources.length} files
274-
**Response time:** ${(response.metadata.totalResponseTimeMs / 1000).toFixed(1)}s
275-
**Model:** ${response.metadata.modelName}
276266
`;
277267

278268
return {

packages/mcp/src/schemas.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,13 @@ export const listCommitsResponseSchema = z.array(z.object({
275275
}));
276276

277277
export const askCodebaseRequestSchema = z.object({
278-
question: z.string().describe("The question to ask about the codebase"),
278+
query: z
279+
.string()
280+
.describe("The query to ask about the codebase."),
281+
repos: z
282+
.array(z.string())
283+
.optional()
284+
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
279285
});
280286

281287
export const sourceSchema = z.object({
@@ -291,12 +297,4 @@ export const askCodebaseResponseSchema = z.object({
291297
answer: z.string().describe("The agent's final answer in markdown format"),
292298
chatId: z.string().describe("ID of the persisted chat session"),
293299
chatUrl: z.string().describe("URL to view the chat in the web UI"),
294-
sources: z.array(sourceSchema).describe("Files the agent referenced during research"),
295-
metadata: z.object({
296-
totalTokens: z.number(),
297-
inputTokens: z.number(),
298-
outputTokens: z.number(),
299-
totalResponseTimeMs: z.number(),
300-
modelName: z.string(),
301-
}).describe("Metadata about the response"),
302300
});

packages/web/src/app/api/(server)/chat/blocking/route.ts

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { sew } from "@/actions";
22
import { _getConfiguredLanguageModelsFull, _getAISDKLanguageModelAndOptions, updateChatMessages, generateAndUpdateChatNameFromMessage } from "@/features/chat/actions";
3-
import { SBChatMessage, Source } from "@/features/chat/types";
3+
import { SBChatMessage, SearchScope } from "@/features/chat/types";
44
import { convertLLMOutputToPortableMarkdown, getAnswerPartFromAssistantMessage } from "@/features/chat/utils";
55
import { ErrorCode } from "@/lib/errorCodes";
6-
import { requestBodySchemaValidationError, ServiceError, serviceErrorResponse } from "@/lib/serviceError";
6+
import { requestBodySchemaValidationError, ServiceError, ServiceErrorException, serviceErrorResponse } from "@/lib/serviceError";
77
import { isServiceError } from "@/lib/utils";
88
import { getBaseUrl } from "@/lib/utils.server";
99
import { withOptionalAuthV2 } from "@/withAuthV2";
@@ -24,8 +24,13 @@ const logger = createLogger('chat-blocking-api');
2424
* This is a simpler interface designed for MCP and other programmatic integrations.
2525
*/
2626
const blockingChatRequestSchema = z.object({
27-
// The question to ask about the codebase
28-
question: z.string().min(1, "Question is required"),
27+
query: z
28+
.string()
29+
.describe("The query to ask about the codebase."),
30+
repos: z
31+
.array(z.string())
32+
.optional()
33+
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
2934
});
3035

3136
/**
@@ -54,7 +59,7 @@ export async function POST(request: Request) {
5459
return serviceErrorResponse(requestBodySchemaValidationError(parsed.error));
5560
}
5661

57-
const { question } = parsed.data;
62+
const { query, repos = [] } = parsed.data;
5863

5964
const response: BlockingChatResponse | ServiceError = await sew(() =>
6065
withOptionalAuthV2(async ({ org, user, prisma }) => {
@@ -88,23 +93,47 @@ export async function POST(request: Request) {
8893
// Run the agent to completion
8994
logger.debug(`Starting blocking agent for chat ${chat.id}`, {
9095
chatId: chat.id,
91-
question: question.substring(0, 100),
96+
query: query.substring(0, 100),
9297
model: modelName,
9398
});
9499

95100
// Create the initial user message
96101
const userMessage: SBChatMessage = {
97102
id: randomUUID(),
98103
role: 'user',
99-
parts: [{ type: 'text', text: question }],
104+
parts: [{ type: 'text', text: query }],
100105
};
101106

107+
const selectedSearchScopes: SearchScope[] = [];
108+
for (const repo of repos) {
109+
const repoDB = await prisma.repo.findFirst({
110+
where: {
111+
name: repo,
112+
},
113+
});
114+
115+
if (!repoDB) {
116+
throw new ServiceErrorException({
117+
statusCode: StatusCodes.BAD_REQUEST,
118+
errorCode: ErrorCode.INVALID_REQUEST_BODY,
119+
message: `Repository '${repo}' not found.`,
120+
})
121+
}
122+
123+
selectedSearchScopes.push({
124+
type: 'repo',
125+
value: repoDB.name,
126+
name: repoDB.displayName ?? repoDB.name.split('/').pop() ?? repoDB.name,
127+
codeHostType: repoDB.external_codeHostType,
128+
})
129+
}
130+
102131
// We'll capture the final messages and usage from the stream
103132
let finalMessages: SBChatMessage[] = [];
104133

105134
const stream = await createMessageStream({
106135
messages: [userMessage],
107-
selectedSearchScopes: [],
136+
selectedSearchScopes,
108137
model,
109138
modelName,
110139
modelProviderOptions: providerOptions,
@@ -122,7 +151,7 @@ export async function POST(request: Request) {
122151
generateAndUpdateChatNameFromMessage({
123152
chatId: chat.id,
124153
languageModelId: languageModelConfig.model,
125-
message: question,
154+
message: query,
126155
})
127156
]);
128157

packages/web/src/app/api/(server)/chat/route.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export const createMessageStream = async ({
182182

183183
const startTime = new Date();
184184

185-
const expandedReposArrays = await Promise.all(selectedSearchScopes.map(async (scope) => {
185+
const expandedRepos = (await Promise.all(selectedSearchScopes.map(async (scope) => {
186186
if (scope.type === 'repo') {
187187
return [scope.value];
188188
}
@@ -204,15 +204,14 @@ export const createMessageStream = async ({
204204
}
205205

206206
return [];
207-
}));
208-
const expandedRepos = expandedReposArrays.flat();
207+
}))).flat()
209208

210209
const researchStream = await createAgentStream({
211210
model,
212211
providerOptions: modelProviderOptions,
213212
inputMessages: messageHistory,
214213
inputSources: sources,
215-
searchScopeRepoNames: expandedRepos,
214+
selectedRepos: expandedRepos,
216215
onWriteSource: (source) => {
217216
writer.write({
218217
type: 'data-source',

packages/web/src/features/chat/agent.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const logger = createLogger('chat-agent');
1717
interface AgentOptions {
1818
model: LanguageModel;
1919
providerOptions?: ProviderOptions;
20-
searchScopeRepoNames: string[];
20+
selectedRepos: string[];
2121
inputMessages: ModelMessage[];
2222
inputSources: Source[];
2323
onWriteSource: (source: Source) => void;
@@ -29,7 +29,7 @@ export const createAgentStream = async ({
2929
providerOptions,
3030
inputMessages,
3131
inputSources,
32-
searchScopeRepoNames,
32+
selectedRepos,
3333
onWriteSource,
3434
traceId,
3535
}: AgentOptions) => {
@@ -59,7 +59,7 @@ export const createAgentStream = async ({
5959
).filter((source) => source !== undefined);
6060

6161
const systemPrompt = createPrompt({
62-
repos: searchScopeRepoNames,
62+
repos: selectedRepos,
6363
files: resolvedFileSources,
6464
});
6565

@@ -69,7 +69,7 @@ export const createAgentStream = async ({
6969
messages: inputMessages,
7070
system: systemPrompt,
7171
tools: {
72-
[toolNames.searchCode]: createCodeSearchTool(searchScopeRepoNames),
72+
[toolNames.searchCode]: createCodeSearchTool(selectedRepos),
7373
[toolNames.readFiles]: readFilesTool,
7474
[toolNames.findSymbolReferences]: findSymbolReferencesTool,
7575
[toolNames.findSymbolDefinitions]: findSymbolDefinitionsTool,
@@ -172,10 +172,12 @@ const createPrompt = ({
172172
During the research phase, use the tools available to you to gather comprehensive context before answering. Always explain why you're using each tool. Depending on the user's question, you may need to use multiple tools. If the question is vague, ask the user for more information.
173173
</research_phase_instructions>
174174
175-
<available_repositories>
176-
The user has selected the following repositories for analysis:
177-
${repos.map(scope => `- ${scope}`).join('\n')}
178-
</available_repositories>
175+
${repos.length > 0 ? dedent`
176+
<selected_repositories>
177+
The user has explicitly selected the following repositories for analysis:
178+
${repos.map(repo => `- ${repo}`).join('\n')}
179+
</selected_repositories>
180+
` : ''}
179181
180182
${files ? dedent`
181183
<files>

0 commit comments

Comments
 (0)