How to build a Google Antigravity Agent That Fixes Failing CI with CopilotKit
Antigravity is Google's agentic development platform. Its Python SDK brings the same agent harness into your own code, with built-in tools to read files, run shell commands and make edits.
The Antigravity AG-UI SDK (ag-ui-antigravity) streams every step of that agent over AG-UI, the open protocol between agents and user-facing apps. Connect it to CopilotKit and those steps show up in your UI as they happen, from tool calls and live state to approval prompts for risky actions.
In this tutorial, we'll build Repo Doctor, that fixes broken CI on GitHub repos. It reads the CI results, runs the tests, reproduces the failure, then fixes the code, shows the change as a diff and proves the fix before pushing it. You approve each step or let auto-approve handle it, then download a report of the whole run.
Every conversation is saved in CopilotKit Intelligence and opens on any device with its full history, while Product Analytics shows how the agent is used. The whole app runs as one Cloud Run service on Google Cloud, with each conversation's workspace backed up to Cloud Storage.
Here are the GitHub Source Code & Cookbook docs to build this same agent.
{% embed youtu.be %}
---
1. Introduction to Antigravity
An agent harness is everything around the model. It sends the conversation to the model, runs the tools the model asks for, passes the results back and decides when the turn is over.
Antigravity gives you Google's own harness, shipped as a compiled binary inside the google-antigravity package, and your Python code drives it over a local connection.
Out of the box, the harness can list, search, view and edit files, run shell commands, search the web and hand work to subagents. It only touches the folders you pass in workspaces, so you decide where the agent is allowed to work.
Your own tools are plain Python functions, and the SDK builds each tool's schema from its signature and docstring. Hooks run before every tool call and can allow or block it, which is how Repo Doctor asks you before anything risky runs.
The Antigravity AG-UI SDK wraps that agent in an AG-UI server. Every step the harness takes streams to the app as a typed event: text as it's written, tool calls with their results, state updates, and more.
from ag_ui_antigravity import AntigravityAgent, create_antigravity_app
agent = AntigravityAgent(
model="gemini-3.5-flash",
api_key="...", # or GEMINI_API_KEY in the environment
system_instructions="You are a helpful assistant.",
workspaces=["/path/to/a/sandbox"],
)
app = create_antigravity_app(agent, path="/")
create_antigravity_app returns a FastAPI app, so the agent runs as a regular web service.
On the frontend, CopilotKit connects to the agent over AG-UI and turns each event into UI, from a card for each tool call and a side panel that follows the agent's state to Approve and Deny buttons for an interrupt.
!Google Antigravity agent streaming tool calls, state and approval interrupts over AG-UI to a CopilotKit frontend
---
2. What are we building?
Repo Doctor takes a GitHub repo with red CI, finds out why it’s failing and gets it back to green. Here’s the flow:
- you ask about a repo or click a suggested prompt
- the agent clones the repo and finds the failing step in its GitHub Actions results
- it installs the project and runs the tests, with each run as a test results card
- it reruns the failing step locally as one short check
- it edits a fix and shows it as a diff
- it runs the same check again, and once it passes and the tests go green, it pushes a fix branch
Installs, file edits, shell commands and pushes each wait for you to approve or deny them. Pushes go to a local copy of the repo, so nothing reaches GitHub.
Here’s the architecture. The CopilotKit runtime sends each message over AG-UI to the Python agent, which plans each step with Gemini and runs the tools, all on Cloud Run.
!Architecture of a Google Antigravity agent with CopilotKit over AG-UI on Google Cloud Run, with Gemini, Secret Manager, Cloud Storage, GitHub and CopilotKit Intelligence
---
3. Tech stack
Here’s the stack behind Repo Doctor.
- Antigravity SDK - the agent harness, with built-in tools to read, edit and run code
- Antigravity AG-UI SDK - streams the agent’s steps, state and approval interrupts over AG-UI
- CopilotKit - the chat, the runtime and the hooks that render tool calls, state and approvals
- A2UI - the format the agent uses to describe each test results card
- CopilotKit Intelligence - saves every conversation and powers Product Analytics
- Gemini - the model behind the agent (gemini-3.5-flash)
- FastAPI - the Python server the agent runs on
- Next.js - the web app and the runtime route
- Cloud Run - hosts the app and the agent in one container
- Secret Manager - stores the Gemini, Intelligence and GitHub keys
- Cloud Storage - backs up each conversation’s workspace
---
4. Connect an Antigravity agent to CopilotKit
Repo Doctor has two parts: a Next.js app with the chat and the CopilotKit runtime, and the Python agent in agent/. We’ll connect the chat to the agent first, then give the agent its CI tools in the next section.
You'll need Node.js 22 or newer, pnpm, Python 3.13, uv and a Gemini API key from Google AI Studio.
Step 1: Create the Next.js app
Create the app and add the CopilotKit packages:
pnpm create next-app@latest repo-doctor --yes
cd repo-doctor
pnpm add @copilotkit/react-core @copilotkit/runtime @copilotkit/a2ui-renderer @ag-ui/client zod@3
@copilotkit/a2ui-renderer draws the test results card, and @ag-ui/client lets the runtime talk to the Python agent. The A2UI catalog needs zod 3, so zod stays on version 3.
The CopilotKit provider wraps the app. It points at the runtime route, registers the A2UI catalog we'll build in the next section, and turns on the CopilotKit Inspector in development (useful for debugging).
"use client";
import {
CopilotChatConfigurationProvider,
CopilotKit,
} from "@copilotkit/react-core/v2";
import { catalog } from "@/a2ui/catalog";
// ...
const A2UI = { catalog };
export function AppShell(props: ShellProps) {
return (
<CopilotKit
runtimeUrl="/api/copilotkit"
useSingleEndpoint={false}
a2ui={A2UI}
enableInspector={process.env.NODE_ENV === "development"}
>
<CopilotChatConfigurationProvider agentId={AGENT_ID}>
{/* ... */}
</CopilotChatConfigurationProvider>
</CopilotKit>
);
}
The chat is one CopilotChat component pointed at the agent. Its welcomeScreen prop replaces the empty chat with a start screen of example repos, each showing its CI status and a suggested prompt.
<CopilotChat
agentId={AGENT_ID}
className="h-full"
welcomeScreen={Welcome}
/>
Step 2: Create the Antigravity agent
The agent lives in agent/ as a uv project. Create it and add the Antigravity SDK along with its AG-UI adapter.
uv init --package --name repo-doctor agent
cd agent
uv add ag-ui-antigravity==0.2.1 google-antigravity==0.1.22 ag-ui-protocol==1.0.0 fastapi "uvicorn[standard]" pydantic pydantic-settings
The agent is an AntigravityAgent from the Antigravity AG-UI SDK, wrapped in a small subclass, ThreadScopedAntigravityAgent, that gives each conversation its own workspace folder.
The config picks the model and the prompt, lists the built-in tools the agent may use, points the harness at a workspace and turns on approvals.
def create_agent(settings: Settings) -> ThreadScopedAntigravityAgent:
# ...
return ThreadScopedAntigravityAgent(
settings=settings,
model=settings.gemini_model,
api_key=settings.gemini_api_key.get_secret_value(),
system_instructions=SYSTEM_INSTRUCTIONS,
capabilities=CapabilitiesConfig(enabled_tools=ENABLED_BUILTINS, enable_subagents=False),
workspaces=[str(settings.workspace_root)],
tool_approval=True,
auto_approve_tools=list(AUTO_APPROVED),
# ...
)
tool_approval=True pauses every tool call that isn’t in auto_approve_tools:
- runs on its own: viewing files, cloning, checking CI, running the tests and showing the diff
- waits for you: installs, checks, file edits, shell commands and pushes
ENABLED_BUILTINS turns on the file and shell tools, plus finish, which the harness needs to end a turn. SYSTEM_INSTRUCTIONS tells the model which tool to use when, and that a fix only counts once a check goes from failed to passed.
To serve the agent, create_antigravity_app turns it into an AG-UI endpoint at POST /.
from ag_ui_antigravity import create_antigravity_app
# ...
def create_app() -> FastAPI:
settings = get_settings()
agent = create_agent(settings)
app: FastAPI = create_antigravity_app(agent, path="/")
return app
def main() -> None:
settings = get_settings()
uvicorn.run(create_app(), host=settings.agent_host, port=settings.agent_port)
The settings, shell and workspace helpers are small files you can copy from the agent folder.
The app and the agent share one .env at the project root. GITHUB_TOKEN is optional and raises the CI lookups from 60 to 5,000 an hour.
GEMINI_API_KEY=...
GITHUB_TOKEN=... # optional
AGENT_URL=http://127.0.0.1:8000/
Start the agent from agent/ with uv run python -m repo_doctor.server. It listens on 127.0.0.1:8000, and only the Next.js server talks to it.
Step 3: Connect the CopilotKit runtime
The runtime route sits between the chat and the agent. HttpAgent sends each run to the agent’s URL over AG-UI, and the a2ui option turns on the A2UI middleware, which passes the agent’s test results cards to the chat.
// src/app/api/copilotkit/[[...slug]]/route.ts
import { HttpAgent } from "@ag-ui/client";
import { CopilotRuntime, createCopilotRuntimeHandler } from "@copilotkit/runtime/v2";
const agent = new HttpAgent({ url: env.AGENT_URL });
const runtime = new CopilotRuntime({
agents: { [AGENT_ID]: agent },
a2ui: { injectA2UITool: false },
// ...
});
const handler = createCopilotRuntimeHandler({ runtime, basePath: "/api/copilotkit" });
export const GET = handler;
export const POST = handler;
export const PATCH = handler;
export const DELETE = handler;
The runtime has no model config, because the harness calls Gemini itself. injectA2UITool: false tells the runtime that the agent writes its own A2UI, so the model doesn’t get an extra render tool.
With the agent running, start the app with pnpm dev in a second terminal. Open http://localhost:3000, say hello, and the Antigravity agent answers in the chat.
---
5. Give the agent its CI tools and show them in the chat
With the chat connected, the agent gets its CI tools, and each tool gets its own piece of UI.
Step 1: Add the CI tools
The built-in tools already read and edit code. The CI work lives in seven tools in tools.py:
- clone_repo makes a shallow clone of the repo
- ci_status reads the latest GitHub Actions results and names the failing jobs and steps
- install_dependencies installs the project
- run_tests runs the test suite
- run_check runs one command that reproduces the CI failure
- show_diff returns the real git diff
- git_push commits the fix on a new branch and pushes it
Each one is a plain async function with a docstring, and the SDK offers it to the model next to the built-in tools.
Tools also write what happened into shared state, which streams to the browser for the side panel. experimental_set_state replaces the whole state, so update_state merges each change into it.
from ag_ui_antigravity import experimental_get_state, experimental_set_state
def _update_state(**changes: Any) -> None:
experimental_set_state({**_state(), **changes})
clone_repo uses _update_state to save the repo’s details under repo. It also keeps pushes away from GitHub. The GitHub remote becomes upstream with pushing turned off, and a local bare repo becomes origin, so git_push only ever pushes to that local copy.
All seven tools are bound to the conversation’s folder, and ThreadScopedAntigravityAgent builds a fresh set for each conversation, so two conversations never share a checkout.
!Repo Doctor Antigravity agent calling custom CI tools in the CopilotKit chat: clone repo, check GitHub Actions and run tests
Step 2: Prove the fix with a check
Green tests alone don’t prove the fix addressed what CI saw. So the agent turns the failing CI step into one short command and runs it twice, once before the fix to reproduce the failure and once after to confirm it’s gone.
async def run_check(command: str, label: str) -> dict[str, Any]:
"""Runs one shell command that reproduces a failing CI step, and records it."""
# ...
result = await shell.run(
[shell_bin, "-c", command], cwd=repo, timeout=CHECK_TIMEOUT_SECONDS, env=_check_env()
)
exit_code = result.exit_code
status = "passed" if exit_code == 0 else "failed"
checks: list[dict[str, Any]] = list(_state().get("checks", []))
record = {
"run": len(checks) + 1,
"label": label,
"command": command,
"status": status,
# ...
}
checks.append(record)
_update_state(checks=checks)
return {"ok": True, **record}
The exit code decides passed or failed. Both runs land in checks, so the side panel shows the same check failing and then passing, and the agent quotes both in its final summary.
!Antigravity agent reproducing a failing GitHub Actions CI step locally with a shell check
!Side panel showing the same CI check going from failed to passed after the Antigravity agent's fix
Step 3: Approve or deny risky steps
When the model calls a tool that needs approval:
- the Antigravity AG-UI SDK holds the call and ends the run with an interrupt
- the interrupt’s reason is tool_approval, and its metadata carries the tool’s name and arguments
- useInterrupt renders a card for it, and resolve sends your answer back to the held call
useInterrupt({
agentId: AGENT_ID,
enabled: (event) =>
(event.value as { reason?: string } | undefined)?.reason === "tool_approval",
render: ({ interrupt, resolve }) => {
const { tool_name, args, ...meta } = (interrupt?.metadata ?? {}) as {
tool_name?: string;
args?: Record<string, unknown>;
} & Record<string, unknown>;
const tool = tool_name ?? "tool";
return (
<ApprovalCard
key={interrupt?.id}
request={{ tool, args: args ?? {}, meta }}
autoApprove={autoApprove && !ALWAYS_ASK.has(tool)}
onAnswer={(answer: ApprovalAnswer) => resolve(answer, interrupt?.id)}
/>
);
},
});
The card shows exactly what’s about to run, from the install command to the lines an edit changes or the branch a push creates. Approve sends { approved: true } and the held call runs. Deny sends { approved: false, message }. The call is skipped, and your note tells the agent why, so it can try another way or ask you.
The Auto-approve switch in the navbar answers the cards for you, except for pushes. That’s the ALWAYS_ASK check in the snippet.
!CopilotKit human-in-the-loop approval card for an Antigravity tool call, with Approve, Deny and Auto-approve
Step 4: Show the fix as a diff
show_diff returns the workspace’s real git diff with a one-line summary from the model. useRenderTool matches the tool call by name and draws it in the chat.
const Any = z.record(z.unknown());
useRenderTool({
name: "show_diff",
agentId: AGENT_ID,
parameters: Any,
render: (p) => <DiffBlock status={p.status} result={p.result} />,
});
DiffBlock waits for the call to complete, then draws each changed file with its added and removed lines. useRenderTool draws a row or card for each of the other tools the same way.
!Git diff of the Antigravity agent's code fix rendered in the chat with CopilotKit useRenderTool
Step 5: Draw the test results with A2UI
With A2UI, the agent describes a card as data and the app draws it with its own component. Each card lives in a surface, its own slot in the chat. run_tests runs the suite, turns the output into a TestReport and returns a2ui_operations next to the results the model reads.
async def run_tests(target: str = "") -> dict[str, Any]:
"""Runs the test suite and shows a live report card to the user."""
# ...
return {
"a2ui_operations": report_surface_operations(report, run=number),
"status": report.status,
"summary": report.headline(),
# ...
}
report_surface_operations builds three operations:
- createSurface opens a new surface tied to the catalog
- updateComponents sets its one TestReport component (COMPONENTS never changes)
- updateDataModel fills in that run’s data
CATALOG_ID = "https://repo-doctor.dev/a2ui/catalog/v1"
def report_surface_operations(report: TestReport, *, run: int) -> list[dict[str, Any]]:
sid = surface_id(run)
return [
{"version": "v0.9", "createSurface": {"surfaceId": sid, "catalogId": CATALOG_ID}},
{"version": "v0.9", "updateComponents": {"surfaceId": sid, "components": COMPONENTS}},
{
"version": "v0.9",
"updateDataModel": {"surfaceId": sid, "path": "/", "value": report_data(report, run=run)},
},
]
Each run gets its own surface id (test-run-1, test-run-2 and so on), so a new run adds a new card and the earlier ones stay in the chat.
!A2UI test results card showing failing tests, generated by the Antigravity agent and rendered by CopilotKit
On the web side, createCatalog maps TestReport onto the app’s own React component. It uses the same catalog id as the agent, which is how the two sides find each other. This is the catalog the provider registered in section 4.
export const CATALOG_ID = "https://repo-doctor.dev/a2ui/catalog/v1";
const renderers: CatalogRenderers<Definitions> = {
TestReport: ({ props }) => (
<TestReport
run={num(props.run)}
status={str(props.status)}
headline={str(props.headline)}
files={list<FileResultData>(props.files)}
failures={list<FailedTestData>(props.failures)}
summary={str(props.summary)}
/>
),
};
export const catalog = createCatalog(definitions, renderers, {
catalogId: CATALOG_ID,
includeBasicCatalog: false,
});
definitions holds a zod schema for the card’s props, which is the contract between the agent and the UI. With includeBasicCatalog: false, TestReport is the only component the agent can draw.
!A2UI test results card showing all tests passing after the Antigravity agent fixed the CI failure
Step 6: Track the run in a side panel
By now every tool has written its part into shared state: repo, install, testRuns, checks, approvals and push. useAgent gives the browser that state, so the side panel updates the moment a tool finishes.
const { agent } = useAgent({ agentId: AGENT_ID });
const state = useMemo(() => parseAgentState(agent.state), [agent.state]);
parseAgentState checks the state with zod first, since it comes over the network. The panel then shows the repo, the check going from failed to passed, every test run, every approval and the pushed branch.
The Download report button turns the same state into a Markdown report of the whole run, right in the browser.
!Repo Doctor side panel tracking the agent's shared state: repo, checks, test runs, approvals and the pushed fix branch
---
6. CopilotKit Intelligence: saved conversations and product analytics
CopilotKit Intelligence saves every conversation under the person who started it, and turns the runs into Product Analytics. Connect a project with the CopilotKit CLI, which writes CPK_INTELLIGENCE_API_KEY to .env.
npx copilotkit@latest login
npx copilotkit@latest project select --create repo-doctor --env-name .env
Then add Intelligence to the runtime from section 4, in route.ts.
const runtime = new CopilotRuntime({
agents: { [AGENT_ID]: agent },
a2ui: { injectA2UITool: false },
intelligence: new CopilotKitIntelligence({ apiKey: env.CPK_INTELLIGENCE_API_KEY }),
generateThreadNames: false,
identifyUser: (request: Request) =>
userFromCookieHeader(request.headers.get("cookie")) ?? {
id: env.DEMO_USER_ID,
name: "Demo user",
},
});
identifyUser tells Intelligence who made each request. Repo Doctor reads it from the name you type in the navbar, stored as a cookie by user.ts. That works for a demo, so swap in your own login before real users get the link.
generateThreadNames is off because naming a thread would run the whole agent a second time, so the conversation list names each thread from its first message. Without an Intelligence key, the runtime keeps runs in memory instead.
AG-UI Streams (Threads)
Every conversation is saved as a thread with its messages, tool calls, cards and state. The conversation list in the app is one hook, useThreads.
const { threads, isLoading, archiveThread, deleteThread } = useThreads({ agentId: AGENT_ID, limit: 50 });
Type the same name on another device and you’re back in the conversation, with the cards, test results and approvals where you left them.
In the Intelligence dashboard, Threads shows every event of every run. A run that stopped for an approval ends with an interrupt outcome holding the exact command that’s waiting for you.
!threads as ag-ui streams
Product Analytics
Product Analytics reads the same runs, with nothing extra to set up. The Activity tab shows how many people used the agent and which tools it calls most. The Reliability tab shows the error rate over time and the share of runs that finished cleanly.
!CopilotKit Intelligence Product Analytics Activity tab showing agent users and most used tools
!CopilotKit Intelligence Product Analytics Reliability tab showing agent error rate and clean run share
---
7. Running it locally
Clone the repo and install the dependencies.
git clone --depth 1 <https://github.com/CopilotKit/CopilotKit.git>
cd CopilotKit/examples/showcases/antigravity-repo-doctor
pnpm install
Run pnpm dev. The first run creates a .env file, sets up the Python agent, then stops because the Gemini key is empty. Open .env and paste your key:
GEMINI_API_KEY=...
If you’ve been following along, the two CLI commands from section 6 already set up CopilotKit Intelligence and added its API key to .env.
Run pnpm dev again and open http://localhost:3000. The agent runs code from the repos you give it, so only use repos you trust.
!Repo Doctor running locally, with a start screen of example GitHub repos with failing CI
Here’s the agent fixing CI on google-gemini/genai-processors from start to finish.
{% embed youtu.be %}
Debug it with the CopilotKit Inspector
Click the CopilotKit logo in the corner of the app to open the CopilotKit Inspector. Its AG-UI Events tab shows every tool call streaming in, the ACTIVITY_SNAPSHOT that carries each test results card and the STATE_SNAPSHOT the side panel reads.
!CopilotKit Inspector AG-UI Events tab showing Antigravity tool calls, ACTIVITY_SNAPSHOT and STATE_SNAPSHOT events
---
8. Deploying to Google Cloud
The whole app runs as one Cloud Run service: the Next.js app with the CopilotKit runtime and the Python agent with the harness, in one container. Run these from the app folder with the gcloud CLI, after gcloud auth login.
Build the image with Cloud Build:
PROJECT=your-project
REGION=us-central1
IMAGE=$REGION-docker.pkg.dev/$PROJECT/repo-doctor/app
gcloud config set project $PROJECT
gcloud services enable run.googleapis.com artifactregistry.googleapis.com \
cloudbuild.googleapis.com secretmanager.googleapis.com
gcloud artifacts repositories create repo-doctor --repository-format=docker --location=$REGION
gcloud builds submit --tag $IMAGE
!Building the Antigravity agent and Next.js app container image with Google Cloud Build
Put the keys in Secret Manager, straight from .env without printing them. Then create a service account that can only read those secrets and one bucket, and the bucket that holds each conversation's workspace:
secret() { grep "^$1=" .env | cut -d= -f2- | tr -d '\n"' | gcloud secrets create "$2" --data-file=-; }
secret GEMINI_API_KEY gemini-api-key
secret CPK_INTELLIGENCE_API_KEY cpk-intelligence-api-key
secret GITHUB_TOKEN github-token
gcloud iam service-accounts create repo-doctor --display-name="Repo Doctor"
SA=repo-doctor@$PROJECT.iam.gserviceaccount.com
# A new service account takes a few seconds to appear, so each binding retries.
for s in gemini-api-key cpk-intelligence-api-key github-token; do
for try in 1 2 3 4 5 6; do
gcloud secrets add-iam-policy-binding $s --member=serviceAccount:$SA \
--role=roles/secretmanager.secretAccessor && break
sleep 10
done
done
BUCKET=$PROJECT-repo-doctor-ws
gcloud storage buckets create gs://$BUCKET --location=$REGION --uniform-bucket-level-access
gcloud storage buckets add-iam-policy-binding gs://$BUCKET \
--member=serviceAccount:$SA --role=roles/storage.objectUser
Deploy it as a private service:
gcloud run deploy repo-doctor \
--image $IMAGE --region $REGION --service-account $SA \
--min-instances 1 --max-instances 1 \
--cpu 4 --memory 16Gi --no-cpu-throttling \
--timeout 3600 --execution-environment gen2 \
--set-secrets GEMINI_API_KEY=gemini-api-key:latest,CPK_INTELLIGENCE_API_KEY=cpk-intelligence-api-key:latest,GITHUB_TOKEN=github-token:latest \
--set-env-vars GEMINI_MODEL=gemini-3.5-flash,DEMO_USER_ID=repo-doctor-demo,WORKSPACE_BUCKET=gs://$BUCKET \
--no-allow-unauthenticated
APP_URL=$(gcloud run services describe repo-doctor --region $REGION --format='value(status.url)')
gcloud run services update repo-doctor --region $REGION --update-env-vars APP_URL=$APP_URL
A paused approval waits in the agent's memory, so the service runs exactly one instance that never scales to zero. --no-cpu-throttling keeps the agent working while it waits for you, and --timeout 3600 covers long runs.
!Deploying the Antigravity agent and CopilotKit app to Google Cloud Run as a private service
After every run, the agent saves the conversation to its own folder in the bucket: the repo with its edits, the pushed fix branches and the agent's session. If the service restarts, the next message in that conversation brings everything back and the agent picks up where it left off.
The service is private, and it should stay that way: the cloned repos' code runs inside it, and every message spends your Gemini credits. Open it through an authenticated proxy at http://localhost:8080.
gcloud run services proxy repo-doctor --region $REGION --port 8080
!Repo Doctor running on Google Cloud Run, opened through the gcloud authenticated proxy
---
Get started
Repo Doctor is one way to put a real interface on an Antigravity agent. Swap the CI tools for your own, and the same AG-UI wiring gives you approvals, live cards and saved conversations.
- Antigravity SDK
- Antigravity AG-UI SDK
- CopilotKit docs
- Repo Doctor GitHub source code
- Cookbook: build it step by step
- CopilotKit Intelligence
Follow CopilotKit on Twitter and join the Discord community. If you get stuck, reach out in the CopilotKit or AG-UI communities.
12 reactions
Mentions
How to build a Google Antigravity Agent That Fixes Failing CI with CopilotKit Antigravity is Google's agentic development platform. Its Python SDK brings the same agent harness into your own code, with built-in tools to read files, run shell commands and make edits. The Antigravity AG-UI SDK (ag-ui-antigravity) streams every step of that agent over AG-UI, the open protocol between agents and user-facing apps. Connect it to CopilotKit and those steps show up in your UI as they happen, from tool calls and live state to approval prompts for risky actions. In this tutorial, we'll build Repo Doctor, that fixes broken CI on GitHub repos. It reads the CI results, runs the tests, reproduces the failure, then fixes the code, shows the change as a diff and proves the fix before pushing it. You approve each step or let auto-approve handle it, then download a report of the whole run. Every conversation is saved in CopilotKit Intelligence and opens on any device with its full history, while Product Analytics shows how the agent is used. The whole app runs as one Cloud Run service on Google Cloud, with each conversation's workspace backed up to Cloud Storage. Here are the GitHub Source Code & Cookbook docs to build this same agent. {% embed youtu.be %} --- 1. Introduction to Antigravity An agent harness is everything around the model. It sends the conversation to the model, runs the tools the model asks for, passes the results back and decides when the turn is over. Antigravity gives you Google's own harness, shipped as a compiled binary inside the google-antigravity package, and your Python code drives it over a local connection. Out of the box, the harness can list, search, view and edit files, run shell commands, search the web and hand work to subagents. It only touches the folders you pass in workspaces, so you decide where the agent is allowed to work. Your own tools are plain Python functions, and the SDK builds each tool's schema from its signature and docstring. Hooks run before every tool call and can allow or block it, which is how Repo Doctor asks you before anything risky runs. The Antigravity AG-UI SDK wraps that agent in an AG-UI server. Every step the harness takes streams to the app as a typed event: text as it's written, tool calls with their results, state updates, and more. from ag_ui_antigravity import AntigravityAgent, create_antigravity_app agent = AntigravityAgent( model="gemini-3.5-flash", api_key="...", # or GEMINI_API_KEY in the environment system_instructions="You are a helpful assistant.", workspaces=["/path/to/a/sandbox"], ) app = create_antigravity_app(agent, path="/") create_antigravity_app returns a FastAPI app, so the agent runs as a regular web service. On the frontend, CopilotKit connects to the agent over AG-UI and turns each event into UI, from a card for each tool call and a side panel that follows the agent's state to Approve and Deny buttons for an interrupt. !Google Antigravity agent streaming tool calls, state and approval interrupts over AG-UI to a CopilotKit frontend --- 2. What are we building? Repo Doctor takes a GitHub repo with red CI, finds out why it’s failing and gets it back to green. Here’s the flow: - you ask about a repo or click a suggested prompt - the agent clones the repo and finds the failing step in its GitHub Actions results - it installs the project and runs the tests, with each run as a test results card - it reruns the failing step locally as one short check - it edits a fix and shows it as a diff - it runs the same check again, and once it passes and the tests go green, it pushes a fix branch Installs, file edits, shell commands and pushes each wait for you to approve or deny them. Pushes go to a local copy of the repo, so nothing reaches GitHub. Here’s the architecture. The CopilotKit runtime sends each message over AG-UI to the Python agent, which plans each step with Gemini and runs the tools, all on Cloud Run. !Architecture of a Google Antigravity agent with CopilotKit over AG-UI on Google Cloud Run, with Gemini, Secret Manager, Cloud Storage, GitHub and CopilotKit Intelligence --- 3. Tech stack Here’s the stack behind Repo Doctor. - Antigravity SDK - the agent harness, with built-in tools to read, edit and run code - Antigravity AG-UI SDK - streams the agent’s steps, state and approval interrupts over AG-UI - CopilotKit - the chat, the runtime and the hooks that render tool calls, state and approvals - A2UI - the format the agent uses to describe each test results card - CopilotKit Intelligence - saves every conversation and powers Product Analytics - Gemini - the model behind the agent (gemini-3.5-flash) - FastAPI - the Python server the agent runs on - Next.js - the web app and the runtime route - Cloud Run - hosts the app and the agent in one container - Secret Manager - stores the Gemini, Intelligence and GitHub keys - Cloud Storage - backs up each conversation’s workspace --- 4. Connect an Antigravity agent to CopilotKit Repo Doctor has two parts: a Next.js app with the chat and the CopilotKit runtime, and the Python agent in agent/. We’ll connect the chat to the agent first, then give the agent its CI tools in the next section. You'll need Node.js 22 or newer, pnpm, Python 3.13, uv and a Gemini API key from Google AI Studio. Step 1: Create the Next.js app Create the app and add the CopilotKit packages: pnpm create next-app@latest repo-doctor --yes cd repo-doctor pnpm add @copilotkit/react-core @copilotkit/runtime @copilotkit/a2ui-renderer @ag-ui/client zod@3 @copilotkit/a2ui-renderer draws the test results card, and @ag-ui/client lets the runtime talk to the Python agent. The A2UI catalog needs zod 3, so zod stays on version 3. The CopilotKit provider wraps the app. It points at the runtime route, registers the A2UI catalog we'll build in the next section, and turns on the CopilotKit Inspector in development (useful for debugging). "use client"; import { CopilotChatConfigurationProvider, CopilotKit, } from "@copilotkit/react-core/v2"; import { catalog } from "@/a2ui/catalog"; // ... const A2UI = { catalog }; export function AppShell(props: ShellProps) { return ( <CopilotKit runtimeUrl="/api/copilotkit" useSingleEndpoint={false} a2ui={A2UI} enableInspector={process.env.NODE_ENV === "development"} > <CopilotChatConfigurationProvider agentId={AGENT_ID}> {/* ... */} </CopilotChatConfigurationProvider> </CopilotKit> ); } The chat is one CopilotChat component pointed at the agent. Its welcomeScreen prop replaces the empty chat with a start screen of example repos, each showing its CI status and a suggested prompt. <CopilotChat agentId={AGENT_ID} className="h-full" welcomeScreen={Welcome} /> Step 2: Create the Antigravity agent The agent lives in agent/ as a uv project. Create it and add the Antigravity SDK along with its AG-UI adapter. uv init --package --name repo-doctor agent cd agent uv add ag-ui-antigravity==0.2.1 google-antigravity==0.1.22 ag-ui-protocol==1.0.0 fastapi "uvicorn[standard]" pydantic pydantic-settings The agent is an AntigravityAgent from the Antigravity AG-UI SDK, wrapped in a small subclass, ThreadScopedAntigravityAgent, that gives each conversation its own workspace folder. The config picks the model and the prompt, lists the built-in tools the agent may use, points the harness at a workspace and turns on approvals. def create_agent(settings: Settings) -> ThreadScopedAntigravityAgent: # ... return ThreadScopedAntigravityAgent( settings=settings, model=settings.gemini_model, api_key=settings.gemini_api_key.get_secret_value(), system_instructions=SYSTEM_INSTRUCTIONS, capabilities=CapabilitiesConfig(enabled_tools=ENABLED_BUILTINS, enable_subagents=False), workspaces=[str(settings.workspace_root)], tool_approval=True, auto_approve_tools=list(AUTO_APPROVED), # ... ) tool_approval=True pauses every tool call that isn’t in auto_approve_tools: - runs on its own: viewing files, cloning, checking CI, running the tests and showing the diff - waits for you: installs, checks, file edits, shell commands and pushes ENABLED_BUILTINS turns on the file and shell tools, plus finish, which the harness needs to end a turn. SYSTEM_INSTRUCTIONS tells the model which tool to use when, and that a fix only counts once a check goes from failed to passed. To serve the agent, create_antigravity_app turns it into an AG-UI endpoint at POST /. from ag_ui_antigravity import create_antigravity_app # ... def create_app() -> FastAPI: settings = get_settings() agent = create_agent(settings) app: FastAPI = create_antigravity_app(agent, path="/") return app def main() -> None: settings = get_settings() uvicorn.run(create_app(), host=settings.agent_host, port=settings.agent_port) The settings, shell and workspace helpers are small files you can copy from the agent folder. The app and the agent share one .env at the project root. GITHUB_TOKEN is optional and raises the CI lookups from 60 to 5,000 an hour. GEMINI_API_KEY=... GITHUB_TOKEN=... # optional AGENT_URL=http://127.0.0.1:8000/ Start the agent from agent/ with uv run python -m repo_doctor.server. It listens on 127.0.0.1:8000, and only the Next.js server talks to it. Step 3: Connect the CopilotKit runtime The runtime route sits between the chat and the agent. HttpAgent sends each run to the agent’s URL over AG-UI, and the a2ui option turns on the A2UI middleware, which passes the agent’s test results cards to the chat. // src/app/api/copilotkit/[[...slug]]/route.ts import { HttpAgent } from "@ag-ui/client"; import { CopilotRuntime, createCopilotRuntimeHandler } from "@copilotkit/runtime/v2"; const agent = new HttpAgent({ url: env.AGENT_URL }); const runtime = new CopilotRuntime({ agents: { [AGENT_ID]: agent }, a2ui: { injectA2UITool: false }, // ... }); const handler = createCopilotRuntimeHandler({ runtime, basePath: "/api/copilotkit" }); export const GET = handler; export const POST = handler; export const PATCH = handler; export const DELETE = handler; The runtime has no model config, because the harness calls Gemini itself. injectA2UITool: false tells the runtime that the agent writes its own A2UI, so the model doesn’t get an extra render tool. With the agent running, start the app with pnpm dev in a second terminal. Open http://localhost:3000, say hello, and the Antigravity agent answers in the chat. --- 5. Give the agent its CI tools and show them in the chat With the chat connected, the agent gets its CI tools, and each tool gets its own piece of UI. Step 1: Add the CI tools The built-in tools already read and edit code. The CI work lives in seven tools in tools.py: - clone_repo makes a shallow clone of the repo - ci_status reads the latest GitHub Actions results and names the failing jobs and steps - install_dependencies installs the project - run_tests runs the test suite - run_check runs one command that reproduces the CI failure - show_diff returns the real git diff - git_push commits the fix on a new branch and pushes it Each one is a plain async function with a docstring, and the SDK offers it to the model next to the built-in tools. Tools also write what happened into shared state, which streams to the browser for the side panel. experimental_set_state replaces the whole state, so update_state merges each change into it. from ag_ui_antigravity import experimental_get_state, experimental_set_state def _update_state(**changes: Any) -> None: experimental_set_state({**_state(), **changes}) clone_repo uses _update_state to save the repo’s details under repo. It also keeps pushes away from GitHub. The GitHub remote becomes upstream with pushing turned off, and a local bare repo becomes origin, so git_push only ever pushes to that local copy. All seven tools are bound to the conversation’s folder, and ThreadScopedAntigravityAgent builds a fresh set for each conversation, so two conversations never share a checkout. !Repo Doctor Antigravity agent calling custom CI tools in the CopilotKit chat: clone repo, check GitHub Actions and run tests Step 2: Prove the fix with a check Green tests alone don’t prove the fix addressed what CI saw. So the agent turns the failing CI step into one short command and runs it twice, once before the fix to reproduce the failure and once after to confirm it’s gone. async def run_check(command: str, label: str) -> dict[str, Any]: """Runs one shell command that reproduces a failing CI step, and records it.""" # ... result = await shell.run( [shell_bin, "-c", command], cwd=repo, timeout=CHECK_TIMEOUT_SECONDS, env=_check_env() ) exit_code = result.exit_code status = "passed" if exit_code == 0 else "failed" checks: list[dict[str, Any]] = list(_state().get("checks", [])) record = { "run": len(checks) + 1, "label": label, "command": command, "status": status, # ... } checks.append(record) _update_state(checks=checks) return {"ok": True, **record} The exit code decides passed or failed. Both runs land in checks, so the side panel shows the same check failing and then passing, and the agent quotes both in its final summary. !Antigravity agent reproducing a failing GitHub Actions CI step locally with a shell check !Side panel showing the same CI check going from failed to passed after the Antigravity agent's fix Step 3: Approve or deny risky steps When the model calls a tool that needs approval: - the Antigravity AG-UI SDK holds the call and ends the run with an interrupt - the interrupt’s reason is tool_approval, and its metadata carries the tool’s name and arguments - useInterrupt renders a card for it, and resolve sends your answer back to the held call useInterrupt({ agentId: AGENT_ID, enabled: (event) => (event.value as { reason?: string } | undefined)?.reason === "tool_approval", render: ({ interrupt, resolve }) => { const { tool_name, args, ...meta } = (interrupt?.metadata ?? {}) as { tool_name?: string; args?: Record<string, unknown>; } & Record<string, unknown>; const tool = tool_name ?? "tool"; return ( <ApprovalCard key={interrupt?.id} request={{ tool, args: args ?? {}, meta }} autoApprove={autoApprove && !ALWAYS_ASK.has(tool)} onAnswer={(answer: ApprovalAnswer) => resolve(answer, interrupt?.id)} /> ); }, }); The card shows exactly what’s about to run, from the install command to the lines an edit changes or the branch a push creates. Approve sends { approved: true } and the held call runs. Deny sends { approved: false, message }. The call is skipped, and your note tells the agent why, so it can try another way or ask you. The Auto-approve switch in the navbar answers the cards for you, except for pushes. That’s the ALWAYS_ASK check in the snippet. !CopilotKit human-in-the-loop approval card for an Antigravity tool call, with Approve, Deny and Auto-approve Step 4: Show the fix as a diff show_diff returns the workspace’s real git diff with a one-line summary from the model. useRenderTool matches the tool call by name and draws it in the chat. const Any = z.record(z.unknown()); useRenderTool({ name: "show_diff", agentId: AGENT_ID, parameters: Any, render: (p) => <DiffBlock status={p.status} result={p.result} />, }); DiffBlock waits for the call to complete, then draws each changed file with its added and removed lines. useRenderTool draws a row or card for each of the other tools the same way. !Git diff of the Antigravity agent's code fix rendered in the chat with CopilotKit useRenderTool Step 5: Draw the test results with A2UI With A2UI, the agent describes a card as data and the app draws it with its own component. Each card lives in a surface, its own slot in the chat. run_tests runs the suite, turns the output into a TestReport and returns a2ui_operations next to the results the model reads. async def run_tests(target: str = "") -> dict[str, Any]: """Runs the test suite and shows a live report card to the user.""" # ... return { "a2ui_operations": report_surface_operations(report, run=number), "status": report.status, "summary": report.headline(), # ... } report_surface_operations builds three operations: - createSurface opens a new surface tied to the catalog - updateComponents sets its one TestReport component (COMPONENTS never changes) - updateDataModel fills in that run’s data CATALOG_ID = "https://repo-doctor.dev/a2ui/catalog/v1" def report_surface_operations(report: TestReport, *, run: int) -> list[dict[str, Any]]: sid = surface_id(run) return [ {"version": "v0.9", "createSurface": {"surfaceId": sid, "catalogId": CATALOG_ID}}, {"version": "v0.9", "updateComponents": {"surfaceId": sid, "components": COMPONENTS}}, { "version": "v0.9", "updateDataModel": {"surfaceId": sid, "path": "/", "value": report_data(report, run=run)}, }, ] Each run gets its own surface id (test-run-1, test-run-2 and so on), so a new run adds a new card and the earlier ones stay in the chat. !A2UI test results card showing failing tests, generated by the Antigravity agent and rendered by CopilotKit On the web side, createCatalog maps TestReport onto the app’s own React component. It uses the same catalog id as the agent, which is how the two sides find each other. This is the catalog the provider registered in section 4. export const CATALOG_ID = "https://repo-doctor.dev/a2ui/catalog/v1"; const renderers: CatalogRenderers<Definitions> = { TestReport: ({ props }) => ( <TestReport run={num(props.run)} status={str(props.status)} headline={str(props.headline)} files={list<FileResultData>(props.files)} failures={list<FailedTestData>(props.failures)} summary={str(props.summary)} /> ), }; export const catalog = createCatalog(definitions, renderers, { catalogId: CATALOG_ID, includeBasicCatalog: false, }); definitions holds a zod schema for the card’s props, which is the contract between the agent and the UI. With includeBasicCatalog: false, TestReport is the only component the agent can draw. !A2UI test results card showing all tests passing after the Antigravity agent fixed the CI failure Step 6: Track the run in a side panel By now every tool has written its part into shared state: repo, install, testRuns, checks, approvals and push. useAgent gives the browser that state, so the side panel updates the moment a tool finishes. const { agent } = useAgent({ agentId: AGENT_ID }); const state = useMemo(() => parseAgentState(agent.state), [agent.state]); parseAgentState checks the state with zod first, since it comes over the network. The panel then shows the repo, the check going from failed to passed, every test run, every approval and the pushed branch. The Download report button turns the same state into a Markdown report of the whole run, right in the browser. !Repo Doctor side panel tracking the agent's shared state: repo, checks, test runs, approvals and the pushed fix branch --- 6. CopilotKit Intelligence: saved conversations and product analytics CopilotKit Intelligence saves every conversation under the person who started it, and turns the runs into Product Analytics. Connect a project with the CopilotKit CLI, which writes CPK_INTELLIGENCE_API_KEY to .env. npx copilotkit@latest login npx copilotkit@latest project select --create repo-doctor --env-name .env Then add Intelligence to the runtime from section 4, in route.ts. const runtime = new CopilotRuntime({ agents: { [AGENT_ID]: agent }, a2ui: { injectA2UITool: false }, intelligence: new CopilotKitIntelligence({ apiKey: env.CPK_INTELLIGENCE_API_KEY }), generateThreadNames: false, identifyUser: (request: Request) => userFromCookieHeader(request.headers.get("cookie")) ?? { id: env.DEMO_USER_ID, name: "Demo user", }, }); identifyUser tells Intelligence who made each request. Repo Doctor reads it from the name you type in the navbar, stored as a cookie by user.ts. That works for a demo, so swap in your own login before real users get the link. generateThreadNames is off because naming a thread would run the whole agent a second time, so the conversation list names each thread from its first message. Without an Intelligence key, the runtime keeps runs in memory instead. AG-UI Streams (Threads) Every conversation is saved as a thread with its messages, tool calls, cards and state. The conversation list in the app is one hook, useThreads. const { threads, isLoading, archiveThread, deleteThread } = useThreads({ agentId: AGENT_ID, limit: 50 }); Type the same name on another device and you’re back in the conversation, with the cards, test results and approvals where you left them. In the Intelligence dashboard, Threads shows every event of every run. A run that stopped for an approval ends with an interrupt outcome holding the exact command that’s waiting for you. !threads as ag-ui streams Product Analytics Product Analytics reads the same runs, with nothing extra to set up. The Activity tab shows how many people used the agent and which tools it calls most. The Reliability tab shows the error rate over time and the share of runs that finished cleanly. !CopilotKit Intelligence Product Analytics Activity tab showing agent users and most used tools !CopilotKit Intelligence Product Analytics Reliability tab showing agent error rate and clean run share --- 7. Running it locally Clone the repo and install the dependencies. git clone --depth 1 <https://github.com/CopilotKit/CopilotKit.git> cd CopilotKit/examples/showcases/antigravity-repo-doctor pnpm install Run pnpm dev. The first run creates a .env file, sets up the Python agent, then stops because the Gemini key is empty. Open .env and paste your key: GEMINI_API_KEY=... If you’ve been following along, the two CLI commands from section 6 already set up CopilotKit Intelligence and added its API key to .env. Run pnpm dev again and open http://localhost:3000. The agent runs code from the repos you give it, so only use repos you trust. !Repo Doctor running locally, with a start screen of example GitHub repos with failing CI Here’s the agent fixing CI on google-gemini/genai-processors from start to finish. {% embed youtu.be %} Debug it with the CopilotKit Inspector Click the CopilotKit logo in the corner of the app to open the CopilotKit Inspector. Its AG-UI Events tab shows every tool call streaming in, the ACTIVITY_SNAPSHOT that carries each test results card and the STATE_SNAPSHOT the side panel reads. !CopilotKit Inspector AG-UI Events tab showing Antigravity tool calls, ACTIVITY_SNAPSHOT and STATE_SNAPSHOT events --- 8. Deploying to Google Cloud The whole app runs as one Cloud Run service: the Next.js app with the CopilotKit runtime and the Python agent with the harness, in one container. Run these from the app folder with the gcloud CLI, after gcloud auth login. Build the image with Cloud Build: PROJECT=your-project REGION=us-central1 IMAGE=$REGION-docker.pkg.dev/$PROJECT/repo-doctor/app gcloud config set project $PROJECT gcloud services enable run.googleapis.com artifactregistry.googleapis.com \ cloudbuild.googleapis.com secretmanager.googleapis.com gcloud artifacts repositories create repo-doctor --repository-format=docker --location=$REGION gcloud builds submit --tag $IMAGE !Building the Antigravity agent and Next.js app container image with Google Cloud Build Put the keys in Secret Manager, straight from .env without printing them. Then create a service account that can only read those secrets and one bucket, and the bucket that holds each conversation's workspace: secret() { grep "^$1=" .env | cut -d= -f2- | tr -d '\n"' | gcloud secrets create "$2" --data-file=-; } secret GEMINI_API_KEY gemini-api-key secret CPK_INTELLIGENCE_API_KEY cpk-intelligence-api-key secret GITHUB_TOKEN github-token gcloud iam service-accounts create repo-doctor --display-name="Repo Doctor" SA=repo-doctor@$PROJECT.iam.gserviceaccount.com # A new service account takes a few seconds to appear, so each binding retries. for s in gemini-api-key cpk-intelligence-api-key github-token; do for try in 1 2 3 4 5 6; do gcloud secrets add-iam-policy-binding $s --member=serviceAccount:$SA \ --role=roles/secretmanager.secretAccessor && break sleep 10 done done BUCKET=$PROJECT-repo-doctor-ws gcloud storage buckets create gs://$BUCKET --location=$REGION --uniform-bucket-level-access gcloud storage buckets add-iam-policy-binding gs://$BUCKET \ --member=serviceAccount:$SA --role=roles/storage.objectUser Deploy it as a private service: gcloud run deploy repo-doctor \ --image $IMAGE --region $REGION --service-account $SA \ --min-instances 1 --max-instances 1 \ --cpu 4 --memory 16Gi --no-cpu-throttling \ --timeout 3600 --execution-environment gen2 \ --set-secrets GEMINI_API_KEY=gemini-api-key:latest,CPK_INTELLIGENCE_API_KEY=cpk-intelligence-api-key:latest,GITHUB_TOKEN=github-token:latest \ --set-env-vars GEMINI_MODEL=gemini-3.5-flash,DEMO_USER_ID=repo-doctor-demo,WORKSPACE_BUCKET=gs://$BUCKET \ --no-allow-unauthenticated APP_URL=$(gcloud run services describe repo-doctor --region $REGION --format='value(status.url)') gcloud run services update repo-doctor --region $REGION --update-env-vars APP_URL=$APP_URL A paused approval waits in the agent's memory, so the service runs exactly one instance that never scales to zero. --no-cpu-throttling keeps the agent working while it waits for you, and --timeout 3600 covers long runs. !Deploying the Antigravity agent and CopilotKit app to Google Cloud Run as a private service After every run, the agent saves the conversation to its own folder in the bucket: the repo with its edits, the pushed fix branches and the agent's session. If the service restarts, the next message in that conversation brings everything back and the agent picks up where it left off. The service is private, and it should stay that way: the cloned repos' code runs inside it, and every message spends your Gemini credits. Open it through an authenticated proxy at http://localhost:8080. gcloud run services proxy repo-doctor --region $REGION --port 8080 !Repo Doctor running on Google Cloud Run, opened through the gcloud authenticated proxy --- Get started Repo Doctor is one way to put a real interface on an Antigravity agent. Swap the CI tools for your own, and the same AG-UI wiring gives you approvals, live cards and saved conversations. - Antigravity SDK - Antigravity AG-UI SDK - CopilotKit docs - Repo Doctor GitHub source code - Cookbook: build it step by step - CopilotKit Intelligence Follow CopilotKit on Twitter and join the Discord community. If you get stuck, reach out in the CopilotKit or AG-UI communities.
OpenBot: self-hosted alternative to Grok Bot that works with any agent and any harness (9.8k stars) Grok Bot and other hosted AI coworkers (OpenAI Dots, Meta Muse, Claude Duet) lock you into their model, their agent and their cloud. Your data and the agent’s computer all live on their side. OpenBot gives you AI coworkers you run yourself. Each Bot gets its own computer with a browser, files and a shell, and you decide what it’s allowed to touch. It’s built on AG-UI (Agent-User Interaction Protocol), so you can bring any AG-UI agent. LangGraph, CrewAI, Mastra, Claude Agent SDK, Google ADK, Pydantic AI, Agno, etc or something you built yourself, it just plugs in as a Bot. You can also pick a model for each Bot so you can use a cheap model for simple tasks and a heavier one for the hard stuff. Hosted tools by big companies don’t let you do that. What you get: - A computer per Bot you can watch live, and take over when it hits a login or 2FA screen - Policy rules plus an audit log of what was allowed or refused, and why - Connect third-party tools via MCP (Drive, Notion, GitHub, etc) with more through Composio - Talk to your Bots wherever you work: web chat, voice calls, Slack, Teams or plain text messages - Built-in routines that run on a schedule - Desktop app (sign in with Claude or Codex), mobile and a browser extension - 13 ready coworkers to start from (ticket triage, release notes, expense claims and more) Memory and threads run on CopilotKit Intelligence, which you can self-host entirely. It also has something called Automatic Learning - when you correct a Bot, it picks up the pattern and turns it into a skill.md you can review, edit and ship. The Bot uses it the next time without redeploying. Self-hosting is one Docker Compose, a single image or a Helm chart, with your data in your own Postgres. 9.8k stars, 1.2k forks, 50 contributors and 604 merged PRs in about 2.5 months. TypeScript, MIT licensed. CopilotKit/OpenBot
OpenBot — Open-source AI coworkers that each get a computer of their own: a browser, files and tools, with every action decided before it happens and recorded after.... Link: CopilotKit/OpenBot #OpenSource #GitHub #DevTools
OpenDuet is an open source, self-hostable version of Claude Duet that works with any agent harness. THE PROACTIVE AGENT ERA JUST WENT OPEN SOURCE. WHY RENT IT WHEN YOU CAN OWN IT? CopilotKit/OpenDuet
OpenMuse: A personal agent with a browser, terminal, files, and work that keeps going (6.0k stars) What it does All text below is quoted directly from the project README. >A personal agent with a browser, terminal, files, and work that keeps going built with CopilotKit and AG-UI. Features: - Chat: CopilotKit headless chat with streamed AG-UI events, mailbox search and reading, send/stop in one input pill, a visible follow-up queue, retained drafts, delegated tasks, and inline email, browser, PDF, plan, and finance cards. - Agent computer: Persistent browser profiles and takeover console; optional isolated Linux terminal, saved command receipts, editable workspace files, and PDF transfer. - Activity: Durable task plans, progress, input requests, pause/resume/cancel/retry, approvals, and saved receipts. SQL leases recover interrupted work. - Goals & Tracking: Goals and milestones; recurring public-page checks for changes, text availability, or USD price thresholds, with deduplicated alerts and failure backoff. - Finance: Import transaction CSV to create a spending summary with categories, transactions, and a savings-goal action. - Gmail & Calendar: Google OAuth adapters, complete mail threads, drafts/attachments, calendar discovery, and reviewed event creation/update/deletion. Live credentials required. - Personal context: Editable name, tone, avatar, and memories. Background-update preferences and durable in-app notifications. >OpenMuse is a personal-agent application with an agent computer, visible work, and rich results. It runs its own server, task worker, and browser worker. Repository CopilotKit/openmuse Repository stats |Metric|Value| |:-|:-| |Stars|5,970| |Forks|860| |Last release|None| |Languages|TypeScript 99.4%, Other 0.6%| |Contributors|36| |AI contributor accounts (Claude / Codex / Cursor / Copilot)|None| |AI co-authored commits|Claude: 7, Codex: 2| Main contributor |Field|Value| |:-|:-| |Name|Jerel Velarde| |GitHub|@jerelvelarde| |Commits to this repo|39 (top contributor)| |Bio|Staff Applied AI Engineer @CopilotKit AI agents & generative UI Pr| |Followers|228| ────────────────────────────── 📌 If you want to browse more repos like this, we've organized them by category and rankings here: bestgithubrepositories.com 🗂️ We're also archiving repos where possible, so useful projects don't disappear when they're removed from GitHub.
OpenBot: The AI assistant your company can actually own (8.4k stars) What it does All text below is quoted directly from the project README. >Open-source AI coworkers that each get a computer of their own: a browser, files and tools, with every action decided before it happens and recorded after. Bring any AG-UI agent. Features: - A computer per person: the supervisor gives each person their own container, with a folder and browser profile for each of their Bots. - A shell, not just a browser: a Bot can run a command in its workspace, install what it needs, and process a file it saved. - The gateway is the only way in: it resolves the target from a server-held snapshot, evaluates the policy, writes the audit row, and only then calls the computer. - Take the wheel: a Bot that hits a login wall or a 2FA prompt asks for help. - Secrets never enter the transcript: the trail records that a secret was requested and how long it was, not what it said. - Bring your own agent: any AG-UI endpoint is a Bot, on a framework or hand-written. - An audit trail you can read: /admin/audit lists what was permitted, what was refused and what failed, and every refusal carries the rule that caused it. - Credentials encrypted at rest: stored through /admin/credentials, never returned by an API, and redacted from audit events. >cp .env.example .env Repository CopilotKit/OpenBot Repository stats |Metric|Value| |:-|:-| |Stars|8,436| |Forks|1.1k| |Last release|v0.2.0 (2026-10-12)| |Languages|TypeScript 89.3%, Rust 8.3%, Python 1.3%| |Contributors|47| |AI contributor accounts (Claude / Codex / Cursor / Copilot)|@claude (77 commits)| |AI co-authored commits|None| Main contributor |Field|Value| |:-|:-| |Name|David McKay| |GitHub|@davidmckayv| |Commits to this repo|431 (top contributor)| |Company / Location|Seattle, WA| |Followers|66| ────────────────────────────── 📌 If you want to browse more repos like this, we've organized them by category and rankings here: bestgithubrepositories.com 🗂️ We're also archiving repos where possible, so useful projects don't disappear when they're removed from GitHub.
OpenBot (GitHub Trending #3) The best open source Grok Bot / AI coworker available. Repo (9.1k ⭐️): CopilotKit/OpenBot t.co
Create beautiful agent UX Give your agent every UI component it might need in just 2 lines of code. It will respond with text when it's relevant, and generative UI when it's smarter. Customizable and open source: CopilotKit/CopilotKit t.co
今日のGitHubトレンド CopilotKit/OpenBot 自社インフラで運用し、自由にカスタマイズできるAIアシスタントの構築用テンプレートです。 AG-UIを通じて任意のエージェントを導入でき、個別ブラウザ環境の提供や動作への介入が可能です。 テキスト以外のコンポーネント形式での回答にも対応しています。 製品としての提供ではなく、クローンして自社専用に作り上げることを目的としています。
一個人也能開遊戲公司?49 個 AI 員工分工,從點子管到上市 今天的 GitHub 熱榜前五名:第一名 impeccable 讓 AI 寫網頁有設計語彙,還能偵測 AI slop。後面有 OpenMausBot、Claude-Code-Game-Studios、claude-mem 和 OpenBot,全都跟 AI agent 有關,一次看完。 1|impeccable:幫 AI 補上設計語彙 pbakaus/impeccable 2|OpenMausBot:開源的 AI 聊天團隊 milind-soni/OpenMausBot 3|Claude-Code-Game-Studios:遊戲工作室模板 Donchitos/Claude-Code-Game-Studios 4|claude-mem:讓 AI 記得做過的事 thedotmack/claude-mem 5|OpenBot:公司自有 AI 同事 CopilotKit/OpenBot 資料來源:GitHub Trending(今日新增 star) 每天幫你篩一遍 GitHub 開源熱榜。 排名依 GitHub Trending 當日新增 star 數,僅供參考,不代表推薦或背書。 #GitHub #開源 #程式設計 #開源專案 #GitHubTrending #軟體開發 #impeccable #Rust #TypeScript #Shell #AIagent #ClaudeCode #Shorts