SlopScore
10 crowdincl. 1 critic

ebi-agent-chat-relay

Multi-frontend agent relay: run Claude Code, Codex, local, and AG-UI agents from Discord or Microsoft Teams
Open repo on GitHubgithub.com/ebibibi/ebi-agent-chat-relay
Python · ★ 58 · 29 forks · MIT · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)agent🤖 claude-code
listed 1 hour ago by ebibibi · last checked 1 hour ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-09-30: Multi-frontend agent relay: run Claude Code, Codex, local, and AG-UI agents from Discord or Microsoft Teams; its own README says "Built entirely by Claude Code". 58 stars; MIT license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as ebibibi. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
Multi-frontend agent relay: run Claude Code, Codex, local, and AG-UI agents from Discord or Microsoft Teams
topics
ag-uiai-agentsclaude-codediscordmicrosoft-teamsopenai-codex
created
2026-02-18 · pushed 4 hours ago · 875 commits · 10 contributors
release
v4.2.0 · 2026-09-22
languages
Python 99%Shell 1%Makefile 0%Dockerfile 0%
paperwork
contributingpull request templatelicensereadme 85% health
dependencies
✓ 33 deps, none with known advisories · OSV.dev, checked 1 hour ago

Disclosures, inferred by the Cap'm

slopbucket
vibe-coded
category
agent
ai_generated
mostly
human_touch
light
status
works-on-my-machine
built_with
claude-code
language (detected)
dockerfilemakefilepythonshell
topic (detected)
ag-uiai-agentsclaude-codecodexdiscordmicrosoft-teams
license (detected)
mit

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: Multi-frontend agent relay: run Claude Code, Codex, local, and AG-UI agents from Discord or Microsoft Teams; its own README says "Built entirely by Claude Code". It carries the MIT license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README — the repo's own words, folded up so the grading fits on one screen

Ebi Agent Chat Relay

Formerly Claude Code Discord Bridge, then Claude & Codex Discord Bridge. Every existing identifier still works: the package is claude-code-discord-bridge (kebab-case), the command is ccdb, and ccdb remains the short name used throughout this document.

CI CodeQL Python 3.12+ License: MIT

Run coding agents from Discord or Microsoft Teams. Choose Claude Code, OpenAI Codex, a local model, or any compatible AG-UI agent behind the same conversation.

Ebi Agent Chat Relay turns each Discord thread or Teams conversation into an isolated, persistent agent session. Work on a feature in one conversation, review a PR in another, and run a background task in a third — simultaneously. Discord can mix backends per thread; Teams uses the configured/global backend in v4. The relay handles coordination so sessions do not clobber each other.

Why the name changed. This started as a bridge between one AI and one chat app. It is now a relay with two production frontends and five backend choices. Three of the four words in the old name had stopped being true. See ADR-0001 for the decision and the rename plan for the compatibility-preserving transition.

Use your existing subscriptions, your own infrastructure, or a remote agent. ccdb can run the official Claude Code and Codex CLIs, a Codex-compatible local endpoint, an AG-UI HTTP/SSE agent, or the pi CLI. Discord exposes runtime /backend switching; Teams uses the configured backend through the same factory in v4.

What's new in v4

Version 4 makes two independent choices explicit: where people talk and which agent does the work. Any supported frontend can use any supported backend.

Frontend × backend

Claude Code OpenAI Codex Local AG-UI pi
Discord ✅ ✅ ✅ ✅ ✅
Microsoft Teams ✅ ✅ ✅ ✅ ✅
  • Discord remains the zero-migration default. Existing deployments start exactly as before.
  • Microsoft Teams is production-ready through a small public receiver and an outbound-only ActivityPuller on the private session host. Discord and Teams can run together in one process with CCDB_FRONTENDS=discord,teams.
  • AG-UI connects either chat surface to an HTTP/SSE agent implementing the Agent–User Interaction Protocol. Claude Code, Codex, and the guarded local backend remain available.
  • pi runs the pi CLI, which reaches Anthropic, OpenAI, Google, GitHub Copilot and OpenAI-compatible local servers through one agent. It has no sandbox in the non-interactive modes ccdb uses, so it refuses to spawn until CCDB_PI_ALLOW_UNSANDBOXED=1 states that the host's own isolation is trusted.

