Skip to content

Commit f323bb0

Browse files
committed
chore(acp): apply chaodu-agent NITs from PR #760
- pool.liveness_check_secs: hoist the recv-loop poll cadence out of a hard-coded const onto PoolConfig so deployments can tune it. Default remains 30s. - adapter: change hard-timeout error message from ({}m) to ({}s) so non-multiple-of-60 ceilings render correctly (e.g. 90s → "(90s)"). - acp/connection: emit a tracing::trace! line when an id-bearing message arrives whose pending entry was already abandoned. Behaviour is unchanged — the adapter recv loop still filters by request_id; this just makes the stale-response path observable at trace level. cargo check + cargo clippy -- -D warnings + cargo test --bin openab all clean (238 passed).
1 parent c3e0095 commit f323bb0

6 files changed

Lines changed: 24 additions & 7 deletions

File tree

config.toml.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ max_sessions = 10
106106
session_ttl_hours = 24
107107
# Hard ceiling (sec) per prompt; see #732. Default: 1800 (30 min).
108108
# prompt_hard_timeout_secs = 1800
109+
# Liveness-check cadence (sec) for the recv loop; see #732. Default: 30.
110+
# liveness_check_secs = 30
109111

110112
[markdown]
111113
tables = "code" # "code" (default) | "bullets" | "off"

src/acp/connection.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
88
use tokio::process::{Child, ChildStdin};
99
use tokio::sync::{mpsc, oneshot, Mutex};
1010
use tokio::task::JoinHandle;
11-
use tracing::{debug, error, info};
11+
use tracing::{debug, error, info, trace};
1212

1313

1414
/// Pick the most permissive selectable permission option from ACP options.
@@ -300,6 +300,10 @@ impl AcpConnection {
300300
let _ = tx.send(msg);
301301
continue;
302302
}
303+
// Stale id (#732): pending was already abandoned. Falls through
304+
// to subscriber forwarding; the adapter recv loop filters by
305+
// request_id so it can't leak into the next prompt.
306+
trace!(request_id = id, "stale id-bearing message after abandon");
303307
}
304308

305309
// Notification → forward to subscriber

src/adapter.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,15 @@ pub trait ChatAdapter: Send + Sync + 'static {
153153

154154
// --- AdapterRouter ---
155155

156-
/// Polling cadence for the recv-loop liveness check (#732).
157-
const LIVENESS_CHECK_INTERVAL: std::time::Duration = std::time::Duration::from_secs(30);
158-
159156
/// Shared logic for routing messages to ACP agents, managing sessions,
160157
/// streaming edits, and controlling reactions. Platform-independent.
161158
pub struct AdapterRouter {
162159
pool: Arc<SessionPool>,
163160
reactions_config: ReactionsConfig,
164161
table_mode: TableMode,
165162
prompt_hard_timeout: std::time::Duration,
163+
/// Polling cadence for the recv-loop liveness check (#732).
164+
liveness_check_interval: std::time::Duration,
166165
}
167166

168167
impl AdapterRouter {
@@ -171,12 +170,14 @@ impl AdapterRouter {
171170
reactions_config: ReactionsConfig,
172171
table_mode: TableMode,
173172
prompt_hard_timeout_secs: u64,
173+
liveness_check_secs: u64,
174174
) -> Self {
175175
Self {
176176
pool,
177177
reactions_config,
178178
table_mode,
179179
prompt_hard_timeout: std::time::Duration::from_secs(prompt_hard_timeout_secs),
180+
liveness_check_interval: std::time::Duration::from_secs(liveness_check_secs),
180181
}
181182
}
182183

@@ -334,6 +335,7 @@ impl AdapterRouter {
334335
let table_mode = self.table_mode;
335336
let tool_display = self.reactions_config.tool_display;
336337
let prompt_hard_timeout = self.prompt_hard_timeout;
338+
let liveness_check_interval = self.liveness_check_interval;
337339

338340
self.pool
339341
.with_connection(thread_key, |conn| {
@@ -407,16 +409,16 @@ impl AdapterRouter {
407409
// Reader saw EOF and already drained pending; nothing to abandon.
408410
None => break,
409411
},
410-
_ = tokio::time::sleep(LIVENESS_CHECK_INTERVAL) => {
412+
_ = tokio::time::sleep(liveness_check_interval) => {
411413
if !conn.alive() {
412414
response_error = Some("Agent process died".into());
413415
conn.abandon_request(request_id).await;
414416
break;
415417
}
416418
if prompt_start.elapsed() > prompt_hard_timeout {
417419
response_error = Some(format!(
418-
"Agent exceeded hard timeout ({}m)",
419-
prompt_hard_timeout.as_secs() / 60,
420+
"Agent exceeded hard timeout ({}s)",
421+
prompt_hard_timeout.as_secs(),
420422
));
421423
conn.abandon_request(request_id).await;
422424
break;

src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ pub struct PoolConfig {
295295
/// next prompt's subscriber.
296296
#[serde(default = "default_prompt_hard_timeout_secs")]
297297
pub prompt_hard_timeout_secs: u64,
298+
/// Polling cadence (seconds) for the recv-loop liveness check (#732).
299+
/// Lower = faster reaction to a dead agent / hard ceiling at the cost of
300+
/// more wakeups while the agent is streaming normally.
301+
#[serde(default = "default_liveness_check_secs")]
302+
pub liveness_check_secs: u64,
298303
}
299304

300305
#[derive(Debug, Clone, Deserialize)]
@@ -402,6 +407,7 @@ fn default_working_dir() -> String { "/tmp".into() }
402407
fn default_max_sessions() -> usize { 10 }
403408
fn default_ttl_hours() -> u64 { 4 }
404409
pub(crate) fn default_prompt_hard_timeout_secs() -> u64 { 30 * 60 }
410+
pub(crate) fn default_liveness_check_secs() -> u64 { 30 }
405411
fn default_true() -> bool { true }
406412

407413
fn emoji_queued() -> String { "👀".into() }
@@ -424,6 +430,7 @@ impl Default for PoolConfig {
424430
max_sessions: default_max_sessions(),
425431
session_ttl_hours: default_ttl_hours(),
426432
prompt_hard_timeout_secs: default_prompt_hard_timeout_secs(),
433+
liveness_check_secs: default_liveness_check_secs(),
427434
}
428435
}
429436
}

src/dispatch.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,7 @@ mod tests {
10461046
crate::config::ReactionsConfig::default(),
10471047
crate::markdown::TableMode::Off,
10481048
crate::config::default_prompt_hard_timeout_secs(),
1049+
crate::config::default_liveness_check_secs(),
10491050
));
10501051
Dispatcher::with_idle_timeout(router, 10, 24_000, grouping, DEFAULT_CONSUMER_IDLE_TIMEOUT)
10511052
}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ async fn main() -> anyhow::Result<()> {
144144
cfg.reactions,
145145
cfg.markdown.tables,
146146
cfg.pool.prompt_hard_timeout_secs,
147+
cfg.pool.liveness_check_secs,
147148
));
148149

149150
// Shutdown signal for Slack adapter

0 commit comments

Comments
 (0)