Metadata-Version: 2.4
Name: yothere
Version: 1.7.0
Summary: yothere — an ambient-agent cockpit: a voice loop + fleet board that drives any agent brain over the Brain Protocol and tells a human when to look.
Project-URL: Homepage, https://github.com/phios-ai/yothere
Project-URL: Repository, https://github.com/phios-ai/yothere
Author: Phil Wenger, Oscar
License: MIT
License-File: LICENSE
Keywords: agents,ambient-agent,claude-code,cockpit,fleet,voice
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: <3.14,>=3.11
Requires-Dist: pywebpush>=1.14
Requires-Dist: pyyaml>=6
Requires-Dist: websockets>=12
Provides-Extra: dev
Requires-Dist: pyflakes>=3; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: e2e
Requires-Dist: playwright>=1.48; extra == 'e2e'
Provides-Extra: llm
Requires-Dist: litellm>=1.89; extra == 'llm'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == 'mcp'
Provides-Extra: postgres
Requires-Dist: psycopg-pool>=3.2; extra == 'postgres'
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
Provides-Extra: voice
Requires-Dist: aioice>=0.10.2; extra == 'voice'
Requires-Dist: aiortc==1.14.0; extra == 'voice'
Requires-Dist: av==16.1.0; extra == 'voice'
Requires-Dist: cryptography>=49.0.0; extra == 'voice'
Requires-Dist: deepgram-sdk>=3.7; extra == 'voice'
Requires-Dist: fastapi>=0.115; extra == 'voice'
Requires-Dist: google-genai>=1.75; extra == 'voice'
Requires-Dist: onnxruntime>=1.24.4; extra == 'voice'
Requires-Dist: opencv-python==4.13.0.92; extra == 'voice'
Requires-Dist: pipecat-ai-small-webrtc-prebuilt==2.5.0; extra == 'voice'
Requires-Dist: pipecat-ai[deepgram,google,silero,webrtc]==1.4.0; extra == 'voice'
Requires-Dist: pylibsrtp>=1.0.0; extra == 'voice'
Requires-Dist: python-multipart>=0.0.9; extra == 'voice'
Requires-Dist: twilio>=9.0; extra == 'voice'
Requires-Dist: uvicorn[standard]>=0.30; extra == 'voice'
Requires-Dist: wsproto>=1.2; extra == 'voice'
Provides-Extra: voice-billing
Requires-Dist: google-genai>=1.75; extra == 'voice-billing'
Provides-Extra: voice-worker
Requires-Dist: cryptography>=49.0.0; extra == 'voice-worker'
Requires-Dist: deepgram-sdk>=3.7; extra == 'voice-worker'
Requires-Dist: fastapi>=0.115; extra == 'voice-worker'
Requires-Dist: google-genai>=1.75; extra == 'voice-worker'
Requires-Dist: onnxruntime>=1.24.4; extra == 'voice-worker'
Requires-Dist: pipecat-ai[daily,deepgram,google,silero]==1.4.0; extra == 'voice-worker'
Requires-Dist: python-multipart>=0.0.9; extra == 'voice-worker'
Requires-Dist: uvicorn[standard]>=0.30; extra == 'voice-worker'
Requires-Dist: wsproto>=1.2; extra == 'voice-worker'
Provides-Extra: web
Requires-Dist: fastapi>=0.115; extra == 'web'
Requires-Dist: python-multipart>=0.0.9; extra == 'web'
Requires-Dist: uvicorn[standard]>=0.30; extra == 'web'
Requires-Dist: wsproto>=1.2; extra == 'web'
Description-Content-Type: text/markdown

# yothere

**An ambient-agent cockpit.** yothere is the *interface* for long-running agents: a
fleet board, a voice loop, and a headless runner that advances work and tells a
human *when to look*. The agent that actually does the work, the **brain**, is
anything you point yothere at, over one published wire contract:
[`docs/brain-protocol-v1.md`](docs/brain-protocol-v1.md) (WebSocket + JSON-RPC 2.0).

yothere is harness-agnostic by design. The same cockpit drives:

- **Claude Code** running locally (`claude -p` per thread),
- **ubob** or any model-agnostic harness over its WebSocket daemon,
- a **remote brain** over the internet (a hosted sprite, a teammate's stack, a
  work platform) — anything that speaks the Brain Protocol.

It runs the same whether the brain is on this machine or behind a `wss://`
endpoint, so you can start a thread at your desk and watch it from your phone.