Start with the backend guide. For Teams, follow the complete Microsoft Teams setup guide, then use the deeper surface behavior and relay security model references.

日本語 | 简体中文 | 한국어 | Español | Português | Français

Disclaimer: This project is not affiliated with, endorsed by, or officially connected to Anthropic or OpenAI. "Claude" and "Claude Code" are trademarks of Anthropic, PBC; "OpenAI", "Codex", and "ChatGPT" are trademarks of OpenAI. This is an independent open-source tool that interfaces with the Claude Code CLI and the OpenAI Codex CLI.

Built entirely by Claude Code. This entire codebase — architecture, implementation, tests, documentation — was written by Claude Code itself. The human author provided requirements and direction via natural language. See How This Project Was Built.


The Big Idea: Parallel Sessions Without Fear

When you send tasks to Claude Code or OpenAI Codex in separate Discord threads, the relay does four things automatically — regardless of which backend you picked:

  1. Concurrency notice injection — Every session's system prompt includes mandatory instructions: create a git worktree, work only inside it, never touch the main working directory directly.

  2. Active session registry — Each running session knows about the others. If two sessions are about to touch the same repo, they can coordinate rather than conflict.

  3. AI Lounge — A session-to-session "breakroom" injected into every prompt. Before starting, each session reads recent lounge messages to see what other sessions are doing, and claims the repo, issue or file it is about to touch (see Resource Claims) so a second session is turned away before it duplicates the work. Before disruptive operations (force push, bot restart, DB drop), sessions check the lounge first so they don't stomp on each other's work.

  4. Backend-agnostic surface — The same Discord UI, slash commands, scheduler, API, and Lounge work the same way whether a thread runs Claude or Codex. Mix backends across threads if you want — e.g. Claude for refactors, Codex for code review — using /backend per thread.

Thread A (feature)    ──→  Claude Code  (worktree-A)  ─┐
Thread B (PR review)  ──→  OpenAI Codex (worktree-B)   ├─→  #ai-lounge
Thread C (docs)       ──→  Claude Code  (worktree-C)  ─┘    "A: auth refactor in progress"
                                                             "B: PR #42 review done (codex)"
                                                             "C: updating README"

No race conditions. No lost work. No merge surprises. No backend lock-in.


What You Can Do

Interactive Chat (Mobile / Desktop)

Use Claude Code or OpenAI Codex from anywhere Discord runs — phone, tablet, or desktop. Each message creates or continues a thread that maps 1:1 to a persistent AI session. Switch backend at any time with /backend claude or /backend codex — per thread, or globally as the new default.

Parallel Development

Open multiple threads simultaneously. Each is an independent AI session — Claude Code or Codex — with its own context, working directory, and git worktree. Useful patterns:

  • Feature + review in parallel: Start a feature with Claude in one thread while Codex reviews the PR in another.
  • Multiple contributors: Different team members each get their own thread (and their preferred backend); sessions stay aware of each other via the AI Lounge.
  • Experiment safely: Try an approach in thread A while keeping thread B on stable code.
  • A/B the same prompt on both AIs: Spawn two threads with the same task, one on /backend claude and one on /backend codex, then compare the diffs side-by-side.

Scheduled Tasks (SchedulerCog)

Register periodic Claude Code tasks from a Discord conversation or via REST API — no code changes, no redeploys. Tasks are stored in SQLite and run on a configurable schedule. Claude can self-register tasks during a session using POST /api/tasks.

/skill name:goodmorning         → runs immediately
Claude calls POST /api/tasks    → registers a periodic task
SchedulerCog (30s master loop)  → fires due tasks automatically

CI/CD Automation

Trigger Claude Code tasks from GitHub Actions via Discord webhooks. Claude runs autonomously — reads code, updates docs, creates PRs, enables auto-merge.

GitHub Actions → Discord Webhook → Bridge → Claude Code CLI
                                                  ↓
GitHub PR ←── git push ←── Claude Code ──────────┘

Real example: On every push to main, Claude analyzes the diff, updates English + Japanese documentation, creates a bilingual PR, and enables auto-merge. Zero human interaction.

Session Sync

Already use Claude Code CLI directly? Sync your existing terminal sessions into Discord threads with /sync-sessions. Backfills recent conversation messages so you can continue a CLI session from your phone without losing context.

AI Lounge

