Skip to content

Local engine model management: core, gateway, and protocol - #486

Merged
rexlunae merged 6 commits into
mainfrom
feat/local-engines-backend
Aug 18, 2026
Merged

Local engine model management: core, gateway, and protocol#486
rexlunae merged 6 commits into
mainfrom
feat/local-engines-backend

Conversation

@rexlunae

@rexlunae rexlunae commented Aug 18, 2026

Copy link
Copy Markdown
Owner

What & why

Local inference engines (Joshua, llama.cpp, Ollama, LM Studio, exo) now have real model-management backing for the UI: pickers show what is actually available on disk, the engine layer knows which servers are already running on the host, and engine parameters are typed config instead of opaque extra_args.

Changes

Engine config & parameters (rustyclaw-core/src/engines)

  • EngineConfig gains typed, optional parameters: context_length, device, huge_pages, mmap, lazy_weights, max_output_tokens, max_concurrency (all defaulted — existing configs parse unchanged). Joshua maps them to its serve flags (--n-ctx, --device, --huge-pages, --mmap, --lazy-weights, --max-output-tokens, --max-concurrency); llama.cpp maps context_length--ctx-size. Raw extra_args remain the escape hatch and still win on the command line.
  • joshua_serve_flags() centralises the flag mapping for both the spawn path and the auto-start service definitions.

Running-server detection

  • running_server_cmdlines() / parse_server_cmdlines() (Linux, best-effort) find joshua serve and llama-server processes already on the host — including ones started outside RustyClaw — so status() reports them as running and list_models() marks the models they serve as loaded. This is what lets the UI say which local models are running.
  • stop() now receives the engine config and scopes its kill to the engine's own port (no more pkill -f 'joshua serve' nuking every server on the machine). load/unload/start are honest about externally-run servers: report which port they're on, refuse to kill them, and don't spawn a duplicate.

Provider model-list fallback

  • provider_models_with_local_fallback() (and a detailed variant) merges the live provider API list with the engine's own model list — for the file-based engines that is a scan of the models directory, so pickers show on-disk models even when the server isn't running — and reports which models are loaded. llama.cpp's list_models gained the same on-disk scan.

Honest failures

  • Joshua's spawn now returns a real error when the server never answers ("did not answer on … within 10s — run joshua serve --model … manually") instead of a forever "may still be loading", and reports when joshua isn't installed.

Configurable model-call deadline (rustyclaw-gateway/src/dispatch.rs)

  • The per-turn model call used a hard-coded 180s cap that local engines on a loaded machine can exceed while prefilling a large prompt. RUSTYCLAW_MODEL_TIMEOUT_SECS overrides it (default stays 180).

