From a5dcf3d30059149393c343d8dc16725a4d1bc86c Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Mon, 4 May 2026 02:12:16 -0500 Subject: [PATCH] fix(control-ui): filter archived sessions (#77132) Summary: - Use sessions.list as the Control UI source of truth for available sessions. - Hide archived sessions by default and keep the Sessions filter UI explicit, compact, and reversible. - Preserve session-change behavior, checkpoint details, generated i18n output, and chat/session picker consistency. Validation: - pnpm ui:i18n:check - pnpm test ui/src/styles/components.test.ts ui/src/ui/views/sessions.test.ts ui/src/ui/app-render.helpers.node.test.ts - pnpm test ui/src/ui/controllers/sessions.test.ts ui/src/ui/app-gateway.sessions.node.test.ts ui/src/ui/views/sessions.test.ts ui/src/styles/components.test.ts ui/src/ui/app-render.helpers.node.test.ts - pnpm tsgo:test:ui - Blacksmith Testbox: pnpm check:changed -- --- CHANGELOG.md | 6 + scripts/control-ui-i18n.ts | 30 +- src/agents/agent-command.ts | 3 +- src/commands/onboard-remote.test.ts | 8 +- ui/src/i18n/.i18n/ar.meta.json | 20 +- ui/src/i18n/.i18n/ar.tm.jsonl | 1 + ui/src/i18n/.i18n/de.meta.json | 20 +- ui/src/i18n/.i18n/de.tm.jsonl | 1 + ui/src/i18n/.i18n/es.meta.json | 20 +- ui/src/i18n/.i18n/es.tm.jsonl | 1 + ui/src/i18n/.i18n/fa.meta.json | 20 +- ui/src/i18n/.i18n/fa.tm.jsonl | 1 + ui/src/i18n/.i18n/fr.meta.json | 20 +- ui/src/i18n/.i18n/fr.tm.jsonl | 1 + ui/src/i18n/.i18n/id.meta.json | 20 +- ui/src/i18n/.i18n/id.tm.jsonl | 1 + ui/src/i18n/.i18n/it.meta.json | 20 +- ui/src/i18n/.i18n/it.tm.jsonl | 1 + ui/src/i18n/.i18n/ja-JP.meta.json | 20 +- ui/src/i18n/.i18n/ja-JP.tm.jsonl | 1 + ui/src/i18n/.i18n/ko.meta.json | 20 +- ui/src/i18n/.i18n/ko.tm.jsonl | 1 + ui/src/i18n/.i18n/nl.meta.json | 20 +- ui/src/i18n/.i18n/nl.tm.jsonl | 1 + ui/src/i18n/.i18n/pl.meta.json | 20 +- ui/src/i18n/.i18n/pl.tm.jsonl | 1 + ui/src/i18n/.i18n/pt-BR.meta.json | 20 +- ui/src/i18n/.i18n/pt-BR.tm.jsonl | 1 + ui/src/i18n/.i18n/raw-copy-baseline.json | 7 + ui/src/i18n/.i18n/th.meta.json | 20 +- ui/src/i18n/.i18n/th.tm.jsonl | 2 + ui/src/i18n/.i18n/tr.meta.json | 20 +- ui/src/i18n/.i18n/tr.tm.jsonl | 1 + ui/src/i18n/.i18n/uk.meta.json | 20 +- ui/src/i18n/.i18n/uk.tm.jsonl | 1 + ui/src/i18n/.i18n/vi.meta.json | 20 +- ui/src/i18n/.i18n/vi.tm.jsonl | 1 + ui/src/i18n/.i18n/zh-CN.meta.json | 20 +- ui/src/i18n/.i18n/zh-CN.tm.jsonl | 1 + ui/src/i18n/.i18n/zh-TW.meta.json | 20 +- ui/src/i18n/.i18n/zh-TW.tm.jsonl | 1 + ui/src/i18n/locales/ar.ts | 14 + ui/src/i18n/locales/de.ts | 14 + ui/src/i18n/locales/en.ts | 10 + ui/src/i18n/locales/es.ts | 14 + ui/src/i18n/locales/fa.ts | 14 + ui/src/i18n/locales/fr.ts | 14 + ui/src/i18n/locales/id.ts | 14 + ui/src/i18n/locales/it.ts | 14 + ui/src/i18n/locales/ja-JP.ts | 14 + ui/src/i18n/locales/ko.ts | 14 + ui/src/i18n/locales/nl.ts | 14 + ui/src/i18n/locales/pl.ts | 14 + ui/src/i18n/locales/pt-BR.ts | 14 + ui/src/i18n/locales/th.ts | 14 + ui/src/i18n/locales/tr.ts | 14 + ui/src/i18n/locales/uk.ts | 14 + ui/src/i18n/locales/vi.ts | 14 + ui/src/i18n/locales/zh-CN.ts | 14 + ui/src/i18n/locales/zh-TW.ts | 14 + ui/src/styles/chat/grouped.css | 8 +- ui/src/styles/components.css | 296 ++++++++++++++++++++ ui/src/styles/components.test.ts | 10 + ui/src/ui/app-defaults.test.ts | 11 + ui/src/ui/app-defaults.ts | 5 + ui/src/ui/app-gateway.sessions.node.test.ts | 27 +- ui/src/ui/app-gateway.ts | 4 +- ui/src/ui/app-render.helpers.node.test.ts | 9 +- ui/src/ui/app-render.helpers.ts | 2 + ui/src/ui/app-render.ts | 15 + ui/src/ui/app-view-state.ts | 2 + ui/src/ui/app.ts | 12 +- ui/src/ui/chat/session-controls.ts | 5 +- ui/src/ui/controllers/sessions.test.ts | 283 ++++++++++++++++++- ui/src/ui/controllers/sessions.ts | 68 ++++- ui/src/ui/types.ts | 1 + ui/src/ui/views/sessions.test.ts | 194 +++++++++++++ ui/src/ui/views/sessions.ts | 284 ++++++++++++++----- 78 files changed, 1737 insertions(+), 194 deletions(-) create mode 100644 ui/src/ui/app-defaults.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 324c5779f3f..7b21aa62853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -466,6 +466,12 @@ Docs: https://docs.openclaw.ai - Control UI: allow deployments to configure grouped chat message max-width with a validated `gateway.controlUi.chatMessageMaxWidth` setting instead of patching bundled CSS after upgrades. Fixes #67935. Thanks @xiew4589-lang. - Control UI/Cron: ignore malformed persisted cron rows without valid payloads before they enter UI state and guard stale cron render paths, preventing blank Control UI sections after a bad cron snapshot. Fixes #55047 and #54439; supersedes #54550 and #54552. - Control UI/sessions: bound the default Sessions tab query to recent activity and fewer rows, avoiding expensive full-history loads while keeping filters editable. Fixes #76050. (#76051) Thanks @Neomail2. +- Control UI/sessions: apply reliable `sessions.changed` snapshots in-place and refetch only for partial events, avoiding redundant `sessions.list` regeneration during active session updates. +- Control UI/sessions: explain the Sessions filter controls with hover tooltips and raise the default list limit to 200 rows. +- Control UI/sessions: expand compaction checkpoint details from checkpoint-bearing rows and keep token totals on one line. +- Control UI/sessions: group Active and Limit filters together, streamline source toggles, and make the filter section collapsible. +- Control UI/sessions: shorten filter tooltips and remove duplicate browser-native tooltip popovers. +- Control UI/sessions: keep the expanded filter controls on one row on large screens. - Gateway/channels: cap startup fanout at four channel/account handoffs and recover from Bonjour ciao self-probe races, reducing Windows startup stalls with many Telegram accounts. Fixes #75687. - Gateway/sessions: keep `sessions.list` polling responsive on large session stores by reusing list-safe session cache/indexes and returning a lightweight compaction checkpoint preview instead of heavyweight summaries. Thanks @rolandrscheel. - Control UI/Gateway: keep long-running dashboard WebSocket sessions alive with protocol pings and keep Stop available after reconnect or reload by recovering session-scoped active-run abort state. Fixes #70991. Thanks @alexandre-leng. diff --git a/scripts/control-ui-i18n.ts b/scripts/control-ui-i18n.ts index b2f26e3f0bf..34c0474b0d2 100644 --- a/scripts/control-ui-i18n.ts +++ b/scripts/control-ui-i18n.ts @@ -953,7 +953,35 @@ async function formatGeneratedTypeScript(filePath: string, source: string): Prom rejectOnFailure: true, }, ); - return result.stdout; + return restoreReplacementCorruptedStringLiterals(source, result.stdout); +} + +function restoreReplacementCorruptedStringLiterals(source: string, formatted: string): string { + if (!formatted.includes("\uFFFD") || source.includes("\uFFFD")) { + return formatted; + } + + const stringLiteralPattern = /"(?:\\.|[^"\\])*"/gu; + const sourceLiterals = [...source.matchAll(stringLiteralPattern)]; + const formattedLiterals = [...formatted.matchAll(stringLiteralPattern)]; + if (sourceLiterals.length !== formattedLiterals.length) { + return formatted; + } + + let output = ""; + let cursor = 0; + for (const [index, formattedLiteral] of formattedLiterals.entries()) { + const replacement = sourceLiterals[index]?.[0]; + const literal = formattedLiteral[0]; + const start = formattedLiteral.index; + if (replacement === undefined || start === undefined) { + return formatted; + } + output += formatted.slice(cursor, start); + output += literal.includes("\uFFFD") && !replacement.includes("\uFFFD") ? replacement : literal; + cursor = start + literal.length; + } + return `${output}${formatted.slice(cursor)}`; } type PendingPrompt = { diff --git a/src/agents/agent-command.ts b/src/agents/agent-command.ts index cc2767984ff..dfde018cd13 100644 --- a/src/agents/agent-command.ts +++ b/src/agents/agent-command.ts @@ -1023,8 +1023,7 @@ async function agentCommandInternal( allowTransientCooldownProbe: runOptions?.allowTransientCooldownProbe, sessionHasHistory: !isNewSession || (await attemptExecutionRuntime.sessionFileHasContent(sessionFile)), - suppressPromptPersistenceOnRetry: - isFallbackRetry && currentTurnUserMessagePersisted, + suppressPromptPersistenceOnRetry: isFallbackRetry && currentTurnUserMessagePersisted, onUserMessagePersisted: () => { currentTurnUserMessagePersisted = true; }, diff --git a/src/commands/onboard-remote.test.ts b/src/commands/onboard-remote.test.ts index 9827771fded..9f27beaab35 100644 --- a/src/commands/onboard-remote.test.ts +++ b/src/commands/onboard-remote.test.ts @@ -391,9 +391,7 @@ describe("promptRemoteGatewayConfig", () => { const next = await promptRemoteGatewayConfig(cfg, prompter); expect(next.gateway?.remote?.token).toBe("preexisting-remote-token"); - expect(text).not.toHaveBeenCalledWith( - expect.objectContaining({ message: "Gateway token" }), - ); + expect(text).not.toHaveBeenCalledWith(expect.objectContaining({ message: "Gateway token" })); }); it("keeps an existing remote gateway password when user confirms via masked-preview prompt", async () => { @@ -429,8 +427,6 @@ describe("promptRemoteGatewayConfig", () => { const next = await promptRemoteGatewayConfig(cfg, prompter); expect(next.gateway?.remote?.password).toBe("preexisting-remote-password"); - expect(text).not.toHaveBeenCalledWith( - expect.objectContaining({ message: "Gateway password" }), - ); + expect(text).not.toHaveBeenCalledWith(expect.objectContaining({ message: "Gateway password" })); }); }); diff --git a/ui/src/i18n/.i18n/ar.meta.json b/ui/src/i18n/.i18n/ar.meta.json index a948455fcb8..187be273191 100644 --- a/ui/src/i18n/.i18n/ar.meta.json +++ b/ui/src/i18n/.i18n/ar.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:38.725Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:58.631Z", "locale": "ar", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/ar.tm.jsonl b/ui/src/i18n/.i18n/ar.tm.jsonl index a331f8efe8e..d6f3da69b79 100644 --- a/ui/src/i18n/.i18n/ar.tm.jsonl +++ b/ui/src/i18n/.i18n/ar.tm.jsonl @@ -451,6 +451,7 @@ {"cache_key":"71fa5870018c723d0179b28b99583ceea6f765f50a78e9f2e368ba789ba33949","model":"gpt-5.5","provider":"openai","segment_id":"overview.palette.placeholder","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Type a command…","text_hash":"96489e83623d94011df336e2a4d1a62eaf2b14913aecb4845bb11e13d88733e7","tgt_lang":"ar","translated":"اكتب أمرًا…","updated_at":"2026-04-29T17:38:06.592Z"} {"cache_key":"72388d2a619110968d6f8a2f5afc4668f1ad99eb8eb679baa8476c31e67d1a86","model":"gpt-5.5","provider":"openai","segment_id":"dreaming.stats.grounded","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Grounded","text_hash":"5b6f73f04fe1a6af2dc43bebb45478862b0bd1fe079eed12f8bc2000a59bf68c","tgt_lang":"ar","translated":"مؤرض","updated_at":"2026-04-29T17:38:25.053Z"} {"cache_key":"723e0ef6630a94e28022dafbf14a431eba2e1bec2bad42011f383acd2e08ed5c","model":"gpt-5.5","provider":"openai","segment_id":"cron.quickCreate.namePlaceholder","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"e.g., Morning inbox check","text_hash":"149ef2da53b9dbcd4cb688e9d86fdb3780f50a88886ae841004fa3993ccd4e9f","tgt_lang":"ar","translated":"مثلاً، فحص البريد الوارد الصباحي","updated_at":"2026-04-29T20:14:54.627Z"} +{"cache_key":"724ff4c9622ee7b4744f5527c6c5492e9197779bdac4b681b11e270501a1decc","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"ar","translated":"عوامل التصفية","updated_at":"2026-04-29T17:38:45.816Z"} {"cache_key":"725383f534697e9ecbb3f625af9a3a29d9091a464f1b6b04105eaf650726b32c","model":"gpt-5.5","provider":"openai","segment_id":"cron.errors.invalidRunTime","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Invalid run time.","text_hash":"51465fa3cb94966411a49d8d1972fe997ac028fd249e05df55db8a2179975b48","tgt_lang":"ar","translated":"وقت التشغيل غير صالح.","updated_at":"2026-04-29T17:40:46.558Z"} {"cache_key":"73100cf996326ecfd714c375a917d94ef845d4fca16f760f840d8401a8ef54aa","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.loadingCheckpoints","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Loading checkpoints…","text_hash":"28f4a96c140d1effc48388a1f67e650dfcf892df7003d38cd0ebeab22d65ba34","tgt_lang":"ar","translated":"جارٍ تحميل نقاط التحقق…","updated_at":"2026-04-29T20:14:44.891Z"} {"cache_key":"73d44e33e51a34c6665fa8783255ff3311fff166eb58493b4429fddc054773b7","model":"gpt-5.5","provider":"openai","segment_id":"agents.context.openFilesTab","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Open Files tab","text_hash":"423a21a02bc6f7c21d6c85e30f0bc0827c497b6bc4123767375edd67f463c7bf","tgt_lang":"ar","translated":"فتح تبويب الملفات","updated_at":"2026-04-29T19:26:14.154Z"} diff --git a/ui/src/i18n/.i18n/de.meta.json b/ui/src/i18n/.i18n/de.meta.json index d98d12daebb..568cc256a97 100644 --- a/ui/src/i18n/.i18n/de.meta.json +++ b/ui/src/i18n/.i18n/de.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:22.358Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:56.445Z", "locale": "de", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/de.tm.jsonl b/ui/src/i18n/.i18n/de.tm.jsonl index e520575911c..d8a03d1e040 100644 --- a/ui/src/i18n/.i18n/de.tm.jsonl +++ b/ui/src/i18n/.i18n/de.tm.jsonl @@ -69,6 +69,7 @@ {"cache_key":"180bef4481fcfd7f7aaedc2c8b7727dbbc66a3bdbb23614986a21d614d8589bc","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.timezoneHelp","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Pick a common timezone or enter any valid IANA timezone.","text_hash":"a56f7de52b59658470a0ed6ae48112ff64e57b49b0e77e10d707d95b6822878b","tgt_lang":"de","translated":"Wähle eine gängige Zeitzone oder gib eine beliebige gültige IANA-Zeitzone ein.","updated_at":"2026-04-05T17:12:43.392Z"} {"cache_key":"181f6584bb9d14faec276e604018c371ebb2a4e5caf9c63bd442830b22ac9f31","model":"gpt-5.4","provider":"openai","segment_id":"common.hideAdvanced","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Hide Advanced","text_hash":"e6292a1e4e93ffea9b4e609d464a6c935bb10a8dafe6593795a9b43aed8ebcca","tgt_lang":"de","translated":"Erweitert ausblenden","updated_at":"2026-04-06T02:47:31.228Z"} {"cache_key":"182d7e4f797aff2943aed4f3accb1c8c454180d72d03b7317d657388214f18f8","model":"gpt-5.4","provider":"openai","segment_id":"overview.access.showToken","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Show token","text_hash":"2faef0ba40dc420f67de983b6c1be8f0f4b9b60f18409f2d2368b53b3c28a7bd","tgt_lang":"de","translated":"Token anzeigen","updated_at":"2026-04-20T06:26:17.877Z"} +{"cache_key":"18397a1b25aee54adad75650a359aed3cf472aa25e28fb5e3ea0c0c56420938b","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"de","translated":"Filter","updated_at":"2026-04-05T17:10:45.990Z"} {"cache_key":"183f83b1b860e812736b0dbea82a75738aaf9c9830630bda90d4d368544e7cbb","model":"gpt-5.4","provider":"openai","segment_id":"overview.stats.cron","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Cron","text_hash":"dd9d24965dbedc026915308732b77c1af68dcf52d3c0ca2421b1fdb0d197aca1","tgt_lang":"de","translated":"Cron","updated_at":"2026-04-06T02:59:27.518Z"} {"cache_key":"186e64f5c7705d37d6753c6235c2509a4e0f67fea0c9c04b9aaec80237f0f057","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.phrases.weavingShortTerm","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"weaving short-term into long-term…","text_hash":"1d64d672d34876489dc3885e05677abcae21d06bfa1d25ed87001721e441bd12","tgt_lang":"de","translated":"Kurzfristiges wird ins Langfristige eingewebt…","updated_at":"2026-04-06T02:48:28.029Z"} {"cache_key":"1967ab05b305f0272c9bdf6795a64b4edb4e63a3a6b9c9f08d96e8f58ce51376","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.modelHelp","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Start typing to pick a known model, or enter a custom one.","text_hash":"6ebac6c51e0da79d2ad76fe3d1395dff0c7a51ec7aa0d6b39ac38b0ba9fd8724","tgt_lang":"de","translated":"Beginne zu tippen, um ein bekanntes Modell auszuwählen, oder gib ein benutzerdefiniertes ein.","updated_at":"2026-04-05T17:12:53.251Z"} diff --git a/ui/src/i18n/.i18n/es.meta.json b/ui/src/i18n/.i18n/es.meta.json index e6082fa3a6a..8291d17b886 100644 --- a/ui/src/i18n/.i18n/es.meta.json +++ b/ui/src/i18n/.i18n/es.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:25.316Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:56.952Z", "locale": "es", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/es.tm.jsonl b/ui/src/i18n/.i18n/es.tm.jsonl index 2b162eb31af..1f52dc0ca33 100644 --- a/ui/src/i18n/.i18n/es.tm.jsonl +++ b/ui/src/i18n/.i18n/es.tm.jsonl @@ -549,6 +549,7 @@ {"cache_key":"ec05bc5196a50da0fb5e56605552e8aafe47454b47b090fa5ab25a70a1764aaa","model":"gpt-5.4","provider":"openai","segment_id":"nodes.binding.defaultBindingHint","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"Used when agents do not override a node binding.","text_hash":"a61df1a47c1edd595446e4954df0f8a0a3f84ee01ad399ef66c92cf03a75826d","tgt_lang":"es","translated":"Se usa cuando los agentes no reemplazan una vinculación de nodo.","updated_at":"2026-04-06T02:48:58.952Z"} {"cache_key":"ec22fce103367ebd0215aea5541c1a56f4ce3f5412de22396ae8f885745074ea","model":"gpt-5.5","provider":"openai","segment_id":"cron.quickCreate.title","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"New Automation","text_hash":"a3a41a0f86882e4c32217466e6386277710a4a39c703ad802bc9c7fb7363f776","tgt_lang":"es","translated":"Nueva automatización","updated_at":"2026-04-29T20:13:37.107Z"} {"cache_key":"ec771cf19645d1552acdaa429ea7298d97baa7e24b746c2f7f4440898c7869c5","model":"gpt-5.5","provider":"openai","segment_id":"cron.quickCreate.schedules.weekdays.label","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"Weekdays","text_hash":"6f4b602bb52984dc6244023f94410f3711649f7fe93b8de47b3b3a608312f49e","tgt_lang":"es","translated":"Días laborables","updated_at":"2026-04-29T20:13:33.610Z"} +{"cache_key":"edd0a477f8670af0902cff07bf114d01002a9baee19d53bf5c30cbc1a76ccc4d","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"es","translated":"Filtros","updated_at":"2026-04-05T17:12:08.307Z"} {"cache_key":"edd42acdbffcecc9324d051a7447bdb04a0b0d11ec7776f228d80b3f925ae3fa","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.label","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"Label","text_hash":"0e66373f45dcf3dd656151e519f7ee5e3d558d9c22cb87df339bbdd2b6c6a3c1","tgt_lang":"es","translated":"Etiqueta","updated_at":"2026-04-29T20:13:26.402Z"} {"cache_key":"ee4e2cc81c1938aac92acb4e4217750f3e62a60c0d741d0825e54290c82eaf3e","model":"gpt-5.4","provider":"openai","segment_id":"languages.th","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"ไทย (Thai)","text_hash":"0339954ca7e472c2f007782682a76629a864d63d3e419430bb5f6c72c4c1c88d","tgt_lang":"es","translated":"ไทย (tailandés)","updated_at":"2026-04-23T06:30:10.624Z"} {"cache_key":"eecad09f8c66054f482c32cb351ef8b4a76ce2e033243067ffeb728f3bcd7734","model":"gpt-5.4","provider":"openai","segment_id":"usage.sessions.avg","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"avg","text_hash":"ca5c8585b0760a760e0b887800360306b60288aa8581d4800ab42bc2c0d591a5","tgt_lang":"es","translated":"prom.","updated_at":"2026-04-05T17:12:30.161Z"} diff --git a/ui/src/i18n/.i18n/fa.meta.json b/ui/src/i18n/.i18n/fa.meta.json index 8e1fae798fb..c8aae9486da 100644 --- a/ui/src/i18n/.i18n/fa.meta.json +++ b/ui/src/i18n/.i18n/fa.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:29:08.558Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:57:02.119Z", "locale": "fa", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/fa.tm.jsonl b/ui/src/i18n/.i18n/fa.tm.jsonl index e3aed1b0eb6..00073c64dcb 100644 --- a/ui/src/i18n/.i18n/fa.tm.jsonl +++ b/ui/src/i18n/.i18n/fa.tm.jsonl @@ -240,6 +240,7 @@ {"cache_key":"404eca6904768d3467f8d6592e4895d61758269946bdfb7dee049829152f73b4","model":"gpt-5.5","provider":"openai","segment_id":"cron.quickCreate.promptPlaceholder","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"e.g., Check my inbox for urgent emails and summarize them...","text_hash":"f4675787351dcf3b421c7f187fc9e501f37cb0a5ca79ddcde938cf99efe2dac1","tgt_lang":"fa","translated":"مثلاً، صندوق ورودی من را برای ایمیل‌های فوری بررسی کن و آن‌ها را خلاصه کن...","updated_at":"2026-04-29T20:17:43.272Z"} {"cache_key":"407f619024266ae9f03ce10c0042977dce1cb99d21a4fb3e96da38c56f36d1f2","model":"gpt-5.5","provider":"openai","segment_id":"agentTools.builtIn","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"Built-in","text_hash":"1f43948106d1d47fef7b0afa5c60be05f7334dc4fe43a0b77d8716ef6ec22611","tgt_lang":"fa","translated":"داخلی","updated_at":"2026-04-29T17:41:37.156Z"} {"cache_key":"4094cac26f18845f2a9ba0cf36b9574366e3282fb79dde171dcf473dd79e1bdd","model":"gpt-5.5","provider":"openai","segment_id":"channels.nostr.usernameHelp","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"Short username (e.g., satoshi)","text_hash":"5e91f6b09039a459d4574c826d4280878ff019aeb382aa65e96c108472df0acf","tgt_lang":"fa","translated":"نام کاربری کوتاه (مثلاً satoshi)","updated_at":"2026-04-29T17:41:22.192Z"} +{"cache_key":"40ba8bb8eebeb6c92b40d69cfad90e6c4a76f81444f748b01a124f1f0768929b","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"fa","translated":"فیلترها","updated_at":"2026-04-29T17:43:25.173Z"} {"cache_key":"40c32a2a822f0dbde6074ab1ef0d738fd75319ab0396eb71533f289639e6009a","model":"gpt-5.5","provider":"openai","segment_id":"cron.form.editJob","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"Edit Job","text_hash":"c492f013040b1041820951af390ee398a4cd71c47fe66908410f6cfe2055d01e","tgt_lang":"fa","translated":"ویرایش کار","updated_at":"2026-04-29T17:44:55.845Z"} {"cache_key":"40ffdd69ae7f63f4bcff764734d2e7f6c4ddf6cc82fe73dbe0d71aaf979ea8ac","model":"gpt-5.5","provider":"openai","segment_id":"debug.security.critical","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"{count} critical","text_hash":"97e8a7b9fe4cf2aec17af2d2f9e452ed4adef3ec84899cba45ec4b6c5045e1ec","tgt_lang":"fa","translated":"{count} بحرانی","updated_at":"2026-04-29T19:29:02.592Z"} {"cache_key":"4131cd39227df0fecfb126ca48a8d817d511b788c2a70b5dc7a5467b1818cf1b","model":"gpt-5.5","provider":"openai","segment_id":"debug.security.warnings","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"{count} warnings","text_hash":"20c152eb8c81aba048645d91a49f254c1f8cb41ed6e6290ac1e6c3bf4a94b913","tgt_lang":"fa","translated":"{count} هشدار","updated_at":"2026-04-29T19:29:02.592Z"} diff --git a/ui/src/i18n/.i18n/fr.meta.json b/ui/src/i18n/.i18n/fr.meta.json index 18c32a757dd..c2097e579cd 100644 --- a/ui/src/i18n/.i18n/fr.meta.json +++ b/ui/src/i18n/.i18n/fr.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:35.244Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:58.233Z", "locale": "fr", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/fr.tm.jsonl b/ui/src/i18n/.i18n/fr.tm.jsonl index 9de42da819f..aad4d1f2802 100644 --- a/ui/src/i18n/.i18n/fr.tm.jsonl +++ b/ui/src/i18n/.i18n/fr.tm.jsonl @@ -555,6 +555,7 @@ {"cache_key":"a8795fe00724649ffd8ca87304895643d7ac3feed1fe665afd644d3e52144119","model":"gpt-5.4","provider":"openai","segment_id":"channels.nostr.displayName","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Display Name","text_hash":"18d67c992b71ce69eb924554dbace110236c7e2db06effceb3d690b8cd64a671","tgt_lang":"fr","translated":"Nom d’affichage","updated_at":"2026-04-06T02:49:46.449Z"} {"cache_key":"a888baa6b2270129490da862e611512c2d1eb15ca5e1d70fe45109dcf19bdd18","model":"gpt-5.4","provider":"openai","segment_id":"overview.snapshot.uptime","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Uptime","text_hash":"d63ab4711473b0398feb4b56622605d5d2ec7ecd3b1bb5070a7dd56de96aaf88","tgt_lang":"fr","translated":"Temps de fonctionnement","updated_at":"2026-04-05T17:13:59.772Z"} {"cache_key":"a8d9315c5ae546b9f2349ab8b24a1bfffc3bc1daa9053b436da242fe6a7a0a63","model":"gpt-5.4","provider":"openai","segment_id":"overview.access.togglePasswordVisibility","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Toggle password visibility","text_hash":"1016c07b0f58d365790cc799fb215afd92fde1aeb5ac47cd17260e327465b2d6","tgt_lang":"fr","translated":"Basculer la visibilité du mot de passe","updated_at":"2026-04-20T06:26:41.876Z"} +{"cache_key":"a915748ef5f8a7d8413e485610fc62bdc124f800a1fb5fbf97d74873b1cfd2a9","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"fr","translated":"Filtres","updated_at":"2026-04-05T17:14:09.807Z"} {"cache_key":"a9842d69515965223d3a93dec7e3e8cf887b9ede327390053fbfc7635d6a0863","model":"gpt-5.4","provider":"openai","segment_id":"agentTools.builtIn","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Built-in","text_hash":"1f43948106d1d47fef7b0afa5c60be05f7334dc4fe43a0b77d8716ef6ec22611","tgt_lang":"fr","translated":"Intégré","updated_at":"2026-04-06T02:49:52.510Z"} {"cache_key":"a9c7c238b27d69675285eceedb5f3c95a9870227f8b46fa56c21e2b87b4a121e","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.advanced.description","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"See what replayed from the daily log, what is waiting for promotion, and what already made it through.","text_hash":"db88d5beb64b2a10b51e81d01c279fa7a663905c2953c0615b48e5408393c311","tgt_lang":"fr","translated":"Voyez ce qui a été relu depuis le journal quotidien, ce qui attend une promotion et ce qui a déjà été validé.","updated_at":"2026-04-10T07:52:25.458Z"} {"cache_key":"aa64dd821827d39455444c72d04adfe4869a029057beb27b61c8d07eba2814e6","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.stats.grounded","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Grounded","text_hash":"5b6f73f04fe1a6af2dc43bebb45478862b0bd1fe079eed12f8bc2000a59bf68c","tgt_lang":"fr","translated":"Ancré","updated_at":"2026-04-08T22:27:49.211Z"} diff --git a/ui/src/i18n/.i18n/id.meta.json b/ui/src/i18n/.i18n/id.meta.json index a05624aba6e..79546ae24af 100644 --- a/ui/src/i18n/.i18n/id.meta.json +++ b/ui/src/i18n/.i18n/id.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:50.810Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:57:00.188Z", "locale": "id", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/id.tm.jsonl b/ui/src/i18n/.i18n/id.tm.jsonl index cafdec54d2d..ba1d043c301 100644 --- a/ui/src/i18n/.i18n/id.tm.jsonl +++ b/ui/src/i18n/.i18n/id.tm.jsonl @@ -623,6 +623,7 @@ {"cache_key":"bef75a6124889b9bc1766ab134dbe40f63758827237042ebb2d84e21793fae34","model":"gpt-5.4","provider":"openai","segment_id":"cron.runs.deliveryUnknown","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Unknown","text_hash":"b764cdc0eab7137467211272fa539f1260d1bf2e71bcf6ff3bdc960f5c16aa14","tgt_lang":"id","translated":"Tidak diketahui","updated_at":"2026-04-05T17:16:01.471Z"} {"cache_key":"bf440e878311009b3d32a300e0ccc8b766f5cece3c21ce3fe18ab90bc1557e35","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.manual","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"manual","text_hash":"36bde66f289a35683683b041c6d8f418a5f36607b547da25d00ad55891e80b88","tgt_lang":"id","translated":"manual","updated_at":"2026-04-29T20:16:03.576Z"} {"cache_key":"bf9f59cdf467d45d572604e15124bdcdea9734af5a916ce97071e041e24258ce","model":"gpt-5.4","provider":"openai","segment_id":"common.saving","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Saving…","text_hash":"23e39291d6135814ed7c936e278974544b0df5fbf0eb0427b6700979b7472a93","tgt_lang":"id","translated":"Menyimpan…","updated_at":"2026-04-06T02:50:40.390Z"} +{"cache_key":"bfd1c5c6f72ea91777e8c37639788c1144bdfe7495ca2aec798bce8a5dae1ee9","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"id","translated":"Filter","updated_at":"2026-04-05T17:15:25.362Z"} {"cache_key":"c0312d7af14a8cc2ffabcb46d9909f5fdda76be4eac7175c36a67bf4ab087025","model":"gpt-5.4","provider":"openai","segment_id":"usage.overview.errorsHint","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Total message and tool errors in range.","text_hash":"d99a4b10fb87bda650577c36cec57f531433cbee6046ebb8e614af9e2fffce28","tgt_lang":"id","translated":"Total kesalahan pesan dan alat dalam rentang.","updated_at":"2026-04-05T17:15:35.078Z"} {"cache_key":"c0b8bb54a1a3ed3e4ec20a71be07bdc6b4968cc07fe6e09940f0b214d65254da","model":"gpt-5.4","provider":"openai","segment_id":"cron.errors.scheduleAtInvalid","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Enter a valid date/time.","text_hash":"4878bf3e9a06845a2ac4fee29c4518ac244808363fc4fa23e04e929c6e4a0554","tgt_lang":"id","translated":"Masukkan tanggal/waktu yang valid.","updated_at":"2026-04-05T17:16:23.016Z"} {"cache_key":"c128d7134df55bcdab6c1237b0d934e3d269702e51220ebb626aa71bb80c02d4","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.advanced.promotedDescription","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Items that already made it through promotion.","text_hash":"e64d609511dff83e5fe8d8906292d4f253e9aebe1e2787391dc02d7ce8d7234a","tgt_lang":"id","translated":"Item yang sudah berhasil melewati promosi.","updated_at":"2026-04-10T07:59:45.707Z"} diff --git a/ui/src/i18n/.i18n/it.meta.json b/ui/src/i18n/.i18n/it.meta.json index 21887a263c5..ed5688fa2b4 100644 --- a/ui/src/i18n/.i18n/it.meta.json +++ b/ui/src/i18n/.i18n/it.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:41.755Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:59.016Z", "locale": "it", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/it.tm.jsonl b/ui/src/i18n/.i18n/it.tm.jsonl index ebf66949eef..cea4d2a6f1f 100644 --- a/ui/src/i18n/.i18n/it.tm.jsonl +++ b/ui/src/i18n/.i18n/it.tm.jsonl @@ -590,6 +590,7 @@ {"cache_key":"9d6278f6a149c3bb0bb31b55a71803ade478e7011ac54497c1c042446ba48bd3","model":"gpt-5.5","provider":"openai","segment_id":"cron.errors.systemTextRequired","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"System text is required.","text_hash":"7b13b35a0dabfa257fada59d07a81a0559c20e8a5049419e4969e2c538f110e5","tgt_lang":"it","translated":"Il testo di sistema è obbligatorio.","updated_at":"2026-04-29T17:39:49.212Z"} {"cache_key":"9d95011a47d3f9952be90336254662ccebac1f43ca0bada63718bb8508b8a196","model":"gpt-5.5","provider":"openai","segment_id":"cron.jobState.last","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"Last","text_hash":"eb970eb0951c6cdeac1ec0cc723fc91e30b0c26ee6f3b5ee0e574db7f487dc55","tgt_lang":"it","translated":"Ultimo","updated_at":"2026-04-29T17:39:49.212Z"} {"cache_key":"9dd2619d6dcf0cc3308deae5739648da70375005709d2be19bad7b1b552bd771","model":"gpt-5.5","provider":"openai","segment_id":"agents.files.editFile","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"Edit file","text_hash":"9608dd142d9b10d7799422fe96eab7262a5db3de6edbb1fde660d74ddaa2617b","tgt_lang":"it","translated":"Modifica file","updated_at":"2026-04-29T19:26:42.188Z"} +{"cache_key":"9e9dd8b49a714df25c8cc09f0afda1f3e382d824026c11a559ce145d12761847","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"it","translated":"Filtri","updated_at":"2026-04-29T17:38:27.250Z"} {"cache_key":"9ea9e3abf8ce9785ebf83e7570eacc98b05d9da920000f5c7c7ae6930d3a4d9f","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.selected","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"{count} selected","text_hash":"529aacfdfd2b17bf9fe56ebad9a24339a2d1151327dd420c52c5f163aeb9acc6","tgt_lang":"it","translated":"{count} selezionati","updated_at":"2026-04-29T20:14:41.034Z"} {"cache_key":"9ebd7991e429843da1af7cfbb82fba12693658840e5c14e5040f40ddc8d3a04b","model":"gpt-5.5","provider":"openai","segment_id":"usage.cacheStatus.status.refreshing","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"refreshing","text_hash":"0b61ac5d9426518ad7908a62037255c6881f9a5fa404ef3b99c24baa2111a174","tgt_lang":"it","translated":"aggiornamento in corso","updated_at":"2026-05-03T18:28:41.603Z"} {"cache_key":"9eeb15be964af258a78a5c23082db3fe3dd377c939f58f11915eb87237071806","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.loadingCheckpoints","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"Loading checkpoints…","text_hash":"28f4a96c140d1effc48388a1f67e650dfcf892df7003d38cd0ebeab22d65ba34","tgt_lang":"it","translated":"Caricamento checkpoint…","updated_at":"2026-04-29T20:14:47.968Z"} diff --git a/ui/src/i18n/.i18n/ja-JP.meta.json b/ui/src/i18n/.i18n/ja-JP.meta.json index 7ac7bb4027c..af5a459cf25 100644 --- a/ui/src/i18n/.i18n/ja-JP.meta.json +++ b/ui/src/i18n/.i18n/ja-JP.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:28.581Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:57.392Z", "locale": "ja-JP", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/ja-JP.tm.jsonl b/ui/src/i18n/.i18n/ja-JP.tm.jsonl index e09c50de7c5..6b32cda0102 100644 --- a/ui/src/i18n/.i18n/ja-JP.tm.jsonl +++ b/ui/src/i18n/.i18n/ja-JP.tm.jsonl @@ -277,6 +277,7 @@ {"cache_key":"4e049a994528b656da4c43f153fb597d557000f621960554fddbf517ba09ea17","model":"gpt-5.4","provider":"openai","segment_id":"usage.overview.avgSession","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"avg session","text_hash":"a8ce1dc2f9461f5c3cf015b40c54888e55840ac786b8f878465ff1c77348a6df","tgt_lang":"ja-JP","translated":"平均セッション","updated_at":"2026-04-05T17:13:20.304Z"} {"cache_key":"4ee726808917d4ab2a629ca344d974b3c2ca818a5b9e6eb00dcd16aa5f1baa85","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.phase.light","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"Light","text_hash":"dbcd5e7bb7a0f538810de44c3efbd813037ee3fa358747bb71fa58e157af45f7","tgt_lang":"ja-JP","translated":"浅い","updated_at":"2026-04-10T07:59:01.981Z"} {"cache_key":"4ffeb0530ed210a7e62ebc63d338fb7f4b47db4749e09bcce83056b71830c3f7","model":"gpt-5.4","provider":"openai","segment_id":"usage.details.noMessages","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"No messages","text_hash":"a06faf2668c28d0b26a3d89a7cb8751f4d952bc6f38ba9e0c202218269bdc659","tgt_lang":"ja-JP","translated":"メッセージがありません","updated_at":"2026-04-05T17:13:29.278Z"} +{"cache_key":"504c6359edd3abe9067c8efb09402b2008b6cb89126b1bfca199907baec6755e","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"ja-JP","translated":"フィルター","updated_at":"2026-04-05T17:13:06.557Z"} {"cache_key":"505f21a664756308c8fa44c0782c13542b9269dfc688cdbcab2e1af5be865ace","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.diary.newer","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"Newer","text_hash":"718c45696575a3aae41c3701a734767de3f3d1d7658c292804a6e3e90b1ce3a5","tgt_lang":"ja-JP","translated":"次へ","updated_at":"2026-04-06T02:49:20.465Z"} {"cache_key":"50b9b4718bf3e07611bf12a326e6bcb3739312ecb4a7d50600a31d134f4a4803","model":"gpt-5.4","provider":"openai","segment_id":"usage.filters.selectAll","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"Select All","text_hash":"d1ec69e64b9609d089aae09f7adc5c566d2cd222f8d8325f0ab3b523f0ac2690","tgt_lang":"ja-JP","translated":"すべて選択","updated_at":"2026-04-05T17:13:06.557Z"} {"cache_key":"50f51132263d992e77b6c205759f1f40ebdbee08930a94979098611211820b9d","model":"gpt-5.4","provider":"openai","segment_id":"usage.sessions.copyName","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"Copy session name","text_hash":"30a6a5c11915b5b6a99698ebe1cee13b7b84adcc45ccd0a827decce17ce45a2d","tgt_lang":"ja-JP","translated":"セッション名をコピー","updated_at":"2026-04-05T17:13:26.669Z"} diff --git a/ui/src/i18n/.i18n/ko.meta.json b/ui/src/i18n/.i18n/ko.meta.json index 877cc1edf0f..9dc771856b7 100644 --- a/ui/src/i18n/.i18n/ko.meta.json +++ b/ui/src/i18n/.i18n/ko.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:31.698Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:57.843Z", "locale": "ko", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/ko.tm.jsonl b/ui/src/i18n/.i18n/ko.tm.jsonl index 9aa1585a66d..a64592b22ef 100644 --- a/ui/src/i18n/.i18n/ko.tm.jsonl +++ b/ui/src/i18n/.i18n/ko.tm.jsonl @@ -651,6 +651,7 @@ {"cache_key":"c33423236669e4bf11c80adda81ae32cd36bce5c890ad3c759451ab99ff26963","model":"gpt-5.4","provider":"openai","segment_id":"usage.export.json","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"JSON","text_hash":"db1a21a0bc2ef8fbe13ac4cf044e8c9116d29137d5ed8b916ab63dcb2d4290df","tgt_lang":"ko","translated":"JSON","updated_at":"2026-04-06T02:59:47.203Z"} {"cache_key":"c33b9f4c41efa629f253dba4459ed3d60d23353ecdbd1a69b5cef0af7f82629f","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.nextHeartbeat","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"Next heartbeat","text_hash":"35e70a7ab8a0d3998180f789eecbec9bbcfe0520d436d8eb142ad6a8fbd55ec1","tgt_lang":"ko","translated":"다음 하트비트","updated_at":"2026-04-05T17:14:56.105Z"} {"cache_key":"c39a36a4dd2f76f335655974704e62edee9bee29156c044bfae69c7285ecad38","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.exactTiming","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"Exact timing (no stagger)","text_hash":"02c679552df9fa650dcbc6302ae5f8e954f0303b05cf5b5bddcadf40d6892849","tgt_lang":"ko","translated":"정확한 타이밍(스태거 없음)","updated_at":"2026-04-05T17:15:04.550Z"} +{"cache_key":"c3cfa6984c5322da0604d8682a42d0e38b87c09f6c7e2840c32c3736506267a8","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"ko","translated":"필터","updated_at":"2026-04-05T17:14:06.820Z"} {"cache_key":"c4021d1057371c8c447d74314118747655da5dd0db7e02a7d1cc316af9f4358c","model":"gpt-5.4","provider":"openai","segment_id":"usage.metrics.session","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"session","text_hash":"3f3af1ecebbd1410ab417ec0d27bbfcb5d340e177ae159b59fc8626c2dfd9175","tgt_lang":"ko","translated":"세션","updated_at":"2026-04-05T17:14:06.820Z"} {"cache_key":"c459f8514db55b3efbe5b2eb40f47d10349e04e745458b62425e34ab1e3b99b8","model":"gpt-5.4","provider":"openai","segment_id":"cron.errors.invalidRunTime","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"Invalid run time.","text_hash":"51465fa3cb94966411a49d8d1972fe997ac028fd249e05df55db8a2179975b48","tgt_lang":"ko","translated":"잘못된 실행 시간입니다.","updated_at":"2026-04-05T17:15:15.338Z"} {"cache_key":"c475af0ad025fa1fa926ddb2c0dfa7c246d22dba653f8b809d47e156db804fed","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.timeoutPlaceholder","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"Optional, e.g. 90","text_hash":"6df8499092f2542448e280448a6915fe0d1b5354749ad0170108e193bfd23583","tgt_lang":"ko","translated":"선택 사항, 예: 90","updated_at":"2026-04-05T17:15:00.492Z"} diff --git a/ui/src/i18n/.i18n/nl.meta.json b/ui/src/i18n/.i18n/nl.meta.json index c309ba39649..fb90c0c31e7 100644 --- a/ui/src/i18n/.i18n/nl.meta.json +++ b/ui/src/i18n/.i18n/nl.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:29:04.694Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:57:01.663Z", "locale": "nl", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/nl.tm.jsonl b/ui/src/i18n/.i18n/nl.tm.jsonl index 5510515153e..493f0cfb456 100644 --- a/ui/src/i18n/.i18n/nl.tm.jsonl +++ b/ui/src/i18n/.i18n/nl.tm.jsonl @@ -690,6 +690,7 @@ {"cache_key":"af416913453514b6c44e01158b501cd30718997d83cb976948a4bd9a0b88d9ef","model":"gpt-5.5","provider":"openai","segment_id":"cron.form.requiredSr","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"required","text_hash":"d0a3630555bbec7fc05a98d311c23b00fd1ab4d8296ac4a4125976d80b6a6959","tgt_lang":"nl","translated":"vereist","updated_at":"2026-04-29T17:41:38.443Z"} {"cache_key":"af430e4142abd05454225e4aea2127841b975530a67d897bfef2c1c00919fa69","model":"gpt-5.5","provider":"openai","segment_id":"usage.details.tokensWrittenToCache","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"Tokens written to cache","text_hash":"7abf026d6ca218c915b61286a73e94b7c71c6744b63702eab9bc41b4a3b20797","tgt_lang":"nl","translated":"Tokens geschreven naar cache","updated_at":"2026-04-29T17:41:14.532Z"} {"cache_key":"af7f75f9579a44c3c19e9b9398ef8a746de7efb1ae7677d346b3f3e6fd72c067","model":"gpt-5.5","provider":"openai","segment_id":"dreaming.scene.dedupeDiary","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"Dedupe Diary","text_hash":"805725ab08dda39943858e1ed241464dc23bc100fac04ce55d0f14a6009d06e4","tgt_lang":"nl","translated":"Dagboek dedupliceren","updated_at":"2026-04-29T17:40:35.784Z"} +{"cache_key":"af9905b0a45706ec44bf0d16f7c455a37358f6fba470640c58e6c80536aca885","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"nl","translated":"Filters","updated_at":"2026-04-29T17:40:52.013Z"} {"cache_key":"afa20cd1986e25c5177d869a026e8b2efb251e02168d2862b284006c68217921","model":"gpt-5.5","provider":"openai","segment_id":"dreaming.advanced.originMixed","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"mixed","text_hash":"3f8fee624f43b2a9d685353269a0ab3eac785863ab6227636db1060fba1855e0","tgt_lang":"nl","translated":"gemengd","updated_at":"2026-04-29T17:40:40.727Z"} {"cache_key":"afd6aeecdb51c44532950b67c8a1721992d88cf986b4630d3e9b2ae9c5384a76","model":"gpt-5.5","provider":"openai","segment_id":"tabs.logs","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"Logs","text_hash":"ea2100dc89ae9fe21fa9b08ab1bf18662dca1e53a3eebd7d03afebcaf5d57515","tgt_lang":"nl","translated":"Logs","updated_at":"2026-04-29T17:40:10.194Z"} {"cache_key":"aff9f88e1f6394bdf7341a4e88927035576638ff0a0f1872fa734fbcf87ad798","model":"gpt-5.5","provider":"openai","segment_id":"tabs.chat","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"Chat","text_hash":"460b3a7da007b7af9d35bca54181dc91382263b2bf133ca214871ca1fed1fc1c","tgt_lang":"nl","translated":"Chat","updated_at":"2026-04-29T17:40:06.931Z"} diff --git a/ui/src/i18n/.i18n/pl.meta.json b/ui/src/i18n/.i18n/pl.meta.json index 0fa0cac89fc..e106c4c9129 100644 --- a/ui/src/i18n/.i18n/pl.meta.json +++ b/ui/src/i18n/.i18n/pl.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:54.082Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:57:00.556Z", "locale": "pl", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/pl.tm.jsonl b/ui/src/i18n/.i18n/pl.tm.jsonl index 8700f119d9a..4b03943fcf3 100644 --- a/ui/src/i18n/.i18n/pl.tm.jsonl +++ b/ui/src/i18n/.i18n/pl.tm.jsonl @@ -397,6 +397,7 @@ {"cache_key":"7ca9147599a71da8a098c7b4ff118ae61db0f144420bb088abc4e2ab5eeb1450","model":"gpt-5.4","provider":"openai","segment_id":"overview.connection.step4","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"Or generate a reusable token:","text_hash":"e9512b115cf5e0471b6c45328a8c304ae1a1b5541c3bd9bd26f3c7d2dcbed14b","tgt_lang":"pl","translated":"Lub wygeneruj token wielokrotnego użytku:","updated_at":"2026-04-05T17:16:31.522Z"} {"cache_key":"7d3a2b0aedc672a542e7f5d746ae27d622568da1f0224933b2f5573bcbbb2a3f","model":"gpt-5.4","provider":"openai","segment_id":"agentTools.channelSource","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"Channel: {id}","text_hash":"deeba4ed0001ba82ab20e37ea762c26095e52817c28b99b94e2e5026f88fee6c","tgt_lang":"pl","translated":"Kanał: {id}","updated_at":"2026-04-06T02:51:20.539Z"} {"cache_key":"7ddf8c65fe4362bcf9829450151550ac2d6fdc65d0d09a3153e0cc1ec5fe76da","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.phrases.dreamingEmbeddings","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"dreaming in embeddings…","text_hash":"e17cd00c9abf4330434e5209a2fbb57d9ae277a90c390a0b42522fb836b54494","tgt_lang":"pl","translated":"śnienie w embeddingach…","updated_at":"2026-04-06T02:51:30.967Z"} +{"cache_key":"7e3436fe7dcc4c9d10f62040ccd87b13d7c304954576bf29eaf85fe2e78aa506","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"pl","translated":"Filtry","updated_at":"2026-04-05T17:16:37.480Z"} {"cache_key":"7e3ac9f1f828422379fdf099f79967d48713995db760b0311ed55f41f784108e","model":"gpt-5.4","provider":"openai","segment_id":"chat.hideCronSessions","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"Hide cron sessions","text_hash":"32ac86b13fa25acc4626f518ba49fe9c6307d7bb1b518e05c7eaf4b327a85840","tgt_lang":"pl","translated":"Ukryj sesje Cron","updated_at":"2026-04-05T17:17:09.206Z"} {"cache_key":"7eb2f5906c58c6b726ef25bab1d3167824a49d085fee784f01c3deada07f0f75","model":"gpt-5.4","provider":"openai","segment_id":"usage.sessions.copy","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"Copy","text_hash":"e21f935f11d7e966dbbae78da9daa378fe8142a14e7c0cd7434183005faa6c5c","tgt_lang":"pl","translated":"Kopiuj","updated_at":"2026-04-05T17:16:59.033Z"} {"cache_key":"7eb45b77171bc76de41acab6576e7d918a673d3da94e6690cb5d7dd322bffb62","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.tokensBefore","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"{count} tokens before","text_hash":"375c48d7ec146984195cb4f88984b9184fb243f05e738cf7bd3896fabfe66976","tgt_lang":"pl","translated":"{count} tokenów przed","updated_at":"2026-04-29T20:16:06.818Z"} diff --git a/ui/src/i18n/.i18n/pt-BR.meta.json b/ui/src/i18n/.i18n/pt-BR.meta.json index 9bf0942991c..96aa2d59faf 100644 --- a/ui/src/i18n/.i18n/pt-BR.meta.json +++ b/ui/src/i18n/.i18n/pt-BR.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:19.439Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:56.059Z", "locale": "pt-BR", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/pt-BR.tm.jsonl b/ui/src/i18n/.i18n/pt-BR.tm.jsonl index b9a7a09f244..070dd726726 100644 --- a/ui/src/i18n/.i18n/pt-BR.tm.jsonl +++ b/ui/src/i18n/.i18n/pt-BR.tm.jsonl @@ -399,6 +399,7 @@ {"cache_key":"935616f2c011ebb986c74a2fc87862634e19d41d6300fca871ca56a175cb7a13","model":"gpt-5.4","provider":"openai","segment_id":"usage.mosaic.sat","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Sat","text_hash":"fdeb71b569e0034d827041c354d2a609ee60b2d3ab71eb0e390faa70c10e36e1","tgt_lang":"pt-BR","translated":"Sáb","updated_at":"2026-04-05T17:11:28.481Z"} {"cache_key":"942c70889f3af35ebedb038fc4eb7a371e58bd99ea75613a7b0b4d5b2ef78b80","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.systemEvent","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Post message to main timeline","text_hash":"114ef03ed867cd1fabd71e0475822261a5baf3e84210260e8bed84ac005f0a3a","tgt_lang":"pt-BR","translated":"Publicar mensagem na linha do tempo principal","updated_at":"2026-04-05T17:11:52.042Z"} {"cache_key":"9441e66882e6d44d0519e0f37aba0e4a717fbc9d6ad464834e8dde613dc26d08","model":"gpt-5.4","provider":"openai","segment_id":"usage.filters.endDate","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"End date","text_hash":"14303aa0c4a08d390e1180d9ed4ecbad43d4c4176d82ea8b8ae3f4b648b07380","tgt_lang":"pt-BR","translated":"Data de término","updated_at":"2026-04-05T17:10:40.263Z"} +{"cache_key":"944820bd7b779650edc52744d74068450bc9eab3d9860cfb9fa56dff4f55fac1","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"pt-BR","translated":"Filtros","updated_at":"2026-04-05T17:10:40.263Z"} {"cache_key":"94550816e6ca1cc607ac224085a048be1be4288b4a387002dee3c31b638f82bc","model":"gpt-5.4","provider":"openai","segment_id":"cron.jobs.direction","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Direction","text_hash":"9c8a9579abe55bdc8a7b97031705e2738d912de38a35262863d8f47e05d3d641","tgt_lang":"pt-BR","translated":"Direção","updated_at":"2026-04-05T17:11:32.154Z"} {"cache_key":"9484a8e7dd30002d167b7606e82cf73aa5ce2eda0d2f741446eb21f5563e9f1f","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.cronOption","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Cron","text_hash":"dd9d24965dbedc026915308732b77c1af68dcf52d3c0ca2421b1fdb0d197aca1","tgt_lang":"pt-BR","translated":"Cron","updated_at":"2026-04-06T02:59:24.089Z"} {"cache_key":"94d32ff0dcc95e5dc6ea8a58e8d24dbfe90f079ba2b7e5f9604b8d9a495be61a","model":"gpt-5.4","provider":"openai","segment_id":"usage.overview.toolCalls","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Tool Calls","text_hash":"548ddc303bacce6b519d601219508cdbf5a27f81b466ccae5268286ae6c9fab9","tgt_lang":"pt-BR","translated":"Chamadas de ferramenta","updated_at":"2026-04-05T17:10:58.366Z"} diff --git a/ui/src/i18n/.i18n/raw-copy-baseline.json b/ui/src/i18n/.i18n/raw-copy-baseline.json index a82968c13ac..e819b8a71ca 100644 --- a/ui/src/i18n/.i18n/raw-copy-baseline.json +++ b/ui/src/i18n/.i18n/raw-copy-baseline.json @@ -4166,6 +4166,13 @@ "path": "ui/src/ui/views/overview.ts", "text": "openclaw doctor --generate-gateway-token" }, + { + "count": 1, + "kind": "html-attribute", + "name": "aria-label", + "path": "ui/src/ui/views/sessions.ts", + "text": "Session filters" + }, { "count": 1, "kind": "html-text", diff --git a/ui/src/i18n/.i18n/th.meta.json b/ui/src/i18n/.i18n/th.meta.json index be0022dded0..6b92f505c24 100644 --- a/ui/src/i18n/.i18n/th.meta.json +++ b/ui/src/i18n/.i18n/th.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:57.460Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:57:00.916Z", "locale": "th", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/th.tm.jsonl b/ui/src/i18n/.i18n/th.tm.jsonl index be4dfac764f..d37cf975bf8 100644 --- a/ui/src/i18n/.i18n/th.tm.jsonl +++ b/ui/src/i18n/.i18n/th.tm.jsonl @@ -175,6 +175,7 @@ {"cache_key":"3896f117ae78c4672515183a863fc9d72db1766358a503ba48d8abc877eee062","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.trace.emptySignals","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"No active signals.","text_hash":"0d9d086593baedf3d8af5a8f30c9bdb495209fdb3413e02f1e74c6f8ce77e876","tgt_lang":"th","translated":"ไม่มีสัญญาณที่ใช้งานอยู่","updated_at":"2026-04-23T06:27:13.277Z"} {"cache_key":"389a4cc41cd739a3a8ffdc7e42115917caf3aa70e42fac17fcee08dd6c4007cc","model":"gpt-5.4","provider":"openai","segment_id":"usage.filters.pin","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Pin","text_hash":"ff1cee74414621d812efa8f77a6024850158c209fba6158772088703c2a02ff9","tgt_lang":"th","translated":"ปักหมุด","updated_at":"2026-04-23T06:27:29.651Z"} {"cache_key":"390a25d8d33e52ff8a4c48188402249e740843eb50e362930242a7b41ae172d8","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.timeoutRetry","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"timeout retry","text_hash":"79d153651a03220f4efa053666d2102b238e62f65f0d5358891699656eb5a0d4","tgt_lang":"th","translated":"ลองใหม่เมื่อหมดเวลา","updated_at":"2026-04-29T20:16:17.116Z"} +{"cache_key":"398da2ff49ed8663b865024c57f60a706be89cd3789ecf90dfa223831e7e3454","model":"gpt-5.5","provider":"openai","segment_id":"cron.form.webhookHelp","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Send run summaries to a webhook endpoint.","text_hash":"cb5f366ea218ef2d0c803e1c814ed6cc24abd93701d5c5c87e9503869eb11070","tgt_lang":"th","translated":"ส่งสรุปผลการทำงานไปยังปลายทาง webhook","updated_at":"2026-05-04T06:00:00.000Z"} {"cache_key":"39a3998255817c48adfa4ade723397d3e9e0d8631135c6cd16a5d08c3dc4dafe","model":"gpt-5.4","provider":"openai","segment_id":"usage.daily.total","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Total","text_hash":"c9b3c38247f744e17dd26fda097d6a9ba9332586b6bdaa038bf8f313a863f2b8","tgt_lang":"th","translated":"รวม","updated_at":"2026-04-23T06:27:38.605Z"} {"cache_key":"39f8b3b0c8662478b0d414c27f010e55cc9142b41a210e64dcaa7fe29c6ac7c8","model":"gpt-5.4","provider":"openai","segment_id":"languages.ptBR","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Português (Brazilian Portuguese)","text_hash":"218d74650d53faa34f3263ebca533ed034422d1aec61d98ebd2ef353c0b9d492","tgt_lang":"th","translated":"Português (โปรตุเกสแบบบราซิล)","updated_at":"2026-04-23T06:28:14.367Z"} {"cache_key":"3a0210e94d14f26172b94e0d320019e09a831edd59eb95c2259b8965abc5d784","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.diary.waitingHint","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Narrative entries will appear after the next dreaming cycle.","text_hash":"c183c67ee0ad3800a518c6eac25bb58b19d4c9f944a961f2c1e371f581a465cd","tgt_lang":"th","translated":"รายการบันทึกเชิงบรรยายจะแสดงหลังจากรอบการฝันถัดไป","updated_at":"2026-04-23T06:27:13.277Z"} @@ -660,6 +661,7 @@ {"cache_key":"c26a4048ef7fddba423a40fc96a94dccb2646953c01cf5bddda426fc002a7008","model":"gpt-5.4","provider":"openai","segment_id":"overview.access.password","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Password (not stored)","text_hash":"a693085108fe8ddea3acb78ba8ac0c275e593fc85db1c526006247ceb1372dda","tgt_lang":"th","translated":"รหัสผ่าน (ไม่จัดเก็บ)","updated_at":"2026-04-23T06:26:22.932Z"} {"cache_key":"c2c3870589ba6d437dc17f13521dfee3e11c430212bee049bb4757c4e78a4837","model":"gpt-5.4","provider":"openai","segment_id":"usage.overview.toolResults","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"tool results","text_hash":"a5594e12dfffd8e54c36d9b99bc31c7d41f0389d2251790338f34e836a3211fe","tgt_lang":"th","translated":"ผลลัพธ์ของ tool","updated_at":"2026-04-23T06:27:44.114Z"} {"cache_key":"c32c330e4f39e19226b60165c7e211f5c539de9543b6d34a2e6fefec07750717","model":"gpt-5.4","provider":"openai","segment_id":"cron.jobList.edit","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Edit","text_hash":"464c4ffd019e1e9691dcf0537c797353ef2b1c1d4833d3d463e5b74ae4547344","tgt_lang":"th","translated":"แก้ไข","updated_at":"2026-04-23T06:28:51.244Z"} +{"cache_key":"c3a445b2b1b0a122417b66ff7add63b639705c4e771e246a69bec7acfaf06627","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"th","translated":"ตัวกรอง","updated_at":"2026-04-23T06:27:29.651Z"} {"cache_key":"c44f7b80a319548d19de739c1956f5449e5e5e8e87e0d2728f152c7c39057f14","model":"gpt-5.4","provider":"openai","segment_id":"nav.collapse","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Collapse sidebar","text_hash":"aab31cde23ba9783050a754575b80c05e0e799b1542990b24b4b4bde2327e37e","tgt_lang":"th","translated":"ย่อแถบด้านข้าง","updated_at":"2026-04-23T06:26:03.424Z"} {"cache_key":"c4a21b12587ab2444055336a10450c0c02d43073dfce82b77cc3ceebecfb0f6c","model":"gpt-5.5","provider":"openai","segment_id":"chat.openCommandPalette","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Open command palette","text_hash":"c022b19a38a632d9f0981df1407ed11743b7fd8a80b159b76a7cf78ad61a43b1","tgt_lang":"th","translated":"เปิดแถบคำสั่ง","updated_at":"2026-04-29T20:16:17.117Z"} {"cache_key":"c4b196fe79b21964fd255fb4916cb850f8c480d03e9de5ea6495e92e2eb5e828","model":"gpt-5.5","provider":"openai","segment_id":"lazyView.errorTitle","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Panel failed to load","text_hash":"f8c9d26f13962ea24220d44bb42badfec39d7f37b22dffdbb75a67c873cc044d","tgt_lang":"th","translated":"โหลดแผงไม่สำเร็จ","updated_at":"2026-04-27T12:13:28.547Z"} diff --git a/ui/src/i18n/.i18n/tr.meta.json b/ui/src/i18n/.i18n/tr.meta.json index 7cc0bd1a9cd..2262fcba87d 100644 --- a/ui/src/i18n/.i18n/tr.meta.json +++ b/ui/src/i18n/.i18n/tr.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:44.586Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:59.387Z", "locale": "tr", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/tr.tm.jsonl b/ui/src/i18n/.i18n/tr.tm.jsonl index c50eaba09ca..095afd6ca3c 100644 --- a/ui/src/i18n/.i18n/tr.tm.jsonl +++ b/ui/src/i18n/.i18n/tr.tm.jsonl @@ -250,6 +250,7 @@ {"cache_key":"4c63c479a4885ef3bc77cdf94fb219a23bb1bb0f769444ba6e539b3ce2a7aa61","model":"gpt-5.4","provider":"openai","segment_id":"languages.tr","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Türkçe (Turkish)","text_hash":"d7ba05ad20ad9e92b3f8b724f1c164bd0db7173a9f9fa9f961f5b588c413c0d4","tgt_lang":"tr","translated":"Türkçe (Türkçe)","updated_at":"2026-04-05T17:15:57.661Z"} {"cache_key":"4c9e54e204f210245167a9ea9054f76d3beaa8571ce329c614bbd284916685dd","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.phrases.simmeringIdeas","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"simmering half-formed ideas…","text_hash":"bb9432dfcd536797972bc477a1cc8e154d4b639552bdb67b9be0ee1517e6037b","tgt_lang":"tr","translated":"yarı şekillenmiş fikirler demleniyor…","updated_at":"2026-04-06T02:50:36.506Z"} {"cache_key":"4cc21ca774eb668391fe67ab660ab29b7dbd5fbe2efa1e6f78a3c4bdec42e0a8","model":"gpt-5.4","provider":"openai","segment_id":"usage.details.collapseAll","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Collapse All","text_hash":"55988e28a4e8720a588c5c53fd47616d929a404d3d2af7e6f8ba313dce6dc3e4","tgt_lang":"tr","translated":"Tümünü Daralt","updated_at":"2026-04-05T17:15:44.742Z"} +{"cache_key":"4cd6fd7b3041566a2df0f571e51c4b1a3a623e2c42c24300ebe0b4dab5f6b653","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"tr","translated":"Filtreler","updated_at":"2026-04-05T17:15:14.133Z"} {"cache_key":"4d58b946022f634546d176ca6f35ddb7b475999aa684085ae46f3a296bba354b","model":"gpt-5.4","provider":"openai","segment_id":"channels.generic.subtitle","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Channel status and configuration.","text_hash":"af598d2e3f8e7a9dcacdc23e2865c738ceced7ac9c98bb19ff0fde64e76d5be0","tgt_lang":"tr","translated":"Kanal durumu ve yapılandırması.","updated_at":"2026-04-06T02:50:07.323Z"} {"cache_key":"4d69b13b725892190573e55a9f0ec2afd4f6ca51f0af0c17fb959e584ff03e25","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.advanced.title","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Daily Log Replay","text_hash":"aafb35de5bb78185d5268c25978163b98291c650afcd56df7ab95ec773c3c988","tgt_lang":"tr","translated":"Günlük Kayıt Tekrarı","updated_at":"2026-04-10T07:52:37.528Z"} {"cache_key":"4ef3226510a1d1e465493c29b51264bb69f8252c52f90731ce5e9bac20f93ad3","model":"gpt-5.4","provider":"openai","segment_id":"chat.showCronSessions","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Show cron sessions","text_hash":"0cc0314eb8ffe4f1b14e774b3eec8f0433cc0ab073f396ca789d6ee35cb37385","tgt_lang":"tr","translated":"Cron oturumlarını göster","updated_at":"2026-04-05T17:15:52.927Z"} diff --git a/ui/src/i18n/.i18n/uk.meta.json b/ui/src/i18n/.i18n/uk.meta.json index ae619c0f0bc..1d32164ad94 100644 --- a/ui/src/i18n/.i18n/uk.meta.json +++ b/ui/src/i18n/.i18n/uk.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:47.748Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:59.815Z", "locale": "uk", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/uk.tm.jsonl b/ui/src/i18n/.i18n/uk.tm.jsonl index d719f9f8c4c..199d70569cf 100644 --- a/ui/src/i18n/.i18n/uk.tm.jsonl +++ b/ui/src/i18n/.i18n/uk.tm.jsonl @@ -98,6 +98,7 @@ {"cache_key":"1c4884f073580a600a2ee56a7033f53bc908c3f9b17156152b7ec3219532f8af","model":"gpt-5.4","provider":"openai","segment_id":"overview.connection.tailscaleDocsTitle","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"Tailscale Serve docs (opens in new tab)","text_hash":"2ba82f0f0c4820765bb47b714ebdba4b491e2729f2b415496411900a702d4fdb","tgt_lang":"uk","translated":"Документація щодо Tailscale Serve (відкривається в новій вкладці)","updated_at":"2026-04-20T06:26:53.139Z"} {"cache_key":"1c941be301f363ff9dcef04704d6a6486d2a105c96abe75372fd2e086d073e48","model":"gpt-5.4","provider":"openai","segment_id":"cron.runs.allDelivery","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"All delivery","text_hash":"41ae1c2395e52fa33ba7df91afec0e316cd9e36a74a39b87a825f65a7dce707b","tgt_lang":"uk","translated":"Уся доставка","updated_at":"2026-04-05T17:23:29.777Z"} {"cache_key":"1cccc505aa43a900279be9c21babef4eed902580c54613950a634d543a328c1b","model":"gpt-5.4","provider":"openai","segment_id":"common.probeOk","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"Probe ok","text_hash":"c3d8dac3db6b4f2768483a199b2c0784645995f63459d91e8d0bddee2f6993c7","tgt_lang":"uk","translated":"Перевірку пройдено","updated_at":"2026-04-06T02:50:29.304Z"} +{"cache_key":"1cf7a6c657246ab71e39911b627e4794d74f2f6a5fd0d96120e99194c63f69a1","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"uk","translated":"Фільтри","updated_at":"2026-04-05T17:22:39.086Z"} {"cache_key":"1d21fb4e720fdf0aa7968a0a2064e3aa82c1d87de5307b0491231305b6bff293","model":"gpt-5.4","provider":"openai","segment_id":"dreaming.trace.shortTerm","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"Short-term","text_hash":"5bb852d4225d676aa64e8933284475ce54fd35d9535b4f5b4b37c42245112df0","tgt_lang":"uk","translated":"Короткострокові","updated_at":"2026-04-08T18:39:02.532Z"} {"cache_key":"1d6a807ba1fe5f29292c1e1138ea743313e908cdcf2d4f768099e626257ae3d4","model":"gpt-5.4","provider":"openai","segment_id":"overview.cards.modelAuthAttentionExpiredDesc","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"{providers} — re-authenticate with openclaw models auth","text_hash":"e883d59961999e0948fec3d4acbf11dc7c55a16beb27d313a0ce6cceb7ad2cb8","tgt_lang":"uk","translated":"{providers} — повторно виконайте авторизацію через openclaw models auth","updated_at":"2026-04-15T05:45:16.519Z"} {"cache_key":"1d926c6ca1ca8542c8ccbd82545ba6a7f1e0e14f35417b613a89c4c486272d3c","model":"gpt-5.4","provider":"openai","segment_id":"cron.jobList.disable","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"Disable","text_hash":"b7e3e4aa4257b9a11a82f59faf34c8450ca10d4116885b0a29fedf60842d81d5","tgt_lang":"uk","translated":"Вимкнути","updated_at":"2026-04-05T17:23:50.170Z"} diff --git a/ui/src/i18n/.i18n/vi.meta.json b/ui/src/i18n/.i18n/vi.meta.json index d318d39785e..43a147f35ee 100644 --- a/ui/src/i18n/.i18n/vi.meta.json +++ b/ui/src/i18n/.i18n/vi.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:29:00.531Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:57:01.264Z", "locale": "vi", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/vi.tm.jsonl b/ui/src/i18n/.i18n/vi.tm.jsonl index 0ea7e4920a9..7b89311ca9f 100644 --- a/ui/src/i18n/.i18n/vi.tm.jsonl +++ b/ui/src/i18n/.i18n/vi.tm.jsonl @@ -200,6 +200,7 @@ {"cache_key":"338e95308eade034cfdd794e21694ade0cf0333ccfdd28ae3269ccd1065fd708","model":"gpt-5.5","provider":"openai","segment_id":"cron.quickCreate.schedules.everyEvening.description","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Daily at 6:00 PM","text_hash":"260b9ed9fe7fac30932e89488f121facfb1cd04b8ecda5ca2ece9a207ad6662a","tgt_lang":"vi","translated":"Hằng ngày lúc 6:00 CH","updated_at":"2026-04-29T20:16:47.550Z"} {"cache_key":"339644c0fa5c48da3c660f5fe04ff24adb36d70238e191220037cc256d7957a9","model":"gpt-5.5","provider":"openai","segment_id":"dreaming.phrases.consolidatingMemories","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"consolidating memories…","text_hash":"89baaaae1f0e1ad3d02d40be2987273190f86bf34e8a27dd35c8e7faa76e2841","tgt_lang":"vi","translated":"đang hợp nhất ký ức…","updated_at":"2026-04-29T17:40:41.874Z"} {"cache_key":"33a2e759514cde43ae2fac511a1b6b9f8950815ab7a2ea14f35134817ab95755","model":"gpt-5.5","provider":"openai","segment_id":"login.toggleTokenVisibility","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Toggle token visibility","text_hash":"81fc4b962be0e4a4748879f1645272c8f2302e101c59544f1fac347b3f892f26","tgt_lang":"vi","translated":"Bật/tắt hiển thị token","updated_at":"2026-04-29T17:41:23.093Z"} +{"cache_key":"34203a416c3659071700de15490dfc95fc7eeb44d583ca410b044f46ee46b5f9","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"vi","translated":"Bộ lọc","updated_at":"2026-04-29T17:40:44.671Z"} {"cache_key":"345cbb35705a925d8869f7e21af9f90364253bae766d59a44eaa42be9de03e7a","model":"gpt-5.5","provider":"openai","segment_id":"languages.es","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Español (Spanish)","text_hash":"b785e11e822c061a3a5368c55fbeb3f436766ef1e9b3448a605083d0b06ecddb","tgt_lang":"vi","translated":"Español (Tiếng Tây Ban Nha)","updated_at":"2026-04-29T17:41:29.250Z"} {"cache_key":"3485543d6cc5f1756ffcd246aca399c782b456447d7f070cb47472f3c9df9164","model":"gpt-5.5","provider":"openai","segment_id":"agents.files.liveDraftPreview","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Live Draft Preview","text_hash":"eb6b2fefeacd2aac68f7ea96e616e8ba9eefd3d7c74a0e100bdcafe2d515052f","tgt_lang":"vi","translated":"Bản xem trước bản nháp trực tiếp","updated_at":"2026-04-29T19:28:16.225Z"} {"cache_key":"349bd72e0e1c90712d49edf439cfb885b7c4a961c36217a7f4fe8c8c94d09e07","model":"gpt-5.5","provider":"openai","segment_id":"agents.files.updated","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Updated {time}","text_hash":"2f87419441e6111b4d62893d3c4ef5ddeb2c8e1af82fabab6132856faf77f907","tgt_lang":"vi","translated":"Đã cập nhật {time}","updated_at":"2026-04-29T19:28:16.225Z"} diff --git a/ui/src/i18n/.i18n/zh-CN.meta.json b/ui/src/i18n/.i18n/zh-CN.meta.json index 5ad7be1ba74..a3dbfcc980d 100644 --- a/ui/src/i18n/.i18n/zh-CN.meta.json +++ b/ui/src/i18n/.i18n/zh-CN.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:13.442Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:55.059Z", "locale": "zh-CN", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/zh-CN.tm.jsonl b/ui/src/i18n/.i18n/zh-CN.tm.jsonl index bd3961c6a36..25fb038a9ff 100644 --- a/ui/src/i18n/.i18n/zh-CN.tm.jsonl +++ b/ui/src/i18n/.i18n/zh-CN.tm.jsonl @@ -153,6 +153,7 @@ {"cache_key":"416f556b264c42fccba8bab1e8877917155eb16207a0c60dab0a045873f9010c","model":"gpt-5.5","provider":"openai","segment_id":"lazyView.retry","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Retry","text_hash":"942087cc2d41e01304b7195558d093d10c72af8e838c7556d6a02d471ee71852","tgt_lang":"zh-CN","translated":"重试","updated_at":"2026-04-27T12:10:49.684Z"} {"cache_key":"41c33a4091090aafbe2f6303f026cf661ff8c8c4d6bce6c2c04fca72763a3291","model":"gpt-5.4","provider":"openai","segment_id":"usage.details.tool","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Tool","text_hash":"2e53bdcd0740867b597599e733c04a994f55fb17c89a61595183a001742e5705","tgt_lang":"zh-CN","translated":"工具","updated_at":"2026-04-05T17:11:02.649Z"} {"cache_key":"4297c339c27b767ffb25c04b0402467d032c04e4272e369c33dad1b2fad9462d","model":"gpt-5.4","provider":"openai","segment_id":"common.lastStart","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Last start","text_hash":"37a1eec0a7895251539d960c0ee5951c83da27223bdf5223c8440a4a48e061ef","tgt_lang":"zh-CN","translated":"上次启动","updated_at":"2026-04-06T02:47:28.112Z"} +{"cache_key":"42a2f0a683536f3df65516b4b660a06bfa4c1abbdf46a00f1d0cfed59261d7ed","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"zh-CN","translated":"筛选","updated_at":"2026-04-05T17:10:36.565Z"} {"cache_key":"43e22bbbc8ae9553cb8c30edcc49eed0b287b681446a2ba39e5f6a6da386f268","model":"gpt-5.4","provider":"openai","segment_id":"usage.mosaic.sun","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Sun","text_hash":"db18f17fe532007616d0d0fcc303281c35aafc940b13e6af55e63f8fed304718","tgt_lang":"zh-CN","translated":"周日","updated_at":"2026-04-05T17:11:05.447Z"} {"cache_key":"440f26448eacf4104c8e58d2fc5ad9ea4c20591f2a35e7f6052aafa8734a0099","model":"gpt-5.4","provider":"openai","segment_id":"cron.jobs.schedule","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Schedule","text_hash":"f4830a1dae2980447c716bd4b5779b7013575ef09f70ef4731457218792487b3","tgt_lang":"zh-CN","translated":"计划","updated_at":"2026-04-05T17:11:07.427Z"} {"cache_key":"441e3efbef12124ea08db6950e26ab0ea5ce908493e8509ff849988de1e4d9ac","model":"gpt-5.4","provider":"openai","segment_id":"channels.nostr.bannerHelp","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"HTTPS URL to a banner image","text_hash":"5feb792028cf20b11294d2bed052e34770970d0a8a991fdc8eeb39045a9c42ca","tgt_lang":"zh-CN","translated":"横幅图片的 HTTPS URL","updated_at":"2026-04-06T02:47:39.053Z"} diff --git a/ui/src/i18n/.i18n/zh-TW.meta.json b/ui/src/i18n/.i18n/zh-TW.meta.json index 423a90f7f5d..1dcdd9c88ee 100644 --- a/ui/src/i18n/.i18n/zh-TW.meta.json +++ b/ui/src/i18n/.i18n/zh-TW.meta.json @@ -1,11 +1,21 @@ { - "fallbackKeys": [], - "generatedAt": "2026-05-03T18:28:16.548Z", + "fallbackKeys": [ + "sessionsView.activeTooltip", + "sessionsView.globalTooltip", + "sessionsView.hideFilters", + "sessionsView.limitTooltip", + "sessionsView.showArchived", + "sessionsView.showArchivedTooltip", + "sessionsView.showFilters", + "sessionsView.sourceFilters", + "sessionsView.unknownTooltip" + ], + "generatedAt": "2026-05-04T05:56:55.670Z", "locale": "zh-TW", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "d994d064e7880cb40c055c5201db0b9045c15f6abf37fac9b317e25e82b6b7b6", - "totalKeys": 991, - "translatedKeys": 991, + "sourceHash": "66b99bdd39fc9dae0476c0e2fd2c0392f068bc3f7f91b532b04a106f14a3f3df", + "totalKeys": 1001, + "translatedKeys": 992, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/zh-TW.tm.jsonl b/ui/src/i18n/.i18n/zh-TW.tm.jsonl index ff23be65775..5e578a66a9f 100644 --- a/ui/src/i18n/.i18n/zh-TW.tm.jsonl +++ b/ui/src/i18n/.i18n/zh-TW.tm.jsonl @@ -524,6 +524,7 @@ {"cache_key":"b269cb408b9d173f35cea21df10a8df5141c31a54bd05a857aae267a431f892f","model":"gpt-5.4","provider":"openai","segment_id":"usage.filters.sessionsCount","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-04-05T17:10:38.462Z"} {"cache_key":"b27fdf8fe6eda22093c13a6518f4bccb13f1a2ecfbdcd979b22db0c46a227a11","model":"gpt-5.5","provider":"openai","segment_id":"cron.quickCreate.whatHint","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Describe the task in natural language. The agent will run this prompt each time.","text_hash":"740434f6a8f3a54a5bbe7362b393c2d4c4a25789d52c76dddb57c96c25432f0e","tgt_lang":"zh-TW","translated":"以自然語言描述任務。代理程式每次都會執行此提示。","updated_at":"2026-04-29T20:12:36.404Z"} {"cache_key":"b3810becb628fd4a8565e19b7dfbfb4dc1ba31f48692f496a7ffbf0e5ee6a120","model":"gpt-5.4","provider":"openai","segment_id":"cron.runs.selectJobHint","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Select a job to inspect run history.","text_hash":"cd1410f81b92c15d46b317f73d250066fbcaf4dc1f9e1978309f36ab21f17135","tgt_lang":"zh-TW","translated":"選擇一個工作以檢視執行記錄。","updated_at":"2026-04-05T17:11:25.473Z"} +{"cache_key":"b3d605bc167ceb0536d65bec566f2397c44a2faf8e77ae98e248f4ef9cd1276e","model":"gpt-5.4","provider":"openai","segment_id":"sessionsView.filters","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Filters","text_hash":"546ebb8eb993ea561029d9febd84c363bdb09010bb2cb915a8287762b76b9a64","tgt_lang":"zh-TW","translated":"篩選條件","updated_at":"2026-04-05T17:10:31.705Z"} {"cache_key":"b44759978cb549a8f29d8066804c61830059f0b604049eba195f1d4334d26092","model":"gpt-5.4","provider":"openai","segment_id":"cron.form.timezonePlaceholder","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"America/Los_Angeles","text_hash":"2d4bbedff807854084b7855fd6e0d49ab55b41e8c9395debd40d0e8e1d3390cf","tgt_lang":"zh-TW","translated":"America/Los_Angeles","updated_at":"2026-04-06T02:59:17.485Z"} {"cache_key":"b4cb2889b6ce8d4b3933053db7ac9586bfa640f87e40933783e388ed8e9d9c26","model":"gpt-5.4","provider":"openai","segment_id":"usage.metrics.tokens","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Tokens","text_hash":"a039dfb9628b53ddaebcfe8ef0793e3fdf19867601295f00d192acef59050869","tgt_lang":"zh-TW","translated":"Token","updated_at":"2026-04-05T17:10:31.705Z"} {"cache_key":"b5a677e7111689d2cac8ff845e690ca219e4fc96480dab57613facb3dc16376a","model":"gpt-5.5","provider":"openai","segment_id":"cron.quickCreate.delivery.silent.label","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Silent","text_hash":"ddbcf06726488a43af36838754808ac5041b05ab6434735615979d820725b56f","tgt_lang":"zh-TW","translated":"靜默","updated_at":"2026-04-29T20:12:31.105Z"} diff --git a/ui/src/i18n/locales/ar.ts b/ui/src/i18n/locales/ar.ts index 7065cd42cb3..c9f43df3430 100644 --- a/ui/src/i18n/locales/ar.ts +++ b/ui/src/i18n/locales/ar.ts @@ -159,8 +159,22 @@ export const ar: TranslationMap = { store: "المخزن: {path}", active: "نشط", limit: "الحد", + filters: "عوامل التصفية", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "عام", unknown: "غير معروف", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "دقيقة", searchPlaceholder: "تصفية حسب المفتاح أو الوكيل أو التسمية أو النوع…", selected: "تم تحديد {count}", diff --git a/ui/src/i18n/locales/de.ts b/ui/src/i18n/locales/de.ts index 97505ccf800..329a27bf4a7 100644 --- a/ui/src/i18n/locales/de.ts +++ b/ui/src/i18n/locales/de.ts @@ -163,8 +163,22 @@ export const de: TranslationMap = { store: "Speicher: {path}", active: "Aktiv", limit: "Limit", + filters: "Filter", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Global", unknown: "Unbekannt", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "Min.", searchPlaceholder: "Nach Schlüssel, Agent, Label, Art filtern…", selected: "{count} ausgewählt", diff --git a/ui/src/i18n/locales/en.ts b/ui/src/i18n/locales/en.ts index ee345619359..8d82b6c1b46 100644 --- a/ui/src/i18n/locales/en.ts +++ b/ui/src/i18n/locales/en.ts @@ -158,8 +158,18 @@ export const en: TranslationMap = { store: "Store: {path}", active: "Active", limit: "Limit", + filters: "Filters", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Global", unknown: "Unknown", + showArchived: "Show archived", + activeTooltip: "Updated in the last N minutes.", + limitTooltip: "Max sessions to load.", + globalTooltip: "Include global sessions.", + unknownTooltip: "Include unknown sessions.", + showArchivedTooltip: "Include archived sessions.", minutesPlaceholder: "min", searchPlaceholder: "Filter by key, agent, label, kind…", selected: "{count} selected", diff --git a/ui/src/i18n/locales/es.ts b/ui/src/i18n/locales/es.ts index 981c1ae84b3..40d4bdc976d 100644 --- a/ui/src/i18n/locales/es.ts +++ b/ui/src/i18n/locales/es.ts @@ -160,8 +160,22 @@ export const es: TranslationMap = { store: "Almacén: {path}", active: "Activo", limit: "Límite", + filters: "Filtros", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Global", unknown: "Desconocido", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "min", searchPlaceholder: "Filtrar por clave, agente, etiqueta, tipo…", selected: "{count} seleccionados", diff --git a/ui/src/i18n/locales/fa.ts b/ui/src/i18n/locales/fa.ts index 8af35bc3359..4b3e02434b4 100644 --- a/ui/src/i18n/locales/fa.ts +++ b/ui/src/i18n/locales/fa.ts @@ -161,8 +161,22 @@ export const fa: TranslationMap = { store: "ذخیره‌گاه: {path}", active: "فعال", limit: "حد", + filters: "فیلترها", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "سراسری", unknown: "نامشخص", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "دقیقه", searchPlaceholder: "فیلتر بر اساس کلید، عامل، برچسب، نوع…", selected: "{count} انتخاب‌شده", diff --git a/ui/src/i18n/locales/fr.ts b/ui/src/i18n/locales/fr.ts index 0c0dd15c336..714f0e7322c 100644 --- a/ui/src/i18n/locales/fr.ts +++ b/ui/src/i18n/locales/fr.ts @@ -162,8 +162,22 @@ export const fr: TranslationMap = { store: "Stockage : {path}", active: "Actif", limit: "Limite", + filters: "Filtres", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Global", unknown: "Inconnu", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "min", searchPlaceholder: "Filtrer par clé, agent, libellé, type…", selected: "{count} sélectionné(s)", diff --git a/ui/src/i18n/locales/id.ts b/ui/src/i18n/locales/id.ts index 35db9fac47b..4524df472f1 100644 --- a/ui/src/i18n/locales/id.ts +++ b/ui/src/i18n/locales/id.ts @@ -160,8 +160,22 @@ export const id: TranslationMap = { store: "Penyimpanan: {path}", active: "Aktif", limit: "Batas", + filters: "Filter", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Global", unknown: "Tidak diketahui", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "mnt", searchPlaceholder: "Filter menurut kunci, agen, label, jenis…", selected: "{count} dipilih", diff --git a/ui/src/i18n/locales/it.ts b/ui/src/i18n/locales/it.ts index 4e4b33ffd3c..6f51ff907e8 100644 --- a/ui/src/i18n/locales/it.ts +++ b/ui/src/i18n/locales/it.ts @@ -160,8 +160,22 @@ export const it: TranslationMap = { store: "Archivio: {path}", active: "Attivo", limit: "Limite", + filters: "Filtri", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Globale", unknown: "Sconosciuto", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "min", searchPlaceholder: "Filtra per chiave, agente, etichetta, tipo…", selected: "{count} selezionati", diff --git a/ui/src/i18n/locales/ja-JP.ts b/ui/src/i18n/locales/ja-JP.ts index 08f451f6419..439913181c8 100644 --- a/ui/src/i18n/locales/ja-JP.ts +++ b/ui/src/i18n/locales/ja-JP.ts @@ -163,8 +163,22 @@ export const ja_JP: TranslationMap = { store: "ストア: {path}", active: "有効", limit: "制限", + filters: "フィルター", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "グローバル", unknown: "不明", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "分", searchPlaceholder: "キー、エージェント、ラベル、種類で絞り込み…", selected: "{count} 件選択中", diff --git a/ui/src/i18n/locales/ko.ts b/ui/src/i18n/locales/ko.ts index 00bf03341fc..ea65aa83af9 100644 --- a/ui/src/i18n/locales/ko.ts +++ b/ui/src/i18n/locales/ko.ts @@ -159,8 +159,22 @@ export const ko: TranslationMap = { store: "저장소: {path}", active: "활성", limit: "제한", + filters: "필터", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "전역", unknown: "알 수 없음", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "분", searchPlaceholder: "키, 에이전트, 레이블, 종류로 필터링…", selected: "{count}개 선택됨", diff --git a/ui/src/i18n/locales/nl.ts b/ui/src/i18n/locales/nl.ts index 50fcdd5f070..ed02d3338f2 100644 --- a/ui/src/i18n/locales/nl.ts +++ b/ui/src/i18n/locales/nl.ts @@ -162,8 +162,22 @@ export const nl: TranslationMap = { store: "Opslag: {path}", active: "Actief", limit: "Limiet", + filters: "Filters", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Globaal", unknown: "Onbekend", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "min", searchPlaceholder: "Filter op sleutel, agent, label, type…", selected: "{count} geselecteerd", diff --git a/ui/src/i18n/locales/pl.ts b/ui/src/i18n/locales/pl.ts index 5d27f341abb..bb2147d2620 100644 --- a/ui/src/i18n/locales/pl.ts +++ b/ui/src/i18n/locales/pl.ts @@ -161,8 +161,22 @@ export const pl: TranslationMap = { store: "Magazyn: {path}", active: "Aktywny", limit: "Limit", + filters: "Filtry", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Globalne", unknown: "Nieznane", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "min", searchPlaceholder: "Filtruj według klucza, agenta, etykiety, rodzaju…", selected: "Wybrano: {count}", diff --git a/ui/src/i18n/locales/pt-BR.ts b/ui/src/i18n/locales/pt-BR.ts index 82e39d55238..650de5e4c0a 100644 --- a/ui/src/i18n/locales/pt-BR.ts +++ b/ui/src/i18n/locales/pt-BR.ts @@ -160,8 +160,22 @@ export const pt_BR: TranslationMap = { store: "Armazenamento: {path}", active: "Ativo", limit: "Limite", + filters: "Filtros", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Global", unknown: "Desconhecido", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "min", searchPlaceholder: "Filtrar por chave, agente, rótulo, tipo…", selected: "{count} selecionado(s)", diff --git a/ui/src/i18n/locales/th.ts b/ui/src/i18n/locales/th.ts index 334cb0dadb0..3df83fc10a9 100644 --- a/ui/src/i18n/locales/th.ts +++ b/ui/src/i18n/locales/th.ts @@ -158,8 +158,22 @@ export const th: TranslationMap = { store: "ที่เก็บ: {path}", active: "ใช้งานอยู่", limit: "ขีดจำกัด", + filters: "ตัวกรอง", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "ส่วนกลาง", unknown: "ไม่ทราบ", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "นาที", searchPlaceholder: "กรองตามคีย์, agent, ป้ายกำกับ, ชนิด…", selected: "เลือกแล้ว {count} รายการ", diff --git a/ui/src/i18n/locales/tr.ts b/ui/src/i18n/locales/tr.ts index 612276e120e..ab0de580b0d 100644 --- a/ui/src/i18n/locales/tr.ts +++ b/ui/src/i18n/locales/tr.ts @@ -162,8 +162,22 @@ export const tr: TranslationMap = { store: "Depo: {path}", active: "Etkin", limit: "Sınır", + filters: "Filtreler", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Genel", unknown: "Bilinmiyor", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "dk", searchPlaceholder: "Anahtara, ajana, etikete, türe göre filtrele…", selected: "{count} seçildi", diff --git a/ui/src/i18n/locales/uk.ts b/ui/src/i18n/locales/uk.ts index 89fa4577ebc..9fe871fbec6 100644 --- a/ui/src/i18n/locales/uk.ts +++ b/ui/src/i18n/locales/uk.ts @@ -161,8 +161,22 @@ export const uk: TranslationMap = { store: "Сховище: {path}", active: "Активно", limit: "Обмеження", + filters: "Фільтри", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Глобально", unknown: "Невідомо", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "хв", searchPlaceholder: "Фільтрувати за ключем, агентом, міткою, типом…", selected: "Вибрано: {count}", diff --git a/ui/src/i18n/locales/vi.ts b/ui/src/i18n/locales/vi.ts index 8a1e1380121..a00b8100423 100644 --- a/ui/src/i18n/locales/vi.ts +++ b/ui/src/i18n/locales/vi.ts @@ -160,8 +160,22 @@ export const vi: TranslationMap = { store: "Kho lưu trữ: {path}", active: "Đang hoạt động", limit: "Giới hạn", + filters: "Bộ lọc", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "Toàn cục", unknown: "Không rõ", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "phút", searchPlaceholder: "Lọc theo khóa, agent, nhãn, loại…", selected: "Đã chọn {count}", diff --git a/ui/src/i18n/locales/zh-CN.ts b/ui/src/i18n/locales/zh-CN.ts index 531574e490d..e18a9d0cf4d 100644 --- a/ui/src/i18n/locales/zh-CN.ts +++ b/ui/src/i18n/locales/zh-CN.ts @@ -158,8 +158,22 @@ export const zh_CN: TranslationMap = { store: "存储:{path}", active: "活跃", limit: "限制", + filters: "筛选", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "全局", unknown: "未知", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "分钟", searchPlaceholder: "按密钥、代理、标签、类型筛选…", selected: "已选择 {count} 项", diff --git a/ui/src/i18n/locales/zh-TW.ts b/ui/src/i18n/locales/zh-TW.ts index 522b0977b8c..0e9522b3912 100644 --- a/ui/src/i18n/locales/zh-TW.ts +++ b/ui/src/i18n/locales/zh-TW.ts @@ -158,8 +158,22 @@ export const zh_TW: TranslationMap = { store: "儲存位置:{path}", active: "啟用中", limit: "限制", + filters: "篩選條件", + showFilters: "Show filters", + hideFilters: "Hide filters", + sourceFilters: "Session source filters", global: "全域", unknown: "未知", + showArchived: "Show archived", + activeTooltip: + "Only request sessions updated within the last N minutes. Clear the field, or show archived sessions, to remove the active-time cutoff.", + limitTooltip: + "Maximum rows to request from the Gateway. Higher limits can make large stores slower.", + globalTooltip: + "Include the special global session bucket shared outside a specific agent or chat.", + unknownTooltip: "Include the special unknown session bucket for legacy or unresolved traffic.", + showArchivedTooltip: + "Include explicitly archived rows and older store-backed sessions by removing the active-time cutoff.", minutesPlaceholder: "分鐘", searchPlaceholder: "依金鑰、代理程式、標籤、類型篩選…", selected: "已選取 {count} 個", diff --git a/ui/src/styles/chat/grouped.css b/ui/src/styles/chat/grouped.css index 562098b3d95..6f391bdeda1 100644 --- a/ui/src/styles/chat/grouped.css +++ b/ui/src/styles/chat/grouped.css @@ -217,9 +217,7 @@ img.chat-avatar { border-radius: var(--radius-lg); padding: 10px 14px; box-shadow: none; - transition: - background var(--duration-fast) ease-out, - border-color var(--duration-fast) ease-out; + transition: border-color var(--duration-fast) ease-out; width: auto; max-width: 100%; box-sizing: border-box; @@ -347,7 +345,7 @@ img.chat-avatar { } .chat-bubble:hover { - background: var(--bg-hover); + border-color: color-mix(in srgb, var(--accent) 28%, var(--border)); } .chat-bubble--tool-shell:hover { @@ -366,7 +364,7 @@ img.chat-avatar { } .chat-group.user .chat-bubble:hover { - background: color-mix(in srgb, var(--accent) 20%, transparent); + border-color: color-mix(in srgb, var(--accent) 32%, transparent); } /* Streaming animation */ diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index 594f2b6446e..651f3e29865 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -2309,6 +2309,10 @@ font-size: 13px; } +.sessions-table { + min-width: 1240px; +} + .data-table th { position: sticky; top: 0; @@ -2380,6 +2384,15 @@ background: var(--bg-hover); } +.session-data-row--expandable { + cursor: pointer; +} + +.session-data-row--expandable:focus-visible { + outline: 1px solid var(--border-strong); + outline-offset: -2px; +} + .data-table tbody tr:last-child td { border-bottom: none; } @@ -2419,6 +2432,11 @@ background: var(--bg-hover); } +.session-token-cell { + min-width: 112px; + white-space: nowrap; +} + /* Checkbox column */ .data-table-checkbox-col { width: var(--data-table-checkbox-col-width); @@ -2579,6 +2597,284 @@ td.data-table-key-col { accent-color: var(--accent); } +.sessions-filter-panel { + margin-bottom: 12px; + border: 1px solid color-mix(in srgb, var(--border) 82%, transparent); + border-radius: var(--radius-md); + background: color-mix(in srgb, var(--bg-muted) 58%, transparent); +} + +.sessions-filter-panel__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 6px; +} + +.sessions-filter-panel__title { + padding-left: 4px; + color: var(--muted); + font-size: 12px; + font-weight: 700; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +.sessions-filter-panel__toggle { + display: inline-flex; + align-items: center; + gap: 5px; + min-height: 28px; + padding: 4px 8px; + color: var(--muted); + background: transparent; + border: 1px solid transparent; + border-radius: var(--radius-sm); + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: + color var(--duration-fast) ease, + background-color var(--duration-fast) ease, + border-color var(--duration-fast) ease; +} + +.sessions-filter-panel__toggle:hover { + color: var(--text); + background: color-mix(in srgb, var(--bg-hover) 72%, transparent); + border-color: color-mix(in srgb, var(--border) 74%, transparent); +} + +.sessions-filter-panel__toggle:focus-visible { + box-shadow: var(--focus-ring); + outline: none; +} + +.sessions-filter-panel__toggle svg { + width: 14px; + height: 14px; +} + +.sessions-filter-bar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + padding: 0 6px 6px; +} + +.session-filter-primary-row, +.session-filter-toggle-group { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.session-filter-field, +.session-filter-check { + position: relative; + min-height: 34px; + box-sizing: border-box; + border: 1px solid color-mix(in srgb, var(--border) 76%, transparent); + border-radius: var(--radius-sm); + background: color-mix(in srgb, var(--card) 88%, transparent); +} + +.session-filter-field { + display: inline-grid; + grid-template-columns: auto minmax(54px, max-content); + align-items: center; + gap: 8px; + padding: 4px 8px 4px 10px; +} + +.session-filter-label, +.session-filter-check__label { + color: var(--muted); + font-size: 12px; + font-weight: 600; + line-height: 1; + white-space: nowrap; +} + +.session-filter-input { + width: 62px; + box-sizing: border-box; + padding: 5px 7px; + color: var(--text); + background: var(--bg-elevated); + border: 1px solid transparent; + border-radius: calc(var(--radius-sm) - 2px); + font-size: 12px; + line-height: 1.2; + outline: none; + transition: + border-color var(--duration-fast) ease, + box-shadow var(--duration-fast) ease, + opacity var(--duration-fast) ease; +} + +.session-filter-input--limit { + width: 56px; +} + +.session-filter-input:focus { + border-color: var(--border-strong); + box-shadow: var(--focus-ring); +} + +.session-filter-input:disabled { + cursor: not-allowed; + opacity: 0.48; +} + +.session-filter-check { + position: relative; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + cursor: pointer; + user-select: none; + transition: + border-color var(--duration-fast) ease, + background-color var(--duration-fast) ease, + color var(--duration-fast) ease; +} + +.session-filter-check:hover { + border-color: color-mix(in srgb, var(--border-strong) 74%, transparent); + background: color-mix(in srgb, var(--bg-hover) 72%, transparent); +} + +.session-filter-check--active { + border-color: color-mix(in srgb, var(--accent) 64%, var(--border)); + color: var(--text); + background: color-mix(in srgb, var(--accent-subtle) 72%, var(--card)); +} + +.session-filter-field[data-tooltip]::before, +.session-filter-check[data-tooltip]::before, +.session-filter-field[data-tooltip]::after, +.session-filter-check[data-tooltip]::after { + position: absolute; + z-index: 20; + opacity: 0; + pointer-events: none; + transition: + opacity var(--duration-fast) ease, + transform var(--duration-fast) ease; +} + +.session-filter-field[data-tooltip]::before, +.session-filter-check[data-tooltip]::before { + content: ""; + bottom: calc(100% + 4px); + left: 18px; + width: 8px; + height: 8px; + background: var(--text); + transform: translateY(3px) rotate(45deg); +} + +.session-filter-field[data-tooltip]::after, +.session-filter-check[data-tooltip]::after { + content: attr(data-tooltip); + bottom: calc(100% + 8px); + left: 0; + width: max-content; + max-width: min(320px, calc(100vw - 32px)); + padding: 7px 9px; + color: var(--card); + background: var(--text); + border-radius: var(--radius-sm); + box-shadow: var(--shadow-md); + font-size: 12px; + font-weight: 500; + line-height: 1.35; + white-space: normal; + transform: translateY(3px); +} + +@media (hover: hover) { + .session-filter-field[data-tooltip]:hover::before, + .session-filter-field[data-tooltip]:hover::after, + .session-filter-check[data-tooltip]:hover::before, + .session-filter-check[data-tooltip]:hover::after { + opacity: 1; + transform: translateY(0) rotate(45deg); + } + + .session-filter-field[data-tooltip]:hover::after, + .session-filter-check[data-tooltip]:hover::after { + transform: translateY(0); + } +} + +.session-filter-check__input { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; +} + +.session-filter-check__mark { + display: inline-grid; + place-items: center; + width: 14px; + height: 14px; + flex: 0 0 auto; + color: color-mix(in srgb, var(--muted) 48%, transparent); + opacity: 0.42; + transition: + color var(--duration-fast) ease, + opacity var(--duration-fast) ease; +} + +.session-filter-check__mark svg { + width: 13px; + height: 13px; + stroke-width: 3; +} + +.session-filter-check__input:checked + .session-filter-check__mark { + color: var(--accent); + opacity: 1; +} + +.session-filter-check__input:focus-visible + .session-filter-check__mark { + box-shadow: var(--focus-ring); +} + +.session-filter-check__input:checked ~ .session-filter-check__label { + color: var(--text); +} + +@media (max-width: 760px) { + .sessions-filter-bar { + flex-direction: column; + align-items: stretch; + } + + .sessions-filter-bar, + .session-filter-primary-row, + .session-filter-toggle-group, + .session-filter-field, + .session-filter-check { + width: 100%; + } + + .session-filter-field { + grid-template-columns: minmax(0, 1fr) auto; + } +} + /* =========================================== Log Stream =========================================== */ diff --git a/ui/src/styles/components.test.ts b/ui/src/styles/components.test.ts index 599c5ed64a9..12b05734409 100644 --- a/ui/src/styles/components.test.ts +++ b/ui/src/styles/components.test.ts @@ -24,3 +24,13 @@ describe("agent fallback chip styles", () => { expect(css).toContain(".agent-chip-input .chip-remove:disabled"); }); }); + +describe("sessions filter styles", () => { + it("keeps the expanded sessions filters on one row until the mobile breakpoint", () => { + const css = readComponentsCss(); + + expect(css).toContain(".sessions-filter-bar {\n display: flex;\n flex-wrap: wrap;"); + expect(css).toContain("@media (max-width: 760px)"); + expect(css).toContain(".sessions-filter-bar {\n flex-direction: column;"); + }); +}); diff --git a/ui/src/ui/app-defaults.test.ts b/ui/src/ui/app-defaults.test.ts new file mode 100644 index 00000000000..e0816ddf672 --- /dev/null +++ b/ui/src/ui/app-defaults.test.ts @@ -0,0 +1,11 @@ +import { describe, expect, it } from "vitest"; +import { DEFAULT_SESSIONS_FILTERS } from "./app-defaults.ts"; + +describe("app defaults", () => { + it("defaults session list requests to a broader but bounded result set", () => { + expect(DEFAULT_SESSIONS_FILTERS).toEqual({ + activeMinutes: "120", + limit: "200", + }); + }); +}); diff --git a/ui/src/ui/app-defaults.ts b/ui/src/ui/app-defaults.ts index fa8eff7012c..a93761fa775 100644 --- a/ui/src/ui/app-defaults.ts +++ b/ui/src/ui/app-defaults.ts @@ -10,6 +10,11 @@ export const DEFAULT_LOG_LEVEL_FILTERS: Record = { fatal: true, }; +export const DEFAULT_SESSIONS_FILTERS = { + activeMinutes: "120", + limit: "200", +} as const; + export const DEFAULT_CRON_FORM: CronFormState = { name: "", description: "", diff --git a/ui/src/ui/app-gateway.sessions.node.test.ts b/ui/src/ui/app-gateway.sessions.node.test.ts index b9382a62bcc..79c72939410 100644 --- a/ui/src/ui/app-gateway.sessions.node.test.ts +++ b/ui/src/ui/app-gateway.sessions.node.test.ts @@ -113,7 +113,29 @@ function createHost() { } describe("handleGatewayEvent sessions.changed", () => { - it("reloads sessions when the gateway pushes a sessions.changed event", () => { + it("applies reliable session change snapshots without refetching the list", () => { + loadSessionsMock.mockReset(); + applySessionsChangedEventMock.mockReset().mockReturnValue({ applied: true, change: "updated" }); + const host = createHost(); + const payload = { + sessionKey: "agent:main:main", + sessionId: "sess-main", + kind: "direct", + reason: "patch", + }; + + handleGatewayEvent(host, { + type: "event", + event: "sessions.changed", + payload, + seq: 1, + }); + + expect(applySessionsChangedEventMock).toHaveBeenCalledWith(host, payload); + expect(loadSessionsMock).not.toHaveBeenCalled(); + }); + + it("reloads sessions when a change event cannot be applied locally", () => { loadSessionsMock.mockReset(); applySessionsChangedEventMock.mockReset().mockReturnValue({ applied: false }); const host = createHost(); @@ -121,11 +143,10 @@ describe("handleGatewayEvent sessions.changed", () => { handleGatewayEvent(host, { type: "event", event: "sessions.changed", - payload: { sessionKey: "agent:main:main", reason: "patch" }, + payload: { sessionKey: "agent:main:main", reason: "cleanup" }, seq: 1, }); - expect(loadSessionsMock).toHaveBeenCalledTimes(1); expect(loadSessionsMock).toHaveBeenCalledWith(host); }); diff --git a/ui/src/ui/app-gateway.ts b/ui/src/ui/app-gateway.ts index 7257bc60cc5..2428bd0908d 100644 --- a/ui/src/ui/app-gateway.ts +++ b/ui/src/ui/app-gateway.ts @@ -755,8 +755,8 @@ function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) { } if (evt.event === "sessions.changed") { - applySessionsChangedEvent(host as unknown as SessionsState, evt.payload); - if (isChatTurnSessionChangedPayload(evt.payload)) { + const result = applySessionsChangedEvent(host as unknown as SessionsState, evt.payload); + if (result.applied || isChatTurnSessionChangedPayload(evt.payload)) { return; } void loadSessions(host as unknown as SessionsState); diff --git a/ui/src/ui/app-render.helpers.node.test.ts b/ui/src/ui/app-render.helpers.node.test.ts index e4c6334f39c..6e19369080a 100644 --- a/ui/src/ui/app-render.helpers.node.test.ts +++ b/ui/src/ui/app-render.helpers.node.test.ts @@ -133,6 +133,7 @@ function createChatSessionState(overrides: Partial = {}) { client: { request: vi.fn() }, sessionsLoading: false, sessionsError: null, + sessionsShowArchived: false, sessionsResult: { ts: 0, path: "", @@ -533,12 +534,10 @@ describe("resolveSessionOptionGroups", () => { ); }); - it("keeps scoped fallbacks for active grouped sessions without useful row metadata", () => { + it("does not synthesize active grouped sessions without a listed row", () => { const sessionKey = "agent:main:subagent:4f2146de-887b-4176-9abe-91140082959b"; - expect(labelsForSessionOptions({ sessionKey })).toContain( - "subagent:4f2146de-887b-4176-9abe-91140082959b", - ); + expect(labelsForSessionOptions({ sessionKey })).toEqual([]); expect( labelsForSessionOptions({ sessionKey, @@ -622,6 +621,7 @@ describe("createChatSession", () => { limit: 0, includeGlobal: true, includeUnknown: true, + showArchived: false, }, ); expect(state.sessionKey).toBe("agent:ops:dashboard:new-chat"); @@ -810,6 +810,7 @@ describe("switchChatSession", () => { limit: 0, includeGlobal: true, includeUnknown: true, + showArchived: false, }); }); diff --git a/ui/src/ui/app-render.helpers.ts b/ui/src/ui/app-render.helpers.ts index 0178ea06866..a1c897b26f0 100644 --- a/ui/src/ui/app-render.helpers.ts +++ b/ui/src/ui/app-render.helpers.ts @@ -650,6 +650,7 @@ export async function createChatSession(state: AppViewState) { limit: 0, includeGlobal: true, includeUnknown: true, + showArchived: Boolean(state.sessionsShowArchived), }, ); if ( @@ -680,6 +681,7 @@ async function refreshSessionOptions(state: AppViewState) { limit: 0, includeGlobal: true, includeUnknown: true, + showArchived: Boolean(state.sessionsShowArchived), }); } diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index 08fe9d26e06..98869e14edf 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -1680,6 +1680,8 @@ export function renderApp(state: AppViewState) { limit: state.sessionsFilterLimit, includeGlobal: state.sessionsIncludeGlobal, includeUnknown: state.sessionsIncludeUnknown, + showArchived: state.sessionsShowArchived, + filtersCollapsed: state.sessionsFiltersCollapsed, basePath: state.basePath, searchQuery: state.sessionsSearchQuery, agentIdentityById: state.agentIdentityById, @@ -1698,6 +1700,19 @@ export function renderApp(state: AppViewState) { state.sessionsFilterLimit = next.limit; state.sessionsIncludeGlobal = next.includeGlobal; state.sessionsIncludeUnknown = next.includeUnknown; + state.sessionsShowArchived = next.showArchived; + state.sessionsSelectedKeys = new Set(); + state.sessionsPage = 0; + void loadSessions(state, { + activeMinutes: Number(next.activeMinutes) || 0, + limit: Number(next.limit) || 0, + includeGlobal: next.includeGlobal, + includeUnknown: next.includeUnknown, + showArchived: next.showArchived, + }); + }, + onToggleFiltersCollapsed: () => { + state.sessionsFiltersCollapsed = !state.sessionsFiltersCollapsed; }, onSearchChange: (q) => { state.sessionsSearchQuery = q; diff --git a/ui/src/ui/app-view-state.ts b/ui/src/ui/app-view-state.ts index f221065c8a8..f728a62b21f 100644 --- a/ui/src/ui/app-view-state.ts +++ b/ui/src/ui/app-view-state.ts @@ -260,6 +260,8 @@ export type AppViewState = { sessionsFilterLimit: string; sessionsIncludeGlobal: boolean; sessionsIncludeUnknown: boolean; + sessionsShowArchived: boolean; + sessionsFiltersCollapsed: boolean; sessionsHideCron: boolean; sessionsSearchQuery: string; sessionsSortColumn: "key" | "kind" | "updated" | "tokens"; diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index 6b366cde4fe..5d263701aa9 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -25,7 +25,11 @@ import { type ChatInputHistoryKeyInput, type ChatInputHistoryKeyResult, } from "./app-chat.ts"; -import { DEFAULT_CRON_FORM, DEFAULT_LOG_LEVEL_FILTERS } from "./app-defaults.ts"; +import { + DEFAULT_CRON_FORM, + DEFAULT_LOG_LEVEL_FILTERS, + DEFAULT_SESSIONS_FILTERS, +} from "./app-defaults.ts"; import type { EventLogEntry } from "./app-events.ts"; import { connectGateway as connectGatewayInternal } from "./app-gateway.ts"; import { @@ -370,10 +374,12 @@ export class OpenClawApp extends LitElement { @state() sessionsLoading = false; @state() sessionsResult: SessionsListResult | null = null; @state() sessionsError: string | null = null; - @state() sessionsFilterActive = "120"; - @state() sessionsFilterLimit = "50"; + @state() sessionsFilterActive = DEFAULT_SESSIONS_FILTERS.activeMinutes; + @state() sessionsFilterLimit = DEFAULT_SESSIONS_FILTERS.limit; @state() sessionsIncludeGlobal = true; @state() sessionsIncludeUnknown = false; + @state() sessionsShowArchived = false; + @state() sessionsFiltersCollapsed = false; @state() sessionsHideCron = true; @state() sessionsSearchQuery = ""; @state() sessionsSortColumn: "key" | "kind" | "updated" | "tokens" = "updated"; diff --git a/ui/src/ui/chat/session-controls.ts b/ui/src/ui/chat/session-controls.ts index 05ec7e97713..199bf4d0c77 100644 --- a/ui/src/ui/chat/session-controls.ts +++ b/ui/src/ui/chat/session-controls.ts @@ -77,6 +77,7 @@ async function refreshSessionOptions(state: AppViewState) { limit: 0, includeGlobal: true, includeUnknown: true, + showArchived: Boolean(state.sessionsShowArchived), }); } @@ -539,7 +540,9 @@ export function resolveSessionOptionGroups( } addOption(row.key); } - addOption(sessionKey); + if (byKey.has(sessionKey)) { + addOption(sessionKey); + } for (const group of groups.values()) { const counts = new Map(); diff --git a/ui/src/ui/controllers/sessions.test.ts b/ui/src/ui/controllers/sessions.test.ts index 3bec7e41b27..e39f0817377 100644 --- a/ui/src/ui/controllers/sessions.test.ts +++ b/ui/src/ui/controllers/sessions.test.ts @@ -29,6 +29,7 @@ function createState(request: RequestFn, overrides: Partial = {}) sessionsFilterLimit: "0", sessionsIncludeGlobal: true, sessionsIncludeUnknown: true, + sessionsShowArchived: false, sessionsExpandedCheckpointKey: null, sessionsCheckpointItemsByKey: {}, sessionsCheckpointLoadingKey: null, @@ -249,6 +250,160 @@ describe("deleteSessionsAndRefresh", () => { }); describe("loadSessions", () => { + it("hides explicitly archived sessions by default", async () => { + const request = vi.fn(async (method: string) => { + if (method !== "sessions.list") { + throw new Error(`unexpected method: ${method}`); + } + return { + ts: 1, + path: "(multiple)", + count: 2, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [ + { key: "agent:main:main", kind: "direct", updatedAt: 2 }, + { + key: "agent:main:subagent:archived", + kind: "direct", + updatedAt: 1, + status: "done", + archived: true, + }, + ], + }; + }); + const state = createState(request); + + await loadSessions(state); + + expect(state.sessionsResult?.sessions.map((session) => session.key)).toEqual([ + "agent:main:main", + ]); + expect(state.sessionsResult?.count).toBe(1); + }); + + it("includes explicitly archived sessions when explicitly shown", async () => { + const request = vi.fn(async (method: string) => { + if (method !== "sessions.list") { + throw new Error(`unexpected method: ${method}`); + } + return { + ts: 1, + path: "(multiple)", + count: 2, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [ + { key: "agent:main:main", kind: "direct", updatedAt: 2 }, + { + key: "agent:main:subagent:archived", + kind: "direct", + updatedAt: 1, + status: "done", + archived: true, + }, + ], + }; + }); + const state = createState(request, { sessionsShowArchived: true }); + + await loadSessions(state); + + expect(state.sessionsResult?.sessions.map((session) => session.key)).toEqual([ + "agent:main:main", + "agent:main:subagent:archived", + ]); + expect(state.sessionsResult?.count).toBe(2); + }); + + it("keeps terminal non-archived sessions visible by default", async () => { + const request = vi.fn(async (method: string) => { + if (method !== "sessions.list") { + throw new Error(`unexpected method: ${method}`); + } + return { + ts: 1, + path: "(multiple)", + count: 2, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [ + { key: "agent:main:main", kind: "direct", updatedAt: 2 }, + { + key: "agent:main:subagent:done", + kind: "direct", + updatedAt: 1, + status: "done", + }, + ], + }; + }); + const state = createState(request); + + await loadSessions(state); + + expect(state.sessionsResult?.sessions.map((session) => session.key)).toEqual([ + "agent:main:main", + "agent:main:subagent:done", + ]); + expect(state.sessionsResult?.count).toBe(2); + }); + + it("omits the active-window cutoff when archived sessions are shown", async () => { + const request = vi.fn(async (method: string) => { + if (method !== "sessions.list") { + throw new Error(`unexpected method: ${method}`); + } + return { + ts: 1, + path: "(multiple)", + count: 0, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [], + }; + }); + const state = createState(request, { + sessionsFilterActive: "120", + sessionsFilterLimit: "50", + sessionsShowArchived: true, + }); + + await loadSessions(state); + + expect(request).toHaveBeenCalledWith("sessions.list", { + limit: 50, + includeGlobal: true, + includeUnknown: true, + }); + }); + + it("applies the active-window cutoff while archived sessions are hidden", async () => { + const request = vi.fn(async (method: string) => { + if (method !== "sessions.list") { + throw new Error(`unexpected method: ${method}`); + } + return { + ts: 1, + path: "(multiple)", + count: 0, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [], + }; + }); + const state = createState(request, { + sessionsFilterActive: "120", + sessionsFilterLimit: "50", + sessionsShowArchived: false, + }); + + await loadSessions(state); + + expect(request).toHaveBeenCalledWith("sessions.list", { + activeMinutes: 120, + limit: 50, + includeGlobal: true, + includeUnknown: true, + }); + }); + it("coalesces overlapping refreshes instead of dropping the latest request", async () => { let resolveFirst: () => void = () => undefined; const firstBlocker = new Promise((resolve) => { @@ -272,7 +427,7 @@ describe("loadSessions", () => { ts: 2, path: "(multiple)", count: 0, - defaults: {}, + defaults: { modelProvider: null, model: null, contextTokens: null }, sessions: [], }; }); @@ -391,6 +546,127 @@ describe("loadSessions", () => { }); describe("applySessionsChangedEvent", () => { + it("removes deleted sessions instead of keeping archived rows visible", () => { + const state = createState(async () => undefined, { + sessionsResult: { + ts: 1, + path: "(multiple)", + count: 2, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [ + { key: "agent:main:main", kind: "direct", updatedAt: 1 }, + { key: "agent:main:old", kind: "direct", updatedAt: 1 }, + ], + }, + }); + + const applied = applySessionsChangedEvent(state, { + sessionKey: "agent:main:old", + reason: "delete", + ts: 2, + }); + + expect(applied).toEqual({ applied: true, change: "deleted" }); + expect(state.sessionsResult?.sessions.map((session) => session.key)).toEqual([ + "agent:main:main", + ]); + expect(state.sessionsResult?.count).toBe(1); + }); + + it("does not synthesize new sessions from partial events without a store-backed row", () => { + const state = createState(async () => undefined, { + sessionsResult: { + ts: 1, + path: "(multiple)", + count: 0, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [], + }, + }); + + const applied = applySessionsChangedEvent(state, { + sessionKey: "agent:main:ephemeral", + reason: "message", + ts: 2, + }); + + expect(applied).toEqual({ applied: false }); + expect(state.sessionsResult?.sessions).toEqual([]); + }); + + it("applies partial events only to existing source-of-truth rows", () => { + const state = createState(async () => undefined, { + sessionsResult: { + ts: 1, + path: "(multiple)", + count: 1, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [{ key: "agent:main:main", kind: "direct", updatedAt: 1 }], + }, + }); + + const applied = applySessionsChangedEvent(state, { + sessionKey: "agent:main:main", + reason: "message", + ts: 2, + }); + + expect(applied).toEqual({ applied: true, change: "updated" }); + expect(state.sessionsResult?.sessions).toEqual([ + { key: "agent:main:main", kind: "direct", updatedAt: 1 }, + ]); + }); + + it("drops rows that become explicitly archived while archived sessions are hidden", () => { + const state = createState(async () => undefined, { + sessionsResult: { + ts: 1, + path: "(multiple)", + count: 1, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [{ key: "agent:main:subagent:done", kind: "direct", updatedAt: 1 }], + }, + }); + + const applied = applySessionsChangedEvent(state, { + sessionKey: "agent:main:subagent:done", + sessionId: "sess-done", + status: "done", + archived: true, + ts: 2, + }); + + expect(applied).toEqual({ applied: true, change: "deleted" }); + expect(state.sessionsResult?.sessions).toEqual([]); + }); + + it("keeps terminal status updates visible while archived sessions are hidden", () => { + const state = createState(async () => undefined, { + sessionsResult: { + ts: 1, + path: "(multiple)", + count: 1, + defaults: { modelProvider: null, model: null, contextTokens: null }, + sessions: [{ key: "agent:main:subagent:done", kind: "direct", updatedAt: 1 }], + }, + }); + + const applied = applySessionsChangedEvent(state, { + sessionKey: "agent:main:subagent:done", + sessionId: "sess-done", + status: "done", + ts: 2, + }); + + expect(applied).toEqual({ applied: true, change: "updated" }); + expect(state.sessionsResult?.sessions).toMatchObject([ + { + key: "agent:main:subagent:done", + status: "done", + }, + ]); + }); + it("updates fresh context usage from websocket event payloads", () => { const state = createState(async () => undefined, { sessionsResult: { @@ -413,6 +689,7 @@ describe("applySessionsChangedEvent", () => { const applied = applySessionsChangedEvent(state, { sessionKey: "agent:main:main", + sessionId: "sess-main", ts: 2, totalTokens: 190_000, totalTokensFresh: true, @@ -453,6 +730,7 @@ describe("applySessionsChangedEvent", () => { applySessionsChangedEvent(state, { sessionKey: "agent:main:main", + sessionId: "sess-main", totalTokensFresh: false, contextTokens: 200_000, }); @@ -497,7 +775,7 @@ describe("applySessionsChangedEvent", () => { ]); }); - it("reports when websocket event payloads insert new rows", () => { + it("reports when reliable websocket event payloads insert new rows", () => { const state = createState(async () => undefined, { sessionsResult: { ts: 1, @@ -510,6 +788,7 @@ describe("applySessionsChangedEvent", () => { const applied = applySessionsChangedEvent(state, { sessionKey: "agent:main:new", + sessionId: "sess-new", ts: 2, kind: "direct", updatedAt: 2, diff --git a/ui/src/ui/controllers/sessions.ts b/ui/src/ui/controllers/sessions.ts index 7ad98f64f14..3a5aa1b1985 100644 --- a/ui/src/ui/controllers/sessions.ts +++ b/ui/src/ui/controllers/sessions.ts @@ -23,6 +23,7 @@ export type SessionsState = { sessionsFilterLimit: string; sessionsIncludeGlobal: boolean; sessionsIncludeUnknown: boolean; + sessionsShowArchived: boolean; sessionsExpandedCheckpointKey: string | null; sessionsCheckpointItemsByKey: Record; sessionsCheckpointLoadingKey: string | null; @@ -35,6 +36,7 @@ type LoadSessionsOverrides = { limit?: number; includeGlobal?: boolean; includeUnknown?: boolean; + showArchived?: boolean; }; type CreateSessionParams = { @@ -79,6 +81,7 @@ const SESSION_EVENT_ROW_FIELDS = [ "spawnedBy", "startedAt", "status", + "archived", "subject", "surface", "systemSent", @@ -127,6 +130,29 @@ function normalizeSessionKind(value: unknown): GatewaySessionRow["kind"] | undef : undefined; } +export function isArchivedSessionRow(row: GatewaySessionRow): boolean { + return row.archived === true; +} + +function filterAvailableSessionRows( + rows: GatewaySessionRow[], + options: { showArchived: boolean }, +): GatewaySessionRow[] { + return rows.filter((row) => row.key && (options.showArchived || !isArchivedSessionRow(row))); +} + +function projectSessionsResultForAvailability( + result: SessionsListResult, + options: { showArchived: boolean }, +): SessionsListResult { + const sessions = filterAvailableSessionRows(result.sessions, options); + return { + ...result, + count: sessions.length, + sessions, + }; +} + function compareSessionRowsByUpdatedAt(a: GatewaySessionRow, b: GatewaySessionRow): number { return (b.updatedAt ?? 0) - (a.updatedAt ?? 0); } @@ -240,7 +266,7 @@ async function runCompactionMutation( export type SessionsChangedApplyResult = | { applied: false } - | { applied: true; change: "inserted" | "updated" }; + | { applied: true; change: "deleted" | "inserted" | "updated" }; export function applySessionsChangedEvent( state: SessionsState, @@ -262,7 +288,24 @@ export function applySessionsChangedEvent( const previousRows = state.sessionsResult.sessions; const existingIndex = previousRows.findIndex((row) => row.key === key); + if (payload.reason === "delete") { + if (existingIndex < 0) { + return { applied: false }; + } + state.sessionsResult = { + ...state.sessionsResult, + count: Math.max(0, state.sessionsResult.count - 1), + sessions: previousRows.filter((row) => row.key !== key), + }; + invalidateCheckpointCacheForKey(state, key); + return { applied: true, change: "deleted" }; + } const existing = existingIndex >= 0 ? previousRows[existingIndex] : undefined; + const hasReliableSource = + existingIndex >= 0 || eventSession !== null || typeof source.sessionId === "string"; + if (!hasReliableSource) { + return { applied: false }; + } const previousCheckpointSignature = checkpointSummarySignature(existing); const fallbackKind = normalizeSessionKind(source.kind) ?? existing?.kind ?? "unknown"; const nextRow: GatewaySessionRow = { @@ -285,6 +328,18 @@ export function applySessionsChangedEvent( if (nextRow.totalTokensFresh === false && !hasOwn(source, "totalTokens")) { delete nextRow.totalTokens; } + if (!state.sessionsShowArchived && isArchivedSessionRow(nextRow)) { + if (existingIndex < 0) { + return { applied: false }; + } + state.sessionsResult = { + ...state.sessionsResult, + count: Math.max(0, state.sessionsResult.count - 1), + sessions: previousRows.filter((row) => row.key !== key), + }; + invalidateCheckpointCacheForKey(state, key); + return { applied: true, change: "deleted" }; + } const nextRows = existingIndex >= 0 @@ -366,7 +421,10 @@ async function loadSessionsOnce( ); const includeGlobal = overrides?.includeGlobal ?? state.sessionsIncludeGlobal; const includeUnknown = overrides?.includeUnknown ?? state.sessionsIncludeUnknown; - const activeMinutes = overrides?.activeMinutes ?? toNumber(state.sessionsFilterActive, 0); + const showArchived = overrides?.showArchived ?? state.sessionsShowArchived; + const activeMinutes = showArchived + ? 0 + : (overrides?.activeMinutes ?? toNumber(state.sessionsFilterActive, 0)); const limit = overrides?.limit ?? toNumber(state.sessionsFilterLimit, 0); const params: Record = { includeGlobal, @@ -380,15 +438,15 @@ async function loadSessionsOnce( } const res = await client.request("sessions.list", params); if (res) { - state.sessionsResult = res; - const nextKeys = new Set(res.sessions.map((row) => row.key)); + state.sessionsResult = projectSessionsResultForAvailability(res, { showArchived }); + const nextKeys = new Set(state.sessionsResult.sessions.map((row) => row.key)); for (const key of Object.keys(state.sessionsCheckpointItemsByKey)) { if (!nextKeys.has(key)) { invalidateCheckpointCacheForKey(state, key); } } let expandedNeedsRefetch = false; - for (const row of res.sessions) { + for (const row of state.sessionsResult.sessions) { const previous = previousRows.get(row.key); if (checkpointSummarySignature(previous) !== checkpointSummarySignature(row)) { invalidateCheckpointCacheForKey(state, row.key); diff --git a/ui/src/ui/types.ts b/ui/src/ui/types.ts index 783ea649f96..d222b298e0c 100644 --- a/ui/src/ui/types.ts +++ b/ui/src/ui/types.ts @@ -441,6 +441,7 @@ export type GatewaySessionRow = { totalTokens?: number; totalTokensFresh?: boolean; status?: SessionRunStatus; + archived?: boolean; hasActiveRun?: boolean; subagentRunState?: SubagentRunState; hasActiveSubagentRun?: boolean; diff --git a/ui/src/ui/views/sessions.test.ts b/ui/src/ui/views/sessions.test.ts index 68a1f6f48e6..cc1bf981b50 100644 --- a/ui/src/ui/views/sessions.test.ts +++ b/ui/src/ui/views/sessions.test.ts @@ -34,6 +34,8 @@ function buildProps(result: SessionsListResult): SessionsProps { limit: "120", includeGlobal: false, includeUnknown: false, + showArchived: false, + filtersCollapsed: false, basePath: "", searchQuery: "", agentIdentityById: {}, @@ -48,6 +50,7 @@ function buildProps(result: SessionsListResult): SessionsProps { checkpointBusyKey: null, checkpointErrorByKey: {}, onFiltersChange: () => undefined, + onToggleFiltersCollapsed: () => undefined, onSearchChange: () => undefined, onSortChange: () => undefined, onPageChange: () => undefined, @@ -66,6 +69,123 @@ function buildProps(result: SessionsListResult): SessionsProps { } describe("sessions view", () => { + it("renders an explicit archived-session toggle", async () => { + const container = document.createElement("div"); + const onFiltersChange = vi.fn(); + render( + renderSessions({ + ...buildProps(buildMultiResult([])), + onFiltersChange, + }), + container, + ); + await Promise.resolve(); + + const archivedToggle = container.querySelector( + ".session-archive-toggle input", + ) as HTMLInputElement | null; + expect(archivedToggle?.checked).toBe(false); + + archivedToggle!.checked = true; + archivedToggle!.dispatchEvent(new Event("change", { bubbles: true })); + + expect(onFiltersChange).toHaveBeenCalledWith({ + activeMinutes: "", + limit: "120", + includeGlobal: false, + includeUnknown: false, + showArchived: true, + }); + }); + + it("uses one short styled tooltip per session filter", async () => { + const container = document.createElement("div"); + render(renderSessions(buildProps(buildMultiResult([]))), container); + await Promise.resolve(); + + const filters = container.querySelector(".sessions-filter-bar"); + const activeField = filters + ?.querySelector(".session-filter-input--minutes") + ?.closest("label"); + const limitField = filters + ?.querySelector(".session-filter-input--limit") + ?.closest("label"); + const globalToggle = filters + ?.querySelector(".session-filter-check__input[name=includeGlobal]") + ?.closest("label"); + const unknownToggle = filters + ?.querySelector(".session-filter-check__input[name=includeUnknown]") + ?.closest("label"); + const archivedToggle = filters + ?.querySelector(".session-filter-check__input[name=showArchived]") + ?.closest("label"); + + expect(activeField?.getAttribute("data-tooltip")).toBe("Updated in the last N minutes."); + expect(limitField?.getAttribute("data-tooltip")).toBe("Max sessions to load."); + expect(globalToggle?.getAttribute("data-tooltip")).toBe("Include global sessions."); + expect(unknownToggle?.getAttribute("data-tooltip")).toBe("Include unknown sessions."); + expect(archivedToggle?.getAttribute("data-tooltip")).toBe("Include archived sessions."); + expect( + Array.from(filters?.querySelectorAll("[title]") ?? []).map((node) => node.className), + ).toEqual([]); + }); + + it("keeps active and limit together and renders streamlined source toggles", async () => { + const container = document.createElement("div"); + render( + renderSessions({ + ...buildProps(buildMultiResult([])), + activeMinutes: "120", + limit: "200", + includeGlobal: true, + }), + container, + ); + await Promise.resolve(); + + const primaryRow = container.querySelector(".session-filter-primary-row"); + expect(primaryRow?.querySelector(".session-filter-input--minutes")?.closest("label")).toBe( + primaryRow?.firstElementChild, + ); + expect(primaryRow?.querySelector(".session-filter-input--limit")?.closest("label")).toBe( + primaryRow?.lastElementChild, + ); + + const toggleGroup = container.querySelector(".session-filter-toggle-group"); + expect(toggleGroup?.getAttribute("role")).toBe("group"); + expect(toggleGroup?.getAttribute("aria-label")).toBe("Session source filters"); + expect(toggleGroup?.querySelectorAll(".session-filter-check")).toHaveLength(3); + expect( + toggleGroup + ?.querySelector(".session-filter-check__input[name=includeGlobal]") + ?.closest("label") + ?.classList.contains("session-filter-check--active"), + ).toBe(true); + expect(toggleGroup?.querySelector(".session-filter-check__box")).toBeNull(); + }); + + it("collapses the whole session filter section from the header", async () => { + const container = document.createElement("div"); + const onToggleFiltersCollapsed = vi.fn(); + render( + renderSessions({ + ...buildProps(buildMultiResult([])), + filtersCollapsed: true, + onToggleFiltersCollapsed, + }), + container, + ); + await Promise.resolve(); + + const toggle = container.querySelector(".sessions-filter-panel__toggle"); + expect(toggle?.getAttribute("aria-expanded")).toBe("false"); + expect(container.querySelector(".sessions-filter-bar")).toBeNull(); + + toggle?.click(); + + expect(onToggleFiltersCollapsed).toHaveBeenCalledTimes(1); + }); + it("renders and patches provider-owned thinking ids", async () => { const container = document.createElement("div"); const onPatch = vi.fn(); @@ -261,6 +381,80 @@ describe("sessions view", () => { expect(text).not.toContain("Object (telegram)"); }); + it("expands checkpoint details from row activation when checkpoints exist", async () => { + const container = document.createElement("div"); + const onToggleCheckpointDetails = vi.fn(); + render( + renderSessions({ + ...buildProps( + buildResult({ + key: "agent:main:main", + kind: "direct", + updatedAt: Date.now(), + totalTokens: 123456, + contextTokens: 200000, + compactionCheckpointCount: 1, + latestCompactionCheckpoint: { + checkpointId: "checkpoint-1", + createdAt: Date.now(), + reason: "manual", + }, + }), + ), + onToggleCheckpointDetails, + }), + container, + ); + await Promise.resolve(); + + const row = container.querySelector("tbody tr.session-data-row"); + row?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + + expect(onToggleCheckpointDetails).toHaveBeenCalledWith("agent:main:main"); + const tokenCell = container.querySelector(".session-token-cell"); + expect(tokenCell?.textContent?.trim()).toBe("123456 / 200000"); + }); + + it("does not expand checkpoint details when the row has none or a nested control was used", async () => { + const container = document.createElement("div"); + const onToggleCheckpointDetails = vi.fn(); + render( + renderSessions({ + ...buildProps( + buildMultiResult([ + { + key: "agent:main:with-checkpoint", + kind: "direct", + updatedAt: 20, + compactionCheckpointCount: 1, + latestCompactionCheckpoint: { + checkpointId: "checkpoint-1", + createdAt: 20, + reason: "manual", + }, + }, + { + key: "agent:main:no-checkpoint", + kind: "direct", + updatedAt: 10, + compactionCheckpointCount: 0, + }, + ]), + ), + onToggleCheckpointDetails, + }), + container, + ); + await Promise.resolve(); + + const rows = container.querySelectorAll("tbody tr.session-data-row"); + const checkbox = rows[0]?.querySelector("input[type=checkbox]"); + checkbox?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + rows[1]?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + + expect(onToggleCheckpointDetails).not.toHaveBeenCalled(); + }); + it("filters rows by agent identity name", async () => { const container = document.createElement("div"); render( diff --git a/ui/src/ui/views/sessions.ts b/ui/src/ui/views/sessions.ts index b3574107a52..11b8c9a0be9 100644 --- a/ui/src/ui/views/sessions.ts +++ b/ui/src/ui/views/sessions.ts @@ -22,6 +22,8 @@ export type SessionsProps = { limit: string; includeGlobal: boolean; includeUnknown: boolean; + showArchived: boolean; + filtersCollapsed: boolean; basePath: string; searchQuery: string; agentIdentityById: Record; @@ -40,7 +42,9 @@ export type SessionsProps = { limit: string; includeGlobal: boolean; includeUnknown: boolean; + showArchived: boolean; }) => void; + onToggleFiltersCollapsed: () => void; onSearchChange: (query: string) => void; onSortChange: (column: "key" | "kind" | "updated" | "tokens", dir: "asc" | "desc") => void; onPageChange: (page: number) => void; @@ -254,6 +258,44 @@ function formatCheckpointDelta(checkpoint: SessionCompactionCheckpoint): string return t("sessionsView.tokenDeltaUnavailable"); } +function isRowControlTarget(target: EventTarget | null): boolean { + return ( + target instanceof Element && + Boolean(target.closest("a, button, input, label, select, textarea")) + ); +} + +function renderFilterToggle(params: { + name: string; + checked: boolean; + label: string; + title: string; + extraClass?: string; + onChange: (checked: boolean) => void; +}) { + const className = [ + "session-filter-check", + "session-filter-toggle", + params.extraClass ?? "", + params.checked ? "session-filter-check--active" : "", + ] + .filter(Boolean) + .join(" "); + return html` + + `; +} + export function renderSessions(props: SessionsProps) { const rawRows = props.result?.sessions ?? []; const filtered = filterRows(rawRows, props.searchQuery, props.agentIdentityById); @@ -262,6 +304,16 @@ export function renderSessions(props: SessionsProps) { const totalPages = Math.max(1, Math.ceil(totalRows / props.pageSize)); const page = Math.min(props.page, totalPages - 1); const paginated = paginateRows(sorted, page, props.pageSize); + const activeTooltip = t("sessionsView.activeTooltip"); + const limitTooltip = t("sessionsView.limitTooltip"); + const globalTooltip = t("sessionsView.globalTooltip"); + const unknownTooltip = t("sessionsView.unknownTooltip"); + const showArchivedTooltip = t("sessionsView.showArchivedTooltip"); + const filtersExpanded = !props.filtersCollapsed; + const filterPanelTitle = t("sessionsView.filters"); + const filterToggleLabel = filtersExpanded + ? t("sessionsView.hideFilters") + : t("sessionsView.showFilters"); const sortHeader = ( col: "key" | "kind" | "updated" | "tokens", @@ -299,64 +351,114 @@ export function renderSessions(props: SessionsProps) { -
- - - - +
+
+
${filterPanelTitle}
+ +
+ + ${filtersExpanded + ? html` +
+
+ + +
+
+ ${renderFilterToggle({ + name: "includeGlobal", + checked: props.includeGlobal, + label: t("sessionsView.global"), + title: globalTooltip, + onChange: (checked) => + props.onFiltersChange({ + activeMinutes: props.activeMinutes, + limit: props.limit, + includeGlobal: checked, + includeUnknown: props.includeUnknown, + showArchived: props.showArchived, + }), + })} + ${renderFilterToggle({ + name: "includeUnknown", + checked: props.includeUnknown, + label: t("sessionsView.unknown"), + title: unknownTooltip, + onChange: (checked) => + props.onFiltersChange({ + activeMinutes: props.activeMinutes, + limit: props.limit, + includeGlobal: props.includeGlobal, + includeUnknown: checked, + showArchived: props.showArchived, + }), + })} + ${renderFilterToggle({ + name: "showArchived", + checked: props.showArchived, + label: t("sessionsView.showArchived"), + title: showArchivedTooltip, + extraClass: "session-archive-toggle", + onChange: (checked) => + props.onFiltersChange({ + activeMinutes: props.activeMinutes, + limit: props.limit, + includeGlobal: props.includeGlobal, + includeUnknown: props.includeUnknown, + showArchived: checked, + }), + })} +
+
+ ` + : nothing}
${props.error @@ -396,7 +498,7 @@ export function renderSessions(props: SessionsProps) { : nothing}
- +
+ html` { + if (!hasCheckpoints || isRowControlTarget(e.target)) { + return; + } + activateCheckpointDetails(); + }} + @keydown=${(e: KeyboardEvent) => { + if (!hasCheckpoints || isRowControlTarget(e.target)) { + return; + } + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + activateCheckpointDetails(); + } + }} + > - + `, - ...(isExpanded + ...(isExpanded && hasCheckpoints ? [ - html` + html`
@@ -495,9 +597,11 @@ function renderRows(row: GatewaySessionRow, props: SessionsProps) { const reasoningLevels = withCurrentOption(REASONING_LEVELS, reasoning); const latestCheckpoint = row.latestCompactionCheckpoint; const checkpointCount = row.compactionCheckpointCount ?? 0; + const hasCheckpoints = checkpointCount > 0 || Boolean(latestCheckpoint); const isExpanded = props.expandedCheckpointKey === row.key; const checkpointItems = props.checkpointItemsByKey[row.key] ?? []; const checkpointError = props.checkpointErrorByKey[row.key]; + const detailsId = `session-checkpoints-${encodeURIComponent(row.key)}`; const displayName = normalizeOptionalString(row.displayName) ?? null; const trimmedLabel = normalizeOptionalString(row.label) ?? ""; const showDisplayName = Boolean( @@ -528,9 +632,41 @@ function renderRows(row: GatewaySessionRow, props: SessionsProps) { : row.kind === "global" ? "data-table-badge--global" : "data-table-badge--unknown"; + const rowClass = [ + "session-data-row", + hasCheckpoints ? "session-data-row--expandable" : "", + isExpanded ? "session-data-row--expanded" : "", + ] + .filter(Boolean) + .join(" "); + const activateCheckpointDetails = () => { + if (hasCheckpoints) { + props.onToggleCheckpointDetails(row.key); + } + }; return [ - html`
${row.kind} ${updated}${formatSessionTokens(row)}${formatSessionTokens(row)}
@@ -606,13 +742,21 @@ function renderRows(row: GatewaySessionRow, props: SessionsProps) { ` : nothing} - + ${hasCheckpoints + ? html` + + ` + : nothing}
@@ -686,9 +830,9 @@ function renderRows(row: GatewaySessionRow, props: SessionsProps) {