feat: stabilize page AI ACP runtimes
实现并稳定页面 AI 的 ACP Hermes / ACP Reasonix 运行路径。 主要内容: - 分离 profile 与 acpRuntime,ACP Hermes 按所选 Hermes profile 启动并注入 provider key。 - 修复 Reasonix ACP wrapper 的 API key 读取、ToolRegistry 注册、LoopEvent role 映射和 reasoning/final 分流。 - 修复 ACP agent_thought_chunk 被 untagged enum 误解析为 message.delta 的问题,补充 thought 相关单测。 - 补充页面 AI 浏览器验证 skill 证据到 7-15 设计稿,并记录严格验收标准。 - 同步提交当前仓库中已存在的 rust-web / Hermes tools / SSE / bug 文档相关改动。 验证: - node --check scripts/reasonix-acp-wrapper.mjs - cargo test -p mnote-web acp -- --nocapture - 页面 AI ACP 浏览器验证:tmp/page-ai-acp-browser-UAYwyM/
This commit is contained in:
@@ -956,9 +956,10 @@ fn compute_next_content_via_actor(
|
||||
}
|
||||
|
||||
// 在内存中 apply
|
||||
let _apply_result = state
|
||||
.editor_actor
|
||||
.apply_command(&document_id, command.clone(), command_name)?;
|
||||
let _apply_result =
|
||||
state
|
||||
.editor_actor
|
||||
.apply_command(&document_id, command.clone(), command_name)?;
|
||||
|
||||
// 从 actor 获取 legacy content(用于 Convex save 的 payload)
|
||||
let content = state.editor_actor.legacy_content_for_save(&document_id)?;
|
||||
|
||||
@@ -108,8 +108,7 @@ pub async fn doc_fetch(
|
||||
.map(|t| t == "heading")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(hl) =
|
||||
block.pointer("/attrs/level").and_then(Value::as_u64)
|
||||
if let Some(hl) = block.pointer("/attrs/level").and_then(Value::as_u64)
|
||||
{
|
||||
if hl <= level {
|
||||
end = idx;
|
||||
@@ -206,7 +205,10 @@ pub async fn doc_fetch(
|
||||
"maxChars": max_chars
|
||||
}));
|
||||
}
|
||||
let source = if document_id.starts_with('/') || document_id.starts_with("./") || document_id.contains('/') {
|
||||
let source = if document_id.starts_with('/')
|
||||
|| document_id.starts_with("./")
|
||||
|| document_id.contains('/')
|
||||
{
|
||||
"local_fs"
|
||||
} else {
|
||||
"convex"
|
||||
@@ -901,9 +903,15 @@ fn search_replace(text: &str, search: &str, replace: &str) -> Result<String, Str
|
||||
s.trim()
|
||||
.chars()
|
||||
.map(|ch| match ch {
|
||||
'A'..='Z' => ((ch as u32).saturating_sub('A' as u32) + 'A' as u32).try_into().unwrap_or(ch),
|
||||
'a'..='z' => ((ch as u32).saturating_sub('a' as u32) + 'a' as u32).try_into().unwrap_or(ch),
|
||||
'0'..='9' => ((ch as u32).saturating_sub('0' as u32) + '0' as u32).try_into().unwrap_or(ch),
|
||||
'A'..='Z' => ((ch as u32).saturating_sub('A' as u32) + 'A' as u32)
|
||||
.try_into()
|
||||
.unwrap_or(ch),
|
||||
'a'..='z' => ((ch as u32).saturating_sub('a' as u32) + 'a' as u32)
|
||||
.try_into()
|
||||
.unwrap_or(ch),
|
||||
'0'..='9' => ((ch as u32).saturating_sub('0' as u32) + '0' as u32)
|
||||
.try_into()
|
||||
.unwrap_or(ch),
|
||||
'\u{3000}' => ' ',
|
||||
_ => ch,
|
||||
})
|
||||
@@ -919,7 +927,11 @@ fn search_replace(text: &str, search: &str, replace: &str) -> Result<String, Str
|
||||
"{}{}{}",
|
||||
&line[..line.char_indices().nth(start).map(|(i, _)| i).unwrap_or(0)],
|
||||
replace,
|
||||
&line[line.char_indices().nth(end).map(|(i, _)| i).unwrap_or(line.len())..]
|
||||
&line[line
|
||||
.char_indices()
|
||||
.nth(end)
|
||||
.map(|(i, _)| i)
|
||||
.unwrap_or(line.len())..]
|
||||
);
|
||||
return Ok(text.replacen(line, &replaced, 1));
|
||||
}
|
||||
@@ -933,7 +945,14 @@ fn search_replace(text: &str, search: &str, replace: &str) -> Result<String, Str
|
||||
}
|
||||
}
|
||||
// Level 4: 失败
|
||||
Err(format!("无法匹配 \"{}\"", if search.len() > 60 { format!("{}...", &search[..60]) } else { search.to_string() }))
|
||||
Err(format!(
|
||||
"无法匹配 \"{}\"",
|
||||
if search.len() > 60 {
|
||||
format!("{}...", &search[..60])
|
||||
} else {
|
||||
search.to_string()
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
fn fuzzy_match(text: &str, pattern: &str, max_diff_ratio: f64) -> bool {
|
||||
@@ -977,10 +996,7 @@ fn build_block_ops_from_markdown_edit(
|
||||
|
||||
if let Some(block) = matched_block {
|
||||
let block_id = block_id_of(block).unwrap_or_default();
|
||||
let block_text = block
|
||||
.get("text")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("");
|
||||
let block_text = block.get("text").and_then(Value::as_str).unwrap_or("");
|
||||
let new_text = block_text.replacen(search, replace, 1);
|
||||
block_ops.push(json!({
|
||||
"op": "replace",
|
||||
|
||||
Reference in New Issue
Block a user