I created a mechanism to talk to autonomous AI agents in real-time for debugging from Claude Code

I created a mechanism to talk to autonomous AI agents in real-time for debugging from Claude Code

Combining Mailbox API, Discord Webhook, and GitHub Actions as three channels to build a mechanism where Claude Code (development-side AI) can talk to and debug the autonomous agent Ruby (production AI) in real time. I will introduce how to shorten the development cycle where AI debugs AI.
2026.03.23

This page has been translated by machine translation. View original

Introduction

When developing the autonomous AI agent "Ruby", post-deployment verification becomes the biggest bottleneck. I was repeating a manual cycle of writing code, pushing it, waiting for the container to rebuild, sending messages via Discord to check responses, and examining logs to identify problems.

"What if Claude Code (the development AI) could directly talk to Ruby (the production AI) for debugging? Wouldn't that dramatically speed up the development cycle?"

In this article, I'll introduce how I built a system that combines three channels for Claude Code to converse and debug with Ruby in real-time.

Overall Architecture

Claude Code accesses Ruby through three routes:

Channel Purpose Context from Ruby's perspective
Mailbox API Direct conversation as DM DM (full profile)
Discord Webhook Post messages to Discord channel Channel message (chat profile)
debug-logs workflow Retrieve container logs — (read logs only)

Prerequisites/Environment

  • Ruby: Autonomous AI agent (Python 3.12+, Discord/LINE/Slack compatible)
  • Runtime environment: Oracle Cloud VM, Docker container
  • Development environment: Windows 11 + Claude Code CLI
  • Deployment: git push → GitHub Actions → SSH → Docker rebuild
  • Discord: Send messages via Webhook

Channel 1: Mailbox API — Conversation in DM Context

Ruby has a Supabase-based Mailbox API that allows direct conversation through the /api/chat endpoint. Authentication uses SSH Ed25519 key signatures.

How it works

# Generate authentication header and make API call
ts=$(date +%s)
device=$(hostname | tr '[:upper:]' '[:lower:]')
sig=$(uv run python -c "
from cryptography.hazmat.primitives.serialization import load_ssh_private_key
from pathlib import Path; import base64
k = load_ssh_private_key(
    Path.home().joinpath('.ssh','id_ed25519').read_bytes(), password=None)
print(base64.b64encode(k.sign(b'${ts}:${device}')).decode())")

curl -s -X POST \
  -H "X-Device: ${device}" \
  -H "X-Timestamp: ${ts}" \
  -H "X-Signature: ${sig}" \
  -H "Content-Type: application/json" \
  -d '{"text":"Hi Ruby, what model are you running?"}' \
  "https://api.rubyclaw.uk/api/chat"

Response example

{
  "response": "I'm running on Gemini 3.1 Flash Lite. The delegation system is working — I can call Claude Code via the delegate tool."
}

Features

  • Ruby processes it as a DM context → full profile (all personality files + all tools)
  • Response returns as JSON, making it easy for Claude Code to process programmatically
  • Secure SSH key signature authentication

Channel 2: Discord Webhook — Testing in Channel Context

Discord Webhooks allow posting messages to specific channels. Ruby processes these as regular channel messages, allowing testing of channel prompt profiles.

Setup

  1. Discord channel settings → Integrations → Webhooks → Create New
  2. Save Webhook URL to .claude/secrets.json (already in gitignore)
.claude/secrets.json
{
  "discord_webhook_url": "https://discordapp.com/api/webhooks/..."
}

Implemented as a Claude Code skill

I defined a skill in .claude/commands/discord.md that can be called with the /discord command:

.claude/commands/discord.md
Send a message to Ruby on Discord via webhook and optionally check her response.

User input: $ARGUMENTS

## Instructions

1. Read the webhook URL from `.claude/secrets.json`
2. Write message payload to temp file, send via curl
3. Wait 15 seconds, then fetch container logs to check response

Usage

/discord Hi Ruby! What are your Discord formatting rules?

Claude Code automatically:

  • Reads the webhook URL
  • Sends the message via curl
  • Waits 15 seconds then retrieves logs to check Ruby's response

Why Webhooks?

There are several ways to send messages via Discord's REST API, but I chose webhooks because:

  • No bot token needed — Using Ruby's own bot token would cause Ruby to ignore her own messages
  • User tokens violate ToS — Discord prohibits using user tokens for automation
  • Webhooks are safe — Created in 30 seconds from channel settings, requires only URL for sending
  • Ruby processes correctly as channel message — Ideal for testing prompt profiles

DM vs Channel Comparison Testing

The true value of this system is the ability to send the same question via both DM and channel to verify differences in Ruby's behavior:

Test DM (Mailbox API) Channel (Webhook)
Rules loaded SOUL + IDENTITY + USER + CORE + SELF_MODIFY + MEMORY_RULES + SYNC SOUL + IDENTITY + CORE + DISCORD + GROUP_CHAT
Available tools All 12 tools 6 tools (chat set)
Prompt size ~4,200 tokens ~1,500 tokens

Channel 3: debug-logs Workflow — Container Log Retrieval

Even without direct SSH access to the Oracle Cloud VM, we've set up a workflow to retrieve container logs via GitHub Actions.

.github/workflows/debug-logs.yml
name: Debug - Fetch container logs

on:
  workflow_dispatch:
    inputs:
      add_ssh_key:
        description: "Public SSH key to add to VM (optional)"
        required: false
      tail_lines:
        description: "Number of log lines to fetch"
        default: "200"

jobs:
  logs:
    runs-on: ubuntu-latest
    steps:
      - name: Add SSH key to VM (if provided)
        if: inputs.add_ssh_key != ''
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.OCI_HOST }}
          username: ${{ secrets.OCI_USER }}
          key: ${{ secrets.OCI_SSH_KEY }}
          script: |
            mkdir -p ~/.ssh
            echo "${{ inputs.add_ssh_key }}" >> ~/.ssh/authorized_keys
            sort -u -o ~/.ssh/authorized_keys ~/.ssh/authorized_keys

      - name: Fetch container logs
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.OCI_HOST }}
          username: ${{ secrets.OCI_USER }}
          key: ${{ secrets.OCI_SSH_KEY }}
          script: |
            cd /opt/rubyclaw && docker compose ps
            docker compose logs --tail=${{ inputs.tail_lines }}

