From d233de2146416c5b82d820d8e11e0d3c957127c4 Mon Sep 17 00:00:00 2001 From: Jerel John Velarde Date: Wed, 5 Aug 2026 08:18:38 -0700 Subject: [PATCH 1/2] docs: rebuild onboarding around the CopilotKit channels CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documented path to a first reply could not work. Both Slack manifests set socket_mode_enabled with no request_url, while managed delivery requires Slack to POST to an Intelligence-hosted Request URL — so an app created the way the README instructed installed green and delivered nothing, forever. setup.md also asked for an xapp- app-level token, which does not exist on this path; the adapter takes a bot token and a signing secret. Replace the hand-written Channel walkthrough with `copilotkit channels add`, which generates a Slack manifest already pointed at the right Request URL and so removes the failure structurally rather than documenting around it. - README is now a five-minute path to a working bot, ending in three trigger checks that separate "installed" from "answering": mention replies, unmentioned follow-up in a subscribed thread replies, unmentioned message in a fresh conversation stays silent. - Document the diagnostics that were missing: `channels status --json`, LOG_LEVEL=debug (the logger defaults to error while every Channel lifecycle breadcrumb logs at warn), /invite, and the signals that look like health but are not — ready() resolving on setup_required, /api/copilotkit/info 200. - setup.md becomes reference only. Slash commands and modals are now marked unverified on the managed path instead of listed as working features. - Move the internal @kite cutover to docs/migration-kite.md, out of the path of anyone reading this repo for the first time, with its credential steps corrected. - Warn that Channel names claim deliveries: two runtimes on one name race and the loser is silently starved, so forks need their own name. Delete both Socket Mode manifests. OpenTag only supports the managed path, so they could not work here and existed only to mislead. Stop restating pinned versions in prose. README and setup.md both said 0.7.0 while package.json said 0.7.3, and app/cleanup.test.ts asserted 0.7.0 too — that third copy had main's test suite red. It now asserts the pin *shape*, so it keeps guaranteeing reproducible deploys without drifting on every bump. Gitignore the artifacts `copilotkit channels setup` writes (.agents, .claude/skills, agent/skills, skills-lock.json, .copilotkit/artifacts) while leaving .copilotkit/channels.json trackable. --- .env.example | 14 +- .gitignore | 10 ++ README.md | 302 +++++++++++++++++++++++++--------------- app/cleanup.test.ts | 15 +- docs/migration-kite.md | 125 +++++++++++++++++ setup.md | 251 +++++++++++++++++++-------------- slack-app-manifest.json | 103 -------------- slack-app-manifest.yaml | 88 ------------ 8 files changed, 493 insertions(+), 415 deletions(-) create mode 100644 docs/migration-kite.md delete mode 100644 slack-app-manifest.json delete mode 100644 slack-app-manifest.yaml diff --git a/.env.example b/.env.example index e1ffe93d..97f9620c 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,16 @@ -# -- Intelligence Variables -- -export INTELLIGENCE_API_KEY=cpk-... # Create a runtime key at https://intelligence.copilotkit.ai. -export INTELLIGENCE_CHANNEL_NAME=open-tag # Must match the Channel name; defaults to "open-tag". +# -- Runtime Variables -- +export INTELLIGENCE_API_KEY=cpk-... # Runtime key from `copilotkit project select`, or https://intelligence.copilotkit.ai. +export INTELLIGENCE_CHANNEL_NAME=open-tag # Must match .copilotkit/channels.json exactly. Forks: use your own name. +export AGENT_URL=http://localhost:8123/ # AG-UI endpoint the runtime calls; the bundled Python agent serves this locally. + +# Slack and Teams credentials are NOT set here. Intelligence owns the adapters; +# `copilotkit channels add` tells you where its credentials go. # -- Agent Variables -- export OPENAI_API_KEY=sk-... # Create a key at https://platform.openai.com/api-keys. -export AGENT_URL=http://localhost:8123/ # AG-UI endpoint; the bundled Deep Agent uses this locally. + +# -- Runtime Overrides (Optional) -- +# export LOG_LEVEL=debug # Defaults to "error"; Channel lifecycle breadcrumbs log at "warn". # -- Agent Overrides (Optional) -- # export TAVILY_API_KEY=tvly-... diff --git a/.gitignore b/.gitignore index 76fabf87..196416bc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,16 @@ node_modules .env .env.local +# CopilotKit CLI channel artifacts. +# .copilotkit/channels.json is the tracked Channel declaration; artifacts are not. +.copilotkit/artifacts + +# CopilotKit CLI skill installs (`copilotkit channels setup`, `copilotkit skills install`) +.agents +.claude/skills +agent/skills +skills-lock.json + # E2E harness artifacts e2e/.chrome-profile e2e/results diff --git a/README.md b/README.md index ba07d2d2..c3f65a0f 100644 --- a/README.md +++ b/README.md @@ -5,97 +5,159 @@ Teams, with research available on demand. It runs on CopilotKit Channels, connects to CopilotKit Intelligence, and uses a Python LangGraph agent over AG-UI. -The launch supports: +Slack and Microsoft Teams are supported today. Discord, Telegram, and WhatsApp +are coming soon. -- Slack through Intelligence, including the existing production `@kite` app. -- Microsoft Teams through Intelligence. +## Quick start -Discord, Telegram, and WhatsApp are coming soon. +Prerequisites: Node.js 22+, pnpm, Python 3.12, +[`uv`](https://docs.astral.sh/uv/), a CopilotKit account, an OpenAI API key, +and a Slack workspace you can install an app into. -## Architecture +### 1. Install dependencies -```text -Slack / Microsoft Teams - │ - ▼ -CopilotKit Intelligence - │ realtime - ▼ -runtime (Node + CopilotRuntime with embedded Channels) - │ AG-UI - ▼ -agent (Python + LangGraph deepagents) - ├── OpenAI - ├── Tavily (optional) - ├── GitHub MCP (optional, read-only) - ├── PostHog MCP (optional, read-only) - ├── Linear MCP (optional) - └── Notion MCP (optional remote server) +```bash +pnpm install ``` -There is one canonical runtime host: [`server.ts`](./server.ts). -[`app/index.ts`](./app/index.ts) composes one `CopilotKitIntelligence`, one -`CopilotRuntime`, and one adapter-free managed Channel. Intelligence owns its -Slack and Microsoft Teams adapters, credentials, and attachments. +### 2. Create the managed Channel -The SDK versions are pinned for reproducible deploys: +Do this **before** touching Slack. The Channel is what generates a Slack app +manifest already pointed at the right Request URL, so creating the Slack app +first means creating the wrong one. -- `@copilotkit/channels@0.7.0` -- `@copilotkit/runtime@1.66.0` +```bash +npx --yes copilotkit@latest project select +``` -## Quick start +```bash +npx --yes copilotkit@latest channels add --name open-tag --display-name "OpenTag" --adapter slack --json +``` -Prerequisites: Node.js 22+, pnpm, Python 3.12, and -[`uv`](https://docs.astral.sh/uv/). +`channels add` declares the Channel in `.copilotkit/channels.json`, creates it +on the server, and returns a JSON envelope with one of three states: -1. Install the Node dependencies. +- `completed` — the adapter is attached. Continue to step 3. +- `blocked` — a normal pause waiting on you in the Slack console. Read + `nextAction`: it carries the prefilled manifest link, the exact environment + variable names to set, and the `resumeCommand` to run afterward. This exits 0. +- `failed` — stop and read the error code. Do not continue. - ```bash - pnpm install - ``` +Follow the emitted `nextAction` rather than remembered Slack steps. Three +details in that flow cost the most time when skipped: -2. Configure both services from the shared root environment. +- After creating the app from the link, open **OAuth & Permissions** and choose + **Reinstall to Workspace**. Slack applies the manifest's real scopes only on + reinstall. +- Take the **Bot User OAuth Token** (`xoxb-`) from **OAuth & Permissions**, not + the token shown in the app-creation modal, and the **Signing Secret** from + **Basic Information → App Credentials**. Those two values are all the Slack + adapter wants — there is no app-level `xapp-` token anywhere on this path. +- The signing secret is reissued on reinstall. If auth fails right after a + reinstall, suspect a stale stored secret before a missing scope. - ```bash - cp .env.example .env - ``` +`--name` is a slug and must match `INTELLIGENCE_CHANNEL_NAME` character for +character. If you are running a fork against your own project, pick your own +name — see [Channel names claim deliveries](#channel-names-claim-deliveries). - Set: +For Microsoft Teams, use `--adapter teams`. Two Teams steps stay yours because +nothing can work around them: granting tenant admin consent, and uploading the +app package through **Apps → Manage your apps → Upload an app**. - ```dotenv - OPENAI_API_KEY=sk-... - AGENT_URL=http://localhost:8123/ - INTELLIGENCE_API_KEY=cpk-... - ``` +### 3. Configure the environment - Tavily, GitHub, PostHog, Linear, and Notion are optional. Both Node and - Python load this root `.env`; Railway supplies the same values as service - variables. +```bash +cp .env.example .env +``` + +Set: + +```dotenv +OPENAI_API_KEY=sk-... +AGENT_URL=http://localhost:8123/ +INTELLIGENCE_API_KEY=cpk-... +INTELLIGENCE_CHANNEL_NAME=open-tag +``` - `INTELLIGENCE_API_URL` and `INTELLIGENCE_GATEWAY_WS_URL` default to the - production Intelligence endpoints. `INTELLIGENCE_CHANNEL_NAME` defaults to - `open-tag`. +Both the Node runtime and the Python agent load this one root `.env`; Railway +supplies the same values as service variables. Tavily, GitHub, PostHog, Linear, +and Notion are optional — see [Optional research +sources](#optional-research-sources). - The Intelligence API key selects a project, and the Channel name selects a - Channel inside that project. Use a non-production project and API key - locally; Railway keeps its existing production key and `open-tag` Channel. +`INTELLIGENCE_API_URL` and `INTELLIGENCE_GATEWAY_WS_URL` already default to the +production Intelligence endpoints in +[`app/env.ts`](./app/env.ts), so leaving them unset is correct. + +### 4. Run the stack + +```bash +pnpm dev +``` + +The `predev` hook syncs the locked Python environment and installs Playwright's +Chromium. `pnpm dev` then runs the Python agent with reload enabled and the Node +runtime in watch mode. The runtime waits for its Intelligence connection to +become ready before its HTTP listener accepts traffic. + +### 5. Invite the bot + +```text +/invite @OpenTag +``` -3. In the project selected by your local Intelligence API key, create one - managed Channel named `open-tag` and configure its Slack and Microsoft - Teams adapters. +Installed in the workspace is not the same as present in a conversation. Slack +emits no `app_mention` at all for a channel the app is not a member of, so +without this OpenTag looks broken while behaving correctly. -4. Run the complete local stack. +## Prove it works - ```bash - pnpm dev - ``` +A Channel that installs cleanly and answers nothing is the most expensive +failure available here, because it looks finished. Three checks separate the +two. Send them from a real human account: - The `predev` hook syncs the locked Python environment and installs - Playwright's Chromium browser. `pnpm dev` then runs the Python agent with - reload enabled and the Node runtime in watch mode. +1. **Mention it.** `@OpenTag what changed in the last deploy?` — expect a + useful, model-backed reply. +2. **Follow up without mentioning it,** in that same thread — expect a reply. + A mention subscribes the thread; unmentioned messages run the agent only in + already-subscribed threads. +3. **Send an unmentioned message in a fresh conversation** — expect + **silence**. A reply here means the trigger rules are wrong. -The runtime waits for its Intelligence connection to become ready before its -HTTP listener accepts traffic. +If any of those fail, in this order: + +```bash +npx --yes copilotkit@latest channels status --json +``` + +It reports declaration, source, server, adapter, environment, and lifecycle +diagnostics. Resolve every one. Two of its warnings are expected for OpenTag +and are not faults: it flags `INTELLIGENCE_API_URL` and +`INTELLIGENCE_GATEWAY_WS_URL` as unset because +[`app/env.ts`](./app/env.ts) defaults them in code rather than in `.env`. + +```bash +LOG_LEVEL=debug pnpm runtime +``` + +The runtime logger defaults to `error`, while every Channel lifecycle +breadcrumb is emitted at `warn` — including `channel "" requires setup`, +the single highest-value diagnostic here. At the default level it is written and +discarded. + +Note that the runtime does not hot-reload Channel wiring. After editing a +handler, the agent, or the Channel, restart the process and confirm `online` +again before retesting. A stale process answering with the old behavior is +indistinguishable from a change that did not work. + +### Channel names claim deliveries + +Managed delivery is claim-based: two runtimes declaring the same Channel name in +the same Intelligence project race per delivery, and the loser silently receives +nothing. The tell is a Slack reply your terminal knows nothing about. + +`INTELLIGENCE_CHANNEL_NAME` defaults to `open-tag`, which is the name the +production deployment uses. Give a local or forked runtime its own Intelligence +project, its own API key, and its own Channel name. ## What OpenTag includes @@ -113,54 +175,72 @@ The Python agent is the only supported backend. Its identity and behavior live in [`agent/agent.py`](./agent/agent.py); the Channel UI and behavior live under [`app/`](./app/). -## Platform setup - -### Intelligence - -Use the normal Intelligence flow for both launch platforms: +## Architecture -1. Create one OpenTag project. -2. Create one Channel named `open-tag`. -3. Issue a runtime API key. -4. Configure the Slack and Microsoft Teams adapters on that Channel. -5. Run one `pnpm runtime` process with that Channel name and key. +```text +Slack / Microsoft Teams + │ HTTPS to an Intelligence-hosted Request URL + ▼ +CopilotKit Intelligence + │ outbound websocket from your runtime + ▼ +runtime (Node + CopilotRuntime with embedded Channels) + │ AG-UI + ▼ +agent (Python + LangGraph deepagents) + ├── OpenAI + ├── Tavily (optional) + ├── GitHub MCP (optional, read-only) + ├── PostHog MCP (optional, read-only) + ├── Linear MCP (optional) + └── Notion MCP (optional remote server) +``` -No organization, project, Channel ID, or runtime-instance ID environment -variables—or Slack/Teams credentials—are required by the runtime. +Neither leg is Socket Mode, and neither needs a tunnel or a public URL of your +own. Slack reaches Intelligence over HTTPS, authenticated by the signing secret +Intelligence holds. Intelligence reaches your runtime over a websocket your +process opens outbound, authenticated by `INTELLIGENCE_API_KEY`. -### Slack manifests +There is one canonical runtime host: [`server.ts`](./server.ts). +[`app/index.ts`](./app/index.ts) composes one `CopilotKitIntelligence`, one +`CopilotRuntime`, and one adapter-free managed Channel. Intelligence owns the +Slack and Microsoft Teams adapters, their credentials, and attachments — no +platform credential belongs in this repository's environment. -[`slack-app-manifest.yaml`](./slack-app-manifest.yaml) and -[`slack-app-manifest.json`](./slack-app-manifest.json) describe OpenTag for new -installations. +`@copilotkit/channels` and `@copilotkit/runtime` are pinned for reproducible +deploys. [`package.json`](./package.json) is the source of truth for both +versions. -For the production migration, **do not recreate or reinstall the existing -Slack app**. Reusing it preserves the bot user, workspace installation, and -`@kite` handle. Stop the old Socket Mode consumer before attaching its existing -`xapp` and `xoxb` tokens in Intelligence so only one consumer is active. +One naming collision is worth internalizing: the CopilotKit CLI's `channels` +commands configure **managed Intelligence Channels**. They do not configure the +open-source `@copilotkit/channels` adapter packages, which are a separate +product that shares the words "channels" and "Slack". OpenTag uses the package +to define its Channel and Intelligence to deliver to it. ## Optional research sources -- `TAVILY_API_KEY` enables live web research. Without it, OpenTag still chats, - triages requests and renders UI from model knowledge. -- `GITHUB_PERSONAL_ACCESS_TOKEN` enables read-only repository, code, issue, and - pull-request search through GitHub's hosted MCP. `GITHUB_MCP_URL` can override - the endpoint; OpenTag still sends GitHub's read-only configuration header. -- `POSTHOG_PERSONAL_API_KEY` enables PostHog analytics through its hosted MCP. - Create the key with PostHog's **MCP Server** preset. The default connection is - token-efficient and read-only; `POSTHOG_MCP_URL` can override the endpoint. -- `LINEAR_API_KEY` enables the hosted Linear MCP. -- Notion is optional and remote-only. Set both `NOTION_MCP_URL` and - `NOTION_MCP_AUTH_TOKEN`; setting only one disables the integration. +Every one of these is optional. Without them OpenTag still chats, triages +requests, and renders UI from model knowledge. + +| Variable | Enables | +| --- | --- | +| `TAVILY_API_KEY` | Live web research | +| `GITHUB_PERSONAL_ACCESS_TOKEN` | Read-only repository, code, issue, and PR search | +| `POSTHOG_PERSONAL_API_KEY` | PostHog analytics, read-only (use the **MCP Server** key preset) | +| `LINEAR_API_KEY` | Hosted Linear MCP | +| `NOTION_MCP_URL` + `NOTION_MCP_AUTH_TOKEN` | Remote Notion MCP; setting only one disables it | Every Linear and Notion mutation is intercepted in code before the MCP request runs. The interceptor emits `confirm_write` and proceeds only after approval; reads and rendering do not pause. -## Railway +[`setup.md`](./setup.md) documents each source, its overrides, and the full +environment contract. + +## Deploying [`.railway/railway.ts`](./.railway/railway.ts) defines exactly two services, -all sourced from `CopilotKit/OpenTag` on `main`: +both sourced from `CopilotKit/OpenTag` on `main`: | Service | Root | Start | Health | | --- | --- | --- | --- | @@ -168,15 +248,9 @@ all sourced from `CopilotKit/OpenTag` on `main`: | `runtime` | repository root | `pnpm runtime` | `/api/copilotkit/info` | The runtime reaches the agent over Railway private networking and embeds the -managed `open-tag` Channel. Railway sets -`INTELLIGENCE_CHANNEL_NAME=open-tag`; Intelligence owns both platform -adapters. The runtime API key is preserved. OpenAI is required on `agent`; -Tavily, GitHub search, PostHog, Linear, and remote Notion settings are optional. -Connecting both services to `main` enables GitHub-triggered deployments after -merges. - -The repository configuration does not mutate the existing production Railway -project. Inventory and cutover should happen after Railway authentication. +managed Channel. Connecting both services to `main` enables GitHub-triggered +deployments after merges. See [`setup.md`](./setup.md#railway) for the variable +contract. ## Verification @@ -190,8 +264,14 @@ node node_modules/railway/dist/iac/bin.js ``` The Slack live harness is documented in [`e2e/README.md`](./e2e/README.md). -Production acceptance is one end-to-end `@kite` mention that returns the -OpenTag persona through the Python agent. + +## Docs + +- [`setup.md`](./setup.md) — full reference: components, environment contract, + commands, optional sources, Railway. +- [`docs/migration-kite.md`](./docs/migration-kite.md) — CopilotKit-internal + cutover of the existing production `@kite` app. +- [`e2e/README.md`](./e2e/README.md) — Slack live harness. ## License diff --git a/app/cleanup.test.ts b/app/cleanup.test.ts index c51a5fc0..7e35a6cc 100644 --- a/app/cleanup.test.ts +++ b/app/cleanup.test.ts @@ -10,13 +10,18 @@ const packageJson = JSON.parse( dependencies: Record; }; +// A bare semver with no range operator, so deploys resolve reproducibly. +const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/; + describe("launch dependency and cleanup contract", () => { - it("keeps the exact Channels and Runtime versions", () => { - expect(packageJson.dependencies["@copilotkit/channels"]).toBe( - "0.7.0", + // Asserts the pin *shape*, not a literal version: the docs and this test both + // used to restate "0.7.0", and both drifted the moment the deps were bumped. + it("pins the Channels and Runtime versions exactly", () => { + expect(packageJson.dependencies["@copilotkit/channels"]).toMatch( + EXACT_VERSION, ); - expect(packageJson.dependencies["@copilotkit/runtime"]).toBe( - "1.66.0", + expect(packageJson.dependencies["@copilotkit/runtime"]).toMatch( + EXACT_VERSION, ); }); diff --git a/docs/migration-kite.md b/docs/migration-kite.md new file mode 100644 index 00000000..1e518545 --- /dev/null +++ b/docs/migration-kite.md @@ -0,0 +1,125 @@ +# Migrating production `@kite` to OpenTag + +This is a CopilotKit-internal runbook for cutting the existing production +`@kite` Slack app over to OpenTag on managed Intelligence Channels. It is not +part of getting OpenTag running — for that, use the +[README quick start](../README.md#quick-start), which creates a fresh Slack app +from a CLI-generated manifest and never touches `@kite`. + +## Constraint + +**Do not create, reinstall, or replace the production Slack app.** It owns the +bot user, the workspace installation, and the `@kite` handle. Losing any of the +three is user-visible and not cheaply reversible. + +That constraint is in tension with one fact about the managed path, and the +tension is the whole difficulty of this cutover: Slack must POST events to an +Intelligence-hosted Request URL, and `@kite` was built for Socket Mode. Socket +Mode and a Request URL are mutually exclusive delivery modes. So the app's event +delivery has to change even though the app itself must not be recreated. + +Two consequences follow, and both need confirming against the live app before +anyone touches it: + +- Slack applies manifest scope changes only on **Reinstall to Workspace**. If the + cutover needs no scope change, no reinstall is needed. If it does, reinstalling + is unavoidable and its blast radius on the existing installation must be + understood first. +- The **signing secret is reissued on reinstall**. Any reinstall invalidates the + secret stored in Intelligence, so treat a post-reinstall auth failure as a + stale stored secret before suspecting a missing scope. + +## Credentials + +The managed Slack adapter takes exactly two values: + +| Field | Where | +| --- | --- | +| `channelToken` | **Bot User OAuth Token** (`xoxb-`), from **OAuth & Permissions** | +| `signingSecret` | **Signing Secret**, from **Basic Information → App Credentials** | + +There is no app-level `xapp-` token anywhere on this path. If you are looking for +a field to paste one into, you are on the Socket Mode path, which is the thing +being migrated away from. Earlier revisions of this runbook asked for `xapp-`; +that was wrong. + +Never put either value in source control or in chat. + +## Sequence + +Drive the Slack-side changes from the CLI's emitted next actions rather than from +remembered steps. Run `channels add` against the existing app's Channel and read +`nextAction.instructions`, `nextAction.caveats`, `nextAction.requiredEnvVars`, +and `nextAction.resumeCommand`: + +```bash +npx --yes copilotkit@latest channels add --name open-tag --display-name "OpenTag" --adapter slack --json +``` + +Then: + +1. **Stop the old Kite Socket Mode runtime**, so there is exactly one consumer. + Managed delivery is claim-based: two runtimes on the same Channel name in the + same project race per delivery and the loser silently receives nothing. The + tell is a Slack reply nobody's terminal knows anything about. +2. **Reconfigure the existing app's event delivery** per the CLI's emitted + instructions — Socket Mode off, the Intelligence Request URL set, and + interactivity enabled so human-in-the-loop buttons fire. +3. **Attach the existing `xoxb-` token and signing secret** in Intelligence, + entered directly into the Slack attachment. +4. **Start the OpenTag `runtime` service** with `INTELLIGENCE_CHANNEL_NAME` set + to the Channel name in `.copilotkit/channels.json`. +5. **Verify.** See below. + +## Acceptance + +Production acceptance is one end-to-end `@kite` mention that returns the OpenTag +persona through the Python agent. Add the two trigger checks that catch the +regressions this repo has actually shipped — an unmentioned follow-up in that +subscribed thread should reply, and an unmentioned message in a fresh +conversation should stay silent. + +Run diagnostics before concluding anything: + +```bash +npx --yes copilotkit@latest channels status --json +``` + +```bash +LOG_LEVEL=debug pnpm runtime +``` + +The runtime logger defaults to `error` while every Channel lifecycle breadcrumb +is emitted at `warn`. The line `channel "" requires setup` is the single +highest-value diagnostic here, and at the default level it is written and +discarded. + +### Signals that are not health + +Several things look like success and are not: + +- **`ready()` resolving.** It resolves on `setup_required` too, which is a valid + degraded state, not a failure. Only `controls.status()` distinguishes them. +- **`/api/copilotkit/info` returning 200.** It reports license and runtime state + and says nothing about Slack. +- **The dashboard's Agent run column reading `—`**, Overview showing + `AGENT: Not declared`, and an `…:activation` pseudo-thread. None of those + indicate a failed turn; the activation thread only means the runtime activated. + +The tab that proves a real round trip is **Usage**: `Completed turns` plus a +non-zero `Outbound`. + +## Rollback + +If the cutover fails, stop the new Channel **before** restoring the prior Railway +deployment, so the old Socket Mode consumer does not come back up alongside a +live managed Channel. + +## Railway + +The repository configuration does not mutate the existing production Railway +project. The live migration reuses the existing Kite `runtime` service, adds the +`agent` service, connects both to `CopilotKit/OpenTag` on `main`, and enables +GitHub autodeploys. Inventory and cutover happen after Railway authentication. + +See [`setup.md`](../setup.md#railway) for the service and variable contract. diff --git a/setup.md b/setup.md index d9c5116d..530641f6 100644 --- a/setup.md +++ b/setup.md @@ -1,10 +1,13 @@ -# OpenTag setup +# OpenTag reference -This guide covers the canonical OpenTag deployment: one Python agent service -and one Node CopilotRuntime service with Channels embedded. +The five-minute path to a working OpenTag is in the +[README quick start](./README.md#quick-start). This file is the reference behind +it: components, the full environment contract, Channel commands, optional +sources, Railway, and tests. -Slack and Microsoft Teams are supported for this launch. Discord, Telegram, -and WhatsApp are coming soon. +The canonical deployment is one Python agent service and one Node +CopilotRuntime service with Channels embedded. Slack and Microsoft Teams are +supported; Discord, Telegram, and WhatsApp are coming soon. ## Components @@ -14,12 +17,14 @@ and WhatsApp are coming soon. | Application composition | [`app/index.ts`](./app/index.ts) | SDK agent factory, managed Channel, and runtime | | Channel definition | [`app/channel.tsx`](./app/channel.tsx) | Mentions, commands, components, modals, and interrupts | | Intelligence runtime | [`app/runtime-host.ts`](./app/runtime-host.ts) | One `CopilotKitIntelligence` and one `CopilotRuntime` | +| Environment contract | [`app/env.ts`](./app/env.ts) | Required variables and in-code defaults | | Python agent | [`agent/`](./agent) | LangGraph deep agent served over AG-UI | | Railway topology | [`.railway/railway.ts`](./.railway/railway.ts) | Two services sourced from OpenTag `main` | The host always uses the Intelligence-owned runtime. It declares one -adapter-free Channel using the configured name. Its Slack and Microsoft Teams -adapters, credentials, and attachments are configured only in Intelligence. +adapter-free Channel using the configured name. The Slack and Microsoft Teams +adapters, their credentials, and attachments are configured only in +Intelligence — never here. ## Install @@ -32,8 +37,6 @@ Prerequisites: - A CopilotKit Intelligence project, Channel, and runtime API key - An OpenAI API key for the Python agent -Install both dependency sets: - ```bash pnpm install --frozen-lockfile cd agent @@ -41,19 +44,23 @@ uv sync cd .. ``` -The Channels and Runtime packages are intentionally pinned: - -```text -@copilotkit/channels 0.7.0 -@copilotkit/runtime 1.66.0 -``` +`@copilotkit/channels` and `@copilotkit/runtime` are intentionally pinned. +[`package.json`](./package.json) is the single source of truth for both +versions; this file does not restate them, because a hand-copied pin drifts on +the next bump. -## Configure the environment +## Environment contract ```bash cp .env.example .env ``` +One root `.env` configures both services. The Python agent loads it explicitly +for local development; Railway supplies the same values as service variables +without a checked-in file. + +### Agent + | Variable | Required | Purpose | | --- | --- | --- | | `OPENAI_API_KEY` | Yes | Model access | @@ -73,13 +80,11 @@ cp .env.example .env | `SERVER_PORT` / `PORT` | No | Local port; defaults to `8123` | Only `OPENAI_API_KEY` is required. Without Tavily or internal-source -credentials, the agent still chats, triages, and renders supported UI -components. Planning and virtual files remain available for explicitly -substantial work. The Python agent explicitly loads this root `.env` for local -development; Railway service variables work normally without a checked-in -environment file. +credentials the agent still chats, triages, and renders supported UI +components; planning and virtual files remain available for explicitly +substantial work. -Run it: +Run it alone: ```bash pnpm agent @@ -88,28 +93,29 @@ pnpm agent The AG-UI endpoint is `http://localhost:8123/`; `/health` reports the `opentag-agent` service. -## Configure Intelligence - -In [CopilotKit Intelligence](https://intelligence.copilotkit.ai): - -1. Create or select the OpenTag project. -2. Create one Channel named `open-tag`. -3. Issue a runtime API key. -4. Configure the Slack and Microsoft Teams adapters on that Channel. +### Runtime | Variable | Required | Purpose | | --- | --- | --- | | `AGENT_URL` | Yes | Python AG-UI endpoint, locally `http://localhost:8123/` | -| `INTELLIGENCE_API_KEY` | Yes | Runtime authentication | +| `INTELLIGENCE_API_KEY` | Yes | Runtime authentication; also selects the project | +| `INTELLIGENCE_CHANNEL_NAME` | No | Defaults to `open-tag`; must match the Channel name exactly | | `INTELLIGENCE_API_URL` | No | Defaults to `https://api.intelligence.copilotkit.ai` | | `INTELLIGENCE_GATEWAY_WS_URL` | No | Defaults to `wss://realtime.intelligence.copilotkit.ai` | -| `INTELLIGENCE_CHANNEL_NAME` | No | Defaults to `open-tag`; Railway uses `open-tag` | | `AGENT_AUTH_HEADER` | No | Authorization header forwarded to the agent | | `PORT` | No | Channel HTTP port; defaults to `3000` | +| `LOG_LEVEL` | No | Defaults to `error`; use `debug` to see Channel lifecycle breadcrumbs | +The API key selects a project; the Channel name selects a Channel inside it. Legacy organization, project, Channel ID, and runtime-instance ID variables are -not used. Slack and Teams credentials also do not belong in this environment; -Intelligence owns them. +not used. Slack and Teams credentials do not belong here — Intelligence owns +them. + +Both Intelligence URLs are defaulted in [`app/env.ts`](./app/env.ts) rather than +in `.env`. That is deliberate, and it is why `copilotkit channels status` +reports them as unset. A genuinely missing `INTELLIGENCE_GATEWAY_WS_URL` does +not error: the realtime plane is a different host from the API plane and is not +derived from it, so `channels.ready()` simply hangs until it times out. Start the runtime: @@ -117,44 +123,71 @@ Start the runtime: pnpm runtime ``` -Use `pnpm dev` for watch mode. `pnpm start` and `pnpm runtime` both run the -same canonical entrypoint. Startup waits for `listener.channels.ready()` before -opening HTTP. SIGINT and SIGTERM stop Channels, HTTP, and the rendering browser -once, even if shutdown is requested more than once. - -## Slack - -### Existing production `@kite` - -Do not create, reinstall, or replace the current production Slack app. The -existing app owns the bot user, workspace installation, and `@kite` handle. - -For cutover: - -1. Stop the old Kite Socket Mode runtime so there is only one consumer. -2. Enter the existing `xapp` and `xoxb` tokens directly into the Slack - attachment in Intelligence. Do not put them in source control or chat. -3. Start the OpenTag `runtime` service with - `INTELLIGENCE_CHANNEL_NAME=open-tag`. -4. Send one `@kite` mention and verify the reply uses the OpenTag persona and - Python deep agent. - -If the cutover fails, stop the new Channel before restoring the prior Railway -deployment. - -### New Slack installations - -The JSON and YAML Slack manifests in this repository describe OpenTag for -future installations. Create a new Slack app from either manifest, install it -to the workspace, and attach its app-level and bot tokens in Intelligence. - -Those manifests are not a production migration step for `@kite`. - -## Microsoft Teams - -Managed Microsoft Teams is supported. Configure the Teams adapter on the same -`open-tag` Channel in Intelligence. The same Node process and runtime host both -platforms; there is no direct adapter or Railway platform credential. +`pnpm start` and `pnpm runtime` run the same canonical entrypoint; `pnpm dev` +adds watch mode for both services. Startup waits for +`listener.channels.ready()` before opening HTTP. SIGINT and SIGTERM stop +Channels, HTTP, and the rendering browser exactly once, even if shutdown is +requested more than once. + +Note that `ready()` resolving is not proof of health. It also resolves on +`setup_required`, which is a valid degraded state rather than a failure. Only +`controls.status()` → `{ overall, channels }` distinguishes them, and +`/api/copilotkit/info` returning 200 reports license and runtime state while +saying nothing at all about Slack. + +## Channel reference + +The Channel is created and reconciled with the public CopilotKit CLI. These +commands configure **managed Intelligence Channels**; they do not configure the +open-source `@copilotkit/channels` adapter packages, which are a separate +product sharing the words "channels" and "Slack". + +| Command | Purpose | +| --- | --- | +| `copilotkit project select` | Select or create the hosted Intelligence project | +| `copilotkit channels add [name]` | Declare a Channel, reconcile it, and report the next step | +| `copilotkit channels status` | Compare your configuration, your code, and the server | +| `copilotkit channels list` | List Channels and their attachment state | +| `copilotkit channels rotate ` | Replace stored provider credentials | +| `copilotkit channels providers` | List providers and the credentials each asks for | +| `copilotkit channels setup` | Hand the whole flow to your coding agent | + +No flag accepts a credential value. Credentials are read from `.env`, from a +named variable via `--credential-env =`, or from a JSON document on +stdin via `--credentials-stdin` for CI and secret managers. `--json` implies +non-interactive: it never prompts and never opens a browser. + +`channels add` writes `.copilotkit/channels.json`. Keep that file tracked; keep +`.env` and `.copilotkit/artifacts/` ignored. + +### Credentials each provider asks for + +| Provider | Fields | +| --- | --- | +| `slack` | `channelToken` — Bot User OAuth Token (`xoxb-`), from **OAuth & Permissions**; `signingSecret`, from **Basic Information → App Credentials** | +| `teams` | `clientId` and `tenantId`, from the Entra app registration **Overview**; `clientSecret` — the secret **Value**, not the Secret ID | + +There is no app-level `xapp-` token on the managed path. Slack reaches +Intelligence over HTTPS at an Intelligence-hosted Request URL, authenticated by +the signing secret Intelligence holds, and Intelligence reaches your runtime +over a websocket your process opens outbound. Nothing here uses Socket Mode, and +a Slack app configured for Socket Mode installs green and delivers nothing. + +`copilotkit channels add --adapter teams --provision` can create the +provider-side Teams app for you. Two Teams gates stay user-owned regardless: +granting tenant admin consent, and uploading the app package through **Apps → +Manage your apps → Upload an app**. + +### Channel names claim deliveries + +Managed delivery is claim-based. Two runtimes declaring the same Channel name in +the same project race per delivery, and the loser silently receives nothing — +the tell is a Slack reply your terminal knows nothing about. Give a local or +forked runtime its own project, key, and Channel name rather than reusing +`open-tag`. + +The name is a slug: lowercase, digits, single hyphens. It must match +`INTELLIGENCE_CHANNEL_NAME` character for character. ## Tools, commands, and UI @@ -169,47 +202,62 @@ The Channel also forwards sender context, Slack-specific tools on Slack turns, file content, and rich issue/page/table/native-Slack-chart/diagram/status/ incident/link components. +Trigger routing is not symmetric. A mentioned turn goes to `onMention` if +registered and falls back to `onMessage` otherwise; an unmentioned turn reaches +`onMessage` only. `onMention` subscribes the thread, which is what lets +unmentioned follow-ups in that thread run the agent. Always verify with a +channel mention first. + +Mentions, messages, and button and select clicks are the proven managed-path +triggers — interactivity is enabled deliberately, which is what makes +human-in-the-loop fire. **Slash commands and modals are registered in code but +their managed-path delivery depends on the Channel's generated Slack manifest +declaring them.** As of the last verification against `@copilotkit/channels` +0.7.0 the generated manifest declared no `slash_commands` and `view_submission` +was not handled, so those handlers compiled, started, reported online, and never +fired. Send a real command and submit a real modal before relying on either. + Before a Linear or Notion mutation reaches MCP, a Python interceptor emits `confirm_write`. The Channel posts an approval card, and the button resumes the -graph with the user's decision. The MCP handler runs only after approval. -Reads and UI rendering are never gated. +graph with the user's decision. The MCP handler runs only after approval. Reads +and UI rendering are never gated. ## Optional sources ### Tavily -Set `TAVILY_API_KEY` in the root `.env` to enable live web research. The -`web_search` tool is not registered when the key is absent. +Set `TAVILY_API_KEY` to enable live web research. The `web_search` tool is not +registered when the key is absent. ### GitHub -Set `GITHUB_PERSONAL_ACCESS_TOKEN` in the root `.env` to enable GitHub search. -Use a fine-grained personal access token limited to the repositories and read -permissions the agent needs. OpenTag connects to GitHub's hosted MCP with only -the repository, issue, and pull-request toolsets and requests read-only mode. -Set `GITHUB_MCP_URL` only to override the hosted endpoint, then restart -`pnpm agent` so it discovers the tools. +Set `GITHUB_PERSONAL_ACCESS_TOKEN` to enable GitHub search. Use a fine-grained +personal access token limited to the repositories and read permissions the agent +needs. OpenTag connects to GitHub's hosted MCP with only the repository, issue, +and pull-request toolsets and requests read-only mode. Set `GITHUB_MCP_URL` only +to override the hosted endpoint, then restart `pnpm agent` so it rediscovers the +tools. ### PostHog Create a PostHog personal API key using the **MCP Server** preset, then set -`POSTHOG_PERSONAL_API_KEY` in the root `.env`. OpenTag connects to -`https://mcp.posthog.com/mcp` in token-efficient CLI mode with server-enforced -read-only access. Set `POSTHOG_MCP_URL` only to override the complete endpoint, -including its `mode=cli&readonly=true` safety parameters. Restart `pnpm agent` -after changing either variable. +`POSTHOG_PERSONAL_API_KEY`. OpenTag connects to `https://mcp.posthog.com/mcp` in +token-efficient CLI mode with server-enforced read-only access. Set +`POSTHOG_MCP_URL` only to override the complete endpoint, including its +`mode=cli&readonly=true` safety parameters. Restart `pnpm agent` after changing +either variable. ### Linear -Set `LINEAR_API_KEY` in the root `.env`. OpenTag connects to the hosted Linear -MCP by default. Railway preserves this optional secret on the `agent` service. +Set `LINEAR_API_KEY`. OpenTag connects to the hosted Linear MCP by default. +Railway preserves this optional secret on the `agent` service. ### Notion -Notion is optional and is not a separate Railway service. Configure an existing -remote MCP endpoint by setting both `NOTION_MCP_URL` and -`NOTION_MCP_AUTH_TOKEN`, then restart `pnpm agent` so it discovers the tools. -If either value is absent, OpenTag skips Notion without blocking startup. +Notion is optional and remote-only, not a separate Railway service. Set both +`NOTION_MCP_URL` and `NOTION_MCP_AUTH_TOKEN`, then restart `pnpm agent` so it +discovers the tools. If either value is absent OpenTag skips Notion without +blocking startup. ## Railway @@ -232,15 +280,6 @@ Evaluate the configuration locally without applying it: node node_modules/railway/dist/iac/bin.js ``` -The live Railway migration reuses the existing Kite `runtime` service, adds the -`agent`, connects both to OpenTag, and enables GitHub autodeploys. - -## Coming soon - -Discord, Telegram, and WhatsApp are intentionally not configured for this -launch. Their adapters and setup instructions will be added after launch -support is ready. - ## Tests ```bash @@ -257,5 +296,9 @@ pnpm e2e ``` See [`e2e/README.md`](./e2e/README.md) for its required workspace credentials. -There is no launch-blocking Teams E2E harness; production acceptance is the -single `@kite` round trip described above. +There is no launch-blocking Teams E2E harness. + +## Coming soon + +Discord, Telegram, and WhatsApp are intentionally not configured. Their adapters +and setup instructions will be added once launch support is ready. diff --git a/slack-app-manifest.json b/slack-app-manifest.json deleted file mode 100644 index bebd6088..00000000 --- a/slack-app-manifest.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "display_information": { - "name": "OpenTag", - "description": "OpenTag research and triage assistant for Slack.", - "background_color": "#1a1a1a" - }, - "features": { - "app_home": { - "home_tab_enabled": false, - "messages_tab_enabled": true, - "messages_tab_read_only_enabled": false - }, - "bot_user": { - "display_name": "OpenTag", - "always_online": true - }, - "assistant_view": { - "assistant_description": "OpenTag research and triage assistant.", - "suggested_prompts": [ - { - "title": "Triage my open issues", - "message": "Triage my open issues" - }, - { - "title": "What shipped this week?", - "message": "Summarize what shipped this week" - } - ] - }, - "slash_commands": [ - { - "command": "/agent", - "description": "Talk to the AG-UI agent from any channel", - "usage_hint": "", - "should_escape": false - }, - { - "command": "/triage", - "description": "Triage this thread \u2014 summarize and propose Linear issues", - "usage_hint": "", - "should_escape": false - }, - { - "command": "/preview", - "description": "Privately preview the issue I'd file", - "usage_hint": "", - "should_escape": false - }, - { - "command": "/file-issue", - "description": "Open a form to file a Linear issue", - "usage_hint": "", - "should_escape": false - } - ] - }, - "oauth_config": { - "scopes": { - "user": ["chat:write"], - "bot": [ - "app_mentions:read", - "assistant:write", - "channels:history", - "groups:history", - "im:history", - "mpim:history", - "channels:read", - "groups:read", - "im:read", - "mpim:read", - "users:read", - "users:read.email", - "team:read", - "chat:write", - "chat:write.public", - "chat:write.customize", - "im:write", - "mpim:write", - "files:read", - "files:write", - "channels:join", - "commands" - ] - } - }, - "settings": { - "event_subscriptions": { - "bot_events": [ - "app_mention", - "assistant_thread_started", - "assistant_thread_context_changed", - "message.im", - "message.mpim" - ] - }, - "interactivity": { - "is_enabled": true - }, - "socket_mode_enabled": true, - "org_deploy_enabled": false, - "token_rotation_enabled": false - } -} diff --git a/slack-app-manifest.yaml b/slack-app-manifest.yaml deleted file mode 100644 index 94b38c0a..00000000 --- a/slack-app-manifest.yaml +++ /dev/null @@ -1,88 +0,0 @@ -display_information: - name: OpenTag - description: OpenTag research and triage assistant for Slack. - background_color: "#1a1a1a" -features: - bot_user: - display_name: OpenTag - always_online: true - # App Home: enable the Messages tab so users can DM the bot (and so the tab - # isn't read-only — otherwise Slack shows "Sending messages to this app has - # been turned off"). - app_home: - home_tab_enabled: false - messages_tab_enabled: true - messages_tab_read_only_enabled: false - # Enables the assistant pane ("Agents & AI Apps"). The greeting + chips here - # mirror the suggestions registered when an OpenTag thread starts. - assistant_view: - assistant_description: OpenTag research and triage assistant. - suggested_prompts: - - title: Triage my open issues - message: Triage my open issues - - title: What shipped this week? - message: Summarize what shipped this week - slash_commands: - - command: /agent - description: Talk to the AG-UI agent from any channel - usage_hint: "" - should_escape: false - - command: /triage - description: Triage this thread — summarize and propose Linear issues - usage_hint: "" - should_escape: false - - command: /preview - description: Privately preview the issue I'd file - usage_hint: "" - should_escape: false - - command: /file-issue - description: Open a form to file a Linear issue - usage_hint: "" - should_escape: false -oauth_config: - scopes: - user: - - chat:write # user-token write access for the optional live E2E harness - bot: - # --- Read what the user said --- - - app_mentions:read # see @mentions of the bot - - assistant:write # assistant pane: status, title, suggested prompts - - channels:history # read messages in public channels bot is in - - groups:history # read messages in private channels bot is in - - im:history # read DMs - - mpim:history # read group DMs - - channels:read # list/lookup public channels (names, members) - - groups:read # list/lookup private channels - - im:read # list DMs - - mpim:read # list group DMs - - users:read # resolve user IDs to names for context - - users:read.email # resolve user emails to match Slack users to Linear/Notion - - team:read # workspace info - # --- Write replies and generative UI --- - - chat:write # post and edit messages in channels bot is in - - chat:write.public # post in channels bot isn't a member of - - chat:write.customize # per-message username/icon (handy for branding) - - im:write # open DMs - - mpim:write # open group DMs - # --- Files (for future image/file passing) --- - - files:read - - files:write - # --- Bot can join channels on its own --- - - channels:join - # --- Slash commands --- - - commands -settings: - event_subscriptions: - bot_events: - - app_mention - - assistant_thread_started # user opened the assistant pane - - assistant_thread_context_changed # the channel/context the user is viewing - - message.im # DMs to the bot + assistant-pane messages - - message.mpim # messages in group DMs the bot is in - # Add message.channels/message.groups only when using - # respondTo.threadReplies: "afterBotReply" for legacy plain thread continuation. - interactivity: - is_enabled: true - socket_mode_enabled: true - org_deploy_enabled: false - token_rotation_enabled: false From ddaa01c6582bb6f3a727b6c7509ea7c91ec6c8f0 Mon Sep 17 00:00:00 2001 From: Jerel John Velarde Date: Wed, 5 Aug 2026 08:56:22 -0700 Subject: [PATCH 2/2] docs: offer the channels-setup skill in the quick start Step 2 now presents both paths: `channels setup`, which installs the `channels-setup` skill and hands the flow to a coding agent, and the explicit `project select` + `channels add` sequence. Two caveats a reader would otherwise hit: - `channels setup` installs to every coding agent it detects with no way to narrow the list. `skills onboard --channels --agent ` takes the same prompt and does let you narrow it. - The skill's workflow is written for a project starting from nothing, so it has phases for building the agent and writing the Channel runtime. OpenTag already has both, and an agent following the guide literally could rewrite working code. Say so, and point at agent/ and server.ts. Also lift the three Slack console gotchas out of the manual path into their own subsection, since they apply either way. --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++---------- setup.md | 10 +++++++++- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c3f65a0f..dbcaf6c5 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,43 @@ Do this **before** touching Slack. The Channel is what generates a Slack app manifest already pointed at the right Request URL, so creating the Slack app first means creating the wrong one. +Two ways to do it. Both end in the same place. + +#### Let your coding agent drive it + +```bash +npx --yes copilotkit@latest channels setup +``` + +That installs a `channels-setup` skill and copies a one-line prompt to your +clipboard (`--no-clipboard` prints it instead). Paste it into your agent: + +> Use the `channels-setup` skill to set up a Channel for this project. + +The skill is deliberately a pointer rather than a copy of the steps: it fetches +its workflow from at run time, so it +cannot go stale against the CLI. It covers the whole path — selecting the +project, creating and reconciling the Channel, walking you through the Slack +console handoff, and proving a real mention gets a reply. It hands every secret +back to you; it never asks you to paste one into chat. + +It writes `.agents/skills/channels-setup` and a `skills-lock.json`, both already +gitignored here. Note that it installs to every coding agent it detects with no +way to narrow the list. To install for one agent only, use the equivalent +command, which takes the same prompt: + +```bash +npx --yes copilotkit@latest skills onboard --channels --agent claude-code +``` + +The skill will also carry you through the rest of this quick start. One thing to +watch: its workflow is written for a project starting from nothing, so it has +phases for building the agent and writing the Channel runtime. **OpenTag already +has both** — [`agent/`](./agent) and [`server.ts`](./server.ts). Point your agent +at the existing code to verify and run, not to rewrite. + +#### Or run it yourself + ```bash npx --yes copilotkit@latest project select ``` @@ -43,8 +80,18 @@ on the server, and returns a JSON envelope with one of three states: variable names to set, and the `resumeCommand` to run afterward. This exits 0. - `failed` — stop and read the error code. Do not continue. -Follow the emitted `nextAction` rather than remembered Slack steps. Three -details in that flow cost the most time when skipped: +`--name` is a slug and must match `INTELLIGENCE_CHANNEL_NAME` character for +character. If you are running a fork against your own project, pick your own +name — see [Channel names claim deliveries](#channel-names-claim-deliveries). + +For Microsoft Teams, use `--adapter teams`. Two Teams steps stay yours because +nothing can work around them: granting tenant admin consent, and uploading the +app package through **Apps → Manage your apps → Upload an app**. + +#### Three Slack details that cost the most time + +These apply on either path. Follow the CLI's emitted `nextAction` rather than +remembered Slack steps, and watch for: - After creating the app from the link, open **OAuth & Permissions** and choose **Reinstall to Workspace**. Slack applies the manifest's real scopes only on @@ -56,14 +103,6 @@ details in that flow cost the most time when skipped: - The signing secret is reissued on reinstall. If auth fails right after a reinstall, suspect a stale stored secret before a missing scope. -`--name` is a slug and must match `INTELLIGENCE_CHANNEL_NAME` character for -character. If you are running a fork against your own project, pick your own -name — see [Channel names claim deliveries](#channel-names-claim-deliveries). - -For Microsoft Teams, use `--adapter teams`. Two Teams steps stay yours because -nothing can work around them: granting tenant admin consent, and uploading the -app package through **Apps → Manage your apps → Upload an app**. - ### 3. Configure the environment ```bash diff --git a/setup.md b/setup.md index 530641f6..353203b9 100644 --- a/setup.md +++ b/setup.md @@ -150,7 +150,8 @@ product sharing the words "channels" and "Slack". | `copilotkit channels list` | List Channels and their attachment state | | `copilotkit channels rotate ` | Replace stored provider credentials | | `copilotkit channels providers` | List providers and the credentials each asks for | -| `copilotkit channels setup` | Hand the whole flow to your coding agent | +| `copilotkit channels setup` | Install the `channels-setup` skill and hand the flow to your coding agent | +| `copilotkit skills onboard --channels` | The same prompt, but `--agent` narrows which agents it installs to | No flag accepts a credential value. Credentials are read from `.env`, from a named variable via `--credential-env =`, or from a JSON document on @@ -160,6 +161,13 @@ non-interactive: it never prompts and never opens a browser. `channels add` writes `.copilotkit/channels.json`. Keep that file tracked; keep `.env` and `.copilotkit/artifacts/` ignored. +The `channels-setup` skill installed by `channels setup` is a pointer, not a copy +of the steps: it fetches its workflow from + at run time so it cannot go stale +against the CLI. That workflow assumes a project starting from nothing, so it +includes phases for building the agent and writing the Channel runtime — OpenTag +has both already. Its Slack handoff never asks anyone to paste a secret into chat. + ### Credentials each provider asks for | Provider | Fields |