feat(tui): approval card leads with the plain summary; footer labels its values; flag roster-dropped pins

0.10.1 lane B (experience marks 4 and 8, U3, #6035).

Approval card (E6, mark 4):
- ApprovalRequest carries the engine's model-independent `summary`
  (tools/approval_summary.rs) and its workspace. The English card heading
  is that sentence ("Run `cargo test`", "Write src/main.rs"); other packs
  keep the tool name until the summary is localized. File/Path/Dir detail
  rows show workspace-relative paths.
- Badges name the effect, not a tier: Reads only / Changes files / Runs a
  command / Uses the network / Uses a connected app / Starts an agent /
  Unclassified tool, and "Can't be undone" for destructive or publishing
  calls. REVIEW/APPROVAL badge ids are renamed; five ids are added.
- Categories: Command, Connected app (server), Agent. Choices: Allow for
  this conversation, Always allow in this repo, Don't allow, Stop this
  turn; Esc reads "stop". Critical-card prose drops policy/abort/Bash/MCP.
  All 15 packs translated.

Footer (mark 8, U3):
- The route's effort field is labeled ("thinking: max") and the context
  reading reads "context 12%" instead of "ctx".
- A keyless launch paints "model not connected" on the route chip instead
  of naming a default route that cannot answer, and the chip is not a
  route control until a model is connected. Tests whose fixtures carry no
  key but are about a connected route now set that explicitly; new
  keyless_launch_route_chip_says_not_connected pins the chip.

Fleet (#6035): the Fleet overview and the route picker (where a pin is
edited) flag a route whose fresh live roster no longer lists the model,
even when a bundled catalog row still offers it. Warning only; the pin
is never rewritten.

Checks:
- cargo test -p codewhale-localization: 50 passed, 0 failed
- cargo test -p codewhale-tui --lib -- approval widgets tui::ui:: infoline
  phase_strip fleet_detail underwater golden
  commands::groups::config::status provider_catalog_live: 1545 passed,
  1 failed (task_manager pending_approval_suspends_idle_... timing test;
  alone: 1 passed, 0 failed)
- cargo test -p codewhale-tui --lib -- approval (after the prose pass):
  295 passed, 1 failed (same timing test)
- scripts/check-lexicon.py: no findings left in approval or info-line keys
- not run: PTY suites, live providers

Refs #6035

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01TvKGZz4LrKfrQiAXip48JS
This commit is contained in:
CodeWhale BotandClaude Opus 5.5 committed 2026-09-22 20:41:49 -07:00
1 parent caa5f94876
commit c52a5c43ae
24 files changed
+702 -337

No files matched your search

+20 -14
View File
@@ -1067,30 +1067,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERIR --",
"VimModeVisual": "-- VISUAL --",
"ApprovalRiskReview": "REVISIÓ",
"ApprovalRiskElevated": "APROVACIÓ",
"ApprovalRiskDestructive": "DESTRUCTIU",
"ApprovalEffectReadsOnly": "Només llegeix",
"ApprovalEffectChangesFiles": "Canvia fitxers",
"ApprovalRiskDestructive": "No es pot desfer",
"ApprovalEffectRunsCommand": "Executa una ordre",
"ApprovalEffectUsesNetwork": "Usa la xarxa",
"ApprovalEffectConnectedApp": "Usa una app connectada",
"ApprovalEffectStartsAgent": "Inicia un agent",
"ApprovalEffectUnclassified": "Eina sense classificar",
"ApprovalTimedOutDenied": "La sol·licitud d'aprovació ha expirat - denegada",
"ApprovalCategorySafe": "Segur",
"ApprovalCategoryFileWrite": "Escriptura de fitxer",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Ordre",
"ApprovalCategoryNetwork": "Xarxa",
"ApprovalCategoryMcpRead": "Lectura MCP",
"ApprovalCategoryMcpAction": "Acció MCP",
"ApprovalCategoryAgent": "Subagent",
"ApprovalCategoryMcpRead": "App connectada",
"ApprovalCategoryMcpAction": "App connectada",
"ApprovalCategoryAgent": "Agent",
"ApprovalCategoryUnknown": "Desconegut",
"ApprovalFieldType": "Tipus: ",
"ApprovalFieldAbout": "Quant a: ",
"ApprovalFieldImpact": "Impacte: ",
"ApprovalFieldParams": "Paràmetres: ",
"ApprovalOptionApproveOnce": "Permet una vegada",
"ApprovalOptionApproveAlways": "Permet per a aquesta sessió (aquest tipus)",
"ApprovalOptionAllowExactRepo": "Permet sempre aquesta regla exacta en aquest repo",
"ApprovalOptionApproveAlways": "Permet en aquesta conversa",
"ApprovalOptionAllowExactRepo": "Permet sempre en aquest repo",
"ApprovalSaveAskRuleHint": " s permet una vegada + pregunta sempre per la regla exacta",
"ApprovalOptionDeny": "Denega aquesta crida",
"ApprovalOptionAbortTurn": "Avorta el torn",
"ApprovalOptionDeny": "No ho permetis",
"ApprovalOptionAbortTurn": "Atura aquest torn",
"ApprovalBlockTitle": "aprovació",
"ApprovalControlsHint": " · Pg↑/↓ revisa · {details} detalls · Esc avorta",
"ApprovalControlsHint": " · Pg↑/↓ revisa · {details} detalls · Esc atura",
"ApprovalTruncationHint": " … truncat · prem {details} per a tots els detalls",
"ApprovalFullAccessPolicyBlocked": "{tool} blocat: Full Access no pot evitar aquesta política",
"AutoReviewQuestionSkipped": "Auto-Review ha omès una pregunta de l'usuari i ha continuat autònomament",
@@ -1220,7 +1225,7 @@
"ApprovalDescUnknown": "Sol·licita executar una eina no classificada. Revisa els paràmetres amb cura.",
"ApprovalImpactSafe": "Operació de només lectura.",
"ApprovalImpactFileWrite": "Escriu fitxers a l'espai de treball o a un abast d'escriptura aprovat.",
"ApprovalImpactShell": "Executa una ordre Bash al teu espai de treball.",
"ApprovalImpactShell": "Executa una ordre shell al teu espai de treball.",
"ApprovalImpactNetwork": "Pot arribar a serveis de xarxa o contingut remot.",
"ApprovalImpactMcpRead": "Llegeix d'un servidor MCP sense escriptura local evident.",
"ApprovalImpactMcpAction": "Crida una acció d'un servidor MCP que pot tenir efectes secundaris.",
@@ -1350,13 +1355,14 @@
"FooterHintOutput": "sortida",
"FooterHintContext": "context",
"InfoLineHelp": "ajuda",
"InfoLineContext": "ctx",
"InfoLineContext": "context",
"InfoLineTtft": "ttft",
"InfoLinePeak": "hora punta",
"InfoLineOffPeak": "hora vall",
"InfoLineWhales": "balenes",
"InfoLineAutomation": "automatització",
"InfoLineNotConnected": "no connectat",
"InfoLineThinking": "raonament: {level}",
"EmptyStateNoGit": "sense git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "Què vols aconseguir?",
+20 -14
View File
@@ -1067,30 +1067,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERT --",
"VimModeVisual": "-- VISUAL --",
"ApprovalRiskReview": "PRÜFUNG",
"ApprovalRiskElevated": "FREIGABE",
"ApprovalRiskDestructive": "DESTRUKTIV",
"ApprovalEffectReadsOnly": "Nur lesen",
"ApprovalEffectChangesFiles": "Ändert Dateien",
"ApprovalRiskDestructive": "Nicht rückgängig zu machen",
"ApprovalEffectRunsCommand": "Führt einen Befehl aus",
"ApprovalEffectUsesNetwork": "Nutzt das Netzwerk",
"ApprovalEffectConnectedApp": "Nutzt eine verbundene App",
"ApprovalEffectStartsAgent": "Startet einen Agenten",
"ApprovalEffectUnclassified": "Nicht eingestuftes Werkzeug",
"ApprovalTimedOutDenied": "Genehmigungsanfrage abgelaufen - verweigert",
"ApprovalCategorySafe": "Sicher",
"ApprovalCategoryFileWrite": "Datei schreiben",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Befehl",
"ApprovalCategoryNetwork": "Netzwerk",
"ApprovalCategoryMcpRead": "MCP-Lesen",
"ApprovalCategoryMcpAction": "MCP-Aktion",
"ApprovalCategoryAgent": "Subagent",
"ApprovalCategoryMcpRead": "Verbundene App",
"ApprovalCategoryMcpAction": "Verbundene App",
"ApprovalCategoryAgent": "Agent",
"ApprovalCategoryUnknown": "Unbekannt",
"ApprovalFieldType": "Typ: ",
"ApprovalFieldAbout": "Info: ",
"ApprovalFieldImpact": "Auswirkung: ",
"ApprovalFieldParams": "Parameter: ",
"ApprovalOptionApproveOnce": "Einmal erlauben",
"ApprovalOptionApproveAlways": "Für diese Sitzung erlauben (diese Art)",
"ApprovalOptionAllowExactRepo": "Diese exakte Regel in diesem Repo immer erlauben",
"ApprovalOptionApproveAlways": "In dieser Unterhaltung erlauben",
"ApprovalOptionAllowExactRepo": "In diesem Repo immer erlauben",
"ApprovalSaveAskRuleHint": " s einmal erlauben + exakte Regel immer erfragen",
"ApprovalOptionDeny": "Aufruf ablehnen",
"ApprovalOptionAbortTurn": "Turn abbrechen",
"ApprovalOptionDeny": "Nicht erlauben",
"ApprovalOptionAbortTurn": "Diese Runde stoppen",
"ApprovalBlockTitle": "Freigabe",
"ApprovalControlsHint": " · Pg↑/↓ prüfen · {details} Details · Esc abbrechen",
"ApprovalControlsHint": " · Pg↑/↓ prüfen · {details} Details · Esc stoppen",
"ApprovalTruncationHint": " … gekürzt · {details} für volle Details",
"ApprovalFullAccessPolicyBlocked": "{tool} blockiert: Full Access kann diese Policy nicht umgehen",
"AutoReviewQuestionSkipped": "Auto-Review hat eine Nutzerfrage übersprungen und autonom fortgefahren",
@@ -1220,7 +1225,7 @@
"ApprovalDescUnknown": "Fordert an, ein nicht klassifiziertes Tool auszuführen. Parameter sorgfältig prüfen.",
"ApprovalImpactSafe": "Read-only-Operation.",
"ApprovalImpactFileWrite": "Schreibt Dateien im Workspace oder in einem freigegebenen Schreibbereich.",
"ApprovalImpactShell": "Führt einen Bash-Befehl in Ihrem Workspace aus.",
"ApprovalImpactShell": "Führt einen Shell-Befehl in Ihrem Workspace aus.",
"ApprovalImpactNetwork": "Kann Netzwerkdienste oder Remote-Inhalte erreichen.",
"ApprovalImpactMcpRead": "Liest von einem MCP-Server ohne erkennbaren lokalen Schreibzugriff.",
"ApprovalImpactMcpAction": "Ruft eine MCP-Server-Aktion mit möglichen Seiteneffekten auf.",
@@ -1350,13 +1355,14 @@
"FooterHintOutput": "Ausgabe",
"FooterHintContext": "Kontext",
"InfoLineHelp": "Hilfe",
"InfoLineContext": "ctx",
"InfoLineContext": "Kontext",
"InfoLineTtft": "ttft",
"InfoLinePeak": "Spitzenzeit",
"InfoLineOffPeak": "Nebenzeit",
"InfoLineWhales": "Wale",
"InfoLineAutomation": "Automatisierung",
"InfoLineNotConnected": "nicht verbunden",
"InfoLineThinking": "Denken: {level}",
"EmptyStateNoGit": "kein git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "Was möchtest du erreichen?",
+26 -20
View File
@@ -1090,30 +1090,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERT --",
"VimModeVisual": "-- VISUAL --",
"ApprovalRiskReview": "REVIEW",
"ApprovalRiskElevated": "APPROVAL",
"ApprovalRiskDestructive": "DESTRUCTIVE",
"ApprovalEffectReadsOnly": "Reads only",
"ApprovalEffectChangesFiles": "Changes files",
"ApprovalRiskDestructive": "Can't be undone",
"ApprovalEffectRunsCommand": "Runs a command",
"ApprovalEffectUsesNetwork": "Uses the network",
"ApprovalEffectConnectedApp": "Uses a connected app",
"ApprovalEffectStartsAgent": "Starts an agent",
"ApprovalEffectUnclassified": "Unclassified tool",
"ApprovalTimedOutDenied": "Approval request timed out - denied",
"ApprovalCategorySafe": "Safe",
"ApprovalCategoryFileWrite": "File Write",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Command",
"ApprovalCategoryNetwork": "Network",
"ApprovalCategoryMcpRead": "MCP Read",
"ApprovalCategoryMcpAction": "MCP Action",
"ApprovalCategoryAgent": "Sub-agent",
"ApprovalCategoryMcpRead": "Connected app",
"ApprovalCategoryMcpAction": "Connected app",
"ApprovalCategoryAgent": "Agent",
"ApprovalCategoryUnknown": "Unknown",
"ApprovalFieldType": "Type: ",
"ApprovalFieldAbout": "About: ",
"ApprovalFieldImpact": "Impact: ",
"ApprovalFieldParams": "Params: ",
"ApprovalOptionApproveOnce": "Allow once",
"ApprovalOptionApproveAlways": "Allow for this session (this kind)",
"ApprovalOptionAllowExactRepo": "Always allow this exact rule in this repo",
"ApprovalOptionApproveAlways": "Allow for this conversation",
"ApprovalOptionAllowExactRepo": "Always allow in this repo",
"ApprovalSaveAskRuleHint": " s allow once + always ask exact rule",
"ApprovalOptionDeny": "Deny this call",
"ApprovalOptionAbortTurn": "Abort the turn",
"ApprovalOptionDeny": "Don't allow",
"ApprovalOptionAbortTurn": "Stop this turn",
"ApprovalBlockTitle": "approval",
"ApprovalControlsHint": " · Pg↑/↓ review · {details} details · Esc abort",
"ApprovalControlsHint": " · Pg↑/↓ review · {details} details · Esc stop",
"ApprovalTruncationHint": " … truncated · press {details} for full details",
"ApprovalFullAccessPolicyBlocked": "Blocked {tool}: Full Access cannot bypass this policy",
"AutoReviewQuestionSkipped": "Auto-Review skipped a user question and continued autonomously",
@@ -1237,17 +1242,17 @@
"ApprovalDescFileWrite": "Requesting to modify a file. Please confirm path and content.",
"ApprovalDescShell": "Requesting to execute a shell command. Review command and working directory.",
"ApprovalDescNetwork": "Requesting to access network or remote content. Verify the target is trusted.",
"ApprovalDescMcpRead": "Requesting to read from an MCP server.",
"ApprovalDescMcpAction": "Requesting to call an MCP server action that may have side effects.",
"ApprovalDescAgent": "Requesting to start or inspect a sub-agent; sub-agents still have their own gating.",
"ApprovalDescMcpRead": "Requesting to read from a connected app.",
"ApprovalDescMcpAction": "Requesting to use a connected app action that may have side effects.",
"ApprovalDescAgent": "Requesting to start or check on an agent; agents still ask for their own approvals.",
"ApprovalDescUnknown": "Requesting to run an unclassified tool. Review parameters carefully.",
"ApprovalImpactSafe": "Read-only operation.",
"ApprovalImpactFileWrite": "Writes files in the workspace or an approved write scope.",
"ApprovalImpactShell": "Executes a Bash command in your workspace.",
"ApprovalImpactShell": "Runs a shell command in your workspace.",
"ApprovalImpactNetwork": "May reach network services or remote content.",
"ApprovalImpactMcpRead": "Reads from an MCP server without an obvious local write.",
"ApprovalImpactMcpAction": "Calls an MCP server action that may have side effects.",
"ApprovalImpactAgent": "Starts or inspects a sub-agent; sub-agents have their own gating.",
"ApprovalImpactMcpRead": "Reads from a connected app without an obvious local write.",
"ApprovalImpactMcpAction": "Uses a connected app action that may have side effects.",
"ApprovalImpactAgent": "Starts or checks on an agent; the agent still asks for its own approvals.",
"ApprovalImpactUnknown": "Tool is not classified. Review params carefully before approving.",
"ApprovalLabelCommand": "Command",
"ApprovalLabelDir": "Dir",
@@ -1373,13 +1378,14 @@
"FooterHintOutput": "output",
"FooterHintContext": "context",
"InfoLineHelp": "help",
"InfoLineContext": "ctx",
"InfoLineContext": "context",
"InfoLineTtft": "ttft",
"InfoLinePeak": "peak",
"InfoLineOffPeak": "off-peak",
"InfoLineWhales": "whales",
"InfoLineAutomation": "automation",
"InfoLineNotConnected": "not connected",
"InfoLineThinking": "thinking: {level}",
"EmptyStateNoGit": "no git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "What do you want to accomplish?",
+20 -14
View File
@@ -1088,30 +1088,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERTAR --",
"VimModeVisual": "-- VISUAL --",
"ApprovalRiskReview": "REVISAR",
"ApprovalRiskElevated": "APROBACIÓN",
"ApprovalRiskDestructive": "DESTRUCTIVO",
"ApprovalEffectReadsOnly": "Solo lee",
"ApprovalEffectChangesFiles": "Cambia archivos",
"ApprovalRiskDestructive": "No se puede deshacer",
"ApprovalEffectRunsCommand": "Ejecuta un comando",
"ApprovalEffectUsesNetwork": "Usa la red",
"ApprovalEffectConnectedApp": "Usa una app conectada",
"ApprovalEffectStartsAgent": "Inicia un agente",
"ApprovalEffectUnclassified": "Herramienta sin clasificar",
"ApprovalTimedOutDenied": "La solicitud de aprobación agotó el tiempo de espera - rechazada",
"ApprovalCategorySafe": "Seguro",
"ApprovalCategoryFileWrite": "Escritura de Archivo",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Comando",
"ApprovalCategoryNetwork": "Red",
"ApprovalCategoryMcpRead": "Lectura MCP",
"ApprovalCategoryMcpAction": "Acción MCP",
"ApprovalCategoryAgent": "Subagente",
"ApprovalCategoryMcpRead": "App conectada",
"ApprovalCategoryMcpAction": "App conectada",
"ApprovalCategoryAgent": "Agente",
"ApprovalCategoryUnknown": "Desconocido",
"ApprovalFieldType": "Tipo:",
"ApprovalFieldAbout": "Acerca de:",
"ApprovalFieldImpact": "Impacto:",
"ApprovalFieldParams": "Parámetros:",
"ApprovalOptionApproveOnce": "Permitir una vez",
"ApprovalOptionApproveAlways": "Permitir durante esta sesión (este tipo)",
"ApprovalOptionAllowExactRepo": "Permitir siempre esta regla exacta en este repositorio",
"ApprovalOptionApproveAlways": "Permitir en esta conversación",
"ApprovalOptionAllowExactRepo": "Permitir siempre en este repositorio",
"ApprovalSaveAskRuleHint": " s permitir una vez + preguntar siempre por la regla exacta",
"ApprovalOptionDeny": "Denegar esta llamada",
"ApprovalOptionAbortTurn": "Abortar turno",
"ApprovalOptionDeny": "No permitir",
"ApprovalOptionAbortTurn": "Detener este turno",
"ApprovalBlockTitle": "aprobación",
"ApprovalControlsHint": " · Pg↑/↓ revisar · {details} detalles · Esc abortar",
"ApprovalControlsHint": " · Pg↑/↓ revisar · {details} detalles · Esc detener",
"ApprovalTruncationHint": " … truncado · presiona {details} para ver todos los detalles",
"ApprovalFullAccessPolicyBlocked": "Bloqueado {tool}: Full Access no puede omitir esta política",
"AutoReviewQuestionSkipped": "Auto-Review omitió una pregunta y continuó de forma autónoma",
@@ -1241,7 +1246,7 @@
"ApprovalDescUnknown": "Solicitando ejecutar una herramienta no clasificada. Revise los parámetros cuidadosamente.",
"ApprovalImpactSafe": "Operación de solo lectura.",
"ApprovalImpactFileWrite": "Escribe archivos en el workspace o ámbito de escritura aprobado.",
"ApprovalImpactShell": "Ejecuta un comando Bash en su workspace.",
"ApprovalImpactShell": "Ejecuta un comando shell en su workspace.",
"ApprovalImpactNetwork": "Puede acceder a servicios de red o contenido remoto.",
"ApprovalImpactMcpRead": "Lee de un servidor MCP sin escritura local obvia.",
"ApprovalImpactMcpAction": "Llama a una acción MCP que puede tener efectos secundarios.",
@@ -1373,13 +1378,14 @@
"FooterHintOutput": "salida",
"FooterHintContext": "contexto",
"InfoLineHelp": "ayuda",
"InfoLineContext": "ctx",
"InfoLineContext": "contexto",
"InfoLineTtft": "ttft",
"InfoLinePeak": "hora pico",
"InfoLineOffPeak": "fuera de pico",
"InfoLineWhales": "ballenas",
"InfoLineAutomation": "automatización",
"InfoLineNotConnected": "no conectado",
"InfoLineThinking": "razonamiento: {level}",
"EmptyStateNoGit": "sin git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "¿Qué quieres lograr?",
+20 -14
View File
@@ -1067,30 +1067,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERTION --",
"VimModeVisual": "-- VISUEL --",
"ApprovalRiskReview": "RÉVISION",
"ApprovalRiskElevated": "APPROBATION",
"ApprovalRiskDestructive": "DESTRUCTIF",
"ApprovalEffectReadsOnly": "Lecture seule",
"ApprovalEffectChangesFiles": "Modifie des fichiers",
"ApprovalRiskDestructive": "Irréversible",
"ApprovalEffectRunsCommand": "Exécute une commande",
"ApprovalEffectUsesNetwork": "Utilise le réseau",
"ApprovalEffectConnectedApp": "Utilise une app connectée",
"ApprovalEffectStartsAgent": "Lance un agent",
"ApprovalEffectUnclassified": "Outil non classé",
"ApprovalTimedOutDenied": "La demande d'approbation a expiré - refusée",
"ApprovalCategorySafe": "Sûr",
"ApprovalCategoryFileWrite": "Écriture de fichier",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Commande",
"ApprovalCategoryNetwork": "Réseau",
"ApprovalCategoryMcpRead": "Lecture MCP",
"ApprovalCategoryMcpAction": "Action MCP",
"ApprovalCategoryAgent": "Sous-agent",
"ApprovalCategoryMcpRead": "App connectée",
"ApprovalCategoryMcpAction": "App connectée",
"ApprovalCategoryAgent": "Agent",
"ApprovalCategoryUnknown": "Inconnu",
"ApprovalFieldType": "Type : ",
"ApprovalFieldAbout": "Sujet : ",
"ApprovalFieldImpact": "Impact : ",
"ApprovalFieldParams": "Paramètres : ",
"ApprovalOptionApproveOnce": "Autoriser une fois",
"ApprovalOptionApproveAlways": "Autoriser pour cette session (ce type)",
"ApprovalOptionAllowExactRepo": "Toujours autoriser cette règle exacte dans ce dépôt",
"ApprovalOptionApproveAlways": "Autoriser dans cette conversation",
"ApprovalOptionAllowExactRepo": "Toujours autoriser dans ce dépôt",
"ApprovalSaveAskRuleHint": " s autoriser une fois + toujours redemander la règle exacte",
"ApprovalOptionDeny": "Refuser cet appel",
"ApprovalOptionAbortTurn": "Abandonner le tour",
"ApprovalOptionDeny": "Ne pas autoriser",
"ApprovalOptionAbortTurn": "Arrêter ce tour",
"ApprovalBlockTitle": "approbation",
"ApprovalControlsHint": " · Pg↑/↓ réviser · {details} détails · Esc abandonner",
"ApprovalControlsHint": " · Pg↑/↓ réviser · {details} détails · Esc arrêter",
"ApprovalTruncationHint": " … tronqué · appuyez sur {details} pour tous les détails",
"ApprovalFullAccessPolicyBlocked": "{tool} bloqué : Full Access ne peut pas contourner cette politique",
"AutoReviewQuestionSkipped": "Auto-Review a ignoré une question de l'utilisateur et a continué de façon autonome",
@@ -1220,7 +1225,7 @@
"ApprovalDescUnknown": "Demande l'exécution d'un outil non classé. Vérifiez attentivement les paramètres.",
"ApprovalImpactSafe": "Opération en lecture seule.",
"ApprovalImpactFileWrite": "Écrit des fichiers dans le workspace ou dans une portée d'écriture approuvée.",
"ApprovalImpactShell": "Exécute une commande Bash dans votre workspace.",
"ApprovalImpactShell": "Exécute une commande shell dans votre workspace.",
"ApprovalImpactNetwork": "Peut atteindre des services réseau ou du contenu distant.",
"ApprovalImpactMcpRead": "Lit depuis un serveur MCP sans écriture locale évidente.",
"ApprovalImpactMcpAction": "Appelle une action de serveur MCP pouvant avoir des effets de bord.",
@@ -1350,13 +1355,14 @@
"FooterHintOutput": "sortie",
"FooterHintContext": "contexte",
"InfoLineHelp": "aide",
"InfoLineContext": "ctx",
"InfoLineContext": "contexte",
"InfoLineTtft": "ttft",
"InfoLinePeak": "heures pleines",
"InfoLineOffPeak": "heures creuses",
"InfoLineWhales": "baleines",
"InfoLineAutomation": "automatisation",
"InfoLineNotConnected": "non connecté",
"InfoLineThinking": "réflexion : {level}",
"EmptyStateNoGit": "pas de git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "Que voulez-vous accomplir ?",
+19 -13
View File
@@ -1067,28 +1067,33 @@
"VimModeNormal": "-- सामान्य --",
"VimModeInsert": "-- डालना --",
"VimModeVisual": "-- विज़ुअल --",
"ApprovalRiskReview": "समीक्षा",
"ApprovalRiskElevated": "अनुमति",
"ApprovalRiskDestructive": "विनाशकारी",
"ApprovalEffectReadsOnly": "केवल पढ़ता है",
"ApprovalEffectChangesFiles": "फ़ाइलें बदलता है",
"ApprovalRiskDestructive": "पूर्ववत नहीं हो सकता",
"ApprovalEffectRunsCommand": "कमांड चलाता है",
"ApprovalEffectUsesNetwork": "नेटवर्क का उपयोग करता है",
"ApprovalEffectConnectedApp": "कनेक्टेड ऐप का उपयोग करता है",
"ApprovalEffectStartsAgent": "एजेंट शुरू करता है",
"ApprovalEffectUnclassified": "अवर्गीकृत टूल",
"ApprovalTimedOutDenied": "अनुमोदन अनुरोध का समय समाप्त - अस्वीकृत",
"ApprovalCategorySafe": "सुरक्षित",
"ApprovalCategoryFileWrite": "फ़ाइल लेखन",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "कमांड",
"ApprovalCategoryNetwork": "नेटवर्क",
"ApprovalCategoryMcpRead": "MCP पठन",
"ApprovalCategoryMcpAction": "MCP एक्शन",
"ApprovalCategoryAgent": "सब-एजेंट",
"ApprovalCategoryMcpRead": "कनेक्टेड ऐप",
"ApprovalCategoryMcpAction": "कनेक्टेड ऐप",
"ApprovalCategoryAgent": "एजेंट",
"ApprovalCategoryUnknown": "अज्ञात",
"ApprovalFieldType": "प्रकार: ",
"ApprovalFieldAbout": "विषय: ",
"ApprovalFieldImpact": "प्रभाव: ",
"ApprovalFieldParams": "पैराम्स: ",
"ApprovalOptionApproveOnce": "एक बार अनुमति दें",
"ApprovalOptionApproveAlways": "इस सत्र के लिए अनुमति दें (इस प्रकार)",
"ApprovalOptionAllowExactRepo": "इस रेपो में यह सटीक नियम हमेशा अनुमति दें",
"ApprovalOptionApproveAlways": "इस बातचीत के लिए अनुमति दें",
"ApprovalOptionAllowExactRepo": "इस रेपो में हमेशा अनुमति दें",
"ApprovalSaveAskRuleHint": " s एक बार अनुमति + सटीक नियम हमेशा पूछें",
"ApprovalOptionDeny": "यह कॉल अस्वीकार करें",
"ApprovalOptionAbortTurn": "टर्न रोकें",
"ApprovalOptionDeny": "अनुमति न दें",
"ApprovalOptionAbortTurn": "यह टर्न रोकें",
"ApprovalBlockTitle": "अनुमति",
"ApprovalControlsHint": " · Pg↑/↓ समीक्षा · {details} विवरण · Esc रोकें",
"ApprovalTruncationHint": " … छाँटा गया · पूरा विवरण के लिए {details} दबाएँ",
@@ -1220,7 +1225,7 @@
"ApprovalDescUnknown": "अवर्गीकृत टूल चलाने का अनुरोध। पैरामीटर ध्यान से जाँचें।",
"ApprovalImpactSafe": "रीड-ओनली ऑपरेशन।",
"ApprovalImpactFileWrite": "वर्कस्पेस या स्वीकृत लेखन दायरे में फ़ाइलें लिखता है।",
"ApprovalImpactShell": "आपके वर्कस्पेस में Bash कमांड चलाता है।",
"ApprovalImpactShell": "आपके वर्कस्पेस में shell कमांड चलाता है।",
"ApprovalImpactNetwork": "नेटवर्क सेवाओं या रिमोट सामग्री तक पहुँच सकता है।",
"ApprovalImpactMcpRead": "बिना स्पष्ट लोकल लेखन के MCP सर्वर से पढ़ता है।",
"ApprovalImpactMcpAction": "MCP सर्वर एक्शन कॉल करता है, जिसके दुष्प्रभाव हो सकते हैं।",
@@ -1350,13 +1355,14 @@
"FooterHintOutput": "आउटपुट",
"FooterHintContext": "कॉन्टेक्स्ट",
"InfoLineHelp": "मदद",
"InfoLineContext": "ctx",
"InfoLineContext": "संदर्भ",
"InfoLineTtft": "ttft",
"InfoLinePeak": "पीक",
"InfoLineOffPeak": "ऑफ-पीक",
"InfoLineWhales": "व्हेल",
"InfoLineAutomation": "स्वचालन",
"InfoLineNotConnected": "कनेक्ट नहीं है",
"InfoLineThinking": "सोच: {level}",
"EmptyStateNoGit": "git नहीं",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "आप क्या हासिल करना चाहते हैं?",
+20 -14
View File
@@ -1067,30 +1067,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- SISIP --",
"VimModeVisual": "-- VISUAL --",
"ApprovalRiskReview": "TINJAU",
"ApprovalRiskElevated": "PERSETUJUAN",
"ApprovalRiskDestructive": "DESTRUKTIF",
"ApprovalEffectReadsOnly": "Hanya membaca",
"ApprovalEffectChangesFiles": "Mengubah file",
"ApprovalRiskDestructive": "Tidak dapat dibatalkan",
"ApprovalEffectRunsCommand": "Menjalankan perintah",
"ApprovalEffectUsesNetwork": "Menggunakan jaringan",
"ApprovalEffectConnectedApp": "Menggunakan aplikasi terhubung",
"ApprovalEffectStartsAgent": "Memulai agen",
"ApprovalEffectUnclassified": "Alat tak terklasifikasi",
"ApprovalTimedOutDenied": "Permintaan persetujuan habis waktu - ditolak",
"ApprovalCategorySafe": "Aman",
"ApprovalCategoryFileWrite": "Tulis File",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Perintah",
"ApprovalCategoryNetwork": "Jaringan",
"ApprovalCategoryMcpRead": "Baca MCP",
"ApprovalCategoryMcpAction": "Aksi MCP",
"ApprovalCategoryAgent": "Sub-agent",
"ApprovalCategoryMcpRead": "Aplikasi terhubung",
"ApprovalCategoryMcpAction": "Aplikasi terhubung",
"ApprovalCategoryAgent": "Agen",
"ApprovalCategoryUnknown": "Tidak diketahui",
"ApprovalFieldType": "Jenis: ",
"ApprovalFieldAbout": "Tentang: ",
"ApprovalFieldImpact": "Dampak: ",
"ApprovalFieldParams": "Param: ",
"ApprovalOptionApproveOnce": "Izinkan sekali",
"ApprovalOptionApproveAlways": "Izinkan untuk sesi ini (jenis ini)",
"ApprovalOptionAllowExactRepo": "Selalu izinkan aturan persis ini di repo ini",
"ApprovalOptionApproveAlways": "Izinkan untuk percakapan ini",
"ApprovalOptionAllowExactRepo": "Selalu izinkan di repo ini",
"ApprovalSaveAskRuleHint": " s izinkan sekali + selalu tanya aturan persis",
"ApprovalOptionDeny": "Tolak panggilan ini",
"ApprovalOptionAbortTurn": "Batalkan giliran",
"ApprovalOptionDeny": "Jangan izinkan",
"ApprovalOptionAbortTurn": "Hentikan giliran ini",
"ApprovalBlockTitle": "persetujuan",
"ApprovalControlsHint": " · Pg↑/↓ tinjau · {details} detail · Esc batal",
"ApprovalControlsHint": " · Pg↑/↓ tinjau · {details} detail · Esc hentikan",
"ApprovalTruncationHint": " … terpotong · tekan {details} untuk detail lengkap",
"ApprovalFullAccessPolicyBlocked": "Diblokir {tool}: Full Access tidak dapat melewati kebijakan ini",
"AutoReviewQuestionSkipped": "Auto-Review melewati pertanyaan pengguna dan lanjut secara mandiri",
@@ -1220,7 +1225,7 @@
"ApprovalDescUnknown": "Meminta untuk menjalankan tool tak terklasifikasi. Tinjau parameter dengan cermat.",
"ApprovalImpactSafe": "Operasi baca-saja.",
"ApprovalImpactFileWrite": "Menulis file di workspace atau cakupan tulis yang disetujui.",
"ApprovalImpactShell": "Mengeksekusi perintah Bash di workspace Anda.",
"ApprovalImpactShell": "Mengeksekusi perintah shell di workspace Anda.",
"ApprovalImpactNetwork": "Dapat menjangkau layanan jaringan atau konten remote.",
"ApprovalImpactMcpRead": "Membaca dari server MCP tanpa penulisan lokal yang jelas.",
"ApprovalImpactMcpAction": "Memanggil aksi server MCP yang mungkin memiliki efek samping.",
@@ -1350,13 +1355,14 @@
"FooterHintOutput": "output",
"FooterHintContext": "konteks",
"InfoLineHelp": "bantuan",
"InfoLineContext": "ctx",
"InfoLineContext": "konteks",
"InfoLineTtft": "ttft",
"InfoLinePeak": "jam puncak",
"InfoLineOffPeak": "non-puncak",
"InfoLineWhales": "paus",
"InfoLineAutomation": "otomatisasi",
"InfoLineNotConnected": "tidak terhubung",
"InfoLineThinking": "berpikir: {level}",
"EmptyStateNoGit": "tanpa git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "Apa yang ingin Anda capai?",
+20 -14
View File
@@ -1088,30 +1088,35 @@
"VimModeNormal": "-- ノーマル --",
"VimModeInsert": "-- 挿入 --",
"VimModeVisual": "-- ビジュアル --",
"ApprovalRiskReview": "確認",
"ApprovalRiskElevated": "承認",
"ApprovalRiskDestructive": "破壊的操作",
"ApprovalEffectReadsOnly": "読み取りのみ",
"ApprovalEffectChangesFiles": "ファイルを変更",
"ApprovalRiskDestructive": "元に戻せません",
"ApprovalEffectRunsCommand": "コマンドを実行",
"ApprovalEffectUsesNetwork": "ネットワークを使用",
"ApprovalEffectConnectedApp": "接続済みアプリを使用",
"ApprovalEffectStartsAgent": "エージェントを開始",
"ApprovalEffectUnclassified": "未分類のツール",
"ApprovalTimedOutDenied": "承認リクエストがタイムアウトしました - 拒否しました",
"ApprovalCategorySafe": "安全",
"ApprovalCategoryFileWrite": "ファイル書き込み",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "コマンド",
"ApprovalCategoryNetwork": "ネットワーク",
"ApprovalCategoryMcpRead": "MCP読み取り",
"ApprovalCategoryMcpAction": "MCPアクション",
"ApprovalCategoryAgent": "サブエージェント",
"ApprovalCategoryMcpRead": "接続済みアプリ",
"ApprovalCategoryMcpAction": "接続済みアプリ",
"ApprovalCategoryAgent": "エージェント",
"ApprovalCategoryUnknown": "未分類",
"ApprovalFieldType": "種類:",
"ApprovalFieldAbout": "詳細:",
"ApprovalFieldImpact": "影響:",
"ApprovalFieldParams": "パラメータ:",
"ApprovalOptionApproveOnce": "今回のみ許可",
"ApprovalOptionApproveAlways": "このセッションで許可(同種)",
"ApprovalOptionAllowExactRepo": "このリポジトリでこの完全一致ルールを常に許可",
"ApprovalOptionApproveAlways": "この会話で許可",
"ApprovalOptionAllowExactRepo": "このリポジトリで常に許可",
"ApprovalSaveAskRuleHint": " s 今回のみ許可 + 完全一致ルールを常に確認",
"ApprovalOptionDeny": "拒否",
"ApprovalOptionAbortTurn": "中断",
"ApprovalOptionDeny": "許可しない",
"ApprovalOptionAbortTurn": "このターンを停止",
"ApprovalBlockTitle": "承認",
"ApprovalControlsHint": " · Pg↑/↓ 履歴 · {details} 詳細 · Esc 中止",
"ApprovalControlsHint": " · Pg↑/↓ 履歴 · {details} 詳細 · Esc 停止",
"ApprovalTruncationHint": " … 省略 · {details} で詳細を表示",
"ApprovalFullAccessPolicyBlocked": "{tool} をブロック: Full Access ではこのポリシーを回避できません",
"AutoReviewQuestionSkipped": "Auto-Review は質問をスキップし、自律的に続行しました",
@@ -1241,7 +1246,7 @@
"ApprovalDescUnknown": "未分類のツールの実行をリクエストしています。パラメータを慎重に確認してください。",
"ApprovalImpactSafe": "読み取り専用操作。",
"ApprovalImpactFileWrite": "ワークスペースまたは承認された書き込み範囲内のファイルに書き込みます。",
"ApprovalImpactShell": "ワークスペースで Bash コマンドを実行します。",
"ApprovalImpactShell": "ワークスペースで shell コマンドを実行します。",
"ApprovalImpactNetwork": "ネットワークサービスまたはリモートコンテンツにアクセスする可能性があります。",
"ApprovalImpactMcpRead": "MCP サーバーから読み取り、ローカル書き込みはありません。",
"ApprovalImpactMcpAction": "副作用の可能性がある MCP サーバーアクションを呼び出します。",
@@ -1373,13 +1378,14 @@
"FooterHintOutput": "出力",
"FooterHintContext": "コンテキスト",
"InfoLineHelp": "ヘルプ",
"InfoLineContext": "ctx",
"InfoLineContext": "コンテキスト",
"InfoLineTtft": "ttft",
"InfoLinePeak": "ピーク",
"InfoLineOffPeak": "オフピーク",
"InfoLineWhales": "クジラ",
"InfoLineAutomation": "自動化",
"InfoLineNotConnected": "未接続",
"InfoLineThinking": "思考: {level}",
"EmptyStateNoGit": "git なし",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "何を達成したいですか?",
+20 -14
View File
@@ -1090,30 +1090,35 @@
"VimModeNormal": "-- 일반 --",
"VimModeInsert": "-- 입력 --",
"VimModeVisual": "-- 비주얼 --",
"ApprovalRiskReview": "검토",
"ApprovalRiskElevated": "승인",
"ApprovalRiskDestructive": "파괴적",
"ApprovalEffectReadsOnly": "읽기만",
"ApprovalEffectChangesFiles": "파일 변경",
"ApprovalRiskDestructive": "되돌릴 수 없음",
"ApprovalEffectRunsCommand": "명령 실행",
"ApprovalEffectUsesNetwork": "네트워크 사용",
"ApprovalEffectConnectedApp": "연결된 앱 사용",
"ApprovalEffectStartsAgent": "에이전트 시작",
"ApprovalEffectUnclassified": "분류되지 않은 도구",
"ApprovalTimedOutDenied": "승인 요청 시간 초과 - 거부됨",
"ApprovalCategorySafe": "안전",
"ApprovalCategoryFileWrite": "파일 쓰기",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "명령",
"ApprovalCategoryNetwork": "네트워크",
"ApprovalCategoryMcpRead": "MCP 읽기",
"ApprovalCategoryMcpAction": "MCP 동작",
"ApprovalCategoryAgent": "서브 에이전트",
"ApprovalCategoryMcpRead": "연결된 앱",
"ApprovalCategoryMcpAction": "연결된 앱",
"ApprovalCategoryAgent": "에이전트",
"ApprovalCategoryUnknown": "알 수 없음",
"ApprovalFieldType": "종류: ",
"ApprovalFieldAbout": "설명: ",
"ApprovalFieldImpact": "영향: ",
"ApprovalFieldParams": "매개변수: ",
"ApprovalOptionApproveOnce": "한 번 허용",
"ApprovalOptionApproveAlways": "이 세션에서 허용 (이 종류)",
"ApprovalOptionAllowExactRepo": "이 저장소에서 이 정확한 규칙을 항상 허용",
"ApprovalOptionApproveAlways": "이 대화에서 허용",
"ApprovalOptionAllowExactRepo": "이 저장소에서 항상 허용",
"ApprovalSaveAskRuleHint": " s 한 번 허용 + 정확한 규칙은 항상 묻기",
"ApprovalOptionDeny": "이 호출 거부",
"ApprovalOptionAbortTurn": "턴 중단",
"ApprovalOptionDeny": "허용 안 함",
"ApprovalOptionAbortTurn": "이번 턴 중지",
"ApprovalBlockTitle": "승인",
"ApprovalControlsHint": " · Pg↑/↓ 기록 · {details} 상세 · Esc 중단",
"ApprovalControlsHint": " · Pg↑/↓ 기록 · {details} 상세 · Esc 중지",
"ApprovalTruncationHint": " … 잘림 · 전체 상세는 {details}",
"ApprovalFullAccessPolicyBlocked": "{tool} 차단됨: Full Access는 이 정책을 우회할 수 없습니다",
"AutoReviewQuestionSkipped": "Auto-Review가 사용자 질문을 건너뛰고 자율적으로 계속했습니다",
@@ -1243,7 +1248,7 @@
"ApprovalDescUnknown": "분류되지 않은 도구 실행을 요청하고 있습니다. 매개변수를 신중히 검토하세요.",
"ApprovalImpactSafe": "읽기 전용 작업입니다.",
"ApprovalImpactFileWrite": "작업 공간이나 승인된 쓰기 범위 내에 파일을 씁니다.",
"ApprovalImpactShell": "작업 공간에서 Bash 명령을 실행합니다.",
"ApprovalImpactShell": "작업 공간에서 shell 명령을 실행합니다.",
"ApprovalImpactNetwork": "네트워크 서비스나 원격 콘텐츠에 접근할 수 있습니다.",
"ApprovalImpactMcpRead": "명백한 로컬 쓰기 없이 MCP 서버에서 읽습니다.",
"ApprovalImpactMcpAction": "부작용이 있을 수 있는 MCP 서버 동작을 호출합니다.",
@@ -1373,13 +1378,14 @@
"FooterHintOutput": "출력",
"FooterHintContext": "컨텍스트",
"InfoLineHelp": "도움말",
"InfoLineContext": "ctx",
"InfoLineContext": "컨텍스트",
"InfoLineTtft": "ttft",
"InfoLinePeak": "피크",
"InfoLineOffPeak": "오프피크",
"InfoLineWhales": "고래",
"InfoLineAutomation": "자동화",
"InfoLineNotConnected": "연결 안 됨",
"InfoLineThinking": "사고: {level}",
"EmptyStateNoGit": "git 없음",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "무엇을 이루고 싶으신가요?",
+20 -14
View File
@@ -1088,30 +1088,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERIR --",
"VimModeVisual": "-- VISUAL --",
"ApprovalRiskReview": "REVISÃO",
"ApprovalRiskElevated": "APROVAÇÃO",
"ApprovalRiskDestructive": "DESTRUTIVO",
"ApprovalEffectReadsOnly": "Só leitura",
"ApprovalEffectChangesFiles": "Altera arquivos",
"ApprovalRiskDestructive": "Não pode ser desfeito",
"ApprovalEffectRunsCommand": "Executa um comando",
"ApprovalEffectUsesNetwork": "Usa a rede",
"ApprovalEffectConnectedApp": "Usa um app conectado",
"ApprovalEffectStartsAgent": "Inicia um agente",
"ApprovalEffectUnclassified": "Ferramenta não classificada",
"ApprovalTimedOutDenied": "A solicitação de aprovação expirou - negada",
"ApprovalCategorySafe": "Seguro",
"ApprovalCategoryFileWrite": "Escrita de Arquivo",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Comando",
"ApprovalCategoryNetwork": "Rede",
"ApprovalCategoryMcpRead": "Leitura MCP",
"ApprovalCategoryMcpAction": "Ação MCP",
"ApprovalCategoryAgent": "Subagente",
"ApprovalCategoryMcpRead": "App conectado",
"ApprovalCategoryMcpAction": "App conectado",
"ApprovalCategoryAgent": "Agente",
"ApprovalCategoryUnknown": "Desconhecido",
"ApprovalFieldType": "Tipo:",
"ApprovalFieldAbout": "Sobre:",
"ApprovalFieldImpact": "Impacto:",
"ApprovalFieldParams": "Parâmetros:",
"ApprovalOptionApproveOnce": "Permitir uma vez",
"ApprovalOptionApproveAlways": "Permitir nesta sessão (este tipo)",
"ApprovalOptionAllowExactRepo": "Sempre permitir esta regra exata neste repositório",
"ApprovalOptionApproveAlways": "Permitir nesta conversa",
"ApprovalOptionAllowExactRepo": "Sempre permitir neste repositório",
"ApprovalSaveAskRuleHint": " s permitir uma vez + sempre perguntar pela regra exata",
"ApprovalOptionDeny": "Negar esta chamada",
"ApprovalOptionAbortTurn": "Abortar turno",
"ApprovalOptionDeny": "Não permitir",
"ApprovalOptionAbortTurn": "Parar este turno",
"ApprovalBlockTitle": "aprovação",
"ApprovalControlsHint": " · Pg↑/↓ revisar · {details} detalhes · Esc abortar",
"ApprovalControlsHint": " · Pg↑/↓ revisar · {details} detalhes · Esc parar",
"ApprovalTruncationHint": " … truncado · pressione {details} para ver todos os detalhes",
"ApprovalFullAccessPolicyBlocked": "{tool} bloqueado: Full Access não pode ignorar esta política",
"AutoReviewQuestionSkipped": "Auto-Review ignorou uma pergunta e continuou de forma autônoma",
@@ -1241,7 +1246,7 @@
"ApprovalDescUnknown": "Solicitando execução de ferramenta não classificada. Revise os parâmetros cuidadosamente.",
"ApprovalImpactSafe": "Operação somente leitura.",
"ApprovalImpactFileWrite": "Escreve arquivos no workspace ou escopo de escrita aprovado.",
"ApprovalImpactShell": "Executa um comando Bash no seu workspace.",
"ApprovalImpactShell": "Executa um comando shell no seu workspace.",
"ApprovalImpactNetwork": "Pode acessar serviços de rede ou conteúdo remoto.",
"ApprovalImpactMcpRead": "Lê de um servidor MCP sem escrita local óbvia.",
"ApprovalImpactMcpAction": "Chama uma ação MCP que pode ter efeitos colaterais.",
@@ -1373,13 +1378,14 @@
"FooterHintOutput": "saída",
"FooterHintContext": "contexto",
"InfoLineHelp": "ajuda",
"InfoLineContext": "ctx",
"InfoLineContext": "contexto",
"InfoLineTtft": "ttft",
"InfoLinePeak": "pico",
"InfoLineOffPeak": "fora de pico",
"InfoLineWhales": "baleias",
"InfoLineAutomation": "automação",
"InfoLineNotConnected": "não conectado",
"InfoLineThinking": "raciocínio: {level}",
"EmptyStateNoGit": "sem git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "O que você quer realizar?",
+20 -14
View File
@@ -1067,30 +1067,35 @@
"VimModeNormal": "-- ОБЫЧНЫЙ --",
"VimModeInsert": "-- ВСТАВКА --",
"VimModeVisual": "-- ВЫДЕЛЕНИЕ --",
"ApprovalRiskReview": "ПРОВЕРКА",
"ApprovalRiskElevated": "ОДОБРЕНИЕ",
"ApprovalRiskDestructive": "РАЗРУШИТЕЛЬНО",
"ApprovalEffectReadsOnly": "Только чтение",
"ApprovalEffectChangesFiles": "Изменяет файлы",
"ApprovalRiskDestructive": "Нельзя отменить",
"ApprovalEffectRunsCommand": "Выполняет команду",
"ApprovalEffectUsesNetwork": "Использует сеть",
"ApprovalEffectConnectedApp": "Использует подключённое приложение",
"ApprovalEffectStartsAgent": "Запускает агента",
"ApprovalEffectUnclassified": "Неклассифицированный инструмент",
"ApprovalTimedOutDenied": "Запрос на одобрение истёк - отклонено",
"ApprovalCategorySafe": "Безопасно",
"ApprovalCategoryFileWrite": "Запись файла",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Команда",
"ApprovalCategoryNetwork": "Сеть",
"ApprovalCategoryMcpRead": "Чтение MCP",
"ApprovalCategoryMcpAction": "Действие MCP",
"ApprovalCategoryAgent": "Субагент",
"ApprovalCategoryMcpRead": "Подключённое приложение",
"ApprovalCategoryMcpAction": "Подключённое приложение",
"ApprovalCategoryAgent": "Агент",
"ApprovalCategoryUnknown": "Неизвестно",
"ApprovalFieldType": "Тип: ",
"ApprovalFieldAbout": "О чём: ",
"ApprovalFieldImpact": "Влияние: ",
"ApprovalFieldParams": "Параметры: ",
"ApprovalOptionApproveOnce": "Разрешить один раз",
"ApprovalOptionApproveAlways": "Разрешить в этой сессии (этот тип)",
"ApprovalOptionAllowExactRepo": "Всегда разрешать это правило в этом репозитории",
"ApprovalOptionApproveAlways": "Разрешить в этом разговоре",
"ApprovalOptionAllowExactRepo": "Всегда разрешать в этом репозитории",
"ApprovalSaveAskRuleHint": " s разрешить раз + всегда спрашивать это правило",
"ApprovalOptionDeny": "Отклонить этот вызов",
"ApprovalOptionAbortTurn": "Прервать ход",
"ApprovalOptionDeny": "Не разрешать",
"ApprovalOptionAbortTurn": "Остановить этот ход",
"ApprovalBlockTitle": "одобрение",
"ApprovalControlsHint": " · Pg↑/↓ просмотр · {details} детали · Esc отмена",
"ApprovalControlsHint": " · Pg↑/↓ просмотр · {details} детали · Esc стоп",
"ApprovalTruncationHint": " … обрезано · нажмите {details} для полных деталей",
"ApprovalFullAccessPolicyBlocked": "{tool} заблокирован: Full Access не может обойти эту политику",
"AutoReviewQuestionSkipped": "Auto-Review пропустил вопрос пользователю и продолжил автономно",
@@ -1220,7 +1225,7 @@
"ApprovalDescUnknown": "Запрашивается запуск неклассифицированного инструмента. Внимательно проверьте параметры.",
"ApprovalImpactSafe": "Операция только для чтения.",
"ApprovalImpactFileWrite": "Записывает файлы в рабочей области или в одобренной области записи.",
"ApprovalImpactShell": "Выполняет команду Bash в вашей рабочей области.",
"ApprovalImpactShell": "Выполняет команду shell в вашей рабочей области.",
"ApprovalImpactNetwork": "Может обращаться к сетевым службам или удалённому содержимому.",
"ApprovalImpactMcpRead": "Читает с сервера MCP без явной локальной записи.",
"ApprovalImpactMcpAction": "Вызывает действие сервера MCP, которое может иметь побочные эффекты.",
@@ -1350,13 +1355,14 @@
"FooterHintOutput": "вывод",
"FooterHintContext": "контекст",
"InfoLineHelp": "справка",
"InfoLineContext": "ctx",
"InfoLineContext": "контекст",
"InfoLineTtft": "ttft",
"InfoLinePeak": "пик",
"InfoLineOffPeak": "непиковое",
"InfoLineWhales": "киты",
"InfoLineAutomation": "автоматизация",
"InfoLineNotConnected": "не подключено",
"InfoLineThinking": "размышление: {level}",
"EmptyStateNoGit": "нет git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "Чего вы хотите достичь?",
+20 -14
View File
@@ -1067,30 +1067,35 @@
"VimModeNormal": "-- NORMAL --",
"VimModeInsert": "-- INSERT --",
"VimModeVisual": "-- VISUAL --",
"ApprovalRiskReview": "ПЕРЕГЛЯД",
"ApprovalRiskElevated": "СХВАЛЕННЯ",
"ApprovalRiskDestructive": "РУЙНІВНА",
"ApprovalEffectReadsOnly": "Лише читання",
"ApprovalEffectChangesFiles": "Змінює файли",
"ApprovalRiskDestructive": "Не можна скасувати",
"ApprovalEffectRunsCommand": "Виконує команду",
"ApprovalEffectUsesNetwork": "Використовує мережу",
"ApprovalEffectConnectedApp": "Використовує підключений застосунок",
"ApprovalEffectStartsAgent": "Запускає агента",
"ApprovalEffectUnclassified": "Некласифікований інструмент",
"ApprovalTimedOutDenied": "Запит на схвалення минув - відхилено",
"ApprovalCategorySafe": "Безпечна",
"ApprovalCategoryFileWrite": "Запис файлу",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Команда",
"ApprovalCategoryNetwork": "Мережа",
"ApprovalCategoryMcpRead": "Читання MCP",
"ApprovalCategoryMcpAction": "Дія MCP",
"ApprovalCategoryAgent": "Субагент",
"ApprovalCategoryMcpRead": "Підключений застосунок",
"ApprovalCategoryMcpAction": "Підключений застосунок",
"ApprovalCategoryAgent": "Агент",
"ApprovalCategoryUnknown": "Невідомо",
"ApprovalFieldType": "Тип: ",
"ApprovalFieldAbout": "Про що: ",
"ApprovalFieldImpact": "Наслідки: ",
"ApprovalFieldParams": "Параметри: ",
"ApprovalOptionApproveOnce": "Дозволити один раз",
"ApprovalOptionApproveAlways": "Дозволити для цієї сесії (цей тип)",
"ApprovalOptionAllowExactRepo": "Завжди дозволяти це точне правило в цьому репозиторії",
"ApprovalOptionApproveAlways": "Дозволити в цій розмові",
"ApprovalOptionAllowExactRepo": "Завжди дозволяти в цьому репозиторії",
"ApprovalSaveAskRuleHint": " s дозволити один раз + завжди питати за точним правилом",
"ApprovalOptionDeny": "Відхилити цей виклик",
"ApprovalOptionAbortTurn": "Перервати хід",
"ApprovalOptionDeny": "Не дозволяти",
"ApprovalOptionAbortTurn": "Зупинити цей хід",
"ApprovalBlockTitle": "схвалення",
"ApprovalControlsHint": " · Pg↑/↓ перегляд · {details} деталі · Esc перервати",
"ApprovalControlsHint": " · Pg↑/↓ перегляд · {details} деталі · Esc зупинити",
"ApprovalTruncationHint": " … обрізано · натисніть {details} для повних деталей",
"ApprovalFullAccessPolicyBlocked": "Заблоковано {tool}: Full Access не може обійти цю політику",
"AutoReviewQuestionSkipped": "Auto-Review пропустив запитання користувача й продовжив автономно",
@@ -1220,7 +1225,7 @@
"ApprovalDescUnknown": "Запит на запуск некласифікованого інструмента. Уважно перевірте параметри.",
"ApprovalImpactSafe": "Операція лише для читання.",
"ApprovalImpactFileWrite": "Записує файли в робочому просторі або схваленій області запису.",
"ApprovalImpactShell": "Виконує команду Bash у вашому робочому просторі.",
"ApprovalImpactShell": "Виконує команду shell у вашому робочому просторі.",
"ApprovalImpactNetwork": "Може звертатися до мережевих служб або віддаленого вмісту.",
"ApprovalImpactMcpRead": "Читає з сервера MCP без явного локального запису.",
"ApprovalImpactMcpAction": "Викликає дію сервера MCP, яка може мати побічні ефекти.",
@@ -1350,13 +1355,14 @@
"FooterHintOutput": "вивід",
"FooterHintContext": "контекст",
"InfoLineHelp": "довідка",
"InfoLineContext": "ctx",
"InfoLineContext": "контекст",
"InfoLineTtft": "ttft",
"InfoLinePeak": "пік",
"InfoLineOffPeak": "непіковий",
"InfoLineWhales": "кити",
"InfoLineAutomation": "автоматизація",
"InfoLineNotConnected": "не підключено",
"InfoLineThinking": "міркування: {level}",
"EmptyStateNoGit": "немає git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "Чого ви хочете досягти?",
+20 -14
View File
@@ -1088,30 +1088,35 @@
"VimModeNormal": "-- BÌNH THƯỜNG --",
"VimModeInsert": "-- CHÈN --",
"VimModeVisual": "-- TRỰC QUAN --",
"ApprovalRiskReview": "XEM XÉT",
"ApprovalRiskElevated": "PHÊ DUYỆT",
"ApprovalRiskDestructive": "NGUY HẠI",
"ApprovalEffectReadsOnly": "Chỉ đọc",
"ApprovalEffectChangesFiles": "Thay đổi tệp",
"ApprovalRiskDestructive": "Không thể hoàn tác",
"ApprovalEffectRunsCommand": "Chạy lệnh",
"ApprovalEffectUsesNetwork": "Dùng mạng",
"ApprovalEffectConnectedApp": "Dùng ứng dụng đã kết nối",
"ApprovalEffectStartsAgent": "Khởi chạy tác tử",
"ApprovalEffectUnclassified": "Công cụ chưa phân loại",
"ApprovalTimedOutDenied": "Yêu cầu phê duyệt quá hạn - bị từ chối",
"ApprovalCategorySafe": "An toàn",
"ApprovalCategoryFileWrite": "Ghi Tệp",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "Lệnh",
"ApprovalCategoryNetwork": "Mạng",
"ApprovalCategoryMcpRead": "Đọc MCP",
"ApprovalCategoryMcpAction": "Hành động MCP",
"ApprovalCategoryAgent": "Sub-agent",
"ApprovalCategoryMcpRead": "Ứng dụng đã kết nối",
"ApprovalCategoryMcpAction": "Ứng dụng đã kết nối",
"ApprovalCategoryAgent": "Tác tử",
"ApprovalCategoryUnknown": "Không xác định",
"ApprovalFieldType": "Loại:",
"ApprovalFieldAbout": "Mô tả:",
"ApprovalFieldImpact": "Tác động:",
"ApprovalFieldParams": "Tham số:",
"ApprovalOptionApproveOnce": "Cho phép một lần",
"ApprovalOptionApproveAlways": "Cho phép trong phiên này (loại này)",
"ApprovalOptionAllowExactRepo": "Luôn cho phép quy tắc chính xác này trong kho mã",
"ApprovalOptionApproveAlways": "Cho phép trong cuộc trò chuyện này",
"ApprovalOptionAllowExactRepo": "Luôn cho phép trong kho mã này",
"ApprovalSaveAskRuleHint": " s cho phép một lần + luôn hỏi với quy tắc chính xác",
"ApprovalOptionDeny": "Từ chối lần gọi này",
"ApprovalOptionAbortTurn": "Hủy bỏ lượt",
"ApprovalOptionDeny": "Không cho phép",
"ApprovalOptionAbortTurn": "Dừng lượt này",
"ApprovalBlockTitle": "phê duyệt",
"ApprovalControlsHint": " · Pg↑/↓ xem lại · {details} chi tiết · Esc hủy",
"ApprovalControlsHint": " · Pg↑/↓ xem lại · {details} chi tiết · Esc dừng",
"ApprovalTruncationHint": " … đã rút gọn · nhấn {details} để xem đầy đủ",
"ApprovalFullAccessPolicyBlocked": "Đã chặn {tool}: Full Access không thể bỏ qua chính sách này",
"AutoReviewQuestionSkipped": "Auto-Review đã bỏ qua một câu hỏi và tiếp tục tự động",
@@ -1241,7 +1246,7 @@
"ApprovalDescUnknown": "Yêu cầu chạy công cụ chưa phân loại. Hãy kiểm tra tham số cẩn thận.",
"ApprovalImpactSafe": "Thao tác chỉ đọc.",
"ApprovalImpactFileWrite": "Ghi tệp trong workspace hoặc phạm vi ghi đã được phê duyệt.",
"ApprovalImpactShell": "Thực thi lệnh Bash trong workspace của bạn.",
"ApprovalImpactShell": "Thực thi lệnh shell trong workspace của bạn.",
"ApprovalImpactNetwork": "Có thể truy cập dịch vụ mạng hoặc nội dung từ xa.",
"ApprovalImpactMcpRead": "Đọc từ máy chủ MCP mà không ghi cục bộ rõ ràng.",
"ApprovalImpactMcpAction": "Gọi hành động máy chủ MCP có thể có tác dụng phụ.",
@@ -1373,13 +1378,14 @@
"FooterHintOutput": "đầu ra",
"FooterHintContext": "ngữ cảnh",
"InfoLineHelp": "trợ giúp",
"InfoLineContext": "ctx",
"InfoLineContext": "ngữ cảnh",
"InfoLineTtft": "ttft",
"InfoLinePeak": "cao điểm",
"InfoLineOffPeak": "thấp điểm",
"InfoLineWhales": "cá voi",
"InfoLineAutomation": "tự động hóa",
"InfoLineNotConnected": "chưa kết nối",
"InfoLineThinking": "suy nghĩ: {level}",
"EmptyStateNoGit": "không có git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "Bạn muốn hoàn thành điều gì?",
+26 -20
View File
@@ -1088,30 +1088,35 @@
"VimModeNormal": "-- 普通 --",
"VimModeInsert": "-- 插入 --",
"VimModeVisual": "-- 可视 --",
"ApprovalRiskReview": "审查",
"ApprovalRiskElevated": "需要批准",
"ApprovalRiskDestructive": "破坏性",
"ApprovalEffectReadsOnly": "只读",
"ApprovalEffectChangesFiles": "修改文件",
"ApprovalRiskDestructive": "无法撤销",
"ApprovalEffectRunsCommand": "运行命令",
"ApprovalEffectUsesNetwork": "使用网络",
"ApprovalEffectConnectedApp": "使用已连接应用",
"ApprovalEffectStartsAgent": "启动代理",
"ApprovalEffectUnclassified": "未分类工具",
"ApprovalTimedOutDenied": "审批请求已超时 - 已拒绝",
"ApprovalCategorySafe": "安全",
"ApprovalCategoryFileWrite": "文件写入",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "命令",
"ApprovalCategoryNetwork": "网络",
"ApprovalCategoryMcpRead": "MCP 读取",
"ApprovalCategoryMcpAction": "MCP 操作",
"ApprovalCategoryAgent": "子代理",
"ApprovalCategoryMcpRead": "已连接应用",
"ApprovalCategoryMcpAction": "已连接应用",
"ApprovalCategoryAgent": "代理",
"ApprovalCategoryUnknown": "未知",
"ApprovalFieldType": "类型:",
"ApprovalFieldAbout": "说明:",
"ApprovalFieldImpact": "影响:",
"ApprovalFieldParams": "参数:",
"ApprovalOptionApproveOnce": "仅允许本次",
"ApprovalOptionApproveAlways": "本会话允许同类操作",
"ApprovalOptionAllowExactRepo": "在此仓库中始终允许这条精确规则",
"ApprovalOptionApproveAlways": "在此对话中允许",
"ApprovalOptionAllowExactRepo": "在此仓库中始终允许",
"ApprovalSaveAskRuleHint": " s 仅允许本次并始终询问精确规则",
"ApprovalOptionDeny": "拒绝本次调用",
"ApprovalOptionAbortTurn": "终止本轮",
"ApprovalOptionDeny": "不允许",
"ApprovalOptionAbortTurn": "停止本轮",
"ApprovalBlockTitle": "审批",
"ApprovalControlsHint": " · Pg↑/↓ 回看 · {details} 详情 · Esc 终止",
"ApprovalControlsHint": " · Pg↑/↓ 回看 · {details} 详情 · Esc 停止",
"ApprovalTruncationHint": " … 已截断 · 按 {details} 查看完整内容",
"ApprovalFullAccessPolicyBlocked": "已阻止 {tool}:Full Access 无法绕过此策略",
"AutoReviewQuestionSkipped": "Auto-Review 已跳过用户问题并自主继续",
@@ -1235,17 +1240,17 @@
"ApprovalDescFileWrite": "请求修改文件。请确认路径和内容符合预期。",
"ApprovalDescShell": "请求执行 shell 命令。请先检查命令和工作目录。",
"ApprovalDescNetwork": "请求访问网络或远程内容。请确认目标可信。",
"ApprovalDescMcpRead": "请求从 MCP 服务器读取信息。",
"ApprovalDescMcpAction": "请求调用 MCP 服务器操作,可能产生副作用。",
"ApprovalDescAgent": "请求启动或查看子代理任务;子代理仍受其自身工具门控约束。",
"ApprovalDescMcpRead": "请求从已连接应用读取信息。",
"ApprovalDescMcpAction": "请求调用已连接应用的操作,可能产生副作用。",
"ApprovalDescAgent": "请求启动或查看代理;代理仍会自行请求批准。",
"ApprovalDescUnknown": "请求运行未分类工具。批准前请仔细检查参数。",
"ApprovalImpactSafe": "只读操作。",
"ApprovalImpactFileWrite": "会写入工作区或已批准写入范围内的文件。",
"ApprovalImpactShell": "在工作区执行 Bash 命令。",
"ApprovalImpactShell": "在工作区运行 shell 命令。",
"ApprovalImpactNetwork": "可能访问网络服务或远程内容。",
"ApprovalImpactMcpRead": "从 MCP 服务器读取信息,不应产生本地写入。",
"ApprovalImpactMcpAction": "调用可能产生副作用的 MCP 服务器操作。",
"ApprovalImpactAgent": "启动或查看子代理任务;子代理仍受其自身工具门控约束。",
"ApprovalImpactMcpRead": "从已连接应用读取信息,不应产生本地写入。",
"ApprovalImpactMcpAction": "调用可能产生副作用的已连接应用操作。",
"ApprovalImpactAgent": "启动或查看代理;代理仍会自行请求批准。",
"ApprovalImpactUnknown": "工具未分类。批准前请仔细检查参数。",
"ApprovalLabelCommand": "命令",
"ApprovalLabelDir": "目录",
@@ -1373,13 +1378,14 @@
"FooterHintOutput": "输出",
"FooterHintContext": "上下文",
"InfoLineHelp": "帮助",
"InfoLineContext": "ctx",
"InfoLineContext": "上下文",
"InfoLineTtft": "ttft",
"InfoLinePeak": "高峰",
"InfoLineOffPeak": "错峰",
"InfoLineWhales": "鲸鱼",
"InfoLineAutomation": "自动化",
"InfoLineNotConnected": "未连接",
"InfoLineThinking": "思考:{level}",
"EmptyStateNoGit": "无 git",
"EmptyStateMcpLabel": "mcp",
"EmptyStatePrompt": "你想完成什么?",
+26 -20
View File
@@ -127,21 +127,21 @@
"AppModeYoloHint": "僅相容 — Act + 完全存取,不是可見模式",
"ApprovalAutoDeniedSession": "已自動拒絕 {tool}:本輪中已有相符請求被你拒絕。若要重新詢問,請傳送新訊息。",
"ApprovalBlockTitle": "審批",
"ApprovalCategoryAgent": "子代理",
"ApprovalCategoryAgent": "代理",
"ApprovalCategoryFileWrite": "檔案寫入",
"ApprovalCategoryMcpAction": "MCP 操作",
"ApprovalCategoryMcpRead": "MCP 讀取",
"ApprovalCategoryMcpAction": "已連接的應用程式",
"ApprovalCategoryMcpRead": "已連接的應用程式",
"ApprovalCategoryNetwork": "網路",
"ApprovalCategorySafe": "安全",
"ApprovalCategoryShell": "Bash",
"ApprovalCategoryShell": "命令",
"ApprovalCategoryUnknown": "未分類",
"ApprovalChooseAction": "Enter 執行選中項,或直接按 y/a/d",
"ApprovalChooseHint": "選擇:",
"ApprovalControlsHint": " · Pg↑/↓ 回看 · {details} 詳情 · Esc 終止",
"ApprovalDescAgent": "請求啟動或檢視子代理任務;子代理仍受其自身工具門控約束。",
"ApprovalControlsHint": " · Pg↑/↓ 回看 · {details} 詳情 · Esc 停止",
"ApprovalDescAgent": "請求啟動或檢視代理;代理仍會自行請求批准。",
"ApprovalDescFileWrite": "請求修改檔案。請確認路徑和內容符合預期。",
"ApprovalDescMcpAction": "請求呼叫 MCP 伺服器操作,可能產生副作用。",
"ApprovalDescMcpRead": "請求從 MCP 伺服器讀取資訊。",
"ApprovalDescMcpAction": "請求呼叫已連接應用程式的操作,可能產生副作用。",
"ApprovalDescMcpRead": "請求從已連接的應用程式讀取資訊。",
"ApprovalDescNetwork": "請求存取網路或遠端內容。請確認目標可信。",
"ApprovalDescSafe": "請求執行唯讀操作。",
"ApprovalDescShell": "請求執行 shell 命令。請先檢查命令和工作目錄。",
@@ -151,13 +151,13 @@
"ApprovalFieldParams": "參數:",
"ApprovalFieldType": "類型:",
"ApprovalFullAccessPolicyBlocked": "已阻止 {tool}:Full Access 無法繞過此政策",
"ApprovalImpactAgent": "啟動或檢視子代理任務;子代理仍受其自身工具門控約束。",
"ApprovalImpactAgent": "啟動或檢視代理;代理仍會自行請求批准。",
"ApprovalImpactFileWrite": "會寫入工作區或已批准寫入範圍內的檔案。",
"ApprovalImpactMcpAction": "呼叫可能產生副作用的 MCP 伺服器操作。",
"ApprovalImpactMcpRead": "從 MCP 伺服器讀取資訊,不應產生本地寫入。",
"ApprovalImpactMcpAction": "呼叫可能產生副作用的已連接應用程式操作。",
"ApprovalImpactMcpRead": "從已連接的應用程式讀取資訊,不應產生本地寫入。",
"ApprovalImpactNetwork": "可能存取網路服務或遠端內容。",
"ApprovalImpactSafe": "唯讀操作。",
"ApprovalImpactShell": "在工作區執行 Bash 命令。",
"ApprovalImpactShell": "在工作區執行 shell 命令。",
"ApprovalImpactUnknown": "工具未分類。批准前請仔細檢查參數。",
"ApprovalIntentLabel": "意圖:",
"ApprovalLabelAbout": "說明:",
@@ -177,19 +177,24 @@
"ApprovalLabelType": "類型",
"ApprovalLabelWithThis": "取代為",
"ApprovalMoreLines": " … (還有 {count} 行)",
"ApprovalOptionAbortTurn": "終止本輪",
"ApprovalOptionAllowExactRepo": "在此儲存庫中一律允許這條精確規則",
"ApprovalOptionApproveAlways": "在此工作階段允許(此類)",
"ApprovalOptionAbortTurn": "停止本輪",
"ApprovalOptionAllowExactRepo": "在此儲存庫中一律允許",
"ApprovalOptionApproveAlways": "在此對話中允許",
"ApprovalOptionApproveOnce": "僅允許一次",
"ApprovalOptionDeny": "拒絕本次調用",
"ApprovalOptionDeny": "不允許",
"ApprovalRepoLawBadge": "儲存庫規則",
"ApprovalRepoLawRuleLabel": "規則 ",
"ApprovalRepoLawTitle": "儲存庫規則",
"ApprovalRepoLawWarning": "儲存庫規則會在啟用審批的權限模式下要求確認。",
"ApprovalRiskDestructive": "破壞性",
"ApprovalRiskDestructive": "無法復原",
"ApprovalEffectRunsCommand": "執行命令",
"ApprovalEffectUsesNetwork": "使用網路",
"ApprovalEffectConnectedApp": "使用已連接的應用程式",
"ApprovalEffectStartsAgent": "啟動代理",
"ApprovalEffectUnclassified": "未分類工具",
"ApprovalTimedOutDenied": "審批請求逾時 - 已拒絕",
"ApprovalRiskElevated": "需要批准",
"ApprovalRiskReview": "審查",
"ApprovalEffectChangesFiles": "修改檔案",
"ApprovalEffectReadsOnly": "唯讀",
"ApprovalSaveAskRuleHint": " s 僅允許一次 + 一律詢問精確規則",
"ApprovalTruncationHint": " … 已截斷 · 按 {details} 查看完整內容",
"AutoReviewQuestionSkipped": "Auto-Review 已略過使用者問題並自主繼續",
@@ -943,13 +948,14 @@
"FooterBalancePrefix": "餘額",
"FooterHintContext": "上下文",
"InfoLineHelp": "幫助",
"InfoLineContext": "ctx",
"InfoLineContext": "上下文",
"InfoLineTtft": "ttft",
"InfoLinePeak": "尖峰",
"InfoLineOffPeak": "離峰",
"InfoLineWhales": "鯨魚",
"InfoLineAutomation": "自動化",
"InfoLineNotConnected": "未連線",
"InfoLineThinking": "思考:{level}",
"FooterHintKeys": "快捷鍵",
"FooterHintOutput": "輸出",
"FooterPressCtrlCAgain": "再次按 Ctrl+C 退出",
+16 -4
View File
@@ -1333,9 +1333,14 @@ pub enum MessageId {
VimModeVisual,
// Approval dialog — risk badges, category labels, field labels, options.
ApprovalRiskReview,
ApprovalRiskElevated,
ApprovalEffectReadsOnly,
ApprovalEffectChangesFiles,
ApprovalRiskDestructive,
ApprovalEffectRunsCommand,
ApprovalEffectUsesNetwork,
ApprovalEffectConnectedApp,
ApprovalEffectStartsAgent,
ApprovalEffectUnclassified,
ApprovalCategorySafe,
ApprovalCategoryFileWrite,
ApprovalCategoryShell,
@@ -1662,6 +1667,7 @@ pub enum MessageId {
InfoLineWhales,
InfoLineAutomation,
InfoLineNotConnected,
InfoLineThinking,
// Session metrics strip short labels (phase strip ledger and /status).
SessionMetricsTurn,
SessionMetricsTurns,
@@ -3657,9 +3663,14 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[
MessageId::VimModeNormal,
MessageId::VimModeInsert,
MessageId::VimModeVisual,
MessageId::ApprovalRiskReview,
MessageId::ApprovalRiskElevated,
MessageId::ApprovalEffectReadsOnly,
MessageId::ApprovalEffectChangesFiles,
MessageId::ApprovalRiskDestructive,
MessageId::ApprovalEffectRunsCommand,
MessageId::ApprovalEffectUsesNetwork,
MessageId::ApprovalEffectConnectedApp,
MessageId::ApprovalEffectStartsAgent,
MessageId::ApprovalEffectUnclassified,
MessageId::ApprovalCategorySafe,
MessageId::ApprovalCategoryFileWrite,
MessageId::ApprovalCategoryShell,
@@ -3954,6 +3965,7 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[
MessageId::InfoLineWhales,
MessageId::InfoLineAutomation,
MessageId::InfoLineNotConnected,
MessageId::InfoLineThinking,
MessageId::SessionMetricsTurn,
MessageId::SessionMetricsTurns,
MessageId::SessionMetricsStep,
+52 -11
View File
@@ -34,7 +34,6 @@ use codewhale_config::ToolAskRule;
use codewhale_localization::{Locale, MessageId, tr};
use serde_json::Value;
use std::path::Path;
#[cfg(test)]
use std::path::PathBuf;
#[cfg(test)]
@@ -97,6 +96,12 @@ pub struct ApprovalRequest {
pub tool_name: String,
/// Human-readable tool description from the engine
pub description: String,
/// One plain sentence naming what the call does ("Search the web for
/// 'espresso'", "Write notes/espresso.md"), built from the tool name and
/// its arguments only (E6). The card leads with it.
pub summary: String,
/// Workspace the call runs in; card paths are shown relative to it.
pub workspace: PathBuf,
/// Tool category
pub category: ToolCategory,
/// Stakes-based routing for the compact approval card
@@ -198,6 +203,12 @@ impl ApprovalRequest {
id: id.to_string(),
tool_name: tool_name.to_string(),
description: description.to_string(),
summary: crate::tools::approval_summary::approval_summary(
tool_name,
params,
Some(workspace),
),
workspace: workspace.to_path_buf(),
category,
risk,
impacts: build_impact_summary(semantic_tool_name, category, params),
@@ -227,7 +238,7 @@ impl ApprovalRequest {
match locale {
Locale::ZhHans => localized_description_zh_hans(self.category),
_ if self.category == ToolCategory::Shell => {
"Review the Bash command before it runs.".to_string()
"Review the command before it runs.".to_string()
}
_ => self.description.clone(),
}
@@ -287,6 +298,9 @@ impl ApprovalRequest {
build_prominent_details(semantic_tool_name, self.category, &self.params)
.into_iter()
.map(|mut detail| {
if matches!(detail.label.as_str(), "File" | "Path" | "Dir") {
detail.value = workspace_relative(&detail.value, &self.workspace);
}
let is_preview = detail.label == "Preview";
detail.label = localize_detail_label(&detail.label, locale).to_string();
if is_preview && let Some(lines) = detail.shell_lines.as_mut() {
@@ -302,6 +316,33 @@ impl ApprovalRequest {
}
}
/// Show `value` relative to `workspace` when it is an absolute path inside
/// it, so the card never spends a row on the workspace prefix.
fn workspace_relative(value: &str, workspace: &Path) -> String {
let path = Path::new(value);
if workspace.as_os_str().is_empty() || !path.is_absolute() {
return value.to_string();
}
match path.strip_prefix(workspace) {
Ok(relative) if relative.as_os_str().is_empty() => ".".to_string(),
Ok(relative) => relative.display().to_string(),
Err(_) => value.to_string(),
}
}
/// The connected-app server named by an `mcp_<server>_<tool>` tool name.
/// Presentation only: server names may themselves hold `_`, so this is never
/// a policy input.
#[must_use]
pub fn connected_app_server(tool_name: &str) -> Option<&str> {
let rest = tool_name.strip_prefix("mcp_")?;
match rest.split_once('_') {
Some((server, _)) if !server.is_empty() => Some(server),
_ if !rest.is_empty() => Some(rest),
_ => None,
}
}
fn description_is_repo_law_prompt(description: &str) -> bool {
description.starts_with("Repo law holds this write:")
&& description.contains(".codewhale/constitution.json")
@@ -366,7 +407,7 @@ fn build_impact_summary(tool_name: &str, category: ToolCategory, params: &Value)
impacts
}
ToolCategory::Shell => {
vec!["Executes a Bash command in your workspace.".to_string()]
vec!["Runs a shell command in your workspace.".to_string()]
}
ToolCategory::Network => {
let mut impacts = vec!["May reach network services or remote content.".to_string()];
@@ -379,17 +420,17 @@ fn build_impact_summary(tool_name: &str, category: ToolCategory, params: &Value)
}
ToolCategory::McpRead => {
let mut impacts =
vec!["Reads from an MCP server without an obvious local write.".to_string()];
vec!["Reads from a connected app without an obvious local write.".to_string()];
if let Some(target) = mcp_target_hint(tool_name) {
impacts.push(format!("MCP target: {target}"));
impacts.push(format!("Connected app: {target}"));
}
impacts
}
ToolCategory::McpAction => {
let mut impacts =
vec!["Calls an MCP server action that may have side effects.".to_string()];
vec!["Uses a connected app action that may have side effects.".to_string()];
if let Some(target) = mcp_target_hint(tool_name) {
impacts.push(format!("MCP target: {target}"));
impacts.push(format!("Connected app: {target}"));
}
impacts
}
@@ -400,11 +441,11 @@ fn build_impact_summary(tool_name: &str, category: ToolCategory, params: &Value)
}
ToolCategory::Agent => {
let mut impacts = vec![
"Starts or inspects a child agent task; the child's own tool gates still apply."
"Starts or checks on an agent; the agent still asks for its own approvals."
.to_string(),
];
if let Some(kind) = param_preview(params, &["type"], 40) {
impacts.push(format!("Child type: {kind}"));
impacts.push(format!("Agent type: {kind}"));
}
impacts
}
@@ -474,14 +515,14 @@ fn build_impact_summary_zh_hans(
ToolCategory::McpRead => {
let mut impacts = vec![tr(locale, MessageId::ApprovalImpactMcpRead).to_string()];
if let Some(target) = mcp_target_hint(tool_name) {
impacts.push(format!("MCP 目标:{target}"));
impacts.push(format!("已连接应用:{target}"));
}
impacts
}
ToolCategory::McpAction => {
let mut impacts = vec![tr(locale, MessageId::ApprovalImpactMcpAction).to_string()];
if let Some(target) = mcp_target_hint(tool_name) {
impacts.push(format!("MCP 目标:{target}"));
impacts.push(format!("已连接应用:{target}"));
}
impacts
}
+36 -25
View File
@@ -265,7 +265,7 @@ fn test_approval_request_derives_impact_summary() {
request
.impacts
.iter()
.any(|line| line.contains("Executes a Bash command"))
.any(|line| line.contains("Runs a shell command"))
);
assert!(
request
@@ -296,7 +296,7 @@ fn mcp_impact_summary_preserves_full_target_for_underscored_names() {
request
.impacts
.iter()
.any(|line| line == "MCP target: my_db_execute_sql")
.any(|line| line == "Connected app: my_db_execute_sql")
);
assert!(!request.impacts.iter().any(|line| line == "Server: my"));
@@ -304,7 +304,7 @@ fn mcp_impact_summary_preserves_full_target_for_underscored_names() {
assert!(
zh_impacts
.iter()
.any(|line| line == "MCP 目标:my_db_execute_sql")
.any(|line| line == "已连接应用:my_db_execute_sql")
);
assert!(!zh_impacts.iter().any(|line| line == "服务器:my"));
}
@@ -1800,8 +1800,8 @@ fn agent_tool_is_classified_and_renders_calm() {
let view = ApprovalView::new(request);
let lines = render_lines(&view, 100, 40);
let joined = lines.join("\n");
assert!(joined.contains("APPROVAL"), "{joined}");
assert!(!joined.contains("DESTRUCTIVE"), "{joined}");
assert!(joined.contains("Starts an agent"), "{joined}");
assert!(!joined.contains("Can't be undone"), "{joined}");
assert!(
!joined.contains("not classified"),
"agent must not render the unknown-tool warning:\n{joined}"
@@ -1832,7 +1832,12 @@ fn render_benign_includes_review_badge_and_selection_hint() {
let view = ApprovalView::new(benign_request());
let lines = render_lines(&view, 100, 40);
let joined = lines.join("\n");
assert!(joined.contains("REVIEW"), "missing REVIEW badge:\n{joined}");
assert!(
joined.contains("Reads only"),
"missing effect badge:\n{joined}"
);
// The card leads with the plain summary, workspace-relative (E6).
assert!(joined.contains("Read src/main.rs"), "{joined}");
assert_approval_key_badges_visible(&joined);
// The selection prose moved into the per-option key badges; the footer
// keeps only the escape-hatch hints.
@@ -1840,7 +1845,6 @@ fn render_benign_includes_review_badge_and_selection_hint() {
joined.contains("Pg↑/↓ review"),
"footer controls hint missing:\n{joined}"
);
assert!(joined.contains("read_file"));
}
#[test]
@@ -1877,16 +1881,19 @@ fn approval_footer_hints_use_muted_contrast_tier() {
#[test]
fn render_elevated_write_is_calm_and_compact() {
// Ordinary state-touching work (a file write) renders as a calm
// APPROVAL ask: no DESTRUCTIVE badge, no policy dossier, no
// impact/category taxonomy — that detail stays one details chord away.
// Ordinary state-touching work (a file write) renders as a calm ask
// that names its effect: no "Can't be undone" badge, no policy dossier,
// no impact/category taxonomy — that detail stays one details chord away.
let view = ApprovalView::new(destructive_request());
let lines = render_lines(&view, 100, 40);
let joined = lines.join("\n");
assert!(joined.contains("APPROVAL"), "missing calm badge:\n{joined}");
assert!(
!joined.contains("DESTRUCTIVE"),
"routine write must not scream DESTRUCTIVE:\n{joined}"
joined.contains("Changes files"),
"missing effect badge:\n{joined}"
);
assert!(
!joined.contains("Can't be undone"),
"routine write must not claim it is irreversible:\n{joined}"
);
assert_approval_key_badges_visible(&joined);
assert!(
@@ -1894,7 +1901,7 @@ fn render_elevated_write_is_calm_and_compact() {
"footer controls hint missing:\n{joined}"
);
assert!(
!joined.contains("active approval policy"),
!joined.contains("Your permissions"),
"policy prose is critical-only:\n{joined}"
);
assert!(
@@ -1905,7 +1912,7 @@ fn render_elevated_write_is_calm_and_compact() {
!joined.contains("Type:"),
"category taxonomy is critical-only:\n{joined}"
);
assert!(joined.contains("write_file"));
assert!(joined.contains("Write src/main.rs"), "{joined}");
}
#[test]
@@ -1916,18 +1923,22 @@ fn render_critical_shows_warning_badge_and_policy_semantics() {
let lines = render_lines(&view, 100, 40);
let joined = lines.join("\n");
assert!(
joined.contains("DESTRUCTIVE"),
"missing DESTRUCTIVE badge:\n{joined}"
joined.contains("Can't be undone"),
"missing irreversible badge:\n{joined}"
);
assert_approval_key_badges_visible(&joined);
assert!(
joined.contains("active approval policy"),
"missing policy/review-rule semantics:\n{joined}"
joined.contains("Your permissions, a review rule"),
"missing permission/review-rule semantics:\n{joined}"
);
assert!(
joined.contains("Deny rejects only this tool call"),
"missing deny-vs-abort semantics:\n{joined}"
joined.contains("Don't allow skips only this step"),
"missing don't-allow-vs-stop semantics:\n{joined}"
);
// Mark 4: no approval surface says Bash, MCP or abort.
for banned in ["Bash", "MCP", "abort", "Abort"] {
assert!(!joined.contains(banned), "{banned} on the card:\n{joined}");
}
assert!(joined.contains("rm -rf"));
}
@@ -1937,11 +1948,11 @@ fn render_elevated_zh_hans_is_calm_and_localized() {
let lines = render_lines(&view, 100, 40);
let joined = compact_rendered_text(&lines);
assert!(
joined.contains("需要批准"),
"missing zh calm badge:\n{joined}"
joined.contains("修改文件"),
"missing zh effect badge:\n{joined}"
);
assert!(
!joined.contains("破坏性"),
!joined.contains("无法撤销"),
"routine write must not use the destructive zh badge:\n{joined}"
);
assert!(
@@ -1989,7 +2000,7 @@ fn render_critical_zh_hans_localizes_security_copy() {
let lines = render_lines(&view, 100, 40);
let joined = compact_rendered_text(&lines);
assert!(
joined.contains("破坏性"),
joined.contains("无法撤销"),
"missing zh risk badge:\n{joined}"
);
assert!(
+8 -1
View File
@@ -58,7 +58,14 @@ pub(crate) fn route_identity_fields(
// rather than `high→effective unavailable` (#5950): a placeholder that
// can never resolve is noise, not a reading. First-party routes keep
// their tier, `auto: tier` and `req→eff` labels.
let effort = app.provable_reasoning_effort_label().unwrap_or_default();
// Labeled, so a bare "max" never sits on the row unexplained (mark 8).
let effort = app
.provable_reasoning_effort_label()
.map(|level| {
app.tr(MessageId::InfoLineThinking)
.replace("{level}", &level)
})
.unwrap_or_default();
if model.is_empty() {
return None;
}
+81 -14
View File
@@ -140,7 +140,10 @@ pub(crate) fn info_segments(app: &App, width: u16) -> Vec<InfoSegment> {
// header all still name the route.
if shows(StatusItem::Model) {
let (_, model) = app.effective_route_identity_display();
if model.is_empty() {
// A keyless first run carries a default model id but nothing can
// answer it: the chip says "not connected", matching the launch
// card's no-model line (U3), instead of naming a route that fails.
if model.is_empty() || app.onboarding_needs_api_key {
segments.push(InfoSegment::new(
InfoSegmentId::Model,
app.tr(MessageId::StartupDefaultSubjectModel).as_ref(),
@@ -151,13 +154,12 @@ pub(crate) fn info_segments(app: &App, width: u16) -> Vec<InfoSegment> {
// The context reading and the metrics claim the rest of the row;
// the route sheds its own qualifiers first.
let budget = crate::tui::phase_strip::info_route_budget(width);
let fields = crate::tui::phase_strip::route_identity_fields(app, tier, budget)
.unwrap_or_else(|| {
vec![crate::tui::phase_strip::RouteIdentityField {
kind: crate::tui::phase_strip::RouteFieldKind::Model,
text: model,
}]
});
let fields = info_route_fields(app, tier, budget).unwrap_or_else(|| {
vec![crate::tui::phase_strip::RouteIdentityField {
kind: crate::tui::phase_strip::RouteFieldKind::Model,
text: model,
}]
});
segments.push(InfoSegment::new(
InfoSegmentId::Model,
"",
@@ -340,6 +342,20 @@ pub(crate) fn info_segments(app: &App, width: u16) -> Vec<InfoSegment> {
segments
}
/// Route fields the info line paints, or `None` when it paints the
/// "not connected" chip instead. `info_segments` and the hitbox split both
/// read this, so a click can never land on a route the row did not draw.
fn info_route_fields(
app: &App,
tier: crate::tui::underwater::ShellTier,
budget: usize,
) -> Option<Vec<crate::tui::phase_strip::RouteIdentityField>> {
if app.onboarding_needs_api_key {
return None;
}
crate::tui::phase_strip::route_identity_fields(app, tier, budget)
}
/// The info line's controls that actually painted in this frame.
///
/// The route target intentionally contains no copied route metadata. The
@@ -482,7 +498,7 @@ fn render_info_row(
.map(|hitbox| hitbox.area);
// Same pure call `info_segments` made, with the same budget owner, so the
// split lines up with the text that was just measured.
let route_fields = crate::tui::phase_strip::route_identity_fields(
let route_fields = info_route_fields(
app,
crate::tui::underwater::ShellTier::for_chrome_width(area.width),
crate::tui::phase_strip::info_route_budget(area.width),
@@ -2308,6 +2324,7 @@ mod tests {
fn infoline_route_segment_registers_interaction_target() {
let mut app =
crate::test_support::test_app_with_options(crate::test_support::test_tui_options("."));
app.onboarding_needs_api_key = false;
let mut terminal =
Terminal::new(TestBackend::new(160, 1)).expect("info-line test terminal should build");
@@ -2365,6 +2382,49 @@ mod tests {
}
}
/// U3: a keyless first run keeps a default model id, but nothing can
/// answer it. The route chip says "not connected" instead of naming that
/// route, and it is not a route control until a model is connected.
#[test]
fn keyless_launch_route_chip_says_not_connected() {
let mut app =
crate::test_support::test_app_with_options(crate::test_support::test_tui_options("."));
app.ui_locale = codewhale_localization::Locale::En;
app.onboarding_needs_api_key = true;
let (_, model) = app.effective_route_identity_display();
assert!(!model.is_empty(), "the fixture carries a default model id");
let mut terminal =
Terminal::new(TestBackend::new(160, 1)).expect("info-line test terminal should build");
let mut hitboxes = super::InfoLineInteractionHitboxes::default();
terminal
.draw(|frame| {
let area = frame.area();
hitboxes = render_info_row(frame, &mut app, area, false);
})
.expect("info line should render");
let row: String = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol().to_string())
.collect();
assert!(row.contains("not connected"), "{row:?}");
assert!(!row.contains(&model), "a dead route is not named: {row:?}");
assert!(hitboxes.route.is_none(), "no provider control: {row:?}");
// Once a key lands the same row names the route again.
app.onboarding_needs_api_key = false;
let segments = super::info_segments(&app, 160);
assert!(
segments.iter().any(
|segment| segment.id == crate::tui::infoline::InfoSegmentId::Model
&& segment.value.contains(&model)
),
"{segments:?}"
);
}
/// "Where did the github info go?" — the workspace segment names the
/// repository when `origin` resolves to a forge slug, and only falls back
/// to the folder basename when it does not. The basename rides along as
@@ -2414,6 +2474,9 @@ mod tests {
use codewhale_models::{ContentBlock, Message};
let mut app =
crate::test_support::test_app_with_options(crate::test_support::test_tui_options("."));
// A keyless test config would paint "not connected" (U3); these
// readings are about a connected route.
app.onboarding_needs_api_key = false;
app.api_messages = std::sync::Arc::new(vec![Message {
role: codewhale_models::Role::User,
content: vec![ContentBlock::Text {
@@ -2480,7 +2543,7 @@ mod tests {
for width in [40u16, 80, 160] {
let row = metrics_row(&app, width);
assert!(
row.contains(&format!("ctx {pct}%")),
row.contains(&format!("context {pct}%")),
"{pct}% at {width} columns: {row:?}"
);
}
@@ -2514,7 +2577,7 @@ mod tests {
let mut app = app_with_context_percent(10);
assert!(
metrics_row(&app, 160).contains("ctx 10%"),
metrics_row(&app, 160).contains("context 10%"),
"the reading starts on the row"
);
@@ -2542,7 +2605,7 @@ mod tests {
app.status_items = items;
let row = metrics_row(&app, 160);
assert!(
!row.contains("ctx "),
!row.contains("context "),
"the toggle must take it off: {row:?}"
);
assert!(
@@ -2598,7 +2661,8 @@ mod tests {
assert!(
fields
.iter()
.any(|field| field.kind == RouteFieldKind::Effort && field.text == label),
.any(|field| field.kind == RouteFieldKind::Effort
&& field.text == format!("thinking: {label}")),
"{fields:?}"
);
}
@@ -2694,7 +2758,10 @@ mod tests {
row.contains("saved coverage unavailable"),
"an unclassified route preserves the reason: {row:?}"
);
assert!(row.contains("ctx 10%"), "and nothing else moves: {row:?}");
assert!(
row.contains("context 10%"),
"and nothing else moves: {row:?}"
);
// A real price on an otherwise unclassified route still prints.
app.session.cost_coverage_unknown_legacy = false;
+16 -12
View File
@@ -3,7 +3,7 @@
//!
//! Under the composer: row 1 is the posture bar (permission, mode, live
//! counts, the one hint that applies now), row 2 is the metrics line (model,
//! ctx, cost, ttft, tok/s, output tokens); the roster and to-do rows follow
//! context, cost, ttft, tok/s, output tokens); the roster and to-do rows follow
//! only when they have content. Every fact below is asserted to appear in
//! the composed frame exactly once.
@@ -43,6 +43,9 @@ fn frame_app() -> App {
// passed locally and failed on both CI legs until it was pinned. Force
// the wider, unenforced reading so every host asserts the same row.
app.sandbox_backend = None;
// The fixture config carries no key; a keyless launch paints "model not
// connected" (U3). These rows are about a connected route.
app.onboarding_needs_api_key = false;
app
}
@@ -164,7 +167,7 @@ fn composed_frame_paints_each_fact_in_exactly_one_row() {
("agent count", "2 agents".to_string()),
("ttft", "ttft 400ms".to_string()),
];
facts.push(("context reading", format!("ctx {pct}%")));
facts.push(("context reading", format!("context {pct}%")));
facts.push(("output rate", "40 avg tok/s".to_string()));
if width >= 120 {
facts.push((
@@ -191,7 +194,7 @@ fn composed_frame_paints_each_fact_in_exactly_one_row() {
.expect("posture bar");
let metrics = rows
.iter()
.position(|row| row.contains("ctx "))
.position(|row| row.contains("context "))
.expect("metrics line");
let composer = app
.viewport
@@ -282,7 +285,7 @@ fn idle_frame_keeps_two_chrome_rows_and_last_turn_metrics() {
// The idle fixture sits at 0% context and says so: the reading is on
// the row at every fullness (#5950), not only once it is a problem.
assert!(
rows[composer + 1].contains("ctx 0%"),
rows[composer + 1].contains("context 0%"),
"{}",
rows[composer + 1]
);
@@ -368,7 +371,7 @@ fn row_presets_reclaim_rows_and_quiet_them_in_the_composed_frame() {
// chip from that width up, and this test asserts the full row's clocks.
let (width, height) = (160u16, 32u16);
let posture_row = |rows: &[String]| rows.iter().position(|row| row.contains("(Shift+Tab)"));
let metrics_row = |rows: &[String]| rows.iter().position(|row| row.contains("ctx "));
let metrics_row = |rows: &[String]| rows.iter().position(|row| row.contains("context "));
let mut app = working_app();
let full = draw(&mut app, width, height);
@@ -402,7 +405,7 @@ fn row_presets_reclaim_rows_and_quiet_them_in_the_composed_frame() {
"the metrics line keeps its row"
);
assert_eq!(
count_rows_containing(&rows, "ctx "),
count_rows_containing(&rows, "context "),
1,
"the context reading is still painted once"
);
@@ -439,7 +442,7 @@ fn row_presets_reclaim_rows_and_quiet_them_in_the_composed_frame() {
);
let pct = super::info_context_percent(&app);
assert!(
rows[metrics].contains(&format!("ctx {pct}%")),
rows[metrics].contains(&format!("context {pct}%")),
"{:?}",
rows[metrics]
);
@@ -622,7 +625,7 @@ fn statusline_full_frame_presets_preserve_transcript_composer_and_hitboxes() {
context.is_none() && model.is_none(),
"hidden chrome has no stale actions: {evidence}"
);
assert_eq!(count_rows_containing(&rows, "ctx "), 0, "{evidence}");
assert_eq!(count_rows_containing(&rows, "context "), 0, "{evidence}");
} else {
let context = context.expect("visible context has an inspector hitbox");
let model = model.expect("visible model has a picker hitbox");
@@ -642,7 +645,7 @@ fn statusline_full_frame_presets_preserve_transcript_composer_and_hitboxes() {
);
assert!(!composer.intersects(target.area), "{evidence}");
}
assert_eq!(count_rows_containing(&rows, "ctx 0%"), 1, "{evidence}");
assert_eq!(count_rows_containing(&rows, "context 0%"), 1, "{evidence}");
}
if metrics == ChromeRowPreset::Compact {
if width >= 60 {
@@ -709,7 +712,7 @@ fn statusline_full_frame_custom_cost_preserves_evidence_and_width_shedding() {
let (rows, _) = draw_into(&mut app, &mut terminal);
eprintln!("{width}x{height} custom-saved-unknown\n{}", rows.join("\n"));
let metrics = rows.last().unwrap();
assert!(metrics.contains("ctx 0%"), "{metrics}");
assert!(metrics.contains("context 0%"), "{metrics}");
if width >= 60 {
assert!(metrics.contains(expected), "{width}: {metrics}");
} else {
@@ -863,7 +866,7 @@ fn statusline_full_frame_context_reading_updates_below_and_at_warning() {
let (rows, cursor) = draw_into(&mut app, &mut terminal);
let evidence = format!("{width}x{height} context-{pct}\n{}", rows.join("\n"));
eprintln!("{evidence}");
let label = format!("ctx {pct}%");
let label = format!("context {pct}%");
assert_eq!(count_rows_containing(&rows, &label), 1, "{evidence}");
assert!(
rows.iter()
@@ -890,7 +893,8 @@ fn statusline_full_frame_context_reading_updates_below_and_at_warning() {
ChromeInk::Metadata
};
let buffer = terminal.backend().buffer();
for (x, ink) in [(context.area.x, label_ink), (context.area.x + 4, value_ink)] {
// The value starts after the "context " label.
for (x, ink) in [(context.area.x, label_ink), (context.area.x + 8, value_ink)] {
assert_eq!(
buffer[(x, context.area.y)].fg,
codewhale_palette::grammar::chrome_style(&app.ui_theme, ink)
+6 -1
View File
@@ -240,6 +240,9 @@ fn composer_rows_stay_pinned_across_turn_state_transitions() {
app.onboarding = crate::tui::app::OnboardingState::None;
app.launch.visible = false;
app.ui_locale = codewhale_localization::Locale::En;
// A keyless fixture paints "model not connected" (U3); this one is
// about a connected route.
app.onboarding_needs_api_key = false;
// The empty launch shell intentionally hides session metrics. This
// fixture covers stable geometry once a conversation exists.
app.history.push(HistoryCell::User {
@@ -6770,13 +6773,15 @@ fn empty_shell_keeps_model_identity_without_session_metrics() {
for (width, height) in [(40, 12), (60, 16), (100, 32), (140, 40)] {
let mut app = create_test_app();
app.model = "gpt-4.1".into();
// A connected route: keyless, the chip says "model not connected" (U3).
app.onboarding_needs_api_key = false;
app.history.clear();
app.resync_history_revisions();
assert!(crate::tui::widgets::should_render_empty_state(&app));
let body = render_underwater_test_app(&mut app, width, height);
assert!(body.contains("gpt-4.1"), "{width}x{height}: {body}");
assert!(
!body.contains("ctx 0%"),
!body.contains("context 0%"),
"empty metrics must stay quiet: {body}"
);
assert!(
+106 -13
View File
@@ -86,6 +86,10 @@ struct RouteRow {
summary: String,
provider: Option<String>,
model: Option<String>,
/// The provider's fresh live roster no longer lists this model (#6035).
/// A bundled catalog row can outlive the account's roster, so offering
/// the route is not proof it is still listed.
roster_missing: bool,
}
pub struct FleetDetailView {
@@ -992,19 +996,24 @@ impl ModalView for FleetDetailView {
impl FleetDetailView {
/// A saved route pin is drifted when the `(provider, model)` pair is not
/// among the routes the picker can currently offer — the provider table
/// was removed, or the model dropped out of the provider's roster. The
/// pin may still serve upstream, so this only flags; it never rewrites.
/// among the routes the picker can currently offer (the provider table
/// was removed), or when the provider's fresh live roster no longer
/// lists the model even though a bundled row still offers it (#6035).
/// The pin may still serve upstream, so this only flags; it never
/// rewrites.
fn pin_drifted(&self, provider: &str, model: &str) -> bool {
!self.routes.iter().any(|row| {
row.provider
.as_deref()
.is_some_and(|p| p.eq_ignore_ascii_case(provider))
&& row
.model
self.routes
.iter()
.find(|row| {
row.provider
.as_deref()
.is_some_and(|m| m.eq_ignore_ascii_case(model))
})
.is_some_and(|p| p.eq_ignore_ascii_case(provider))
&& row
.model
.as_deref()
.is_some_and(|m| m.eq_ignore_ascii_case(model))
})
.is_none_or(|row| row.roster_missing)
}
fn render_overview(&self, area: Rect, buf: &mut Buffer) {
@@ -1210,7 +1219,7 @@ impl FleetDetailView {
} else {
Style::default().fg(palette::TEXT_SECONDARY)
};
lines.push(Line::from(vec![
let mut spans = vec![
Span::styled(if selected { "» " } else { " " }, base),
Span::styled(route.label.clone(), base),
Span::styled(" ", Style::default()),
@@ -1218,7 +1227,15 @@ impl FleetDetailView {
route.summary.clone(),
Style::default().fg(palette::TEXT_DIM),
),
]));
];
// Where the pin is edited, say so before it is picked (#6035).
if route.roster_missing {
spans.push(Span::styled(
tr(self.locale, MessageId::FleetRouteNotInCatalog),
Style::default().fg(palette::STATUS_WARNING),
));
}
lines.push(Line::from(spans));
}
Paragraph::new(ratatui::text::Text::from(lines)).render(area, buf);
}
@@ -1233,6 +1250,7 @@ fn build_route_rows(config: &Config) -> Vec<RouteRow> {
summary: String::new(),
provider: None,
model: None,
roster_missing: false,
}];
let health = crate::provider_readiness::ProviderReadinessSnapshot::default();
let active = config
@@ -1247,11 +1265,15 @@ fn build_route_rows(config: &Config) -> Vec<RouteRow> {
.blocked_reason()
.map(|r| r.into_owned())
.unwrap_or_else(|| readiness.label().into_owned());
let roster_missing =
crate::provider_catalog_live::pin_missing_from_fresh_roster(config, &provider, &model)
== Some(true);
rows.push(RouteRow {
label: format!("{provider_label}/{model}"),
summary: readiness_label,
provider: Some(provider),
model: Some(model),
roster_missing,
});
}
rows
@@ -1922,6 +1944,77 @@ mod tests {
);
}
/// #6035: a bundled catalog row can outlive the provider's live roster.
/// A route the fresh roster dropped is flagged in the overview and in the
/// pin editor, and the pin is never rewritten. (`build_route_rows` asks
/// `pin_missing_from_fresh_roster`, which carries its own roster tests;
/// seeding the process-wide catalog here would leak into parallel tests.)
#[test]
fn a_pin_the_fresh_roster_dropped_is_flagged_in_overview_and_picker() {
let ws = tempfile::TempDir::new().unwrap();
// The operator pins deepseek/deepseek-v4-flash.
let fleet = sample_fleet("Roster");
save_fleet(&fleet, FleetScope::Workspace, ws.path()).unwrap();
let mut view = FleetDetailView::open(
&app_in(ws.path().to_path_buf()),
&Config::default(),
"Roster",
FleetScope::Workspace,
)
.expect("open");
let dropped = |row: &RouteRow| {
row.provider.as_deref() == Some("deepseek")
&& row.model.as_deref() == Some("deepseek-v4-flash")
};
match view.routes.iter_mut().find(|row| dropped(row)) {
Some(row) => row.roster_missing = true,
None => view.routes.push(RouteRow {
label: "DeepSeek/deepseek-v4-flash".to_string(),
summary: String::new(),
provider: Some("deepseek".to_string()),
model: Some("deepseek-v4-flash".to_string()),
roster_missing: true,
}),
}
assert!(view.pin_drifted("deepseek", "deepseek-v4-flash"));
let render = |view: &FleetDetailView, pick: bool| {
let area = Rect::new(0, 0, 160, 12);
let mut buf = Buffer::empty(area);
if pick {
view.render_pick_route(area, &mut buf);
} else {
view.render_overview(area, &mut buf);
}
(0..area.height)
.map(|y| (0..area.width).map(|x| buf[(x, y)].symbol()).collect())
.collect::<Vec<String>>()
};
let overview = render(&view, false);
let operator_row = overview
.iter()
.find(|row| row.contains("deepseek-v4-flash"))
.expect("operator row rendered");
assert!(
operator_row.contains("not in current catalog"),
"{operator_row}"
);
view.open_route_picker(FleetRouteTarget::Operator);
view.pick_query = "deepseek-v4-flash".to_string();
let picker = render(&view, true);
let row = picker
.iter()
.find(|row| row.contains("/deepseek-v4-flash"))
.expect("dropped route still offered in the picker");
assert!(row.contains("not in current catalog"), "{row}");
assert_eq!(
view.fleet.operator.as_ref().map(|op| op.model.as_str()),
Some("deepseek-v4-flash"),
"a warning never rewrites the pin"
);
}
#[test]
fn save_writes_the_file_and_receipt_names_the_path() {
let ws = tempfile::TempDir::new().unwrap();
+64 -29
View File
@@ -2045,7 +2045,8 @@ impl<'a> ApprovalWidget<'a> {
let critical = matches!(stakes, crate::tui::approval::ApprovalStakes::Critical);
let mut body: Vec<Line<'static>> = Vec::with_capacity(16);
// Header: stakes badge + tool identifier.
// Header: effect badge + the plain summary of the call (E6). The raw
// tool name stays one details chord away in the pager.
body.push(Line::from(vec![
Span::raw(" "),
Span::styled(
@@ -2054,7 +2055,7 @@ impl<'a> ApprovalWidget<'a> {
if repo_law {
tr(locale, MessageId::ApprovalRepoLawBadge)
} else {
stakes_badge_text(stakes, locale)
effect_badge_text(self.request, stakes, locale)
}
),
Style::default()
@@ -2068,10 +2069,10 @@ impl<'a> ApprovalWidget<'a> {
format!(
"{} · {}",
tr(locale, MessageId::ApprovalRepoLawTitle),
self.request.tool_name
approval_heading(self.request, locale)
)
} else {
self.request.tool_name.clone()
approval_heading(self.request, locale)
},
Style::default()
.fg(palette::WHALE_ACTION)
@@ -2232,7 +2233,7 @@ impl<'a> ApprovalWidget<'a> {
]));
}
// Category line — localized risk category.
let (cat_label, cat_color) = category_label_for(self.request.category, locale);
let (cat_label, cat_color) = category_label_for(self.request, locale);
body.push(Line::from(vec![
Span::raw(" "),
Span::styled(label_type(locale), Style::default().fg(palette::TEXT_HINT)),
@@ -2325,12 +2326,12 @@ impl Renderable for ApprovalWidget<'_> {
if repo_law {
tr(self.view.locale(), MessageId::ApprovalRepoLawTitle)
} else {
Cow::Borrowed(self.request.tool_name.as_str())
Cow::Owned(approval_heading(self.request, self.view.locale()))
},
if repo_law {
tr(self.view.locale(), MessageId::ApprovalRepoLawBadge)
} else {
stakes_badge_text(stakes, self.view.locale())
effect_badge_text(self.request, stakes, self.view.locale())
},
);
let line = Line::from(Span::styled(
@@ -2644,19 +2645,42 @@ fn approval_option_style(is_selected: bool, color: Color) -> Style {
}
}
fn stakes_badge_text(
stakes: crate::tui::approval::ApprovalStakes,
locale: Locale,
) -> Cow<'static, str> {
use crate::tui::approval::ApprovalStakes;
match stakes {
ApprovalStakes::Routine => tr(locale, MessageId::ApprovalRiskReview),
ApprovalStakes::Elevated => tr(locale, MessageId::ApprovalRiskElevated),
ApprovalStakes::Critical => tr(locale, MessageId::ApprovalRiskDestructive),
/// The approval card's heading. English leads with the plain summary of the
/// call (E6); the summary is not localized yet, so other packs keep the tool
/// name rather than mixing an English sentence into translated chrome.
fn approval_heading(request: &ApprovalRequest, locale: Locale) -> String {
if matches!(locale, Locale::En) && !request.summary.trim().is_empty() {
request.summary.clone()
} else {
request.tool_name.clone()
}
}
fn category_label_for(category: ToolCategory, locale: Locale) -> (Cow<'static, str>, Color) {
/// Badge naming what the call does, not a risk tier: "Reads only", "Changes
/// files", "Runs a command", "Uses the network". Anything the stakes
/// classifier calls destructive or publishing reads "Can't be undone".
fn effect_badge_text(
request: &ApprovalRequest,
stakes: crate::tui::approval::ApprovalStakes,
locale: Locale,
) -> Cow<'static, str> {
if stakes == crate::tui::approval::ApprovalStakes::Critical {
return tr(locale, MessageId::ApprovalRiskDestructive);
}
let id = match request.category {
ToolCategory::Safe | ToolCategory::McpRead => MessageId::ApprovalEffectReadsOnly,
ToolCategory::FileWrite => MessageId::ApprovalEffectChangesFiles,
ToolCategory::Shell => MessageId::ApprovalEffectRunsCommand,
ToolCategory::Network => MessageId::ApprovalEffectUsesNetwork,
ToolCategory::McpAction => MessageId::ApprovalEffectConnectedApp,
ToolCategory::Agent => MessageId::ApprovalEffectStartsAgent,
ToolCategory::Unknown => MessageId::ApprovalEffectUnclassified,
};
tr(locale, id)
}
fn category_label_for(request: &ApprovalRequest, locale: Locale) -> (Cow<'static, str>, Color) {
let category = request.category;
let label = match category {
ToolCategory::Safe => tr(locale, MessageId::ApprovalCategorySafe),
ToolCategory::FileWrite => tr(locale, MessageId::ApprovalCategoryFileWrite),
@@ -2667,6 +2691,16 @@ fn category_label_for(category: ToolCategory, locale: Locale) -> (Cow<'static, s
ToolCategory::Agent => tr(locale, MessageId::ApprovalCategoryAgent),
ToolCategory::Unknown => tr(locale, MessageId::ApprovalCategoryUnknown),
};
// "Connected app (github)": name the server the tool comes from.
let label = match (
category,
crate::tui::approval::connected_app_server(&request.tool_name),
) {
(ToolCategory::McpRead | ToolCategory::McpAction, Some(server)) => {
Cow::Owned(format!("{label} ({server})"))
}
_ => label,
};
let color = match category {
ToolCategory::Safe => palette::STATUS_SUCCESS,
ToolCategory::FileWrite => palette::STATUS_WARNING,
@@ -2947,8 +2981,8 @@ fn destructive_approval_compact_semantics(locale: Locale) -> (&'static str, &'st
match locale {
Locale::ZhHans => ("规则: ", "批准策略要求确认;拒绝跳过本次,Esc 中止整轮。"),
_ => (
"Policy: ",
"Approval policy requires review; d denies, Esc aborts.",
"Why: ",
"Your permissions ask before this; d doesn't allow it, Esc stops the turn.",
),
}
}
@@ -2964,12 +2998,12 @@ fn destructive_approval_semantics(locale: Locale) -> [(&'static str, &'static st
],
_ => [
(
"Policy: ",
"The active approval policy, a review rule, or an explicit ask-rule requires confirmation.",
"Why: ",
"Your permissions, a review rule, or an ask rule requires confirmation.",
),
(
"Cancel: ",
"Deny rejects only this tool call; Esc aborts the whole turn.",
"Stop: ",
"Don't allow skips only this step; Esc stops the whole turn.",
),
],
}
@@ -8738,7 +8772,9 @@ mod tests {
.find(|line| line.contains("[2 / a]"))
.expect("full approval card should render the session option");
assert!(
full_session_option.to_lowercase().contains("this session")
full_session_option
.to_lowercase()
.contains("this conversation")
&& !full_session_option.to_lowercase().contains("always"),
"full approval option must state session scope without saying always:\n{full}"
);
@@ -8751,7 +8787,9 @@ mod tests {
.find(|line| line.contains("[2 / a]"))
.expect("short approval card should render the session option");
assert!(
compact_session_option.to_lowercase().contains("session")
compact_session_option
.to_lowercase()
.contains("conversation")
&& !compact_session_option.to_lowercase().contains("always"),
"short-terminal controls must label [2 / a] as session-scoped:\n{compact}"
);
@@ -8804,10 +8842,7 @@ mod tests {
rendered.contains("s allow once + always ask exact rule"),
"{rendered}"
);
assert!(
rendered.contains("Always allow this exact rule in this repo"),
"{rendered}"
);
assert!(rendered.contains("Always allow in this repo"), "{rendered}");
assert!(rendered.contains("Save:"), "{rendered}");
assert!(rendered.contains("1 ask rule"), "{rendered}");
assert!(rendered.contains("1 allow rule"), "{rendered}");