{
  "source": {
    "fromChat": "From {{name}} in {{chat}}:",
    "from": "From {{name}}:",
    "member": "member {{code}}",
    "delayedDelivery": "(Originally received at {{time}}; delivered late after a dispatch failure)"
  },
  "settings": {
    "title": "Chat",
    "description": "Connect chat bots to Ycodium. One row per bot: create the bot on its platform, paste its credentials, and pick one access mode that decides who can use it in private chats and groups. Where the allowlist applies, list the people in \"Allowed people\". For Lark, Connect a Lark bot does all of that with one scan. Admitted people message the bot (or add it to a group), pick a project in the chat, and can start work.",
    "bots": "Bots",
    "language": {
      "label": "Reply language",
      "description": "The language of the bot's own sentences in the chat: help, status, confirmations and notices. Chinese shortcut commands only work when this is Chinese. A change applies from the bot's next sentence.",
      "zhCN": "中文",
      "en": "English"
    },
    "defaultModel": {
      "follow": "Follow chat / project default",
      "unavailable": "{{value}} (unavailable)"
    },
    "defaultPreset": {
      "none": "No preset",
      "unavailable": "{{value}} (unavailable)"
    },
    "runtimeMode": {
      "approvalRequired": "Ask before commands and file changes",
      "autoAcceptEdits": "Auto-accept edits, ask before other actions",
      "auto": "Approve routine actions",
      "fullAccess": "Commands and edits without prompts"
    },
    "groupMention": {
      "always": "Only when it is mentioned",
      "never": "Every message",
      "ambient": "Every message, except ones aimed at someone else"
    },
    "deliveryHttp": {
      "enabled": {
        "label": "Enable proactive delivery HTTP",
        "description": "Off by default. When on, listens only on 127.0.0.1 so local scripts can POST botId+targetId text. Do not expose publicly; set a Bearer token when other local processes might call it."
      },
      "port": {
        "label": "Delivery HTTP port",
        "description": "Bound to 127.0.0.1 only. Default 3847."
      },
      "token": {
        "label": "Delivery HTTP token",
        "description": "Optional. When set, requests must send Authorization: Bearer <token>."
      }
    },
    "deliveryTargets": {
      "label": "Delivery targets",
      "description": "Callers store only botId (settings row id) and targetId. Keep platform chat ids here; do not send them on each request."
    },
    "deliveryKind": {
      "chat": "Chat",
      "user": "User / DM",
      "group": "Group"
    },
    "queueMode": {
      "queue": "Queue (default)",
      "steer": "Steer (auto-join the running turn)"
    },
    "onOff": {
      "on": "On",
      "off": "Off"
    },
    "larkPanelMode": {
      "default": "Default (all commands)",
      "minimal": "Minimal"
    },
    "accessMode": {
      "allowlist": "Allowlist only (default)",
      "open": "Allow everyone",
      "privateOpen": "Private chats: open; groups: allowlist",
      "groupOpen": "Private chats: allowlist; groups: open"
    }
  },
  "platform": {
    "lark": "Lark",
    "telegram": "Telegram",
    "wecom": "WeCom",
    "wecom-app": "WeCom App",
    "wechat": "WeChat (experimental)"
  },
  "column": {
    "displayName": {
      "label": "Name",
      "placeholder": "Team bot"
    },
    "platform": {
      "label": "Platform"
    },
    "larkAppId": {
      "label": "Lark app ID",
      "placeholder": "cli_…"
    },
    "larkAppSecret": {
      "label": "Lark app secret"
    },
    "telegramToken": {
      "label": "Telegram bot token"
    },
    "wecomBotId": {
      "label": "WeCom bot ID"
    },
    "wecomBotSecret": {
      "label": "WeCom bot secret"
    },
    "allowedSenders": {
      "label": "Allowed people",
      "description": "Comma-separated user ids from that platform. Used in chats where the access mode requires the allowlist; while the list is empty, nobody can use the bot in those chats. To find an id: from an account not yet on the list, message the bot in a chat that requires the allowlist (mention it in a group). The bot replies with the id, at most once every 10 minutes per account."
    },
    "proxy": {
      "label": "Proxy",
      "description": "Some platforms are only reachable through a proxy on this machine, and which one can differ per bot."
    },
    "defaultModel": {
      "label": "Default model",
      "description": "Used for new sessions when the chat has no /model selection. Existing sessions keep their model; send /new to use this default. Unavailable choices fall back to the project default."
    },
    "defaultPreset": {
      "label": "Default preset",
      "description": "Used for new sessions when the chat has no /preset selection. Existing sessions keep their preset; send /new to use this default. Unavailable presets are skipped."
    },
    "runtimeMode": {
      "label": "What a chat message may do",
      "description": "Admitted people start turns from chat, so this is deliberately not full access by default. Whichever mode asks, the request lands in the chat."
    },
    "groupMention": {
      "label": "In groups, answer",
      "description": "When the bot answers in a group (unset means only when it is mentioned); direct chats always count. WeCom only delivers messages that mention the bot, so this changes nothing there."
    },
    "deliveryBotId": {
      "label": "Bot",
      "description": "The bot that sends the message. Callers pass the id in brackets as botId.",
      "placeholder": "bot row id"
    },
    "deliveryTargetId": {
      "label": "Target ID",
      "description": "Caller alias unique under that bot; immutable after save.",
      "placeholder": "release-alerts"
    },
    "deliveryName": {
      "label": "Display name",
      "placeholder": "Release alerts"
    },
    "deliveryKind": {
      "label": "Kind"
    },
    "deliveryPlatformChatId": {
      "label": "Platform chat ID",
      "description": "Lark open_id/chat_id, Telegram chat id (a forum topic: chat id:topic id), WeCom user/group id, etc.",
      "placeholder": "oc_… / ou_… / -100…"
    },
    "queueMode": {
      "label": "New messages while busy",
      "description": "When a turn is already running, how to handle new plain-text messages. Queue waits for the current turn (Steer is still a button); Steer auto-joins the running turn."
    },
    "approvalTimeoutMinutes": {
      "label": "Approval timeout (minutes)",
      "description": "Unanswered approvals are denied automatically after this many minutes. Defaults to 60; enter an integer from 0 to 1440, and 0 turns it off. An invalid value counts as 60."
    },
    "larkPanelMode": {
      "label": "Command panel",
      "description": "Which commands /panel (or typing /) lists. Minimal hides low-frequency system items; typing the same command still works."
    },
    "larkTopicOnMention": {
      "label": "Open a topic when mentioned",
      "description": "When someone @-mentions the bot, its answer opens a Lark topic under that message, so the question and the answer stay together. Messages already inside a topic are answered in that topic either way."
    },
    "wecomNumberedHints": {
      "label": "Numbered-reply hints",
      "description": "WeCom has no buttons; approvals and questions use reply numbers. When on, include fuller usage hints."
    },
    "wecomAppStream": {
      "label": "Streaming replies",
      "description": "Off by default: the bot first confirms it got the message and sends the full reply once finished. When on, replies appear as they are written if the WeCom client supports it; otherwise the full reply is sent as text automatically when it is done. Turning it on is a trial: whether the WeCom client shows replies as they are written has not been verified."
    },
    "wecomAppCorpId": {
      "label": "Corp ID",
      "placeholder": "ww…"
    },
    "wecomAppAgentId": {
      "label": "AgentId",
      "placeholder": "1000002"
    },
    "wecomAppSecret": {
      "label": "App Secret"
    },
    "wecomAppToken": {
      "label": "Callback Token"
    },
    "wecomAppEncodingAESKey": {
      "label": "EncodingAESKey",
      "placeholder": "43 alphanumeric chars"
    },
    "wecomAppCallbackBaseUrl": {
      "label": "Public callback base URL (optional)",
      "description": "The public address that forwards to this machine's callback port. When set, the full callback URL to paste into WeCom “Receive messages → API receive” appears below; otherwise only the path does.",
      "placeholder": "https://im.example.com or http://IP:30987"
    },
    "wecomAppApiBaseUrl": {
      "label": "API proxy base (optional)",
      "description": "Base URL for WeCom API calls (tokens, messages, images and files). Leave empty for qyapi.weixin.qq.com. The outbound IP must be on the app's trusted IP list.",
      "placeholder": "https://qyapi.weixin.qq.com"
    },
    "wecomAppListenPort": {
      "label": "Local callback port (optional)",
      "description": "Defaults to 30987. Self-built apps listening on the same address share one port; rows with different listen addresses (say 0.0.0.0 and 127.0.0.1) need different ports, or the second one fails to listen.",
      "placeholder": "30987"
    },
    "wecomAppListenHost": {
      "label": "Local listen address (optional)",
      "description": "Defaults to 127.0.0.1: only this machine can reach the callback port, so put a reverse proxy or tunnel in front of it. 0.0.0.0 listens on every network interface, which opens the callback port to the network; only use it behind a firewall you control.",
      "placeholder": "127.0.0.1"
    },
    "wechatBotToken": {
      "label": "WeChat bot token",
      "description": "bot_token from QR binding. Live iLink login is gated by proposal 0069; filling this in does not connect yet."
    },
    "accessMode": {
      "label": "Access mode",
      "description": "Allowlist admits only \"Allowed people\"; open admits any identifiable sender. New bots default to allowlist. \"Private chats: open; groups: allowlist\" and \"Private chats: allowlist; groups: open\" share the same \"Allowed people\"; in either of them, when the chat type cannot be confirmed, only \"Allowed people\" can use the bot."
    }
  },
  "reply": {
    "deliveryFailed": "The reply could not be delivered. Check this bot's connection and permissions, then try again.",
    "connectionTest": "✅ Ycodium connection test\nThis test message was sent by “{{name}}” using Check connection in Settings. No reply needed.",
    "wecomDeliveryFailed": "This reply didn't reach {{recipient}} in WeCom: they are outside this app's visible range. Add them to the app's visible range in the WeCom admin console, then ask the agent to send it again.",
    "conclusionRecovered": "(Finished while restarting; delivered now)",
    "notAllowed": "You are not on this bot's allow list, so this tap had no effect.",
    "noDefaultModel": "Project \"{{title}}\" has no agent selected yet, so a thread cannot be started. Send /model to pick one, or set a default in the project settings, then message again.",
    "projectArchived": "Project \"{{title}}\" is archived. Restore it, or send /repo to pick another project, then message again.",
    "projectMissing": "The project this chat was working on is no longer on this machine (it may have been deleted), so a thread cannot be started. Send /repo to pick another one.",
    "threadGone": "The earlier thread is no longer available here, so this chat now starts a new one.",
    "threadArchived": "This chat's thread \"{{title}}\" is archived. Messages sent while it is archived are not passed to the agent, not even after it is unarchived. Send /new to start a new thread, or unarchive it on the web or in another client and send again.",
    "threadStillArchived": "The thread is still archived, so this message was not passed to the agent either.",
    "dispatchFailed": "This message couldn't be dispatched. Please send it again later.",
    "turnFailedToStart": "{{message}} Reference {{ref}}.",
    "turnFailedToStartQuoted": "This message (starting with \"{{excerpt}}\") couldn't be dispatched. Please send it again later. Reference {{ref}}.",
    "turnFailedToStartAttachment": "This message (with the attachment \"{{excerpt}}\") couldn't be dispatched. Please send it again later. Reference {{ref}}.",
    "turnRetrying": "Couldn't dispatch this message yet. Retrying automatically in the background, so there's no need to send it again. You'll get another message if it still fails. Reference {{ref}}.",
    "turnFailed": "This turn didn't finish: {{detail}}\nReference {{ref}}. Send the message again; if it keeps failing, send /new to start a new conversation.",
    "turnFailedNoDetail": "This turn didn't finish. Reference {{ref}}. Send the message again; if it keeps failing, send /new to start a new conversation.",
    "turnFailedBrief": "This turn didn't finish: {{detail}}\nSend the message again; if it keeps failing, send /new to start a new conversation.",
    "turnFailedBriefNoDetail": "This turn didn't finish. Send the message again; if it keeps failing, send /new to start a new conversation.",
    "turnFailedWithReference": "{{message}}\nReference {{ref}}.",
    "turnFailedQuota": "The model quota or balance is insufficient, so this turn didn't finish. Send /model to switch to another model, or ask an administrator to add quota, then try again.",
    "turnFailedRateLimit": "The model service is rate-limiting requests, so this turn didn't finish. Please try again later.",
    "turnFailedContextLimit": "This conversation exceeds the model context limit, so this turn didn't finish. Send /new to start a new conversation, then send your request again.",
    "turnFailedAuth": "The model credentials are missing or invalid, so this turn didn't finish. Ask an administrator to check the model settings, then try again.",
    "turnFailedModelUnavailable": "The current model does not exist or does not support the selected settings, so this turn didn't finish. Send /model to list models, then send /model followed by a number (for example /model 2) to pick one for this chat, and send your request again.",
    "turnFailedTransport": "The model service is temporarily unreachable, so this turn didn't finish. Please try again later.",
    "attachmentReceived": "Got {{names}} ({{count}} waiting). Tell me what to do with them.",
    "attachmentFailed": "Could not take {{name}}: {{reason}}.",
    "attachmentReason": {
      "tooLarge": "it is over the {{limit}} limit",
      "downloadFailed": "the download failed",
      "storeFailed": "it could not be saved",
      "empty": "it is empty"
    },
    "attachmentLimit": "At most {{max}} attachments go with one message, so {{name}} was left out. Send a message about the current ones first.",
    "attachmentsNoTeam": "Files cannot be sent to a team yet, so they were left out. Send /thread to go back to a normal session.",
    "unsupportedType": "I can't process {{type}} messages yet.",
    "unsupportedFolder": "Folders can't be downloaded. Zip it and send the archive instead.",
    "unsupportedVoice": "Voice messages aren't supported yet. Please send text.",
    "sendingFile": "Sending {{name}}:",
    "notAllowedYet": "You are not on the allow list yet. Your user id is {{id}}. Add it under Settings → Plugins → Chat, in this bot's \"Allowed people\" box.",
    "received": "Got it, working on it.",
    "inboundFailed": "This message couldn't be handled. Please send it again later.",
    "inboundDeferred": "No need to send this again — it will be handled automatically the next time the machine starts.",
    "welcome": "Connected. Send text or images to get started, or send /help to see the available commands."
  },
  "approval": {
    "timedOut": "No one handled this within the {{minutes}}-minute limit, so it was denied automatically.",
    "timedOutReason": "No one handled this within the {{minutes}}-minute limit.",
    "timedOutWithSubject": "Approval “{{subject}}”: no one handled it within the {{minutes}}-minute limit, so it was denied automatically.",
    "allow": "Allow",
    "deny": "Deny",
    "alreadyHandled": "Already handled",
    "replyHint": "Reply with the number, or reply approve/deny.",
    "replyHintFull": "Reply with the number. You can also reply approve/deny; for multi-select questions, comma-separate numbers."
  },
  "question": {
    "title": "❓ Question",
    "planTitle": "📝 Plan review",
    "planApprove": "Approve plan",
    "planApproveHint": "Let the agent start implementing this plan.",
    "planRevise": "Revise plan",
    "planReviseHint": "Ask the agent to rework the plan before going ahead.",
    "planStop": "Stop here",
    "planStopHint": "Leave plan mode without implementing this plan.",
    "freeformHint": "Or reply with a message to answer in your own words.",
    "replyOne": "Reply with the number.",
    "replyMany": "Reply with the numbers, separated by commas.",
    "submit": "✅ Submit",
    "pickFirst": "Pick at least one option first.",
    "answered": "Answer: {{answer}}",
    "answerWithAttachments": "{{answer}} ({{count}} attached)",
    "attachmentsHeld": "This question only takes the listed options, so {{names}} will be kept and sent with your next message.",
    "elsewhere": "Already answered elsewhere (the web app or another client).",
    "cancelled": "This question was closed without an answer.",
    "alreadyHandled": "Already handled",
    "sendFailed": "Could not hand your answer to the agent. The question may have been answered elsewhere or already closed."
  },
  "queue": {
    "title": "⏳ Queued",
    "waitRunning": "It goes out when the current reply finishes. To add it to the reply in progress now, tap Steer.",
    "waitPaused": "Sending is paused (the last reply was stopped or failed). Tap Resume sending to carry on.",
    "waitIdle": "It goes out shortly.",
    "steer": "➡️ Steer",
    "withdraw": "↩️ Withdraw",
    "resume": "▶️ Resume sending",
    "steeringTitle": "⏳ Steering…",
    "steering": "💬 Adding it to the current reply…",
    "sendingTitle": "📤 Sending…",
    "sending": "Sending it now.",
    "steeredTitle": "✅ Steered",
    "steered": "Added to the current reply.",
    "sentTitle": "📤 Sent",
    "sent": "It went out as its own turn.",
    "removedTitle": "🗑️ Withdrawn",
    "removed": "Withdrawn. It will not be sent.",
    "steerFailed": "Could not add it to the current reply (the reply may have just ended or changed). It stays queued and goes out when the reply finishes.",
    "withdrawFailed": "Could not withdraw it: it has already started sending.",
    "resumeFailed": "Could not resume sending. Try again in a moment.",
    "alreadyHandled": "Already handled",
    "plainNotice": "Queued: this message goes out when the current reply finishes."
  },
  "command": {
    "teamSelected": "OK. Each message now gives this project's team a goal. Send /thread to go back to a normal session.",
    "threadSelected": "OK. From now on messages start a normal session.",
    "newSession": "OK. The next message starts a new session.",
    "stopNoSession": "Nothing is running in this chat.",
    "stopNoTurn": "No turn is running right now.",
    "continued": "Resumed. The waiting messages are being sent.",
    "continueNothing": "Nothing to resume: sending is not paused in this chat.",
    "continueFailed": "Could not resume sending. Try again in a moment.",
    "stopped": "Stopped. The session is still there — just keep talking."
  },
  "status": {
    "routedTeam": "Sent to: this project's team",
    "routedThread": "Normal session",
    "nothingRunning": "{{where}}. Nothing is running.",
    "session": "Session: {{title}}",
    "running": "Running. Send /stop to stop it.",
    "idle": "Idle.",
    "model": "Model: {{model}}",
    "planOn": "Plan mode: on",
    "project": "Project: {{title}}",
    "noProject": "No project picked yet. Send /repo to pick one."
  },
  "version": {
    "current": "Ycodium Chat bridge v{{version}}",
    "usage": "Usage: /version (no arguments)."
  },
  "batch": {
    "usage": "Usage: /{{command}} (no arguments).",
    "noSubmission": "There is no batch to submit. Send /batch first.",
    "noBatch": "There is no batch input in progress.",
    "commandTextOnly": "Batch input commands take text only. Remove the images or files and send it again.",
    "started": "Batch input mode is on. Send up to {{limit}} text messages; each one is collected without a reply until you submit, and restarting Ycodium clears them.\nWhen finished, send /send; to cancel, send /cancel.",
    "progress": "Batch input mode is already on, with {{count}}/{{limit}} messages collected.\nWhen finished, send /send; to cancel, send /cancel.",
    "progressFull": "Batch input mode is already on, with {{count}}/{{limit}} messages collected; the batch is full.\nSend /send to submit or /cancel to cancel.",
    "submittingSend": "This batch is being submitted. Do not send /send again.",
    "submittingCancel": "The batch has already been submitted and cannot be cancelled.\nTo stop the current task, send /stop.",
    "submittingBatch": "This batch is being submitted. Wait until it finishes before starting another batch.",
    "cancelledEmpty": "Batch input cancelled.",
    "cancelled": "Batch input cancelled; {{count}} messages discarded.",
    "empty": "This batch has no content yet. Send text first, or use /cancel to cancel.",
    "introduction": "The user sent the following messages through batch input mode. Process them in order together as a single input.",
    "message": "[Message {{index}}]",
    "textOnly": "Batch input mode currently supports text only, not images or files. This message was not collected.\nKeep sending text, or use /send or /cancel.",
    "blockedCommand": "Batch input is in progress, so that command was not run.\nSend /send to submit, or /cancel to cancel (this discards the collected messages), then resend the command.",
    "full": "This batch is full. This message was not collected.\nSend /send to submit or /cancel to cancel first, then resend this message.",
    "reachedLimit": "Collected {{count}}/{{limit}} messages; the batch is full.\nSend /send to submit or /cancel to cancel.",
    "failed": "Could not submit the batch. All {{count}} messages were kept.\nSend /send again to retry or /cancel to cancel.",
    "noProject": "Pick a project with /repo before starting batch input.",
    "projectArchived": "Project “{{title}}” is archived, so nothing was submitted; the batch was kept.\nUnarchive it in the web app or another client, then send /send again.\nTo use another project, send /cancel (this discards the collected messages), pick one with /repo, then start again with /batch.",
    "threadArchived": "This chat's thread “{{title}}” is archived, so nothing was submitted; the batch was kept.\nUnarchive it in the web app or another client, then send /send again.\nTo start a new thread, send /cancel (this discards the collected messages), then /new, then start again with /batch.",
    "projectMissing": "This chat's project is no longer on this machine, so nothing was submitted; the batch was kept.\nTo use another project, send /cancel (this discards the collected messages), pick one with /repo, then start again with /batch.",
    "teamTarget": "Batch input is not available while this chat talks to a team. Send /thread to switch back, then /batch.",
    "privateOnly": "Batch input mode only works in private chats. Open a direct chat with the bot to use it.",
    "busy": "This chat has a running task, an unanswered question or a pending approval.\nFinish the current interaction or send /stop before using /batch."
  },
  "steer": {
    "usage": "Usage: /steer instruction.",
    "textOnly": "/steer takes text only. Remove the images or files and send it again.",
    "pending": "The current turn is waiting for your answer or approval, so the instruction was not sent. Handle that first, or send /stop, then send /steer again.",
    "noTurn": "Nothing is running, so the instruction was not sent. Send it as a normal message to start a new turn.",
    "sent": "Sent. If the current turn is still running, the agent reads it at its next step; if that turn just ended, it is handled as your next message."
  },
  "compact": {
    "usage": "Usage: /compact (no arguments).",
    "busy": "A reply is in progress. Wait until this turn finishes, then send /compact again.",
    "unsupported": "The current model can't compact context. Use /model to switch to one that can.",
    "started": "Started compacting the context. I'll post here when it finishes; you can keep sending messages meanwhile.",
    "done": "Context compacted.",
    "failed": "Couldn't compact the context. Try again in a moment.",
    "stuck": "The previous reply failed and its turn is still open. Send /stop to end that turn, then send /compact again.",
    "noSession": "This chat has no current session. Send a message to start one, or use /sessions and /resume to switch to one, then send /compact again."
  },
  "archived": {
    "on": "On: the session list includes archived sessions. They are listed so you can find them; open one on the web to read it. They can't be continued.",
    "off": "Off: the session list shows only sessions that are not archived.",
    "usage": "Usage: /archived on or /archived off."
  },
  "help": {
    "permission": "· /permission — list modes, the mode for new turns and the bot ceiling; /permission number or mode id selects; /permission default returns to the bot setting",
    "title": "Commands:",
    "continue": "· /continue — resume sending after /stop or a failure paused it; the waiting messages go out",
    "stop": "· /stop — stop the turn that is running; the session stays",
    "steer": "· /steer instruction — steer: add it to the turn that is running",
    "batch": "· /batch — collect up to {{limit}} text messages in a private chat",
    "send": "· /send — submit the collected messages as one input",
    "cancel": "· /cancel — cancel batch input and discard its messages",
    "compact": "· /compact — compact the current session's context (no arguments)",
    "status": "· /status — what is going on right now",
    "version": "· /version — the chat bridge's version (no arguments)",
    "new": "· /new — forget the current session; the next message starts a new one",
    "team": "· /team — give this project's team a goal with each message; use /repo to pick the project first",
    "thread": "· /thread — go back to a normal session",
    "aliases": "Chinese shortcuts (停 / 继续发送 / 压缩 / 状态 / 新对话 / 会话列表 / 切换会话 / 会话历史 / 换模型 / 思考强度 / 权限 / 计划模式 / 换项目 / 建群 / 换团队 / 换对话) work when the reply language is Chinese.",
    "sessions": "· /sessions — list this project's sessions; add a project number from /repo to list the ones this bot opened in another project",
    "archived": "· /archived on|off — include archived sessions in this chat's session list; /archived alone shows the current setting",
    "resume": "· /resume number-or-id-prefix — switch sessions; one from another project switches the project too",
    "history": "· /history [count] — preview recent messages in the current session, in private chats only (default {{defaultCount}}, maximum {{maxCount}})",
    "model": "· /model — see the model this chat uses; /model number or /model instance/model switches it",
    "reasoning": "· /reasoning — see the reasoning effort for this chat's current model; /reasoning value or /reasoning number switches it; /reasoning default resets it to the model default",
    "preset": "· /preset — see the preset this chat uses for new sessions; /preset number or /preset name-or-id switches it; /preset none clears the chat choice",
    "plan": "· /plan — toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it",
    "panel": "Send /panel to open the command panel with a button for every command.",
    "repo": "· /repo — pick the project this chat works on; /repo number or /repo name picks directly",
    "group": "· /group name — Lark only: the bot creates a group and adds you (bots on other platforms cannot)",
    "syncNote": "· After a private chat attaches a session, web user text and final assistant text sync here (groups never sync)."
  },
  "reasoning": {
    "current": "Current reasoning effort: {{value}}",
    "currentUnlisted": "Current reasoning effort: {{value}} (no longer in the list; it stays in effect until you change it)",
    "resetNow": "Reasoning effort reset to the model default. The current session uses it from the next turn; new sessions in this chat use it too.",
    "resetNext": "Reasoning effort reset to the model default. New sessions in this chat will use it.",
    "notFound": "\"{{query}}\" is not an available reasoning effort.",
    "numberOutOfRange": "Number {{number}} is out of range; choose 1–{{max}}.",
    "listTitle": "Available reasoning efforts (★ current):",
    "listTitleDefault": "Available reasoning efforts (none chosen; the model default is used):",
    "more": "{{count}} more not shown.",
    "usage": "Usage: /reasoning — see the reasoning effort for this chat's current model; /reasoning value or /reasoning number switches it; /reasoning default resets it to the model default.",
    "choices": "Choices: {{values}}.",
    "unsupported": "The current model does not support switching the reasoning effort.",
    "readFailed": "Could not read the reasoning options. Try again in a moment.",
    "setFailed": "Switching the reasoning effort failed. Try again in a moment.",
    "setNow": "Reasoning effort set to {{value}}. The current session uses it from the next turn; new sessions in this chat use it too.",
    "setNext": "Reasoning effort set to {{value}}. New sessions in this chat will use it.",
    "cleared": "The new model does not support the previous reasoning effort ({{value}}). It was cleared; the model default will be used.",
    "noModel": "This chat has no model yet. Pick one with /model first.",
    "switchReadFailed": "Could not read the new model's reasoning efforts, so the model was not switched. Try again in a moment.",
    "listTitlePlain": "Available reasoning efforts:"
  },
  "permission": {
    "current": "Mode for new turns in this chat: {{mode}}",
    "ceiling": "Bot ceiling: {{mode}}",
    "cappedChoice": "This chat's choice: {{mode}} (above the bot ceiling, not in effect)",
    "ceilingMarker": "bot ceiling",
    "unavailable": "unavailable",
    "usage": "Usage: /permission to list modes; /permission number or mode id to select; /permission default to use the bot setting.",
    "nextTurn": "Applies from the next turn in this chat; a running turn keeps its mode.",
    "notAllowed": "Only people on the bot's Allowed people list can change the mode. You can still send /permission to view the modes; ask the bot's admin to change it.",
    "exceedsCeiling": "This mode exceeds the bot ceiling ({{mode}}). The chat setting was not changed. Send /permission to see the modes you can pick.",
    "selected": "Mode for this chat set to: {{mode}}",
    "reset": "Mode choice for this chat cleared; using the bot setting: {{mode}}",
    "saveFailed": "Could not save the mode for this chat. The chat setting was not changed; try again.",
    "alreadyDefault": "This chat has no mode of its own; it follows the bot setting: {{mode}}",
    "numberOutOfRange": "Number {{number}} is out of range; the list is numbered 1–{{max}}.",
    "notFound": "\"{{query}}\" is not a mode this bot knows.",
    "savedChoice": "This chat's choice: {{mode}}",
    "following": "This chat has no mode of its own; it follows the bot setting.",
    "readOnly": "You're not on the bot's Allowed people list, so you can only view the modes; ask the bot's admin to change them.",
    "readOnlyUnknown": "Your identity couldn't be confirmed, so you can only view the modes; ask the bot's admin to change them.",
    "notAllowedUnknown": "Your identity couldn't be confirmed, so you can't change the mode. You can still send /permission to view the modes; ask the bot's admin to change it."
  },
  "history": {
    "usage": "Usage: /history [count] — 1 to {{maxCount}} (larger numbers count as {{maxCount}}), default {{defaultCount}}.",
    "directOnly": "In a group this would show the session to everyone. Send /history in a private chat with the bot.",
    "noSession": "This chat has no current session. Send a message to start one, or use /sessions to find a session and /resume to switch to it.",
    "empty": "No messages to show in this session yet.",
    "noneRecent": "Nothing to show among this session's most recent messages (older ones were not checked).",
    "failed": "Could not read the recent messages. Try again in a moment.",
    "title": "Recent messages in this session ({{count}}):",
    "user": "User",
    "assistant": "Assistant",
    "image": "[Image]",
    "file": "[File]",
    "noText": "[No previewable text]",
    "truncated": "[Remaining text omitted]"
  },
  "sessions": {
    "title": "This project's sessions ({{count}}):",
    "empty": "This bot has not opened any session in this project yet. Send a message to start one.",
    "more": "{{count}} more not shown — reach them with /resume and the start of an id.",
    "footer": "Switch with /resume number, or /resume and the start of an id.",
    "untitled": "Untitled",
    "unavailable": "Could not read the session list right now. Try again in a moment.",
    "projectNotFound": "That project number does not exist. Send /repo to see the numbers.",
    "projectNumberExpected": "Put a project number after /sessions (a number from /repo), for example /sessions 2.",
    "project": "Project: \"{{title}}\"",
    "otherProjectTitle": "Sessions this bot opened in project \"{{project}}\" ({{count}}):",
    "otherProjectFooter": "You are still in this chat's current project. /resume number, or /resume with the start of an id (also an unlisted one this bot opened), switches there and switches the chat's project too.",
    "projectEmpty": "This bot has not opened any session in that project yet. To start one there, send /repo with that project number to move this chat there, then send a message.",
    "badge": {
      "current": "★ current",
      "running": "● running",
      "elsewhere": "in another chat",
      "web": "web",
      "archived": "archived"
    },
    "age": {
      "justNow": "just now",
      "minutes": "{{count}} min ago",
      "hours": "{{count}} h ago",
      "days": "{{count}} d ago"
    }
  },
  "resume": {
    "notFound": "No session matches \"{{query}}\". Use /sessions to see the list.",
    "ambiguous": "More than one session starts with \"{{query}}\". Give a few more characters of the id.",
    "busy": "A turn is running in this chat — send /stop first, then switch.",
    "already": "This chat is already on that session.",
    "gone": "That session was deleted or archived, so it cannot be resumed.",
    "archived": "That session is archived and can't be continued. To just read it, open it on the web; to continue it, unarchive it on the web or in another client first, then switch to it, or send /new to start a new one.",
    "runningElsewhere": "That session is running a turn in another chat — stop it there first.",
    "done": "Switched to \"{{title}}\". Send a message to keep going from there.",
    "doneInProject": "Switched to project \"{{project}}\", session \"{{title}}\". Send a message to keep going from there.",
    "switchFailed": "Could not switch to that session. The chat's project and session have not changed; send that /resume again in a moment.",
    "confirmWeb": "\"{{title}}\" was opened on the web. Send /resume with the same number again (or /resume confirm) to attach; /resume cancel to abort.",
    "cancelled": "Attach cancelled.",
    "nothingPending": "No web session is waiting for confirm. Use /sessions to see the list."
  },
  "preset": {
    "current": "This chat's preset for new sessions: {{preset}}",
    "botDefault": "Bot default preset for this chat's new sessions: {{preset}}",
    "none": "No preset for this chat's new sessions.",
    "unavailable": "{{value}} (unavailable)",
    "item": "{{label}} ({{id}})",
    "listTitle": "Available presets:",
    "more": "{{count}} more not shown — pick one with /preset name-or-id.",
    "empty": "No presets are available.",
    "footer": "Switch with /preset number, or /preset name-or-id. Clear the chat choice with /preset none to use the bot default.",
    "existing": "Existing sessions keep their preset. Send /new, then a message to use the new setting.",
    "updated": "This chat's preset for new sessions set to: {{preset}}",
    "cleared": "Chat preset cleared. New sessions will use the bot default, if one is set.",
    "notFound": "No single preset matches \"{{query}}\". Use /preset to see the list.",
    "readFailed": "Could not read the preset list. Try again later.",
    "textOnly": "Preset commands only accept text. Remove attachments and try again."
  },
  "model": {
    "current": "Model: {{model}}",
    "currentUnknown": "Model: not decided yet — a new session uses the project's default.",
    "unavailable": "The available models cannot be read right now, so the model cannot be switched.",
    "listTitle": "Available models:",
    "more": "{{count}} more not shown — pick one with /model instance/model.",
    "footer": "Switch with /model number, or /model instance/model.",
    "notFound": "No single model matches \"{{query}}\". Use /model to see the list.",
    "setNow": "Model set to {{model}}. The current session uses it from its next turn.",
    "setNext": "Model set to {{model}}. It applies to the next new session.",
    "setFailed": "Could not switch the current session's model. Try again in a moment."
  },
  "plan": {
    "on": "Plan mode is on: from the next message the agent proposes a plan instead of acting. Send /plan off to leave it.",
    "off": "Plan mode is off. Messages are handled normally again.",
    "alreadyOn": "Plan mode is already on.",
    "alreadyOff": "Plan mode is already off.",
    "usage": "Usage: /plan toggles plan mode; /plan on and /plan off set it."
  },
  "card": {
    "untitled": "Chat",
    "status": {
      "working": "Working on it…",
      "done": "Done",
      "error": "Something went wrong",
      "interrupted": "Interrupted",
      "continued": "Continues in the card below"
    },
    "thinking": "Thinking…",
    "usingTool": "Using {{name}}…",
    "step": {
      "think": "think",
      "command": "command",
      "file": "edit",
      "mcp": "mcp",
      "tool": "tool",
      "agent": "agent",
      "web": "search",
      "image": "image",
      "omitted": "{{count}} earlier steps"
    },
    "truncated": "… ({{count}} earlier characters not shown)",
    "approval": {
      "doneTitle": "Handled",
      "timedOutTitle": "Denied automatically",
      "originalLabel": "Original approval:",
      "chosen": "Chosen: {{label}}",
      "elsewhere": "Already handled elsewhere (the web app or another client)."
    }
  },
  "panel": {
    "title": "Command panel",
    "hint": "A button does the same as typing its command. You can still type.",
    "wecomTruncated": "Some commands do not fit on this card. Send /help for the full list, then type the command; in a group, mention the bot first.",
    "wecomExpired": "This panel has expired. Send /panel again; in a group, mention the bot first.",
    "category": {
      "session": "Session",
      "target": "Send to",
      "system": "Settings and status"
    },
    "cmd": {
      "new": "New session",
      "continue": "Resume sending",
      "stop": "Stop this turn",
      "sessions": "Sessions",
      "team": "Send to a team",
      "thread": "Normal session",
      "model": "Model",
      "planOn": "Plan mode: on",
      "planOff": "Plan mode: off",
      "status": "Status",
      "help": "Text help",
      "repo": "Pick project"
    },
    "back": "Back",
    "prev": "Previous",
    "next": "Next",
    "page": "Page {{page}} of {{total}}",
    "loading": "Loading…",
    "working": "Working on it…",
    "expired": "This panel has expired. Send /panel again.",
    "busy": "The last tap is still being handled. Give it a moment.",
    "loadFailed": "Could not read that just now. Try again in a moment.",
    "sessions": {
      "title": "Pick a session",
      "hint": "Tap one to switch to it."
    },
    "models": {
      "title": "Pick a model"
    },
    "projects": {
      "title": "Pick a project",
      "hint": "Tap a project and this chat works on it.",
      "pickedTitle": "Project picked",
      "picked": "{{title}}\nSend /repo to change it."
    }
  },
  "wecomSetup": {
    "action": {
      "label": "Scan to create a WeCom smart bot",
      "description": "Scan with WeCom to create a smart bot. Ycodium saves its Bot ID and Secret in the bot list on this machine."
    },
    "status": {
      "waiting": "Scan the QR code with WeCom, then confirm in WeCom to create the smart bot."
    },
    "done": "The WeCom smart bot was created and saved. Message it in WeCom to try it, then pick a project in the chat.",
    "doneNeedsAllowList": "The WeCom smart bot was created and saved. Three steps to start using it:",
    "nextSteps": "1. Message it in WeCom; it replies with your WeCom user id (the QR result doesn't include it).\n2. Put that id in “Allowed people” on the bot's row and save.\n3. Pick a project in the chat.",
    "failure": {
      "expired": "This QR code expired before the bot was created. Click “Try again” to get a new one.",
      "failed": "Could not create the WeCom smart bot. Click “Try again” to start over.",
      "saveFailed": "The smart bot was created, but it could not be saved on this machine. Find the bot in WeCom, copy its Bot ID and Secret, and add it with “Add a bot”. If WeCom doesn't show the Secret, click “Try again” to create a new bot instead; the bot from this attempt stays in WeCom, and you can delete it there."
    }
  },
  "setup": {
    "action": {
      "label": "Connect a Lark bot",
      "description": "Scan once with the Lark app. Ycodium creates a bot app under your account, publishes it for you only, and adds the bot to the list with its credentials saved. Nothing to fill in.",
      "badge": "Beta",
      "warning": "Lark QR setup relies on Lark developer console interfaces that are not public, so it may stop working. If it does, set the app up by hand in the developer console."
    },
    "manual": {
      "createApp": {
        "title": "Create the app",
        "body": "Open the Feishu developer console and create a self-built app for your company. Copy its App ID and App secret from Credentials & Basic Info."
      },
      "enableBot": {
        "title": "Turn on the bot",
        "body": "In App Features, add the Bot capability."
      },
      "longConnection": {
        "title": "Use the long connection for events and card callbacks",
        "body": "In Events & Callbacks, set both the event subscription and the callback subscription to Long connection, then subscribe to the event and the card callback listed below. Setting only the events leaves every card button dead."
      },
      "scopes": {
        "title": "Add the permissions",
        "body": "In Permissions, add every permission listed below. A missing one only breaks the feature that needs it."
      },
      "publish": {
        "title": "Publish a version",
        "body": "Create a version and publish it. Choose \"visible to me only\" so it is approved at once, without waiting for an administrator."
      },
      "fillRow": {
        "title": "Add the bot",
        "body": "Under Chat in Settings, add a bot: choose Lark, paste the App ID and App secret, put your own Lark user id in Allowed people (message the bot and it tells you your id), then save."
      }
    },
    "status": {
      "waiting": "Waiting for the scan",
      "scanned": "Scanned. Confirm on your phone.",
      "expired": "The code expired"
    },
    "step": {
      "login": "Sign in with the QR code",
      "identity": "Read your account",
      "findApp": "Look for an app from an earlier try",
      "createApp": "Create the app",
      "enableBot": "Turn on the bot",
      "longConnection": "Switch events and callbacks to the long connection",
      "scopes": "Add permissions",
      "events": "Subscribe to events",
      "callbacks": "Subscribe to card callbacks",
      "publish": "Publish a version",
      "secret": "Read the App secret"
    },
    "progress": {
      "login": "Signing in ({{n}}/{{total}})",
      "identity": "Reading your account ({{n}}/{{total}})",
      "findApp": "Looking for an app from an earlier try ({{n}}/{{total}})",
      "createApp": "Creating the app ({{n}}/{{total}})",
      "enableBot": "Turning on the bot ({{n}}/{{total}})",
      "longConnection": "Switching events and callbacks to the long connection ({{n}}/{{total}})",
      "scopes": "Adding permissions ({{n}}/{{total}})",
      "events": "Subscribing to events ({{n}}/{{total}})",
      "callbacks": "Subscribing to card callbacks ({{n}}/{{total}})",
      "publish": "Publishing a version ({{n}}/{{total}})",
      "secret": "Reading the App secret ({{n}}/{{total}})"
    },
    "done": "Connected and saved. Message the bot in Lark to try it, then pick a project in the chat.",
    "warn": {
      "senderUnknown": "Could not read your Lark user id, so this bot's Allowed people list is empty and nobody can use it yet. Message the bot: it replies with your id. Put that id in Allowed people on the bot's row and save.",
      "scopesMissing": "These permissions could not be added automatically: {{names}}. Add them in the developer console."
    },
    "failure": {
      "qrExpired": "The code expired before it was scanned. Show a new code and scan again.",
      "scanTimeout": "Nobody scanned the code in time. Show a new code and try again.",
      "network": "Could not reach the Feishu developer console. Check this machine's network or proxy, then try again.",
      "timeout": "The developer console took too long to answer. Try again, or follow the manual steps.",
      "sessionInvalid": "The developer console did not accept this sign-in. Scan again.",
      "consoleRejected": "The developer console turned the request down. Follow the manual steps, or try again later.",
      "ownerDenied": "This account is not the owner of that app, so it cannot change it. Scan with the account that created it, or follow the manual steps.",
      "verificationFailed": "The console did not keep the event settings, so the bot would receive nothing. Nothing was published. Follow the manual steps.",
      "publishNeedsManual": "The app is set up but its version could not be published. Publish it in the developer console, then add the bot here by hand with its App ID and App secret.",
      "busy": "Another setup is already running on this machine.",
      "unknown": "Setup stopped on an unexpected error. Try again, or follow the manual steps.",
      "createUnknown": "The console did not confirm whether the app was created. Try again: an app made by an earlier try is found and reused.",
      "secretMissing": "The app {{appId}} is published, but its App secret could not be read. Copy it from Credentials & Basic Info in the developer console.",
      "stopped": "Setup was stopped.",
      "saveFailed": "The app {{appId}} is set up and published, but the bot could not be saved here. Add it by hand: choose Lark, paste the App ID and the App secret from Credentials & Basic Info, and fill in Allowed people."
    }
  },
  "repo": {
    "autoSelected": "Using project {{title}}.",
    "needProject": "This chat has no project yet, so I won't start any work. Pick one and I will.",
    "currentLine": "This chat works on project \"{{title}}\".",
    "noProjects": "There are no projects on this machine yet. Add one in Ycodium first.",
    "more": "{{count}} more not shown — pick one with /repo and a project name or the start of it.",
    "footer": "Reply /repo number to pick, or /repo and a project name.",
    "done": "Connected to project \"{{title}}\". The next message starts a new session there.",
    "already": "This chat is already on project \"{{title}}\".",
    "notFound": "No single project matches \"{{query}}\". Send /repo to see the list.",
    "busy": "A turn is running in this chat — send /stop first, then switch.",
    "gone": "That project was archived or deleted, so it cannot be picked."
  },
  "group": {
    "usage": "Usage: /group name — the bot creates a group with that name and adds you to it.",
    "created": "Created the group \"{{name}}\" and added you. Open it to pick a project.",
    "unsupported": "Bots on this platform cannot create groups. Create the group in the app and add the bot to it.",
    "noSender": "I cannot tell who you are on this platform, so I cannot add you to a new group.",
    "failed": "Could not create the group. Check that the app is allowed to create groups and add members, then try again."
  },
  "sync": {
    "webUser": "Web: {{text}}"
  },
  "rowAction": {
    "checkConnection": {
      "label": "Check connection",
      "description": "Verify credentials and send a test message to the most recent private chat; does not open a session or call a model.",
      "running": "Checking {{rowId}}…",
      "done": "Connection OK",
      "doneTestFailed": "Credentials OK, but the test message was not sent",
      "doneNoTarget": "Credentials OK, but no test message was sent",
      "failed": "Connection failed",
      "notSaved": "Save this row first (credentials are read from saved config only).",
      "credentialRef": "Credential check reference: {{ref}}\n{{detail}}",
      "testMessage": {
        "sent": "Sent to whoever last messaged the bot privately. Ask them to confirm.",
        "test-target-unavailable": "No private chat to test with yet. Message the bot privately from an account allowed to use the bot, then check again.",
        "test-message-failed": "Check that the bot can still send to that private chat (reason below), then check again.",
        "ref": "Test message reference: {{ref}}\n{{detail}}"
      },
      "hint": {
        "networkMultipleCauses": "This failure has more than one network cause. Check this machine's network and this bot's Proxy setting, then check again.",
        "networkDns": "The service's domain name could not be resolved. Check this machine's DNS and network settings, then check again.",
        "networkRefused": "The connection was refused. Check whether a firewall or security software on this machine or its network is blocking it, then check again.",
        "networkReset": "The connection was cut off by the other side. Check this machine's network and whether the target service is running normally, then check again.",
        "networkUnreachable": "The target network is unreachable. Check this machine's outbound network access and access restrictions, then check again.",
        "networkTls": "TLS or certificate validation failed. Check this machine's system clock, certificate chain and network settings, and whether a proxy or security software is intercepting HTTPS, then check again.",
        "networkTimeout": "The request timed out. Check whether the target service is running normally and check this machine's network, then check again.",
        "networkProxy": "Proxy connection or authentication failed. Check this row's Proxy setting and the selected proxy's configuration on this machine; if no proxy is set, check other proxies on this machine or its network, then check again.",
        "platformRateLimited": "The platform is rate limiting requests. Try again later.",
        "platformUnavailable": "The target service is temporarily having problems. Try again later.",
        "connecting": "Still connecting. There is usually a result within 20 seconds; check again in a moment.",
        "badToken": "This does not look like a bot token. Copy it again from BotFather: digits, a colon, then the key.",
        "unauthorized": "Telegram does not accept this token. It may have been revoked; get the current one from BotFather. Save the corrected token here to reconnect automatically; if nothing needs changing, use Reconnect below.",
        "notBot": "This token does not belong to a bot. Use the token BotFather gave you.",
        "webhookActive": "This bot has a webhook set by another service, so it cannot receive messages here. Remove the webhook there, then check again.",
        "larkCredentials": "Lark does not accept this App ID or App Secret. Copy both from Credentials & Basic Info for your self-built app in the Lark developer console. Save the corrected App ID and App Secret here to reconnect automatically; if nothing needs changing, use Reconnect below.",
        "larkScope": "This app is missing a required permission. In the Lark developer console, open Permission Management, add the permissions listed in the details, then publish a version.",
        "larkBotOff": "This app's bot capability is not enabled. In the Lark developer console, open App Capabilities → Bot, enable the bot, then publish a version.",
        "larkAppUnavailable": "This app is unavailable in your organization. In the Lark developer console, check that the app is enabled and published; ask your administrator to confirm it is installed and enabled for your organization.",
        "wecomBotCredentials": "WeCom does not accept this Bot ID or Secret. Open this smart bot's configuration in the WeCom admin console, select API mode → Long connection, and copy its Bot ID and Secret. These are not the callback Token or EncodingAESKey. Save the corrected Bot ID and Secret here to reconnect automatically; if nothing needs changing, use Reconnect below.",
        "wecomLongConnOff": "This smart bot's long connection mode is not enabled. Open its configuration in the WeCom admin console, select API mode → Long connection and save; the bot reconnects on its own within a minute, or use Reconnect below.",
        "wecomTakenOver": "Another machine or program is using the same Bot ID. Stop using this smart bot there, then use Reconnect below.",
        "wecomTrustedIp": "Add this machine's outbound public IP to the app's trusted IP list in the WeCom admin console.",
        "wecomInvalidUser": "Check that the recipient, department or tag exists and is in this app's visible range.",
        "wecomInvalidAgent": "Check the AgentId against this self-built application in the WeCom admin console.",
        "wecomCallbackNotSeen": "Credentials are valid, but no verified callback has arrived since this startup. In the app's Receive messages settings in the WeCom admin console, paste the callback URL shown on this bot's settings card and save; WeCom will send a verification request. This machine must be reachable by WeCom over the public internet.",
        "wecomCallbackSignature": "A callback arrived, but its signature or decryption failed; no verified callback has arrived since this startup. Match the Callback Token and EncodingAESKey to this app's Receive messages settings in the WeCom admin console, then save there to verify again."
      }
    },
    "reconnect": {
      "label": "Reconnect",
      "description": "Disconnect and reconnect this bot using its saved settings.",
      "notAvailable": "This platform can't be connected yet, so reconnecting won't help.",
      "running": "Starting to reconnect {{rowId}}…",
      "done": "Reconnection started. Check the bot's connection status for the result.",
      "notSaved": "Save this row first before reconnecting. Only saved settings are used.",
      "notConfigured": "Complete and save this bot's settings before reconnecting.",
      "duplicate": "Another bot row uses these credentials. Resolve the duplicate before reconnecting.",
      "inactive": "The chat bridge is turned off. Enable it before reconnecting."
    },
    "resetInbox": {
      "label": "Reset inbox",
      "confirm": "Reset inbox",
      "description": "Reset only this bot's inbox and reconnect using its saved settings. Its unprocessed messages will be lost and duplicate-delivery records cleared.",
      "telegramOffline": "Telegram will skip messages sent while the machine was offline.",
      "alreadyReadable": "This bot's inbox can be read. No reset was performed.",
      "running": "Resetting the inbox for {{rowId}}…",
      "done": "Inbox reset and reconnection started; check the bot's connection status. Its unprocessed messages have been lost and duplicate-delivery records cleared.",
      "notSaved": "Save this bot before resetting its inbox.",
      "failed": "Could not reset this bot's inbox. It remains stopped; check that the machine can read and write its saved data, then try again.",
      "reconnectFailed": "Inbox reset, but reconnection could not start. Its unprocessed messages have been lost and duplicate-delivery records cleared."
    },
    "copyDiagnostics": {
      "label": "Copy diagnostics",
      "description": "Run a connection probe and produce copy-paste diagnostics (no secrets).",
      "running": "Building diagnostics for {{rowId}}…",
      "done": "Diagnostic report ready — see details below",
      "notSaved": "Save this row first before copying diagnostics."
    },
    "diagnostic": {
      "ref": "Reference: {{ref}}\n{{detail}}"
    },
    "larkCallbackProbe": {
      "label": "Verify card buttons",
      "description": "Send a private test card to the first person in this bot's allowlist (usually you) and wait up to {{seconds}} seconds for your click.",
      "step": "Send a private test card and wait for the click",
      "waiting": "Test card sent privately to the first person in the allowlist ({{id}}, usually you). Click its button in Lark within {{seconds}} seconds.",
      "notConnected": "Skipped: this bot is not connected on this machine. Use “Reconnect” on this row to connect it, then verify again.",
      "noRecipient": "Skipped: nobody to send the test card to. Set this bot up with “Connect a Lark bot” (the QR scan) first; it adds you to the allowlist for you.",
      "sendFailed": "Could not send the test card. Use “Repair scopes” on this row to check the messaging permissions, make sure the bot is connected, then verify again.",
      "timeout": "No test-card callback arrived within {{seconds}} seconds. If you didn't click the card in time, just click “Verify card buttons” again. If you did, open Events & Callbacks in the developer console, set callback subscriptions to Long connection, and add {{callbacks}}, then verify again.\nDeveloper console URL:\n{{url}}",
      "toast": "Click received. Card buttons are working.",
      "staleToast": "This test card's check has ended; the result is in the settings panel and your private message. To check again, click “Verify card buttons” in settings.",
      "notYoursToast": "This test card was sent to the first person in the bot's allowlist; your click doesn't count toward the check. You can ignore it.",
      "verified": "Received card.action.trigger from the test card. Card buttons are working.",
      "aborted": "Verification was interrupted because the bot disconnected. Wait for it to reconnect, or use “Reconnect” on this row, then verify again.",
      "card": {
        "title": "🧪 Verify card buttons",
        "body": "Click the button below within {{seconds}} seconds. Verification passes only after the machine running this bot confirms it received your click.",
        "button": "Complete verification"
      },
      "notice": {
        "verified": "✅ Verification passed: your click on the test card arrived as card.action.trigger. Card buttons are working.",
        "timeout": "⚠️ Verification timed out: no card.action.trigger arrived from the test card, so buttons are still unverified. If you didn't click in time, click “Verify card buttons” again. If you did, open Events & Callbacks in the developer console, set callback subscriptions to Long connection, add card.action.trigger, then click “Verify card buttons” again.\nDeveloper console URL:\n{{url}}",
        "sendFailed": "⚠️ Verification failed: the test card could not be sent, so card.action.trigger is still unverified. Use “Repair scopes” on the bot's row in settings to check messaging permissions, then click “Verify card buttons” again.",
        "aborted": "⚠️ Verification interrupted: the bot disconnected before card.action.trigger was confirmed. Wait for the bot to reconnect, or use “Reconnect” on its row in settings, then click “Verify card buttons” again."
      }
    },
    "larkRepair": {
      "label": "Repair scopes",
      "description": "List missing required scopes and a link to request them on the Feishu open platform; an app administrator then publishes the app version manually.",
      "step": {
        "token": "Get a tenant token with the saved credentials",
        "scopes": "Check the app's granted tenant scopes"
      },
      "missing": "Some required scopes are missing.",
      "allGranted": "All required scopes are enabled.",
      "unverified": "Unable to check scopes automatically.",
      "missingScopes": "Missing scopes:\n{{scopes}}",
      "requiredScopes": "Required scopes — check each one manually:\n{{scopes}}\n\nApps without application:application:self_manage cannot report their granted scopes, so this list could not be checked automatically.",
      "openApplyLink": "Request scopes on the Feishu open platform",
      "openCheckLink": "Check and request scopes on the Feishu open platform",
      "applyLink": "Open platform URL:\n{{url}}",
      "publishHint": "As an app administrator, select the scopes at that link. Then publish a version via App release → Version management & release → Create version, and return here to click “Check connection”.",
      "botHint": "To enable the bot, go to App → Add app capability → Bot. Bot capability is not checked here.",
      "eventHint": "To receive messages, set both subscriptions in Events & Callbacks to Long connection, then add these (not checked here):\nEvents: {{events}}\nCallbacks: {{callbacks}}",
      "failed": "Could not check the app's required scopes. Review the error details and try again.",
      "notLark": "This row is not a saved Lark bot."
    }
  },
  "menu": {
    "new": "Start a new session with the next message",
    "stop": "Stop the turn that is running",
    "continue": "Resume sending after a stop or a failure",
    "status": "What is going on right now",
    "sessions": "List the sessions for this chat's project",
    "resume": "Switch to a session (number from /sessions)",
    "model": "See or switch the model",
    "plan": "Toggle plan mode",
    "repo": "Pick the project this chat works on",
    "team": "Give this project's team a goal with each message",
    "thread": "Go back to a normal session",
    "panel": "Open the command panel",
    "help": "Show all commands"
  },
  "settingsSection": {
    "lastMessageError": {
      "title": "Last message failed",
      "separator": ": ",
      "code": "Error code {{code}}",
      "reference": "Reference {{referenceId}}"
    },
    "empty": "No bots yet",
    "emptyGuide": "Use \"{{larkLabel}}\" or \"{{wecomLabel}}\" to scan and create a bot in one step. For other platforms, choose \"{{addBotLabel}}\" and enter the credentials from that platform's developer console.",
    "unavailable": "This machine doesn't have the chat bridge right now.",
    "loadFailed": "Couldn't load the chat-bridge settings.",
    "retry": "Try again",
    "general": "General",
    "addBot": "Add a bot",
    "moreSettings": "More settings",
    "moreSettingsIssues": "More settings ({{count}} to fix)",
    "save": "Save",
    "revert": "Undo changes",
    "saveFailed": "Couldn't save.",
    "remove": "Delete",
    "removeConfirm": "Delete this bot",
    "cancel": "Cancel",
    "removeHint": "Deleting this bot drops the bindings for every chat on it.",
    "copy": "Copy",
    "copied": "Copied.",
    "copyFailed": "Couldn't copy that.",
    "removeFailed": "Couldn't delete this bot.",
    "status": {
      "ready": "Ready",
      "incomplete": "Incomplete",
      "connecting": "Connecting",
      "connected": "Connected",
      "notConnected": "Not connected"
    },
    "online": "{{connected}} / {{configured}} online",
    "onlineLabel": "{{configured}} configured bots, {{connected}} online",
    "connection": {
      "since": "Since {{time}}",
      "failed": "The last connection attempt failed.",
      "details": "Details: {{error}}",
      "reconnect": "You can try again with Reconnect below.",
      "reason": {
        "inbox-unreadable": "This bot's inbox cannot be read, so it has stopped receiving messages. Use “Reset inbox” on this card to recover.",
        "not-available": "This platform can't be connected yet.",
        "invalid-app-id": "The App ID isn't in the right format.",
        "disconnected": "The connection to the platform is down.",
        "subscription-failed": "The platform didn't confirm the subscription.",
        "taken-over": "This connection was taken over elsewhere, so automatic reconnection has stopped here.",
        "token-unavailable": "Couldn't get an access token.",
        "credentials-rejected": "The platform rejected the credentials."
      },
      "duplicate": "Uses the same credentials as “{{name}}”. Only that row connects."
    },
    "secret": {
      "saved": "Saved. Leave blank to keep it.",
      "clear": "Clear saved value",
      "clearUndo": "Undo clear"
    },
    "issue": {
      "required": "Fill this in.",
      "format": "This format isn't right."
    },
    "publicField": {
      "callbackUrl": "Callback URL",
      "callbackPath": "Callback path"
    },
    "action": {
      "running": "Working…",
      "done": "Done.",
      "close": "Close",
      "failedToRun": "Couldn't run this on the machine.",
      "saveFirst": "Save or undo your changes first. These actions use the saved settings.",
      "qrTitle": "QR code to scan with the Lark app"
    },
    "step": {
      "pending": "Waiting",
      "running": "In progress",
      "done": "Done",
      "skipped": "Skipped",
      "failed": "Failed"
    },
    "select": {
      "default": "Default"
    },
    "target": {
      "description": "Places a script or another tool can send a message to through a bot. The caller names the bot and the target id; the chat id on the platform stays here.",
      "add": "Add a target",
      "untitled": "New target",
      "removeConfirm": "Delete this target",
      "removeFailed": "Couldn't delete this target."
    },
    "publicFieldHint": {
      "callbackNeedsKeys": "Fill in Token and EncodingAESKey and save; the callback URL is derived from them and appears here.",
      "callbackPathNeedsBase": "Put the public address that forwards to this machine in front of this path, or fill in the public callback base URL above to get the full URL.",
      "callbackFollowsKeys": "This URL changes when Token or EncodingAESKey changes. Paste it into WeCom again after changing them."
    }
  },
  "mergeForward": {
    "title": "[Forwarded chat history, messages: {{count}}]",
    "nested": "[Nested forward, messages: {{count}}]",
    "someone": "Someone",
    "bot": "A bot",
    "unknownTime": "[unknown time]",
    "resource": {
      "image": "Image",
      "file": "File",
      "audio": "Audio",
      "video": "Video",
      "folder": "Folder"
    },
    "unavailable": "[{{type}}: could not be retrieved]",
    "unavailableNamed": "[{{type}} {{name}}: could not be retrieved]",
    "omitted": "… not shown: {{count}} more",
    "truncated": "… [message truncated]"
  },
  "team": {
    "noRoster": "This project has no team roster yet. Save a roster in the Teams tab and choose a lead first.",
    "unavailable": "Could not read this project's team roster. Check that the Teams plugin is enabled and try again.",
    "invalidGoal": "The goal is empty or too long. Edit your message and send it again.",
    "startFailed": "Could not give this goal to the team lead. Try sending the message again.",
    "startedUnnamed": "Handed to the team lead. Goal: {{goal}}",
    "started": "Given to team lead {{name}}. Goal: {{goal}}"
  },
  "annotation": {
    "channel": {
      "wechat": "WeChat",
      "chat": "Chat app"
    }
  }
}
