Skip to content

Commit be65d75

Browse files
authored
feat: support idempotent clarification dismiss over stdio (A6) (#361)
1 parent 3398610 commit be65d75

4 files changed

Lines changed: 32 additions & 9 deletions

File tree

src-tauri/Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/crates/altai-cli/src/serve/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,12 +440,18 @@ pub async fn run(workspace: WorkspacePaths) -> Result<(), String> {
440440
.and_then(|value| value.as_object().cloned())
441441
.unwrap_or_default();
442442
let chat_id = params.get("chat_id").and_then(Value::as_str).unwrap_or("");
443+
let action = params.get("action").and_then(Value::as_str).unwrap_or("reply");
443444
let text = params.get("text").and_then(Value::as_str).unwrap_or("");
444-
if chat_id.trim().is_empty() || chat_id.len() > 256 || text.trim().is_empty() || text.len() > 16_384 {
445+
if chat_id.trim().is_empty() || chat_id.len() > 256 || !matches!(action, "reply" | "dismiss") || (action == "reply" && (text.trim().is_empty() || text.len() > 16_384)) {
445446
respond(&writer, id, None, Some(error_value(-32602, "invalid_clarification_response"))).await?;
446447
continue;
447448
}
448-
match host.deliver_clarification_reply(chat_id, text.to_string()).await {
449+
let result = if action == "dismiss" {
450+
host.dismiss_clarification(chat_id).await
451+
} else {
452+
host.deliver_clarification_reply(chat_id, text.to_string()).await
453+
};
454+
match result {
449455
Ok(()) => respond(&writer, id, Some(json!({"accepted": true})), None).await?,
450456
Err(error) => respond(&writer, id, None, Some(error_value(-32002, &error))).await?,
451457
}

src-tauri/crates/altai-cli/src/stdio_host.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ impl StdioHost {
8686
}
8787
}
8888

89+
/// Dismiss a live clarification exactly once.
90+
pub async fn dismiss_clarification(&self, chat_id: &str) -> Result<(), String> {
91+
let workspace_root = self.workspace.root.to_string_lossy().to_string();
92+
let services = self.workspace_bundle_inner(&workspace_root).await?;
93+
let session_key = isanagent::bus::clarification_session_key("stdio", chat_id, None);
94+
if services
95+
.clarification_hub
96+
.cancel_wait_if_pending(&session_key)
97+
{
98+
Ok(())
99+
} else {
100+
Err("clarification_not_pending".to_string())
101+
}
102+
}
103+
89104
async fn workspace_bundle_inner(
90105
&self,
91106
workspace_root: &str,

src-tauri/crates/altai-cli/tests/serve_stdio.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,10 @@ fn clarification_response_rejects_a_non_pending_ticket() {
151151
assert_eq!(process.next()["id"], 1);
152152
process.frame(json!({"jsonrpc":"2.0","id":2,"method":"clarification/respond","params":{"chat_id":"chat-test","text":"yes"}}));
153153
assert_eq!(process.next()["error"]["message"], "clarification_not_pending");
154-
process.frame(json!({"jsonrpc":"2.0","id":3,"method":"shutdown"}));
155-
assert_eq!(process.next()["id"], 3);
154+
process.frame(json!({"jsonrpc":"2.0","id":3,"method":"clarification/respond","params":{"chat_id":"chat-test","action":"dismiss"}}));
155+
assert_eq!(process.next()["error"]["message"], "clarification_not_pending");
156+
process.frame(json!({"jsonrpc":"2.0","id":4,"method":"shutdown"}));
157+
assert_eq!(process.next()["id"], 4);
156158
let _stderr = process.shutdown();
157159
}
158160

0 commit comments

Comments
 (0)