Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
656 changes: 656 additions & 0 deletions docs/api-reference/sourcebot-public.openapi.json

Large diffs are not rendered by default.

33 changes: 29 additions & 4 deletions docs/docs/features/agents/review-agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,36 @@ By default, the agent does not review PRs and MRs automatically. To enable autom

You can also trigger a review manually by commenting `/review` on any PR or MR. To use a different command, set `REVIEW_AGENT_REVIEW_COMMAND` to your preferred value (without the leading slash).

# Agent configs

Agent configs let you customise how the review agent behaves per repository, connection, or your whole org. You can override the model, review command, auto-review behaviour, custom prompt, and context files — all without changing environment variables.

Configs are managed on the **Agents** page in the Sourcebot UI. Each config has a **scope**:

- **Repo** — applies to specific repositories (highest priority)
- **Connection** — applies to all repos in a specific connection
- **Org** — applies to all repositories in your org (lowest priority, catch-all)
Comment on lines +146 to +148
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace em dashes in bullet list.

Lines 146-148 use em dashes () which are disallowed by the docs style guide. Use a different separator (e.g., a colon or a period after the bolded term).

✏️ Proposed fix
-- **Repo** — applies to specific repositories (highest priority)
-- **Connection** — applies to all repos in a specific connection
-- **Org** — applies to all repositories in your org (lowest priority, catch-all)
+- **Repo**: applies to specific repositories (highest priority)
+- **Connection**: applies to all repos in a specific connection
+- **Org**: applies to all repositories in your org (lowest priority, catch-all)

As per coding guidelines: "do NOT use em dashes (—)".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Repo** applies to specific repositories (highest priority)
- **Connection** applies to all repos in a specific connection
- **Org** applies to all repositories in your org (lowest priority, catch-all)
- **Repo**: applies to specific repositories (highest priority)
- **Connection**: applies to all repos in a specific connection
- **Org**: applies to all repositories in your org (lowest priority, catch-all)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/docs/features/agents/review-agent.mdx` around lines 146 - 148, Replace
the em dashes in the three bullets ("**Repo** —", "**Connection** —", "**Org**
—") with an allowed separator; update each line to use a colon (e.g., "**Repo:**
applies to specific repositories (highest priority)"), or a period after the
bold term, ensuring the rest of the bullet text remains unchanged and no em dash
characters remain in review-agent.mdx.


When a PR or MR arrives, Sourcebot selects the most specific matching config. If no config exists, the agent falls back to global environment variable defaults.

## Custom prompt

Each config can include a custom prompt. Two modes are available:

- **Append** (default) — your instructions are added after the built-in review rules.
- **Replace** — your instructions entirely replace the built-in rules. Use this when you want full control over what the agent looks for.

## Context files

You can configure one or more repository files to be fetched at review time and injected as additional context for the model. This is useful for encoding project-specific conventions that the model should be aware of when reviewing diffs — for example, preferred error handling patterns, style rules, or areas of the codebase that need extra scrutiny.

Set **Context files** in the agent config form to a comma or space separated list of paths relative to the repository root (e.g. `AGENTS.md .sourcebot/review.md`). Files that do not exist in the repository are silently ignored. The files are fetched once per PR from the head commit and included in the context for every diff hunk.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix hyphenation in compound adjective.

The phrase "comma or space separated list" should be "comma- or space-separated list" per standard grammar rules for compound adjectives.

✏️ Proposed fix
-Set **Context files** in the agent config form to a comma or space separated list of paths relative to the repository root (e.g. `AGENTS.md .sourcebot/review.md`).
+Set **Context files** in the agent config form to a comma- or space-separated list of paths relative to the repository root (e.g. `AGENTS.md .sourcebot/review.md`).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Set **Context files** in the agent config form to a comma or space separated list of paths relative to the repository root (e.g. `AGENTS.md .sourcebot/review.md`). Files that do not exist in the repository are silently ignored. The files are fetched once per PR from the head commit and included in the context for every diff hunk.
Set **Context files** in the agent config form to a comma- or space-separated list of paths relative to the repository root (e.g. `AGENTS.md .sourcebot/review.md`). Files that do not exist in the repository are silently ignored. The files are fetched once per PR from the head commit and included in the context for every diff hunk.
🧰 Tools
🪛 LanguageTool

[grammar] ~171-~171: Use a hyphen to join words.
Context: ...he agent config form to a comma or space separated list of paths relative to the ...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/docs/features/agents/review-agent.mdx` at line 171, The sentence under
the "Context files" description uses incorrect hyphenation; update the phrase
that currently reads "comma or space separated list of paths" (the sentence
starting with "Set **Context files** in the agent config form...") to "comma- or
space-separated list of paths relative to the repository root (e.g. `AGENTS.md
.sourcebot/review.md`)" so the compound adjectives are properly hyphenated.


