feat(config): expose hot-reload API for GUI integration#3715
Merged
Conversation
Adds HTTP endpoints and protocol plumbing so GUI clients can read, persist, and hot-reload TUI configuration without restarting the engine: - `GET /v1/config` returns the full GUI-facing config snapshot - `POST /v1/config/set` persists a single key without mutating the in-memory state (caller POSTs /v1/config/reload to apply) - `POST /v1/config/reload` re-reads the on-disk config, updates the live Arc<RwLock<Config>>, and syncs running engines via Op::SetModel / Op::SetCompaction / Op::SetSubagentRuntimeConfig - `app/config/reload` mirrors the same flow over the stdio app-server protocol; Runtime::update_config / reload_config_and_policy push fresh snapshots into the headless Runtime on every set/unset Notable fixes folded in: - `--config <path>` is now preserved end-to-end across reload and persistence so GUI edits target the same file the server booted from instead of falling back to default discovery - `subagents_enabled` and `subagents_max_depth` now persist correctly via persist_subagents_bool_key / persist_subagents_integer_key - `max_history` rejects non-integer input with 400 instead of silently falling back to the default - RuntimeThreadManager.config moved from Config to Arc<RwLock<Config>> so reloads are visible to active threads; all read sites go through read_config() or a snapshot clone Tests: 14 new runtime_api::tests cover the set/get/reload contract, validation errors, subagent edge cases, persist:false dry-run, and multi-key accumulated reloads. Existing 64 runtime_api + 45 app-server tests still pass. Signed-off-by: Ben Gao <bengao168@msn.com>
Resolve 3 conflicts so PR Hmbown#3715 merges cleanly: - app-server/src/lib.rs (ConfigSet/ConfigUnset): keep BOTH the PR's runtime.update_config() hot-reload for the in-process Runtime AND main's invalidate_stdio_bridge() for the stdio bridge child. - runtime_threads.rs: use the PR's cfg.* snapshot style (self.config is now Arc<RwLock<Config>>) and add main's new moraine_fallback field. Verified: cargo check --workspace --all-features, 78 runtime_api tests, 47 app-server tests all pass.
The config hot-reload PR expands process_app_request enough that the macOS test runner can overflow while exercising stdio goal methods in the full app-server suite. Route stdio app requests through a small helper and box the app-request future so the dispatcher does not carry the full app response state machine on the caller stack. Verified with cargo fmt --all -- --check and cargo test -p codewhale-app-server --lib --locked. Signed-off-by: Hunter B <hmbown@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds HTTP endpoints and protocol plumbing so GUI clients (CodeWhale VSCode) can read, persist, and hot-reload TUI configuration without restarting the engine. This unblocks the GUI
/configpanel — saving a setting in the GUI now takes effect on the next prompt turn in the running TUI engine.New endpoints (runtime API,
--port)GET /v1/configreturns the full GUI-facing config snapshot (model, provider, approval mode, subagents, MCP path, settings-backed keys, …)POST /v1/config/setpersists a single key toconfig.tomlorsettings.tomlwithout mutating the in-memory state — the caller POSTs/v1/config/reloadto applyPOST /v1/config/reloadre-reads the on-disk config, swaps the liveArc<RwLock<Config>>, and syncs running engines viaOp::SetModel/Op::SetCompaction/Op::SetSubagentRuntimeConfigNew app-server protocol
AppRequest::ConfigReload+app/config/reloadroute — mirrors the HTTP reload over stdioRuntime::update_config/Runtime::reload_config_and_policypush fresh snapshots into the headless Runtime on every set/unsetFixes folded in
--config <path>preserved end-to-end:RuntimeApiState.config_pathflows from CLI →Config::load→ allconfig_persistence::persist_*calls. Previously a custom--configpath was lost on reload/persist, causing GUI edits to silently target the default discovery path instead of the file the server booted from.subagents_enabledandsubagents_max_depthpersist correctly viapersist_subagents_bool_key/persist_subagents_integer_key. Previously these two keys were rejected as unknown.max_historyreturns 400 for non-integer input instead of silently falling back to100viaunwrap_or.RuntimeThreadManager.configmoved fromConfigtoArc<RwLock<Config>>so reloads are visible to active threads. All 3 read sites go throughread_config()or a snapshot clone, avoiding held guards across await points.Testing
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-features(no new warnings; 2 pre-existing&*configderef warnings fixed)cargo test --workspace --all-features(78 runtime_api::tests + 45 app-server tests pass)New tests (14 in
runtime_api::tests)set_configvalidates input formax_history,subagents_enabled,subagents_max_depth(rejects non-integer / non-bool with 400)set_configrejects unknown keys with 400set_configwithpersist:falseis a true dry run — disk file unchangedset_configwith--config <path>writes to the specified file (not default discovery)set_configforsubagents_enabled=falseandsubagents_max_depthbelow ceiling persist correctlyset_configresponse contains all expected fields (key/value/persisted/requires_reload)reload_configreturns success, refreshesmcp_config_path, reads fromconfig_path(not default), returns 500 on malformed TOMLreload_configapplies multiple accumulated persisted keys in one shot (model + max_depth + enabled)Checklist
Runtime::update_config/reload_config_and_policyexplain what is and isn't refreshed)