- Category
- AI Agents
- Rank
- No. 929Tools index
- Pricing
- Open Source
- Platform
- cli · web
- Type
- AGENT
- Builder
- @CopilotKit
- GitHub
- 1.2k stars
- Latest release
- v0.4.1
- Date
About
OpenTag is an open-source, self-hosted on-call triage bot for Slack and Microsoft Teams, built on CopilotKit's Channels SDK to bring any AG-UI agent into chat. It ships a working example combining a Node runtime, a Python LangGraph agent, native Slack UI (charts, approval gates for writes to Linear/Notion), and cited web research, meant to be cloned and customized rather than just read as documentation.
What it does
This is a working chat assistant for Slack and Teams that a team clones and adapts rather than deploys as-is. Message it and a backend agent researches a question, summarizes findings with its sources, or turns an uploaded file into a chart posted directly in the thread. Before it writes to an external system, such as filing a ticket or saving a note, it pauses and shows a card asking a person to approve or cancel that specific action.
Why it's ranked here
It ranks here because it is a complete, licensed, deployable system, not a demo: container images ship for both halves, deployment paths exist for two different hosts, and the code itself documents real production incidents it was patched to fix, from a middleware ordering bug that broke browser auth to a race in resuming paused approvals. That is unusually candid engineering for a repository meant to be forked, and it signals a team debugging this in a live workspace rather than only in a demo.
What's good
The approval flow for risky actions is genuinely durable: a decision made by clicking a button is stored so it survives a restart, and clicking it twenty minutes later still resolves the exact write it was meant for, not a random one, because each card is bound to the specific paused step that created it. The code also distinguishes carefully between a personal connected account and a shared workspace one for every action, and separates a lookup failure from an app the user genuinely has not connected yet, so error messages sent to a person are specific rather than generic.
Tradeoffs
Getting it running needs several outside accounts and services at once: a CopilotKit account, an OpenAI key, a Slack workspace with admin access, and by default a hosted service that handles delivery and state rather than a fully self-managed one. The stack itself is two languages glued together, a Node process and a separate Python process sharing one configuration file, which is more moving parts than a single-runtime bot. Only Slack and Microsoft Teams are supported chat platforms today; other messaging apps are not yet an option.
How to use it well
This suits a team that already has, or is willing to build, its own agent logic and wants a proven Slack or Teams wrapper around it: replace the example agent while keeping the channel plumbing, deployment scripts, and approval UI as-is. It is not the right pick for a team that wants a bot to install and configure through a UI, since running it means editing source, managing two runtimes, and standing up accounts with several outside services first. Best used as a template to fork and thin out, not as a dependency to import.
Technical notes+
The Node side (server.ts, app/runtime-host.ts, app/channel.tsx) wires CopilotRuntime and a Channels SDK Channel around a set of Slack-facing components, while the agent itself is a Python LangGraph graph built in agent/agent.py and served through a FastAPI app in agent/main.py that gates most routes behind an optional shared-secret middleware, with CORS registered after that middleware so a preflight request is not rejected before it reaches it. agent/agui.py layers a custom AG-UI agent subclass over LangGraph to recover a step-limit crash into a user-visible message and to stamp the acting user's identity into graph state on every run rather than trusting whatever the checkpoint last saw. Tool access to third-party apps goes through a two-step search-then-execute pattern in agent/composio_tools/tools.py rather than binding every available action up front. Writes with real side effects are paused as LangGraph interrupts and rendered as an approval card by app/human-in-the-loop/confirm-write.tsx, which resumes the specific interrupt by ID and distinguishes a resume the SDK confirms was never sent from one whose outcome is genuinely unknown. package.json shows a pnpm-managed Node package alongside a separately uv-managed Python environment synced through a predev script.
Observed
- License
- MIT
- Polyglot codebase
- a TypeScript Node process and a separate Python process (FastAPI plus LangGraph) that share a single root .env file
- Packaging
- Packaged as two separate container images, one for the agent process and one for the runtime process
- Packaging
- Two documented deployment targets: a two-service Railway configuration and a single AWS ECS Fargate task
- Interfaces
- Slack and Microsoft Teams chat, plus a small HTTP API (a health-check route and an account-connect route) alongside the chat surface
- Structural
- Writes to connected third-party tools are gated behind a human-in-the-loop approval interrupt backed by a durable, restart-surviving decision store rather than executing directly
- Security
- An optional shared-secret HTTP middleware protects most agent routes and is inactive when no secret is configured
Read from README.md, package.json, server.ts, agent/main.py, agent/agent.py, agent/agui.py, agent/composio_tools/tools.py, app/channel.tsx, app/runtime-host.ts, app/human-in-the-loop/confirm-write.tsx.
What it can do
Answer on-call questions in chat
User question in Slack/Teams → Chat response
Generate charts from CSV data
CSV file → Chart displayed in Slack UI
Gate writes to Linear/Notion behind human approval
Proposed write action → Approval-gated execution
Return cited web research
Research query → Answer with citations
Connect bot to Slack or Microsoft Teams
Slack/Teams workspace credentials → Active chat integration
Tags
Tech Stack
Media
Comments (0)
No comments yet
Editorially curated, with community endorsements as a secondary signal. Corrections welcome.
