tether-memory 0.1.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.
@@ -0,0 +1,30 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ name: ${{ matrix.os }} / py${{ matrix.python }}
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest]
16
+ python: ["3.10", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python }}
23
+
24
+ - name: Install package + dev deps
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]"
28
+
29
+ - name: Run tests
30
+ run: pytest -v
@@ -0,0 +1,29 @@
1
+ name: Release
2
+
3
+ # Publish to PyPI when a version tag is pushed (e.g. v0.1.0).
4
+ # Uses PyPI Trusted Publishing (OIDC) — no API tokens stored anywhere.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ jobs:
10
+ pypi-publish:
11
+ runs-on: ubuntu-latest
12
+ environment: pypi # configure this name in the PyPI trusted publisher
13
+ permissions:
14
+ contents: read # required for actions/checkout (private repo)
15
+ id-token: write # required for OIDC trusted publishing
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+
23
+ - name: Build sdist + wheel
24
+ run: |
25
+ python -m pip install --upgrade build
26
+ python -m build
27
+
28
+ - name: Publish to PyPI
29
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,18 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ venv/
9
+
10
+ # tether local data / secrets
11
+ *.db
12
+ *.db-*
13
+ .env
14
+
15
+ # OS / editor
16
+ .DS_Store
17
+ .idea/
18
+ .vscode/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 sidyellur
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.
@@ -0,0 +1,25 @@
1
+ # tether v0.1 — Blog Journal
2
+
3
+ ## The core insight: memory as a shared substrate
4
+
5
+ The problem tether solves: personal agents will live across devices (laptop, desktop, phone). For that to feel like *one* assistant, not several amnesiac ones, memory has to be a substrate that follows you — readable and writable from every device and any agent, not siloed in a single tool. This isn't about perfect sync or guarantees; it's about being a *convenience layer* that helps when present and never breaks the agent when degraded.
6
+
7
+ ## Design choice: local-first + opt-in sync
8
+
9
+ We chose to ship a local SQLite file as the default — zero config, no accounts, no network. The alternative (Turso/libSQL primary with local replicas) only works if sync is reliable. By making it opt-in, we sidestep the vendor risk and deployment complexity. Agents that don't need cross-device memory use it as-is. Agents that do can point at a Turso primary and get an embedded replica. The same code path handles both.
10
+
11
+ ## Real-world spike: .sync() doesn't fail fast
12
+
13
+ During Task 7, we discovered libsql-experimental's `.sync()` doesn't fail fast on unreachable backends — it retries the handshake internally (observed every ~2-3s) without returning control. The plan assumed we could call `conn.sync()` inline as an initial connectivity probe. We corrected this by bounding the initial sync in a background thread + timeout, the same pattern we already use for later syncs. This change is subtle but critical: it keeps server startup from hanging indefinitely on a dead network.
14
+
15
+ ## The upsert-on-write design
16
+
17
+ The store deduplicates on write, not on read. A memory with the same `type` + normalized `title` updates in place. This was a deliberate choice: agents shouldn't need to know "is this fact new or existing?" before calling remember. Re-remembering a fact refines it. The cost is a dedup index on (type, title_norm), but the benefit is that the store doesn't rot into a heap of near-duplicates.
18
+
19
+ ## Four verbs, nothing more
20
+
21
+ We resisted the urge to add `list`, `get`, or raw SQL. The four verbs (remember, recall, link, forget) are the minimum surface needed for an agent to use memory. A fifth verb (the boot-index resource) is auto-loaded each session, so agents don't need to think about it. This minimalism makes the tool usable across contexts and easy to reason about.
22
+
23
+ ## Deferred, not dropped
24
+
25
+ The spec laid out three things we didn't build: semantic search (embeddings), entity/edge graph model, and automatic corrupt-DB recovery. We designed the schema so all three slot in without data migration. The important part: we called out what's deferred in the docs, not silently dropped it. v0.1 works; v0.2's roadmap is visible.
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: tether-memory
3
+ Version: 0.1.0
4
+ Summary: A shared memory layer for personal agents, across devices: an MCP server over local SQLite with opt-in libSQL/Turso sync.
5
+ Project-URL: Homepage, https://github.com/sidyellur/tether
6
+ Project-URL: Issues, https://github.com/sidyellur/tether/issues
7
+ Author-email: sidyellur <20009719+sidyellur@users.noreply.github.com>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 sidyellur
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: agent,claude,claude-code,libsql,mcp,memory,sqlite,turso
31
+ Classifier: Development Status :: 3 - Alpha
32
+ Classifier: Intended Audience :: Developers
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: POSIX
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Topic :: Database
37
+ Requires-Python: >=3.10
38
+ Requires-Dist: mcp>=1.0
39
+ Provides-Extra: dev
40
+ Requires-Dist: build; extra == 'dev'
41
+ Requires-Dist: pytest>=7; extra == 'dev'
42
+ Provides-Extra: sync
43
+ Requires-Dist: libsql-experimental>=0.0.30; extra == 'sync'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # tether
47
+
48
+ **A shared memory layer for personal agents, across devices.** `tether` is an
49
+ [MCP](https://modelcontextprotocol.io) server backed by a local SQLite file. Any
50
+ MCP-compatible agent can `remember`, `recall`, `link`, and `forget` durable notes
51
+ — facts about you, your projects, your preferences — so context follows you
52
+ instead of dying with each session.
53
+
54
+ It runs **local-only with zero configuration**. Point it at a hosted
55
+ [libSQL/Turso](https://turso.tech) primary and the same file becomes an embedded
56
+ replica that syncs your memory across every device in near-real-time.
57
+
58
+ ## Why
59
+
60
+ The near future is personal agents living across many devices — laptop, desktop,
61
+ phone. For that to feel like *one* assistant rather than several amnesiac ones,
62
+ memory has to be a substrate that follows you: readable and writable from every
63
+ device and from any agent, not siloed inside a single tool.
64
+
65
+ `tether` is that substrate. It is deliberately a *convenience layer* — it makes an
66
+ agent more useful when present, and never breaks the agent's work when degraded.
67
+
68
+ ## Status
69
+
70
+ v0.1 is implemented. Design and rationale: [`docs/superpowers/specs/2026-07-03-tether-design.md`](docs/superpowers/specs/2026-07-03-tether-design.md). Implementation plan: [`docs/superpowers/plans/2026-07-03-tether-v0.1-implementation.md`](docs/superpowers/plans/2026-07-03-tether-v0.1-implementation.md).
71
+
72
+ ## Design at a glance
73
+
74
+ - **Four verbs**, nothing more: `remember` · `recall` · `link` · `forget`.
75
+ - **Upsert on write** so the store doesn't rot into near-duplicates.
76
+ - **Rich recall** (id, type, title, body, tags, `updated_at`) so an agent can
77
+ judge staleness and cite what it updates.
78
+ - **An auto-loaded boot index** — a compact one-line-per-memory list surfaced to
79
+ the agent each session, so memory helps even when the agent doesn't think to
80
+ search.
81
+ - **Local-first, sync optional** — the local path is untouched when no backend is
82
+ configured; degradation never throws.
83
+ - **Keyword search now, embeddings later** — the SQLite schema is built so
84
+ semantic search and a full entity/edge graph slot in without migrating data.
85
+
86
+ ## Install
87
+
88
+ Requires Python ≥3.10 on a POSIX system (Linux/macOS).
89
+
90
+ Register it with Claude Code — with [uv](https://docs.astral.sh/uv/):
91
+
92
+ ```sh
93
+ claude mcp add tether -- uvx --from tether-memory tether
94
+ ```
95
+
96
+ …or install it first:
97
+
98
+ ```sh
99
+ pip install tether-memory
100
+ claude mcp add tether -- tether
101
+ ```
102
+
103
+ (The PyPI package is named `tether-memory` — `tether` was already reserved on
104
+ PyPI as a common brand name — but the installed command is still `tether`.)
105
+
106
+ By default memory lives in a local SQLite file at
107
+ `~/.local/share/tether/memory.db` (override with `TETHER_DB`). No accounts, no
108
+ network — this is the whole tool for a single machine.
109
+
110
+ ## Sync across devices (optional)
111
+
112
+ Point tether at a [Turso](https://turso.tech) / libSQL database and the local
113
+ file becomes an embedded replica — local-speed reads, writes that propagate to
114
+ your other devices. Install the extra and set two env vars:
115
+
116
+ ```sh
117
+ pip install 'tether-memory[sync]'
118
+ export TETHER_SYNC_URL='libsql://<your-db>.turso.io'
119
+ export TETHER_SYNC_TOKEN='<your-auth-token>'
120
+ ```
121
+
122
+ If the backend is unreachable, tether logs `sync offline` and keeps working
123
+ against the local file; writes converge when it comes back.
124
+
125
+ ## Tools
126
+
127
+ | Tool | What it does |
128
+ |---|---|
129
+ | `remember(type, title, body, tags?, links?)` | Save a memory; upserts on `type`+`title` so facts refine rather than duplicate |
130
+ | `recall(query, type?, limit?)` | Keyword search; returns id/type/title/body/tags/updated_at |
131
+ | `link(id_a, id_b)` | Bidirectional link between two memories |
132
+ | `forget(id)` | Delete a memory |
133
+
134
+ Plus an auto-loaded resource `tether://memory-index` — a compact one-line-per-memory index surfaced each session.
135
+
136
+ ## License
137
+
138
+ MIT
@@ -0,0 +1,93 @@
1
+ # tether
2
+
3
+ **A shared memory layer for personal agents, across devices.** `tether` is an
4
+ [MCP](https://modelcontextprotocol.io) server backed by a local SQLite file. Any
5
+ MCP-compatible agent can `remember`, `recall`, `link`, and `forget` durable notes
6
+ — facts about you, your projects, your preferences — so context follows you
7
+ instead of dying with each session.
8
+
9
+ It runs **local-only with zero configuration**. Point it at a hosted
10
+ [libSQL/Turso](https://turso.tech) primary and the same file becomes an embedded
11
+ replica that syncs your memory across every device in near-real-time.
12
+
13
+ ## Why
14
+
15
+ The near future is personal agents living across many devices — laptop, desktop,
16
+ phone. For that to feel like *one* assistant rather than several amnesiac ones,
17
+ memory has to be a substrate that follows you: readable and writable from every
18
+ device and from any agent, not siloed inside a single tool.
19
+
20
+ `tether` is that substrate. It is deliberately a *convenience layer* — it makes an
21
+ agent more useful when present, and never breaks the agent's work when degraded.
22
+
23
+ ## Status
24
+
25
+ v0.1 is implemented. Design and rationale: [`docs/superpowers/specs/2026-07-03-tether-design.md`](docs/superpowers/specs/2026-07-03-tether-design.md). Implementation plan: [`docs/superpowers/plans/2026-07-03-tether-v0.1-implementation.md`](docs/superpowers/plans/2026-07-03-tether-v0.1-implementation.md).
26
+
27
+ ## Design at a glance
28
+
29
+ - **Four verbs**, nothing more: `remember` · `recall` · `link` · `forget`.
30
+ - **Upsert on write** so the store doesn't rot into near-duplicates.
31
+ - **Rich recall** (id, type, title, body, tags, `updated_at`) so an agent can
32
+ judge staleness and cite what it updates.
33
+ - **An auto-loaded boot index** — a compact one-line-per-memory list surfaced to
34
+ the agent each session, so memory helps even when the agent doesn't think to
35
+ search.
36
+ - **Local-first, sync optional** — the local path is untouched when no backend is
37
+ configured; degradation never throws.
38
+ - **Keyword search now, embeddings later** — the SQLite schema is built so
39
+ semantic search and a full entity/edge graph slot in without migrating data.
40
+
41
+ ## Install
42
+
43
+ Requires Python ≥3.10 on a POSIX system (Linux/macOS).
44
+
45
+ Register it with Claude Code — with [uv](https://docs.astral.sh/uv/):
46
+
47
+ ```sh
48
+ claude mcp add tether -- uvx --from tether-memory tether
49
+ ```
50
+
51
+ …or install it first:
52
+
53
+ ```sh
54
+ pip install tether-memory
55
+ claude mcp add tether -- tether
56
+ ```
57
+
58
+ (The PyPI package is named `tether-memory` — `tether` was already reserved on
59
+ PyPI as a common brand name — but the installed command is still `tether`.)
60
+
61
+ By default memory lives in a local SQLite file at
62
+ `~/.local/share/tether/memory.db` (override with `TETHER_DB`). No accounts, no
63
+ network — this is the whole tool for a single machine.
64
+
65
+ ## Sync across devices (optional)
66
+
67
+ Point tether at a [Turso](https://turso.tech) / libSQL database and the local
68
+ file becomes an embedded replica — local-speed reads, writes that propagate to
69
+ your other devices. Install the extra and set two env vars:
70
+
71
+ ```sh
72
+ pip install 'tether-memory[sync]'
73
+ export TETHER_SYNC_URL='libsql://<your-db>.turso.io'
74
+ export TETHER_SYNC_TOKEN='<your-auth-token>'
75
+ ```
76
+
77
+ If the backend is unreachable, tether logs `sync offline` and keeps working
78
+ against the local file; writes converge when it comes back.
79
+
80
+ ## Tools
81
+
82
+ | Tool | What it does |
83
+ |---|---|
84
+ | `remember(type, title, body, tags?, links?)` | Save a memory; upserts on `type`+`title` so facts refine rather than duplicate |
85
+ | `recall(query, type?, limit?)` | Keyword search; returns id/type/title/body/tags/updated_at |
86
+ | `link(id_a, id_b)` | Bidirectional link between two memories |
87
+ | `forget(id)` | Delete a memory |
88
+
89
+ Plus an auto-loaded resource `tether://memory-index` — a compact one-line-per-memory index surfaced each session.
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,26 @@
1
+ # tether — task tracker
2
+
3
+ Checklist view of the v0.1 build. Full step-by-step detail (code, tests,
4
+ commands) lives in
5
+ [`docs/superpowers/plans/2026-07-03-tether-v0.1-implementation.md`](docs/superpowers/plans/2026-07-03-tether-v0.1-implementation.md).
6
+ Design rationale is in
7
+ [`docs/superpowers/specs/2026-07-03-tether-design.md`](docs/superpowers/specs/2026-07-03-tether-design.md).
8
+
9
+ ## v0.1 — local-first memory MCP server + opt-in sync
10
+
11
+ - [ ] **Task 1 — Scaffolding.** `pyproject.toml`, `src/tether/__init__.py`, `tests/__init__.py`. Editable install + pytest run green.
12
+ - [ ] **Task 2 — `config.py`.** DB path, sync credentials, device id resolved from env. Zero-config default = local file, no sync.
13
+ - [ ] **Task 3 — `store.py` schema.** `memories` table + external-content FTS5 index + sync triggers; idempotent `migrate()`.
14
+ - [ ] **Task 4 — `remember()`.** Upsert on `type` + normalized `title` so facts refine instead of duplicating.
15
+ - [ ] **Task 5 — `recall()`.** FTS5 keyword search, type filter, rich returns (id/type/title/body/tags/updated_at); punctuation-safe.
16
+ - [ ] **Task 6 — `link()` / `forget()` / `boot_index()`.** Bidirectional links, hard delete, compact newest-first index.
17
+ - [ ] **Task 7 — `sync.py`.** Connection factory: local `sqlite3` or libSQL embedded replica; degrade-to-local on any failure. **Spike the `libsql-experimental` client first.**
18
+ - [ ] **Task 8 — `server.py`.** `FastMCP` — four verbs + `tether://memory-index` resource + `main()`. Tools return `{"error": ...}` rather than crash.
19
+ - [ ] **Task 9 — Docs + self-test.** README install/sync/tools; `scripts/selftest.py` end-to-end smoke.
20
+
21
+ ## Deferred (not in v0.1 — designed for, not built)
22
+
23
+ - [ ] Semantic/embedding search (`embedding BLOB` + `sqlite-vec`, backfill; no data migration).
24
+ - [ ] Entity/edge graph model layered alongside `memories`.
25
+ - [ ] Automatic corrupt-DB move-aside-and-recreate recovery (v0.1 degrades to a per-call error instead).
26
+ - [ ] True bounded/backgrounded sync tick beyond the best-effort `sync_now`.
@@ -0,0 +1,92 @@
1
+ # Tether v0.1: A Shared Memory Layer for Personal Agents
2
+
3
+ We shipped [tether](https://github.com/sidyellur/tether) yesterday — an MCP server that turns a local SQLite file into a durable memory substrate for personal agents across devices. If you're building agents that span a laptop, desktop, and phone, this is the post for you.
4
+
5
+ ## The problem is amnesia at scale
6
+
7
+ Picture this: you're using Claude on your laptop, and it learns something important about your project — a decision you made, a constraint you mentioned, a preference you established. You close the tab. Three hours later, on your phone, you ask the same agent a question. It has no idea what you told it before. So you explain again. And again. Each device is a fresh start, and you're burning tokens re-establishing context that already exists.
8
+
9
+ This matters because personal agents are *mobile* by definition. If an agent is useful, you'll want to talk to it from wherever you are. But context doesn't follow you unless memory does — and not the kind locked in vector stores or fine-tuning checkpoints. You need a *durable, writable, cross-device memory substrate* that any agent can read and update from any device, any time.
10
+
11
+ That's what tether solves.
12
+
13
+ ## The core insight: memory as shared ground
14
+
15
+ The key design decision isn't technical — it's architectural. Instead of building a memory *system* (embeddings, entity graphs, reasoning layers), tether is a *substrate*. Think of it like a filesystem: not everyone needs to understand ext4 to use a laptop, but a common storage layer makes it possible for different tools to share files.
16
+
17
+ In our case, different agents on different devices share the same SQLite database. Your Claude session on the laptop can call `remember()` to save a fact. Your Claude session on the phone calls `recall()` to find it. A third-party agent in the middle can link memories together or refine them. None of them need to coordinate — they're all reading and writing the same shared ground.
18
+
19
+ This isn't about perfect consistency or strong guarantees. It's about being a *convenience layer* that helps when present and doesn't break the agent when degraded. An agent using tether should never feel worse off for having it than without it.
20
+
21
+ ## Design choice: local-first, sync optional
22
+
23
+ Here's where we got the most pushback during design: why local-first at all? Why not just build against a cloud primary from day one?
24
+
25
+ The answer is deployment friction. A truly cross-device sync system requires a reliable backend, proper authentication, and careful handling of offline states. That's a lot of operational complexity for a v0.1. More importantly, many agents don't *need* cross-device sync — they're happy running on a single machine. Forcing them through that complexity would be a mistake.
26
+
27
+ So we inverted it: local SQLite is the default. Zero config. No accounts. No network. You install tether, point Claude Code at it, and memory just works. An agent that wants to stay local never thinks about sync.
28
+
29
+ For agents that do need to follow you across devices, you point tether at a Turso/libSQL primary and it becomes an embedded replica. The exact same code path handles both cases — the difference is a single environment variable. Write a memory on your laptop, and it propagates to your phone within seconds. Go offline, and the agent keeps working against the replica; changes converge when you reconnect.
30
+
31
+ This design sidesteps vendor risk (you're not forced into a SaaS dependency) and keeps the surface simple. It also means we can iterate on sync without breaking single-machine deployments.
32
+
33
+ ## The hidden cost of `.sync()`: a real-world lesson
34
+
35
+ During implementation, we hit a snag that wouldn't have shown up in unit tests or design docs.
36
+
37
+ The plan was straightforward: when tether starts up and a backend is configured, call `conn.sync()` on libsql-experimental to probe connectivity. If the handshake fails, log it and move on. This was meant to be cheap and fast — a way to know whether the server is online before we commit to any behavior.
38
+
39
+ What we actually found: `conn.sync()` doesn't fail fast. When the backend is unreachable, libSQL retries the handshake internally and retries again every ~2-3 seconds. This is good for resilience in the common case, but it means calling `sync()` inline will hang your startup if the backend is down.
40
+
41
+ So we redesigned: the initial sync now runs in a background thread with a timeout. It's the same pattern we already use for periodic syncs later in the server lifecycle, just moved to startup. The change is subtle — three lines of code — but critical. Without it, tether's startup blocks indefinitely on a dead network, which defeats the purpose of having local-first defaults.
42
+
43
+ This is the kind of thing that unit tests don't catch because the tests run on localhost. It's worth noting because it illustrates a principle: *be careful about libraries that abstract away I/O and timeouts*. They're usually trying to be helpful, but they can surprise you when your assumptions about control flow don't match the implementation.
44
+
45
+ ## Four verbs: deliberate minimalism
46
+
47
+ The tether API is four functions.
48
+
49
+ - `remember(type, title, body, tags?, links?)` — save a memory, upserting on type+title
50
+ - `recall(query, type?, limit?)` — keyword search
51
+ - `link(id_a, id_b)` — bidirectional link
52
+ - `forget(id)` — delete a memory
53
+
54
+ That's it. No `get()`, no `list()`, no raw SQL. No builder patterns or state machines. Just four verbs that do one thing each.
55
+
56
+ This restraint is intentional. We resisted adding a read-first `get()` method because agents shouldn't need to know "is this fact new or existing?" before calling remember. In tether, re-remembering a fact refines it in place. We deduplicate on write, not on read — the store is indexed on (type, title_norm), and a new fact with the same normalized title updates the existing row.
57
+
58
+ The upsert-on-write model is more powerful than it looks. It means agents can be optimistic: just remember the fact, and the system handles deduplication. Over time, a given memory might be updated a hundred times as an agent learns more about it, but the store stays clean instead of rotting into a heap of near-duplicates.
59
+
60
+ We also ship an auto-loaded resource, `tether://memory-index`, that surfaces a compact one-line-per-memory index at the start of each session. Agents don't need to think about searching — the index is already there, ready to jog their memory or seed a conversation.
61
+
62
+ ## What we deferred (and why we did it right)
63
+
64
+ The design spec called out three things we didn't build for v0.1:
65
+
66
+ - Semantic search (embeddings)
67
+ - Entity/edge graph model (for richer relationships)
68
+ - Automatic corrupt-database recovery
69
+
70
+ All three are useful, and all three would have added weeks to the timeline. So we made a deliberate choice: design the schema so they slot in without data migration. The `memories` table has room for embedding vectors. The schema supports rich relationships even if we're only using simple bidirectional links right now. The durability story is clear about what we don't handle.
71
+
72
+ The important part: we called this out explicitly in the docs, not silently dropped it. v0.1 works and is useful today. v0.2's roadmap is visible. Anyone deploying tether knows what they're getting and what's coming.
73
+
74
+ ## Why this matters for multi-agent systems
75
+
76
+ If you're building a system where multiple agents (or multiple instantiations of the same agent) need to collaborate on behalf of the same person, tether gives you a common language. One agent can `remember()` a decision. Another can `recall()` it and build on it. A third can `link()` it to a related fact. None of them need to implement their own memory system — they just read and write to shared ground.
77
+
78
+ This becomes crucial as AI tooling matures. Right now, most agents are single-threaded: you ask, it thinks, you get an answer. But future agents will be long-lived, collaborative, and distributed across devices. They'll need to accumulate knowledge over time, share context across sessions, and let you bounce between tools without losing continuity. That's only possible if memory is a substrate, not a silo.
79
+
80
+ tether is a small piece of that puzzle. It's deliberately boring — four verbs, SQLite, no magic. That boringness is the point. In a few years, when agents are everywhere, boring shared infrastructure will be more valuable than novel but isolated systems.
81
+
82
+ ## Try it
83
+
84
+ Install with Claude Code:
85
+
86
+ ```sh
87
+ claude mcp add tether -- uvx tether
88
+ ```
89
+
90
+ Local memory is free. If you want cross-device sync, set `TETHER_SYNC_URL` and `TETHER_SYNC_TOKEN` pointing to a Turso database and you're done.
91
+
92
+ Feedback and issues are welcome. We're building this in public, and v0.2 will be shaped by what agents actually need.