fix(agent-manager): correct activity indicators after recovery

This commit is contained in:
marius-kilocode
2026-08-28 13:10:34 +02:00
parent b92a36f60b
commit 67b299f66e
27 changed files with 311 additions and 63 deletions
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Clear stale failures when an agent resumes, keep optional review suggestions from triggering attention, and explain Agent Manager status icons on hover. Keep progress spinners separate from attention warnings during worktree operations.
@@ -32,7 +32,8 @@ Object.assign(globalThis, {
})
const { render } = await import("solid-js/web")
const { For } = await import("solid-js")
const { For, createSignal } = await import("solid-js")
const { WorktreeItem } = await import("../../webview-ui/agent-manager/WorktreeItem")
const { DragDropProvider, SortableProvider } = await import("@thisbeyond/solid-dnd")
const { renderTab } = await import("../../webview-ui/agent-manager/tab-rendering")
const { VSCodeProvider } = await import("../../webview-ui/src/context/vscode")
@@ -81,6 +82,8 @@ const language = {
}
const ref = { value: undefined as ReturnType<typeof useSession> | undefined }
const [operation, setOperation] = createSignal(false)
const [run, setRun] = createSignal(false)
const Probe = () => {
const session = useSession()
ref.value = session
@@ -105,6 +108,40 @@ const Probe = () => {
<SortableProvider ids={ids}>
<For each={ids}>{(id) => renderTab(id, deps)}</For>
</SortableProvider>
<WorktreeItem
worktree={{
id: "worktree",
path: "/test/worktree",
branch: "test",
parentBranch: "main",
createdAt: "2026-01-01T00:00:00.000Z",
}}
label="Recovery test"
active
pendingDelete={false}
busy={operation()}
activity={session.activityFor("root")}
runStatus={run() ? { worktreeId: "worktree", state: "running" } : undefined}
stale={false}
sessions={1}
grouped={false}
groupStart={false}
groupEnd={false}
groupSize={0}
renaming={false}
renameValue=""
closeKeybind=""
openKeybind=""
onClick={() => {}}
onDelete={() => {}}
onStartRename={() => {}}
onRenameInput={() => {}}
onCommitRename={() => {}}
onCancelRename={() => {}}
onRemoveStale={() => {}}
onCopyPath={() => {}}
onOpen={() => {}}
/>
</DragDropProvider>
)
}
@@ -145,12 +182,19 @@ const state = (id: string) => {
assert(value)
return value.activityFor(id)
}
const card = (expected: string) => {
const icon = host.querySelector('[data-sidebar-id="worktree"] .am-wt-icon')
assert(icon)
assert.equal(icon.getAttribute("data-activity"), expected)
assert.equal(!!icon.querySelector('[data-component="spinner"]'), expected === "busy" || expected === "retry")
}
const check = async (id: string, expected: string) => {
await settle()
step.value += 1
const value = ref.value
assert(value)
const actual = state(id)
if (id === "root") card(expected)
const tab = host.querySelector(`[data-tab-id="${id}"] [data-activity]`)
if (id === "root" || id === "background") assert(tab, `Missing rendered tab for ${id}`)
if (tab && tab.getAttribute("data-activity") !== expected) {
@@ -199,6 +243,14 @@ try {
value.setCurrentSessionID("root")
await check("root", "idle")
await check("background", "idle")
for (const update of [setOperation, setRun]) {
update(true)
await settle()
card("busy")
update(false)
await settle()
card("idle")
}
await emit({ type: "sessionStatus", sessionID: "background", status: "busy" })
await check("background", "busy")
@@ -240,6 +292,12 @@ try {
await check("root", "waiting")
await check("task-child", "waiting")
await check("task-grand", "waiting")
for (const update of [setOperation, setRun]) {
update(true)
await settle()
card("waiting")
update(false)
}
await emit({ type: "permissionError", permissionID: "permission", stale: true })
await check("root", "idle")
assert.equal(value.permissions().length, 0)
@@ -290,7 +348,9 @@ try {
type: "suggestionRequest",
suggestion: { id: "suggestion", sessionID: "task-grand", text: "Try this", actions: [] },
})
await check("root", "waiting")
await check("root", "idle")
await check("task-grand", "idle")
assert.equal(value.scopedSuggestions("root").length, 1)
await emit({ type: "suggestionResolved", requestID: "suggestion" })
await check("root", "idle")
assert.equal(value.suggestions().length, 0)
@@ -307,11 +367,17 @@ try {
await check("root", "idle")
await emit({ type: "sessionTurnClosed", sessionID: "root", reason: "completed" })
await emit({
type: "suggestionRequest",
suggestion: { id: "review", sessionID: "root", text: "Review the changes", actions: [] },
})
await check("root", "done")
assert.equal(value.suggestions().length, 1)
await emit({ type: "sessionStatus", sessionID: "root", status: "busy" })
await check("root", "busy")
await emit({ type: "sessionStatus", sessionID: "root", status: "idle" })
await check("root", "idle")
await emit({ type: "suggestionResolved", requestID: "review" })
await emit({ type: "sessionTurnClosed", sessionID: "root", reason: "completed" })
await check("root", "done")
@@ -370,14 +436,39 @@ try {
await check("root", "idle")
await emit({ type: "sessionError", eventID: "root-error", error: { name: "ProviderError" } })
await check("root", "error")
for (const update of [setOperation, setRun]) {
update(true)
await settle()
card("error")
update(false)
}
assert.equal(value.inUseFor("root"), false)
await emit({ type: "sessionTurnClosed", sessionID: "root", reason: "completed" })
await check("root", "error")
await emit({ type: "sessionStatus", sessionID: "root", status: "busy" })
await check("root", "busy")
for (const status of ["busy", "retry"] as const) {
await emit({ type: "sessionStatus", sessionID: "root", status })
await emit({
type: "sessionError",
sessionID: "root",
eventID: `root-${status}-error`,
error: { name: "ProviderError", message: "Request Entity Too Large" },
})
await check("root", "error")
await emit({ type: "sessionStatus", sessionID: "root", status })
await check("root", status)
assert.equal(value.closeReason(), undefined)
assert(value.messages().some((message) => message.sessionErrorID === `root-${status}-error`))
}
await emit({ type: "sessionTurnClosed", sessionID: "root", reason: "completed" })
await emit({ type: "sessionStatus", sessionID: "root", status: "idle" })
await check("root", "idle")
await check("root", "done")
await emit({ type: "sessionStatus", sessionID: "root", status: "busy" })
await emit({ type: "sessionError", sessionID: "root", eventID: "terminal-error", error: { name: "ProviderError" } })
await emit({ type: "sessionStatus", sessionID: "root", status: "idle" })
await check("root", "error")
await emit({
type: "questionRequest",
@@ -5,7 +5,6 @@
import { Component, For, Match, Show, Switch, createSignal } from "solid-js"
import { Icon } from "@kilocode/kilo-ui/icon"
import { IconButton } from "@kilocode/kilo-ui/icon-button"
import { Spinner } from "@kilocode/kilo-ui/spinner"
import { Tooltip, TooltipKeybind } from "@kilocode/kilo-ui/tooltip"
import { HoverCard } from "@kilocode/kilo-ui/hover-card"
import { ContextMenu } from "@kilocode/kilo-ui/context-menu"
@@ -13,7 +12,7 @@ import { Button } from "@kilocode/kilo-ui/button"
import type { WorktreeState, WorktreeGitStats, SectionState, RunStatus } from "../src/types/messages"
import type { PRStatus } from "../src/types/messages"
import { ActivityIcon } from "../src/components/shared/ActivityIcon"
import { label, running, type Activity } from "../src/utils/session-activity"
import { description, label, running, strongest, type Activity } from "../src/utils/session-activity"
import { colorCss } from "./section-colors"
import { useLanguage } from "../src/context/language"
import { formatRelativeDate } from "../src/utils/date"
@@ -157,7 +156,7 @@ export const WorktreeItem: Component<WorktreeItemProps> = (props) => {
const { t } = useLanguage()
const [hovered, setHovered] = createSignal(false)
const [overClose, setOverClose] = createSignal(false)
const state = () => props.activity
const state = () => strongest([props.activity, props.busy || props.runStatus?.state === "running" ? "busy" : "idle"])
const blocked = () =>
props.busy ||
props.blocked ||
@@ -209,12 +208,7 @@ export const WorktreeItem: Component<WorktreeItemProps> = (props) => {
onClick={() => props.onClick()}
>
<div class="am-wt-icon" data-activity={state()} aria-label={t(label(state()))}>
<Show
when={!props.busy && props.runStatus?.state !== "running"}
fallback={<Spinner class="am-worktree-spinner" />}
>
<ActivityIcon state={state()} idle={<Icon name="branch" size="small" />} />
</Show>
<ActivityIcon state={state()} idle={<Icon name="branch" size="small" />} />
</div>
<div class="am-wt-content">
{/* Row 1: label + stale badge + stats/hover-actions overlay */}
@@ -412,6 +406,11 @@ export const WorktreeItem: Component<WorktreeItemProps> = (props) => {
<span class="am-hover-card-keybind">{props.navHint}</span>
</Show>
</div>
<Show when={state() !== "idle"}>
<div class="am-hover-card-divider" />
<div class="am-hover-card-label">{t(label(state()))}</div>
<div class="am-hover-card-note">{t(description(state()))}</div>
</Show>
<div class="am-hover-card-divider" />
<div class="am-hover-card-row">
<span class="am-hover-card-row-label">{t("agentManager.hoverCard.worktree")}</span>
@@ -2,7 +2,8 @@ import { IconButton } from "@kilocode/kilo-ui/icon-button"
import { TooltipKeybind } from "@kilocode/kilo-ui/tooltip"
import { Show, type Component, type JSX } from "solid-js"
import { ActivityIcon } from "../shared/ActivityIcon"
import type { Activity } from "../../utils/session-activity"
import { description, type Activity } from "../../utils/session-activity"
import { useLanguage } from "../../context/language"
export const SessionTab: Component<{
title: string
@@ -22,56 +23,59 @@ export const SessionTab: Component<{
onMiddleClick?: (event: MouseEvent) => void
onKeyDown?: JSX.EventHandlerUnion<HTMLDivElement, KeyboardEvent>
onClose: () => void
}> = (props) => (
<div class={`am-tab ${props.active ? "am-tab-active" : ""}`} data-activity={props.state}>
<div
class="am-tab-target"
role={props.role}
aria-selected={props.selected}
aria-keyshortcuts={props.keyShortcuts}
tabIndex={props.tabIndex}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
onKeyDown={props.onKeyDown}
>
}> = (props) => {
const { t } = useLanguage()
return (
<div class={`am-tab ${props.active ? "am-tab-active" : ""}`} data-activity={props.state}>
<div
class="am-tab-target"
role={props.role}
aria-selected={props.selected}
aria-keyshortcuts={props.keyShortcuts}
tabIndex={props.tabIndex}
onClick={props.onSelect}
onMouseDown={props.onMiddleClick}
onKeyDown={props.onKeyDown}
>
<TooltipKeybind
title={props.state === "idle" ? props.title : `${props.title}: ${t(description(props.state))}`}
keybind={props.keybind ?? ""}
placement="bottom"
gutter={8}
class="am-tab-tooltip"
openDelay={0}
>
<span class="am-tab-title">
<Show when={props.state !== "idle"}>
<span class="am-tab-icon" data-activity={props.state} aria-label={props.stateLabel}>
<ActivityIcon state={props.state} />
</span>
</Show>
<span class="am-tab-label">{props.title}</span>
</span>
</TooltipKeybind>
</div>
<TooltipKeybind
title={props.title}
keybind={props.keybind ?? ""}
placement="bottom"
title={props.closeTitle}
keybind={props.closeKeybind ?? ""}
placement="top"
gutter={8}
class="am-tab-tooltip"
class="am-tab-close-wrap"
openDelay={0}
>
<span class="am-tab-title">
<Show when={props.state !== "idle"}>
<span class="am-tab-icon" data-activity={props.state} aria-label={props.stateLabel}>
<ActivityIcon state={props.state} />
</span>
</Show>
<span class="am-tab-label">{props.title}</span>
</span>
<IconButton
icon="close-small"
size="small"
variant="ghost"
aria-label={props.closeLabel}
tabIndex={props.closeTabIndex}
class="am-tab-close"
onClick={(event) => {
event.stopPropagation()
props.onClose()
}}
/>
</TooltipKeybind>
</div>
<TooltipKeybind
title={props.closeTitle}
keybind={props.closeKeybind ?? ""}
placement="top"
gutter={8}
class="am-tab-close-wrap"
openDelay={0}
>
<IconButton
icon="close-small"
size="small"
variant="ghost"
aria-label={props.closeLabel}
tabIndex={props.closeTabIndex}
class="am-tab-close"
onClick={(event) => {
event.stopPropagation()
props.onClose()
}}
/>
</TooltipKeybind>
</div>
)
)
}
@@ -1518,9 +1518,8 @@ export const SessionProvider: ParentComponent = (props) => {
? { type: "offline", message: message ?? "" }
: { type: newStatus }
setStatusMap(sessionID, info)
// Track busy start time and discard the previous turn's terminal state.
if (newStatus === "busy" || newStatus === "retry") clearClose(sessionID)
if (prev === "idle" && newStatus !== "idle") {
clearClose(sessionID)
if (!busySinceMap[sessionID]) setBusySinceMap(sessionID, Date.now())
}
if (newStatus === "idle") {
@@ -1781,7 +1780,7 @@ export const SessionProvider: ParentComponent = (props) => {
parents: lineage().parents,
statuses: statusMap,
outcomes: closeMap,
blocked: [...permissions(), ...questions().filter((item) => item.blocking !== false), ...suggestions()].map(
blocked: [...permissions(), ...questions().filter((item) => item.blocking !== false)].map(
(item) => item.sessionID,
),
submitting: Object.keys(submissionMap),
+7
View File
@@ -58,6 +58,13 @@ export const dict = {
"command.provider.connect": "اتصال بموفر",
"session.activity.waiting": "في انتظار إجابة أو موافقة.",
"session.activity.error": "حدث خطأ أو فُقد الاتصال.",
"session.activity.retry": "إعادة المحاولة تلقائيًا.",
"session.activity.busy": "قيد التنفيذ.",
"session.activity.done": "اكتمل الدور.",
"session.activity.idle": "غير قيد التشغيل.",
"command.session.new": "جلسة جديدة",
"command.session.show.changes": "إظهار التغييرات",
"command.review.toggle": "تبديل المراجعة",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Conectar provedor",
"session.activity.waiting": "Aguardando uma resposta ou aprovação.",
"session.activity.error": "Erro ou conexão perdida.",
"session.activity.retry": "Tentando novamente automaticamente.",
"session.activity.busy": "Em andamento.",
"session.activity.done": "Turno concluído.",
"session.activity.idle": "Não está em execução.",
"command.session.new": "Nova sessão",
"command.session.show.changes": "Mostrar alterações",
"command.review.toggle": "Alternar revisão",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Poveži provajdera",
"session.activity.waiting": "Čeka se odgovor ili odobrenje.",
"session.activity.error": "Greška ili prekinuta veza.",
"session.activity.retry": "Automatski novi pokušaj.",
"session.activity.busy": "U toku.",
"session.activity.done": "Potez završen.",
"session.activity.idle": "Ne radi.",
"command.session.new": "Nova sesija",
"command.session.show.changes": "Prikaži promjene",
"command.review.toggle": "Prikaži/sakrij pregled",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Tilslut udbyder",
"session.activity.waiting": "Venter på et svar eller en godkendelse.",
"session.activity.error": "Fejl eller mistet forbindelse.",
"session.activity.retry": "Prøver automatisk igen.",
"session.activity.busy": "I gang.",
"session.activity.done": "Tur afsluttet.",
"session.activity.idle": "Kører ikke.",
"command.session.new": "Ny session",
"command.session.show.changes": "Vis ændringer",
"command.review.toggle": "Skift gennemgang",
@@ -65,6 +65,13 @@ export const dict = {
"command.provider.connect": "Anbieter verbinden",
"session.activity.waiting": "Warten auf eine Antwort oder Genehmigung.",
"session.activity.error": "Fehler oder Verbindung verloren.",
"session.activity.retry": "Automatischer erneuter Versuch.",
"session.activity.busy": "In Bearbeitung.",
"session.activity.done": "Turn abgeschlossen.",
"session.activity.idle": "Nicht aktiv.",
"command.session.new": "Neue Sitzung",
"command.session.show.changes": "Änderungen anzeigen",
"command.review.toggle": "Überprüfung umschalten",
@@ -58,6 +58,13 @@ export const dict = {
"command.provider.connect": "Connect provider",
"session.activity.waiting": "Waiting for an answer or approval.",
"session.activity.error": "Error or connection lost.",
"session.activity.retry": "Retrying automatically.",
"session.activity.busy": "In progress.",
"session.activity.done": "Turn completed.",
"session.activity.idle": "Not running.",
"command.session.new": "New session",
"command.session.show.changes": "Show Changes",
"command.review.toggle": "Toggle review",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Conectar proveedor",
"session.activity.waiting": "Esperando una respuesta o aprobación.",
"session.activity.error": "Error o conexión perdida.",
"session.activity.retry": "Reintentando automáticamente.",
"session.activity.busy": "En curso.",
"session.activity.done": "Turno completado.",
"session.activity.idle": "No está en ejecución.",
"command.session.new": "Nueva sesión",
"command.session.show.changes": "Mostrar cambios",
"command.review.toggle": "Alternar revisión",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "اتصال به ارائه‌دهنده",
"session.activity.waiting": "در انتظار پاسخ یا تأیید.",
"session.activity.error": "خطا یا قطع اتصال.",
"session.activity.retry": "تلاش مجدد خودکار.",
"session.activity.busy": "در حال انجام.",
"session.activity.done": "نوبت به پایان رسید.",
"session.activity.idle": "در حال اجرا نیست.",
"command.session.new": "جلسه جدید",
"command.session.show.changes": "نمایش تغییرات",
"command.review.toggle": "نمایش/پنهان کردن بررسی",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Connecter un fournisseur",
"session.activity.waiting": "En attente dune réponse ou dune approbation.",
"session.activity.error": "Erreur ou connexion perdue.",
"session.activity.retry": "Nouvelle tentative automatique.",
"session.activity.busy": "En cours.",
"session.activity.done": "Tour terminé.",
"session.activity.idle": "Inactif.",
"command.session.new": "Nouvelle session",
"command.session.show.changes": "Afficher les modifications",
"command.review.toggle": "Basculer la revue",
+6
View File
@@ -57,6 +57,12 @@ export const dict = {
...anacondaDesktopDict,
...cloudProviderDict,
"command.provider.connect": "Connetti provider",
"session.activity.waiting": "In attesa di una risposta o approvazione.",
"session.activity.error": "Errore o connessione persa.",
"session.activity.retry": "Nuovo tentativo automatico.",
"session.activity.busy": "In corso.",
"session.activity.done": "Turno completato.",
"session.activity.idle": "Non in esecuzione.",
"command.session.new": "Nuova sessione",
"command.session.show.changes": "Mostra modifiche",
"command.review.toggle": "Mostra/nascondi revisione",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "プロバイダーに接続",
"session.activity.waiting": "回答または承認を待っています。",
"session.activity.error": "エラーまたは接続切断。",
"session.activity.retry": "自動的に再試行中。",
"session.activity.busy": "進行中。",
"session.activity.done": "ターン完了。",
"session.activity.idle": "実行されていません。",
"command.session.new": "新しいセッション",
"command.session.show.changes": "変更を表示",
"command.review.toggle": "レビューの切り替え",
+7
View File
@@ -63,6 +63,13 @@ export const dict = {
"command.provider.connect": "공급자 연결",
"session.activity.waiting": "답변 또는 승인을 기다리는 중입니다.",
"session.activity.error": "오류 또는 연결 끊김.",
"session.activity.retry": "자동으로 재시도하는 중입니다.",
"session.activity.busy": "진행 중입니다.",
"session.activity.done": "턴이 완료되었습니다.",
"session.activity.idle": "실행 중이 아닙니다.",
"command.session.new": "새 세션",
"command.session.show.changes": "변경 사항 보기",
"command.review.toggle": "검토 토글",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Provider verbinden",
"session.activity.waiting": "Wachten op een antwoord of goedkeuring.",
"session.activity.error": "Fout of verbinding verbroken.",
"session.activity.retry": "Automatisch opnieuw proberen.",
"session.activity.busy": "In uitvoering.",
"session.activity.done": "Beurt voltooid.",
"session.activity.idle": "Niet actief.",
"command.session.new": "Nieuwe sessie",
"command.session.show.changes": "Wijzigingen tonen",
"command.review.toggle": "Review in-/uitschakelen",
+7
View File
@@ -62,6 +62,13 @@ export const dict = {
"command.provider.connect": "Koble til leverandør",
"session.activity.waiting": "Venter på et svar eller en godkjenning.",
"session.activity.error": "Feil eller tilkobling brutt.",
"session.activity.retry": "Prøver automatisk på nytt.",
"session.activity.busy": "Pågår.",
"session.activity.done": "Runde fullført.",
"session.activity.idle": "Kjører ikke.",
"command.session.new": "Ny sesjon",
"command.session.show.changes": "Vis endringer",
"command.review.toggle": "Veksle gjennomgang",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Połącz dostawcę",
"session.activity.waiting": "Oczekiwanie na odpowiedź lub zatwierdzenie.",
"session.activity.error": "Błąd lub utrata połączenia.",
"session.activity.retry": "Automatyczne ponawianie próby.",
"session.activity.busy": "W toku.",
"session.activity.done": "Tura zakończona.",
"session.activity.idle": "Nie działa.",
"command.session.new": "Nowa sesja",
"command.session.show.changes": "Pokaż zmiany",
"command.review.toggle": "Przełącz przegląd",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Подключить провайдера",
"session.activity.waiting": "Ожидается ответ или подтверждение.",
"session.activity.error": "Ошибка или потеря соединения.",
"session.activity.retry": "Автоматический повтор.",
"session.activity.busy": "Выполняется.",
"session.activity.done": "Ход завершён.",
"session.activity.idle": "Агент не запущен.",
"command.session.new": "Новая сессия",
"command.session.show.changes": "Показать изменения",
"command.review.toggle": "Переключить обзор",
+7
View File
@@ -58,6 +58,13 @@ export const dict = {
"command.provider.connect": "เชื่อมต่อผู้ให้บริการ",
"session.activity.waiting": "กำลังรอคำตอบหรือการอนุมัติ",
"session.activity.error": "เกิดข้อผิดพลาดหรือการเชื่อมต่อขาดหาย",
"session.activity.retry": "กำลังลองใหม่โดยอัตโนมัติ",
"session.activity.busy": "กำลังดำเนินการ",
"session.activity.done": "จบรอบการทำงานแล้ว",
"session.activity.idle": "ไม่ได้ทำงานอยู่",
"command.session.new": "เซสชันใหม่",
"command.session.show.changes": "แสดงการเปลี่ยนแปลง",
"command.review.toggle": "สลับการตรวจสอบ",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Sağlayıcı bağla",
"session.activity.waiting": "Yanıt veya onay bekleniyor.",
"session.activity.error": "Hata veya bağlantı kaybı.",
"session.activity.retry": "Otomatik olarak yeniden deneniyor.",
"session.activity.busy": "Devam ediyor.",
"session.activity.done": "Tur tamamlandı.",
"session.activity.idle": "Çalışmıyor.",
"command.session.new": "Yeni oturum",
"command.session.show.changes": "Değişiklikleri Göster",
"command.review.toggle": "İncelemeyi aç/kapat",
+7
View File
@@ -59,6 +59,13 @@ export const dict = {
"command.provider.connect": "Підключити провайдера",
"session.activity.waiting": "Очікується відповідь або підтвердження.",
"session.activity.error": "Помилка або втрата з'єднання.",
"session.activity.retry": "Автоматична повторна спроба.",
"session.activity.busy": "Виконується.",
"session.activity.done": "Хід завершено.",
"session.activity.idle": "Не запущено.",
"command.session.new": "Нова сесія",
"command.session.show.changes": "Показати зміни",
"command.review.toggle": "Перемкнути огляд",
+7
View File
@@ -60,6 +60,13 @@ export const dict = {
"command.provider.connect": "连接提供商",
"session.activity.waiting": "正在等待回复或批准。",
"session.activity.error": "错误或连接已断开。",
"session.activity.retry": "正在自动重试。",
"session.activity.busy": "进行中。",
"session.activity.done": "回合已完成。",
"session.activity.idle": "未运行。",
"command.session.new": "新建会话",
"command.session.show.changes": "显示更改",
"command.review.toggle": "切换审查",
+7
View File
@@ -60,6 +60,13 @@ export const dict = {
"command.provider.connect": "連接供應商",
"session.activity.waiting": "正在等待回覆或核准。",
"session.activity.error": "錯誤或連線已中斷。",
"session.activity.retry": "正在自動重試。",
"session.activity.busy": "進行中。",
"session.activity.done": "回合已完成。",
"session.activity.idle": "未執行。",
"command.session.new": "新增工作階段",
"command.session.show.changes": "顯示變更",
"command.review.toggle": "切換審查",
@@ -84,3 +84,7 @@ const LABELS: Record<Activity, string> = {
export function label(state: Activity): string {
return LABELS[state]
}
export function description(state: Activity): string {
return `session.activity.${state}`
}