live

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 19 Imported by: 0

README

togo

togo-framework/live

marketplace pkg.go.dev MIT

Bring AI agents to life in togo — push a prompt, an agent answers, the reply streams back over any channel.

Install

togo install togo-framework/live

What it is

live is an AI-agent bridge. You push a prompt — a chat message, a row in a table, or a third-party message like WhatsApp — and a live agent (Claude or omni, typically running inside its own Coder workspace) answers it. The reply is stored, streamed live over a WebSocket, and delivered back out over the channel the conversation came in on.

It generalizes the autopilot loop from "issue → code → PR" into "prompt → reply → any channel", and reuses the same impl/exec provider slots — so omnigent inside coder composes for free.

Data model

Table Meaning
live_agents a persona + backend (echo/claude/omni) + a scoped MCP token
live_conversations one thread with an agent on one channel (chat/whatsapp/…)
live_messages one turn — role=user (a prompt) or role=agent (a reply)

Inserting a role=user message is "pushing a prompt". status=pending → the agent answers it → a role=agent reply appears and is delivered.

Two runtime modes (one API), via LIVE_MODE

agent (default) — bring the agent to life. A long-running claude/omni inside a Coder workspace polls its inbox and pushes replies through the live MCP bridge. Its loop is literally "call live_inbox → think → call live_reply".

claude/omni (in Coder ws) ──MCP──> live-mcp ──HTTPS──> /api/live/inbox + /reply ──> app DB ──> channel

serverzero always-on workspace. An in-app runner claims pending prompts and invokes the agent one-shot via a Responder (echo/claude/cmd), replaying the transcript for context. Great for demos, tests, and simple deployments.

LIVE_MODE=server LIVE_RESPONDER=claude   # or echo (deterministic) / cmd (any binary)

Quick start

# 1. run an app with live installed (server mode + a seeded demo agent)
LIVE_MODE=server LIVE_RESPONDER=echo LIVE_SEED_AGENT=Ada togo serve

# 2. open the built-in chat board
open http://localhost:8080/live

# 3. or drive it over the API
curl -sX POST localhost:8080/api/live/conversations/$CONV/messages \
  -H content-type:application/json -d '{"body":"hello agent"}'

See example/ for a runnable app + a Playwright end-to-end test.

REST API (/api/live)

Method & path Purpose
POST /agents register an agent → returns its bearer token once
GET /agents list agents
POST /conversations · GET /conversations · GET /conversations/{id} manage threads
GET /conversations/{id}/messages transcript
POST /conversations/{id}/messages push a prompt ({"body":"…"})
GET /ws live event stream (new message, typing)
GET /inbox · POST /reply · POST /typing · GET /history agent surfaceAuthorization: Bearer <agent token>

The MCP bridge (cmd/live-mcp)

The binary an agent runs inside its workspace to become live. Tools: live_inbox, live_history, live_typing, live_reply. Configure it in the workspace's .mcp.json:

{ "mcpServers": { "live": {
  "command": "live-mcp",
  "env": { "LIVE_API_URL": "https://your-app.example", "LIVE_AGENT_TOKEN": "lat_…" }
} } }

Channels

chat and table are in-app (stored + streamed over the WS). Third-party channels are driver plugins that implement live.Channel and RegisterChannel(...) — the same shape as notifications channels:

  • live-whatsapp — WhatsApp Cloud API (in + out)
  • live-notifyone bridge to the whole notifications system: a conversation on the slack/discord/notify channel delivers through the existing notifications-slack/-discord/-webpush/-fcm/-pusher drivers, no per-provider code. Set LIVE_NOTIFY_CHANNELS=slack,discord for the fan-out notify channel.

Deploying a live agent (agent mode)

A live-agent Coder workspace (or any box / Docker) that installs claude/omni, self-registers (POST /api/live/agents), writes the returned token into its .mcp.json for live-mcp, then polls and answers on its own. Turn-key scripts, docker-compose, and a Coder template are in deploy/bash deploy/scripts/agent-boot.sh && bash deploy/scripts/agent-loop.sh.


💎 Premium sponsors — ID8 Media · One Studio

Documentation

Overview

Package live turns togo into an AI-agent bridge: push a prompt (a row in a table, a chat message, or a third-party message like WhatsApp) and a live agent — Claude/omni running in its own Coder workspace — answers it, with the reply streamed back over the same channel.

Two runtime modes, selected by LIVE_MODE, share one data model and API:

  • agent-driven (default): a long-running claude/omni inside a Coder workspace polls its inbox and pushes replies through the live MCP bridge (cmd/live-mcp → token-guarded /api/live/inbox + /reply). "The agent reads the table over MCP and pushes responses."
  • server-driven: an in-app runner claims pending prompts and invokes the agent one-shot via a Responder (echo/claude/slot), replaying the transcript for context. Zero always-on workspace; reuses the togo impl/exec slots (omnigent + coder) when selected.