Usage

# Retrieve logs
gh workflow run debug-logs.yml -f tail_lines="100"

# Read results
gh run view <run_id> --log 2>&1 | grep "rubyclaw-1" | grep -v "healthz"

SSH Key Addition Feature

We've also included functionality to add SSH access from new development machines:

gh workflow run debug-logs.yml \
  -f add_ssh_key="$(cat ~/.ssh/id_ed25519.pub)" \
  -f tail_lines="100"

This allows adding a new machine's public key to the VM's authorized_keys via GitHub Actions' SSH key.

Actual Debugging Flow

Here I'll introduce some cases where I used this system for debugging.

Case: Verifying v0.23.0 post-deployment

After deploying v0.23.0 with major refactoring (prompt profiles + delegation tools), this was the verification flow:

Step 1: Confirm deployment completion

gh run list --workflow deploy.yml --limit 1
# completed  success  Deploy to Oracle Cloud

Step 2: Talk to Ruby via Mailbox API

curl -s -X POST ... \
  -d '{"text":"What model are you running?"}' \
  "https://api.rubyclaw.uk/api/chat"

→ Ruby responds. Confirm model name and tool availability.

Step 3: Test channel context via Discord Webhook

/discord Hi Ruby! What are your rules about markdown tables?

→ Confirm Ruby responds according to Discord rules (DISCORD.md).

Step 4: Check internal behavior via logs

gh workflow run debug-logs.yml -f tail_lines="50"
# → LiteLLM completion() model= gemini-3.1-flash-lite-preview; provider = gemini
# → tool:delegate args={'task': '...'}
# → Delegation completed (1735 chars)

→ Verify the correct model is being used, tools are being called, and no errors are present.

Case: Discovering Rate Limits

During testing, I found this in the logs:

rate_limit_error: 50,000 input tokens per minute

Claude Code was able to detect the rate limit from logs, analyze the cause, and immediately suggest countermeasures (provider switching). What would have taken tens of minutes manually ("Ruby isn't responding → check logs → determine cause") was completed in minutes.

Security Considerations

Element Measures
Webhook URL Stored in .claude/secrets.json, excluded by .gitignore
Mailbox API authentication SSH Ed25519 key signature, device registration
Container logs Via GitHub Actions, OCI SSH keys stored in Secrets
Discord Webhook Created only by channel admins, be careful with URL leakage

Summary

  • Mailbox API → Direct conversation with Ruby in DM context. Authenticated, JSON response
  • Discord Webhook → Testing in channel context. Ideal for prompt profile verification
  • /discord skill → One command from Claude Code for webhook sending + log checking
  • debug-logs workflow → Container log retrieval without SSH. Also allows adding new machine keys

By combining these three, Claude Code can complete the "write code → push → talk to Ruby → check logs" cycle entirely within the terminal. No need for humans to open Discord in a browser and type messages.

AI debugging AI — when this becomes reality, the only bottleneck in the development cycle becomes "waiting for deployment."

Share this article

FacebookHatena blogX