feat(ui): show background tasks live in the web Control UI (#100789)

* feat(gateway): broadcast task ledger changes as task events

* feat(ui): add background Tasks page to Control UI

* fix(gateway): address tasks page review findings

* fix(ui): surface tasks.cancel refusals on the Tasks page

* fix(gateway): guard stale task observer disposal against replacement gateways

* chore: satisfy lint and docs-map gates for tasks page

* test(gateway): await async agent unsub in stale-dispose test
This commit is contained in:
Peter Steinberger 2026-07-06 14:36:33 +01:00 committed by GitHub
parent 88f1ec38d4
commit f623d81f52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 2135 additions and 150 deletions

View file

@ -287,6 +287,10 @@ When the current session has no visible linked tasks, `/tasks` falls back to age
For the full operator ledger, use the CLI: `openclaw tasks list`.
### Control UI
The web Control UI has a **Tasks** page in the sidebar with live active and recent background tasks. Use it to inspect progress, open linked sessions, refresh the ledger, or cancel queued and running tasks.
## Status integration (task pressure)
`openclaw status` includes an at-a-glance task line:

View file

@ -199,6 +199,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- H3: Notification policies
- H2: CLI reference
- H2: Chat task board (/tasks)
- H3: Control UI
- H2: Status integration (task pressure)
- H2: Storage and maintenance
- H3: Where tasks live

View file

@ -142,8 +142,9 @@ The compact footer keeps connection status, **Settings**, **Docs**, and mobile p
- Dreams: dreaming status, enable/disable toggle, and Dream Diary reader (`doctor.memory.status`, `doctor.memory.dreamDiary`, `config.patch`).
</Accordion>
<Accordion title="Cron, skills, nodes, exec approvals">
<Accordion title="Cron, tasks, skills, nodes, exec approvals">
- Cron jobs: list/add/edit/run/enable/disable plus run history (`cron.*`).
- Tasks: live active and recent background task ledger with linked sessions and cancellation (`tasks.*`).
- Skills: status, enable/disable, install, API key updates (`skills.*`).
- Nodes: list plus caps (`node.list`), create mobile setup codes, and approve device pairing (`device.pair.*`).
- Exec approvals: edit gateway or node allowlists and ask policy for `exec host=gateway/node` (`exec.approvals.*`).

View file

@ -8,6 +8,7 @@
"shutdown": "iOS relies on socket close plus reconnect/backoff instead of the shutdown notice.",
"heartbeat": "iOS liveness uses tick and WebSocket-level ping; heartbeat is unused.",
"cron": "Cron run activity is not surfaced in the iOS app.",
"task": "Background task activity is not surfaced in the iOS app.",
"node.pair.requested": "Node pairing state is fetched on demand; no push consumer on iOS yet.",
"node.pair.resolved": "Node pairing state is fetched on demand; no push consumer on iOS yet.",
"device.pair.requested": "Device pairing flows poll via device.pair.* methods on iOS.",
@ -27,6 +28,7 @@
"shutdown": "Android relies on socket close plus reconnect/backoff instead of the shutdown notice.",
"heartbeat": "Android liveness uses tick and WebSocket-level ping; heartbeat is unused.",
"cron": "Cron run activity is not surfaced in the Android app.",
"task": "Background task activity is not surfaced in the Android app.",
"node.pair.requested": "Android's bounded operator session lacks operator.pairing; onboarding refreshes node approval with explicit node.list requests.",
"node.pair.resolved": "Android's bounded operator session lacks operator.pairing; onboarding refreshes node approval with explicit node.list requests.",
"device.pair.requested": "Device pairing flows poll via device.pair.* methods on Android.",

View file

@ -439,6 +439,19 @@ describe("gateway broadcaster", () => {
expectSentEvents(adminSocket, expectedEvents);
});
it("requires operator.read for task ledger broadcast events", () => {
const { pairingSocket, nodeSocket, readSocket, writeSocket, adminSocket, broadcast } =
makeScopedBroadcastContext();
broadcast("task", { action: "deleted", taskId: "task-1" });
expect(pairingSocket.send).not.toHaveBeenCalled();
expect(nodeSocket.send).not.toHaveBeenCalled();
expectSentEvents(readSocket, ["task"]);
expectSentEvents(writeSocket, ["task"]);
expectSentEvents(adminSocket, ["task"]);
});
it("allows plugin.* broadcast events for operator.write and operator.admin", () => {
const { pairingSocket, nodeSocket, readSocket, writeSocket, adminSocket, broadcast } =
makeScopedBroadcastContext();

View file

@ -37,6 +37,7 @@ const EVENT_SCOPE_GUARDS: Record<string, string[]> = {
tick: [],
"talk.event": [READ_SCOPE],
"talk.mode": [WRITE_SCOPE],
task: [READ_SCOPE],
"update.available": [],
"voicewake.changed": [READ_SCOPE],
"voicewake.routing.changed": [READ_SCOPE],

View file

@ -118,6 +118,7 @@ function createGatewayCloseTestDeps(
mediaCleanup: null,
worktreeCleanup: null,
agentUnsub: null,
taskUnsub: null,
heartbeatUnsub: null,
transcriptUnsub: null,
lifecycleUnsub: null,
@ -1297,12 +1298,14 @@ describe("createGatewayCloseHandler", () => {
it("unsubscribes lifecycle listeners and disposes bundle runtimes during shutdown", async () => {
const lifecycleUnsub = vi.fn();
const taskUnsub = vi.fn();
const transcriptUnsub = vi.fn();
const stopTaskRegistryMaintenance = vi.fn();
const close = createGatewayCloseHandler(
createGatewayCloseTestDeps({
stopTaskRegistryMaintenance,
lifecycleUnsub,
taskUnsub,
transcriptUnsub,
}),
);
@ -1310,6 +1313,7 @@ describe("createGatewayCloseHandler", () => {
await close({ reason: "test shutdown" });
expect(lifecycleUnsub).toHaveBeenCalledTimes(1);
expect(taskUnsub).toHaveBeenCalledTimes(1);
expect(transcriptUnsub).toHaveBeenCalledTimes(1);
expect(stopTaskRegistryMaintenance).toHaveBeenCalledTimes(1);
expect(mocks.disposeAgentHarnesses).toHaveBeenCalledTimes(1);

View file

@ -692,6 +692,7 @@ export function createGatewayCloseHandler(
heartbeatUnsub: (() => void) | null;
transcriptUnsub: (() => void) | null;
lifecycleUnsub: (() => void) | null;
taskUnsub: (() => void) | null;
getPendingReplyCount?: () => number;
clients: Set<{ socket: { close: (code: number, reason: string) => void } }>;
configReloader: { stop: () => Promise<void> };
@ -909,6 +910,9 @@ export function createGatewayCloseHandler(
if (params.lifecycleUnsub) {
await shutdownStep("lifecycle-unsub", () => params.lifecycleUnsub!(), warnings);
}
if (params.taskUnsub) {
await shutdownStep("task-unsub", () => params.taskUnsub!(), warnings);
}
params.chatRunState.clear();
let clientCloseFailures = 0;
for (const c of params.clients) {

View file

@ -52,6 +52,7 @@ export const GATEWAY_EVENTS = [
"health",
"heartbeat",
"cron",
"task",
"node.pair.requested",
"node.pair.resolved",
"node.invoke.request",

View file

@ -0,0 +1,70 @@
// Public task summaries keep task-registry internals and unbounded status text
// out of gateway responses and events.
import type { TaskSummary } from "../../../packages/gateway-protocol/src/index.js";
import type { TaskRecord, TaskStatus } from "../../tasks/task-registry.types.js";
import {
TASK_STATUS_DETAIL_MAX_CHARS,
formatTaskStatusTitle,
sanitizeTaskStatusText,
} from "../../tasks/task-status.js";
type TaskLedgerStatus = TaskSummary["status"];
export const TASK_STATUS_TO_LEDGER_STATUS: Record<TaskStatus, TaskLedgerStatus> = {
queued: "queued",
running: "running",
succeeded: "completed",
failed: "failed",
timed_out: "timed_out",
cancelled: "cancelled",
lost: "failed",
};
export type TaskEventPayload =
| { action: "upserted"; task: TaskSummary }
| { action: "deleted"; taskId: string }
| { action: "restored" };
export function taskUpdatedAt(task: TaskRecord): number {
return task.lastEventAt ?? task.endedAt ?? task.startedAt ?? task.createdAt;
}
function sanitizeOptionalTaskText(
value: unknown,
opts?: { errorContext?: boolean },
): string | undefined {
const sanitized = sanitizeTaskStatusText(value, {
errorContext: opts?.errorContext,
maxChars: TASK_STATUS_DETAIL_MAX_CHARS,
});
return sanitized || undefined;
}
export function mapTaskSummary(task: TaskRecord): TaskSummary {
const progressSummary = sanitizeOptionalTaskText(task.progressSummary);
const terminalSummary = sanitizeOptionalTaskText(task.terminalSummary, { errorContext: true });
const error = sanitizeOptionalTaskText(task.error, { errorContext: true });
return {
id: task.taskId,
taskId: task.taskId,
kind: task.taskKind ?? task.runtime,
runtime: task.runtime,
status: TASK_STATUS_TO_LEDGER_STATUS[task.status],
title: formatTaskStatusTitle(task),
...(task.agentId ? { agentId: task.agentId } : {}),
sessionKey: task.requesterSessionKey,
...(task.childSessionKey ? { childSessionKey: task.childSessionKey } : {}),
ownerKey: task.ownerKey,
...(task.runId ? { runId: task.runId } : {}),
...(task.parentFlowId ? { flowId: task.parentFlowId } : {}),
...(task.parentTaskId ? { parentTaskId: task.parentTaskId } : {}),
...(task.sourceId ? { sourceId: task.sourceId } : {}),
createdAt: task.createdAt,
updatedAt: taskUpdatedAt(task),
...(task.startedAt !== undefined ? { startedAt: task.startedAt } : {}),
...(task.endedAt !== undefined ? { endedAt: task.endedAt } : {}),
...(progressSummary ? { progressSummary } : {}),
...(terminalSummary ? { terminalSummary } : {}),
...(error ? { error } : {}),
};
}

View file

@ -168,6 +168,39 @@ describe("tasks gateway handlers", () => {
expect(listedTask?.runId).toBe("run-running");
});
it("orders the ledger by last activity, not creation time", async () => {
// The registry lists newest-created first; the wire must page by last
// activity so an old task that just finished is not hidden behind
// newer-created records.
const base = Date.now();
const oldButJustFinished = createTaskRecord({
runtime: "subagent",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
task: "Old long-running task",
status: "succeeded",
deliveryStatus: "not_applicable",
lastEventAt: base + 60_000,
});
const newerQuietTask = createTaskRecord({
runtime: "cli",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
task: "Newer quiet task",
status: "succeeded",
deliveryStatus: "not_applicable",
lastEventAt: base + 1_000,
});
const { payload } = await runTaskHandler("tasks.list", {});
const ids = payload?.tasks?.map((task) => task.id);
expect(ids?.indexOf(oldButJustFinished.taskId)).toBeLessThan(
ids?.indexOf(newerQuietTask.taskId) ?? -1,
);
});
it("treats explicit task agentId as authoritative over the session-key fallback", async () => {
// Cross-agent subagent task: the registry derives agentId=worker from the
// child session key, while owner/requester keys belong to main. tasks.list

View file

@ -15,11 +15,7 @@ import { parseAgentSessionKey } from "../../routing/session-key.js";
import { cancelDetachedTaskRunById } from "../../tasks/detached-task-runtime.js";
import { getTaskById, listTaskRecords } from "../../tasks/runtime-internal.js";
import type { TaskRecord, TaskStatus } from "../../tasks/task-registry.types.js";
import {
TASK_STATUS_DETAIL_MAX_CHARS,
formatTaskStatusTitle,
sanitizeTaskStatusText,
} from "../../tasks/task-status.js";
import { mapTaskSummary, taskUpdatedAt } from "./task-summary.js";
import type { GatewayRequestHandlers } from "./types.js";
const DEFAULT_TASKS_LIST_LIMIT = 100;
@ -27,18 +23,6 @@ const MAX_TASKS_LIST_LIMIT = 500;
type TaskLedgerStatus = TaskSummary["status"];
// Gateway task APIs preserve the older ledger status vocabulary while the
// runtime registry tracks finer-grained task states such as `lost`.
const TASK_STATUS_TO_LEDGER_STATUS: Record<TaskStatus, TaskLedgerStatus> = {
queued: "queued",
running: "running",
succeeded: "completed",
failed: "failed",
timed_out: "timed_out",
cancelled: "cancelled",
lost: "failed",
};
const LEDGER_STATUS_TO_TASK_STATUSES: Record<TaskLedgerStatus, TaskStatus[]> = {
queued: ["queued"],
running: ["running"],
@ -48,52 +32,6 @@ const LEDGER_STATUS_TO_TASK_STATUSES: Record<TaskLedgerStatus, TaskStatus[]> = {
cancelled: ["cancelled"],
};
function taskUpdatedAt(task: TaskRecord): number {
return task.lastEventAt ?? task.endedAt ?? task.startedAt ?? task.createdAt;
}
// Status text can originate from providers, shells, and subprocesses. Keep the
// public task shape bounded before it reaches control-plane clients.
function sanitizeOptionalTaskText(
value: unknown,
opts?: { errorContext?: boolean },
): string | undefined {
const sanitized = sanitizeTaskStatusText(value, {
errorContext: opts?.errorContext,
maxChars: TASK_STATUS_DETAIL_MAX_CHARS,
});
return sanitized || undefined;
}
function mapTaskSummary(task: TaskRecord): TaskSummary {
const progressSummary = sanitizeOptionalTaskText(task.progressSummary);
const terminalSummary = sanitizeOptionalTaskText(task.terminalSummary, { errorContext: true });
const error = sanitizeOptionalTaskText(task.error, { errorContext: true });
return {
id: task.taskId,
taskId: task.taskId,
kind: task.taskKind ?? task.runtime,
runtime: task.runtime,
status: TASK_STATUS_TO_LEDGER_STATUS[task.status],
title: formatTaskStatusTitle(task),
...(task.agentId ? { agentId: task.agentId } : {}),
sessionKey: task.requesterSessionKey,
...(task.childSessionKey ? { childSessionKey: task.childSessionKey } : {}),
ownerKey: task.ownerKey,
...(task.runId ? { runId: task.runId } : {}),
...(task.parentFlowId ? { flowId: task.parentFlowId } : {}),
...(task.parentTaskId ? { parentTaskId: task.parentTaskId } : {}),
...(task.sourceId ? { sourceId: task.sourceId } : {}),
createdAt: task.createdAt,
updatedAt: taskUpdatedAt(task),
...(task.startedAt !== undefined ? { startedAt: task.startedAt } : {}),
...(task.endedAt !== undefined ? { endedAt: task.endedAt } : {}),
...(progressSummary ? { progressSummary } : {}),
...(terminalSummary ? { terminalSummary } : {}),
...(error ? { error } : {}),
};
}
function normalizeTaskStatusFilter(status: TasksListParams["status"]): Set<TaskStatus> | null {
if (!status) {
return null;
@ -171,12 +109,25 @@ export const tasksHandlers: GatewayRequestHandlers = {
}
const statusFilter = normalizeTaskStatusFilter(params.status);
const limit = Math.min(params.limit ?? DEFAULT_TASKS_LIST_LIMIT, MAX_TASKS_LIST_LIMIT);
const filtered = listTaskRecords().filter((task) => {
if (statusFilter && !statusFilter.has(task.status)) {
return false;
}
return taskMatchesAgent(task, params.agentId) && taskMatchesSession(task, params.sessionKey);
});
// The registry lists newest-created first; the ledger view pages by last
// activity so an old long-running task that just finished still surfaces
// on the first page instead of hiding behind newer-created records.
const filtered = listTaskRecords()
.filter((task) => {
if (statusFilter && !statusFilter.has(task.status)) {
return false;
}
return (
taskMatchesAgent(task, params.agentId) && taskMatchesSession(task, params.sessionKey)
);
})
.toSorted((left, right) => {
const updatedDiff = taskUpdatedAt(right) - taskUpdatedAt(left);
if (updatedDiff !== 0) {
return updatedDiff;
}
return left.taskId < right.taskId ? -1 : left.taskId > right.taskId ? 1 : 0;
});
const page = filtered.slice(cursor, cursor + limit);
const nextOffset = cursor + page.length;
respond(true, {

View file

@ -40,6 +40,7 @@ export type GatewayServerMutableState = {
heartbeatUnsub: (() => void) | null;
transcriptUnsub: (() => void) | null;
lifecycleUnsub: (() => void) | null;
taskUnsub: (() => void) | null;
};
/** Creates gateway mutable state with inert handles that are safe to stop before startup finishes. */
@ -77,5 +78,6 @@ export function createGatewayServerMutableState(): GatewayServerMutableState {
heartbeatUnsub: null as (() => void) | null,
transcriptUnsub: null as (() => void) | null,
lifecycleUnsub: null as (() => void) | null,
taskUnsub: null as (() => void) | null,
};
}

View file

@ -7,12 +7,16 @@ import {
emitInternalSessionTranscriptUpdate,
type InternalSessionTranscriptUpdate,
} from "../sessions/transcript-events.js";
import { createTaskRecord, resetTaskRegistryForTests } from "../tasks/task-registry.js";
import { getTaskRegistryObservers } from "../tasks/task-registry.store.js";
import { installInMemoryTaskRegistryRuntime } from "../test-utils/task-registry-runtime.js";
import {
createChatRunState,
createSessionEventSubscriberRegistry,
createSessionMessageSubscriberRegistry,
createToolEventRecipientRegistry,
} from "./server-chat-state.js";
import type { TaskEventPayload } from "./server-methods/task-summary.js";
const warn = vi.fn();
const mockLog: SubsystemLogger = {
@ -95,6 +99,7 @@ describe("startGatewayEventSubscriptions", () => {
auditTestState.enabled = true;
auditTestState.created = 0;
auditTestState.stopped = 0;
installInMemoryTaskRegistryRuntime();
});
afterEach(async () => {
@ -102,7 +107,9 @@ describe("startGatewayEventSubscriptions", () => {
unsubs?.heartbeatUnsub();
unsubs?.transcriptUnsub();
unsubs?.lifecycleUnsub();
void unsubs?.taskUnsub();
resetAgentEventsForTest();
resetTaskRegistryForTests({ persist: false });
});
it("records audit events by default and stops the recorder on unsubscribe", async () => {
@ -161,4 +168,115 @@ describe("startGatewayEventSubscriptions", () => {
expect.objectContaining({ sessionKey: "agent:main:main" }),
);
});
it("broadcasts bounded public task summaries with ledger statuses", async () => {
const broadcast = vi.fn<SubscriptionParams["broadcast"]>();
unsubs = startGatewayEventSubscriptions({ ...createParams(), broadcast });
await vi.waitFor(() => expect(getTaskRegistryObservers()).not.toBeNull());
const completed = createTaskRecord({
runtime: "subagent",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
task: "Completed task",
status: "succeeded",
deliveryStatus: "not_applicable",
notifyPolicy: "silent",
terminalSummary: "x".repeat(10_000),
});
const lost = createTaskRecord({
runtime: "cli",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
task: "Lost task",
status: "lost",
deliveryStatus: "not_applicable",
notifyPolicy: "silent",
});
if (!completed || !lost) {
throw new Error("expected task records to be created");
}
const taskUpsertsById = new Map(
broadcast.mock.calls
.filter(([event]) => event === "task")
.map(([, payload]) => payload as TaskEventPayload)
.filter(
(payload): payload is Extract<TaskEventPayload, { action: "upserted" }> =>
payload.action === "upserted",
)
.map((payload) => [payload.task.id, payload.task]),
);
expect(broadcast).toHaveBeenCalledWith("task", expect.anything(), { dropIfSlow: true });
// Runtime registry statuses translate to the public ledger vocabulary.
expect(taskUpsertsById.get(completed.taskId)?.status).toBe("completed");
expect(taskUpsertsById.get(lost.taskId)?.status).toBe("failed");
// Unbounded status text from providers/shells must be truncated on the wire.
const wireTerminalSummary = taskUpsertsById.get(completed.taskId)?.terminalSummary;
expect(wireTerminalSummary).toBeTruthy();
expect(wireTerminalSummary?.length ?? 0).toBeLessThan(10_000);
void unsubs?.taskUnsub();
await vi.waitFor(() => expect(getTaskRegistryObservers()).toBeNull());
broadcast.mockClear();
createTaskRecord({
runtime: "cli",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
task: "After dispose",
status: "queued",
deliveryStatus: "not_applicable",
notifyPolicy: "silent",
});
expect(broadcast).not.toHaveBeenCalled();
});
it("keeps a replacement gateway's task observer when a stale unsub runs late", async () => {
const staleBroadcast = vi.fn<SubscriptionParams["broadcast"]>();
const staleSubs = startGatewayEventSubscriptions({
...createParams(),
broadcast: staleBroadcast,
});
await vi.waitFor(() => expect(getTaskRegistryObservers()).not.toBeNull());
const staleObservers = getTaskRegistryObservers();
const replacementBroadcast = vi.fn<SubscriptionParams["broadcast"]>();
unsubs = startGatewayEventSubscriptions({
...createParams(),
broadcast: replacementBroadcast,
});
await vi.waitFor(() => {
const current = getTaskRegistryObservers();
expect(current).not.toBeNull();
expect(current).not.toBe(staleObservers);
});
// The stale dispose must not clear the replacement's observer slot.
await staleSubs.taskUnsub();
await staleSubs.agentUnsub();
staleSubs.heartbeatUnsub();
staleSubs.transcriptUnsub();
staleSubs.lifecycleUnsub();
expect(getTaskRegistryObservers()).not.toBeNull();
createTaskRecord({
runtime: "cli",
requesterSessionKey: "agent:main:main",
ownerKey: "agent:main:main",
scopeKind: "session",
task: "After stale dispose",
status: "queued",
deliveryStatus: "not_applicable",
notifyPolicy: "silent",
});
expect(replacementBroadcast).toHaveBeenCalledWith("task", expect.anything(), {
dropIfSlow: true,
});
expect(staleBroadcast).not.toHaveBeenCalledWith("task", expect.anything(), {
dropIfSlow: true,
});
});
});

View file

@ -10,6 +10,7 @@ import type { SubsystemLogger } from "../logging/subsystem.js";
import { onSessionLifecycleEvent } from "../sessions/session-lifecycle-events.js";
import { onInternalSessionTranscriptUpdate } from "../sessions/transcript-events.js";
import { createLazyPromise } from "../shared/lazy-runtime.js";
import type { TaskRegistryObserverEvent } from "../tasks/task-registry.store.js";
import {
type ChatAbortControllerEntry,
removeChatAbortControllerEntry,
@ -22,6 +23,7 @@ import type {
ToolEventRecipientRegistry,
} from "./server-chat-state.js";
import { resolveVisibleActiveSessionRunState } from "./server-methods/session-active-runs.js";
import { mapTaskSummary, type TaskEventPayload } from "./server-methods/task-summary.js";
function dispatchEventHandler<TEvent>(params: {
loadHandler: () => Promise<(event: TEvent) => unknown>;
@ -316,10 +318,53 @@ export function startGatewayEventSubscriptions(params: {
});
});
let taskObserverDisposed = false;
const taskObservers = {
onEvent: (event: TaskRegistryObserverEvent) => {
let payload: TaskEventPayload;
switch (event.kind) {
case "upserted":
payload = { action: "upserted", task: mapTaskSummary(event.task) };
break;
case "deleted":
payload = { action: "deleted", taskId: event.taskId };
break;
case "restored":
payload = { action: "restored" };
break;
}
params.broadcast("task", payload, { dropIfSlow: true });
},
};
const taskObserverRuntimePromise = import("../tasks/task-registry.store.js").then((module) => {
if (!taskObserverDisposed) {
module.configureTaskRegistryRuntime({ observers: taskObservers });
}
return module;
});
void taskObserverRuntimePromise.catch((error: unknown) => {
params.log.warn("Task registry observer registration failed", { error });
});
// The observer slot is a process-wide singleton. Cleanup returns its promise
// so shutdown can await it, and only clears the slot when it still holds
// this subscription's observer — a replacement gateway may have registered
// its own observer before a stale deferred dispose runs.
const taskUnsub = () => {
taskObserverDisposed = true;
return taskObserverRuntimePromise
.then((module) => {
if (module.getTaskRegistryObservers() === taskObservers) {
module.configureTaskRegistryRuntime({ observers: null });
}
})
.catch(() => undefined);
};
return {
agentUnsub,
heartbeatUnsub,
transcriptUnsub,
lifecycleUnsub,
taskUnsub,
};
}

View file

@ -1915,6 +1915,7 @@ describe("startGatewayPostAttachRuntime", () => {
mediaCleanup: null,
worktreeCleanup: null,
agentUnsub: null,
taskUnsub: null,
heartbeatUnsub: null,
transcriptUnsub: null,
lifecycleUnsub: null,

View file

@ -1070,6 +1070,7 @@ export async function startGatewayServer(
heartbeatUnsub: runtimeState.heartbeatUnsub,
transcriptUnsub: runtimeState.transcriptUnsub,
lifecycleUnsub: runtimeState.lifecycleUnsub,
taskUnsub: runtimeState.taskUnsub,
chatRunState,
chatAbortControllers,
chatQueuedTurns,

View file

@ -42,6 +42,7 @@ describe("navigationIconForRoute", () => {
sessions: "fileText",
usage: "barChart",
cron: "loader",
tasks: "loader",
agents: "folder",
skills: "zap",
"skill-workshop": "wrench",
@ -81,6 +82,7 @@ describe("titleForRoute", () => {
sessions: "Sessions",
usage: "Usage",
cron: "Cron Jobs",
tasks: "Tasks",
agents: "Agents",
skills: "Skills",
"skill-workshop": "Skill Workshop",
@ -114,6 +116,7 @@ describe("subtitleForRoute", () => {
sessions: "Active sessions and defaults.",
usage: "API usage and costs.",
cron: "Wakeups and recurring runs.",
tasks: "Background tasks: subagents, cron runs, CLI.",
agents: "Workspaces, tools, identities.",
skills: "Skills and API keys.",
"skill-workshop": "Review, refine, and apply proposals before they become live skills.",

View file

@ -21,6 +21,7 @@ export const SIDEBAR_NAV_ROUTES = [
"sessions",
"usage",
"cron",
"tasks",
"agents",
"skills",
"skill-workshop",
@ -86,6 +87,7 @@ const NAVIGATION_ICONS: NavigationItem = {
sessions: "fileText",
usage: "barChart",
cron: "loader",
tasks: "loader",
skills: "zap",
"skill-workshop": "wrench",
nodes: "monitor",
@ -170,6 +172,7 @@ const NAVIGATION_COPY: Record<NavigationRouteId, { titleKey: string; subtitleKey
sessions: { titleKey: "tabs.sessions", subtitleKey: "subtitles.sessions" },
usage: { titleKey: "tabs.usage", subtitleKey: "subtitles.usage" },
cron: { titleKey: "tabs.cron", subtitleKey: "subtitles.cron" },
tasks: { titleKey: "tabs.tasks", subtitleKey: "subtitles.tasks" },
skills: { titleKey: "tabs.skills", subtitleKey: "subtitles.skills" },
"skill-workshop": {
titleKey: "tabs.skillWorkshop",

View file

@ -24,6 +24,7 @@ export const APP_ROUTE_DEFINITIONS = {
"skill-workshop": { path: "/skills/workshop" },
skills: { path: "/skills" },
cron: { path: "/cron" },
tasks: { path: "/tasks" },
nodes: { path: "/nodes" },
plugin: { path: "/plugin" },
dreams: { path: "/dreaming", aliases: ["/dreams"] },

View file

@ -18,6 +18,7 @@ import { page as pluginPage } from "./pages/plugin/route.ts";
import { page as sessionsPage } from "./pages/sessions/route.ts";
import { page as skillWorkshopPage } from "./pages/skill-workshop/route.ts";
import { page as skillsPage } from "./pages/skills/route.ts";
import { page as tasksPage } from "./pages/tasks/route.ts";
import { page as usagePage } from "./pages/usage/route.ts";
import { page as workboardPage } from "./pages/workboard/route.ts";
import { page as worktreesPage } from "./pages/worktrees/route.ts";
@ -51,6 +52,7 @@ const APP_ROUTE_TREE = [
skillWorkshopPage,
skillsPage,
cronPage,
tasksPage,
nodesPage,
dreamsPage,
pluginPage,

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:50.688Z",
"generatedAt": "2026-07-06T13:33:02.233Z",
"locale": "ar",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:49.128Z",
"generatedAt": "2026-07-06T13:33:00.707Z",
"locale": "de",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:49.380Z",
"generatedAt": "2026-07-06T13:33:00.963Z",
"locale": "es",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:52.967Z",
"generatedAt": "2026-07-06T13:33:04.461Z",
"locale": "fa",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:50.150Z",
"generatedAt": "2026-07-06T13:33:01.731Z",
"locale": "fr",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -10,12 +10,15 @@
{"cache_key":"10a71530d545b73727a8bf867acf9450cd23be9cb7bbe02edfb89eea7ff52a66","model":"claude-opus-4-8","provider":"anthropic","segment_id":"cron.jobDetail.command","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Command","text_hash":"713166971d730f81fcf8b757f2ea239d1a0360d9f74e8f5afe60fba97105879c","tgt_lang":"fr","translated":"Commande","updated_at":"2026-06-16T14:14:40.922Z"}
{"cache_key":"10c47c1f85d37e29ce8e5d39c4fb55a31878f742cd3731dce30ef89ffd6266a7","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.layoutComfortable","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Comfortable card density","text_hash":"bfaaf4553fd254bf24431ebabf62faebfd862685e9e7a52f5e799b11488dc7fe","tgt_lang":"fr","translated":"Densité de carte confortable","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"11b685e8bc937c3f3a8cccf7984408a1803d5bca4bddfce6bf1f74d1bb994cea","model":"gpt-5.5","provider":"openai","segment_id":"tabs.mcp","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"MCP","text_hash":"53f13ae99ed53bd346eb8e1c8cefb7ef8260683b50401caf101360967ea052aa","tgt_lang":"fr","translated":"MCP","updated_at":"2026-05-31T05:36:40.482Z"}
{"cache_key":"11f38606779c84950d52fe00444b6a7cc0cafda8ebfb556516ffc3c1787f89b8","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.cron","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Cron","text_hash":"dd9d24965dbedc026915308732b77c1af68dcf52d3c0ca2421b1fdb0d197aca1","tgt_lang":"fr","translated":"Cron","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"127526bce2459285625f3d800371e550e4d1d76f82d98c4b50a42eb9584a4d0c","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.refresh","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Refresh session workspace","text_hash":"c7a97b20a3a3ce348239c4893c99f1902d44877567cb32f752c30cdfbc9a2468","tgt_lang":"fr","translated":"Actualiser l'espace de travail de session","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"145f6ee918a43d1b18f1b804a46bcd53be629bf767c419bfa9d024df0078444a","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.failed","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Failed","text_hash":"031a8f0f659df890dfd53c92e45295b0f14c997185bae46e168831e403b273f7","tgt_lang":"fr","translated":"Échoué","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"16a93b1f00240d80d35b21351370279d6a3cf9c2a9df7129db4681c9d706e094","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.empty","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"No managed worktrees.","text_hash":"67f97698da5ed0bb1cc4cadd157e403c0fcd22fc4735d8f39bcf27ea6dd612c8","tgt_lang":"fr","translated":"Aucun worktree géré.","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"193a027d27061ee7de015b356ae38b8e2b41f3d1b6c6c25b62bcf92b835cca1e","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomationTenant","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Tenant: {tenant}","text_hash":"e896dc96a6847d7aaa593069e890e7a712fd60d7be60280ee24e1942e10411b0","tgt_lang":"fr","translated":"Locataire : {tenant}","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"19a2dfca17792e57db3d8190d8446902ff7f995966691a370b575e79d84ac0f6","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailNotePlaceholder","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Add a decision, blocker, or proof note...","text_hash":"0e40ea8371be2fcbd8379458b0da541ca0dce5dc86357dea64a4d8fac1c742dc","tgt_lang":"fr","translated":"Ajouter une décision, un blocage ou une note de preuve...","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"1badc8ec37f3de047ab11f96a0029ddc3036aaa079b38099c789876304b8f1b0","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.expand","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Expand session workspace","text_hash":"ac1d210db40c5026879774849ad74a9e1247523192a795ac33965b3ee72691c2","tgt_lang":"fr","translated":"Développer l'espace de travail de session","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"1cc235967bf9e738046472d651b35350a9cc43cfa4be1c9021cebab91911863c","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.artifacts","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Artifacts","text_hash":"314ae71b8c8dc9c952f0ffc58e35e6d9a41b5cf4756471c7cab0c9476cd5d20b","tgt_lang":"fr","translated":"Artefacts","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"1dbcda198a2075b61f25320ebf6da48377dbfdbe02a12167d2e55071744d8441","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.active","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Active","text_hash":"92340695899bd2d86223e4a007620e0d6502fc0e08809773634c7e0743764a9c","tgt_lang":"fr","translated":"Actives","updated_at":"2026-07-06T08:42:28.841Z"}
{"cache_key":"1dfd8f335c574b5b17d3bc5d093241f9a55f81b3e843b363f56688b9eb6939c5","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.dependenciesBlocked","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} blocked","text_hash":"fb39869b0fb3b8933126014e5c3739d7d67a620b8369781ca27e7395c595bde8","tgt_lang":"fr","translated":"{count} bloquées","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"21e8c75a1d1195121d70203fc266bcbc03b756e14a84533ff18c2292bda91f4e","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.status","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Status","text_hash":"920e413c7d411b61ef3e8c63b1cb6ad058d5f95f8b481dbafe60248387d8c355","tgt_lang":"fr","translated":"Statut","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"2309c1ac36a8a19d601737e764f6784226f2ba21edd0c8cda475fa7a1510255f","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.pairingQrExpired.reason","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Run /pair qr again to generate a fresh setup code.","text_hash":"876a304827f54ae5996c4e804aa72953f43568d31e8a15dd2a5b5a40d91c13d3","tgt_lang":"fr","translated":"Exécutez /pair qr à nouveau pour générer un nouveau code de configuration.","updated_at":"2026-07-01T10:32:03.163Z"}
@ -40,9 +43,11 @@
{"cache_key":"41417f07a0c6ed543378910a910566667149f9b110fab6e5ffb36d64400e9f12","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.pairingQrExpired.badge","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Expired","text_hash":"424a2551d356754c882d04ac16c63e6b50b80b159549d23231001f629455756e","tgt_lang":"fr","translated":"Expiré","updated_at":"2026-07-01T10:32:03.163Z"}
{"cache_key":"424f9b28aaa8357013433e8caad46c51ad5141083f8de91f9215824cad9d34e3","model":"gpt-5.5","provider":"openai","segment_id":"workboard.eventOrchestration","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Orchestration","text_hash":"ed4fdd1721677737cffb2862fe34d5b63901c7cc76b8c67c51e92a467b31a5e7","tgt_lang":"fr","translated":"Orchestration","updated_at":"2026-05-30T15:38:23.998Z"}
{"cache_key":"4255a719f4f69f4be7edecc3dbdc5279ceb09478ea0e2d4f428d4b10b5dd6740","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.parentFolder","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Parent folder","text_hash":"158f5a01ef8cfb1e6d91f8c321dd3a63f5e457f9650eecd662857701762bd31d","tgt_lang":"fr","translated":"Dossier parent","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"4348d8b8b481e750f43855decc77578211c8b11edff0aab7474256f612664206","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.subagent","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Subagent","text_hash":"d6cb4188b8fa57aae3e4ca3a1210c9afe7ca995375c2fb36d90a1fa73529a44e","tgt_lang":"fr","translated":"Sous-agent","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"44af3c13b3d3654d758b55ff2be9d8ad98ef796691eaab42a796a2434baa2c7d","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupByDate","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Date","text_hash":"99c40ab405926cb5ad1def9cff4d7ce624f8f8abfff4e85f655347fcb949d08e","tgt_lang":"fr","translated":"Date","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"45f77c4535f74f1e1b676ac2ecc391404f91a9c8b4c84ffc37d34273717001c8","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.emptyFilteredHint","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Change the view, search, priority, agent, or archive filter.","text_hash":"049dfae940263ace9707334af06b298c1223c38a449b1cec5a712553badebbd0","tgt_lang":"fr","translated":"Modifiez la vue, la recherche, la priorité, l'agent ou le filtre d'archive.","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"4667b37b6fd6426af22fc905b46caad8f5b3f36ea33cf521c3e3ea151c9e0aeb","model":"gpt-5.5","provider":"openai","segment_id":"workboard.eventProtocolViolation","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Protocol violation","text_hash":"367bb2052963f7d75beb672d3ca0430d7d49ac48a2759d578c7df933178fe564","tgt_lang":"fr","translated":"Violation du protocole","updated_at":"2026-05-30T15:38:23.998Z"}
{"cache_key":"466df8deac3e9fc53aad06b96fa1049818b2b4800029e9a7cd875c36b1157d30","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.emptyRecent","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"No recent completed tasks.","text_hash":"71aceaf6accb5308950898b4d2fd7d938fd190cc7cf6314f000466577ed8de24","tgt_lang":"fr","translated":"Aucune tâche terminée récemment.","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"47100c749023e0e173a6e87f2187d80d2925ee743b18a6e6a32f7da166feea49","model":"gpt-5.5","provider":"openai","segment_id":"chat.archivedSessionDisabled","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Restore this session to send messages.","text_hash":"c21bd35c2bef2266f419cbb4214cfd7b0af89386aef88ec8b11837844240e0d9","tgt_lang":"fr","translated":"Restaurez cette session pour envoyer des messages.","updated_at":"2026-07-02T14:30:13.470Z"}
{"cache_key":"47e30e6a54e4acb67442f35402515c1a99e9e6b78ab66d45fe0e92895426b8a5","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateNoActivity","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"No activity","text_hash":"0cf9505f9f97c8359cc143ba3e88bedaba3a4ae92c2794ffd1b097270732ed18","tgt_lang":"fr","translated":"Aucune activité","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"4824a546c58c73b812aefc3b0586dbbb84dcf0416d42300c89fb365b4cebc002","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.preview","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Preview","text_hash":"324b134f57c70c729ae3dc4d298bb451656717d70523e942c1ce667b8024ea07","tgt_lang":"fr","translated":"Aperçu","updated_at":"2026-06-16T14:14:40.922Z"}
@ -53,6 +58,7 @@
{"cache_key":"4b5db0ad286694866b63bc0cc6e371abefbff497099982cfc517f7681ede0e9c","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.path","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Workspace path","text_hash":"1fddb73e40f0f5cc6fbf747930a11f857b7a37991caeb4d8677433bbc50a2a38","tgt_lang":"fr","translated":"Chemin de l'espace de travail","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"4d1c670522c8374ec8bf4004ecdf2df6153c2553637f3f5e0fe4e242c5928673","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthStale","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"stale","text_hash":"a03f2386ae06b21109577020844df367857b72c2fcce384c1896fed98a89c82b","tgt_lang":"fr","translated":"obsolète","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"4ff4268771ec7e318270a2cbebb0038128971ca954bedf744519ced4144a4f87","model":"gpt-5","provider":"openai","segment_id":"usage.costWindows.perDay","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"/ day","text_hash":"122faff7033fbaa4fac55b95788a16f370e13ab272d734f33bfcf15021170fe7","tgt_lang":"fr","translated":"/ jour","updated_at":"2026-07-05T20:24:32.108Z"}
{"cache_key":"508d8ba945c4b7079e6b27b147be30042dc3df0a3684b8d0469ae467860f7119","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.untitled","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Background task","text_hash":"dba3626059c35bd2e98b0d10db53d9832106dca8a364c3f6106f2788b4d032c6","tgt_lang":"fr","translated":"Tâche en arrière-plan","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"5122b87f1257c4a84049372ffc6c6a24836fbb9f18a58407350c39b1a54f3020","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupByKind","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Kind","text_hash":"f5387f9bb6ed70315a77fdcb9335facc27a9bf241f35955bd2755c55e0c016c7","tgt_lang":"fr","translated":"Type","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"5141910c57fb2558f451f18f8b90bb0f60eb86a6b8e0dc6de89ad93b5be8cf44","model":"gpt-5.5","provider":"openai","segment_id":"workboard.badgeWorkerProtocol","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"worker {state}","text_hash":"f16b9e04d42182b421ce4f4e982b2ef75fab9bd581bdc8b87e62899ba28de11c","tgt_lang":"fr","translated":"worker {state}","updated_at":"2026-05-30T15:38:23.998Z"}
{"cache_key":"52b016beef2059b321a9cba3b7eecebe951c89414837597b03641e763ad0a422","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupRowCountOne","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} session","text_hash":"c0975b42c84d7da963f4c962d1e2c1ee78eb18efc7ebec75fb3ce761ce9a40db","tgt_lang":"fr","translated":"{count} session","updated_at":"2026-07-05T14:39:49.624Z"}
@ -64,6 +70,7 @@
{"cache_key":"5b2e0dd2b5bb202e6ecef6fa600de352d52bd94c344b9d543deffdf7563d2456","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.artifactCount","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} artifacts","text_hash":"022b6b55a10f1864b7aff7c307e49ab37e11e6999111fb87349f040d100abba3","tgt_lang":"fr","translated":"{count} artefacts","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"5bb0b721ebc755b2676aa27b98c9f23bbd760718c50553f2c4210ae61e4d07b6","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.confirmForceDelete","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Snapshot failed: {error}\n\nDelete without a snapshot?","text_hash":"200ce9b8fb04659df79e1d26e69ec1014631ad5a85a92130dbdb9fcb550ba34f","tgt_lang":"fr","translated":"Échec de la création de linstantané : {error}\n\nSupprimer sans instantané ?","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"5bc8019001e7a0367c3f99398dc9c2ce155837a5b2c710ba829542c3c185d2cb","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailOperatorNotes","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Operator notes","text_hash":"7d2a121620cebfb9c4f6c0f82b693b75d65a4210b8232d77ef87e45fce334347","tgt_lang":"fr","translated":"Notes de l'opérateur","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"5c307aff8a311320396f004379dfae07eb259b5d571aeec5be1d9434cf434891","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.completed","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Completed","text_hash":"22a970d2e5b1cc233e462be7c7b64e135a275bb09d83d87683bf4236c43113a1","tgt_lang":"fr","translated":"Terminé","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"5d00df2458f95482fc2608b8b57fa266f2e259785acb0d5c8e1eb5221c498b06","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailTitle","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Card details","text_hash":"93985f84673405070ffdf7e6f64175caff0f2c489c10e40627718525e79af631","tgt_lang":"fr","translated":"Détails de la carte","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"5e558b657ccaf02af17707249b719ad1bd7debb134f069adffbf755d8c9aff15","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailDiagnostics","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Diagnostics","text_hash":"268f14bbfe119c1e92150583af960a086d7db9619a097f8aa72ff6779842f610","tgt_lang":"fr","translated":"Diagnostics","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"5e7423e31cb02ad7039dc5457bce70cf780cdec5d8999a83cc1c7f3392bd8e3a","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.composer.dismissTalkError","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Dismiss Talk error","text_hash":"72f032a5a37e7197cc94ea95f5da0829abb2262396cdcc35229bd8ce9a52de1e","tgt_lang":"fr","translated":"Ignorer l'erreur Talk","updated_at":"2026-06-16T14:14:33.243Z"}
@ -78,15 +85,22 @@
{"cache_key":"6a7849ff15ea111f06a321fd96464667ee69938ab69a94ba33a5b6b02ad7f386","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAddNote","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Add note","text_hash":"63565c0485fec4f743719849734553a5d7947f5962ec9e831e3bce131b3c47fb","tgt_lang":"fr","translated":"Ajouter une note","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"6abe60843e794988fefd7905ac60565e6a5f0e835efd5548fdc85bae0396dab2","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.layoutCompact","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Compact card density","text_hash":"2f532993d5a6ccda4d758c7ecdda8bebaa857218045a3d4a011fec73d9728785","tgt_lang":"fr","translated":"Densité de carte compacte","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"6b3b656273d0f13e29ae1e0286cf8c5cba6662e46f6657d1108bb9230565cc7c","model":"gpt-5.5","provider":"openai","segment_id":"workboard.dispatch","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Nudge dispatcher","text_hash":"c3d20147447cc75f5e1e8cc895af0bc287a4a720372aec4795c5dfbfa9eeda67","tgt_lang":"fr","translated":"Relancer le répartiteur","updated_at":"2026-05-30T15:38:23.998Z"}
{"cache_key":"6b5bc1b76cbb911552e1681c93aa43d7a30e0a9e0740ccbcffdf834a6fb51f73","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.openSession","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Open session","text_hash":"b205bb47f81a30968789eac28cefb848c4b849245d4d12f9311557c5f56ce770","tgt_lang":"fr","translated":"Ouvrir la session","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"6ee4700695cb437e9ca635a8d61793548a701d4755daf6a71484698aa6533063","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.copyPath","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Copy path","text_hash":"720ff4160412b943370afdb8fc1c082ff057d54713d5fb4b4b7a9634bfabf5fe","tgt_lang":"fr","translated":"Copier le chemin","updated_at":"2026-06-16T14:14:40.922Z"}
{"cache_key":"6ef2cd73b1ddfdd8b71f51ed60a7b9779cfdf83dc16cdd7bbc7ce8f8eff21759","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.acp","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"ACP","text_hash":"75ad69d7586c3d7e42c1ac14e80c7938dc0e7413f7f6f867c3be14d5304cc66b","tgt_lang":"fr","translated":"ACP","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"7002d22e03ddb1e5bcc6c8e00a5f82751bdaa35003b2f337f8e71a1ab891e77b","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.taskCountOne","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"1 task","text_hash":"cba293c13f302204af2ae5b202d80ea840fdf1cf7904d59e1a62efbadf1e5256","tgt_lang":"fr","translated":"1 tâche","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"70ffd7a78b610cc2643444d10e4aa03c2e553a1166ecc02d6b54c9a309df2ab3","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.agentFilterUnassignedHelp","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Cards without an explicit agent.","text_hash":"f716a36252b33511df056fe7d1092be598eca17ea76bedc5d6d3532ec6b0ffea","tgt_lang":"fr","translated":"Cartes sans agent explicite.","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"7326bfc1b896e188b85434fa1b3660fc59c02c85b1cc1d814063eb0c84bd71be","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.newGroupPrompt","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"New group name","text_hash":"cee9e709525c90b1a97dff72c62082602b199b54036ed573926d1b11c6d54ec7","tgt_lang":"fr","translated":"Nom du nouveau groupe","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"75eeffde33d9a4f451b77a2e7fffeb88539d8d8d32811da06ce468da6916db3d","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailProof","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Proof","text_hash":"7fbb3ccf9640651f69af3626de6836fb302a0a088c7cd27721c367b8b530e502","tgt_lang":"fr","translated":"Preuve","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"7645e0eedba437e1cac2433e0bf38b1ee2255ea40c88f6b5450bab93f24bb1ff","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.truncated","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Showing the first matching files. Refine the search to narrow results.","text_hash":"62005877ff0fc1f73ce05ca4c459157c57a8c57a3443245b1df4d3b033df98e9","tgt_lang":"fr","translated":"Affichage des premiers fichiers correspondants. Affinez la recherche pour réduire les résultats.","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"770518abebf7a78c6588cd6d026cd7691be706b273106fb1678679262ca6d737","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.cancelFailed","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Could not cancel the task.","text_hash":"604b3f1a92694f8b8ccf5cd07a47947d3cc1a4b6c0fd5719a36dba2ffbe38b17","tgt_lang":"fr","translated":"Impossible dannuler la tâche.","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"78cc82a6282b5f7b980cf96128494d5dbcf42077806877b4592b2402e06b3cd8","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.dependencies","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Dependencies","text_hash":"2e41b118eb209c139f2bcbf690486f6e1509ab978aa96feb053877a70a1a5a09","tgt_lang":"fr","translated":"Dépendances","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"7ac7d349892690195de768cfaf98b198a274dab96b92406c199bb3f83564b28e","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.cancelTask","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Cancel {title}","text_hash":"74513d73b6ce74627b24c7f8a82fc52ffb27f69f27e07ecc6efbb64f25d4180d","tgt_lang":"fr","translated":"Annuler {title}","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"7b89af9b738646ccb50364ac9873eeadadbbe27bc01d9cfbbfd36e8b55ac45a7","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.pinSession","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Pin session","text_hash":"813273b54d2df112a0fa1903110e9386779f8848ae288142d3f91d7a5891c8ff","tgt_lang":"fr","translated":"Épingler la session","updated_at":"2026-07-02T14:30:13.470Z"}
{"cache_key":"7c080dbae61c58ee3f14258bdbfe35f939b47f96b2d2e89dca5d18f2bd6343e6","model":"gpt-5.5","provider":"openai","segment_id":"chat.sidebar.allSessions","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"All sessions","text_hash":"78648d4d66499d8dc19049a4e3bad87b404f99ea7a7f125ced52546e2d92bb79","tgt_lang":"fr","translated":"Toutes les sessions","updated_at":"2026-07-03T07:37:43.296Z"}
{"cache_key":"7caa9a415394d00b8d1c232cdf394ec58e5e38381f22a841b720e317c8fada93","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.agent","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Agent: {agent}","text_hash":"b0a224d2a72b2aa43d4e0a1ffa0523c8c5da621a16408810fcb0385da86054a4","tgt_lang":"fr","translated":"Agent : {agent}","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"7cab0e0501ed3357d85b25a52f5c424b26bef99c43b3af80fdf000ef13dcb74c","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.ungrouped","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Ungrouped","text_hash":"674b38cae72bb0c8be97cea114f7ce84a6ad4ae3c7f3ceb0c869d62db8e53fa2","tgt_lang":"fr","translated":"Non regroupé","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"7d635da233e17b0287c6201c6da2bd2576211a62e0d18e0601fbe917a651e484","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.loadFailed","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Could not load tasks.","text_hash":"a4d24c89cb53e14f67055c1cdc6c0f98bca7e013ad8e533cabef5d276381e106","tgt_lang":"fr","translated":"Impossible de charger les tâches.","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"7db98ed1985cdddbda35001dc08b5334f248249d3dbd05104971d0139b53e53c","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.changedCount","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} changed","text_hash":"db3cb1c116f0a410592fe8556a43513156ce84faa3b69de7e68635474b2f6a10","tgt_lang":"fr","translated":"{count} modifiés","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"7e84bdee7d7e5174f0a2263dd0ed661b6e477cfb4449c5bbccc918372248bdda","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.title","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Managed Worktrees","text_hash":"dde32010185098a47e873fb25dd99446b0cb1a75614068587f7cd0bffb5aed18","tgt_lang":"fr","translated":"Worktrees gérés","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"810b51afce34bdcaf6f590104802bc3f9aaa917675f8457b57fddff3278c64c0","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthRunning","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"running","text_hash":"c071cf5f5ed6f884cc70155b6f05f755fd46a302d05e4261b7e92ce878bbfed8","tgt_lang":"fr","translated":"en cours","updated_at":"2026-06-17T14:14:37.466Z"}
@ -95,12 +109,16 @@
{"cache_key":"862ae26ea822ed677c9389fbe4e68110d0f50d298c5d35fed4ee64bf2013b495","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomationWorkspace","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Workspace: {workspace}","text_hash":"17f5e696e557a646a9003fc8448f6f6761f5fe6bdf7478f750f471496e87c17b","tgt_lang":"fr","translated":"Espace de travail : {workspace}","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"899c54e4537943aa09fb972d4ed1160342eaa6d28da0c2d27d9c992ab5e02edd","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailTask","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Gateway task","text_hash":"6696e7c592238747dd39d7ba000db600a92f843add73ee90b028c72a2dfd37dd","tgt_lang":"fr","translated":"Tâche Gateway","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"8a407cc06ec5c609e1eee29c0d973582c258dcb68fe007a4f57ed5e71351be57","model":"gpt-5.5","provider":"openai","segment_id":"chat.composer.contextUsage.contextWindow","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Context window","text_hash":"7696d0855331622dc12438057f5509348f9d6f0ec2eb3580e18a99d31eba86db","tgt_lang":"fr","translated":"Fenêtre de contexte","updated_at":"2026-07-05T10:16:09.189Z"}
{"cache_key":"8b6ed1715f1c83100bcb5e86d65a3eaab7b42a07476aca5dbc92a25cc0a5b88a","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.invalidResponse","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"The gateway returned an invalid task list.","text_hash":"7aa61df7c36183096eba8284474d80ba8df86966ca8d8eed803e54a9fa938996","tgt_lang":"fr","translated":"Le Gateway a renvoyé une liste de tâches non valide.","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"8d53af57f69db68d6c9c73501a4cccdb878660a2f410f8b62bd255391be33753","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.agentCurrentUnconfigured","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{agent} (not configured)","text_hash":"d138ab0079dea760c723d7c947d0c31178252e28e7dd70a40b9d3d85e5549b1d","tgt_lang":"fr","translated":"{agent} (non configuré)","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"8ecafab89950c6b64ae2da092f1ff324f73026c41d9a7748b334114b36921bfb","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.last7Days","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"7 days","text_hash":"7f920bb639c9307589b65e5f639391d65dcb86b0611ac47f58f7c769215326ee","tgt_lang":"fr","translated":"7 jours","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"8f06525900f13a5e36695bc02d3ac91aae42b666b37b9a23caa9096441ffd1b3","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewStale","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Stale","text_hash":"40c9e59c5e152b0ae9affc84d8461c29b75a7709e4506307eeedf246b526014e","tgt_lang":"fr","translated":"Obsolète","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"8fb42eb44abd2bd897ce802bee490cc134a0c87388361cac93acd9252258f5cb","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.cli","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"CLI","text_hash":"e759793341ff3757eaa76e814db0170ec13b6b0a988a742d9a81240c505f48ec","tgt_lang":"fr","translated":"CLI","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"8fbe3ddaee1855b6f3532db5de8e15561946607e558f67713b5d697ef238891e","model":"gpt-5.5","provider":"openai","segment_id":"subtitles.tasks","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Background tasks: subagents, cron runs, CLI.","text_hash":"86b1cdd143e0bee775431403456aa967dad1c7f0a952e10200c332d8ba382d35","tgt_lang":"fr","translated":"Tâches en arrière-plan : sous-agents, exécutions cron, CLI.","updated_at":"2026-07-06T08:42:28.841Z"}
{"cache_key":"907b09795789cf241ab4c1f560071c47dca23b82e4753a7a236f92801226603e","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.pinned","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Pinned","text_hash":"f20c879465551f0d1457a13d4390d0f1ece456b115d75463169c5d55341b9b1e","tgt_lang":"fr","translated":"Épinglée","updated_at":"2026-07-02T14:30:13.470Z"}
{"cache_key":"912c845bf86c50b9569c3c3f425d1016695cd0c3897f1d7a178d7b943609248b","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.lastDays","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} days","text_hash":"e9f0a85930cc6fa61b7ac01763893020adc4c712d1b8e8897bdd13971637d529","tgt_lang":"fr","translated":"{count} jours","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"95f90b7f222de72dd86b680cc5c17d4c88d2367c3de07c8111930b4cf901bf95","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.cleanNow","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Clean up now","text_hash":"da367b57478fe6da969f5ff3a78717074d7fca77a312ac2c8f77dc2f56032578","tgt_lang":"fr","translated":"Nettoyer maintenant","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"96116292d6f062e3ae27a509224a1119f36a5fc5849ca66bff3f8a2a20f1fa34","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.timedOut","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Timed out","text_hash":"9718cc761cfd5810041ef007ae258fa1308b8487d0f30c5f36a442f8fe5f76c5","tgt_lang":"fr","translated":"Expiration du délai","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"967f8313ca28e793e44686d5cc03aa93f3e0ee149b45cde549a8292ef0244f3b","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.ageHours","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count}h","text_hash":"5828ef1c1e95e0bae1c98548d1795a2482cc8e14a8b161b183960a06018ce10d","tgt_lang":"fr","translated":"{count} h","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"96a761aa83f58ae83de0513925995b1e4d6b2b889c4b62651a87ac6d2763728f","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.inputTokens","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} input","text_hash":"f24231cff78fed82d155712973ede6f9369e96b015acc30d5de2b740677edce9","tgt_lang":"fr","translated":"{count} jetons dentrée","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"985c7bf9ceb4920aba727dfb9a3e2faecf627f12446dd21242781739b0969454","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateToday","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Today","text_hash":"2b065c7c9ce466e5ebcad757987d5d660ee4c9ea708bc62c43444b53334738ba","tgt_lang":"fr","translated":"Aujourdhui","updated_at":"2026-07-05T14:39:49.624Z"}
@ -122,6 +140,7 @@
{"cache_key":"ae1c4f710fd0b813ec9fc5f7de8294f953bad91984d826da10faaabd81ba9ae3","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthMissingProof","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"missing proof","text_hash":"748797f5ab1c31c8aeeaf7f76bce76064b175a1d1f530849ec683cacbe6555eb","tgt_lang":"fr","translated":"preuve manquante","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"aebc06b31bd0f72c34dc505ef1571fa58d518362ac30fa609967bd4f60484072","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.topModels","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Top models","text_hash":"79489561d9efe32b89add781323318355ae49e3d71f9c5a45a34c21825778663","tgt_lang":"fr","translated":"Principaux modèles","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"aed90dc6280045598d163409ef823bbd1c0ae4cae6fc9755d7ff296bea88dbb8","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailRun","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Run","text_hash":"00d60e31a4e6b8344d4201f25a6a7dee770713107f6d097abb01559d32b17f26","tgt_lang":"fr","translated":"Exécution","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"aff5a1544cce789af1653110d9185d1bf578e02438f504fa77af76ff73c3fb3b","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.cancelled","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Cancelled","text_hash":"d353a99eb4556847ceeb3c306ac886cf6b4fa13043eb1864b9aeb0607fd5f2cc","tgt_lang":"fr","translated":"Annulé","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"b0c168a2e6ac1dbd6a08f25d2564be870ca7b20f3ba9f35fbec1f71e15ba6e6b","model":"claude-opus-4-8","provider":"anthropic","segment_id":"cron.jobDetail.cwd","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"CWD","text_hash":"0217f1cb7725737f15a6710df3bcfa3bc10a239f0f7801ec3d7168e675f5ebd6","tgt_lang":"fr","translated":"CWD","updated_at":"2026-06-16T14:14:40.922Z"}
{"cache_key":"b0d2c0967af01f3d8dad7b872d72255bff91f9e1491d950500305cdda3606901","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.cacheTokens","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} cache","text_hash":"9d5f9230d1dea8b0d5b0f0705199920c0be54b3087c4f9d7fb4014284623eb49","tgt_lang":"fr","translated":"{count} jetons en cache","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"b0eb0963aaaa5eacdebfc95b0b1bb553369b2a5922194829e8df941bac36c3a8","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthFailedAttempts","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"failed attempts","text_hash":"fd9023af0795825a458100ddbe894a7a8f603324a2b7ad2305d4c9d2334cbd26","tgt_lang":"fr","translated":"tentatives échouées","updated_at":"2026-06-17T14:14:37.466Z"}
@ -134,10 +153,13 @@
{"cache_key":"bc9904c49788d97a2f27902a8f087c23d266ef3ca0e6d6c96df9547dbe90f54e","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthReadyUnassigned","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"ready unassigned","text_hash":"1e1a31a02e9da6ffa99a459bfd82e5af723794c2175405a5a0d6a32afa955167","tgt_lang":"fr","translated":"prêt non attribué","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"bdfdbb3aea12cc2a90a8dfed0804800180dd1ea048727b8b9772244fffd4a2a1","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.refreshError","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Refresh failed","text_hash":"8fa7e6d90bef4e5cb735233347bf6a71b5b30d96e7c1a50b73f10cb441b275c2","tgt_lang":"fr","translated":"Échec de l'actualisation","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"be27e99ebe691dae16cef909c18ff5292aca21fa9123aff5b3374f2a9c88ffd5","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.lastActive","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Last active","text_hash":"bcdf701c4dfbaee3a2162f9b9affd87a23a13426c391f95e964d983851b58a05","tgt_lang":"fr","translated":"Dernière activité","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"be6a92d80f86e7f0517082527765d3fb3c9fc2b6e5018a4af2347e5879781b57","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.empty","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"No background tasks yet.","text_hash":"e920d0a7849ab499c0eb22fe353bad656795e9a48c55e72edc58a04e2dff58b1","tgt_lang":"fr","translated":"Aucune tâche en arrière-plan pour le moment.","updated_at":"2026-07-06T08:42:28.841Z"}
{"cache_key":"bef535e7635a45bf77d764f80a9e2b7dc173fdeea801e0039566227172ea9dfa","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefresh15s","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"15s","text_hash":"21b5f52ded33ab19c16a680c4e280b8f9992395b514290163abf272f06394a6f","tgt_lang":"fr","translated":"15s","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"bfad2f9e64a059ffbb82f9ff0cfcb05729f7c99e7703fc2c3c19f7493871e7fa","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.cancelling","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Cancelling…","text_hash":"91b104db05da1b2d48c57a5aa60128f660e6572f89835ec858f6eb25b8f4af0f","tgt_lang":"fr","translated":"Annulation…","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"bff9e9d6c21068b2333181dbd4976ef3056cdd8d502937aa49a1bdbcc752007f","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.pairingQrExpired.title","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Pairing QR expired","text_hash":"74e09eafc1d35cad5b62b7a9c321a4d090bb8fefdfa8b33913d6194186eadda6","tgt_lang":"fr","translated":"QR d'appairage expiré","updated_at":"2026-07-01T10:32:03.163Z"}
{"cache_key":"c2ad01204fbf7466b5e4e7a4f67f6acbfcce78e915e9feb47107250b068b6c05","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewRecentlyDone","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Recently done","text_hash":"46b889592a2f5e79197f62b5f96c76993545626bf207740ea58632ceed9623be","tgt_lang":"fr","translated":"Récemment terminé","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"c31d13e27ac0ec0fb45eeef6fb0ea05cb17b7bad168694323a9f2a1ce0d9e743","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.name","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Name","text_hash":"dcd1d5223f73b3a965c07e3ff5dbee3eedcfedb806686a05b9b3868a2c3d6d50","tgt_lang":"fr","translated":"Nom","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"c3f3445804d6d40711ed4f179aae2863ee1d667dd2e9cf44131128b741140937","model":"claude-opus-4-8","provider":"anthropic","segment_id":"tasksPage.status.running","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Running","text_hash":"f4ccae29e1bb0c20a124570a1b43f4347ea94bba9f84ffdfddd9c7445b126128","tgt_lang":"fr","translated":"En cours","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"c474a1515a769d2f83c2b5f640c81eab4ee4644270f4742822080e64fd49c6df","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewMissingProof","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Missing proof","text_hash":"b46debe888e32eec183dc5936c79d22ea43bec580c410c2b3c1aa24aaa75d677","tgt_lang":"fr","translated":"Preuve manquante","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"c4a7e1723aad5a006fede7d87b811516a2650ca61ea80d078f07918f5947b19f","model":"gpt-5.5","provider":"openai","segment_id":"workboard.badgeAttachments","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} attachments","text_hash":"7bb1847693bc91e6e4624d996a96840396a71052786ab143ccb47fbdaa77cf41","tgt_lang":"fr","translated":"{count} pièces jointes","updated_at":"2026-05-30T15:38:23.998Z"}
{"cache_key":"c57b6089a8064cf26172436a1e12bc7cc7b9a38b747a13ea3bb5f69143dac266","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomationBoard","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Board: {board}","text_hash":"96d7493589e40e17803b3bf643dff1b891a4ebf57f5d2b36af0a7ddd09e64b84","tgt_lang":"fr","translated":"Tableau : {board}","updated_at":"2026-06-16T14:14:26.985Z"}
@ -160,22 +182,27 @@
{"cache_key":"d321120597c21a4c2112eefb541b872d45e34c82c6c3136bc5324eada06c8873","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.readCount","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} read","text_hash":"b3c6c64f1153fb7b2672d2894f532d3f7adea1dd1c473363587fc520be35998e","tgt_lang":"fr","translated":"{count} lus","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"d4042ae3c9008b6e58effba20022bc8fad3392ca9ba50876b80b28b401ff4184","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomation","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Automation","text_hash":"d909750b1bbb71a39b6330ba8f81f4f8f6e889ed96d7ab366e74857909750c64","tgt_lang":"fr","translated":"Automatisation","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"d438054e1f4e795992b67e7412526e77f0893d60efab62412cee2926bcb3a828","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateOlder","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Older","text_hash":"03281c889c2869e091390f9ad5dd13f0f0e46b42c9c4698f857902451deb3450","tgt_lang":"fr","translated":"Plus ancien","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"d4e165aa087815a6e72c5641ead31812ba3373dc8a07000bb9f3d9caef7df0e0","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.recent","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Recent","text_hash":"690dbe9dc0993c4256683738fc3fd541cfa96f60d299be33343615dd58179d93","tgt_lang":"fr","translated":"Récentes","updated_at":"2026-07-06T08:42:28.841Z"}
{"cache_key":"d7e7c590ca7259006bed696ffefdc1ca41205fbe47894658e54f1d2b0c939bcf","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.unpinSession","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Unpin session","text_hash":"f4c582ee4d7a87bf069d05a49bb0211759d3db8366daab4ba7024425961af5dc","tgt_lang":"fr","translated":"Désépingler la session","updated_at":"2026-07-02T14:30:13.470Z"}
{"cache_key":"d87387cd9d519bf878b4f986dab78f4bc45b42e10890ac88257a49a1a3940419","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupBy","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Group by","text_hash":"956a51f6b098a41b7c3108015f0790bb24af7693717b07cee39d5df6a5da1826","tgt_lang":"fr","translated":"Regrouper par","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"d8b82816b2447f9cdf8c1f6e790726787cc313c2e9369bee4b5573e70acc0dad","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefresh30s","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"30s","text_hash":"d3382a4f0e03f8b14cf99424376886c236f1503d4b332137667484fc96d58fc4","tgt_lang":"fr","translated":"30s","updated_at":"2026-06-17T14:14:37.466Z"}
{"cache_key":"dba4de5b395e649c53c56af6e8683b3ec249196ff14953ec51d61e7dd68914e7","model":"gpt-5","provider":"openai","segment_id":"usage.costWindows.lastDays","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Last {count} days","text_hash":"4aa456a0fa9b73dcc14766740b19fc52c452950ccb7bc892499c3c29a4122162","tgt_lang":"fr","translated":"{count} derniers jours","updated_at":"2026-07-05T20:24:32.108Z"}
{"cache_key":"dbcc1e6ff0588e27c2292f68df4039426a50b65b151a4cd043db53a9476c8090","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.unknown","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Task","text_hash":"4bc74b21357c6cf5cca8f66b4d4ee948be64d0396feb434c9645e168ad61ceaf","tgt_lang":"fr","translated":"Tâche","updated_at":"2026-07-06T08:42:32.187Z"}
{"cache_key":"dcb48055b5aafa2813da3820617d5c5653945c57dfbf0d6cb81d4c7f74049b43","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.noBrowserFiles","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"No files in this folder.","text_hash":"3847020c79b1c74e28aa550f0ae53838b764e87f1daf1480dd6aae45ae0529d6","tgt_lang":"fr","translated":"Aucun fichier dans ce dossier.","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"dee70ec60c911896eff244b9424d125be13ed5078065ad7ad11270460cad9168","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.label","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Session workspace","text_hash":"c0e8ea0cf983d14e8ba3f8fc28976954d637fd50f807c69e9695715fd2384b78","tgt_lang":"fr","translated":"Espace de travail de session","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"def903c07d7e651f99d9b009d9e1b6ed6984186c0bec8ff5314a8341032a6609","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefresh","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Auto-refresh","text_hash":"9ea4d7fd1550f0866089d18b1344546bfed91502b41c0484d6023ceb0fdeb75c","tgt_lang":"fr","translated":"Actualisation automatique","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"df7cd4128b09c7b3fa344f5ad65e9dc2ce752edbd1dffef36237744b38f023f4","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.branch","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Branch","text_hash":"52656e8104eef3fddd3d4546903fa0de93c0625abf47b3dd8130f7705d6a513e","tgt_lang":"fr","translated":"Branche","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"e12357a302d15450b5851e2fb8cdac311db998d9f4546c5be51b78350438c03e","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupByChannel","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Channel","text_hash":"ce4683e7013a18cdf3d224bfcb4e9594ea8f559e946a837c633defe7d3c32172","tgt_lang":"fr","translated":"Canal","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"e1f8d7c3673675977feabc07b0b5324da1599ff09308fa3252af0385456af32a","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewPresetCount","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} cards","text_hash":"4b3e5442ebd2f839d45fddf95b2c2a18427dbd6ac06c8b57f9d9e996dcb73607","tgt_lang":"fr","translated":"{count} cartes","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"e281f177d74fb0da06732248aab00ce43946f7770a88e6a2ca5352403e267f4e","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.queued","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Queued","text_hash":"661ff40a07e037bbd5f7d4ec97a4df1503096ca910e7c9e2d7a4e9abd4e4e1a0","tgt_lang":"fr","translated":"En file dattente","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"e28a6c04ef3e4af3f8ea165d9cfe624d97b2de24a60779c9e6485cfac38154fc","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.missing","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Missing","text_hash":"6be36ca49ee85210c5d1ad9c377d90a9859c66d889110a2a5b0dccd390d12e20","tgt_lang":"fr","translated":"Manquant","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"e36fe7681487b782a0f7a062a7000cab29d3e6450572ea66e368a6ed0b9f7f39","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewReady","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Ready","text_hash":"5fa7aac5375c5815787fba3f49559f9b45b14023147ce0652803387974144e5f","tgt_lang":"fr","translated":"Prêt","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"e38ce4f9cf29ea514e7c70a78691168d094af6955ecb888ffcea28f0c62fac56","model":"gpt-5.5","provider":"openai","segment_id":"languages.ru","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Русский (Russian)","text_hash":"ea81bf0fd46410b501bddee074ab6f00b0cdf377a6cafe608dcf2c28f7cb2f4e","tgt_lang":"fr","translated":"Русский (russe)","updated_at":"2026-06-26T21:43:30.551Z"}
{"cache_key":"e40ff2000ab1340d5848062fc8bd54ba9bb38aa39ccb7d42cd33c8c027ce4350","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.changed","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Changed","text_hash":"2a6141e43be0c2125e3b5d9f74b4ff1261a0b320ff927c83d4d9b1b65585bad7","tgt_lang":"fr","translated":"Modifié","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"e558087a282be8525eb74ef395090ebfac5df35d2b0d19649fde4f72dbc134cd","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.dependencyStatusMissing","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Missing","text_hash":"6be36ca49ee85210c5d1ad9c377d90a9859c66d889110a2a5b0dccd390d12e20","tgt_lang":"fr","translated":"Manquante","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"e5974eebd9dde7a10ac616f085d3c37ef2648dc8d05ab48ecf75a52676d3b6bc","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.disconnected","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Connect to the gateway to load and manage tasks.","text_hash":"f809605f626a2f8eeff5c864a30e78d538a878ec5de7934f21d60bc01b81f125","tgt_lang":"fr","translated":"Connectez-vous au Gateway pour charger et gérer les tâches.","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"e601fd3af074100b5ca609dcbecf632f30e3a51fbb4c1010f61a18f17af1f071","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.collapse","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Collapse session workspace","text_hash":"b6174b882c37a98e640339d728652a0c1fa70d28ed53d8ccfb6e99363e86973b","tgt_lang":"fr","translated":"Réduire l'espace de travail de session","updated_at":"2026-06-16T14:14:33.243Z"}
{"cache_key":"e6a0e18840d6e9ddc9a732663a509ce8dc66dbf938bd2afdba5baf2a05a420ba","model":"gpt-5.5","provider":"openai","segment_id":"tabs.tasks","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Tasks","text_hash":"b3a60e61a5233d0506ac737405a2a45280349683cac68722f18d0b73eb495ef4","tgt_lang":"fr","translated":"Tâches","updated_at":"2026-07-06T08:42:28.841Z"}
{"cache_key":"e7ce17017703abfa56accdea7fd8c7cec085be211a621f43e7cebff5f58ea654","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.agentFilterUnassigned","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Unassigned (uses {agent})","text_hash":"2700af0c4ab5e86726f72a723ecdf50370b87690db35f00b83723d6457879c8e","tgt_lang":"fr","translated":"Non assigné (utilise {agent})","updated_at":"2026-06-17T14:14:32.183Z"}
{"cache_key":"e8cc817b71a29e04910077228a6a2062cc9008e42829f503ef7198b9192efd69","model":"gpt-5.5","provider":"openai","segment_id":"subtitles.worktrees","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Isolated agent task checkouts and recovery snapshots.","text_hash":"bc794dc846493e3c5f88964268af19b7dd818eae942c596002ef4067ba5a3d0c","tgt_lang":"fr","translated":"Checkouts de tâches dagent isolés et instantanés de récupération.","updated_at":"2026-07-05T21:00:55.929Z"}
{"cache_key":"ea55fad91205f9fdce8210dea4000210abdc156e2a239dd2924bdf0e15302caa","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupRowCount","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} sessions","text_hash":"27de9b3be346a2abd2cb67f9f93abfe8100d7ce996e1204b75fc84670c7818e6","tgt_lang":"fr","translated":"{count} sessions","updated_at":"2026-07-05T14:39:49.624Z"}
@ -189,9 +216,12 @@
{"cache_key":"ef8651b25fc9e66a484efa8c31484827299fa1589240dbe3f6b6faf50f8fc6d7","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.searchResults","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Search results","text_hash":"e978b00de465a271a13bf2f6b9d74d67fdfaa7d973a37378fa32f988c3280599","tgt_lang":"fr","translated":"Résultats de recherche","updated_at":"2026-06-16T14:14:38.496Z"}
{"cache_key":"efc947df0cf72da7bc32079bd35c910cfc6989e683a7e969130f567016490814","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.dailyCost","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Daily provider cost","text_hash":"0d03078a4d1fba12122e32e9abbc929ea64b948445810cf1e0d29cbdfd5cb18d","tgt_lang":"fr","translated":"Coût quotidien du fournisseur","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"f17ed95b76278c50b592087cc112d448162b91e7a486fe0f3b7c13abf341cb37","model":"gpt-5.5","provider":"openai","segment_id":"tabs.skillWorkshop","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Skill Workshop","text_hash":"3912c65bdd0a43563438762a43ecbd4b14637844a18decbf9249df73d21152a0","tgt_lang":"fr","translated":"Atelier Skills","updated_at":"2026-05-31T21:48:25.015Z"}
{"cache_key":"f35669581e6e7aa1ce077ed0861750cbea6514b670fa0186dd6d9e6ed3e395b8","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.taskCount","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{count} tasks","text_hash":"1d43774da9d4e2aabcff69b02e03591836a632f430121f8ecdaf2f115a250233","tgt_lang":"fr","translated":"{count} tâches","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"f418809d90cb219c8110d7a026431dab4a63f6a89ceca68999ac2de56d49f7bd","model":"gpt-5","provider":"openai","segment_id":"usage.overview.costShare","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"{percent}% of cost","text_hash":"1d0533da07d6ee21af9d1d02f4636bd9f70df239ad62388b0a415e550ee2de8b","tgt_lang":"fr","translated":"{percent} % du coût","updated_at":"2026-07-05T20:24:32.108Z"}
{"cache_key":"f66ed36e8fc5edf56e4c228748a9be1d51b2d4cac0e0a57fd3734517c956c69c","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.renameSessionPrompt","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Rename session","text_hash":"2cad0766accc2d3f70f007068524cea983e0d53f54543e9e82a4e73b2a07987f","tgt_lang":"fr","translated":"Renommer la session","updated_at":"2026-07-02T14:30:13.470Z"}
{"cache_key":"f77ad88f824118b4b9c4d2bedd2fde2c6f82962461ee7c45c1ea641d958ca1b1","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailWorkerProtocol","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Worker protocol","text_hash":"e445d823cfa48c4e8fa1d8854771e9939955e772428be6d7957deec0f7968764","tgt_lang":"fr","translated":"Protocole du worker","updated_at":"2026-06-16T14:14:26.985Z"}
{"cache_key":"f77f24dd6d1b674d31f32ba4a99d7f3b048128d54ce4f32e8e233027e17ed81f","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateThisWeek","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"This week","text_hash":"8c4eef5ab2532515ef24a662db70f6e5b8063c7f924342b2a463f763f1091634","tgt_lang":"fr","translated":"Cette semaine","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"faa7f865d7b3bb036dd703281b2b8efb480fac88b07808dd4ca9a7e02563fd19","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.emptyActive","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"No queued or running tasks.","text_hash":"00db4a453c2e92f4d807847fc0d8d340708ed9ab547280ce376ba1d610bcb5a6","tgt_lang":"fr","translated":"Aucune tâche en file dattente ou en cours dexécution.","updated_at":"2026-07-06T08:42:28.842Z"}
{"cache_key":"fb6686fd89bfeddf30898c0d757d0d1ddb8f99f0e963caa158bd53ad334abde1","model":"gpt-5","provider":"openai","segment_id":"usage.costWindows.selectedRange","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Selected Range","text_hash":"95917ae71066a19c266cd4530068f4bf775ed2401951ebf37ab0c91daa1a67d3","tgt_lang":"fr","translated":"Période sélectionnée","updated_at":"2026-07-05T20:24:32.108Z"}
{"cache_key":"fdf35debad083bccc14e4150ddf0d3ea9d7ab588e70751112546b618fed7d928","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupByNone","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"None","text_hash":"dc937b59892604f5a86ac96936cd7ff09e25f18ae6b758e8014a24c7fa039e91","tgt_lang":"fr","translated":"Aucun","updated_at":"2026-07-05T14:39:49.624Z"}
{"cache_key":"fe4c7b853a38db3e5948fa65bf5a02558ce5960dfeecf7bedad12fdf808aca18","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.loading","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Loading tasks…","text_hash":"9ae9f7d835d95a2cf1362c130da3a0ebacae4331dbb431e60e1735477591bf7b","tgt_lang":"fr","translated":"Chargement des tâches…","updated_at":"2026-07-06T08:42:28.841Z"}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:50.419Z",
"generatedAt": "2026-07-06T13:33:01.987Z",
"locale": "hi",
"model": "gpt-5.5",
"provider": "openai",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:51.676Z",
"generatedAt": "2026-07-06T13:33:03.260Z",
"locale": "id",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:50.950Z",
"generatedAt": "2026-07-06T13:33:02.485Z",
"locale": "it",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:49.628Z",
"generatedAt": "2026-07-06T13:33:01.213Z",
"locale": "ja-JP",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:49.893Z",
"generatedAt": "2026-07-06T13:33:01.474Z",
"locale": "ko",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:52.725Z",
"generatedAt": "2026-07-06T13:33:04.243Z",
"locale": "nl",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:51.931Z",
"generatedAt": "2026-07-06T13:33:03.531Z",
"locale": "pl",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:48.912Z",
"generatedAt": "2026-07-06T13:33:00.437Z",
"locale": "pt-BR",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:53.241Z",
"generatedAt": "2026-07-06T13:33:04.684Z",
"locale": "ru",
"model": "gpt-5.5",
"provider": "openai",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:52.194Z",
"generatedAt": "2026-07-06T13:33:03.780Z",
"locale": "th",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:51.181Z",
"generatedAt": "2026-07-06T13:33:02.765Z",
"locale": "tr",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:51.428Z",
"generatedAt": "2026-07-06T13:33:03.022Z",
"locale": "uk",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:52.470Z",
"generatedAt": "2026-07-06T13:33:04.026Z",
"locale": "vi",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:48.143Z",
"generatedAt": "2026-07-06T13:32:59.770Z",
"locale": "zh-CN",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,11 +1,11 @@
{
"fallbackKeys": [],
"generatedAt": "2026-07-06T12:51:48.692Z",
"generatedAt": "2026-07-06T13:33:00.222Z",
"locale": "zh-TW",
"model": "claude-opus-4-8",
"provider": "anthropic",
"sourceHash": "c52977f539e8b79fa9f5f3983022534970d135152f5606ce481cb8c2cd49b3d2",
"totalKeys": 1589,
"translatedKeys": 1589,
"sourceHash": "7e7cf6170ee25ffc6379def455c1170efe81475611470c86d3ac4ead09e3bf12",
"totalKeys": 1619,
"translatedKeys": 1619,
"workflow": 1
}

View file

@ -1,5 +1,6 @@
{"cache_key":"0058dd63eb3fb24da56f4bdc46818c272c7219177e42c8da79de612edbc78e2d","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.expand","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Expand session workspace","text_hash":"ac1d210db40c5026879774849ad74a9e1247523192a795ac33965b3ee72691c2","tgt_lang":"zh-TW","translated":"展開工作階段工作區","updated_at":"2026-06-16T14:13:10.175Z"}
{"cache_key":"04fc76d25faad9f51b570fb4f1f7306b7ff547836da07e12a23bfc61b65b2b33","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.root","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Root","text_hash":"44cb005ee2e65d9cc817b0a083579369fb6c24a4be728cb43fd9d4c3ca7f4c2e","tgt_lang":"zh-TW","translated":"根目錄","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"055a682eaf869d24f02cb651f6c57b56a60eec647fe40f6fb3ad55b458a8e39a","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.loading","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Loading tasks…","text_hash":"9ae9f7d835d95a2cf1362c130da3a0ebacae4331dbb431e60e1735477591bf7b","tgt_lang":"zh-TW","translated":"正在載入任務…","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"06771a5ec62ce1986d8cdc8ce5570aaec28a25e00725985bc483f5f0871db53b","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.loading","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Loading session workspace…","text_hash":"bc6b2400fad20ee1d95d8de4ec6eef9ff1818ab080f86513384029519eaf4f4e","tgt_lang":"zh-TW","translated":"正在載入工作階段工作區…","updated_at":"2026-06-16T14:13:10.175Z"}
{"cache_key":"072d8ba80b36e088ec29965d4ee60fa0cf7e0130e9a77a4504f41c9a64341059","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.lastActive","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Last active","text_hash":"bcdf701c4dfbaee3a2162f9b9affd87a23a13426c391f95e964d983851b58a05","tgt_lang":"zh-TW","translated":"上次活動","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"08982382123994fc547aa072a6d9ffbc0baf90cb80de0cba19d2c31c94a02510","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.changedCount","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} changed","text_hash":"db3cb1c116f0a410592fe8556a43513156ce84faa3b69de7e68635474b2f6a10","tgt_lang":"zh-TW","translated":"{count} 個已變更","updated_at":"2026-06-16T14:13:16.738Z"}
@ -13,7 +14,10 @@
{"cache_key":"0b9e56448e469f671c96b858dc73ae37897dc20ca920c90646a5f717635ea2c2","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.parentFolder","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Parent folder","text_hash":"158f5a01ef8cfb1e6d91f8c321dd3a63f5e457f9650eecd662857701762bd31d","tgt_lang":"zh-TW","translated":"上層資料夾","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"0d18df5d6830393c6ddbd56732339fa028e3941ef7ace89f10a8bfdda13b9fa2","model":"gpt-5.5","provider":"openai","segment_id":"chat.composer.contextUsage.contextWindow","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Context window","text_hash":"7696d0855331622dc12438057f5509348f9d6f0ec2eb3580e18a99d31eba86db","tgt_lang":"zh-TW","translated":"上下文視窗","updated_at":"2026-07-05T10:15:56.105Z"}
{"cache_key":"0e070148b7195ab0e9642937d106d4df172c9d654dcf217565f04eee4fdc5f74","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefresh15s","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"15s","text_hash":"21b5f52ded33ab19c16a680c4e280b8f9992395b514290163abf272f06394a6f","tgt_lang":"zh-TW","translated":"15秒","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"0f7e79303f419c97c2d8a8b626041b3cdaf9652d5cd91857c542dac3b3f88052","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.timedOut","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Timed out","text_hash":"9718cc761cfd5810041ef007ae258fa1308b8487d0f30c5f36a442f8fe5f76c5","tgt_lang":"zh-TW","translated":"逾時","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"1387e86e171dde748f1bf188b25d81b23ed8933cb63f841fa7f8ec34c4b02924","model":"gpt-5.5","provider":"openai","segment_id":"workboard.badgeWorkerProtocol","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"worker {state}","text_hash":"f16b9e04d42182b421ce4f4e982b2ef75fab9bd581bdc8b87e62899ba28de11c","tgt_lang":"zh-TW","translated":"worker {state}","updated_at":"2026-05-30T15:38:05.607Z"}
{"cache_key":"13da946215e3385c3a9aeeb1f848d3aa42385229923c6a98ba9ece5fbb21bc4e","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.untitled","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Background task","text_hash":"dba3626059c35bd2e98b0d10db53d9832106dca8a364c3f6106f2788b4d032c6","tgt_lang":"zh-TW","translated":"背景任務","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"13e555a572384c20d960fddf442456cc734a59a4daa9739261a3ed50a46c028d","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.unknown","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Task","text_hash":"4bc74b21357c6cf5cca8f66b4d4ee948be64d0396feb434c9645e168ad61ceaf","tgt_lang":"zh-TW","translated":"任務","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"147bf0036bab269e4e76958c888d7a598be90be9639d930333cf15e4416702a9","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.emptyFilteredTitle","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No cards match this view","text_hash":"a272617e183ba1dff3f7f140d0851b64baf95f4827ff729d23dfeb05c2069875","tgt_lang":"zh-TW","translated":"沒有符合此檢視的卡片","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"15b777d3c74a6aaefa88ea632e0db636dc1ca26a4c40943a08132573a89f4f3c","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthMissingProof","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"missing proof","text_hash":"748797f5ab1c31c8aeeaf7f76bce76064b175a1d1f530849ec683cacbe6555eb","tgt_lang":"zh-TW","translated":"缺少證明","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"1721597dddda5bdbcd49e85f8038348ca5f343e1ec730814a95aa85993633366","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewReady","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Ready","text_hash":"5fa7aac5375c5815787fba3f49559f9b45b14023147ce0652803387974144e5f","tgt_lang":"zh-TW","translated":"就緒","updated_at":"2026-06-17T14:13:17.815Z"}
@ -26,6 +30,7 @@
{"cache_key":"1eedf3a9f4a8a161a15d4187d1962531aeb80fe63e7627c31fc9038bb34e7944","model":"gpt-5.5","provider":"openai","segment_id":"workboard.badgeWorkerLogs","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} worker logs","text_hash":"2923e2a84e6ed0ca048d280206198b156da6148859e474498d335a44e323e0a8","tgt_lang":"zh-TW","translated":"{count} 則工作者記錄","updated_at":"2026-05-30T15:38:05.607Z"}
{"cache_key":"222d58f2bcd3645b5b6a572fead6894c2c46eb5a5dc19279bb6159051d6664be","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.cleanNow","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Clean up now","text_hash":"da367b57478fe6da969f5ff3a78717074d7fca77a312ac2c8f77dc2f56032578","tgt_lang":"zh-TW","translated":"立即清理","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"239830e854cd40a1a174c34d37efde05b8b2c8a469068a0771ea117cf4642af6","model":"gpt-5.5","provider":"openai","segment_id":"workboard.eventAttachmentAdded","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Attachment added","text_hash":"f39a309fb0054d8e6c512733d6f3a4791c6b63157a388d72f635574d98b49b3e","tgt_lang":"zh-TW","translated":"已新增附件","updated_at":"2026-05-30T15:38:05.607Z"}
{"cache_key":"246bfe07ee9295208d03e36bf4aba3e40a6d21d2ac69af28ccfcdb3420b4a6b3","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.agent","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Agent: {agent}","text_hash":"b0a224d2a72b2aa43d4e0a1ffa0523c8c5da621a16408810fcb0385da86054a4","tgt_lang":"zh-TW","translated":"代理:{agent}","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"267cd9e2120e64ee28fc527714522de3b91c2a4f729ccd7c267e3c63dcd7c9a4","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.path","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Workspace path","text_hash":"1fddb73e40f0f5cc6fbf747930a11f857b7a37991caeb4d8677433bbc50a2a38","tgt_lang":"zh-TW","translated":"工作區路徑","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"26b24ef4a9d46efa15c790026e63b376dab6ad86cd0a5dc2f089ec35a893195e","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.restoreSession","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Restore session","text_hash":"35e4dc9befd3b3a29b50938839af0efb41b08051988dde53e1c1e73d1c2a039f","tgt_lang":"zh-TW","translated":"還原工作階段","updated_at":"2026-07-02T14:29:59.020Z"}
{"cache_key":"281ebd5735d09792156416816e295aabf6ad64a80038a9f5d16c1aef9bd1c8bc","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthRunning","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"running","text_hash":"c071cf5f5ed6f884cc70155b6f05f755fd46a302d05e4261b7e92ce878bbfed8","tgt_lang":"zh-TW","translated":"執行中","updated_at":"2026-06-17T14:13:23.259Z"}
@ -34,6 +39,7 @@
{"cache_key":"2b4270d6ba9f28986c52dcc245aa5e65c336f25d206447290983fcc04e0d7db7","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.newGroupPrompt","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"New group name","text_hash":"cee9e709525c90b1a97dff72c62082602b199b54036ed573926d1b11c6d54ec7","tgt_lang":"zh-TW","translated":"新增群組名稱","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"2dcadb6a56d4dae637c2be124e0077eaa96831f70abeafe9eeb50d47de750d67","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.layoutCompact","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Compact card density","text_hash":"2f532993d5a6ccda4d758c7ecdda8bebaa857218045a3d4a011fec73d9728785","tgt_lang":"zh-TW","translated":"精簡卡片密度","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"2e3c5d81d525ededb9f9fb2b74cd666a54ea490456bb465e19a4ca06dbf823d2","model":"gpt-5.5","provider":"openai","segment_id":"workboard.badgeAttachments","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} attachments","text_hash":"7bb1847693bc91e6e4624d996a96840396a71052786ab143ccb47fbdaa77cf41","tgt_lang":"zh-TW","translated":"{count} 個附件","updated_at":"2026-05-30T15:38:05.607Z"}
{"cache_key":"2ff7826aeeb2445b9410639a3d15efee7077877e037fa6b3c5bdaeb089219ff8","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.emptyActive","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No queued or running tasks.","text_hash":"00db4a453c2e92f4d807847fc0d8d340708ed9ab547280ce376ba1d610bcb5a6","tgt_lang":"zh-TW","translated":"沒有排入佇列或正在執行的任務。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"3079b1a8bf5ee737871de1a3c7683989f4459c96fa92db422a76603e2a92d748","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.changed","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Changed","text_hash":"2a6141e43be0c2125e3b5d9f74b4ff1261a0b320ff927c83d4d9b1b65585bad7","tgt_lang":"zh-TW","translated":"已變更","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"30a2bcf0485a73ede97f894f87ba3fb9852fc44f692a63c20dffb0ca7b0e5202","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.emptyFilteredHint","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Change the view, search, priority, agent, or archive filter.","text_hash":"049dfae940263ace9707334af06b298c1223c38a449b1cec5a712553badebbd0","tgt_lang":"zh-TW","translated":"變更檢視、搜尋、優先順序、代理或封存篩選條件。","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"354885e514d3d7e78e589fb33ff5e6aea796e7ccf065b94443f22a804f43f799","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.dependencyStatusMissing","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Missing","text_hash":"6be36ca49ee85210c5d1ad9c377d90a9859c66d889110a2a5b0dccd390d12e20","tgt_lang":"zh-TW","translated":"缺少","updated_at":"2026-06-16T14:13:10.175Z"}
@ -41,6 +47,7 @@
{"cache_key":"37db510b10e60bebd333f2087cbcf645bb39dc0edaa1d4c8b14d4151ade20ebb","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.summary","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Session workspace summary","text_hash":"1ed422c34dc1802d4c7366164ae810c496e206fe82e8e6565cefc38230b56bb4","tgt_lang":"zh-TW","translated":"工作階段工作區摘要","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"3895e70568e93923af79ea30a018187ad1e81c5d46af82198cf045b94285d70b","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateThisWeek","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"This week","text_hash":"8c4eef5ab2532515ef24a662db70f6e5b8063c7f924342b2a463f763f1091634","tgt_lang":"zh-TW","translated":"本週","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"39bb76742f8d0a5f7646ad59caaec4ce1d3c1786c38fa5bb1e47f086f77de188","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.copyPath","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Copy path","text_hash":"720ff4160412b943370afdb8fc1c082ff057d54713d5fb4b4b7a9634bfabf5fe","tgt_lang":"zh-TW","translated":"複製路徑","updated_at":"2026-06-16T14:13:18.599Z"}
{"cache_key":"3a12fb18b9756570ff22152940da62a3f15e237246a783b872d5255be34a6f85","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.openSession","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Open session","text_hash":"b205bb47f81a30968789eac28cefb848c4b849245d4d12f9311557c5f56ce770","tgt_lang":"zh-TW","translated":"開啟工作階段","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"3bd00b55cdb2ad5aad20be9a9398d7294767493f0b82a641d6f0c865a9f4d6d5","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewPreset","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Workboard view","text_hash":"cc2b05179ad742029156bb45578e880c46599fd28e1c2ab66f5a6f9e7f8fa08e","tgt_lang":"zh-TW","translated":"工作看板檢視","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"3dbdf3ec391fa277c0209262adb51d0c0d5b282cca134422024a21254df4057a","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthReadyUnassigned","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"ready unassigned","text_hash":"1e1a31a02e9da6ffa99a459bfd82e5af723794c2175405a5a0d6a32afa955167","tgt_lang":"zh-TW","translated":"就緒但未指派","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"3e02e5ec54de769bcd1a3093a75945bce7ea115003fe627e4668596b1da69119","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.group","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Group","text_hash":"34ca0e76608842ff3e7d924a455a396a82f471052c15e3f2ed7f090ac702e5c1","tgt_lang":"zh-TW","translated":"群組","updated_at":"2026-07-05T14:39:31.777Z"}
@ -57,6 +64,7 @@
{"cache_key":"4f177fdbb26337eb2db30ba994ac05b55cfb472f4d830559b3394c11610eb957","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.truncated","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Showing the first matching files. Refine the search to narrow results.","text_hash":"62005877ff0fc1f73ce05ca4c459157c57a8c57a3443245b1df4d3b033df98e9","tgt_lang":"zh-TW","translated":"顯示前幾個符合的檔案。請調整搜尋以縮小結果範圍。","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"501b2bbc7235d896c0dd9c2651034305d435fb6175276866a44f7aeb49e481e8","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.pinned","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Pinned","text_hash":"f20c879465551f0d1457a13d4390d0f1ece456b115d75463169c5d55341b9b1e","tgt_lang":"zh-TW","translated":"已釘選","updated_at":"2026-07-02T14:29:59.019Z"}
{"cache_key":"50be400be8862d08c57270cb9620dbeea1600e27bcbce68e28fa210e8fdbbc38","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefresh60s","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"60s","text_hash":"f79f071ab5b033ca8fb42c077f39708930d194b18f4608eb26ac1d9665a8836f","tgt_lang":"zh-TW","translated":"60秒","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"511f8bfd51554d8e1821976f9888eec24f0529d3aa2f83851ff750d6845b0ce1","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.emptyRecent","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No recent completed tasks.","text_hash":"71aceaf6accb5308950898b4d2fd7d938fd190cc7cf6314f000466577ed8de24","tgt_lang":"zh-TW","translated":"沒有最近完成的任務。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"513170b3b7c5805fd2dc37e5a11c97f25b12dadae83b99119ee6d9254a2a98b0","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.subtitle","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Isolated repository checkouts owned by OpenClaw.","text_hash":"6a3984ca864c9188fa8c05e732f6831b501b4caed6bd61b60e48e9b0cf74bd0c","tgt_lang":"zh-TW","translated":"由 OpenClaw 擁有的隔離儲存庫簽出。","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"55e67b46942869ce5df7ce634a270965886711b8df81a4cde3ea71d42d730450","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.repo","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Repository","text_hash":"13d6ff07b8a5d792ec87d5ec83bff2730ee77fa8f4fcd89ca5f1d688f64b4c73","tgt_lang":"zh-TW","translated":"儲存庫","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"564fcd615407c9dd0b1d6278ebbfad922f73e6acd80e07a2e7fb8ac0e0e3129b","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.searchResults","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Search results","text_hash":"e978b00de465a271a13bf2f6b9d74d67fdfaa7d973a37378fa32f988c3280599","tgt_lang":"zh-TW","translated":"搜尋結果","updated_at":"2026-06-16T14:13:16.738Z"}
@ -64,6 +72,7 @@
{"cache_key":"5908515aa8604870d9515a93841699ccd7e7e82b7c03d86c5c63ff91735fb08e","model":"gpt-5.5","provider":"openai","segment_id":"tabs.skillWorkshop","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Skill Workshop","text_hash":"3912c65bdd0a43563438762a43ecbd4b14637844a18decbf9249df73d21152a0","tgt_lang":"zh-TW","translated":"Skill Workshop","updated_at":"2026-05-31T21:48:14.460Z"}
{"cache_key":"5af0160814a0540948b587d27afba0a04ad887266104b48f291d139007cc1aa9","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefresh","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Auto-refresh","text_hash":"9ea4d7fd1550f0866089d18b1344546bfed91502b41c0484d6023ceb0fdeb75c","tgt_lang":"zh-TW","translated":"自動重新整理","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"5afb29184638b931fa5ce6ff85ce2fff231728cd0f07067a58ea9633eded9ef9","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.layoutComfortable","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Comfortable card density","text_hash":"bfaaf4553fd254bf24431ebabf62faebfd862685e9e7a52f5e799b11488dc7fe","tgt_lang":"zh-TW","translated":"舒適卡片密度","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"5b11d5e93bc9a41f61e4a73ff57d8d88985b677b4953c24e2989dcbdd409afee","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.acp","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"ACP","text_hash":"75ad69d7586c3d7e42c1ac14e80c7938dc0e7413f7f6f867c3be14d5304cc66b","tgt_lang":"zh-TW","translated":"ACP","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"5b80708472d328a98783d1b13fb97dfa82a9234dda4bb5fc5611355fef1d4c48","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupByChannel","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Channel","text_hash":"ce4683e7013a18cdf3d224bfcb4e9594ea8f559e946a837c633defe7d3c32172","tgt_lang":"zh-TW","translated":"頻道","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"5d1e8d597098ba19a9c87ac363c32879aeb18bee837770d06098a0e52ba393df","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.preview","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Preview","text_hash":"324b134f57c70c729ae3dc4d298bb451656717d70523e942c1ce667b8024ea07","tgt_lang":"zh-TW","translated":"預覽","updated_at":"2026-06-16T14:13:18.599Z"}
{"cache_key":"63513fe26688720e10895a9dbf7a0a4507be558ab76ae20baa4247ed9ac49735","model":"gpt-5.5","provider":"openai","segment_id":"chat.composer.contextUsage.summary","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Session context usage: {used} of {limit} ({pct}%)","text_hash":"a62b97af0e5d02b8722725e2be0a936dd3d317a1f506ea15c766e87413b66a0d","tgt_lang":"zh-TW","translated":"工作階段上下文使用量:{used} / {limit}{pct}%","updated_at":"2026-07-05T10:15:56.105Z"}
@ -71,12 +80,16 @@
{"cache_key":"68711cfcdcc5e4532e160717eb88fb0da381a469381c4c9cdb2858325ed9d774","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.inputTokens","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} input","text_hash":"f24231cff78fed82d155712973ede6f9369e96b015acc30d5de2b740677edce9","tgt_lang":"zh-TW","translated":"{count} 個輸入權杖","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"68e197e5c60c5c190c1c1910c14549358df6212d61205c104928b20d4fed2d41","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomationTenant","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Tenant: {tenant}","text_hash":"e896dc96a6847d7aaa593069e890e7a712fd60d7be60280ee24e1942e10411b0","tgt_lang":"zh-TW","translated":"租戶:{tenant}","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"69651c1b633ce1fcb92d8d1586fd21b6dc26c4b828f4290cb90fbb2707235591","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.empty","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No files touched in this session yet","text_hash":"6b295e4e11bcdd52340c4cc7987565f848cf477a1f0b96c0f47a2b718418298e","tgt_lang":"zh-TW","translated":"此工作階段尚未變更任何檔案","updated_at":"2026-06-16T14:13:10.175Z"}
{"cache_key":"69f08562e3d51c6b402ccefa9cc170217ce3198bf0434b7aa5136d9950658adb","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.failed","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Failed","text_hash":"031a8f0f659df890dfd53c92e45295b0f14c997185bae46e168831e403b273f7","tgt_lang":"zh-TW","translated":"失敗","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"6d2665398b5ae919883e0965313055d2d1b8de713cb1aa70b0a2b9f6623ad91c","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewRecentlyDone","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Recently done","text_hash":"46b889592a2f5e79197f62b5f96c76993545626bf207740ea58632ceed9623be","tgt_lang":"zh-TW","translated":"近期完成","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"6d984985a7c0e8f18326df65ab87cd871bc60830d881d34d2406da28f8fdd9fc","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewRunning","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Running","text_hash":"f4ccae29e1bb0c20a124570a1b43f4347ea94bba9f84ffdfddd9c7445b126128","tgt_lang":"zh-TW","translated":"執行中","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"6db77cbb3c3b33e8fcea05c48648a2d393c9d3e6dbf6239fc823fcf70d09c5d0","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.cancelTask","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Cancel {title}","text_hash":"74513d73b6ce74627b24c7f8a82fc52ffb27f69f27e07ecc6efbb64f25d4180d","tgt_lang":"zh-TW","translated":"取消 {title}","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"6e932b8ddacec2f0bf96a35e8d63b319a02a96c91331bac2350f0dcc103f74cd","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailUpdatedValue","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Updated: {time}","text_hash":"5e72d5445f018c9d08aa34ae0178fb9aa49eea6a0afd0c8d379f20b7af3e8aa0","tgt_lang":"zh-TW","translated":"已更新:{time}","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"6f8e95fe408f11283fd035b30042d1d546df363e750033a88cbbc54b230630c2","model":"gpt-5.5","provider":"openai","segment_id":"workboard.dispatch","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Nudge dispatcher","text_hash":"c3d20147447cc75f5e1e8cc895af0bc287a4a720372aec4795c5dfbfa9eeda67","tgt_lang":"zh-TW","translated":"提醒分派器","updated_at":"2026-05-30T15:38:05.607Z"}
{"cache_key":"70e23c83e306a237326d5115e9666ff3795c80f5965583fe450c1d4ca326aec9","model":"gpt-5.5","provider":"openai","segment_id":"subtitles.skillWorkshop","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Review, refine, and apply proposals before they become live skills.","text_hash":"f907c72e9f18a205027257cd6fecdd52b03732227a17dcec0db038e11de3f8cc","tgt_lang":"zh-TW","translated":"在提案成為上線技能之前,先進行審閱、調整並套用。","updated_at":"2026-05-31T21:48:14.460Z"}
{"cache_key":"710e16e7c8eaf7982df6f35b3c0b1016adef95effb46903834d48451f9e6dd67","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.invalidResponse","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"The gateway returned an invalid task list.","text_hash":"7aa61df7c36183096eba8284474d80ba8df86966ca8d8eed803e54a9fa938996","tgt_lang":"zh-TW","translated":"Gateway 傳回了無效的任務清單。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"7151008114b05262863619b0bec92f47c0c9331d88aa2b1d445370fac7e4daec","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.confirmForceDelete","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Snapshot failed: {error}\n\nDelete without a snapshot?","text_hash":"200ce9b8fb04659df79e1d26e69ec1014631ad5a85a92130dbdb9fcb550ba34f","tgt_lang":"zh-TW","translated":"快照失敗:{error}\n\n要在沒有快照的情況下刪除嗎","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"71d18db5fb40697c2b1d0f0ad0f19f6c1450b88bd3145bd4b615a031e092fc63","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.completed","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Completed","text_hash":"22a970d2e5b1cc233e462be7c7b64e135a275bb09d83d87683bf4236c43113a1","tgt_lang":"zh-TW","translated":"已完成","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"71d4a6d99b4f33e29ab279ab7a3e5b853f02c720fe781469ac21cd3f486f149b","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.agentFilterConfiguredDefaultHelp","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Cards explicitly assigned to the configured default agent.","text_hash":"9bb80530da1dfd473936d94642b83cc668b7362cb65675a565f17569937af92f","tgt_lang":"zh-TW","translated":"已明確指派給設定的預設代理人的卡片。","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"727ad347ef0ba67b8694ef5e86b0d0a1693fa70499b2e21e8b074b80af01d3a8","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.dailyCost","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Daily provider cost","text_hash":"0d03078a4d1fba12122e32e9abbc929ea64b948445810cf1e0d29cbdfd5cb18d","tgt_lang":"zh-TW","translated":"每日供應商費用","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"737a46b33974978d8774585e219cc270151df3bf1d482c87c4ece4b066190fb7","model":"claude-opus-4-8","provider":"anthropic","segment_id":"skillWorkshop.header.useCurrentChat","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Use current chat","text_hash":"fbc1ffd63daa506e927c7a85f6e43acd11e0b8c9f52a3951fc782b236ce9a787","tgt_lang":"zh-TW","translated":"使用目前的對話","updated_at":"2026-06-16T14:12:59.400Z"}
@ -95,11 +108,14 @@
{"cache_key":"7dac50dbdfbf5c96a0b54f4ad4d3e7a6e5dae056e4e9b211fd590b53784036e9","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateYesterday","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Yesterday","text_hash":"566181254b293aa66653e43313be9e39c12d44f9ac4fcd3236ef1e9c50a2903f","tgt_lang":"zh-TW","translated":"昨天","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"7edabd9d6f643852a532b979fd0baebdd731016b041c8b595a7238d7edaaf215","model":"gpt-5.5","provider":"openai","segment_id":"tabs.mcp","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"MCP","text_hash":"53f13ae99ed53bd346eb8e1c8cefb7ef8260683b50401caf101360967ea052aa","tgt_lang":"zh-TW","translated":"MCP","updated_at":"2026-05-31T05:36:31.990Z"}
{"cache_key":"826b48585a8f13512d608fb30b352d6f944f1b0e0673c169d5d41c66af1287e8","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.noSearchResults","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No matching files.","text_hash":"6ba2ede6c6019b640f63e7e48c5ee8238e701c6e539ce9abb5a7a9d9c71d8a73","tgt_lang":"zh-TW","translated":"沒有符合的檔案。","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"8410fae784a09c9294f96c4597791082040559685ad1a99b1ace40492095a138","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.queued","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Queued","text_hash":"661ff40a07e037bbd5f7d4ec97a4df1503096ca910e7c9e2d7a4e9abd4e4e1a0","tgt_lang":"zh-TW","translated":"已排入佇列","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"84eb7f989a1b603c37cc3c034a3cefadddb7e2e5b4dd2594aacffc2699fbc507","model":"gpt-5.5","provider":"openai","segment_id":"chat.archivedSessionDisabled","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Restore this session to send messages.","text_hash":"c21bd35c2bef2266f419cbb4214cfd7b0af89386aef88ec8b11837844240e0d9","tgt_lang":"zh-TW","translated":"還原此工作階段以傳送訊息。","updated_at":"2026-07-02T14:29:59.020Z"}
{"cache_key":"8703e3f6174426ea308da95b235bace248f3029faaa6959e881257ec66bfca36","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupRowCountOne","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} session","text_hash":"c0975b42c84d7da963f4c962d1e2c1ee78eb18efc7ebec75fb3ce761ce9a40db","tgt_lang":"zh-TW","translated":"{count} 個工作階段","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"89177c7414a815aba0b43c4f1ba3b90adc797d170acc5724648356551a4a261f","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.subagent","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Subagent","text_hash":"d6cb4188b8fa57aae3e4ca3a1210c9afe7ca995375c2fb36d90a1fa73529a44e","tgt_lang":"zh-TW","translated":"子代理","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"892ea67d82274b90a2a037200cf74c6aafd1ee90ec43db2208517e488500c0c2","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.artifactCount","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} artifacts","text_hash":"022b6b55a10f1864b7aff7c307e49ab37e11e6999111fb87349f040d100abba3","tgt_lang":"zh-TW","translated":"{count} 個成品","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"8b873107c534fa32cb4dbd267226e5658da1f5dc839478ea29156572ba516a70","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.empty","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No managed worktrees.","text_hash":"67f97698da5ed0bb1cc4cadd157e403c0fcd22fc4735d8f39bcf27ea6dd612c8","tgt_lang":"zh-TW","translated":"沒有受管理的 worktrees。","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"8c6b223ab1650b2bb316696ac86fd5392d36db4292face01f91b64335699ddb0","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.name","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Name","text_hash":"dcd1d5223f73b3a965c07e3ff5dbee3eedcfedb806686a05b9b3868a2c3d6d50","tgt_lang":"zh-TW","translated":"名稱","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"8e37c07d47eccdd87e9152b0ff74a13e70309bb1eef000a83329caed9303db6a","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.cron","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Cron","text_hash":"dd9d24965dbedc026915308732b77c1af68dcf52d3c0ca2421b1fdb0d197aca1","tgt_lang":"zh-TW","translated":"Cron","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"8eafdd71c805e6f90f537427ba07bf27a903c96b1a301fc642daea0fe90f4e45","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.pinSession","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Pin session","text_hash":"813273b54d2df112a0fa1903110e9386779f8848ae288142d3f91d7a5891c8ff","tgt_lang":"zh-TW","translated":"釘選工作階段","updated_at":"2026-07-02T14:29:59.020Z"}
{"cache_key":"8f747acaf2ebeab7a6f11f77f1b218b9b57bced8c0b3c00b85aec6c78bc609cb","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.branch","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Branch","text_hash":"52656e8104eef3fddd3d4546903fa0de93c0625abf47b3dd8130f7705d6a513e","tgt_lang":"zh-TW","translated":"分支","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"925657f5c27104973a054ec9ac589584058de47dca67f743fc7ad5dc3bc0b27d","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.read","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Read","text_hash":"9b9a8d05a7ec353bda84f9c1bb3178c299de3001b5e970508ddc889c487f92ca","tgt_lang":"zh-TW","translated":"已讀取","updated_at":"2026-06-16T14:13:16.738Z"}
@ -113,17 +129,25 @@
{"cache_key":"98f5b98568834cd92a7de13536e3ca6a7579d615d6abb0ac6585a2f4d220a49a","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.archiveSession","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Archive session","text_hash":"740ded37480365eae8bf833ccaaa58350fc9434a77b7bb65b5516eba476fbec3","tgt_lang":"zh-TW","translated":"封存工作階段","updated_at":"2026-07-02T14:29:59.020Z"}
{"cache_key":"9979d886403e24df60997801e043da82148173019f9df8141fce453200283ac0","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"zh-TW","translated":"目標備註","updated_at":"2026-05-29T20:59:51.845Z"}
{"cache_key":"99a41ad86108940067d4f9f76ab79dd780de0f0f6cb751e9e2b3625e79cab983","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomationSummary","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Summary: {summary}","text_hash":"3a2270b3cd47b523936c13efec489f36112e0a64fe763dbe972d21fef029e814","tgt_lang":"zh-TW","translated":"摘要:{summary}","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"9a391a8e8858d492a3810d7bee370589ad830ef61d2465d4dfa7b01a5c24ad84","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.runtime.cli","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"CLI","text_hash":"e759793341ff3757eaa76e814db0170ec13b6b0a988a742d9a81240c505f48ec","tgt_lang":"zh-TW","translated":"CLI","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"9b598144b59bd4c5544201dafa4170984c21e16e8d0dcea21d5570e44ef7c1ff","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.agentFilterConfiguredDefault","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{agent} (default)","text_hash":"7e996234f0fa55605720f9dc954a58411795bd882e948c87c739d43bd02137c3","tgt_lang":"zh-TW","translated":"{agent}(預設)","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"9ba9184d9ba805ed7d0a9265b9f36897fc56fc72ec32588ac8d0ae9033bb815f","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateOlder","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Older","text_hash":"03281c889c2869e091390f9ad5dd13f0f0e46b42c9c4698f857902451deb3450","tgt_lang":"zh-TW","translated":"較舊","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"9bedafc1dc458e55b71ddc2b7a5d68def02deb9f41ff1743888a2069ae32e355","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.empty","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No background tasks yet.","text_hash":"e920d0a7849ab499c0eb22fe353bad656795e9a48c55e72edc58a04e2dff58b1","tgt_lang":"zh-TW","translated":"尚無背景任務。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"9e28a47bf132b7ffd017f55a96a9d85f99aff5bc2491849a039e97d7f9a3e448","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.dateToday","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Today","text_hash":"2b065c7c9ce466e5ebcad757987d5d660ee4c9ea708bc62c43444b53334738ba","tgt_lang":"zh-TW","translated":"今天","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"9fef17754bf65f1dc47ebe02a1b4ed804fcdfa45de89d9ee7e2b645cc5c7e586","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.badgeHeartbeat","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"heartbeat {age}","text_hash":"000637b3800ae069edbbe207cfad0a3f5037f06e9661ee89d70a1dfe6f404485","tgt_lang":"zh-TW","translated":"心跳 {age}","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"a111f4b4990f470fab42a780c89cd24f606ed4b24326e7f406dd7104845173a8","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomation","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Automation","text_hash":"d909750b1bbb71a39b6330ba8f81f4f8f6e889ed96d7ab366e74857909750c64","tgt_lang":"zh-TW","translated":"自動化","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"a51869c24096afe415b4608bcdb55b4908d163564f906e9a298c74d3de3cec45","model":"gpt-5.5","provider":"openai","segment_id":"tabs.tasks","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Tasks","text_hash":"b3a60e61a5233d0506ac737405a2a45280349683cac68722f18d0b73eb495ef4","tgt_lang":"zh-TW","translated":"任務","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"a70fe70333817727bf583e057b19b3426ee1950056aeb0e5bf410132ea48a5a3","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.taskCountOne","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"1 task","text_hash":"cba293c13f302204af2ae5b202d80ea840fdf1cf7904d59e1a62efbadf1e5256","tgt_lang":"zh-TW","translated":"1 個任務","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"a82f5cd6a688c601e757b2a30520941a0c2c8f173a224ed6ab3ee881f2a2e3db","model":"gpt-5.5","provider":"openai","segment_id":"chat.composer.contextUsage.latestRunTokens","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Latest run tokens","text_hash":"969b6403862b70df744bc747c801663034a7837cd04b55973b7ee6c051e2e5ca","tgt_lang":"zh-TW","translated":"最新執行權杖","updated_at":"2026-07-05T10:15:56.105Z"}
{"cache_key":"a8f31af41e19ba668eb7707a51e9b607c155a60ff6b5a153057762771d8e2f60","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.cancelling","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Cancelling…","text_hash":"91b104db05da1b2d48c57a5aa60128f660e6572f89835ec858f6eb25b8f4af0f","tgt_lang":"zh-TW","translated":"正在取消…","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"a91a71a2f73b2030bf54d60b4932c6362c4c8e5e34b4859265e224d4ef091a19","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailNoNotes","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"No operator notes yet.","text_hash":"497e07f47e33851483b6fb1254e88dc640d9fb25525c51f89934a7d39d7b2b9c","tgt_lang":"zh-TW","translated":"尚無操作員備註。","updated_at":"2026-06-16T14:13:10.175Z"}
{"cache_key":"aa556bfe98000ca7518d9332d394802128db838dedb9ac803ef3dbd10b0bad06","model":"claude-opus-4-8","provider":"anthropic","segment_id":"tasksPage.status.running","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Running","text_hash":"f4ccae29e1bb0c20a124570a1b43f4347ea94bba9f84ffdfddd9c7445b126128","tgt_lang":"zh-TW","translated":"執行中","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"ab06fbb9d8d974c976c4d5c0da4b57f0f0e4f52e02901f510e2beab725d000b1","model":"gpt-5.5","provider":"openai","segment_id":"workboard.eventProtocolViolation","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Protocol violation","text_hash":"367bb2052963f7d75beb672d3ca0430d7d49ac48a2759d578c7df933178fe564","tgt_lang":"zh-TW","translated":"通訊協定違規","updated_at":"2026-05-30T15:38:05.607Z"}
{"cache_key":"b09f9660feb7d2926356a053faeb445e2ed51b7e81bfdab9aa857266624d7f79","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.status.cancelled","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Cancelled","text_hash":"d353a99eb4556847ceeb3c306ac886cf6b4fa13043eb1864b9aeb0607fd5f2cc","tgt_lang":"zh-TW","translated":"已取消","updated_at":"2026-07-06T08:41:48.829Z"}
{"cache_key":"b15c1b608fdf836eb76f8ebb5c941e58aa26ea9cfc16820d247164a08e05f027","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.lastRefreshed","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Updated {time}","text_hash":"2f87419441e6111b4d62893d3c4ef5ddeb2c8e1af82fabab6132856faf77f907","tgt_lang":"zh-TW","translated":"已於 {time} 更新","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"b2ca53fb2712b0eb0cd0e1a76f10855ed6f871f65e3bdce25c6185cf15860425","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailAutomationBoard","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Board: {board}","text_hash":"96d7493589e40e17803b3bf643dff1b891a4ebf57f5d2b36af0a7ddd09e64b84","tgt_lang":"zh-TW","translated":"面板:{board}","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"b39fd60c0abac153fcfbc0a09a2074511f958c619922f3f349ff65f79ac29e25","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.pairingQrExpired.reason","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Run /pair qr again to generate a fresh setup code.","text_hash":"876a304827f54ae5996c4e804aa72953f43568d31e8a15dd2a5b5a40d91c13d3","tgt_lang":"zh-TW","translated":"請再次執行 /pair qr 以產生新的設定碼。","updated_at":"2026-07-01T10:30:56.042Z"}
{"cache_key":"b3bd5b5cd8c45898a1015723c722a7066b07174bf0da9626058d92eb3c4f1148","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.loadFailed","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Could not load tasks.","text_hash":"a4d24c89cb53e14f67055c1cdc6c0f98bca7e013ad8e533cabef5d276381e106","tgt_lang":"zh-TW","translated":"無法載入任務。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"b4df0e90f6cf93fd830dfb9e04a86d2609f9468da7dd9e6314343f82daade64c","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefresh5s","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"5s","text_hash":"93e3d8c5b10657d2884f177488b689aadf82a83f962237cb602b3314386ab3b7","tgt_lang":"zh-TW","translated":"5秒","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"b56d8243bb1d796539d140436bce73f58f54c5a1006754acadadccae9777ee4f","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.missing","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Missing","text_hash":"6be36ca49ee85210c5d1ad9c377d90a9859c66d889110a2a5b0dccd390d12e20","tgt_lang":"zh-TW","translated":"遺失","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"b5980fafe5a080fbaaf78fb593803c0b6c9d8c3b1a97987c2f97b88ee75a50f0","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewPresetCount","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} cards","text_hash":"4b3e5442ebd2f839d45fddf95b2c2a18427dbd6ac06c8b57f9d9e996dcb73607","tgt_lang":"zh-TW","translated":"{count} 張卡片","updated_at":"2026-06-17T14:13:17.815Z"}
@ -133,9 +157,12 @@
{"cache_key":"b8385c7e828b1cd97ec44d7bec8587df7e8c9146428812f9e1b11a2b575d1227","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupByNone","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"None","text_hash":"dc937b59892604f5a86ac96936cd7ff09e25f18ae6b758e8014a24c7fa039e91","tgt_lang":"zh-TW","translated":"無","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"b84e72a38aabb2284230a45953bd7538b0e0010a9da6e72c24149484add7e680","model":"gpt-5.5","provider":"openai","segment_id":"subtitles.mcp","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"MCP servers, auth, tools, and diagnostics.","text_hash":"3eb7bf08a81e00ed41da1b60096320c5b90ff4d4e78b3f84ecd9ce45a62eaea1","tgt_lang":"zh-TW","translated":"MCP 伺服器、驗證、工具與診斷。","updated_at":"2026-05-31T05:36:31.990Z"}
{"cache_key":"b85956299d75a80be03573d014caefcd79ae5354542c1d7c07a79cc0c07d4736","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.confirmDelete","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Snapshot and delete {name}?","text_hash":"3c3ee9b4dd86ac95d852528c6fd78c214c61cbb434f857051d8f0d73a615bd2f","tgt_lang":"zh-TW","translated":"要建立快照並刪除 {name} 嗎?","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"b9400a3eb48c20dda82c4fdc1debeed4c7605fb04f448e4cbee3556ab238eccf","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.cancelFailed","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Could not cancel the task.","text_hash":"604b3f1a92694f8b8ccf5cd07a47947d3cc1a4b6c0fd5719a36dba2ffbe38b17","tgt_lang":"zh-TW","translated":"無法取消該任務。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"bad90f5193fd4085041d69433704d0f0b0b2d9b5bd013504760b1fedc6056c8b","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailOperatorNotes","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Operator notes","text_hash":"7d2a121620cebfb9c4f6c0f82b693b75d65a4210b8232d77ef87e45fce334347","tgt_lang":"zh-TW","translated":"操作員備註","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"bb30d72179bf02a357ba2c875d1730d8d4bbdede6fc387503165f47a9548b7af","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewAll","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"All cards","text_hash":"2306120917506b5998ec702f3661068b102dac538ba4c9e4634d65fe33eea98a","tgt_lang":"zh-TW","translated":"所有卡片","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"bb3e10d71875b61d47bc962e2812ed3258ed178a40cbd6b0f17a77ea310ec2b4","model":"claude-opus-4-8","provider":"anthropic","segment_id":"skillWorkshop.header.useCurrentChatAria","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Use current chat for revision requests","text_hash":"9c551a423ae74aedaaa90e4df9899dbdc02f846d6ee058bf2576a812e2c52119","tgt_lang":"zh-TW","translated":"使用目前的對話進行修訂請求","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"bdb97aab9d3a44dfd2ee445bc9bc00353619bcc3cd20b9455b3647b77163eb1b","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.taskCount","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} tasks","text_hash":"1d43774da9d4e2aabcff69b02e03591836a632f430121f8ecdaf2f115a250233","tgt_lang":"zh-TW","translated":"{count} 個任務","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"be6b1590029094dafcfef53b3b27bf4aeabbc26851596ab4ffc91d6415762822","model":"gpt-5.5","provider":"openai","segment_id":"subtitles.tasks","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Background tasks: subagents, cron runs, CLI.","text_hash":"86b1cdd143e0bee775431403456aa967dad1c7f0a952e10200c332d8ba382d35","tgt_lang":"zh-TW","translated":"背景任務子代理、cron 執行、CLI。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"bf6cd22d093ceb33e2ce995a234bbb8c2c4a8509c7e889c083a2224033f60a05","model":"gpt-5.5","provider":"openai","segment_id":"terminal.detached","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"detached","text_hash":"88e34e4cdbb5c6066cb1b0d0abe74714bd72f86259433bc8287614e4ae052605","tgt_lang":"zh-TW","translated":"已分離","updated_at":"2026-07-04T21:23:47.359Z"}
{"cache_key":"c2b2d7476857ba2c76bf4af5fc6d0bbf8d93921250580da64b5acc9314a8dd23","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.lastDays","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} days","text_hash":"e9f0a85930cc6fa61b7ac01763893020adc4c712d1b8e8897bdd13971637d529","tgt_lang":"zh-TW","translated":"{count} 天","updated_at":"2026-07-06T06:40:15.357Z"}
{"cache_key":"c2b674c569aa61cd0608286bf9e7a2cf3582e40f9a59151a99b2e8054f2fcc1f","model":"claude-opus-4-8","provider":"anthropic","segment_id":"skillWorkshop.header.useCurrentChatTooltip","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Send revision requests to the current chat session instead of the proposal's workshop session.","text_hash":"9db782d40e88750d4faed33c8a73c24552070f101483881c60af8cf446c674a6","tgt_lang":"zh-TW","translated":"將修訂請求傳送至目前的對話工作階段,而非提案的工作坊工作階段。","updated_at":"2026-06-16T14:12:59.400Z"}
@ -151,6 +178,7 @@
{"cache_key":"cf4e3394a5987940d5291fdc17aa2ec16879c7cc092331651b68900552d4ea08","model":"gpt-5.5","provider":"openai","segment_id":"chat.toolCards.toolError","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Tool error","text_hash":"a6c64c286a8795034ac5030b74633d3b476b5375e094485698b982879b0bb617","tgt_lang":"zh-TW","translated":"工具錯誤","updated_at":"2026-05-31T06:43:46.257Z"}
{"cache_key":"d082cf53a732e9463630017b5e5b86b179258b9517e2c7b302e97863a918cabd","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.refreshError","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Refresh failed","text_hash":"8fa7e6d90bef4e5cb735233347bf6a71b5b30d96e7c1a50b73f10cb441b275c2","tgt_lang":"zh-TW","translated":"重新整理失敗","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"d4a6c06bfdfedcffd1b6e7ad155ea4138c82ffa07146a06a5747b8b69310a82d","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"zh-TW","translated":"目標","updated_at":"2026-05-29T20:59:51.845Z"}
{"cache_key":"d698788abe9c34100cf12047af2dc88415bcd58514e1834b94fdb152a2004059","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.disconnected","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Connect to the gateway to load and manage tasks.","text_hash":"f809605f626a2f8eeff5c864a30e78d538a878ec5de7934f21d60bc01b81f125","tgt_lang":"zh-TW","translated":"連線至 Gateway 以載入並管理任務。","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"d7e37c29f5d68135f382d36dc064472a98fe594ce6a70fc5a3f7c02d94051f8e","model":"gpt-5.5","provider":"openai","segment_id":"chat.composer.contextUsage.estimatedCost","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Est. cost","text_hash":"3199f14286736527bfc6ad9165629415a5aa087517acd2c0e9b1ae8bb5d26766","tgt_lang":"zh-TW","translated":"預估費用","updated_at":"2026-07-05T16:00:06.097Z"}
{"cache_key":"d850899057ac1e583498793ba5ea9d62fe586c2ba8dc39681dccff2a59db48dd","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailProof","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Proof","text_hash":"7fbb3ccf9640651f69af3626de6836fb302a0a088c7cd27721c367b8b530e502","tgt_lang":"zh-TW","translated":"證明","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"d92258d6c8d1491219a65f844b2fc4e3efc7a714602fc1ceef0bf41662c7e8cf","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.requests","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} requests","text_hash":"1e23ff6956124091cd470f5091cee8108c3766314b69871b3ff792eaf506455f","tgt_lang":"zh-TW","translated":"{count} 個請求","updated_at":"2026-07-06T06:40:15.357Z"}
@ -167,6 +195,7 @@
{"cache_key":"e5ac548645d9837c323dfd6196904e0b9a7781db16ba3c3bab721adf9954060d","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.viewBlocked","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Blocked","text_hash":"18f2a0947f9d6523991b29b450307f22773f57d65f7efb98d48a167df04d6b1d","tgt_lang":"zh-TW","translated":"已封鎖","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"e69bbdd9df33ab895e53778ce3f8f4e1e65fcb08d8527be16d27ed7f01b25f7e","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.autoRefreshOff","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Off","text_hash":"ca7981b46ecf2c1787b6d76d81d9fd7fa0ca95842e2fcc2a452869891a9334d1","tgt_lang":"zh-TW","translated":"關閉","updated_at":"2026-06-17T14:13:17.815Z"}
{"cache_key":"e6ae7651c8fd279ed69f2c40d56b56b696daf9302431a98290f5f54d683a8ac9","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.unpinSession","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Unpin session","text_hash":"f4c582ee4d7a87bf069d05a49bb0211759d3db8366daab4ba7024425961af5dc","tgt_lang":"zh-TW","translated":"取消釘選工作階段","updated_at":"2026-07-02T14:29:59.020Z"}
{"cache_key":"e989b932f7e9fa4b9a2899b262071a4e56f537ef10bb7406025363f78e18fb8b","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.active","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Active","text_hash":"92340695899bd2d86223e4a007620e0d6502fc0e08809773634c7e0743764a9c","tgt_lang":"zh-TW","translated":"進行中","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"e9988e477c8d4af18134f8cf365500e393e00099c63e0cdcd80b6bcf51e4d55e","model":"claude-opus-4-8","provider":"anthropic","segment_id":"chat.workspaceFiles.search","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Search files","text_hash":"179fed85ec50a433bb23932745d18f1ade2f84a6ebe145b0025ed3ce5f89fd5a","tgt_lang":"zh-TW","translated":"搜尋檔案","updated_at":"2026-06-16T14:13:16.738Z"}
{"cache_key":"ea19450c08adfe2b3310d66e9570afe87f91399bbe3a0b0a115c3a88be15c413","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.newGroup","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"New group…","text_hash":"ce58c189f2045434c28a50c9875a7362d28c79fb34d4b365c09f59180ca2712a","tgt_lang":"zh-TW","translated":"新增群組…","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"eb1cc6412192b655a08bf7487b10178f19fdf1bb43cdfa8918224bec515f5d40","model":"gpt-5.5","provider":"openai","segment_id":"usage.providerUsage.today","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Today","text_hash":"2b065c7c9ce466e5ebcad757987d5d660ee4c9ea708bc62c43444b53334738ba","tgt_lang":"zh-TW","translated":"今天","updated_at":"2026-07-05T14:39:31.777Z"}
@ -182,6 +211,7 @@
{"cache_key":"f5dfebffaf445b574685fda80f52b609123bc67bfe73bc674763c4c71a938497","model":"gpt-5.5","provider":"openai","segment_id":"worktrees.actions","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Actions","text_hash":"ff8059dc6752afdd30d275932b1d5031a2ec854b387a8c57ecc6689915293a43","tgt_lang":"zh-TW","translated":"操作","updated_at":"2026-07-05T21:00:31.397Z"}
{"cache_key":"f725cc564ca677916917da2b7c52b1769a1588dfef4e9bb5f26eb3f75fcfbb82","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.agentFilterUnassigned","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Unassigned (uses {agent})","text_hash":"2700af0c4ab5e86726f72a723ecdf50370b87690db35f00b83723d6457879c8e","tgt_lang":"zh-TW","translated":"未指派(使用 {agent}","updated_at":"2026-06-17T14:13:17.814Z"}
{"cache_key":"f7572a09c157f969a2b7c81023fbbe3078ec606f3fe951ac483b4dbb8fcc7fa5","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.healthStale","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"stale","text_hash":"a03f2386ae06b21109577020844df367857b72c2fcce384c1896fed98a89c82b","tgt_lang":"zh-TW","translated":"過時","updated_at":"2026-06-17T14:13:23.259Z"}
{"cache_key":"f92d8c5d7be48c8e2ca2faecbddeb1b5207640c9164f842d22750898857f5265","model":"gpt-5.5","provider":"openai","segment_id":"tasksPage.recent","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Recent","text_hash":"690dbe9dc0993c4256683738fc3fd541cfa96f60d299be33343615dd58179d93","tgt_lang":"zh-TW","translated":"最近","updated_at":"2026-07-06T08:41:46.878Z"}
{"cache_key":"f9d70c811b055a8d009c8841adde028b717e8f455d6070f1e4fb1ea8f656e85f","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.groupRowCount","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} sessions","text_hash":"27de9b3be346a2abd2cb67f9f93abfe8100d7ce996e1204b75fc84670c7818e6","tgt_lang":"zh-TW","translated":"{count} 個工作階段","updated_at":"2026-07-05T14:39:31.777Z"}
{"cache_key":"fae21f57902d46cec227ed9783d0aca914b2d0354c4081087df4ebe3ce7cf633","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.detailDiagnostics","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Diagnostics","text_hash":"268f14bbfe119c1e92150583af960a086d7db9619a097f8aa72ff6779842f610","tgt_lang":"zh-TW","translated":"診斷","updated_at":"2026-06-16T14:12:59.400Z"}
{"cache_key":"fc692a69e2adb5a42f774a5adde96dd42dd90a3b96312c263af731c4ed699872","model":"claude-opus-4-8","provider":"anthropic","segment_id":"workboard.dependenciesReady","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"{count} ready","text_hash":"f5f5fd424d7c18f19a51ee147857efddc320a0ec6e1eeb4354be129425632f05","tgt_lang":"zh-TW","translated":"{count} 個就緒","updated_at":"2026-06-16T14:13:10.175Z"}

View file

@ -501,6 +501,7 @@ export const ar: TranslationMap = {
sessions: "الجلسات",
usage: "الاستخدام",
cron: "مهام Cron",
tasks: "المهام",
skills: "Skills",
skillWorkshop: "ورشة Skills",
nodes: "العقد",
@ -528,6 +529,7 @@ export const ar: TranslationMap = {
sessions: "الجلسات النشطة والافتراضيات.",
usage: "استخدام API والتكاليف.",
cron: "الإيقاظات والتشغيلات المتكررة.",
tasks: "مهام الخلفية: الوكلاء الفرعيون، تشغيلات cron، CLI.",
skills: "Skills ومفاتيح API.",
skillWorkshop: "راجع المقترحات وحسّنها وطبّقها قبل أن تصبح مهارات نشطة.",
nodes: "الأجهزة المقترنة والأوامر.",
@ -544,6 +546,40 @@ export const ar: TranslationMap = {
dreams: "حلم الذاكرة، والدمج، والتأمل.",
plugin: "لوحة مقدمة من المكوّن الإضافي.",
},
tasksPage: {
active: "نشطة",
recent: "الأخيرة",
loading: "جارٍ تحميل المهام…",
empty: "لا توجد مهام خلفية بعد.",
emptyActive: "لا توجد مهام في قائمة الانتظار أو قيد التشغيل.",
emptyRecent: "لا توجد مهام مكتملة حديثًا.",
disconnected: "اتصل بـ Gateway لتحميل المهام وإدارتها.",
loadFailed: "تعذر تحميل المهام.",
cancelFailed: "تعذر إلغاء المهمة.",
invalidResponse: "أرجع Gateway قائمة مهام غير صالحة.",
untitled: "مهمة خلفية",
taskCount: "{count} مهام",
taskCountOne: "مهمة واحدة",
agent: "الوكيل: {agent}",
openSession: "فتح الجلسة",
cancelTask: "إلغاء {title}",
cancelling: "جارٍ الإلغاء…",
status: {
queued: "في قائمة الانتظار",
running: "قيد التشغيل",
completed: "مكتمل",
failed: "فشل",
cancelled: "مُلغى",
timedOut: "انتهت المهلة",
},
runtime: {
subagent: "الوكيل الفرعي",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "مهمة",
},
},
skillWorkshop: {
header: {
useCurrentChat: "استخدام المحادثة الحالية",

View file

@ -505,6 +505,7 @@ export const de: TranslationMap = {
sessions: "Sitzungen",
usage: "Nutzung",
cron: "Cron-Aufgaben",
tasks: "Aufgaben",
skills: "Skills",
skillWorkshop: "Skill Workshop",
nodes: "Geräte",
@ -532,6 +533,7 @@ export const de: TranslationMap = {
sessions: "Aktive Sitzungen inspizieren und Standardeinstellungen pro Sitzung anpassen.",
usage: "API-Nutzung und Kosten überwachen.",
cron: "Aufweckzeiten und wiederkehrende Agent-Läufe planen.",
tasks: "Hintergrundaufgaben: Subagents, Cron-Läufe, CLI.",
skills: "Skill-Verfügbarkeit und API-Schlüsselinjektion verwalten.",
skillWorkshop:
"Vorschläge prüfen, verfeinern und anwenden, bevor sie zu aktiven Skills werden.",
@ -549,6 +551,40 @@ export const de: TranslationMap = {
dreams: "Speicherkonsolidierung im Schlaf.",
plugin: "Vom Plugin bereitgestelltes Panel.",
},
tasksPage: {
active: "Aktiv",
recent: "Zuletzt",
loading: "Aufgaben werden geladen…",
empty: "Noch keine Hintergrundaufgaben.",
emptyActive: "Keine Aufgaben in der Warteschlange oder in Ausführung.",
emptyRecent: "Keine kürzlich abgeschlossenen Aufgaben.",
disconnected: "Mit der Gateway verbinden, um Aufgaben zu laden und zu verwalten.",
loadFailed: "Aufgaben konnten nicht geladen werden.",
cancelFailed: "Die Aufgabe konnte nicht abgebrochen werden.",
invalidResponse: "Die Gateway hat eine ungültige Aufgabenliste zurückgegeben.",
untitled: "Hintergrundaufgabe",
taskCount: "{count} Aufgaben",
taskCountOne: "1 Aufgabe",
agent: "Agent: {agent}",
openSession: "Sitzung öffnen",
cancelTask: "{title} abbrechen",
cancelling: "Wird abgebrochen…",
status: {
queued: "In Warteschlange",
running: "Läuft",
completed: "Abgeschlossen",
failed: "Fehlgeschlagen",
cancelled: "Abgebrochen",
timedOut: "Zeitüberschreitung",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Aufgabe",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Aktuellen Chat verwenden",

View file

@ -500,6 +500,7 @@ export const en: TranslationMap = {
sessions: "Sessions",
usage: "Usage",
cron: "Cron Jobs",
tasks: "Tasks",
skills: "Skills",
skillWorkshop: "Skill Workshop",
nodes: "Nodes",
@ -527,6 +528,7 @@ export const en: TranslationMap = {
sessions: "Active sessions and defaults.",
usage: "API usage and costs.",
cron: "Wakeups and recurring runs.",
tasks: "Background tasks: subagents, cron runs, CLI.",
skills: "Skills and API keys.",
skillWorkshop: "Review, refine, and apply proposals before they become live skills.",
nodes: "Paired devices and commands.",
@ -543,6 +545,40 @@ export const en: TranslationMap = {
dreams: "Memory dreaming, consolidation, and reflection.",
plugin: "Plugin-provided panel.",
},
tasksPage: {
active: "Active",
recent: "Recent",
loading: "Loading tasks…",
empty: "No background tasks yet.",
emptyActive: "No queued or running tasks.",
emptyRecent: "No recent completed tasks.",
disconnected: "Connect to the gateway to load and manage tasks.",
loadFailed: "Could not load tasks.",
cancelFailed: "Could not cancel the task.",
invalidResponse: "The gateway returned an invalid task list.",
untitled: "Background task",
taskCount: "{count} tasks",
taskCountOne: "1 task",
agent: "Agent: {agent}",
openSession: "Open session",
cancelTask: "Cancel {title}",
cancelling: "Cancelling…",
status: {
queued: "Queued",
running: "Running",
completed: "Completed",
failed: "Failed",
cancelled: "Cancelled",
timedOut: "Timed out",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Task",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Use current chat",

View file

@ -503,6 +503,7 @@ export const es: TranslationMap = {
sessions: "Sesiones",
usage: "Uso",
cron: "Tareas Cron",
tasks: "Tareas",
skills: "Skills",
skillWorkshop: "Taller de Skills",
nodes: "Nodos",
@ -530,6 +531,7 @@ export const es: TranslationMap = {
sessions: "Inspeccionar sesiones activas y ajustar valores predeterminados por sesión.",
usage: "Monitorear uso de API y costes.",
cron: "Programar despertares y ejecuciones recurrentes de agentes.",
tasks: "Tareas en segundo plano: subagentes, ejecuciones de cron, CLI.",
skills: "Gestionar disponibilidad de habilidades e inyección de claves API.",
skillWorkshop:
"Revisa, perfecciona y aplica propuestas antes de que se conviertan en Skills activas.",
@ -547,6 +549,40 @@ export const es: TranslationMap = {
dreams: "Consolidación de la memoria durante el sueño.",
plugin: "Panel proporcionado por el plugin.",
},
tasksPage: {
active: "Activas",
recent: "Recientes",
loading: "Cargando tareas…",
empty: "Aún no hay tareas en segundo plano.",
emptyActive: "No hay tareas en cola ni en ejecución.",
emptyRecent: "No hay tareas completadas recientemente.",
disconnected: "Conéctate al Gateway para cargar y gestionar tareas.",
loadFailed: "No se pudieron cargar las tareas.",
cancelFailed: "No se pudo cancelar la tarea.",
invalidResponse: "El Gateway devolvió una lista de tareas no válida.",
untitled: "Tarea en segundo plano",
taskCount: "{count} tareas",
taskCountOne: "1 tarea",
agent: "Agente: {agent}",
openSession: "Abrir sesión",
cancelTask: "Cancelar {title}",
cancelling: "Cancelando…",
status: {
queued: "En cola",
running: "En ejecución",
completed: "Completada",
failed: "Error",
cancelled: "Cancelada",
timedOut: "Tiempo agotado",
},
runtime: {
subagent: "Subagente",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Tarea",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Usar el chat actual",

View file

@ -503,6 +503,7 @@ export const fa: TranslationMap = {
sessions: "نشست‌ها",
usage: "مصرف",
cron: "کارهای Cron",
tasks: "وظایف",
skills: "Skills",
skillWorkshop: "کارگاه Skill",
nodes: "گره‌ها",
@ -530,6 +531,7 @@ export const fa: TranslationMap = {
sessions: "نشست‌های فعال و پیش‌فرض‌ها.",
usage: "مصرف و هزینه‌های API.",
cron: "بیدارباش‌ها و اجراهای تکرارشونده.",
tasks: "وظایف پس‌زمینه: subagents، cron runs، CLI.",
skills: "Skills و کلیدهای API.",
skillWorkshop: "پیشنهادها را پیش از تبدیل‌شدن به مهارت‌های فعال، بررسی، اصلاح و اعمال کنید.",
nodes: "دستگاه‌های جفت‌شده و فرمان‌ها.",
@ -546,6 +548,40 @@ export const fa: TranslationMap = {
dreams: "رؤیاپردازی حافظه، یکپارچه‌سازی و بازتاب.",
plugin: "پنل ارائه‌شده توسط افزونه.",
},
tasksPage: {
active: "فعال",
recent: "اخیر",
loading: "در حال بارگذاری وظایف…",
empty: "هنوز هیچ وظیفهٔ پس‌زمینه‌ای وجود ندارد.",
emptyActive: "هیچ وظیفه‌ای در صف یا در حال اجرا نیست.",
emptyRecent: "هیچ وظیفهٔ تکمیل‌شدهٔ اخیری وجود ندارد.",
disconnected: "برای بارگذاری و مدیریت وظایف، به gateway متصل شوید.",
loadFailed: "امکان بارگذاری وظایف وجود نداشت.",
cancelFailed: "امکان لغو وظیفه وجود نداشت.",
invalidResponse: "gateway فهرست وظایف نامعتبری برگرداند.",
untitled: "وظیفهٔ پس‌زمینه",
taskCount: "{count} وظیفه",
taskCountOne: "1 وظیفه",
agent: "عامل: {agent}",
openSession: "باز کردن جلسه",
cancelTask: "لغو {title}",
cancelling: "در حال لغو…",
status: {
queued: "در صف",
running: "در حال اجرا",
completed: "تکمیل‌شده",
failed: "ناموفق",
cancelled: "لغوشده",
timedOut: "زمان به پایان رسید",
},
runtime: {
subagent: "زیرعامل",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "وظیفه",
},
},
skillWorkshop: {
header: {
useCurrentChat: "استفاده از گفتگوی فعلی",

View file

@ -506,6 +506,7 @@ export const fr: TranslationMap = {
sessions: "Sessions",
usage: "Utilisation",
cron: "Tâches cron",
tasks: "Tâches",
skills: "Skills",
skillWorkshop: "Atelier Skills",
nodes: "Nœuds",
@ -533,6 +534,7 @@ export const fr: TranslationMap = {
sessions: "Sessions actives et valeurs par défaut.",
usage: "Utilisation de lAPI et coûts.",
cron: "Réveils et exécutions récurrentes.",
tasks: "Tâches en arrière-plan : sous-agents, exécutions cron, CLI.",
skills: "Skills et clés API.",
skillWorkshop:
"Examinez, affinez et appliquez les propositions avant quelles ne deviennent des skills actives.",
@ -550,6 +552,40 @@ export const fr: TranslationMap = {
dreams: "Consolidation de la mémoire pendant le sommeil.",
plugin: "Panneau fourni par le plugin.",
},
tasksPage: {
active: "Actives",
recent: "Récentes",
loading: "Chargement des tâches…",
empty: "Aucune tâche en arrière-plan pour le moment.",
emptyActive: "Aucune tâche en file dattente ou en cours dexécution.",
emptyRecent: "Aucune tâche terminée récemment.",
disconnected: "Connectez-vous au Gateway pour charger et gérer les tâches.",
loadFailed: "Impossible de charger les tâches.",
cancelFailed: "Impossible dannuler la tâche.",
invalidResponse: "Le Gateway a renvoyé une liste de tâches non valide.",
untitled: "Tâche en arrière-plan",
taskCount: "{count} tâches",
taskCountOne: "1 tâche",
agent: "Agent : {agent}",
openSession: "Ouvrir la session",
cancelTask: "Annuler {title}",
cancelling: "Annulation…",
status: {
queued: "En file dattente",
running: "En cours",
completed: "Terminé",
failed: "Échoué",
cancelled: "Annulé",
timedOut: "Expiration du délai",
},
runtime: {
subagent: "Sous-agent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Tâche",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Utiliser la discussion actuelle",

View file

@ -502,6 +502,7 @@ export const hi: TranslationMap = {
sessions: "सत्र",
usage: "उपयोग",
cron: "Cron Jobs",
tasks: "कार्य",
skills: "कौशल",
skillWorkshop: "कौशल वर्कशॉप",
nodes: "नोड्स",
@ -529,6 +530,7 @@ export const hi: TranslationMap = {
sessions: "सक्रिय सेशन और डिफ़ॉल्ट्स।",
usage: "API उपयोग और लागतें।",
cron: "वेकअप्स और आवर्ती रन।",
tasks: "पृष्ठभूमि कार्य: subagents, cron runs, CLI.",
skills: "स्किल्स और API कुंजियाँ।",
skillWorkshop: "प्रस्तावों के लाइव स्किल्स बनने से पहले उनकी समीक्षा करें, उन्हें परिष्कृत करें और लागू करें।",
nodes: "पेयर किए गए डिवाइस और कमांड्स।",
@ -545,6 +547,40 @@ export const hi: TranslationMap = {
dreams: "मेमोरी ड्रीमिंग, कंसॉलिडेशन, और रिफ्लेक्शन।",
plugin: "प्लगइन द्वारा उपलब्ध कराया गया पैनल।",
},
tasksPage: {
active: "सक्रिय",
recent: "हाल के",
loading: "कार्य लोड हो रहे हैं…",
empty: "अभी तक कोई पृष्ठभूमि कार्य नहीं है.",
emptyActive: "कोई कतारबद्ध या चल रहा कार्य नहीं है.",
emptyRecent: "हाल ही में पूरा हुआ कोई कार्य नहीं है.",
disconnected: "कार्य लोड और प्रबंधित करने के लिए Gateway से कनेक्ट करें.",
loadFailed: "कार्य लोड नहीं किए जा सके.",
cancelFailed: "कार्य रद्द नहीं किया जा सका.",
invalidResponse: "Gateway ने अमान्य कार्य सूची लौटाई.",
untitled: "पृष्ठभूमि कार्य",
taskCount: "{count} कार्य",
taskCountOne: "1 कार्य",
agent: "Agent: {agent}",
openSession: "सेशन खोलें",
cancelTask: "{title} रद्द करें",
cancelling: "रद्द किया जा रहा है…",
status: {
queued: "कतारबद्ध",
running: "चल रहा है",
completed: "पूर्ण",
failed: "विफल",
cancelled: "रद्द किया गया",
timedOut: "समय समाप्त",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "कार्य",
},
},
skillWorkshop: {
header: {
useCurrentChat: "वर्तमान चैट का उपयोग करें",

View file

@ -502,6 +502,7 @@ export const id: TranslationMap = {
sessions: "Sesi",
usage: "Penggunaan",
cron: "Tugas Cron",
tasks: "Tugas",
skills: "Skills",
skillWorkshop: "Skill Workshop",
nodes: "Node",
@ -529,6 +530,7 @@ export const id: TranslationMap = {
sessions: "Sesi aktif dan default.",
usage: "Penggunaan API dan biaya.",
cron: "Bangun dan proses berulang.",
tasks: "Tugas latar belakang: subagen, eksekusi cron, CLI.",
skills: "Skills dan kunci API.",
skillWorkshop: "Tinjau, sempurnakan, dan terapkan proposal sebelum menjadi Skills aktif.",
nodes: "Perangkat yang dipasangkan dan perintah.",
@ -545,6 +547,40 @@ export const id: TranslationMap = {
dreams: "Konsolidasi memori saat tidur.",
plugin: "Panel yang disediakan plugin.",
},
tasksPage: {
active: "Aktif",
recent: "Terbaru",
loading: "Memuat tugas…",
empty: "Belum ada tugas latar belakang.",
emptyActive: "Tidak ada tugas dalam antrean atau sedang berjalan.",
emptyRecent: "Tidak ada tugas selesai terbaru.",
disconnected: "Hubungkan ke gateway untuk memuat dan mengelola tugas.",
loadFailed: "Tidak dapat memuat tugas.",
cancelFailed: "Tidak dapat membatalkan tugas.",
invalidResponse: "Gateway mengembalikan daftar tugas yang tidak valid.",
untitled: "Tugas latar belakang",
taskCount: "{count} tugas",
taskCountOne: "1 tugas",
agent: "Agen: {agent}",
openSession: "Buka sesi",
cancelTask: "Batalkan {title}",
cancelling: "Membatalkan…",
status: {
queued: "Dalam antrean",
running: "Berjalan",
completed: "Selesai",
failed: "Gagal",
cancelled: "Dibatalkan",
timedOut: "Waktu habis",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Tugas",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Gunakan obrolan saat ini",

View file

@ -506,6 +506,7 @@ export const it: TranslationMap = {
sessions: "Sessioni",
usage: "Utilizzo",
cron: "Processi cron",
tasks: "Attività",
skills: "Skills",
skillWorkshop: "Workshop delle Skill",
nodes: "Nodi",
@ -533,6 +534,7 @@ export const it: TranslationMap = {
sessions: "Sessioni attive e valori predefiniti.",
usage: "Utilizzo API e costi.",
cron: "Risvegli ed esecuzioni ricorrenti.",
tasks: "Attività in background: subagenti, esecuzioni cron, CLI.",
skills: "Skills e chiavi API.",
skillWorkshop: "Esamina, perfeziona e applica le proposte prima che diventino skill attive.",
nodes: "Dispositivi associati e comandi.",
@ -549,6 +551,40 @@ export const it: TranslationMap = {
dreams: "Sogni della memoria, consolidamento e riflessione.",
plugin: "Pannello fornito dal plugin.",
},
tasksPage: {
active: "Attive",
recent: "Recenti",
loading: "Caricamento attività…",
empty: "Nessuna attività in background al momento.",
emptyActive: "Nessuna attività in coda o in esecuzione.",
emptyRecent: "Nessuna attività completata di recente.",
disconnected: "Connettiti al Gateway per caricare e gestire le attività.",
loadFailed: "Impossibile caricare le attività.",
cancelFailed: "Impossibile annullare l'attività.",
invalidResponse: "Il Gateway ha restituito un elenco di attività non valido.",
untitled: "Attività in background",
taskCount: "{count} attività",
taskCountOne: "1 attività",
agent: "Agente: {agent}",
openSession: "Apri sessione",
cancelTask: "Annulla {title}",
cancelling: "Annullamento…",
status: {
queued: "In coda",
running: "In esecuzione",
completed: "Completato",
failed: "Non riuscito",
cancelled: "Annullato",
timedOut: "Tempo scaduto",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Attività",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Usa la chat corrente",

View file

@ -507,6 +507,7 @@ export const ja_JP: TranslationMap = {
sessions: "セッション",
usage: "使用状況",
cron: "Cron ジョブ",
tasks: "タスク",
skills: "Skills",
skillWorkshop: "Skill Workshop",
nodes: "ノード",
@ -534,6 +535,7 @@ export const ja_JP: TranslationMap = {
sessions: "アクティブなセッションとデフォルト。",
usage: "API 使用量とコスト。",
cron: "ウェイクアップと定期実行。",
tasks: "バックグラウンドタスク: サブエージェント、cron 実行、CLI。",
skills: "Skills と API キー。",
skillWorkshop: "提案がライブスキルになる前に、確認、調整、適用します。",
nodes: "ペアリング済みデバイスとコマンド。",
@ -550,6 +552,40 @@ export const ja_JP: TranslationMap = {
dreams: "スリープ中のメモリ統合。",
plugin: "プラグインが提供するパネル。",
},
tasksPage: {
active: "アクティブ",
recent: "最近",
loading: "タスクを読み込み中…",
empty: "バックグラウンドタスクはまだありません。",
emptyActive: "キュー内または実行中のタスクはありません。",
emptyRecent: "最近完了したタスクはありません。",
disconnected: "タスクを読み込み、管理するには Gateway に接続してください。",
loadFailed: "タスクを読み込めませんでした。",
cancelFailed: "タスクをキャンセルできませんでした。",
invalidResponse: "Gateway が無効なタスクリストを返しました。",
untitled: "バックグラウンドタスク",
taskCount: "{count} 件のタスク",
taskCountOne: "1 件のタスク",
agent: "エージェント: {agent}",
openSession: "セッションを開く",
cancelTask: "{title} をキャンセル",
cancelling: "キャンセル中…",
status: {
queued: "キュー内",
running: "実行中",
completed: "完了",
failed: "失敗",
cancelled: "キャンセル済み",
timedOut: "タイムアウト",
},
runtime: {
subagent: "サブエージェント",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "タスク",
},
},
skillWorkshop: {
header: {
useCurrentChat: "現在のチャットを使用",

View file

@ -501,6 +501,7 @@ export const ko: TranslationMap = {
sessions: "세션",
usage: "사용량",
cron: "Cron 작업",
tasks: "작업",
skills: "Skills",
skillWorkshop: "Skill Workshop",
nodes: "노드",
@ -528,6 +529,7 @@ export const ko: TranslationMap = {
sessions: "활성 세션 및 기본값.",
usage: "API 사용량 및 비용.",
cron: "웨이크업 및 반복 실행.",
tasks: "백그라운드 작업: 하위 에이전트, cron 실행, CLI.",
skills: "Skills 및 API 키.",
skillWorkshop: "제안이 실제 Skills가 되기 전에 검토하고, 다듬고, 적용하세요.",
nodes: "페어링된 디바이스 및 명령.",
@ -544,6 +546,40 @@ export const ko: TranslationMap = {
dreams: "수면 중 메모리 통합.",
plugin: "플러그인이 제공하는 패널입니다.",
},
tasksPage: {
active: "활성",
recent: "최근",
loading: "작업을 불러오는 중…",
empty: "아직 백그라운드 작업이 없습니다.",
emptyActive: "대기 중이거나 실행 중인 작업이 없습니다.",
emptyRecent: "최근 완료된 작업이 없습니다.",
disconnected: "작업을 불러오고 관리하려면 Gateway에 연결하세요.",
loadFailed: "작업을 불러올 수 없습니다.",
cancelFailed: "작업을 취소할 수 없습니다.",
invalidResponse: "Gateway가 잘못된 작업 목록을 반환했습니다.",
untitled: "백그라운드 작업",
taskCount: "{count}개 작업",
taskCountOne: "작업 1개",
agent: "에이전트: {agent}",
openSession: "세션 열기",
cancelTask: "{title} 취소",
cancelling: "취소 중…",
status: {
queued: "대기 중",
running: "실행 중",
completed: "완료됨",
failed: "실패함",
cancelled: "취소됨",
timedOut: "시간 초과됨",
},
runtime: {
subagent: "하위 에이전트",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "작업",
},
},
skillWorkshop: {
header: {
useCurrentChat: "현재 채팅 사용",

View file

@ -504,6 +504,7 @@ export const nl: TranslationMap = {
sessions: "Sessies",
usage: "Gebruik",
cron: "Cronjobs",
tasks: "Taken",
skills: "Skills",
skillWorkshop: "Skill Workshop",
nodes: "Nodes",
@ -531,6 +532,7 @@ export const nl: TranslationMap = {
sessions: "Actieve sessies en standaarden.",
usage: "API-gebruik en kosten.",
cron: "Wakeups en terugkerende runs.",
tasks: "Achtergrondtaken: subagents, cron-runs, CLI.",
skills: "Skills en API-sleutels.",
skillWorkshop: "Bekijk, verfijn en pas voorstellen toe voordat ze live Skills worden.",
nodes: "Gekoppelde apparaten en commando's.",
@ -547,6 +549,40 @@ export const nl: TranslationMap = {
dreams: "Geheugendromen, consolidatie en reflectie.",
plugin: "Door een plugin geleverd paneel.",
},
tasksPage: {
active: "Actief",
recent: "Recent",
loading: "Taken laden…",
empty: "Nog geen achtergrondtaken.",
emptyActive: "Geen taken in de wachtrij of actief.",
emptyRecent: "Geen recent voltooide taken.",
disconnected: "Maak verbinding met de Gateway om taken te laden en te beheren.",
loadFailed: "Kan taken niet laden.",
cancelFailed: "Kan de taak niet annuleren.",
invalidResponse: "De Gateway heeft een ongeldige takenlijst geretourneerd.",
untitled: "Achtergrondtaak",
taskCount: "{count} taken",
taskCountOne: "1 taak",
agent: "Agent: {agent}",
openSession: "Sessie openen",
cancelTask: "{title} annuleren",
cancelling: "Annuleren…",
status: {
queued: "In wachtrij",
running: "Actief",
completed: "Voltooid",
failed: "Mislukt",
cancelled: "Geannuleerd",
timedOut: "Time-out",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Taak",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Huidige chat gebruiken",

View file

@ -503,6 +503,7 @@ export const pl: TranslationMap = {
sessions: "Sesje",
usage: "Użycie",
cron: "Zadania Cron",
tasks: "Zadania",
skills: "Skills",
skillWorkshop: "Warsztat Skills",
nodes: "Węzły",
@ -530,6 +531,7 @@ export const pl: TranslationMap = {
sessions: "Aktywne sesje i ustawienia domyślne.",
usage: "Zużycie API i koszty.",
cron: "Wybudzenia i cykliczne uruchomienia.",
tasks: "Zadania w tle: subagenci, uruchomienia cron, CLI.",
skills: "Skills i klucze API.",
skillWorkshop: "Przeglądaj, dopracowuj i stosuj propozycje, zanim staną się aktywnymi skills.",
nodes: "Sparowane urządzenia i polecenia.",
@ -546,6 +548,40 @@ export const pl: TranslationMap = {
dreams: "Konsolidacja pamięci podczas snu.",
plugin: "Panel udostępniany przez wtyczkę.",
},
tasksPage: {
active: "Aktywne",
recent: "Ostatnie",
loading: "Ładowanie zadań…",
empty: "Brak zadań w tle.",
emptyActive: "Brak zadań w kolejce lub uruchomionych.",
emptyRecent: "Brak ostatnio ukończonych zadań.",
disconnected: "Połącz się z Gateway, aby wczytywać zadania i zarządzać nimi.",
loadFailed: "Nie udało się wczytać zadań.",
cancelFailed: "Nie udało się anulować zadania.",
invalidResponse: "Gateway zwrócił nieprawidłową listę zadań.",
untitled: "Zadanie w tle",
taskCount: "{count} zadań",
taskCountOne: "1 zadanie",
agent: "Agent: {agent}",
openSession: "Otwórz sesję",
cancelTask: "Anuluj {title}",
cancelling: "Anulowanie…",
status: {
queued: "W kolejce",
running: "W toku",
completed: "Ukończono",
failed: "Niepowodzenie",
cancelled: "Anulowano",
timedOut: "Przekroczono limit czasu",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Zadanie",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Użyj bieżącego czatu",

View file

@ -502,6 +502,7 @@ export const pt_BR: TranslationMap = {
sessions: "Sessões",
usage: "Uso",
cron: "Tarefas Cron",
tasks: "Tarefas",
skills: "Skills",
skillWorkshop: "Oficina de Skills",
nodes: "Nós",
@ -529,6 +530,7 @@ export const pt_BR: TranslationMap = {
sessions: "Sessões ativas e padrões.",
usage: "Uso e custos da API.",
cron: "Despertares e execuções.",
tasks: "Tarefas em segundo plano: subagentes, execuções de cron, CLI.",
skills: "Habilidades e chaves API.",
skillWorkshop: "Revise, refine e aplique propostas antes que elas se tornem skills ativas.",
nodes: "Dispositivos e comandos.",
@ -545,6 +547,40 @@ export const pt_BR: TranslationMap = {
dreams: "Consolidação de memória durante o sono.",
plugin: "Painel fornecido pelo plugin.",
},
tasksPage: {
active: "Ativas",
recent: "Recentes",
loading: "Carregando tarefas…",
empty: "Ainda não há tarefas em segundo plano.",
emptyActive: "Nenhuma tarefa na fila ou em execução.",
emptyRecent: "Nenhuma tarefa concluída recentemente.",
disconnected: "Conecte-se ao Gateway para carregar e gerenciar tarefas.",
loadFailed: "Não foi possível carregar as tarefas.",
cancelFailed: "Não foi possível cancelar a tarefa.",
invalidResponse: "O Gateway retornou uma lista de tarefas inválida.",
untitled: "Tarefa em segundo plano",
taskCount: "{count} tarefas",
taskCountOne: "1 tarefa",
agent: "Agente: {agent}",
openSession: "Abrir sessão",
cancelTask: "Cancelar {title}",
cancelling: "Cancelando…",
status: {
queued: "Na fila",
running: "Em execução",
completed: "Concluído",
failed: "Falhou",
cancelled: "Cancelado",
timedOut: "Tempo esgotado",
},
runtime: {
subagent: "Subagente",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Tarefa",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Usar conversa atual",

View file

@ -505,6 +505,7 @@ export const ru: TranslationMap = {
sessions: "Сеансы",
usage: "Использование",
cron: "Задания Cron",
tasks: "Задачи",
skills: "Навыки",
skillWorkshop: "Мастерская навыков",
nodes: "Узлы",
@ -532,6 +533,7 @@ export const ru: TranslationMap = {
sessions: "Активные сеансы и значения по умолчанию.",
usage: "Использование API и расходы.",
cron: "Пробуждения и повторяющиеся запуски.",
tasks: "Фоновые задачи: субагенты, запуски cron, CLI.",
skills: "Навыки и ключи API.",
skillWorkshop:
"Проверяйте, уточняйте и применяйте предложения до того, как они станут активными навыками.",
@ -549,6 +551,40 @@ export const ru: TranslationMap = {
dreams: "Сновидения памяти, консолидация и рефлексия.",
plugin: "Панель, предоставленная плагином.",
},
tasksPage: {
active: "Активно",
recent: "Недавние",
loading: "Загрузка задач…",
empty: "Фоновых задач пока нет.",
emptyActive: "Нет задач в очереди или в работе.",
emptyRecent: "Нет недавно завершенных задач.",
disconnected: "Подключитесь к Gateway, чтобы загружать задачи и управлять ими.",
loadFailed: "Не удалось загрузить задачи.",
cancelFailed: "Не удалось отменить задачу.",
invalidResponse: "Gateway вернул недопустимый список задач.",
untitled: "Фоновая задача",
taskCount: "{count} задач",
taskCountOne: "1 задача",
agent: "Агент: {agent}",
openSession: "Открыть сеанс",
cancelTask: "Отменить {title}",
cancelling: "Отмена…",
status: {
queued: "В очереди",
running: "Выполняется",
completed: "Завершено",
failed: "Сбой",
cancelled: "Отменено",
timedOut: "Время ожидания истекло",
},
runtime: {
subagent: "Субагент",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Задача",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Использовать текущий чат",

View file

@ -500,6 +500,7 @@ export const th: TranslationMap = {
sessions: "เซสชัน",
usage: "การใช้งาน",
cron: "งาน Cron",
tasks: "งาน",
skills: "ทักษะ",
skillWorkshop: "Skill Workshop",
nodes: "โหนด",
@ -527,6 +528,7 @@ export const th: TranslationMap = {
sessions: "เซสชันที่ใช้งานอยู่และค่าเริ่มต้น",
usage: "การใช้งาน API และค่าใช้จ่าย",
cron: "การปลุกและการทำงานตามรอบ",
tasks: "งานเบื้องหลัง: subagents, การรัน cron, CLI.",
skills: "ทักษะและคีย์ API",
skillWorkshop: "ตรวจสอบ ปรับแต่ง และนำข้อเสนอไปใช้ก่อนที่จะกลายเป็น Skills ที่ใช้งานจริง",
nodes: "อุปกรณ์ที่จับคู่และคำสั่ง",
@ -543,6 +545,40 @@ export const th: TranslationMap = {
dreams: "การฝันของหน่วยความจำ การรวมข้อมูล และการสะท้อนคิด",
plugin: "แผงที่ปลั๊กอินจัดเตรียมไว้",
},
tasksPage: {
active: "กำลังใช้งาน",
recent: "ล่าสุด",
loading: "กำลังโหลดงาน…",
empty: "ยังไม่มีงานเบื้องหลัง",
emptyActive: "ไม่มีงานที่อยู่ในคิวหรือกำลังทำงาน",
emptyRecent: "ไม่มีงานที่เสร็จสิ้นล่าสุด",
disconnected: "เชื่อมต่อกับ gateway เพื่อโหลดและจัดการงาน",
loadFailed: "ไม่สามารถโหลดงานได้",
cancelFailed: "ไม่สามารถยกเลิกงานได้",
invalidResponse: "gateway ส่งคืนรายการงานที่ไม่ถูกต้อง",
untitled: "งานเบื้องหลัง",
taskCount: "{count} งาน",
taskCountOne: "1 งาน",
agent: "เอเจนต์: {agent}",
openSession: "เปิดเซสชัน",
cancelTask: "ยกเลิก {title}",
cancelling: "กำลังยกเลิก…",
status: {
queued: "อยู่ในคิว",
running: "กำลังทำงาน",
completed: "เสร็จสิ้น",
failed: "ล้มเหลว",
cancelled: "ยกเลิกแล้ว",
timedOut: "หมดเวลา",
},
runtime: {
subagent: "Subagent",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "งาน",
},
},
skillWorkshop: {
header: {
useCurrentChat: "ใช้แชทปัจจุบัน",

View file

@ -505,6 +505,7 @@ export const tr: TranslationMap = {
sessions: "Oturumlar",
usage: "Kullanım",
cron: "Cron İşleri",
tasks: "Görevler",
skills: "Skills",
skillWorkshop: "Skill Atölyesi",
nodes: "Düğümler",
@ -532,6 +533,7 @@ export const tr: TranslationMap = {
sessions: "Etkin oturumlar ve varsayılanlar.",
usage: "API kullanımı ve maliyetler.",
cron: "Uyandırmalar ve yinelenen çalıştırmalar.",
tasks: "Arka plan görevleri: alt ajanlar, cron çalıştırmaları, CLI.",
skills: "Skills ve API anahtarları.",
skillWorkshop:
"Teklifleri canlı becerilere dönüşmeden önce gözden geçirin, iyileştirin ve uygulayın.",
@ -549,6 +551,40 @@ export const tr: TranslationMap = {
dreams: "Uyku sırasında bellek birleştirme.",
plugin: "Eklenti tarafından sağlanan panel.",
},
tasksPage: {
active: "Etkin",
recent: "Son",
loading: "Görevler yükleniyor…",
empty: "Henüz arka plan görevi yok.",
emptyActive: "Kuyrukta veya çalışan görev yok.",
emptyRecent: "Yakın zamanda tamamlanan görev yok.",
disconnected: "Görevleri yüklemek ve yönetmek için gateway'e bağlanın.",
loadFailed: "Görevler yüklenemedi.",
cancelFailed: "Görev iptal edilemedi.",
invalidResponse: "Gateway geçersiz bir görev listesi döndürdü.",
untitled: "Arka plan görevi",
taskCount: "{count} görev",
taskCountOne: "1 görev",
agent: "Ajan: {agent}",
openSession: "Oturumu aç",
cancelTask: "{title} görevini iptal et",
cancelling: "İptal ediliyor…",
status: {
queued: "Kuyrukta",
running: "Çalışıyor",
completed: "Tamamlandı",
failed: "Başarısız oldu",
cancelled: "İptal edildi",
timedOut: "Zaman aşımına uğradı",
},
runtime: {
subagent: "Alt ajan",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Görev",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Mevcut sohbeti kullan",

View file

@ -503,6 +503,7 @@ export const uk: TranslationMap = {
sessions: "Сеанси",
usage: "Використання",
cron: "Завдання Cron",
tasks: "Завдання",
skills: "Навички",
skillWorkshop: "Майстерня Skills",
nodes: "Вузли",
@ -530,6 +531,7 @@ export const uk: TranslationMap = {
sessions: "Активні сеанси та типові значення.",
usage: "Використання API та витрати.",
cron: "Пробудження та повторювані запуски.",
tasks: "Фонові завдання: субагенти, запуски cron, CLI.",
skills: "Навички та API-ключі.",
skillWorkshop:
"Переглядайте, уточнюйте й застосовуйте пропозиції, перш ніж вони стануть активними Skills.",
@ -547,6 +549,40 @@ export const uk: TranslationMap = {
dreams: "Консолідація пам’яті під час сну.",
plugin: "Панель, надана плагіном.",
},
tasksPage: {
active: "Активні",
recent: "Нещодавні",
loading: "Завантаження завдань…",
empty: "Фонових завдань ще немає.",
emptyActive: "Немає завдань у черзі або виконанні.",
emptyRecent: "Немає нещодавно завершених завдань.",
disconnected: "Підключіться до Gateway, щоб завантажувати завдання й керувати ними.",
loadFailed: "Не вдалося завантажити завдання.",
cancelFailed: "Не вдалося скасувати завдання.",
invalidResponse: "Gateway повернув недійсний список завдань.",
untitled: "Фонове завдання",
taskCount: "{count} завдань",
taskCountOne: "1 завдання",
agent: "Агент: {agent}",
openSession: "Відкрити сеанс",
cancelTask: "Скасувати {title}",
cancelling: "Скасування…",
status: {
queued: "У черзі",
running: "Виконується",
completed: "Завершено",
failed: "Не вдалося",
cancelled: "Скасовано",
timedOut: "Час очікування минув",
},
runtime: {
subagent: "Субагент",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Завдання",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Використовувати поточний чат",

View file

@ -502,6 +502,7 @@ export const vi: TranslationMap = {
sessions: "Phiên",
usage: "Mức sử dụng",
cron: "Tác vụ Cron",
tasks: "Tác vụ",
skills: "Skills",
skillWorkshop: "Xưởng kỹ năng",
nodes: "Nút",
@ -529,6 +530,7 @@ export const vi: TranslationMap = {
sessions: "Phiên đang hoạt động và mặc định.",
usage: "Mức sử dụng API và chi phí.",
cron: "Đánh thức và chạy định kỳ.",
tasks: "Tác vụ nền: subagent, cron run, CLI.",
skills: "Skills và khóa API.",
skillWorkshop:
"Xem xét, tinh chỉnh và áp dụng các đề xuất trước khi chúng trở thành kỹ năng đang hoạt động.",
@ -546,6 +548,40 @@ export const vi: TranslationMap = {
dreams: "Mơ bộ nhớ, hợp nhất và phản chiếu.",
plugin: "Bảng điều khiển do plugin cung cấp.",
},
tasksPage: {
active: "Đang hoạt động",
recent: "Gần đây",
loading: "Đang tải tác vụ…",
empty: "Chưa có tác vụ nền.",
emptyActive: "Không có tác vụ nào đang xếp hàng hoặc đang chạy.",
emptyRecent: "Không có tác vụ nào đã hoàn tất gần đây.",
disconnected: "Kết nối với gateway để tải và quản lý tác vụ.",
loadFailed: "Không thể tải tác vụ.",
cancelFailed: "Không thể hủy tác vụ.",
invalidResponse: "gateway đã trả về danh sách tác vụ không hợp lệ.",
untitled: "Tác vụ nền",
taskCount: "{count} tác vụ",
taskCountOne: "1 tác vụ",
agent: "Agent: {agent}",
openSession: "Mở phiên",
cancelTask: "Hủy {title}",
cancelling: "Đang hủy…",
status: {
queued: "Đã xếp hàng",
running: "Đang chạy",
completed: "Đã hoàn tất",
failed: "Thất bại",
cancelled: "Đã hủy",
timedOut: "Đã hết thời gian",
},
runtime: {
subagent: "Tác nhân phụ",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "Tác vụ",
},
},
skillWorkshop: {
header: {
useCurrentChat: "Dùng cuộc trò chuyện hiện tại",

View file

@ -499,6 +499,7 @@ export const zh_CN: TranslationMap = {
sessions: "会话",
usage: "使用情况",
cron: "定时任务",
tasks: "任务",
skills: "技能",
skillWorkshop: "技能工坊",
nodes: "节点",
@ -526,6 +527,7 @@ export const zh_CN: TranslationMap = {
sessions: "活动会话和默认设置。",
usage: "API 使用情况和成本。",
cron: "唤醒和重复运行。",
tasks: "后台任务子代理、cron 运行、CLI。",
skills: "技能和 API 密钥。",
skillWorkshop: "在提案成为上线技能之前,进行审查、优化并应用。",
nodes: "配对设备和命令。",
@ -542,6 +544,40 @@ export const zh_CN: TranslationMap = {
dreams: "睡眠时进行记忆巩固。",
plugin: "插件提供的面板。",
},
tasksPage: {
active: "活跃",
recent: "最近",
loading: "正在加载任务…",
empty: "还没有后台任务。",
emptyActive: "没有排队或正在运行的任务。",
emptyRecent: "没有最近完成的任务。",
disconnected: "连接到 Gateway 以加载和管理任务。",
loadFailed: "无法加载任务。",
cancelFailed: "无法取消该任务。",
invalidResponse: "Gateway 返回了无效的任务列表。",
untitled: "后台任务",
taskCount: "{count} 个任务",
taskCountOne: "1 个任务",
agent: "代理:{agent}",
openSession: "打开会话",
cancelTask: "取消 {title}",
cancelling: "正在取消…",
status: {
queued: "已排队",
running: "运行中",
completed: "已完成",
failed: "失败",
cancelled: "已取消",
timedOut: "已超时",
},
runtime: {
subagent: "子代理",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "任务",
},
},
skillWorkshop: {
header: {
useCurrentChat: "使用当前聊天",

View file

@ -499,6 +499,7 @@ export const zh_TW: TranslationMap = {
sessions: "工作階段",
usage: "使用情況",
cron: "定時任務",
tasks: "任務",
skills: "Skills",
skillWorkshop: "Skill Workshop",
nodes: "節點",
@ -526,6 +527,7 @@ export const zh_TW: TranslationMap = {
sessions: "活動會話和默認設置。",
usage: "API 使用情況和成本。",
cron: "喚醒和重複運行。",
tasks: "背景任務子代理、cron 執行、CLI。",
skills: "技能和 API 密鑰。",
skillWorkshop: "在提案成為上線技能之前,先進行審閱、調整並套用。",
nodes: "配對設備和命令。",
@ -542,6 +544,40 @@ export const zh_TW: TranslationMap = {
dreams: "睡眠期間的記憶整合。",
plugin: "外掛程式提供的面板。",
},
tasksPage: {
active: "進行中",
recent: "最近",
loading: "正在載入任務…",
empty: "尚無背景任務。",
emptyActive: "沒有排入佇列或正在執行的任務。",
emptyRecent: "沒有最近完成的任務。",
disconnected: "連線至 Gateway 以載入並管理任務。",
loadFailed: "無法載入任務。",
cancelFailed: "無法取消該任務。",
invalidResponse: "Gateway 傳回了無效的任務清單。",
untitled: "背景任務",
taskCount: "{count} 個任務",
taskCountOne: "1 個任務",
agent: "代理:{agent}",
openSession: "開啟工作階段",
cancelTask: "取消 {title}",
cancelling: "正在取消…",
status: {
queued: "已排入佇列",
running: "執行中",
completed: "已完成",
failed: "失敗",
cancelled: "已取消",
timedOut: "逾時",
},
runtime: {
subagent: "子代理",
cron: "Cron",
acp: "ACP",
cli: "CLI",
unknown: "任務",
},
},
skillWorkshop: {
header: {
useCurrentChat: "使用目前的對話",

View file

@ -0,0 +1,120 @@
import { describe, expect, it } from "vitest";
import {
applyTaskEvent,
mergeTaskLists,
normalizeTaskSummary,
normalizeTasksCancelResult,
normalizeTasksListResult,
partitionTasks,
sortTasks,
type TaskSummary,
} from "./data.ts";
function task(overrides: Partial<TaskSummary> & Pick<TaskSummary, "id" | "status">): TaskSummary {
return {
taskId: overrides.id,
updatedAt: 100,
...overrides,
};
}
describe("tasks page data", () => {
it("normalizes valid task summaries and rejects invalid statuses", () => {
expect(
normalizeTaskSummary({
id: " task-1 ",
status: "running",
runtime: "subagent",
title: " Build release ",
updatedAt: "2026-07-05T12:00:00.000Z",
}),
).toEqual({
id: "task-1",
taskId: "task-1",
status: "running",
runtime: "subagent",
title: "Build release",
updatedAt: "2026-07-05T12:00:00.000Z",
});
expect(normalizeTaskSummary({ id: "task-2", status: "lost" })).toBeNull();
expect(normalizeTasksListResult({ tasks: "not-an-array" })).toBeNull();
});
it("sorts by updated time descending with an id tiebreak", () => {
const sorted = sortTasks([
task({ id: "b", status: "queued", updatedAt: 200 }),
task({ id: "c", status: "completed", updatedAt: 300 }),
task({ id: "a", status: "running", updatedAt: 200 }),
]);
expect(sorted.map((entry) => entry.id)).toEqual(["c", "a", "b"]);
});
it("partitions active tasks and caps recent terminal tasks at 50", () => {
const terminals = Array.from({ length: 55 }, (_, index) =>
task({ id: `terminal-${index}`, status: "completed", updatedAt: index }),
);
const result = partitionTasks([
task({ id: "running", status: "running", updatedAt: 1000 }),
task({ id: "queued", status: "queued", updatedAt: 999 }),
...terminals,
]);
expect(result.active.map((entry) => entry.id)).toEqual(["running", "queued"]);
expect(result.recent).toHaveLength(50);
expect(result.recent[0]?.id).toBe("terminal-54");
});
it("merges task lists by id with later lists winning", () => {
const recentPage = [
task({ id: "new-terminal", status: "completed", updatedAt: 900 }),
task({ id: "shared", status: "running", updatedAt: 800 }),
];
const activePage = [
task({ id: "shared", status: "running", updatedAt: 850 }),
task({ id: "old-running", status: "running", updatedAt: 10 }),
];
const merged = mergeTaskLists(recentPage, activePage);
expect(merged.map((entry) => entry.id)).toEqual(["new-terminal", "shared", "old-running"]);
expect(merged.find((entry) => entry.id === "shared")?.updatedAt).toBe(850);
});
it("normalizes cancel results including refusals with reasons", () => {
expect(
normalizeTasksCancelResult({
found: true,
cancelled: false,
reason: "task already finished",
task: { id: "task-1", taskId: "task-1", status: "completed" },
}),
).toEqual({
found: true,
cancelled: false,
reason: "task already finished",
task: { id: "task-1", taskId: "task-1", status: "completed" },
});
expect(normalizeTasksCancelResult({ found: true, cancelled: true })).toEqual({
found: true,
cancelled: true,
});
expect(normalizeTasksCancelResult({ found: true })).toBeNull();
expect(normalizeTasksCancelResult("nope")).toBeNull();
});
it("merges upserts, applies deletes, and requests refetches for restored events", () => {
const initial = [task({ id: "task-1", status: "running", updatedAt: 100 })];
const completed = task({ id: "task-1", status: "completed", updatedAt: 200 });
const upserted = applyTaskEvent(initial, { action: "upserted", task: completed });
expect(upserted).toEqual({ tasks: [completed], refetch: false });
const deleted = applyTaskEvent(upserted.tasks, { action: "deleted", taskId: "task-1" });
expect(deleted).toEqual({ tasks: [], refetch: false });
expect(applyTaskEvent(initial, { action: "restored" })).toEqual({
tasks: initial,
refetch: true,
});
expect(applyTaskEvent(initial, { action: "upserted", task: { id: "broken" } })).toEqual({
tasks: initial,
refetch: true,
});
});
});

231
ui/src/pages/tasks/data.ts Normal file
View file

@ -0,0 +1,231 @@
export type TaskStatus = "queued" | "running" | "completed" | "failed" | "cancelled" | "timed_out";
export type TaskRuntime = "subagent" | "cron" | "acp" | "cli";
export type TaskTimestamp = number | string;
export type TaskSummary = {
id: string;
taskId: string;
status: TaskStatus;
kind?: string;
runtime?: TaskRuntime;
title?: string;
agentId?: string;
sessionKey?: string;
childSessionKey?: string;
createdAt?: TaskTimestamp;
updatedAt?: TaskTimestamp;
startedAt?: TaskTimestamp;
endedAt?: TaskTimestamp;
progressSummary?: string;
terminalSummary?: string;
error?: string;
};
export type TaskEventPayload =
| { action: "upserted"; task: TaskSummary }
| { action: "deleted"; taskId: string }
| { action: "restored" };
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function optionalString(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
function normalizeTaskStatus(value: unknown): TaskStatus | null {
switch (value) {
case "queued":
case "running":
case "completed":
case "failed":
case "cancelled":
case "timed_out":
return value;
default:
return null;
}
}
function normalizeTaskRuntime(value: unknown): TaskRuntime | undefined {
switch (value) {
case "subagent":
case "cron":
case "acp":
case "cli":
return value;
default:
return undefined;
}
}
function normalizeTimestamp(value: unknown): TaskTimestamp | undefined {
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
return value;
}
if (typeof value === "string" && Number.isFinite(Date.parse(value))) {
return value;
}
return undefined;
}
export function normalizeTaskSummary(value: unknown): TaskSummary | null {
if (!isRecord(value)) {
return null;
}
const id = optionalString(value.id);
const taskId = optionalString(value.taskId) ?? id;
const status = normalizeTaskStatus(value.status);
if (!id || !taskId || !status) {
return null;
}
const runtime = normalizeTaskRuntime(value.runtime);
const kind = optionalString(value.kind);
const title = optionalString(value.title);
const agentId = optionalString(value.agentId);
const sessionKey = optionalString(value.sessionKey);
const childSessionKey = optionalString(value.childSessionKey);
const createdAt = normalizeTimestamp(value.createdAt);
const updatedAt = normalizeTimestamp(value.updatedAt);
const startedAt = normalizeTimestamp(value.startedAt);
const endedAt = normalizeTimestamp(value.endedAt);
const progressSummary = optionalString(value.progressSummary);
const terminalSummary = optionalString(value.terminalSummary);
const error = optionalString(value.error);
return {
id,
taskId,
status,
...(kind ? { kind } : {}),
...(runtime ? { runtime } : {}),
...(title ? { title } : {}),
...(agentId ? { agentId } : {}),
...(sessionKey ? { sessionKey } : {}),
...(childSessionKey ? { childSessionKey } : {}),
...(createdAt !== undefined ? { createdAt } : {}),
...(updatedAt !== undefined ? { updatedAt } : {}),
...(startedAt !== undefined ? { startedAt } : {}),
...(endedAt !== undefined ? { endedAt } : {}),
...(progressSummary ? { progressSummary } : {}),
...(terminalSummary ? { terminalSummary } : {}),
...(error ? { error } : {}),
};
}
export function taskTimestampMs(value: TaskTimestamp | undefined): number {
if (typeof value === "number") {
return value;
}
if (typeof value === "string") {
const parsed = Date.parse(value);
return Number.isFinite(parsed) ? parsed : 0;
}
return 0;
}
export function sortTasks(tasks: readonly TaskSummary[]): TaskSummary[] {
return tasks.toSorted((left, right) => {
const timeDelta = taskTimestampMs(right.updatedAt) - taskTimestampMs(left.updatedAt);
if (timeDelta !== 0) {
return timeDelta;
}
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
});
}
export function partitionTasks(tasks: readonly TaskSummary[]): {
active: TaskSummary[];
recent: TaskSummary[];
} {
const sorted = sortTasks(tasks);
return {
active: sorted.filter((task) => task.status === "queued" || task.status === "running"),
recent: sorted
.filter((task) => task.status !== "queued" && task.status !== "running")
.slice(0, 50),
};
}
export function normalizeTasksListResult(value: unknown): TaskSummary[] | null {
if (!isRecord(value) || !Array.isArray(value.tasks)) {
return null;
}
return sortTasks(
value.tasks.map(normalizeTaskSummary).filter((task): task is TaskSummary => task !== null),
);
}
// The ledger pages newest-first, so one page can hide long-running tasks behind
// newer terminal records; callers fetch active tasks separately and merge here.
export function mergeTaskLists(...lists: readonly (readonly TaskSummary[])[]): TaskSummary[] {
const byId = new Map<string, TaskSummary>();
for (const list of lists) {
for (const task of list) {
byId.set(task.id, task);
}
}
return sortTasks([...byId.values()]);
}
export type TasksCancelResult = {
found: boolean;
cancelled: boolean;
reason?: string;
task?: TaskSummary;
};
// Cancellation refusals (already-terminal, missing handle, stale id) arrive as
// successful responses with cancelled=false + reason, not thrown errors.
export function normalizeTasksCancelResult(value: unknown): TasksCancelResult | null {
if (!isRecord(value) || typeof value.cancelled !== "boolean") {
return null;
}
const reason = optionalString(value.reason);
const task = normalizeTaskSummary(value.task);
return {
found: value.found === true,
cancelled: value.cancelled,
...(reason ? { reason } : {}),
...(task ? { task } : {}),
};
}
export function normalizeTaskEventPayload(value: unknown): TaskEventPayload | null {
if (!isRecord(value)) {
return null;
}
if (value.action === "restored") {
return { action: "restored" };
}
if (value.action === "deleted") {
const taskId = optionalString(value.taskId);
return taskId ? { action: "deleted", taskId } : null;
}
if (value.action === "upserted") {
const task = normalizeTaskSummary(value.task);
return task ? { action: "upserted", task } : null;
}
return null;
}
export function applyTaskEvent(
tasks: readonly TaskSummary[],
value: unknown,
): { tasks: TaskSummary[]; refetch: boolean } {
const event = normalizeTaskEventPayload(value);
if (!event || event.action === "restored") {
return { tasks: [...tasks], refetch: true };
}
if (event.action === "deleted") {
return {
tasks: sortTasks(tasks.filter((task) => task.id !== event.taskId)),
refetch: false,
};
}
return {
tasks: sortTasks([event.task, ...tasks.filter((task) => task.id !== event.task.id)]),
refetch: false,
};
}

View file

@ -0,0 +1,12 @@
import { definePage } from "@openclaw/uirouter";
import { html } from "lit";
export const page = definePage({
id: "tasks",
path: "/tasks",
component: () =>
import("./tasks-page.ts").then(() => ({
header: true,
render: () => html`<openclaw-tasks-page></openclaw-tasks-page>`,
})),
});

View file

@ -0,0 +1,191 @@
import { consume } from "@lit/context";
import { html, LitElement } from "lit";
import { state } from "lit/decorators.js";
import type { GatewayBrowserClient } from "../../api/gateway.ts";
import { subtitleForRoute, titleForRoute } from "../../app-navigation.ts";
import { applicationContext, type ApplicationContext } from "../../app/context.ts";
import { hasOperatorWriteAccess } from "../../app/operator-access.ts";
import { t } from "../../i18n/index.ts";
import { searchForSession } from "../../lib/sessions/index.ts";
import {
applyTaskEvent,
mergeTaskLists,
normalizeTasksCancelResult,
normalizeTasksListResult,
type TaskSummary,
} from "./data.ts";
import { renderTasks } from "./view.ts";
function formatTaskError(error: unknown, fallback: string): string {
if (error instanceof Error && error.message.trim()) {
return error.message.trim();
}
return typeof error === "string" && error.trim() ? error.trim() : fallback;
}
export class TasksPage extends LitElement {
override createRenderRoot() {
return this;
}
@consume({ context: applicationContext, subscribe: false })
private context!: ApplicationContext;
@state() private tasks: TaskSummary[] = [];
@state() private connected = false;
@state() private loading = false;
@state() private error: string | null = null;
@state() private cancellingTaskIds = new Set<string>();
private client: GatewayBrowserClient | null = null;
private loadGeneration = 0;
private stopGatewaySubscription?: () => void;
private stopGatewayEvents?: () => void;
override connectedCallback() {
super.connectedCallback();
this.syncGatewayState();
this.stopGatewaySubscription = this.context.gateway.subscribe(() => {
const wasConnected = this.connected;
const previousClient = this.client;
this.syncGatewayState();
if (this.connected && (this.client !== previousClient || !wasConnected)) {
void this.refreshTasks();
}
});
this.stopGatewayEvents = this.context.gateway.subscribeEvents((event) => {
if (event.event !== "task") {
return;
}
const result = applyTaskEvent(this.tasks, event.payload);
if (result.refetch) {
void this.refreshTasks();
return;
}
this.tasks = result.tasks;
});
if (this.connected) {
void this.refreshTasks();
}
}
override disconnectedCallback() {
this.loadGeneration += 1;
this.stopGatewaySubscription?.();
this.stopGatewaySubscription = undefined;
this.stopGatewayEvents?.();
this.stopGatewayEvents = undefined;
super.disconnectedCallback();
}
private syncGatewayState() {
const gateway = this.context.gateway.snapshot;
if (this.client !== gateway.client) {
this.loadGeneration += 1;
this.client = gateway.client;
this.tasks = [];
this.loading = false;
this.error = null;
this.cancellingTaskIds = new Set();
}
this.connected = gateway.connected;
}
private async refreshTasks() {
const client = this.client;
if (!this.connected || !client) {
return;
}
const generation = ++this.loadGeneration;
this.loading = true;
this.error = null;
try {
// Active tasks need their own query: the ledger pages newest-first, so a
// long-running task can hide behind newer terminal records on page one.
const [activePayload, recentPayload] = await Promise.all([
client.request("tasks.list", { status: ["queued", "running"], limit: 500 }),
client.request("tasks.list", { limit: 200 }),
]);
const active = normalizeTasksListResult(activePayload);
const recent = normalizeTasksListResult(recentPayload);
if (!active || !recent) {
throw new Error(t("tasksPage.invalidResponse"));
}
const tasks = mergeTaskLists(recent, active);
if (generation === this.loadGeneration && client === this.client) {
this.tasks = tasks;
}
} catch (error) {
if (generation === this.loadGeneration && client === this.client) {
this.error = formatTaskError(error, t("tasksPage.loadFailed"));
}
} finally {
if (generation === this.loadGeneration && client === this.client) {
this.loading = false;
}
}
}
private async cancelTask(taskId: string) {
const client = this.client;
if (!this.connected || !client || this.cancellingTaskIds.has(taskId)) {
return;
}
this.cancellingTaskIds = new Set([...this.cancellingTaskIds, taskId]);
this.error = null;
try {
const payload = await client.request("tasks.cancel", { taskId });
const result = normalizeTasksCancelResult(payload);
if (result?.task) {
this.tasks = applyTaskEvent(this.tasks, { action: "upserted", task: result.task }).tasks;
}
// Refusals (already terminal, stale id, no cancellation handle) are
// successful responses with cancelled=false; surface them like errors.
if (!result?.cancelled) {
this.error = result?.reason?.trim() || t("tasksPage.cancelFailed");
}
} catch (error) {
this.error = formatTaskError(error, t("tasksPage.cancelFailed"));
} finally {
const next = new Set(this.cancellingTaskIds);
next.delete(taskId);
this.cancellingTaskIds = next;
}
}
override render() {
return html`
<section class="content-header content-header--page">
<div>
<div class="page-title">${titleForRoute("tasks")}</div>
<div class="page-sub">${subtitleForRoute("tasks")}</div>
</div>
<button
class="btn"
type="button"
?disabled=${!this.connected || this.loading}
@click=${() => void this.refreshTasks()}
>
${this.loading ? t("common.refreshing") : t("common.refresh")}
</button>
</section>
${renderTasks({
basePath: this.context.basePath,
connected: this.connected,
// tasks.cancel needs operator.write; read-only operators get no button.
canCancel: hasOperatorWriteAccess(this.context.gateway.snapshot.hello?.auth ?? null),
loading: this.loading,
error: this.error,
tasks: this.tasks,
cancellingTaskIds: this.cancellingTaskIds,
onCancel: (taskId) => void this.cancelTask(taskId),
onNavigateToChat: (sessionKey) =>
this.context.navigate("chat", { search: searchForSession(sessionKey) }),
})}
`;
}
}
if (!customElements.get("openclaw-tasks-page")) {
customElements.define("openclaw-tasks-page", TasksPage);
}

View file

@ -0,0 +1,163 @@
import { copyFile, mkdir, rm } from "node:fs/promises";
import path from "node:path";
import { chromium, type Browser } from "playwright";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import {
canRunPlaywrightChromium,
installMockGateway,
resolvePlaywrightChromiumExecutablePath,
startControlUiE2eServer,
type ControlUiE2eServer,
} from "../../test-helpers/control-ui-e2e.ts";
const chromiumExecutablePath = resolvePlaywrightChromiumExecutablePath(chromium.executablePath());
const chromiumAvailable = canRunPlaywrightChromium(chromiumExecutablePath);
const allowMissingChromium = process.env.OPENCLAW_UI_E2E_ALLOW_MISSING_CHROMIUM === "1";
const describeControlUiE2e = chromiumAvailable || !allowMissingChromium ? describe : describe.skip;
const artifactDir = path.resolve(process.cwd(), ".artifacts/control-ui-e2e/tasks");
const baseTime = Date.parse("2026-07-05T18:00:00.000Z");
const runningTask = {
id: "task-running",
taskId: "task-running",
kind: "subagent",
runtime: "subagent",
status: "running",
title: "Review gateway changes",
agentId: "main",
childSessionKey: "agent:main:subagent:review",
createdAt: baseTime - 5_000,
updatedAt: baseTime,
progressSummary: "Reading subscription paths",
};
const queuedTask = {
id: "task-queued",
taskId: "task-queued",
kind: "cron",
runtime: "cron",
status: "queued",
title: "Nightly cleanup",
agentId: "main",
sessionKey: "agent:main:cron:cleanup",
createdAt: baseTime - 10_000,
updatedAt: baseTime - 1_000,
};
const completedTask = {
id: "task-completed",
taskId: "task-completed",
kind: "cli",
runtime: "cli",
status: "completed",
title: "Generate media index",
createdAt: baseTime - 30_000,
updatedAt: baseTime - 20_000,
terminalSummary: "Index generated",
};
const failedTask = {
id: "task-failed",
taskId: "task-failed",
kind: "acp",
runtime: "acp",
status: "failed",
title: "Run ACP worker",
createdAt: baseTime - 40_000,
updatedAt: baseTime - 30_000,
error: "Worker exited",
};
let server: ControlUiE2eServer;
let browser: Browser;
describeControlUiE2e("Control UI Tasks mocked Gateway E2E", () => {
beforeAll(async () => {
if (!chromiumAvailable) {
throw new Error(
`Playwright Chromium is not installed at ${chromiumExecutablePath}. Run \`pnpm --dir ui exec playwright install chromium\`, or set OPENCLAW_UI_E2E_ALLOW_MISSING_CHROMIUM=1 only when intentionally skipping this lane.`,
);
}
server = await startControlUiE2eServer();
browser = await chromium.launch({ executablePath: chromiumExecutablePath });
});
afterAll(async () => {
await browser?.close();
await server?.close();
});
it("renders task sections, applies pushed completion, and sends cancel", async () => {
await rm(artifactDir, { force: true, recursive: true });
await mkdir(artifactDir, { recursive: true });
const rawVideoDir = path.join(artifactDir, "raw-video");
await mkdir(rawVideoDir, { recursive: true });
const context = await browser.newContext({
locale: "en-US",
recordVideo: { dir: rawVideoDir, size: { width: 1440, height: 900 } },
serviceWorkers: "block",
viewport: { width: 1440, height: 900 },
});
const page = await context.newPage();
const video = page.video();
try {
const gateway = await installMockGateway(page, {
methodResponses: {
"tasks.list": {
tasks: [runningTask, queuedTask, completedTask, failedTask],
},
"tasks.cancel": {
found: true,
cancelled: true,
task: { ...queuedTask, status: "cancelled", updatedAt: baseTime + 2_000 },
},
},
});
const response = await page.goto(`${server.baseUrl}tasks`);
expect(response?.status()).toBe(200);
const active = page.locator('[data-task-section="active"]');
const recent = page.locator('[data-task-section="recent"]');
await active.locator('[data-task-id="task-running"]').waitFor({ state: "visible" });
await active.locator('[data-task-id="task-queued"]').waitFor({ state: "visible" });
await recent.locator('[data-task-id="task-completed"]').waitFor({ state: "visible" });
await recent.locator('[data-task-id="task-failed"]').waitFor({ state: "visible" });
expect(await active.textContent()).toContain("Reading subscription paths");
expect(await recent.textContent()).toContain("Worker exited");
await page.screenshot({
path: path.join(artifactDir, "01-task-sections.png"),
fullPage: true,
});
await gateway.emitGatewayEvent("task", {
action: "upserted",
task: {
...runningTask,
status: "completed",
updatedAt: baseTime + 1_000,
terminalSummary: "Review complete",
},
});
await recent.locator('[data-task-id="task-running"]').waitFor({ state: "visible" });
await active.locator('[data-task-id="task-running"]').waitFor({ state: "detached" });
expect(await recent.textContent()).toContain("Review complete");
await page.screenshot({
path: path.join(artifactDir, "02-pushed-completion.png"),
fullPage: true,
});
await active
.locator('[data-task-id="task-queued"]')
.getByRole("button", { name: "Cancel Nightly cleanup" })
.click();
const cancelRequest = await gateway.waitForRequest("tasks.cancel");
expect(cancelRequest.params).toEqual({ taskId: "task-queued" });
} finally {
await context.close();
if (video) {
await copyFile(await video.path(), path.join(artifactDir, "tasks-flow.webm"));
}
await rm(rawVideoDir, { force: true, recursive: true });
}
});
});

191
ui/src/pages/tasks/view.ts Normal file
View file

@ -0,0 +1,191 @@
import { html, nothing } from "lit";
import { repeat } from "lit/directives/repeat.js";
import { pathForRoute } from "../../app-route-paths.ts";
import { t } from "../../i18n/index.ts";
import { formatMs, formatRelativeTimestamp } from "../../lib/format.ts";
import { searchForSession } from "../../lib/sessions/index.ts";
import { partitionTasks, taskTimestampMs, type TaskStatus, type TaskSummary } from "./data.ts";
export type TasksProps = {
basePath: string;
connected: boolean;
canCancel: boolean;
loading: boolean;
error: string | null;
tasks: TaskSummary[];
cancellingTaskIds: ReadonlySet<string>;
onCancel: (taskId: string) => void;
onNavigateToChat: (sessionKey: string) => void;
};
const STATUS_LABEL_KEYS = {
queued: "tasksPage.status.queued",
running: "tasksPage.status.running",
completed: "tasksPage.status.completed",
failed: "tasksPage.status.failed",
cancelled: "tasksPage.status.cancelled",
timed_out: "tasksPage.status.timedOut",
} as const satisfies Record<TaskStatus, string>;
const STATUS_CHIP_CLASSES = {
queued: "chip-warn",
running: "chip-warn",
completed: "chip-ok",
failed: "chip-danger",
cancelled: "",
timed_out: "chip-danger",
} as const satisfies Record<TaskStatus, string>;
function statusLabel(status: TaskStatus): string {
return t(STATUS_LABEL_KEYS[status]);
}
function statusClass(status: TaskStatus): string {
return STATUS_CHIP_CLASSES[status];
}
function runtimeLabel(task: TaskSummary): string {
switch (task.runtime) {
case "subagent":
return t("tasksPage.runtime.subagent");
case "cron":
return t("tasksPage.runtime.cron");
case "acp":
return t("tasksPage.runtime.acp");
case "cli":
return t("tasksPage.runtime.cli");
default:
return t("tasksPage.runtime.unknown");
}
}
function taskTitle(task: TaskSummary): string {
return task.title ?? task.kind ?? (task.runtime ? runtimeLabel(task) : t("tasksPage.untitled"));
}
function taskDetail(task: TaskSummary): string | null {
if (task.status === "queued" || task.status === "running") {
return task.progressSummary ?? null;
}
if (task.status === "failed" || task.status === "timed_out") {
return task.error ?? task.terminalSummary ?? task.progressSummary ?? null;
}
return task.terminalSummary ?? task.error ?? task.progressSummary ?? null;
}
function renderSessionLink(task: TaskSummary, props: TasksProps) {
const sessionKey = task.childSessionKey ?? task.sessionKey;
if (!sessionKey) {
return nothing;
}
const href = `${pathForRoute("chat", props.basePath)}${searchForSession(sessionKey)}`;
return html`<a
class="session-link"
href=${href}
@click=${(event: MouseEvent) => {
if (
event.defaultPrevented ||
event.button !== 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey
) {
return;
}
event.preventDefault();
props.onNavigateToChat(sessionKey);
}}
>${t("tasksPage.openSession")}</a
>`;
}
function renderTask(task: TaskSummary, props: TasksProps) {
const active = task.status === "queued" || task.status === "running";
const timestamp = taskTimestampMs(task.updatedAt ?? task.createdAt);
const detail = taskDetail(task);
const title = taskTitle(task);
const cancelling = props.cancellingTaskIds.has(task.id);
return html`
<div class="list-item" data-task-id=${task.id}>
<div class="list-main">
<div class="list-title">${title}</div>
<div class="chip-row">
<span class="chip ${statusClass(task.status)}">${statusLabel(task.status)}</span>
<span class="chip">${runtimeLabel(task)}</span>
${task.agentId
? html`<span class="chip">${t("tasksPage.agent", { agent: task.agentId })}</span>`
: nothing}
</div>
${detail ? html`<div class="list-sub">${detail}</div>` : nothing}
</div>
<div class="list-meta">
${timestamp > 0
? html`<span title=${formatMs(timestamp)}>${formatRelativeTimestamp(timestamp)}</span>`
: html`<span>${t("common.na")}</span>`}
${renderSessionLink(task, props)}
${active && props.canCancel
? html`<button
class="btn"
type="button"
aria-label=${t("tasksPage.cancelTask", { title })}
?disabled=${cancelling || !props.connected}
@click=${() => props.onCancel(task.taskId)}
>
${cancelling ? t("tasksPage.cancelling") : t("common.cancel")}
</button>`
: nothing}
</div>
</div>
`;
}
function renderSection(
id: "active" | "recent",
title: string,
tasks: readonly TaskSummary[],
emptyText: string,
props: TasksProps,
) {
return html`
<section class="card stack" data-task-section=${id}>
<div>
<div class="card-title">${title}</div>
<div class="card-sub">
${tasks.length === 1
? t("tasksPage.taskCountOne")
: t("tasksPage.taskCount", { count: String(tasks.length) })}
</div>
</div>
${tasks.length === 0
? html`<div class="muted">${emptyText}</div>`
: html`<div class="list">
${repeat(
tasks,
(task) => task.id,
(task) => renderTask(task, props),
)}
</div>`}
</section>
`;
}
export function renderTasks(props: TasksProps) {
const { active, recent } = partitionTasks(props.tasks);
return html`
<div class="stack">
${!props.connected
? html`<div class="callout warn">${t("tasksPage.disconnected")}</div>`
: nothing}
${props.error ? html`<div class="callout danger">${props.error}</div>` : nothing}
${props.loading && props.tasks.length === 0
? html`<div class="card muted">${t("tasksPage.loading")}</div>`
: nothing}
${!props.loading && props.tasks.length === 0
? html`<div class="card muted">${t("tasksPage.empty")}</div>`
: nothing}
${renderSection("active", t("tasksPage.active"), active, t("tasksPage.emptyActive"), props)}
${renderSection("recent", t("tasksPage.recent"), recent, t("tasksPage.emptyRecent"), props)}
</div>
`;
}