Wire / protocol

  • EngineConfigSet client command (persists an engine's full config to config.toml).
  • EngineInfoDto carries the engine's full EngineConfig so clients can round-trip it.
  • ProviderModelListResult carries loaded model ids (appended last, positional-bincode safe).

Testing

  • cargo check --workspace, clippy clean on touched crates.
  • Unit tests: engine fallback, cmdline parsing for running servers, Joshua flag mapping (23 engine tests).
  • Verified live against a preview gateway + desktop client: on-disk scans, running-server detection (real joshua/llama-server processes on the host), parameters save/round-trip, honest Load/Unload errors, and a real chat reply through a managed joshua server.

Open in Devin Review

Local inference engines now have real model management backing the UI:

- EngineConfig gains typed, optional parameters (context_length, device,
  huge_pages, mmap, lazy_weights, max_output_tokens, max_concurrency) that
  Joshua maps to its serve flags (--n-ctx/--device/--huge-pages/--mmap/
  --lazy-weights/--max-output-tokens/--max-concurrency); llama.cpp maps
  context_length to --ctx-size.  extra_args remains the escape hatch and
  still wins on the command line.
- The engine registry detects servers already running on the host
  (joshua serve / llama-server, Linux, best-effort) and reports them as
  running with the models they serve, so the UI shows what is actually
  loaded even when a server was started outside RustyClaw.
- stop() now receives the engine config so engines scope their kill to
  their own port instead of pkill'ing every matching server on the host;
  Load/Unload/Start are honest about externally-run servers (refuse to
  kill them, report which port they are on, don't spawn a duplicate).
- provider_models_with_local_fallback() merges the live provider API list
  with the engine's on-disk model list (GGUF scans for Joshua/llama.cpp),
  clears the fetch error when local models exist, and reports which models
  are loaded — the data behind on-disk model pickers.
- Joshua's spawn reports a real error when the server never answers
  (instead of a forever "may still be loading").
- The gateway's per-turn model-call deadline is configurable via
  RUSTYCLAW_MODEL_TIMEOUT_SECS (default 180) — local engines on loaded
  machines can exceed the old hard-coded cap while prefilling.
- Wire/protocol: EngineConfigSet client command, EngineInfoDto carries the
  full engine config, ProviderModelListResult carries loaded-model ids.
  Config example + CHANGELOG updated.
devin-ai-integration[bot]

This comment was marked as resolved.

- joshua_serve_flags only emits --device/--huge-pages for the values
  Joshua actually accepts (auto/cpu/metal/cuda and transparent/2mb/1gb/
  huge), so a hostile free-form config value can no longer inject shell
  syntax into the spawn command.  Numeric fields were already safe.
- Joshua's spawn no longer declares failure when the server has simply
  not answered yet: after the health-probe window it checks whether the
  spawned process is still alive and reports "still loading" if so,
  reserving the error for a process that actually exited.  Switching to
  a large model no longer shows a scary error while the model loads fine
  moments later.
- llama.cpp remove resolves the model name back to the scanned on-disk
  path (file stem, possibly inside a per-repo subdirectory) before
  deleting, and fails with the available list when nothing matches —
  previously it rm -f'd a non-existent path and reported phantom
  success while the file stayed on disk.
- ProviderModels.models/.error gain the doc comments the style guide
  requires for public fields.
@rexlunae

Copy link
Copy Markdown
Owner Author

All four Devin review findings are addressed in f0c96a8:

  1. llama.cpp remove phantom successLlamaCppEngine::remove now resolves the model name back to the scanned on-disk path (file stem, possibly inside a per-repo subdirectory) before deleting, and errors with the available list when nothing matches, so the UI can no longer report success while the file stays on disk.
  2. Joshua slow-load false failurespawn_server now checks whether the spawned process is still alive after the health-probe window: a live process reports "still loading", and only an exited process produces the error. Switching to a large model no longer shows a scary error while it loads fine moments later.
  3. Missing public-field docsProviderModels::models and ::error now carry doc comments per the style guide.
  4. Shell injection via device/huge_pagesjoshua_serve_flags only emits --device/--huge-pages for the values Joshua actually accepts (auto/cpu/metal/cuda, transparent/2mb/1gb/huge), dropping anything else instead of interpolating it into the shell command. Added a regression test for hostile values.

Verified: cargo check --workspace clean, clippy clean on the touched crates, engine tests 24/24 (incl. the new hostile-value test).

devin-ai-integration[bot]

This comment was marked as resolved.

Protocol: the enriched payloads now ride in new frames with new pinned
discriminants instead of widening existing messages — the wire format is
positional bincode and deserialize_frame rejects trailing bytes, so adding
fields to ProviderModelListResult/EngineInfoDto broke older peers (the
project's own protocol rule: new capabilities need new frames).
EngineConfigList (after EngineListResult) and ProviderModelLoadedList
(after ProviderModelListResult) carry the engine configs and the loaded-
model markers; the old frames are byte-identical to before.

Joshua start: only a server on the engine's own configured port counts as
'already running'.  A joshua started outside RustyClaw on another port no
longer prevents the configured server from starting (or Restart from
bringing it back).

llama.cpp stop: reports honestly — 'no llama-server is running on port N'
when nothing matched, instead of always claiming success while a detected
server on another port keeps serving.

llama.cpp start: applies the typed context window (--ctx-size) on manual
starts too, matching the auto-start path.

Clients gain minimal arms for the two new frames here; the desktop/TUI
consume them in their UI PRs.
devin-ai-integration[bot]

This comment was marked as resolved.

- Joshua spawn: the 'process exited' hard failure is only reported on
  Linux, where process inspection exists; on other platforms a slow load
  falls back to the informational 'may still be loading' instead of a
  false failure.  Same for the llama.cpp stop check.
- llama.cpp auto-start: the service definition always passes the resolved
  --port (not only when one is configured), so the port-scoped stop can
  actually identify auto-started servers instead of silently matching
  nothing.
- llama.cpp stop: on non-Linux (no process inspection) it falls back to
  stopping every llama-server rather than claiming success while one
  keeps running.
- llama.cpp start: the configured models directory is interpolated via
  sh_quote instead of naive single quotes.
- running_server_cmdlines: no unused-variable warning on non-Linux
  builds (cfg_attr allow with the Linux-only body).
- Joshua load: 'already loaded' is only claimed when the configured
  endpoint actually serves the model; a joshua started outside RustyClaw
  on another port no longer makes Load report success while the
  configured server has nothing loaded.
devin-ai-integration[bot]

This comment was marked as resolved.

@rexlunae

Copy link
Copy Markdown
Owner Author

Second-round Devin findings addressed in 6e3e115 (all threads resolved):

  • Non-Linux start/stop false failures — the 'process exited' hard failure is now Linux-only (process inspection exists there); on macOS/Windows a slow load reports 'may still be loading' instead of a failure, and llama.cpp stop falls back to stopping every llama-server rather than claiming success while one keeps running.
  • llama.cpp auto-start stop — the service definition now always passes the resolved --port, so the port-scoped stop can identify auto-started servers.
  • Non-Linux warningrunning_server_cmdlines no longer emits an unused-variable warning off Linux.
  • models_dir quoting — llama.cpp start interpolates the configured models directory via sh_quote.
  • Joshua 'already loaded' — Load only claims already-loaded when the configured endpoint actually serves the model; a joshua on another port no longer produces a phantom success.

All review threads across #486/#487/#488 are resolved. CI is re-running (Lint already green).

- Bump WIRE_PROTOCOL_VERSION to 4: EngineConfig gained its typed parameter
  fields (the shipped EngineConfigSet payload widened positionally) and the
  EngineConfigList / ProviderModelLoadedList frames are new, so a mismatched
  peer must fail at the first affected frame instead of mis-parsing.
- Joshua: sh_quote the model path and every extra_arg interpolated into the
  serve command line; server_process_alive is Linux-only (dead code and an
  unused parameter on other platforms).
- llama.cpp: stop() keeps its port/pattern bindings inside the Linux block
  (no unused-variable warnings on macOS/non-Linux); start() sh_quotes
  extra_args; load() falls back to the typed context_length.
- engine_start_command: llama.cpp built-in flags now come first so a
  hand-written --port/--ctx-size in extra_args overrides them (auto-start).
- Ollama: load() applies the typed context_length (--num-ctx) when no
  per-load override is given; list_models marks models resident per /api/ps
  instead of never marking anything loaded.
- LM Studio: list_models no longer claims every listed model is running.
devin-ai-integration[bot]

This comment was marked as resolved.

- llama.cpp list_models: a model served by a running llama-server but
  living outside the scanned models dir (e.g. started manually with an
  explicit --model path) was dropped from the list — loaded ids are now
  surfaced like Joshua's list_models does.
- Port-scoped stop/liveness patterns terminate the digit run (joshua
  pkill '...127.0.0.1:{port}( |$)' and llamacpp '...--port {port}( |$)', and
  joshua's server_process_alive now requires a non-digit after the port),
  so a short port (1234/808) can no longer match a server on 12345/8080.
- Engine status only reports Running when a detected server sits on the
  engine's own configured port (or the configured endpoint answers); a
  foreign server on another port no longer hides the Start button or
  turns Stop into a no-op that claims success.
- validate_engine_config() rejects structurally unusable configs (port 0)
  before they are persisted or reach the pkill/pgrep patterns; the
  gateway's EngineConfigSet handler refuses and reports the error instead
  of relying on the port's u16 type alone.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +217 to 230
async fn server_process_alive(port: u16) -> bool {
let needle = format!("127.0.0.1:{}", port);
crate::engines::running_server_cmdlines("joshua serve")
.await
.iter()
.any(|line| {
line.match_indices(&needle).any(|(end, _)| {
line[end..]
.chars()
.next()
.is_none_or(|c| !c.is_ascii_digit())
})
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Starting a slow-loading local model always reports a bogus failure

The check for whether the just-started server is still alive scans for the port text and then inspects the first character of the match itself instead of the character following it (line.match_indices(&needle) at crates/rustyclaw-core/src/engines/joshua.rs:223), so the check can never succeed and a model that is merely slow to load is declared crashed.

Impact: On Linux, starting a large local model that needs more than ten seconds to become responsive always ends with an error telling the user the process exited, even though the server is running fine and finishes loading moments later.

Why the alive-check can never return true

String::match_indices yields (start_index, matched_str) pairs — the index is the start of the match, not its end. The closure binds it as end and evaluates line[end..].chars().next(), which is therefore the first character of the needle itself, i.e. '1' of "127.0.0.1:<port>". Since '1'.is_ascii_digit() is true, is_none_or(|c| !c.is_ascii_digit()) is false for every match, so server_process_alive returns false unconditionally.

In spawn_server (crates/rustyclaw-core/src/engines/joshua.rs:186-201) the 20×500 ms health-probe loop falls through to the Linux block, server_process_alive(port) reports false, and the function bails with "…its process exited — the model file may be invalid…" even though the process is alive and still prefilling. This defeats the purpose of the new branch, which exists precisely so slow loads are not reported as failures.

The intended index is start + needle.len().

Suggested change
async fn server_process_alive(port: u16) -> bool {
let needle = format!("127.0.0.1:{}", port);
crate::engines::running_server_cmdlines("joshua serve")
.await
.iter()
.any(|line| {
line.match_indices(&needle).any(|(end, _)| {
line[end..]
.chars()
.next()
.is_none_or(|c| !c.is_ascii_digit())
})
})
}
async fn server_process_alive(port: u16) -> bool {
let needle = format!("127.0.0.1:{}", port);
crate::engines::running_server_cmdlines("joshua serve")
.await
.iter()
.any(|line| {
line.match_indices(&needle).any(|(start, m)| {
line[start + m.len()..]
.chars()
.next()
.is_none_or(|c| !c.is_ascii_digit())
})
})
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +437 to +447
async fn stop(&self, cfg: &EngineConfig) -> Result<String> {
// Scoped to the configured port: `pkill -f 'joshua serve'` would
// also kill servers started manually on other ports. The pattern
// terminates the digit run (`( |$)`), so a short port such as 808
// cannot match a server running on 8080.
let port = cfg.port.unwrap_or(DEFAULT_PORT);
Self::sh(&format!(
"pkill -f 'joshua serve .*127.0.0.1:{}( |$)' 2>/dev/null; echo 'stopped'",
port
))
.await

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟨 Client-supplied engine port is interpolated into pkill/pgrep shell patterns

EngineConfigSet lets a connected client set an arbitrary engine port, which is later interpolated into sh -c command lines used for process matching (pkill -f 'joshua serve .*127.0.0.1:{port}( |$)' at crates/rustyclaw-core/src/engines/joshua.rs:443-446, and the llama.cpp equivalent at crates/rustyclaw-core/src/engines/llamacpp.rs:253-267). The value is a u16, so no shell metacharacters can reach the command line, and validate_engine_config (crates/rustyclaw-core/src/engines/mod.rs:131-136) additionally rejects 0. The residual risk is behavioural rather than injective: the pattern is regex-matched against every process command line, so an attacker-chosen port could be crafted to match unrelated processes and have them killed by the pkill on the stop path.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@rexlunae
rexlunae merged commit 460ab50 into main Aug 18, 2026
17 checks passed
rexlunae added a commit that referenced this pull request Aug 18, 2026
The squashed backend kept the placeholder arms for the new
ProviderModelLoadedList/EngineConfigList frames; keep the desktop's real
handling (patched configs + loaded markers + the configs_received guard).
rexlunae added a commit that referenced this pull request Aug 18, 2026
* feat(engines): local model management — core, gateway, and protocol

Local inference engines now have real model management backing the UI:

- EngineConfig gains typed, optional parameters (context_length, device,
  huge_pages, mmap, lazy_weights, max_output_tokens, max_concurrency) that
  Joshua maps to its serve flags (--n-ctx/--device/--huge-pages/--mmap/
  --lazy-weights/--max-output-tokens/--max-concurrency); llama.cpp maps
  context_length to --ctx-size.  extra_args remains the escape hatch and
  still wins on the command line.
- The engine registry detects servers already running on the host
  (joshua serve / llama-server, Linux, best-effort) and reports them as
  running with the models they serve, so the UI shows what is actually
  loaded even when a server was started outside RustyClaw.
- stop() now receives the engine config so engines scope their kill to
  their own port instead of pkill'ing every matching server on the host;
  Load/Unload/Start are honest about externally-run servers (refuse to
  kill them, report which port they are on, don't spawn a duplicate).
- provider_models_with_local_fallback() merges the live provider API list
  with the engine's on-disk model list (GGUF scans for Joshua/llama.cpp),
  clears the fetch error when local models exist, and reports which models
  are loaded — the data behind on-disk model pickers.
- Joshua's spawn reports a real error when the server never answers
  (instead of a forever "may still be loading").
- The gateway's per-turn model-call deadline is configurable via
  RUSTYCLAW_MODEL_TIMEOUT_SECS (default 180) — local engines on loaded
  machines can exceed the old hard-coded cap while prefilling.
- Wire/protocol: EngineConfigSet client command, EngineInfoDto carries the
  full engine config, ProviderModelListResult carries loaded-model ids.
  Config example + CHANGELOG updated.

* fix(engines): address Devin review findings on #486

- joshua_serve_flags only emits --device/--huge-pages for the values
  Joshua actually accepts (auto/cpu/metal/cuda and transparent/2mb/1gb/
  huge), so a hostile free-form config value can no longer inject shell
  syntax into the spawn command.  Numeric fields were already safe.
- Joshua's spawn no longer declares failure when the server has simply
  not answered yet: after the health-probe window it checks whether the
  spawned process is still alive and reports "still loading" if so,
  reserving the error for a process that actually exited.  Switching to
  a large model no longer shows a scary error while the model loads fine
  moments later.
- llama.cpp remove resolves the model name back to the scanned on-disk
  path (file stem, possibly inside a per-repo subdirectory) before
  deleting, and fails with the available list when nothing matches —
  previously it rm -f'd a non-existent path and reported phantom
  success while the file stayed on disk.
- ProviderModels.models/.error gain the doc comments the style guide
  requires for public fields.

* fix(engines): address Devin review findings (protocol, start, stop, ctx)

Protocol: the enriched payloads now ride in new frames with new pinned
discriminants instead of widening existing messages — the wire format is
positional bincode and deserialize_frame rejects trailing bytes, so adding
fields to ProviderModelListResult/EngineInfoDto broke older peers (the
project's own protocol rule: new capabilities need new frames).
EngineConfigList (after EngineListResult) and ProviderModelLoadedList
(after ProviderModelListResult) carry the engine configs and the loaded-
model markers; the old frames are byte-identical to before.

Joshua start: only a server on the engine's own configured port counts as
'already running'.  A joshua started outside RustyClaw on another port no
longer prevents the configured server from starting (or Restart from
bringing it back).

llama.cpp stop: reports honestly — 'no llama-server is running on port N'
when nothing matched, instead of always claiming success while a detected
server on another port keeps serving.

llama.cpp start: applies the typed context window (--ctx-size) on manual
starts too, matching the auto-start path.

Clients gain minimal arms for the two new frames here; the desktop/TUI
consume them in their UI PRs.

* fix(engines): address second-round Devin review findings

- Joshua spawn: the 'process exited' hard failure is only reported on
  Linux, where process inspection exists; on other platforms a slow load
  falls back to the informational 'may still be loading' instead of a
  false failure.  Same for the llama.cpp stop check.
- llama.cpp auto-start: the service definition always passes the resolved
  --port (not only when one is configured), so the port-scoped stop can
  actually identify auto-started servers instead of silently matching
  nothing.
- llama.cpp stop: on non-Linux (no process inspection) it falls back to
  stopping every llama-server rather than claiming success while one
  keeps running.
- llama.cpp start: the configured models directory is interpolated via
  sh_quote instead of naive single quotes.
- running_server_cmdlines: no unused-variable warning on non-Linux
  builds (cfg_attr allow with the Linux-only body).
- Joshua load: 'already loaded' is only claimed when the configured
  endpoint actually serves the model; a joshua started outside RustyClaw
  on another port no longer makes Load report success while the
  configured server has nothing loaded.

* fix(engines): address third-round Devin review findings

- Bump WIRE_PROTOCOL_VERSION to 4: EngineConfig gained its typed parameter
  fields (the shipped EngineConfigSet payload widened positionally) and the
  EngineConfigList / ProviderModelLoadedList frames are new, so a mismatched
  peer must fail at the first affected frame instead of mis-parsing.
- Joshua: sh_quote the model path and every extra_arg interpolated into the
  serve command line; server_process_alive is Linux-only (dead code and an
  unused parameter on other platforms).
- llama.cpp: stop() keeps its port/pattern bindings inside the Linux block
  (no unused-variable warnings on macOS/non-Linux); start() sh_quotes
  extra_args; load() falls back to the typed context_length.
- engine_start_command: llama.cpp built-in flags now come first so a
  hand-written --port/--ctx-size in extra_args overrides them (auto-start).
- Ollama: load() applies the typed context_length (--num-ctx) when no
  per-load override is given; list_models marks models resident per /api/ps
  instead of never marking anything loaded.
- LM Studio: list_models no longer claims every listed model is running.

* fix(engines): address fourth-round Devin review findings

- llama.cpp list_models: a model served by a running llama-server but
  living outside the scanned models dir (e.g. started manually with an
  explicit --model path) was dropped from the list — loaded ids are now
  surfaced like Joshua's list_models does.
- Port-scoped stop/liveness patterns terminate the digit run (joshua
  pkill '...127.0.0.1:{port}( |$)' and llamacpp '...--port {port}( |$)', and
  joshua's server_process_alive now requires a non-digit after the port),
  so a short port (1234/808) can no longer match a server on 12345/8080.
- Engine status only reports Running when a detected server sits on the
  engine's own configured port (or the configured endpoint answers); a
  foreign server on another port no longer hides the Start button or
  turns Stop into a no-op that claims success.
- validate_engine_config() rejects structurally unusable configs (port 0)
  before they are persisted or reach the pkill/pgrep patterns; the
  gateway's EngineConfigSet handler refuses and reports the error instead
  of relying on the port's u16 type alone.

* feat(desktop): local engines dialog, parameters editor, and running-model pickers

The Local Engines & Models dialog becomes a real management surface and
the composer picker reflects what is actually available locally:

- Each engine tab gains a Parameters editor persisted via EngineConfigSet
  (context window, device, huge pages, mmap, lazy weights, max output
  tokens, max concurrency, default model picked from the local model
  list, auto-start).  Applied on the next Start/Load, with a Restart
  button to apply immediately.
- The model table marks loaded models as "running" (from the engine
  registry's host process detection) alongside on-disk ones, and the
  composer's model dropdown appends a "● running" marker to models the
  local engine reports as loaded.
- Load/Unload give real feedback: the clicked row's button shows
  "Loading…" until the gateway answers, and the outcome (success or an
  honest error) is rendered inline in the dialog, dismissible.
- The dialog auto-loads the active engine's model list when it opens,
  instead of waiting for a tab click.
- The engines dialog's EngineModelAction now carries per-model context
  overrides for Joshua (--n-ctx).

* fix(desktop): address Devin review findings

- Engine configs and loaded-model markers now arrive in their own frames
  (EngineConfigList / ProviderModelLoadedList) matching the protocol
  change on the backend: the panel entries get their config patched from
  EngineConfigList, and the picker's running markers come from
  ProviderModelLoadedList.
- Load actions honour the context window saved in the engine parameters
  (the gateway maps it per engine: --n-ctx / --ctx-size / --num-ctx), so
  the saved value actually applies for llama.cpp and Ollama instead of
  being silently ignored.
- The Restart button now works for engines with an unrelated server
  running on another port (backend start guard is port-scoped); it stops
  and restarts the engine's own server.

* style(desktop): satisfy rustfmt in the loaded-markers handler

* fix(desktop): address third-round Devin review findings

- A load/unload action whose answer never arrives (dropped connection)
  left the model buttons stuck on Loading… forever: the Disconnected
  handler now clears the in-flight action marker and its result.
- Saving engine parameters no longer blanks out enabled/endpoint/port/
  models_dir/extra_args when the EngineConfigList snapshot has not
  arrived: the Save button is disabled (with a hint) until the real
  configs are in, since the panel entries are placeholders before that.
- Drop the stray, unreachable global_settings.rs dialog from the branch
  (it was never declared as a module and references types that do not
  exist); the on-disk copy is left untouched.
@rexlunae
rexlunae deleted the feat/local-engines-backend branch August 18, 2026 16:45
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