From 2e597de17f427223634697ba3531e0407a7dc957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Beutin?= Date: Fri, 9 Jan 2026 01:08:40 +0100 Subject: [PATCH 1/2] feat(agent-manager): add YOLO mode toggle and session rename Add YOLO mode toggle button in session header to enable/disable auto-approval of operations with indicator in session list. Add inline session rename functionality. Also fixes messages not loading when reopening panel and race condition with pending session timeout. --- .changeset/agent-manager-yolo-toggle.md | 21 ++ .../agent-manager/AgentManagerProvider.ts | 14 ++ .../kilocode/agent-manager/AgentRegistry.ts | 21 +- .../kilocode/agent-manager/CliArgsBuilder.ts | 14 +- .../agent-manager/CliProcessHandler.ts | 23 ++- .../__tests__/CliArgsBuilder.spec.ts | 37 ++++ src/core/kilocode/agent-manager/types.ts | 14 +- .../src/i18n/locales/ar/agentManager.json | 15 +- .../src/i18n/locales/ca/agentManager.json | 15 +- .../src/i18n/locales/cs/agentManager.json | 15 +- .../src/i18n/locales/de/agentManager.json | 15 +- .../src/i18n/locales/en/agentManager.json | 15 +- .../src/i18n/locales/es/agentManager.json | 15 +- .../src/i18n/locales/fr/agentManager.json | 15 +- .../src/i18n/locales/hi/agentManager.json | 15 +- .../src/i18n/locales/id/agentManager.json | 15 +- .../src/i18n/locales/it/agentManager.json | 15 +- .../src/i18n/locales/ja/agentManager.json | 15 +- .../src/i18n/locales/ko/agentManager.json | 15 +- .../src/i18n/locales/nl/agentManager.json | 15 +- .../src/i18n/locales/pl/agentManager.json | 15 +- .../src/i18n/locales/pt-BR/agentManager.json | 15 +- .../src/i18n/locales/ru/agentManager.json | 15 +- .../src/i18n/locales/th/agentManager.json | 15 +- .../src/i18n/locales/tr/agentManager.json | 15 +- .../src/i18n/locales/uk/agentManager.json | 15 +- .../src/i18n/locales/vi/agentManager.json | 15 +- .../src/i18n/locales/zh-CN/agentManager.json | 15 +- .../src/i18n/locales/zh-TW/agentManager.json | 15 +- .../components/AgentManagerApp.css | 179 +++++++++++++++++ .../agent-manager/components/MessageList.tsx | 187 +++++++++++++++++- .../components/SessionDetail.tsx | 117 ++++++++++- .../components/SessionSidebar.tsx | 7 +- .../agent-manager/state/atoms/sessions.ts | 27 ++- 34 files changed, 909 insertions(+), 82 deletions(-) create mode 100644 .changeset/agent-manager-yolo-toggle.md diff --git a/.changeset/agent-manager-yolo-toggle.md b/.changeset/agent-manager-yolo-toggle.md new file mode 100644 index 00000000000..8e6974069c2 --- /dev/null +++ b/.changeset/agent-manager-yolo-toggle.md @@ -0,0 +1,21 @@ +--- +"kilo-code": minor +--- + +Agent Manager: Add YOLO mode toggle and session rename features + +**New Features:** + +- Add YOLO mode toggle button in session header to enable/disable auto-approval of operations +- Add YOLO mode indicator (⚡) in session list for sessions running in YOLO mode +- Add inline session rename functionality - click on session title to edit + +**Bug Fixes:** + +- Fix messages not loading when reopening Agent Manager panel (pre-existing bug) +- Fix race condition where pending session timeout could fire after provisional session was already created + +**Improvements:** + +- Add TypeScript types for `respondToApproval` message +- Add translations for YOLO mode and rename features in all 21 supported locales diff --git a/src/core/kilocode/agent-manager/AgentManagerProvider.ts b/src/core/kilocode/agent-manager/AgentManagerProvider.ts index b508c46e4c4..780a964b2a1 100644 --- a/src/core/kilocode/agent-manager/AgentManagerProvider.ts +++ b/src/core/kilocode/agent-manager/AgentManagerProvider.ts @@ -240,6 +240,10 @@ export class AgentManagerProvider implements vscode.Disposable { case "agentManager.webviewReady": this.postStateToWebview() void this.fetchAndPostRemoteSessions() + // Load messages for the currently selected session if any + if (this.registry.selectedId) { + this.selectSession(this.registry.selectedId) + } break case "agentManager.startSession": void this.handleStartSession(message) @@ -299,6 +303,10 @@ export class AgentManagerProvider implements vscode.Disposable { case "agentManager.showTerminal": this.terminalManager.showTerminal(message.sessionId as string) break + case "agentManager.renameSession": + this.registry.updateSessionLabel(message.sessionId as string, message.label as string) + this.postStateToWebview() + break case "agentManager.sessionShare": SessionManager.init() ?.shareSession(message.sessionId as string) @@ -338,6 +346,7 @@ export class AgentManagerProvider implements vscode.Disposable { const rawLabels = message.labels as string[] | undefined const labels = rawLabels?.length === versions ? rawLabels : undefined const parallelMode = (message.parallelMode as boolean) ?? false + const yoloMode = (message.yoloMode as boolean) ?? true // Default true for backward compat const existingBranch = (message.existingBranch as string) ?? undefined // Extract session configurations @@ -348,6 +357,7 @@ export class AgentManagerProvider implements vscode.Disposable { const config = configs[0] await this.startAgentSession(config.prompt, { parallelMode: config.parallelMode, + yoloMode, labelOverride: config.label, existingBranch: config.existingBranch, }) @@ -364,6 +374,7 @@ export class AgentManagerProvider implements vscode.Disposable { await this.startAgentSession(config.prompt, { parallelMode: config.parallelMode, + yoloMode, labelOverride: config.label, existingBranch: config.existingBranch, }) @@ -454,6 +465,7 @@ export class AgentManagerProvider implements vscode.Disposable { prompt: string, options?: { parallelMode?: boolean + yoloMode?: boolean labelOverride?: string existingBranch?: string }, @@ -503,6 +515,7 @@ export class AgentManagerProvider implements vscode.Disposable { prompt, { parallelMode: options?.parallelMode, + yoloMode: options?.yoloMode, label: options?.labelOverride, gitUrl, existingBranch: options?.existingBranch, @@ -554,6 +567,7 @@ export class AgentManagerProvider implements vscode.Disposable { prompt: string, options: { parallelMode?: boolean + yoloMode?: boolean label?: string gitUrl?: string existingBranch?: string diff --git a/src/core/kilocode/agent-manager/AgentRegistry.ts b/src/core/kilocode/agent-manager/AgentRegistry.ts index b8c450834c6..68de3e06475 100644 --- a/src/core/kilocode/agent-manager/AgentRegistry.ts +++ b/src/core/kilocode/agent-manager/AgentRegistry.ts @@ -27,7 +27,10 @@ export class AgentRegistry { /** * Set a pending session while waiting for CLI's session_created event */ - public setPendingSession(prompt: string, options?: CreateSessionOptions & { gitUrl?: string }): PendingSession { + public setPendingSession( + prompt: string, + options?: CreateSessionOptions & { gitUrl?: string; yoloMode?: boolean }, + ): PendingSession { const label = this.truncatePrompt(prompt) this._pendingSession = { prompt, @@ -35,6 +38,7 @@ export class AgentRegistry { startTime: Date.now(), parallelMode: options?.parallelMode, gitUrl: options?.gitUrl, + yoloMode: options?.yoloMode, } return this._pendingSession } @@ -53,7 +57,7 @@ export class AgentRegistry { sessionId: string, prompt: string, startTime?: number, - options?: CreateSessionOptions & { labelOverride?: string; gitUrl?: string }, + options?: CreateSessionOptions & { labelOverride?: string; gitUrl?: string; yoloMode?: boolean }, ): AgentSession { const label = options?.labelOverride ?? this.truncatePrompt(prompt) @@ -67,6 +71,7 @@ export class AgentRegistry { source: "local", ...(options?.parallelMode && { parallelMode: { enabled: true } }), gitUrl: options?.gitUrl, + yoloMode: options?.yoloMode, } this.sessions.set(sessionId, session) @@ -212,6 +217,18 @@ export class AgentRegistry { return this.sessions.delete(sessionId) } + /** + * Update a session's display label + */ + public updateSessionLabel(sessionId: string, label: string): boolean { + const session = this.sessions.get(sessionId) + if (!session) { + return false + } + session.label = label + return true + } + /** * Rename a session from one ID to another. * Used when upgrading a provisional session to a real session ID. diff --git a/src/core/kilocode/agent-manager/CliArgsBuilder.ts b/src/core/kilocode/agent-manager/CliArgsBuilder.ts index f5817ede85b..ac5de520694 100644 --- a/src/core/kilocode/agent-manager/CliArgsBuilder.ts +++ b/src/core/kilocode/agent-manager/CliArgsBuilder.ts @@ -1,5 +1,10 @@ export interface BuildCliArgsOptions { sessionId?: string + /** + * When true (default), adds --yolo flag to auto-approve all tool operations. + * When false, CLI will send ask messages requiring user approval via JSON-IO. + */ + yoloMode?: boolean } /** @@ -10,8 +15,15 @@ export interface BuildCliArgsOptions { export function buildCliArgs(workspace: string, prompt: string, options?: BuildCliArgsOptions): string[] { // --json-io: enables bidirectional JSON communication via stdin/stdout // Note: --json (without -io) exists for CI/CD read-only mode but isn't used here + const args = ["--json-io"] + // --yolo: auto-approve tool uses (file reads, writes, commands, etc.) - const args = ["--json-io", "--yolo", `--workspace=${workspace}`] + // Default to true for backward compatibility - only omit when explicitly false + if (options?.yoloMode !== false) { + args.push("--yolo") + } + + args.push(`--workspace=${workspace}`) if (options?.sessionId) { args.push(`--session=${options.sessionId}`) diff --git a/src/core/kilocode/agent-manager/CliProcessHandler.ts b/src/core/kilocode/agent-manager/CliProcessHandler.ts index 374b08e4c2f..da9991ec7c5 100644 --- a/src/core/kilocode/agent-manager/CliProcessHandler.ts +++ b/src/core/kilocode/agent-manager/CliProcessHandler.ts @@ -38,6 +38,7 @@ interface PendingProcessInfo { prompt: string startTime: number parallelMode?: boolean + yoloMode?: boolean desiredSessionId?: string desiredLabel?: string worktreeBranch?: string // Captured from welcome event before session_created @@ -142,6 +143,7 @@ export class CliProcessHandler { options: | { parallelMode?: boolean + yoloMode?: boolean sessionId?: string label?: string gitUrl?: string @@ -179,6 +181,7 @@ export class CliProcessHandler { const pendingSession = this.registry.setPendingSession(prompt, { parallelMode: options?.parallelMode, gitUrl: options?.gitUrl, + yoloMode: options?.yoloMode, }) this.debugLog(`Pending session created, waiting for CLI session_created event`) this.callbacks.onPendingSessionChanged(pendingSession) @@ -189,6 +192,7 @@ export class CliProcessHandler { // and passing the worktree path as the workspace. CLI is unaware of worktrees. const cliArgs = buildCliArgs(workspace, prompt, { sessionId: options?.sessionId, + yoloMode: options?.yoloMode, }) const env = this.buildEnvWithApiConfiguration(options?.apiConfiguration, options?.shellPath) @@ -243,6 +247,7 @@ export class CliProcessHandler { prompt, startTime: Date.now(), parallelMode: options?.parallelMode, + yoloMode: options?.yoloMode, desiredSessionId: options?.sessionId, desiredLabel: options?.label, gitUrl: options?.gitUrl, @@ -513,13 +518,23 @@ export class CliProcessHandler { const provisionalId = `provisional-${Date.now()}` this.pendingProcess.provisionalSessionId = provisionalId - const { prompt, startTime, parallelMode, desiredLabel, gitUrl, parser, worktreeBranch, worktreePath } = - this.pendingProcess + const { + prompt, + startTime, + parallelMode, + yoloMode, + desiredLabel, + gitUrl, + parser, + worktreeBranch, + worktreePath, + } = this.pendingProcess this.registry.createSession(provisionalId, prompt, startTime, { parallelMode, labelOverride: desiredLabel, gitUrl, + yoloMode, }) if (parallelMode && (worktreeBranch || worktreePath)) { @@ -535,6 +550,8 @@ export class CliProcessHandler { this.registry.setSessionPid(provisionalId, proc.pid) } + // Clear the timeout BEFORE clearing the pending session to prevent race conditions + this.clearPendingTimeout() this.registry.clearPendingSession() this.callbacks.onPendingSessionChanged(null) this.callbacks.onSessionCreated(this.pendingProcess?.sawApiReqStarted ?? false) @@ -634,6 +651,7 @@ export class CliProcessHandler { startTime, parser, parallelMode, + yoloMode, worktreeBranch, worktreePath, worktreeInfo, @@ -682,6 +700,7 @@ export class CliProcessHandler { parallelMode, labelOverride: desiredLabel, gitUrl, + yoloMode, }) this.debugLog(`Created new session: ${sessionId}`) } diff --git a/src/core/kilocode/agent-manager/__tests__/CliArgsBuilder.spec.ts b/src/core/kilocode/agent-manager/__tests__/CliArgsBuilder.spec.ts index 49079a3661f..f86ce8a563d 100644 --- a/src/core/kilocode/agent-manager/__tests__/CliArgsBuilder.spec.ts +++ b/src/core/kilocode/agent-manager/__tests__/CliArgsBuilder.spec.ts @@ -70,4 +70,41 @@ describe("buildCliArgs", () => { expect(args).toContain("--yolo") expect(args).not.toContain("--auto") }) + + // YOLO mode tests + describe("yoloMode option", () => { + it("includes --yolo by default (undefined)", () => { + const args = buildCliArgs("/workspace", "prompt") + + expect(args).toContain("--yolo") + }) + + it("includes --yolo when yoloMode is true", () => { + const args = buildCliArgs("/workspace", "prompt", { yoloMode: true }) + + expect(args).toContain("--yolo") + }) + + it("excludes --yolo when yoloMode is false", () => { + const args = buildCliArgs("/workspace", "prompt", { yoloMode: false }) + + expect(args).not.toContain("--yolo") + }) + + it("excludes --yolo when yoloMode is false with session", () => { + const args = buildCliArgs("/workspace", "prompt", { yoloMode: false, sessionId: "abc123" }) + + expect(args).not.toContain("--yolo") + expect(args).toContain("--session=abc123") + expect(args).toEqual(["--json-io", "--workspace=/workspace", "--session=abc123", "prompt"]) + }) + + it("includes --yolo when yoloMode is true with session", () => { + const args = buildCliArgs("/workspace", "prompt", { yoloMode: true, sessionId: "abc123" }) + + expect(args).toContain("--yolo") + expect(args).toContain("--session=abc123") + expect(args).toEqual(["--json-io", "--yolo", "--workspace=/workspace", "--session=abc123", "prompt"]) + }) + }) }) diff --git a/src/core/kilocode/agent-manager/types.ts b/src/core/kilocode/agent-manager/types.ts index 0dccd17d006..2af341ca371 100644 --- a/src/core/kilocode/agent-manager/types.ts +++ b/src/core/kilocode/agent-manager/types.ts @@ -32,6 +32,7 @@ export interface AgentSession { source: SessionSource parallelMode?: ParallelModeInfo gitUrl?: string + yoloMode?: boolean // True if session was started with --yolo flag (auto-approve operations) } /** @@ -43,6 +44,7 @@ export interface PendingSession { startTime: number parallelMode?: boolean gitUrl?: string + yoloMode?: boolean // True if session will be started with --yolo flag } // Re-export remote session shape from shared session client for consistency @@ -58,11 +60,21 @@ export interface AgentManagerState { */ export type AgentManagerMessage = | { type: "agentManager.webviewReady" } - | { type: "agentManager.startSession"; prompt: string; parallelMode?: boolean; existingBranch?: string } + | { + type: "agentManager.startSession" + prompt: string + parallelMode?: boolean + yoloMode?: boolean + existingBranch?: string + versions?: number + labels?: string[] + } | { type: "agentManager.stopSession"; sessionId: string } | { type: "agentManager.selectSession"; sessionId: string } | { type: "agentManager.refreshRemoteSessions" } | { type: "agentManager.listBranches" } + | { type: "agentManager.respondToApproval"; sessionId: string; approved: boolean; text?: string; messageTs: number } + | { type: "agentManager.renameSession"; sessionId: string; label: string } /** * Messages from Extension to Webview diff --git a/webview-ui/src/i18n/locales/ar/agentManager.json b/webview-ui/src/i18n/locales/ar/agentManager.json index 42061a4f113..71dd6e27d0a 100644 --- a/webview-ui/src/i18n/locales/ar/agentManager.json +++ b/webview-ui/src/i18n/locales/ar/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "مشاركة الجلسة", "shareConfirmMessage": "هل ترغب في مشاركة لقطة من هذه الجلسة علنًا؟", "shareConfirmYes": "نعم", - "shareConfirmNo": "لا" + "shareConfirmNo": "لا", + "yoloMode": "وضع YOLO" }, "sessionDetail": { "startNewAgent": "بدء وكيل جديد", @@ -59,7 +60,13 @@ "branchPickerTooltip": "تبديل الفرع الأساسي", "noBranches": "لم يتم العثور على فروع", "noMatchingBranches": "لا توجد فروع تطابق بحثك", - "openTerminal": "فتح الطرفية" + "openTerminal": "فتح الطرفية", + "yoloModeOn": "وضع YOLO مفعّل - جميع العمليات معتمدة تلقائياً", + "yoloModeOff": "وضع YOLO معطّل - العمليات تحتاج موافقة", + "toggleYoloMode": "تبديل وضع YOLO", + "renameSession": "إعادة تسمية الجلسة", + "clickToRename": "انقر لإعادة التسمية", + "confirmRename": "تأكيد إعادة التسمية" }, "messages": { "waiting": "في انتظار رد الوكيل...", @@ -74,7 +81,9 @@ "usingTool": "استخدام الأداة: **{{tool}}** {{details}}", "expandOutput": "توسيع المخرجات", "collapseOutput": "طي المخرجات", - "copyCommand": "نسخ الأمر" + "copyCommand": "نسخ الأمر", + "approve": "موافقة", + "deny": "رفض" }, "chatInput": { "autoMode": "الوكيل يعمل في وضع الأتمتة الكاملة - لا يُسمح بالإدخال", diff --git a/webview-ui/src/i18n/locales/ca/agentManager.json b/webview-ui/src/i18n/locales/ca/agentManager.json index 72b852e6f7c..a21bd2760a6 100644 --- a/webview-ui/src/i18n/locales/ca/agentManager.json +++ b/webview-ui/src/i18n/locales/ca/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Compartir sessió", "shareConfirmMessage": "Vols compartir públicament una instantània d'aquesta sessió?", "shareConfirmYes": "Sí", - "shareConfirmNo": "No" + "shareConfirmNo": "No", + "yoloMode": "Mode YOLO" }, "sessionDetail": { "startNewAgent": "Iniciar un agent nou", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Canvia la branca base", "noBranches": "No s'han trobat branques", "noMatchingBranches": "No hi ha branques que coincideixin amb la teva cerca", - "openTerminal": "Obrir terminal" + "openTerminal": "Obrir terminal", + "yoloModeOn": "Mode YOLO activat - totes les operacions s'aproven automàticament", + "yoloModeOff": "Mode YOLO desactivat - es requereix aprovació per a les operacions", + "toggleYoloMode": "Alternar mode YOLO", + "renameSession": "Canviar el nom de la sessió", + "clickToRename": "Fes clic per canviar el nom", + "confirmRename": "Confirmar canvi de nom" }, "messages": { "waiting": "Esperant resposta de l'agent...", @@ -70,7 +77,9 @@ "usingTool": "Utilitzant eina: **{{tool}}** {{details}}", "expandOutput": "Expandir sortida", "collapseOutput": "Replegar sortida", - "copyCommand": "Copiar comanda" + "copyCommand": "Copiar comanda", + "approve": "Aprovar", + "deny": "Rebutjar" }, "chatInput": { "autoMode": "Agent en mode automàtic complet - no es permet entrada", diff --git a/webview-ui/src/i18n/locales/cs/agentManager.json b/webview-ui/src/i18n/locales/cs/agentManager.json index c74102563db..18d72279b92 100644 --- a/webview-ui/src/i18n/locales/cs/agentManager.json +++ b/webview-ui/src/i18n/locales/cs/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Sdílet relaci", "shareConfirmMessage": "Chcete veřejně sdílet snímek této relace?", "shareConfirmYes": "Ano", - "shareConfirmNo": "Ne" + "shareConfirmNo": "Ne", + "yoloMode": "Režim YOLO" }, "sessionDetail": { "startNewAgent": "Spustit nového agenta", @@ -56,7 +57,13 @@ "branchPickerTooltip": "Změnit základní větev", "noBranches": "Nebyly nalezeny žádné větve", "noMatchingBranches": "Žádné větve neodpovídají tvému vyhledávání", - "openTerminal": "Otevřít terminál" + "openTerminal": "Otevřít terminál", + "yoloModeOn": "Režim YOLO zapnutý - všechny operace jsou automaticky schváleny", + "yoloModeOff": "Režim YOLO vypnutý - vyžaduje se schválení operací", + "toggleYoloMode": "Přepnout režim YOLO", + "renameSession": "Přejmenovat relaci", + "clickToRename": "Klikni pro přejmenování", + "confirmRename": "Potvrdit přejmenování" }, "messages": { "waiting": "Čekání na odpověď agenta...", @@ -71,7 +78,9 @@ "usingTool": "Používání nástroje: **{{tool}}** {{details}}", "expandOutput": "Rozbalit výstup", "collapseOutput": "Sbalit výstup", - "copyCommand": "Kopírovat příkaz" + "copyCommand": "Kopírovat příkaz", + "approve": "Schválit", + "deny": "Odmítnout" }, "chatInput": { "autoMode": "Agent běží v plně automatickém režimu - vstup není povolen", diff --git a/webview-ui/src/i18n/locales/de/agentManager.json b/webview-ui/src/i18n/locales/de/agentManager.json index fc0d6a750f6..eb289c7e244 100644 --- a/webview-ui/src/i18n/locales/de/agentManager.json +++ b/webview-ui/src/i18n/locales/de/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Sitzung teilen", "shareConfirmMessage": "Möchtest du einen Schnappschuss dieser Sitzung öffentlich teilen?", "shareConfirmYes": "Ja", - "shareConfirmNo": "Nein" + "shareConfirmNo": "Nein", + "yoloMode": "YOLO-Modus" }, "sessionDetail": { "startNewAgent": "Einen neuen Agenten starten", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Basis-Branch wechseln", "noBranches": "Keine Branches gefunden", "noMatchingBranches": "Keine Branches entsprechen deiner Suche", - "openTerminal": "Terminal öffnen" + "openTerminal": "Terminal öffnen", + "yoloModeOn": "YOLO-Modus AN - alle Operationen werden automatisch genehmigt", + "yoloModeOff": "YOLO-Modus AUS - Genehmigung für Operationen erforderlich", + "toggleYoloMode": "YOLO-Modus umschalten", + "renameSession": "Sitzung umbenennen", + "clickToRename": "Klicken zum Umbenennen", + "confirmRename": "Umbenennung bestätigen" }, "messages": { "waiting": "Warte auf Antwort des Agenten...", @@ -70,7 +77,9 @@ "usingTool": "Verwende Werkzeug: **{{tool}}** {{details}}", "expandOutput": "Ausgabe erweitern", "collapseOutput": "Ausgabe einklappen", - "copyCommand": "Befehl kopieren" + "copyCommand": "Befehl kopieren", + "approve": "Genehmigen", + "deny": "Ablehnen" }, "chatInput": { "autoMode": "Agent läuft im Vollautomatik-Modus - keine Eingabe möglich", diff --git a/webview-ui/src/i18n/locales/en/agentManager.json b/webview-ui/src/i18n/locales/en/agentManager.json index f1d452de587..05020116578 100644 --- a/webview-ui/src/i18n/locales/en/agentManager.json +++ b/webview-ui/src/i18n/locales/en/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Share session", "shareConfirmMessage": "Would you like to publicly share a snapshot of this session?", "shareConfirmYes": "Yes", - "shareConfirmNo": "No" + "shareConfirmNo": "No", + "yoloMode": "YOLO mode" }, "sessionDetail": { "startNewAgent": "Start a new agent", @@ -39,6 +40,9 @@ "creatingSession": "Creating session...", "waitingForCli": "Initializing agent session...", "autoModeWarning": "YOLO mode - tool operations are auto-approved", + "yoloModeOn": "YOLO mode ON - auto-approve all operations", + "yoloModeOff": "YOLO mode OFF - require approval for operations", + "toggleYoloMode": "Toggle YOLO mode", "versions": "Versions", "versionsTooltip": "Parallel agents exploring different approaches", "versionsHelperText": "{{count}} parallel agents on separate branches", @@ -55,7 +59,10 @@ "branchPickerTooltip": "Switch base branch", "noBranches": "No branches found", "noMatchingBranches": "No branches match your search", - "openTerminal": "Open terminal" + "openTerminal": "Open terminal", + "renameSession": "Rename session", + "clickToRename": "Click to rename", + "confirmRename": "Confirm rename" }, "messages": { "waiting": "Waiting for agent response...", @@ -70,7 +77,9 @@ "usingTool": "Using tool: **{{tool}}** {{details}}", "expandOutput": "Expand output", "collapseOutput": "Collapse output", - "copyCommand": "Copy command" + "copyCommand": "Copy command", + "approve": "Approve", + "deny": "Deny" }, "chatInput": { "autoMode": "Agent running in full-auto mode - no input allowed", diff --git a/webview-ui/src/i18n/locales/es/agentManager.json b/webview-ui/src/i18n/locales/es/agentManager.json index e839de0590a..3972b1a13dd 100644 --- a/webview-ui/src/i18n/locales/es/agentManager.json +++ b/webview-ui/src/i18n/locales/es/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Compartir sesión", "shareConfirmMessage": "¿Te gustaría compartir públicamente una instantánea de esta sesión?", "shareConfirmYes": "Sí", - "shareConfirmNo": "No" + "shareConfirmNo": "No", + "yoloMode": "Modo YOLO" }, "sessionDetail": { "startNewAgent": "Iniciar un nuevo agente", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Cambiar rama base", "noBranches": "No se encontraron ramas", "noMatchingBranches": "Ninguna rama coincide con tu búsqueda", - "openTerminal": "Abrir terminal" + "openTerminal": "Abrir terminal", + "yoloModeOn": "Modo YOLO activado - todas las operaciones se aprueban automáticamente", + "yoloModeOff": "Modo YOLO desactivado - se requiere aprobación para las operaciones", + "toggleYoloMode": "Alternar modo YOLO", + "renameSession": "Renombrar sesión", + "clickToRename": "Clic para renombrar", + "confirmRename": "Confirmar renombrado" }, "messages": { "waiting": "Esperando respuesta del agente...", @@ -70,7 +77,9 @@ "usingTool": "Usando herramienta: **{{tool}}** {{details}}", "expandOutput": "Expandir salida", "collapseOutput": "Colapsar salida", - "copyCommand": "Copiar comando" + "copyCommand": "Copiar comando", + "approve": "Aprobar", + "deny": "Rechazar" }, "chatInput": { "autoMode": "Agente ejecutándose en modo totalmente automático - no se permite entrada", diff --git a/webview-ui/src/i18n/locales/fr/agentManager.json b/webview-ui/src/i18n/locales/fr/agentManager.json index 18306e6213d..f6c4306e4fe 100644 --- a/webview-ui/src/i18n/locales/fr/agentManager.json +++ b/webview-ui/src/i18n/locales/fr/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Partager la session", "shareConfirmMessage": "Souhaites-tu partager publiquement un instantané de cette session ?", "shareConfirmYes": "Oui", - "shareConfirmNo": "Non" + "shareConfirmNo": "Non", + "yoloMode": "Mode YOLO" }, "sessionDetail": { "startNewAgent": "Démarrer un nouvel agent", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Changer de branche de base", "noBranches": "Aucune branche trouvée", "noMatchingBranches": "Aucune branche ne correspond à ta recherche", - "openTerminal": "Ouvrir le terminal" + "openTerminal": "Ouvrir le terminal", + "yoloModeOn": "Mode YOLO activé - toutes les opérations sont approuvées automatiquement", + "yoloModeOff": "Mode YOLO désactivé - approbation requise pour les opérations", + "toggleYoloMode": "Basculer le mode YOLO", + "renameSession": "Renommer la session", + "clickToRename": "Cliquer pour renommer", + "confirmRename": "Confirmer le renommage" }, "messages": { "waiting": "En attente de la réponse de l'agent...", @@ -70,7 +77,9 @@ "usingTool": "Utilisation de l'outil : **{{tool}}** {{details}}", "expandOutput": "Développer la sortie", "collapseOutput": "Réduire la sortie", - "copyCommand": "Copier la commande" + "copyCommand": "Copier la commande", + "approve": "Approuver", + "deny": "Refuser" }, "chatInput": { "autoMode": "Agent en mode entièrement automatique - aucune entrée autorisée", diff --git a/webview-ui/src/i18n/locales/hi/agentManager.json b/webview-ui/src/i18n/locales/hi/agentManager.json index 25debc72c47..c10ec68393f 100644 --- a/webview-ui/src/i18n/locales/hi/agentManager.json +++ b/webview-ui/src/i18n/locales/hi/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "सत्र साझा करें", "shareConfirmMessage": "क्या आप इस सत्र का एक स्नैपशॉट सार्वजनिक रूप से साझा करना चाहेंगे?", "shareConfirmYes": "हाँ", - "shareConfirmNo": "नहीं" + "shareConfirmNo": "नहीं", + "yoloMode": "YOLO मोड" }, "sessionDetail": { "startNewAgent": "नया एजेंट शुरू करें", @@ -55,7 +56,13 @@ "branchPickerTooltip": "बेस ब्रांच बदलें", "noBranches": "कोई ब्रांच नहीं मिली", "noMatchingBranches": "आपकी खोज से मेल खाने वाली कोई ब्रांच नहीं", - "openTerminal": "टर्मिनल खोलें" + "openTerminal": "टर्मिनल खोलें", + "yoloModeOn": "YOLO मोड चालू - सभी ऑपरेशन स्वचालित रूप से स्वीकृत", + "yoloModeOff": "YOLO मोड बंद - ऑपरेशन के लिए स्वीकृति आवश्यक", + "toggleYoloMode": "YOLO मोड टॉगल करें", + "renameSession": "सत्र का नाम बदलें", + "clickToRename": "नाम बदलने के लिए क्लिक करें", + "confirmRename": "नाम बदलने की पुष्टि करें" }, "messages": { "waiting": "एजेंट की प्रतिक्रिया की प्रतीक्षा में...", @@ -70,7 +77,9 @@ "usingTool": "टूल का उपयोग: **{{tool}}** {{details}}", "expandOutput": "आउटपुट विस्तार करें", "collapseOutput": "आउटपुट संक्षिप्त करें", - "copyCommand": "कमांड कॉपी करें" + "copyCommand": "कमांड कॉपी करें", + "approve": "स्वीकृत करें", + "deny": "अस्वीकृत करें" }, "chatInput": { "autoMode": "एजेंट पूर्ण-स्वचालित मोड में चल रहा है - इनपुट की अनुमति नहीं है", diff --git a/webview-ui/src/i18n/locales/id/agentManager.json b/webview-ui/src/i18n/locales/id/agentManager.json index 152f8b90718..b07d3760d01 100644 --- a/webview-ui/src/i18n/locales/id/agentManager.json +++ b/webview-ui/src/i18n/locales/id/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Bagikan sesi", "shareConfirmMessage": "Apakah Anda ingin membagikan snapshot sesi ini secara publik?", "shareConfirmYes": "Ya", - "shareConfirmNo": "Tidak" + "shareConfirmNo": "Tidak", + "yoloMode": "Mode YOLO" }, "sessionDetail": { "startNewAgent": "Mulai agen baru", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Ganti cabang dasar", "noBranches": "Tidak ada cabang ditemukan", "noMatchingBranches": "Tidak ada cabang yang cocok dengan pencarian kamu", - "openTerminal": "Buka terminal" + "openTerminal": "Buka terminal", + "yoloModeOn": "Mode YOLO aktif - semua operasi disetujui otomatis", + "yoloModeOff": "Mode YOLO nonaktif - persetujuan diperlukan untuk operasi", + "toggleYoloMode": "Alihkan mode YOLO", + "renameSession": "Ubah nama sesi", + "clickToRename": "Klik untuk mengubah nama", + "confirmRename": "Konfirmasi ubah nama" }, "messages": { "waiting": "Menunggu respons agen...", @@ -70,7 +77,9 @@ "usingTool": "Menggunakan alat: **{{tool}}** {{details}}", "expandOutput": "Perluas output", "collapseOutput": "Ciutkan output", - "copyCommand": "Salin perintah" + "copyCommand": "Salin perintah", + "approve": "Setujui", + "deny": "Tolak" }, "chatInput": { "autoMode": "Agen berjalan dalam mode otomatis penuh - input tidak diizinkan", diff --git a/webview-ui/src/i18n/locales/it/agentManager.json b/webview-ui/src/i18n/locales/it/agentManager.json index 4037b9b2019..1d7c4ddb152 100644 --- a/webview-ui/src/i18n/locales/it/agentManager.json +++ b/webview-ui/src/i18n/locales/it/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Condividi sessione", "shareConfirmMessage": "Vuoi condividere pubblicamente uno snapshot di questa sessione?", "shareConfirmYes": "Sì", - "shareConfirmNo": "No" + "shareConfirmNo": "No", + "yoloMode": "Modalità YOLO" }, "sessionDetail": { "startNewAgent": "Avvia un nuovo agente", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Cambia branch di base", "noBranches": "Nessun branch trovato", "noMatchingBranches": "Nessun branch corrisponde alla tua ricerca", - "openTerminal": "Apri terminale" + "openTerminal": "Apri terminale", + "yoloModeOn": "Modalità YOLO attiva - tutte le operazioni vengono approvate automaticamente", + "yoloModeOff": "Modalità YOLO disattiva - approvazione richiesta per le operazioni", + "toggleYoloMode": "Attiva/disattiva modalità YOLO", + "renameSession": "Rinomina sessione", + "clickToRename": "Clicca per rinominare", + "confirmRename": "Conferma rinomina" }, "messages": { "waiting": "In attesa della risposta dell'agente...", @@ -70,7 +77,9 @@ "usingTool": "Utilizzo strumento: **{{tool}}** {{details}}", "expandOutput": "Espandi output", "collapseOutput": "Comprimi output", - "copyCommand": "Copia comando" + "copyCommand": "Copia comando", + "approve": "Approva", + "deny": "Rifiuta" }, "chatInput": { "autoMode": "Agent in esecuzione in modalità completamente automatica - input non consentito", diff --git a/webview-ui/src/i18n/locales/ja/agentManager.json b/webview-ui/src/i18n/locales/ja/agentManager.json index fcec7994083..b3ca0bdd264 100644 --- a/webview-ui/src/i18n/locales/ja/agentManager.json +++ b/webview-ui/src/i18n/locales/ja/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "セッションを共有", "shareConfirmMessage": "このセッションのスナップショットを公開で共有しますか?", "shareConfirmYes": "はい", - "shareConfirmNo": "いいえ" + "shareConfirmNo": "いいえ", + "yoloMode": "YOLOモード" }, "sessionDetail": { "startNewAgent": "新しいエージェントを開始", @@ -55,7 +56,13 @@ "branchPickerTooltip": "ベースブランチを切り替え", "noBranches": "ブランチが見つかりません", "noMatchingBranches": "検索に一致するブランチがありません", - "openTerminal": "ターミナルを開く" + "openTerminal": "ターミナルを開く", + "yoloModeOn": "YOLOモード オン - すべての操作が自動承認されます", + "yoloModeOff": "YOLOモード オフ - 操作には承認が必要です", + "toggleYoloMode": "YOLOモードを切り替え", + "renameSession": "セッション名を変更", + "clickToRename": "クリックして名前を変更", + "confirmRename": "名前変更を確認" }, "messages": { "waiting": "エージェントの応答を待っています...", @@ -70,7 +77,9 @@ "usingTool": "ツールを使用中: **{{tool}}** {{details}}", "expandOutput": "出力を展開", "collapseOutput": "出力を折りたたむ", - "copyCommand": "コマンドをコピー" + "copyCommand": "コマンドをコピー", + "approve": "承認", + "deny": "拒否" }, "chatInput": { "autoMode": "エージェントが完全自動モードで実行中 - 入力はできません", diff --git a/webview-ui/src/i18n/locales/ko/agentManager.json b/webview-ui/src/i18n/locales/ko/agentManager.json index 440d5bf0cf1..ab7c800a74b 100644 --- a/webview-ui/src/i18n/locales/ko/agentManager.json +++ b/webview-ui/src/i18n/locales/ko/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "세션 공유", "shareConfirmMessage": "이 세션의 스냅샷을 공개적으로 공유하시겠습니까?", "shareConfirmYes": "예", - "shareConfirmNo": "아니요" + "shareConfirmNo": "아니요", + "yoloMode": "YOLO 모드" }, "sessionDetail": { "startNewAgent": "새 에이전트 시작", @@ -55,7 +56,13 @@ "branchPickerTooltip": "기본 브랜치 변경", "noBranches": "브랜치를 찾을 수 없습니다", "noMatchingBranches": "검색과 일치하는 브랜치가 없습니다", - "openTerminal": "터미널 열기" + "openTerminal": "터미널 열기", + "yoloModeOn": "YOLO 모드 켜짐 - 모든 작업이 자동 승인됩니다", + "yoloModeOff": "YOLO 모드 꺼짐 - 작업에 승인이 필요합니다", + "toggleYoloMode": "YOLO 모드 전환", + "renameSession": "세션 이름 변경", + "clickToRename": "클릭하여 이름 변경", + "confirmRename": "이름 변경 확인" }, "messages": { "waiting": "에이전트 응답 대기 중...", @@ -70,7 +77,9 @@ "usingTool": "도구 사용 중: **{{tool}}** {{details}}", "expandOutput": "출력 펼치기", "collapseOutput": "출력 접기", - "copyCommand": "명령 복사" + "copyCommand": "명령 복사", + "approve": "승인", + "deny": "거부" }, "chatInput": { "autoMode": "에이전트가 완전 자동 모드로 실행 중 - 입력이 허용되지 않습니다", diff --git a/webview-ui/src/i18n/locales/nl/agentManager.json b/webview-ui/src/i18n/locales/nl/agentManager.json index 47fe692c0cd..a6ec12387ef 100644 --- a/webview-ui/src/i18n/locales/nl/agentManager.json +++ b/webview-ui/src/i18n/locales/nl/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Sessie delen", "shareConfirmMessage": "Wil je een momentopname van deze sessie publiek delen?", "shareConfirmYes": "Ja", - "shareConfirmNo": "Nee" + "shareConfirmNo": "Nee", + "yoloMode": "YOLO-modus" }, "sessionDetail": { "startNewAgent": "Een nieuwe agent starten", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Wijzig basisbranch", "noBranches": "Geen branches gevonden", "noMatchingBranches": "Geen branches komen overeen met je zoekopdracht", - "openTerminal": "Terminal openen" + "openTerminal": "Terminal openen", + "yoloModeOn": "YOLO-modus aan - alle operaties worden automatisch goedgekeurd", + "yoloModeOff": "YOLO-modus uit - goedkeuring vereist voor operaties", + "toggleYoloMode": "YOLO-modus wisselen", + "renameSession": "Sessie hernoemen", + "clickToRename": "Klik om te hernoemen", + "confirmRename": "Hernoemen bevestigen" }, "messages": { "waiting": "Wachten op antwoord van agent...", @@ -70,7 +77,9 @@ "usingTool": "Tool gebruiken: **{{tool}}** {{details}}", "expandOutput": "Uitvoer uitvouwen", "collapseOutput": "Uitvoer invouwen", - "copyCommand": "Opdracht kopiëren" + "copyCommand": "Opdracht kopiëren", + "approve": "Goedkeuren", + "deny": "Weigeren" }, "chatInput": { "autoMode": "Agent draait in volledig automatische modus - geen invoer toegestaan", diff --git a/webview-ui/src/i18n/locales/pl/agentManager.json b/webview-ui/src/i18n/locales/pl/agentManager.json index 09d72fe03cf..8fb4a263d0d 100644 --- a/webview-ui/src/i18n/locales/pl/agentManager.json +++ b/webview-ui/src/i18n/locales/pl/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Udostępnij sesję", "shareConfirmMessage": "Czy chcesz publicznie udostępnić migawkę tej sesji?", "shareConfirmYes": "Tak", - "shareConfirmNo": "Nie" + "shareConfirmNo": "Nie", + "yoloMode": "Tryb YOLO" }, "sessionDetail": { "startNewAgent": "Uruchom nowego agenta", @@ -57,7 +58,13 @@ "branchPickerTooltip": "Zmień gałąź bazową", "noBranches": "Nie znaleziono gałęzi", "noMatchingBranches": "Brak gałęzi pasujących do twojego wyszukiwania", - "openTerminal": "Otwórz terminal" + "openTerminal": "Otwórz terminal", + "yoloModeOn": "Tryb YOLO włączony - wszystkie operacje są automatycznie zatwierdzane", + "yoloModeOff": "Tryb YOLO wyłączony - wymagana zgoda na operacje", + "toggleYoloMode": "Przełącz tryb YOLO", + "renameSession": "Zmień nazwę sesji", + "clickToRename": "Kliknij, aby zmienić nazwę", + "confirmRename": "Potwierdź zmianę nazwy" }, "messages": { "waiting": "Oczekiwanie na odpowiedź agenta...", @@ -72,7 +79,9 @@ "usingTool": "Używanie narzędzia: **{{tool}}** {{details}}", "expandOutput": "Rozwiń wyjście", "collapseOutput": "Zwiń wyjście", - "copyCommand": "Kopiuj polecenie" + "copyCommand": "Kopiuj polecenie", + "approve": "Zatwierdź", + "deny": "Odrzuć" }, "chatInput": { "autoMode": "Agent działa w trybie w pełni automatycznym - wprowadzanie danych niemożliwe", diff --git a/webview-ui/src/i18n/locales/pt-BR/agentManager.json b/webview-ui/src/i18n/locales/pt-BR/agentManager.json index f4e1f415bbf..78b7a88637c 100644 --- a/webview-ui/src/i18n/locales/pt-BR/agentManager.json +++ b/webview-ui/src/i18n/locales/pt-BR/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Compartilhar sessão", "shareConfirmMessage": "Gostaria de compartilhar publicamente um instantâneo desta sessão?", "shareConfirmYes": "Sim", - "shareConfirmNo": "Não" + "shareConfirmNo": "Não", + "yoloMode": "Modo YOLO" }, "sessionDetail": { "startNewAgent": "Iniciar um novo agente", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Mudar branch base", "noBranches": "Nenhum branch encontrado", "noMatchingBranches": "Nenhum branch corresponde à sua pesquisa", - "openTerminal": "Abrir terminal" + "openTerminal": "Abrir terminal", + "yoloModeOn": "Modo YOLO ativado - todas as operações são aprovadas automaticamente", + "yoloModeOff": "Modo YOLO desativado - aprovação necessária para operações", + "toggleYoloMode": "Alternar modo YOLO", + "renameSession": "Renomear sessão", + "clickToRename": "Clique para renomear", + "confirmRename": "Confirmar renomeação" }, "messages": { "waiting": "Aguardando resposta do agente...", @@ -70,7 +77,9 @@ "usingTool": "Usando ferramenta: **{{tool}}** {{details}}", "expandOutput": "Expandir saída", "collapseOutput": "Recolher saída", - "copyCommand": "Copiar comando" + "copyCommand": "Copiar comando", + "approve": "Aprovar", + "deny": "Recusar" }, "chatInput": { "autoMode": "Agente executando em modo totalmente automático - entrada não permitida", diff --git a/webview-ui/src/i18n/locales/ru/agentManager.json b/webview-ui/src/i18n/locales/ru/agentManager.json index b25411abd30..0025ebda447 100644 --- a/webview-ui/src/i18n/locales/ru/agentManager.json +++ b/webview-ui/src/i18n/locales/ru/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Поделиться сессией", "shareConfirmMessage": "Хотите опубликовать снимок этой сессии?", "shareConfirmYes": "Да", - "shareConfirmNo": "Нет" + "shareConfirmNo": "Нет", + "yoloMode": "Режим YOLO" }, "sessionDetail": { "startNewAgent": "Запустить нового агента", @@ -57,7 +58,13 @@ "branchPickerTooltip": "Сменить базовую ветку", "noBranches": "Ветки не найдены", "noMatchingBranches": "Нет веток, соответствующих твоему поиску", - "openTerminal": "Открыть терминал" + "openTerminal": "Открыть терминал", + "yoloModeOn": "Режим YOLO включён - все операции одобряются автоматически", + "yoloModeOff": "Режим YOLO выключен - требуется одобрение операций", + "toggleYoloMode": "Переключить режим YOLO", + "renameSession": "Переименовать сессию", + "clickToRename": "Нажмите для переименования", + "confirmRename": "Подтвердить переименование" }, "messages": { "waiting": "Ожидание ответа агента...", @@ -72,7 +79,9 @@ "usingTool": "Использование инструмента: **{{tool}}** {{details}}", "expandOutput": "Развернуть вывод", "collapseOutput": "Свернуть вывод", - "copyCommand": "Копировать команду" + "copyCommand": "Копировать команду", + "approve": "Одобрить", + "deny": "Отклонить" }, "chatInput": { "autoMode": "Агент работает в полностью автоматическом режиме - ввод запрещён", diff --git a/webview-ui/src/i18n/locales/th/agentManager.json b/webview-ui/src/i18n/locales/th/agentManager.json index 1185e2ea78d..a2fc32a151c 100644 --- a/webview-ui/src/i18n/locales/th/agentManager.json +++ b/webview-ui/src/i18n/locales/th/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "แชร์เซสชัน", "shareConfirmMessage": "คุณต้องการแชร์สแนปช็อตของเซสชันนี้แบบสาธารณะหรือไม่?", "shareConfirmYes": "ใช่", - "shareConfirmNo": "ไม่" + "shareConfirmNo": "ไม่", + "yoloMode": "โหมด YOLO" }, "sessionDetail": { "startNewAgent": "เริ่มเอเจนต์ใหม่", @@ -55,7 +56,13 @@ "branchPickerTooltip": "เปลี่ยนสาขาหลัก", "noBranches": "ไม่พบสาขา", "noMatchingBranches": "ไม่มีสาขาที่ตรงกับการค้นหาของคุณ", - "openTerminal": "เปิดเทอร์มินัล" + "openTerminal": "เปิดเทอร์มินัล", + "yoloModeOn": "โหมด YOLO เปิด - อนุมัติการดำเนินการทั้งหมดโดยอัตโนมัติ", + "yoloModeOff": "โหมด YOLO ปิด - ต้องได้รับอนุมัติสำหรับการดำเนินการ", + "toggleYoloMode": "สลับโหมด YOLO", + "renameSession": "เปลี่ยนชื่อเซสชัน", + "clickToRename": "คลิกเพื่อเปลี่ยนชื่อ", + "confirmRename": "ยืนยันการเปลี่ยนชื่อ" }, "messages": { "waiting": "รอการตอบกลับจากเอเจนต์...", @@ -70,7 +77,9 @@ "usingTool": "กำลังใช้เครื่องมือ: **{{tool}}** {{details}}", "expandOutput": "ขยายผลลัพธ์", "collapseOutput": "ยุบผลลัพธ์", - "copyCommand": "คัดลอกคำสั่ง" + "copyCommand": "คัดลอกคำสั่ง", + "approve": "อนุมัติ", + "deny": "ปฏิเสธ" }, "chatInput": { "autoMode": "เอเจนต์กำลังทำงานในโหมดอัตโนมัติเต็มรูปแบบ - ไม่อนุญาตให้ป้อนข้อมูล", diff --git a/webview-ui/src/i18n/locales/tr/agentManager.json b/webview-ui/src/i18n/locales/tr/agentManager.json index c940da503f5..3347ae8eec8 100644 --- a/webview-ui/src/i18n/locales/tr/agentManager.json +++ b/webview-ui/src/i18n/locales/tr/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Oturumu paylaş", "shareConfirmMessage": "Bu oturumun bir anlık görüntüsünü herkese açık olarak paylaşmak istiyor musunuz?", "shareConfirmYes": "Evet", - "shareConfirmNo": "Hayır" + "shareConfirmNo": "Hayır", + "yoloMode": "YOLO modu" }, "sessionDetail": { "startNewAgent": "Yeni bir ajan başlat", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Temel dalı değiştir", "noBranches": "Dal bulunamadı", "noMatchingBranches": "Aramanla eşleşen dal yok", - "openTerminal": "Terminali aç" + "openTerminal": "Terminali aç", + "yoloModeOn": "YOLO modu açık - tüm işlemler otomatik onaylanır", + "yoloModeOff": "YOLO modu kapalı - işlemler için onay gerekli", + "toggleYoloMode": "YOLO modunu değiştir", + "renameSession": "Oturumu yeniden adlandır", + "clickToRename": "Yeniden adlandırmak için tıkla", + "confirmRename": "Yeniden adlandırmayı onayla" }, "messages": { "waiting": "Ajan yanıtı bekleniyor...", @@ -70,7 +77,9 @@ "usingTool": "Araç kullanılıyor: **{{tool}}** {{details}}", "expandOutput": "Çıktıyı genişlet", "collapseOutput": "Çıktıyı daralt", - "copyCommand": "Komutu kopyala" + "copyCommand": "Komutu kopyala", + "approve": "Onayla", + "deny": "Reddet" }, "chatInput": { "autoMode": "Agent tam otomatik modda çalışıyor - girişe izin verilmiyor", diff --git a/webview-ui/src/i18n/locales/uk/agentManager.json b/webview-ui/src/i18n/locales/uk/agentManager.json index a137e9bd738..d964f9b54e1 100644 --- a/webview-ui/src/i18n/locales/uk/agentManager.json +++ b/webview-ui/src/i18n/locales/uk/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Поділитися сесією", "shareConfirmMessage": "Хочете опублікувати знімок цієї сесії?", "shareConfirmYes": "Так", - "shareConfirmNo": "Ні" + "shareConfirmNo": "Ні", + "yoloMode": "Режим YOLO" }, "sessionDetail": { "startNewAgent": "Запустити нового агента", @@ -57,7 +58,13 @@ "branchPickerTooltip": "Змінити базову гілку", "noBranches": "Гілки не знайдено", "noMatchingBranches": "Немає гілок, що відповідають твоєму пошуку", - "openTerminal": "Відкрити термінал" + "openTerminal": "Відкрити термінал", + "yoloModeOn": "Режим YOLO увімкнено - всі операції схвалюються автоматично", + "yoloModeOff": "Режим YOLO вимкнено - потрібне схвалення операцій", + "toggleYoloMode": "Перемкнути режим YOLO", + "renameSession": "Перейменувати сесію", + "clickToRename": "Натисніть для перейменування", + "confirmRename": "Підтвердити перейменування" }, "messages": { "waiting": "Очікування відповіді агента...", @@ -72,7 +79,9 @@ "usingTool": "Використання інструменту: **{{tool}}** {{details}}", "expandOutput": "Розгорнути вивід", "collapseOutput": "Згорнути вивід", - "copyCommand": "Копіювати команду" + "copyCommand": "Копіювати команду", + "approve": "Схвалити", + "deny": "Відхилити" }, "chatInput": { "autoMode": "Агент працює в повністю автоматичному режимі - введення заборонено", diff --git a/webview-ui/src/i18n/locales/vi/agentManager.json b/webview-ui/src/i18n/locales/vi/agentManager.json index 44b6629429f..31ee7a6f9a6 100644 --- a/webview-ui/src/i18n/locales/vi/agentManager.json +++ b/webview-ui/src/i18n/locales/vi/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "Chia sẻ phiên", "shareConfirmMessage": "Bạn có muốn chia sẻ công khai một ảnh chụp của phiên này không?", "shareConfirmYes": "Có", - "shareConfirmNo": "Không" + "shareConfirmNo": "Không", + "yoloMode": "Chế độ YOLO" }, "sessionDetail": { "startNewAgent": "Khởi động agent mới", @@ -55,7 +56,13 @@ "branchPickerTooltip": "Chuyển nhánh cơ sở", "noBranches": "Không tìm thấy nhánh nào", "noMatchingBranches": "Không có nhánh nào khớp với tìm kiếm của bạn", - "openTerminal": "Mở terminal" + "openTerminal": "Mở terminal", + "yoloModeOn": "Chế độ YOLO bật - tự động phê duyệt tất cả các thao tác", + "yoloModeOff": "Chế độ YOLO tắt - cần phê duyệt cho các thao tác", + "toggleYoloMode": "Bật/tắt chế độ YOLO", + "renameSession": "Đổi tên phiên", + "clickToRename": "Nhấp để đổi tên", + "confirmRename": "Xác nhận đổi tên" }, "messages": { "waiting": "Đang chờ phản hồi từ agent...", @@ -70,7 +77,9 @@ "usingTool": "Đang sử dụng công cụ: **{{tool}}** {{details}}", "expandOutput": "Mở rộng đầu ra", "collapseOutput": "Thu gọn đầu ra", - "copyCommand": "Sao chép lệnh" + "copyCommand": "Sao chép lệnh", + "approve": "Phê duyệt", + "deny": "Từ chối" }, "chatInput": { "autoMode": "Agent đang chạy ở chế độ tự động hoàn toàn - không cho phép nhập", diff --git a/webview-ui/src/i18n/locales/zh-CN/agentManager.json b/webview-ui/src/i18n/locales/zh-CN/agentManager.json index 29f826375d2..e95ae673c42 100644 --- a/webview-ui/src/i18n/locales/zh-CN/agentManager.json +++ b/webview-ui/src/i18n/locales/zh-CN/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "分享会话", "shareConfirmMessage": "是否要公开分享此会话的快照?", "shareConfirmYes": "是", - "shareConfirmNo": "否" + "shareConfirmNo": "否", + "yoloMode": "YOLO 模式" }, "sessionDetail": { "startNewAgent": "启动新代理", @@ -55,7 +56,13 @@ "branchPickerTooltip": "切换基础 Branch", "noBranches": "未找到 Branch", "noMatchingBranches": "没有匹配的 Branch", - "openTerminal": "打开终端" + "openTerminal": "打开终端", + "yoloModeOn": "YOLO 模式已开启 - 所有操作自动批准", + "yoloModeOff": "YOLO 模式已关闭 - 操作需要批准", + "toggleYoloMode": "切换 YOLO 模式", + "renameSession": "重命名会话", + "clickToRename": "点击重命名", + "confirmRename": "确认重命名" }, "messages": { "waiting": "等待代理响应...", @@ -70,7 +77,9 @@ "usingTool": "正在使用工具:**{{tool}}** {{details}}", "expandOutput": "展开输出", "collapseOutput": "收起输出", - "copyCommand": "复制命令" + "copyCommand": "复制命令", + "approve": "批准", + "deny": "拒绝" }, "chatInput": { "autoMode": "Agent 正在全自动模式下运行 - 不允许输入", diff --git a/webview-ui/src/i18n/locales/zh-TW/agentManager.json b/webview-ui/src/i18n/locales/zh-TW/agentManager.json index 96d733564ec..0a3681bcee4 100644 --- a/webview-ui/src/i18n/locales/zh-TW/agentManager.json +++ b/webview-ui/src/i18n/locales/zh-TW/agentManager.json @@ -14,7 +14,8 @@ "shareSession": "分享工作階段", "shareConfirmMessage": "是否要公開分享此工作階段的快照?", "shareConfirmYes": "是", - "shareConfirmNo": "否" + "shareConfirmNo": "否", + "yoloMode": "YOLO 模式" }, "sessionDetail": { "startNewAgent": "啟動新代理", @@ -55,7 +56,13 @@ "branchPickerTooltip": "切換基礎 Branch", "noBranches": "找不到 Branch", "noMatchingBranches": "沒有符合的 Branch", - "openTerminal": "開啟終端機" + "openTerminal": "開啟終端機", + "yoloModeOn": "YOLO 模式已開啟 - 所有操作自動批准", + "yoloModeOff": "YOLO 模式已關閉 - 操作需要批准", + "toggleYoloMode": "切換 YOLO 模式", + "renameSession": "重新命名工作階段", + "clickToRename": "點擊重新命名", + "confirmRename": "確認重新命名" }, "messages": { "waiting": "等待代理回應...", @@ -70,7 +77,9 @@ "usingTool": "正在使用工具:**{{tool}}** {{details}}", "expandOutput": "展開輸出", "collapseOutput": "收合輸出", - "copyCommand": "複製指令" + "copyCommand": "複製指令", + "approve": "批准", + "deny": "拒絕" }, "chatInput": { "autoMode": "Agent 正在全自動模式下執行 - 不允許輸入", diff --git a/webview-ui/src/kilocode/agent-manager/components/AgentManagerApp.css b/webview-ui/src/kilocode/agent-manager/components/AgentManagerApp.css index 1067935cab5..81ca68f65dd 100644 --- a/webview-ui/src/kilocode/agent-manager/components/AgentManagerApp.css +++ b/webview-ui/src/kilocode/agent-manager/components/AgentManagerApp.css @@ -260,6 +260,89 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + display: flex; + align-items: center; + gap: 8px; +} + +/* Editable title styles */ +.am-title-text { + cursor: pointer; + border-radius: 4px; + padding: 2px 4px; + margin: -2px -4px; + transition: background-color 0.15s; +} + +.am-title-text:hover { + background: var(--vscode-list-hoverBackground); +} + +.am-title-edit-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border: none; + background: transparent; + color: var(--vscode-descriptionForeground); + border-radius: 3px; + cursor: pointer; + opacity: 0; + transition: + opacity 0.15s, + background-color 0.15s; +} + +.am-header-title:hover .am-title-edit-btn { + opacity: 0.7; +} + +.am-title-edit-btn:hover { + opacity: 1; + background: var(--vscode-toolbar-hoverBackground); + color: var(--vscode-foreground); +} + +.am-title-edit-container { + display: flex; + align-items: center; + gap: 4px; + flex: 1; + min-width: 0; +} + +.am-title-input { + flex: 1; + min-width: 0; + padding: 4px 8px; + font-size: 18px; + font-weight: 600; + font-family: inherit; + background: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-focusBorder); + border-radius: 4px; + outline: none; +} + +.am-title-confirm-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border: none; + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border-radius: 4px; + cursor: pointer; + transition: background-color 0.15s; +} + +.am-title-confirm-btn:hover { + background: var(--vscode-button-hoverBackground); } .am-header-meta { @@ -588,6 +671,13 @@ text-overflow: ellipsis; } +/* YOLO mode indicator in session list */ +.am-yolo-indicator { + display: inline-flex; + align-items: center; + color: var(--vscode-charts-yellow, #e2c08d); +} + .am-ready-to-merge { font-size: calc(var(--vscode-font-size) * 0.8); color: var(--vscode-charts-green); @@ -620,6 +710,14 @@ min-width: 0; } +/* YOLO mode badge in session header */ +.am-yolo-badge { + display: inline-flex; + align-items: center; + color: var(--vscode-charts-yellow, #e2c08d); + vertical-align: middle; +} + /* Inline Run Mode Dropdown (next to send button) */ .am-run-mode-dropdown-inline { position: relative; @@ -825,3 +923,84 @@ .am-followup-copy-btn:hover { color: var(--vscode-foreground); } + +/* YOLO mode active state - yellow highlight */ +.am-run-mode-trigger-inline.am-yolo-active { + color: var(--vscode-charts-yellow, #e2c08d); + opacity: 1; +} + +.am-run-mode-trigger-inline.am-yolo-active:hover:not(:disabled) { + color: var(--vscode-charts-yellow, #e2c08d); +} + +/* Approval buttons for ask messages when YOLO mode is OFF */ +.am-approval-buttons { + display: flex; + gap: 8px; + margin-top: 8px; +} + +.am-approve-btn, +.am-deny-btn { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 4px 12px; + border-radius: 4px; + font-size: 12px; + cursor: pointer; + transition: all 0.15s; +} + +.am-approve-btn { + background: transparent; + color: var(--vscode-testing-iconPassed, #73c991); + border: 1px solid var(--vscode-testing-iconPassed, #73c991); +} + +.am-approve-btn:hover:not(:disabled) { + /* No VS Code variable for success background - rgba matches testing-iconPassed (#73c991) at 15% opacity */ + background: rgba(115, 201, 145, 0.15); +} + +.am-deny-btn { + background: transparent; + color: var(--vscode-errorForeground, #f14c4c); + border: 1px solid var(--vscode-errorForeground, #f14c4c); +} + +.am-deny-btn:hover:not(:disabled) { + /* rgba matches errorForeground (#f14c4c) at 15% opacity */ + background: var(--vscode-inputValidation-errorBackground, rgba(241, 76, 76, 0.15)); +} + +/* Disabled state - grayed out and not clickable */ +.am-approve-btn.am-approval-disabled, +.am-deny-btn.am-approval-disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.am-approve-btn.am-approval-disabled:hover, +.am-deny-btn.am-approval-disabled:hover { + background: inherit; +} + +/* Selected state - shows which button was clicked or auto-approved */ +/* Use higher specificity to override disabled state */ +.am-approve-btn.am-approval-disabled.am-approval-selected, +.am-approve-btn.am-approval-selected { + opacity: 1; + background: var(--vscode-testing-iconPassed, #73c991); + color: var(--vscode-editor-background); + border-color: var(--vscode-testing-iconPassed, #73c991); +} + +.am-deny-btn.am-approval-disabled.am-approval-selected, +.am-deny-btn.am-approval-selected { + opacity: 1; + background: var(--vscode-errorForeground); + color: var(--vscode-editor-background); + border-color: var(--vscode-errorForeground); +} diff --git a/webview-ui/src/kilocode/agent-manager/components/MessageList.tsx b/webview-ui/src/kilocode/agent-manager/components/MessageList.tsx index e749026dc60..7eccfcc6e46 100644 --- a/webview-ui/src/kilocode/agent-manager/components/MessageList.tsx +++ b/webview-ui/src/kilocode/agent-manager/components/MessageList.tsx @@ -1,9 +1,9 @@ -import React, { useEffect, useRef, useCallback, useMemo } from "react" +import React, { useState, useEffect, useRef, useCallback, useMemo } from "react" import { useAtomValue, useSetAtom } from "jotai" import { useTranslation } from "react-i18next" import { Virtuoso, VirtuosoHandle } from "react-virtuoso" import { sessionMessagesAtomFamily } from "../state/atoms/messages" -import { sessionInputAtomFamily } from "../state/atoms/sessions" +import { sessionInputAtomFamily, selectedSessionAtom } from "../state/atoms/sessions" import { sessionMessageQueueAtomFamily, sessionSendingMessageIdAtomFamily, @@ -28,6 +28,8 @@ import { User, Clock, Loader, + Check, + X, } from "lucide-react" import { cn } from "../../../lib/utils" @@ -65,8 +67,12 @@ export function MessageList({ sessionId }: MessageListProps) { const setInputValue = useSetAtom(sessionInputAtomFamily(sessionId)) const retryFailedMessage = useSetAtom(retryFailedMessageAtom) const removeFromQueue = useSetAtom(removeFromQueueAtom) + const selectedSession = useAtomValue(selectedSessionAtom) const virtuosoRef = useRef(null) + // Get session's yoloMode - if true, don't show approval buttons + const sessionYoloMode = selectedSession?.yoloMode ?? true // Default true for backward compat + // Combine command and command_output messages into single entries const combinedMessages = useMemo(() => combineCommandSequences(messages), [messages]) @@ -77,7 +83,7 @@ export function MessageList({ sessionId }: MessageListProps) { const msg = messages[i] if (msg.type !== "ask" || msg.ask !== "command") continue - let data: ReturnType = null + let data: { exitCode?: number; status?: string; isRunning?: boolean } | null = null // Find output messages following this command for (let j = i + 1; j < messages.length; j++) { @@ -85,7 +91,20 @@ export function MessageList({ sessionId }: MessageListProps) { if (next.type === "ask" && next.ask === "command") break if (next.ask !== "command_output" && next.say !== "command_output") continue - data = extractCommandMetadata(next) + const extracted = extractCommandMetadata(next) + if (extracted) { + // Merge data, keeping exitCode if we already have it + // (ask.command_output has exitCode, say.command_output doesn't) + if (data === null) { + data = extracted + } else { + data = { + exitCode: data.exitCode ?? extracted.exitCode, + status: extracted.status ?? data.status, + isRunning: extracted.isRunning, + } + } + } } if (data) info.set(msg.ts, data) @@ -167,6 +186,9 @@ export function MessageList({ sessionId }: MessageListProps) { key={msg.ts || index} message={msg} isLast={isLastCombinedMessage} + sessionId={sessionId} + sessionYoloMode={sessionYoloMode} + allMessages={messages} commandExecutionByTs={commandExecutionByTs} onSuggestionClick={handleSuggestionClick} onCopyToInput={handleCopyToInput} @@ -175,12 +197,15 @@ export function MessageList({ sessionId }: MessageListProps) { }, [ combinedMessages.length, + messages, commandExecutionByTs, handleSuggestionClick, handleCopyToInput, sendingMessageId, handleRetryMessage, handleDiscardMessage, + sessionId, + sessionYoloMode, ], ) @@ -227,12 +252,24 @@ function extractFollowUpData(message: ClineMessage): { question: string; suggest interface MessageItemProps { message: ClineMessage isLast: boolean + sessionId: string + sessionYoloMode: boolean + allMessages: ClineMessage[] commandExecutionByTs: Map onSuggestionClick?: (suggestion: SuggestionItem) => void onCopyToInput?: (suggestion: SuggestionItem) => void } -function MessageItem({ message, isLast, commandExecutionByTs, onSuggestionClick, onCopyToInput }: MessageItemProps) { +function MessageItem({ + message, + isLast, + sessionId, + sessionYoloMode, + allMessages, + commandExecutionByTs, + onSuggestionClick, + onCopyToInput, +}: MessageItemProps) { const { t } = useTranslation("agentManager") // --- 1. Determine Message Style & Content --- @@ -244,6 +281,10 @@ function MessageItem({ message, isLast, commandExecutionByTs, onSuggestionClick, let content: React.ReactNode = null let extraInfo: React.ReactNode = null let suggestions: SuggestionItem[] | undefined + let needsApproval = false // Track if this ask message needs approval buttons + let wasAutoApproved = false // Track if command was auto-approved (executed without user action) + let wasDenied = false // Track if user clicked Deny button + let userRespondedViaText = false // Track if user responded via text instead of clicking buttons // --- SAY --- if (message.type === "say") { @@ -320,6 +361,27 @@ function MessageItem({ message, isLast, commandExecutionByTs, onSuggestionClick, terminalStatus={execInfo?.status} /> ) + // Show approval buttons only when NOT in YOLO mode + if (!sessionYoloMode) { + needsApproval = true + // Check if command was executed or user responded via text + const msgIndex = allMessages.findIndex((m) => m.ts === message.ts) + if (msgIndex !== -1) { + const messagesAfter = allMessages.slice(msgIndex + 1) + const hasOutput = messagesAfter.some((m) => m.type === "say" && m.say === "command_output") + const userFeedbackMsg = messagesAfter.find((m) => m.type === "say" && m.say === "user_feedback") + if (hasOutput) { + wasAutoApproved = true + } else if (userFeedbackMsg) { + const feedbackText = userFeedbackMsg.text || (userFeedbackMsg as any).content || "" + if (feedbackText === "User denied this operation") { + wasDenied = true + } else { + userRespondedViaText = true + } + } + } + } break } case "command_output": { @@ -347,6 +409,25 @@ function MessageItem({ message, isLast, commandExecutionByTs, onSuggestionClick, } else { content = } + // Show approval buttons only when NOT in YOLO mode and not already answered + if (!sessionYoloMode && !message.isAnswered) { + needsApproval = true + // Check if user responded via text or clicked Deny + const msgIndex = allMessages.findIndex((m) => m.ts === message.ts) + if (msgIndex !== -1) { + const userFeedbackMsg = allMessages + .slice(msgIndex + 1) + .find((m) => m.type === "say" && m.say === "user_feedback") + if (userFeedbackMsg) { + const feedbackText = userFeedbackMsg.text || (userFeedbackMsg as any).content || "" + if (feedbackText === "User denied this operation") { + wasDenied = true + } else { + userRespondedViaText = true + } + } + } + } break } default: @@ -365,6 +446,16 @@ function MessageItem({ message, isLast, commandExecutionByTs, onSuggestionClick, {extraInfo} {content &&
{content}
} + {needsApproval && ( + + )} {suggestions && suggestions.length > 0 && onSuggestionClick && ( (null) + + // Determine final state: user clicked OR auto-approved by CLI OR command executed OR denied OR user responded via text + const isResolved = response !== null || isAnswered || wasAutoApproved || wasDenied || userRespondedViaText + // Determine which button should appear selected + // Priority: local click state > wasDenied > wasAutoApproved/isAnswered > null (for text response) + const effectiveResponse = + response ?? + (wasDenied ? "denied" : userRespondedViaText ? null : isAnswered || wasAutoApproved ? "approved" : null) + + const handleApprove = () => { + if (isResolved) return + setResponse("approved") + vscode.postMessage({ + type: "agentManager.respondToApproval", + sessionId, + approved: true, + messageTs, + }) + } + + const handleDeny = () => { + if (isResolved) return + setResponse("denied") + vscode.postMessage({ + type: "agentManager.respondToApproval", + sessionId, + approved: false, + text: "User denied this operation", + messageTs, + }) + } + + return ( +
+ + +
+ ) +} + interface QueuedMessageItemProps { queuedMessage: QueuedMessage isSending: boolean diff --git a/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx b/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx index d632ca8a26e..abf9b6c03f7 100644 --- a/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx +++ b/webview-ui/src/kilocode/agent-manager/components/SessionDetail.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useMemo, useRef } from "react" -import { useAtom, useAtomValue } from "jotai" +import { useAtom, useAtomValue, useSetAtom } from "jotai" import { useTranslation } from "react-i18next" import { selectedSessionAtom, @@ -7,6 +7,8 @@ import { pendingSessionAtom, preferredRunModeAtom, versionCountAtom, + yoloModeAtom, + renameSessionAtom, generateVersionLabels, VERSION_COUNT_OPTIONS, type RunMode, @@ -29,6 +31,8 @@ import { Layers, X, Terminal, + Pencil, + Check, } from "lucide-react" import DynamicTextArea from "react-textarea-autosize" import { cn } from "../../../lib/utils" @@ -42,10 +46,52 @@ export function SessionDetail() { const machineUiState = useAtomValue(sessionMachineUiStateAtom) const selectedSessionState = useAtomValue(selectedSessionMachineStateAtom) const prevSessionStateRef = useRef<{ id: string; status: string } | undefined>(undefined) + const renameSession = useSetAtom(renameSessionAtom) + + // Editable title state + const [isEditingTitle, setIsEditingTitle] = useState(false) + const [editTitleValue, setEditTitleValue] = useState("") + const titleInputRef = useRef(null) // Hooks must be called unconditionally before any early returns const timeLabels = useMemo(() => createRelativeTimeLabels(t), [t]) + // Focus input when entering edit mode + useEffect(() => { + if (isEditingTitle && titleInputRef.current) { + titleInputRef.current.focus() + titleInputRef.current.select() + } + }, [isEditingTitle]) + + // Reset edit state when session changes + useEffect(() => { + setIsEditingTitle(false) + }, [selectedSession?.sessionId]) + + // Handlers for title editing + const startEditingTitle = () => { + if (selectedSession) { + setEditTitleValue(selectedSession.label) + setIsEditingTitle(true) + } + } + + const commitTitleEdit = () => { + if (!selectedSession) return + const trimmed = editTitleValue.trim() + if (trimmed && trimmed !== selectedSession.label) { + renameSession({ sessionId: selectedSession.sessionId, label: trimmed }) + // Persist to backend so rename survives panel close/reopen + vscode.postMessage({ + type: "agentManager.renameSession", + sessionId: selectedSession.sessionId, + label: trimmed, + }) + } + setIsEditingTitle(false) + } + // Auto-cancel session when it ends (as if user clicked the red cancel button) // Only send cancel once when transitioning from "running" to a terminal state // Track both sessionId and status to avoid spurious cancels on session switches @@ -95,7 +141,57 @@ export function SessionDetail() {
- {selectedSession.label} + {isEditingTitle ? ( +
+ setEditTitleValue(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + commitTitleEdit() + } else if (e.key === "Escape") { + setIsEditingTitle(false) + } + }} + onBlur={commitTitleEdit} + aria-label={t("sessionDetail.renameSession")} + /> + +
+ ) : ( + <> + {selectedSession.yoloMode !== false && ( + + + + )} + e.key === "Enter" && startEditingTitle()} + title={t("sessionDetail.clickToRename")}> + {selectedSession.label} + + + + )}
{showSpinner && ( @@ -151,7 +247,7 @@ export function SessionDetail() {
)} - {isActive && ( + {isActive && selectedSession.yoloMode !== false && (
{t("sessionDetail.autoModeWarning")} @@ -229,6 +325,7 @@ function NewAgentForm() { const [promptText, setPromptText] = useState("") const [runMode, setRunMode] = useAtom(preferredRunModeAtom) const [versionCount, setVersionCount] = useAtom(versionCountAtom) + const [yoloMode, setYoloMode] = useAtom(yoloModeAtom) const [isStarting, setIsStarting] = useState(false) const [isFocused, setIsFocused] = useState(false) const [isDropdownOpen, setIsDropdownOpen] = useState(false) @@ -297,6 +394,7 @@ function NewAgentForm() { type: "agentManager.startSession", prompt: trimmedPrompt, parallelMode: effectiveRunMode === "worktree", + yoloMode, versions: versionCount, labels, existingBranch: selectedBranch || undefined, @@ -387,6 +485,19 @@ function NewAgentForm() { />
+ {/* YOLO Mode Toggle */} + + + +
)} + {session.yoloMode !== false && ( + + + + )}
{isWorktree && isCompleted &&
{t("sidebar.readyToMerge")}
}
diff --git a/webview-ui/src/kilocode/agent-manager/state/atoms/sessions.ts b/webview-ui/src/kilocode/agent-manager/state/atoms/sessions.ts index 13df610ee7e..5abe1af73e5 100644 --- a/webview-ui/src/kilocode/agent-manager/state/atoms/sessions.ts +++ b/webview-ui/src/kilocode/agent-manager/state/atoms/sessions.ts @@ -25,6 +25,7 @@ export interface AgentSession { parallelMode?: ParallelModeInfo gitUrl?: string autoMode?: boolean // True if session was started with --auto flag (non-interactive) + yoloMode?: boolean // True if session was started with --yolo flag (auto-approve operations) } /** @@ -37,6 +38,7 @@ export interface PendingSession { parallelMode?: boolean gitUrl?: string autoMode?: boolean // True if session will be started with --auto flag + yoloMode?: boolean // True if session will be started with --yolo flag } export interface RemoteSession { @@ -65,6 +67,10 @@ export type RunMode = "local" | "worktree" // Default to local until worktree mode is ready to ship export const preferredRunModeAtom = atom("local") +// YOLO mode preference - when ON, auto-approve all operations; when OFF, require manual approval +// Default to true for backward compatibility with existing behavior +export const yoloModeAtom = atom(true) + // Version count for multi-version mode (1 = single, 2-4 = multi-version with worktrees) export type VersionCount = 1 | 2 | 3 | 4 export const MAX_VERSION_COUNT = 4 @@ -145,8 +151,13 @@ export const upsertSessionAtom = atom(null, (get, set, session: AgentSession) => const current = get(sessionsMapAtom) const order = get(sessionOrderAtom) const isNewSession = !order.includes(session.sessionId) + const existingSession = current[session.sessionId] + + // Preserve locally-modified label if session already exists + // This prevents the extension from overwriting user's rename + const mergedSession = existingSession ? { ...session, label: existingSession.label } : session - set(sessionsMapAtom, { ...current, [session.sessionId]: session }) + set(sessionsMapAtom, { ...current, [session.sessionId]: mergedSession }) if (isNewSession) { set(sessionOrderAtom, [session.sessionId, ...order]) if (get(selectedSessionIdAtom) === null) { @@ -202,3 +213,17 @@ export const setRemoteSessionsAtom = atom(null, (_get, set, sessions: RemoteSess set(remoteSessionsAtom, sessions) set(isRefreshingRemoteSessionsAtom, false) }) + +export const renameSessionAtom = atom(null, (get, set, payload: { sessionId: string; label: string }) => { + const current = get(sessionsMapAtom) + const session = current[payload.sessionId] + if (!session) return + + set(sessionsMapAtom, { + ...current, + [payload.sessionId]: { + ...session, + label: payload.label, + }, + }) +}) From 5b6c869d8d1867882db43c9645162f8cf8169969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Beutin?= Date: Fri, 16 Jan 2026 10:14:35 +0100 Subject: [PATCH 2/2] fix(cli): isolate yoloMode state per session to prevent global pollution Previously, when a CLI was launched with --yolo flag, it would send a yoloMode message to the extension host, modifying the global state. This caused a bug where launching a CLI with --yolo outside of Agent Manager would affect all Agent Manager sessions. Changes: - Remove the code in cli.ts that sends yoloMode to extension host - Update toggleYoloModeAtom to only modify local state - Update tests to reflect the new isolation behavior Each CLI instance now manages its own yoloMode state locally via yoloModeAtom. Agent Manager sessions get their isolated yoloMode through the --yolo flag passed to each CLI process. Fixes issue reported in PR #4890 review by @marius-kilocode --- cli/src/__tests__/cli-yolo-mode.test.ts | 75 +++++++++++++++---------- cli/src/cli.ts | 16 ++---- cli/src/state/atoms/actions.ts | 27 +++++---- cli/src/state/atoms/ui.ts | 9 ++- 4 files changed, 70 insertions(+), 57 deletions(-) diff --git a/cli/src/__tests__/cli-yolo-mode.test.ts b/cli/src/__tests__/cli-yolo-mode.test.ts index ac9ff5c889b..1c7300f9c60 100644 --- a/cli/src/__tests__/cli-yolo-mode.test.ts +++ b/cli/src/__tests__/cli-yolo-mode.test.ts @@ -1,63 +1,76 @@ import { describe, it, expect } from "vitest" /** - * Tests for YOLO mode behavior in JSON-IO mode. + * Tests for YOLO mode isolation behavior. * - * In JSON-IO mode (--json-io flag), we don't send the yoloMode message to the extension host. - * This prevents Task.ts from auto-answering followup questions, allowing the CLI's own - * approval layer to handle YOLO behavior (which correctly excludes followups from auto-approval). + * IMPORTANT: The CLI does NOT send yoloMode messages to the extension host. + * This is intentional to ensure session isolation: + * + * 1. Each CLI instance manages its own yoloMode state locally via yoloModeAtom + * 2. Sending yoloMode to the extension host would pollute the global state + * 3. This would affect other sessions (like Agent Manager) that should have + * isolated yoloMode per session via the --yolo flag + * + * See: https://github.com/Kilo-Org/kilocode/pull/4890 */ -describe("CLI yoloMode in JSON-IO mode", () => { - describe("shouldSendYoloModeToExtension", () => { - // Extract the logic we're testing into a pure function - function shouldSendYoloModeToExtension(options: { +describe("CLI yoloMode isolation", () => { + describe("yoloMode state management", () => { + /** + * This function represents the OLD behavior that was removed. + * Previously, CLI would send yoloMode to extension host in non-JSON-IO mode. + * Now, yoloMode is NEVER sent to extension host to ensure session isolation. + */ + function shouldSendYoloModeToExtension(_options: { jsonInteractive?: boolean ci?: boolean yolo?: boolean }): boolean { - // This mirrors the condition in cli.ts: - // if (!this.options.jsonInteractive) { sendWebviewMessage({ type: "yoloMode", ... }) } - return !options.jsonInteractive + // NEW BEHAVIOR: Never send yoloMode to extension host + // Each CLI instance manages its own state locally + return false } - function getYoloModeValue(options: { ci?: boolean; yolo?: boolean }): boolean { - // This mirrors: Boolean(this.options.ci || this.options.yolo) + /** + * Helper to get local yoloMode value based on CLI flags. + * This value is stored in the CLI's local yoloModeAtom, not sent to extension. + */ + function getLocalYoloModeValue(options: { ci?: boolean; yolo?: boolean }): boolean { return Boolean(options.ci || options.yolo) } - it("should send yoloMode message when not in JSON-IO mode", () => { - expect(shouldSendYoloModeToExtension({ jsonInteractive: false })).toBe(true) - expect(shouldSendYoloModeToExtension({ jsonInteractive: undefined })).toBe(true) - expect(shouldSendYoloModeToExtension({})).toBe(true) + it("should NEVER send yoloMode message to extension host (session isolation)", () => { + // Even in non-JSON-IO mode, we don't send yoloMode to avoid global state pollution + expect(shouldSendYoloModeToExtension({ jsonInteractive: false })).toBe(false) + expect(shouldSendYoloModeToExtension({ jsonInteractive: undefined })).toBe(false) + expect(shouldSendYoloModeToExtension({})).toBe(false) }) - it("should NOT send yoloMode message when in JSON-IO mode", () => { + it("should NOT send yoloMode message in JSON-IO mode", () => { expect(shouldSendYoloModeToExtension({ jsonInteractive: true })).toBe(false) }) - it("should NOT send yoloMode message in JSON-IO mode even with yolo flag", () => { + it("should NOT send yoloMode message with any flag combination", () => { expect(shouldSendYoloModeToExtension({ jsonInteractive: true, yolo: true })).toBe(false) - }) - - it("should NOT send yoloMode message in JSON-IO mode even with ci flag", () => { expect(shouldSendYoloModeToExtension({ jsonInteractive: true, ci: true })).toBe(false) + expect(shouldSendYoloModeToExtension({ jsonInteractive: false, yolo: true })).toBe(false) + expect(shouldSendYoloModeToExtension({ jsonInteractive: false, ci: true })).toBe(false) }) - it("should set yoloMode value to true when yolo flag is set", () => { - expect(getYoloModeValue({ yolo: true })).toBe(true) + it("should set local yoloMode value to true when yolo flag is set", () => { + expect(getLocalYoloModeValue({ yolo: true })).toBe(true) }) - it("should set yoloMode value to true when ci flag is set", () => { - expect(getYoloModeValue({ ci: true })).toBe(true) + it("should set local yoloMode value to true when ci flag is set", () => { + expect(getLocalYoloModeValue({ ci: true })).toBe(true) }) - it("should set yoloMode value to true when both flags are set", () => { - expect(getYoloModeValue({ yolo: true, ci: true })).toBe(true) + it("should set local yoloMode value to true when both flags are set", () => { + expect(getLocalYoloModeValue({ yolo: true, ci: true })).toBe(true) }) - it("should set yoloMode value to false when neither flag is set", () => { - expect(getYoloModeValue({})).toBe(false) - expect(getYoloModeValue({ yolo: false, ci: false })).toBe(false) + it("should set local yoloMode value to false when neither flag is set", () => { + expect(getLocalYoloModeValue({})).toBe(false) + expect(getLocalYoloModeValue({ yolo: false, ci: false })).toBe(false) }) }) }) diff --git a/cli/src/cli.ts b/cli/src/cli.ts index 0f7c3b2dc2f..8b7c0d09bfa 100644 --- a/cli/src/cli.ts +++ b/cli/src/cli.ts @@ -292,16 +292,12 @@ export class CLI { await this.injectConfigurationToExtension() logs.debug("CLI configuration injected into extension", "CLI") - const extensionHost = this.service.getExtensionHost() - // In JSON-IO mode, don't set yoloMode on the extension host. - // This prevents Task.ts from auto-answering followup questions. - // The CLI's approval layer handles YOLO behavior and correctly excludes followups. - if (!this.options.jsonInteractive) { - extensionHost.sendWebviewMessage({ - type: "yoloMode", - bool: Boolean(this.options.ci || this.options.yolo), - }) - } + // NOTE: We intentionally do NOT send yoloMode to the extension host here. + // Each CLI instance manages its own yoloMode state locally via yoloModeAtom. + // Sending yoloMode to the extension host would pollute the global state, + // affecting other sessions (like Agent Manager sessions) that should have + // isolated yoloMode per session via the --yolo flag. + // See: https://github.com/Kilo-Org/kilocode/pull/4890 // Request router models after configuration is injected void this.requestRouterModels() diff --git a/cli/src/state/atoms/actions.ts b/cli/src/state/atoms/actions.ts index e0985f81947..f6a5ba538c8 100644 --- a/cli/src/state/atoms/actions.ts +++ b/cli/src/state/atoms/actions.ts @@ -307,21 +307,20 @@ export const sendSecondaryButtonClickAtom = atom(null, async (get, set) => { }) /** - * Action atom to toggle YOLO mode - * Sends the yoloMode message to the extension to enable/disable auto-approval of all operations + * Action atom to toggle YOLO mode locally. + * Updates the local yoloModeAtom to enable/disable auto-approval of all operations. + * + * NOTE: We intentionally do NOT send yoloMode to the extension host. + * This ensures session isolation - each CLI instance manages its own yoloMode state. + * Sending to extension host would pollute global state and affect other sessions. + * See: https://github.com/Kilo-Org/kilocode/pull/4890 */ -export const toggleYoloModeAtom = atom(null, async (get, set) => { - const currentValue = get(yoloModeAtom) - const newValue = !currentValue - - set(yoloModeAtom, newValue) - logs.info(`YOLO mode ${newValue ? "enabled" : "disabled"}`, "actions") - - const message: WebviewMessage = { - type: "yoloMode", - bool: newValue, - } - await set(sendWebviewMessageAtom, message) +export const toggleYoloModeAtom = atom(null, (_get, set) => { + set(yoloModeAtom, (currentValue) => { + const newValue = !currentValue + logs.info(`YOLO mode ${newValue ? "enabled" : "disabled"}`, "actions") + return newValue + }) }) /** diff --git a/cli/src/state/atoms/ui.ts b/cli/src/state/atoms/ui.ts index 085e6e71b99..894ede2c501 100644 --- a/cli/src/state/atoms/ui.ts +++ b/cli/src/state/atoms/ui.ts @@ -55,8 +55,13 @@ export const messageCutoffTimestampAtom = atom(0) export const errorAtom = atom(null) /** - * Atom to track YOLO mode state - * When enabled, all operations are auto-approved without confirmation + * Atom to track YOLO mode state (local to this CLI instance) + * When enabled, all operations are auto-approved without confirmation. + * + * NOTE: This state is intentionally NOT sent to the extension host. + * Each CLI instance manages its own yoloMode locally to ensure session isolation. + * Agent Manager sessions get their isolated yoloMode through the --yolo flag. + * See: https://github.com/Kilo-Org/kilocode/pull/4890 */ export const yoloModeAtom = atom(false)