# AgentLine Agent Relay

The relay connects a local agent runtime to live calls. It makes an outbound
WebSocket connection, so it needs no public URL or open port. Install it
whenever the agent has terminal access. Without terminal access, use
https://agentline.md/webhooks.md (polling is not live)

## Install

```bash
curl -fsSL https://api.agentline.cloud/static/agentline_relay.py -o agentline_relay.py
python agentline_relay.py install --agent-id agt_xxx
```

PowerShell:

```powershell
Invoke-WebRequest https://api.agentline.cloud/static/agentline_relay.py -OutFile agentline_relay.py
python agentline_relay.py install --agent-id agt_xxx
```

Use the real agent ID. The installer reads `AGENTLINE_API_KEY` from the
environment, detects the runtime, creates an isolated environment, verifies
readiness, keeps one runtime session per phone call, and installs a persistent
user service. Do not ask the human which runtime they use.

- Check detection: `python agentline_relay.py detect`.
- Override only when needed: `--runtime hermes|openclaw|claude-code|codex|custom`.
- Installing a service or package may trigger the runtime's safety approval.
  Ask the human once; never bypass it.

## Inbox

Live `call.utterance` events invoke the runtime. SMS, call completions, and
owner tasks go to a private local inbox instead of running automatically.
At startup, read the inbox once, handle each event, then acknowledge it:

```bash
python ~/.agentline/agentline_relay.py inbox list --agent-id agt_xxx
python ~/.agentline/agentline_relay.py inbox ack --agent-id agt_xxx --event-id evt_xxx
```

## Runtimes

| Runtime | Session per call |
|---|---|
| Hermes | Local Responses API, named conversation, session key. |
| OpenClaw | Gateway-backed `--session-key`. |
| Claude Code | Captures and resumes the CLI `session_id`. |
| Codex | Resumes the `codex exec` thread ID. |
| `custom` | Your stdin/stdout handler. |

## Answering a Live Caller

1. The caller asks something the hosted voice cannot answer. AgentLine sends
   `call.utterance` with `call_id`, `turn_id`, `session_key`, `conversation`,
   `push_context_url`, and `push_token`.
2. Do the work quickly. The caller is waiting.
3. Push short facts, not a script. The hosted voice rephrases them in one or
   two sentences; it never reads your text aloud.

Over the relay WebSocket:

```json
{"type":"context","event_id":"evt_xxx","call_id":"call_xxx","turn_id":"turn_xxx","push_token":"...","disposition":"done","context":"Three unread emails: John, invoice; Mary, lunch; bank statement."}
{"type":"ack","event_id":"evt_xxx"}
```

Over HTTP, to the event's `push_context_url`:

```bash
curl -s -X POST "https://api.agentline.cloud/v1/calls/call_xxx/context?turn_id=turn_xxx" \
  -H "X-Push-Token: $PUSH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"turn_id":"turn_xxx","disposition":"done","context":"Order ships Tuesday."}'
```

Auth is the push token (`X-Push-Token` header, `?token=`, or `push_token` in the
body) or `Authorization: Bearer $AGENTLINE_API_KEY`. Over MCP:
`push_call_context(call_id="call_xxx", body={"turn_id":"turn_xxx","disposition":"done","context":"..."})`.

## Disposition

Always send `disposition` explicitly.

| Value | Effect |
|---|---|
| `progress` | Adds a note; caller hears hold lines ("One moment."). Not spoken. Turn stays open. |
| `done` | Closes the turn. The voice rephrases the facts. |
| `facts` | Same as `done`. |
| `failed` | Closes the turn and rephrases the facts. Use when the lookup failed. |
| `noop` | Closes the turn with no facts. |

## Responses

| Response | Meaning |
|---|---|
| `200` `delivered=true`, `status: "live"` | Accepted for this turn. |
| `200` `delivered=true`, `status: "duplicate"` | Identical retry already accepted. |
| `409` | Turn is stale or cancelled. Stop. Never reuse the result for another turn. |
| `410` | Call ended. Abandon in-flight work. |

## Do Not

- Send a caller-ready script. Send facts.
- Answer by SMS, chat, or email. The caller is on the phone and hears silence.
- Create plans or files, or ask follow-up questions. Answer now.
- Rely on a generic acknowledgement. Context must be pushed explicitly.
- Apply one turn's result to another `turn_id`.

## Protocol

The relay connects to:

```text
wss://api.agentline.cloud/v1/events/ws?agent_id=agt_xxx&runtime=claude-code
```

`runtime` is `hermes`, `openclaw`, `claude-code`, `codex`, or `custom`. Events
are durable until acknowledged; reconnects replay unacknowledged events.
Deduplicate by `event_id`. Relay mode turns on automatically while a relay or
healthy webhook is connected. With neither, calls run in pure hosted mode.

The relay supports two protocols. `agentline-relay/2` adds typed frames
(`session.start`, `task.open`, `task.amend`, `task.cancel`, `session.end`):
caller follow-ups arrive as `task.amend` on the same `turn_id`, `task.cancel`
withdraws a turn, and `partial` can push one fact early. The installer handles
this. Machine-readable details:
`https://api.agentline.cloud/.well-known/agentline.json`.

AgentLine: https://agentline.cloud | API: https://api.agentline.cloud | All guides: https://agentline.md
