Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,13 @@ INTELLIGENCE_GATEWAY_WS_URL=wss://realtime.intelligence.copilotkit.ai
#
# npx --yes copilotkit@latest login # browser sign-in
# npx --yes copilotkit@latest project select # prints the cpk-... runtime key -> INTELLIGENCE_API_KEY
# npx --yes copilotkit@latest license --write # writes COPILOTKIT_LICENSE_TOKEN into this file
#
# The runtime key is also under "API Keys" in your project at https://intelligence.copilotkit.ai
INTELLIGENCE_API_KEY=

# Optional, and blank on purpose. Managed Intelligence derives entitlement from the project key
# above and issues no licence token. A self-hosted Intelligence with its own licence sets this and
# it is forwarded to the runtime; startup does not require it.
COPILOTKIT_LICENSE_TOKEN=

# How long a Bot's stream may say nothing before this deployment gives up on the turn, in
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,6 @@ jobs:
-e INTELLIGENCE_API_URL=https://api.intelligence.copilotkit.ai \
-e INTELLIGENCE_GATEWAY_WS_URL=wss://realtime.intelligence.copilotkit.ai \
-e INTELLIGENCE_API_KEY=ci-not-a-real-key \
-e COPILOTKIT_LICENSE_TOKEN=ci-not-a-real-licence \
openbot:ci
for attempt in $(seq 1 150); do
if curl -fsS http://localhost:3001/api/capabilities >/dev/null 2>&1; then
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ A Bot is any endpoint speaking [AG-UI](https://github.com/ag-ui-protocol/ag-ui),
```sh
npx --yes copilotkit@latest login
npx --yes copilotkit@latest project select
npx --yes copilotkit@latest license --write
```

Put the `cpk-...` runtime key from `project select` in `.env` as
`INTELLIGENCE_API_KEY`. `license --write` writes
`COPILOTKIT_LICENSE_TOKEN` into the existing `.env`.
`INTELLIGENCE_API_KEY`. That is the only Intelligence credential you need:
managed Intelligence derives entitlement from the project key, so there is
no separate licence token to fetch.

3. Fill the remaining required values:

Expand Down Expand Up @@ -202,7 +202,10 @@ See [docs/configuration.md](docs/configuration.md) and [docs/coworkers.md](docs/
- `INTELLIGENCE_API_URL`
- `INTELLIGENCE_GATEWAY_WS_URL`
- `INTELLIGENCE_API_KEY`
- `COPILOTKIT_LICENSE_TOKEN`

`COPILOTKIT_LICENSE_TOKEN` is optional. A self-hosted Intelligence with its own
licence can still set it and it is forwarded to the runtime; managed Intelligence
does not issue one and startup no longer asks for it.

Settings worth knowing:

Expand Down
4 changes: 2 additions & 2 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@ag-ui/core": "0.0.57",
"@ag-ui/core": "0.0.59",
"@base-ui/react": "^1.6.0",
"@better-auth/sso": "^1.7.1",
"@copilotkit/react-core": "1.69.0",
"@copilotkit/react-core": "1.70.1",
"@fontsource-variable/inter": "^5.3.0",
"@shadcn/react": "^0.3.0",
"@tabler/icons-react": "^3.36.1",
Expand Down
68 changes: 39 additions & 29 deletions bun.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions charts/openbot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ docker manifest inspect ghcr.io/copilotkit/openbot:v0.0.4 | grep architecture
```

**Intelligence credentials.** OpenBot requires CopilotKit Intelligence and the chart refuses to
install without `secrets.intelligenceApiKey` and `secrets.licenseToken`. Both come from the CLI, on
any machine with a browser:
install without `secrets.intelligenceApiKey`. It comes from the CLI, on any machine with a browser:

```sh
npx --yes copilotkit@latest login # browser sign-in
npx --yes copilotkit@latest project select # prints the cpk-... runtime key
npx --yes copilotkit@latest license --print # prints the licence token
```

`--print` rather than `--write` here: `--write` puts the token in a local `.env`, which is what a
laptop wants and not what you are about to paste into a Secret. The free plan is enough to install.
That key is the only Intelligence credential a managed install needs; the free plan is enough.
`secrets.licenseToken` is optional and exists for a self-hosted Intelligence that issues its own
licence. `npx --yes copilotkit@latest license --print` prints one without writing it to a local
`.env`, which is what you want for something you are about to paste into a Secret.

**A default StorageClass**, or a named one. Both a Bot's computer and the bundled database ask for
a volume, and a fresh cluster often has no class marked default. See
Expand Down
2 changes: 2 additions & 0 deletions charts/openbot/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,13 @@ and in whatever holds the release, which is not where `KEY_ENCRYPTION_KEY` belon
secretKeyRef:
name: {{ include "openbot.secretName" . }}
key: intelligence-api-key
{{- if .Values.secrets.licenseToken }}
- name: COPILOTKIT_LICENSE_TOKEN
valueFrom:
secretKeyRef:
name: {{ include "openbot.secretName" . }}
key: license-token
{{- end }}
{{- with .Values.config.managedAgent.url }}
- name: MANAGED_AGENT_AG_UI_URL
value: {{ . | quote }}
Expand Down
4 changes: 3 additions & 1 deletion charts/openbot/templates/secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ type: Opaque
stringData:
key-encryption-key: {{ required "secrets.keyEncryptionKey is required unless secrets.existingSecret or externalSecrets is used. Generate one with: openssl rand -base64 32" .Values.secrets.keyEncryptionKey | quote }}
intelligence-api-key: {{ required "secrets.intelligenceApiKey is required. OpenBot needs CopilotKit Intelligence and refuses to start without it." .Values.secrets.intelligenceApiKey | quote }}
license-token: {{ required "secrets.licenseToken is required. OpenBot needs CopilotKit Intelligence and refuses to start without it." .Values.secrets.licenseToken | quote }}
{{- with .Values.secrets.licenseToken }}
license-token: {{ . | quote }}
{{- end }}
{{- with .Values.secrets.betterAuthSecret }}
better-auth-secret: {{ . | quote }}
{{- end }}
Expand Down
11 changes: 7 additions & 4 deletions charts/openbot/templates/validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,15 @@ This template renders nothing.
{{- /*
Intelligence, which is not optional.

All four values are required together and the server refuses to start on a partial set, so the
same rule is applied here: caught at install with the values named, rather than in a crash loop
whose message is in a log nobody has opened.
The three addressing values are required together and the server refuses to start on a partial
set, so the same rule is applied here: caught at install with the values named, rather than in a
crash loop whose message is in a log nobody has opened.

`secrets.licenseToken` is NOT among them. Managed Intelligence issues no licence token, so
requiring one here would block an install the server would have accepted.
*/}}
{{- if or (not .Values.config.intelligence.apiUrl) (not .Values.config.intelligence.gatewayWsUrl) }}
{{- fail "OpenBot requires CopilotKit Intelligence. Set config.intelligence.apiUrl and config.intelligence.gatewayWsUrl, and the matching secrets.intelligenceApiKey and secrets.licenseToken." }}
{{- fail "OpenBot requires CopilotKit Intelligence. Set config.intelligence.apiUrl and config.intelligence.gatewayWsUrl, and the matching secrets.intelligenceApiKey." }}
{{- end }}

{{- /*
Expand Down
8 changes: 5 additions & 3 deletions charts/openbot/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,12 @@ secrets:
modelApiKey: ""
computerToken: ""
supervisorToken: ""
# Both from the CopilotKit CLI: `npx --yes copilotkit@latest project select` prints the runtime
# key, and `npx --yes copilotkit@latest license --print` prints the licence. The chart refuses to
# install without them, because there is no mode where this runs with Intelligence missing.
# From the CopilotKit CLI: `npx --yes copilotkit@latest project select` prints the runtime key.
# The chart refuses to install without it, because there is no mode where this runs with
# Intelligence missing.
intelligenceApiKey: ""
# Optional. Managed Intelligence issues no licence token; set this only for a self-hosted
# Intelligence that has one, and it is passed to the server as COPILOTKIT_LICENSE_TOKEN.
licenseToken: ""
# Sent to `config.managedAgent.url` on every call. Required when that url is set.
managedAgentToken: ""
Expand Down
6 changes: 4 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ bash scripts/start.sh
| `INTELLIGENCE_API_URL` | CopilotKit Intelligence API URL. |
| `INTELLIGENCE_GATEWAY_WS_URL` | CopilotKit Intelligence realtime gateway URL. |
| `INTELLIGENCE_API_KEY` | Runtime key for the Intelligence project. |
| `COPILOTKIT_LICENSE_TOKEN` | License token for the Intelligence project. |

All four Intelligence values are required together. Missing any of them stops server startup.
The three above are required together. Missing any of them stops server startup.

`COPILOTKIT_LICENSE_TOKEN` is optional: managed Intelligence issues no licence token, and a
self-hosted Intelligence that has one sets this and has it forwarded to the runtime.

`MANAGED_AGENT_AG_UI_URL` names the Bot in the box: the default endpoint for coworkers created in
the product. It needs `MANAGED_AGENT_TOKEN` beside it, or the server refuses to start. Unset, the
Expand Down
2 changes: 1 addition & 1 deletion docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ and the fix would not be available.
| `EMBEDDED_POSTGRES` | `on` to run the database inside the container. Off by default |
| `KEY_ENCRYPTION_KEY` | base64 32 bytes. `openssl rand -base64 32`. The example key is refused in production |
| `INTELLIGENCE_API_URL`, `INTELLIGENCE_GATEWAY_WS_URL`, `INTELLIGENCE_API_KEY` | CopilotKit Intelligence. A free plan is available and it can be self-hosted |
| `COPILOTKIT_LICENSE_TOKEN` | from `npx copilotkit@latest license --write` |
| `COPILOTKIT_LICENSE_TOKEN` | optional. Managed Intelligence issues none; set it only for a self-hosted Intelligence that has one |
| a model key | `OPENAI_API_KEY`, or the provider you configured |

`COMPUTER_TOKEN` is generated at start if you do not set one. Both processes that need it are inside
Expand Down
4 changes: 1 addition & 3 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ Provision CopilotKit Intelligence after `.env` exists:
```sh
npx --yes copilotkit@latest login
npx --yes copilotkit@latest project select
npx --yes copilotkit@latest license --write
```

Put the `cpk-...` runtime key from `project select` in `.env` as
`INTELLIGENCE_API_KEY`. `license --write` writes `COPILOTKIT_LICENSE_TOKEN`.
Then add `OPENAI_API_KEY`.
`INTELLIGENCE_API_KEY`. There is no licence step. Then add `OPENAI_API_KEY`.

Start the stack:

Expand Down
11 changes: 6 additions & 5 deletions prompt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ fills most of them itself: it defaults COMPUTER_TOKEN and WORKER_SHARED_SECRET,
AGENT_TOOL_TOKEN and writes it back into `.env`. What it cannot invent is:

1. INTELLIGENCE_API_KEY the `cpk-...` runtime key
2. COPILOTKIT_LICENSE_TOKEN the licence
3. OPENAI_API_KEY the model credential
2. OPENAI_API_KEY the model credential

Everything else in `.env.example` is either already correct or generated. Do not talk the person
through the other seven.
Expand All @@ -33,7 +32,9 @@ THE STEPS

npx --yes copilotkit@latest login # opens a browser
npx --yes copilotkit@latest project select # prints the cpk-... runtime key
npx --yes copilotkit@latest license --write # writes COPILOTKIT_LICENSE_TOKEN into ./.env

There is no licence step. `copilotkit license` still exists for a self-hosted Intelligence, and
COPILOTKIT_LICENSE_TOKEN is honoured when set, but managed Intelligence needs only the cpk- key.

Put the `cpk-...` key in `.env` as INTELLIGENCE_API_KEY. Put their model key in OPENAI_API_KEY.
Then:
Expand All @@ -56,8 +57,8 @@ FAILURES YOU WILL ACTUALLY SEE, AND WHAT THEY MEAN
The server refuses to start rather than running half-configured. The message names the variable.

- "CopilotKit Intelligence is required and is not configured. Missing: ..."
One of the four Intelligence values is blank. Almost always INTELLIGENCE_API_KEY or
COPILOTKIT_LICENSE_TOKEN, from skipping a CLI step.
One of the three Intelligence values is blank. Almost always INTELLIGENCE_API_KEY, from
skipping `project select`.

- "No identity provider is configured. Set GOOGLE_OAUTH_* ... or set OPENBOT_SINGLE_USER=true"
`.env.example` already sets OPENBOT_SINGLE_USER=true, so this means it was removed or edited.
Expand Down
2 changes: 1 addition & 1 deletion scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ info = json.loads(sys.argv[1])
status, agents = info.get("licenseStatus"), list(info.get("agents", {}))
if status != "valid":
print(f"\033[31m licence is '{status}', not 'valid'.\033[0m")
print("\033[31m Run: npx copilotkit@latest login && npx copilotkit@latest license --write\033[0m")
print("\033[31m Check INTELLIGENCE_API_KEY: npx copilotkit@latest login && npx copilotkit@latest project select\033[0m")
print("\033[31m See README.md for Intelligence setup.\033[0m")
raise SystemExit(1)
if not agents:
Expand Down
4 changes: 2 additions & 2 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@ag-ui/client": "0.0.57",
"@ag-ui/client": "0.0.59",
"@better-auth/drizzle-adapter": "^1.7.1",
"@better-auth/sso": "^1.7.1",
"@copilotkit/runtime": "1.69.0",
"@copilotkit/runtime": "1.70.1",
"@modelcontextprotocol/sdk": "^1.30.0",
"better-auth": "^1.7.1",
"cel-js": "^0.8.2",
Expand Down
23 changes: 17 additions & 6 deletions server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ export type RuntimeCapabilities = {
intelligence: IntelligenceSettings;
};

/** The Intelligence contract. Every field is required; see runtimeCapabilities. */
/**
* The Intelligence contract. Three values are required; see runtimeCapabilities.
*
* `licenseToken` is optional. Managed Intelligence derives entitlement from the project key, and
* `@copilotkit/runtime` declares `licenseToken` optional with a `COPILOTKIT_LICENSE_TOKEN` fallback
* of its own. A deployment that still holds one keeps passing it; nothing here requires it.
*/
export type IntelligenceSettings = {
apiUrl: string;
gatewayWsUrl: string;
apiKey: string;
licenseToken: string;
licenseToken?: string;
};

export type DockerComputerConfig = {
Expand Down Expand Up @@ -558,9 +564,15 @@ function oktaAuth(
/**
* Resolve the Intelligence contract, or refuse to start.
*
* All four values are required together. A partial set is the more dangerous shape than none at all:
* it means somebody intended to configure Intelligence and got it wrong, so failing on the partial
* set alone (as this did) let a completely unconfigured deployment through as if that were a choice.
* The three addressing values are required together. A partial set is the more dangerous shape than
* none at all: it means somebody intended to configure Intelligence and got it wrong, so failing on
* the partial set alone (as this did) let a completely unconfigured deployment through as if that
* were a choice.
*
* COPILOTKIT_LICENSE_TOKEN IS NO LONGER ONE OF THEM. Managed Intelligence issues a single project
* key and derives entitlement from it, and requiring a second credential here sent people hunting
* for a token the platform had stopped handing out. It is still read and still forwarded when a
* deployment sets one, which is what a self-hosted Intelligence with its own licence needs.
*/
function runtimeCapabilities(environment: Environment): RuntimeCapabilities {
const settings = {
Expand All @@ -574,7 +586,6 @@ function runtimeCapabilities(environment: Environment): RuntimeCapabilities {
INTELLIGENCE_API_URL: settings.apiUrl,
INTELLIGENCE_GATEWAY_WS_URL: settings.gatewayWsUrl,
INTELLIGENCE_API_KEY: settings.apiKey,
COPILOTKIT_LICENSE_TOKEN: settings.licenseToken,
})
.filter(([, value]) => !value)
.map(([name]) => name);
Expand Down
33 changes: 31 additions & 2 deletions server/tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ describe("deployment configuration", () => {
INTELLIGENCE_API_URL: baseEnvironment.INTELLIGENCE_API_URL,
INTELLIGENCE_GATEWAY_WS_URL: baseEnvironment.INTELLIGENCE_GATEWAY_WS_URL,
INTELLIGENCE_API_KEY: baseEnvironment.INTELLIGENCE_API_KEY,
COPILOTKIT_LICENSE_TOKEN: baseEnvironment.COPILOTKIT_LICENSE_TOKEN,
MANAGED_AGENT_AG_UI_URL: baseEnvironment.MANAGED_AGENT_AG_UI_URL,
MANAGED_AGENT_TOKEN: baseEnvironment.MANAGED_AGENT_TOKEN,
// Explicit, because no provider means every visitor is the administrator and a deployment has
Expand All @@ -96,7 +95,6 @@ describe("deployment configuration", () => {
"INTELLIGENCE_API_URL",
"INTELLIGENCE_GATEWAY_WS_URL",
"INTELLIGENCE_API_KEY",
"COPILOTKIT_LICENSE_TOKEN",
])("refuses to start when %s is missing", (name) => {
const environment: Record<string, string | undefined> = {
...baseEnvironment,
Expand All @@ -108,6 +106,37 @@ describe("deployment configuration", () => {
);
});

test("starts without COPILOTKIT_LICENSE_TOKEN, because managed Intelligence no longer issues one", () => {
const environment: Record<string, string | undefined> = {
...baseEnvironment,
};
delete environment.COPILOTKIT_LICENSE_TOKEN;

const config = loadConfig(environment);

if (config.runtime.mode !== "intelligence") {
throw new Error("expected the Intelligence runtime");
}
expect(config.runtime.intelligence.licenseToken).toBeUndefined();
expect(config.runtime.intelligence.apiKey).toBe(
baseEnvironment.INTELLIGENCE_API_KEY,
);
});

test("still forwards a licence token when a deployment sets one", () => {
const config = loadConfig({
...baseEnvironment,
COPILOTKIT_LICENSE_TOKEN: "self-hosted-licence",
});

if (config.runtime.mode !== "intelligence") {
throw new Error("expected the Intelligence runtime");
}
expect(config.runtime.intelligence.licenseToken).toBe(
"self-hosted-licence",
);
});

test("refuses to start when Intelligence is absent entirely, rather than degrading", () => {
expect(() =>
loadConfig({
Expand Down