# Environment variable reference

| Variable | Default | Description |
|---|---|---|
| `REVIEW_AGENT_AUTO_REVIEW_ENABLED` | `false` | Automatically review new and updated PRs/MRs |
| `REVIEW_AGENT_REVIEW_COMMAND` | `review` | Comment command that triggers a manual review (without the `/`) |
| `REVIEW_AGENT_MODEL` | first configured model | `displayName` of the language model to use for reviews |
| `REVIEW_AGENT_LOGGING_ENABLED` | unset | Write prompt and response logs to disk for debugging |
| `REVIEW_AGENT_AUTO_REVIEW_ENABLED` | `false` | Automatically review new and updated PRs/MRs. Can be overridden per agent config. |
| `REVIEW_AGENT_REVIEW_COMMAND` | `review` | Comment command that triggers a manual review (without the `/`). Can be overridden per agent config. |
| `REVIEW_AGENT_MODEL` | first configured model | `displayName` of the language model to use for reviews. Can be overridden per agent config. |
| `REVIEW_AGENT_LOGGING_ENABLED` | unset | Write prompt and response logs to disk for debugging. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- CreateEnum
CREATE TYPE "AgentType" AS ENUM ('CODE_REVIEW');

-- CreateEnum
CREATE TYPE "AgentScope" AS ENUM ('ORG', 'CONNECTION', 'REPO');

-- CreateEnum
CREATE TYPE "PromptMode" AS ENUM ('REPLACE', 'APPEND');

-- CreateTable
CREATE TABLE "AgentConfig" (
"id" TEXT NOT NULL,
"orgId" INTEGER NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"type" "AgentType" NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"prompt" TEXT,
"promptMode" "PromptMode" NOT NULL DEFAULT 'APPEND',
"scope" "AgentScope" NOT NULL,
"settings" JSONB NOT NULL DEFAULT '{}',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "AgentConfig_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "AgentConfigToRepo" (
"agentConfigId" TEXT NOT NULL,
"repoId" INTEGER NOT NULL,

CONSTRAINT "AgentConfigToRepo_pkey" PRIMARY KEY ("agentConfigId","repoId")
);

-- CreateTable
CREATE TABLE "AgentConfigToConnection" (
"agentConfigId" TEXT NOT NULL,
"connectionId" INTEGER NOT NULL,

CONSTRAINT "AgentConfigToConnection_pkey" PRIMARY KEY ("agentConfigId","connectionId")
);

-- CreateIndex
CREATE INDEX "AgentConfig_orgId_type_enabled_idx" ON "AgentConfig"("orgId", "type", "enabled");

-- CreateIndex
CREATE UNIQUE INDEX "AgentConfig_orgId_name_key" ON "AgentConfig"("orgId", "name");

