Skip to content

Commit 2136b9e

Browse files
committed
OpenBot
0 parents  commit 2136b9e

350 files changed

Lines changed: 55753 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.env.example‎

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
DATABASE_URL=postgres://openbot:openbot@localhost:5432/openbot
2+
# Credential vault encryption key. The example value is public and only suitable for local
3+
# development. Production refuses to start with this key.
4+
# openssl rand -base64 32
5+
KEY_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
6+
PORT=3001
7+
TENANT_PACKAGE_DIR=../examples/fintech
8+
# What this deployment calls itself, when more than one shares an Intelligence project. A copy of a
9+
# deployment made for development uses the same project key, and threads are listed per Bot with
10+
# nothing to say which deployment a conversation came from. The name goes into every thread id this
11+
# deployment mints, so its own conversations stay identifiable. Unset, the tenant package's id is
12+
# used, which tells two packages apart but not two copies of one.
13+
# DEPLOYMENT_ID=
14+
# Google sign-in. Leave commented for local development with OPENBOT_DEV_NO_AUTH; uncomment all five
15+
# settings together so authentication is either fully configured or absent.
16+
#
17+
# BETTER_AUTH_SECRET must be a high-entropy secret of at least 32 characters. Generate one:
18+
# openssl rand -base64 32
19+
# TRUSTED_ORIGINS is where the app is served from, which is port 3010 locally.
20+
# BETTER_AUTH_URL=http://localhost:3001
21+
# BETTER_AUTH_SECRET=
22+
# GOOGLE_OAUTH_CLIENT_ID=
23+
# GOOGLE_OAUTH_CLIENT_SECRET=
24+
# INITIAL_ADMIN_EMAILS=admin@example.com
25+
26+
# Local development only. This admits every request as one local administrator. Keep it explicit and
27+
# never expose a deployment to the internet in this state; production refuses to start with it.
28+
OPENBOT_DEV_NO_AUTH=true
29+
TRUSTED_ORIGINS=http://localhost:3010
30+
31+
# CopilotKit Intelligence. Required: the server refuses to start without all four because
32+
# Intelligence owns durable threads and memory and a deployment without it forgets every
33+
# conversation. There is no degraded mode.
34+
#
35+
# Do not run Intelligence yourself. These two point at the managed service and should be left
36+
# unchanged; the CLI below provisions a free licence for it. Running Intelligence on your own
37+
# infrastructure is an Enterprise Intelligence Platform feature deployed by Helm chart, and is not
38+
# self-serve: https://docs.showcase.copilotkit.ai/premium/self-hosting
39+
INTELLIGENCE_API_URL=https://api.intelligence.copilotkit.ai
40+
INTELLIGENCE_GATEWAY_WS_URL=wss://realtime.intelligence.copilotkit.ai
41+
#
42+
# Get both of the next two from the CopilotKit CLI:
43+
#
44+
# npx --yes copilotkit@latest login # browser sign-in
45+
# npx --yes copilotkit@latest project select # prints the cpk-... runtime key -> INTELLIGENCE_API_KEY
46+
# npx --yes copilotkit@latest license --write # writes COPILOTKIT_LICENSE_TOKEN into this file
47+
#
48+
# The runtime key is also under "API Keys" in your project at https://intelligence.copilotkit.ai
49+
INTELLIGENCE_API_KEY=
50+
COPILOTKIT_LICENSE_TOKEN=
51+
52+
# Model key. Required by the proof-of-concept Bot, which speaks OpenAI's API directly, and by the
53+
# framework Bot unless you point it at another provider below.
54+
OPENAI_API_KEY=
55+
56+
# Framework Bot provider: openai, anthropic or google. It reads that provider's own
57+
# key and refuses to start without it, so a deployment on Anthropic never needs an OpenAI key for it.
58+
# The proof-of-concept Bot is OpenAI only by construction: it speaks that API directly.
59+
# BOT_PROVIDER=openai
60+
# ANTHROPIC_API_KEY=
61+
# GOOGLE_API_KEY=
62+
63+
# Which model. Defaults per provider: gpt-5.5, claude-sonnet-4-5, gemini-2.5-flash.
64+
# BOT_MODEL=gpt-5.5
65+
66+
# OpenAI only. Its newer models require the Responses API, which the framework Bot handles and the
67+
# proof-of-concept one cannot.
68+
# BOT_RESPONSES_API=false
69+
70+
# The Bot computer. Absent means the feature is off and its routes are not mounted.
71+
AGENT_COMPUTER_URL=http://localhost:4100
72+
# Secret every computer requires from its caller. `agent-computer` drives a browser holding real
73+
# logins, while policy, audit and sign-in live in the API server. The computer refuses to start
74+
# without this value and refuses every request that does not present it. Use a long random value;
75+
# `scripts/start.sh` sets a development one for you.
76+
COMPUTER_TOKEN=
77+
# Local only. Lets a Bot browse this machine's own services; never set this in a deployment.
78+
AGENT_COMPUTER_ALLOW_PRIVATE_HOSTS=true
79+
#
80+
# What a Bot may do on its computer, as one JSON object. Absent uses the built-in default, which
81+
# permits the acting tools and forbids nothing, and records every action either way.
82+
#
83+
# `deny` is evaluated first and beats `allow`. An empty `allow` permits nothing, a missing policy
84+
# permits nothing, and a rule that fails to parse denies rather than letting the action through. The
85+
# server refuses to start if this is set and malformed, so an invalid restriction never falls back to
86+
# permissive behavior.
87+
#
88+
# Workspace and browser profile per Bot. Each Bot's computer is its own container with its own
89+
# volumes, so one Bot cannot read another's files or use another's logins, and every action records
90+
# which Bot took it. A rule can still restrict a single Bot with `bot.id`.
91+
#
92+
# Attributes: tool.name, bot.id, actor.id, page.url, page.host, element.ref/role/name/type,
93+
# key, file.path, file.name, file.extension.
94+
#
95+
# Name every route to the same effect. A form submits from a keypress in any of its fields, so a rule
96+
# that only blocks a Submit button does not block Enter from another field. The example below refuses
97+
# Enter outright for that reason.
98+
# Functions: contains(haystack, needle) and matches(value, pattern), both case-insensitive.
99+
# `enforce` blocks; `dry-run` decides and records but lets everything through, so a new rule can be
100+
# tried against real traffic before it starts refusing anybody's work.
101+
#
102+
# AGENT_COMPUTER_POLICY={"mode":"enforce","deny":["(intent == \"activate\" && contains(element.name, \"submit\")) || (tool.name == \"computer_key\" && key == \"Enter\")"],"allow":["true"]}
103+
104+
# How long one action waits for its element, in ms. Read by agent-computer, not the server.
105+
# ACTION_TIMEOUT_MS=10000
106+
107+
# ---------------------------------------------------------------------------
108+
# The computer's profile and where its traffic leaves from
109+
# ---------------------------------------------------------------------------
110+
111+
# Chromium profile directory inside agent-computer, mounted from the `agent-profiles` volume in
112+
# docker-compose.yml. Pointing it somewhere unmounted disables login persistence.
113+
# PROFILES_DIR=/profiles
114+
115+
# Which Bot this computer belongs to, and therefore which profile directory it uses.
116+
# COMPUTER_BOT_ID=shared
117+
118+
# Per-Bot egress identity. A proxy the Bot's browser sends everything through, so the far side sees a
119+
# stable address it can allow-list or attribute to that Bot. `EGRESS_PROXY_<BOT>` names one Bot,
120+
# `EGRESS_PROXY_DEFAULT` covers the rest, and absent means the browser goes out directly.
121+
#
122+
# Credentials may be in the URL and are split out before Playwright sees them. Only the HOST is ever
123+
# reported back on the admin page or the API, so the password does not end up on a screen.
124+
#
125+
# This is attribution, not anonymity, and it is not a boundary by itself: it gives a security team a
126+
# per-Bot address for network rules alongside AGENT_COMPUTER_POLICY.
127+
# EGRESS_PROXY_DEFAULT=http://user:password@proxy.internal:8080
128+
# EGRESS_PROXY_SALES_BOT=http://sales.proxy.internal:8080
129+
130+
131+
# The managed teammate AG-UI endpoint. Required: use an HTTP(S) URL.
132+
MANAGED_AGENT_AG_UI_URL=http://localhost:4200/ag-ui
133+
134+
# The second Bot in the box runs on http://localhost:4201/ag-ui, on a framework rather than
135+
# proof of concept, and is reached the same way: point MANAGED_AGENT_AG_UI_URL at it, or add it as a
136+
# Bot of its own in the tenant package or at /agents.
137+
138+
# Which model the Bots use. agent-bot speaks /v1/chat/completions and cannot use gpt-5.6-*, which
139+
# require the Responses API. agent-langgraph can: set BOT_RESPONSES_API=true and give it a 5.6 model.
140+
# BOT_RESPONSES_API=false
141+
142+
# One computer per Bot. Unset, every Bot shares the computer at AGENT_COMPUTER_URL, suitable on a
143+
# laptop and explicit about being one shared machine. Set, the supervisor gives each Bot a container
144+
# of its own, with its own /workspace and its own browser profile.
145+
COMPUTER_SUPERVISOR_URL=
146+
# Shared with the supervisor. Not the security boundary, its four verbs are, but it keeps anything
147+
# else that can reach the port from cycling a Bot's computer.
148+
SUPERVISOR_TOKEN=
149+
# Set to runsc to run every computer under gVisor, if the host has it. Unset, a computer is an
150+
# ordinary container and shares the host kernel, which is worth knowing when the Bot is not ours.
151+
COMPUTER_RUNTIME=