Mounted under /api/live/*; a self-contained chat board is served at /live.

Index

Constants

View Source
const (
	ModeAgent  = "agent"  // agent-driven (MCP inbox/reply)
	ModeServer = "server" // server-driven (in-app runner + Responder)
)

Runtime modes.

View Source
const (
	RoleUser   = "user"
	RoleAgent  = "agent"
	RoleSystem = "system"

	MsgPending    = "pending"
	MsgProcessing = "processing"
	MsgDone       = "done"
	MsgError      = "error"
)

Message roles / statuses.

Variables

This section is empty.

Functions

func RegisterChannel

func RegisterChannel(name string, f ChannelFactory)

RegisterChannel registers an egress channel driver. Call from a driver package's init(); the app activates it by blank-importing the package. This mirrors togo-framework/notifications' RegisterChannel so a live channel and a notifications channel are authored the same way.

Types

type Agent

type Agent struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Persona   string `json:"persona"`   // system prompt / character
	Provider  string `json:"provider"`  // claude | omni | echo
	Workspace string `json:"workspace"` // Coder workspace name (agent-driven mode)
	Status    string `json:"status"`    // idle | busy | offline
	Token     string `json:"token"`     // bearer token for the MCP bridge (write-only)
	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

Agent is a live AI agent: a persona bound to an execution backend (claude or omni, typically running inside its own Coder workspace) and addressed by a scoped token. The token authenticates the agent's MCP bridge to /inbox and /reply so it can read prompts and push responses.

type Channel

type Channel interface {
	// Name is the conversation.channel value this handles (e.g. "whatsapp").
	Name() string
	// Deliver pushes msg (an agent reply) to conv's external thread.
	Deliver(ctx context.Context, conv Conversation, msg Message) error
}

Channel delivers an agent's reply out to where the conversation lives — the egress side. The built-in "table"/"chat" channels are in-app only (the reply is stored and streamed over the WS hub); third-party channels (whatsapp, slack, discord) are registered by driver plugins and push the reply to the provider. Delivery for the in-app channels is handled directly by the server, so a registered Channel only needs to cover its external transport.

type ChannelFactory

type ChannelFactory func(k *togo.Kernel) Channel

ChannelFactory builds a Channel from the kernel (config/secrets from env).

type Conversation

type Conversation struct {
	ID          string `json:"id"`
	AgentID     string `json:"agent_id"`
	Channel     string `json:"channel"`      // table | chat | whatsapp | slack | discord
	ExternalRef string `json:"external_ref"` // provider thread id (whatsapp msisdn, …)
	Title       string `json:"title"`
	Status      string `json:"status"` // open | closed
	CreatedAt   string `json:"created_at"`
	UpdatedAt   string `json:"updated_at"`
}

Conversation is one thread with an agent on one channel. external_ref binds it to a third-party thread (a WhatsApp number, a Slack thread ts, …) so inbound provider callbacks can be routed back to the right conversation.

type Message

type Message struct {
	ID             string `json:"id"`
	ConversationID string `json:"conversation_id"`
	Role           string `json:"role"` // user | agent | system
	Body           string `json:"body"`
	Status         string `json:"status"` // pending | processing | done | error
	Meta           string `json:"meta"`   // opaque JSON
	ClaimedBy      string `json:"claimed_by"`
	ClaimedAt      string `json:"claimed_at"`
	CreatedAt      string `json:"created_at"`
	UpdatedAt      string `json:"updated_at"`
}

Message is one turn. role=user with status=pending is an inbound prompt waiting to be answered; role=agent is the response. Inserting a user Message is "push a prompt to the table".

type Responder

type Responder interface {
	Respond(ctx context.Context, agent Agent, conv Conversation, history []Message, prompt Message) (string, error)
}

Responder produces an agent reply for a prompt in server-driven mode. The loop hands it the agent, the conversation, the full prior transcript and the new user message; it returns the reply text. Implementations: echoResponder (deterministic, for tests/demos), claudeResponder (shells `claude -p`), and slotResponder (reuses the togo `impl`/`exec` provider slots, i.e. omnigent inside a Coder workspace). In agent-driven mode no Responder runs — the agent's own MCP bridge calls /reply instead.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the public handle channel drivers and app code use to drive the live plugin. Retrieve it with FromKernel(k). The plugin publishes itself into the kernel container during Boot.

func FromKernel

func FromKernel(k *togo.Kernel) (*Service, bool)

FromKernel returns the live Service if the plugin is installed.

func (*Service) Ingest

func (svc *Service) Ingest(ctx context.Context, channel, externalRef, body string) (Message, error)

Ingest turns an inbound third-party message into a user prompt on the right conversation. It routes by (channel, externalRef): an existing conversation is reused, otherwise a new one is opened against the channel's default agent (LIVE_CHANNEL_AGENT, else the first registered agent). This is the entry point an inbound channel webhook (WhatsApp/Slack/…) calls. The reply is later delivered back out through the matching Channel.Deliver.

func (*Service) Kernel

func (svc *Service) Kernel() *togo.Kernel

Kernel exposes the underlying togo kernel to channel drivers (for config/logs).

Directories

Path Synopsis
cmd
live-mcp command
Command live-mcp is the MCP bridge an agent runs inside its Coder workspace to "come alive": it exposes tools the running claude/omni calls to read prompts and push replies through a togo app's live plugin.
Command live-mcp is the MCP bridge an agent runs inside its Coder workspace to "come alive": it exposes tools the running claude/omni calls to read prompts and push replies through a togo app's live plugin.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL