Metadata-Version: 2.4
Name: zillion_agent
Version: 0.3.10
Summary: Zillion for your agent — API MCP server (VMs, sandboxes, credit, SSH keys), Roka chat MCP server and terminal chat, with browser sign-in.
Author: Zillion Network
License: Proprietary
Project-URL: Homepage, https://app.zillion.network
Keywords: zillion,mcp,agent,vm,sandbox
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: websockets>=12.0
Requires-Dist: mcp<2,>=1.21.1

# zillion_agent

Zillion for your own agent (Codex, Claude Code, Cursor, any MCP host) and your
terminal. Two interfaces, one browser sign-in:

- **API MCP** (`zillion-agent api`) — the portal's functions as tools: buy and
  manage VMs and sandboxes, check credit and usage, manage SSH keys. The tools run
  the portal's own operations, so prices, checks and confirmations are the same as
  on the portal.
- **Roka** — chat with the Zillion Agent (Roka):
  - **Roka MCP** (`zillion-agent mcp`) — async `zillion_send` / `zillion_poll`
    tools, so a long Roka task never blocks your agent.
  - **Terminal chat** (`zillion-agent chat`) — streams Roka live.

Account settings, Telegram linking, access tokens and top-ups are not in the
adapter; they stay on the portal (`https://app.zillion.network`).

## Prerequisites

1. **Python 3.10+** and a recent pip (`python3 -m pip install -U pip`).
2. **A Zillion account** — register on the portal; registration is portal-only.

## Install

The package is `zillion-agent` on PyPI. Any of these works; a tool install keeps
it apart from your other Python packages:

```bash
uv tool install zillion-agent              # or: pipx install zillion-agent
                                           # or: python3 -m pip install --user zillion-agent
zillion-agent setup     # registers the servers with your agent apps, then signs you in
```

Check the name: it is `zillion-agent`, from the regular PyPI index, and nothing
else (not a fork, a similar name or another index).

### Updating

```bash
zillion-agent update            # installs the newest release, then runs setup again
zillion-agent update --check    # only says whether there is one
zillion-agent --version
```

`update` reads the newest release from PyPI, checks the downloaded wheel against
its published sha256, and installs that file the way the adapter was installed
(uv tool, pip or pipx). On Windows it prints the command to run instead.

If you installed a beta from TestPyPI, `zillion-agent update` moves you to the
PyPI releases; nothing else is needed.

Restart the agent apps afterwards: they keep the MCP servers running as
subprocesses, which run the old code until restarted. Your sign-in is kept.

### After upgrading to 0.3.0

- **No more PAT.** `ZILLION_EMAIL` / `ZILLION_PAT` and the `email` / `pat` keys in
  `config.json` are no longer read. Run `zillion-agent setup`: it re-registers the
  servers without them and signs you in. You can delete the PAT on the portal's
  Tokens page.
- **New API MCP server**, registered by `zillion-agent setup` next to the Roka server.
- The session token is now sent with every message, as the backend requires.

## Sign in

```bash
zillion-agent login     # opens the sign-in page in your browser
zillion-agent whoami    # which account, and until when
zillion-agent logout    # ends the session and deletes it from this machine
```

`login` prints a link and a short code (`ABCD-EFGH`). The browser page shows the
same code, so you can check the link is yours; sign in with your portal email and
password. On a machine without a browser (SSH, a server), open the printed link
on any device: when the browser is on a different network from the machine that
started the sign-in, the page warns about it and asks for the code. Inside an MCP
host, the agent calls `zillion_login` instead and shows you the same link and code.

Only use a sign-in link you started yourself. Whoever started it receives the
session, so never sign in on a link, or enter a code, that someone sent you.

The adapter's session can only do what the adapter does (Roka chat and the tools
below). Access tokens, password, profile and Telegram stay with a portal session.

