hermes-agent/locales/fr.yaml
Siddharth Balyan 7ba5df0d52
feat(billing): /credits command — balance + portal top-up handoff (#44776)
* feat(billing): /usage → portal top-up browser handoff

Add the terminal side of the billing slice (phase 2a): start a top-up by
throwing the user to the portal billing page with the top-up modal open. The
terminal does not confirm, poll, or track payment — checkout completes in the
browser and the next /usage shows the new balance.

- nous_account.py: parse organisation.slug/name from /api/oauth/account into
  NousPortalAccountInfo; add nous_portal_topup_url() building the org-pinned
  {base}/orgs/{slug}/billing?topup=open with a null-slug fallback to the legacy
  {base}/billing?topup=open (never /orgs/None/...).
- portal_cli.py: 'hermes portal topup' — fresh account fetch, identity line
  (Topping up as <email> / org <name>), browser open with printed-URL fallback,
  no-wait closing copy. No polling/confirmation (deferred to 2b).
- account_usage.py: the shared /usage credits block now links the org-pinned
  top-up URL (auto-opens the modal) + points to the command.

Depends on NAS #409 (organisation.slug/name + ?topup=open). Do not merge until
that is live on the target env; until then /api/oauth/account returns
organisation: { id } only and the URL falls back to legacy.

* feat(billing): /credits command for balance + top-up handoff

Replace the standalone `hermes portal topup` subcommand with an in-session
/credits slash command — a focused money surface (balance in, top-up out) that
works in the CLI, TUI, and every messaging platform from one registry entry.

- commands.py: register /credits (Info category). Slack is at its 50-slash cap,
  so /credits is routed via /hermes credits on Slack only (new
  _SLACK_VIA_HERMES_ONLY set) to avoid clamping a canonical command off the
  native list and breaking Telegram parity; native everywhere else.
- account_usage.py: build_credits_view() — one portal fetch → balance lines +
  identity line + org-pinned top-up URL + depleted flag, consumed by all
  surfaces. Reuses the same snapshot/URL builder as /usage so numbers match.
- cli.py: _show_credits() — balance block + identity line + 3-button panel
  (Open top-up / Copy link / Cancel) via the existing prompt_toolkit modal.
  ASK, never auto-launch; headless falls back to printing the URL.
- gateway/slash_commands.py: _handle_credits_command() — renders the block +
  tappable top-up URL + no-wait copy; works on button and plain-text platforms.
- /usage credits line now points to /credits.
- Retire `hermes portal topup` (portal_cli.py back to baseline); the engine
  (slug/name parse + nous_portal_topup_url) stays as the shared core.

No polling, no payment confirmation (billing phase 2a). Depends on NAS #409.

* fix(credits): /credits works in the TUI slash-worker (non-interactive)

In the TUI, /credits runs in the slash-worker subprocess where there is no
live prompt_toolkit app and stdin is the JSON-RPC pipe. _show_credits called
the 3-button modal unconditionally, which fell back to reading stdin →
exception → slash.exec rejected → the command produced no output (only the
pre-existing 'Credit access paused' banner showed).

- _show_credits: when self._app is None (TUI worker / piped / non-interactive),
  render the text variant — balance block + tappable top-up URL + no-wait line,
  same affordance as the messaging surfaces — and skip the modal entirely. The
  3-button panel still renders in the interactive CLI.
- Depleted banner copy: 'run /usage for balance' → 'run /credits to top up'
  now that /credits is the dedicated money surface (+ tests).
- Regression tests: _show_credits with self._app=None renders text and never
  invokes the modal; logged-out path.

* feat(tui): credits.view RPC for the /credits tappable top-up button

Add a credits.view JSON-RPC method returning the structured CreditsView
(logged_in, balance_lines, identity_line, topup_url, depleted) so the TUI can
render a clickable <Link> top-up button instead of plain text. Account-
independent (portal fetch gated on a logged-in Nous account), fail-open to
{logged_in: false} on any hiccup. Mirrors session.usage's credits-block pattern.

Frontend (TUI-local /credits command + Ink component) lands separately.

* feat(tui): /credits command with keyboard-driven top-up confirm

TUI-local /credits: fetches the structured balance via the credits.view RPC,
prints the balance + identity + top-up URL, then arms the EXISTING confirm
overlay (Enter = open top-up in browser via openExternalUrl, Esc = cancel).
Reuses ConfirmReq — no new overlay component/state/input handler. Headless
(openExternalUrl returns false) falls back to printing the URL.

- gatewayTypes.ts: CreditsViewResponse.
- commands/credits.ts: the command (mirrors /status's rpc+guarded pattern).
- registry.ts: register creditsCommands.
- test: balance+overlay armed, headless fallback, no-url, logged-out (4 cases).

Matches the CLI /credits 'Enter to open' affordance. Phase 2a: no polling.
2026-06-12 08:51:10 +00:00

374 lines
28 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Hermes static-message catalog -- French (français)
# See locales/en.yaml for the source of truth; keep keys in sync.
approval:
dangerous_header: "⚠️ COMMANDE DANGEREUSE : {description}"
choose_long: " [o]ne fois | [s]ession | [t]oujours | [r]efuser"
choose_short: " [o]ne fois | [s]ession | [r]efuser"
prompt_long: " Choix [o/s/t/R] : "
prompt_short: " Choix [o/s/R] : "
timeout: " ⏱ Délai dépassé — commande refusée"
allowed_once: " ✓ Autorisé une fois"
allowed_session: " ✓ Autorisé pour cette session"
allowed_always: " ✓ Ajouté à la liste d'autorisation permanente"
denied: " ✗ Refusé"
cancelled: " ✗ Annulé"
blocklist_message: "Cette commande est sur la liste de blocage inconditionnel et ne peut pas être approuvée."
gateway:
approval_expired: "⚠️ Approbation expirée (l'agent n'attend plus). Demandez à l'agent de réessayer."
draining: "⏳ Vidage de {count} agent(s) actif(s) avant redémarrage..."
goal_cleared: "✓ Objectif effacé."
no_active_goal: "Aucun objectif actif."
config_read_failed: "⚠️ Impossible de lire config.yaml : {error}"
config_save_failed: "⚠️ Impossible de sauvegarder la configuration : {error}"
model:
error_prefix: "Erreur : {error}"
switched: "Modèle changé pour `{model}`"
provider_label: "Fournisseur : {provider}"
context_label: "Contexte : {tokens} tokens"
max_output_label: "Sortie max. : {tokens} tokens"
cost_label: "Coût : {cost}"
capabilities_label: "Capacités : {capabilities}"
prompt_caching_enabled: "Cache de prompts : activé"
warning_prefix: "Avertissement : {warning}"
saved_global: "Enregistré dans config.yaml (`--global`)"
session_only_hint: "_(session uniquement — ajoutez `--global` pour conserver)_"
current_label: "Actuel : `{model}` chez {provider}"
current_tag: " (actuel)"
more_models_suffix: " (+{count} autres)"
usage_switch_model: "`/model <name>` — changer de modèle"
usage_switch_provider: "`/model <name> --provider <slug>` — changer de fournisseur"
usage_persist: "`/model <name> --global` — conserver"
agents:
header: "🤖 **Agents et tâches actifs**"
active_agents: "**Agents actifs :** {count}"
this_chat: " · ce chat"
more: "... et {count} de plus"
running_processes: "**Processus d'arrière-plan en cours :** {count}"
async_jobs: "**Tâches asynchrones du gateway :** {count}"
none: "Aucun agent actif ni tâche en cours."
state_starting: "démarrage"
state_running: "en cours"
approve:
no_pending: "Aucune commande en attente d'approbation."
once_singular: "✅ Commande approuvée. L'agent reprend..."
once_plural: "✅ Commandes approuvées ({count} commandes). L'agent reprend..."
session_singular: "✅ Commande approuvée (modèle approuvé pour cette session). L'agent reprend..."
session_plural: "✅ Commandes approuvées (modèle approuvé pour cette session) ({count} commandes). L'agent reprend..."
always_singular: "✅ Commande approuvée (modèle approuvé de manière permanente). L'agent reprend..."
always_plural: "✅ Commandes approuvées (modèle approuvé de manière permanente) ({count} commandes). L'agent reprend..."
background:
usage: "Usage : /background <prompt>\nExemple : /background Résume les meilleures histoires HN d'aujourd'hui\n\nExécute le prompt dans une session séparée. Vous pouvez continuer à discuter — le résultat apparaîtra ici une fois terminé."
started: "🔄 Tâche d'arrière-plan démarrée : « {preview} »\nID de tâche : {task_id}\nVous pouvez continuer à discuter — les résultats apparaîtront ici une fois terminés."
branch:
db_unavailable: "Base de données des sessions indisponible."
no_conversation: "Aucune conversation à brancher — envoyez d'abord un message."
create_failed: "Échec de la création de la branche : {error}"
switch_failed: "Branche créée mais impossible de basculer dessus."
branched_one: "⑂ Branche **{title}** créée ({count} message copié)\nOriginal : `{parent}`\nBranche : `{new}`\nUtilisez `/resume` pour revenir à l'original."
branched_many: "⑂ Branche **{title}** créée ({count} messages copiés)\nOriginal : `{parent}`\nBranche : `{new}`\nUtilisez `/resume` pour revenir à l'original."
commands:
usage: "Utilisation : `/commands [page]`"
skill_header: "⚡ **Commandes de skill** :"
default_desc: "Commande de skill"
none: "Aucune commande disponible."
header: "📚 **Commandes** ({total} au total, page {page}/{total_pages})"
nav_prev: "`/commands {page}` ← précédent"
nav_next: "suivant → `/commands {page}`"
out_of_range: "_(La page demandée {requested} était hors limites, affichage de la page {page}.)_"
compress:
not_enough: "Conversation insuffisante pour la compression (au moins 4 messages nécessaires)."
no_provider: "Aucun fournisseur configuré — compression impossible."
nothing_to_do: "Rien à compresser pour l'instant (la transcription est encore entièrement du contexte protégé)."
focus_line: "Focus : \"{topic}\""
summary_failed: "⚠️ Échec de la génération du résumé ({error}). {count} message(s) historique(s) ont été supprimés et remplacés par un espace réservé ; le contexte antérieur n'est plus récupérable. Vérifiez la configuration du modèle auxiliary.compression."
aborted: "⚠️ Compression interrompue ({error}). Aucun message n'a été supprimé — la conversation est inchangée. Lancez /compress pour réessayer, /reset pour une nouvelle session, ou vérifiez la configuration de votre modèle auxiliary.compression."
aux_failed: " Le modèle de compression configuré `{model}` a échoué ({error}). Récupéré avec votre modèle principal — le contexte est intact — mais vous pouvez vérifier `auxiliary.compression.model` dans config.yaml."
failed: "Échec de la compression : {error}"
debug:
upload_failed: "✗ Échec de l'envoi du rapport de débogage : {error}"
header: "**Rapport de débogage envoyé :**"
auto_delete: "⏱ Les pastes s'effaceront automatiquement dans 6 heures."
full_logs_hint: "Pour envoyer les journaux complets, utilisez `hermes debug share` depuis la CLI."
share_hint: "Partagez ces liens avec l'équipe Hermes pour obtenir de l'aide."
deny:
stale: "❌ Commande refusée (l'approbation était périmée)."
no_pending: "Aucune commande en attente de refus."
denied_singular: "❌ Commande refusée."
denied_plural: "❌ Commandes refusées ({count} commandes)."
fast:
not_supported: "⚡ /fast n'est disponible que pour les modèles OpenAI qui prennent en charge Priority Processing."
status: "⚡ Priority Processing\n\nMode actuel : `{mode}`\n\n_Usage :_ `/fast <normal|fast|status>`"
unknown_arg: "⚠️ Argument inconnu : `{arg}`\n\n**Options valides :** normal, fast, status"
saved: "⚡ ✓ Priority Processing : **{label}** (enregistré dans la configuration)\n_(prend effet au prochain message)_"
session_only: "⚡ ✓ Priority Processing : **{label}** (cette session uniquement)"
label_fast: "FAST"
label_normal: "NORMAL"
status_fast: "fast"
status_normal: "normal"
footer:
status: "📎 Pied de page d'exécution : **{state}**\nChamps : `{fields}`\nPlateforme : `{platform}`"
usage: "Usage : `/footer [on|off|status]`"
saved: "📎 Pied de page d'exécution : **{state}**{example}\n_(enregistré globalement — prend effet au prochain message)_"
example_line: "\nExemple : `{preview}`"
state_on: "ON"
state_off: "OFF"
goal:
unavailable: "Les objectifs ne sont pas disponibles dans cette session."
no_goal_set: "Aucun objectif défini."
paused: "⏸ Objectif en pause : {goal}"
no_resume: "Aucun objectif à reprendre."
resumed: "▶ Objectif repris : {goal}\nEnvoyez un message pour continuer, ou attendez — je passerai à l'étape suivante au prochain tour."
invalid: "Objectif invalide : {error}"
set: "⊙ Objectif défini (budget de {budget} tours) : {goal}\nJe continuerai jusqu'à ce que l'objectif soit terminé, que vous le mettiez en pause/effaciez, ou que le budget soit épuisé.\nContrôles : /goal status · /goal pause · /goal resume · /goal clear"
help:
header: "📖 **Commandes Hermes**\n"
skill_header: "\n⚡ **Commandes de skill** ({count} actives) :"
more_use_commands: "\n... et {count} de plus. Utilisez `/commands` pour la liste paginée complète."
insights:
invalid_days: "Valeur --days invalide : {value}"
error: "Erreur lors de la génération des analyses : {error}"
kanban:
error_prefix: "⚠ erreur kanban : {error}"
subscribed_suffix: "(abonné — vous serez notifié lorsque {task_id} se terminera ou sera bloqué)"
truncated_suffix: "… (tronqué ; utilisez `hermes kanban …` dans votre terminal pour la sortie complète)"
no_output: "(aucune sortie)"
personality:
none_configured: "Aucune personnalité configurée dans `{path}/config.yaml`"
header: "🎭 **Personnalités disponibles**\n"
none_option: "• `none` — (aucune superposition de personnalité)"
item: "• `{name}` — {preview}"
usage: "\nUtilisation : `/personality <name>`"
save_failed: "⚠️ Échec de l'enregistrement du changement de personnalité : {error}"
cleared: "🎭 Personnalité effacée — comportement de base de l'agent utilisé.\n_(prend effet au prochain message)_"
set_to: "🎭 Personnalité définie sur **{name}**\n_(prend effet au prochain message)_"
unknown: "Personnalité inconnue : `{name}`\n\nDisponibles : {available}"
profile:
header: "👤 **Profil :** `{profile}`"
home: "📂 **Dossier personnel :** `{home}`"
reasoning:
level_default: "medium (par défaut)"
level_disabled: "none (désactivé)"
scope_session: "remplacement de session"
scope_global: "configuration globale"
status: "🧠 **Paramètres de raisonnement**\n\n**Effort :** `{level}`\n**Portée :** {scope}\n**Affichage :** {display}\n\n_Usage :_ `/reasoning <none|minimal|low|medium|high|xhigh|reset|show|hide> [--global]`"
display_on: "activé ✓"
display_off: "désactivé"
display_set_on: "🧠 ✓ Affichage du raisonnement : **ACTIVÉ**\nLa réflexion du modèle sera affichée avant chaque réponse sur **{platform}**."
display_set_off: "🧠 ✓ Affichage du raisonnement : **DÉSACTIVÉ** pour **{platform}**"
reset_global_unsupported: "⚠️ `/reasoning reset --global` n'est pas pris en charge. Utilisez `/reasoning <level> --global` pour modifier la valeur globale par défaut."
reset_done: "🧠 ✓ Remplacement de raisonnement de la session effacé ; retour à la configuration globale."
unknown_arg: "⚠️ Argument inconnu : `{arg}`\n\n**Niveaux valides :** none, minimal, low, medium, high, xhigh\n**Affichage :** show, hide\n**Persister :** ajoutez `--global` pour enregistrer au-delà de cette session"
set_global: "🧠 ✓ Effort de raisonnement défini sur `{effort}` (enregistré dans la configuration)\n_(prend effet au prochain message)_"
set_global_save_failed: "🧠 ✓ Effort de raisonnement défini sur `{effort}` (session uniquement — échec de l'enregistrement de la configuration)\n_(prend effet au prochain message)_"
set_session: "🧠 ✓ Effort de raisonnement défini sur `{effort}` (session uniquement — ajoutez `--global` pour persister)\n_(prend effet au prochain message)_"
reload_mcp:
cancelled: "🟡 /reload-mcp annulé. Outils MCP inchangés."
always_followup: " Les prochains appels `/reload-mcp` s'exécuteront sans confirmation. Réactivez via `approvals.mcp_reload_confirm: true` dans `config.yaml`."
confirm_prompt: "⚠️ **Confirmer /reload-mcp**\n\nRecharger les serveurs MCP reconstruit l'ensemble d'outils de cette session et **invalide le cache de prompt du fournisseur** — le prochain message renverra l'intégralité des jetons d'entrée. Sur les modèles à long contexte ou à raisonnement élevé, cela peut être coûteux.\n\nChoisissez :\n• **Approuver une fois** — recharger maintenant\n• **Toujours approuver** — recharger maintenant et masquer cette confirmation définitivement\n• **Annuler** — laisser les outils MCP inchangés\n\n_Alternative texte : répondez `/approve`, `/always` ou `/cancel`._"
header: "🔄 **Serveurs MCP rechargés**\n"
reconnected: "♻️ Reconnectés : {names}"
added: " Ajoutés : {names}"
removed: " Supprimés : {names}"
none_connected: "Aucun serveur MCP connecté."
tools_available: "\n🔧 {tools} outil(s) disponible(s) sur {servers} serveur(s)"
failed: "❌ Échec du rechargement MCP : {error}"
reload_skills:
header: "🔄 **Skills rechargées**\n"
no_new: "Aucune nouvelle skill détectée."
total: "\n📚 {count} skill(s) disponible(s)"
added_header: " **Skills ajoutées :**"
removed_header: " **Skills supprimées :**"
item_with_desc: " - {name} : {desc}"
item_no_desc: " - {name}"
failed: "❌ Échec du rechargement des skills : {error}"
reset:
header_default: "✨ Session réinitialisée ! Nouveau départ."
header_new: "✨ Nouvelle session démarrée !"
header_titled: "✨ Nouvelle session démarrée : {title}"
title_rejected: "\n⚠ Titre refusé : {error}"
title_error_untitled: "\n⚠ {error} — session démarrée sans titre."
title_empty_untitled: "\n⚠ Le titre est vide après nettoyage — session démarrée sans titre."
tip: "\n✦ Astuce : {tip}"
restart:
in_progress: "⏳ Redémarrage du gateway déjà en cours..."
restarting: "♻ Redémarrage du gateway. Si vous n'êtes pas notifié dans les 60 secondes, redémarrez depuis la console avec `hermes gateway restart`."
resume:
db_unavailable: "Base de données des sessions indisponible."
parse_error: "⚠️ Could not parse `/resume` arguments: {error}.
Use quotes around titles with spaces, for example: `/resume \"Project A Plan\"`."
matrix_no_named_sessions: "No named sessions found for this Matrix room.
Use `/title My Session` to name the current room session, `/resume --all` to list all Matrix sessions, or `/resume --cross-room <session name>` to explicitly cross room boundaries."
matrix_blocked_no_origin: "⚠️ Matrix /resume blocked: this named session has no recorded room origin, so Hermes will not resume it inside the current room by default. Use `/resume --cross-room {name}` if you intentionally want to cross room boundaries."
matrix_blocked_other_room: "⚠️ Matrix /resume blocked: that session belongs to a different Matrix room ({room}). Use `/resume --cross-room {name}` if you intentionally want to resume it here."
matrix_cross_room_success: "⚠️ Cross-room resume: resumed **{title}** inside Matrix room **{room}**.
Future messages in this room will use that transcript until `/reset` or another `/resume`.{msg_part}"
no_named_sessions: "Aucune session nommée trouvée.\nUtilisez `/title Ma session` pour nommer la session actuelle, puis `/resume Ma session` pour y revenir plus tard."
list_header: "📋 **Sessions nommées**\n"
list_item: "• **{title}**{preview_part}"
list_item_numbered: "{index}. **{title}**{preview_part}"
list_preview_suffix: " — _{preview}_"
list_footer: "\nUsage : `/resume <nom de session>`"
list_footer_numbered: "\nUtilisation : `/resume <nom de session>` ou `/resume <numéro>` (par exemple `/resume 1` pour la plus récente)"
list_failed: "Impossible de lister les sessions : {error}"
out_of_range: "L'index de reprise {index} est hors limites.\nUtilisez `/resume` sans arguments pour voir les sessions disponibles."
not_found: "Aucune session correspondant à '**{name}**' trouvée.\nUtilisez `/resume` sans argument pour voir les sessions disponibles."
already_on: "📌 Déjà sur la session **{name}**."
switch_failed: "Échec du changement de session."
resumed_one: "↻ Session **{title}** reprise ({count} message). Conversation restaurée."
resumed_many: "↻ Session **{title}** reprise ({count} messages). Conversation restaurée."
resumed_no_count: "↻ Session **{title}** reprise. Conversation restaurée."
retry:
no_previous: "Aucun message précédent à réessayer."
rollback:
not_enabled: "Les points de contrôle ne sont pas activés.\nActivez-les dans config.yaml :\n```\ncheckpoints:\n enabled: true\n```"
none_found: "Aucun point de contrôle trouvé pour {cwd}"
invalid_number: "Numéro de point de contrôle invalide. Utilisez 1-{max}."
restored: "✅ Restauré au point de contrôle {hash} : {reason}\nUn instantané pré-rollback a été enregistré automatiquement."
restore_failed: "❌ {error}"
set_home:
save_failed: "Impossible d'enregistrer le canal principal : {error}"
success: "✅ Canal principal défini sur **{name}** (ID : {chat_id}).\nLes tâches cron et les messages multi-plateformes seront livrés ici."
status:
header: "📊 **État de Hermes Gateway**"
matrix_scope_header: "**Matrix scope:**"
matrix_scope_room: " room: {room}"
matrix_scope_room_id: " room_id: {room_id}"
matrix_scope_thread: " thread_id: {thread_id}"
matrix_scope_mode: " session_scope: {scope}"
matrix_scope_key: " session_key: {session_key}"
session_id: "**ID de session :** `{session_id}`"
title: "**Titre :** {title}"
created: "**Créé :** {timestamp}"
last_activity: "**Dernière activité :** {timestamp}"
tokens: "**Jetons :** {tokens}"
agent_running: "**Agent en cours :** {state}"
state_yes: "Oui ⚡"
state_no: "Non"
queued: "**Suivis en file :** {count}"
platforms: "**Plateformes connectées :** {platforms}"
stop:
stopped_pending: "⚡ Arrêté. L'agent n'avait pas encore commencé — vous pouvez continuer cette session."
stopped: "⚡ Arrêté. Vous pouvez continuer cette session."
no_active: "Aucune tâche active à arrêter."
title:
db_unavailable: "Base de données des sessions indisponible."
warn_prefix: "⚠️ {error}"
empty_after_clean: "⚠️ Le titre est vide après nettoyage. Utilisez des caractères imprimables."
set_to: "✏️ Titre de session défini : **{title}**"
not_found: "Session introuvable dans la base de données."
current_with_title: "📌 Session : `{session_id}`\nTitre : **{title}**"
current_no_title: "📌 Session : `{session_id}`\nAucun titre défini. Usage : `/title Mon nom de session`"
topic:
not_telegram_dm: "La commande /topic n'est disponible que dans les chats privés Telegram."
no_session_db: "Base de données de sessions non disponible."
unauthorized: "Vous n'êtes pas autorisé à utiliser /topic sur ce bot."
restore_needs_topic: "Pour restaurer une session, créez ou ouvrez d'abord un topic Telegram, puis envoyez /topic <session-id> dans ce topic. Pour créer un nouveau topic, ouvrez All Messages et envoyez-y n'importe quel message."
topics_disabled: "Les topics Telegram ne sont pas encore activés pour ce bot.\n\nComment les activer :\n1. Ouvrez @BotFather.\n2. Choisissez votre bot.\n3. Ouvrez Bot Settings → Threads Settings.\n4. Activez Threaded Mode et assurez-vous que les utilisateurs sont autorisés à créer de nouveaux threads.\n\nPuis envoyez /topic à nouveau."
topics_user_disallowed: "Les topics Telegram sont activés, mais les utilisateurs ne peuvent pas en créer.\n\nOuvrez @BotFather → choisissez votre bot → Bot Settings → Threads Settings, puis désactivez 'Disallow users to create new threads'.\n\nPuis envoyez /topic à nouveau."
enable_failed: "Échec de l'activation du mode topic Telegram : {error}"
bound_status: "Ce topic est lié à :\nSession : {label}\nID : {session_id}\n\nUtilisez /new pour remplacer ce topic par une nouvelle session.\nPour un travail parallèle, ouvrez All Messages et envoyez-y un message pour créer un autre topic."
thread_ready: "Les topics multi-sessions Telegram sont activés.\n\nCe topic sera utilisé comme session Hermes indépendante. Utilisez /new pour remplacer la session actuelle de ce topic. Pour un travail parallèle, ouvrez All Messages et envoyez-y un message pour créer un autre topic."
untitled_session: "Session sans titre"
undo:
nothing: "Rien à annuler."
removed: "↩️ {turns} tour(s) annulé(s) ({count} message(s)).\nSauvegardé dans : « {preview} »\nCopiez/modifiez le texte ci-dessus et envoyez-le pour relancer à partir d'ici."
invalid_count: "Nombre invalide « {arg} » — utilisez /undo ou /undo N."
update:
platform_not_messaging: "✗ /update n'est disponible que depuis les plateformes de messagerie. Exécutez `hermes update` depuis le terminal."
not_git_repo: "✗ Pas un dépôt git — impossible de mettre à jour."
hermes_cmd_not_found: "✗ Impossible de localiser la commande `hermes`. Hermes est en cours d'exécution, mais la commande de mise à jour n'a pas pu trouver l'exécutable dans le PATH ni via l'interpréteur Python actuel. Essayez d'exécuter `hermes update` manuellement dans votre terminal."
start_failed: "✗ Échec du démarrage de la mise à jour : {error}"
starting: "⚕ Démarrage de la mise à jour Hermes… Je diffuserai la progression ici."
usage:
rate_limits: "⏱️ **Limites de débit :** {state}"
header_session: "📊 **Utilisation des jetons de session**"
label_model: "Modèle : `{model}`"
label_input_tokens: "Jetons d'entrée : {count}"
label_cache_read: "Jetons de lecture du cache : {count}"
label_cache_write: "Jetons d'écriture du cache : {count}"
label_output_tokens: "Jetons de sortie : {count}"
label_total: "Total : {count}"
label_api_calls: "Appels API : {count}"
label_cost: "Coût : {prefix}${amount}"
label_cost_included: "Coût : inclus"
label_context: "Contexte : {used} / {total} ({pct}%)"
label_compressions: "Compressions : {count}"
header_session_info: "📊 **Infos de session**"
label_messages: "Messages : {count}"
label_estimated_context: "Contexte estimé : ~{count} jetons"
detailed_after_first: "_(Utilisation détaillée disponible après la première réponse de l'agent)_"
no_data: "Aucune donnée d'utilisation disponible pour cette session."
credits:
not_logged_in: "Non connecté à Nous Portal. Connecte-toi pour voir ton solde de crédits et recharger."
verbose:
not_enabled: "La commande `/verbose` n'est pas activée pour les plateformes de messagerie.\n\nActivez-la dans `config.yaml` :\n```yaml\ndisplay:\n tool_progress_command: true\n```"
mode_off: "⚙️ Progression des outils : **OFF** — aucune activité d'outil affichée."
mode_new: "⚙️ Progression des outils : **NEW** — affichée lors d'un changement d'outil (longueur d'aperçu : `display.tool_preview_length`, par défaut 40)."
mode_all: "⚙️ Progression des outils : **ALL** — chaque appel d'outil est affiché (longueur d'aperçu : `display.tool_preview_length`, par défaut 40)."
mode_verbose: "⚙️ Progression des outils : **VERBOSE** — chaque appel d'outil avec ses arguments complets."
saved_suffix: "_(enregistré pour **{platform}** — prend effet au prochain message)_"
save_failed: "_(impossible d'enregistrer dans la configuration : {error})_"
voice:
enabled_voice_only: "Mode vocal activé.\nJe répondrai en vocal quand vous envoyez des messages vocaux.\nUtilisez /voice tts pour obtenir des réponses vocales à tous les messages."
disabled_text: "Mode vocal désactivé. Réponses uniquement textuelles."
tts_enabled: "TTS automatique activé.\nToutes les réponses incluront un message vocal."
status_mode: "Mode vocal : {label}"
status_channel: "Canal vocal : #{channel}"
status_participants: "Participants : {count}"
status_member: " - {name}{status}"
speaking: " (parle)"
enabled_short: "Mode vocal activé."
disabled_short: "Mode vocal désactivé."
label_off: "Désactivé (texte seulement)"
label_voice_only: "Activé (réponse vocale aux messages vocaux)"
label_all: "TTS (réponse vocale à tous les messages)"
help: "{toggle}\n\n**Comment fonctionne /voice**\n• `/voice on` — réponse vocale quand vous envoyez un message vocal\n• `/voice tts` — réponse vocale à *chaque* message\n• `/voice off` — retour aux réponses texte uniquement\n• `/voice status` — afficher le mode actuel\n• `/voice` (sans argument) — bascule rapide entre activé et désactivé{channels}"
help_channels: "\n\n**Salons vocaux en direct (Discord)**\n• Rejoignez d'abord un salon vocal, puis `/voice channel` — je rejoins, j'écoute et je parle mes réponses\n• `/voice leave` — se déconnecter du salon vocal"
yolo:
disabled: "⚠️ Mode YOLO **DÉSACTIVÉ** pour cette session — les commandes dangereuses nécessiteront une approbation."
enabled: "⚡ Mode YOLO **ACTIVÉ** pour cette session — toutes les commandes sont auto-approuvées. À utiliser avec prudence."
shared:
session_db_unavailable: "Base de données de sessions indisponible."
session_db_unavailable_prefix: "Base de données de sessions indisponible"
session_not_found: "Session introuvable dans la base de données."
warn_passthrough: "⚠️ {error}"