Skip to content
Merged

build #110

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 101 additions & 14 deletions agent/response-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { autumn, type BillingCategory, CREDIT_COSTS } from "@/lib/autumn"
import { db } from "@/lib/db/client"
import { comments, posts } from "@/lib/db/schema"
import { ERROR_CODES } from "@/lib/errors"
import { getTools } from "./tools"
import type { AgentUIMessage } from "./types"
import { getAllTools, getTools } from "./tools"
import type { AgentMode, AgentUIMessage } from "./types"
import { getWorkspace } from "./workspace"

export async function responseAgent({
Expand All @@ -29,6 +29,10 @@ export async function responseAgent({
userId,
billingCategory,
branch,
mode = "ask",
userAccessToken,
userEmail,
userName,
}: {
commentId: string
streamId: string
Expand All @@ -39,6 +43,10 @@ export async function responseAgent({
userId: string
billingCategory: BillingCategory
branch?: string
mode?: AgentMode
userAccessToken?: string | null
userEmail?: string | null
userName?: string | null
}) {
"use workflow"

Expand All @@ -51,6 +59,9 @@ export async function responseAgent({
owner,
repo,
branch,
mode,
userEmail,
userName,
})

let finishReason: FinishReason | undefined
Expand All @@ -69,6 +80,11 @@ export async function responseAgent({
sandboxId,
initialMessages,
newMessages,
mode,
postId,
userAccessToken: userAccessToken ?? undefined,
userEmail: userEmail ?? undefined,
userName: userName ?? undefined,
})
finishReason = result.finishReason
newMessages.push(...result.newMessages)
Expand Down Expand Up @@ -154,12 +170,18 @@ async function setupStep({
owner,
repo,
branch,
mode = "ask",
userEmail,
userName,
}: {
postId: string
commentId: string
owner: string
repo: string
branch?: string
mode?: AgentMode
userEmail?: string | null
userName?: string | null
}): Promise<{
initialMessages: AgentUIMessage[]
sandboxId: string
Expand Down Expand Up @@ -202,6 +224,10 @@ async function setupStep({
const workspace = await getWorkspace({
sandboxId: null,
gitContext: { owner, repo, ref: existingGitContext?.sha ?? branch },
mode,
postId,
userEmail,
userName,
})

if (!existingGitContext) {
Expand All @@ -222,6 +248,49 @@ async function setupStep({
}
}

const ASK_SYSTEM_PROMPT = (owner: string, repo: string) =>
`You're assisting users in a forum about the GitHub repository \`${owner}/${repo}\`.

## Environment
The repo is already cloned and available. All file paths are relative to the workspace root. You can use Read, Grep, and List tools to explore the codebase.

## General Goals
Users might ask you anything, but generally, your goal should be to ground your knowledge with the source code to provide a sourced answer. Users want to get to the source. As you explore source code, you'll note that sometimes, repositories are documented (say, with comments, or markdown files). While that's certainly useful, nothing beats reading the actual source code, as documentation gets stale overtime.

Explore freely but not eagerly: let the user direct you, don't waste your context by being over-eager.`

const BUILD_SYSTEM_PROMPT = (owner: string, repo: string) =>
`You're a developer assistant for the GitHub repository \`${owner}/${repo}\`.

## Environment
The repo is cloned in a temporary sandbox at \`/vercel/sandbox/${repo}\`. You start in this directory. You have full read/write access. All file paths are relative to the workspace root.

## Tools Available
- Read, Grep, List: explore and search the codebase
- Write: create or overwrite files
- Edit: make targeted replacements in existing files
- Bash: run shell commands (git, npm, tests, etc.)

## IMPORTANT: Always Create a PR
The sandbox is temporary - the user can ONLY see your changes if you push them to GitHub. Unless the user explicitly says otherwise, you MUST:
1. Create a feature branch with a unique 5-char suffix: \`git checkout -b forums/short-description-$(openssl rand -hex 3 | head -c5)\`
2. Make the requested changes
3. Commit with a clear message
4. Push and create a PR: \`git push -u origin HEAD && gh pr create --fill\`

Without a PR, your work is invisible and lost when the sandbox ends.

## Git Configuration
- Git is pre-configured: user identity is set to the authenticated user, and credentials are handled automatically.
- \`git push origin HEAD\` and \`gh\` commands just work. Do not modify git config or attempt manual authentication.
- Use \`git status\` to check state before committing
- Keep commits atomic and focused

## Best Practices
- Read relevant files before making changes
- Run tests after changes when applicable
- If tests fail, fix them before creating the PR`

async function streamTextStep({
owner,
repo,
Expand All @@ -231,6 +300,11 @@ async function streamTextStep({
sandboxId,
initialMessages,
newMessages,
mode = "ask",
postId,
userAccessToken,
userEmail,
userName,
}: {
owner: string
repo: string
Expand All @@ -240,6 +314,11 @@ async function streamTextStep({
sandboxId: string
initialMessages: AgentUIMessage[]
newMessages: UIMessage[]
mode?: AgentMode
postId?: string
userAccessToken?: string
userEmail?: string
userName?: string
}): Promise<{
finishReason: FinishReason
newMessages: AgentUIMessage[]
Expand All @@ -251,35 +330,43 @@ async function streamTextStep({
const workspace = await getWorkspace({
sandboxId,
gitContext: { owner, repo, ref: gitRef },
mode,
postId,
userEmail,
userName,
})
const allMessages = [...initialMessages, ...newMessages] as AgentUIMessage[]

const result = streamText({
messages: await convertToModelMessages(allMessages),
tools: getTools({ workspace }),
system: `You're assisting users in a forum about the GitHub repository \`${owner}/${repo}\`.

## Environment
The repo is already cloned and available. All file paths are relative to the workspace root. You can use Read, Grep, and List tools to explore the codebase.
const tools =
mode === "build" && userAccessToken
? getAllTools({ workspace, userAccessToken })
: getTools({ workspace })

## General Goals
Users might ask you anything, but generally, your goal should be to ground your knowledge with the source code to provide a sourced answer. Users want to get to the source. As you explore source code, you'll note that sometimes, repositories are documented (say, with comments, or markdown files). While that's certainly useful, nothing beats reading the actual source code, as documentation gets stale overtime.
const systemPrompt =
mode === "build"
? BUILD_SYSTEM_PROMPT(owner, repo)
: ASK_SYSTEM_PROMPT(owner, repo)

Explore freely but not eagerly: let the user direct you, don't waste your context by being over-eager.`,
const result = streamText({
messages: await convertToModelMessages(allMessages),
tools,
system: systemPrompt,
model,
})

const stepNewMessages: AgentUIMessage[] = []
const isFirstStep = newMessages.length === 0

await result
.toUIMessageStream({
onFinish: ({ messages }) => {
stepNewMessages.push(
...messages.map((m) => {
...messages.map((m, index) => {
return {
...m,
id: nanoid(),
metadata: {},
metadata:
isFirstStep && index === 0 && mode === "build" ? { mode } : {},
} satisfies AgentUIMessage
})
)
Expand Down
Loading