The portal's **Agent adapter** page lists the machines signed in to your account,
whether each is connected right now and from which agent apps, and can sign one
out. For that, the adapter sends the machine's host name, OS, CPU architecture,
Python and adapter versions when a sign-in starts, and the agent app's name with
each connection. It keeps a random machine id in `~/.config/zillion/device.json`
(mode 600), so signing in again on the same machine replaces that machine's
earlier session.

An agent's shell cannot keep a command waiting while you are in the browser, so
agents use two steps: `zillion-agent login --start` prints the link and code and
returns, and `zillion-agent login --wait` (up to 30 s, repeat while pending)
finishes the sign-in. The pending sign-in is kept in
`~/.config/zillion/pending_sign_in.json` (mode 600) until it completes or the
link expires.

The session is the portal's own: it lasts **24 hours from the last activity**
(chatting with Roka counts; reading lists does not) and ends early if you change
your password or sign out. When it ends, the tools answer `not_signed_in`; sign
in again. The session is stored in `~/.config/zillion/credentials.json` with mode
600. Your password never passes through the adapter.

A session the backend refuses is deleted at once and never re-sent: repeated
refused sign-ins from one address get that address blocked for a while.

## Set up your agent apps

```bash
zillion-agent setup
```

It registers both servers (`zillion-api` and `zillion`) with every agent app it
finds — Claude Code, Cursor, Codex, Claude Desktop — using the Python it runs
with, installs `SKILL.md` where the app reads skills (Claude Code, Cursor, Codex),
and offers the browser sign-in. Restart the apps afterwards. When an agent runs
`setup` for you, it starts the sign-in right away (the `login --start` step above)
and prints what the agent should do next, so you are signed in before the restart.

- `--dry-run` shows what would change; `--only cursor,codex` limits the apps;
  `--no-skill` / `--no-login` skip those steps; `--remove` undoes everything.
- It only touches the entries named `zillion-api` and `zillion`. A config file is
  copied to `<file>.bak-zillion-<timestamp>` before it is changed; Claude Code is
  configured through `claude mcp add --scope user`.
- Other MCP apps: add a stdio server with command `python -m zillion_agent api`
  (and `mcp`). If no app is found, `setup` prints a config block to paste.

The agent learns how to use the tools without any extra files: each server sends
its usage guide as MCP server instructions when the app connects (the same text
as `SKILL.md`; print it with `zillion-agent skill`). Rules for a single tool sit
in that tool's description.

Both servers share the sign-in; signing in through either one signs in both.

## API MCP tools

| Area | Tools |
|---|---|
| Sign-in | `zillion_login`, `zillion_login_status`, `zillion_logout`, `zillion_whoami` |
| Credit | `zillion_credit` (balance, status, spend, renewals, runway, notices), `zillion_credit_alert`, `zillion_usage(period, start, end)`, `zillion_usage_statements`, `zillion_usage_statement(month)` |
| SSH keys | `zillion_ssh_keys`, `zillion_ssh_key_add(public_key, name)`, `zillion_ssh_key_delete(key_id)` |
| VMs | `zillion_vm_catalog(region)`, `zillion_vm_terms(product_id)`, `zillion_vm_quote(...)`, `zillion_vm_purchase(...)`, `zillion_vm_purchases`, `zillion_vm_purchase_status(purchase_id)`, `zillion_vm_cancel_options(purchase_id)`, `zillion_vm_cancel(purchase_id, when)`, `zillion_servers`, `zillion_vm_power_status(server_ip)`, `zillion_vm_power(server_ip, action, user_confirmed)`, `zillion_server_rename(server_ip, new_name)` |
| Sandboxes | `zillion_sandbox_options`, `zillion_sandbox_create(...)`, `zillion_sandboxes`, `zillion_sandbox_extend(sandbox_id, additional_seconds)`, `zillion_sandbox_suspend`, `zillion_sandbox_resume`, `zillion_sandbox_delete`, `zillion_sandbox_exec(sandbox_id, command, cwd, timeout_seconds)`, `zillion_sandbox_token`, `zillion_sandbox_usage` |

