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:
lix-2026
2026-05-17 20:11:39 +08:00
parent 2ea559beaa
commit bb2f190f50
19 changed files with 1307 additions and 451 deletions
+13 -8
View File
@@ -90,9 +90,7 @@ pub enum DeltaOperation {
block_type: Option<String>,
},
#[serde(rename = "delete")]
DeleteBlock {
block_id: String,
},
DeleteBlock { block_id: String },
#[serde(rename = "move_after")]
MoveBlock {
block_id: String,
@@ -134,12 +132,19 @@ impl EditorRuntimeActor {
.read()
.map_err(|e| WebError::internal(format!("EditorRuntimeActor 锁失败:{e}")))?;
let state = documents.get(document_id).ok_or_else(|| {
WebError::bad_request_code("mnote_editor_document_not_loaded", format!("文档 {document_id} 尚未加载"))
WebError::bad_request_code(
"mnote_editor_document_not_loaded",
format!("文档 {document_id} 尚未加载"),
)
})?;
let operations = match command {
EditorCommand::ReplaceBlock(cmd) => {
let block = state.document.blocks.iter().find(|b| b.block_id == cmd.block_id);
let block = state
.document
.blocks
.iter()
.find(|b| b.block_id == cmd.block_id);
vec![DeltaOperation::ReplaceBlock {
block_id: cmd.block_id.clone(),
text: block_text_from_block(block),
@@ -254,9 +259,9 @@ impl EditorRuntimeActor {
let changed_blocks = extract_changed_blocks(&state.document, &command);
apply_editor_command_to_document(&mut state.document, command.clone()).map_err(|error| {
WebError::bad_request_code("mnote_editor_command_failed", format!("{error:?}"))
})?;
apply_editor_command_to_document(&mut state.document, command.clone()).map_err(
|error| WebError::bad_request_code("mnote_editor_command_failed", format!("{error:?}")),
)?;
state.revision += 1;
state.conflict_detection_key = format!(