A shared "breakroom" channel where all concurrent sessions announce themselves, read each other's updates, and coordinate before disruptive operations.

Each session receives the lounge context automatically as ephemeral system/developer instructions (--append-system-prompt for Claude, developer_instructions for Codex), rather than as part of the conversation history. This prevents the context from accumulating across turns, which would otherwise cause "Prompt is too long" errors in long-running sessions. The injected context includes recent messages from other sessions plus the rule to check before doing anything destructive.

# Sessions post their intentions before starting:
curl -X POST "$CCDB_API_URL/api/lounge" \
  -H "Content-Type: application/json" \
  -d '{"message": "Starting auth refactor on feature/oauth — worktree-A", "label": "feature dev"}'

# Read recent lounge messages (also injected into each session automatically):
curl "$CCDB_API_URL/api/lounge"

Keep posts short — 200 characters, one or two lines. Every lounge message is injected into every session that starts after it, so length is a cost shared by all of them, not a private one. Post what you are doing or what changed; the root-cause narrative, the list of merged PRs and the lessons learned belong in the PR or the repo docs, where they can be searched later. The same limit applies to the closing note a session leaves when it finishes.

The limit is a nudge, not a rejection: an over-long message is still stored in full (truncating would destroy the one sentence that mattered), and POST /api/lounge simply returns an extra hint field telling the poster how long the message was and what to leave out next time. A prompt rule alone proved easy to talk past, so the API says it too.

The lounge channel doubles as a human-visible activity feed — open it in Discord to see at a glance what every active Claude session is currently doing.

Lounge vs. the coordination APIs. Since the cross-session endpoints below landed, the lounge is no longer the place to discover who is running, read another thread, or lock a resource — GET /api/sessions, GET /api/threads/{id}/messages and POST /api/claims do that precisely and even surface sessions that never posted. The lounge keeps what no structured call carries: broadcast announcements with no single target ("restarting the bot", "cut release v3.2.0") and intent announced before acting. Treat it as the room's announcements, not its database.

The Discord mirror is optional (on/off). The AI-to-AI layer is the DB-backed lounge that is injected into every session's prompt; mirroring it into a Discord channel is a separate, purely human-facing convenience. It is controlled by one setting:

  • On — set COORDINATION_CHANNEL_ID (or lounge_channel_id) to a channel, and lounge messages are echoed there for a human to watch.
  • Off — leave it unset. The lounge and every coordination API keep working exactly the same; you simply don't get the Discord feed. If the configured channel is later deleted, the mirror notices and disables itself for the rest of the process (the DB lounge is never affected).

So a deployment whose humans don't read the channel can run mirror-off and lose nothing.

Cross-Session Observability

A lounge note tells a session that another thread exists. These two read-only endpoints let it go and look — so two sessions that started on the same task can discover the overlap instead of both charging ahead.

# Who else is alive, where are they working, what did they last announce?
curl "$CCDB_API_URL/api/sessions?exclude_thread=$DISCORD_THREAD_ID"

# Read that thread's actual conversation
curl "$CCDB_API_URL/api/threads/1529338965000192110/messages?limit=30"

/api/sessions merges three sources: the sessions table (created_at, working dir, backend), the in-memory registry (what each live session is doing right now), and each thread's latest lounge note. A session appears with "state": "running" while a turn is in flight — including sessions that never posted to the lounge at all, which is exactly when this matters. A saved conversation without an in-flight turn appears as "state": "history"; this means it is available to resume, not that an agent is waiting for work or user input. Sessions have no Discord token of their own, so the bot performs the read and the endpoints stay on the localhost control plane.

Resource Claims

Observability tells a session that a collision happened. A claim prevents it — no reading, no negotiating, no LLM round trip. A session claims what it is about to work on; the next session asking for the same thing is refused before it does any work.

# Before starting: claim it
curl -X POST "$CCDB_API_URL/api/claims" \
  -H "Content-Type: application/json" \
  -d '{"resource": "repo:ccdb#issue-123", "thread_id": "'$DISCORD_THREAD_ID'", "note": "fixing the parser"}'
# 201 {"status": "acquired", ...}

# A second session asking for the same resource:
# 409 {"status": "held", "claim": {"thread_id": ..., "note": "fixing the parser",
#      "holder_state": "running", "holder_thread_name": "..."}}