Money and machines are guarded the same way as on the portal:

- **Buying a VM** takes two steps. `zillion_vm_quote` prices it without charging and
  returns what the portal's checkout shows: OS, SSH keys, terms and the billing
  notices, for the agent to show the user before they agree.
  `zillion_vm_purchase` needs `confirmed_total` copied from the quote (if the
  price moved, the order is refused and nothing is charged) and
  `user_accepted_terms`. If the reply is lost, the tool says so; check
  `zillion_vm_purchases` instead of buying again.
- **Sandboxes** bill per second while running. A create that is still starting
  answers `pending`; calling again with the same `idempotency_key` picks up the
  same sandbox instead of starting a second one.
- **stop / shutdown / reboot** need `user_confirmed=true`; one power action per VM
  every 30 seconds. Power control covers VMs bought through Zillion only, marked
  `power_control: true` in `zillion_servers`; other servers answer `no_power_control`.
  GPU VMs take start, stop and reboot (a power off then a start; no graceful shutdown),
  show the outcome of the last power action, and keep billing while powered off.
- **Credit** can be checked but not added: every top-up goes through the portal's
  Billing & usage page. Neither the adapter nor Roka can add credit.
- Server passwords are never returned. A sandbox API token is returned only by
  `zillion_sandbox_create` (once) and `zillion_sandbox_token` (which revokes the
  previous one).

## Configure (optional)

Nothing is required. Preferences come from environment variables or
`~/.config/zillion/config.json`:

| Variable | Meaning |
|---|---|
| `ZILLION_WS_URL` | Backend base URL; defaults to `wss://app-backend.zillion.network` |
| `ZILLION_WORKFLOW_ID` | Pin a Roka workflow; defaults to the standard workflow |
| `ZILLION_ZDR` | `1`/`true` starts new Roka conversations in Zero Data Retention mode |
| `ZILLION_ZDR_ENFORCE` | `1`/`true` refuses to create any persisted Roka conversation |
| `ZILLION_CONFIG` | Path of `config.json` |
| `ZILLION_CREDENTIALS` | Path of the sign-in file (default `~/.config/zillion/credentials.json`) |
| `ZILLION_CA_BUNDLE` | Custom CA bundle (rarely needed; e.g. a TLS-inspecting proxy) |
| `ZILLION_INSECURE_TLS` | `1`/`true` disables TLS verification — testing only, insecure |

```json
{
  "ws_url": "wss://app-backend.zillion.network",
  "workflow_id": "1781915952622",
  "zero_data_retention": false
}
```

The default endpoint uses a normal, publicly trusted certificate; the session is
always sent encrypted. A sign-in belongs to the backend it was made against:
pointing `ZILLION_WS_URL` elsewhere needs a new sign-in.

## Roka: run

```bash
zillion-agent chat              # standard: the conversation is saved
zillion-agent chat --zdr        # Zero Data Retention: nothing is saved
```

`chat` offers the browser sign-in if you are not signed in yet.

### Switching accounts

The account is bound by the authenticated connection, which the Roka MCP server
holds for its whole lifetime. Signing in as another account (`zillion-agent
login`, or `zillion_login`) is noticed on the next `zillion_send` /
`zillion_new_conversation`, which rebinds and reports the switch in the result;
`zillion_reload` forces it immediately, and `zillion_status` shows
`config_changed` when the sign-in on disk no longer matches. Rebinding
**discards conversations started under the previous account**, so any earlier
`session_id` or `turn_id` is invalid. The API MCP server rebinds on its next call.

## Zero Data Retention (ZDR)

A ZDR conversation is **ephemeral**: the backend keeps it in memory for the life
of the session and then discards it. Nothing is written down.

