yothere 1.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yothere-1.5.2/.gitignore +14 -0
- yothere-1.5.2/CHANGELOG.md +227 -0
- yothere-1.5.2/CONTRIBUTING.md +76 -0
- yothere-1.5.2/LICENSE +21 -0
- yothere-1.5.2/PKG-INFO +201 -0
- yothere-1.5.2/QUICKSTART.md +80 -0
- yothere-1.5.2/README.md +155 -0
- yothere-1.5.2/SECURITY.md +49 -0
- yothere-1.5.2/pyproject.toml +92 -0
- yothere-1.5.2/src/yothere/__init__.py +10 -0
- yothere-1.5.2/src/yothere/attention.py +236 -0
- yothere-1.5.2/src/yothere/board.py +220 -0
- yothere-1.5.2/src/yothere/brain/__init__.py +16 -0
- yothere-1.5.2/src/yothere/brain/registry.py +196 -0
- yothere-1.5.2/src/yothere/brain_advance.py +336 -0
- yothere-1.5.2/src/yothere/capture.py +158 -0
- yothere-1.5.2/src/yothere/card.py +297 -0
- yothere-1.5.2/src/yothere/cli.py +624 -0
- yothere-1.5.2/src/yothere/config.py +268 -0
- yothere-1.5.2/src/yothere/demo_brain.py +167 -0
- yothere-1.5.2/src/yothere/doctor.py +342 -0
- yothere-1.5.2/src/yothere/envcompat.py +59 -0
- yothere-1.5.2/src/yothere/experiments/fixtures.jsonl +18 -0
- yothere-1.5.2/src/yothere/fleet_sources/__init__.py +7 -0
- yothere-1.5.2/src/yothere/fleet_sources/remote.py +86 -0
- yothere-1.5.2/src/yothere/fleet_state.py +263 -0
- yothere-1.5.2/src/yothere/listen.py +390 -0
- yothere-1.5.2/src/yothere/llm.py +62 -0
- yothere-1.5.2/src/yothere/maintenance.py +190 -0
- yothere-1.5.2/src/yothere/mcp_register.py +200 -0
- yothere-1.5.2/src/yothere/mcp_server.py +121 -0
- yothere-1.5.2/src/yothere/notify.py +47 -0
- yothere-1.5.2/src/yothere/onboarding.py +501 -0
- yothere-1.5.2/src/yothere/presence.py +157 -0
- yothere-1.5.2/src/yothere/presets.py +161 -0
- yothere-1.5.2/src/yothere/pwa.py +295 -0
- yothere-1.5.2/src/yothere/ratelimit.py +143 -0
- yothere-1.5.2/src/yothere/render.py +518 -0
- yothere-1.5.2/src/yothere/runner.py +659 -0
- yothere-1.5.2/src/yothere/runner_watchdog.py +157 -0
- yothere-1.5.2/src/yothere/scope.py +293 -0
- yothere-1.5.2/src/yothere/service.py +429 -0
- yothere-1.5.2/src/yothere/spawn.py +123 -0
- yothere-1.5.2/src/yothere/store.py +248 -0
- yothere-1.5.2/src/yothere/task_source.py +721 -0
- yothere-1.5.2/src/yothere/tenant_supervisor.py +189 -0
- yothere-1.5.2/src/yothere/thread_model/__init__.py +30 -0
- yothere-1.5.2/src/yothere/thread_model/ask.py +171 -0
- yothere-1.5.2/src/yothere/thread_model/autonomy.py +171 -0
- yothere-1.5.2/src/yothere/thread_model/prompts/thread-system.md +174 -0
- yothere-1.5.2/src/yothere/thread_model/resume.py +542 -0
- yothere-1.5.2/src/yothere/thread_model/thread.py +357 -0
- yothere-1.5.2/src/yothere/thread_model/vt_config.py +182 -0
- yothere-1.5.2/src/yothere/thread_model/worker_policy.py +97 -0
- yothere-1.5.2/src/yothere/voice.py +418 -0
- yothere-1.5.2/src/yothere/voicecall/__init__.py +29 -0
- yothere-1.5.2/src/yothere/voicecall/auth.py +547 -0
- yothere-1.5.2/src/yothere/voicecall/bob_brain.py +392 -0
- yothere-1.5.2/src/yothere/voicecall/config.py +335 -0
- yothere-1.5.2/src/yothere/voicecall/dispatch.py +58 -0
- yothere-1.5.2/src/yothere/voicecall/echo_brain.py +120 -0
- yothere-1.5.2/src/yothere/voicecall/headless_call.py +823 -0
- yothere-1.5.2/src/yothere/voicecall/overview_state.py +428 -0
- yothere-1.5.2/src/yothere/voicecall/pipeline.py +720 -0
- yothere-1.5.2/src/yothere/voicecall/remote_brain.py +399 -0
- yothere-1.5.2/src/yothere/voicecall/requirements.lock +127 -0
- yothere-1.5.2/src/yothere/voicecall/requirements.txt +19 -0
- yothere-1.5.2/src/yothere/voicecall/session_manager.py +1808 -0
- yothere-1.5.2/src/yothere/voicecall/ubob_brain.py +333 -0
- yothere-1.5.2/src/yothere/voicecall/webrtc_server.py +2174 -0
- yothere-1.5.2/src/yothere/web.py +42 -0
- yothere-1.5.2/src/yothere/worker.py +156 -0
- yothere-1.5.2/src/yothere/worker_hooks/__init__.py +72 -0
- yothere-1.5.2/src/yothere/worker_hooks/send_deny_gate.py +311 -0
- yothere-1.5.2/tests/relay_test.py +2508 -0
- yothere-1.5.2/tests/test_auth_pg.py +142 -0
- yothere-1.5.2/tests/test_call_visibility.py +512 -0
- yothere-1.5.2/tests/test_doctor.py +272 -0
- yothere-1.5.2/tests/test_example_brain.py +102 -0
- yothere-1.5.2/tests/test_init.py +203 -0
- yothere-1.5.2/tests/test_invites.py +179 -0
- yothere-1.5.2/tests/test_mcp_register.py +194 -0
- yothere-1.5.2/tests/test_offmode_parity.py +87 -0
- yothere-1.5.2/tests/test_onboarding.py +334 -0
- yothere-1.5.2/tests/test_registry.py +217 -0
- yothere-1.5.2/tests/test_render.py +298 -0
- yothere-1.5.2/tests/test_service.py +250 -0
- yothere-1.5.2/tests/test_session_status.py +122 -0
- yothere-1.5.2/tests/test_task_source.py +498 -0
- yothere-1.5.2/tests/test_tenant_supervisor.py +142 -0
- yothere-1.5.2/tests/test_user_scoping.py +140 -0
- yothere-1.5.2/tests/test_worker_policy.py +449 -0
- yothere-1.5.2/tests/voicecall_test.py +2862 -0
yothere-1.5.2/.gitignore
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to yothere (formerly `relay-cockpit`). Format loosely follows
|
|
4
|
+
Keep a Changelog; versions follow semver.
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **RENAME: the package, module, and PyPI distribution are now `yothere`**
|
|
10
|
+
(formerly `relay-cockpit`). Concretely: the import path `relay.*` -> `yothere.*`
|
|
11
|
+
(`src/relay/` -> `src/yothere/`); the PyPI dist name `relay-cockpit` -> `yothere`
|
|
12
|
+
(`pip install yothere`, extras `[voice]`/`[web]`/`[mcp]`/`[llm]`/`[postgres]`
|
|
13
|
+
unchanged); the MCP console script `relay-mcp` -> `yothere-mcp` and the MCP
|
|
14
|
+
server registration name `relay` -> `yothere`.
|
|
15
|
+
- **CLI verb: `yothere` is now the canonical command (`yothere <verb>`); `relay`
|
|
16
|
+
is kept as a deprecated back-compat alias.** Both console scripts invoke the
|
|
17
|
+
same CLI, so `yothere init`/`yothere board`/... and the old `relay ...` forms
|
|
18
|
+
both work; help/usage and the printed next-step hints now say `yothere`.
|
|
19
|
+
- **Default home: `~/.yothere`** (was `~/.relay`) when neither `YOTHERE_HOME` nor
|
|
20
|
+
`RELAY_HOME` is set. For back-compat, an existing `~/.relay` is used as a
|
|
21
|
+
deprecated fallback when `~/.yothere` does not yet exist, so a host running the
|
|
22
|
+
old default is not stranded. Any explicit `YOTHERE_HOME`/`RELAY_HOME` still
|
|
23
|
+
overrides. The `relay.env` file and the `com.*.relay-*` launchd/systemd unit
|
|
24
|
+
names are unchanged — a running host that pins `RELAY_HOME` keeps its wiring.
|
|
25
|
+
- **Env vars: `YOTHERE_*` is the going-forward namespace; every legacy `RELAY_*`
|
|
26
|
+
name still works.** A back-compat shim (`yothere.envcompat`) mirrors the two
|
|
27
|
+
prefixes bidirectionally at package import (over `os.environ`) and inside the
|
|
28
|
+
env-file parsers, so a host still exporting `RELAY_HOME`, `RELAY_NOTIFIER`, etc.
|
|
29
|
+
is unaffected. The legacy `RELAY_*` names are supported for at least this
|
|
30
|
+
release; new configs should prefer `YOTHERE_*`.
|
|
31
|
+
- **SECURITY-RELEVANT DEFAULT: `claude`-harness worker turns now run
|
|
32
|
+
`--permission-mode bypassPermissions` by default** (full capability), contained
|
|
33
|
+
by a relay-owned PreToolUse send-gate injected via `--settings` (denies
|
|
34
|
+
outward sends: send scripts, `git push`, HTTP/mail egress with a body, MCP
|
|
35
|
+
send tools; plain GET reads stay allowed). Self-hosters upgrading get this
|
|
36
|
+
posture change; opt out with `worker_permission_mode: default` in vt config or
|
|
37
|
+
`RELAY_WORKER_PERMISSION_MODE=default`. The gate fails CLOSED: if its settings
|
|
38
|
+
file cannot be written, the turn drops `--permission-mode` entirely. Hosted
|
|
39
|
+
tenants are clamped to `default` unless the operator sets
|
|
40
|
+
`RELAY_HOSTED_FULL_CAPABILITY=1`. The gate is best-effort argv inspection
|
|
41
|
+
(defense-in-depth), not an adversarial sandbox.
|
|
42
|
+
|
|
43
|
+
Deferred, explicitly out of v1:
|
|
44
|
+
- Brain-side token lifecycle: issue / rotate / revoke / scope.
|
|
45
|
+
- Publish to PyPI (wheels currently ship via GitHub Releases).
|
|
46
|
+
|
|
47
|
+
## [1.5.1] — 2026-07-01
|
|
48
|
+
|
|
49
|
+
Version-integrity release. No runtime code changes.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
- **Wheel version metadata** — the v1.5.0 tag was cut without bumping
|
|
53
|
+
`pyproject.toml`, so the published "v1.5.0" wheel self-reports 1.4.0. This
|
|
54
|
+
release restores tag == package version (1.5.0 is skipped as ambiguous).
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
- **`relay init` — zero-key first run** (`relay.cli`, new `relay.demo_brain`):
|
|
58
|
+
one idempotent command that creates the `RELAY_HOME` layout, detects any
|
|
59
|
+
existing brain (env url / `brains.yaml` / `claude` CLI), seeds a commented
|
|
60
|
+
`brains.yaml` with a bundled demo brain when the file doesn't exist (never
|
|
61
|
+
overwrites anything), and prints the exact next commands. `relay init --demo`
|
|
62
|
+
starts the bundled Brain Protocol v1 demo brain on a free localhost port and
|
|
63
|
+
drives one canned thread through the real runner -> worker -> brain path
|
|
64
|
+
(blocked -> reply -> done), filling the board with no API key.
|
|
65
|
+
- Remote-brain `done` turns now also write `summary` (not just `last_result`),
|
|
66
|
+
so a remote brain's final answer actually renders on the board / `relay show`.
|
|
67
|
+
- **Release guard** in `.github/workflows/release.yml`: the job fails early when
|
|
68
|
+
the pushed tag does not match the `pyproject.toml` version, so a metadata
|
|
69
|
+
mismatch can never publish again.
|
|
70
|
+
- `docs/RELEASING.md` — the release checklist (bump + changelog + tag + verify).
|
|
71
|
+
- Tester onboarding guide (`docs/ONBOARDING.md`) + tester-feedback issue
|
|
72
|
+
template (#12; merged after the v1.5.0 tag, so first shipped in this wheel).
|
|
73
|
+
|
|
74
|
+
## [1.5.0] — 2026-06-30
|
|
75
|
+
|
|
76
|
+
Customer-cockpit Session 4b: the locally-proven self-onboarding cockpit becomes a
|
|
77
|
+
deployable single-box hosted service (Fly.io), with tenant agents advancing
|
|
78
|
+
against a hosted brain over the Brain Protocol. (#11)
|
|
79
|
+
|
|
80
|
+
> Note: the published v1.5.0 wheel self-reports version 1.4.0 (the tag was cut
|
|
81
|
+
> without a `pyproject.toml` bump) — fixed in 1.5.1.
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
- **Postgres-backed AuthStore** (`[postgres]` extra) — a one-file driver seam in
|
|
85
|
+
`relay.voicecall.auth`: `DATABASE_URL` set → Postgres via lazily-imported
|
|
86
|
+
psycopg; unset → SQLite, byte-identical to before. Schema self-initialises on
|
|
87
|
+
first connect; public method surface unchanged.
|
|
88
|
+
- **Per-tenant hosted brain** — accounts gain nullable `brain_url`/`brain_token`
|
|
89
|
+
+ `auth.tenant_brain()` (per-account override, else deploy-wide
|
|
90
|
+
`RELAY_HOSTED_BRAIN_URL`/`TOKEN`); the tenant supervisor injects
|
|
91
|
+
`RELAY_REMOTE_BRAIN_URL`/`TOKEN` per tenant so each runner's `remote` harness
|
|
92
|
+
advances with that tenant's token.
|
|
93
|
+
- **`relay.web` + `config.web_config()`** — boot the FastAPI cockpit without the
|
|
94
|
+
Twilio/Gemini required-keys gate (voice degrades to "unavailable"); lean
|
|
95
|
+
`[web]` extra (FastAPI/uvicorn only, no media stack).
|
|
96
|
+
- **Container + deploy**: `deploy/{Dockerfile,entrypoint.sh,fly.toml}`
|
|
97
|
+
(python:3.12-slim, tini, one always-on machine, durable `/data` volume) +
|
|
98
|
+
`docs/DEPLOY-fly.md` runbook.
|
|
99
|
+
- CI: dedicated Postgres parity job (`tests/test_auth_pg.py`) + off-mode parity
|
|
100
|
+
suite (`tests/test_offmode_parity.py`) locking the local-first invariant.
|
|
101
|
+
|
|
102
|
+
## [1.4.0] — 2026-06-30
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
- **Opt-in keyless tailnet Connect (`RELAY_TRUST_TAILNET_IDENTITY=1`)** — the
|
|
106
|
+
bearer-gated cockpit routes accept the verified `tailscale-user-login` header
|
|
107
|
+
injected by `tailscale serve`, so a tailnet browser connects without the token
|
|
108
|
+
in the URL. Spoof-safe: the identity is trusted only when no cloudflare edge
|
|
109
|
+
marker (`cf-ray`/`cf-connecting-ip`) is present, so the public cloudflared
|
|
110
|
+
tunnel can never forge it. Default (flag unset) is byte-identical:
|
|
111
|
+
fully bearer-gated. (#10)
|
|
112
|
+
|
|
113
|
+
## [1.3.0] — 2026-06-30
|
|
114
|
+
|
|
115
|
+
Customer-cockpit Session 4: self-onboarding, local-first. A stranger redeems an
|
|
116
|
+
invite, signs up, and is walked through a guided first task — zero terminal
|
|
117
|
+
steps. All surfaces are hosted-mode-only; off mode stays byte-identical.
|
|
118
|
+
|
|
119
|
+
### Added
|
|
120
|
+
- **Self-onboarding (hosted mode)** — invite-gated `GET/POST /signup` +
|
|
121
|
+
guided `GET/POST /onboarding` (same spawn front door as `/dispatch`); invites
|
|
122
|
+
table with atomic single-use consume, per-account `budget_usd`, and a
|
|
123
|
+
`relay invite create` CLI. Both routes 404 in off mode. (#8)
|
|
124
|
+
- **Runner-per-tenant supervisor** (`relay.tenant_supervisor`) — reconciles one
|
|
125
|
+
`relay.runner loop` per tenant with per-tenant `RELAY_HOME`/`RELAY_USER` and a
|
|
126
|
+
daily cost cap (`RELAY_DAILY_COST_CAP_USD`), so non-Phil tenants' threads
|
|
127
|
+
actually advance. (#8)
|
|
128
|
+
- **MCP server surface** (`relay.mcp_server`, `relay-mcp` console script, `[mcp]`
|
|
129
|
+
extra) — drive a fleet from any MCP client over stdio: `spawn_thread`, `board`,
|
|
130
|
+
`reply`. Wraps the same spawn/store/reply front doors as the CLI/cockpit; the tool
|
|
131
|
+
logic is SDK-free `_*_impl` helpers so it imports + tests without `mcp` installed.
|
|
132
|
+
Single-user/local (drives `~/.relay`); orthogonal to the hosted cockpit. (#7)
|
|
133
|
+
|
|
134
|
+
## [1.2.0] — 2026-06-30
|
|
135
|
+
|
|
136
|
+
Customer-cockpit Session 3: real auth + multi-tenant isolation, gated entirely
|
|
137
|
+
behind a deployment-mode flag so local single-user use is byte-identical to before.
|
|
138
|
+
|
|
139
|
+
### Added
|
|
140
|
+
- **Hosted mode (`RELAY_AUTH_MODE=hosted`)** — a Python-native, stdlib-only auth
|
|
141
|
+
layer (`relay.voicecall.auth`): scrypt password hashing, opaque session token in
|
|
142
|
+
SQLite (`RELAY_AUTH_DB`, WAL + busy_timeout, separate from fleet state), login
|
|
143
|
+
lockout, KDF-on-unknown-user (no existence timing leak), token rotation on login,
|
|
144
|
+
and a `Secure` cookie tied to the mode (not the request URL).
|
|
145
|
+
- **Per-tenant isolation** — each account is scoped to its own `~/.relay-<tenant>`
|
|
146
|
+
home under `RELAY_TENANTS_ROOT`. The cockpit resolves the tenant per request and
|
|
147
|
+
threads an explicit `root=` into spawn/store/fleet_state — never the process
|
|
148
|
+
singleton — so two logins see two isolated fleets with no cross-tenant read.
|
|
149
|
+
- `GET/POST /login` + `POST /logout` routes and a brand-consistent login page; the
|
|
150
|
+
workspace chip now renders the logged-in account (dynamic; off mode = static).
|
|
151
|
+
- `fleet_state.load_fleet(root=…)`, `config.auth_mode`, `config.tenants_root`,
|
|
152
|
+
`config.auth_db`.
|
|
153
|
+
|
|
154
|
+
### Changed
|
|
155
|
+
- In hosted mode the tailnet/loopback public carve-out is removed: every route
|
|
156
|
+
except `/healthz` + `/login` requires a session. Off mode is unchanged (the gate
|
|
157
|
+
short-circuits to the original path; no auth/tenant code runs).
|
|
158
|
+
|
|
159
|
+
## [1.1.0] — 2026-06-30
|
|
160
|
+
|
|
161
|
+
Architecture-audit pass: re-applied the 84-finding relay audit (originally landed
|
|
162
|
+
in the bob monorepo) against the package layout. Net ~1250 lines removed, runner
|
|
163
|
+
reliability hardened, voicecall teardown made deterministic, test suite raised to
|
|
164
|
+
231 (relay) + 177/3-skip (voicecall).
|
|
165
|
+
|
|
166
|
+
### Removed
|
|
167
|
+
- **The dead offline router + feedback-learning loop** — `router.py`,
|
|
168
|
+
`router_learn.py`, and the `relay feedback` CLI subcommand are deleted (the
|
|
169
|
+
cascade that called `router.decide()` was retired conceptually but never removed;
|
|
170
|
+
it never ran in production). The live spawn-path classifier is `scope.py`, which
|
|
171
|
+
is unaffected.
|
|
172
|
+
- **`attention.should_ping` / `push_pings` / `maybe_llm_rerank`** and the divergent
|
|
173
|
+
fleet-ping interval constant — the live nudge gate lives in the runner; the
|
|
174
|
+
attention copies were dead and drifting.
|
|
175
|
+
- Dead voicecall-brain probes (`connect()`/`hello()`), `dispatch` GREETING/GOODBYE
|
|
176
|
+
constants, and `scope.log_case`'s unwired logging sink.
|
|
177
|
+
|
|
178
|
+
### Fixed (reliability)
|
|
179
|
+
- **Stale-429 false hold** — the 429 scan is now scoped to the current turn, so a
|
|
180
|
+
prior turn's rate-limit no longer falsely holds a healthy thread.
|
|
181
|
+
- **Silent cost-ledger overspend** — a record_cost write failure now emits a
|
|
182
|
+
rate-limited nudge / stderr signal instead of being swallowed, so the daily cap
|
|
183
|
+
can't be defeated with zero signal. The cost ledger is compacted by maintenance.
|
|
184
|
+
- **Voicecall teardown leaks** — `SessionManager.cancel_all()` cancels in-flight
|
|
185
|
+
bg sessions and releases `worker.lock` fds on call-end/evict; `evict()` awaits
|
|
186
|
+
the cancelled runner; the sessions dict is bounded; `config.load` raises
|
|
187
|
+
`ConfigError` instead of `SystemExit`.
|
|
188
|
+
- Quiet-hours honors the configured timezone (defensive `getattr` for callers
|
|
189
|
+
without a `timezone` field); PTT mode gets the transcribe watchdog; `listen`
|
|
190
|
+
uses `presence.runner_alive()` instead of the false-green pgrep probe.
|
|
191
|
+
- `send_nudge` extracted into `relay.notify` so the watchdog stops importing the
|
|
192
|
+
whole runner engine.
|
|
193
|
+
|
|
194
|
+
### Changed
|
|
195
|
+
- `RELAY_THREADS_DIR` is `mkdir`-guarded; `runner.lock` records its PID; the
|
|
196
|
+
needs-eyes nudge count aligns to the freshly-changed subset; `_WAKE` regex and
|
|
197
|
+
the fleet state vocabulary are each single-sourced.
|
|
198
|
+
|
|
199
|
+
## [1.0.0] — 2026-06-29
|
|
200
|
+
|
|
201
|
+
First standalone release, extracted from the bob monorepo into `phios-ai/relay`.
|
|
202
|
+
|
|
203
|
+
### Added
|
|
204
|
+
- **Pip-installable package** `relay-cockpit` with the `relay` console script and
|
|
205
|
+
a `[voice]` extra for the Gemini-Live surface, `[llm]` for the optional routing
|
|
206
|
+
tiebreak.
|
|
207
|
+
- **`relay.config.settings`** — one env-overridable config rooted at `~/.relay`,
|
|
208
|
+
replacing the bob monorepo path anchoring.
|
|
209
|
+
- **`relay.thread_model`** — the de-vendored dir-per-thread Thread model, now a
|
|
210
|
+
first-class package (the old exec-based import landmine is gone).
|
|
211
|
+
- **Brain registry** (`relay.brain.registry`) — config-driven named brains in
|
|
212
|
+
`~/.relay/brains.yaml`, so a thread can target a local Claude, a ubob daemon, or
|
|
213
|
+
any remote Brain-Protocol endpoint by name.
|
|
214
|
+
- **Per-user scoping** (`RELAY_USER`) — a shared fleet no longer cross-shows
|
|
215
|
+
another person's threads.
|
|
216
|
+
- **`sessionStatus`** — an additive Brain-Protocol v1 method + a remote fleet
|
|
217
|
+
source, so a client with no local thread filesystem (phone, web, another
|
|
218
|
+
machine) can render the board by querying the brain. This is the "use it
|
|
219
|
+
online" path.
|
|
220
|
+
|
|
221
|
+
### Changed
|
|
222
|
+
- The headless worker is spawned via `python -m relay.worker`; the notifier,
|
|
223
|
+
launchd label, and all paths are config seams.
|
|
224
|
+
|
|
225
|
+
### Notes
|
|
226
|
+
- Safety/permissions and real cost budgets live on the brain side for remote
|
|
227
|
+
brains — see [SECURITY.md](SECURITY.md).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Contributing to yothere
|
|
2
|
+
|
|
3
|
+
yothere is a shipped product that other systems (bob, SIDES) depend on. Two people
|
|
4
|
+
build it, and neither should be able to break `main` alone. This is our working
|
|
5
|
+
agreement.
|
|
6
|
+
|
|
7
|
+
## Golden rule
|
|
8
|
+
|
|
9
|
+
**Never commit to `main` directly.** Every change flows:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
branch → pull request → the OTHER founder approves → CI green → merge
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`main` is protected on GitHub: you cannot merge without one approval, and you
|
|
16
|
+
cannot approve your own PR. So the review is mutual by construction. Either Oscar
|
|
17
|
+
approves Phil's change, or Phil approves Oscar's, before more parts of the product
|
|
18
|
+
get changed.
|
|
19
|
+
|
|
20
|
+
## What needs a real review vs a glance
|
|
21
|
+
|
|
22
|
+
Everything goes through a PR. How hard the reviewer looks depends on the change.
|
|
23
|
+
|
|
24
|
+
**Bigger changes — read the whole diff.** Anything touching:
|
|
25
|
+
- `src/yothere/` core (runner, watchdog, fleet, brain, auth, registry)
|
|
26
|
+
- the brain protocol (`docs/brain-protocol-v1.md` and the wire code that implements it)
|
|
27
|
+
- `deploy/` (Dockerfile, entrypoint, fly config)
|
|
28
|
+
- DB schema, migrations, or the auth store
|
|
29
|
+
- `.github/workflows/` (CI/release)
|
|
30
|
+
- `pyproject.toml`, dependencies, or anything that cuts a release/tag
|
|
31
|
+
|
|
32
|
+
For these, the reviewer reads the diff, checks that tests cover the change, and
|
|
33
|
+
explicitly flags any breaking change to the Brain Protocol or to a consumer
|
|
34
|
+
(bob, SIDES).
|
|
35
|
+
|
|
36
|
+
**Trivial changes — fast-approve on sight.** Docs, comments, CHANGELOG, README,
|
|
37
|
+
test-only additions. Still a PR (the gate always applies), but the review is a
|
|
38
|
+
quick look.
|
|
39
|
+
|
|
40
|
+
## Before you request review (author)
|
|
41
|
+
|
|
42
|
+
- Tests pass locally:
|
|
43
|
+
```bash
|
|
44
|
+
PYTHONPATH=src python tests/relay_test.py
|
|
45
|
+
# plus the feature suites you touched, e.g.
|
|
46
|
+
PYTHONPATH=src python tests/test_registry.py
|
|
47
|
+
```
|
|
48
|
+
- CHANGELOG updated if the change is notable.
|
|
49
|
+
- PR is scoped small. Split unrelated changes into separate PRs.
|
|
50
|
+
- Fill in the PR template (it loads automatically).
|
|
51
|
+
|
|
52
|
+
## When you review (reviewer)
|
|
53
|
+
|
|
54
|
+
Ask: does this break the Brain Protocol or another consumer? Are there tests?
|
|
55
|
+
Are migrations safe and reversible? Then **Approve**, **Request changes**, or
|
|
56
|
+
**Comment**. Resolve all conversations before merge (the gate requires it).
|
|
57
|
+
|
|
58
|
+
## Merging
|
|
59
|
+
|
|
60
|
+
Squash-merge. The branch is auto-deleted after merge. `main` must be up to date
|
|
61
|
+
first (GitHub enforces this).
|
|
62
|
+
|
|
63
|
+
## Emergency escape hatch
|
|
64
|
+
|
|
65
|
+
Admins can bypass the gate (`enforce_admins` is off) — this exists **only** for a
|
|
66
|
+
genuine emergency: prod is down, or a live security issue. If you bypass, you must:
|
|
67
|
+
|
|
68
|
+
1. Post the diff to the other person immediately, and
|
|
69
|
+
2. Open a retroactive PR the same day so the change is on the record.
|
|
70
|
+
|
|
71
|
+
The escape hatch is never for skipping review out of impatience. If you are
|
|
72
|
+
tempted to use it because the other person is asleep, wait or ping them.
|
|
73
|
+
|
|
74
|
+
## Branch naming
|
|
75
|
+
|
|
76
|
+
`feat/…`, `fix/…`, `chore/…`, `docs/…`.
|
yothere-1.5.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Phil Wenger and Oscar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
yothere-1.5.2/PKG-INFO
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yothere
|
|
3
|
+
Version: 1.5.2
|
|
4
|
+
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.
|
|
5
|
+
Project-URL: Homepage, https://github.com/phios-ai/yothere
|
|
6
|
+
Project-URL: Repository, https://github.com/phios-ai/yothere
|
|
7
|
+
Author: Phil Wenger, Oscar
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agents,ambient-agent,claude-code,cockpit,fleet,voice
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: pywebpush>=1.14
|
|
21
|
+
Requires-Dist: pyyaml>=6
|
|
22
|
+
Requires-Dist: websockets>=12
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pyflakes>=3; extra == 'dev'
|
|
25
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
26
|
+
Provides-Extra: llm
|
|
27
|
+
Requires-Dist: litellm>=1.89; extra == 'llm'
|
|
28
|
+
Provides-Extra: mcp
|
|
29
|
+
Requires-Dist: mcp>=1.2; extra == 'mcp'
|
|
30
|
+
Provides-Extra: postgres
|
|
31
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
|
|
32
|
+
Provides-Extra: voice
|
|
33
|
+
Requires-Dist: deepgram-sdk>=3.7; extra == 'voice'
|
|
34
|
+
Requires-Dist: fastapi>=0.115; extra == 'voice'
|
|
35
|
+
Requires-Dist: google-genai>=1.75; extra == 'voice'
|
|
36
|
+
Requires-Dist: pipecat-ai-small-webrtc-prebuilt>=2.5; extra == 'voice'
|
|
37
|
+
Requires-Dist: pipecat-ai[deepgram,google,silero,webrtc]>=1.4; extra == 'voice'
|
|
38
|
+
Requires-Dist: twilio>=9.0; extra == 'voice'
|
|
39
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'voice'
|
|
40
|
+
Requires-Dist: wsproto>=1.2; extra == 'voice'
|
|
41
|
+
Provides-Extra: web
|
|
42
|
+
Requires-Dist: fastapi>=0.115; extra == 'web'
|
|
43
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'web'
|
|
44
|
+
Requires-Dist: wsproto>=1.2; extra == 'web'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# yothere
|
|
48
|
+
|
|
49
|
+
**An ambient-agent cockpit.** yothere is the *interface* for long-running agents: a
|
|
50
|
+
fleet board, a voice loop, and a headless runner that advances work and tells a
|
|
51
|
+
human *when to look*. The agent that actually does the work, the **brain**, is
|
|
52
|
+
anything you point yothere at, over one published wire contract:
|
|
53
|
+
[`docs/brain-protocol-v1.md`](docs/brain-protocol-v1.md) (WebSocket + JSON-RPC 2.0).
|
|
54
|
+
|
|
55
|
+
yothere is harness-agnostic by design. The same cockpit drives:
|
|
56
|
+
|
|
57
|
+
- **Claude Code** running locally (`claude -p` per thread),
|
|
58
|
+
- **ubob** or any model-agnostic harness over its WebSocket daemon,
|
|
59
|
+
- a **remote brain** over the internet (a hosted sprite, a teammate's stack, a
|
|
60
|
+
work platform) — anything that speaks the Brain Protocol.
|
|
61
|
+
|
|
62
|
+
It runs the same whether the brain is on this machine or behind a `wss://`
|
|
63
|
+
endpoint, so you can start a thread at your desk and watch it from your phone.
|
|
64
|
+
|
|
65
|
+
> **New here?** Start with [`docs/ONBOARDING.md`](docs/ONBOARDING.md): a 20-minute
|
|
66
|
+
> walkthrough from install to a real task advancing, plus how to send feedback.
|
|
67
|
+
|
|
68
|
+
## Why it exists
|
|
69
|
+
|
|
70
|
+
A fleet of agents working in the background is only useful if a human knows which
|
|
71
|
+
one needs them *now*. yothere is an **attention router for the human**, not a work
|
|
72
|
+
router for the agents: pull-on-glance by default, one rate-limited nudge ("N
|
|
73
|
+
threads need your eyes"), never a firehose, never interrupting your focus thread.
|
|
74
|
+
|
|
75
|
+
## Install
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install yothere # core: fleet runner + board + remote-brain client
|
|
79
|
+
pip install 'yothere[voice]' # + the Gemini-Live voice surface (WebRTC/Twilio)
|
|
80
|
+
pip install 'yothere[llm]' # + optional LLM tiebreak for routing (deterministic otherwise)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
yothere keeps all of its state under `~/.yothere` (override with `YOTHERE_HOME`,
|
|
84
|
+
or the legacy `RELAY_HOME`; an existing `~/.relay` is used as a fallback); see
|
|
85
|
+
[`docs/configuration.md`](docs/configuration.md) for every env seam (each `RELAY_*`
|
|
86
|
+
name also accepts its `YOTHERE_*` sibling).
|
|
87
|
+
|
|
88
|
+
## Quickstart — drive the bundled reference brain
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# 1. Start the conformance brain (the smallest valid Brain Protocol implementation).
|
|
92
|
+
python -m yothere.voicecall.echo_brain --port 9999 &
|
|
93
|
+
|
|
94
|
+
# 2. Point yothere at it and spawn a thread.
|
|
95
|
+
export RELAY_REMOTE_BRAIN_URL=ws://127.0.0.1:9999
|
|
96
|
+
export RELAY_THREAD_HARNESS=remote
|
|
97
|
+
yothere spawn "research agentic commerce"
|
|
98
|
+
|
|
99
|
+
# 3. Advance the fleet one tick, then glance at the board.
|
|
100
|
+
python -m yothere.runner once # or: python -m yothere.runner loop (always-on engine)
|
|
101
|
+
yothere board --open
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Swap `RELAY_REMOTE_BRAIN_URL` for your real endpoint and yothere drives your brain.
|
|
105
|
+
To run threads with a local Claude Code instead, set `RELAY_THREAD_HARNESS=claude`.
|
|
106
|
+
|
|
107
|
+
## The contract
|
|
108
|
+
|
|
109
|
+
A brain implements [`docs/brain-protocol-v1.md`](docs/brain-protocol-v1.md):
|
|
110
|
+
`hello` / `streamSubscribe` / `prompt` / `cancel` / `close`, streaming back
|
|
111
|
+
`delta` (text), and optionally `progress`, `status` (drives the attention
|
|
112
|
+
router), and `cost`. The reference is
|
|
113
|
+
[`src/yothere/voicecall/echo_brain.py`](src/yothere/voicecall/echo_brain.py), ~60
|
|
114
|
+
lines. Two load-bearing caveats live in [`SECURITY.md`](SECURITY.md): a remote
|
|
115
|
+
brain's cost cap is advisory, and **the brain owns its own safety/permissions**.
|
|
116
|
+
|
|
117
|
+
## Architecture
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
yothere (cockpit + voice + runner) YOUR BRAIN
|
|
121
|
+
┌───────────────────────────────────────────┐ ┌──────────────────┐
|
|
122
|
+
cli ──► spawn ──► store (dir-per-thread) ◄── runner ─┐ │
|
|
123
|
+
│ │ │ │ │ Brain │
|
|
124
|
+
board ◄─ fleet_state ◄──┘ brain_advance ──ws──► Protocol │
|
|
125
|
+
│ (attention router: rank · focus) │ │ v1 │
|
|
126
|
+
voice ──► session_manager ──────────────────────┘ └──────────────────┘
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- **store / thread_model** — the dir-per-thread state machine (atomic
|
|
130
|
+
`status.json`, session-id resume, the worker contract).
|
|
131
|
+
- **attention** — deterministic ranking + the human-facing guardrails.
|
|
132
|
+
- **runner / worker** — the headless advance engine (cost caps, stale sweep,
|
|
133
|
+
429 usage-cap hold, coalesced nudges).
|
|
134
|
+
- **brain/** — the harness clients: local Claude, ubob daemon, any remote brain.
|
|
135
|
+
- **board / card** — the glance UI (server-rendered, XSS-safe).
|
|
136
|
+
|
|
137
|
+
## Hosted mode (multi-tenant cockpit)
|
|
138
|
+
|
|
139
|
+
The `/overview` cockpit runs in one of two modes, selected by `RELAY_AUTH_MODE`:
|
|
140
|
+
|
|
141
|
+
- **`off` (default) — local, single-user.** No login. The page, its SSE stream
|
|
142
|
+
(`/live`), and the plan-review reply are reachable over loopback/tailnet
|
|
143
|
+
(`_PUBLIC_PATHS`); the dangerous surface (WebRTC offer, `/harness`, `/start`,
|
|
144
|
+
`/client`) stays behind the `RELAY_VOICECALL_BEARER` gate with a loopback
|
|
145
|
+
exemption. This is byte-identical to the pre-auth cockpit — zero config.
|
|
146
|
+
- **`hosted` — multi-tenant.** A login is required: the tailnet carve-out is gone,
|
|
147
|
+
every route except `/healthz` + `/login` needs a session cookie, and each account
|
|
148
|
+
is scoped to its **own** `~/.relay-<tenant>` home (separate threads/data/state),
|
|
149
|
+
so two logins see two isolated fleets with no cross-tenant read. Auth is
|
|
150
|
+
Python-native and stdlib-only (`yothere.voicecall.auth`): scrypt password hashing,
|
|
151
|
+
an opaque session token in SQLite (`RELAY_AUTH_DB`, WAL, separate from fleet
|
|
152
|
+
state), login lockout, and a `Secure` cookie tied to the mode (not the request
|
|
153
|
+
URL, so a TLS-terminating proxy can't drop it). Tenant homes live under
|
|
154
|
+
`RELAY_TENANTS_ROOT` (default: beside `~/.relay`).
|
|
155
|
+
|
|
156
|
+
Hosted env: `RELAY_AUTH_MODE=hosted`, `RELAY_AUTH_DB=<path>`,
|
|
157
|
+
`RELAY_TENANTS_ROOT=<dir>`. Seed users via `yothere.voicecall.auth.AuthStore.create_user`
|
|
158
|
+
(public signup + hosted deployment are the next increment). The headless runner is
|
|
159
|
+
still single-tenant in this increment: a non-default tenant's threads are created and
|
|
160
|
+
isolated, but advancing them needs per-tenant runner orchestration (next increment).
|
|
161
|
+
|
|
162
|
+
## MCP surface
|
|
163
|
+
|
|
164
|
+
Drive a yothere fleet from any MCP client (Claude Desktop, etc.) — an orthogonal channel
|
|
165
|
+
that exposes the same front doors as the CLI and cockpit over stdio MCP:
|
|
166
|
+
|
|
167
|
+
- **`spawn_thread(task, mode, focus)`** — spawn fleet thread(s) from a natural-language
|
|
168
|
+
task (read/draft-only, blocks for your approval before any outward action).
|
|
169
|
+
- **`board()`** — the fleet at a glance, needs-eyes first.
|
|
170
|
+
- **`reply(thread_id, text)`** — approve / edit / reject a blocked thread.
|
|
171
|
+
|
|
172
|
+
Single-user / local (drives this machine's `~/.relay` fleet). `mcp` is an optional
|
|
173
|
+
extra; install + register:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
pip install 'yothere[mcp]'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```jsonc
|
|
180
|
+
// Claude Desktop: claude_desktop_config.json
|
|
181
|
+
{ "mcpServers": { "yothere": { "command": "yothere-mcp" } } }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The tool logic lives in SDK-free `_*_impl` helpers (`yothere.mcp_server`), so it imports
|
|
185
|
+
and tests without the `mcp` dependency installed.
|
|
186
|
+
|
|
187
|
+
## Status
|
|
188
|
+
|
|
189
|
+
v1.0.0. Fleet runner, board, voice surface, and the remote-brain path are tested
|
|
190
|
+
(`python tests/relay_test.py`). The hosted web cockpit and brain-side token
|
|
191
|
+
lifecycle are out of v1 (see [`CHANGELOG.md`](CHANGELOG.md)).
|
|
192
|
+
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
Two people build yothere and neither should break `main` alone. Every change goes
|
|
196
|
+
through a PR that the other founder approves before it merges — see
|
|
197
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
Five minutes from install to a thread advancing against a brain.
|
|
4
|
+
|
|
5
|
+
## 1. Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install yothere
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 90-second start (no API key)
|
|
12
|
+
|
|
13
|
+
`yothere init` sets up `~/.yothere` (override with `YOTHERE_HOME`/`RELAY_HOME`;
|
|
14
|
+
an existing `~/.relay` is reused), detects any brain
|
|
15
|
+
you already have (a remote `RELAY_REMOTE_BRAIN_URL`, a `brains.yaml`, or the
|
|
16
|
+
`claude` CLI), and wires the bundled zero-key demo brain if you have none.
|
|
17
|
+
It is idempotent and never overwrites existing config.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
yothere init # one-time setup + a status block with your next commands
|
|
21
|
+
yothere init --demo # watch a live fleet end to end (~30s, zero keys)
|
|
22
|
+
yothere board --open # the board it just filled
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`--demo` starts a bundled Brain Protocol v1 brain on localhost, spawns one
|
|
26
|
+
thread ("Demo: competitive scan"), and advances it through the real
|
|
27
|
+
runner -> worker -> brain path: you'll see it stream progress, go **blocked**
|
|
28
|
+
asking for your eyes (the attention router at work), get replied to the way
|
|
29
|
+
you would (`yothere reply <id> "ship it"`), and finish **done** with a result
|
|
30
|
+
summary on the board. Everything stays under `RELAY_HOME`; Ctrl-C is safe.
|
|
31
|
+
|
|
32
|
+
## 2. Run against the bundled reference brain
|
|
33
|
+
|
|
34
|
+
The `echo_brain` is the smallest valid Brain-Protocol implementation (~60 lines)
|
|
35
|
+
and the conformance fixture. Use it to prove the loop before wiring a real brain.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# terminal 1 — the reference brain
|
|
39
|
+
python -m yothere.voicecall.echo_brain --port 9999
|
|
40
|
+
|
|
41
|
+
# terminal 2 — point yothere at it and drive a thread
|
|
42
|
+
export RELAY_REMOTE_BRAIN_URL=ws://127.0.0.1:9999
|
|
43
|
+
export RELAY_THREAD_HARNESS=remote
|
|
44
|
+
yothere spawn "summarize the state of agentic commerce"
|
|
45
|
+
python -m yothere.runner once # advance one tick
|
|
46
|
+
yothere board --open # glance at the fleet
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 3. Point at a real brain
|
|
50
|
+
|
|
51
|
+
Either set `RELAY_REMOTE_BRAIN_URL` (+ `RELAY_REMOTE_BRAIN_TOKEN`) to your
|
|
52
|
+
`wss://` endpoint, or register named brains in `~/.relay/brains.yaml` and select
|
|
53
|
+
one with `RELAY_BRAIN` (see [docs/configuration.md](docs/configuration.md)).
|
|
54
|
+
|
|
55
|
+
To run threads with a local Claude Code instead of a remote brain:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
export RELAY_THREAD_HARNESS=claude # needs the `claude` CLI on PATH
|
|
59
|
+
yothere spawn "draft the Q3 update"
|
|
60
|
+
python -m yothere.runner loop # always-on advance engine
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 4. Implement your own brain
|
|
64
|
+
|
|
65
|
+
Implement [docs/brain-protocol-v1.md](docs/brain-protocol-v1.md) (`hello`,
|
|
66
|
+
`streamSubscribe`, `prompt`, streaming `delta` frames). Add `progress` / `status`
|
|
67
|
+
/ `cost` to light up the fleet cockpit fully, and `sessionStatus` so a stateless
|
|
68
|
+
client can render the board over the network. Read `echo_brain.py` for the shape.
|
|
69
|
+
|
|
70
|
+
## Everyday commands
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
yothere spawn "<task>" # create a thread (or `yothere preset <key> <arg>`)
|
|
74
|
+
yothere list # live threads + state
|
|
75
|
+
yothere show <id> # status, plan, transcript
|
|
76
|
+
yothere reply <id> "<text>" # answer a blocked thread -> it re-advances
|
|
77
|
+
yothere focus <id> # pin the focus thread (never auto-interrupted)
|
|
78
|
+
yothere board --open # the glance board
|
|
79
|
+
yothere note "<text>" # capture a reminder (no thread)
|
|
80
|
+
```
|