mirror of
https://github.com/openai/codex.git
synced 2026-05-07 21:06:39 +00:00
Compare commits
1 Commits
pakrym/fea
...
etraut/non
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4386ba2eda |
@@ -111,6 +111,7 @@ pub enum TurnSteerRejectionReason {
|
||||
ExpectedTurnMismatch,
|
||||
NonSteerableReview,
|
||||
NonSteerableCompact,
|
||||
NonSteerableUserShell,
|
||||
EmptyInput,
|
||||
InputTooLarge,
|
||||
}
|
||||
@@ -137,6 +138,7 @@ pub enum TurnSteerRequestError {
|
||||
ExpectedTurnMismatch,
|
||||
NonSteerableReview,
|
||||
NonSteerableCompact,
|
||||
NonSteerableUserShell,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -152,6 +154,7 @@ impl From<TurnSteerRequestError> for TurnSteerRejectionReason {
|
||||
TurnSteerRequestError::ExpectedTurnMismatch => Self::ExpectedTurnMismatch,
|
||||
TurnSteerRequestError::NonSteerableReview => Self::NonSteerableReview,
|
||||
TurnSteerRequestError::NonSteerableCompact => Self::NonSteerableCompact,
|
||||
TurnSteerRequestError::NonSteerableUserShell => Self::NonSteerableUserShell,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +528,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -1930,7 +1930,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -5813,7 +5813,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -9963,7 +9963,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2430,7 +2430,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -6735,7 +6735,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -140,7 +140,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -524,7 +524,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -615,7 +615,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -550,7 +550,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -550,7 +550,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -550,7 +550,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -615,7 +615,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -550,7 +550,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -615,7 +615,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -550,7 +550,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -550,7 +550,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -524,7 +524,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -524,7 +524,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review` or manual `/compact`.",
|
||||
"description": "Returned when `turn/start` or `turn/steer` is submitted while the current active turn cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.",
|
||||
"properties": {
|
||||
"activeTurnNotSteerable": {
|
||||
"properties": {
|
||||
@@ -524,7 +524,8 @@
|
||||
"NonSteerableTurnKind": {
|
||||
"enum": [
|
||||
"review",
|
||||
"compact"
|
||||
"compact",
|
||||
"userShell"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type NonSteerableTurnKind = "review" | "compact";
|
||||
export type NonSteerableTurnKind = "review" | "compact" | "userShell";
|
||||
|
||||
@@ -139,6 +139,7 @@ macro_rules! v2_enum_from_core {
|
||||
pub enum NonSteerableTurnKind {
|
||||
Review,
|
||||
Compact,
|
||||
UserShell,
|
||||
}
|
||||
|
||||
/// This translation layer make sure that we expose codex error code in camel case.
|
||||
@@ -181,7 +182,7 @@ pub enum CodexErrorInfo {
|
||||
http_status_code: Option<u16>,
|
||||
},
|
||||
/// Returned when `turn/start` or `turn/steer` is submitted while the current active turn
|
||||
/// cannot accept same-turn steering, for example `/review` or manual `/compact`.
|
||||
/// cannot accept same-turn steering, for example `/review`, manual `/compact`, or a user shell command.
|
||||
ActiveTurnNotSteerable {
|
||||
#[serde(rename = "turnKind")]
|
||||
#[ts(rename = "turnKind")]
|
||||
@@ -228,6 +229,7 @@ impl From<CoreNonSteerableTurnKind> for NonSteerableTurnKind {
|
||||
match value {
|
||||
CoreNonSteerableTurnKind::Review => Self::Review,
|
||||
CoreNonSteerableTurnKind::Compact => Self::Compact,
|
||||
CoreNonSteerableTurnKind::UserShell => Self::UserShell,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,7 +1058,7 @@ There are additional item-specific events:
|
||||
- `ResponseStreamDisconnected { httpStatusCode? }`: disconnect of the response SSE stream in the middle of a turn before completion
|
||||
- `ResponseTooManyFailedAttempts { httpStatusCode? }`
|
||||
- `ActiveTurnNotSteerable { turnKind }`: `turn/start` or `turn/steer` was submitted while the
|
||||
current active turn was not steerable, for example `/review` or manual `/compact`
|
||||
current active turn was not steerable, for example `/review`, manual `/compact`, or a user shell command
|
||||
- `BadRequest`
|
||||
- `Unauthorized`
|
||||
- `SandboxError`
|
||||
|
||||
@@ -7061,6 +7061,10 @@ impl CodexMessageProcessor {
|
||||
"cannot steer a compact turn".to_string(),
|
||||
TurnSteerRequestError::NonSteerableCompact,
|
||||
),
|
||||
codex_protocol::protocol::NonSteerableTurnKind::UserShell => (
|
||||
"cannot steer a user shell turn".to_string(),
|
||||
TurnSteerRequestError::NonSteerableUserShell,
|
||||
),
|
||||
};
|
||||
let error = TurnError {
|
||||
message: message.clone(),
|
||||
|
||||
@@ -216,6 +216,7 @@ impl SteerInputError {
|
||||
let turn_kind_label = match turn_kind {
|
||||
NonSteerableTurnKind::Review => "review",
|
||||
NonSteerableTurnKind::Compact => "compact",
|
||||
NonSteerableTurnKind::UserShell => "user shell",
|
||||
};
|
||||
ErrorEvent {
|
||||
message: format!("cannot steer a {turn_kind_label} turn"),
|
||||
@@ -2787,6 +2788,11 @@ impl Session {
|
||||
turn_kind: NonSteerableTurnKind::Compact,
|
||||
});
|
||||
}
|
||||
Some(crate::state::TaskKind::UserShell) => {
|
||||
return Err(SteerInputError::ActiveTurnNotSteerable {
|
||||
turn_kind: NonSteerableTurnKind::UserShell,
|
||||
});
|
||||
}
|
||||
None => return Err(SteerInputError::NoActiveTurn(input)),
|
||||
}
|
||||
|
||||
|
||||
@@ -5391,6 +5391,7 @@ async fn steer_input_rejects_non_regular_turns() {
|
||||
for (task_kind, turn_kind) in [
|
||||
(TaskKind::Review, NonSteerableTurnKind::Review),
|
||||
(TaskKind::Compact, NonSteerableTurnKind::Compact),
|
||||
(TaskKind::UserShell, NonSteerableTurnKind::UserShell),
|
||||
] {
|
||||
let (sess, _tc, _rx) = make_session_and_context_with_rx().await;
|
||||
let input = vec![UserInput::Text {
|
||||
|
||||
@@ -64,6 +64,7 @@ pub(crate) enum TaskKind {
|
||||
Regular,
|
||||
Review,
|
||||
Compact,
|
||||
UserShell,
|
||||
}
|
||||
|
||||
pub(crate) struct RunningTask {
|
||||
|
||||
@@ -63,7 +63,7 @@ impl UserShellCommandTask {
|
||||
|
||||
impl SessionTask for UserShellCommandTask {
|
||||
fn kind(&self) -> TaskKind {
|
||||
TaskKind::Regular
|
||||
TaskKind::UserShell
|
||||
}
|
||||
|
||||
fn span_name(&self) -> &'static str {
|
||||
|
||||
@@ -1845,6 +1845,7 @@ pub enum AgentStatus {
|
||||
pub enum NonSteerableTurnKind {
|
||||
Review,
|
||||
Compact,
|
||||
UserShell,
|
||||
}
|
||||
|
||||
/// Codex errors that we expose to clients.
|
||||
|
||||
@@ -1027,6 +1027,71 @@ async fn bang_shell_command_submits_run_user_shell_command_in_app_server_tui() {
|
||||
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_shell_steer_rejection_queues_follow_up_until_shell_turn_completes() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "shell-turn-start".into(),
|
||||
msg: EventMsg::TurnStarted(TurnStartedEvent {
|
||||
turn_id: "shell-turn".to_string(),
|
||||
started_at: None,
|
||||
model_context_window: None,
|
||||
collaboration_mode_kind: ModeKind::Default,
|
||||
}),
|
||||
});
|
||||
|
||||
chat.submit_user_message(UserMessage::from("hi"));
|
||||
|
||||
assert_eq!(chat.pending_steers.len(), 1);
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn { items, .. } => assert_eq!(
|
||||
items,
|
||||
vec![UserInput::Text {
|
||||
text: "hi".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
),
|
||||
other => panic!("expected running-turn steer submit, got {other:?}"),
|
||||
}
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "shell-steer-rejected".into(),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message: "cannot steer a user shell turn".to_string(),
|
||||
codex_error_info: Some(CodexErrorInfo::ActiveTurnNotSteerable {
|
||||
turn_kind: NonSteerableTurnKind::UserShell,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
assert!(chat.pending_steers.is_empty());
|
||||
assert_eq!(chat.queued_user_message_texts(), vec!["hi"]);
|
||||
assert!(drain_insert_history(&mut rx).is_empty());
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "shell-turn-complete".into(),
|
||||
msg: EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
turn_id: "shell-turn".to_string(),
|
||||
last_agent_message: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
}),
|
||||
});
|
||||
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn { items, .. } => assert_eq!(
|
||||
items,
|
||||
vec![UserInput::Text {
|
||||
text: "hi".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
),
|
||||
other => panic!("expected queued follow-up submit, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn disabled_slash_command_while_task_running_snapshot() {
|
||||
// Build a chat widget and simulate an active task
|
||||
|
||||
Reference in New Issue
Block a user