story-kit: start 62_story_allow_frontend_ui_to_accept_permissions_requests
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Agent Permission Prompts in Web UI
|
||||||
|
test_plan: pending
|
||||||
|
---
|
||||||
|
|
||||||
|
# Story 62: Agent Permission Prompts in Web UI
|
||||||
|
|
||||||
|
## User Story
|
||||||
|
|
||||||
|
As a user interacting with an agent through the web UI, I want to be prompted for permission approvals (e.g. file writes, commits) so that the agent can complete tasks that require elevated permissions without getting blocked.
|
||||||
|
|
||||||
|
Right now, the web UI does not have any way of the user allowing permissions requests, so dev processes are basically blocked.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] When an agent action requires permission (e.g. writing to a file, committing), the web UI surfaces a prompt to the user
|
||||||
|
- [ ] The user can approve or deny the permission request from the UI
|
||||||
|
- [ ] On approval, the agent continues with the requested action
|
||||||
|
- [ ] On denial, the agent receives the denial and adjusts its approach
|
||||||
|
- [ ] Permission prompts display enough context (file path, action type) for the user to make an informed decision
|
||||||
|
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Bulk/blanket permission grants (e.g. "allow all writes to this directory")
|
||||||
|
- Persisting permission decisions across sessions
|
||||||
@@ -369,6 +369,53 @@ fn run_pty_session(
|
|||||||
let _ = writeln!(pty_writer, "{}", response);
|
let _ = writeln!(pty_writer, "{}", response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Claude Code is requesting user approval before executing a tool.
|
||||||
|
// Forward the request to the async context via permission_tx and
|
||||||
|
// block until the user responds (or a 5-minute timeout elapses).
|
||||||
|
"permission_request" => {
|
||||||
|
let request_id = json
|
||||||
|
.get("id")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("")
|
||||||
|
.to_string();
|
||||||
|
let tool_name = json
|
||||||
|
.get("tool_name")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("unknown")
|
||||||
|
.to_string();
|
||||||
|
let tool_input = json
|
||||||
|
.get("input")
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(serde_json::Value::Object(serde_json::Map::new()));
|
||||||
|
|
||||||
|
if let Some(ref ptx) = permission_tx {
|
||||||
|
let (resp_tx, resp_rx) = std::sync::mpsc::sync_channel(1);
|
||||||
|
let _ = ptx.send(PermissionReqMsg {
|
||||||
|
request_id: request_id.clone(),
|
||||||
|
tool_name,
|
||||||
|
tool_input,
|
||||||
|
response_tx: resp_tx,
|
||||||
|
});
|
||||||
|
// Block until the user responds or a 5-minute timeout elapses.
|
||||||
|
let approved = resp_rx
|
||||||
|
.recv_timeout(std::time::Duration::from_secs(300))
|
||||||
|
.unwrap_or(false);
|
||||||
|
let response = serde_json::json!({
|
||||||
|
"type": "permission_response",
|
||||||
|
"id": request_id,
|
||||||
|
"approved": approved,
|
||||||
|
});
|
||||||
|
let _ = writeln!(pty_writer, "{}", response);
|
||||||
|
} else {
|
||||||
|
// No handler configured — deny by default.
|
||||||
|
let response = serde_json::json!({
|
||||||
|
"type": "permission_response",
|
||||||
|
"id": request_id,
|
||||||
|
"approved": false,
|
||||||
|
});
|
||||||
|
let _ = writeln!(pty_writer, "{}", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user