feat: 拆分 worker 架构并增强 assistant 安全保护

- 新增 daemon/channel/assistant/function/scheduler/logging worker 架构和 JetStream 消息协议

- 修复邮件通知误入 assistant、迁移 checksum、日志 subject 和工具调用无限循环风险

- 新增 assistant 工具轮次熔断、工具结果截断和提示词软收敛规则

- 补充邮件推送关键字、可信发件人配置、升级日志和验证测试
This commit is contained in:
2026-07-10 00:41:36 +08:00
parent 7deae286ca
commit af6cfdaa83
70 changed files with 5953 additions and 1608 deletions
+37
View File
@@ -17,6 +17,8 @@ pub struct MailPollerConfig {
pub source_key: String,
pub notify_account_id: String,
pub notify_user_id: String,
pub notification_subject_keywords: Vec<String>,
pub trusted_senders: Vec<String>,
pub poll_interval: Duration,
pub fetch_limit: usize,
pub accept_invalid_certs: bool,
@@ -35,6 +37,10 @@ impl MailPollerConfig {
mailbox: account.mailbox,
notify_account_id: account.notify_account_id,
notify_user_id: account.notify_user_id,
notification_subject_keywords: normalize_setting_values(
account.notification_subject_keywords,
),
trusted_senders: normalize_setting_values(account.trusted_senders),
poll_interval: Duration::from_secs(account.poll_seconds.max(10) as u64),
fetch_limit: account.fetch_limit.clamp(1, 100) as usize,
accept_invalid_certs: account.accept_invalid_certs,
@@ -83,6 +89,11 @@ impl MailPollerConfig {
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(10)
.clamp(1, 100);
let notification_subject_keywords = optional_env_list(&[
"IA_MAIL_NOTIFICATION_SUBJECT_KEYWORDS",
"IA_MAIL_NOTIFY_SUBJECT_KEYWORDS",
]);
let trusted_senders = optional_env_list(&["IA_MAIL_TRUSTED_SENDERS"]);
Ok(Some(Self {
mail_account_id: None,
@@ -95,6 +106,8 @@ impl MailPollerConfig {
mailbox,
notify_account_id: notify_account_id.unwrap(),
notify_user_id: notify_user_id.unwrap(),
notification_subject_keywords,
trusted_senders,
poll_interval: Duration::from_secs(poll_seconds),
fetch_limit,
accept_invalid_certs: env_bool("IA_MAIL_ACCEPT_INVALID_CERTS", false),
@@ -109,6 +122,30 @@ fn optional_env(name: &str) -> Option<String> {
.filter(|value| !value.is_empty())
}
fn optional_env_list(names: &[&str]) -> Vec<String> {
names
.iter()
.find_map(|name| optional_env(name))
.map(|value| parse_setting_list(&value))
.unwrap_or_default()
}
pub(crate) fn parse_setting_list(value: &str) -> Vec<String> {
normalize_setting_values(
value
.split([',', '', ';', '', '\n'])
.map(ToString::to_string),
)
}
pub(crate) fn normalize_setting_values(values: impl IntoIterator<Item = String>) -> Vec<String> {
values
.into_iter()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.collect()
}
fn env_bool(name: &str, default: bool) -> bool {
env::var(name)
.map(|value| {