tether-ai 0.2.0__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.
- tether_ai-0.2.0/MANIFEST.in +3 -0
- tether_ai-0.2.0/PKG-INFO +302 -0
- tether_ai-0.2.0/README.md +246 -0
- tether_ai-0.2.0/pyproject.toml +114 -0
- tether_ai-0.2.0/setup.cfg +4 -0
- tether_ai-0.2.0/tests/test_api.py +592 -0
- tether_ai-0.2.0/tests/test_bridge_base.py +516 -0
- tether_ai-0.2.0/tests/test_claude_sdk_worker.py +376 -0
- tether_ai-0.2.0/tests/test_claude_subprocess.py +914 -0
- tether_ai-0.2.0/tests/test_cli.py +121 -0
- tether_ai-0.2.0/tests/test_codex_discovery.py +85 -0
- tether_ai-0.2.0/tests/test_config.py +157 -0
- tether_ai-0.2.0/tests/test_discord_bridge.py +561 -0
- tether_ai-0.2.0/tests/test_external_agent_api.py +434 -0
- tether_ai-0.2.0/tests/test_external_agent_integration.py +620 -0
- tether_ai-0.2.0/tests/test_external_sessions_api.py +21 -0
- tether_ai-0.2.0/tests/test_external_sessions_codex.py +157 -0
- tether_ai-0.2.0/tests/test_external_to_telegram.py +137 -0
- tether_ai-0.2.0/tests/test_formatting.py +197 -0
- tether_ai-0.2.0/tests/test_git.py +52 -0
- tether_ai-0.2.0/tests/test_init_wizard.py +58 -0
- tether_ai-0.2.0/tests/test_log_redaction.py +23 -0
- tether_ai-0.2.0/tests/test_mcp_server.py +110 -0
- tether_ai-0.2.0/tests/test_middleware.py +130 -0
- tether_ai-0.2.0/tests/test_multi_adapter_sessions.py +285 -0
- tether_ai-0.2.0/tests/test_pi_discovery.py +220 -0
- tether_ai-0.2.0/tests/test_pi_integration.py +68 -0
- tether_ai-0.2.0/tests/test_pi_runner.py +360 -0
- tether_ai-0.2.0/tests/test_runner_events.py +401 -0
- tether_ai-0.2.0/tests/test_runner_registry.py +164 -0
- tether_ai-0.2.0/tests/test_session_usage.py +112 -0
- tether_ai-0.2.0/tests/test_settings.py +184 -0
- tether_ai-0.2.0/tests/test_sidecar_unavailable_error.py +37 -0
- tether_ai-0.2.0/tests/test_slack_bridge.py +380 -0
- tether_ai-0.2.0/tests/test_slack_discord_list_search.py +46 -0
- tether_ai-0.2.0/tests/test_state.py +263 -0
- tether_ai-0.2.0/tests/test_status_api.py +134 -0
- tether_ai-0.2.0/tests/test_store.py +270 -0
- tether_ai-0.2.0/tests/test_subscriber.py +316 -0
- tether_ai-0.2.0/tests/test_telegram_bridge.py +207 -0
- tether_ai-0.2.0/tests/test_telegram_external_pagination.py +83 -0
- tether_ai-0.2.0/tether/__init__.py +5 -0
- tether_ai-0.2.0/tether/api/__init__.py +8 -0
- tether_ai-0.2.0/tether/api/debug.py +38 -0
- tether_ai-0.2.0/tether/api/deps.py +28 -0
- tether_ai-0.2.0/tether/api/diff.py +42 -0
- tether_ai-0.2.0/tether/api/directories.py +57 -0
- tether_ai-0.2.0/tether/api/emit.py +397 -0
- tether_ai-0.2.0/tether/api/errors.py +23 -0
- tether_ai-0.2.0/tether/api/events.py +26 -0
- tether_ai-0.2.0/tether/api/external_sessions.py +378 -0
- tether_ai-0.2.0/tether/api/health.py +15 -0
- tether_ai-0.2.0/tether/api/router.py +26 -0
- tether_ai-0.2.0/tether/api/runner_events.py +221 -0
- tether_ai-0.2.0/tether/api/runner_registry.py +83 -0
- tether_ai-0.2.0/tether/api/schemas.py +225 -0
- tether_ai-0.2.0/tether/api/sessions.py +729 -0
- tether_ai-0.2.0/tether/api/spa.py +29 -0
- tether_ai-0.2.0/tether/api/state.py +101 -0
- tether_ai-0.2.0/tether/api/status.py +134 -0
- tether_ai-0.2.0/tether/bridges/__init__.py +5 -0
- tether_ai-0.2.0/tether/bridges/base.py +19 -0
- tether_ai-0.2.0/tether/bridges/discord/__init__.py +1 -0
- tether_ai-0.2.0/tether/bridges/discord/bot.py +3 -0
- tether_ai-0.2.0/tether/bridges/discord/pairing_state.py +7 -0
- tether_ai-0.2.0/tether/bridges/glue.py +307 -0
- tether_ai-0.2.0/tether/bridges/manager.py +4 -0
- tether_ai-0.2.0/tether/bridges/slack/__init__.py +1 -0
- tether_ai-0.2.0/tether/bridges/slack/bot.py +3 -0
- tether_ai-0.2.0/tether/bridges/subscriber.py +41 -0
- tether_ai-0.2.0/tether/bridges/telegram/__init__.py +1 -0
- tether_ai-0.2.0/tether/bridges/telegram/bot.py +3 -0
- tether_ai-0.2.0/tether/bridges/telegram/formatting.py +10 -0
- tether_ai-0.2.0/tether/bridges/telegram/state.py +3 -0
- tether_ai-0.2.0/tether/bridges/thread_state.py +3 -0
- tether_ai-0.2.0/tether/cli.py +89 -0
- tether_ai-0.2.0/tether/config.py +105 -0
- tether_ai-0.2.0/tether/db/__init__.py +122 -0
- tether_ai-0.2.0/tether/diff.py +47 -0
- tether_ai-0.2.0/tether/discovery/__init__.py +29 -0
- tether_ai-0.2.0/tether/discovery/claude_code.py +8 -0
- tether_ai-0.2.0/tether/discovery/codex_sessions.py +6 -0
- tether_ai-0.2.0/tether/discovery/pi_sessions.py +9 -0
- tether_ai-0.2.0/tether/discovery/running.py +10 -0
- tether_ai-0.2.0/tether/git.py +25 -0
- tether_ai-0.2.0/tether/init_wizard.py +139 -0
- tether_ai-0.2.0/tether/log_config.py +123 -0
- tether_ai-0.2.0/tether/log_redaction.py +154 -0
- tether_ai-0.2.0/tether/main.py +184 -0
- tether_ai-0.2.0/tether/maintenance.py +76 -0
- tether_ai-0.2.0/tether/mcp_server/__init__.py +5 -0
- tether_ai-0.2.0/tether/mcp_server/server.py +129 -0
- tether_ai-0.2.0/tether/mcp_server/tools.py +219 -0
- tether_ai-0.2.0/tether/middleware.py +87 -0
- tether_ai-0.2.0/tether/models.py +109 -0
- tether_ai-0.2.0/tether/prompts.py +12 -0
- tether_ai-0.2.0/tether/py.typed +0 -0
- tether_ai-0.2.0/tether/runner/__init__.py +153 -0
- tether_ai-0.2.0/tether/runner/api_runner_base.py +253 -0
- tether_ai-0.2.0/tether/runner/base.py +7 -0
- tether_ai-0.2.0/tether/runner/claude_api.py +165 -0
- tether_ai-0.2.0/tether/runner/claude_sdk_worker.py +373 -0
- tether_ai-0.2.0/tether/runner/claude_subprocess.py +532 -0
- tether_ai-0.2.0/tether/runner/codex_sdk_sidecar.py +301 -0
- tether_ai-0.2.0/tether/runner/litellm_runner.py +290 -0
- tether_ai-0.2.0/tether/runner/pi_rpc.py +649 -0
- tether_ai-0.2.0/tether/settings.py +383 -0
- tether_ai-0.2.0/tether/sse.py +92 -0
- tether_ai-0.2.0/tether/startup.py +30 -0
- tether_ai-0.2.0/tether/static_ui/assets/index-D-8yNyFt.js +382 -0
- tether_ai-0.2.0/tether/static_ui/assets/index-DobFHZ7L.css +1 -0
- tether_ai-0.2.0/tether/static_ui/favicon.ico +0 -0
- tether_ai-0.2.0/tether/static_ui/index.html +14 -0
- tether_ai-0.2.0/tether/static_ui/logo.png +0 -0
- tether_ai-0.2.0/tether/static_ui/manifest.webmanifest +1 -0
- tether_ai-0.2.0/tether/static_ui/registerSW.js +1 -0
- tether_ai-0.2.0/tether/static_ui/sw.js +1 -0
- tether_ai-0.2.0/tether/static_ui/workbox-8c29f6e4.js +1 -0
- tether_ai-0.2.0/tether/store.py +910 -0
- tether_ai-0.2.0/tether/tools/__init__.py +6 -0
- tether_ai-0.2.0/tether/tools/definitions.py +82 -0
- tether_ai-0.2.0/tether/tools/executor.py +190 -0
- tether_ai-0.2.0/tether_ai.egg-info/PKG-INFO +302 -0
- tether_ai-0.2.0/tether_ai.egg-info/SOURCES.txt +126 -0
- tether_ai-0.2.0/tether_ai.egg-info/dependency_links.txt +1 -0
- tether_ai-0.2.0/tether_ai.egg-info/entry_points.txt +4 -0
- tether_ai-0.2.0/tether_ai.egg-info/requires.txt +34 -0
- tether_ai-0.2.0/tether_ai.egg-info/top_level.txt +1 -0
tether_ai-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tether-ai
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Control your AI coding agents from your phone when you're away from your desk
|
|
5
|
+
Author-email: Lars de Ridder <lars@xithing.io>
|
|
6
|
+
Maintainer-email: Lars de Ridder <lars@xithing.io>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
Project-URL: Homepage, https://gettether.dev
|
|
9
|
+
Project-URL: Documentation, https://github.com/larsderidder/tether#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/larsderidder/tether
|
|
11
|
+
Project-URL: Issues, https://github.com/larsderidder/tether/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/larsderidder/tether/releases
|
|
13
|
+
Keywords: ai,agent,claude,codex,remote,mobile,coding-assistant
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Web Environment
|
|
16
|
+
Classifier: Framework :: FastAPI
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Software Development
|
|
24
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
Requires-Dist: agent-tether[all]>=0.3.0
|
|
29
|
+
Requires-Dist: agent-sessions>=0.1.0
|
|
30
|
+
Requires-Dist: fastapi==0.110.0
|
|
31
|
+
Requires-Dist: uvicorn==0.27.1
|
|
32
|
+
Requires-Dist: pydantic==2.7.4
|
|
33
|
+
Requires-Dist: structlog==24.4.0
|
|
34
|
+
Requires-Dist: payload-redactor>=0.3.0
|
|
35
|
+
Requires-Dist: anthropic>=0.39.0
|
|
36
|
+
Requires-Dist: sqlmodel>=0.0.22
|
|
37
|
+
Requires-Dist: alembic>=1.13
|
|
38
|
+
Requires-Dist: claude-agent-sdk>=0.1.0
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: black==24.4.2; extra == "dev"
|
|
41
|
+
Requires-Dist: httpx==0.27.0; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest==8.2.2; extra == "dev"
|
|
43
|
+
Requires-Dist: pytest-cov==5.0.0; extra == "dev"
|
|
44
|
+
Requires-Dist: anyio[trio]>=4.0.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-anyio>=0.0.0; extra == "dev"
|
|
46
|
+
Provides-Extra: telegram
|
|
47
|
+
Requires-Dist: python-telegram-bot>=20.0; extra == "telegram"
|
|
48
|
+
Provides-Extra: slack
|
|
49
|
+
Requires-Dist: slack-sdk>=3.27.0; extra == "slack"
|
|
50
|
+
Provides-Extra: discord
|
|
51
|
+
Requires-Dist: discord.py>=2.3.0; extra == "discord"
|
|
52
|
+
Provides-Extra: litellm
|
|
53
|
+
Requires-Dist: litellm>=1.0; extra == "litellm"
|
|
54
|
+
Provides-Extra: mcp
|
|
55
|
+
Requires-Dist: mcp>=0.1.0; extra == "mcp"
|
|
56
|
+
|
|
57
|
+
# Tether Agent
|
|
58
|
+
|
|
59
|
+
Control your AI coding agents from your phone when you're away from your desk.
|
|
60
|
+
|
|
61
|
+
You start a coding agent, walk away for lunch, and come back to find it stuck waiting for input for an hour. Tether fixes that. Get notified when your agent needs you, respond from anywhere.
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
- **Local-first** — Runs on your machine, your data stays yours
|
|
66
|
+
- **Multi-agent** — Supports Claude and Codex, more to come
|
|
67
|
+
- **Web UI** — Monitor sessions from your phone or desktop
|
|
68
|
+
- **External Agent API** — WebSocket and REST API for connecting any AI agent
|
|
69
|
+
- **Messaging Platform Integrations** — Telegram, Slack, and Discord bridges
|
|
70
|
+
- **MCP Server** — Model Context Protocol server for Claude Desktop and other MCP clients
|
|
71
|
+
- **No API keys required** — Uses Claude / Codex local OAuth by default
|
|
72
|
+
|
|
73
|
+
## Installation
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install tether-ai
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Optional Platform Integrations
|
|
80
|
+
|
|
81
|
+
Install platform-specific dependencies as needed:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Telegram bridge
|
|
85
|
+
pip install tether-ai[telegram]
|
|
86
|
+
|
|
87
|
+
# Slack bridge
|
|
88
|
+
pip install tether-ai[slack]
|
|
89
|
+
|
|
90
|
+
# Discord bridge
|
|
91
|
+
pip install tether-ai[discord]
|
|
92
|
+
|
|
93
|
+
# All bridges
|
|
94
|
+
pip install tether-ai[telegram,slack,discord]
|
|
95
|
+
|
|
96
|
+
# Development tools
|
|
97
|
+
pip install tether-ai[dev]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Quick Start
|
|
101
|
+
|
|
102
|
+
### Run the Agent Server
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Start the agent server
|
|
106
|
+
tether-agent
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Then open http://localhost:8787 in your browser.
|
|
110
|
+
|
|
111
|
+
### Use with MCP (Claude Desktop)
|
|
112
|
+
|
|
113
|
+
Add Tether as an MCP server in your Claude Desktop config:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"mcpServers": {
|
|
118
|
+
"tether": {
|
|
119
|
+
"command": "python",
|
|
120
|
+
"args": ["-m", "tether.mcp_server.server"],
|
|
121
|
+
"env": {
|
|
122
|
+
"TETHER_API_URL": "http://localhost:8787"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Configuration
|
|
130
|
+
|
|
131
|
+
Set environment variables to configure:
|
|
132
|
+
|
|
133
|
+
| Variable | Description | Default |
|
|
134
|
+
|----------|-------------|---------|
|
|
135
|
+
| `TETHER_AGENT_HOST` | Host to bind to | `0.0.0.0` |
|
|
136
|
+
| `TETHER_AGENT_PORT` | Port to listen on | `8787` |
|
|
137
|
+
| `TETHER_AGENT_TOKEN` | Auth token (optional; if set, API/UI/MCP require bearer auth) | — |
|
|
138
|
+
| `TETHER_AGENT_DEV_MODE` | Enable dev mode (no token required) | `0` |
|
|
139
|
+
| `TETHER_AGENT_ADAPTER` | AI adapter to use | `claude_auto` |
|
|
140
|
+
| `TETHER_AGENT_DATA_DIR` | Data storage directory | `./data` |
|
|
141
|
+
|
|
142
|
+
### AI Adapters
|
|
143
|
+
|
|
144
|
+
| Adapter | Description |
|
|
145
|
+
|---------|-------------|
|
|
146
|
+
| `claude_auto` | Auto-detect (prefer OAuth, fallback to API key) |
|
|
147
|
+
| `claude_subprocess` | Claude via Agent SDK in subprocess (CLI OAuth) |
|
|
148
|
+
| `claude_api` | Claude via API key (set `ANTHROPIC_API_KEY`) |
|
|
149
|
+
| `codex_sdk_sidecar` | Codex via sidecar |
|
|
150
|
+
|
|
151
|
+
### Messaging Platform Bridges
|
|
152
|
+
|
|
153
|
+
Configure bridges to get notifications on your preferred platform:
|
|
154
|
+
|
|
155
|
+
#### Telegram
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
export TELEGRAM_BOT_TOKEN="your_bot_token"
|
|
159
|
+
export TELEGRAM_FORUM_GROUP_ID="your_group_id"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Slack
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
export SLACK_BOT_TOKEN="xoxb-your-token"
|
|
166
|
+
export SLACK_CHANNEL_ID="C01234567"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Discord
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
export DISCORD_BOT_TOKEN="your_bot_token"
|
|
173
|
+
export DISCORD_CHANNEL_ID="1234567890"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## External Agent API
|
|
177
|
+
|
|
178
|
+
Tether exposes a WebSocket and REST API for external agents to connect and interact with users through messaging platforms.
|
|
179
|
+
|
|
180
|
+
### REST API Endpoints
|
|
181
|
+
|
|
182
|
+
#### Create a Session
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
POST /external/sessions
|
|
186
|
+
Content-Type: application/json
|
|
187
|
+
|
|
188
|
+
{
|
|
189
|
+
"agent_metadata": {
|
|
190
|
+
"name": "My Custom Agent",
|
|
191
|
+
"type": "custom",
|
|
192
|
+
"icon": "🤖",
|
|
193
|
+
"workspace": "my-workspace"
|
|
194
|
+
},
|
|
195
|
+
"session_name": "Code Review Task",
|
|
196
|
+
"platform": "telegram"
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Response:
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"session_id": "sess_abc123",
|
|
204
|
+
"platform": "telegram",
|
|
205
|
+
"thread_info": {
|
|
206
|
+
"thread_id": "123456",
|
|
207
|
+
"platform": "telegram"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### Send Output
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
POST /external/sessions/{session_id}/output
|
|
216
|
+
Content-Type: application/json
|
|
217
|
+
|
|
218
|
+
{
|
|
219
|
+
"text": "Agent output text here",
|
|
220
|
+
"metadata": {}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Request Approval
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
POST /external/sessions/{session_id}/approval
|
|
228
|
+
Content-Type: application/json
|
|
229
|
+
|
|
230
|
+
{
|
|
231
|
+
"title": "Approve Changes?",
|
|
232
|
+
"description": "Ready to commit these changes",
|
|
233
|
+
"options": ["Approve", "Reject", "Review"]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Check for Input
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
GET /external/sessions/{session_id}/input?timeout=30
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Response:
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"type": "human_input",
|
|
247
|
+
"data": {
|
|
248
|
+
"text": "User's message",
|
|
249
|
+
"timestamp": "2025-01-01T12:00:00Z"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### WebSocket API
|
|
255
|
+
|
|
256
|
+
Connect to `/external/sessions/{session_id}/ws` for bidirectional communication:
|
|
257
|
+
|
|
258
|
+
**Agent → Tether events:**
|
|
259
|
+
- `output`: Send text output to user
|
|
260
|
+
- `approval_request`: Request user approval
|
|
261
|
+
- `status`: Update agent status (thinking, executing, done, error)
|
|
262
|
+
|
|
263
|
+
**Tether → Agent events:**
|
|
264
|
+
- `human_input`: User sent a message
|
|
265
|
+
- `approval_response`: User responded to approval request
|
|
266
|
+
|
|
267
|
+
Example WebSocket message:
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"type": "output",
|
|
271
|
+
"data": {
|
|
272
|
+
"text": "Processing your request...",
|
|
273
|
+
"metadata": {}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Development
|
|
279
|
+
|
|
280
|
+
### Run Tests
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
pytest tests/
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Database Migrations
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Create a new migration
|
|
290
|
+
alembic revision --autogenerate -m "description"
|
|
291
|
+
|
|
292
|
+
# Apply migrations
|
|
293
|
+
alembic upgrade head
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Documentation
|
|
297
|
+
|
|
298
|
+
For full documentation, see [github.com/larsderidder/tether](https://github.com/larsderidder/tether).
|
|
299
|
+
|
|
300
|
+
## License
|
|
301
|
+
|
|
302
|
+
Apache 2.0. See [LICENSE](https://github.com/larsderidder/tether/blob/main/LICENSE) for details.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Tether Agent
|
|
2
|
+
|
|
3
|
+
Control your AI coding agents from your phone when you're away from your desk.
|
|
4
|
+
|
|
5
|
+
You start a coding agent, walk away for lunch, and come back to find it stuck waiting for input for an hour. Tether fixes that. Get notified when your agent needs you, respond from anywhere.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Local-first** — Runs on your machine, your data stays yours
|
|
10
|
+
- **Multi-agent** — Supports Claude and Codex, more to come
|
|
11
|
+
- **Web UI** — Monitor sessions from your phone or desktop
|
|
12
|
+
- **External Agent API** — WebSocket and REST API for connecting any AI agent
|
|
13
|
+
- **Messaging Platform Integrations** — Telegram, Slack, and Discord bridges
|
|
14
|
+
- **MCP Server** — Model Context Protocol server for Claude Desktop and other MCP clients
|
|
15
|
+
- **No API keys required** — Uses Claude / Codex local OAuth by default
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install tether-ai
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Optional Platform Integrations
|
|
24
|
+
|
|
25
|
+
Install platform-specific dependencies as needed:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Telegram bridge
|
|
29
|
+
pip install tether-ai[telegram]
|
|
30
|
+
|
|
31
|
+
# Slack bridge
|
|
32
|
+
pip install tether-ai[slack]
|
|
33
|
+
|
|
34
|
+
# Discord bridge
|
|
35
|
+
pip install tether-ai[discord]
|
|
36
|
+
|
|
37
|
+
# All bridges
|
|
38
|
+
pip install tether-ai[telegram,slack,discord]
|
|
39
|
+
|
|
40
|
+
# Development tools
|
|
41
|
+
pip install tether-ai[dev]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
### Run the Agent Server
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Start the agent server
|
|
50
|
+
tether-agent
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Then open http://localhost:8787 in your browser.
|
|
54
|
+
|
|
55
|
+
### Use with MCP (Claude Desktop)
|
|
56
|
+
|
|
57
|
+
Add Tether as an MCP server in your Claude Desktop config:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"tether": {
|
|
63
|
+
"command": "python",
|
|
64
|
+
"args": ["-m", "tether.mcp_server.server"],
|
|
65
|
+
"env": {
|
|
66
|
+
"TETHER_API_URL": "http://localhost:8787"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Configuration
|
|
74
|
+
|
|
75
|
+
Set environment variables to configure:
|
|
76
|
+
|
|
77
|
+
| Variable | Description | Default |
|
|
78
|
+
|----------|-------------|---------|
|
|
79
|
+
| `TETHER_AGENT_HOST` | Host to bind to | `0.0.0.0` |
|
|
80
|
+
| `TETHER_AGENT_PORT` | Port to listen on | `8787` |
|
|
81
|
+
| `TETHER_AGENT_TOKEN` | Auth token (optional; if set, API/UI/MCP require bearer auth) | — |
|
|
82
|
+
| `TETHER_AGENT_DEV_MODE` | Enable dev mode (no token required) | `0` |
|
|
83
|
+
| `TETHER_AGENT_ADAPTER` | AI adapter to use | `claude_auto` |
|
|
84
|
+
| `TETHER_AGENT_DATA_DIR` | Data storage directory | `./data` |
|
|
85
|
+
|
|
86
|
+
### AI Adapters
|
|
87
|
+
|
|
88
|
+
| Adapter | Description |
|
|
89
|
+
|---------|-------------|
|
|
90
|
+
| `claude_auto` | Auto-detect (prefer OAuth, fallback to API key) |
|
|
91
|
+
| `claude_subprocess` | Claude via Agent SDK in subprocess (CLI OAuth) |
|
|
92
|
+
| `claude_api` | Claude via API key (set `ANTHROPIC_API_KEY`) |
|
|
93
|
+
| `codex_sdk_sidecar` | Codex via sidecar |
|
|
94
|
+
|
|
95
|
+
### Messaging Platform Bridges
|
|
96
|
+
|
|
97
|
+
Configure bridges to get notifications on your preferred platform:
|
|
98
|
+
|
|
99
|
+
#### Telegram
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
export TELEGRAM_BOT_TOKEN="your_bot_token"
|
|
103
|
+
export TELEGRAM_FORUM_GROUP_ID="your_group_id"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Slack
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
export SLACK_BOT_TOKEN="xoxb-your-token"
|
|
110
|
+
export SLACK_CHANNEL_ID="C01234567"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Discord
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
export DISCORD_BOT_TOKEN="your_bot_token"
|
|
117
|
+
export DISCORD_CHANNEL_ID="1234567890"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## External Agent API
|
|
121
|
+
|
|
122
|
+
Tether exposes a WebSocket and REST API for external agents to connect and interact with users through messaging platforms.
|
|
123
|
+
|
|
124
|
+
### REST API Endpoints
|
|
125
|
+
|
|
126
|
+
#### Create a Session
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
POST /external/sessions
|
|
130
|
+
Content-Type: application/json
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
"agent_metadata": {
|
|
134
|
+
"name": "My Custom Agent",
|
|
135
|
+
"type": "custom",
|
|
136
|
+
"icon": "🤖",
|
|
137
|
+
"workspace": "my-workspace"
|
|
138
|
+
},
|
|
139
|
+
"session_name": "Code Review Task",
|
|
140
|
+
"platform": "telegram"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Response:
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"session_id": "sess_abc123",
|
|
148
|
+
"platform": "telegram",
|
|
149
|
+
"thread_info": {
|
|
150
|
+
"thread_id": "123456",
|
|
151
|
+
"platform": "telegram"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Send Output
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
POST /external/sessions/{session_id}/output
|
|
160
|
+
Content-Type: application/json
|
|
161
|
+
|
|
162
|
+
{
|
|
163
|
+
"text": "Agent output text here",
|
|
164
|
+
"metadata": {}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Request Approval
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
POST /external/sessions/{session_id}/approval
|
|
172
|
+
Content-Type: application/json
|
|
173
|
+
|
|
174
|
+
{
|
|
175
|
+
"title": "Approve Changes?",
|
|
176
|
+
"description": "Ready to commit these changes",
|
|
177
|
+
"options": ["Approve", "Reject", "Review"]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Check for Input
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
GET /external/sessions/{session_id}/input?timeout=30
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Response:
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"type": "human_input",
|
|
191
|
+
"data": {
|
|
192
|
+
"text": "User's message",
|
|
193
|
+
"timestamp": "2025-01-01T12:00:00Z"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### WebSocket API
|
|
199
|
+
|
|
200
|
+
Connect to `/external/sessions/{session_id}/ws` for bidirectional communication:
|
|
201
|
+
|
|
202
|
+
**Agent → Tether events:**
|
|
203
|
+
- `output`: Send text output to user
|
|
204
|
+
- `approval_request`: Request user approval
|
|
205
|
+
- `status`: Update agent status (thinking, executing, done, error)
|
|
206
|
+
|
|
207
|
+
**Tether → Agent events:**
|
|
208
|
+
- `human_input`: User sent a message
|
|
209
|
+
- `approval_response`: User responded to approval request
|
|
210
|
+
|
|
211
|
+
Example WebSocket message:
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"type": "output",
|
|
215
|
+
"data": {
|
|
216
|
+
"text": "Processing your request...",
|
|
217
|
+
"metadata": {}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Development
|
|
223
|
+
|
|
224
|
+
### Run Tests
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
pytest tests/
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Database Migrations
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Create a new migration
|
|
234
|
+
alembic revision --autogenerate -m "description"
|
|
235
|
+
|
|
236
|
+
# Apply migrations
|
|
237
|
+
alembic upgrade head
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Documentation
|
|
241
|
+
|
|
242
|
+
For full documentation, see [github.com/larsderidder/tether](https://github.com/larsderidder/tether).
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
Apache 2.0. See [LICENSE](https://github.com/larsderidder/tether/blob/main/LICENSE) for details.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tether-ai"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Control your AI coding agents from your phone when you're away from your desk"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Lars de Ridder", email = "lars@xithing.io" },
|
|
14
|
+
]
|
|
15
|
+
maintainers = [
|
|
16
|
+
{ name = "Lars de Ridder", email = "lars@xithing.io" },
|
|
17
|
+
]
|
|
18
|
+
keywords = [
|
|
19
|
+
"ai",
|
|
20
|
+
"agent",
|
|
21
|
+
"claude",
|
|
22
|
+
"codex",
|
|
23
|
+
"remote",
|
|
24
|
+
"mobile",
|
|
25
|
+
"coding-assistant",
|
|
26
|
+
]
|
|
27
|
+
classifiers = [
|
|
28
|
+
"Development Status :: 3 - Alpha",
|
|
29
|
+
"Environment :: Web Environment",
|
|
30
|
+
"Framework :: FastAPI",
|
|
31
|
+
"Intended Audience :: Developers",
|
|
32
|
+
"Operating System :: OS Independent",
|
|
33
|
+
"Programming Language :: Python :: 3",
|
|
34
|
+
"Programming Language :: Python :: 3.10",
|
|
35
|
+
"Programming Language :: Python :: 3.11",
|
|
36
|
+
"Programming Language :: Python :: 3.12",
|
|
37
|
+
"Topic :: Software Development",
|
|
38
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
39
|
+
"Typing :: Typed",
|
|
40
|
+
]
|
|
41
|
+
dependencies = [
|
|
42
|
+
"agent-tether[all]>=0.3.0",
|
|
43
|
+
"agent-sessions>=0.1.0",
|
|
44
|
+
"fastapi==0.110.0",
|
|
45
|
+
"uvicorn==0.27.1",
|
|
46
|
+
"pydantic==2.7.4",
|
|
47
|
+
"structlog==24.4.0",
|
|
48
|
+
"payload-redactor>=0.3.0",
|
|
49
|
+
"anthropic>=0.39.0",
|
|
50
|
+
"sqlmodel>=0.0.22",
|
|
51
|
+
"alembic>=1.13",
|
|
52
|
+
"claude-agent-sdk>=0.1.0",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.optional-dependencies]
|
|
56
|
+
dev = [
|
|
57
|
+
"black==24.4.2",
|
|
58
|
+
"httpx==0.27.0",
|
|
59
|
+
"pytest==8.2.2",
|
|
60
|
+
"pytest-cov==5.0.0",
|
|
61
|
+
"anyio[trio]>=4.0.0",
|
|
62
|
+
"pytest-anyio>=0.0.0",
|
|
63
|
+
]
|
|
64
|
+
telegram = [
|
|
65
|
+
"python-telegram-bot>=20.0",
|
|
66
|
+
]
|
|
67
|
+
slack = [
|
|
68
|
+
"slack-sdk>=3.27.0",
|
|
69
|
+
]
|
|
70
|
+
discord = [
|
|
71
|
+
"discord.py>=2.3.0",
|
|
72
|
+
]
|
|
73
|
+
litellm = [
|
|
74
|
+
"litellm>=1.0",
|
|
75
|
+
]
|
|
76
|
+
mcp = [
|
|
77
|
+
"mcp>=0.1.0",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
[project.urls]
|
|
81
|
+
Homepage = "https://gettether.dev"
|
|
82
|
+
Documentation = "https://github.com/larsderidder/tether#readme"
|
|
83
|
+
Repository = "https://github.com/larsderidder/tether"
|
|
84
|
+
Issues = "https://github.com/larsderidder/tether/issues"
|
|
85
|
+
Changelog = "https://github.com/larsderidder/tether/releases"
|
|
86
|
+
|
|
87
|
+
[project.scripts]
|
|
88
|
+
tether = "tether.cli:main"
|
|
89
|
+
tether-agent = "tether.cli:_start_compat"
|
|
90
|
+
tether-mcp = "tether.mcp_server.server:main"
|
|
91
|
+
|
|
92
|
+
[tool.setuptools.packages.find]
|
|
93
|
+
include = ["tether*"]
|
|
94
|
+
|
|
95
|
+
[tool.setuptools.package-data]
|
|
96
|
+
tether = ["static_ui/**/*", "py.typed"]
|
|
97
|
+
|
|
98
|
+
[tool.pytest.ini_options]
|
|
99
|
+
testpaths = ["tests"]
|
|
100
|
+
|
|
101
|
+
[tool.coverage.run]
|
|
102
|
+
source = ["tether"]
|
|
103
|
+
omit = ["*/tests/*"]
|
|
104
|
+
|
|
105
|
+
[tool.coverage.report]
|
|
106
|
+
exclude_lines = [
|
|
107
|
+
"pragma: no cover",
|
|
108
|
+
"if TYPE_CHECKING:",
|
|
109
|
+
"raise NotImplementedError",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
[tool.black]
|
|
113
|
+
line-length = 88
|
|
114
|
+
target-version = ["py310"]
|