Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 0 additions & 136 deletions .github/workflows/release.yml

This file was deleted.

37 changes: 25 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

- ✅ **Multi-agent** - Run multiple isolated AI agents with specialized roles
- ✅ **Multi-team collaboration** - Agents hand off work to teammates via chain execution and fan-out
- ✅ **Multi-channel** - Discord, WhatsApp, and Telegram
- ✅ **Agent-to-agent handoff** - Non-team agents can hand off to each other via `@agent_id` prefix in responses
- ✅ **Multi-channel** - Discord (with slash commands and guild channels), WhatsApp, and Telegram
- ✅ **Web portal (TinyOffice)** - Browser-based dashboard for chat, agents, teams, tasks, logs, and settings
- ✅ **Team Observation** - You can observe agent teams conversations via `tinyclaw team visualize`
- ✅ **Multiple AI providers** - Anthropic Claude and OpenAI Codex using existing subscriptions without breaking ToS
Expand Down Expand Up @@ -100,7 +101,12 @@ The setup wizard will guide you through:
2. Create application → Bot section → Create bot
3. Copy bot token
4. Enable "Message Content Intent"
5. Invite bot using OAuth2 URL Generator
5. Invite bot using OAuth2 URL Generator (include `applications.commands` scope for slash commands)

**Discord features:**
- **Slash commands** — `/agent`, `/team`, `/reset` appear in Discord's autocomplete menu
- **Guild channels** — Bind server channels to specific agents (messages in a bound channel auto-route to that agent without `@` prefix)
- **Agent signatures** — Responses include `— AgentName` footer (disable with `"sign_responses": false` under `channels.discord`)

### Telegram Setup

Expand Down Expand Up @@ -281,15 +287,16 @@ export TINYCLAW_SKIP_UPDATE_CHECK=1

These commands work in Discord, Telegram, and WhatsApp:

| Command | Description | Example |
| ------------------- | ------------------------------------ | ----------------------- |
| `@agent_id message` | Route message to specific agent | `@coder fix the bug` |
| `@team_id message` | Route message to team leader | `@dev fix the auth bug` |
| `/agent` | List all available agents | `/agent` |
| `/team` | List all available teams | `/team` |
| `@agent_id /reset` | Reset specific agent conversation | `@coder /reset` |
| `/reset` | Reset conversation (WhatsApp/global) | `/reset` or `!reset` |
| `message` | Send to default agent (no prefix) | `help me with this` |
| Command | Description | Example |
| ---------------------------- | ------------------------------------ | ----------------------------- |
| `@agent_id message` | Route message to specific agent | `@coder fix the bug` |
| `@team_id message` | Route message to team leader | `@dev fix the auth bug` |
| `/agent` | List all available agents | `/agent` |
| `/team` | List all available teams | `/team` |
| `/reset agent_id [agent_id]` | Reset one or more agent conversations| `/reset coder writer` |
| `message` | Send to default agent (no prefix) | `help me with this` |

On Discord, `/agent`, `/team`, and `/reset` are native slash commands with autocomplete. On Telegram and WhatsApp they work as text commands.

**Note:** The `@agent_id` routing prefix requires a space after it (e.g., `@coder fix` not `@coderfix`).

Expand Down Expand Up @@ -454,7 +461,13 @@ Located at `.tinyclaw/settings.json`:
{
"channels": {
"enabled": ["discord", "telegram", "whatsapp"],
"discord": { "bot_token": "..." },
"discord": {
"bot_token": "...",
"guild_channels": {
"CHANNEL_ID": { "default_agent": "coder" }
},
"sign_responses": true
},
"telegram": { "bot_token": "..." },
"whatsapp": {}
},
Expand Down
92 changes: 92 additions & 0 deletions docs/stream-logs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Per-Agent Log Streaming to Discord Threads

Stream real-time agent activity (tool calls, tool results) into a Discord thread while the agent processes a message. The final answer is still delivered as a reply to the original message.

## How It Works

1. User sends a message in a Discord guild channel
2. A thread is created on that message titled `<AgentName> working...`
3. As the agent works, tool calls and results stream into the thread in real-time
4. When the agent finishes, a completion message is posted in the thread
5. The final response is delivered as a normal reply to the original message

## Enabling

Add `"stream_logs": true` to any agent in `.tinyclaw/settings.json`:

```json
{
"agents": {
"my-agent": {
"name": "MyAgent",
"provider": "anthropic",
"model": "opus",
"working_directory": "/path/to/workspace",
"stream_logs": true
}
}
}
```

To disable, set `"stream_logs": false` or remove the field.

## Requirements

- **Provider**: Only works with `anthropic` (Claude) agents. Other providers fall back to normal invocation silently.
- **Channel**: Only works in Discord guild (server) channels. DMs are skipped gracefully — the agent still responds normally, just without the thread.
- **System**: Requires the `script` command (from `util-linux`, available on all standard Linux systems).

## What Shows in the Thread

| Agent Activity | Thread Message |
|---|---|
| Tool call (Bash) | `` Tool: `Bash` `echo hello` `` |
| Tool call (Read) | `` Tool: `Read` `/path/to/file` `` |
| Tool call (Glob) | `` Tool: `Glob` `**/*.ts` `` |
| Tool result | `Result: <first 200 chars of output>` |
| Agent starts | `Agent **MyAgent** is processing...` |
| Agent finishes | `Agent **MyAgent** finished.` |

Events that are **not** shown: system init, rate limits, final text response, thinking blocks.

## Architecture

```
Discord User
Discord Client ──POST──▶ Queue Processor
│ │
│◀──────── SSE ───────────┤ (stream_start, stream_log, stream_end)
│ │
▼ ▼
Discord Thread Claude CLI (--output-format stream-json --verbose)
```

- **Queue Processor** invokes Claude CLI with `--output-format stream-json --verbose` via a PTY (`script -qec`) to get real-time line-by-line JSON output
- Each JSON line is parsed by `formatStreamEvent()` into a human-readable string
- Formatted events are broadcast as SSE events (`stream_start`, `stream_log`, `stream_end`)
- **Discord Client** connects to the SSE endpoint on startup, creates threads on `stream_start`, buffers and flushes log lines on `stream_log`, and cleans up on `stream_end`

### Batching

Log messages are batched to avoid Discord rate limits:
- Flushed every **2 seconds** or when **15 lines** accumulate (whichever comes first)
- Each message is capped at Discord's **2000 character** limit

## Files Changed

| File | Change |
|---|---|
| `src/lib/types.ts` | Added `stream_logs?: boolean` to `AgentConfig`, new `StreamLogEvent` interface |
| `src/lib/invoke.ts` | Added `runCommandStreaming()` (PTY-based line streaming) and `invokeAgentStreaming()` |
| `src/queue-processor.ts` | Added `formatStreamEvent()` helper and streaming branch in `processMessage()` |
| `src/channels/discord-client.ts` | Added SSE client (`connectSSE`), thread management, batched log delivery |

## Troubleshooting

**Thread not created**: Check that the message is from a guild channel (not a DM) and the agent has `"stream_logs": true`.

**No tool calls in thread**: Check queue logs for `[stream-debug]` entries. If `total lines: 0`, the PTY wrapper may not be working — verify `script` command is available.

**Thread shows only start/end**: The agent may have responded without using any tools (e.g., a simple text reply).
Loading