Skip to content

engine: no fixed cap on a tool's result; the cut and the routing follow the model's window, read from OpenRouter on every turn - #1448

Merged
feder-cr merged 3 commits into
mainfrom
no-char-caps
Oct 12, 2026
Merged

feder-cr merged 3 commits into
mainfrom
no-char-caps

Conversation

@feder-cr

Copy link
Copy Markdown
Owner

No tool result has a fixed cap any more. The only limit left is the model's own context window, read from OpenRouter on every turn, and a model's requests now go only to the providers that have that window.

Tool results come back whole

These fixed caps are gone:

Cap Was
General cap on every tool result 12000 characters
exec output 10000, at most 50000, kept as head and tail
glob/grep pages 12000
Each matched line 2000
read_file 128000

The model's own paging stays: read_file's offset/limit and the search tools' head_limit.

A request that outgrows the model's window is compacted first, as before: old results are cleared, then the history is summarized. Only if it still does not fit are the results of its latest round cut, by ContextGovernor.fit_latest_round:

  • The cut is measured with the same estimate that decides whether the request fits.
  • The results share the room in their order, each leaving room for the notes of the results after it.
  • The note says how much came through and how to ask for less: [Cut here: the first N of M characters. The whole result does not fit in the context window of <model> (<W> tokens) beside the rest of the request; ask for less to see the rest: a narrower selector, a pattern, an offset and a limit.]
  • A result cut again keeps the whole result's length in its note.

A result is stored cut only past what no compaction could make room for: the window less the system prompt and the tool definitions.

The prompt caps stay: MEMORY.md at 25000 characters and MCP server instructions at 2048.

The window: read on every turn, and the routing follows it

  • Limits: they are read at the start of every turn from GET /models/{model}/endpoints, about 32 KB. Before, the whole GET /models list (768 KB) was read once per process, so a long-lived engine never saw a model's providers change. Of the providers serving the model (a negative status means down), the model takes the widest window and the longest answer at that window.
  • Routing: OpenRouter routes by the answer a request asks for, not by its prompt. A request sized for the widest window could therefore land on a narrower provider, for example Reka at 262144 among glm-5.3-flash's providers at 1048576. The model's requests now carry provider.only, set to the slugs of the providers with the widest window, from the same reading.
    • Checked live: such a request is served.
    • When none of those providers can serve, OpenRouter answers 404 "No allowed providers are available for the selected model" and the turn fails with those words.
    • A router (openrouter/auto) lists no providers, so it gets no limits and no route.

Image

invisible-playwright-mcp 0.72.1 removes its last caps:

  • browser_type quotes the value the field kept in full.
  • An error detail in its built-in chat and routes comes back in full.

The tools' schemas are unchanged; the recaptured interface differs only in its version.

Verification

  • Engine pytest: 2492/2492, plus 272/272 in the provider and turn suites after the routing change.
  • Image vitest (golden, runtime): 28/28.
  • Engine smoke: 257 passed. Browser smoke: 74 passed. Nothing failed or was skipped in either.
  • e2e on a real VM against the real OpenRouter: 16/16. The golden image was rebuilt from the 0.72.1 lock, and the turns read their limits from the endpoints and were routed with provider.only.

Known effect

An exec session that nobody polls keeps its whole output in memory until the next poll or the idle cleanup.

…w, read from OpenRouter on every turn

A result comes back whole. exec, glob/grep and read_file lose their fixed caps (10000/50000, 12000 and 2000
per line, 128000) and the general 12000-character cut is gone. A request that outgrows the model's window is
compacted first, as before; only if it still does not fit are the results of its latest round cut to what the
window leaves, measured with the estimate that judges the request, and the cut says how many characters came
through of how many and how to ask for less. A result is stored cut only beyond what no compaction could make
room for.

The window is read on every turn from the providers OpenRouter lists for the model
(GET /models/{model}/endpoints), not once per process from the whole list: of the providers serving it, the
widest window and the longest answer at that window.
…dow; image: invisible-playwright-mcp 0.72.1

OpenRouter routes a request by the answer it asks for but not by the prompt it carries, so a request sized for
the model's widest window could land on a provider with a narrower one (glm-5.3-flash: Reka at 262144 among
providers at 1048576) and fail there. The providers read with the limits on every turn now route the model's
requests: provider.only is the slugs of the widest window's providers. When none of them can serve, OpenRouter
refuses with "No allowed providers are available" and the turn fails with those words.

invisible-playwright-mcp 0.72.1 drops its last caps: browser_type quotes the value the field kept whole, and an
error detail in its chat and routes comes whole. The tools' schemas are unchanged.
@feder-cr
feder-cr merged commit a2584f2 into main Oct 12, 2026
13 checks passed
@feder-cr
feder-cr deleted the no-char-caps branch October 12, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant