fix(review): 对抗性审查修复——spawn 竞态窗口、max_run_secs 配置化、删除计数一致性
- 子代理 spawn 顺序调整为先注册 cancel_registry 再写 DB running 记录,消除 reap 误杀刚启动子代理的微秒级竞态窗口 - max_run_secs 接入 AgentConfig 配置项(config.json agents 段),不再硬编码;From 透传 + 全部字面构造点补齐 - delete_messages_by_ids 删除后按活 COUNT 重算 sessions/topics 的 message_count,修复计数列漂移 - cli init 修改 agent 路径保留既有 max_run_secs 而非重置默认
This commit is contained in:
parent
452c5ad0ef
commit
6ac653bf51
@ -2348,6 +2348,7 @@ mod tests {
|
|||||||
max_tool_iterations: 1,
|
max_tool_iterations: 1,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1410,6 +1410,7 @@ mod tests {
|
|||||||
max_tool_iterations: 100,
|
max_tool_iterations: 100,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 2_000,
|
context_tool_result_trim_chars: 2_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 10,
|
max_images_in_context: 10,
|
||||||
max_image_age_rounds: 50,
|
max_image_age_rounds: 50,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,7 +8,7 @@ pub use agent_loop::{
|
|||||||
PersistingEmittedMessageHandler, SkillProvider,
|
PersistingEmittedMessageHandler, SkillProvider,
|
||||||
};
|
};
|
||||||
pub use context_compressor::ContextCompressor;
|
pub use context_compressor::ContextCompressor;
|
||||||
pub use runtime_config::AgentRuntimeConfig;
|
pub use runtime_config::{AgentRuntimeConfig, DEFAULT_MAX_RUN_SECS};
|
||||||
pub use system_prompt::{
|
pub use system_prompt::{
|
||||||
CompositeSystemPromptProvider, SystemPrompt, SystemPromptContext, SystemPromptProvider,
|
CompositeSystemPromptProvider, SystemPrompt, SystemPromptContext, SystemPromptProvider,
|
||||||
generate_system_env_prompt,
|
generate_system_env_prompt,
|
||||||
|
|||||||
@ -51,7 +51,7 @@ impl From<LLMProviderConfig> for AgentRuntimeConfig {
|
|||||||
max_images_in_context: config.max_images_in_context,
|
max_images_in_context: config.max_images_in_context,
|
||||||
max_image_age_rounds: config.max_image_age_rounds,
|
max_image_age_rounds: config.max_image_age_rounds,
|
||||||
max_retries: config.max_retries,
|
max_retries: config.max_retries,
|
||||||
max_run_secs: DEFAULT_MAX_RUN_SECS,
|
max_run_secs: config.max_run_secs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -564,6 +564,7 @@ impl InitWizard {
|
|||||||
max_tool_iterations: 1000,
|
max_tool_iterations: 1000,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 2000,
|
context_tool_result_trim_chars: 2000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut agents = existing.agents.clone();
|
let mut agents = existing.agents.clone();
|
||||||
@ -629,6 +630,7 @@ impl InitWizard {
|
|||||||
max_tool_iterations: current_agent.max_tool_iterations,
|
max_tool_iterations: current_agent.max_tool_iterations,
|
||||||
tool_result_max_chars: current_agent.tool_result_max_chars,
|
tool_result_max_chars: current_agent.tool_result_max_chars,
|
||||||
context_tool_result_trim_chars: current_agent.context_tool_result_trim_chars,
|
context_tool_result_trim_chars: current_agent.context_tool_result_trim_chars,
|
||||||
|
max_run_secs: current_agent.max_run_secs,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut agents = existing.agents.clone();
|
let mut agents = existing.agents.clone();
|
||||||
|
|||||||
@ -571,6 +571,10 @@ pub struct AgentConfig {
|
|||||||
pub tool_result_max_chars: usize,
|
pub tool_result_max_chars: usize,
|
||||||
#[serde(default = "default_context_tool_result_trim_chars")]
|
#[serde(default = "default_context_tool_result_trim_chars")]
|
||||||
pub context_tool_result_trim_chars: usize,
|
pub context_tool_result_trim_chars: usize,
|
||||||
|
/// 单次 agent run 的墙钟预算(秒)。超时后优雅退出并提示用户可继续。
|
||||||
|
/// 0 表示不限制。默认见 default_max_run_secs。
|
||||||
|
#[serde(default = "default_max_run_secs")]
|
||||||
|
pub max_run_secs: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_max_tool_iterations() -> usize {
|
fn default_max_tool_iterations() -> usize {
|
||||||
@ -597,6 +601,10 @@ fn default_max_retries() -> u32 {
|
|||||||
3
|
3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_max_run_secs() -> u64 {
|
||||||
|
crate::agent::DEFAULT_MAX_RUN_SECS
|
||||||
|
}
|
||||||
|
|
||||||
fn default_mcp_tool_timeout_secs() -> u64 {
|
fn default_mcp_tool_timeout_secs() -> u64 {
|
||||||
300
|
300
|
||||||
}
|
}
|
||||||
@ -982,6 +990,8 @@ pub struct LLMProviderConfig {
|
|||||||
pub max_tool_iterations: usize,
|
pub max_tool_iterations: usize,
|
||||||
pub tool_result_max_chars: usize,
|
pub tool_result_max_chars: usize,
|
||||||
pub context_tool_result_trim_chars: usize,
|
pub context_tool_result_trim_chars: usize,
|
||||||
|
/// 单次 agent run 墙钟预算(秒,透传自 AgentConfig)。0 表示不限制。
|
||||||
|
pub max_run_secs: u64,
|
||||||
/// 图片上下文限制配置
|
/// 图片上下文限制配置
|
||||||
pub max_images_in_context: usize,
|
pub max_images_in_context: usize,
|
||||||
pub max_image_age_rounds: usize,
|
pub max_image_age_rounds: usize,
|
||||||
@ -1130,6 +1140,7 @@ impl Config {
|
|||||||
max_tool_iterations: agent.max_tool_iterations,
|
max_tool_iterations: agent.max_tool_iterations,
|
||||||
tool_result_max_chars: agent.tool_result_max_chars,
|
tool_result_max_chars: agent.tool_result_max_chars,
|
||||||
context_tool_result_trim_chars: agent.context_tool_result_trim_chars,
|
context_tool_result_trim_chars: agent.context_tool_result_trim_chars,
|
||||||
|
max_run_secs: agent.max_run_secs,
|
||||||
max_images_in_context: self.image_context.max_images_in_context,
|
max_images_in_context: self.image_context.max_images_in_context,
|
||||||
max_image_age_rounds: self.image_context.max_image_age_rounds,
|
max_image_age_rounds: self.image_context.max_image_age_rounds,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -129,6 +129,7 @@ mod tests {
|
|||||||
max_tool_iterations: 1,
|
max_tool_iterations: 1,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 20_000,
|
context_tool_result_trim_chars: 20_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,6 +68,7 @@ mod tests {
|
|||||||
max_tool_iterations: 1,
|
max_tool_iterations: 1,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 20_000,
|
context_tool_result_trim_chars: 20_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1372,6 +1372,7 @@ mod tests {
|
|||||||
max_tool_iterations: 1,
|
max_tool_iterations: 1,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
}
|
}
|
||||||
@ -1696,6 +1697,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -1754,6 +1756,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -1833,6 +1836,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -1910,6 +1914,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2051,6 +2056,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2123,6 +2129,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2205,6 +2212,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2296,6 +2304,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2386,6 +2395,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2458,6 +2468,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2540,6 +2551,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
@ -2608,6 +2620,7 @@ mod tests {
|
|||||||
max_retries: 3,
|
max_retries: 3,
|
||||||
tool_result_max_chars: 100_000,
|
tool_result_max_chars: 100_000,
|
||||||
context_tool_result_trim_chars: 100_000,
|
context_tool_result_trim_chars: 100_000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -642,10 +642,30 @@ impl SessionStore {
|
|||||||
}
|
}
|
||||||
let conn = self.pool.get()?;
|
let conn = self.pool.get()?;
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
let mut affected_topics: Vec<String> = Vec::new();
|
||||||
// SQLite 绑定变量上限(SQLITE_MAX_VARIABLE_NUMBER 默认 999),
|
// SQLite 绑定变量上限(SQLITE_MAX_VARIABLE_NUMBER 默认 999),
|
||||||
// 分批构造 IN 子句,避免超限。
|
// 分批构造 IN 子句,避免超限。
|
||||||
for chunk in ids.chunks(500) {
|
for chunk in ids.chunks(500) {
|
||||||
let placeholders = vec!["?"; chunk.len()].join(",");
|
let placeholders = vec!["?"; chunk.len()].join(",");
|
||||||
|
|
||||||
|
// 先收集将被删除行的 topic_id,用于删除后重算 topics.message_count
|
||||||
|
let topic_sql = format!(
|
||||||
|
"SELECT DISTINCT topic_id FROM messages
|
||||||
|
WHERE session_id = ? AND topic_id IS NOT NULL AND id IN ({placeholders})"
|
||||||
|
);
|
||||||
|
let mut topic_params: Vec<&dyn rusqlite::ToSql> = Vec::with_capacity(chunk.len() + 1);
|
||||||
|
topic_params.push(&session_id);
|
||||||
|
for id in chunk {
|
||||||
|
topic_params.push(id);
|
||||||
|
}
|
||||||
|
let mut stmt = conn.prepare(&topic_sql)?;
|
||||||
|
let rows = stmt.query_map(topic_params.as_slice(), |row| {
|
||||||
|
row.get::<_, String>(0)
|
||||||
|
})?;
|
||||||
|
for tid in rows {
|
||||||
|
affected_topics.push(tid?);
|
||||||
|
}
|
||||||
|
|
||||||
let sql = format!(
|
let sql = format!(
|
||||||
"DELETE FROM messages WHERE session_id = ? AND id IN ({placeholders})"
|
"DELETE FROM messages WHERE session_id = ? AND id IN ({placeholders})"
|
||||||
);
|
);
|
||||||
@ -656,6 +676,28 @@ impl SessionStore {
|
|||||||
}
|
}
|
||||||
total += conn.execute(&sql, params_vec.as_slice())?;
|
total += conn.execute(&sql, params_vec.as_slice())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计数列与消息行的一致性:sessions/topics 的 message_count 是增量维护列
|
||||||
|
// (append +1、clear 归零),删除后必须同步。用活 COUNT 重算而非减法,
|
||||||
|
// 自愈历史漂移且不会减为负数。
|
||||||
|
if total > 0 {
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE sessions SET message_count =
|
||||||
|
(SELECT COUNT(*) FROM messages WHERE session_id = ?1)
|
||||||
|
WHERE id = ?1",
|
||||||
|
params![session_id],
|
||||||
|
)?;
|
||||||
|
affected_topics.sort_unstable();
|
||||||
|
affected_topics.dedup();
|
||||||
|
for tid in &affected_topics {
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE topics SET message_count =
|
||||||
|
(SELECT COUNT(*) FROM messages WHERE topic_id = ?1)
|
||||||
|
WHERE id = ?1",
|
||||||
|
params![tid],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(total)
|
Ok(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -981,6 +981,8 @@ fn test_delete_messages_by_ids_removes_only_target_rows() {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
// 消息计数同步修正
|
// 消息计数同步修正(活查询 + 计数列均一致)
|
||||||
assert_eq!(store.get_topic_message_count(&topic.id).unwrap(), 3);
|
assert_eq!(store.get_topic_message_count(&topic.id).unwrap(), 3);
|
||||||
|
let session_after = store.get_session(&session.id).unwrap().unwrap();
|
||||||
|
assert_eq!(session_after.message_count, 3);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -134,6 +134,7 @@ mod tests {
|
|||||||
max_tool_iterations: 1,
|
max_tool_iterations: 1,
|
||||||
tool_result_max_chars: 1000,
|
tool_result_max_chars: 1000,
|
||||||
context_tool_result_trim_chars: 1000,
|
context_tool_result_trim_chars: 1000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
},
|
},
|
||||||
@ -168,6 +169,7 @@ mod tests {
|
|||||||
max_tool_iterations: 1,
|
max_tool_iterations: 1,
|
||||||
tool_result_max_chars: 1000,
|
tool_result_max_chars: 1000,
|
||||||
context_tool_result_trim_chars: 1000,
|
context_tool_result_trim_chars: 1000,
|
||||||
|
max_run_secs: crate::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1039,7 +1039,18 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
|||||||
let topic_id = session.parent_topic_id.clone().unwrap_or_default();
|
let topic_id = session.parent_topic_id.clone().unwrap_or_default();
|
||||||
let task_id = session.id.clone();
|
let task_id = session.id.clone();
|
||||||
|
|
||||||
// 8a. INSERT pending_subagents 记录
|
// 8a. 注册 cancel_registry(必须先于 DB 写入 running 记录)。
|
||||||
|
// reap_orphan_subagents 以"registry 中存在条目"判定执行任务存活,
|
||||||
|
// 因此不变量为:DB 出现 running 记录时 registry 必然已有条目。
|
||||||
|
// 若顺序颠倒,spawn 间隙(DB 已写、registry 未写)的并发 reap
|
||||||
|
// 会把刚启动的子代理误标为 interrupted。
|
||||||
|
let cancel_registry = self.cancel_registry.clone();
|
||||||
|
let cancel_token = tokio_util::sync::CancellationToken::new();
|
||||||
|
cancel_registry
|
||||||
|
.lock()
|
||||||
|
.insert(task_id.clone(), cancel_token.clone());
|
||||||
|
|
||||||
|
// 8b. INSERT pending_subagents 记录
|
||||||
let pending_record = PendingSubagentRecord {
|
let pending_record = PendingSubagentRecord {
|
||||||
task_id: task_id.clone(),
|
task_id: task_id.clone(),
|
||||||
parent_session_id: session.parent_session_id.clone(),
|
parent_session_id: session.parent_session_id.clone(),
|
||||||
@ -1058,7 +1069,7 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8b. tokio::spawn 后台执行子代理
|
// 8c. tokio::spawn 后台执行子代理
|
||||||
let store = self.store.clone();
|
let store = self.store.clone();
|
||||||
let task_repository = self.task_repository.clone();
|
let task_repository = self.task_repository.clone();
|
||||||
let bus = self.bus.clone();
|
let bus = self.bus.clone();
|
||||||
@ -1070,16 +1081,10 @@ impl SubAgentRuntime for DefaultSubAgentRuntime {
|
|||||||
let max_secs = self.effective_max_execution_secs(&def);
|
let max_secs = self.effective_max_execution_secs(&def);
|
||||||
let task_id_for_spawn = task_id.clone();
|
let task_id_for_spawn = task_id.clone();
|
||||||
let semaphore = self.semaphore.clone();
|
let semaphore = self.semaphore.clone();
|
||||||
let cancel_registry = self.cancel_registry.clone();
|
|
||||||
|
|
||||||
// 创建 CancellationToken 并注册到 registry(供 /stop 取消传播)
|
|
||||||
let cancel_token = tokio_util::sync::CancellationToken::new();
|
|
||||||
cancel_registry
|
|
||||||
.lock()
|
|
||||||
.insert(task_id_for_spawn.clone(), cancel_token.clone());
|
|
||||||
|
|
||||||
// RAII guard:spawn 任务退出时(正常/early return/panic)确定性清理 registry
|
// RAII guard:spawn 任务退出时(正常/early return/panic)确定性清理 registry
|
||||||
// 不变量 3:清理与作用域绑定,避免末行清理被 panic 绕过
|
// 不变量 3:清理与作用域绑定,避免末行清理被 panic 绕过
|
||||||
|
// (cancel_token 已在 8a 注册,此处仅绑定生命周期清理)
|
||||||
let registry_guard =
|
let registry_guard =
|
||||||
CancelRegistryGuard::new(task_id_for_spawn.clone(), cancel_registry.clone());
|
CancelRegistryGuard::new(task_id_for_spawn.clone(), cancel_registry.clone());
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,7 @@ fn load_config() -> Option<LLMProviderConfig> {
|
|||||||
max_tool_iterations: 20,
|
max_tool_iterations: 20,
|
||||||
tool_result_max_chars: 20_000,
|
tool_result_max_chars: 20_000,
|
||||||
context_tool_result_trim_chars: 20_000,
|
context_tool_result_trim_chars: 20_000,
|
||||||
|
max_run_secs: picobot::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -47,6 +47,7 @@ fn load_openai_config() -> Option<LLMProviderConfig> {
|
|||||||
max_tool_iterations: 20,
|
max_tool_iterations: 20,
|
||||||
tool_result_max_chars: 20_000,
|
tool_result_max_chars: 20_000,
|
||||||
context_tool_result_trim_chars: 20_000,
|
context_tool_result_trim_chars: 20_000,
|
||||||
|
max_run_secs: picobot::agent::DEFAULT_MAX_RUN_SECS,
|
||||||
max_images_in_context: 1,
|
max_images_in_context: 1,
|
||||||
max_image_age_rounds: 10,
|
max_image_age_rounds: 10,
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user