detached 8bb11d5

Merge pull request #160 from frkr/feat/openroutertoolsconfig

config: make OpenRouter server-tool call limit configurable via config.json
commit
8bb11d5f954537af5f4156837a25241a008d2727
author
Patric Eckhart · · 2026-09-04 05:39 UTC
committer
GitHub · · 2026-09-04 05:39 UTC
merge of
532d7f6 + 90b3571 · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
6 files · +45 -3
M README.md +11 -1
@@ -403,7 +403,17 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o
403403 - **auto-swarm** — let the main agent spawn background sub-agents in parallel via a built-in `swarm_spawn` tool. Off by default. When on, the tool is registered with the running agent, the system prompt gains a short addendum telling the model to delegate independent sub-tasks proactively, and zot watches every sub-agent the main agent spawns. As soon as the last sub-agent in a batch finishes its initial task, an `[auto-swarm update]` message is injected back into the chat with each agent's status / task / transcript tail, so the main agent can summarise the collective outcome. Flipping off mid-session removes the tool from the live agent and strips the addendum on the next turn — the model stops trying to delegate. See `/swarm` for the dashboard that lets you monitor, message, kill, or remove the spawned agents.
404404 - **auto-compact threshold** — choose `off`, `70%`, `80%`, `85%` (default), or `90%` of the model's advertised context window. The selected percentage controls automatic compaction before and after interactive turns and persists as `auto_compact_threshold`. `off` disables percentage-based triggers but keeps manual `/compact` and automatic recovery from context-window and payload-too-large responses.
405405 - **jail new sessions by default**: start every new agent with tools confined to its working directory. Off by default. The setting applies to interactive, print, JSON, RPC, and background-agent runs, persists as `jail_by_default`, and immediately updates the current interactive session. `/jail` and `/unjail` remain session-scoped overrides and do not change this default.
406-- **OpenRouter Server Tools**: opt in to OpenRouter's beta web search, web fetch, advisor, subagent, fusion, image generation, datetime, and model-search tools. Off by default and available only with the OpenRouter provider. zot limits the remote loop to four server-tool calls per turn. `--no-tools` always disables them. Presets keep their own tool configuration and are not overridden by this setting. Server tools run remotely without zot's local tool confirmation dialog and may incur additional OpenRouter charges.
406+- **OpenRouter Server Tools**: opt in to OpenRouter's beta web search, web fetch, advisor, subagent, fusion, image generation, datetime, and model-search tools. Off by default and available only with the OpenRouter provider. zot limits the remote loop to four server-tool calls per turn (adjustable via `openrouter_server_tool_call_limit` in `config.json`). `--no-tools` always disables them. Presets keep their own tool configuration and are not overridden by this setting. Server tools run remotely without zot's local tool confirmation dialog and may incur additional OpenRouter charges.
407+
408+ **config.json example:**
409+
410+ ```json
411+ {
412+ "openrouter_server_tools_enabled": true,
413+ "openrouter_server_tool_call_limit": 4
414+ }
415+ ```
416+
407417 - **compact transcript rendering**: reduce visual chrome in the chat transcript. Tool calls render as a quiet header plus indented output instead of a bordered box, and sent messages render without padded background bubbles. Off by default. Changes apply immediately and persist to `config.json` as `compact_mode`.
408418 - **show loaded resources at startup**: list the active Zotfile agent (for `zot run`), loaded `AGENTS.md` paths, extensions, and user-installed skills in compact sections above the transcript. Built-in skills are omitted. Off by default. Changes apply immediately and persist to `config.json` as `show_instructions_at_startup`.
409419 - **TUI settings**: opens a sub-view for input layout and status placement. **Input style** can be `plain` (default prompt line), `lines` (separator lines above and below the input), or `block` (a user-bubble-style input block). **Status position** places model, usage, and working-directory information above or below the input. **Working spinner position** places the busy spinner above or below the input. Changes apply immediately and persist to `config.json` as `tui_input_style`, `tui_status_position`, and `tui_working_position` (`above_input` or `below_input` for the position fields).
@@ -403,7 +403,17 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o
403 - **auto-swarm** — let the main agent spawn background sub-agents in parallel via a built-in `swarm_spawn` tool. Off by default. When on, the tool is registered with the running agent, the system prompt gains a short addendum telling the model to delegate independent sub-tasks proactively, and zot watches every sub-agent the main agent spawns. As soon as the last sub-agent in a batch finishes its initial task, an `[auto-swarm update]` message is injected back into the chat with each agent's status / task / transcript tail, so the main agent can summarise the collective outcome. Flipping off mid-session removes the tool from the live agent and strips the addendum on the next turn — the model stops trying to delegate. See `/swarm` for the dashboard that lets you monitor, message, kill, or remove the spawned agents.403 - **auto-swarm** — let the main agent spawn background sub-agents in parallel via a built-in `swarm_spawn` tool. Off by default. When on, the tool is registered with the running agent, the system prompt gains a short addendum telling the model to delegate independent sub-tasks proactively, and zot watches every sub-agent the main agent spawns. As soon as the last sub-agent in a batch finishes its initial task, an `[auto-swarm update]` message is injected back into the chat with each agent's status / task / transcript tail, so the main agent can summarise the collective outcome. Flipping off mid-session removes the tool from the live agent and strips the addendum on the next turn — the model stops trying to delegate. See `/swarm` for the dashboard that lets you monitor, message, kill, or remove the spawned agents.
404 - **auto-compact threshold** — choose `off`, `70%`, `80%`, `85%` (default), or `90%` of the model's advertised context window. The selected percentage controls automatic compaction before and after interactive turns and persists as `auto_compact_threshold`. `off` disables percentage-based triggers but keeps manual `/compact` and automatic recovery from context-window and payload-too-large responses.404 - **auto-compact threshold** — choose `off`, `70%`, `80%`, `85%` (default), or `90%` of the model's advertised context window. The selected percentage controls automatic compaction before and after interactive turns and persists as `auto_compact_threshold`. `off` disables percentage-based triggers but keeps manual `/compact` and automatic recovery from context-window and payload-too-large responses.
405 - **jail new sessions by default**: start every new agent with tools confined to its working directory. Off by default. The setting applies to interactive, print, JSON, RPC, and background-agent runs, persists as `jail_by_default`, and immediately updates the current interactive session. `/jail` and `/unjail` remain session-scoped overrides and do not change this default.405 - **jail new sessions by default**: start every new agent with tools confined to its working directory. Off by default. The setting applies to interactive, print, JSON, RPC, and background-agent runs, persists as `jail_by_default`, and immediately updates the current interactive session. `/jail` and `/unjail` remain session-scoped overrides and do not change this default.
406-- **OpenRouter Server Tools**: opt in to OpenRouter's beta web search, web fetch, advisor, subagent, fusion, image generation, datetime, and model-search tools. Off by default and available only with the OpenRouter provider. zot limits the remote loop to four server-tool calls per turn. `--no-tools` always disables them. Presets keep their own tool configuration and are not overridden by this setting. Server tools run remotely without zot's local tool confirmation dialog and may incur additional OpenRouter charges.406+- **OpenRouter Server Tools**: opt in to OpenRouter's beta web search, web fetch, advisor, subagent, fusion, image generation, datetime, and model-search tools. Off by default and available only with the OpenRouter provider. zot limits the remote loop to four server-tool calls per turn (adjustable via `openrouter_server_tool_call_limit` in `config.json`). `--no-tools` always disables them. Presets keep their own tool configuration and are not overridden by this setting. Server tools run remotely without zot's local tool confirmation dialog and may incur additional OpenRouter charges.
407+
408+ **config.json example:**
409+
410+ ```json
411+ {
412+ "openrouter_server_tools_enabled": true,
413+ "openrouter_server_tool_call_limit": 4
414+ }
415+ ```
416+
407 - **compact transcript rendering**: reduce visual chrome in the chat transcript. Tool calls render as a quiet header plus indented output instead of a bordered box, and sent messages render without padded background bubbles. Off by default. Changes apply immediately and persist to `config.json` as `compact_mode`.417 - **compact transcript rendering**: reduce visual chrome in the chat transcript. Tool calls render as a quiet header plus indented output instead of a bordered box, and sent messages render without padded background bubbles. Off by default. Changes apply immediately and persist to `config.json` as `compact_mode`.
408 - **show loaded resources at startup**: list the active Zotfile agent (for `zot run`), loaded `AGENTS.md` paths, extensions, and user-installed skills in compact sections above the transcript. Built-in skills are omitted. Off by default. Changes apply immediately and persist to `config.json` as `show_instructions_at_startup`.418 - **show loaded resources at startup**: list the active Zotfile agent (for `zot run`), loaded `AGENTS.md` paths, extensions, and user-installed skills in compact sections above the transcript. Built-in skills are omitted. Off by default. Changes apply immediately and persist to `config.json` as `show_instructions_at_startup`.
409 - **TUI settings**: opens a sub-view for input layout and status placement. **Input style** can be `plain` (default prompt line), `lines` (separator lines above and below the input), or `block` (a user-bubble-style input block). **Status position** places model, usage, and working-directory information above or below the input. **Working spinner position** places the busy spinner above or below the input. Changes apply immediately and persist to `config.json` as `tui_input_style`, `tui_status_position`, and `tui_working_position` (`above_input` or `below_input` for the position fields).419 - **TUI settings**: opens a sub-view for input layout and status placement. **Input style** can be `plain` (default prompt line), `lines` (separator lines above and below the input), or `block` (a user-bubble-style input block). **Status position** places model, usage, and working-directory information above or below the input. **Working spinner position** places the busy spinner above or below the input. Changes apply immediately and persist to `config.json` as `tui_input_style`, `tui_status_position`, and `tui_working_position` (`above_input` or `below_input` for the position fields).
M packages/agent/build.go +14 -1
@@ -988,11 +988,24 @@ func (r Resolved) NewAgent() *core.Agent {
988988 a.Reasoning = r.Reasoning
989989 a.Temperature = r.Temperature
990990 if hasOpenRouterServerTools(r.ToolRegistry) {
991- a.MaxToolCalls = tools.OpenRouterServerToolCallLimit
991+ a.MaxToolCalls = openRouterServerToolCallLimit()
992992 }
993993 return a
994994 }
995995
996+// openRouterServerToolCallLimit returns the configured call limit from
997+// config.json, falling back to the built-in default (4).
998+func openRouterServerToolCallLimit() int {
999+ cfg, err := LoadConfig()
1000+ if err != nil {
1001+ return tools.OpenRouterServerToolCallLimit
1002+ }
1003+ if cfg.OpenRouterServerToolCallLimit != nil && *cfg.OpenRouterServerToolCallLimit > 0 {
1004+ return *cfg.OpenRouterServerToolCallLimit
1005+ }
1006+ return tools.OpenRouterServerToolCallLimit
1007+}
1008+
9961009 // OpenRouterServerToolsEnabled reports the persisted preference.
9971010 // nil/missing and unreadable configuration fail closed to disabled.
9981011 func OpenRouterServerToolsEnabled() bool {
@@ -988,11 +988,24 @@ func (r Resolved) NewAgent() *core.Agent {
988 a.Reasoning = r.Reasoning988 a.Reasoning = r.Reasoning
989 a.Temperature = r.Temperature989 a.Temperature = r.Temperature
990 if hasOpenRouterServerTools(r.ToolRegistry) {990 if hasOpenRouterServerTools(r.ToolRegistry) {
991- a.MaxToolCalls = tools.OpenRouterServerToolCallLimit991+ a.MaxToolCalls = openRouterServerToolCallLimit()
992 }992 }
993 return a993 return a
994 }994 }
995 995
996+// openRouterServerToolCallLimit returns the configured call limit from
997+// config.json, falling back to the built-in default (4).
998+func openRouterServerToolCallLimit() int {
999+ cfg, err := LoadConfig()
1000+ if err != nil {
1001+ return tools.OpenRouterServerToolCallLimit
1002+ }
1003+ if cfg.OpenRouterServerToolCallLimit != nil && *cfg.OpenRouterServerToolCallLimit > 0 {
1004+ return *cfg.OpenRouterServerToolCallLimit
1005+ }
1006+ return tools.OpenRouterServerToolCallLimit
1007+}
1008+
996 // OpenRouterServerToolsEnabled reports the persisted preference.1009 // OpenRouterServerToolsEnabled reports the persisted preference.
997 // nil/missing and unreadable configuration fail closed to disabled.1010 // nil/missing and unreadable configuration fail closed to disabled.
998 func OpenRouterServerToolsEnabled() bool {1011 func OpenRouterServerToolsEnabled() bool {
M packages/agent/cli.go +1 -0
@@ -1271,6 +1271,7 @@ func runInteractive(ctx context.Context, args Args, version string) error {
12711271 AutoCompactThreshold: initialCfg.AutoCompactThreshold,
12721272 JailByDefault: initialCfg.JailByDefault,
12731273 OpenRouterServerToolsEnabled: initialCfg.OpenRouterServerToolsEnabled,
1274+ OpenRouterServerToolCallLimit: initialCfg.OpenRouterServerToolCallLimit,
12741275 NoTools: args.NoTools,
12751276 QuickModelShortcuts: quickModelShortcuts,
12761277 RecursiveFileSuggest: initialCfg.RecursiveFileSuggest,
@@ -1271,6 +1271,7 @@ func runInteractive(ctx context.Context, args Args, version string) error {
1271 AutoCompactThreshold: initialCfg.AutoCompactThreshold,1271 AutoCompactThreshold: initialCfg.AutoCompactThreshold,
1272 JailByDefault: initialCfg.JailByDefault,1272 JailByDefault: initialCfg.JailByDefault,
1273 OpenRouterServerToolsEnabled: initialCfg.OpenRouterServerToolsEnabled,1273 OpenRouterServerToolsEnabled: initialCfg.OpenRouterServerToolsEnabled,
1274+ OpenRouterServerToolCallLimit: initialCfg.OpenRouterServerToolCallLimit,
1274 NoTools: args.NoTools,1275 NoTools: args.NoTools,
1275 QuickModelShortcuts: quickModelShortcuts,1276 QuickModelShortcuts: quickModelShortcuts,
1276 RecursiveFileSuggest: initialCfg.RecursiveFileSuggest,1277 RecursiveFileSuggest: initialCfg.RecursiveFileSuggest,
M packages/agent/config.go +4 -0
@@ -73,6 +73,10 @@ type Config struct {
7373 // Default off: users explicitly opt in from /settings.
7474 OpenRouterServerToolsEnabled *bool `json:"openrouter_server_tools_enabled,omitempty"`
7575
76+ // OpenRouterServerToolCallLimit caps OpenRouter server-tool calls per turn
77+ // when server tools are enabled. nil/missing uses the built-in default (4).
78+ OpenRouterServerToolCallLimit *int `json:"openrouter_server_tool_call_limit,omitempty"`
79+
7680 // RecursiveFileSuggest controls the @-mention file picker. When true
7781 // the picker fuzzy-searches the whole project tree below the working
7882 // directory; nil/missing/false keeps the default directory-by-
@@ -73,6 +73,10 @@ type Config struct {
73 // Default off: users explicitly opt in from /settings.73 // Default off: users explicitly opt in from /settings.
74 OpenRouterServerToolsEnabled *bool `json:"openrouter_server_tools_enabled,omitempty"`74 OpenRouterServerToolsEnabled *bool `json:"openrouter_server_tools_enabled,omitempty"`
75 75
76+ // OpenRouterServerToolCallLimit caps OpenRouter server-tool calls per turn
77+ // when server tools are enabled. nil/missing uses the built-in default (4).
78+ OpenRouterServerToolCallLimit *int `json:"openrouter_server_tool_call_limit,omitempty"`
79+
76 // RecursiveFileSuggest controls the @-mention file picker. When true80 // RecursiveFileSuggest controls the @-mention file picker. When true
77 // the picker fuzzy-searches the whole project tree below the working81 // the picker fuzzy-searches the whole project tree below the working
78 // directory; nil/missing/false keeps the default directory-by-82 // directory; nil/missing/false keeps the default directory-by-
M packages/agent/modes/interactive.go +5 -1
@@ -76,6 +76,10 @@ type InteractiveConfig struct {
7676 // nil means the default, off.
7777 OpenRouterServerToolsEnabled *bool
7878
79+ // OpenRouterServerToolCallLimit mirrors the persisted call limit.
80+ // nil means the built-in default (4).
81+ OpenRouterServerToolCallLimit *int
82+
7983 // NoTools preserves the launch-time promise that all tools stay disabled.
8084 NoTools bool
8185
@@ -6680,7 +6684,7 @@ func (i *Interactive) applyOpenRouterServerTools(active bool) {
66806684 for _, t := range tools.OpenRouterServerTools() {
66816685 next[t.Name()] = t
66826686 }
6683- i.agent.MaxToolCalls = tools.OpenRouterServerToolCallLimit
6687+ i.agent.MaxToolCalls = tools.ResolveToolCallLimit(i.cfg.OpenRouterServerToolCallLimit)
66846688 }
66856689 i.agent.SetTools(next)
66866690 }
@@ -76,6 +76,10 @@ type InteractiveConfig struct {
76 // nil means the default, off.76 // nil means the default, off.
77 OpenRouterServerToolsEnabled *bool77 OpenRouterServerToolsEnabled *bool
78 78
79+ // OpenRouterServerToolCallLimit mirrors the persisted call limit.
80+ // nil means the built-in default (4).
81+ OpenRouterServerToolCallLimit *int
82+
79 // NoTools preserves the launch-time promise that all tools stay disabled.83 // NoTools preserves the launch-time promise that all tools stay disabled.
80 NoTools bool84 NoTools bool
81 85
@@ -6680,7 +6684,7 @@ func (i *Interactive) applyOpenRouterServerTools(active bool) {
6680 for _, t := range tools.OpenRouterServerTools() {6684 for _, t := range tools.OpenRouterServerTools() {
6681 next[t.Name()] = t6685 next[t.Name()] = t
6682 }6686 }
6683- i.agent.MaxToolCalls = tools.OpenRouterServerToolCallLimit6687+ i.agent.MaxToolCalls = tools.ResolveToolCallLimit(i.cfg.OpenRouterServerToolCallLimit)
6684 }6688 }
6685 i.agent.SetTools(next)6689 i.agent.SetTools(next)
6686 }6690 }
M packages/agent/tools/openrouter_server.go +10 -0
@@ -13,6 +13,16 @@ import (
1313 // advertises OpenRouter server tools.
1414 const OpenRouterServerToolCallLimit = 4
1515
16+// ResolveToolCallLimit returns n when it is a positive int, otherwise the
17+// built-in default. This lets callers resolve a config.json *int without
18+// importing the agent config package.
19+func ResolveToolCallLimit(n *int) int {
20+ if n != nil && *n > 0 {
21+ return *n
22+ }
23+ return OpenRouterServerToolCallLimit
24+}
25+
1626 // OpenRouterServerToolNames lists every OpenRouter server tool that zot
1727 // can advertise on Chat Completions. Shell, bash, apply_patch, and
1828 // tool_search require Responses/Messages and are omitted here.
@@ -13,6 +13,16 @@ import (
13 // advertises OpenRouter server tools.13 // advertises OpenRouter server tools.
14 const OpenRouterServerToolCallLimit = 414 const OpenRouterServerToolCallLimit = 4
15 15
16+// ResolveToolCallLimit returns n when it is a positive int, otherwise the
17+// built-in default. This lets callers resolve a config.json *int without
18+// importing the agent config package.
19+func ResolveToolCallLimit(n *int) int {
20+ if n != nil && *n > 0 {
21+ return *n
22+ }
23+ return OpenRouterServerToolCallLimit
24+}
25+
16 // OpenRouterServerToolNames lists every OpenRouter server tool that zot26 // OpenRouterServerToolNames lists every OpenRouter server tool that zot
17 // can advertise on Chat Completions. Shell, bash, apply_patch, and27 // can advertise on Chat Completions. Shell, bash, apply_patch, and
18 // tool_search require Responses/Messages and are omitted here.28 // tool_search require Responses/Messages and are omitted here.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close