@octocodeai/freellama-mcp-server 0.1.0 → 0.6.0

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.
@@ -1,25 +1,59 @@
1
1
  # FreeLlama architecture
2
2
 
3
- FreeLlama is a localhost control plane in front of Ollama. Ollama owns model storage, model loading,
4
- scheduling, inference engines, and token generation. FreeLlama owns model-aware policy around those
5
- operations.
3
+ FreeLlama is the management layer between an AI agent and local Ollama models. Agents offload
4
+ bounded tasks through MCP, the CLI, or the control API. FreeLlama selects eligible models,
5
+ coordinates resources, and returns results with execution evidence.
6
+
7
+ The calling agent owns task decomposition, prompts, and answer verification. FreeLlama owns managed
8
+ admission, memory checks, residency coordination, and routing within operator-configured CPU/GPU
9
+ backends. Ollama owns model storage, runner loading, inference engines, and token generation;
10
+ the operating system and drivers schedule physical work.
6
11
 
7
12
  ## Responsibility boundary
8
13
 
9
- The following table defines the ownership boundary:
14
+ This is the canonical ownership rule; other guides link here.
10
15
 
11
16
  | Responsibility | Owner |
12
17
  |---|---|
18
+ | Goal decomposition, prompts, system messages, task dependencies, and answer verification | Calling agent |
19
+ | Ollama processes and endpoints, exact `--cpu-model` assignments, runtime settings, and approval of pulls, stops, and deletes | Operator |
20
+ | Prompt templates and model defaults | Ollama model metadata or a `Modelfile` |
21
+ | Physical CPU/GPU scheduling and memory | OS, drivers, and accelerator runtime |
13
22
  | Model files, runners, Metal, MLX, llama.cpp, and token generation | Ollama |
14
23
  | Native `/api/*` and OpenAI-compatible `/v1/*` semantics | Ollama |
15
24
  | Installed-model discovery and capability normalization | FreeLlama |
16
- | Task routing, advertised-context compatibility, K/V lower-bound preflight, evidence policy, and model rejection reasons | FreeLlama |
17
- | Managed admission, session affinity, and model-transition coordination | FreeLlama |
25
+ | Task routing, advertised-context compatibility, advisory K/V estimates, evidence policy, and model rejection reasons | FreeLlama |
26
+ | Managed admission, session affinity, bounded scope history, and model-transition coordination | FreeLlama |
27
+ | Forecast memory reservations, host-pressure holds, and requests to unload idle unpinned models | FreeLlama managed execution |
18
28
  | Explicit per-model CPU/GPU backend assignment | FreeLlama and separate Ollama processes |
19
29
  | Grounded research adapters, citations, and verification verdicts | FreeLlama MCP server |
30
+ | Bearer authentication of every route | FreeLlama; external ingress owns TLS and tenant authorization |
31
+
32
+ FreeLlama is a governed router, not an unrestricted hardware scheduler: a CPU preference cannot move
33
+ an arbitrary tag, and one managed call contains one task. Only the calling agent knows which tasks
34
+ are independent.
35
+
36
+ ### Ollama sidecar
37
+
38
+ FreeLlama is a loopback sidecar, not an Ollama fork. It talks to Ollama over its documented HTTP
39
+ contract, which also makes a second, process-isolated CPU backend possible without changing client
40
+ payloads:
41
+
42
+ ```mermaid
43
+ flowchart LR
44
+ C["Ollama-compatible client"] --> F["FreeLlama loopback sidecar"]
45
+ F -->|"raw /api and /v1"| G["Primary Ollama"]
46
+ F -->|"managed primary task"| G
47
+ F -->|"managed assigned task"| U["Optional CPU Ollama"]
48
+ G --> E["Metal, MLX, CUDA, ROCm, Vulkan, or CPU runner"]
49
+ U --> E2["CPU runner"]
50
+ ```
20
51
 
