fix local office resource editing
- add local-folder OnlyOffice sign/callback writeback and edit-tab handling - align main resource tabs, attachment edit menu, slash isolation, and filetree context behavior - record Sidex/Hermes gap reviews and Reasonix task checklists
This commit is contained in:
@@ -196,6 +196,7 @@ pub fn acp_event_to_sse(event: AcpSessionEvent) -> Option<SseEvent> {
|
||||
kind,
|
||||
status,
|
||||
raw_input,
|
||||
locations,
|
||||
} => Some(SseEvent {
|
||||
event: "tool.started".into(),
|
||||
data: json!({
|
||||
@@ -204,6 +205,7 @@ pub fn acp_event_to_sse(event: AcpSessionEvent) -> Option<SseEvent> {
|
||||
"kind": kind,
|
||||
"status": status,
|
||||
"input": raw_input,
|
||||
"locations": locations,
|
||||
}),
|
||||
}),
|
||||
AcpSessionEvent::ToolCallUpdate {
|
||||
@@ -238,25 +240,30 @@ pub fn acp_event_to_sse(event: AcpSessionEvent) -> Option<SseEvent> {
|
||||
tool_name,
|
||||
params,
|
||||
decision,
|
||||
} => Some(SseEvent {
|
||||
event: if decision == "allowed" {
|
||||
"permission.allowed".into()
|
||||
} else {
|
||||
"permission.denied".into()
|
||||
},
|
||||
data: json!({
|
||||
"permissionId": permission_id,
|
||||
"toolName": tool_name,
|
||||
"params": params,
|
||||
"decision": decision,
|
||||
}),
|
||||
} => {
|
||||
let event = match decision.as_str() {
|
||||
"allowed" => "permission.allowed",
|
||||
"requested" => "permission.requested",
|
||||
_ => "permission.denied",
|
||||
};
|
||||
Some(SseEvent {
|
||||
event: event.into(),
|
||||
data: json!({
|
||||
"permissionId": permission_id,
|
||||
"toolName": tool_name,
|
||||
"params": params,
|
||||
"decision": decision,
|
||||
}),
|
||||
})
|
||||
}
|
||||
AcpSessionEvent::SessionInfoUpdate { title } => Some(SseEvent {
|
||||
event: "session.info.updated".into(),
|
||||
data: json!({ "title": title }),
|
||||
}),
|
||||
AcpSessionEvent::PlanUpdate { entries } => Some(SseEvent {
|
||||
event: "plan.updated".into(),
|
||||
data: json!({ "entries": entries }),
|
||||
}),
|
||||
AcpSessionEvent::SessionInfoUpdate { .. } => {
|
||||
None // Not forwarded to frontend
|
||||
}
|
||||
AcpSessionEvent::PlanUpdate { .. } => {
|
||||
None // Not forwarded (Phase C)
|
||||
}
|
||||
AcpSessionEvent::Disconnected { reason } if reason == "session closed" => None,
|
||||
AcpSessionEvent::Disconnected { reason } => Some(SseEvent {
|
||||
event: "run.failed".into(),
|
||||
@@ -327,6 +334,37 @@ mod tests {
|
||||
assert_eq!(sse.data["decision"], "denied");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_permission_requested_emits_permission_requested_event() {
|
||||
let event = AcpSessionEvent::PermissionRequest {
|
||||
permission_id: "perm_2".into(),
|
||||
tool_name: "mnote.page.get".into(),
|
||||
params: json!({"documentId": "doc_2"}),
|
||||
decision: "requested".into(),
|
||||
};
|
||||
|
||||
let sse = acp_event_to_sse(event).expect("permission requested should be forwarded");
|
||||
assert_eq!(sse.event, "permission.requested");
|
||||
assert_eq!(sse.data["permissionId"], "perm_2");
|
||||
assert_eq!(sse.data["toolName"], "mnote.page.get");
|
||||
assert_eq!(sse.data["decision"], "requested");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_permission_allowed_emits_permission_allowed_event() {
|
||||
let event = AcpSessionEvent::PermissionRequest {
|
||||
permission_id: "perm_3".into(),
|
||||
tool_name: "mnote.page.save".into(),
|
||||
params: json!({"documentId": "doc_1"}),
|
||||
decision: "allowed".into(),
|
||||
};
|
||||
|
||||
let sse = acp_event_to_sse(event).expect("permission allowed should be forwarded");
|
||||
assert_eq!(sse.event, "permission.allowed");
|
||||
assert_eq!(sse.data["permissionId"], "perm_3");
|
||||
assert_eq!(sse.data["decision"], "allowed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_tool_events_keep_detail_for_collapsible_ui() {
|
||||
let started = acp_event_to_sse(AcpSessionEvent::ToolCall {
|
||||
@@ -335,12 +373,18 @@ mod tests {
|
||||
kind: "read".into(),
|
||||
status: crate::acp_types::ToolCallStatus::InProgress,
|
||||
raw_input: Some(json!({"documentId": "doc_1", "includeBody": true})),
|
||||
locations: vec!["/mnt/Data1T/mnote/src/main.rs".into()],
|
||||
})
|
||||
.expect("tool start");
|
||||
assert_eq!(started.event, "tool.started");
|
||||
assert_eq!(started.data["tool"], "mnote.page.get");
|
||||
assert_eq!(started.data["status"], "in_progress");
|
||||
assert_eq!(started.data["input"]["documentId"], "doc_1");
|
||||
assert_eq!(
|
||||
started.data["locations"][0],
|
||||
"/mnt/Data1T/mnote/src/main.rs"
|
||||
);
|
||||
assert_eq!(started.data["locations"].as_array().unwrap().len(), 1);
|
||||
|
||||
let completed = acp_event_to_sse(AcpSessionEvent::ToolCallUpdate {
|
||||
tool_call_id: "tool_1".into(),
|
||||
@@ -367,4 +411,34 @@ mod tests {
|
||||
assert_eq!(running.event, "tool.started");
|
||||
assert_eq!(running.data["status"], "in_progress");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_session_info_update_emits_session_info_updated_sse() {
|
||||
let sse = acp_event_to_sse(AcpSessionEvent::SessionInfoUpdate {
|
||||
title: "我的新会话标题".into(),
|
||||
})
|
||||
.expect("session info update should be forwarded");
|
||||
assert_eq!(sse.event, "session.info.updated");
|
||||
assert_eq!(sse.data["title"], "我的新会话标题");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_plan_update_emits_plan_updated_sse() {
|
||||
let entries = vec![
|
||||
"步骤 1:读取文件".into(),
|
||||
"步骤 2:修改配置".into(),
|
||||
"步骤 3:验证更改".into(),
|
||||
];
|
||||
let sse = acp_event_to_sse(AcpSessionEvent::PlanUpdate {
|
||||
entries: entries.clone(),
|
||||
})
|
||||
.expect("plan update should be forwarded");
|
||||
assert_eq!(sse.event, "plan.updated");
|
||||
let sse_entries: Vec<String> =
|
||||
serde_json::from_value(sse.data["entries"].clone()).unwrap_or_default();
|
||||
assert_eq!(sse_entries.len(), 3);
|
||||
assert_eq!(sse_entries[0], "步骤 1:读取文件");
|
||||
assert_eq!(sse_entries[1], "步骤 2:修改配置");
|
||||
assert_eq!(sse_entries[2], "步骤 3:验证更改");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user