-- AddForeignKey
ALTER TABLE "AgentConfig" ADD CONSTRAINT "AgentConfig_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentConfigToRepo" ADD CONSTRAINT "AgentConfigToRepo_agentConfigId_fkey" FOREIGN KEY ("agentConfigId") REFERENCES "AgentConfig"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentConfigToRepo" ADD CONSTRAINT "AgentConfigToRepo_repoId_fkey" FOREIGN KEY ("repoId") REFERENCES "Repo"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentConfigToConnection" ADD CONSTRAINT "AgentConfigToConnection_agentConfigId_fkey" FOREIGN KEY ("agentConfigId") REFERENCES "AgentConfig"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "AgentConfigToConnection" ADD CONSTRAINT "AgentConfigToConnection_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "Connection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
89 changes: 89 additions & 0 deletions packages/db/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,28 @@ enum CodeHostType {
azuredevops
}

enum AgentType {
CODE_REVIEW
}

/// Determines which repositories an AgentConfig applies to.
enum AgentScope {
/// Applies to all repositories in the organization.
ORG
/// Applies to all repositories under specific connections.
CONNECTION
/// Applies to specific repositories only.
REPO
}

/// Controls how a custom prompt is combined with the built-in system rules.
enum PromptMode {
/// Custom prompt replaces the built-in rules entirely.
REPLACE
/// Custom prompt is appended after the built-in rules.
APPEND
}

