tracetrim 0.3.5__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.
Files changed (36) hide show
  1. tracetrim-0.3.5/LICENSE +21 -0
  2. tracetrim-0.3.5/LICENSES.md +23 -0
  3. tracetrim-0.3.5/PKG-INFO +332 -0
  4. tracetrim-0.3.5/README.md +297 -0
  5. tracetrim-0.3.5/pyproject.toml +49 -0
  6. tracetrim-0.3.5/setup.cfg +4 -0
  7. tracetrim-0.3.5/src/tracetrim/__init__.py +7 -0
  8. tracetrim-0.3.5/src/tracetrim/about.py +23 -0
  9. tracetrim-0.3.5/src/tracetrim/auto.py +245 -0
  10. tracetrim-0.3.5/src/tracetrim/cli.py +271 -0
  11. tracetrim-0.3.5/src/tracetrim/measure.py +82 -0
  12. tracetrim-0.3.5/src/tracetrim/oauth.py +175 -0
  13. tracetrim-0.3.5/src/tracetrim/projection.py +133 -0
  14. tracetrim-0.3.5/src/tracetrim/proxy.py +513 -0
  15. tracetrim-0.3.5/src/tracetrim/report.py +671 -0
  16. tracetrim-0.3.5/src/tracetrim/runtime.py +65 -0
  17. tracetrim-0.3.5/src/tracetrim/structural.py +127 -0
  18. tracetrim-0.3.5/src/tracetrim/traces.py +133 -0
  19. tracetrim-0.3.5/src/tracetrim/usage.py +216 -0
  20. tracetrim-0.3.5/src/tracetrim.egg-info/PKG-INFO +332 -0
  21. tracetrim-0.3.5/src/tracetrim.egg-info/SOURCES.txt +34 -0
  22. tracetrim-0.3.5/src/tracetrim.egg-info/dependency_links.txt +1 -0
  23. tracetrim-0.3.5/src/tracetrim.egg-info/entry_points.txt +2 -0
  24. tracetrim-0.3.5/src/tracetrim.egg-info/requires.txt +13 -0
  25. tracetrim-0.3.5/src/tracetrim.egg-info/top_level.txt +1 -0
  26. tracetrim-0.3.5/tests/test_auto.py +71 -0
  27. tracetrim-0.3.5/tests/test_c4_audit.py +31 -0
  28. tracetrim-0.3.5/tests/test_core.py +106 -0
  29. tracetrim-0.3.5/tests/test_function_calling.py +73 -0
  30. tracetrim-0.3.5/tests/test_multi.py +88 -0
  31. tracetrim-0.3.5/tests/test_oauth.py +93 -0
  32. tracetrim-0.3.5/tests/test_proxy.py +73 -0
  33. tracetrim-0.3.5/tests/test_proxy_passthrough.py +52 -0
  34. tracetrim-0.3.5/tests/test_report.py +95 -0
  35. tracetrim-0.3.5/tests/test_safeguards.py +58 -0
  36. tracetrim-0.3.5/tests/test_structural.py +35 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alessio Pomaro
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,23 @@
1
+ # Licence map
2
+
3
+ | Material | Path | Licence | Notes |
4
+ |---|---|---|---|
5
+ | Library and experiment code | `src/`, `experiments/`, `tests/` | MIT (`LICENSE`) | |
6
+ | Paper text, figures, documentation, protocol | `paper/`, `docs/`, `protocol.md`, README files | CC BY 4.0 | |
7
+ | Aggregate results | `results/` | CC BY 4.0 | counts and measures only, no third-party text |
8
+ | Traces of C3 (literature scan) | `dataset/traces/C3/` (Zenodo) | CC BY 4.0 for our part | tool outputs are OpenAlex records, CC0 |
9
+ | Traces of C4 and C5 (simulated) | `dataset/traces/C4`, `C5` (Zenodo) | CC BY 4.0 | payloads generated by our code |
10
+ | Derived records of C1 and C2 | `dataset/derived/` (Zenodo) | CC BY 4.0 | field paths and token counts only; DataForSEO results are not redistributed |
11
+
12
+ ## Third-party materials used, not redistributed in this repository
13
+
14
+ | Material | Use | Terms |
15
+ |---|---|---|
16
+ | DataForSEO API (via its MCP server) | tools of C1 and C2 | DataForSEO Terms of Service (read 2026-11-04): aggregates and field names published; raw results not redistributed |
17
+ | OpenAlex API | tools of C3 | data CC0; used without an API key within the free allowance |
18
+ | Stripe OpenAPI description (`stripe/openapi`, commit b213739) | schemas of the C4 simulator | MIT; downloaded at build time, not vendored |
19
+ | GitHub REST API description (`github/rest-api-description`, commit 99b1432) | schemas of the C5 simulator | MIT; downloaded at build time, not vendored |
20
+ | Headroom (`headroom-ai`) | optional backend, token baseline | Apache-2.0; optional dependency, not vendored |
21
+ | TOON format | the library's own TOON-style encoder follows the public specification | specification by J. Schopplich; no code reused |
22
+
23
+ Tavily was not used: its Platform Terms (§3.2(x)) forbid disclosing performance analyses of the service.
@@ -0,0 +1,332 @@
1
+ Metadata-Version: 2.4
2
+ Name: tracetrim
3
+ Version: 0.3.5
4
+ Summary: Learn which fields of tool outputs an agent visibly reuses, from its traces, and trim the rest.
5
+ Author: Alessio Pomaro
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://www.alessiopomaro.it/
8
+ Project-URL: Repository, https://github.com/alepom/tracetrim
9
+ Project-URL: Paper, https://doi.org/10.5281/zenodo.23953934
10
+ Project-URL: Software DOI, https://doi.org/10.5281/zenodo.23954125
11
+ Project-URL: Changelog, https://github.com/alepom/tracetrim/blob/master/CHANGELOG.md
12
+ Keywords: agents,mcp,llm,tool outputs,token,telemetry
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Operating System :: OS Independent
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ License-File: LICENSES.md
25
+ Provides-Extra: measure
26
+ Requires-Dist: tiktoken>=0.7; extra == "measure"
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest>=8; extra == "test"
29
+ Requires-Dist: tiktoken>=0.7; extra == "test"
30
+ Provides-Extra: proxy
31
+ Requires-Dist: mcp>=2.3; extra == "proxy"
32
+ Provides-Extra: headroom
33
+ Requires-Dist: headroom-ai>=0.39; extra == "headroom"
34
+ Dynamic: license-file
35
+
36
+ # tracetrim
37
+
38
+ **Agents read more than they visibly reuse.** tracetrim learns, from your agent's own traces, which
39
+ fields of each tool output the agent reuses in what it writes, and helps you remove the rest — with a
40
+ telemetry report you can read, a configuration you can review, and an MCP proxy that applies it
41
+ in front of any server without changing your agent.
42
+
43
+ It comes with a [study](https://doi.org/10.5281/zenodo.23953934), with protocols frozen before every run, of
44
+ where this works and where it fails. In short:
45
+
46
+ | | |
47
+ |---|---|
48
+ | Tool-output tokens removed on unseen runs (0.1) | real APIs: 63% and 70% (12% on the third agent); simulated: 69% and 77% |
49
+ | …of which re-serialising the JSON alone, removing nothing | up to 13% (real APIs), up to 17% (simulated) |
50
+ | Same conversations: tracetrim 0.1 / 0.2 / Headroom (stateless) / full minifier, no field removed | the same two real-API agents: 59–68% / 46–53% / 17–28% / 16–24%; simulated: 69–77% / 57–61% / 31–33% / 18–33% |
51
+ | Live runs with `expand`: billed input tokens | −56% (real API) and −32% (simulated); every run passed its gates; a later re-check found one unsupported volume among 180 reported keyword–volume pairs |
52
+ | Learning from tool calls only (what a gateway sees) | a used field missed in 49% of the held-out structured calls it trims (156/317, 3 agents on real APIs), against 4% with the answer |
53
+ | Where it fails | values the agent transforms, fields used only to reason, and — without an id field — agents **inventing** ids |
54
+
55
+ Version 0.2 keeps identifying fields and recognises transformed values by default. Version 0.3
56
+ adds an automatic mode: put one proxy in front of your MCP servers and it observes, learns and
57
+ trims on its own. Read the [paper](https://doi.org/10.5281/zenodo.23953934) before trimming
58
+ anything that matters.
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pip install "tracetrim[measure,proxy]"
64
+ pip install "tracetrim[headroom]" # + Headroom backend (experimental, heavy)
65
+ ```
66
+
67
+ Python 3.10+. From GitHub: `pip install "tracetrim[measure,proxy] @ git+https://github.com/alepom/tracetrim"`.
68
+ From a clone: `pip install -e ".[measure,proxy,test]"`. To reproduce the
69
+ study's numbers, figures and analyses: `pip install -e ".[measure,proxy,test]"` is enough (they need no agent SDK);
70
+ to run new agent sessions, also `pip install -r requirements-study.txt` (the versions used, OpenAI SDK included).
71
+
72
+ ## The short way: automatic mode
73
+
74
+ Your agent already uses one or more MCP servers. Copy their `mcpServers` block — the same JSON
75
+ Claude Desktop, Claude Code and Cursor use — into a file:
76
+
77
+ ```json
78
+ {"mcpServers": {
79
+ "billing": {"url": "https://billing.example/mcp", "headers": {"Authorization": "Bearer ${BILLING_TOKEN}"}},
80
+ "code": {"command": "python", "args": ["-m", "my_code_server"], "env": {"API_KEY": "${CODE_KEY}"}}}}
81
+ ```
82
+
83
+ Start one tracetrim in front of all of them:
84
+
85
+ ```bash
86
+ tracetrim proxy --servers mcp.json --auto tracetrim-data/ # stdio, for local clients
87
+ export TRACETRIM_TOKEN=CHOOSE_A_SECRET # over HTTP: clients must send it
88
+ tracetrim proxy --servers mcp.json --auto tracetrim-data/ --http 8799
89
+ ```
90
+
91
+ In the agent, **replace** those servers with tracetrim (one entry instead of N; keeping both would
92
+ show the agent two copies of every tool), and add one line to its instructions:
93
+
94
+ ```text
95
+ Last step, required if the tool tracetrim_done is available: when your final answer is ready,
96
+ call tracetrim_done once with the complete answer, before you show it; then show the same answer.
97
+ ```
98
+
99
+ The agent sees the same tools with the same names (prefixed with the server name only if two
100
+ servers share one), plus `expand` and `tracetrim_done`. Then use the agent as usual:
101
+
102
+ - **from the first run**, a telemetry report is kept up to date in `tracetrim-data/report.html`
103
+ (over HTTP also live at `/report?token=...`): which tools fill the context, which fields the agent
104
+ never uses, with suggestions;
105
+ - **during the first runs** (5 by default, `--min-runs`) nothing is changed: the proxy only observes.
106
+ `tracetrim_done` is how it sees the answer — in the study, learning from tool calls alone missed a
107
+ used field in 49% of held-out calls (three agents on real APIs), against 4% with the answer;
108
+ - **after that**, it applies the report's automatic actions on its own: the prudent projection, with
109
+ identifying fields kept, transformed values recognised and `expand` on. Everything else stays in the
110
+ report as a manual action for you to review;
111
+ - **it corrects itself**: a tool whose trimmed outputs the agent expands too often is no longer
112
+ trimmed, and the report says so. Runs without `tracetrim_done` count for telemetry, not for learning.
113
+ One agent at a time: calls that arrive together from several agents end up in the same run. Over MCP the
114
+ proxy cannot see the model's turns, so calls emitted together are compared by arrival order: a later call's
115
+ arguments can count as using an earlier sibling's output. In the study this changed no figure; where turns
116
+ matter, record them (`AutoTrim` takes `turn`, JSONL traces accept a `"turn"` field).
117
+
118
+ Everything lives in the work directory (traces, report, config, state), so learning survives restarts;
119
+ delete it to start again. In a check with a real agent on the billing case (5 runs, 3 to learn), the
120
+ tool outputs of the two runs after learning went from 33,492 to 18,725 tokens (−44%, fields asked back
121
+ included), the agent asked for a hidden field once in each, and all five tickets passed the keyword test in every
122
+ run: a check, not a measurement (`results/checks/auto_check_mcp.json`) — the measurements are in the
123
+ paper.
124
+
125
+ ### Servers that need credentials
126
+
127
+ Two separate links, two separate credentials: **agent → tracetrim** is protected by the token you
128
+ choose, in `$TRACETRIM_TOKEN` (or `--token`, which other users of the machine can see in the process
129
+ list), only when tracetrim is reachable over HTTP; requests whose Host is not local or listed with
130
+ `--allowed-host` are refused. **tracetrim → your servers**
131
+ uses your servers' own credentials, which tracetrim holds and the agent no longer sees.
132
+
133
+ | how your server authenticates | in `mcp.json` |
134
+ |---|---|
135
+ | API key or bearer token in a header | `"headers": {"Authorization": "Bearer ${MY_TOKEN}"}` (any header name) |
136
+ | local server reading a key from the environment | `"env": {"API_KEY": "${MY_KEY}"}` |
137
+ | OAuth, with a browser login (as in Claude Code or Cursor) | just `"url"`: no headers needed |
138
+
139
+ `${VAR}` is read from the environment when the proxy starts, so keys stay out of the file. For
140
+ OAuth servers, log in once:
141
+
142
+ ```bash
143
+ tracetrim login --servers mcp.json # opens the login page of each OAuth server
144
+ ```
145
+
146
+ Discovery, client registration, PKCE and token renewal follow the MCP authorization spec (through
147
+ the MCP SDK); tokens are kept in `~/.config/tracetrim/oauth/`, readable only by you, and renewed on
148
+ their own, so the proxy never waits for a browser. `tracetrim logout --servers mcp.json` forgets them.
149
+ Not supported: a different identity per end user (the proxy uses one identity for everyone) and the
150
+ legacy SSE transport.
151
+
152
+ ### Hosted agents (OpenAI Playground, Responses API)
153
+
154
+ There is no `mcpServers` file in the Playground: each MCP server is added in the UI (*Tools → Add →
155
+ MCP server*) with a URL and, if needed, a header. Write `mcp.json` from those same values — one
156
+ entry per server, URL and header as you typed them — and run tracetrim over HTTP where OpenAI can
157
+ reach it (a server of yours, or a tunnel such as ngrok):
158
+
159
+ ```bash
160
+ export TRACETRIM_TOKEN=CHOOSE_A_SECRET
161
+ tracetrim proxy --servers mcp.json --auto tracetrim-data/ --http 8799 --allowed-host your-tunnel.example
162
+ ```
163
+
164
+ Then, in the Playground, switch off the servers tracetrim now stands in for and add one MCP server:
165
+ URL `https://your-tunnel.example/mcp`, header `Authorization: Bearer CHOOSE_A_SECRET`. The live
166
+ report is at `http://127.0.0.1:8799/report?token=CHOOSE_A_SECRET` on the machine running the proxy.
167
+ `docs/demo_playground.sh` does all of this for the demo.
168
+
169
+ ### Function calling, without MCP
170
+
171
+ If your agent runs its own tools (function calling), there is no proxy to put in between: the
172
+ loop that runs the tools is yours, so tracetrim goes in there, in three places. No
173
+ `tracetrim_done` either: your code already has the final answer.
174
+
175
+ ```python
176
+ from tracetrim.auto import AutoTrim
177
+
178
+ tt = AutoTrim("tracetrim-data/", min_runs=5)
179
+ tools = my_tools + [{"type": "function", **tt.expand_tool}] # 1. offer expand
180
+
181
+ # 2. in the loop, for every tool call the model makes
182
+ if call.name == "expand":
183
+ result = tt.expand(call.arguments)
184
+ else:
185
+ result = tt.output(call.name, call.arguments, run_my_tool(call.name, call.arguments))
186
+
187
+ # 3. when the run is over
188
+ tt.done(final_answer)
189
+ ```
190
+
191
+ `tt.output` records the call and returns what the model should receive: the raw result during the
192
+ first runs, the trimmed one after. `tt.done` closes the run, updates the report
193
+ (`tt.report`, `tracetrim-data/report.html`) and, once `min_runs` runs exist, starts trimming; what
194
+ was learned survives restarts. The rules are those of the proxy's automatic mode. Arguments can be
195
+ a dict or the JSON string most SDKs hand you; `tt.expand_tool` is a name, a description and a JSON
196
+ schema, to adapt to your provider's tool format (`{"type": "function", ...}` for OpenAI, `name` /
197
+ `description` / `input_schema` for Anthropic).
198
+
199
+ Checked with a real agent on the billing case, function calling, 5 runs with 3 to learn: after learning,
200
+ tool outputs went from 33,862 to 18,947 tokens (−44%, fields asked back included), the agent asked for a
201
+ hidden field once, and all five tickets passed the keyword test in every run (a check, not a measurement:
202
+ `results/checks/auto_check_function.json`, from
203
+ `python -m experiments.auto_check --runs 5 --min-runs 3 --mode function`).
204
+
205
+ **Mind the work directory.** To learn, tracetrim keeps the full tool outputs of every run in
206
+ `tracetrim-data/traces/`. If your servers return confidential data, treat that directory as you
207
+ treat the data: local, not shared, not committed.
208
+
209
+ ## Quickstart: five runs, one report, one config
210
+
211
+ The manual way, with a review step before anything is applied.
212
+
213
+ **1. Record a few runs with full tool outputs.** A trace is JSONL, one event per line:
214
+
215
+ ```json
216
+ {"kind": "prompt", "text": "You are a billing support agent..."}
217
+ {"kind": "call", "tool": "stripe_list_charges", "arguments": {"customer": "cus_123"}, "output": "{\"object\": \"list\", ...}"}
218
+ {"kind": "message", "text": "{\"ticket\": [...]}"}
219
+ ```
220
+
221
+ `output` is the raw text the tool returned; the last `message` is the agent's answer. The
222
+ answer matters: without it the learner cannot see what the agent used. An optional `"turn"` on
223
+ every event (the model turn that produced it; the prompt is `-1`) lets the learner tell calls
224
+ emitted together apart: their arguments were written before any of their outputs existed. OpenAI
225
+ Playground Python exports are also read directly (`.py`).
226
+
227
+ **2. Generate the report.** It changes nothing; it is useful on its own.
228
+
229
+ ```bash
230
+ tracetrim report traces/*.jsonl -o report.html
231
+ ```
232
+
233
+ You get `report.html` (bilingual EN/IT: which tools fill the context, which calls fail and with
234
+ which provider error, which fields the agent never uses, with suggestions), `report.json`, and a
235
+ runtime config with the projection it uses: `report.config.json`, `report.projection.json`.
236
+
237
+ **3. Review the config.** It is plain JSON: which tools are trimmed, with `expand` on. The
238
+ projection lists, per tool, the fields kept and the fields dropped. Remove a tool from the config,
239
+ or a field from the dropped list, if you know the task needs it.
240
+
241
+ **4. Apply it.**
242
+
243
+ In your own code:
244
+
245
+ ```python
246
+ from tracetrim.runtime import Policy
247
+ policy = Policy.load("report.config.json")
248
+ tool_message = policy.apply(tool_name, raw_output)
249
+ ```
250
+
251
+ `Policy.apply` only trims: it adds no `[trimmed]` line and there is no `expand` behind it. For the way
252
+ back, use `AutoTrim` (function calling, below) or the proxy.
253
+
254
+ Or in front of any MCP server, with no code change — locally (Claude Desktop, Claude Code,
255
+ Cursor):
256
+
257
+ ```json
258
+ {"mcpServers": {"billing": {"command": "tracetrim", "args": [
259
+ "proxy", "--upstream-cmd", "python -m my_billing_server", "--config", "/path/report.config.json"]}}}
260
+ ```
261
+
262
+ or over HTTP, for hosted clients such as the OpenAI Playground:
263
+
264
+ ```bash
265
+ export TRACETRIM_TOKEN=CHOOSE_A_SECRET
266
+ tracetrim proxy --upstream-url https://mcp.example.com/mcp --header "Authorization=Bearer UPSTREAM_TOKEN" \
267
+ --config report.config.json --http 8799 --allowed-host your-tunnel.example
268
+ ```
269
+
270
+ (`--header` puts the upstream credential in the process list; `--servers mcp.json` with `${VAR}` does not.)
271
+
272
+ The proxy exposes the same tools plus `expand`: every trimmed output ends with a line listing the
273
+ hidden fields, and the agent can fetch any of them back (`"*"` for all of them) while the proxy keeps the original:
274
+ the last 256 trimmed results; at most 25 groups are listed, the rest summarised. `--record traces/live.jsonl`
275
+ records calls and outputs for telemetry; it does not record the agent's answer, so a report learned from it misses
276
+ most of what the agent uses (the study's central finding). To learn while running, use `--auto`, which receives
277
+ the answer through `tracetrim_done`.
278
+
279
+ How it wraps another server: the proxy is an MCP *server* towards your agent and an MCP *client*
280
+ towards the upstream. At start it asks the upstream for its tools and publishes the same names and
281
+ input schemas as its own (plus `expand`); each call is forwarded unchanged, and only the result is
282
+ rewritten on its way back. One proxy can stand in front of several servers (`--servers`).
283
+
284
+ While it runs, the proxy prints one line per call on stderr — tokens from the tool, tokens to the
285
+ agent, hidden fields, `expand` requests and the session total (`--quiet` to silence it). Add
286
+ `--structural toon` or `--structural headroom` to re-encode outputs after the projection
287
+ (experimental; Headroom needs `tracetrim[headroom]`).
288
+
289
+ **5. Measure before you trust it.**
290
+
291
+ ```bash
292
+ tracetrim measure traces/*.jsonl --per-tool
293
+ ```
294
+
295
+ Leave-one-out: each run is trimmed with a projection learned from the others; you see the tokens
296
+ removed and the *misses* (held-out calls in which a field the agent used would have been removed).
297
+
298
+ ## What it does not do
299
+
300
+ - It does not learn from a proxy alone: learn offline from complete traces, apply anywhere.
301
+ - It does not see reasoning: a field read only to decide something looks unused. Identifiers and
302
+ titles are kept for this reason; other such fields need your review.
303
+ - It does not translate: a value the agent rewrites in another language (or form) that the
304
+ normalisers do not cover looks unused.
305
+ - Structural re-encoding (`"structural": "toon"` or `"headroom"` in the config) is optional and was
306
+ seen to change how a model resolves ambiguous data. Validate on your task first.
307
+
308
+ ## Reproducing the study
309
+
310
+ `experiments/` contains the agent loop, the simulated MCP servers (Stripe- and GitHub-shaped,
311
+ generated from their public OpenAPI descriptions), the gates and thermometers, and every
312
+ analysis. `protocol.md` is the frozen protocol with all deviations. Every number of the paper is
313
+ generated by `python -m experiments.paper_numbers` from `results/`, and every chart by
314
+ `python -m experiments.paper_figures` from `results/` and the traces. Run them from the repository root
315
+ with `PYTHONPATH=src`. The traces are in the Zenodo dataset: unzip it at the root, and the scripts read
316
+ `dataset/traces/` (C3, C4 and C5; C1 and C2 are released as derived records only, since the search
317
+ data cannot be redistributed, so their analyses run where they were recorded and their results are in
318
+ `results/`; `python -m experiments.misses_from_derived` recomputes their misses from the records). The simulated
319
+ servers download their pinned OpenAPI descriptions on first use. Changes by version are in
320
+ [CHANGELOG.md](https://github.com/alepom/tracetrim/blob/master/CHANGELOG.md).
321
+
322
+ ## Citation
323
+
324
+ Paper: Alessio Pomaro, *Agents Read More Than They Use: Learning Tool-Output Projections from Execution Traces,
325
+ and Where They Fail*, preprint, 2026. DOI [10.5281/zenodo.23953934](https://doi.org/10.5281/zenodo.23953934).
326
+
327
+ Software, protocols and dataset: DOI [10.5281/zenodo.23954125](https://doi.org/10.5281/zenodo.23954125).
328
+ See `CITATION.cff`.
329
+
330
+ ## License
331
+
332
+ MIT for the code. Third-party materials and their terms: see `LICENSES.md`.