> **New here?** Start with [`docs/ONBOARDING.md`](docs/ONBOARDING.md): a 20-minute
> walkthrough from install to a real task advancing, plus how to send feedback.

## Why it exists

A fleet of agents working in the background is only useful if a human knows which
one needs them *now*. yothere is an **attention router for the human**, not a work
router for the agents: pull-on-glance by default, one rate-limited nudge ("N
threads need your eyes"), never a firehose, never interrupting your focus thread.

## Hosted beta — the fastest way in

The hosted cockpit is **live at [app.yothere.ai](https://app.yothere.ai)**
(invite-gated beta). The control plane holds auth, state, and the cockpit; your
own laptop runs the agent (BYO compute). The end-to-end path is
[`docs/BETA-GUIDE.md`](docs/BETA-GUIDE.md); the short version:

```bash
# 0. Sign up with your invite code at https://app.yothere.ai
pipx install yothere                                          # Python 3.11-3.13
yothere login --url https://app.yothere.ai --token <paste>    # token from the cockpit's pairing panel
yothere service          # lease jobs from the control plane; runs `claude` locally
yothere devices list     # paired machines (yothere devices revoke <id> for a lost one)
yothere doctor           # diagnose the install (--bundle for a redacted bug-report tarball)
```

Then dispatch tasks from the cockpit and, when you want to talk to the fleet,
run `yothere voice` on the laptop and hit Connect in the browser. Invites and
support: hey@yothere.ai.

## Install

```bash
# macOS: pip is usually not on PATH (only pip3) and PEP 668 blocks global installs,
# so install the CLI with pipx (run `brew install pipx` first if needed).
pipx install yothere                 # core: fleet runner + board + remote-brain client
# Optional extras bake in at install time, e.g.:
#   pipx install 'yothere[voice]'    # + the Gemini-Live voice surface (WebRTC/Twilio)
#   pipx install 'yothere[voice,llm]'# + optional LLM tiebreak for routing (deterministic otherwise)
```

yothere keeps all of its state under `~/.yothere` (override with `YOTHERE_HOME`;
an existing `~/.relay` is used as a fallback); see
[`docs/configuration.md`](docs/configuration.md) for every env seam. `YOTHERE_*`
is the canonical env namespace; legacy `RELAY_*` names still resolve as
deprecated back-compat aliases (the `yothere.envcompat` shim mirrors both).

## Quickstart — drive the bundled reference brain

```bash
# 1. Start the conformance brain (the smallest valid Brain Protocol implementation).
python -m yothere.voicecall.echo_brain --port 9999 &

# 2. Point yothere at it and spawn a thread.
export YOTHERE_REMOTE_BRAIN_URL=ws://127.0.0.1:9999
export YOTHERE_THREAD_HARNESS=remote
yothere spawn "research agentic commerce"

# 3. Advance the fleet one tick, then glance at the board.
python -m yothere.runner once   # or: python -m yothere.runner loop  (always-on engine)
yothere board --open
```

Swap `YOTHERE_REMOTE_BRAIN_URL` for your real endpoint and yothere drives your brain.
To run threads with a local Claude Code instead, set `YOTHERE_THREAD_HARNESS=claude`.

## The contract

A brain implements [`docs/brain-protocol-v1.md`](docs/brain-protocol-v1.md):
`hello` / `streamSubscribe` / `prompt` / `cancel` / `close`, streaming back
`delta` (text), and optionally `progress`, `status` (drives the attention
router), and `cost`. The reference is
[`src/yothere/voicecall/echo_brain.py`](src/yothere/voicecall/echo_brain.py), ~60
lines. Two load-bearing caveats live in [`SECURITY.md`](SECURITY.md): a remote
brain's cost cap is advisory, and **the brain owns its own safety/permissions**.

## Architecture

```
            yothere (cockpit + voice + runner)            YOUR BRAIN
  ┌───────────────────────────────────────────┐   ┌──────────────────┐
  cli  ──► spawn ──► store (dir-per-thread) ◄── runner ─┐            │
   │         │            │                       │     │  Brain     │
  board ◄─ fleet_state ◄──┘                  brain_advance ──ws──►  Protocol  │
   │     (attention router: rank · focus)          │     │  v1        │
  voice ──► session_manager ──────────────────────┘     └──────────────────┘
```

- **store / thread_model** — the dir-per-thread state machine (atomic
  `status.json`, session-id resume, the worker contract).
- **attention** — deterministic ranking + the human-facing guardrails.
- **runner / worker** — the headless advance engine (cost caps, stale sweep,
  429 usage-cap hold, coalesced nudges).
- **brain/** — the harness clients: local Claude, ubob daemon, any remote brain.
- **board / card** — the glance UI (server-rendered, XSS-safe).

## Hosted mode (multi-tenant cockpit)

The `/overview` cockpit runs in one of two modes, selected by `YOTHERE_AUTH_MODE`:

- **`off` (default) — local, single-user, gated (A1.7).** Every route except the
  liveness probe, the PWA statics, and `/login` requires direct loopback, the
  opt-in trusted tailnet identity, the shared `YOTHERE_VOICECALL_BEARER` bearer, or
  a signed local-session cookie. A browser gets the cookie by entering the token
  once at `/login` (or opening `/overview?token=<bearer>` once); API callers keep
  sending `Authorization: Bearer`. Rotating the bearer logs every browser out.
  `YOTHERE_COCKPIT_PUBLIC=1` (dev only) restores the old open overview/reply set.
- **`hosted` — multi-tenant.** A login is required: the tailnet carve-out is gone,
  every route except `/healthz` + `/login` needs a session cookie, and each account
  is scoped to its **own** `~/.yothere-<tenant>` home (separate threads/data/state),
  so two logins see two isolated fleets with no cross-tenant read. Auth is
  Python-native and stdlib-only (`yothere.voicecall.auth`): scrypt password hashing,
  an opaque session token in SQLite (`YOTHERE_AUTH_DB`, WAL, separate from fleet
  state), login lockout, and a `Secure` cookie tied to the mode (not the request
  URL, so a TLS-terminating proxy can't drop it). Tenant homes live under
  `YOTHERE_TENANTS_ROOT` (default: beside `~/.yothere`).

Hosted env: `YOTHERE_AUTH_MODE=hosted`, `YOTHERE_AUTH_DB=<path>`,
`YOTHERE_TENANTS_ROOT=<dir>`. This mode is **deployed at `app.yothere.ai`** (Fly + managed
Postgres) with invite-gated signup. Execution is **BYO-compute**: each tenant pairs their
own machine, which long-polls a device-token job queue and runs the brain locally, so
advancing a tenant's threads no longer needs a server-side per-tenant runner. A
control-plane supervisor tick handles cross-tenant housekeeping (lease expiry, ask-park).
Signup is invite-gated for the current beta (open public signup is deliberately not
enabled); the Postgres AuthStore reconnects stale connections on use — a pooled
driver remains a scale-out follow-up.

## MCP surface

Drive a yothere fleet from any MCP client (Claude Desktop, etc.) — an orthogonal channel
that exposes the same front doors as the CLI and cockpit over stdio MCP:

- **`spawn_thread(task, mode, focus)`** — spawn fleet thread(s) from a natural-language
  task (read/draft-only, blocks for your approval before any outward action).
- **`board()`** — the fleet at a glance, needs-eyes first.
- **`reply(thread_id, text)`** — approve / edit / reject a blocked thread.

Single-user / local (drives this machine's `~/.yothere` fleet). `mcp` is an optional
extra; install + register:

```bash
pipx install 'yothere[mcp]'
```

```jsonc
// Claude Desktop: claude_desktop_config.json
{ "mcpServers": { "yothere": { "command": "yothere-mcp" } } }
```

The tool logic lives in SDK-free `_*_impl` helpers (`yothere.mcp_server`), so it imports
and tests without the `mcp` dependency installed.

## Status

v1.5.7 — invite-only beta. The fleet runner, board, voice surface, and the
remote-brain path are tested (`python tests/relay_test.py`), and `yothere doctor`
diagnoses an install in place. The **hosted multi-tenant cockpit is live** at
`app.yothere.ai`: invite-gated signup, BYO-compute (your laptop runs the brain via
a device-token job queue while a hosted control plane holds auth + state), and
hosted voice (Gemini-Live over a laptop-hybrid broker with cross-NAT TURN — media
on your paired laptop today; a fully hosted media plane is built and rolling out).
Beta walkthrough: [`docs/BETA-GUIDE.md`](docs/BETA-GUIDE.md); increment log:
[`CHANGELOG.md`](CHANGELOG.md).

## Contributing

Two people build yothere and neither should break `main` alone. Every change goes
through a PR that the other founder approves before it merges — see
[`CONTRIBUTING.md`](CONTRIBUTING.md).

## License

MIT — see [`LICENSE`](LICENSE).