‎.gitattributes‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

‎.github/workflows/ci.yml‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
8+
# Least privilege by default. Jobs that need more must declare it locally.
9+
permissions:
10+
contents: read
11+
12+
# The newest push on a branch wins; main runs are retained for badge and release history.
13+
concurrency:
14+
group: ci-${{ github.ref }}
15+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
16+
17+
jobs:
18+
# Formatting, linting, typing and tests are independent checks, so each reports its own result.
19+
static:
20+
name: format, lint, types
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24+
with:
25+
# Checkout leaves a usable credential in the runner otherwise, which every later step and
26+
# every action it calls can read. Nothing here pushes.
27+
persist-credentials: false
28+
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
29+
with:
30+
bun-version: 1.3.14
31+
- run: bun install --frozen-lockfile
32+
- run: bun run format:check
33+
- run: bun run lint
34+
- run: bun run typecheck
35+
36+
test:
37+
name: tests
38+
runs-on: ubuntu-latest
39+
# The suite includes a real database integration test. Without a database it fails on every run,
40+
# including on main, which trains everyone to read a red CI as normal. pgvector rather than plain
41+
# postgres because the knowledge schema uses the extension.
42+
services:
43+
postgres:
44+
image: pgvector/pgvector:pg17
45+
env:
46+
POSTGRES_DB: openbot
47+
POSTGRES_USER: openbot
48+
POSTGRES_PASSWORD: openbot
49+
ports:
50+
- 5432:5432
51+
options: >-
52+
--health-cmd "pg_isready -U openbot -d openbot"
53+
--health-interval 5s
54+
--health-timeout 5s
55+
--health-retries 10
56+
env:
57+
DATABASE_URL: postgres://openbot:openbot@localhost:5432/openbot
58+
steps:
59+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
60+
with:
61+
persist-credentials: false
62+
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
63+
with:
64+
bun-version: 1.3.14
65+
- run: bun install --frozen-lockfile
66+
# Not the db:migrate script: that one loads ../.env, which does not exist in CI. DATABASE_URL
67+
# comes from the job env instead, which drizzle.config.ts already reads.
68+
- run: bunx drizzle-kit migrate --config=drizzle.config.ts
69+
working-directory: server
70+
# A passing job must include the expected test floor. Import-time failures can otherwise skip
71+
# files before their tests are registered.
72+
- run: bun run test:ci
73+
74+
build:
75+
name: build
76+
runs-on: ubuntu-latest
77+
steps:
78+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
79+
with:
80+
persist-credentials: false
81+
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
82+
with:
83+
bun-version: 1.3.14
84+
- run: bun install --frozen-lockfile
85+
- run: bun run build
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: security / zizmor
2+
3+
# zizmor runs static analysis over every workflow under .github/workflows looking for the well-known
4+
# classes of GitHub Actions footguns: template injection from untrusted input, dangerous triggers
5+
# like `pull_request_target`, unpinned `uses:` refs, excessive token scopes, secret exfiltration
6+
# through job outputs, and a long tail of others.
7+
#
8+
# Public pull requests make workflow inputs and token scopes a security boundary.
9+
#
10+
# Findings at `low` confidence and above fail the job. Intentional exceptions belong in
11+
# `.github/zizmor.yml` with a justification.
12+
13+
on:
14+
push:
15+
branches: [main]
16+
paths:
17+
- ".github/workflows/**"
18+
- ".github/actions/**"
19+
- ".github/zizmor.yml"
20+
pull_request:
21+
paths:
22+
- ".github/workflows/**"
23+
- ".github/actions/**"
24+
- ".github/zizmor.yml"
25+
schedule:
26+
# Catch findings introduced by newly-published advisories even in a week when no workflow changed.
27+
- cron: "0 9 * * 1"
28+
29+
concurrency:
30+
group: ${{ github.workflow }}-${{ github.ref }}
31+
cancel-in-progress: true
32+
33+
permissions:
34+
contents: read
35+
36+
jobs:
37+
zizmor:
38+
name: Static analysis (zizmor)
39+
runs-on: ubuntu-latest
40+
timeout-minutes: 5
41+
permissions:
42+
contents: read
43+
steps:
44+
- name: Checkout
45+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
46+
with:
47+
persist-credentials: false
48+
49+
- name: Run zizmor
50+
# `min-severity: low` blocks on the broadest set of findings without flagging
51+
# hypothetical-only informational notes.
52+
uses: zizmorcore/zizmor-action@6fc4b006235f201fdab3722e17240ab420d580e5 # v0.6.1
53+
with:
54+
min-severity: low
55+
advanced-security: false
56+
config: .github/zizmor.yml

‎.github/zizmor.yml‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# zizmor configuration, static analysis for GitHub Actions workflows.
2+
#
3+
# Docs: https://docs.zizmor.sh/configuration/
4+
#
5+
# This lives at `.github/zizmor.yml` because zizmor discovers it there. The workflow at
6+
# `.github/workflows/security_zizmor.yml` runs it and fails on findings at the configured level.
7+
#
8+
# Findings that cannot be remediated belong under `rules:` with a justification. Blanket suppressions
9+
# are not allowed.
10+
11+
rules:
12+
# `template-injection` flags untrusted ${{ }} expansions inside `run:` scripts. Route values
13+
# through `env:` and reference the environment variable inside the shell.
14+
template-injection:
15+
ignore: []

‎.gitignore‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.worktrees/
2+
.superpowers/
3+
docs/superpowers/
4+
5+
docs/specs/
6+
docs/plans/
7+
.env
8+
.env.*
9+
!.env.example
10+
node_modules/
11+
**/dist/
12+
app/src/lib/generated/application-config.ts
13+
.logs/
14+
.demo-logs/
15+
**/.impeccable
16+
.wave-state.md

‎LICENSE‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 CopilotKit
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)