add per-agent permission-mode badge to the cockpit roster

Each roster row shows a D/A/P mode badge reflecting the session's live
permission mode. Click cycles the safe trio default -> acceptEdits ->
plan and sends a set_permission_mode control_request to that session
(mirrors interrupt(); fire-and-forget). bypassPermissions is a footgun,
so it is reachable only on Shift-click and behind an inline confirm. A
clide.agent.set-permission-mode command gives the CLI parity.

T-181.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-30 23:25:07 +02:00
co-authored by Claude
parent 4220d97a49
commit 2888efbe5f
8 changed files with 451 additions and 20 deletions
+22
View File
@@ -139,6 +139,28 @@ class ClaudeExtension extends ClideExtension {
return IpcResponse.ok(id: '', data: {'id': id, 'status': 'injected'});
},
),
// T-181: set permission mode for an agent session (D-6 CLI/UI parity).
// Usage: clide claude.agent.set-permission-mode <sessionId> <mode>
// <mode> must be one of: default, acceptEdits, plan, bypassPermissions.
// Note: bypassPermissions is accepted via CLI — the footgun guard is the
// UI's confirm dialog; the CLI caller is responsible for their own safety.
CommandContribution(
id: 'claude.agent.set-permission-mode',
command: 'claude.agent.set-permission-mode',
title: 'Claude: set permission mode for an agent session',
run: (args) async {
final id = args.firstOrNull;
if (id == null) return IpcResponse.ok(id: '', data: const {'error': 'missing session id'});
final mode = args.length >= 2 ? args[1] : null;
if (mode == null) return IpcResponse.ok(id: '', data: const {'error': 'missing mode (default|acceptEdits|plan|bypassPermissions)'});
const valid = {'default', 'acceptEdits', 'plan', 'bypassPermissions'};
if (!valid.contains(mode)) {
return IpcResponse.ok(id: '', data: {'error': 'unknown mode "$mode"; use one of: ${valid.join(', ')}'});
}
_orchestrator?.byId(id)?.session.setPermissionMode(mode);
return IpcResponse.ok(id: '', data: {'id': id, 'mode': mode, 'status': 'sent'});
},
),
// Usage: clide claude.task.reassign <taskId> <toSessionId>
CommandContribution(
id: 'claude.task.reassign',