Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- `zeph-plugins`: replaced blocking `std::fs::write` / `std::fs::read_to_string` calls in the
async `update_one_plugin` with `tokio::fs` equivalents, preventing Tokio thread starvation
during concurrent auto-update checks (closes #4560).
- `apply_response_cache`: hourly cleanup background task now exits cleanly when the session shuts
down. The loop uses `tokio::select!` on a `CancellationToken` child of `mem_cancel`, which is
already cancelled via `shutdown_rx` at session teardown. Closes #4572.
- `runner`: TUI early-status-forwarder `JoinHandle` annotated as intentionally dropped at block
end (self-terminating when the channel closes). Removes misleading bare `let _` and satisfies
`clippy::let_underscore_future`. Closes #4571.

### Added

Expand Down
26 changes: 19 additions & 7 deletions src/agent_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ pub(crate) fn apply_response_cache<C: Channel>(
ttl_secs: u64,
semantic_cache_enabled: bool,
embed_model: String,
cancel: tokio_util::sync::CancellationToken,
) -> Agent<C> {
if !enabled {
if semantic_cache_enabled {
Expand All @@ -681,11 +682,18 @@ pub(crate) fn apply_response_cache<C: Channel>(
let mut interval = tokio::time::interval(std::time::Duration::from_hours(1));
interval.tick().await; // skip immediate first tick
loop {
interval.tick().await;
match cache_clone.cleanup(&embed_model).await {
Ok(n) if n > 0 => tracing::debug!("cleaned up {n} cache entries"),
Ok(_) => {}
Err(e) => tracing::warn!("response cache cleanup failed: {e:#}"),
tokio::select! {
() = cancel.cancelled() => {
tracing::debug!("response cache cleanup loop: shutting down");
break;
}
_ = interval.tick() => {
match cache_clone.cleanup(&embed_model).await {
Ok(n) if n > 0 => tracing::debug!("cleaned up {n} cache entries"),
Ok(_) => {}
Err(e) => tracing::warn!("response cache cleanup failed: {e:#}"),
}
}
}
}
});
Expand Down Expand Up @@ -1702,7 +1710,9 @@ mod tests {
let db_url = format!("sqlite:{}", tmp.path().display());
let pool = zeph_db::sqlx::SqlitePool::connect(&db_url).await.unwrap();
let agent = make_agent();
let result = apply_response_cache(agent, false, pool, 300, false, "embed-model".into());
let cancel = tokio_util::sync::CancellationToken::new();
let result =
apply_response_cache(agent, false, pool, 300, false, "embed-model".into(), cancel);
drop(result);
}

Expand All @@ -1712,7 +1722,9 @@ mod tests {
let db_url = format!("sqlite:{}", tmp.path().display());
let pool = zeph_db::sqlx::SqlitePool::connect(&db_url).await.unwrap();
let agent = make_agent();
let result = apply_response_cache(agent, true, pool, 300, false, "embed-model".into());
let cancel = tokio_util::sync::CancellationToken::new();
let result =
apply_response_cache(agent, true, pool, 300, false, "embed-model".into(), cancel);
drop(result);
}

Expand Down
2 changes: 2 additions & 0 deletions src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,7 @@ pub(crate) async fn run(cli: Cli) -> anyhow::Result<()> {
// dropped at the end of bootstrap. The TUI thread observes the channel close and
// shuts down independently, so explicit abort is not needed. Dropping the handle
// is intentional — we have no cleanup to do on the bootstrap error path here.
// EXEMPT: self-terminating on channel close — handle dropped intentionally at block end
let _early_status_forwarder = tokio::spawn(crate::tui_bridge::forward_status_to_tui(
status_rx,
early.agent_tx.clone(),
Expand Down Expand Up @@ -2459,6 +2460,7 @@ pub(crate) async fn run(cli: Cli) -> anyhow::Result<()> {
config.llm.response_cache_ttl_secs,
config.llm.semantic_cache_enabled,
crate::bootstrap::effective_embedding_model(config),
mem_cancel.child_token(),
);
let agent = agent_setup::apply_cost_tracker(agent, config);
let agent = agent_setup::apply_summary_provider(agent, summary_provider);
Expand Down
Loading