| | Standard conversation | ZDR conversation |
|---|---|---|
| Chat history on disk | Saved | Never written |
| Vector / cross-session memory | Written, and searchable later | Not written; `memory_search` returns nothing |
| `SOUL.md` / `USER.md` / `BEST_PRACTICES.md` | Roka may update them | Writes refused (reads still work) |
| Learned skills | Roka may create/update them | Writes refused (reads still work) |
| Scheduled tasks, alert rules, cross-channel messages | Can be created | Creation refused (listing still works) |
| Appears in portal history | Yes | No |
| Infrastructure operations | Full | Full — nothing is restricted here |

The distinction is **persistence, not capability**. ZDR applies to Roka
conversations only; the API MCP tools act on the account directly.

### A conversation's mode is fixed when it starts

You cannot flip ZDR on or off mid-conversation; switching always means starting
a new conversation. Turning it on would leave the turns already written on disk
stranded, and turning it off would flush the whole supposedly-ephemeral
conversation to disk on the very next save.

### In the terminal

```bash
zillion-agent chat --zdr        # ephemeral
zillion-agent chat --no-zdr     # persisted, even if ZILLION_ZDR=1
zillion-agent chat              # whatever ZILLION_ZDR says (default: persisted)
```

The prompt shows the mode: `you (zdr)>` when nothing is saved, `you>` when the
chat is saved.

| Command | Effect |
|---|---|
| `/zdr` | Show the current mode and exactly what is and isn't saved |
| `/zdr on` | Start a **new** ephemeral conversation (the current one ends) |
| `/zdr off` | Start a **new** persisted conversation (the current one ends) |
| `/new` | New conversation, keeping the current retention mode |
| `/status`, `/reload` | Account and mode; re-read the sign-in |
| `/quit`, `/exit` | Quit, purging the ephemeral session immediately |

A ZDR transcript still sits in your terminal scrollback; clear it if that
matters. Ctrl+C exits without sending the purge; the backend then discards the
session when its idle grace period expires.

### In the Roka MCP server

Strongest first:

1. **Process-level** — register a ZDR server; every conversation it creates is
   ephemeral:

   ```bash
   claude mcp add zillion-zdr -e ZILLION_ZDR=1 -e ZILLION_ZDR_ENFORCE=1 -- python -m zillion_agent mcp
   ```

2. **Session-level** — `zillion_new_conversation(zero_data_retention=True)`
   returns a permanently ephemeral `session_id`.
3. **Per-call** — `zillion_send(message, zero_data_retention=True)` with no
   `session_id`.

Every tool result carries `zero_data_retention` plus a plain-language
`retention` line.

### Limitations

- **The backend does not acknowledge the mode**; the adapter asserts ZDR but
  cannot confirm an old backend honored it.
- **Only one ephemeral conversation per account.** Starting a second ends the
  first, reported as `superseded_session_id`.
- **A dropped connection can end a ZDR conversation.** The adapter re-asserts it
  on reconnect and warns on the next send if the context may be gone.

## How it works

- **Sign-in:** `POST /adapter/login/start` returns the page link, the short code
  and a poll secret only this process holds; the adapter polls
  `/adapter/login/poll` until the user has signed in and receives the session
  once. `zillion-agent logout` calls `/adapter/logout`. The adapter only opens
  https links on zillion.network.
- **Tool annotations:** every tool is marked read-only, a change, or destructive
  (spends credit, stops or deletes something, runs a command), so agent apps can
  ask before the destructive ones. Keep those on "ask every time".
- **API MCP:** one connection to the portal socket
  (`/ws/zillion_portal/client_meta_data/<email>`), first frame
  `{"type":"auth","token":...}`, the token on every message, replies matched by
  `request_id` (or by reply type for the few queued actions).
- **Roka:** one connection to `/ws/zillion_portal/<email>`; frames are routed into
  per-turn buffers behind a non-blocking send/poll API. Retention is attached to
  every outgoing message.

### Docs after install

This README is the package page on PyPI. `zillion-agent setup` installs the skill
into the agent apps it finds, and `zillion-agent skill` prints it.
