mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-11 01:51:34 -10:00
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:
1 parent
caa5f94876
commit
c52a5c43ae
24 files changed
+702
-337
No files matched your search
@@ -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?",
|
||||
|
||||
@@ -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?",
|
||||
|
||||
@@ -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?",
|
||||
|
||||
@@ -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?",
|
||||
|
||||
@@ -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 ?",
|
||||
|
||||
@@ -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": "आप क्या हासिल करना चाहते हैं?",
|
||||
|
||||
@@ -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?",
|
||||
|
||||
@@ -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": "何を達成したいですか?",
|
||||
|
||||
@@ -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": "무엇을 이루고 싶으신가요?",
|
||||
|
||||
@@ -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?",
|
||||
|
||||
@@ -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": "Чего вы хотите достичь?",
|
||||
|
||||
@@ -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": "Чого ви хочете досягти?",
|
||||
|
||||
@@ -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ì?",
|
||||
|
||||
@@ -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": "你想完成什么?",
|
||||
|
||||
@@ -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 退出",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}");
|
||||
|
||||
Reference in new issue
Block a user