model Repo {
id Int @id @default(autoincrement())
name String /// Full repo name, including the vcs hostname (ex. github.com/sourcebot-dev/sourcebot)
Expand Down Expand Up @@ -75,6 +97,8 @@ model Repo {

searchContexts SearchContext[]

agentConfigMappings AgentConfigToRepo[]

@@unique([external_id, external_codeHostUrl, orgId])
@@index([orgId])
@@index([indexedAt])
Expand Down Expand Up @@ -170,6 +194,8 @@ model Connection {
/// When the connection was last synced successfully.
syncedAt DateTime?

agentConfigMappings AgentConfigToConnection[]

/// Controls whether repository permissions are enforced for this connection.
/// When `PERMISSION_SYNC_ENABLED` is false, this setting has no effect.
/// Defaults to the value of `PERMISSION_SYNC_ENABLED`.
Expand Down Expand Up @@ -291,6 +317,8 @@ model Org {
searchContexts SearchContext[]

chats Chat[]

agentConfigs AgentConfig[]
}

enum OrgRole {
Expand Down Expand Up @@ -569,3 +597,64 @@ model OAuthToken {
createdAt DateTime @default(now())
lastUsedAt DateTime?
}

/// Configures a customisable AI agent (e.g. code review) scoped to an org, connection, or specific repos.
model AgentConfig {
id String @id @default(cuid())

org Org @relation(fields: [orgId], references: [id], onDelete: Cascade)
orgId Int

name String
description String?

type AgentType
enabled Boolean @default(true)

/// Custom prompt instructions. Null means the agent uses its built-in rules only.
prompt String?

/// Controls whether the custom prompt replaces or appends to the built-in rules.
promptMode PromptMode @default(APPEND)

/// Determines what this config is scoped to: the whole org, specific connections, or specific repos.
scope AgentScope

/// Repo-level scope mappings (populated when scope = REPO).
repos AgentConfigToRepo[]

/// Connection-level scope mappings (populated when scope = CONNECTION).
connections AgentConfigToConnection[]

/// Extensible per-agent settings stored as JSON.
/// Shape: { autoReviewEnabled?: boolean, reviewCommand?: string, model?: string, contextFiles?: string }
settings Json @default("{}")

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@unique([orgId, name])
@@index([orgId, type, enabled])
}

/// Maps an AgentConfig to specific repositories (used when scope = REPO).
model AgentConfigToRepo {
agentConfig AgentConfig @relation(fields: [agentConfigId], references: [id], onDelete: Cascade)
agentConfigId String

repo Repo @relation(fields: [repoId], references: [id], onDelete: Cascade)
repoId Int

@@id([agentConfigId, repoId])
}

/// Maps an AgentConfig to specific connections (used when scope = CONNECTION).
model AgentConfigToConnection {
agentConfig AgentConfig @relation(fields: [agentConfigId], references: [id], onDelete: Cascade)
agentConfigId String

connection Connection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
connectionId Int

@@id([agentConfigId, connectionId])
}
Comment thread
fatmcgav marked this conversation as resolved.
63 changes: 63 additions & 0 deletions packages/web/src/app/(app)/agents/configs/[agentId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { authenticatedPage } from "@/middleware/authenticatedPage";
import { NavigationMenu } from "@/app/(app)/components/navigationMenu";
import { AgentConfigForm } from "../agentConfigForm";
import { notFound } from "next/navigation";
import { OrgRole } from "@sourcebot/db";

type Props = {
params: Promise<{ agentId: string }>;
};

export default authenticatedPage(async ({ prisma, org }, { params }: Props) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Gate config editing to org owners.

This page exposes the edit form and config details with plain authentication. Add the owner-role option to authenticatedPage so non-owners cannot access agent configuration management.

Proposed fix
+import { OrgRole } from "@sourcebot/db";
...
-export default authenticatedPage(async ({ prisma, org }, { params }: Props) => {
+export default authenticatedPage(async ({ prisma, org }, { params }: Props) => {
     const { agentId } = await params;
...
-});
+}, { minRole: OrgRole.OWNER, redirectTo: "/settings" });

Based on learnings: Use authenticatedPage with { minRole: OrgRole.OWNER, redirectTo: '/settings' } option to gate pages by role in the (app) route group.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default authenticatedPage(async ({ prisma, org }, { params }: Props) => {
import { OrgRole } from "@sourcebot/db";
// ... other imports ...
export default authenticatedPage(async ({ prisma, org }, { params }: Props) => {
const { agentId } = await params;
// ... rest of the function body ...
}, { minRole: OrgRole.OWNER, redirectTo: "/settings" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/`(app)/agents/configs/[agentId]/page.tsx at line 10, The
page currently allows any authenticated user to edit agent configs; update the
authenticatedPage call to require org owners by passing the options object {
minRole: OrgRole.OWNER, redirectTo: '/settings' } to authenticatedPage (i.e.,
change authenticatedPage(async (...) => ...) to authenticatedPage({ minRole:
OrgRole.OWNER, redirectTo: '/settings' }, async (...) => ...)); also ensure
OrgRole is imported where used (add import for OrgRole if missing) so non-owners
are redirected to /settings.

const { agentId } = await params;

const [config, connections, repos] = await Promise.all([
prisma.agentConfig.findFirst({
where: { id: agentId, orgId: org.id },
include: {
repos: { select: { repoId: true } },
connections: { select: { connectionId: true } },
},
}),
prisma.connection.findMany({
where: { orgId: org.id },
select: { id: true, name: true, connectionType: true },
orderBy: { name: "asc" },
}),
prisma.repo.findMany({
where: { orgId: org.id },
select: { id: true, displayName: true, external_id: true, external_codeHostType: true },
orderBy: { displayName: "asc" },
}),
]);

if (!config) {
notFound();
}

return (
<div className="flex flex-col items-center overflow-hidden min-h-screen">
<NavigationMenu />
<div className="w-full max-w-3xl px-4 mt-12 mb-24">
<h1 className="text-2xl font-semibold text-foreground mb-8">Edit agent config</h1>
<AgentConfigForm
initialValues={{
id: config.id,
name: config.name,
description: config.description ?? "",
type: config.type,
enabled: config.enabled,
prompt: config.prompt ?? "",
promptMode: config.promptMode,
scope: config.scope,
repoIds: config.repos.map((r) => r.repoId),
connectionIds: config.connections.map((c) => c.connectionId),
settings: config.settings as Record<string, unknown>,
}}
connections={connections}
repos={repos}
/>
</div>
</div>
);
}, { minRole: OrgRole.OWNER, redirectTo: '/agents' });
Loading
Loading