tare-cli 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.
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ __pycache__/
3
+
4
+ # agent skills installed locally (readme-craft and friends), not part of tare
5
+ .agents/
6
+ .claude/
7
+
8
+ # build output
9
+ dist/
@@ -0,0 +1,308 @@
1
+ # tare: concept
2
+
3
+ tare starts a coding agent in an isolated room without your personal setup, and proves the room
4
+ clean before the run. It prints `tare: 0.00`, names every leak and where it came from, or says
5
+ that it could not see your setup (`tare: not proven (blind)`). It refuses to start the agent
6
+ until the reading is `tare: 0.00`, unless you pass `--allow-dirty`. It supports Claude Code, Codex, Pi
7
+ and the Antigravity CLI (`agy`) on Linux and WSL.
8
+
9
+ This document describes the design and the measurements behind it. The [README](README.md)
10
+ explains the words this document uses: room, probe, reading, leak, declared, dirty twin, check, tail and
11
+ handoff. Every claim marked **measured** was observed on WSL2. The section
12
+ [Measurements](#measurements) lists the agent versions and links each result.
13
+
14
+ ## Why
15
+
16
+ Skill and agent evaluations compare runs with a skill against runs without it. If the agent
17
+ brings your own instructions, memories, skills or connected accounts into both runs, the
18
+ comparison measures your machine, not the skill. Evaluation frameworks that drive several agent
19
+ CLIs exist (see [Prior art](#prior-art)), but they expect you to provide a clean environment.
20
+ None of them checks that the environment is clean.
21
+
22
+ ## Three layers
23
+
24
+ tare keeps your setup out in three layers. Each layer closes a gap that the one before leaves
25
+ open.
26
+
27
+ ### 1. Context: nothing of your setup reaches the model
28
+
29
+ A separate config directory (`CLAUDE_CONFIG_DIR`, `CODEX_HOME`) and a separate `HOME` remove
30
+ your instructions, memories, plugins, hooks and skills. That is not enough.
31
+
32
+ - **Measured:** the login brings context that lives in no config directory. With only a
33
+ separate config directory, a Claude Code session still had the connected accounts
34
+ (mail, calendar, documents) as live tools, the account's skills and the account email.
35
+ `--strict-mcp-config` and `--setting-sources project,local` removed the connected accounts and
36
+ the account skills. The email stays with a subscription login. An API key login without OAuth
37
+ should remove it, but that has not been tested.
38
+ - Codex: `CODEX_HOME` does not move `~/.agents/skills`, so Codex still reads your real skills
39
+ there. Account plugins can also bring personal skills back after the login. tare starts Codex with `--disable remote_plugin` and an empty
40
+ home directory.
41
+ - Claude Code's own bundled skills stay visible. They belong to the product, not to you.
42
+
43
+ ### 2. Containment: nothing of yours within reach
44
+
45
+ - **Measured:** with the context layer alone, any process in the session can still read your
46
+ real `~/.claude/CLAUDE.md`, `~/.claude/.credentials.json`, `~/.codex/auth.json` and the
47
+ project's `.env` files. An agent with shell access and skipped permission prompts is one `cat`
48
+ away.
49
+ - So tare runs the agent in a room built with bubblewrap (user namespaces, no root). The room
50
+ mounts an empty `/home`, then only the room's own home directory and the project. Your real
51
+ files do not exist inside. On WSL, `/etc/resolv.conf` points into `/mnt/wsl`, so the room
52
+ mounts `/mnt/wsl` read-only, or DNS fails.
53
+ - **Measured:** the first prototype (`prototypes/bwrap-room.sh`) passed the calling shell's
54
+ environment into the room, and a secret-like variable was visible inside. tare therefore
55
+ clears the environment and passes only an allowlist (`TERM`, `COLORTERM`, `LANG`, `LC_ALL`).
56
+ - The room holds a usable login on purpose, a copy of yours. **Measured:** a stale copy fails to
57
+ renew (`OAuth session expired`), so tare takes a fresh copy at every start and deletes it
58
+ afterwards.
59
+
60
+ ### 3. Proof: measured, not asked
61
+
62
+ - Asking the agent what it sees is a self-report, and the agent can refuse. **Measured:** Claude
63
+ Code in a room refused a scripted request to list the home directory and search for
64
+ credential files, and called it reconnaissance. That is correct behaviour, but useless for a
65
+ probe. The probe must not depend on the agent's cooperation or honesty.
66
+ - **Reach:** a plain script runs inside the room and looks for your files and inherited
67
+ environment variables.
68
+ - **Context:** tare points the real agent CLI at a fake model server on your machine. The
69
+ server keeps the request, which is the context the agent CLI put together, and answers "ok".
70
+ The idea is the blank run from analytical chemistry, which runs the whole procedure without a
71
+ sample to see what the procedure itself brings in.
72
+ - **Control:** the same agent run in your real setup, the dirty twin, must show your context. The
73
+ risk was that features that come with the login (connected accounts, account skills) would
74
+ not load against a fake server, so the probe would read clean while a real run is not.
75
+ **Measured:** for Claude Code this risk did not occur. Against the fake model server, the
76
+ dirty twin still loaded the connected accounts, account skills, plugin skills, hook output,
77
+ global instructions, memories and the email. The room loaded none of them except the email.
78
+ Pointing Claude Code at a custom base URL turns its tool search off and puts every tool schema
79
+ into the request. So the probe and the runs set `ENABLE_TOOL_SEARCH=true` to see the context as
80
+ a normal run does.
81
+
82
+ ## The parts of tare
83
+
84
+ ### Room
85
+
86
+ A room is a bubblewrap sandbox for one agent run: an empty `/home` with the room's own home
87
+ directory at `/home/tare`, a fresh copy of the login, the agent's executable, the project at
88
+ `/work`, and the environment allowlist. `tare claude` and the other agent commands mount your
89
+ project directly, so the agent changes your real files. Calibrate, Swap and Cliff give every run
90
+ its own copy of the project. The room has network access and a working login. It keeps your setup out of the measurement. It does not protect your machine from a hostile
91
+ agent.
92
+
93
+ Claude Code may replace its login token when it renews it, which could log out your real
94
+ session. This has not been tested, so tare refuses to start when the copy would need renewal
95
+ within the next hour. **Measured:** the Antigravity CLI's Google login keeps its refresh token
96
+ when it renews, so a room may renew its own copy.
97
+
98
+ ### Probe and reading
99
+
100
+ The probe has the three parts of layer 3: the agent in the room against the fake model server,
101
+ the dirty twin, and the reach script. It sorts what it finds into **kinds of context**,
102
+ each checked on its own: instructions, memories, home path, MCP servers (connected accounts count
103
+ here), skills, plugins, extensions, instruction files above the project, the account email, and
104
+ reach.
105
+
106
+ - A kind of context counts only when you have it. tare learns that from your files, for example
107
+ a global instructions file or a skill folder.
108
+ - If you have a kind of context and the dirty twin does not show it, the probe could not see
109
+ it. tare then runs the dirty twin once more after 10 seconds. If the second dirty twin still
110
+ does not show that kind of context, the reading is `tare: not proven (blind)`. **Measured:**
111
+ with six runs starting together, a dirty twin sent its request before its MCP servers were
112
+ connected.
113
+ - Every probe makes no paid model call, because both the room and the dirty twin talk to the
114
+ fake model server.
115
+
116
+ ### Agents
117
+
118
+ Each agent needs its own way to the fake model server, its own login copy and its own markers:
119
+ lines of your own files that the probe looks for in a request.
120
+
121
+ | Agent | How the room is set up | Way to the fake model server | Kinds of context the dirty twin showed on the measuring machine |
122
+ |---|---|---|---|
123
+ | Claude Code | `CLAUDE_CONFIG_DIR` and `HOME` in the room, a copy of `.credentials.json`, `--strict-mcp-config --setting-sources project,local` | `ANTHROPIC_BASE_URL` | instructions, home path, MCP servers, skills, plugins, email, reach |
124
+ | Codex | `CODEX_HOME` and `HOME` in the room, a copy of `auth.json` only, `--disable remote_plugin` | `-c openai_base_url=<server>/v1` keeps the ChatGPT login | instructions, memories, home path, skills, reach |
125
+ | Pi | `PI_CODING_AGENT_DIR` and `HOME` in the room, copies of `auth.json` and `models.json`, a minimal `settings.json`, no flags | a provider of its own (`tare`, api `anthropic-messages`), because Pi has no base-URL variable | instruction files above the project, extensions, home path, reach |
126
+ | Antigravity CLI | `HOME` in the room, a copy of `antigravity-oauth-token` and the chosen model, no flags | `CLOUD_CODE_URL` | home path, reach. Global rules (`~/.gemini/GEMINI.md`) and global skills (`~/.gemini/config/skills/`) showed after copies were placed in the setup for the test, because the measuring machine had none. |
127
+
128
+ What each agent needed:
129
+
130
+ - **Codex:** without `--disable enable_request_compression`, the request arrives compressed.
131
+ Codex tries a websocket five times before it falls back to HTTPS, and it refuses an override
132
+ of its built-in provider that would stop this. So each Codex probe waits about 8 seconds. **Measured:** the captured
133
+ request holds more than `codex debug prompt-input` shows, because Codex's own view leaves out
134
+ the memories. Memories load only when your `config.toml` enables `[features] memories`. Hooks
135
+ did not fire in one-shot runs, and the first request offered no tools from configured MCP
136
+ servers, so the probe cannot check those two kinds of context for Codex. The room has no
137
+ `config.toml` or `hooks.json` of yours, so neither can come in. A fresh `CODEX_HOME` means
138
+ Codex's default model. Pass `-m` when you compare runs.
139
+ - **Pi:** a fresh agent directory is clean on its own and keeps the project's `AGENTS.md`, so the
140
+ room uses no flags. `--no-context-files` would drop the project's `AGENTS.md`. tare finds Pi's
141
+ extension tools by comparing the dirty twin's tools with a run that uses `--no-extensions`.
142
+ With standard input left open, Pi waits for piped input and never answers, so tare closes it.
143
+ - **Antigravity CLI:** the fake model server speaks Google's Cloud Code `v1internal` API and
144
+ offers one model. It lists that model in the groups the CLI shows models in
145
+ (`agentModelSorts`), because the CLI rejects a model that no group lists. The
146
+ CLI writes into `~/.gemini` on every run, so its dirty twin runs with an overlay over
147
+ `~/.gemini`. A user namespace mounts the overlay, and every write goes to a temporary file
148
+ system that disappears after the run. **Measured:** `~/.gemini` kept its fingerprint (paths,
149
+ sizes, modification times) through every dirty twin. The CLI writes each skill's path into
150
+ its skill list, so the probe compares skill lines without the path. Without that, a skill that
151
+ leaked into the room would not match yours, because its path differs.
152
+ - **Instruction files above the project:** Claude Code's one-shot mode, which the probe uses, did
153
+ not load `~/AGENTS.md`. Pi loaded it. So for Claude Code the probe does not show whether an
154
+ interactive session in your real setup loads these files. A room has no parent directories, so these files
155
+ cannot reach a room.
156
+ - **Long prompts:** Linux limits a single argument to about 128 KiB. tare passes the task to
157
+ every agent as an argument, so a longer task fails. Pi could read it from a file with `@file`.
158
+
159
+ ### Capsules, trail and handoff
160
+
161
+ Calibrate, Swap and Cliff need a run they can stop and continue. During a recorded run, a hook
162
+ archives the workspace after every tool call. Each archive is a **capsule**. The hook comes from
163
+ `--settings` for Claude Code, from the room's `hooks.json` for Codex (with
164
+ `--dangerously-bypass-hook-trust`), from a small extension for Pi, and from a global
165
+ `PostToolUse` hook in the room's `~/.gemini/config/hooks.json` for the Antigravity CLI.
166
+
167
+ A run continues from a capsule in one of two ways:
168
+
169
+ - **Its own saved session.** Claude Code resumes the conversation cut after the step. Codex
170
+ resumes a session file cut after the step, with `codex exec resume <id>`. Pi resumes with
171
+ `--session`. **Measured:** a resumed Codex session kept its id and finished the remaining plan
172
+ from step 1.
173
+ - **A handoff.** The **trail** is a record of a run in the same format for every agent: the task,
174
+ the messages, the tool calls and their results. tare writes a handoff from any prefix of it, so
175
+ any agent can continue any run without reading another agent's session format. The Antigravity
176
+ CLI stores its sessions in a format that tare cannot cut, so it always continues with a
177
+ handoff. Its trail comes from `transcript_full.jsonl`.
178
+
179
+ ### Calibrate
180
+
181
+ `tare calibrate` starts each side (an agent with its arguments) several times from a fresh copy
182
+ of the project and reports each side's pass rate with a 95% interval (Wilson). A side can have
183
+ its own prompt, so two skills that start with different commands compare in one call. Without a
184
+ check, a run counts as finished when its agent exits with 0, and tare keeps its workspace for
185
+ scoring elsewhere. **Measured:** in a field test that compared two ideation skills, 18 rooms all
186
+ read `tare: 0.00`, including a skill that started `claude -p` inside the room (see
187
+ [Measurements](#measurements)).
188
+
189
+ ### Judge
190
+
191
+ The judge scores results that no test can decide, such as a web page. tare opens the page in
192
+ headless Google Chrome inside a room with the workspace read-only and no home directory, and
193
+ takes a screenshot. A judge agent (Claude Code with Sonnet by default) then scores the page from
194
+ the screenshot, the source and a rubric, in a fresh room without any agent or model names.
195
+ **Measured:** one test page scored 55, 60, 58, 58, 66 and 62 in six judgements. `tare judge-noise`
196
+ therefore refuses a threshold that lies inside a page's range of scores. On the same page the
197
+ judge found a real one-cent rounding bug and an overflow.
198
+
199
+ ### Swap
200
+
201
+ Swap cuts two runs at several points. At each cut, each agent continues each run's workspace
202
+ by handoff. Swap reports a state effect (how much the state of the workspace matters), a model
203
+ effect (which agent does better), and, where an agent can continue its own saved session, the
204
+ handoff cost. At cut 0 both workspaces are the untouched project, so the state effect there must
205
+ be zero. Swap checks this, and the check is called the null check. When the state effect and the
206
+ model effect are within 0.1 of each other, Swap says they explain the failure about equally.
207
+
208
+ **Measured:** against scripted models with a known truth, the null check passed and the blame
209
+ passed from the model to the workspace between cut 0 and cut 0.5, as the scripts were written
210
+ to cause.
211
+
212
+ ### Cliff
213
+
214
+ Cliff restarts a failed run from its capsules and names the step after which it no longer
215
+ succeeds. The baseline is the set of tails from the start. A step counts as good while its
216
+ tails pass at least half as often as the baseline. Cliff halves the range between the last good
217
+ and the first bad step, then adds tails to the two neighbours until their intervals separate or
218
+ the budget is spent. It reports a model gap instead of a step only when the upper end of the
219
+ baseline's 95% interval lies below 0.2.
220
+
221
+ **Measured:** against a scripted model whose wrong step is known, Cliff found step 4 with
222
+ separated intervals in 18 tails. Cliff stays experimental, because neither of two real test
223
+ rounds had a failure that sat in one step (see [Measurements](#measurements)). Swap already
224
+ shows, at three cuts, how often an agent passes when it continues its own workspace. When a real
225
+ Swap shows that rate dropping between two cuts, Cliff can narrow the drop down to one step. Only
226
+ then does Cliff get new experiments.
227
+
228
+ ### Tokens and cost
229
+
230
+ Each adapter reads its agent's own stream after a run into one shape: all input tokens with the
231
+ cached ones included, the cached part, cache writes, output, reasoning, the cost where the agent
232
+ reports it, and the model where the stream names it. The agents count differently: Claude Code
233
+ and Pi report the cache apart from the input, Codex within it. A check that runs an agent itself
234
+ (`tare judge`) appends that agent's usage to a file that tare names in `TARE_USAGE_LOG`, so the
235
+ judge counts too. Where an agent reports no cost, tare estimates it from a price table and says
236
+ so, with the table's source and date. Totals keep count of runs whose cost stays unknown. A
237
+ subscription login is marked, because its cost is notional.
238
+
239
+ **Measured:** a calibrate run with one Haiku run, one Codex run with gpt-6.1-sol and a Sonnet
240
+ judge read 186.4k tokens in and $0.093 in total, the Codex part estimated from LiteLLM's table.
241
+
242
+ ### Run directory, dashboard and recipes
243
+
244
+ Calibrate, Swap and Cliff write everything into a run directory: a journal (one JSON line per
245
+ event), a `recipe.json`, a `usage.json` with the totals, and the live output of every agent. The dashboard reads only the run
246
+ directory, so `tare watch` shows a finished run the same way as a live one. The recipe holds the
247
+ command, a fingerprint of the project and the agent versions. `tare rerun` repeats a run and
248
+ names what has changed since.
249
+
250
+ ## Measurements
251
+
252
+ All measurements ran on WSL2 on 2026-10-04 and 2026-10-05.
253
+
254
+ | What | Agent and version | Result | Source |
255
+ |---|---|---|---|
256
+ | Dirty twin and room, fake model server | Claude Code 2.1.289 | the dirty twin showed every kind of context, the room only the email | [results](experiments/dirty-twin/RESULTS.md) |
257
+ | Dirty twin and room | Codex CLI 0.160.0 | the room read `tare: 0.00` | [results](experiments/dirty-twin-codex/RESULTS.md) |
258
+ | Dirty twin and room | Pi 1.0.2 | the room read `tare: 0.00` without flags | [results](experiments/dirty-twin-pi/RESULTS.md) |
259
+ | Dirty twin and room | Antigravity CLI 1.2.16 | the room read `tare: 0.00` without flags, and `~/.gemini` stayed unchanged | [results](experiments/dirty-twin-agy/RESULTS.md) |
260
+ | Rooms given copies of real settings on purpose | all four | Claude Code 4 leaks, Codex 6, Pi 5, Antigravity CLI 3, each named with its source | the results above |
261
+ | Scripted Cliff and Swap | Claude Code against scripted models | Cliff found step 4. Swap passed the null check and found the blame passing from the model to the workspace between cut 0 and cut 0.5 | `experiments/cliff-scripted`, `experiments/swap-scripted` |
262
+ | Real task 1: a data migration | Claude Code with Sonnet and Haiku, Codex | Deleting the source file after a wrong-encoding import was planned as the point of no return. Swap showed that the workspace could still be repaired. Haiku passed about 1 in 14 runs, Codex with gpt-6.1-sol 4 of 4 | [results](experiments/migration/RESULTS.md) |
263
+ | Real task 2: a web page judged by Sonnet | Claude Code with Haiku, Codex with gpt-6.1-sol | Swap confirmed all four predictions of the plan written before the runs. Cliff called a failed run unlucky, and a later rescoring of the same page confirmed it | [results](experiments/html/RESULTS.md) |
264
+ | Field test: two ideation skills compared | Claude Code 2.1.289 with Opus 5.5 | all 18 rooms read `tare: 0.00`, including a skill that started `claude -p` inside the room | not published |
265
+
266
+ ## Open questions and ideas
267
+
268
+ - **Account email:** in a clean room the email reached the first request in 3 of 6 runs, which
269
+ suggests a race with the loading of the account profile. tare declares it whenever it appears.
270
+ - **Reused config:** a separate but reused config directory once loaded connected accounts, while
271
+ a fresh room never did. Cached account data is the suspected cause. Not tested.
272
+ - **Claude Code login renewal:** whether a renewal inside the room logs out the real session. Not
273
+ tested, so tare avoids it.
274
+ - **Codex handoffs:** Codex can run several commands in one tool call, which makes its handoffs
275
+ long.
276
+ - **Cost:** every tail is a full agent run, so a Cliff search or a Swap costs as much as its tails.
277
+ - **macOS:** a room built on Seatbelt (`sandbox-exec`), which Claude Code and Codex already use
278
+ for their own sandboxes. In progress (epic #85), not in a release yet.
279
+ - **Other probes, not built:** weighing the first request's input tokens before and after adding
280
+ junk to the real setup, and file tripwires that report which secret files a run opened.
281
+ - **Docker:** could contain files where bubblewrap is missing, but does nothing against context
282
+ that comes with the login.
283
+ - **Copilot CLI:** checked once but not supported. Its `--available-tools` ignores an empty list
284
+ without a warning, so only a list that names no real tool turns its tools off.
285
+
286
+ ## Prior art
287
+
288
+ Checked on 2026-10-04.
289
+
290
+ - Multi-agent evaluation frameworks: UiPath/coder_eval (Claude Code, Codex, Antigravity,
291
+ OpenCode and Pi, in a temporary directory or Docker), google/skill-reach (skill routing across
292
+ Antigravity, Claude Code, Goose and Pi), mgechev/skillgrade (Docker and graders),
293
+ `claude plugin eval` (A/B with and without a plugin). The documentation of coder_eval states
294
+ that the caller provides a clean environment. None of them documents removing the agent's own
295
+ user context or proving that it is gone.
296
+ - Isolation by hand inside test suites, for example setting `CODEX_HOME`, `HOME` and
297
+ `CLAUDE_CONFIG_DIR` in a project's tests (hamza-aziz-ai/codex-claude-council PR #11).
298
+ - Fake model servers that could serve as a building block: Aetheria-LabsJP/puppetllm,
299
+ CopilotKit/llmock, litellm's agent harness.
300
+ - Orchestrators that run several agent CLIs side by side (Google Scion, agent-manager) isolate
301
+ processes, not user context.
302
+
303
+ tare is meant to sit underneath such frameworks. They run the evaluation, and tare provides the
304
+ clean room.
305
+
306
+ ## Name
307
+
308
+ The command is `tare`. The package is called `tare-cli`, because `tare` is taken on PyPI and npm.
tare_cli-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andre Ratzenberger
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.