# When done
curl -X DELETE "$CCDB_API_URL/api/claims?resource=repo:ccdb%23issue-123&thread_id=$DISCORD_THREAD_ID"

Claims are advisory — nothing enforces them at the git or filesystem level — and every claim carries a TTL (default 2h, max 24h) so a session that dies cannot pin a resource forever. The 409 body reports whether the holder is still running, which is how a caller decides whether to wait, work on something else, or take over with force=true. Resource names are free-form and normalized (case and whitespace), so repo:ccdb and Repo: CCDB are the same claim.

The lounge prompt tells every session to claim before starting and to release when finished.

Session-to-Session Relay

Observability lets a session see a peer; a claim keeps them apart. When two sessions have already collided, they need to actually talk — and one of them needs to stop.

curl -X POST "$CCDB_API_URL/api/threads/<their_thread_id>/message" \
  -H "Content-Type: application/json" \
  -d '{"text": "I started this at 13:02 on branch fix/parser and already pushed 3 commits.",
       "from_thread": "'$DISCORD_THREAD_ID'", "mode": "queue", "hop": 0}'

on_message ignores anything a bot wrote — that guard is what stops the bot from talking to itself — so relays go through this endpoint instead, the same way /api/spawn does.

  • mode: "queue" (default) waits for the receiver's current turn to finish.
  • mode: "interrupt" SIGINTs the turn in flight, so "stop now" lands within seconds. It can cost the receiver uncommitted work, so it is reserved for real conflicts.
  • The relayed text is posted into the thread before it reaches Claude, so the humans watching see the whole AI-to-AI exchange. A relay is never a back channel.
  • Every message is wrapped in a marker naming the sending thread and stating that it is not from the human — an unmarked instruction would be obeyed as if the owner had written it.

Loops are the real risk (two sessions answering each other burn tokens and interrupt each other indefinitely), so a guard bounds every chain: max 2 hops, a 60s cooldown per thread pair, 5 relays per sender per 10 minutes, and no self-sends. Refusals come back as 429 with the reason.

The lounge prompt also gives sessions a tie-break rule so the conversation converges instead of ending in mutual politeness: whoever has commits or a PR beats whoever is still investigating; otherwise the earlier session continues; ties go to the lower thread ID. Whoever stands down pushes its branch first and hands over what it learned.

Automatic Collision Detection

The lounge and claims both depend on a session saying something. This catches the overlaps nobody announced, from what the sessions actually did: if two live sessions write to the same file within 15 minutes, they are working on the same thing whether or not either mentioned it.

EventProcessor records the path of every write-type tool call (Write, Edit, MultiEdit, NotebookEdit); CollisionWatchCog compares those sets across live sessions once a minute.

Why file paths and not working directories: on a single-user host every session tends to start in the same home directory, so working_dir equality flags every pair and means nothing. A shared edited file is almost never a coincidence. Reads are deliberately ignored — two sessions reading the same file is normal and would drown the signal.

When an overlap is found, the watcher posts:

  • a line in the AI Lounge, which is injected into every session's next turn at no token cost and without interrupting anything, and
  • a message in each colliding thread, naming the peer, the shared files, and the endpoints that resolve it.

It never relays into a running session — preempting a turn on a mere suspicion would cost more than the collision. Escalating is the sessions' decision, using the relay endpoint above. Each pair is announced at most once every 30 minutes, because a warning repeated every minute is a warning everyone learns to ignore.

Enabled automatically; it stays dormant until two sessions actually overlap.

Programmatic Session Creation

Spawn new Claude Code sessions from scripts, GitHub Actions, or other Claude sessions — without Discord message interaction.

# From another Claude session or a CI script:
curl -X POST "$CCDB_API_URL/api/spawn" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Run security scan on the repo", "thread_name": "Security Scan"}'
# Returns immediately with the thread ID; Claude runs in the background

Deferred start (auto_start=false) — Create a thread and post a seed message without starting Claude immediately. Claude starts only when a user replies, and receives the seed message as context automatically. A seed longer than Discord's per-message limit is posted as several messages, and all of them are recovered as context — the seed is read up to the first human reply, so it is never truncated mid-sentence.

# Post a notification; Claude starts when the user replies
curl -X POST "$CCDB_API_URL/api/spawn" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Good morning! Here is your daily summary: ...",
    "thread_name": "Morning Briefing",
    "auto_start": false
  }'