21
- FreeLlama remains a sidecar rather than an Ollama fork. For the compatibility rationale, see the
22
- [Ollama sidecar boundary](OLLAMA_SIDECAR.md).
52
+ FreeLlama does not make an individual model decode faster. It can improve workload completion by
53
+ avoiding unsuitable routes, reducing avoidable transitions, refusing unsupported work early, and
54
+ overlapping independent CPU and GPU model requests when the machine benefits from that layout.
55
+ Sources: [Ollama API](https://docs.ollama.com/api/introduction),
56
+ [streaming](https://docs.ollama.com/api/streaming), [usage](https://docs.ollama.com/api/usage).
23
57
 
24
58
  ## System topology
25
59
 
@@ -73,10 +107,11 @@ flowchart TD
73
107
  RAW --> PRIMARY["Primary Ollama"]
74
108
  P -->|"Yes"| C{"Control endpoint"}
75
109
  C -->|"health, machine, models"| READ["Read local and Ollama state"]
76
- C -->|"recommendations, routes"| DECIDE["Decide without inference"]
77
- C -->|"natural-routes"| INTENT["Run intent model, then route"]
110
+ C -->|"routes"| DECIDE["Decide without inference"]
78
111
  C -->|"tasks"| RUN["Route, admit, and execute"]
79
112
  C -->|"sessions"| SESSION["Create in-memory affinity id"]
113
+ C -->|"scopes"| SCOPES["Retain revision-protected messages"]
114
+ C -->|"warm"| RUN
80
115
  ```
81
116
 
82
117
  The control API exposes these endpoints:
@@ -86,12 +121,56 @@ The control API exposes these endpoints:
86
121
  | `GET /_freellama/v1/health` | Health, admission capacity, and configured backends | No |
87
122
  | `GET /_freellama/v1/machine` | Machine profile | No |
88
123
  | `GET /_freellama/v1/models` | Normalized installed catalog, evidence, residency, and execution backend | No |
89
- | `POST /_freellama/v1/recommendations` | Installed route or reviewed installation plan | No |
90
124
  | `POST /_freellama/v1/routes` | Deterministic route decision | No |
91
- | `POST /_freellama/v1/natural-routes` | Schema-bound intent interpretation followed by deterministic routing | Intent model only |
92
125
  | `POST /_freellama/v1/sessions` | New model-affinity session | No |
93
126
  | `DELETE /_freellama/v1/sessions/:session_id` | Release model-affinity session | No |
94
- | `POST /_freellama/v1/tasks` | Managed chat, vision, tools, or embedding task | Yes |
127
+ | `POST /_freellama/v1/scopes` | Create bounded message history | No |
128
+ | `GET /_freellama/v1/scopes/:id` | Scope metadata; optional explicit history inclusion | No |
129
+ | `POST /_freellama/v1/scopes/:id/fork` | Fork history at an expected revision | No |
130
+ | `DELETE /_freellama/v1/scopes/:id` | Delete retained history | No |
131
+ | `POST /_freellama/v1/warm` | Admitted load of an exact installed runner profile | Load only |
132
+ | `POST /_freellama/v1/tasks` | Managed chat, vision, tools, or embedding task (body shape under [Request shapes](#request-shapes)) | Yes |
133
+ | `GET /_freellama/v1/jobs` | Deferred job metadata without prompts or results | No |
134
+ | `GET /_freellama/v1/jobs/:id` | One deferred receipt and retained result | No |
135
+ | `POST /_freellama/v1/jobs/:id/cancel` | Cancel one deferred task and release its local permits | No new inference |
136
+ | `DELETE /_freellama/v1/jobs/:id` | Cancel active work, release local permits, and remove its retained record | No new inference |
137
+
138
+ ### Request shapes
139
+
140
+ The control API uses snake_case JSON. A managed task body is the routing fields plus the payload:
141
+
142
+ ```json
143
+ {
144
+ "task": "vision", "objective": "fastest", "model": "my-vision-model",
145
+ "session_id": "<uuid from POST /sessions>", "required_capabilities": ["vision"],
146
+ "prompt": "Text Recognition:", "images": ["<base64, no data-URI prefix>"],
147
+ "priority": "background", "keep_alive": "5m", "max_wait_seconds": 30, "timeout_seconds": 120,
148
+ "stop_on_repetition": true,
149
+ "request_options": { "options": { "temperature": 0, "num_predict": 600 }, "format": "json", "think": false }
150
+ }
151
+ ```
152
+
153
+ `model` is read the way Ollama reads it: an exact installed tag, else `name:latest`, else the one
154
+ installed tag of that family; an unknown or ambiguous name is refused with the installed candidates.
155
+ `messages` (Ollama message objects, images included) replaces `prompt`; `input` serves embeddings;
156
+ `context_tokens` owns `num_ctx` and placement owns `num_gpu`, so neither is accepted inside
157
+ `request_options.options`. `stop_on_repetition: true` streams the chat from Ollama and stops as soon
158
+ as the output repeats its unit twice after the first copy over 96+ characters: the connection
159
+ closes, which cancels generation upstream, and everything generated is kept with
160
+ `done_reason: "repetition"`. An Ollama "token repeat limit" abort is kept the same way
161
+ (`done_reason: "repeat_limit"`). A stopped stream has no Ollama timing fields, so it trains no
162
+ throughput feedback. The receipt holds `route`, `admission` (`queue_wait_ms`,
163
+ `resource_wait_ms`, `transition_wait_ms`, `total_wait_ms`), `execution` (placement, observation,
164
+ `resource_admission`, `context_sizing`, `memory_reservation`), `metrics`, `feedback` and
165
+ `response`, which is Ollama's own answer (`message.content`, `eval_count`, durations).
166
+
167
+ A batch nests whole task bodies:
168
+
169
+ ```json
170
+ { "max_parallelism": 2, "tasks": [ { "id": "item-1", "independent": true, "task": { "task": "vision", "model": "my-vision-model", "prompt": "...", "images": ["..."] } } ] }
171
+ ```
172
+
173
+ `POST /sessions` with an empty object returns `session_id`; `POST /warm` takes `{"model": "<tag>"}`.
95
174
 
96
175
  The machine profile is host-derived rather than model-name-derived. It reports total physical
97
176
  memory on macOS (`sysctl`), Linux (`/proc`), and Windows (system APIs), along with CPU, OS,
@@ -100,7 +179,10 @@ only when host and accelerator memory are known to share a pool; it must not be
100
179
  GPU VRAM. Ollama's `/api/ps` remains the placement authority.
101
180
 
102
181
  All other paths use the compatibility proxy. It buffers request bodies up to 64 MB so a retry can
103
- resend identical bytes, then streams the upstream response. It does not apply CPU model assignment.
182
+ resend identical bytes, then streams the upstream response. It removes hop-by-hop headers, adds
183
+ `x-freellama-proxy: 1`, always targets the primary upstream, and does not apply CPU model
184
+ assignment. Existing Ollama clients keep predictable compatibility; clients that opt into the
185
+ managed API receive routing and policy behavior.
104
186
 
105
187
  ## Model discovery
106
188
 
@@ -114,20 +196,44 @@ flowchart LR
114
196
  BENCH["Local benchmark report"] --> CAT
115
197
  POLICY["Task policy"] --> CAT
116
198
  ASSIGN["CPU model assignments"] --> CAT
117
- CAT --> MODEL["CatalogModel<br/>capabilities · context · residency<br/>benchmark · policy rank · backend"]
199
+ LOOPS["Observed repetition loops"] --> CAT
200
+ CAT --> MODEL["CatalogModel<br/>capabilities · context · residency<br/>benchmark · policy rank · backend · loops"]
118
201
  ```
119
202
 
120
203
  FreeLlama fetches tags and residency from each configured backend. Models assigned to CPU are
121
204
  removed from the primary catalog view and included only from the secondary backend. Static catalog
122
- metadata is cached for 30 seconds; residency is refreshed before routing.
205
+ metadata is cached for 30 seconds; residency and observed loops are refreshed before routing.
206
+
207
+ Every field comes from the model's own metadata and from what FreeLlama observed, never from its
208
+ name. `GET /_freellama/v1/models` adds, per model:
209
+
210
+ - `context_default`: the context Ollama gives a request that leaves `num_ctx` unset (`source` is
211
+ `modelfile`, `config`, `ollama_context_length`, `vram_tier`, or `resident_runner`). A managed
212
+ task that sends a different `num_ctx` reloads the runner.
213
+ - `resident_context`: the `context_length` of the loaded runner from `/api/ps`.
214
+ - `observed_loops`: per task kind, managed runs since start and how many ended in a repetition
215
+ loop (`{runs, loops}`); process-local, not persisted.
216
+ - `kv_cache_bytes_per_token_f16`: estimated from the declared architecture's block count, KV head
217
+ count and key/value lengths, for any architecture; `null` when the metadata declares recurrent,
218
+ state-space, sliding-window, shared or cross-attention caches, which the dense formula misstates.
123
219
 
124
220
  A failed `/api/show` response skips that model instead of failing the entire catalog. An unreachable
125
221
  configured backend fails catalog discovery closed because FreeLlama cannot prove the assigned model's
126
222
  state.
127
223
 
224
+ ### Public model metadata
225
+
226
+ Public model enrichment belongs to the MCP layer's `model-knowledge.ts` and `model-search.ts`.
227
+ Installed/detail requests can opt into GET-only Ollama family and full tag-page lookups. The layer
228
+ matches exact tags and digest prefixes, preserves local supported features, and attaches family-scoped
229
+ source excerpts and usage notes. Its bounded in-memory cache expires successes after one hour and
230
+ failures after one minute. Failed or unmatched lookups leave local inventory and evidence intact.
231
+ Public claims do not enter the Rust catalog, task policy, quality confidence, or routing decisions.
232
+ See [Model metadata](MODEL_SELECTION.md#model-metadata) for the public contract and request bounds.
233
+
128
234
  ## Deterministic routing
129
235
 
130
- Routing starts with an explicit task contract. Supported task kinds are `completion`, `coding`,
236
+ Routing defaults to ordinary `completion`; explicit profiles refine selection. Supported task kinds are `completion`, `coding`,
131
237
  `code_repair`, `tools`, `browser`, `vision`, `embedding`, and `long_context`.
132
238
 
133
239
  ```mermaid
@@ -138,7 +244,7 @@ flowchart TD
138
244
  X -->|"Yes"| E["Use exact model or refuse"]
139
245
  X -->|"No"| O{"Objective"}
140
246
  O -->|"fastest"| S["Rank local performance,<br/>residency, and deterministic ties"]
141
- O -->|"balanced"| B["Require policy-qualified candidates,<br/>then balance score and residency"]
247
+ O -->|"balanced"| B["Prefer policy-qualified candidates;<br/>otherwise use eligible models with low confidence"]
142
248
  O -->|"quality"| Q["Require policy-qualified candidates,<br/>then prefer policy order"]
143
249
  E --> H["Grade advertised-context compatibility and evidence"]
144
250
  S --> H
@@ -149,12 +255,21 @@ flowchart TD
149
255
  G -->|"Yes or unset"| D["RouteDecision"]
150
256
  ```
151
257
 
152
- Explicit model selection never substitutes another model. Every rejected candidate includes a
153
- reason so callers can distinguish capability, context, policy, and installation failures.
258
+ Explicit model selection never substitutes another model. An explicit `model` is read the way
259
+ Ollama reads it: the exact tag, else `name:latest`, else the one installed tag of that family; an
260
+ unknown or ambiguous name is refused with the installed candidates. Every rejected candidate
261
+ includes a reason so callers can distinguish capability, context, policy, and installation failures.
262
+
263
+ Routing never ranks by a model's name or architecture. Automatic selection (fallback ranking and
264
+ session affinity) ranks a model last for a task kind when it has looped on that kind at least twice
265
+ in at most twice as many managed runs; the decision then carries
266
+ `repeats_on_this_task_last_resort`. An operator policy naming the model for the task, or an explicit
267
+ `model`, always wins. No request option is added because of which model was chosen: stop sequences
268
+ and decoding settings belong to the caller.
154
269
 
155
270
  `context_window_fit` means only that requested `num_ctx` fits the model's advertised context
156
271
  window. It is not a live-memory, free-VRAM, K/V-cache, thermal, or runner-admission verdict.
157
- Execution separately reports a conservative model-metadata K/V lower-bound preflight and post-run
272
+ Execution separately reports an advisory model-metadata K/V estimate and post-run
158
273
  placement evidence; Ollama remains authoritative for actual runner allocation and admission.
159
274
 
160
275
  ### Confidence evidence
@@ -171,39 +286,25 @@ Confidence is a grade derived from independent evidence dimensions:
171
286
  `minConfidence: "medium"` refuses before generation when either input is missing. The gate lives in
172
287
  `select_route`, so the HTTP API, CLI, MCP server, and embedded core share the same behavior.
173
288
 
174
- ## Natural-language routing
175
-
176
- Natural-language routing cannot select a model directly:
177
-
178
- ```mermaid
179
- sequenceDiagram
180
- participant C as Client
181
- participant F as FreeLlama
182
- participant I as Intent model
183
- participant R as Deterministic router
184
-
185
- C->>F: Natural-language request
186
- F->>I: Strict JSON schema and bounded profile
187
- I-->>F: Task, objective, context, tool, and vision fields
188
- F->>F: Apply deterministic guards
189
- F->>R: Normalized RouteInput
190
- R-->>F: RouteDecision
191
- F-->>C: Intent, adjustments, and route
192
- ```
193
-
194
- Word-boundary guards prevent incidental substrings such as `photosynthesis` from creating a vision
195
- requirement. Explicit requirements in the original text override weak or unsupported inferred fields.
196
-
197
- The natural route endpoint does not run the selected task. Submit a managed task to execute it.
198
-
199
289
  ## Managed task execution
200
290
 
201
- Managed tasks combine routing with admission and backend-aware transition coordination:
291
+ The implementation separates [admission scheduling](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/admission.rs),
292
+ [execution coordination](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/execution/mod.rs),
293
+ [read-only readiness](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/readiness.rs), and
294
+ [typed errors](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/error.rs). The platform module wires HTTP endpoints,
295
+ configuration, discovery, and shared state; extracting these owners preserves the public routing API.
296
+
297
+ Managed tasks combine routing with admission and backend-aware transition coordination. With
298
+ `defer:true`, the server returns HTTP `202` and a process-local job handle, then runs the same task
299
+ path. The bounded registry owns receipts and cancellation, while the existing governor owns
300
+ admission. Batch items cannot defer. Per-task `timeout_seconds` bounds discovery, waiting, loading,
301
+ and inference together, capped by the server task timeout. See [CLI controls](CLI.md#execute-tasks)
302
+ for retention limits and [monitoring](MONITORING.md) for the observable lifecycle.
202
303
 
203
304
  ```mermaid
204
305
  flowchart TD
205
306
  T["POST /tasks or /task-batches"] --> R["Select eligible model + backend"]
206
- R --> K["Calculate metadata-backed K/V lower bound<br/>(or report unknown)"]
307
+ R --> K["Estimate metadata-backed F16 K/V<br/>(or report unknown)"]
207
308
  K --> A{"Acquire selected backend's<br/>3:2:1 fair weighted budget"}
208
309
  A -->|"No"| E503["503 server busy"]
209
310
  A -->|"Yes"| B{"Execution backend"}
@@ -219,7 +320,7 @@ flowchart TD
219
320
  CR --> OQ
220
321
  CX --> OQ
221
322
  OQ --> SEND["Ollama schedules bounded request"]
222
- SEND --> RETRY{"500, 502, 504,<br/>or connection failure?"}
323
+ SEND --> RETRY{"500, 502,<br/>or connection-establishment failure?"}
223
324
  RETRY -->|"Retryable"| OQ
224
325
  RETRY -->|"Success"| OBS["Query selected Ollama /api/ps"]
225
326
  OBS --> OUT["Route, assignment, physical observation,<br/>admission, metrics, and response"]
@@ -228,40 +329,130 @@ flowchart TD
228
329
 
229
330
  Admission is independent per backend and uses weighted fair round robin: interactive gets three
230
331
  turns, normal two, and background one, preserving FIFO inside each class and preventing starvation.
231
- The primary/GPU pool defaults to two weighted units and the
232
- optional CPU pool defaults to one. A saturated GPU burst therefore cannot consume the permit held
233
- for a small CPU helper. Those values represent one ordinary chat and one embedding, not detected
234
- hardware capacity; operators can tune both budgets from queue-wait and resident-memory evidence.
332
+ The primary/GPU pool defaults to two weighted units per observed `OLLAMA_NUM_PARALLEL` slot,
333
+ falling back to two. The optional CPU pool defaults to one. A saturated GPU burst therefore cannot
334
+ consume the permit held for a small CPU helper. With one parallel slot, those values represent one
335
+ ordinary chat and one embedding. Operators can tune both budgets from queue-wait and resident-memory evidence.
235
336
  After FreeLlama admission, each Ollama process applies its own `OLLAMA_MAX_QUEUE`, scheduler,
236
- `OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy requests bypass FreeLlama admission and
237
- enter the primary Ollama queue directly.
337
+ `OLLAMA_NUM_PARALLEL`, and loaded-model limit. Raw proxy requests bypass weighted admission but
338
+ still pass the raw concurrency cap, backend exclusion, and host-pressure gate.
238
339
 
239
- | Task | Cost |
340
+ | Task | Default cost |
240
341
  |---|---:|
241
342
  | Embedding | `ceil(input_items / 4)` |
242
343
  | Chat, coding, tools, browser, or long context | 2 |
243
344
  | Vision | 4 |
244
345
 
346
+ The runtime file's `[task_costs]` table overrides task-kind base costs. Embeddings multiply their
347
+ base by `ceil(input_items / 4)`; the acquired charge is capped to the backend's current capacity.
348
+ See [monitoring controls](MONITORING.md#adaptive-concurrency) for validation and reload behavior.
349
+
350
+ After six capacity bypasses, the scheduler reserves released capacity for the oldest blocked waiter,
351
+ so a stream of small requests cannot indefinitely starve a larger request. Cancellation releases
352
+ that reservation. Admission, resource waiting, and transition locking share one queue deadline.
353
+
354
+ A task waiting for host resources returns its execution slot after two seconds and continues
355
+ waiting within the same deadline. It remains counted against the backend's bounded queue, so
356
+ memory pressure cannot create an invisible backlog. Health reports slot and resource waiters
357
+ separately. Returning to slot admission transfers the existing queue registration atomically;
358
+ new arrivals and a smaller reloaded queue limit cannot displace accepted work. Expired deadlines
359
+ prevent admission even when an execution slot is free. Cancellation removes either waiter, and releasing a memory reservation wakes resource
360
+ waiters immediately; host telemetry still obeys the sampling and recovery policy below.
361
+
245
362
  FreeLlama acquires the admission slot before a transition lock to avoid deadlock. A model marked
246
363
  resident during discovery is checked again while the shared transition lock is held; stale or
247
364
  unavailable residency falls back to the exclusive transition path. Session affinity is bound only
248
365
  after successful upstream execution, so refused and failed tasks cannot change later routing.
366
+ An explicit `done:false` chat response or a reported upstream error is a failure, even with HTTP 200.
367
+ It cannot train runtime feedback or bind affinity. Completed failed exchanges retain the raw response
368
+ and honor a requested immediate unload. Transport failures also attempt that unload and return its
369
+ lifecycle receipt, but a disconnected inference response alone does not prove upstream cancellation.
370
+ Resource failures return a readable `error`, a stable `code`, and structured `resource_admission`
371
+ data, including per-item batch failures.
249
372
 
250
373
  `POST /_freellama/v1/task-batches` is for caller-declared independent work only: every item has a
251
374
  stable ID and `independent:true`; dependent workflow execution is rejected before any upstream call.
252
375
  It bounds local dispatch and returns ordered per-item success/error receipts. Every child still
253
376
  passes the same global admission and transition path above.
254
377
 
255
- The K/V preflight derives F16 K+V bytes-per-token only when `/api/show` exposes the required model
256
- shape. It blocks only a known weights-plus-cache lower bound above 80% of total CPU/unified memory.
257
- It deliberately reports unknown rather than guessing for other architectures or discrete GPU free
258
- VRAM; Ollama remains the live loader and final memory authority.
378
+ The K/V preflight derives single-sequence F16 K+V bytes-per-token only for supported model
379
+ architectures with sufficient metadata. Explicit key/value dimensions take precedence; sliding-window,
380
+ shared-KV, recurrent, and unknown layouts report unknown. F16 is advisory because quantized KV can
381
+ use less memory. The coarse model-file budget refuses files over 80% of local CPU/unified RAM only
382
+ for loopback backends. Forecasts use each backend's independently attributed parallelism and KV
383
+ cache settings where process inspection is available, with explicit fallback sources otherwise.
384
+ Process observations refresh on requests after five seconds; measurements made under different
385
+ settings cannot supply an exact-fit estimate. Ollama remains the live loader and final memory
386
+ authority. Configured settings alone do not prove physical processor placement.
387
+
388
+ The shared [resource governor](https://github.com/bgauryy/FreeLlama/blob/main/packages/rust-core/src/platform/resources/mod.rs) samples local
389
+ available-memory estimates, load per logical CPU, OS memory pressure, active swap-out growth, and
390
+ thermal throttling when reported. OS memory pressure holds every local demand; active swap-out
391
+ growth holds new loads; CPU-load and thermal holds apply to CPU-assigned work and to cold loads
392
+ (decoding weights is CPU work and a new heat source) but not to a bounded request on a runner
393
+ already resident on the GPU, which neither waits for the cores nor cools the package by waiting (`ResourceDemand::is_held_by`; the assessment's
394
+ `held_by` lists the holds that applied). Low available RAM (below the larger of 15% or 1 GiB, clearing after two
395
+ healthy samples above the larger of 20% or 2 GiB) depends on the platform (`ResourcePolicy::low_memory`).
396
+ On macOS (`advisory`) the estimate undercounts what the OS can reclaim, so a load that does not cover
397
+ it first unloads idle runners and then dispatches with a `memory_advisory`. On Linux, Windows, and
398
+ other platforms (`refuse`) a new load that does not fit first unloads idle runners and then waits
399
+ with `held_insufficient_capacity` until a permit release or a fresh sample frees capacity, or refuses
400
+ with 503 at its deadline: Linux `MemAvailable` already includes reclaimable cache and is the hard cap
401
+ in a container, so over-admitting invites the OOM killer. Already-resident runners add no memory and
402
+ are admitted in both modes, also during an active-swapping hold, so a large load never locks out
403
+ reuse of its own runner; swapping still holds new loads. Historical swap occupancy alone is not pressure. Samples are cached for two
404
+ seconds; unavailable signals stay unknown. Remote backends bypass this host gate.
405
+
406
+ Each pressure signal recovers independently after two fresh samples that meet its recovery
407
+ threshold. A recovered CPU-load signal therefore clears even while RAM remains low.
408
+
409
+ The default `require_memory` telemetry policy holds local inference when available RAM is missing,
410
+ even when a raw request has no model-footprint estimate. `best_effort` is an explicit opt-in;
411
+ `require_all` additionally requires the collector's applicable load, OS-pressure, and thermal signals.
412
+ Unsupported Linux collector signals remain explicitly unavailable rather than being fabricated.
413
+ The pure `ResourceSnapshot::assess_capacity` calculation is shared by admission and previews.
414
+ Previews use the same footprint lookup, subtract existing reservations, and expose both queue and
415
+ resource readiness. Insufficient model headroom reports `runnable_low_memory_estimate` and admits
416
+ no siblings on macOS; elsewhere it reports `held_insufficient_capacity`. Preview capacity is advisory and conservative, never a reservation.
417
+
418
+ Managed CPU/unified-memory tasks reserve estimated additional model-file and known F16 KV bytes
419
+ across both backends. Verified runner footprints replace that estimate only for the same backend,
420
+ immutable model digest, and equal-or-larger observed context. Exact resident digest/context matches
421
+ avoid double-counting existing allocations. The reservation is rechecked after transition locking
422
+ without waiting while holding the backend lock; an increased requirement that no longer fits the
423
+ estimate dispatches with a `memory_advisory`, and a pressure hold refuses with 503. Permits release on completion or cancellation. On discrete-GPU hosts, observed free
424
+ VRAM discounts the estimated host-memory requirement by the bytes the GPU can hold. Missing VRAM
425
+ telemetry reserves the full estimated footprint in host RAM. The governor cannot constrain external
426
+ allocations or preempt an already-running model.
427
+
428
+ Managed text tasks without explicit `context_tokens` select the smallest 2K/4K/8K/16K/32K bucket
429
+ that covers the UTF-8 prompt/schema estimate, output reserve, and 512-token template margin, bounded
430
+ by the selected model's advertised limit. Larger requests require explicit context or compaction.
431
+ Multimodal and embedding requests retain their profiles. The execution receipt exposes
432
+ `context_sizing`; previews contain routing fields only and therefore retain task defaults.
433
+ Managed chat sends `truncate:false` and `shift:false` so supporting Ollama backends refuse overflow
434
+ instead of silently losing earlier instructions. Managed embeddings send `truncate:false` too.
435
+ Only the dedicated CPU backend supplies a thread default: half the logical CPU count on other
436
+ platforms, while macOS leaves Ollama to select its performance cores. `FREELLAMA_CPU_NUM_THREAD`
437
+ overrides that default; explicit request options take precedence. The primary backend leaves thread
438
+ selection to Ollama to avoid changing the options of a shared runner. `runtime_options` reports
439
+ forwarded values; runner support is not assumed.
259
440
 
260
441
  Resident tasks share a backend lock. Nonresident tasks take that backend's exclusive lock so a cold
261
442
  load cannot race another managed task on the same server. CPU and GPU backends have separate locks
262
443
  and admission pools, so they can progress independently.
263
444
 
264
- HTTP `500`, `502`, and `504` status codes and connection failures use bounded retry with backoff.
445
+ In `serve`, mutating raw proxy requests also take the primary backend's exclusive transition lock,
446
+ returning 503 when busy. The response body holds this guard through EOF or disconnect. Metadata
447
+ GET/HEAD and `/api/show` requests remain available. Raw inference waits at most 500 ms for host
448
+ pressure to clear, then returns 503 with `Retry-After`; empty `keep_alive:0` unload requests bypass
449
+ pressure. Raw request-body collection has the configured request deadline too; an incomplete upload
450
+ returns HTTP 408 and releases its guards without contacting Ollama. Admission receipts report
451
+ `queue_wait_ms`, `resource_wait_ms`, `transition_wait_ms`, and
452
+ their sum as `total_wait_ms`. Health and route receipts expose sampled pressure and reservations.
453
+
454
+ HTTP `500` and `502` status codes and connection-establishment failures use bounded retry with backoff.
455
+ HTTP `504`, transport timeouts and post-send disconnects are not replayed.
265
456
  FreeLlama does not retry `503 Service Unavailable` because retrying while holding admission can
266
457
  amplify overload. It does not retry a generation timeout because the first request might still run.
267
458
 
@@ -300,34 +491,18 @@ cold-load cost do not poison the comparison. It requires more than a 10% advanta
300
491
  break a tie before the sample threshold. Quality routing, explicit model selection, and session
301
492
  affinity do not follow the latency loop.
302
493
 
303
- The same selection function supplies preview/recommendation receipts, intent-model execution, and
304
- managed task execution. This keeps advertised placement and actual request routing aligned.
494
+ The same selection function supplies preview receipts and managed task execution. This keeps advertised placement and actual request routing aligned.
305
495
 
306
496
  Raw proxy traffic always uses the primary upstream. For setup and runtime verification, see
307
497
  [Run models on CPU and GPU](CPU_GPU_ROUTING.md).
308
498
 
309
499
  ## MCP tool flow
310
500
 
311
- The MCP server keeps general model work separate from file-backed research and destructive lifecycle
312
- operations:
313
-
314
- ```mermaid
315
- flowchart TD
316
- MC["MCP client"] --> TOOL{"Tool"}
317
- TOOL -->|"doctor"| NAPI["Native core diagnostics"]
318
- TOOL -->|"models"| MIX["Control API or direct Ollama/library HTTP"]
319
- TOOL -->|"run_task"| TASK["Control API route or task"]
320
- TOOL -->|"ollama_manage"| LIFE["Direct Ollama pull or unload"]
321
- TOOL -->|"ollama_delete"| DELETE["Explicit destructive Ollama delete"]
322
- TOOL -->|"delegate_research"| AD["Confined adapter subprocess"]
323
- AD --> FS["Allowlisted workspace"]
324
- AD --> TASK
325
- AD --> VER["Answer, citations, usage,<br/>and verification verdict"]
326
- ```
327
-
328
- The tool split prevents `run_task` from acquiring file access and prevents lifecycle operations from
329
- being hidden inside a generic action. For schemas and security annotations, see the
330
- [MCP server reference](../packages/mcp/README.md).
501
+ The MCP server keeps general model work (`run_task`), file-backed research (`delegate_research`, a
502
+ confined adapter subprocess), and destructive lifecycle operations (`ollama_manage`,
503
+ `ollama_delete`) in separate tools, so `run_task` never acquires file access and lifecycle
504
+ operations are not hidden inside a generic action. The canonical tool table, schemas, and security
505
+ annotations are in the [MCP server reference](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md#tools).
331
506
 
332
507
  ## Grounded research flow
333
508
 
@@ -355,14 +530,33 @@ shape.
355
530
 
356
531
  The adapter loop preserves its control contract through four mechanisms:
357
532
 
358
- - Pagination keeps complete output on disk while showing bounded pages to the model.
533
+ - Pagination keeps complete output in adapter memory and writes it into the final result file,
534
+ while showing bounded pages. Oversized lines split losslessly; the final page ends pagination.
359
535
  - Context fitting byte-preserves the system prompt and question by default, compacts older
360
536
  observations, and fails before Ollama if pinned content cannot fit. The first estimate is
361
537
  configurable; real `prompt_eval_count` values calibrate subsequent fits conservatively.
362
538
  - JSON repair retains gathered evidence across a bounded format correction.
363
539
  - Repeat suppression serves an identical earlier result without rerunning the subprocess command.
364
540
 
365
- For the benchmark implementation of this loop, see [`AGENTS.md`](../AGENTS.md).
541
+ Overflow compaction targets 90% of the input budget using older observations, preserving head/tail
542
+ evidence and exact stored-page recovery actions. Already-fitting prefixes and existing breadcrumbs
543
+ remain unchanged. Compact observation ledgers preserve action, status, source, step, page recovery,
544
+ and untrusted-output provenance. Full observations and page offsets are stored in temporary files;
545
+ only the requested page enters the conversation. Final audit JSON streams the full results from disk
546
+ and is atomically replaced. Temporary files are cleaned on normal and exceptional exits.
547
+ Calibration keys include the endpoint, exact model tag and verified manifest digest, system-prompt
548
+ hash, estimator version, and settings. Unknown model identity disables persisted calibration while
549
+ retaining process-local calibration. Adapter retries cover explicit
550
+ 503 overload and connection refusal, never uncertain timeouts or post-send disconnects.
551
+
552
+ Ollama's optional `prompt_eval_cached_count` is a subset of total `prompt_eval_count`. Prefill rate
553
+ uses uncached tokens only; unavailable cache counts produce null instead of invented zero hits.
554
+ Adapter cache totals remain null if any turn is unreported, with partial-report coverage in metadata.
555
+ See [Ollama usage](https://docs.ollama.com/api/usage) and
556
+ [the pinned prefix-cache implementation](https://github.com/ggml-org/llama.cpp/blob/74a7c897f049c17e7080423aa2111776eff6ebbf/tools/server/server-context.cpp#L3201).
557
+
558
+ Adapter implementations and defaults live in
559
+ [`packages/mcp/src/research/`](../packages/mcp/src/research/).
366
560
 
367
561
  ## Context and token boundary
368
562
 
@@ -381,7 +575,7 @@ flowchart LR
381
575
 
382
576
  Raw source, images, and embedding vectors do not need to enter the frontier model's context.
383
577
  Embedding vectors are withheld by default because they are large and not human-readable. For the
384
- measured accounting, see [Token economics](ECONOMICS.md).
578
+ measured accounting, see [Token economics](https://github.com/bgauryy/FreeLlama/blob/main/packages/mcp/README.md#token-economics).
385
579
 
386
580
  ## State and failure behavior
387
581
 
@@ -390,6 +584,7 @@ FreeLlama keeps these values in memory:
390
584
  - Catalog metadata cache
391
585
  - Current residency snapshots
392
586
  - Session-to-model affinity
587
+ - Revision-protected scope message history and bounded adaptive residency measurements
393
588
  - Independent GPU and CPU priority-fair weighted admission pools
394
589
  - Per-task normalized warm latency and queue feedback for each backend, restored from an optional
395
590
  versioned atomic snapshot
@@ -398,7 +593,19 @@ FreeLlama keeps these values in memory:
398
593
  Sessions are bounded (default 1024) and expire after idle time (default one hour); they store only
399
594
  model affinity, never prompts or Ollama KV. Restarting `freellama serve` clears sessions and the catalog cache. Persisted placement feedback is
400
595
  reloaded; `--ephemeral-feedback` intentionally resets it. Ollama owns model residency, so loaded
401
- models survive a FreeLlama restart.
596
+ models survive a FreeLlama restart. Scope histories and their revisions do not survive restart.
597
+ They store messages independently from affinity and append only a successful generation at the
598
+ expected revision. Warming uses ordinary admission and fit checks; omitted residency controls use
599
+ a finite measured policy. See [Scope history and model warming](SCOPES_AND_WARMING.md) for the
600
+ public contract and configured limits.
601
+
602
+ In `serve`, mutating passthrough traffic shares the primary transition lock and receives
603
+ host-pressure gating, while managed tasks also reserve forecast memory. Direct Ollama callers bypass
604
+ these controls. FreeLlama cannot preempt inference or constrain other processes. A configured but
605
+ unreachable CPU backend makes managed catalog discovery fail closed; raw primary passthrough remains
606
+ usable. Runtime feedback is task-specific, token-normalized, and warm-only: it can steer unpinned
607
+ `fastest` or `balanced` work after three samples per backend and a greater-than-10% advantage, but
608
+ cannot change CPU eligibility or steer quality routing.
402
609
 
403
610
  The design fails closed when it cannot establish a required contract. Examples include an
404
611
  unreachable configured backend, an unknown confidence grade, a policy without qualified models, an