mermaid_cli/constants.rs
1//! Constants module to avoid magic numbers in the codebase
2
3// Network Configuration
4pub const DEFAULT_OLLAMA_PORT: u16 = 11434;
5
6// Timeouts
7pub const COMMAND_TIMEOUT_SECS: u64 = 30;
8pub const COMMAND_MAX_TIMEOUT_SECS: u64 = 300;
9
10// UI Configuration
11pub const UI_MOUSE_SCROLL_LINES: u16 = 3;
12/// How long a first Ctrl+C keeps exit armed: a second press inside this
13/// window exits, after it the press re-arms instead. Long enough to read the
14/// "press ctrl+c again to exit" hint, short enough that a stray press doesn't
15/// leave the app one keystroke from quitting minutes later.
16pub const UI_EXIT_CONFIRM_WINDOW_SECS: i64 = 3;
17
18// Default Model Configuration
19pub const DEFAULT_TEMPERATURE: f32 = 0.7;
20/// The pre-AUTO default output cap (the config default was 4096 until the
21/// model-scaled budget landed; `0` = AUTO is the default now). Kept ONLY so
22/// config loading can recognize the frozen legacy value on disk and migrate it
23/// to AUTO — never used to cap a request.
24pub const LEGACY_DEFAULT_MAX_TOKENS: usize = 4096;
25/// Meta's documented Muse Spark context and per-response output ceilings.
26/// Static-but-documented like the OpenAI gpt catalog rows: Meta's `/v1/models`
27/// exposes no limit metadata (Model schema is id/object/created/owned_by/
28/// metadata — verified against the API reference 2026-07-09), so live limits
29/// discovery has nothing to read. Consumed by the muse-spark catalog row
30/// (context) and `MetaProvider` capabilities (both).
31pub const META_MUSE_SPARK_CONTEXT_WINDOW: usize = 1_048_576;
32pub const META_MUSE_SPARK_MAX_OUTPUT_TOKENS: usize = 131_072;
33
34// Context Management
35/// Auto-compact once the fully-enriched request reaches this percentage
36/// of the model's known context window.
37pub const COMPACTION_AUTO_THRESHOLD_PERCENT: u8 = 85;
38/// Default number of recent user turns preserved verbatim after compaction.
39pub const COMPACTION_TAIL_TURNS: usize = 2;
40/// Maximum estimated tokens to preserve as the recent tail.
41pub const COMPACTION_TAIL_TOKEN_BUDGET: usize = 8_000;
42/// Maximum characters of old tool output included in the summarization prompt.
43pub const COMPACTION_TOOL_OUTPUT_MAX_CHARS: usize = 2_000;
44/// Maximum tokens requested from the compaction summarizer.
45pub const COMPACTION_SUMMARY_MAX_TOKENS: usize = 8_000;
46/// Maximum estimated input tokens sent to the summarizer.
47pub const COMPACTION_SUMMARIZER_INPUT_TOKEN_BUDGET: usize = 64_000;
48/// Minimum response reserve when deciding whether the next request fits.
49pub const COMPACTION_MIN_RESPONSE_RESERVE_TOKENS: usize = 4_000;
50/// Maximum response reserve when deciding whether the next request fits.
51pub const COMPACTION_MAX_RESPONSE_RESERVE_TOKENS: usize = 20_000;
52/// Default cap on consecutive auto-compact-and-continue recoveries after a
53/// context-window truncation, before the run stops and shows the manual levers.
54/// The counter resets whenever the run makes progress; `0` means uncapped.
55pub const COMPACTION_MAX_TRUNCATION_RECOVERIES: u8 = 3;
56/// Cap on consecutive auto-continuations after a response hits the provider's
57/// per-response OUTPUT cap (window room to spare). Each continuation resumes
58/// the reply in a fresh turn; the cap bounds a model that restarts or
59/// re-truncates instead of finishing. Reset whenever a turn ends another way.
60pub const MAX_OUTPUT_CONTINUATIONS: u32 = 4;
61
62// Ollama auto-sizing
63// Mermaid probes an Ollama model's real context window (`/api/show`) and sizes
64// `num_ctx`/`num_predict` automatically so users never touch Ollama config. See
65// `src/models/adapters/ollama_sizing.rs`.
66/// Conservative `num_ctx` used when memory can't be detected (and as the auto
67/// fallback). Comfortably above the compaction response reserve so auto-compaction
68/// stays sane on the smaller probed window.
69pub const DEFAULT_OLLAMA_MAX_AUTO_NUM_CTX: usize = 32_768;
70/// Floor for the auto-fit `num_ctx` (never applied above the model's own max).
71/// Equal to Ollama's own default so flooring is never worse than today.
72pub const OLLAMA_MIN_AUTO_NUM_CTX: usize = 4_096;
73/// Auto-fit `num_ctx` is rounded down to a multiple of this for clean values.
74pub const OLLAMA_NUM_CTX_ROUNDING: usize = 1_024;
75/// Bytes per KV-cache element. fp16 (2 bytes); KV-cache quantization is not
76/// modeled yet (a later refinement).
77pub const OLLAMA_KV_DTYPE_BYTES: usize = 2;
78/// Fraction of the memory budget (VRAM, or system RAM when offload is allowed)
79/// usable for model weights + KV cache; the remainder is headroom for compute
80/// buffers and other processes.
81pub const OLLAMA_MEMORY_BUDGET_FRACTION: f64 = 0.85;
82/// Floor for `num_predict` so a small `num_ctx` can't starve the answer.
83pub const OLLAMA_MIN_NUM_PREDICT: usize = 512;
84/// Tokens held back from `num_ctx` when capping `num_predict`, so the prompt +
85/// output estimate doesn't bump exactly against the window.
86pub const OLLAMA_NUM_PREDICT_MARGIN: usize = 256;
87/// Wall-clock cap for the best-effort `nvidia-smi` VRAM probe. It returns in
88/// tens of ms normally; a wedged driver must not stall model dispatch.
89pub const NVIDIA_SMI_TIMEOUT_SECS: u64 = 3;
90/// Per-request timeout for the `/api/show` + `/api/tags` capability probe. The
91/// chat client has no global timeout (streaming), so the probe sets its own so a
92/// slow/hung Ollama never stalls the turn.
93pub const OLLAMA_PROBE_TIMEOUT_SECS: u64 = 3;
94/// How long a cached `provider_probes` row stays valid — shared by the Ollama
95/// `/api/show` probe and the per-provider limits probes (`limits_probe`).
96/// Model dimensions are static per id; the TTL just lets re-pulled/updated
97/// models refresh.
98pub const PROVIDER_PROBE_TTL_DAYS: i64 = 30;
99
100// Web Content
101/// Maximum characters to keep when truncating fetched web content
102pub const WEB_CONTENT_MAX_CHARS: usize = 5_000;
103
104/// Byte-exact aggregate cap on the complete formatted output of `web_search`.
105/// Per-result content is already truncated to `WEB_CONTENT_MAX_CHARS`, so
106/// at the default 5 results this caps the total at ~25 KB plus headers and
107/// the sources block. The aggregate cap protects against many-results-of-
108/// medium-size cases where individual truncation alone isn't enough.
109pub const WEB_SEARCH_AGGREGATE_MAX_BYTES: usize = 30_000;
110
111/// Maximum characters allowed in the streaming response buffer.
112/// Prevents unbounded memory growth from runaway model responses.
113pub const MAX_RESPONSE_CHARS: usize = 400_000;
114
115/// Largest file `apply_patch` will read to apply a patch against. Deliberately
116/// well above `MAX_RESPONSE_CHARS` (which caps model-visible output) so a
117/// legitimately large source file stays patchable; a file past this is refused
118/// rather than patched from a partial read.
119pub const MAX_PATCH_FILE_BYTES: usize = 5 * 1024 * 1024;
120
121// Tool execution limits
122/// Maximum bytes of combined stdout/stderr captured from a single
123/// `execute_command` invocation. Past this the capture stops and a
124/// truncation marker is appended — prevents a chatty or newline-less
125/// command (`cat /dev/urandom`, `yes`) from exhausting memory.
126pub const MAX_TOOL_OUTPUT_BYTES: usize = 256 * 1024;
127/// Upper bound on the number of parallel tool calls a single streaming model
128/// response may accumulate. A delta whose `index` exceeds this is dropped
129/// rather than used to grow an allocation proportional to an untrusted
130/// integer (guards against a crafted stream OOM-ing the daemon).
131pub const MAX_TOOL_CALLS: usize = 256;
132
133// Bound-before-allocate frame caps (Cause 2)
134// Every one of these guards a buffer whose size is driven by an untrusted
135// peer (an MCP server, a daemon client, a model provider). They're sized
136// generously — well above any legitimate payload — because their only job is
137// to stop a peer that streams bytes *without* a delimiter from growing a
138// buffer without bound. A legitimate large payload is delimited and so is
139// never anywhere near these.
140/// Max bytes in a single MCP JSON-RPC line before the frame is dropped and the
141/// reader resyncs to the next newline. MCP tool results can be large (a server
142/// returning a file), hence the generous ceiling.
143pub const MAX_MCP_FRAME_BYTES: usize = 16 * 1024 * 1024;
144/// Max bytes in a single daemon control-command line. Commands are short JSON
145/// control messages; anything larger is malformed or hostile.
146pub const MAX_DAEMON_COMMAND_BYTES: usize = 1024 * 1024;
147/// Wall-clock ceiling on a single daemon control connection. A client that
148/// opens a connection and never sends a complete command line would otherwise
149/// park the handler task (and hold its fd) forever; bound it so a slow or stuck
150/// peer can't leak connections toward fd exhaustion.
151pub const DAEMON_CONNECTION_TIMEOUT_SECS: u64 = 30;
152/// Max bytes accumulated in an SSE reassembly buffer without a complete event
153/// boundary. A provider that streams bytes but never emits the `\n\n` event
154/// separator would otherwise grow the buffer unbounded.
155pub const MAX_SSE_BUFFER_BYTES: usize = 8 * 1024 * 1024;
156/// Max bytes buffered for one streaming tool call's arguments. Tool arguments
157/// (e.g. a file's contents for a `write_file`) can be large, so this is
158/// generous; past it we stop appending so a crafted stream can't grow the
159/// buffer without bound.
160pub const MAX_TOOL_ARG_BYTES: usize = 4 * 1024 * 1024;
161/// Max number of `queries[]` honored in a single `web_search` call, and of
162/// `paths[]` in a single `read_file` call. Bounds the fan-out a single tool
163/// call can request.
164pub const MAX_BATCH_TOOL_ITEMS: usize = 32;
165/// Max bytes read from an Ollama `web_search`/`web_fetch` HTTP response body
166/// before the read is aborted. The body is JSON we fully buffer to parse; this
167/// stops a compromised or misconfigured endpoint from returning a multi-GB
168/// body that `Response::json` would buffer unbounded.
169pub const MAX_WEB_BODY_BYTES: usize = 16 * 1024 * 1024;
170/// Aggregate decoded web bytes accepted across all calls in one model turn.
171pub const MAX_WEB_TURN_BYTES: usize = 64 * 1024 * 1024;
172/// Maximum web response downloads in flight process-wide across all backends.
173pub const MAX_WEB_DOWNLOAD_CONCURRENCY: usize = 8;
174/// Maximum web response downloads in flight for one transport origin.
175pub const MAX_WEB_PER_ORIGIN_CONCURRENCY: usize = 2;
176/// Maximum CPU-heavy extraction/snapshot jobs in flight process-wide.
177pub const MAX_WEB_EXTRACTION_CONCURRENCY: usize = 2;
178/// Maximum concurrent queries inside one batched web_search call.
179pub const MAX_WEB_SEARCH_CONCURRENCY: usize = 4;
180
181// UI Cache
182/// Maximum entries in the markdown parse cache before eviction
183pub const MARKDOWN_CACHE_MAX_ENTRIES: usize = 200;
184
185// Computer Use
186/// Maximum width for screenshots sent to models (pixels)
187pub const SCREENSHOT_MAX_WIDTH: u32 = 1280;
188
189// Computer-use timing — empirically tuned for typical GUI response.
190// Slow systems (high-load WMs, remote X displays) may need higher values;
191// the right place to make these tunable later is via env vars on
192// `app::Config`, alongside the rest of the configurable surface.
193/// Delay after `xdotool windowactivate --sync` so the window manager has
194/// time to actually move focus before the next action.
195pub const WINDOW_FOCUS_DELAY_MS: u64 = 200;
196/// Delay after a click for the window manager to process focus + UI update,
197/// before we take the auto-screenshot the model uses to decide its next move.
198pub const POST_CLICK_DELAY_MS: u64 = 500;
199/// Delay after typing for the target application to settle (validate input,
200/// re-render) before the auto-screenshot.
201pub const POST_TYPE_DELAY_MS: u64 = 500;
202/// Delay after a key press / shortcut for the target app to react before
203/// the auto-screenshot.
204pub const POST_KEY_DELAY_MS: u64 = 500;
205/// Delay between simulated keystrokes when typing text. The previous
206/// 12ms default lost characters on slow Electron / web targets; 25ms
207/// is a safer default that still types ~40 chars/sec.
208pub const TYPE_KEY_DELAY_MS: u64 = 25;
209/// Maximum scroll wheel ticks in a single `scroll` call. Clamps the
210/// model's `amount` parameter so a runaway model can't request a
211/// million ticks (would exceed `ARG_MAX` building xdotool argv).
212pub const MAX_SCROLL_AMOUNT: i32 = 100;
213/// Capacity of the per-screenshot metadata ring buffer in
214/// `computer_use`. Each call to `screenshot` registers metadata under
215/// a new id; older entries are LRU-evicted past this cap. Sized to
216/// cover any realistic agentic loop without unbounded growth.
217pub const SCREENSHOT_REGISTRY_CAPACITY: usize = 16;
218/// Maximum number of screenshot images retained in the per-call
219/// message history sent to the model. Older messages keep their text
220/// content (with a placeholder noting where the image was elided) so
221/// the model knows what was dropped from context.
222pub const MAX_RETAINED_SCREENSHOTS: usize = 3;
223/// Per-command wall-clock cap for the synchronous geometry/probe helpers
224/// (xdotool getactivewindow/getwindowgeometry, xrandr --query, the list_windows
225/// search). These return in tens of ms; a wedged display (dead X socket,
226/// detached SSH) must not hang the agent loop (#97).
227pub const COMPUTER_USE_CMD_TIMEOUT_SECS: u64 = 5;
228/// Wall-clock cap for the screenshot downscale step (ImageMagick `convert` /
229/// `ffmpeg`). Larger than the probe cap — re-encoding a 4K PNG is legitimately
230/// slower than a geometry query. On timeout we fall through to the next encoder,
231/// then to sending the full-resolution capture (#97).
232pub const SCREENSHOT_DOWNSCALE_TIMEOUT_SECS: u64 = 15;
233
234// Project instructions (Step 5h)
235/// Maximum bytes loaded from project instruction files before truncation. ~10k
236/// tokens at 4 chars/token. Files larger than this likely have
237/// repository-wide notes that don't all need to live in the system
238/// prompt; truncate with a marker so the user knows.
239pub const MAX_INSTRUCTIONS_BYTES: usize = 40_000;
240/// Marker appended to project-instruction content when it exceeds the
241/// byte cap. The model sees this so it knows context was elided.
242pub const INSTRUCTIONS_TRUNCATION_MARKER: &str =
243 "\n\n[Project instructions truncated - exceeds 10k token cap]";
244
245// Durable semantic memory (v0.10.0)
246/// Max bytes of the always-loaded memory INDEX (name + description + path per
247/// fact, all scopes). ~2k tokens at 4 chars/token. The index is terse; if it
248/// overflows, that's a signal to run `/consolidate-memory`.
249pub const MAX_MEMORY_INDEX_BYTES: usize = 8_000;
250/// Marker appended to the memory index when it exceeds the byte cap.
251pub const MEMORY_INDEX_TRUNCATION_MARKER: &str =
252 "\n\n[Memory index truncated - too many entries; run /consolidate-memory]";