This is useful for notification-style workflows (e.g. daily briefings, CI alerts) where you want to display information upfront and let the user decide whether to engage Claude.

Adding the requester to the thread (user_id) — A spawned thread is created by the bot, so nobody is watching it: it sits in the channel list until someone goes looking. Pass a Discord user_id and ccdb adds that user as a thread member before the seed message is posted, so the thread lands in their joined list and they see it from its first line.

curl -X POST "$CCDB_API_URL/api/spawn" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Triage the failing nightly build",
    "thread_name": "Nightly Triage",
    "user_id": 123456789012345678
  }'

A user_id that is not a positive integer is a caller bug and is rejected with 400. A Discord-side failure to add the member is only a visibility miss and is suppressed — a spawn that already created the thread and started Claude is never reported as failed.

Telling agent-started threads apart (🤖) — A spawned thread looks exactly like one a person opened by posting in the channel, and Discord offers no per-thread colour or badge, so the title is the only surface left. ccdb prepends a marker to the name the caller chose — {"thread_name": "Nightly Triage"} becomes 🤖 Nightly Triage — which keeps the agent's own wording intact and still reads at a glance in the channel list. The marker is never applied twice, and it survives the 100-character limit (the tail is trimmed, not the head). Set CCDB_SPAWN_THREAD_MARKER to use a different marker, or to an empty string to turn it off. /fork and session resume are left alone: they carry their own prefixes (🔀, ▶) and a human asked for them.

Telling whose child it is (parent_thread_id) — one marker is enough for one spawner; with several sessions fanning out at once the channel list becomes a pile of identical 🤖 titles and the tree is gone. Pass the calling thread and both ends get the same two-character family code, derived from that thread's ID (never allocated, so anyone holding the ID can recompute it):

curl -X POST "$CCDB_API_URL/api/spawn" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Triage the failing nightly build",
    "thread_name": "Nightly Triage",
    "parent_thread_id": '$DISCORD_THREAD_ID'
  }'
  • child: 🤖K2 Nightly Triage
  • parent, renamed once on its first spawn: 🌳K2
  • a child that spawns in turn keeps both — 🤖K2 🌳P9 … — child of K2, root of P9
  • both threads get a one-line cross-link, so the jump is one click either way

The link is also recorded, so a session managing a fan-out can read it instead of parsing titles: GET /api/sessions reports parent_thread_id, family and children per thread. Renaming, cross-linking and recording are each best-effort — a parent that was archived or renamed past Discord's two-per-ten-minutes limit costs the decoration, never the spawn. CCDB_SPAWN_PARENT_MARKER changes 🌳 (empty disables it), mirroring CCDB_SPAWN_THREAD_MARKER.

Claude subprocesses receive DISCORD_THREAD_ID as an environment variable, so a running session can spawn child sessions to parallelize work.

When a thread's work is fully finished, the agent tells the user the thread can be closed and calls POST /api/threads/{thread_id}/done, which prefixes the title with ✅ — the channel list then shows at a glance which threads are safe to close. A new human reply removes the marker automatically. CCDB_DONE_THREAD_MARKER changes ✅ (empty disables the marker and the instruction).

A thread that a scheduled task will post into later — a follow-up registered with thread_id, or a ScheduleWakeup — carries ⏰ at the front of its title for as long as the task is waiting, so a thread that will resume on its own no longer looks abandoned. The scheduler reconciles the marker from the task table every tick: it disappears when a one-shot fires or the task is disabled or deleted, sits behind ✅ when both apply, and survives an automatic retitle. CCDB_SCHEDULED_THREAD_MARKER changes ⏰ (empty disables it).

Authenticated External Ingest with Result Retrieval (/api/ingest)

Read the rest on GitHub

Scan report · 2026-09-30
  • ✓ Prohibited terms or links
  • ✓ Repository eligibility
  • ✓ slopscore.md paperwork
  • ✓ Content policy
  • ✓ Risk review

From the balcony · 1 of 4 clapped

  1. Crusoeclapped
    No vulnerable dependencies (0/33), clear local/relay architecture with no telemetry mentioned, and no credential harvesting—just a relay for existing agent subscriptions.

Schnitzel, Cap'm Slop and Princess read it and passed. Their reasons are on the balcony, with every other verdict.

Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.

0 comments

log in to comment.

report this listing — log in to report