feat(chat): full approval.request support (#1057)

This commit is contained in:
M57 authored and GitHub committed 2026-09-03 22:33:15 +03:00
1 parent 077ad1fa81
commit bd0b4c2d55
9 files changed
+630 -49

No files matched your search

@@ -283,7 +283,40 @@ object EventParser {
@Suppress("UNCHECKED_CAST")
val patternKeys = (payload?.get("pattern_keys") as? List<*>)?.filterIsInstance<String>()
WsEvent.ApprovalRequest(command, description, patternKeys, sessionId)
val requestId = payload?.get("request_id") as? String
@Suppress("UNCHECKED_CAST")
val rawChoices = (payload?.get("choices") as? List<*>)?.filterIsInstance<String>()
val allowPermanent = payload?.get("allow_permanent") as? Boolean
val allowSession = payload?.get("allow_session") as? Boolean
val smartDenied = payload?.get("smart_denied") as? Boolean
// Backend default (server.py `_approval_request_payload`):
// smart-denied → once/deny only; else once + session? + always? + deny.
val choices =
rawChoices ?: run {
if (smartDenied == true) {
listOf("once", "deny")
} else {
buildList {
add("once")
if (allowSession != false) {
add("session")
if (allowPermanent != false) add("always")
}
add("deny")
}
}
}
WsEvent.ApprovalRequest(
command,
description,
patternKeys,
sessionId,
requestId,
choices,
allowPermanent,
smartDenied,
)
}
"sudo.request" -> {
@@ -230,6 +230,30 @@ sealed class WsEvent {
val description: String?,
val patternKeys: List<String>?,
val sessionId: String?,
/**
* Backend `request_id` — unique across sessions. Sent back as
* `request_id` in `approval.respond` so the gateway resolves the
* EXACT pending instead of FIFO-oldest (desktop parity:
* `prompts.ts` `receiveApprovalRequest` + `approval.tsx` respond).
* Null on legacy payloads → omit, FIFO-compatible.
*/
val requestId: String? = null,
/**
* Backend-advertised choices (e.g. `["once","session","always","deny"]`,
* smart-denied → `["once","deny"]`). Null = legacy → UI falls back
* to Run/Deny.
*/
val choices: List<String>? = null,
/**
* False when the backend won't honor a permanent allow (tirith
* warning) → hide "Always allow" (desktop `allowPermanent` parity).
*/
val allowPermanent: Boolean? = null,
/**
* True when this is an owner override of a Smart DENY — backend
* restricts to once/deny only (desktop `smartDenied` parity).
*/
val smartDenied: Boolean? = null,
) : WsEvent()
// ── Sudo / secret requests ─────────────────────────────────────────
@@ -34,6 +34,12 @@ object WsMethods {
const val PROMPT_BTW = "prompt.btw"
const val CLARIFY_RESPOND = "clarify.respond"
const val APPROVAL_RESPOND = "approval.respond"
/** Ack that the client rendered a pending approval (desktop parity). */
const val APPROVAL_RECEIVED = "approval.received"
/** List unresolved approvals for a session (reconnect replay). */
const val APPROVAL_PENDING = "approval.pending"
const val SUDO_RESPOND = "sudo.respond"
const val SECRET_RESPOND = "secret.respond"