From 8483f434f799ce79da3d076314223fcda7a6f658 Mon Sep 17 00:00:00 2001 From: Benjamin Taylor Date: Wed, 26 Aug 2026 09:57:06 -0500 Subject: [PATCH] fix(examples): stop overriding the managed Intelligence URL defaults (closes OSS-981) CopilotKitIntelligence resolves apiUrl/wsUrl to the managed hosts when they are omitted, and its own docstring says leaving both unset is always correct against the managed service. Every starter's runtime route supplied `?? "http://localhost:4201"` instead, so a managed reader who copied the block got a runtime aimed at a local stack that is not running -- the failure the starter's own .env.example warns about two files away. Replace the fallbacks with the conditional spread these same starters already use in channel-host.mts, so a self-hosted override still works and the managed default applies when it is absent. Three .env.example files also set the values uncommented, two of them directly under a comment telling the reader to leave them unset; comment those out to match the other nineteen starters. Guard both shapes in validate-intelligence-env-names.ts, which already polices the canonical Intelligence key name and hosts and runs unfiltered on every PR. The rule is the pattern rather than the literal, so a staging host substituted for localhost fails the same way. Local e2e harnesses and demo stacks that genuinely target a local deployment are allowlisted with their reasons. Co-Authored-By: Claude Opus 5 (1M context) --- .../static_intelligence-env-names.yml | 6 + .../app/api/copilotkit/[[...slug]]/route.tsx | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- examples/integrations/adk-angular/server.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- examples/integrations/agent-spec/.env.example | 8 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../lambdas/copilotkit-runtime/src/runtime.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- examples/integrations/llamaindex/.env.example | 4 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- examples/integrations/mcp-apps/.env.example | 4 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../app/api/copilotkit/[[...slug]]/route.ts | 9 +- .../validate-intelligence-env-names.test.ts | 144 ++++++++++++++++++ scripts/validate-intelligence-env-names.ts | 138 ++++++++++++++++- 28 files changed, 428 insertions(+), 74 deletions(-) create mode 100644 scripts/__tests__/validate-intelligence-env-names.test.ts diff --git a/.github/workflows/static_intelligence-env-names.yml b/.github/workflows/static_intelligence-env-names.yml index 081a5f325a..ca02dccf40 100644 --- a/.github/workflows/static_intelligence-env-names.yml +++ b/.github/workflows/static_intelligence-env-names.yml @@ -37,5 +37,11 @@ jobs: - run: pnpm install --frozen-lockfile + # The rules are unit-tested here rather than by a general runner: nothing + # else executes scripts/__tests__, so a rule that silently stopped + # matching would leave the check below passing on an empty result. + - name: Test the validator's rules + run: pnpm exec vitest run scripts/__tests__/validate-intelligence-env-names.test.ts + - name: Check Intelligence env var names are canonical run: pnpm check:intelligence-env-names diff --git a/examples/integrations/a2a-a2ui/app/api/copilotkit/[[...slug]]/route.tsx b/examples/integrations/a2a-a2ui/app/api/copilotkit/[[...slug]]/route.tsx index 22d8d5c14d..e0ecd83e20 100644 --- a/examples/integrations/a2a-a2ui/app/api/copilotkit/[[...slug]]/route.tsx +++ b/examples/integrations/a2a-a2ui/app/api/copilotkit/[[...slug]]/route.tsx @@ -90,9 +90,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/a2a-middleware/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/a2a-middleware/app/api/copilotkit/[[...slug]]/route.ts index d9510b7e1c..5e40d29e53 100644 --- a/examples/integrations/a2a-middleware/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/a2a-middleware/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub - replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/adk-angular/server.ts b/examples/integrations/adk-angular/server.ts index 7cdedc4dcf..6f4373a76c 100644 --- a/examples/integrations/adk-angular/server.ts +++ b/examples/integrations/adk-angular/server.ts @@ -19,9 +19,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. The id diff --git a/examples/integrations/adk/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/adk/src/app/api/copilotkit/[[...slug]]/route.ts index 2a39b7f43c..b41f708a44 100644 --- a/examples/integrations/adk/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/adk/src/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/agent-spec/.env.example b/examples/integrations/agent-spec/.env.example index 6f1338ae0d..dc4b98cb6e 100644 --- a/examples/integrations/agent-spec/.env.example +++ b/examples/integrations/agent-spec/.env.example @@ -4,5 +4,9 @@ AGENT_URL=http://localhost:8000/ # Optional: enable CopilotKit Intelligence Threads locally COPILOTKIT_LICENSE_TOKEN= INTELLIGENCE_API_KEY= -INTELLIGENCE_API_URL=http://localhost:4201 -INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4401 +# INTELLIGENCE_API_URL and INTELLIGENCE_GATEWAY_WS_URL point at a self-hosted +# or local Intelligence deployment only — leave them unset when using managed +# Intelligence, or the channel host and runtime will try to reach a local +# stack that usually is not running. +# INTELLIGENCE_API_URL=http://localhost:4201 +# INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4401 diff --git a/examples/integrations/agent-spec/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/agent-spec/src/app/api/copilotkit/[[...slug]]/route.ts index 7d6213a6fb..b8d7d192b0 100644 --- a/examples/integrations/agent-spec/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/agent-spec/src/app/api/copilotkit/[[...slug]]/route.ts @@ -25,9 +25,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/agentcore/infra-cdk/lambdas/copilotkit-runtime/src/runtime.ts b/examples/integrations/agentcore/infra-cdk/lambdas/copilotkit-runtime/src/runtime.ts index 76e91b6d0c..129c3bd96b 100644 --- a/examples/integrations/agentcore/infra-cdk/lambdas/copilotkit-runtime/src/runtime.ts +++ b/examples/integrations/agentcore/infra-cdk/lambdas/copilotkit-runtime/src/runtime.ts @@ -133,9 +133,12 @@ export function buildApp() { ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/agno/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/agno/src/app/api/copilotkit/[[...slug]]/route.ts index 2a39b7f43c..b41f708a44 100644 --- a/examples/integrations/agno/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/agno/src/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/claude-sdk-python/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/claude-sdk-python/src/app/api/copilotkit/[[...slug]]/route.ts index 94c5de9a8f..6cc3cd5a3c 100644 --- a/examples/integrations/claude-sdk-python/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/claude-sdk-python/src/app/api/copilotkit/[[...slug]]/route.ts @@ -21,9 +21,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/claude-sdk-typescript/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/claude-sdk-typescript/src/app/api/copilotkit/[[...slug]]/route.ts index 8eb0f037ab..07f73bab18 100644 --- a/examples/integrations/claude-sdk-typescript/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/claude-sdk-typescript/src/app/api/copilotkit/[[...slug]]/route.ts @@ -21,9 +21,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/crewai-crews/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/crewai-crews/src/app/api/copilotkit/[[...slug]]/route.ts index f2abe7ae9e..342f0e7f9a 100644 --- a/examples/integrations/crewai-crews/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/crewai-crews/src/app/api/copilotkit/[[...slug]]/route.ts @@ -23,9 +23,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/crewai-flows/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/crewai-flows/src/app/api/copilotkit/[[...slug]]/route.ts index 90a69fde67..d5809a7cf6 100644 --- a/examples/integrations/crewai-flows/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/crewai-flows/src/app/api/copilotkit/[[...slug]]/route.ts @@ -18,9 +18,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/langgraph-fastapi/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/langgraph-fastapi/src/app/api/copilotkit/[[...slug]]/route.ts index b6f67cb53b..8278f1dfca 100644 --- a/examples/integrations/langgraph-fastapi/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/langgraph-fastapi/src/app/api/copilotkit/[[...slug]]/route.ts @@ -24,9 +24,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/langgraph-js/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/langgraph-js/src/app/api/copilotkit/[[...slug]]/route.ts index 92cac76ccd..d5867ebe32 100644 --- a/examples/integrations/langgraph-js/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/langgraph-js/src/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/langgraph-python/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/langgraph-python/src/app/api/copilotkit/[[...slug]]/route.ts index ef69d87494..4be73bc6ac 100644 --- a/examples/integrations/langgraph-python/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/langgraph-python/src/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/llamaindex/.env.example b/examples/integrations/llamaindex/.env.example index 919cbe3a3f..90a084581d 100644 --- a/examples/integrations/llamaindex/.env.example +++ b/examples/integrations/llamaindex/.env.example @@ -8,5 +8,5 @@ INTELLIGENCE_API_KEY= # self-hosted or local Intelligence deployment only — leave them unset (or # blank) when using managed Intelligence, or the channel host and runtime # will try to reach a local stack that usually is not running. -INTELLIGENCE_API_URL=http://localhost:4203 -INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4403 +# INTELLIGENCE_API_URL=http://localhost:4203 +# INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4403 diff --git a/examples/integrations/llamaindex/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/llamaindex/src/app/api/copilotkit/[[...slug]]/route.ts index 5e1b9a4cc3..f00bdb9b8e 100644 --- a/examples/integrations/llamaindex/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/llamaindex/src/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/mastra/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/mastra/src/app/api/copilotkit/[[...slug]]/route.ts index cc92c9cbfa..4175576c1a 100644 --- a/examples/integrations/mastra/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/mastra/src/app/api/copilotkit/[[...slug]]/route.ts @@ -14,9 +14,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/mcp-apps/.env.example b/examples/integrations/mcp-apps/.env.example index e906b4ea43..05a614ac67 100644 --- a/examples/integrations/mcp-apps/.env.example +++ b/examples/integrations/mcp-apps/.env.example @@ -7,5 +7,5 @@ INTELLIGENCE_API_KEY= # self-hosted or local Intelligence deployment only — leave them unset (or # blank) when using managed Intelligence, or the channel host and runtime # will try to reach a local stack that usually is not running. -INTELLIGENCE_API_URL=http://localhost:4201 -INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4401 +# INTELLIGENCE_API_URL=http://localhost:4201 +# INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4401 diff --git a/examples/integrations/mcp-apps/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/mcp-apps/app/api/copilotkit/[[...slug]]/route.ts index 222410bd11..2190f2c556 100644 --- a/examples/integrations/mcp-apps/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/mcp-apps/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/ms-agent-framework-dotnet/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/ms-agent-framework-dotnet/src/app/api/copilotkit/[[...slug]]/route.ts index b2964876f9..900a552aee 100644 --- a/examples/integrations/ms-agent-framework-dotnet/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/ms-agent-framework-dotnet/src/app/api/copilotkit/[[...slug]]/route.ts @@ -16,9 +16,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), identifyUser: () => ({ id: "demo-user", name: "Demo User" }), licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN, diff --git a/examples/integrations/ms-agent-framework-python/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/ms-agent-framework-python/src/app/api/copilotkit/[[...slug]]/route.ts index 27c0890190..0f734e6fdd 100644 --- a/examples/integrations/ms-agent-framework-python/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/ms-agent-framework-python/src/app/api/copilotkit/[[...slug]]/route.ts @@ -19,9 +19,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), identifyUser: () => ({ id: "demo-user", name: "Demo User" }), licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN, diff --git a/examples/integrations/pydantic-ai/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/pydantic-ai/src/app/api/copilotkit/[[...slug]]/route.ts index db19db67e2..267a276879 100644 --- a/examples/integrations/pydantic-ai/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/pydantic-ai/src/app/api/copilotkit/[[...slug]]/route.ts @@ -19,9 +19,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your own auth-derived user identity (e.g. OIDC) // before any multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/strands-python/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/strands-python/src/app/api/copilotkit/[[...slug]]/route.ts index 0f613233e7..2e1b906387 100644 --- a/examples/integrations/strands-python/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/strands-python/src/app/api/copilotkit/[[...slug]]/route.ts @@ -21,9 +21,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/examples/integrations/strands-typescript/src/app/api/copilotkit/[[...slug]]/route.ts b/examples/integrations/strands-typescript/src/app/api/copilotkit/[[...slug]]/route.ts index a584f627fc..7ce452a895 100644 --- a/examples/integrations/strands-typescript/src/app/api/copilotkit/[[...slug]]/route.ts +++ b/examples/integrations/strands-typescript/src/app/api/copilotkit/[[...slug]]/route.ts @@ -21,9 +21,12 @@ const runtime = new CopilotRuntime({ ? { intelligence: new CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY ?? "", - apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201", - wsUrl: - process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401", + ...(process.env.INTELLIGENCE_API_URL + ? { apiUrl: process.env.INTELLIGENCE_API_URL } + : {}), + ...(process.env.INTELLIGENCE_GATEWAY_WS_URL + ? { wsUrl: process.env.INTELLIGENCE_GATEWAY_WS_URL } + : {}), }), // Demo stub — replace with your real auth-derived user identity before any // multi-user deployment, or all users share one thread history. diff --git a/scripts/__tests__/validate-intelligence-env-names.test.ts b/scripts/__tests__/validate-intelligence-env-names.test.ts new file mode 100644 index 0000000000..79433d7224 --- /dev/null +++ b/scripts/__tests__/validate-intelligence-env-names.test.ts @@ -0,0 +1,144 @@ +import { describe, expect, it } from "vitest"; +import { + findViolations, + managedUrlEnvFileAssignment, + managedUrlFallback, +} from "../validate-intelligence-env-names.js"; + +/** + * `CopilotKitIntelligence` resolves `apiUrl`/`wsUrl` to the managed hosts when + * they are omitted, so supplying any fallback for the two env vars that feed + * them overrides that default. A starter that does it points a managed user at + * whatever the fallback names — in practice a local stack that is not running + * (OSS-981). + * + * The rule is the pattern, not the literal: a staging host substituted for + * localhost would be just as wrong, so the check flags the fallback itself. + */ +describe("managedUrlFallback", () => { + it("flags a nullish fallback on the API URL", () => { + expect( + managedUrlFallback( + ' apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201",', + ), + ).toBe("INTELLIGENCE_API_URL"); + }); + + it("flags a nullish fallback on the gateway websocket URL", () => { + expect( + managedUrlFallback( + ' process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401",', + ), + ).toBe("INTELLIGENCE_GATEWAY_WS_URL"); + }); + + it("flags a logical-or fallback, which fails the same way", () => { + expect( + managedUrlFallback( + ' const apiUrl = process.env.INTELLIGENCE_API_URL || "https://staging.example.com";', + ), + ).toBe("INTELLIGENCE_API_URL"); + }); + + it("allows the conditional spread, which leaves the managed default in place", () => { + expect( + managedUrlFallback(" ...(process.env.INTELLIGENCE_API_URL"), + ).toBeNull(); + expect( + managedUrlFallback( + " ? { apiUrl: process.env.INTELLIGENCE_API_URL }", + ), + ).toBeNull(); + }); + + it("allows a bare read with no default", () => { + expect( + managedUrlFallback(" apiUrl: process.env.INTELLIGENCE_API_URL,"), + ).toBeNull(); + }); + + it("allows an env file assignment, which is a value and not a code default", () => { + expect( + managedUrlFallback("# INTELLIGENCE_API_URL=http://localhost:4201"), + ).toBeNull(); + expect( + managedUrlFallback("INTELLIGENCE_API_URL=http://localhost:4203"), + ).toBeNull(); + }); + + it("ignores an unrelated variable that merely takes a fallback", () => { + expect( + managedUrlFallback( + ' url: process.env.AGENT_URL ?? "http://localhost:8000/",', + ), + ).toBeNull(); + }); +}); + +/** + * The same failure by a second route. An `.env.example` is copied to `.env`, so + * an uncommented managed URL there hands the reader the local value the code no + * longer defaults to. Three starters did it, two of them directly under a + * comment telling the reader to leave the variable unset (OSS-981). + */ +describe("managedUrlEnvFileAssignment", () => { + it("flags an uncommented assignment with a value", () => { + expect( + managedUrlEnvFileAssignment("INTELLIGENCE_API_URL=http://localhost:4203"), + ).toBe("INTELLIGENCE_API_URL"); + expect( + managedUrlEnvFileAssignment( + "INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4403", + ), + ).toBe("INTELLIGENCE_GATEWAY_WS_URL"); + }); + + it("allows a commented assignment, which sets nothing", () => { + expect( + managedUrlEnvFileAssignment( + "# INTELLIGENCE_API_URL=http://localhost:4201", + ), + ).toBeNull(); + }); + + it("allows an empty assignment, which is the documented managed setting", () => { + expect(managedUrlEnvFileAssignment("INTELLIGENCE_API_URL=")).toBeNull(); + }); + + it("ignores a different Intelligence variable", () => { + expect( + managedUrlEnvFileAssignment("INTELLIGENCE_API_KEY=cpk_example"), + ).toBeNull(); + }); +}); + +describe("the repository", () => { + // A repo-wide scan: several `git grep` passes over the whole tree. + it("never overrides the managed Intelligence URL defaults", () => { + const offenders = findViolations().filter( + (violation) => violation.reason === MANAGED_URL_FALLBACK_REASON, + ); + + expect( + offenders.map((violation) => `${violation.file}:${violation.line}`), + ).toEqual([]); + }, 60_000); + + it("never ships an env example that sets a managed Intelligence URL", () => { + const offenders = findViolations().filter( + (violation) => violation.reason === MANAGED_URL_ENV_FILE_REASON, + ); + + expect( + offenders.map((violation) => `${violation.file}:${violation.line}`), + ).toEqual([]); + }, 60_000); +}); + +/** Kept in step with the reason string the validator reports. */ +const MANAGED_URL_FALLBACK_REASON = + "overrides the managed Intelligence default; omit the fallback"; + +/** Kept in step with the reason string the validator reports. */ +const MANAGED_URL_ENV_FILE_REASON = + "env example sets a managed Intelligence URL; comment it out"; diff --git a/scripts/validate-intelligence-env-names.ts b/scripts/validate-intelligence-env-names.ts index de5a3a247a..7aa83501aa 100644 --- a/scripts/validate-intelligence-env-names.ts +++ b/scripts/validate-intelligence-env-names.ts @@ -30,9 +30,19 @@ import * as path from "node:path"; * The managed pair is `api.intelligence.copilotkit.ai` / * `realtime.intelligence.copilotkit.ai`. * + * Finally it guards the two env vars that feed `CopilotKitIntelligence`'s + * `apiUrl` and `wsUrl`. Those options resolve to the managed hosts when they are + * omitted, so supplying a code fallback for either variable silently overrides + * the one setting that is always correct against the managed service. Every + * starter route did exactly that, defaulting a managed reader onto a local + * stack that is not running (OSS-981) — the failure its own `.env.example` + * warns about. The rule is the pattern rather than the literal: a staging host + * substituted for localhost would be just as wrong. + * * This is a documentation-drift guard, not a runtime check. It fails on a * retired name reappearing anywhere, on the alias appearing outside its - * allowlist, and on a dead host appearing outside its allowlist. + * allowlist, on a dead host appearing outside its allowlist, and on a managed + * URL fallback appearing outside its allowlist. */ const REPO_ROOT = path.resolve(__dirname, ".."); @@ -102,6 +112,99 @@ const DEAD_HOST_ALLOWLIST = [ "scripts/validate-intelligence-env-names.ts", ]; +/** + * Env vars that feed `CopilotKitIntelligence`'s `apiUrl` and `wsUrl`. Both + * options default to the managed hosts when omitted, so a fallback here is + * never load-bearing — it can only replace a correct default with a worse one. + */ +const MANAGED_URL_ENV_VARS = [ + "INTELLIGENCE_API_URL", + "INTELLIGENCE_GATEWAY_WS_URL", +] as const; + +/** Reported for a code fallback on a {@link MANAGED_URL_ENV_VARS} entry. */ +const MANAGED_URL_FALLBACK_REASON = + "overrides the managed Intelligence default; omit the fallback"; + +/** + * Paths allowed to write a managed URL fallback. + * + * Both carry the pattern as text — the rule's own definition and its fixtures — + * so matching them would make the check fail on itself. + */ +const MANAGED_URL_FALLBACK_ALLOWLIST = [ + "scripts/validate-intelligence-env-names.ts", + "scripts/__tests__/validate-intelligence-env-names.test.ts", + // Playwright harnesses that stand up a local Intelligence on dedicated ports + // and drive it with a seed key. Here the fallback is the point: resolving to + // the managed hosts would aim an offline test suite at production. + "examples/showcases/banking/playwright.config.ts", + "examples/showcases/reskinnable-demo/playwright.config.ts", +]; + +/** + * Returns the managed URL env var this line supplies a default for, or `null`. + * + * Only a `process.env` read can carry a code default. A bare `NAME=value` line + * in an `.env.example` is a value a reader opts into, not a default that + * overrides one, so it is left alone; and the conditional-spread form + * (`...(process.env.X ? { apiUrl: process.env.X } : {})`) is the correct + * pattern, which passes because it never names a fallback. + * + * @param text - One line of source. + * @returns The offending variable name, or `null` when the line is fine. + */ +export function managedUrlFallback(text: string): string | null { + for (const name of MANAGED_URL_ENV_VARS) { + if ( + new RegExp(String.raw`process\.env\.${name}\s*(\?\?|\|\|)`).test(text) + ) { + return name; + } + } + return null; +} + +/** Reported for an env example that assigns a {@link MANAGED_URL_ENV_VARS} entry. */ +const MANAGED_URL_ENV_FILE_REASON = + "env example sets a managed Intelligence URL; comment it out"; + +/** + * Paths allowed to assign a managed URL in an env example. + * + * `agentcore/docker` is the local development stack documented in + * `agentcore/docs/LOCAL_DEVELOPMENT.md`; its whole purpose is a local + * deployment, so naming one is correct there. + */ +const MANAGED_URL_ENV_FILE_ALLOWLIST = [ + "examples/integrations/agentcore/docker/.env.example", + // Local demo stacks, each pinned to its own vendored docker-compose ports and + // seeded org key so the two can run side by side. Both name a local + // deployment on purpose; neither is a managed-service starting point. + "examples/showcases/banking/.env.example", + "examples/showcases/reskinnable-demo/.env.example", +]; + +/** + * Returns the managed URL env var this env-file line assigns, or `null`. + * + * An `.env.example` is copied to `.env`, so an uncommented assignment hands the + * reader a value rather than leaving the managed default in place. A commented + * line documents the self-hosted override without setting it, and an empty + * assignment is the documented managed setting; both pass. + * + * @param text - One line of an env file. + * @returns The offending variable name, or `null` when the line is fine. + */ +export function managedUrlEnvFileAssignment(text: string): string | null { + for (const name of MANAGED_URL_ENV_VARS) { + if (new RegExp(String.raw`^\s*${name}=\S`).test(text)) { + return name; + } + } + return null; +} + interface Violation { file: string; line: number; @@ -175,6 +278,33 @@ export function findViolations(): Violation[] { } } + for (const envVar of MANAGED_URL_ENV_VARS) { + for (const hit of grepRepo(envVar)) { + if (MANAGED_URL_FALLBACK_ALLOWLIST.includes(hit.file)) continue; + if (!managedUrlFallback(hit.text)) continue; + violations.push({ + file: hit.file, + line: hit.line, + name: envVar, + reason: MANAGED_URL_FALLBACK_REASON, + }); + } + } + + for (const envVar of MANAGED_URL_ENV_VARS) { + for (const hit of grepRepo(envVar)) { + if (!path.basename(hit.file).startsWith(".env")) continue; + if (MANAGED_URL_ENV_FILE_ALLOWLIST.includes(hit.file)) continue; + if (!managedUrlEnvFileAssignment(hit.text)) continue; + violations.push({ + file: hit.file, + line: hit.line, + name: envVar, + reason: MANAGED_URL_ENV_FILE_REASON, + }); + } + } + return violations; } @@ -199,7 +329,11 @@ function main(): void { "provisions. The canonical hosts are api.intelligence.copilotkit.ai and\n" + "realtime.intelligence.copilotkit.ai. If a site legitimately implements the deprecated\n" + "alias fallback, or genuinely needs a non-resolving host, add it to ALIAS_ALLOWLIST or\n" + - "DEAD_HOST_ALLOWLIST in scripts/validate-intelligence-env-names.ts.", + "DEAD_HOST_ALLOWLIST in scripts/validate-intelligence-env-names.ts.\n\n" + + "For a managed URL fallback, delete the fallback rather than changing it: apiUrl and\n" + + "wsUrl already default to the managed hosts when omitted. To keep a self-hosted override\n" + + "working, spread it conditionally:\n" + + " ...(process.env.INTELLIGENCE_API_URL ? { apiUrl: process.env.INTELLIGENCE_API_URL } : {}),", ); process.exit(1); }