From e13a2401225554855d8594e2f5ff798427cdf556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 27 Aug 2026 05:16:31 +0200 Subject: [PATCH 1/6] feat(mobile): add glanceable agents snapshot contract --- apps/mobile/src/app/(app)/_layout.tsx | 4 + apps/mobile/src/i18n/locales/en.json | 15 + apps/mobile/src/lib/auth/auth-context.tsx | 11 + .../mobile/src/lib/glanceable/cleanup.test.ts | 182 ++++++++++++ apps/mobile/src/lib/glanceable/cleanup.ts | 127 +++++++++ apps/mobile/src/lib/glanceable/mount.tsx | 107 +++++++ apps/mobile/src/lib/glanceable/open-agents.ts | 12 + apps/mobile/src/lib/glanceable/org-fence.ts | 42 +++ .../mobile/src/lib/glanceable/persist.test.ts | 116 ++++++++ apps/mobile/src/lib/glanceable/persist.ts | 139 ++++++++++ .../src/lib/glanceable/presentation.test.ts | 108 ++++++++ .../mobile/src/lib/glanceable/presentation.ts | 107 +++++++ .../src/lib/glanceable/publisher.test.ts | 243 ++++++++++++++++ apps/mobile/src/lib/glanceable/publisher.ts | 260 ++++++++++++++++++ .../src/lib/glanceable/sink-registry.ts | 57 ++++ apps/mobile/src/lib/organization-context.tsx | 29 +- apps/mobile/vitest.pure.config.ts | 1 + packages/app-shared/package.json | 3 +- .../src/glanceable-agents-snapshot.test.ts | 210 ++++++++++++++ .../src/glanceable-agents-snapshot.ts | 187 +++++++++++++ 20 files changed, 1951 insertions(+), 9 deletions(-) create mode 100644 apps/mobile/src/lib/glanceable/cleanup.test.ts create mode 100644 apps/mobile/src/lib/glanceable/cleanup.ts create mode 100644 apps/mobile/src/lib/glanceable/mount.tsx create mode 100644 apps/mobile/src/lib/glanceable/open-agents.ts create mode 100644 apps/mobile/src/lib/glanceable/org-fence.ts create mode 100644 apps/mobile/src/lib/glanceable/persist.test.ts create mode 100644 apps/mobile/src/lib/glanceable/persist.ts create mode 100644 apps/mobile/src/lib/glanceable/presentation.test.ts create mode 100644 apps/mobile/src/lib/glanceable/presentation.ts create mode 100644 apps/mobile/src/lib/glanceable/publisher.test.ts create mode 100644 apps/mobile/src/lib/glanceable/publisher.ts create mode 100644 apps/mobile/src/lib/glanceable/sink-registry.ts create mode 100644 packages/app-shared/src/glanceable-agents-snapshot.test.ts create mode 100644 packages/app-shared/src/glanceable-agents-snapshot.ts diff --git a/apps/mobile/src/app/(app)/_layout.tsx b/apps/mobile/src/app/(app)/_layout.tsx index f0bc6944c5..385bc18510 100644 --- a/apps/mobile/src/app/(app)/_layout.tsx +++ b/apps/mobile/src/app/(app)/_layout.tsx @@ -9,6 +9,8 @@ import { SharePayloadNavigator } from '@/components/share/share-payload-navigato import { privacyScreenLayout } from '@/components/privacy-cover-overlay'; import { ActiveSessionsLiveSyncMount } from '@/lib/active-sessions-live-sync-mount'; import { attemptLogoutReconciliation } from '@/lib/auth/logout-reconciliation'; +import { GlanceablePublisherMount } from '@/lib/glanceable/mount'; +import { useGlanceableOrgFence } from '@/lib/glanceable/org-fence'; import { attemptPushRegistrationReconciliation, subscribeToPushTokenRotation, @@ -108,10 +110,12 @@ export default function AppLayout() { const colors = useThemeColors(); const { fullSheetDetent } = useFormSheetDetents(); useSecurityLifecycleInvalidation(); + useGlanceableOrgFence(); return ( + diff --git a/apps/mobile/src/i18n/locales/en.json b/apps/mobile/src/i18n/locales/en.json index ad2061678c..b0326cb45e 100644 --- a/apps/mobile/src/i18n/locales/en.json +++ b/apps/mobile/src/i18n/locales/en.json @@ -3149,5 +3149,20 @@ "xhigh": "Extra High", "max": "Max" } + }, + "glanceable": { + "waiting": "Updating agents", + "empty": "No work in progress", + "stale": "Can't update now", + "expired": "Status expired", + "signedOut": "Sign in to see agents", + "privacy": "Agents hidden", + "openAgents": "Open agents", + "running": "Running", + "needsInput": "Needs input", + "reconnecting": "Reconnecting", + "channelName": "Active agents", + "activityKitDisabledTitle": "Live Activities are off", + "activityKitDisabledBody": "Turn on Live Activities in Settings to see Active Agents on the Lock Screen." } } diff --git a/apps/mobile/src/lib/auth/auth-context.tsx b/apps/mobile/src/lib/auth/auth-context.tsx index 94d8f8ca6a..bd25a10051 100644 --- a/apps/mobile/src/lib/auth/auth-context.tsx +++ b/apps/mobile/src/lib/auth/auth-context.tsx @@ -15,6 +15,7 @@ import { import { discardPostHog } from '@/lib/analytics/posthog'; import { resetAppsFlyerState, trackEvent } from '@/lib/appsflyer'; import { clearAccountBoundPendingDeepLink, setCurrentDeepLinkUserId } from '@/lib/deep-link-launch'; +import { writeSignedOutSnapshotAndEnd } from '@/lib/glanceable/cleanup'; import { deleteAccountMetadata } from '@/lib/auth/account-metadata-write'; import { runLogoutCleanup } from '@/lib/auth/logout-cleanup'; import { queryClient } from '@/lib/query-client'; @@ -204,6 +205,9 @@ export function AuthProvider({ children }: { readonly children: ReactNode }) { // session out (documented, correct FIFO semantics). await chainSave('auth-transition', async () => { bumpAuthEpoch(); + // Blank the prior account's glanceable surface before any credential + // persist, so a direct account switch never shows the previous account. + writeSignedOutSnapshotAndEnd(); setAuthEpoch(currentAuthEpoch()); // Bind the pending deep-link slot to the new user id at the same // place the auth epoch advances, so a destination captured while this @@ -232,6 +236,10 @@ export function AuthProvider({ children }: { readonly children: ReactNode }) { setSignOutActive(false); trackEvent('login'); resetPurchaseErrorToastDedup(); + // A direct account switch must not keep the prior account's query + // cache: the org list is keyed account-independently, so a stale list + // would otherwise drive a false lost-org blank in the org fence. + queryClient.clear(); setToken(tokenValue); // A direct account switch must not keep the prior account's session // state: trusted hosts, image confirms, media caches, temp copies. @@ -264,6 +272,9 @@ export function AuthProvider({ children }: { readonly children: ReactNode }) { // the read-cache mount unsubscribes and cannot resubscribe while the old // user id is still cached. setSignOutActive(true); + // Blank the glanceable surface synchronously, before the first await, so + // widgets/activities never outlive the session. + writeSignedOutSnapshotAndEnd(); // Drop an account-bound pending deep-link destination synchronously, // before the first await, so a different account signed in later in this // process cannot navigate to the previous account's destination. A diff --git a/apps/mobile/src/lib/glanceable/cleanup.test.ts b/apps/mobile/src/lib/glanceable/cleanup.test.ts new file mode 100644 index 0000000000..00693ce963 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/cleanup.test.ts @@ -0,0 +1,182 @@ +import { afterEach, describe, expect, it } from 'vitest'; + +import { type GlanceableAgentsSnapshot } from '@kilocode/app-shared/glanceable-agents-snapshot'; + +import { + planOrgFenceAction, + republishLastSnapshotStale, + writePrivacySnapshotAndEnd, + writeSignedOutSnapshotAndEnd, +} from './cleanup'; +import { _resetGlanceablePersistForTests, _setLastGlanceableSnapshotForTests } from './persist'; +import { + type GlanceableSink, + registerGlanceableSink, + unregisterGlanceableSink, +} from './sink-registry'; + +type SinkCall = + | { type: 'publish'; snapshot: GlanceableAgentsSnapshot } + | { type: 'startOrUpdate'; snapshot: GlanceableAgentsSnapshot } + | { type: 'endImmediate' }; + +function makeSink() { + const calls: SinkCall[] = []; + const sink: GlanceableSink = { + publish(snapshot) { + calls.push({ type: 'publish', snapshot }); + }, + startOrUpdate(snapshot) { + calls.push({ type: 'startOrUpdate', snapshot }); + }, + endImmediate() { + calls.push({ type: 'endImmediate' }); + }, + }; + return { sink, calls }; +} + +function lastSnapshot(calls: SinkCall[]): GlanceableAgentsSnapshot { + const found = [...calls].toReversed().find(call => call.type === 'publish'); + if (found === undefined) { + throw new Error('no publish call'); + } + return found.snapshot; +} + +afterEach(() => { + _resetGlanceablePersistForTests(); +}); + +describe('cleanup', () => { + it('writes the signed-out snapshot before ending, and skips the terminal wait', () => { + const { sink, calls } = makeSink(); + registerGlanceableSink(sink); + try { + writeSignedOutSnapshotAndEnd(); + expect(calls.map(call => call.type)).toEqual(['publish', 'endImmediate']); + const snapshot = lastSnapshot(calls); + expect(snapshot.status).toBe('signed_out'); + expect(snapshot.running + snapshot.needsInput + snapshot.reconnecting).toBe(0); + } finally { + unregisterGlanceableSink(sink); + } + }); + + it('blanks to privacy on org switch', () => { + const { sink, calls } = makeSink(); + registerGlanceableSink(sink); + try { + writePrivacySnapshotAndEnd(); + const snapshot = lastSnapshot(calls); + expect(snapshot.status).toBe('privacy'); + expect(snapshot.running + snapshot.needsInput + snapshot.reconnecting).toBe(0); + } finally { + unregisterGlanceableSink(sink); + } + }); + + it('blanks to privacy only after a successful list misses the selection', () => { + expect( + planOrgFenceAction({ + organizationId: 'missing-org', + orgs: [{ organizationId: 'kept-org' }], + isLoading: false, + isError: false, + }) + ).toBe('privacy'); + expect( + planOrgFenceAction({ + organizationId: 'missing-org', + orgs: [{ organizationId: 'kept-org' }], + isLoading: true, + isError: false, + }) + ).toBe('none'); + expect( + planOrgFenceAction({ + organizationId: 'kept-org', + orgs: [{ organizationId: 'kept-org' }], + isLoading: false, + isError: false, + }) + ).toBe('none'); + expect( + planOrgFenceAction({ organizationId: null, orgs: [], isLoading: false, isError: false }) + ).toBe('none'); + // An offline or not-yet-fetched list (orgs undefined) is not a lost org. + expect( + planOrgFenceAction({ + organizationId: 'kept-org', + orgs: undefined, + isLoading: false, + isError: false, + }) + ).toBe('none'); + }); + + it('marks stale (keeps counts) when the org list errors', () => { + expect( + planOrgFenceAction({ + organizationId: 'kept-org', + orgs: undefined, + isLoading: false, + isError: true, + }) + ).toBe('stale'); + + const seeded: GlanceableAgentsSnapshot = { + schemaVersion: 1, + revision: 3, + updatedAt: '2026-08-27T00:00:00.000Z', + expiresAt: '2026-08-27T08:00:00.000Z', + scopeKey: 'deadbeef', + organizationBound: false, + status: 'happy', + running: 2, + needsInput: 1, + reconnecting: 0, + eligibleStartedAt: '2026-08-26T23:00:00.000Z', + }; + _setLastGlanceableSnapshotForTests(seeded); + + const { sink, calls } = makeSink(); + registerGlanceableSink(sink); + try { + republishLastSnapshotStale(); + const snapshot = lastSnapshot(calls); + expect(snapshot.status).toBe('stale'); + expect(snapshot.running).toBe(2); + expect(snapshot.needsInput).toBe(1); + expect(snapshot.revision).toBe(4); + } finally { + unregisterGlanceableSink(sink); + } + }); + + it('does not overwrite a terminal blank with a stale republish', () => { + const terminal: GlanceableAgentsSnapshot = { + schemaVersion: 1, + revision: 5, + updatedAt: '2026-08-27T00:00:00.000Z', + expiresAt: '2026-08-27T08:00:00.000Z', + scopeKey: 'terminal:privacy', + organizationBound: false, + status: 'privacy', + running: 0, + needsInput: 0, + reconnecting: 0, + eligibleStartedAt: null, + }; + _setLastGlanceableSnapshotForTests(terminal); + + const { sink, calls } = makeSink(); + registerGlanceableSink(sink); + try { + republishLastSnapshotStale(); + expect(calls).toEqual([]); + } finally { + unregisterGlanceableSink(sink); + } + }); +}); diff --git a/apps/mobile/src/lib/glanceable/cleanup.ts b/apps/mobile/src/lib/glanceable/cleanup.ts new file mode 100644 index 0000000000..57f985a4a2 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/cleanup.ts @@ -0,0 +1,127 @@ +import { + GLANCEABLE_SNAPSHOT_EXPIRY_MS, + GLANCEABLE_SNAPSHOT_SCHEMA_VERSION, + type GlanceableAgentsSnapshot, +} from '@kilocode/app-shared/glanceable-agents-snapshot'; + +import { getLastGlanceableSnapshot } from './persist'; +import { getGlanceableSinks } from './sink-registry'; + +// Monotonic epoch bumped on every terminal blank (signed-out or privacy). The +// publisher captures it at construction and refuses to emit once it advances, +// so a live cache success after a blank can never republish or restart. +let terminalBlankEpoch = 0; + +/** Current terminal-blank epoch; the publisher compares it to its start value. */ +export function getTerminalBlankEpoch(): number { + return terminalBlankEpoch; +} + +/** + * Terminal blanking: signed-out and privacy states are written to every sink + * (publish) and then every sink ends immediately. The 8 s terminal window is + * skipped — logout, account switch, org switch, and confirmed lost org must + * blank at once. + */ + +export type GlanceableOrgFenceState = { + organizationId: string | null; + orgs: readonly { organizationId: string }[] | undefined; + isLoading: boolean; + isError: boolean; +}; + +export type GlanceableOrgFenceAction = 'privacy' | 'stale' | 'none'; + +/** Pure org-fence decision: lost org only after a successful list misses the selection. */ +export function planOrgFenceAction(state: GlanceableOrgFenceState): GlanceableOrgFenceAction { + if (state.isLoading) { + return 'none'; + } + if (state.isError) { + return 'stale'; + } + if ( + state.organizationId !== null && + state.orgs !== undefined && + !state.orgs.some(entry => entry.organizationId === state.organizationId) + ) { + return 'privacy'; + } + return 'none'; +} + +function buildTerminalSnapshot(status: 'signed_out' | 'privacy'): GlanceableAgentsSnapshot { + const previous = getLastGlanceableSnapshot(); + const now = Date.now(); + const updatedAt = new Date(now).toISOString(); + return { + schemaVersion: GLANCEABLE_SNAPSHOT_SCHEMA_VERSION, + revision: (previous?.revision ?? 0) + 1, + updatedAt, + expiresAt: new Date(now + GLANCEABLE_SNAPSHOT_EXPIRY_MS).toISOString(), + // A terminal scope never matches a real push scope, so a late push for the + // old account cannot resurrect the old surface. + scopeKey: `terminal:${status}`, + organizationBound: false, + status, + running: 0, + needsInput: 0, + reconnecting: 0, + eligibleStartedAt: null, + }; +} + +function writeTerminalAndEnd(status: 'signed_out' | 'privacy'): void { + // Arm the publisher gate before any sink writes, so a cache success that + // lands during this window can never emit for the torn-down session. + terminalBlankEpoch += 1; + const snapshot = buildTerminalSnapshot(status); + const sinks = getGlanceableSinks(); + // Write the snapshot first, then end: the surface shows the terminal copy + // before the native activity ends. + for (const sink of sinks) { + sink.publish(snapshot); + } + for (const sink of sinks) { + sink.endImmediate(); + } +} + +/** Blank on logout or direct account switch. */ +export function writeSignedOutSnapshotAndEnd(): void { + writeTerminalAndEnd('signed_out'); +} + +/** Blank on org switch or confirmed lost org. */ +export function writePrivacySnapshotAndEnd(): void { + writeTerminalAndEnd('privacy'); +} + +/** + * Republish the last snapshot with a stale status (keeps counts). Used by the + * org fence when the org list errors: stale, not lost-org. + */ +export function republishLastSnapshotStale(): void { + const previous = getLastGlanceableSnapshot(); + if (previous === null) { + return; + } + // A terminal blank must keep its status: a stale republish would replace the + // signed-out or privacy copy with "Can't update now". + if (previous.status === 'signed_out' || previous.status === 'privacy') { + return; + } + const now = Date.now(); + const updatedAt = new Date(now).toISOString(); + const snapshot: GlanceableAgentsSnapshot = { + ...previous, + revision: previous.revision + 1, + updatedAt, + expiresAt: new Date(now + GLANCEABLE_SNAPSHOT_EXPIRY_MS).toISOString(), + status: 'stale', + }; + for (const sink of getGlanceableSinks()) { + sink.publish(snapshot); + } +} diff --git a/apps/mobile/src/lib/glanceable/mount.tsx b/apps/mobile/src/lib/glanceable/mount.tsx new file mode 100644 index 0000000000..f50680f6a9 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/mount.tsx @@ -0,0 +1,107 @@ +import { hashKey, useQueryClient } from '@tanstack/react-query'; +import { useEffect, useMemo, useState } from 'react'; + +import { + buildActiveSessionsTrayInput, + type CachedActiveSessionsData, +} from '@/lib/active-sessions-live'; +import { useAuth } from '@/lib/auth/auth-context'; +import { useCurrentUserId } from '@/lib/hooks/use-current-user-id'; +import { useOrganization } from '@/lib/organization-context'; +import { useTRPC } from '@/lib/trpc'; + +import { + getLastGlanceableSnapshot, + persistGlanceableSink, + restorePersistedGlanceable, +} from './persist'; +import { getTerminalBlankEpoch } from './cleanup'; +import { GlanceablePublisher } from './publisher'; +import { getGlanceableSinks, registerGlanceableSink } from './sink-registry'; + +// Register only the persist sink here; platform sinks register themselves from +// files their slices own. +registerGlanceableSink(persistGlanceableSink); + +/** + * React entry point for the glanceable publisher. Subscribes to the + * `activeSessions.list` tray cache (the same key the live-sync owner writes) + * and derives snapshots without fetching. A fresh publisher per signed-in + * context, mirroring `ActiveSessionsLiveSyncMount`. + */ +export function GlanceablePublisherMount(): null { + const trpc = useTRPC(); + const queryClient = useQueryClient(); + const { organizationId, isLoaded } = useOrganization(); + const { token } = useAuth(); + const { userId } = useCurrentUserId(); + + const input = useMemo(() => buildActiveSessionsTrayInput(organizationId), [organizationId]); + const queryKey = useMemo(() => trpc.activeSessions.list.queryKey(input), [trpc, input]); + const targetHash = useMemo(() => hashKey(queryKey), [queryKey]); + + const signedIn = token != null; + + // Populate the persisted last snapshot once so cleanup/org-fence can see it, + // and so the publisher below seeds its revision from the persisted value. + const [restored, setRestored] = useState(false); + useEffect(() => { + let cancelled = false; + const restore = async (): Promise => { + await restorePersistedGlanceable(); + if (!cancelled) { + setRestored(true); + } + }; + void restore(); + return () => { + cancelled = true; + }; + }, []); + + useEffect(() => { + if (!isLoaded || !signedIn || userId === undefined || !restored) { + return undefined; + } + + const publisher = new GlanceablePublisher({ + sinks: getGlanceableSinks(), + initial: getLastGlanceableSnapshot(), + terminalBlankEpoch: getTerminalBlankEpoch, + }); + const ctx = { userId, organizationId }; + + // Initial state: derive from the existing cache, or mark waiting while the + // first fetch is in flight. + const state = queryClient.getQueryState(queryKey); + const data = queryClient.getQueryData(queryKey); + if (data !== undefined) { + publisher.handleSessions(data.sessions, ctx); + } else if (state?.fetchStatus === 'fetching') { + publisher.handleFetchStarted(ctx); + } + + const unsubscribe = queryClient.getQueryCache().subscribe(event => { + if (event.type !== 'updated' || event.query.queryHash !== targetHash) { + return; + } + if (event.action.type === 'success') { + const next = queryClient.getQueryData(queryKey); + if (next !== undefined) { + publisher.handleSessions(next.sessions, ctx); + } + } else if (event.action.type === 'error') { + publisher.handleFetchError(ctx); + } else if (event.action.type === 'fetch') { + publisher.handleFetchStarted(ctx); + } + }); + + return () => { + unsubscribe(); + publisher.dispose(); + }; + }, [queryClient, queryKey, targetHash, isLoaded, signedIn, userId, organizationId, restored]); + + return null; +} diff --git a/apps/mobile/src/lib/glanceable/open-agents.ts b/apps/mobile/src/lib/glanceable/open-agents.ts new file mode 100644 index 0000000000..2c180b062a --- /dev/null +++ b/apps/mobile/src/lib/glanceable/open-agents.ts @@ -0,0 +1,12 @@ +import { setPendingDeepLink } from '@/lib/deep-link-launch'; + +/** + * The one safe Open agents action: stash the Agents tab destination through + * the existing pending-deep-link gate, which runs after auth and startup + * gates clear. `universal-link` always wins over a stale notification + * response, so a user tap takes precedence. Do not call `router.navigate` + * here — navigation must flow through the gate, not bypass it. + */ +export function openGlanceableAgents(): void { + setPendingDeepLink('/(app)/(tabs)/(2_agents)', 'universal-link'); +} diff --git a/apps/mobile/src/lib/glanceable/org-fence.ts b/apps/mobile/src/lib/glanceable/org-fence.ts new file mode 100644 index 0000000000..960a5562b1 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/org-fence.ts @@ -0,0 +1,42 @@ +import { useQuery } from '@tanstack/react-query'; +import { useEffect } from 'react'; + +import { useAuth } from '@/lib/auth/auth-context'; +import { useOrganization } from '@/lib/organization-context'; +import { useTRPC } from '@/lib/trpc'; + +import { + planOrgFenceAction, + republishLastSnapshotStale, + writePrivacySnapshotAndEnd, +} from './cleanup'; +import { getLastGlanceableSnapshot } from './persist'; + +/** + * Org fence: a selected organization that is missing from a successful + * `organizations.list` is a confirmed lost org and blanks the surface. A + * loading or errored list never blanks; an error only marks the last snapshot + * stale. + */ +export function useGlanceableOrgFence(): void { + const trpc = useTRPC(); + const { token } = useAuth(); + const { organizationId } = useOrganization(); + const { + data: orgs, + isLoading, + isError, + } = useQuery({ + ...trpc.organizations.list.queryOptions(), + enabled: token != null, + }); + + useEffect(() => { + const action = planOrgFenceAction({ organizationId, orgs, isLoading, isError }); + if (action === 'privacy') { + writePrivacySnapshotAndEnd(); + } else if (action === 'stale' && getLastGlanceableSnapshot() !== null) { + republishLastSnapshotStale(); + } + }, [organizationId, orgs, isLoading, isError]); +} diff --git a/apps/mobile/src/lib/glanceable/persist.test.ts b/apps/mobile/src/lib/glanceable/persist.test.ts new file mode 100644 index 0000000000..86491fa7fe --- /dev/null +++ b/apps/mobile/src/lib/glanceable/persist.test.ts @@ -0,0 +1,116 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { + buildGlanceableSnapshot, + type GlanceableAgentsSnapshot, +} from '@kilocode/app-shared/glanceable-agents-snapshot'; + +import { + _resetGlanceablePersistForTests, + _setSecureStoreForTests, + getLastGlanceableSnapshot, + getLocalScopeKey, + persistGlanceableSink, + restorePersistedGlanceable, +} from './persist'; + +const NOW = 1_750_000_000_000; +const SNAPSHOT_KEY = 'glanceable-snapshot'; +const SCOPE_KEY = 'glanceable-scope-key'; + +const store = new Map(); + +// Fake SecureStore surface backed by an in-memory Map, injected through the +// test-only setter so the durable mirror never loads the real native module. +const secureStoreMock = { + setItemAsync: vi.fn(async (key: string, value: string) => { + store.set(key, value); + await Promise.resolve(); + }), + getItemAsync: vi.fn(async (key: string) => { + await Promise.resolve(); + return store.get(key) ?? null; + }), +}; + +function snapshotFor(sessions: { status: string }[]): GlanceableAgentsSnapshot { + return buildGlanceableSnapshot({ + sessions, + userId: 'u1', + organizationId: null, + now: NOW, + }); +} + +function deferred(): { promise: Promise; resolve: () => void } { + let storedResolve: (() => void) | undefined = undefined; + const promise = new Promise(resolve => { + storedResolve = resolve; + }); + return { + promise, + resolve: () => { + storedResolve?.(); + }, + }; +} + +beforeEach(() => { + _resetGlanceablePersistForTests(); + _setSecureStoreForTests(secureStoreMock); + store.clear(); + vi.clearAllMocks(); +}); + +afterEach(() => { + _resetGlanceablePersistForTests(); + store.clear(); +}); + +describe('restorePersistedGlanceable', () => { + it('does not clobber a snapshot written after the restore read started', async () => { + const stale = snapshotFor([]); + const staleRaw = JSON.stringify(stale); + store.set(SNAPSHOT_KEY, staleRaw); + store.set(SCOPE_KEY, 'stale-scope'); + + // Hold the restore read open so a live publish can land mid-read. + const gate = deferred(); + secureStoreMock.getItemAsync.mockImplementationOnce(async () => { + await gate.promise; + return staleRaw; + }); + + const restorePromise = restorePersistedGlanceable(); + + // A live publish lands while the persisted read is still pending. + const fresh = snapshotFor([{ status: 'busy' }]); + persistGlanceableSink.publish(fresh); + + gate.resolve(); + await restorePromise; + + expect(getLastGlanceableSnapshot()).toEqual(fresh); + expect(getLocalScopeKey()).toBe(fresh.scopeKey); + }); + + it('rejects a malformed stored record and keeps no snapshot', async () => { + store.set(SNAPSHOT_KEY, JSON.stringify({ schemaVersion: 1, revision: 'nope' })); + store.set(SCOPE_KEY, 'scope'); + + await restorePersistedGlanceable(); + + expect(getLastGlanceableSnapshot()).toBeNull(); + }); + + it('restores a schema-valid stored record', async () => { + const stored = snapshotFor([{ status: 'busy' }]); + store.set(SNAPSHOT_KEY, JSON.stringify(stored)); + store.set(SCOPE_KEY, stored.scopeKey); + + await restorePersistedGlanceable(); + + expect(getLastGlanceableSnapshot()).toEqual(stored); + expect(getLocalScopeKey()).toBe(stored.scopeKey); + }); +}); diff --git a/apps/mobile/src/lib/glanceable/persist.ts b/apps/mobile/src/lib/glanceable/persist.ts new file mode 100644 index 0000000000..5263448f54 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/persist.ts @@ -0,0 +1,139 @@ +import { + type GlanceableAgentsSnapshot, + glanceableAgentsSnapshotSchema, +} from '@kilocode/app-shared/glanceable-agents-snapshot'; + +import { type GlanceableSink } from './sink-registry'; + +/** + * Durable mirror of the last glanceable snapshot and its scope key, for JS + * restart fencing only. The snapshot holds generic status, counts, timestamps + * and an opaque scope key — never titles, ids, or other raw content. + * + * iOS widgets read the snapshot through expo-widgets `updateSnapshot` / + * `updateTimeline`; Android widgets read through `react-native-android-widget` + * storage. This store exists so the background push handler can compare an + * incoming scope key without React context. + */ + +// SecureStore keys are defined here (not storage-keys.ts) so this module stays +// self-contained; nothing else owns these two keys. +const GLANCEABLE_SNAPSHOT_KEY = 'glanceable-snapshot'; +const GLANCEABLE_SCOPE_KEY = 'glanceable-scope-key'; + +type SecureStoreLike = { + setItemAsync: (key: string, value: string) => Promise; + getItemAsync: (key: string) => Promise; +}; + +// Test-only override so pure suites do not load expo-secure-store +// (→ expo-modules-core → RN). Mirrors the deep-link-launch pattern. +let secureStoreForTests: SecureStoreLike | null = null; + +function getSecureStore(): SecureStoreLike { + if (secureStoreForTests) { + return secureStoreForTests; + } + // eslint-disable-next-line typescript-eslint/no-require-imports, typescript-eslint/no-var-requires, unicorn/prefer-module -- lazy native load + return require('expo-secure-store') as SecureStoreLike; +} + +let lastSnapshot: GlanceableAgentsSnapshot | null = null; +let localScopeKey: string | null = null; + +// Monotonic epoch bumped on every in-memory write. Restore captures it before +// its async read and only fills when it is unchanged, so a live publish during +// the read can never be clobbered by a stale persisted record. +let persistEpoch = 0; + +export function getLastGlanceableSnapshot(): GlanceableAgentsSnapshot | null { + return lastSnapshot; +} + +export function getLocalScopeKey(): string | null { + return localScopeKey; +} + +/** In-memory write plus a fire-and-forget SecureStore mirror. */ +function persistSnapshot(snapshot: GlanceableAgentsSnapshot): void { + persistEpoch += 1; + lastSnapshot = snapshot; + localScopeKey = snapshot.scopeKey; + void getSecureStore().setItemAsync(GLANCEABLE_SNAPSHOT_KEY, JSON.stringify(snapshot)); + void getSecureStore().setItemAsync(GLANCEABLE_SCOPE_KEY, snapshot.scopeKey); +} + +/** Parse a stored record with the shared schema; a malformed record is absent. */ +function parseStoredSnapshot(raw: string): GlanceableAgentsSnapshot | null { + try { + const parsed: unknown = JSON.parse(raw); + const result = glanceableAgentsSnapshotSchema.safeParse(parsed); + return result.success ? result.data : null; + } catch { + return null; + } +} + +/** + * Restore the in-memory state from SecureStore after a JS restart. Best + * effort: a failed read keeps the null in-memory state. A live write during + * the read owns the state, so the stale persisted record is skipped. + */ +export async function restorePersistedGlanceable(): Promise { + const startEpoch = persistEpoch; + try { + const [rawSnapshot, rawScope] = await Promise.all([ + getSecureStore().getItemAsync(GLANCEABLE_SNAPSHOT_KEY), + getSecureStore().getItemAsync(GLANCEABLE_SCOPE_KEY), + ]); + // A live write landed during the read: it owns the state; skip the fill. + if (persistEpoch !== startEpoch) { + return; + } + if (rawSnapshot !== null) { + const parsed = parseStoredSnapshot(rawSnapshot); + if (parsed !== null) { + lastSnapshot = parsed; + } + } + if (rawScope !== null) { + localScopeKey = rawScope; + } + } catch { + // A malformed mirror is treated as absent; the publisher repopulates it. + } +} + +/** The persist sink owns no native surface, so endImmediate is a no-op. */ +export const persistGlanceableSink: GlanceableSink = { + publish(snapshot) { + persistSnapshot(snapshot); + }, + endImmediate() { + // The widget snapshot stays for later reads; nothing to end. + }, + startOrUpdate(snapshot) { + persistSnapshot(snapshot); + }, +}; + +// ── Test-only helpers ────────────────────────────────────────────────────── + +export function _setSecureStoreForTests(store: SecureStoreLike | null): void { + secureStoreForTests = store; +} + +export function _setLastGlanceableSnapshotForTests( + snapshot: GlanceableAgentsSnapshot | null +): void { + persistEpoch += 1; + lastSnapshot = snapshot; + localScopeKey = snapshot?.scopeKey ?? null; +} + +export function _resetGlanceablePersistForTests(): void { + persistEpoch = 0; + lastSnapshot = null; + localScopeKey = null; + secureStoreForTests = null; +} diff --git a/apps/mobile/src/lib/glanceable/presentation.test.ts b/apps/mobile/src/lib/glanceable/presentation.test.ts new file mode 100644 index 0000000000..87177b1294 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/presentation.test.ts @@ -0,0 +1,108 @@ +import { describe, expect, it } from 'vitest'; + +import { + buildGlanceableSnapshot, + type GlanceableAgentsSnapshot, +} from '@kilocode/app-shared/glanceable-agents-snapshot'; + +import { + glanceableSpokenLabelKeys, + glanceableStatusCopyKey, + primaryGlanceableCount, + resolveGlanceableStatus, +} from './presentation'; + +const NOW = 1_750_000_000_000; + +function snapshot(overrides: { + sessions?: { status: string }[]; + status?: GlanceableAgentsSnapshot['status']; +}): GlanceableAgentsSnapshot { + return buildGlanceableSnapshot({ + sessions: overrides.sessions ?? [], + userId: 'u1', + organizationId: null, + now: NOW, + status: overrides.status, + }); +} + +describe('presentation precedence', () => { + it('signed-out flag wins over org-invalid and the snapshot status', () => { + expect(resolveGlanceableStatus(snapshot({ status: 'happy' }), { signedOut: true })).toBe( + 'signed_out' + ); + expect( + resolveGlanceableStatus(snapshot({ status: 'happy' }), { signedOut: true, orgInvalid: true }) + ).toBe('signed_out'); + }); + + it('org-invalid flag maps to privacy over the snapshot status', () => { + expect(resolveGlanceableStatus(snapshot({ status: 'stale' }), { orgInvalid: true })).toBe( + 'privacy' + ); + }); + + it('falls back to the snapshot status when no flag is set', () => { + expect(resolveGlanceableStatus(snapshot({ status: 'waiting' }))).toBe('waiting'); + expect(resolveGlanceableStatus(snapshot({ status: 'happy' }))).toBe('happy'); + }); +}); + +describe('primary rank and locked copy keys', () => { + it('ranks needs-input, then reconnecting, then running', () => { + const mixed = snapshot({ + sessions: [ + { status: 'busy' }, + { status: 'busy' }, + { status: 'busy' }, + { status: 'retry' }, + { status: 'question' }, + ], + }); + expect(primaryGlanceableCount(mixed)).toEqual({ key: 'glanceable.needsInput', count: 1 }); + + const noInput = snapshot({ sessions: [{ status: 'busy' }, { status: 'retry' }] }); + expect(primaryGlanceableCount(noInput)).toEqual({ key: 'glanceable.reconnecting', count: 1 }); + + const onlyRunning = snapshot({ sessions: [{ status: 'busy' }, { status: 'busy' }] }); + expect(primaryGlanceableCount(onlyRunning)).toEqual({ key: 'glanceable.running', count: 2 }); + + expect(primaryGlanceableCount(snapshot({}))).toBeNull(); + }); + + it('maps each non-happy status to its locked copy key', () => { + expect(glanceableStatusCopyKey(snapshot({ status: 'waiting' }))).toBe('glanceable.waiting'); + expect(glanceableStatusCopyKey(snapshot({ status: 'empty' }))).toBe('glanceable.empty'); + expect(glanceableStatusCopyKey(snapshot({ status: 'stale' }))).toBe('glanceable.stale'); + expect(glanceableStatusCopyKey(snapshot({ status: 'expired' }))).toBe('glanceable.expired'); + expect(glanceableStatusCopyKey(snapshot({ status: 'signed_out' }))).toBe( + 'glanceable.signedOut' + ); + expect(glanceableStatusCopyKey(snapshot({ status: 'privacy' }))).toBe('glanceable.privacy'); + expect(glanceableStatusCopyKey(snapshot({ status: 'happy' }))).toBeNull(); + }); +}); + +describe('spoken label shape', () => { + it('speaks counts then Open agents for happy, never a title or id', () => { + const happy = snapshot({ sessions: [{ status: 'busy' }, { status: 'question' }] }); + expect(glanceableSpokenLabelKeys(happy)).toEqual([ + 'glanceable.needsInput', + 'glanceable.running', + 'glanceable.openAgents', + ]); + expect(glanceableSpokenLabelKeys(happy).join(' ')).not.toContain('u1'); + }); + + it('speaks the status word then Open agents for non-happy statuses', () => { + expect(glanceableSpokenLabelKeys(snapshot({ status: 'empty' }))).toEqual([ + 'glanceable.empty', + 'glanceable.openAgents', + ]); + expect(glanceableSpokenLabelKeys(snapshot({ status: 'signed_out' }))).toEqual([ + 'glanceable.signedOut', + 'glanceable.openAgents', + ]); + }); +}); diff --git a/apps/mobile/src/lib/glanceable/presentation.ts b/apps/mobile/src/lib/glanceable/presentation.ts new file mode 100644 index 0000000000..5935d60d46 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/presentation.ts @@ -0,0 +1,107 @@ +import { type GlanceableAgentsSnapshot } from '@kilocode/app-shared/glanceable-agents-snapshot'; + +/** + * Maps the snapshot plus optional surface flags to the locked copy keys, the + * compact primary count, and the spoken-label shape. Precedence: + * signed-out, then privacy/lost-org, then the snapshot's own status. + */ + +export type GlanceableStatus = GlanceableAgentsSnapshot['status']; + +/** Locked copy key per non-happy status. Happy shows counts, not a status line. */ +export const GLANCEABLE_STATUS_COPY_KEY = { + waiting: 'glanceable.waiting', + empty: 'glanceable.empty', + stale: 'glanceable.stale', + expired: 'glanceable.expired', + signed_out: 'glanceable.signedOut', + privacy: 'glanceable.privacy', +} as const satisfies Record, string>; + +export type GlanceableCountKey = + | 'glanceable.running' + | 'glanceable.needsInput' + | 'glanceable.reconnecting'; + +export type GlanceableCountLine = { key: GlanceableCountKey; count: number }; + +/** Rank order: needs-input, then reconnecting, then running. */ +const COUNT_ORDER: readonly { + key: GlanceableCountKey; + field: 'running' | 'needsInput' | 'reconnecting'; +}[] = [ + { key: 'glanceable.needsInput', field: 'needsInput' }, + { key: 'glanceable.reconnecting', field: 'reconnecting' }, + { key: 'glanceable.running', field: 'running' }, +]; + +/** Every non-zero count in rank order (expanded, medium, large, spoken). */ +export function glanceableCountLines(snapshot: GlanceableAgentsSnapshot): GlanceableCountLine[] { + const lines: GlanceableCountLine[] = []; + for (const { key, field } of COUNT_ORDER) { + const count = snapshot[field]; + if (count > 0) { + lines.push({ key, count }); + } + } + return lines; +} + +/** The single ranked count for compact surfaces; null when nothing is eligible. */ +export function primaryGlanceableCount( + snapshot: GlanceableAgentsSnapshot +): GlanceableCountLine | null { + return glanceableCountLines(snapshot)[0] ?? null; +} + +export type GlanceableSurfaceFlags = { + /** Forced by the auth context when signed out. */ + signedOut?: boolean; + /** Forced by the org fence when the selected org is no longer in the list. */ + orgInvalid?: boolean; +}; + +/** Resolve the display status under the surface precedence list. */ +export function resolveGlanceableStatus( + snapshot: GlanceableAgentsSnapshot, + flags: GlanceableSurfaceFlags = {} +): GlanceableStatus { + if (flags.signedOut) { + return 'signed_out'; + } + if (flags.orgInvalid) { + return 'privacy'; + } + return snapshot.status; +} + +/** The top-line copy key for the snapshot, or null for happy (counts only). */ +export function glanceableStatusCopyKey( + snapshot: GlanceableAgentsSnapshot, + flags: GlanceableSurfaceFlags = {} +): string | null { + const status = resolveGlanceableStatus(snapshot, flags); + return status === 'happy' ? null : GLANCEABLE_STATUS_COPY_KEY[status]; +} + +/** + * Ordered spoken-label parts: status words, counts, then Open agents. Never a + * title, organization name, or id. Each part is a copy key the surface + * resolves to its translated string. + */ +export function glanceableSpokenLabelKeys( + snapshot: GlanceableAgentsSnapshot, + flags: GlanceableSurfaceFlags = {} +): string[] { + const status = resolveGlanceableStatus(snapshot, flags); + const parts: string[] = []; + if (status === 'happy' || status === 'stale') { + for (const { key } of glanceableCountLines(snapshot)) { + parts.push(key); + } + } else { + parts.push(GLANCEABLE_STATUS_COPY_KEY[status]); + } + parts.push('glanceable.openAgents'); + return parts; +} diff --git a/apps/mobile/src/lib/glanceable/publisher.test.ts b/apps/mobile/src/lib/glanceable/publisher.test.ts new file mode 100644 index 0000000000..a470bdf8c9 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/publisher.test.ts @@ -0,0 +1,243 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { + buildGlanceableSnapshot, + GLANCEABLE_SNAPSHOT_EXPIRY_MS, + type GlanceableAgentsSnapshot, +} from '@kilocode/app-shared/glanceable-agents-snapshot'; + +import { getTerminalBlankEpoch, writeSignedOutSnapshotAndEnd } from './cleanup'; +import { GlanceablePublisher } from './publisher'; +import { + type GlanceableSink, + type GlanceableSinkContext, + registerGlanceableSink, + unregisterGlanceableSink, +} from './sink-registry'; + +const NOW = 1_750_000_000_000; +const PUB_CTX = { userId: 'u1', organizationId: null }; + +type SinkCall = + | { type: 'publish'; snapshot: GlanceableAgentsSnapshot } + | { type: 'startOrUpdate'; snapshot: GlanceableAgentsSnapshot; ctx: GlanceableSinkContext } + | { type: 'endImmediate' }; + +function makeSink() { + const calls: SinkCall[] = []; + const sink: GlanceableSink = { + publish(snapshot) { + calls.push({ type: 'publish', snapshot }); + }, + startOrUpdate(snapshot, ctx) { + calls.push({ type: 'startOrUpdate', snapshot, ctx }); + }, + endImmediate() { + calls.push({ type: 'endImmediate' }); + }, + }; + return { sink, calls }; +} + +function count(calls: SinkCall[], type: SinkCall['type']): number { + return calls.filter(call => call.type === type).length; +} + +function lastSnapshot( + calls: SinkCall[], + type: 'publish' | 'startOrUpdate' +): GlanceableAgentsSnapshot { + const found = [...calls].toReversed().find(call => call.type === type); + if (found === undefined) { + throw new Error(`no ${type} call`); + } + return (found as { snapshot: GlanceableAgentsSnapshot }).snapshot; +} + +function snapshotFor(sessions: { status: string }[], now: number, revision = 0) { + return buildGlanceableSnapshot({ + sessions, + userId: 'u1', + organizationId: null, + now, + previousRevision: revision, + }); +} + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('GlanceablePublisher', () => { + it('derives the count map from the session rows', () => { + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.handleSessions( + [ + { status: 'busy' }, + { status: 'busy' }, + { status: 'question' }, + { status: 'retry' }, + { status: 'idle' }, + ], + PUB_CTX + ); + const snapshot = lastSnapshot(calls, 'startOrUpdate'); + expect(snapshot.running).toBe(2); + expect(snapshot.needsInput).toBe(1); + expect(snapshot.reconnecting).toBe(1); + expect(snapshot.status).toBe('happy'); + }); + + it('starts the activity immediately on the first eligible emit', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(1); + publisher.dispose(); + }); + + it('coalesces later happy updates and emits only the latest', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW, coalesceMs: 1000 }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + publisher.handleSessions([{ status: 'busy' }, { status: 'busy' }], PUB_CTX); + publisher.handleSessions([{ status: 'busy' }, { status: 'busy' }, { status: 'busy' }], PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(1); + vi.advanceTimersByTime(1000); + expect(count(calls, 'startOrUpdate')).toBe(2); + expect(lastSnapshot(calls, 'startOrUpdate').running).toBe(3); + publisher.dispose(); + }); + + it('discards an incoming older revision', () => { + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.applySnapshot(snapshotFor([{ status: 'busy' }], NOW, 4), PUB_CTX); + const started = count(calls, 'startOrUpdate'); + publisher.applySnapshot(snapshotFor([{ status: 'busy' }], NOW, 2), PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(started); + }); + + it('publishes empty for idle-only sessions without starting or ending', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.handleSessions([{ status: 'idle' }, { status: 'idle' }], PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(0); + expect(lastSnapshot(calls, 'publish').status).toBe('empty'); + vi.advanceTimersByTime(8000); + expect(count(calls, 'endImmediate')).toBe(0); + publisher.dispose(); + }); + + it('distinguishes waiting (first fetch) from empty (fetch settled)', () => { + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.handleFetchStarted(PUB_CTX); + expect(lastSnapshot(calls, 'publish').status).toBe('waiting'); + expect(count(calls, 'startOrUpdate')).toBe(0); + publisher.handleSessions([{ status: 'idle' }], PUB_CTX); + expect(lastSnapshot(calls, 'publish').status).toBe('empty'); + }); + + it('keeps counts on stale and hides counts on expired', () => { + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + publisher.handleFetchError(PUB_CTX); + expect(lastSnapshot(calls, 'publish').status).toBe('stale'); + expect(lastSnapshot(calls, 'publish').running).toBe(1); + + let now = NOW; + const { sink: sink2, calls: calls2 } = makeSink(); + const publisher2 = new GlanceablePublisher({ sinks: [sink2], now: () => now }); + publisher2.handleSessions([{ status: 'busy' }], PUB_CTX); + now = NOW + GLANCEABLE_SNAPSHOT_EXPIRY_MS; + publisher2.handleSessions([{ status: 'busy' }], PUB_CTX); + const expired = calls2.filter( + (call): call is { type: 'publish'; snapshot: GlanceableAgentsSnapshot } => + call.type === 'publish' && call.snapshot.status === 'expired' + ); + expect(expired.length).toBe(1); + expect(expired[0]?.snapshot.running).toBe(0); + }); + + it('does not schedule the 8s terminal for a signed-out snapshot', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.applySnapshot( + buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW, + status: 'signed_out', + }), + PUB_CTX + ); + vi.advanceTimersByTime(8000); + expect(count(calls, 'endImmediate')).toBe(0); + publisher.dispose(); + }); + + it('does not publish or restart after a terminal blank', () => { + const { sink, calls } = makeSink(); + registerGlanceableSink(sink); + const publisher = new GlanceablePublisher({ + sinks: [sink], + now: () => NOW, + terminalBlankEpoch: getTerminalBlankEpoch, + }); + try { + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(1); + expect(count(calls, 'publish')).toBe(1); + + writeSignedOutSnapshotAndEnd(); + expect(lastSnapshot(calls, 'publish').status).toBe('signed_out'); + expect(count(calls, 'endImmediate')).toBe(1); + + // A live cache success after the blank must not publish or restart. + publisher.handleSessions([{ status: 'busy' }, { status: 'busy' }], PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(1); + expect(count(calls, 'publish')).toBe(2); + expect(lastSnapshot(calls, 'publish').status).toBe('signed_out'); + } finally { + unregisterGlanceableSink(sink); + publisher.dispose(); + } + }); + + it('drops a pending coalesced emit after a terminal blank', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ + sinks: [sink], + now: () => NOW, + coalesceMs: 1000, + terminalBlankEpoch: getTerminalBlankEpoch, + }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + publisher.handleSessions([{ status: 'busy' }, { status: 'busy' }], PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(1); + + writeSignedOutSnapshotAndEnd(); + vi.advanceTimersByTime(1000); + expect(count(calls, 'startOrUpdate')).toBe(1); + publisher.dispose(); + }); + + it('keeps the revision monotonic when seeded from an initial snapshot', () => { + const { sink, calls } = makeSink(); + // Seeded with revision 42; the next snapshot must be 43. + const initial = snapshotFor([{ status: 'busy' }], NOW - 60_000, 41); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW, initial }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + expect(lastSnapshot(calls, 'startOrUpdate').revision).toBe(43); + publisher.dispose(); + }); +}); diff --git a/apps/mobile/src/lib/glanceable/publisher.ts b/apps/mobile/src/lib/glanceable/publisher.ts new file mode 100644 index 0000000000..af203542d7 --- /dev/null +++ b/apps/mobile/src/lib/glanceable/publisher.ts @@ -0,0 +1,260 @@ +import { + buildGlanceableSnapshot, + GLANCEABLE_COALESCE_MS, + GLANCEABLE_SNAPSHOT_EXPIRY_MS, + GLANCEABLE_TERMINAL_MS, + type GlanceableAgentsSnapshot, + type GlanceableAgentsSnapshotStatus, + isEligibleGlanceableWork, + shouldDiscardGlanceableRevision, +} from '@kilocode/app-shared/glanceable-agents-snapshot'; + +import { type GlanceableSink, type GlanceableSinkContext } from './sink-registry'; + +/** + * Framework-agnostic publisher state machine. Derives one versioned snapshot + * from the active-sessions tray cache, coalesces later happy updates, starts + * the activity on the first eligible emit, and schedules the 8 s terminal end + * when work becomes empty. The React glue is `mount.tsx`. + */ + +export type GlanceablePublisherContext = { + userId: string; + organizationId: string | null; +}; + +export type GlanceablePublisherOptions = { + sinks: readonly GlanceableSink[]; + /** Seeded from the persisted last snapshot so revision stays monotonic. */ + initial?: GlanceableAgentsSnapshot | null; + now?: () => number; + coalesceMs?: number; + terminalMs?: number; + /** + * Monotonic terminal-blank epoch reader (see cleanup). The publisher captures + * it at construction and refuses to emit once it advances, so a live cache + * success after a signed-out or privacy blank cannot republish or restart. + */ + terminalBlankEpoch?: () => number; +}; + +type TimerHandle = ReturnType; + +/** Copy a snapshot with a new status and a fresh revision/updatedAt/expiresAt. */ +export function withStatus( + snapshot: GlanceableAgentsSnapshot, + status: GlanceableAgentsSnapshotStatus, + now: number +): GlanceableAgentsSnapshot { + const updatedAt = new Date(now).toISOString(); + return { + ...snapshot, + revision: snapshot.revision + 1, + updatedAt, + expiresAt: new Date(now + GLANCEABLE_SNAPSHOT_EXPIRY_MS).toISOString(), + status, + }; +} + +export class GlanceablePublisher { + private readonly sinks: readonly GlanceableSink[]; + private readonly now: () => number; + private readonly coalesceMs: number; + private readonly terminalMs: number; + private readonly terminalBlankEpoch: () => number; + private readonly blankEpochAtStart: number; + private current: GlanceableAgentsSnapshot | null; + private activityStarted: boolean; + private coalesceTimer: TimerHandle | null = null; + private terminalTimer: TimerHandle | null = null; + private pendingCoalesced: { + snapshot: GlanceableAgentsSnapshot; + ctx: GlanceableSinkContext; + } | null = null; + + constructor(options: GlanceablePublisherOptions) { + this.sinks = options.sinks; + this.now = options.now ?? (() => Date.now()); + this.coalesceMs = options.coalesceMs ?? GLANCEABLE_COALESCE_MS; + this.terminalMs = options.terminalMs ?? GLANCEABLE_TERMINAL_MS; + this.terminalBlankEpoch = options.terminalBlankEpoch ?? (() => 0); + this.blankEpochAtStart = this.terminalBlankEpoch(); + this.current = options.initial ?? null; + this.activityStarted = false; + } + + /** Cache success: derive the next snapshot from the current session rows. */ + handleSessions(sessions: readonly { status: string }[], ctx: GlanceablePublisherContext): void { + if (this.isGated()) { + return; + } + const now = this.now(); + this.applyExpiry(now, ctx); + + const snapshot = buildGlanceableSnapshot({ + sessions, + userId: ctx.userId, + organizationId: ctx.organizationId, + now, + previousRevision: this.current?.revision ?? 0, + previousEligibleStartedAt: this.current?.eligibleStartedAt ?? null, + }); + + if (isEligibleGlanceableWork(snapshot)) { + this.cancelTerminal(); + if (!this.activityStarted) { + // First eligible emit starts the activity immediately, no coalesce wait. + this.emit(snapshot, ctx); + this.activityStarted = true; + } else { + this.scheduleCoalesced(snapshot, ctx); + } + } else { + this.cancelCoalesce(); + this.publish(snapshot); + if (this.activityStarted) { + // Happy → empty: terminal end after the brief empty window. + this.scheduleTerminal(); + } + this.activityStarted = false; + } + this.current = snapshot; + } + + /** First fetch in flight with no snapshot yet: waiting, never started. */ + handleFetchStarted(ctx: GlanceablePublisherContext): void { + if (this.isGated()) { + return; + } + if (this.current !== null) { + return; + } + const snapshot = buildGlanceableSnapshot({ + sessions: [], + userId: ctx.userId, + organizationId: ctx.organizationId, + now: this.now(), + status: 'waiting', + }); + this.publish(snapshot); + this.current = snapshot; + } + + /** Cache update failed: republish the last counts with a stale status. */ + handleFetchError(ctx: GlanceablePublisherContext): void { + if (this.isGated()) { + return; + } + const now = this.now(); + if (this.applyExpiry(now, ctx) || this.current === null) { + return; + } + const snapshot = withStatus(this.current, 'stale', now); + this.publish(snapshot); + this.current = snapshot; + } + + /** + * Apply an incoming snapshot (future background delivery). Older revisions + * are discarded; the local account epoch is applied by the caller. + */ + applySnapshot(incoming: GlanceableAgentsSnapshot, ctx: GlanceablePublisherContext): void { + if (this.current !== null && shouldDiscardGlanceableRevision(incoming, this.current)) { + return; + } + if (isEligibleGlanceableWork(incoming)) { + this.emit(incoming, ctx); + this.activityStarted = true; + } else { + this.publish(incoming); + this.activityStarted = false; + } + this.current = incoming; + } + + dispose(): void { + this.cancelCoalesce(); + this.cancelTerminal(); + } + + private isGated(): boolean { + return this.terminalBlankEpoch() !== this.blankEpochAtStart; + } + + private emit(snapshot: GlanceableAgentsSnapshot, ctx: GlanceableSinkContext): void { + for (const sink of this.sinks) { + sink.publish(snapshot); + sink.startOrUpdate(snapshot, ctx); + } + } + + private publish(snapshot: GlanceableAgentsSnapshot): void { + for (const sink of this.sinks) { + sink.publish(snapshot); + } + } + + private scheduleCoalesced(snapshot: GlanceableAgentsSnapshot, ctx: GlanceableSinkContext): void { + this.pendingCoalesced = { snapshot, ctx }; + if (this.coalesceTimer !== null) { + return; + } + this.coalesceTimer = setTimeout(() => { + this.coalesceTimer = null; + const pending = this.pendingCoalesced; + this.pendingCoalesced = null; + if (pending !== null && !this.isGated()) { + this.emit(pending.snapshot, pending.ctx); + } + }, this.coalesceMs); + } + + private scheduleTerminal(): void { + if (this.terminalTimer !== null) { + return; + } + this.terminalTimer = setTimeout(() => { + this.terminalTimer = null; + this.activityStarted = false; + for (const sink of this.sinks) { + sink.endImmediate(); + } + }, this.terminalMs); + } + + private cancelCoalesce(): void { + if (this.coalesceTimer !== null) { + clearTimeout(this.coalesceTimer); + this.coalesceTimer = null; + } + this.pendingCoalesced = null; + } + + private cancelTerminal(): void { + if (this.terminalTimer !== null) { + clearTimeout(this.terminalTimer); + this.terminalTimer = null; + } + } + + /** Publish an expired snapshot (zero counts) once the current one lapses. */ + private applyExpiry(now: number, ctx: GlanceablePublisherContext): boolean { + if (this.current === null || now < Date.parse(this.current.expiresAt)) { + return false; + } + const snapshot = buildGlanceableSnapshot({ + sessions: [], + userId: ctx.userId, + organizationId: ctx.organizationId, + now, + previousRevision: this.current.revision, + status: 'expired', + }); + this.cancelCoalesce(); + this.cancelTerminal(); + this.publish(snapshot); + this.current = snapshot; + this.activityStarted = false; + return true; + } +} diff --git a/apps/mobile/src/lib/glanceable/sink-registry.ts b/apps/mobile/src/lib/glanceable/sink-registry.ts new file mode 100644 index 0000000000..30228c3dbe --- /dev/null +++ b/apps/mobile/src/lib/glanceable/sink-registry.ts @@ -0,0 +1,57 @@ +import { type GlanceableAgentsSnapshot } from '@kilocode/app-shared/glanceable-agents-snapshot'; + +/** + * One sink consumes the glanceable snapshot for one native surface (persist, + * iOS Live Activity/widgets, Android widget/ongoing). Platform sinks register + * themselves from files their slices own; the layout mount registers only the + * persist sink. + */ +export type GlanceableSinkContext = { + /** For token registration only; must never enter the snapshot. */ + organizationId: string | null; +}; + +export type GlanceableSink = { + publish(snapshot: GlanceableAgentsSnapshot): void; + endImmediate(): void; + startOrUpdate(snapshot: GlanceableAgentsSnapshot, ctx: GlanceableSinkContext): void; +}; + +const sinks = new Set(); + +export function registerGlanceableSink(sink: GlanceableSink): void { + sinks.add(sink); +} + +export function unregisterGlanceableSink(sink: GlanceableSink): void { + sinks.delete(sink); +} + +export function getGlanceableSinks(): readonly GlanceableSink[] { + return [...sinks]; +} + +/** Activity-token registrar, set by a later token slice. No-op by default. */ +export type GlanceableDelivery = { + registerTokens(snapshot: GlanceableAgentsSnapshot, organizationId: string | null): void; + unregisterTokens(): void; +}; + +const noopDelivery: GlanceableDelivery = { + registerTokens() { + // No-op until a token slice registers a delivery. + }, + unregisterTokens() { + // No-op until a token slice registers a delivery. + }, +}; + +let delivery: GlanceableDelivery = noopDelivery; + +export function setGlanceableDelivery(next: GlanceableDelivery): void { + delivery = next; +} + +export function getGlanceableDelivery(): GlanceableDelivery { + return delivery; +} diff --git a/apps/mobile/src/lib/organization-context.tsx b/apps/mobile/src/lib/organization-context.tsx index 1d64585776..2e485bb317 100644 --- a/apps/mobile/src/lib/organization-context.tsx +++ b/apps/mobile/src/lib/organization-context.tsx @@ -11,6 +11,7 @@ import { import { useAuth } from '@/lib/auth/auth-context'; import { deleteAccountMetadata, setAccountMetadata } from '@/lib/auth/account-metadata-write'; +import { writePrivacySnapshotAndEnd } from '@/lib/glanceable/cleanup'; import { ORGANIZATION_STORAGE_KEY } from '@/lib/storage-keys'; type OrganizationContextValue = { @@ -58,14 +59,26 @@ export function OrganizationProvider({ children }: { readonly children: ReactNod }; }, [token]); - const setOrganizationId = useCallback((id: string | null) => { - setOrgState(id); - if (id) { - void setAccountMetadata(ORGANIZATION_STORAGE_KEY, id); - } else { - void deleteAccountMetadata(ORGANIZATION_STORAGE_KEY); - } - }, []); + const setOrganizationId = useCallback( + (id: string | null) => { + // A same-value selection is a no-op: blanking it would bump the terminal + // epoch and permanently gate the publisher, because React bails out of the + // state update and no effect re-runs to rebuild it. + if (id === organizationId) { + return; + } + // Blank the current surface before the selection changes so the prior + // org's counts are never shown under the next org. + writePrivacySnapshotAndEnd(); + setOrgState(id); + if (id) { + void setAccountMetadata(ORGANIZATION_STORAGE_KEY, id); + } else { + void deleteAccountMetadata(ORGANIZATION_STORAGE_KEY); + } + }, + [organizationId] + ); const value = useMemo( () => ({ organizationId, isLoaded, setOrganizationId }), diff --git a/apps/mobile/vitest.pure.config.ts b/apps/mobile/vitest.pure.config.ts index 9ee4f91ea4..45c0e244d6 100644 --- a/apps/mobile/vitest.pure.config.ts +++ b/apps/mobile/vitest.pure.config.ts @@ -27,6 +27,7 @@ export default defineProject({ 'src/lib/auth/**/*.test.tsx', 'src/lib/apple-iap/**/*.test.ts', 'src/lib/apple-iap/**/*.test.tsx', + 'src/lib/glanceable/**/*.test.ts', 'src/lib/hooks/**/*.test.ts', 'src/lib/kilo-pass/**/*.test.ts', 'src/lib/kilo-pass/**/*.test.tsx', diff --git a/packages/app-shared/package.json b/packages/app-shared/package.json index d6d2f8f4bc..62f58c7aec 100644 --- a/packages/app-shared/package.json +++ b/packages/app-shared/package.json @@ -19,7 +19,8 @@ "./app-version": "./src/app-version.ts", "./pr-review": "./src/pr-review/index.ts", "./commerce": "./src/commerce/index.ts", - "./moderation": "./src/moderation/index.ts" + "./moderation": "./src/moderation/index.ts", + "./glanceable-agents-snapshot": "./src/glanceable-agents-snapshot.ts" }, "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/app-shared/src/glanceable-agents-snapshot.test.ts b/packages/app-shared/src/glanceable-agents-snapshot.test.ts new file mode 100644 index 0000000000..c46f20b5c2 --- /dev/null +++ b/packages/app-shared/src/glanceable-agents-snapshot.test.ts @@ -0,0 +1,210 @@ +import { describe, expect, it } from 'vitest'; + +import { + buildGlanceableSnapshot, + buildOpaqueScopeKey, + countGlanceableSessions, + GLANCEABLE_SNAPSHOT_EXPIRY_MS, + isEligibleGlanceableWork, + shouldDiscardGlanceableRevision, +} from './glanceable-agents-snapshot'; + +const NOW = 1_750_000_000_000; + +describe('countGlanceableSessions', () => { + it('maps busy/question/permission/retry and ignores idle and unknown', () => { + const counts = countGlanceableSessions([ + { status: 'busy' }, + { status: 'busy' }, + { status: 'question' }, + { status: 'permission' }, + { status: 'permission' }, + { status: 'retry' }, + { status: 'idle' }, + { status: 'idle' }, + { status: 'completed' }, + { status: 'failed' }, + { status: 'mystery' }, + ]); + expect(counts).toEqual({ running: 2, needsInput: 3, reconnecting: 1 }); + }); + + it('counts Cloud Agent-shaped and CLI-shaped rows together on status alone', () => { + const cloudRow = { status: 'busy', kind: 'cloud-agent' }; + const cliRow = { status: 'retry', connectionId: 'cli-1' }; + expect(countGlanceableSessions([cloudRow, cliRow])).toEqual({ + running: 1, + needsInput: 0, + reconnecting: 1, + }); + }); + + it('produces zero eligible counts for idle-only sessions', () => { + expect(countGlanceableSessions([{ status: 'idle' }, { status: 'idle' }])).toEqual({ + running: 0, + needsInput: 0, + reconnecting: 0, + }); + }); +}); + +describe('buildOpaqueScopeKey', () => { + it('is stable for the same input and never returns the raw ids', () => { + const a = buildOpaqueScopeKey({ userId: 'oauth/user-1', organizationId: 'org-9' }); + const b = buildOpaqueScopeKey({ userId: 'oauth/user-1', organizationId: 'org-9' }); + expect(a).toBe(b); + expect(a).toMatch(/^[0-9a-f]{8}$/); + expect(a).not.toContain('oauth/user-1'); + expect(a).not.toContain('org-9'); + }); + + it('differs when the user or organization differs', () => { + const personal = buildOpaqueScopeKey({ userId: 'u1', organizationId: null }); + const org = buildOpaqueScopeKey({ userId: 'u1', organizationId: 'org-1' }); + const otherUser = buildOpaqueScopeKey({ userId: 'u2', organizationId: 'org-1' }); + expect(new Set([personal, org, otherUser]).size).toBe(3); + }); +}); + +describe('buildGlanceableSnapshot', () => { + it('increases revision and marks expiry 8 hours after updatedAt', () => { + const snapshot = buildGlanceableSnapshot({ + sessions: [{ status: 'busy' }], + userId: 'oauth/user-1', + organizationId: 'org-9', + now: NOW, + }); + expect(snapshot.revision).toBe(1); + expect(snapshot.status).toBe('happy'); + expect(snapshot.running).toBe(1); + expect(Date.parse(snapshot.expiresAt) - Date.parse(snapshot.updatedAt)).toBe( + GLANCEABLE_SNAPSHOT_EXPIRY_MS + ); + }); + + it('keeps revision monotonic and eligibleStartedAt while work stays eligible', () => { + const first = buildGlanceableSnapshot({ + sessions: [{ status: 'busy' }], + userId: 'u1', + organizationId: null, + now: NOW, + }); + const second = buildGlanceableSnapshot({ + sessions: [{ status: 'busy' }, { status: 'question' }], + userId: 'u1', + organizationId: null, + now: NOW + 5000, + previousRevision: first.revision, + previousEligibleStartedAt: first.eligibleStartedAt, + }); + expect(second.revision).toBe(first.revision + 1); + expect(second.eligibleStartedAt).toBe(first.eligibleStartedAt); + expect(second.needsInput).toBe(1); + }); + + it('clears eligibleStartedAt when no eligible work remains', () => { + const snapshot = buildGlanceableSnapshot({ + sessions: [{ status: 'idle' }], + userId: 'u1', + organizationId: null, + now: NOW, + previousRevision: 3, + previousEligibleStartedAt: new Date(NOW - 60_000).toISOString(), + }); + expect(snapshot.status).toBe('empty'); + expect(snapshot.eligibleStartedAt).toBeNull(); + expect(snapshot.revision).toBe(4); + }); + + it('sets organizationBound only when organizationId is a string', () => { + const personal = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW, + }); + const org = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: 'org-9', + now: NOW, + }); + expect(personal.organizationBound).toBe(false); + expect(org.organizationBound).toBe(true); + }); + + it('honours a status override and omits accountEpoch when absent', () => { + const snapshot = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW, + status: 'waiting', + }); + expect(snapshot.status).toBe('waiting'); + expect('accountEpoch' in snapshot).toBe(false); + }); + + it('serializes without any forbidden fixture', () => { + const rows = [ + { status: 'busy', title: 'Secret prompt', gitUrl: 'github.com/acme/repo', id: 'ses_raw_1' }, + { status: 'question', organizationName: 'Acme Org' }, + ]; + const snapshot = buildGlanceableSnapshot({ + sessions: rows, + userId: 'oauth/user-1', + organizationId: 'org-9', + now: NOW, + }); + const json = JSON.stringify(snapshot); + expect(json).not.toContain('Secret prompt'); + expect(json).not.toContain('Acme Org'); + expect(json).not.toContain('github.com/acme/repo'); + expect(json).not.toContain('ses_raw_1'); + expect(json).not.toContain('oauth/user-1'); + expect(json).not.toContain('org-9'); + }); +}); + +describe('isEligibleGlanceableWork and revision discard', () => { + it('reports eligibility from the three counts', () => { + const empty = buildGlanceableSnapshot({ sessions: [], userId: 'u1', organizationId: null, now: NOW }); + const busy = buildGlanceableSnapshot({ sessions: [{ status: 'busy' }], userId: 'u1', organizationId: null, now: NOW }); + expect(isEligibleGlanceableWork(empty)).toBe(false); + expect(isEligibleGlanceableWork(busy)).toBe(true); + }); + + it('discards a lower revision and an older updatedAt at equal revision', () => { + const current = buildGlanceableSnapshot({ + sessions: [{ status: 'busy' }], + userId: 'u1', + organizationId: null, + now: NOW, + previousRevision: 4, + }); + const lowerRevision = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW + 10_000, + previousRevision: 3, + }); + const olderAtEqualRevision = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW - 5000, + previousRevision: 4, + }); + const newerAtEqualRevision = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW + 15_000, + previousRevision: 4, + }); + expect(shouldDiscardGlanceableRevision(lowerRevision, current)).toBe(true); + expect(shouldDiscardGlanceableRevision(olderAtEqualRevision, current)).toBe(true); + expect(shouldDiscardGlanceableRevision(newerAtEqualRevision, current)).toBe(false); + }); +}); diff --git a/packages/app-shared/src/glanceable-agents-snapshot.ts b/packages/app-shared/src/glanceable-agents-snapshot.ts new file mode 100644 index 0000000000..4a492831cd --- /dev/null +++ b/packages/app-shared/src/glanceable-agents-snapshot.ts @@ -0,0 +1,187 @@ +import { z } from 'zod'; + +/** + * One durable, privacy-minimal, versioned snapshot for every glanceable + * surface (Live Activity, Dynamic Island, Home Screen widget, Android + * widget, Android ongoing notification). + * + * Privacy contract: the snapshot carries generic status, counts, safe + * timestamps, and an opaque scope key only. It must never carry a session + * title, prompt, excerpt, organization name, repository name, generated + * text, secret, or a raw account/session id. + */ + +export const GLANCEABLE_SNAPSHOT_SCHEMA_VERSION = 1; +/** 8 hours: matches the usual Live Activity lifetime. */ +export const GLANCEABLE_SNAPSHOT_EXPIRY_MS = 28_800_000; +/** Later happy updates are coalesced for at most this long. */ +export const GLANCEABLE_COALESCE_MS = 1000; +/** Terminal empty lasts at most this long before the activity ends. */ +export const GLANCEABLE_TERMINAL_MS = 8000; + +export type GlanceableAgentsSnapshotStatus = + | 'waiting' + | 'empty' + | 'happy' + | 'stale' + | 'expired' + | 'signed_out' + | 'privacy'; + +export const glanceableAgentsSnapshotSchema = z.object({ + schemaVersion: z.literal(1), + revision: z.number().int().min(1), + /** ISO 8601 timestamp. */ + updatedAt: z.string(), + /** ISO 8601 timestamp; `updatedAt + GLANCEABLE_SNAPSHOT_EXPIRY_MS`. */ + expiresAt: z.string(), + /** Opaque scope key; never a raw user or organization id. */ + scopeKey: z.string().min(1), + /** + * Client-set local auth epoch. Optional on the wire: old and server + * producers omit it; the client sets the current local epoch when it + * applies a remote snapshot. Remove the optional when every producer + * sends it. + */ + accountEpoch: z.number().int().optional(), + organizationBound: z.boolean(), + status: z.enum(['waiting', 'empty', 'happy', 'stale', 'expired', 'signed_out', 'privacy']), + running: z.number().int().min(0), + needsInput: z.number().int().min(0), + reconnecting: z.number().int().min(0), + /** ISO 8601 timestamp or null; binds the elapsed-time display. */ + eligibleStartedAt: z.string().nullable(), +}); + +export type GlanceableAgentsSnapshot = z.infer; + +export type GlanceableCounts = { + running: number; + needsInput: number; + reconnecting: number; +}; + +/** + * Map session rows to the three eligible counts. `busy` → running, + * `question`/`permission` → needs-input, `retry` → reconnecting. `idle` and + * any unknown status are ignored. Do not call `isCompletedStatus` here. + */ +export function countGlanceableSessions( + sessions: readonly { status: string }[] +): GlanceableCounts { + let running = 0; + let needsInput = 0; + let reconnecting = 0; + for (const session of sessions) { + switch (session.status) { + case 'busy': + running += 1; + break; + case 'question': + case 'permission': + needsInput += 1; + break; + case 'retry': + reconnecting += 1; + break; + default: + // idle and unknown statuses contribute nothing. + break; + } + } + return { running, needsInput, reconnecting }; +} + +// FNV-1a 32-bit over UTF-16 code units (two bytes each). Deterministic across +// Node and Hermes and not reversible to the input, so the raw ids never appear +// in the key. +function fnv1a32(input: string): number { + let hash = 0x811c9dc5; + for (let i = 0; i < input.length; i += 1) { + const code = input.charCodeAt(i); + hash = Math.imul(hash ^ (code & 0xff), 0x01000193) >>> 0; + hash = Math.imul(hash ^ ((code >>> 8) & 0xff), 0x01000193) >>> 0; + } + return hash >>> 0; +} + +/** + * Opaque, stable scope key for a user + optional organization pair. The + * client also fences remote snapshots on the local auth epoch, so the epoch + * deliberately does not enter this key. + */ +export function buildOpaqueScopeKey(input: { + userId: string; + organizationId: string | null; +}): string { + // Length-delimited by a NUL separator so `user=ab,org=c` and `user=a,org=bc` + // cannot hash to the same key. + const joined = `${input.userId}\u0000${input.organizationId ?? ''}`; + const hash = fnv1a32(joined); + return hash.toString(16).padStart(8, '0'); +} + +export type BuildGlanceableSnapshotInput = { + sessions: readonly { status: string }[]; + userId: string; + organizationId: string | null; + /** Epoch milliseconds. */ + now: number; + previousRevision?: number; + previousEligibleStartedAt?: string | null; + accountEpoch?: number; + /** Overrides the happy/empty derivation for waiting, stale, expired, signed_out, privacy. */ + status?: GlanceableAgentsSnapshotStatus; +}; + +/** + * Build a snapshot from the current session rows. Revision increases by one + * on every build. `eligibleStartedAt` keeps the previous value while work + * stays eligible, starts at `now` when work becomes eligible, and is null + * otherwise. + */ +export function buildGlanceableSnapshot(input: BuildGlanceableSnapshotInput): GlanceableAgentsSnapshot { + const counts = countGlanceableSessions(input.sessions); + const eligible = counts.running + counts.needsInput + counts.reconnecting > 0; + const now = input.now; + const updatedAt = new Date(now).toISOString(); + const eligibleStartedAt = eligible ? (input.previousEligibleStartedAt ?? updatedAt) : null; + + return { + schemaVersion: GLANCEABLE_SNAPSHOT_SCHEMA_VERSION, + revision: (input.previousRevision ?? 0) + 1, + updatedAt, + expiresAt: new Date(now + GLANCEABLE_SNAPSHOT_EXPIRY_MS).toISOString(), + scopeKey: buildOpaqueScopeKey({ userId: input.userId, organizationId: input.organizationId }), + ...(input.accountEpoch === undefined ? {} : { accountEpoch: input.accountEpoch }), + organizationBound: typeof input.organizationId === 'string', + status: input.status ?? (eligible ? 'happy' : 'empty'), + running: counts.running, + needsInput: counts.needsInput, + reconnecting: counts.reconnecting, + eligibleStartedAt, + }; +} + +/** True when any eligible count is non-zero. */ +export function isEligibleGlanceableWork(snapshot: GlanceableAgentsSnapshot): boolean { + return snapshot.running + snapshot.needsInput + snapshot.reconnecting > 0; +} + +/** + * True when `incoming` must be discarded in favour of `current`: a strictly + * lower revision, or the same revision with an older `updatedAt`. ISO strings + * from `toISOString()` compare correctly as strings. + */ +export function shouldDiscardGlanceableRevision( + incoming: GlanceableAgentsSnapshot, + current: GlanceableAgentsSnapshot +): boolean { + if (incoming.revision < current.revision) { + return true; + } + if (incoming.revision === current.revision) { + return incoming.updatedAt < current.updatedAt; + } + return false; +} From 44f0caa4654385d602706b75aec1ef27c10e5060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Fri, 28 Aug 2026 00:07:52 +0200 Subject: [PATCH 2/6] fix(glanceable): gate publisher and persist, fix level-1 CI --- apps/mobile/knip.json | 7 ++- .../mobile/src/lib/auth/auth-context.test.tsx | 9 ++-- .../mobile/src/lib/glanceable/persist.test.ts | 25 +++++++++ apps/mobile/src/lib/glanceable/persist.ts | 11 ++-- .../src/lib/glanceable/publisher.test.ts | 54 +++++++++++++++++++ apps/mobile/src/lib/glanceable/publisher.ts | 10 ++++ 6 files changed, 109 insertions(+), 7 deletions(-) diff --git a/apps/mobile/knip.json b/apps/mobile/knip.json index 58d44efcad..a86f207727 100644 --- a/apps/mobile/knip.json +++ b/apps/mobile/knip.json @@ -10,7 +10,12 @@ "@sentry/cli" ], "ignoreBinaries": ["oxfmt", "oxlint"], - "ignore": ["src/components/ui/**"], + "ignore": ["src/components/ui/**", "src/lib/glanceable/open-agents.ts"], + "ignoreIssues": { + "src/lib/glanceable/presentation.ts": ["exports", "types"], + "src/lib/glanceable/publisher.ts": ["exports"], + "src/lib/glanceable/sink-registry.ts": ["exports", "types"] + }, "postcss": { "config": ["postcss.config.mjs"] }, diff --git a/apps/mobile/src/lib/auth/auth-context.test.tsx b/apps/mobile/src/lib/auth/auth-context.test.tsx index ad3ac09f73..dafe95c348 100644 --- a/apps/mobile/src/lib/auth/auth-context.test.tsx +++ b/apps/mobile/src/lib/auth/auth-context.test.tsx @@ -631,7 +631,9 @@ describe('stale sign-in continuation', () => { expect(getCtx().sessionEnded).toBe(true); expect(getCtx().isSigningOut).toBe(true); const { queryClient: queryClientMock } = await import('@/lib/query-client'); - expect(vi.mocked(queryClientMock.clear)).toHaveBeenCalledTimes(1); + // The account-switch path in signIn clears once, and the sign-out teardown + // clears a second time. + expect(vi.mocked(queryClientMock.clear)).toHaveBeenCalledTimes(2); unmount(); }); @@ -1170,8 +1172,9 @@ describe('auth-transition queue and sign-out failure matrix', () => { }); // Whole-body FIFO: the sign-out's teardown (including the query-client - // clear) settled before the sign-in's credential write ran. - expect(clearMock).toHaveBeenCalledTimes(1); + // clear) settled before the sign-in's credential write ran; the sign-in + // account-switch path then clears a second time after its credential write. + expect(clearMock).toHaveBeenCalledTimes(2); const clearOrder = clearMock.mock.invocationCallOrder[0]; const setOrder = hoisted.secureStore.setItemAsync.mock.invocationCallOrder[0]; expect(clearOrder).toBeLessThan(setOrder); diff --git a/apps/mobile/src/lib/glanceable/persist.test.ts b/apps/mobile/src/lib/glanceable/persist.test.ts index 86491fa7fe..c45b684da2 100644 --- a/apps/mobile/src/lib/glanceable/persist.test.ts +++ b/apps/mobile/src/lib/glanceable/persist.test.ts @@ -7,6 +7,7 @@ import { import { _resetGlanceablePersistForTests, + _setLastGlanceableSnapshotForTests, _setSecureStoreForTests, getLastGlanceableSnapshot, getLocalScopeKey, @@ -103,6 +104,30 @@ describe('restorePersistedGlanceable', () => { expect(getLastGlanceableSnapshot()).toBeNull(); }); + it('does not clobber an in-memory state already set before the restore read', async () => { + // A stale persisted record from a prior session sits on disk. + const stale = snapshotFor([{ status: 'busy' }]); + store.set(SNAPSHOT_KEY, JSON.stringify(stale)); + store.set(SCOPE_KEY, 'stale-scope'); + + // A logout blank landed in memory before restore started (a remount, not a + // JS restart). Its SecureStore mirror is not part of this test, so the + // disk still holds the stale record. + const blank = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW, + status: 'signed_out', + }); + _setLastGlanceableSnapshotForTests(blank); + + await restorePersistedGlanceable(); + + expect(getLastGlanceableSnapshot()).toEqual(blank); + expect(getLocalScopeKey()).toBe(blank.scopeKey); + }); + it('restores a schema-valid stored record', async () => { const stored = snapshotFor([{ status: 'busy' }]); store.set(SNAPSHOT_KEY, JSON.stringify(stored)); diff --git a/apps/mobile/src/lib/glanceable/persist.ts b/apps/mobile/src/lib/glanceable/persist.ts index 5263448f54..a3939f15b8 100644 --- a/apps/mobile/src/lib/glanceable/persist.ts +++ b/apps/mobile/src/lib/glanceable/persist.ts @@ -77,7 +77,9 @@ function parseStoredSnapshot(raw: string): GlanceableAgentsSnapshot | null { /** * Restore the in-memory state from SecureStore after a JS restart. Best * effort: a failed read keeps the null in-memory state. A live write during - * the read owns the state, so the stale persisted record is skipped. + * the read owns the state, so the stale persisted record is skipped. An + * already-set in-memory state (a remount) is never overwritten: only a real + * JS restart starts null and must restore from the persisted record. */ export async function restorePersistedGlanceable(): Promise { const startEpoch = persistEpoch; @@ -90,13 +92,16 @@ export async function restorePersistedGlanceable(): Promise { if (persistEpoch !== startEpoch) { return; } - if (rawSnapshot !== null) { + // A remount keeps the module alive, so a logout or privacy blank written + // before restore owns the state and must not be clobbered by a stale disk + // record from the prior session. + if (rawSnapshot !== null && lastSnapshot === null) { const parsed = parseStoredSnapshot(rawSnapshot); if (parsed !== null) { lastSnapshot = parsed; } } - if (rawScope !== null) { + if (rawScope !== null && localScopeKey === null) { localScopeKey = rawScope; } } catch { diff --git a/apps/mobile/src/lib/glanceable/publisher.test.ts b/apps/mobile/src/lib/glanceable/publisher.test.ts index a470bdf8c9..fedbba8203 100644 --- a/apps/mobile/src/lib/glanceable/publisher.test.ts +++ b/apps/mobile/src/lib/glanceable/publisher.test.ts @@ -231,6 +231,60 @@ describe('GlanceablePublisher', () => { publisher.dispose(); }); + it('cancels a pending coalesced emit on a fetch error', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW, coalesceMs: 1000 }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + publisher.handleSessions([{ status: 'busy' }, { status: 'busy' }], PUB_CTX); + publisher.handleFetchError(PUB_CTX); + vi.advanceTimersByTime(1000); + // The pre-error happy emit must not fire after the stale republish. + expect(count(calls, 'startOrUpdate')).toBe(1); + expect(lastSnapshot(calls, 'publish').status).toBe('stale'); + publisher.dispose(); + }); + + it('does not apply a snapshot after a terminal blank', () => { + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ + sinks: [sink], + now: () => NOW, + terminalBlankEpoch: getTerminalBlankEpoch, + }); + writeSignedOutSnapshotAndEnd(); + publisher.applySnapshot(snapshotFor([{ status: 'busy' }], NOW), PUB_CTX); + expect(count(calls, 'startOrUpdate')).toBe(0); + expect(count(calls, 'publish')).toBe(0); + publisher.dispose(); + }); + + it('cancels a pending coalesced emit when a newer snapshot applies', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW, coalesceMs: 1000 }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + publisher.handleSessions([{ status: 'busy' }, { status: 'busy' }], PUB_CTX); + publisher.applySnapshot(snapshotFor([{ status: 'busy' }], NOW + 1, 2), PUB_CTX); + vi.advanceTimersByTime(1000); + // Only the applied snapshot emits; the older coalesced happy update must not. + expect(count(calls, 'startOrUpdate')).toBe(2); + expect(lastSnapshot(calls, 'startOrUpdate').running).toBe(1); + publisher.dispose(); + }); + + it('cancels a pending terminal when a newer snapshot applies', () => { + vi.useFakeTimers(); + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); + publisher.handleSessions([{ status: 'busy' }], PUB_CTX); + publisher.handleSessions([{ status: 'idle' }], PUB_CTX); + publisher.applySnapshot(snapshotFor([{ status: 'busy' }], NOW + 1, 1), PUB_CTX); + vi.advanceTimersByTime(8000); + expect(count(calls, 'endImmediate')).toBe(0); + publisher.dispose(); + }); + it('keeps the revision monotonic when seeded from an initial snapshot', () => { const { sink, calls } = makeSink(); // Seeded with revision 42; the next snapshot must be 43. diff --git a/apps/mobile/src/lib/glanceable/publisher.ts b/apps/mobile/src/lib/glanceable/publisher.ts index af203542d7..c4826b4435 100644 --- a/apps/mobile/src/lib/glanceable/publisher.ts +++ b/apps/mobile/src/lib/glanceable/publisher.ts @@ -149,6 +149,9 @@ export class GlanceablePublisher { if (this.applyExpiry(now, ctx) || this.current === null) { return; } + // A fetch error supersedes any pending coalesced happy emit: otherwise the + // pre-error snapshot would fire later and overwrite the stale counts. + this.cancelCoalesce(); const snapshot = withStatus(this.current, 'stale', now); this.publish(snapshot); this.current = snapshot; @@ -159,9 +162,16 @@ export class GlanceablePublisher { * are discarded; the local account epoch is applied by the caller. */ applySnapshot(incoming: GlanceableAgentsSnapshot, ctx: GlanceablePublisherContext): void { + if (this.isGated()) { + return; + } if (this.current !== null && shouldDiscardGlanceableRevision(incoming, this.current)) { return; } + // A late background delivery supersedes a pending coalesced emit and any + // pending 8 s terminal, so neither can fire after the newer snapshot. + this.cancelCoalesce(); + this.cancelTerminal(); if (isEligibleGlanceableWork(incoming)) { this.emit(incoming, ctx); this.activityStarted = true; From 4083fdbf74e4ead7008f9ab82f729393b7497398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Fri, 28 Aug 2026 01:11:07 +0200 Subject: [PATCH 3/6] style(app-shared): format glanceable snapshot files --- .../src/glanceable-agents-snapshot.test.ts | 14 ++++++++++++-- .../app-shared/src/glanceable-agents-snapshot.ts | 8 ++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/app-shared/src/glanceable-agents-snapshot.test.ts b/packages/app-shared/src/glanceable-agents-snapshot.test.ts index c46f20b5c2..c59de79ca7 100644 --- a/packages/app-shared/src/glanceable-agents-snapshot.test.ts +++ b/packages/app-shared/src/glanceable-agents-snapshot.test.ts @@ -168,8 +168,18 @@ describe('buildGlanceableSnapshot', () => { describe('isEligibleGlanceableWork and revision discard', () => { it('reports eligibility from the three counts', () => { - const empty = buildGlanceableSnapshot({ sessions: [], userId: 'u1', organizationId: null, now: NOW }); - const busy = buildGlanceableSnapshot({ sessions: [{ status: 'busy' }], userId: 'u1', organizationId: null, now: NOW }); + const empty = buildGlanceableSnapshot({ + sessions: [], + userId: 'u1', + organizationId: null, + now: NOW, + }); + const busy = buildGlanceableSnapshot({ + sessions: [{ status: 'busy' }], + userId: 'u1', + organizationId: null, + now: NOW, + }); expect(isEligibleGlanceableWork(empty)).toBe(false); expect(isEligibleGlanceableWork(busy)).toBe(true); }); diff --git a/packages/app-shared/src/glanceable-agents-snapshot.ts b/packages/app-shared/src/glanceable-agents-snapshot.ts index 4a492831cd..e7498e80f1 100644 --- a/packages/app-shared/src/glanceable-agents-snapshot.ts +++ b/packages/app-shared/src/glanceable-agents-snapshot.ts @@ -66,9 +66,7 @@ export type GlanceableCounts = { * `question`/`permission` → needs-input, `retry` → reconnecting. `idle` and * any unknown status are ignored. Do not call `isCompletedStatus` here. */ -export function countGlanceableSessions( - sessions: readonly { status: string }[] -): GlanceableCounts { +export function countGlanceableSessions(sessions: readonly { status: string }[]): GlanceableCounts { let running = 0; let needsInput = 0; let reconnecting = 0; @@ -140,7 +138,9 @@ export type BuildGlanceableSnapshotInput = { * stays eligible, starts at `now` when work becomes eligible, and is null * otherwise. */ -export function buildGlanceableSnapshot(input: BuildGlanceableSnapshotInput): GlanceableAgentsSnapshot { +export function buildGlanceableSnapshot( + input: BuildGlanceableSnapshotInput +): GlanceableAgentsSnapshot { const counts = countGlanceableSessions(input.sessions); const eligible = counts.running + counts.needsInput + counts.reconnecting > 0; const now = input.now; From 404cdee870db2e9edae5d077af2872619cdaf8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Fri, 28 Aug 2026 10:43:17 +0200 Subject: [PATCH 4/6] feat(i18n): translate glanceable mobile status keys --- apps/mobile/src/i18n/locales/af.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/am.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ar.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/az.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/be.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/bg.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/bn.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/bs.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ca.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ckb.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/cs.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/cy.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/da.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/de.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/el.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/es.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/et.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/eu.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/fa.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/fi.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/fil.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/fr.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ga.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/gl.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/gu.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ha.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/he.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/hi.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/hr.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ht.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/hu.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/hy.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/id.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ig.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/is.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/it.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ja.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ka.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/kk.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/km.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/kn.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ko.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/lo.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/lt.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/lv.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/mg.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/mi.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/mk.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ml.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/mn.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/mr.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ms.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/mt.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/my.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/nb.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ne.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/nl.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/om.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/or.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/pa.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/pl.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ps.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/pt-BR.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/pt.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ro.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ru.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/si.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/sk.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/sl.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/so.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/sq.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/sr.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/sv.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/sw.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ta.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/te.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/th.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/tr.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/uk.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/ur.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/uz.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/vi.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/yo.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/zh-Hans.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/zh-Hant.json | 15 +++++++++++++++ apps/mobile/src/i18n/locales/zu.json | 15 +++++++++++++++ 86 files changed, 1290 insertions(+) diff --git a/apps/mobile/src/i18n/locales/af.json b/apps/mobile/src/i18n/locales/af.json index bf86dbd3d4..cf670472a7 100644 --- a/apps/mobile/src/i18n/locales/af.json +++ b/apps/mobile/src/i18n/locales/af.json @@ -3191,5 +3191,20 @@ "historyRetry": "Kon nie kletsgeskiedenis laai nie.", "catalogRetry": "Kon nie modelle laai nie", "sendError": "Kon nie die boodskap stuur nie. Probeer asseblief weer." + }, + "glanceable": { + "waiting": "Dateer agente op", + "empty": "Geen werk aan die gang nie", + "stale": "Kan nie nou opdateer nie", + "expired": "Status het verval", + "signedOut": "Meld aan om agente te sien", + "privacy": "Agente versteek", + "openAgents": "Maak agente oop", + "running": "LOOP", + "needsInput": "benodig invoer", + "reconnecting": "Verbind tans weer", + "channelName": "Aktiewe agente", + "activityKitDisabledTitle": "Regstreekse Aktiwiteite is af", + "activityKitDisabledBody": "Skakel Regstreekse Aktiwiteite in Instellings aan om Aktiewe Agente op die Sluitskerm te sien." } } diff --git a/apps/mobile/src/i18n/locales/am.json b/apps/mobile/src/i18n/locales/am.json index 05d88c7601..6eb270f961 100644 --- a/apps/mobile/src/i18n/locales/am.json +++ b/apps/mobile/src/i18n/locales/am.json @@ -3191,5 +3191,20 @@ "historyRetry": "የውይይት ታሪክ መጫን አልተቻለም።", "catalogRetry": "ሞዴሎችን መጫን አልተቻለም", "sendError": "መልእክቱን መላክ አልተቻለም። እንደገና ይሞክሩ።" + }, + "glanceable": { + "waiting": "ወኪሎችን በማዘመን ላይ", + "empty": "በሂደት ላይ ያለ ስራ የለም", + "stale": "አሁን ማዘመን አይቻልም", + "expired": "የሁኔታው ጊዜ አልፏል", + "signedOut": "ወኪሎችን ለማየት ይግቡ", + "privacy": "ወኪሎች ተደብቀዋል", + "openAgents": "ወኪሎችን ይክፈቱ", + "running": "በስራ ላይ", + "needsInput": "ግብዓት ይፈልጋል", + "reconnecting": "እንደገና በመገናኘት ላይ", + "channelName": "ንቁ ወኪሎች", + "activityKitDisabledTitle": "የቀጥታ እንቅስቃሴዎች ጠፍተዋል", + "activityKitDisabledBody": "ንቁ ወኪሎችን በመቆለፊያ ማያ ገጽ ላይ ለማየት በቅንብሮች ውስጥ የቀጥታ እንቅስቃሴዎችን ያብሩ።" } } diff --git a/apps/mobile/src/i18n/locales/ar.json b/apps/mobile/src/i18n/locales/ar.json index aca86d9e3a..baaef6572f 100644 --- a/apps/mobile/src/i18n/locales/ar.json +++ b/apps/mobile/src/i18n/locales/ar.json @@ -3275,5 +3275,20 @@ "historyRetry": "تعذّر تحميل سجل المحادثة.", "catalogRetry": "تعذّر تحميل النماذج", "sendError": "تعذّر إرسال الرسالة. حاول مرة أخرى." + }, + "glanceable": { + "waiting": "جارٍ تحديث الوكلاء", + "empty": "لا يوجد عمل قيد التنفيذ", + "stale": "يتعذّر التحديث الآن", + "expired": "انتهت صلاحية الحالة", + "signedOut": "سجّل الدخول لرؤية الوكلاء", + "privacy": "الوكلاء مخفيون", + "openAgents": "فتح الوكلاء", + "running": "قيد التشغيل", + "needsInput": "يتطلب إدخالًا", + "reconnecting": "جارٍ إعادة الاتصال", + "channelName": "الوكلاء النشطون", + "activityKitDisabledTitle": "الأنشطة المباشرة متوقفة", + "activityKitDisabledBody": "فعّل الأنشطة المباشرة في الإعدادات لرؤية الوكلاء النشطين على شاشة القفل." } } diff --git a/apps/mobile/src/i18n/locales/az.json b/apps/mobile/src/i18n/locales/az.json index 616865ed68..760a5f472b 100644 --- a/apps/mobile/src/i18n/locales/az.json +++ b/apps/mobile/src/i18n/locales/az.json @@ -3191,5 +3191,20 @@ "historyRetry": "Söhbət tarixçəsi yüklənə bilmədi.", "catalogRetry": "Modellər yüklənə bilmədi", "sendError": "Mesaj göndərilə bilmədi. Yenidən cəhd edin." + }, + "glanceable": { + "waiting": "Agentlər yenilənir", + "empty": "Davam edən iş yoxdur", + "stale": "Hazırda yeniləmək mümkün deyil", + "expired": "Statusun müddəti bitib", + "signedOut": "Agentləri görmək üçün daxil olun", + "privacy": "Agentlər gizlədilib", + "openAgents": "Agentləri açın", + "running": "İŞLƏYİR", + "needsInput": "GİRİŞ TƏLƏB OLUNUR", + "reconnecting": "Yenidən qoşulur", + "channelName": "Aktiv agentlər", + "activityKitDisabledTitle": "Canlı fəaliyyətlər söndürülüb", + "activityKitDisabledBody": "Kilid ekranında aktiv agentləri görmək üçün Parametrlərdə Canlı fəaliyyətləri aktivləşdirin." } } diff --git a/apps/mobile/src/i18n/locales/be.json b/apps/mobile/src/i18n/locales/be.json index 6ffed2ad89..dd9e35eea3 100644 --- a/apps/mobile/src/i18n/locales/be.json +++ b/apps/mobile/src/i18n/locales/be.json @@ -3233,5 +3233,20 @@ "historyRetry": "Не атрымалася загрузіць гісторыю чата.", "catalogRetry": "Не атрымалася загрузіць мадэлі", "sendError": "Не атрымалася адправіць паведамленне. Паспрабуйце яшчэ раз." + }, + "glanceable": { + "waiting": "Абнаўленне агентаў", + "empty": "Няма працы ў выкананні", + "stale": "Зараз немагчыма абнавіць", + "expired": "Тэрмін дзеяння статусу скончыўся", + "signedOut": "Увайдзіце, каб бачыць агентаў", + "privacy": "Агенты схаваны", + "openAgents": "Адкрыць агентаў", + "running": "ПРАЦУЕ", + "needsInput": "патрабуецца ўвод", + "reconnecting": "Паўторнае падключэнне", + "channelName": "Актыўныя агенты", + "activityKitDisabledTitle": "Жывыя дзеянні выключаны", + "activityKitDisabledBody": "Уключыце «Жывыя дзеянні» ў «Наладах», каб бачыць актыўных агентаў на экране блакіроўкі." } } diff --git a/apps/mobile/src/i18n/locales/bg.json b/apps/mobile/src/i18n/locales/bg.json index 5e5ddab41e..bd69c06cff 100644 --- a/apps/mobile/src/i18n/locales/bg.json +++ b/apps/mobile/src/i18n/locales/bg.json @@ -3191,5 +3191,20 @@ "historyRetry": "Не можа да се зареди историята на чата.", "catalogRetry": "Не може да се заредят моделите", "sendError": "Не можахме да изпратим съобщението. Опитайте отново." + }, + "glanceable": { + "waiting": "Актуализиране на агентите", + "empty": "Няма работа в ход", + "stale": "Не може да се актуализира сега", + "expired": "Статусът е изтекъл", + "signedOut": "Влезте, за да видите агентите", + "privacy": "Агентите са скрити", + "openAgents": "Отворете агентите", + "running": "Изпълнява се", + "needsInput": "изисква въвеждане", + "reconnecting": "Повторно свързване", + "channelName": "Активни агенти", + "activityKitDisabledTitle": "Дейностите на живо са изключени", + "activityKitDisabledBody": "Включете Дейности на живо в Настройки, за да виждате активните агенти на заключения екран." } } diff --git a/apps/mobile/src/i18n/locales/bn.json b/apps/mobile/src/i18n/locales/bn.json index 5cef607455..c4987395e3 100644 --- a/apps/mobile/src/i18n/locales/bn.json +++ b/apps/mobile/src/i18n/locales/bn.json @@ -3191,5 +3191,20 @@ "historyRetry": "চ্যাট ইতিহাস লোড করা যায়নি।", "catalogRetry": "মডেল লোড করা যায়নি", "sendError": "বার্তাটি পাঠানো যায়নি। আবার চেষ্টা করুন।" + }, + "glanceable": { + "waiting": "এজেন্টগুলি আপডেট করা হচ্ছে", + "empty": "কোনো কাজ চলছে না", + "stale": "এখন আপডেট করা যাচ্ছে না", + "expired": "অবস্থার মেয়াদ শেষ হয়েছে", + "signedOut": "এজেন্টগুলি দেখতে সাইন ইন করুন", + "privacy": "এজেন্টগুলি লুকানো আছে", + "openAgents": "এজেন্টগুলি খুলুন", + "running": "চলছে", + "needsInput": "ইনপুট প্রয়োজন", + "reconnecting": "পুনরায় সংযোগ করা হচ্ছে", + "channelName": "সক্রিয় এজেন্ট", + "activityKitDisabledTitle": "সরাসরি কার্যকলাপ বন্ধ আছে", + "activityKitDisabledBody": "লক স্ক্রিনে সক্রিয় এজেন্টগুলি দেখতে সেটিংসে সরাসরি কার্যকলাপ চালু করুন।" } } diff --git a/apps/mobile/src/i18n/locales/bs.json b/apps/mobile/src/i18n/locales/bs.json index 8706dd3800..df8241f41a 100644 --- a/apps/mobile/src/i18n/locales/bs.json +++ b/apps/mobile/src/i18n/locales/bs.json @@ -3212,5 +3212,20 @@ "historyRetry": "Nije moguće učitati istoriju razgovora.", "catalogRetry": "Nije moguće učitati modele", "sendError": "Poruka nije mogla biti poslana. Pokušajte ponovo." + }, + "glanceable": { + "waiting": "Ažuriranje agenata", + "empty": "Nema rada u toku", + "stale": "Trenutno nije moguće ažurirati", + "expired": "Status je istekao", + "signedOut": "Prijavite se da biste vidjeli agente", + "privacy": "Agenti su skriveni", + "openAgents": "Otvorite agente", + "running": "RADI", + "needsInput": "treba unos", + "reconnecting": "Ponovno povezivanje", + "channelName": "Aktivni agenti", + "activityKitDisabledTitle": "Aktivnosti uživo su isključene", + "activityKitDisabledBody": "Uključite aktivnosti uživo u Postavkama da biste vidjeli aktivne agente na zaključanom ekranu." } } diff --git a/apps/mobile/src/i18n/locales/ca.json b/apps/mobile/src/i18n/locales/ca.json index ebeb0aa7f2..8e1ae0ba1b 100644 --- a/apps/mobile/src/i18n/locales/ca.json +++ b/apps/mobile/src/i18n/locales/ca.json @@ -3212,5 +3212,20 @@ "historyRetry": "No s'ha pogut carregar l'historial del xat.", "catalogRetry": "No s'han pogut carregar els models", "sendError": "No s'ha pogut enviar el missatge. Torna-ho a provar." + }, + "glanceable": { + "waiting": "Actualitzant els agents", + "empty": "No hi ha cap feina en curs", + "stale": "Ara no es pot actualitzar", + "expired": "Estat caducat", + "signedOut": "Inicia la sessió per veure els agents", + "privacy": "Agents ocults", + "openAgents": "Obre els agents", + "running": "EN EXECUCIÓ", + "needsInput": "requereix entrada", + "reconnecting": "Reconnectant", + "channelName": "Agents actius", + "activityKitDisabledTitle": "Les activitats en directe estan desactivades", + "activityKitDisabledBody": "Activa les activitats en directe a Configuració per veure els agents actius a la pantalla de bloqueig." } } diff --git a/apps/mobile/src/i18n/locales/ckb.json b/apps/mobile/src/i18n/locales/ckb.json index cd1202e9ca..b162480f86 100644 --- a/apps/mobile/src/i18n/locales/ckb.json +++ b/apps/mobile/src/i18n/locales/ckb.json @@ -3191,5 +3191,20 @@ "historyRetry": "نەتوانرا مێژووی گفتوگۆ بار بکرێت.", "catalogRetry": "نەتوانرا مۆدێلەکان بار بکرێن", "sendError": "شکستی هێنا لە ناردنی پەیام. تکایە دووبارە هەوڵ بدە." + }, + "glanceable": { + "waiting": "لە نوێکردنەوەی ئەجێنتەکاندایە", + "empty": "هیچ کارێک لە ئەنجامداندان نییە", + "stale": "ئێستا ناتوانرێت نوێ بکرێتەوە", + "expired": "دۆخەکە بەسەرچووە", + "signedOut": "بچۆ ژوورەوە بۆ بینینی ئەجێنتەکان", + "privacy": "ئەجێنتەکان شاراونەتەوە", + "openAgents": "کردنەوەی ئەجێنتەکان", + "running": "لە کاردایە", + "needsInput": "پێویستی بە داخڵکردن", + "reconnecting": "لە پەیوەستبوونەوەدایە", + "channelName": "ئەجێنتە چالاکەکان", + "activityKitDisabledTitle": "چالاکییە ڕاستەوخۆکان ناچالاکن", + "activityKitDisabledBody": "چالاکییە ڕاستەوخۆکان لە ڕێکخستنەکان چالاک بکە بۆ بینینی ئەجێنتە چالاکەکان لە شاشەی قوفڵ." } } diff --git a/apps/mobile/src/i18n/locales/cs.json b/apps/mobile/src/i18n/locales/cs.json index e1d3e860dd..689e0cef35 100644 --- a/apps/mobile/src/i18n/locales/cs.json +++ b/apps/mobile/src/i18n/locales/cs.json @@ -3233,5 +3233,20 @@ "historyRetry": "Historie chatu se nepodařila načíst.", "catalogRetry": "Modely se nepodařilo načíst", "sendError": "Zprávu se nepodařilo odeslat. Zkuste to prosím znovu." + }, + "glanceable": { + "waiting": "Aktualizace agentů", + "empty": "Žádná probíhající práce", + "stale": "Nyní nelze aktualizovat", + "expired": "Platnost stavu vypršela", + "signedOut": "Přihlaste se pro zobrazení agentů", + "privacy": "Agenti jsou skrytí", + "openAgents": "Otevřít agenty", + "running": "BĚŽÍ", + "needsInput": "vyžaduje vstup", + "reconnecting": "Obnovování připojení", + "channelName": "Aktivní agenti", + "activityKitDisabledTitle": "Živé aktivity jsou vypnuté", + "activityKitDisabledBody": "Zapněte Živé aktivity v Nastavení, abyste viděli aktivní agenty na zamknuté obrazovce." } } diff --git a/apps/mobile/src/i18n/locales/cy.json b/apps/mobile/src/i18n/locales/cy.json index b3502cd769..ae034409c3 100644 --- a/apps/mobile/src/i18n/locales/cy.json +++ b/apps/mobile/src/i18n/locales/cy.json @@ -3275,5 +3275,20 @@ "historyRetry": "Ni ellid llwytho hanes y sgwrs.", "catalogRetry": "Methodd llwytho modelau", "sendError": "Methodd anfon neges. Ceisiwch eto." + }, + "glanceable": { + "waiting": "Yn diweddaru asiantau", + "empty": "Dim gwaith ar y gweill", + "stale": "Methu diweddaru nawr", + "expired": "Mae'r statws wedi dod i ben", + "signedOut": "Mewngofnodwch i weld asiantau", + "privacy": "Asiantau wedi'u cuddio", + "openAgents": "Agorwch asiantau", + "running": "YN RHEDEG", + "needsInput": "angen mewnbwn", + "reconnecting": "Yn ailgysylltu", + "channelName": "Asiantau gweithredol", + "activityKitDisabledTitle": "Mae Gweithgareddau Byw wedi'u diffodd", + "activityKitDisabledBody": "Trowch Weithgareddau Byw ymlaen yn Gosodiadau i weld Asiantau gweithredol ar y Sgrin Glo." } } diff --git a/apps/mobile/src/i18n/locales/da.json b/apps/mobile/src/i18n/locales/da.json index 4a71b1e63d..e4e3101fc3 100644 --- a/apps/mobile/src/i18n/locales/da.json +++ b/apps/mobile/src/i18n/locales/da.json @@ -3191,5 +3191,20 @@ "historyRetry": "Kunne ikke indlæse chathistorik.", "catalogRetry": "Kunne ikke indlæse modeller", "sendError": "Kunne ikke sende beskeden. Prøv igen." + }, + "glanceable": { + "waiting": "Opdaterer agenter", + "empty": "Intet arbejde i gang", + "stale": "Kan ikke opdatere nu", + "expired": "Status er udløbet", + "signedOut": "Log ind for at se agenter", + "privacy": "Agenter er skjult", + "openAgents": "Åbn agenter", + "running": "KØRER", + "needsInput": "kræver input", + "reconnecting": "Genopretter forbindelsen", + "channelName": "Aktive agenter", + "activityKitDisabledTitle": "Liveaktiviteter er slået fra", + "activityKitDisabledBody": "Slå Liveaktiviteter til i Indstillinger for at se aktive agenter på låseskærmen." } } diff --git a/apps/mobile/src/i18n/locales/de.json b/apps/mobile/src/i18n/locales/de.json index 9dff50c07d..0d65fb86b4 100644 --- a/apps/mobile/src/i18n/locales/de.json +++ b/apps/mobile/src/i18n/locales/de.json @@ -3191,5 +3191,20 @@ "historyRetry": "Chatverlauf konnte nicht geladen werden.", "catalogRetry": "Modelle konnten nicht geladen werden", "sendError": "Die Nachricht konnte nicht gesendet werden. Versuchen Sie es erneut." + }, + "glanceable": { + "waiting": "Agenten werden aktualisiert", + "empty": "Keine laufende Arbeit", + "stale": "Aktualisierung derzeit nicht möglich", + "expired": "Status abgelaufen", + "signedOut": "Melde dich an, um Agenten zu sehen", + "privacy": "Agenten ausgeblendet", + "openAgents": "Agenten öffnen", + "running": "LÄUFT", + "needsInput": "Eingabe erforderlich", + "reconnecting": "Verbindung wird wiederhergestellt", + "channelName": "Aktive Agenten", + "activityKitDisabledTitle": "Live-Aktivitäten sind deaktiviert", + "activityKitDisabledBody": "Aktiviere Live-Aktivitäten in den Einstellungen, um aktive Agenten auf dem Sperrbildschirm zu sehen." } } diff --git a/apps/mobile/src/i18n/locales/el.json b/apps/mobile/src/i18n/locales/el.json index e4d1a74c0f..afc450b50f 100644 --- a/apps/mobile/src/i18n/locales/el.json +++ b/apps/mobile/src/i18n/locales/el.json @@ -3191,5 +3191,20 @@ "historyRetry": "Δεν ήταν δυνατή η φόρτωση του ιστορικού συνομιλίας.", "catalogRetry": "Δεν ήταν δυνατή η φόρτωση των μοντέλων", "sendError": "Δεν ήταν δυνατή η αποστολή του μηνύματος. Δοκιμάστε ξανά." + }, + "glanceable": { + "waiting": "Ενημέρωση πρακτόρων", + "empty": "Καμία εργασία σε εξέλιξη", + "stale": "Δεν είναι δυνατή η ενημέρωση τώρα", + "expired": "Η κατάσταση έληξε", + "signedOut": "Συνδεθείτε για να δείτε τους πράκτορες", + "privacy": "Οι πράκτορες είναι κρυφοί", + "openAgents": "Ανοίξτε τους πράκτορες", + "running": "Σε εξέλιξη", + "needsInput": "χρειάζεται είσοδο", + "reconnecting": "Επανασύνδεση", + "channelName": "Ενεργοί πράκτορες", + "activityKitDisabledTitle": "Οι Ζωντανές δραστηριότητες είναι απενεργοποιημένες", + "activityKitDisabledBody": "Ενεργοποιήστε τις Ζωντανές δραστηριότητες στις Ρυθμίσεις για να δείτε τους ενεργούς πράκτορες στην Οθόνη κλειδώματος." } } diff --git a/apps/mobile/src/i18n/locales/es.json b/apps/mobile/src/i18n/locales/es.json index 56f4c5c1a7..8eeb9eaa2d 100644 --- a/apps/mobile/src/i18n/locales/es.json +++ b/apps/mobile/src/i18n/locales/es.json @@ -3212,5 +3212,20 @@ "historyRetry": "No se pudo cargar el historial del chat.", "catalogRetry": "No se pudieron cargar los modelos", "sendError": "No se pudo enviar el mensaje. Inténtalo de nuevo." + }, + "glanceable": { + "waiting": "Actualizando agentes", + "empty": "No hay trabajo en curso", + "stale": "No se puede actualizar ahora", + "expired": "Estado caducado", + "signedOut": "Inicia sesión para ver los agentes", + "privacy": "Agentes ocultos", + "openAgents": "Abrir agentes", + "running": "EN EJECUCIÓN", + "needsInput": "requiere entrada", + "reconnecting": "Reconectando", + "channelName": "Agentes activos", + "activityKitDisabledTitle": "Las actividades en directo están desactivadas", + "activityKitDisabledBody": "Activa las actividades en directo en Ajustes para ver los agentes activos en la pantalla de bloqueo." } } diff --git a/apps/mobile/src/i18n/locales/et.json b/apps/mobile/src/i18n/locales/et.json index cf72bc1717..bcc271ae98 100644 --- a/apps/mobile/src/i18n/locales/et.json +++ b/apps/mobile/src/i18n/locales/et.json @@ -3191,5 +3191,20 @@ "historyRetry": "Vestluse ajaloo laadimine ei õnnestunud.", "catalogRetry": "Mudeleid ei õnnestunud laadida", "sendError": "Sõnumi saatmine ebaõnnestus. Proovi uuesti." + }, + "glanceable": { + "waiting": "Agentide uuendamine", + "empty": "Ühtegi tööd pole pooleli", + "stale": "Praegu ei saa uuendada", + "expired": "Olek on aegunud", + "signedOut": "Agentide nägemiseks logige sisse", + "privacy": "Agendid on peidetud", + "openAgents": "Avage agendid", + "running": "TÖÖTAB", + "needsInput": "vajab sisendit", + "reconnecting": "Ühenduse taastamine", + "channelName": "Aktiivsed agendid", + "activityKitDisabledTitle": "Reaalajas tegevused on välja lülitatud", + "activityKitDisabledBody": "Lülitage seadetes reaalajas tegevused sisse, et näha aktiivseid agente lukustuskuval." } } diff --git a/apps/mobile/src/i18n/locales/eu.json b/apps/mobile/src/i18n/locales/eu.json index bff8407646..69401ac1c6 100644 --- a/apps/mobile/src/i18n/locales/eu.json +++ b/apps/mobile/src/i18n/locales/eu.json @@ -3191,5 +3191,20 @@ "historyRetry": "Ezin izan da txat-historia kargatu.", "catalogRetry": "Modeloak ezin izan dira kargatu", "sendError": "Ezin izan da mezua bidali. Saiatu berriro." + }, + "glanceable": { + "waiting": "Agenteak eguneratzen", + "empty": "Ez dago lanik abian", + "stale": "Ezin da orain eguneratu", + "expired": "Egoera iraungi da", + "signedOut": "Hasi saioa agenteak ikusteko", + "privacy": "Agenteak ezkutatuta", + "openAgents": "Ireki agenteak", + "running": "Exekutatzen", + "needsInput": "sarreraren zain", + "reconnecting": "Berriro konektatzen", + "channelName": "Agente aktiboak", + "activityKitDisabledTitle": "Zuzeneko jarduerak desaktibatuta daude", + "activityKitDisabledBody": "Aktibatu Zuzeneko jarduerak Ezarpenetan, Agente aktiboak Blokeo-pantailan ikusteko." } } diff --git a/apps/mobile/src/i18n/locales/fa.json b/apps/mobile/src/i18n/locales/fa.json index 769996caec..4742b0a89b 100644 --- a/apps/mobile/src/i18n/locales/fa.json +++ b/apps/mobile/src/i18n/locales/fa.json @@ -3191,5 +3191,20 @@ "historyRetry": "تاریخچه گفتگو بارگذاری نشد.", "catalogRetry": "بارگذاری مدل‌ها ناموفق بود", "sendError": "ارسال پیام ممکن نشد. دوباره تلاش کنید." + }, + "glanceable": { + "waiting": "در حال به‌روزرسانی عامل‌ها", + "empty": "هیچ کاری در حال انجام نیست", + "stale": "اکنون به‌روزرسانی ممکن نیست", + "expired": "وضعیت منقضی شد", + "signedOut": "برای دیدن عامل‌ها وارد شوید", + "privacy": "عامل‌ها پنهان هستند", + "openAgents": "عامل‌ها را باز کنید", + "running": "در حال اجرا", + "needsInput": "نیاز به ورودی", + "reconnecting": "در حال اتصال مجدد", + "channelName": "عامل‌های فعال", + "activityKitDisabledTitle": "فعالیت‌های زنده خاموش هستند", + "activityKitDisabledBody": "برای دیدن عامل‌های فعال در صفحه قفل، فعالیت‌های زنده را در تنظیمات روشن کنید." } } diff --git a/apps/mobile/src/i18n/locales/fi.json b/apps/mobile/src/i18n/locales/fi.json index 7b2db4bbad..bec672d01b 100644 --- a/apps/mobile/src/i18n/locales/fi.json +++ b/apps/mobile/src/i18n/locales/fi.json @@ -3191,5 +3191,20 @@ "historyRetry": "Keskusteluhistoriaa ei voitu ladata.", "catalogRetry": "Malleja ei voitu ladata", "sendError": "Viestiä ei voitu lähettää. Yritä uudelleen." + }, + "glanceable": { + "waiting": "Päivitetään agentteja", + "empty": "Ei keskeneräisiä töitä", + "stale": "Päivitys ei onnistu nyt", + "expired": "Tila vanhentunut", + "signedOut": "Kirjaudu sisään nähdäksesi agentit", + "privacy": "Agentit piilotettu", + "openAgents": "Avaa agentit", + "running": "KÄYNNISSÄ", + "needsInput": "vaatii syötettä", + "reconnecting": "Yhdistetään uudelleen", + "channelName": "Aktiiviset agentit", + "activityKitDisabledTitle": "Live-aktiviteetit ovat pois päältä", + "activityKitDisabledBody": "Ota live-aktiviteetit käyttöön Asetuksissa, niin näet aktiiviset agentit lukitulla näytöllä." } } diff --git a/apps/mobile/src/i18n/locales/fil.json b/apps/mobile/src/i18n/locales/fil.json index 3e097b8437..0aa3f3643a 100644 --- a/apps/mobile/src/i18n/locales/fil.json +++ b/apps/mobile/src/i18n/locales/fil.json @@ -3191,5 +3191,20 @@ "historyRetry": "Hindi ma-load ang kasaysayan ng chat.", "catalogRetry": "Hindi maload ang mga modelo", "sendError": "Nabigong magpadala ng mensahe. Pakisubukang muli." + }, + "glanceable": { + "waiting": "Ina-update ang mga agent", + "empty": "Walang kasalukuyang gawain", + "stale": "Hindi makapag-update ngayon", + "expired": "Nag-expire ang katayuan", + "signedOut": "Mag-sign in para makita ang mga agent", + "privacy": "Nakatago ang mga agent", + "openAgents": "Buksan ang mga agent", + "running": "TUMATAKBO", + "needsInput": "kailangan ng input", + "reconnecting": "Muling kumokonekta", + "channelName": "Mga aktibong agent", + "activityKitDisabledTitle": "Naka-off ang Mga Live na Aktibidad", + "activityKitDisabledBody": "I-on ang Mga Live na Aktibidad sa Mga setting para makita ang Mga aktibong agent sa Naka-lock na Screen." } } diff --git a/apps/mobile/src/i18n/locales/fr.json b/apps/mobile/src/i18n/locales/fr.json index 80522ea5da..a062104a15 100644 --- a/apps/mobile/src/i18n/locales/fr.json +++ b/apps/mobile/src/i18n/locales/fr.json @@ -3212,5 +3212,20 @@ "historyRetry": "Impossible de charger l'historique de la conversation.", "catalogRetry": "Impossible de charger les modèles", "sendError": "Impossible d'envoyer le message. Veuillez réessayer." + }, + "glanceable": { + "waiting": "Mise à jour des agents", + "empty": "Aucun travail en cours", + "stale": "Mise à jour impossible pour le moment", + "expired": "Statut expiré", + "signedOut": "Connectez-vous pour voir les agents", + "privacy": "Agents masqués", + "openAgents": "Ouvrir les agents", + "running": "EN COURS", + "needsInput": "saisie requise", + "reconnecting": "Reconnexion en cours", + "channelName": "Agents actifs", + "activityKitDisabledTitle": "Les activités en direct sont désactivées", + "activityKitDisabledBody": "Activez les activités en direct dans Réglages pour voir les agents actifs sur l'écran verrouillé." } } diff --git a/apps/mobile/src/i18n/locales/ga.json b/apps/mobile/src/i18n/locales/ga.json index 10b741e8f2..0e54003877 100644 --- a/apps/mobile/src/i18n/locales/ga.json +++ b/apps/mobile/src/i18n/locales/ga.json @@ -3254,5 +3254,20 @@ "historyRetry": "Níorbh fhéidir stair an chomhrá a lódáil.", "catalogRetry": "Níorbh fhéidir múnlaí a lódáil", "sendError": "Níorbh fhéidir an teachtaireacht a sheoladh. Bain triail as arís." + }, + "glanceable": { + "waiting": "Gníomhairí á nuashonrú", + "empty": "Níl aon obair ar siúl", + "stale": "Ní féidir nuashonrú anois", + "expired": "Stádas imithe in éag", + "signedOut": "Sínigh isteach chun gníomhairí a fheiceáil", + "privacy": "Gníomhairí i bhfolach", + "openAgents": "Oscail gníomhairí", + "running": "AG RITH", + "needsInput": "teastaíonn ionchur", + "reconnecting": "Ag athcheangal", + "channelName": "Gníomhairí gníomhacha", + "activityKitDisabledTitle": "Tá Gníomhaíochtaí Beo as", + "activityKitDisabledBody": "Cumasaigh Gníomhaíochtaí Beo sna Socruithe chun Gníomhairí gníomhacha a fheiceáil ar an Scáileán Glasála." } } diff --git a/apps/mobile/src/i18n/locales/gl.json b/apps/mobile/src/i18n/locales/gl.json index 0bda69343a..e59937cb68 100644 --- a/apps/mobile/src/i18n/locales/gl.json +++ b/apps/mobile/src/i18n/locales/gl.json @@ -3191,5 +3191,20 @@ "historyRetry": "Non se puido cargar o historial do chat.", "catalogRetry": "Non se puideron cargar os modelos", "sendError": "Non se puido enviar a mensaxe. Téntao de novo." + }, + "glanceable": { + "waiting": "Actualizando axentes", + "empty": "Non hai traballo en curso", + "stale": "Non se pode actualizar agora", + "expired": "Estado caducado", + "signedOut": "Inicia sesión para ver os axentes", + "privacy": "Axentes ocultos", + "openAgents": "Abrir axentes", + "running": "Executando", + "needsInput": "precisa entrada", + "reconnecting": "Reconectando", + "channelName": "Axentes activos", + "activityKitDisabledTitle": "As actividades en directo están desactivadas", + "activityKitDisabledBody": "Activa as actividades en directo en Configuración para ver os axentes activos na pantalla de bloqueo." } } diff --git a/apps/mobile/src/i18n/locales/gu.json b/apps/mobile/src/i18n/locales/gu.json index 7c4480f51a..b23be114ea 100644 --- a/apps/mobile/src/i18n/locales/gu.json +++ b/apps/mobile/src/i18n/locales/gu.json @@ -3191,5 +3191,20 @@ "historyRetry": "વાતચીત ઇતિહાસ લોડ કરી શકાયો નહીં.", "catalogRetry": "મોડેલ લોડ કરી શકાયા નહીં", "sendError": "સંદેશો મોકલી શકાયો નથી. ફરી પ્રયાસ કરો." + }, + "glanceable": { + "waiting": "એજન્ટો અપડેટ થઈ રહ્યા છે", + "empty": "કોઈ કાર્ય ચાલુ નથી", + "stale": "હમણાં અપડેટ કરી શકાતું નથી", + "expired": "સ્થિતિની સમયસીમા સમાપ્ત થઈ", + "signedOut": "એજન્ટો જોવા માટે સાઇન ઇન કરો", + "privacy": "એજન્ટો છુપાવેલા છે", + "openAgents": "એજન્ટો ખોલો", + "running": "ચાલી રહ્યું છે", + "needsInput": "ઇનપુટ જરૂરી", + "reconnecting": "ફરી કનેક્ટ થઈ રહ્યું છે", + "channelName": "સક્રિય એજન્ટો", + "activityKitDisabledTitle": "લાઇવ પ્રવૃત્તિઓ બંધ છે", + "activityKitDisabledBody": "લૉક સ્ક્રીન પર સક્રિય એજન્ટો જોવા માટે સેટિંગ્સમાં લાઇવ પ્રવૃત્તિઓ ચાલુ કરો." } } diff --git a/apps/mobile/src/i18n/locales/ha.json b/apps/mobile/src/i18n/locales/ha.json index 437e4f041e..0707d0bb16 100644 --- a/apps/mobile/src/i18n/locales/ha.json +++ b/apps/mobile/src/i18n/locales/ha.json @@ -3191,5 +3191,20 @@ "historyRetry": "Ba a iya loda tarihin tattaunawa.", "catalogRetry": "An kasa ɗora samfuran", "sendError": "An kasa aika saƙon. Sake gwadawa." + }, + "glanceable": { + "waiting": "Ana sabunta wakilai", + "empty": "Babu aikin da ke gudana", + "stale": "Ba a iya sabuntawa yanzu", + "expired": "Matsayi ya ƙare", + "signedOut": "Shiga don ganin wakilai", + "privacy": "An ɓoye wakilai", + "openAgents": "Buɗe wakilai", + "running": "Ana gudana", + "needsInput": "yana buƙatar bayani", + "reconnecting": "Ana sake haɗawa", + "channelName": "Wakilai da ke aiki", + "activityKitDisabledTitle": "Ayyukan Kai Tsaye suna a kashe", + "activityKitDisabledBody": "Kunna Ayyukan Kai Tsaye a cikin Saituna don ganin Wakilai da ke Aiki a kan Allon Kulle." } } diff --git a/apps/mobile/src/i18n/locales/he.json b/apps/mobile/src/i18n/locales/he.json index fb0e486aa5..f673155d73 100644 --- a/apps/mobile/src/i18n/locales/he.json +++ b/apps/mobile/src/i18n/locales/he.json @@ -3212,5 +3212,20 @@ "historyRetry": "לא ניתן היה לטעון את היסטוריית השיחה.", "catalogRetry": "לא ניתן לטעון דגמים", "sendError": "לא ניתן היה לשלוח את ההודעה. נסה שוב." + }, + "glanceable": { + "waiting": "מעדכן סוכנים", + "empty": "אין עבודה בתהליך", + "stale": "לא ניתן לעדכן כעת", + "expired": "תוקף המצב פג", + "signedOut": "היכנס כדי לראות סוכנים", + "privacy": "הסוכנים מוסתרים", + "openAgents": "פתח סוכנים", + "running": "רץ", + "needsInput": "נדרש קלט", + "reconnecting": "מתחבר מחדש", + "channelName": "סוכנים פעילים", + "activityKitDisabledTitle": "פעילויות בזמן אמת כבויות", + "activityKitDisabledBody": "הפעל פעילויות בזמן אמת בהגדרות כדי לראות סוכנים פעילים במסך הנעילה." } } diff --git a/apps/mobile/src/i18n/locales/hi.json b/apps/mobile/src/i18n/locales/hi.json index c22ccf2567..63e95174cc 100644 --- a/apps/mobile/src/i18n/locales/hi.json +++ b/apps/mobile/src/i18n/locales/hi.json @@ -3191,5 +3191,20 @@ "historyRetry": "चैट इतिहास लोड नहीं किया जा सका।", "catalogRetry": "मॉडल लोड नहीं हो सके", "sendError": "संदेश भेजा नहीं जा सका। फिर से कोशिश करें।" + }, + "glanceable": { + "waiting": "एजेंट अपडेट हो रहे हैं", + "empty": "कोई कार्य प्रगति में नहीं है", + "stale": "अभी अपडेट नहीं हो सकता", + "expired": "स्थिति की समय सीमा समाप्त हो गई", + "signedOut": "एजेंट देखने के लिए साइन इन करें", + "privacy": "एजेंट छिपे हुए हैं", + "openAgents": "एजेंट खोलें", + "running": "चालू", + "needsInput": "इनपुट आवश्यक", + "reconnecting": "फिर से कनेक्ट हो रहा है", + "channelName": "सक्रिय एजेंट", + "activityKitDisabledTitle": "लाइव ऐक्टिविटी बंद हैं", + "activityKitDisabledBody": "लॉक स्क्रीन पर सक्रिय एजेंट देखने के लिए सेटिंग में लाइव ऐक्टिविटी चालू करें।" } } diff --git a/apps/mobile/src/i18n/locales/hr.json b/apps/mobile/src/i18n/locales/hr.json index 10a411fbac..7cb949d93a 100644 --- a/apps/mobile/src/i18n/locales/hr.json +++ b/apps/mobile/src/i18n/locales/hr.json @@ -3212,5 +3212,20 @@ "historyRetry": "Nije moguće učitati povijest razgovora.", "catalogRetry": "Učitavanje modela nije uspjelo", "sendError": "Nije moguće poslati poruku. Pokušajte ponovno." + }, + "glanceable": { + "waiting": "Ažuriranje agenata", + "empty": "Nema rada u tijeku", + "stale": "Trenutačno nije moguće ažurirati", + "expired": "Status je istekao", + "signedOut": "Prijavite se da biste vidjeli agente", + "privacy": "Agenti su skriveni", + "openAgents": "Otvorite agente", + "running": "RADI", + "needsInput": "treba unos", + "reconnecting": "Ponovno povezivanje", + "channelName": "Aktivni agenti", + "activityKitDisabledTitle": "Aktivnosti uživo su isključene", + "activityKitDisabledBody": "Uključite Aktivnosti uživo u Postavkama kako biste vidjeli aktivne agente na zaključanom zaslonu." } } diff --git a/apps/mobile/src/i18n/locales/ht.json b/apps/mobile/src/i18n/locales/ht.json index 22f48c0dfa..92272deb3c 100644 --- a/apps/mobile/src/i18n/locales/ht.json +++ b/apps/mobile/src/i18n/locales/ht.json @@ -3191,5 +3191,20 @@ "historyRetry": "Pa t ka chaje istwa chat la.", "catalogRetry": "Pa t ka chaje modèl yo", "sendError": "Pa t ka voye mesaj la. Tanpri eseye ankò." + }, + "glanceable": { + "waiting": "Ap mete ajans yo ajou", + "empty": "Pa gen travay k ap fèt", + "stale": "Pa ka mete ajou kounye a", + "expired": "Estati a ekspire", + "signedOut": "Konekte pou wè ajans yo", + "privacy": "Ajans yo kache", + "openAgents": "Louvri ajans yo", + "running": "AP KOURI", + "needsInput": "bezwen input", + "reconnecting": "Ap rekonekte", + "channelName": "Ajans aktif yo", + "activityKitDisabledTitle": "Aktivite an dirèk yo fèmen", + "activityKitDisabledBody": "Aktive Aktivite an dirèk nan Paramèt pou wè Ajans aktif yo sou Ekran bloke a." } } diff --git a/apps/mobile/src/i18n/locales/hu.json b/apps/mobile/src/i18n/locales/hu.json index d53fc5dfde..72be12dc20 100644 --- a/apps/mobile/src/i18n/locales/hu.json +++ b/apps/mobile/src/i18n/locales/hu.json @@ -3191,5 +3191,20 @@ "historyRetry": "Nem sikerült betölteni a csevegés előzményeit.", "catalogRetry": "Nem sikerült betölteni a modelleket", "sendError": "Az üzenet küldése nem sikerült. Próbálja újra." + }, + "glanceable": { + "waiting": "Ügynökök frissítése", + "empty": "Nincs folyamatban lévő munka", + "stale": "Most nem frissíthető", + "expired": "Az állapot lejárt", + "signedOut": "Jelentkezzen be az ügynökök megtekintéséhez", + "privacy": "Ügynökök elrejtve", + "openAgents": "Ügynökök megnyitása", + "running": "Folyamatban", + "needsInput": "bemenetet igényel", + "reconnecting": "Újracsatlakozás", + "channelName": "Aktív ügynökök", + "activityKitDisabledTitle": "Az Élő tevékenységek ki vannak kapcsolva", + "activityKitDisabledBody": "Kapcsolja be az Élő tevékenységeket a Beállításokban, hogy az aktív ügynökök megjelenjenek a zárolási képernyőn." } } diff --git a/apps/mobile/src/i18n/locales/hy.json b/apps/mobile/src/i18n/locales/hy.json index bdb556bee2..fd86d5065b 100644 --- a/apps/mobile/src/i18n/locales/hy.json +++ b/apps/mobile/src/i18n/locales/hy.json @@ -3191,5 +3191,20 @@ "historyRetry": "Հնարավոր չեղավ բեռնել զրույցի պատմությունը.", "catalogRetry": "Հնարավոր չեղավ բեռնել մոդելները", "sendError": "Հնարավոր չեղավ ուղարկել հաղորդագրությունը։ Կրկին փորձեք։" + }, + "glanceable": { + "waiting": "Գործակալների թարմացում", + "empty": "Ընթացքի մեջ աշխատանք չկա", + "stale": "Այժմ հնարավոր չէ թարմացնել", + "expired": "Կարգավիճակի ժամկետը լրացել է", + "signedOut": "Մուտք գործեք՝ գործակալներին տեսնելու համար", + "privacy": "Գործակալները թաքցված են", + "openAgents": "Բացեք գործակալները", + "running": "Ընթացքի մեջ է", + "needsInput": "մուտքագրման կարիք ունի", + "reconnecting": "Կրկին միացում", + "channelName": "Ակտիվ գործակալներ", + "activityKitDisabledTitle": "Ուղիղ ակտիվություններն անջատված են", + "activityKitDisabledBody": "Միացրեք «Ուղիղ ակտիվություններ»-ը Կարգավորումներում՝ ակտիվ գործակալներին կողպման էկրանին տեսնելու համար։" } } diff --git a/apps/mobile/src/i18n/locales/id.json b/apps/mobile/src/i18n/locales/id.json index d9d6ec423e..53be7b234d 100644 --- a/apps/mobile/src/i18n/locales/id.json +++ b/apps/mobile/src/i18n/locales/id.json @@ -3191,5 +3191,20 @@ "historyRetry": "Tidak dapat memuat riwayat obrolan.", "catalogRetry": "Tidak dapat memuat model", "sendError": "Tidak dapat mengirim pesan. Coba lagi." + }, + "glanceable": { + "waiting": "Memperbarui agen", + "empty": "Tidak ada pekerjaan yang berlangsung", + "stale": "Tidak dapat memperbarui sekarang", + "expired": "Status kedaluwarsa", + "signedOut": "Masuk untuk melihat agen", + "privacy": "Agen disembunyikan", + "openAgents": "Buka agen", + "running": "BERJALAN", + "needsInput": "memerlukan input", + "reconnecting": "Menghubungkan kembali", + "channelName": "Agen aktif", + "activityKitDisabledTitle": "Aktivitas Langsung nonaktif", + "activityKitDisabledBody": "Aktifkan Aktivitas Langsung di Pengaturan untuk melihat Agen Aktif di Layar Terkunci." } } diff --git a/apps/mobile/src/i18n/locales/ig.json b/apps/mobile/src/i18n/locales/ig.json index 3b160e1318..c4aadb9e50 100644 --- a/apps/mobile/src/i18n/locales/ig.json +++ b/apps/mobile/src/i18n/locales/ig.json @@ -3191,5 +3191,20 @@ "historyRetry": "Enweghị ike ibutu akụkọ nkata.", "catalogRetry": "Enweghị ike ibunye ụdị", "sendError": "Enweghị ike izipu ozi ahụ. Nwaa ọzọ." + }, + "glanceable": { + "waiting": "Na-emelite ndị ọrụ", + "empty": "Enweghị ọrụ na-aga n'ihu", + "stale": "Enweghị ike imelite ugbu a", + "expired": "Oge ọnọdụ agwụla", + "signedOut": "Banye iji hụ ndị ọrụ", + "privacy": "Ezochiri ndị ọrụ", + "openAgents": "Mepee ndị ọrụ", + "running": "NA-AGBA", + "needsInput": "chọrọ ntinye", + "reconnecting": "Na-ejikọ ọzọ", + "channelName": "Ndị ọrụ na-arụ ọrụ", + "activityKitDisabledTitle": "Agbanyụrụ Ihe Omume Dị Ndụ", + "activityKitDisabledBody": "Gbanye Ihe Omume Dị Ndụ na Ntọala iji hụ Ndị ọrụ na-arụ ọrụ na Ihuenyo Mkpọchi." } } diff --git a/apps/mobile/src/i18n/locales/is.json b/apps/mobile/src/i18n/locales/is.json index 0a8d0ab506..0988544e0d 100644 --- a/apps/mobile/src/i18n/locales/is.json +++ b/apps/mobile/src/i18n/locales/is.json @@ -3191,5 +3191,20 @@ "historyRetry": "Ekki tókst að hlaða samtalsferilinn.", "catalogRetry": "Ekki tókst að hlaða líkön", "sendError": "Ekki tókst að senda skilaboðin. Reyndu aftur." + }, + "glanceable": { + "waiting": "Uppfærir umboð", + "empty": "Engin vinna í gangi", + "stale": "Ekki hægt að uppfæra núna", + "expired": "Staða útrunnin", + "signedOut": "Skráðu þig inn til að sjá umboð", + "privacy": "Umboð falin", + "openAgents": "Opna umboð", + "running": "Í gangi", + "needsInput": "þarfnast inntaks", + "reconnecting": "Tengist aftur", + "channelName": "Virk umboð", + "activityKitDisabledTitle": "Slökkt er á Beinni virkni", + "activityKitDisabledBody": "Kveiktu á Beinni virkni í Stillingum til að sjá Virk umboð á Lásskjánum." } } diff --git a/apps/mobile/src/i18n/locales/it.json b/apps/mobile/src/i18n/locales/it.json index 003c41ead7..a6dff9624e 100644 --- a/apps/mobile/src/i18n/locales/it.json +++ b/apps/mobile/src/i18n/locales/it.json @@ -3212,5 +3212,20 @@ "historyRetry": "Impossibile caricare la cronologia della chat.", "catalogRetry": "Impossibile caricare i modelli", "sendError": "Impossibile inviare il messaggio. Riprova." + }, + "glanceable": { + "waiting": "Aggiornamento degli agenti", + "empty": "Nessun lavoro in corso", + "stale": "Impossibile aggiornare ora", + "expired": "Stato scaduto", + "signedOut": "Accedi per vedere gli agenti", + "privacy": "Agenti nascosti", + "openAgents": "Apri agenti", + "running": "IN ESECUZIONE", + "needsInput": "richiede input", + "reconnecting": "Riconnessione in corso", + "channelName": "Agenti attivi", + "activityKitDisabledTitle": "Le attività in tempo reale sono disattivate", + "activityKitDisabledBody": "Attiva le attività in tempo reale in Impostazioni per vedere gli agenti attivi sulla schermata di blocco." } } diff --git a/apps/mobile/src/i18n/locales/ja.json b/apps/mobile/src/i18n/locales/ja.json index 2c1fc47d4c..01c1057b01 100644 --- a/apps/mobile/src/i18n/locales/ja.json +++ b/apps/mobile/src/i18n/locales/ja.json @@ -3191,5 +3191,20 @@ "historyRetry": "チャット履歴を読み込めませんでした。", "catalogRetry": "モデルを読み込めませんでした", "sendError": "メッセージを送信できませんでした。もう一度お試しください。" + }, + "glanceable": { + "waiting": "エージェントを更新中", + "empty": "進行中の作業はありません", + "stale": "現在更新できません", + "expired": "ステータスの有効期限が切れました", + "signedOut": "エージェントを表示するにはサインインしてください", + "privacy": "エージェントは非表示です", + "openAgents": "エージェントを開く", + "running": "実行中", + "needsInput": "入力が必要", + "reconnecting": "再接続中", + "channelName": "アクティブなエージェント", + "activityKitDisabledTitle": "ライブアクティビティはオフです", + "activityKitDisabledBody": "ロック画面にアクティブなエージェントを表示するには、設定でライブアクティビティをオンにしてください。" } } diff --git a/apps/mobile/src/i18n/locales/ka.json b/apps/mobile/src/i18n/locales/ka.json index 7198ea9e95..fb467472e1 100644 --- a/apps/mobile/src/i18n/locales/ka.json +++ b/apps/mobile/src/i18n/locales/ka.json @@ -3191,5 +3191,20 @@ "historyRetry": "ჩატის ისტორიის ჩატვირთვა ვერ მოხერხდა.", "catalogRetry": "მოდელების ჩატვირთვა ვერ მოხერხდა", "sendError": "შეტყობინების გაგზავნა ვერ მოხერხდა. სცადეთ ხელახლა." + }, + "glanceable": { + "waiting": "აგენტების განახლება", + "empty": "მიმდინარე სამუშაო არ არის", + "stale": "ახლა განახლება ვერ ხერხდება", + "expired": "სტატუსს ვადა გაუვიდა", + "signedOut": "შედით აგენტების სანახავად", + "privacy": "აგენტები დამალულია", + "openAgents": "აგენტების გახსნა", + "running": "მუშაობს", + "needsInput": "მოითხოვს შეყვანას", + "reconnecting": "კავშირის აღდგენა", + "channelName": "აქტიური აგენტები", + "activityKitDisabledTitle": "ცოცხალი აქტივობები გამორთულია", + "activityKitDisabledBody": "ჩართეთ ცოცხალი აქტივობები პარამეტრებში, რათა დაბლოკვის ეკრანზე აქტიური აგენტები ნახოთ." } } diff --git a/apps/mobile/src/i18n/locales/kk.json b/apps/mobile/src/i18n/locales/kk.json index 53730ac5fe..f6f359ecf1 100644 --- a/apps/mobile/src/i18n/locales/kk.json +++ b/apps/mobile/src/i18n/locales/kk.json @@ -3191,5 +3191,20 @@ "historyRetry": "Чат тарихын жүктеу мүмкін болмады.", "catalogRetry": "Модельдерді жүктеу мүмкін болмады", "sendError": "Хабарламаны жіберу мүмкін болмады. Қайта көріңіз." + }, + "glanceable": { + "waiting": "Агенттер жаңартылуда", + "empty": "Орындалып жатқан жұмыс жоқ", + "stale": "Қазір жаңарту мүмкін емес", + "expired": "Күйдің мерзімі өтті", + "signedOut": "Агенттерді көру үшін кіріңіз", + "privacy": "Агенттер жасырылған", + "openAgents": "Агенттерді ашу", + "running": "Орындалуда", + "needsInput": "енгізу қажет", + "reconnecting": "Қайта қосылуда", + "channelName": "Белсенді агенттер", + "activityKitDisabledTitle": "Тікелей әрекеттер өшірулі", + "activityKitDisabledBody": "Құлыптау экранында белсенді агенттерді көру үшін Параметрлерде тікелей әрекеттерді қосыңыз." } } diff --git a/apps/mobile/src/i18n/locales/km.json b/apps/mobile/src/i18n/locales/km.json index eca66cc71b..2831e9cf43 100644 --- a/apps/mobile/src/i18n/locales/km.json +++ b/apps/mobile/src/i18n/locales/km.json @@ -3191,5 +3191,20 @@ "historyRetry": "មិនអាចផ្ទុកប្រវត្តិជជែកបានទេ។", "catalogRetry": "មិនអាចផ្ទុកគំរូបានទេ", "sendError": "មិនអាចផ្ញើសារបានទេ។ សូមព្យាយាមម្តងទៀត។" + }, + "glanceable": { + "waiting": "កំពុងធ្វើបច្ចុប្បន្នភាពភ្នាក់ងារ", + "empty": "គ្មានការងារកំពុងដំណើរការ", + "stale": "មិនអាចធ្វើបច្ចុប្បន្នភាពឥឡូវនេះបានទេ", + "expired": "ស្ថានភាពបានផុតកំណត់", + "signedOut": "ចូលដើម្បីមើលភ្នាក់ងារ", + "privacy": "ភ្នាក់ងារត្រូវបានលាក់", + "openAgents": "បើកភ្នាក់ងារ", + "running": "កំពុងដំណើរការ", + "needsInput": "ត្រូវការបញ្ចូល", + "reconnecting": "កំពុងភ្ជាប់ឡើងវិញ", + "channelName": "ភ្នាក់ងារសកម្ម", + "activityKitDisabledTitle": "សកម្មភាពបន្តផ្ទាល់ត្រូវបានបិទ", + "activityKitDisabledBody": "បើកសកម្មភាពបន្តផ្ទាល់នៅក្នុងការកំណត់ ដើម្បីមើលភ្នាក់ងារសកម្មនៅលើអេក្រង់ចាក់សោ។" } } diff --git a/apps/mobile/src/i18n/locales/kn.json b/apps/mobile/src/i18n/locales/kn.json index 3072ec1d85..c91b75e06e 100644 --- a/apps/mobile/src/i18n/locales/kn.json +++ b/apps/mobile/src/i18n/locales/kn.json @@ -3191,5 +3191,20 @@ "historyRetry": "ಚಾಟ್ ಇತಿಹಾಸವನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ.", "catalogRetry": "ಮಾದರಿಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗಲಿಲ್ಲ", "sendError": "ಸಂದೇಶವನ್ನು ಕಳುಹಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ." + }, + "glanceable": { + "waiting": "ಏಜೆಂಟ್‌ಗಳನ್ನು ನವೀಕರಿಸಲಾಗುತ್ತಿದೆ", + "empty": "ಯಾವುದೇ ಕೆಲಸ ಪ್ರಗತಿಯಲ್ಲಿಲ್ಲ", + "stale": "ಈಗ ನವೀಕರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", + "expired": "ಸ್ಥಿತಿಯ ಅವಧಿ ಮುಗಿದಿದೆ", + "signedOut": "ಏಜೆಂಟ್‌ಗಳನ್ನು ನೋಡಲು ಸೈನ್ ಇನ್ ಮಾಡಿ", + "privacy": "ಏಜೆಂಟ್‌ಗಳನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ", + "openAgents": "ಏಜೆಂಟ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ", + "running": "ಚಾಲನೆಯಲ್ಲಿದೆ", + "needsInput": "ಇನ್‌ಪುಟ್ ಅಗತ್ಯವಿದೆ", + "reconnecting": "ಮರುಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ", + "channelName": "ಸಕ್ರಿಯ ಏಜೆಂಟ್‌ಗಳು", + "activityKitDisabledTitle": "ನೇರ ಚಟುವಟಿಕೆಗಳು ಆಫ್ ಆಗಿವೆ", + "activityKitDisabledBody": "ಲಾಕ್ ಪರದೆಯಲ್ಲಿ ಸಕ್ರಿಯ ಏಜೆಂಟ್‌ಗಳನ್ನು ನೋಡಲು ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ನೇರ ಚಟುವಟಿಕೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ." } } diff --git a/apps/mobile/src/i18n/locales/ko.json b/apps/mobile/src/i18n/locales/ko.json index e33e68497e..9af46ce32a 100644 --- a/apps/mobile/src/i18n/locales/ko.json +++ b/apps/mobile/src/i18n/locales/ko.json @@ -3191,5 +3191,20 @@ "historyRetry": "채팅 기록을 불러올 수 없습니다.", "catalogRetry": "모델을 불러올 수 없습니다", "sendError": "메시지를 보낼 수 없습니다. 다시 시도하세요." + }, + "glanceable": { + "waiting": "에이전트 업데이트 중", + "empty": "진행 중인 작업 없음", + "stale": "지금 업데이트할 수 없습니다", + "expired": "상태 만료됨", + "signedOut": "에이전트를 보려면 로그인하세요", + "privacy": "에이전트 숨겨짐", + "openAgents": "에이전트 열기", + "running": "실행 중", + "needsInput": "입력 필요", + "reconnecting": "다시 연결 중", + "channelName": "활성 에이전트", + "activityKitDisabledTitle": "실시간 현황이 꺼져 있습니다", + "activityKitDisabledBody": "잠금 화면에서 활성 에이전트를 보려면 설정에서 실시간 현황을 켜세요." } } diff --git a/apps/mobile/src/i18n/locales/lo.json b/apps/mobile/src/i18n/locales/lo.json index ab27e9f01a..0f5dc14831 100644 --- a/apps/mobile/src/i18n/locales/lo.json +++ b/apps/mobile/src/i18n/locales/lo.json @@ -3191,5 +3191,20 @@ "historyRetry": "ບໍ່ສາມາດໂຫຼດປະຫວັດການສົນທະນາໄດ້.", "catalogRetry": "ບໍ່ສາມາດໂຫຼດໂມເດວ", "sendError": "ບໍ່ສາມາດສົ່ງຂໍ້ຄວາມໄດ້. ລອງອີກຄັ້ງ." + }, + "glanceable": { + "waiting": "ກຳລັງອັບເດດຕົວແທນ", + "empty": "ບໍ່ມີວຽກທີ່ກຳລັງດຳເນີນການ", + "stale": "ບໍ່ສາມາດອັບເດດໄດ້ໃນຕອນນີ້", + "expired": "ສະຖານະໝົດອາຍຸແລ້ວ", + "signedOut": "ເຂົ້າສູ່ລະບົບເພື່ອເບິ່ງຕົວແທນ", + "privacy": "ຕົວແທນຖືກເຊື່ອງໄວ້", + "openAgents": "ເປີດຕົວແທນ", + "running": "ກຳລັງດຳເນີນການ", + "needsInput": "ຕ້ອງການຂໍ້ມູນເຂົ້າ", + "reconnecting": "ກຳລັງເຊື່ອມຕໍ່ຄືນ", + "channelName": "ຕົວແທນທີ່ກຳລັງເຮັດວຽກ", + "activityKitDisabledTitle": "ກິດຈະກຳສົດປິດຢູ່", + "activityKitDisabledBody": "ເປີດກິດຈະກຳສົດໃນການຕັ້ງຄ່າ ເພື່ອເບິ່ງຕົວແທນທີ່ກຳລັງເຮັດວຽກໃນໜ້າຈໍລັອກ." } } diff --git a/apps/mobile/src/i18n/locales/lt.json b/apps/mobile/src/i18n/locales/lt.json index e56008b2bd..00b614f2ab 100644 --- a/apps/mobile/src/i18n/locales/lt.json +++ b/apps/mobile/src/i18n/locales/lt.json @@ -3233,5 +3233,20 @@ "historyRetry": "Nepavyko įkelti pokalbio istorijos.", "catalogRetry": "Nepavyko įkelti modelių", "sendError": "Nepavyko išsiųsti žinutės. Bandykite dar kartą." + }, + "glanceable": { + "waiting": "Atnaujinami agentai", + "empty": "Nėra vykdomų darbų", + "stale": "Dabar nepavyksta atnaujinti", + "expired": "Būsenos galiojimas baigėsi", + "signedOut": "Prisijunkite, kad matytumėte agentus", + "privacy": "Agentai paslėpti", + "openAgents": "Atidaryti agentus", + "running": "Vykdoma", + "needsInput": "reikia įvesties", + "reconnecting": "Jungiamasi iš naujo", + "channelName": "Aktyvūs agentai", + "activityKitDisabledTitle": "Tiesioginės veiklos išjungtos", + "activityKitDisabledBody": "Nustatymuose įjunkite tiesiogines veiklas, kad užrakinimo ekrane matytumėte aktyvius agentus." } } diff --git a/apps/mobile/src/i18n/locales/lv.json b/apps/mobile/src/i18n/locales/lv.json index 6fd111fda3..e9da3f9f1a 100644 --- a/apps/mobile/src/i18n/locales/lv.json +++ b/apps/mobile/src/i18n/locales/lv.json @@ -3212,5 +3212,20 @@ "historyRetry": "Neizdevās ielādēt tērzēšanas vēsturi.", "catalogRetry": "Neizdevās ielādēt modeļus", "sendError": "Neizdevās nosūtīt ziņojumu. Mēģini vēlreiz." + }, + "glanceable": { + "waiting": "Atjaunina aģentus", + "empty": "Nav darba procesā", + "stale": "Pašlaik nevar atjaunināt", + "expired": "Statusa derīgums ir beidzies", + "signedOut": "Pieraksties, lai redzētu aģentus", + "privacy": "Aģenti ir paslēpti", + "openAgents": "Atvērt aģentus", + "running": "DARBOJAS", + "needsInput": "nepieciešama ievade", + "reconnecting": "Atkārtoti izveido savienojumu", + "channelName": "Aktīvie aģenti", + "activityKitDisabledTitle": "Tiešraides aktivitātes ir izslēgtas", + "activityKitDisabledBody": "Ieslēdz tiešraides aktivitātes iestatījumos, lai bloķēšanas ekrānā redzētu aktīvos aģentus." } } diff --git a/apps/mobile/src/i18n/locales/mg.json b/apps/mobile/src/i18n/locales/mg.json index ea0a2f42cb..dbb5cdcf65 100644 --- a/apps/mobile/src/i18n/locales/mg.json +++ b/apps/mobile/src/i18n/locales/mg.json @@ -3191,5 +3191,20 @@ "historyRetry": "Tsy afaka nampidirina ny tantaran'ny resaka.", "catalogRetry": "Tsy afaka namaky ny modely", "sendError": "Tsy afaka nalefa ny hafatra. Andramo indray." + }, + "glanceable": { + "waiting": "Manavao ny agent", + "empty": "Tsy misy asa mandeha", + "stale": "Tsy afaka manavao izao", + "expired": "Lany daty ny sata", + "signedOut": "Midira mba hahitana ny agent", + "privacy": "Nafenina ny agent", + "openAgents": "Sokafy ny agent", + "running": "MANDEHA", + "needsInput": "mila fampidirana", + "reconnecting": "Mampifandray indray", + "channelName": "Agent mavitrika", + "activityKitDisabledTitle": "Tsy mandeha ny Hetsika Mivantana", + "activityKitDisabledBody": "Alefaso ao amin'ny Fikirana ny Hetsika Mivantana mba hahitana ny Agent Mavitrika eo amin'ny Efijery Fihidy." } } diff --git a/apps/mobile/src/i18n/locales/mi.json b/apps/mobile/src/i18n/locales/mi.json index 64d9167f72..48614e612d 100644 --- a/apps/mobile/src/i18n/locales/mi.json +++ b/apps/mobile/src/i18n/locales/mi.json @@ -3191,5 +3191,20 @@ "historyRetry": "Kāore i taea te uta te hītori kōrero.", "catalogRetry": "Kāore i taea te uta i ngā tauira", "sendError": "I rahua te tuku karere. Whakamātau anō." + }, + "glanceable": { + "waiting": "Kei te whakahou i ngā māngai", + "empty": "Kāore he mahi e haere ana", + "stale": "Kāore e taea te whakahou ināianei", + "expired": "Kua pau te mana o te tūnga", + "signedOut": "Takiuru kia kite i ngā māngai", + "privacy": "Kua huna ngā māngai", + "openAgents": "Whakatuwheratia ngā māngai", + "running": "Kei te oma", + "needsInput": "e hiahia ana ki te whakaurunga", + "reconnecting": "Kei te hono anō", + "channelName": "Ngā māngai hohe", + "activityKitDisabledTitle": "Kua whakawetohia ngā Mahi Mataora", + "activityKitDisabledBody": "Whakakāngia ngā Mahi Mataora i Ngā tautuhinga kia kite i ngā Māngai Hohe i te Mata Maukati." } } diff --git a/apps/mobile/src/i18n/locales/mk.json b/apps/mobile/src/i18n/locales/mk.json index 8187fc07af..ba85de30a5 100644 --- a/apps/mobile/src/i18n/locales/mk.json +++ b/apps/mobile/src/i18n/locales/mk.json @@ -3191,5 +3191,20 @@ "historyRetry": "Не можев да ја вчитам историјата на разговорот.", "catalogRetry": "Не можеше да се вчитаат моделите", "sendError": "Не можеше да се испрати пораката. Обидете се повторно." + }, + "glanceable": { + "waiting": "Ажурирање на агентите", + "empty": "Нема работа во тек", + "stale": "Не може да се ажурира сега", + "expired": "Статусот истече", + "signedOut": "Најавете се за да ги видите агентите", + "privacy": "Агентите се скриени", + "openAgents": "Отворете ги агентите", + "running": "Во тек", + "needsInput": "бара внес", + "reconnecting": "Повторно поврзување", + "channelName": "Активни агенти", + "activityKitDisabledTitle": "Активностите во живо се исклучени", + "activityKitDisabledBody": "Вклучете Активности во живо во Поставки за да ги видите активните агенти на заклучениот екран." } } diff --git a/apps/mobile/src/i18n/locales/ml.json b/apps/mobile/src/i18n/locales/ml.json index 3a4edeb6b8..9322d2271d 100644 --- a/apps/mobile/src/i18n/locales/ml.json +++ b/apps/mobile/src/i18n/locales/ml.json @@ -3191,5 +3191,20 @@ "historyRetry": "ചാറ്റ് ചരിത്രം ലോഡ് ചെയ്യാനായില്ല.", "catalogRetry": "മോഡലുകൾ ലോഡ് ചെയ്യാൻ കഴിഞ്ഞില്ല", "sendError": "സന്ദേശം അയയ്ക്കാൻ പരാജയപ്പെട്ടു. വീണ്ടും ശ്രമിക്കുക." + }, + "glanceable": { + "waiting": "ഏജന്റുകളെ അപ്ഡേറ്റ് ചെയ്യുന്നു", + "empty": "ജോലികളൊന്നും പുരോഗമിക്കുന്നില്ല", + "stale": "ഇപ്പോൾ അപ്ഡേറ്റ് ചെയ്യാൻ കഴിയില്ല", + "expired": "നില കാലഹരണപ്പെട്ടു", + "signedOut": "ഏജന്റുകളെ കാണാൻ സൈൻ ഇൻ ചെയ്യുക", + "privacy": "ഏജന്റുകളെ മറച്ചിരിക്കുന്നു", + "openAgents": "ഏജന്റുകളെ തുറക്കുക", + "running": "പ്രവർത്തിക്കുന്നു", + "needsInput": "ഇൻപുട്ട് ആവശ്യമാണ്", + "reconnecting": "വീണ്ടും ബന്ധിപ്പിക്കുന്നു", + "channelName": "സജീവ ഏജന്റുകൾ", + "activityKitDisabledTitle": "തത്സമയ പ്രവർത്തനങ്ങൾ ഓഫാണ്", + "activityKitDisabledBody": "ലോക്ക് സ്ക്രീനിൽ സജീവ ഏജന്റുകളെ കാണാൻ ക്രമീകരണങ്ങളിൽ തത്സമയ പ്രവർത്തനങ്ങൾ പ്രവർത്തനക്ഷമമാക്കുക." } } diff --git a/apps/mobile/src/i18n/locales/mn.json b/apps/mobile/src/i18n/locales/mn.json index 1c54fdb0cd..b81eab872a 100644 --- a/apps/mobile/src/i18n/locales/mn.json +++ b/apps/mobile/src/i18n/locales/mn.json @@ -3191,5 +3191,20 @@ "historyRetry": "Чат түүхийг ачаалж чадсангүй.", "catalogRetry": "Загваруудыг ачаалж чадсангүй", "sendError": "Мессежийг илгээж чадсангүй. Дахин оролдоно уу." + }, + "glanceable": { + "waiting": "Агентуудыг шинэчилж байна", + "empty": "Гүйцэтгэж буй ажил алга", + "stale": "Одоо шинэчлэх боломжгүй", + "expired": "Төлөвийн хугацаа дууссан", + "signedOut": "Агентуудыг харахын тулд нэвтэрнэ үү", + "privacy": "Агентуудыг нуусан", + "openAgents": "Агентуудыг нээх", + "running": "АЖИЛЛАЖ БАЙНА", + "needsInput": "оролт шаардлагатай", + "reconnecting": "Дахин холбогдож байна", + "channelName": "Идэвхтэй агентууд", + "activityKitDisabledTitle": "Шууд үйл ажиллагаа унтраалттай байна", + "activityKitDisabledBody": "Түгжээтэй дэлгэц дээр Идэвхтэй агентуудыг харахын тулд Тохиргоо хэсэгт Шууд үйл ажиллагааг асаана уу." } } diff --git a/apps/mobile/src/i18n/locales/mr.json b/apps/mobile/src/i18n/locales/mr.json index c440e30f9f..b938634c68 100644 --- a/apps/mobile/src/i18n/locales/mr.json +++ b/apps/mobile/src/i18n/locales/mr.json @@ -3191,5 +3191,20 @@ "historyRetry": "चॅट इतिहास लोड करता आला नाही.", "catalogRetry": "मॉडेल लोड करता आले नाहीत", "sendError": "संदेश पाठवण्यात अयशस्वी. कृपया पुन्हा प्रयत्न करा." + }, + "glanceable": { + "waiting": "एजंट्स अद्यतनित करत आहे", + "empty": "कोणतेही काम सुरू नाही", + "stale": "आता अद्यतनित करता येत नाही", + "expired": "स्थिती कालबाह्य झाली", + "signedOut": "एजंट्स पाहण्यासाठी साइन इन करा", + "privacy": "एजंट्स लपवले आहेत", + "openAgents": "एजंट्स उघडा", + "running": "चालू आहे", + "needsInput": "इनपुट आवश्यक", + "reconnecting": "पुन्हा जोडत आहे", + "channelName": "सक्रिय एजंट्स", + "activityKitDisabledTitle": "थेट क्रियाकलाप बंद आहेत", + "activityKitDisabledBody": "लॉक स्क्रीनवर सक्रिय एजंट्स पाहण्यासाठी सेटिंग्जमध्ये थेट क्रियाकलाप सुरू करा." } } diff --git a/apps/mobile/src/i18n/locales/ms.json b/apps/mobile/src/i18n/locales/ms.json index 22aec75af6..f2383b0678 100644 --- a/apps/mobile/src/i18n/locales/ms.json +++ b/apps/mobile/src/i18n/locales/ms.json @@ -3191,5 +3191,20 @@ "historyRetry": "Tidak dapat memuatkan sejarah sembang.", "catalogRetry": "Tidak dapat memuatkan model", "sendError": "Mesej tidak dapat dihantar. Cuba lagi." + }, + "glanceable": { + "waiting": "Mengemas kini ejen", + "empty": "Tiada kerja sedang dijalankan", + "stale": "Tidak dapat mengemas kini sekarang", + "expired": "Status tamat tempoh", + "signedOut": "Log masuk untuk melihat ejen", + "privacy": "Ejen disembunyikan", + "openAgents": "Buka ejen", + "running": "Sedang berjalan", + "needsInput": "perlu input", + "reconnecting": "Menyambung semula", + "channelName": "Ejen aktif", + "activityKitDisabledTitle": "Aktiviti Langsung dimatikan", + "activityKitDisabledBody": "Hidupkan Aktiviti Langsung dalam Tetapan untuk melihat Ejen Aktif pada Skrin Kunci." } } diff --git a/apps/mobile/src/i18n/locales/mt.json b/apps/mobile/src/i18n/locales/mt.json index 5e8edb2ff2..9de9bda52b 100644 --- a/apps/mobile/src/i18n/locales/mt.json +++ b/apps/mobile/src/i18n/locales/mt.json @@ -3254,5 +3254,20 @@ "historyRetry": "Ma setgħux jitgħabbew l-istorja taċ-chat.", "catalogRetry": "Ma setgħux jittellgħu l-mudelli", "sendError": "Ma setax jintbagħat il-messaġġ. Erġa' pprova." + }, + "glanceable": { + "waiting": "Qed jaġġorna l-aġenti", + "empty": "L-ebda xogħol għaddej", + "stale": "Ma jistax jaġġorna bħalissa", + "expired": "L-istatus skada", + "signedOut": "Idħol biex tara l-aġenti", + "privacy": "Aġenti moħbija", + "openAgents": "Iftaħ l-aġenti", + "running": "Għaddej", + "needsInput": "jeħtieġ input", + "reconnecting": "Qed jerġa' jaqbad", + "channelName": "Aġenti attivi", + "activityKitDisabledTitle": "L-Attivitajiet Diretti huma mitfija", + "activityKitDisabledBody": "Ixgħel l-Attivitajiet Diretti fis-Settings biex tara l-Aġenti Attivi fuq l-Iskrin Imsakkar." } } diff --git a/apps/mobile/src/i18n/locales/my.json b/apps/mobile/src/i18n/locales/my.json index a17b4d7c19..0acbfd71ca 100644 --- a/apps/mobile/src/i18n/locales/my.json +++ b/apps/mobile/src/i18n/locales/my.json @@ -3191,5 +3191,20 @@ "historyRetry": "စကားဝိုင်းသမိုင်းကို တင်မရပါ။", "catalogRetry": "မော်ဒယ်များ ဖွင့်၍ မရပါ", "sendError": "မက်ဆေ့ခ်ျ ပို့ရန် မအောင်မြင်ခဲ့ပါ။ ထပ်ကြိုးစားပါ။" + }, + "glanceable": { + "waiting": "agent များကို အပ်ဒိတ်လုပ်နေသည်", + "empty": "လုပ်ဆောင်နေသော အလုပ် မရှိပါ", + "stale": "ယခု အပ်ဒိတ်လုပ်၍ မရပါ", + "expired": "အခြေအနေ သက်တမ်းကုန်သွားသည်", + "signedOut": "agent များကို ကြည့်ရန် ဝင်ပါ", + "privacy": "agent များကို ဝှက်ထားသည်", + "openAgents": "agent များကို ဖွင့်ပါ", + "running": "လည်ပတ်နေသည်", + "needsInput": "ထည့်သွင်းမှု လိုအပ်သည်", + "reconnecting": "ပြန်ချိတ်ဆက်နေသည်", + "channelName": "လုပ်ဆောင်နေသော agent များ", + "activityKitDisabledTitle": "တိုက်ရိုက်လှုပ်ရှားမှုများ ပိတ်ထားသည်", + "activityKitDisabledBody": "သော့ခတ်မျက်နှာပြင်တွင် လုပ်ဆောင်နေသော agent များကို ကြည့်ရန် ဆက်တင်များတွင် တိုက်ရိုက်လှုပ်ရှားမှုများကို ဖွင့်ပါ။" } } diff --git a/apps/mobile/src/i18n/locales/nb.json b/apps/mobile/src/i18n/locales/nb.json index 8a906acf20..a4298c5f36 100644 --- a/apps/mobile/src/i18n/locales/nb.json +++ b/apps/mobile/src/i18n/locales/nb.json @@ -3191,5 +3191,20 @@ "historyRetry": "Kunne ikke laste inn chathistorikken.", "catalogRetry": "Kunne ikke laste modeller", "sendError": "Kunne ikke sende meldingen. Prøv igjen." + }, + "glanceable": { + "waiting": "Oppdaterer agenter", + "empty": "Ingen arbeid pågår", + "stale": "Kan ikke oppdatere nå", + "expired": "Statusen er utløpt", + "signedOut": "Logg inn for å se agenter", + "privacy": "Agenter er skjult", + "openAgents": "Åpne agenter", + "running": "KJØRER", + "needsInput": "trenger innspill", + "reconnecting": "Kobler til på nytt", + "channelName": "Aktive agenter", + "activityKitDisabledTitle": "Oppdateringer i sanntid er av", + "activityKitDisabledBody": "Slå på Oppdateringer i sanntid i Innstillinger for å se Aktive agenter på låst skjerm." } } diff --git a/apps/mobile/src/i18n/locales/ne.json b/apps/mobile/src/i18n/locales/ne.json index 3835c430b4..ecf5f482fb 100644 --- a/apps/mobile/src/i18n/locales/ne.json +++ b/apps/mobile/src/i18n/locales/ne.json @@ -3191,5 +3191,20 @@ "historyRetry": "कुराकानी इतिहास लोड गर्न सकिएन।", "catalogRetry": "मोडेलहरू लोड गर्न सकिएन", "sendError": "सन्देश पठाउन सकिएन। फेरि प्रयास गर्नुहोस्।" + }, + "glanceable": { + "waiting": "एजेन्टहरू अद्यावधिक गर्दै", + "empty": "कुनै काम चलिरहेको छैन", + "stale": "अहिले अद्यावधिक गर्न सकिँदैन", + "expired": "स्थितिको म्याद सकियो", + "signedOut": "एजेन्टहरू हेर्न साइन इन गर्नुहोस्", + "privacy": "एजेन्टहरू लुकाइएका छन्", + "openAgents": "एजेन्टहरू खोल्नुहोस्", + "running": "चलिरहेको", + "needsInput": "इनपुट चाहिन्छ", + "reconnecting": "पुनः जडान गर्दै", + "channelName": "सक्रिय एजेन्टहरू", + "activityKitDisabledTitle": "प्रत्यक्ष गतिविधिहरू बन्द छन्", + "activityKitDisabledBody": "लक स्क्रिनमा सक्रिय एजेन्टहरू हेर्न सेटिङ्समा प्रत्यक्ष गतिविधिहरू चालू गर्नुहोस्।" } } diff --git a/apps/mobile/src/i18n/locales/nl.json b/apps/mobile/src/i18n/locales/nl.json index 98ed996491..11b04a90b7 100644 --- a/apps/mobile/src/i18n/locales/nl.json +++ b/apps/mobile/src/i18n/locales/nl.json @@ -3191,5 +3191,20 @@ "historyRetry": "Kon de chatgeschiedenis niet laden.", "catalogRetry": "Kon modellen niet laden", "sendError": "Kan het bericht niet verzenden. Probeer het opnieuw." + }, + "glanceable": { + "waiting": "Agents bijwerken", + "empty": "Geen werk in uitvoering", + "stale": "Kan nu niet bijwerken", + "expired": "Status verlopen", + "signedOut": "Log in om agents te zien", + "privacy": "Agents verborgen", + "openAgents": "Agents openen", + "running": "Bezig", + "needsInput": "heeft invoer nodig", + "reconnecting": "Opnieuw verbinden", + "channelName": "Actieve agents", + "activityKitDisabledTitle": "Liveactiviteiten staan uit", + "activityKitDisabledBody": "Schakel liveactiviteiten in via Instellingen om actieve agents op het toegangsscherm te zien." } } diff --git a/apps/mobile/src/i18n/locales/om.json b/apps/mobile/src/i18n/locales/om.json index b5a61cafe8..7e7c873d39 100644 --- a/apps/mobile/src/i18n/locales/om.json +++ b/apps/mobile/src/i18n/locales/om.json @@ -3191,5 +3191,20 @@ "historyRetry": "Seenaa chaat fudhachuu hin dandeenye.", "catalogRetry": "Modeeloota fe'achuu hin dandeenye", "sendError": "Ergaa erguu hin dandeenye. Ammas yaali." + }, + "glanceable": { + "waiting": "Eejentoota haaromsaa jira", + "empty": "Hojiin hojjetamaa jiru hin jiru", + "stale": "Amma haaromsuu hin danda'u", + "expired": "Yeroon haalaa darbeera", + "signedOut": "Eejentoota arguuf seenaa", + "privacy": "Eejentoonni dhokamaniiru", + "openAgents": "Eejentoota banaa", + "running": "Hojii irra jira", + "needsInput": "seensa barbaada", + "reconnecting": "Irra deebi'ee walqabachaa jira", + "channelName": "Eejentoota hojii irra jiran", + "activityKitDisabledTitle": "Sochiiwwan Kallattii cufamaniiru", + "activityKitDisabledBody": "Eejentoota hojii irra jiran Iskiriinii Qulfii irratti arguuf, Qindaa'ina keessatti Sochiiwwan Kallattii banaa." } } diff --git a/apps/mobile/src/i18n/locales/or.json b/apps/mobile/src/i18n/locales/or.json index 62fdd2d1cd..28dfd8f459 100644 --- a/apps/mobile/src/i18n/locales/or.json +++ b/apps/mobile/src/i18n/locales/or.json @@ -3191,5 +3191,20 @@ "historyRetry": "ଚାଟ୍ ଇତିହାସ ଲୋଡ୍ ହୋଇପାରିଲା ନାହିଁ।", "catalogRetry": "ମଡେଲ ଲୋଡ୍ କରାଯାଇପାରିଲା ନାହିଁ", "sendError": "ସନ୍ଦେଶ ପଠାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" + }, + "glanceable": { + "waiting": "ଏଜେଣ୍ଟଗୁଡ଼ିକ ଅପଡେଟ୍ ହେଉଛି", + "empty": "କୌଣସି କାର୍ଯ୍ୟ ଚାଲୁ ନାହିଁ", + "stale": "ଏବେ ଅପଡେଟ୍ କରିହେବ ନାହିଁ", + "expired": "ସ୍ଥିତିର ଅବଧି ସମାପ୍ତ ହୋଇଛି", + "signedOut": "ଏଜେଣ୍ଟଗୁଡ଼ିକ ଦେଖିବାକୁ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ", + "privacy": "ଏଜେଣ୍ଟଗୁଡ଼ିକ ଲୁଚାଯାଇଛି", + "openAgents": "ଏଜେଣ୍ଟଗୁଡ଼ିକ ଖୋଲନ୍ତୁ", + "running": "ଚାଲୁଛି", + "needsInput": "ଇନପୁଟ୍ ଆବଶ୍ୟକ", + "reconnecting": "ପୁଣି ସଂଯୋଗ ହେଉଛି", + "channelName": "ସକ୍ରିୟ ଏଜେଣ୍ଟଗୁଡ଼ିକ", + "activityKitDisabledTitle": "ଲାଇଭ୍ କାର୍ଯ୍ୟକଳାପ ବନ୍ଦ ଅଛି", + "activityKitDisabledBody": "ଲକ୍ ସ୍କ୍ରିନ୍‌ରେ ସକ୍ରିୟ ଏଜେଣ୍ଟଗୁଡ଼ିକ ଦେଖିବାକୁ ସେଟିଂସ୍‌ରେ ଲାଇଭ୍ କାର୍ଯ୍ୟକଳାପ ଚାଲୁ କରନ୍ତୁ।" } } diff --git a/apps/mobile/src/i18n/locales/pa.json b/apps/mobile/src/i18n/locales/pa.json index a17c3d9df7..dc384c98b0 100644 --- a/apps/mobile/src/i18n/locales/pa.json +++ b/apps/mobile/src/i18n/locales/pa.json @@ -3191,5 +3191,20 @@ "historyRetry": "ਚੈਟ ਇਤਿਹਾਸ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।", "catalogRetry": "ਮਾਡਲ ਲੋਡ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕੇ", "sendError": "ਸੁਨੇਹਾ ਭੇਜਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" + }, + "glanceable": { + "waiting": "ਏਜੰਟ ਅੱਪਡੇਟ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ", + "empty": "ਕੋਈ ਕੰਮ ਜਾਰੀ ਨਹੀਂ ਹੈ", + "stale": "ਹੁਣ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ", + "expired": "ਸਥਿਤੀ ਦੀ ਮਿਆਦ ਪੁੱਗ ਗਈ ਹੈ", + "signedOut": "ਏਜੰਟ ਦੇਖਣ ਲਈ ਸਾਈਨ ਇਨ ਕਰੋ", + "privacy": "ਏਜੰਟ ਲੁਕਾਏ ਗਏ ਹਨ", + "openAgents": "ਏਜੰਟ ਖੋਲ੍ਹੋ", + "running": "ਚੱਲ ਰਿਹਾ ਹੈ", + "needsInput": "ਇਨਪੁੱਟ ਦੀ ਲੋੜ ਹੈ", + "reconnecting": "ਮੁੜ ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ", + "channelName": "ਸਰਗਰਮ ਏਜੰਟ", + "activityKitDisabledTitle": "ਲਾਈਵ ਗਤੀਵਿਧੀਆਂ ਬੰਦ ਹਨ", + "activityKitDisabledBody": "ਲਾਕ ਸਕ੍ਰੀਨ 'ਤੇ ਸਰਗਰਮ ਏਜੰਟ ਦੇਖਣ ਲਈ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਲਾਈਵ ਗਤੀਵਿਧੀਆਂ ਚਾਲੂ ਕਰੋ।" } } diff --git a/apps/mobile/src/i18n/locales/pl.json b/apps/mobile/src/i18n/locales/pl.json index e57878bb6b..e0b439e9c0 100644 --- a/apps/mobile/src/i18n/locales/pl.json +++ b/apps/mobile/src/i18n/locales/pl.json @@ -3233,5 +3233,20 @@ "historyRetry": "Nie udało się wczytać historii czatu.", "catalogRetry": "Nie można załadować modeli", "sendError": "Nie udało się wysłać wiadomości. Spróbuj ponownie." + }, + "glanceable": { + "waiting": "Aktualizowanie agentów", + "empty": "Brak pracy w toku", + "stale": "Nie można teraz zaktualizować", + "expired": "Status wygasł", + "signedOut": "Zaloguj się, aby zobaczyć agentów", + "privacy": "Agenci ukryci", + "openAgents": "Otwórz agentów", + "running": "W toku", + "needsInput": "wymaga danych", + "reconnecting": "Ponowne łączenie", + "channelName": "Aktywni agenci", + "activityKitDisabledTitle": "Wydarzenia na żywo są wyłączone", + "activityKitDisabledBody": "Włącz wydarzenia na żywo w Ustawieniach, aby widzieć aktywnych agentów na ekranie blokady." } } diff --git a/apps/mobile/src/i18n/locales/ps.json b/apps/mobile/src/i18n/locales/ps.json index aac55b353f..023a93365b 100644 --- a/apps/mobile/src/i18n/locales/ps.json +++ b/apps/mobile/src/i18n/locales/ps.json @@ -3191,5 +3191,20 @@ "historyRetry": "د چټ تاریخ بار کیدلای نه شو.", "catalogRetry": "ماډلونه بار نه شول", "sendError": "د پیغام لیږل ممکن نه شول. بیا هڅه وکړئ." + }, + "glanceable": { + "waiting": "اجنټان تازه کېږي", + "empty": "هیڅ کار روان نه دی", + "stale": "اوس تازه کول ناشوني دي", + "expired": "د حالت اعتبار پای ته رسېدلی", + "signedOut": "د اجنټانو د لیدلو لپاره ننوزئ", + "privacy": "اجنټان پټ دي", + "openAgents": "اجنټان پرانیزئ", + "running": "روان", + "needsInput": "ورودی ته اړتیا لري", + "reconnecting": "بیا نښلېږي", + "channelName": "فعال اجنټان", + "activityKitDisabledTitle": "ژوندي فعالیتونه بند دي", + "activityKitDisabledBody": "په قلف شوې پرده کې د فعالو اجنټانو د لیدلو لپاره په ترتیباتو کې ژوندي فعالیتونه فعال کړئ." } } diff --git a/apps/mobile/src/i18n/locales/pt-BR.json b/apps/mobile/src/i18n/locales/pt-BR.json index 380b1cfab5..ce93386fce 100644 --- a/apps/mobile/src/i18n/locales/pt-BR.json +++ b/apps/mobile/src/i18n/locales/pt-BR.json @@ -3212,5 +3212,20 @@ "historyRetry": "Não foi possível carregar o histórico do chat.", "catalogRetry": "Não foi possível carregar os modelos", "sendError": "Não foi possível enviar a mensagem. Tente novamente." + }, + "glanceable": { + "waiting": "Atualizando agentes", + "empty": "Nenhum trabalho em andamento", + "stale": "Não é possível atualizar agora", + "expired": "Status expirado", + "signedOut": "Entre para ver os agentes", + "privacy": "Agentes ocultos", + "openAgents": "Abrir agentes", + "running": "Em execução", + "needsInput": "requer entrada", + "reconnecting": "Reconectando", + "channelName": "Agentes ativos", + "activityKitDisabledTitle": "As Atividades ao Vivo estão desativadas", + "activityKitDisabledBody": "Ative as Atividades ao Vivo em Ajustes para ver os agentes ativos na Tela Bloqueada." } } diff --git a/apps/mobile/src/i18n/locales/pt.json b/apps/mobile/src/i18n/locales/pt.json index 88fbcf071d..73f9fcdd8c 100644 --- a/apps/mobile/src/i18n/locales/pt.json +++ b/apps/mobile/src/i18n/locales/pt.json @@ -3212,5 +3212,20 @@ "historyRetry": "Não foi possível carregar o histórico do chat.", "catalogRetry": "Não foi possível carregar os modelos", "sendError": "Não foi possível enviar a mensagem. Tente novamente." + }, + "glanceable": { + "waiting": "A atualizar agentes", + "empty": "Nenhum trabalho em curso", + "stale": "Não é possível atualizar agora", + "expired": "Estado expirado", + "signedOut": "Inicie sessão para ver os agentes", + "privacy": "Agentes ocultos", + "openAgents": "Abrir agentes", + "running": "EM EXECUÇÃO", + "needsInput": "requer entrada", + "reconnecting": "A restabelecer ligação", + "channelName": "Agentes ativos", + "activityKitDisabledTitle": "As Atividades em tempo real estão desativadas", + "activityKitDisabledBody": "Ative as Atividades em tempo real nas Definições para ver os agentes ativos no Ecrã bloqueado." } } diff --git a/apps/mobile/src/i18n/locales/ro.json b/apps/mobile/src/i18n/locales/ro.json index c38ab7a101..f1a3aa9777 100644 --- a/apps/mobile/src/i18n/locales/ro.json +++ b/apps/mobile/src/i18n/locales/ro.json @@ -3212,5 +3212,20 @@ "historyRetry": "Nu s-a putut încărca istoricul de chat.", "catalogRetry": "Modelele nu au putut fi încărcate", "sendError": "Mesajul nu a putut fi trimis. Încercați din nou." + }, + "glanceable": { + "waiting": "Se actualizează agenții", + "empty": "Nicio sarcină în curs", + "stale": "Nu se poate actualiza acum", + "expired": "Stare expirată", + "signedOut": "Autentifică-te pentru a vedea agenții", + "privacy": "Agenți ascunși", + "openAgents": "Deschide agenții", + "running": "Rulează", + "needsInput": "necesită introducere", + "reconnecting": "Se reconectează", + "channelName": "Agenți activi", + "activityKitDisabledTitle": "Activitățile live sunt dezactivate", + "activityKitDisabledBody": "Activează Activități live în Setări pentru a vedea Agenții activi pe ecranul de blocare." } } diff --git a/apps/mobile/src/i18n/locales/ru.json b/apps/mobile/src/i18n/locales/ru.json index 49dc2e426f..8412bf2ac9 100644 --- a/apps/mobile/src/i18n/locales/ru.json +++ b/apps/mobile/src/i18n/locales/ru.json @@ -3233,5 +3233,20 @@ "historyRetry": "Не удалось загрузить историю чата.", "catalogRetry": "Не удалось загрузить модели", "sendError": "Не удалось отправить сообщение. Попробуйте еще раз." + }, + "glanceable": { + "waiting": "Обновление агентов", + "empty": "Нет задач в работе", + "stale": "Сейчас не удается обновить", + "expired": "Статус устарел", + "signedOut": "Войдите, чтобы видеть агентов", + "privacy": "Агенты скрыты", + "openAgents": "Открыть агентов", + "running": "Выполняется", + "needsInput": "требует ввода", + "reconnecting": "Повторное подключение", + "channelName": "Активные агенты", + "activityKitDisabledTitle": "Эфир активности выключен", + "activityKitDisabledBody": "Включите Эфир активности в Настройках, чтобы видеть активных агентов на экране блокировки." } } diff --git a/apps/mobile/src/i18n/locales/si.json b/apps/mobile/src/i18n/locales/si.json index 7bfd45e2e1..f8bd34b3e0 100644 --- a/apps/mobile/src/i18n/locales/si.json +++ b/apps/mobile/src/i18n/locales/si.json @@ -3191,5 +3191,20 @@ "historyRetry": "සංවාද ඉතිහාසය පූරණය කළ නොහැකි විය.", "catalogRetry": "ආකෘති පූරණය කිරීමට නොහැකි විය", "sendError": "පණිවිඩය යැවිය නොහැකි විය. නැවත උත්සාහ කරන්න." + }, + "glanceable": { + "waiting": "නියෝජිතයන් යාවත්කාලීන කරමින්", + "empty": "සිදු කෙරෙන වැඩ කිසිවක් නැත", + "stale": "දැන් යාවත්කාලීන කළ නොහැක", + "expired": "තත්ත්වය කල් ඉකුත් වී ඇත", + "signedOut": "නියෝජිතයන් බැලීමට පුරනය වන්න", + "privacy": "නියෝජිතයන් සඟවා ඇත", + "openAgents": "නියෝජිතයන් විවෘත කරන්න", + "running": "ධාවනය වෙමින්", + "needsInput": "ආදානය අවශ්යයි", + "reconnecting": "නැවත සම්බන්ධ වෙමින්", + "channelName": "සක්‍රිය නියෝජිතයන්", + "activityKitDisabledTitle": "සජීවී ක්‍රියාකාරකම් අක්‍රියයි", + "activityKitDisabledBody": "අගුළු තිරයේ සක්‍රිය නියෝජිතයන් බැලීමට සැකසුම් තුළ සජීවී ක්‍රියාකාරකම් සක්‍රිය කරන්න." } } diff --git a/apps/mobile/src/i18n/locales/sk.json b/apps/mobile/src/i18n/locales/sk.json index ab933af808..5e66e53b27 100644 --- a/apps/mobile/src/i18n/locales/sk.json +++ b/apps/mobile/src/i18n/locales/sk.json @@ -3233,5 +3233,20 @@ "historyRetry": "Históriu chatu sa nepodarilo načítať.", "catalogRetry": "Modely sa nepodarilo načítať", "sendError": "Nepodarilo sa odoslať správu. Skúste to znova." + }, + "glanceable": { + "waiting": "Aktualizácia agentov", + "empty": "Žiadna prebiehajúca práca", + "stale": "Teraz sa nedá aktualizovať", + "expired": "Platnosť stavu vypršala", + "signedOut": "Prihláste sa na zobrazenie agentov", + "privacy": "Agenti sú skrytí", + "openAgents": "Otvoriť agentov", + "running": "Prebieha", + "needsInput": "vyžaduje vstup", + "reconnecting": "Opätovné pripájanie", + "channelName": "Aktívni agenti", + "activityKitDisabledTitle": "Živé aktivity sú vypnuté", + "activityKitDisabledBody": "Zapnite živé aktivity v Nastaveniach, aby sa aktívni agenti zobrazovali na zamknutej obrazovke." } } diff --git a/apps/mobile/src/i18n/locales/sl.json b/apps/mobile/src/i18n/locales/sl.json index 46efd54d56..4cc7c05f56 100644 --- a/apps/mobile/src/i18n/locales/sl.json +++ b/apps/mobile/src/i18n/locales/sl.json @@ -3233,5 +3233,20 @@ "historyRetry": "Zgodovine klepeta ni bilo mogoče naložiti.", "catalogRetry": "Nalaganje modelov ni uspelo", "sendError": "Sporočila ni bilo mogoče poslati. Poskusite znova." + }, + "glanceable": { + "waiting": "Posodabljanje agentov", + "empty": "Ni dela v teku", + "stale": "Trenutno ni mogoče posodobiti", + "expired": "Stanje je poteklo", + "signedOut": "Prijavite se za ogled agentov", + "privacy": "Agenti so skriti", + "openAgents": "Odprite agente", + "running": "DELUJE", + "needsInput": "potrebuje vnos", + "reconnecting": "Ponovno povezovanje", + "channelName": "Aktivni agenti", + "activityKitDisabledTitle": "Dejavnosti v živo so izklopljene", + "activityKitDisabledBody": "V Nastavitvah vklopite Dejavnosti v živo, da bodo Aktivni agenti prikazani na zaklenjenem zaslonu." } } diff --git a/apps/mobile/src/i18n/locales/so.json b/apps/mobile/src/i18n/locales/so.json index 26f277e3a5..218014c494 100644 --- a/apps/mobile/src/i18n/locales/so.json +++ b/apps/mobile/src/i18n/locales/so.json @@ -3191,5 +3191,20 @@ "historyRetry": "Taariikhda wada sheekaysiga lama soo gelin karin.", "catalogRetry": "Moodeelada lama soo dejin karin", "sendError": "Fariinta lama diri karin. Mar kale isku day." + }, + "glanceable": { + "waiting": "Cusboonaysiinaya wakiillada", + "empty": "Ma jirto hawl socota", + "stale": "Hadda lama cusboonaysiin karo", + "expired": "Xaaladdu way dhacday", + "signedOut": "Soo gal si aad u aragto wakiillada", + "privacy": "Wakiillada waa la qariyay", + "openAgents": "Fur wakiillada", + "running": "Socodaya", + "needsInput": "u baahan wax-soo-gal", + "reconnecting": "Dib u xiriirinaya", + "channelName": "Wakiillada firfircoon", + "activityKitDisabledTitle": "Hawlaha Tooska ah waa daman", + "activityKitDisabledBody": "Ku daar Hawlaha Tooska ah Dejinta si aad Wakiillada firfircoon ugu aragto Shaashadda Qufulka." } } diff --git a/apps/mobile/src/i18n/locales/sq.json b/apps/mobile/src/i18n/locales/sq.json index 23ef6f0047..dacf2759e9 100644 --- a/apps/mobile/src/i18n/locales/sq.json +++ b/apps/mobile/src/i18n/locales/sq.json @@ -3191,5 +3191,20 @@ "historyRetry": "Nuk u arrit të ngarkohej historia e bisedës.", "catalogRetry": "Nuk u ngarkuan modelet", "sendError": "Mesazhi nuk u dërgua. Provoni përsëri." + }, + "glanceable": { + "waiting": "Duke përditësuar agjentët", + "empty": "Nuk ka punë në vazhdim", + "stale": "Nuk mund të përditësohet tani", + "expired": "Statusi ka skaduar", + "signedOut": "Identifikohuni për të parë agjentët", + "privacy": "Agjentët janë fshehur", + "openAgents": "Hapni agjentët", + "running": "Në ekzekutim", + "needsInput": "ka nevojë për të dhëna", + "reconnecting": "Duke u rilidhur", + "channelName": "Agjentët aktivë", + "activityKitDisabledTitle": "Aktivitetet e drejtpërdrejta janë çaktivizuar", + "activityKitDisabledBody": "Aktivizoni Aktivitetet e drejtpërdrejta te Cilësimet për të parë Agjentët aktivë në Ekranin e kyçjes." } } diff --git a/apps/mobile/src/i18n/locales/sr.json b/apps/mobile/src/i18n/locales/sr.json index e3224a15c9..472e29f72b 100644 --- a/apps/mobile/src/i18n/locales/sr.json +++ b/apps/mobile/src/i18n/locales/sr.json @@ -3212,5 +3212,20 @@ "historyRetry": "Nije moguće učitati istoriju ćaskanja.", "catalogRetry": "Nije moguće učitati modele", "sendError": "Slanje poruke nije uspelo. Pokušajte ponovo." + }, + "glanceable": { + "waiting": "Ažuriranje agenata", + "empty": "Nema rada u toku", + "stale": "Ažuriranje trenutno nije moguće", + "expired": "Status je istekao", + "signedOut": "Prijavite se da biste videli agente", + "privacy": "Agenti su skriveni", + "openAgents": "Otvorite agente", + "running": "U toku", + "needsInput": "zahteva unos", + "reconnecting": "Ponovno povezivanje", + "channelName": "Aktivni agenti", + "activityKitDisabledTitle": "Aktivnosti uživo su isključene", + "activityKitDisabledBody": "Uključite Aktivnosti uživo u Podešavanjima da biste videli aktivne agente na zaključanom ekranu." } } diff --git a/apps/mobile/src/i18n/locales/sv.json b/apps/mobile/src/i18n/locales/sv.json index 6b2001a6ee..5ddf047e59 100644 --- a/apps/mobile/src/i18n/locales/sv.json +++ b/apps/mobile/src/i18n/locales/sv.json @@ -3191,5 +3191,20 @@ "historyRetry": "Det gick inte att läsa in chatthistoriken.", "catalogRetry": "Kunde inte läsa in modeller", "sendError": "Kunde inte skicka meddelandet. Försök igen." + }, + "glanceable": { + "waiting": "Uppdaterar agenter", + "empty": "Inget arbete pågår", + "stale": "Kan inte uppdatera nu", + "expired": "Statusen har gått ut", + "signedOut": "Logga in för att se agenter", + "privacy": "Agenter dolda", + "openAgents": "Öppna agenter", + "running": "KÖRS", + "needsInput": "kräver indata", + "reconnecting": "Återansluter", + "channelName": "Aktiva agenter", + "activityKitDisabledTitle": "Liveaktiviteter är avstängda", + "activityKitDisabledBody": "Aktivera liveaktiviteter i Inställningar för att se Aktiva agenter på låsskärmen." } } diff --git a/apps/mobile/src/i18n/locales/sw.json b/apps/mobile/src/i18n/locales/sw.json index d8f50f9715..4b57985a59 100644 --- a/apps/mobile/src/i18n/locales/sw.json +++ b/apps/mobile/src/i18n/locales/sw.json @@ -3191,5 +3191,20 @@ "historyRetry": "Haikuweza kupakia historia ya mazungumzo.", "catalogRetry": "Imeshindwa kupakia miundo", "sendError": "Haikuweza kutuma ujumbe. Jaribu tena." + }, + "glanceable": { + "waiting": "Inasasisha mawakala", + "empty": "Hakuna kazi inayoendelea", + "stale": "Haiwezi kusasisha sasa", + "expired": "Muda wa hali umeisha", + "signedOut": "Ingia ili uone mawakala", + "privacy": "Mawakala wamefichwa", + "openAgents": "Fungua mawakala", + "running": "Inaendelea", + "needsInput": "inahitaji mchango", + "reconnecting": "Inaunganisha tena", + "channelName": "Mawakala wanaofanya kazi", + "activityKitDisabledTitle": "Shughuli za Moja kwa Moja zimezimwa", + "activityKitDisabledBody": "Washa Shughuli za Moja kwa Moja katika Mipangilio ili uone Mawakala wanaofanya kazi kwenye Skrini Iliyofungwa." } } diff --git a/apps/mobile/src/i18n/locales/ta.json b/apps/mobile/src/i18n/locales/ta.json index d7285a2073..71c0233906 100644 --- a/apps/mobile/src/i18n/locales/ta.json +++ b/apps/mobile/src/i18n/locales/ta.json @@ -3191,5 +3191,20 @@ "historyRetry": "அரட்டை வரலாற்றை ஏற்ற முடியவில்லை.", "catalogRetry": "மாதிரிகளை ஏற்ற முடியவில்லை", "sendError": "செய்தியை அனுப்ப முடியவில்லை. மீண்டும் முயற்சிக்கவும்." + }, + "glanceable": { + "waiting": "முகவர்களைப் புதுப்பிக்கிறது", + "empty": "எந்தப் பணியும் நடைபெறவில்லை", + "stale": "இப்போது புதுப்பிக்க முடியவில்லை", + "expired": "நிலை காலாவதியானது", + "signedOut": "முகவர்களைக் காண உள்நுழையவும்", + "privacy": "முகவர்கள் மறைக்கப்பட்டுள்ளனர்", + "openAgents": "முகவர்களைத் திறக்கவும்", + "running": "இயங்குகிறது", + "needsInput": "உள்ளீடு தேவை", + "reconnecting": "மீண்டும் இணைக்கிறது", + "channelName": "செயலில் உள்ள முகவர்கள்", + "activityKitDisabledTitle": "நேரலைச் செயல்பாடுகள் முடக்கப்பட்டுள்ளன", + "activityKitDisabledBody": "பூட்டுத் திரையில் செயலில் உள்ள முகவர்களைக் காண அமைப்புகளில் நேரலைச் செயல்பாடுகளை இயக்கவும்." } } diff --git a/apps/mobile/src/i18n/locales/te.json b/apps/mobile/src/i18n/locales/te.json index 55972192cd..7c83980d8f 100644 --- a/apps/mobile/src/i18n/locales/te.json +++ b/apps/mobile/src/i18n/locales/te.json @@ -3191,5 +3191,20 @@ "historyRetry": "చాట్ చరిత్రను లోడ్ చేయలేకపోయాము.", "catalogRetry": "మోడళ్లను లోడ్ చేయలేకపోయాము", "sendError": "సందేశం పంపలేకపోయాము. మళ్ళీ ప్రయత్నించండి." + }, + "glanceable": { + "waiting": "ఏజెంట్లను నవీకరిస్తోంది", + "empty": "పని ఏదీ కొనసాగడం లేదు", + "stale": "ఇప్పుడు నవీకరించలేము", + "expired": "స్థితి గడువు ముగిసింది", + "signedOut": "ఏజెంట్లను చూడటానికి సైన్ ఇన్ చేయండి", + "privacy": "ఏజెంట్లు దాచబడ్డారు", + "openAgents": "ఏజెంట్లను తెరవండి", + "running": "నడుస్తోంది", + "needsInput": "ఇన్పుట్ అవసరం", + "reconnecting": "మళ్లీ కనెక్ట్ అవుతోంది", + "channelName": "చురుకైన ఏజెంట్లు", + "activityKitDisabledTitle": "ప్రత్యక్ష కార్యకలాపాలు ఆఫ్‌లో ఉన్నాయి", + "activityKitDisabledBody": "లాక్ స్క్రీన్‌పై చురుకైన ఏజెంట్లను చూడటానికి సెట్టింగ్‌లలో ప్రత్యక్ష కార్యకలాపాలను ఆన్ చేయండి." } } diff --git a/apps/mobile/src/i18n/locales/th.json b/apps/mobile/src/i18n/locales/th.json index cc1e21766f..b1cad084ff 100644 --- a/apps/mobile/src/i18n/locales/th.json +++ b/apps/mobile/src/i18n/locales/th.json @@ -3191,5 +3191,20 @@ "historyRetry": "ไม่สามารถโหลดประวัติแชทได้", "catalogRetry": "ไม่สามารถโหลดโมเดลได้", "sendError": "ไม่สามารถส่งข้อความได้ ลองอีกครั้ง" + }, + "glanceable": { + "waiting": "กำลังอัปเดตเอเจนต์", + "empty": "ไม่มีงานที่กำลังดำเนินการ", + "stale": "ไม่สามารถอัปเดตได้ในขณะนี้", + "expired": "สถานะหมดอายุ", + "signedOut": "ลงชื่อเข้าใช้เพื่อดูเอเจนต์", + "privacy": "ซ่อนเอเจนต์อยู่", + "openAgents": "เปิดเอเจนต์", + "running": "กำลังทำงาน", + "needsInput": "ต้องป้อนข้อมูล", + "reconnecting": "กำลังเชื่อมต่อใหม่", + "channelName": "เอเจนต์ที่กำลังทำงาน", + "activityKitDisabledTitle": "กิจกรรมสดปิดอยู่", + "activityKitDisabledBody": "เปิดกิจกรรมสดในการตั้งค่าเพื่อดูเอเจนต์ที่กำลังทำงานบนหน้าจอล็อค" } } diff --git a/apps/mobile/src/i18n/locales/tr.json b/apps/mobile/src/i18n/locales/tr.json index e25c538ed5..ea85addb9b 100644 --- a/apps/mobile/src/i18n/locales/tr.json +++ b/apps/mobile/src/i18n/locales/tr.json @@ -3191,5 +3191,20 @@ "historyRetry": "Sohbet geçmişi yüklenemedi.", "catalogRetry": "Modeller yüklenemedi", "sendError": "Mesaj gönderilemedi. Lütfen tekrar deneyin." + }, + "glanceable": { + "waiting": "Ajanlar güncelleniyor", + "empty": "Devam eden iş yok", + "stale": "Şu anda güncellenemiyor", + "expired": "Durumun süresi doldu", + "signedOut": "Ajanları görmek için oturum açın", + "privacy": "Ajanlar gizli", + "openAgents": "Ajanları açın", + "running": "Çalışıyor", + "needsInput": "Girdi gerekli", + "reconnecting": "Yeniden bağlanılıyor", + "channelName": "Etkin ajanlar", + "activityKitDisabledTitle": "Canlı Etkinlikler kapalı", + "activityKitDisabledBody": "Etkin ajanları Kilit Ekranı'nda görmek için Ayarlar'dan Canlı Etkinlikler'i açın." } } diff --git a/apps/mobile/src/i18n/locales/uk.json b/apps/mobile/src/i18n/locales/uk.json index 0cbe343555..3a959178d8 100644 --- a/apps/mobile/src/i18n/locales/uk.json +++ b/apps/mobile/src/i18n/locales/uk.json @@ -3233,5 +3233,20 @@ "historyRetry": "Не вдалося завантажити історію чату.", "catalogRetry": "Не вдалося завантажити моделі", "sendError": "Не вдалося надіслати повідомлення. Спробуйте ще раз." + }, + "glanceable": { + "waiting": "Оновлення агентів", + "empty": "Немає поточної роботи", + "stale": "Зараз не вдається оновити", + "expired": "Термін дії статусу минув", + "signedOut": "Увійдіть, щоб бачити агентів", + "privacy": "Агентів приховано", + "openAgents": "Відкрити агентів", + "running": "Виконується", + "needsInput": "потребує вводу", + "reconnecting": "Повторне підключення", + "channelName": "Активні агенти", + "activityKitDisabledTitle": "Дії наживо вимкнено", + "activityKitDisabledBody": "Увімкніть «Дії наживо» в «Параметрах», щоб бачити активних агентів на замкненому екрані." } } diff --git a/apps/mobile/src/i18n/locales/ur.json b/apps/mobile/src/i18n/locales/ur.json index 5fa3486513..7acf73692f 100644 --- a/apps/mobile/src/i18n/locales/ur.json +++ b/apps/mobile/src/i18n/locales/ur.json @@ -3191,5 +3191,20 @@ "historyRetry": "چیٹ کی سرگزشت لوڈ نہیں ہو سکی۔", "catalogRetry": "ماڈلز لوڈ نہیں ہو سکے", "sendError": "پیغام نہیں بھیجا جا سکا۔ دوبارہ کوشش کریں۔" + }, + "glanceable": { + "waiting": "ایجنٹس اپڈیٹ ہو رہے ہیں", + "empty": "کوئی کام جاری نہیں", + "stale": "ابھی اپڈیٹ نہیں ہو سکتا", + "expired": "حالت کی میعاد ختم ہو گئی", + "signedOut": "ایجنٹس دیکھنے کے لیے سائن ان کریں", + "privacy": "ایجنٹس چھپے ہوئے ہیں", + "openAgents": "ایجنٹس کھولیں", + "running": "چل رہا ہے", + "needsInput": "ان پٹ درکار", + "reconnecting": "دوبارہ منسلک ہو رہا ہے", + "channelName": "فعال ایجنٹس", + "activityKitDisabledTitle": "لائیو سرگرمیاں بند ہیں", + "activityKitDisabledBody": "لاک اسکرین پر فعال ایجنٹس دیکھنے کے لیے ترتیبات میں لائیو سرگرمیاں فعال کریں۔" } } diff --git a/apps/mobile/src/i18n/locales/uz.json b/apps/mobile/src/i18n/locales/uz.json index a4b17d1560..550efd65b8 100644 --- a/apps/mobile/src/i18n/locales/uz.json +++ b/apps/mobile/src/i18n/locales/uz.json @@ -3191,5 +3191,20 @@ "historyRetry": "Suhbat tarixini yuklab bo'lmadi.", "catalogRetry": "Modellarni yuklab bo'lmadi", "sendError": "Xabarni yuborib bo'lmadi. Iltimos, qayta urinib ko'ring." + }, + "glanceable": { + "waiting": "Agentlar yangilanmoqda", + "empty": "Bajarilayotgan ish yo'q", + "stale": "Hozir yangilab bo'lmaydi", + "expired": "Holat muddati tugadi", + "signedOut": "Agentlarni ko'rish uchun tizimga kiring", + "privacy": "Agentlar yashirilgan", + "openAgents": "Agentlarni oching", + "running": "Ishlamoqda", + "needsInput": "kiritish kerak", + "reconnecting": "Qayta ulanmoqda", + "channelName": "Faol agentlar", + "activityKitDisabledTitle": "Jonli faoliyatlar o'chirilgan", + "activityKitDisabledBody": "Qulflangan ekranda Faol agentlarni ko'rish uchun Sozlamalarda Jonli faoliyatlarni yoqing." } } diff --git a/apps/mobile/src/i18n/locales/vi.json b/apps/mobile/src/i18n/locales/vi.json index c1dbdfeb3b..3f06694011 100644 --- a/apps/mobile/src/i18n/locales/vi.json +++ b/apps/mobile/src/i18n/locales/vi.json @@ -3191,5 +3191,20 @@ "historyRetry": "Không thể tải lịch sử trò chuyện.", "catalogRetry": "Không thể tải các mô hình", "sendError": "Không thể gửi tin nhắn. Vui lòng thử lại." + }, + "glanceable": { + "waiting": "Đang cập nhật tác nhân", + "empty": "Không có công việc đang thực hiện", + "stale": "Hiện không thể cập nhật", + "expired": "Trạng thái đã hết hạn", + "signedOut": "Đăng nhập để xem tác nhân", + "privacy": "Đã ẩn tác nhân", + "openAgents": "Mở tác nhân", + "running": "ĐANG CHẠY", + "needsInput": "cần nhập", + "reconnecting": "Đang kết nối lại", + "channelName": "Tác nhân đang hoạt động", + "activityKitDisabledTitle": "Hoạt động trực tiếp đang tắt", + "activityKitDisabledBody": "Bật Hoạt động trực tiếp trong Cài đặt để xem các tác nhân đang hoạt động trên Màn hình khóa." } } diff --git a/apps/mobile/src/i18n/locales/yo.json b/apps/mobile/src/i18n/locales/yo.json index 8ec8e7366f..fb68588575 100644 --- a/apps/mobile/src/i18n/locales/yo.json +++ b/apps/mobile/src/i18n/locales/yo.json @@ -3191,5 +3191,20 @@ "historyRetry": "Kò lè ṣagbewọ ìtàn iwiregbe.", "catalogRetry": "Kò lè gbé àwọn àwoṣe", "sendError": "Kò lè ran ifiranṣẹ náà. Jọ̀wọ́ tún gbìyànjú." + }, + "glanceable": { + "waiting": "Ti n ṣe imudojuiwọn awọn aṣoju", + "empty": "Ko si iṣẹ ti n lọ lọwọ", + "stale": "Ko le ṣe imudojuiwọn bayi", + "expired": "Ipo ti pari akoko", + "signedOut": "Wọle lati ri awọn aṣoju", + "privacy": "Awọn aṣoju wa ni ipamọ", + "openAgents": "Ṣii awọn aṣoju", + "running": "ǸJẸ́ ṢÍṢIṢẸ́", + "needsInput": "nilo igbewọle", + "reconnecting": "Ti n tun sopọ", + "channelName": "Awọn aṣoju to n ṣiṣẹ", + "activityKitDisabledTitle": "Awọn Iṣẹ Lọwọlọwọ wa ni pipa", + "activityKitDisabledBody": "Tan Awọn Iṣẹ Lọwọlọwọ ninu Eto lati ri Awọn aṣoju to n ṣiṣẹ lori Iboju Titiipa." } } diff --git a/apps/mobile/src/i18n/locales/zh-Hans.json b/apps/mobile/src/i18n/locales/zh-Hans.json index 0073963026..6f4c98633c 100644 --- a/apps/mobile/src/i18n/locales/zh-Hans.json +++ b/apps/mobile/src/i18n/locales/zh-Hans.json @@ -3191,5 +3191,20 @@ "historyRetry": "无法加载聊天历史。", "catalogRetry": "无法加载模型", "sendError": "无法发送消息。请重试。" + }, + "glanceable": { + "waiting": "正在更新代理", + "empty": "暂无进行中的任务", + "stale": "暂时无法更新", + "expired": "状态已过期", + "signedOut": "请登录以查看代理", + "privacy": "代理已隐藏", + "openAgents": "打开代理", + "running": "运行中", + "needsInput": "需要输入", + "reconnecting": "正在重新连接", + "channelName": "活动代理", + "activityKitDisabledTitle": "实时活动已关闭", + "activityKitDisabledBody": "请在“设置”中开启“实时活动”,以在锁定屏幕上查看活动代理。" } } diff --git a/apps/mobile/src/i18n/locales/zh-Hant.json b/apps/mobile/src/i18n/locales/zh-Hant.json index ad6215bb1f..0ac6b71722 100644 --- a/apps/mobile/src/i18n/locales/zh-Hant.json +++ b/apps/mobile/src/i18n/locales/zh-Hant.json @@ -3191,5 +3191,20 @@ "historyRetry": "無法載入聊天記錄。", "catalogRetry": "無法載入模型", "sendError": "無法傳送訊息。請再試一次。" + }, + "glanceable": { + "waiting": "正在更新代理", + "empty": "沒有進行中的工作", + "stale": "目前無法更新", + "expired": "狀態已過期", + "signedOut": "請登入以查看代理", + "privacy": "代理已隱藏", + "openAgents": "開啟代理", + "running": "執行中", + "needsInput": "需要輸入", + "reconnecting": "正在重新連線", + "channelName": "使用中的代理", + "activityKitDisabledTitle": "即時動態已關閉", + "activityKitDisabledBody": "請在「設定」中開啟「即時動態」,以在鎖定畫面上查看使用中的代理。" } } diff --git a/apps/mobile/src/i18n/locales/zu.json b/apps/mobile/src/i18n/locales/zu.json index 1b1259c2d2..1fbf891bb2 100644 --- a/apps/mobile/src/i18n/locales/zu.json +++ b/apps/mobile/src/i18n/locales/zu.json @@ -3191,5 +3191,20 @@ "historyRetry": "Asikwazanga ukulayisha umlando wengxoxo.", "catalogRetry": "Akukwazanga ukulayisha amamodeli", "sendError": "Asikwazanga ukuthumela umlayezo. Uzama futhi." + }, + "glanceable": { + "waiting": "Ibuyekeza ama-agent", + "empty": "Awukho umsebenzi oqhubekayo", + "stale": "Akukwazi ukubuyekeza manje", + "expired": "Isimo siphelelwe yisikhathi", + "signedOut": "Ngena ngemvume ukuze ubone ama-agent", + "privacy": "Ama-agent afihliwe", + "openAgents": "Vula ama-agent", + "running": "IYASEBENZA", + "needsInput": "idinga okokufaka", + "reconnecting": "Ixhuma kabusha", + "channelName": "Ama-agent asebenzayo", + "activityKitDisabledTitle": "Imisebenzi Ebukhoma ivaliwe", + "activityKitDisabledBody": "Vula Imisebenzi Ebukhoma ku-Izilungiselelo ukuze ubone Ama-agent asebenzayo Esikrinini Esikhiyiwe." } } From af856729305255833e4842cadd6aa7e65ed0ac6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Fri, 28 Aug 2026 11:26:49 +0200 Subject: [PATCH 5/6] fix(mobile): preserve stale snapshot expiry deadlines --- .../mobile/src/lib/glanceable/cleanup.test.ts | 110 ++++++++++++------ apps/mobile/src/lib/glanceable/cleanup.ts | 15 +-- .../src/lib/glanceable/publisher.test.ts | 107 +++++++++++------ apps/mobile/src/lib/glanceable/publisher.ts | 33 ++++-- 4 files changed, 173 insertions(+), 92 deletions(-) diff --git a/apps/mobile/src/lib/glanceable/cleanup.test.ts b/apps/mobile/src/lib/glanceable/cleanup.test.ts index 00693ce963..6f13311da0 100644 --- a/apps/mobile/src/lib/glanceable/cleanup.test.ts +++ b/apps/mobile/src/lib/glanceable/cleanup.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { type GlanceableAgentsSnapshot } from '@kilocode/app-shared/glanceable-agents-snapshot'; @@ -8,7 +8,11 @@ import { writePrivacySnapshotAndEnd, writeSignedOutSnapshotAndEnd, } from './cleanup'; -import { _resetGlanceablePersistForTests, _setLastGlanceableSnapshotForTests } from './persist'; +import { + _resetGlanceablePersistForTests, + _setLastGlanceableSnapshotForTests, + getLastGlanceableSnapshot, +} from './persist'; import { type GlanceableSink, registerGlanceableSink, @@ -24,6 +28,7 @@ function makeSink() { const calls: SinkCall[] = []; const sink: GlanceableSink = { publish(snapshot) { + _setLastGlanceableSnapshotForTests(snapshot); calls.push({ type: 'publish', snapshot }); }, startOrUpdate(snapshot) { @@ -46,6 +51,7 @@ function lastSnapshot(calls: SinkCall[]): GlanceableAgentsSnapshot { afterEach(() => { _resetGlanceablePersistForTests(); + vi.useRealTimers(); }); describe('cleanup', () => { @@ -115,7 +121,8 @@ describe('cleanup', () => { ).toBe('none'); }); - it('marks stale (keeps counts) when the org list errors', () => { + it('keeps the original deadline through repeated org list failures', () => { + vi.useFakeTimers(); expect( planOrgFenceAction({ organizationId: 'kept-org', @@ -131,11 +138,12 @@ describe('cleanup', () => { updatedAt: '2026-08-27T00:00:00.000Z', expiresAt: '2026-08-27T08:00:00.000Z', scopeKey: 'deadbeef', - organizationBound: false, + accountEpoch: 7, + organizationBound: true, status: 'happy', running: 2, needsInput: 1, - reconnecting: 0, + reconnecting: 1, eligibleStartedAt: '2026-08-26T23:00:00.000Z', }; _setLastGlanceableSnapshotForTests(seeded); @@ -143,40 +151,74 @@ describe('cleanup', () => { const { sink, calls } = makeSink(); registerGlanceableSink(sink); try { - republishLastSnapshotStale(); - const snapshot = lastSnapshot(calls); - expect(snapshot.status).toBe('stale'); - expect(snapshot.running).toBe(2); - expect(snapshot.needsInput).toBe(1); - expect(snapshot.revision).toBe(4); + for (const [index, now] of [ + '2026-08-27T01:00:00.000Z', + '2026-08-27T07:59:59.999Z', + ].entries()) { + vi.setSystemTime(new Date(now)); + republishLastSnapshotStale(); + expect(lastSnapshot(calls)).toEqual({ ...seeded, revision: index + 4, status: 'stale' }); + } + for (const [index, now] of [ + '2026-08-27T08:00:00.000Z', + '2026-08-27T09:00:00.000Z', + ].entries()) { + vi.setSystemTime(new Date(now)); + republishLastSnapshotStale(); + expect(lastSnapshot(calls)).toEqual({ + ...seeded, + revision: index + 6, + status: 'expired', + running: 0, + needsInput: 0, + reconnecting: 0, + eligibleStartedAt: null, + }); + } } finally { unregisterGlanceableSink(sink); } }); - it('does not overwrite a terminal blank with a stale republish', () => { - const terminal: GlanceableAgentsSnapshot = { - schemaVersion: 1, - revision: 5, - updatedAt: '2026-08-27T00:00:00.000Z', - expiresAt: '2026-08-27T08:00:00.000Z', - scopeKey: 'terminal:privacy', - organizationBound: false, - status: 'privacy', - running: 0, - needsInput: 0, - reconnecting: 0, - eligibleStartedAt: null, - }; - _setLastGlanceableSnapshotForTests(terminal); + it.each(['signed_out', 'privacy', 'expired'] as const)( + 'does not replace %s with stale before or after its deadline', + status => { + vi.useFakeTimers(); + const terminal: GlanceableAgentsSnapshot = { + schemaVersion: 1, + revision: 5, + updatedAt: '2026-08-27T00:00:00.000Z', + expiresAt: '2026-08-27T08:00:00.000Z', + scopeKey: `terminal:${status}`, + organizationBound: false, + status, + running: 0, + needsInput: 0, + reconnecting: 0, + eligibleStartedAt: null, + }; + _setLastGlanceableSnapshotForTests(terminal); - const { sink, calls } = makeSink(); - registerGlanceableSink(sink); - try { - republishLastSnapshotStale(); - expect(calls).toEqual([]); - } finally { - unregisterGlanceableSink(sink); + const { sink } = makeSink(); + registerGlanceableSink(sink); + try { + for (const now of ['2026-08-27T01:00:00.000Z', '2026-08-27T09:00:00.000Z']) { + vi.setSystemTime(new Date(now)); + republishLastSnapshotStale(); + expect(getLastGlanceableSnapshot()).toMatchObject({ + status, + scopeKey: terminal.scopeKey, + updatedAt: terminal.updatedAt, + expiresAt: terminal.expiresAt, + running: 0, + needsInput: 0, + reconnecting: 0, + eligibleStartedAt: null, + }); + } + } finally { + unregisterGlanceableSink(sink); + } } - }); + ); }); diff --git a/apps/mobile/src/lib/glanceable/cleanup.ts b/apps/mobile/src/lib/glanceable/cleanup.ts index 57f985a4a2..ded58ef91b 100644 --- a/apps/mobile/src/lib/glanceable/cleanup.ts +++ b/apps/mobile/src/lib/glanceable/cleanup.ts @@ -5,6 +5,7 @@ import { } from '@kilocode/app-shared/glanceable-agents-snapshot'; import { getLastGlanceableSnapshot } from './persist'; +import { withStatus } from './publisher'; import { getGlanceableSinks } from './sink-registry'; // Monotonic epoch bumped on every terminal blank (signed-out or privacy). The @@ -99,8 +100,8 @@ export function writePrivacySnapshotAndEnd(): void { } /** - * Republish the last snapshot with a stale status (keeps counts). Used by the - * org fence when the org list errors: stale, not lost-org. + * Republish the last snapshot as stale until its deadline, then expired. Used + * by the org fence when the org list errors: stale, not lost-org. */ export function republishLastSnapshotStale(): void { const previous = getLastGlanceableSnapshot(); @@ -112,15 +113,7 @@ export function republishLastSnapshotStale(): void { if (previous.status === 'signed_out' || previous.status === 'privacy') { return; } - const now = Date.now(); - const updatedAt = new Date(now).toISOString(); - const snapshot: GlanceableAgentsSnapshot = { - ...previous, - revision: previous.revision + 1, - updatedAt, - expiresAt: new Date(now + GLANCEABLE_SNAPSHOT_EXPIRY_MS).toISOString(), - status: 'stale', - }; + const snapshot = withStatus(previous, 'stale', Date.now()); for (const sink of getGlanceableSinks()) { sink.publish(snapshot); } diff --git a/apps/mobile/src/lib/glanceable/publisher.test.ts b/apps/mobile/src/lib/glanceable/publisher.test.ts index fedbba8203..6615ea8479 100644 --- a/apps/mobile/src/lib/glanceable/publisher.test.ts +++ b/apps/mobile/src/lib/glanceable/publisher.test.ts @@ -143,46 +143,64 @@ describe('GlanceablePublisher', () => { expect(lastSnapshot(calls, 'publish').status).toBe('empty'); }); - it('keeps counts on stale and hides counts on expired', () => { - const { sink, calls } = makeSink(); - const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); - publisher.handleSessions([{ status: 'busy' }], PUB_CTX); - publisher.handleFetchError(PUB_CTX); - expect(lastSnapshot(calls, 'publish').status).toBe('stale'); - expect(lastSnapshot(calls, 'publish').running).toBe(1); - + it('keeps the original deadline through repeated failures and stays expired after it', () => { let now = NOW; - const { sink: sink2, calls: calls2 } = makeSink(); - const publisher2 = new GlanceablePublisher({ sinks: [sink2], now: () => now }); - publisher2.handleSessions([{ status: 'busy' }], PUB_CTX); - now = NOW + GLANCEABLE_SNAPSHOT_EXPIRY_MS; - publisher2.handleSessions([{ status: 'busy' }], PUB_CTX); - const expired = calls2.filter( - (call): call is { type: 'publish'; snapshot: GlanceableAgentsSnapshot } => - call.type === 'publish' && call.snapshot.status === 'expired' + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => now }); + publisher.handleSessions( + [{ status: 'busy' }, { status: 'question' }, { status: 'retry' }], + PUB_CTX ); - expect(expired.length).toBe(1); - expect(expired[0]?.snapshot.running).toBe(0); + const successful = lastSnapshot(calls, 'publish'); + const failures = [ + [60_000, 'stale', 1], + [GLANCEABLE_SNAPSHOT_EXPIRY_MS - 1, 'stale', 1], + [GLANCEABLE_SNAPSHOT_EXPIRY_MS, 'expired', 0], + [GLANCEABLE_SNAPSHOT_EXPIRY_MS + 60_000, 'expired', 0], + ] as const; + for (const [index, [elapsed, status, expectedCount]] of failures.entries()) { + now = NOW + elapsed; + publisher.handleFetchError(PUB_CTX); + expect(lastSnapshot(calls, 'publish')).toMatchObject({ + revision: index + 2, + updatedAt: successful.updatedAt, + expiresAt: successful.expiresAt, + scopeKey: successful.scopeKey, + status, + running: expectedCount, + needsInput: expectedCount, + reconnecting: expectedCount, + }); + } + expect(lastSnapshot(calls, 'publish').eligibleStartedAt).toBeNull(); + publisher.dispose(); }); - it('does not schedule the 8s terminal for a signed-out snapshot', () => { - vi.useFakeTimers(); - const { sink, calls } = makeSink(); - const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW }); - publisher.applySnapshot( - buildGlanceableSnapshot({ + it.each(['signed_out', 'privacy'] as const)( + 'preserves %s through failures and expiry', + status => { + vi.useFakeTimers(); + let now = NOW; + const { sink, calls } = makeSink(); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => now }); + const blank = buildGlanceableSnapshot({ sessions: [], userId: 'u1', organizationId: null, now: NOW, - status: 'signed_out', - }), - PUB_CTX - ); - vi.advanceTimersByTime(8000); - expect(count(calls, 'endImmediate')).toBe(0); - publisher.dispose(); - }); + status, + }); + publisher.applySnapshot(blank, PUB_CTX); + for (const elapsed of [60_000, GLANCEABLE_SNAPSHOT_EXPIRY_MS + 1]) { + now = NOW + elapsed; + publisher.handleFetchError(PUB_CTX); + expect(lastSnapshot(calls, 'publish')).toEqual(blank); + } + vi.advanceTimersByTime(8000); + expect(count(calls, 'endImmediate')).toBe(0); + publisher.dispose(); + } + ); it('does not publish or restart after a terminal blank', () => { const { sink, calls } = makeSink(); @@ -201,7 +219,8 @@ describe('GlanceablePublisher', () => { expect(lastSnapshot(calls, 'publish').status).toBe('signed_out'); expect(count(calls, 'endImmediate')).toBe(1); - // A live cache success after the blank must not publish or restart. + // A cache error or success after the blank must not publish or restart. + publisher.handleFetchError(PUB_CTX); publisher.handleSessions([{ status: 'busy' }, { status: 'busy' }], PUB_CTX); expect(count(calls, 'startOrUpdate')).toBe(1); expect(count(calls, 'publish')).toBe(2); @@ -285,13 +304,25 @@ describe('GlanceablePublisher', () => { publisher.dispose(); }); - it('keeps the revision monotonic when seeded from an initial snapshot', () => { + it('renews the deadline on successful data while keeping seeded revisions monotonic', () => { + let now = NOW; const { sink, calls } = makeSink(); - // Seeded with revision 42; the next snapshot must be 43. const initial = snapshotFor([{ status: 'busy' }], NOW - 60_000, 41); - const publisher = new GlanceablePublisher({ sinks: [sink], now: () => NOW, initial }); - publisher.handleSessions([{ status: 'busy' }], PUB_CTX); - expect(lastSnapshot(calls, 'startOrUpdate').revision).toBe(43); + const publisher = new GlanceablePublisher({ sinks: [sink], now: () => now, initial }); + publisher.handleFetchError(PUB_CTX); + publisher.handleSessions([{ status: 'question' }], PUB_CTX); + const fresh = lastSnapshot(calls, 'startOrUpdate'); + expect(fresh).toMatchObject({ + revision: 44, + status: 'happy', + running: 0, + needsInput: 1, + updatedAt: new Date(NOW).toISOString(), + expiresAt: new Date(NOW + GLANCEABLE_SNAPSHOT_EXPIRY_MS).toISOString(), + }); + now = Date.parse(initial.expiresAt); + publisher.handleFetchError(PUB_CTX); + expect(lastSnapshot(calls, 'publish')).toEqual({ ...fresh, revision: 45, status: 'stale' }); publisher.dispose(); }); }); diff --git a/apps/mobile/src/lib/glanceable/publisher.ts b/apps/mobile/src/lib/glanceable/publisher.ts index c4826b4435..e74f241e72 100644 --- a/apps/mobile/src/lib/glanceable/publisher.ts +++ b/apps/mobile/src/lib/glanceable/publisher.ts @@ -40,12 +40,24 @@ export type GlanceablePublisherOptions = { type TimerHandle = ReturnType; -/** Copy a snapshot with a new status and a fresh revision/updatedAt/expiresAt. */ +/** Advance the status and revision without renewing stale data's lifetime. */ export function withStatus( snapshot: GlanceableAgentsSnapshot, status: GlanceableAgentsSnapshotStatus, now: number ): GlanceableAgentsSnapshot { + if (status === 'stale') { + if (snapshot.status === 'signed_out' || snapshot.status === 'privacy') { + return snapshot; + } + const expired = snapshot.status === 'expired' || now >= Date.parse(snapshot.expiresAt); + return { + ...snapshot, + revision: snapshot.revision + 1, + status: expired ? 'expired' : 'stale', + ...(expired ? { running: 0, needsInput: 0, reconnecting: 0, eligibleStartedAt: null } : {}), + }; + } const updatedAt = new Date(now).toISOString(); return { ...snapshot, @@ -140,19 +152,22 @@ export class GlanceablePublisher { this.current = snapshot; } - /** Cache update failed: republish the last counts with a stale status. */ - handleFetchError(ctx: GlanceablePublisherContext): void { - if (this.isGated()) { - return; - } - const now = this.now(); - if (this.applyExpiry(now, ctx) || this.current === null) { + /** Cache update failed: keep the last counts only until their original deadline. */ + handleFetchError(_ctx: GlanceablePublisherContext): void { + if (this.isGated() || this.current === null) { return; } // A fetch error supersedes any pending coalesced happy emit: otherwise the // pre-error snapshot would fire later and overwrite the stale counts. this.cancelCoalesce(); - const snapshot = withStatus(this.current, 'stale', now); + const snapshot = withStatus(this.current, 'stale', this.now()); + if (snapshot === this.current) { + return; + } + if (snapshot.status === 'expired') { + this.cancelTerminal(); + this.activityStarted = false; + } this.publish(snapshot); this.current = snapshot; } From 8f8b7afa8b382d774d9e1c7917a61f52242c5f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Fri, 28 Aug 2026 13:17:19 +0200 Subject: [PATCH 6/6] fix(i18n): defer native keys to their consuming stack levels --- apps/mobile/src/i18n/locales/af.json | 5 +---- apps/mobile/src/i18n/locales/am.json | 5 +---- apps/mobile/src/i18n/locales/ar.json | 5 +---- apps/mobile/src/i18n/locales/az.json | 5 +---- apps/mobile/src/i18n/locales/be.json | 5 +---- apps/mobile/src/i18n/locales/bg.json | 5 +---- apps/mobile/src/i18n/locales/bn.json | 5 +---- apps/mobile/src/i18n/locales/bs.json | 5 +---- apps/mobile/src/i18n/locales/ca.json | 5 +---- apps/mobile/src/i18n/locales/ckb.json | 5 +---- apps/mobile/src/i18n/locales/cs.json | 5 +---- apps/mobile/src/i18n/locales/cy.json | 5 +---- apps/mobile/src/i18n/locales/da.json | 5 +---- apps/mobile/src/i18n/locales/de.json | 5 +---- apps/mobile/src/i18n/locales/el.json | 5 +---- apps/mobile/src/i18n/locales/en.json | 5 +---- apps/mobile/src/i18n/locales/es.json | 5 +---- apps/mobile/src/i18n/locales/et.json | 5 +---- apps/mobile/src/i18n/locales/eu.json | 5 +---- apps/mobile/src/i18n/locales/fa.json | 5 +---- apps/mobile/src/i18n/locales/fi.json | 5 +---- apps/mobile/src/i18n/locales/fil.json | 5 +---- apps/mobile/src/i18n/locales/fr.json | 5 +---- apps/mobile/src/i18n/locales/ga.json | 5 +---- apps/mobile/src/i18n/locales/gl.json | 5 +---- apps/mobile/src/i18n/locales/gu.json | 5 +---- apps/mobile/src/i18n/locales/ha.json | 5 +---- apps/mobile/src/i18n/locales/he.json | 5 +---- apps/mobile/src/i18n/locales/hi.json | 5 +---- apps/mobile/src/i18n/locales/hr.json | 5 +---- apps/mobile/src/i18n/locales/ht.json | 5 +---- apps/mobile/src/i18n/locales/hu.json | 5 +---- apps/mobile/src/i18n/locales/hy.json | 5 +---- apps/mobile/src/i18n/locales/id.json | 5 +---- apps/mobile/src/i18n/locales/ig.json | 5 +---- apps/mobile/src/i18n/locales/is.json | 5 +---- apps/mobile/src/i18n/locales/it.json | 5 +---- apps/mobile/src/i18n/locales/ja.json | 5 +---- apps/mobile/src/i18n/locales/ka.json | 5 +---- apps/mobile/src/i18n/locales/kk.json | 5 +---- apps/mobile/src/i18n/locales/km.json | 5 +---- apps/mobile/src/i18n/locales/kn.json | 5 +---- apps/mobile/src/i18n/locales/ko.json | 5 +---- apps/mobile/src/i18n/locales/lo.json | 5 +---- apps/mobile/src/i18n/locales/lt.json | 5 +---- apps/mobile/src/i18n/locales/lv.json | 5 +---- apps/mobile/src/i18n/locales/mg.json | 5 +---- apps/mobile/src/i18n/locales/mi.json | 5 +---- apps/mobile/src/i18n/locales/mk.json | 5 +---- apps/mobile/src/i18n/locales/ml.json | 5 +---- apps/mobile/src/i18n/locales/mn.json | 5 +---- apps/mobile/src/i18n/locales/mr.json | 5 +---- apps/mobile/src/i18n/locales/ms.json | 5 +---- apps/mobile/src/i18n/locales/mt.json | 5 +---- apps/mobile/src/i18n/locales/my.json | 5 +---- apps/mobile/src/i18n/locales/nb.json | 5 +---- apps/mobile/src/i18n/locales/ne.json | 5 +---- apps/mobile/src/i18n/locales/nl.json | 5 +---- apps/mobile/src/i18n/locales/om.json | 5 +---- apps/mobile/src/i18n/locales/or.json | 5 +---- apps/mobile/src/i18n/locales/pa.json | 5 +---- apps/mobile/src/i18n/locales/pl.json | 5 +---- apps/mobile/src/i18n/locales/ps.json | 5 +---- apps/mobile/src/i18n/locales/pt-BR.json | 5 +---- apps/mobile/src/i18n/locales/pt.json | 5 +---- apps/mobile/src/i18n/locales/ro.json | 5 +---- apps/mobile/src/i18n/locales/ru.json | 5 +---- apps/mobile/src/i18n/locales/si.json | 5 +---- apps/mobile/src/i18n/locales/sk.json | 5 +---- apps/mobile/src/i18n/locales/sl.json | 5 +---- apps/mobile/src/i18n/locales/so.json | 5 +---- apps/mobile/src/i18n/locales/sq.json | 5 +---- apps/mobile/src/i18n/locales/sr.json | 5 +---- apps/mobile/src/i18n/locales/sv.json | 5 +---- apps/mobile/src/i18n/locales/sw.json | 5 +---- apps/mobile/src/i18n/locales/ta.json | 5 +---- apps/mobile/src/i18n/locales/te.json | 5 +---- apps/mobile/src/i18n/locales/th.json | 5 +---- apps/mobile/src/i18n/locales/tr.json | 5 +---- apps/mobile/src/i18n/locales/uk.json | 5 +---- apps/mobile/src/i18n/locales/ur.json | 5 +---- apps/mobile/src/i18n/locales/uz.json | 5 +---- apps/mobile/src/i18n/locales/vi.json | 5 +---- apps/mobile/src/i18n/locales/yo.json | 5 +---- apps/mobile/src/i18n/locales/zh-Hans.json | 5 +---- apps/mobile/src/i18n/locales/zh-Hant.json | 5 +---- apps/mobile/src/i18n/locales/zu.json | 5 +---- 87 files changed, 87 insertions(+), 348 deletions(-) diff --git a/apps/mobile/src/i18n/locales/af.json b/apps/mobile/src/i18n/locales/af.json index cf670472a7..d6d12d53ba 100644 --- a/apps/mobile/src/i18n/locales/af.json +++ b/apps/mobile/src/i18n/locales/af.json @@ -3202,9 +3202,6 @@ "openAgents": "Maak agente oop", "running": "LOOP", "needsInput": "benodig invoer", - "reconnecting": "Verbind tans weer", - "channelName": "Aktiewe agente", - "activityKitDisabledTitle": "Regstreekse Aktiwiteite is af", - "activityKitDisabledBody": "Skakel Regstreekse Aktiwiteite in Instellings aan om Aktiewe Agente op die Sluitskerm te sien." + "reconnecting": "Verbind tans weer" } } diff --git a/apps/mobile/src/i18n/locales/am.json b/apps/mobile/src/i18n/locales/am.json index 6eb270f961..279c02b9f8 100644 --- a/apps/mobile/src/i18n/locales/am.json +++ b/apps/mobile/src/i18n/locales/am.json @@ -3202,9 +3202,6 @@ "openAgents": "ወኪሎችን ይክፈቱ", "running": "በስራ ላይ", "needsInput": "ግብዓት ይፈልጋል", - "reconnecting": "እንደገና በመገናኘት ላይ", - "channelName": "ንቁ ወኪሎች", - "activityKitDisabledTitle": "የቀጥታ እንቅስቃሴዎች ጠፍተዋል", - "activityKitDisabledBody": "ንቁ ወኪሎችን በመቆለፊያ ማያ ገጽ ላይ ለማየት በቅንብሮች ውስጥ የቀጥታ እንቅስቃሴዎችን ያብሩ።" + "reconnecting": "እንደገና በመገናኘት ላይ" } } diff --git a/apps/mobile/src/i18n/locales/ar.json b/apps/mobile/src/i18n/locales/ar.json index baaef6572f..c78bb4b9fc 100644 --- a/apps/mobile/src/i18n/locales/ar.json +++ b/apps/mobile/src/i18n/locales/ar.json @@ -3286,9 +3286,6 @@ "openAgents": "فتح الوكلاء", "running": "قيد التشغيل", "needsInput": "يتطلب إدخالًا", - "reconnecting": "جارٍ إعادة الاتصال", - "channelName": "الوكلاء النشطون", - "activityKitDisabledTitle": "الأنشطة المباشرة متوقفة", - "activityKitDisabledBody": "فعّل الأنشطة المباشرة في الإعدادات لرؤية الوكلاء النشطين على شاشة القفل." + "reconnecting": "جارٍ إعادة الاتصال" } } diff --git a/apps/mobile/src/i18n/locales/az.json b/apps/mobile/src/i18n/locales/az.json index 760a5f472b..83b88830e0 100644 --- a/apps/mobile/src/i18n/locales/az.json +++ b/apps/mobile/src/i18n/locales/az.json @@ -3202,9 +3202,6 @@ "openAgents": "Agentləri açın", "running": "İŞLƏYİR", "needsInput": "GİRİŞ TƏLƏB OLUNUR", - "reconnecting": "Yenidən qoşulur", - "channelName": "Aktiv agentlər", - "activityKitDisabledTitle": "Canlı fəaliyyətlər söndürülüb", - "activityKitDisabledBody": "Kilid ekranında aktiv agentləri görmək üçün Parametrlərdə Canlı fəaliyyətləri aktivləşdirin." + "reconnecting": "Yenidən qoşulur" } } diff --git a/apps/mobile/src/i18n/locales/be.json b/apps/mobile/src/i18n/locales/be.json index dd9e35eea3..bf323988fc 100644 --- a/apps/mobile/src/i18n/locales/be.json +++ b/apps/mobile/src/i18n/locales/be.json @@ -3244,9 +3244,6 @@ "openAgents": "Адкрыць агентаў", "running": "ПРАЦУЕ", "needsInput": "патрабуецца ўвод", - "reconnecting": "Паўторнае падключэнне", - "channelName": "Актыўныя агенты", - "activityKitDisabledTitle": "Жывыя дзеянні выключаны", - "activityKitDisabledBody": "Уключыце «Жывыя дзеянні» ў «Наладах», каб бачыць актыўных агентаў на экране блакіроўкі." + "reconnecting": "Паўторнае падключэнне" } } diff --git a/apps/mobile/src/i18n/locales/bg.json b/apps/mobile/src/i18n/locales/bg.json index bd69c06cff..99024530e1 100644 --- a/apps/mobile/src/i18n/locales/bg.json +++ b/apps/mobile/src/i18n/locales/bg.json @@ -3202,9 +3202,6 @@ "openAgents": "Отворете агентите", "running": "Изпълнява се", "needsInput": "изисква въвеждане", - "reconnecting": "Повторно свързване", - "channelName": "Активни агенти", - "activityKitDisabledTitle": "Дейностите на живо са изключени", - "activityKitDisabledBody": "Включете Дейности на живо в Настройки, за да виждате активните агенти на заключения екран." + "reconnecting": "Повторно свързване" } } diff --git a/apps/mobile/src/i18n/locales/bn.json b/apps/mobile/src/i18n/locales/bn.json index c4987395e3..1a683c1555 100644 --- a/apps/mobile/src/i18n/locales/bn.json +++ b/apps/mobile/src/i18n/locales/bn.json @@ -3202,9 +3202,6 @@ "openAgents": "এজেন্টগুলি খুলুন", "running": "চলছে", "needsInput": "ইনপুট প্রয়োজন", - "reconnecting": "পুনরায় সংযোগ করা হচ্ছে", - "channelName": "সক্রিয় এজেন্ট", - "activityKitDisabledTitle": "সরাসরি কার্যকলাপ বন্ধ আছে", - "activityKitDisabledBody": "লক স্ক্রিনে সক্রিয় এজেন্টগুলি দেখতে সেটিংসে সরাসরি কার্যকলাপ চালু করুন।" + "reconnecting": "পুনরায় সংযোগ করা হচ্ছে" } } diff --git a/apps/mobile/src/i18n/locales/bs.json b/apps/mobile/src/i18n/locales/bs.json index df8241f41a..ad2c5bb2ee 100644 --- a/apps/mobile/src/i18n/locales/bs.json +++ b/apps/mobile/src/i18n/locales/bs.json @@ -3223,9 +3223,6 @@ "openAgents": "Otvorite agente", "running": "RADI", "needsInput": "treba unos", - "reconnecting": "Ponovno povezivanje", - "channelName": "Aktivni agenti", - "activityKitDisabledTitle": "Aktivnosti uživo su isključene", - "activityKitDisabledBody": "Uključite aktivnosti uživo u Postavkama da biste vidjeli aktivne agente na zaključanom ekranu." + "reconnecting": "Ponovno povezivanje" } } diff --git a/apps/mobile/src/i18n/locales/ca.json b/apps/mobile/src/i18n/locales/ca.json index 8e1ae0ba1b..9e95791e39 100644 --- a/apps/mobile/src/i18n/locales/ca.json +++ b/apps/mobile/src/i18n/locales/ca.json @@ -3223,9 +3223,6 @@ "openAgents": "Obre els agents", "running": "EN EXECUCIÓ", "needsInput": "requereix entrada", - "reconnecting": "Reconnectant", - "channelName": "Agents actius", - "activityKitDisabledTitle": "Les activitats en directe estan desactivades", - "activityKitDisabledBody": "Activa les activitats en directe a Configuració per veure els agents actius a la pantalla de bloqueig." + "reconnecting": "Reconnectant" } } diff --git a/apps/mobile/src/i18n/locales/ckb.json b/apps/mobile/src/i18n/locales/ckb.json index b162480f86..3128184dd0 100644 --- a/apps/mobile/src/i18n/locales/ckb.json +++ b/apps/mobile/src/i18n/locales/ckb.json @@ -3202,9 +3202,6 @@ "openAgents": "کردنەوەی ئەجێنتەکان", "running": "لە کاردایە", "needsInput": "پێویستی بە داخڵکردن", - "reconnecting": "لە پەیوەستبوونەوەدایە", - "channelName": "ئەجێنتە چالاکەکان", - "activityKitDisabledTitle": "چالاکییە ڕاستەوخۆکان ناچالاکن", - "activityKitDisabledBody": "چالاکییە ڕاستەوخۆکان لە ڕێکخستنەکان چالاک بکە بۆ بینینی ئەجێنتە چالاکەکان لە شاشەی قوفڵ." + "reconnecting": "لە پەیوەستبوونەوەدایە" } } diff --git a/apps/mobile/src/i18n/locales/cs.json b/apps/mobile/src/i18n/locales/cs.json index 689e0cef35..5e2eced1b1 100644 --- a/apps/mobile/src/i18n/locales/cs.json +++ b/apps/mobile/src/i18n/locales/cs.json @@ -3244,9 +3244,6 @@ "openAgents": "Otevřít agenty", "running": "BĚŽÍ", "needsInput": "vyžaduje vstup", - "reconnecting": "Obnovování připojení", - "channelName": "Aktivní agenti", - "activityKitDisabledTitle": "Živé aktivity jsou vypnuté", - "activityKitDisabledBody": "Zapněte Živé aktivity v Nastavení, abyste viděli aktivní agenty na zamknuté obrazovce." + "reconnecting": "Obnovování připojení" } } diff --git a/apps/mobile/src/i18n/locales/cy.json b/apps/mobile/src/i18n/locales/cy.json index ae034409c3..5f362a52c6 100644 --- a/apps/mobile/src/i18n/locales/cy.json +++ b/apps/mobile/src/i18n/locales/cy.json @@ -3286,9 +3286,6 @@ "openAgents": "Agorwch asiantau", "running": "YN RHEDEG", "needsInput": "angen mewnbwn", - "reconnecting": "Yn ailgysylltu", - "channelName": "Asiantau gweithredol", - "activityKitDisabledTitle": "Mae Gweithgareddau Byw wedi'u diffodd", - "activityKitDisabledBody": "Trowch Weithgareddau Byw ymlaen yn Gosodiadau i weld Asiantau gweithredol ar y Sgrin Glo." + "reconnecting": "Yn ailgysylltu" } } diff --git a/apps/mobile/src/i18n/locales/da.json b/apps/mobile/src/i18n/locales/da.json index e4e3101fc3..266c995879 100644 --- a/apps/mobile/src/i18n/locales/da.json +++ b/apps/mobile/src/i18n/locales/da.json @@ -3202,9 +3202,6 @@ "openAgents": "Åbn agenter", "running": "KØRER", "needsInput": "kræver input", - "reconnecting": "Genopretter forbindelsen", - "channelName": "Aktive agenter", - "activityKitDisabledTitle": "Liveaktiviteter er slået fra", - "activityKitDisabledBody": "Slå Liveaktiviteter til i Indstillinger for at se aktive agenter på låseskærmen." + "reconnecting": "Genopretter forbindelsen" } } diff --git a/apps/mobile/src/i18n/locales/de.json b/apps/mobile/src/i18n/locales/de.json index 0d65fb86b4..9b5d2d7531 100644 --- a/apps/mobile/src/i18n/locales/de.json +++ b/apps/mobile/src/i18n/locales/de.json @@ -3202,9 +3202,6 @@ "openAgents": "Agenten öffnen", "running": "LÄUFT", "needsInput": "Eingabe erforderlich", - "reconnecting": "Verbindung wird wiederhergestellt", - "channelName": "Aktive Agenten", - "activityKitDisabledTitle": "Live-Aktivitäten sind deaktiviert", - "activityKitDisabledBody": "Aktiviere Live-Aktivitäten in den Einstellungen, um aktive Agenten auf dem Sperrbildschirm zu sehen." + "reconnecting": "Verbindung wird wiederhergestellt" } } diff --git a/apps/mobile/src/i18n/locales/el.json b/apps/mobile/src/i18n/locales/el.json index afc450b50f..1afed8347c 100644 --- a/apps/mobile/src/i18n/locales/el.json +++ b/apps/mobile/src/i18n/locales/el.json @@ -3202,9 +3202,6 @@ "openAgents": "Ανοίξτε τους πράκτορες", "running": "Σε εξέλιξη", "needsInput": "χρειάζεται είσοδο", - "reconnecting": "Επανασύνδεση", - "channelName": "Ενεργοί πράκτορες", - "activityKitDisabledTitle": "Οι Ζωντανές δραστηριότητες είναι απενεργοποιημένες", - "activityKitDisabledBody": "Ενεργοποιήστε τις Ζωντανές δραστηριότητες στις Ρυθμίσεις για να δείτε τους ενεργούς πράκτορες στην Οθόνη κλειδώματος." + "reconnecting": "Επανασύνδεση" } } diff --git a/apps/mobile/src/i18n/locales/en.json b/apps/mobile/src/i18n/locales/en.json index 725ca45c84..511ae214ec 100644 --- a/apps/mobile/src/i18n/locales/en.json +++ b/apps/mobile/src/i18n/locales/en.json @@ -3202,9 +3202,6 @@ "openAgents": "Open agents", "running": "Running", "needsInput": "Needs input", - "reconnecting": "Reconnecting", - "channelName": "Active agents", - "activityKitDisabledTitle": "Live Activities are off", - "activityKitDisabledBody": "Turn on Live Activities in Settings to see Active Agents on the Lock Screen." + "reconnecting": "Reconnecting" } } diff --git a/apps/mobile/src/i18n/locales/es.json b/apps/mobile/src/i18n/locales/es.json index 8eeb9eaa2d..ad24dc8f7a 100644 --- a/apps/mobile/src/i18n/locales/es.json +++ b/apps/mobile/src/i18n/locales/es.json @@ -3223,9 +3223,6 @@ "openAgents": "Abrir agentes", "running": "EN EJECUCIÓN", "needsInput": "requiere entrada", - "reconnecting": "Reconectando", - "channelName": "Agentes activos", - "activityKitDisabledTitle": "Las actividades en directo están desactivadas", - "activityKitDisabledBody": "Activa las actividades en directo en Ajustes para ver los agentes activos en la pantalla de bloqueo." + "reconnecting": "Reconectando" } } diff --git a/apps/mobile/src/i18n/locales/et.json b/apps/mobile/src/i18n/locales/et.json index bcc271ae98..5c6f1acb69 100644 --- a/apps/mobile/src/i18n/locales/et.json +++ b/apps/mobile/src/i18n/locales/et.json @@ -3202,9 +3202,6 @@ "openAgents": "Avage agendid", "running": "TÖÖTAB", "needsInput": "vajab sisendit", - "reconnecting": "Ühenduse taastamine", - "channelName": "Aktiivsed agendid", - "activityKitDisabledTitle": "Reaalajas tegevused on välja lülitatud", - "activityKitDisabledBody": "Lülitage seadetes reaalajas tegevused sisse, et näha aktiivseid agente lukustuskuval." + "reconnecting": "Ühenduse taastamine" } } diff --git a/apps/mobile/src/i18n/locales/eu.json b/apps/mobile/src/i18n/locales/eu.json index 69401ac1c6..6816b5ebdd 100644 --- a/apps/mobile/src/i18n/locales/eu.json +++ b/apps/mobile/src/i18n/locales/eu.json @@ -3202,9 +3202,6 @@ "openAgents": "Ireki agenteak", "running": "Exekutatzen", "needsInput": "sarreraren zain", - "reconnecting": "Berriro konektatzen", - "channelName": "Agente aktiboak", - "activityKitDisabledTitle": "Zuzeneko jarduerak desaktibatuta daude", - "activityKitDisabledBody": "Aktibatu Zuzeneko jarduerak Ezarpenetan, Agente aktiboak Blokeo-pantailan ikusteko." + "reconnecting": "Berriro konektatzen" } } diff --git a/apps/mobile/src/i18n/locales/fa.json b/apps/mobile/src/i18n/locales/fa.json index 4742b0a89b..7f9dfc7d81 100644 --- a/apps/mobile/src/i18n/locales/fa.json +++ b/apps/mobile/src/i18n/locales/fa.json @@ -3202,9 +3202,6 @@ "openAgents": "عامل‌ها را باز کنید", "running": "در حال اجرا", "needsInput": "نیاز به ورودی", - "reconnecting": "در حال اتصال مجدد", - "channelName": "عامل‌های فعال", - "activityKitDisabledTitle": "فعالیت‌های زنده خاموش هستند", - "activityKitDisabledBody": "برای دیدن عامل‌های فعال در صفحه قفل، فعالیت‌های زنده را در تنظیمات روشن کنید." + "reconnecting": "در حال اتصال مجدد" } } diff --git a/apps/mobile/src/i18n/locales/fi.json b/apps/mobile/src/i18n/locales/fi.json index bec672d01b..9574f10953 100644 --- a/apps/mobile/src/i18n/locales/fi.json +++ b/apps/mobile/src/i18n/locales/fi.json @@ -3202,9 +3202,6 @@ "openAgents": "Avaa agentit", "running": "KÄYNNISSÄ", "needsInput": "vaatii syötettä", - "reconnecting": "Yhdistetään uudelleen", - "channelName": "Aktiiviset agentit", - "activityKitDisabledTitle": "Live-aktiviteetit ovat pois päältä", - "activityKitDisabledBody": "Ota live-aktiviteetit käyttöön Asetuksissa, niin näet aktiiviset agentit lukitulla näytöllä." + "reconnecting": "Yhdistetään uudelleen" } } diff --git a/apps/mobile/src/i18n/locales/fil.json b/apps/mobile/src/i18n/locales/fil.json index 0aa3f3643a..0fb827ddc8 100644 --- a/apps/mobile/src/i18n/locales/fil.json +++ b/apps/mobile/src/i18n/locales/fil.json @@ -3202,9 +3202,6 @@ "openAgents": "Buksan ang mga agent", "running": "TUMATAKBO", "needsInput": "kailangan ng input", - "reconnecting": "Muling kumokonekta", - "channelName": "Mga aktibong agent", - "activityKitDisabledTitle": "Naka-off ang Mga Live na Aktibidad", - "activityKitDisabledBody": "I-on ang Mga Live na Aktibidad sa Mga setting para makita ang Mga aktibong agent sa Naka-lock na Screen." + "reconnecting": "Muling kumokonekta" } } diff --git a/apps/mobile/src/i18n/locales/fr.json b/apps/mobile/src/i18n/locales/fr.json index a062104a15..87b5af0562 100644 --- a/apps/mobile/src/i18n/locales/fr.json +++ b/apps/mobile/src/i18n/locales/fr.json @@ -3223,9 +3223,6 @@ "openAgents": "Ouvrir les agents", "running": "EN COURS", "needsInput": "saisie requise", - "reconnecting": "Reconnexion en cours", - "channelName": "Agents actifs", - "activityKitDisabledTitle": "Les activités en direct sont désactivées", - "activityKitDisabledBody": "Activez les activités en direct dans Réglages pour voir les agents actifs sur l'écran verrouillé." + "reconnecting": "Reconnexion en cours" } } diff --git a/apps/mobile/src/i18n/locales/ga.json b/apps/mobile/src/i18n/locales/ga.json index 0e54003877..a240f956b2 100644 --- a/apps/mobile/src/i18n/locales/ga.json +++ b/apps/mobile/src/i18n/locales/ga.json @@ -3265,9 +3265,6 @@ "openAgents": "Oscail gníomhairí", "running": "AG RITH", "needsInput": "teastaíonn ionchur", - "reconnecting": "Ag athcheangal", - "channelName": "Gníomhairí gníomhacha", - "activityKitDisabledTitle": "Tá Gníomhaíochtaí Beo as", - "activityKitDisabledBody": "Cumasaigh Gníomhaíochtaí Beo sna Socruithe chun Gníomhairí gníomhacha a fheiceáil ar an Scáileán Glasála." + "reconnecting": "Ag athcheangal" } } diff --git a/apps/mobile/src/i18n/locales/gl.json b/apps/mobile/src/i18n/locales/gl.json index e59937cb68..26faac0f14 100644 --- a/apps/mobile/src/i18n/locales/gl.json +++ b/apps/mobile/src/i18n/locales/gl.json @@ -3202,9 +3202,6 @@ "openAgents": "Abrir axentes", "running": "Executando", "needsInput": "precisa entrada", - "reconnecting": "Reconectando", - "channelName": "Axentes activos", - "activityKitDisabledTitle": "As actividades en directo están desactivadas", - "activityKitDisabledBody": "Activa as actividades en directo en Configuración para ver os axentes activos na pantalla de bloqueo." + "reconnecting": "Reconectando" } } diff --git a/apps/mobile/src/i18n/locales/gu.json b/apps/mobile/src/i18n/locales/gu.json index b23be114ea..189a6d3be7 100644 --- a/apps/mobile/src/i18n/locales/gu.json +++ b/apps/mobile/src/i18n/locales/gu.json @@ -3202,9 +3202,6 @@ "openAgents": "એજન્ટો ખોલો", "running": "ચાલી રહ્યું છે", "needsInput": "ઇનપુટ જરૂરી", - "reconnecting": "ફરી કનેક્ટ થઈ રહ્યું છે", - "channelName": "સક્રિય એજન્ટો", - "activityKitDisabledTitle": "લાઇવ પ્રવૃત્તિઓ બંધ છે", - "activityKitDisabledBody": "લૉક સ્ક્રીન પર સક્રિય એજન્ટો જોવા માટે સેટિંગ્સમાં લાઇવ પ્રવૃત્તિઓ ચાલુ કરો." + "reconnecting": "ફરી કનેક્ટ થઈ રહ્યું છે" } } diff --git a/apps/mobile/src/i18n/locales/ha.json b/apps/mobile/src/i18n/locales/ha.json index 0707d0bb16..9db5546541 100644 --- a/apps/mobile/src/i18n/locales/ha.json +++ b/apps/mobile/src/i18n/locales/ha.json @@ -3202,9 +3202,6 @@ "openAgents": "Buɗe wakilai", "running": "Ana gudana", "needsInput": "yana buƙatar bayani", - "reconnecting": "Ana sake haɗawa", - "channelName": "Wakilai da ke aiki", - "activityKitDisabledTitle": "Ayyukan Kai Tsaye suna a kashe", - "activityKitDisabledBody": "Kunna Ayyukan Kai Tsaye a cikin Saituna don ganin Wakilai da ke Aiki a kan Allon Kulle." + "reconnecting": "Ana sake haɗawa" } } diff --git a/apps/mobile/src/i18n/locales/he.json b/apps/mobile/src/i18n/locales/he.json index f673155d73..506cad5462 100644 --- a/apps/mobile/src/i18n/locales/he.json +++ b/apps/mobile/src/i18n/locales/he.json @@ -3223,9 +3223,6 @@ "openAgents": "פתח סוכנים", "running": "רץ", "needsInput": "נדרש קלט", - "reconnecting": "מתחבר מחדש", - "channelName": "סוכנים פעילים", - "activityKitDisabledTitle": "פעילויות בזמן אמת כבויות", - "activityKitDisabledBody": "הפעל פעילויות בזמן אמת בהגדרות כדי לראות סוכנים פעילים במסך הנעילה." + "reconnecting": "מתחבר מחדש" } } diff --git a/apps/mobile/src/i18n/locales/hi.json b/apps/mobile/src/i18n/locales/hi.json index 63e95174cc..60947511e1 100644 --- a/apps/mobile/src/i18n/locales/hi.json +++ b/apps/mobile/src/i18n/locales/hi.json @@ -3202,9 +3202,6 @@ "openAgents": "एजेंट खोलें", "running": "चालू", "needsInput": "इनपुट आवश्यक", - "reconnecting": "फिर से कनेक्ट हो रहा है", - "channelName": "सक्रिय एजेंट", - "activityKitDisabledTitle": "लाइव ऐक्टिविटी बंद हैं", - "activityKitDisabledBody": "लॉक स्क्रीन पर सक्रिय एजेंट देखने के लिए सेटिंग में लाइव ऐक्टिविटी चालू करें।" + "reconnecting": "फिर से कनेक्ट हो रहा है" } } diff --git a/apps/mobile/src/i18n/locales/hr.json b/apps/mobile/src/i18n/locales/hr.json index 7cb949d93a..263fdaa48c 100644 --- a/apps/mobile/src/i18n/locales/hr.json +++ b/apps/mobile/src/i18n/locales/hr.json @@ -3223,9 +3223,6 @@ "openAgents": "Otvorite agente", "running": "RADI", "needsInput": "treba unos", - "reconnecting": "Ponovno povezivanje", - "channelName": "Aktivni agenti", - "activityKitDisabledTitle": "Aktivnosti uživo su isključene", - "activityKitDisabledBody": "Uključite Aktivnosti uživo u Postavkama kako biste vidjeli aktivne agente na zaključanom zaslonu." + "reconnecting": "Ponovno povezivanje" } } diff --git a/apps/mobile/src/i18n/locales/ht.json b/apps/mobile/src/i18n/locales/ht.json index 92272deb3c..44aee58be6 100644 --- a/apps/mobile/src/i18n/locales/ht.json +++ b/apps/mobile/src/i18n/locales/ht.json @@ -3202,9 +3202,6 @@ "openAgents": "Louvri ajans yo", "running": "AP KOURI", "needsInput": "bezwen input", - "reconnecting": "Ap rekonekte", - "channelName": "Ajans aktif yo", - "activityKitDisabledTitle": "Aktivite an dirèk yo fèmen", - "activityKitDisabledBody": "Aktive Aktivite an dirèk nan Paramèt pou wè Ajans aktif yo sou Ekran bloke a." + "reconnecting": "Ap rekonekte" } } diff --git a/apps/mobile/src/i18n/locales/hu.json b/apps/mobile/src/i18n/locales/hu.json index 72be12dc20..5813696bc2 100644 --- a/apps/mobile/src/i18n/locales/hu.json +++ b/apps/mobile/src/i18n/locales/hu.json @@ -3202,9 +3202,6 @@ "openAgents": "Ügynökök megnyitása", "running": "Folyamatban", "needsInput": "bemenetet igényel", - "reconnecting": "Újracsatlakozás", - "channelName": "Aktív ügynökök", - "activityKitDisabledTitle": "Az Élő tevékenységek ki vannak kapcsolva", - "activityKitDisabledBody": "Kapcsolja be az Élő tevékenységeket a Beállításokban, hogy az aktív ügynökök megjelenjenek a zárolási képernyőn." + "reconnecting": "Újracsatlakozás" } } diff --git a/apps/mobile/src/i18n/locales/hy.json b/apps/mobile/src/i18n/locales/hy.json index fd86d5065b..f30a70dd75 100644 --- a/apps/mobile/src/i18n/locales/hy.json +++ b/apps/mobile/src/i18n/locales/hy.json @@ -3202,9 +3202,6 @@ "openAgents": "Բացեք գործակալները", "running": "Ընթացքի մեջ է", "needsInput": "մուտքագրման կարիք ունի", - "reconnecting": "Կրկին միացում", - "channelName": "Ակտիվ գործակալներ", - "activityKitDisabledTitle": "Ուղիղ ակտիվություններն անջատված են", - "activityKitDisabledBody": "Միացրեք «Ուղիղ ակտիվություններ»-ը Կարգավորումներում՝ ակտիվ գործակալներին կողպման էկրանին տեսնելու համար։" + "reconnecting": "Կրկին միացում" } } diff --git a/apps/mobile/src/i18n/locales/id.json b/apps/mobile/src/i18n/locales/id.json index 53be7b234d..28e02535b6 100644 --- a/apps/mobile/src/i18n/locales/id.json +++ b/apps/mobile/src/i18n/locales/id.json @@ -3202,9 +3202,6 @@ "openAgents": "Buka agen", "running": "BERJALAN", "needsInput": "memerlukan input", - "reconnecting": "Menghubungkan kembali", - "channelName": "Agen aktif", - "activityKitDisabledTitle": "Aktivitas Langsung nonaktif", - "activityKitDisabledBody": "Aktifkan Aktivitas Langsung di Pengaturan untuk melihat Agen Aktif di Layar Terkunci." + "reconnecting": "Menghubungkan kembali" } } diff --git a/apps/mobile/src/i18n/locales/ig.json b/apps/mobile/src/i18n/locales/ig.json index c4aadb9e50..c17e85e58c 100644 --- a/apps/mobile/src/i18n/locales/ig.json +++ b/apps/mobile/src/i18n/locales/ig.json @@ -3202,9 +3202,6 @@ "openAgents": "Mepee ndị ọrụ", "running": "NA-AGBA", "needsInput": "chọrọ ntinye", - "reconnecting": "Na-ejikọ ọzọ", - "channelName": "Ndị ọrụ na-arụ ọrụ", - "activityKitDisabledTitle": "Agbanyụrụ Ihe Omume Dị Ndụ", - "activityKitDisabledBody": "Gbanye Ihe Omume Dị Ndụ na Ntọala iji hụ Ndị ọrụ na-arụ ọrụ na Ihuenyo Mkpọchi." + "reconnecting": "Na-ejikọ ọzọ" } } diff --git a/apps/mobile/src/i18n/locales/is.json b/apps/mobile/src/i18n/locales/is.json index 0988544e0d..0918a986fe 100644 --- a/apps/mobile/src/i18n/locales/is.json +++ b/apps/mobile/src/i18n/locales/is.json @@ -3202,9 +3202,6 @@ "openAgents": "Opna umboð", "running": "Í gangi", "needsInput": "þarfnast inntaks", - "reconnecting": "Tengist aftur", - "channelName": "Virk umboð", - "activityKitDisabledTitle": "Slökkt er á Beinni virkni", - "activityKitDisabledBody": "Kveiktu á Beinni virkni í Stillingum til að sjá Virk umboð á Lásskjánum." + "reconnecting": "Tengist aftur" } } diff --git a/apps/mobile/src/i18n/locales/it.json b/apps/mobile/src/i18n/locales/it.json index a6dff9624e..8aad0e857b 100644 --- a/apps/mobile/src/i18n/locales/it.json +++ b/apps/mobile/src/i18n/locales/it.json @@ -3223,9 +3223,6 @@ "openAgents": "Apri agenti", "running": "IN ESECUZIONE", "needsInput": "richiede input", - "reconnecting": "Riconnessione in corso", - "channelName": "Agenti attivi", - "activityKitDisabledTitle": "Le attività in tempo reale sono disattivate", - "activityKitDisabledBody": "Attiva le attività in tempo reale in Impostazioni per vedere gli agenti attivi sulla schermata di blocco." + "reconnecting": "Riconnessione in corso" } } diff --git a/apps/mobile/src/i18n/locales/ja.json b/apps/mobile/src/i18n/locales/ja.json index 01c1057b01..83ce883471 100644 --- a/apps/mobile/src/i18n/locales/ja.json +++ b/apps/mobile/src/i18n/locales/ja.json @@ -3202,9 +3202,6 @@ "openAgents": "エージェントを開く", "running": "実行中", "needsInput": "入力が必要", - "reconnecting": "再接続中", - "channelName": "アクティブなエージェント", - "activityKitDisabledTitle": "ライブアクティビティはオフです", - "activityKitDisabledBody": "ロック画面にアクティブなエージェントを表示するには、設定でライブアクティビティをオンにしてください。" + "reconnecting": "再接続中" } } diff --git a/apps/mobile/src/i18n/locales/ka.json b/apps/mobile/src/i18n/locales/ka.json index fb467472e1..d4e298981a 100644 --- a/apps/mobile/src/i18n/locales/ka.json +++ b/apps/mobile/src/i18n/locales/ka.json @@ -3202,9 +3202,6 @@ "openAgents": "აგენტების გახსნა", "running": "მუშაობს", "needsInput": "მოითხოვს შეყვანას", - "reconnecting": "კავშირის აღდგენა", - "channelName": "აქტიური აგენტები", - "activityKitDisabledTitle": "ცოცხალი აქტივობები გამორთულია", - "activityKitDisabledBody": "ჩართეთ ცოცხალი აქტივობები პარამეტრებში, რათა დაბლოკვის ეკრანზე აქტიური აგენტები ნახოთ." + "reconnecting": "კავშირის აღდგენა" } } diff --git a/apps/mobile/src/i18n/locales/kk.json b/apps/mobile/src/i18n/locales/kk.json index f6f359ecf1..63398c3da4 100644 --- a/apps/mobile/src/i18n/locales/kk.json +++ b/apps/mobile/src/i18n/locales/kk.json @@ -3202,9 +3202,6 @@ "openAgents": "Агенттерді ашу", "running": "Орындалуда", "needsInput": "енгізу қажет", - "reconnecting": "Қайта қосылуда", - "channelName": "Белсенді агенттер", - "activityKitDisabledTitle": "Тікелей әрекеттер өшірулі", - "activityKitDisabledBody": "Құлыптау экранында белсенді агенттерді көру үшін Параметрлерде тікелей әрекеттерді қосыңыз." + "reconnecting": "Қайта қосылуда" } } diff --git a/apps/mobile/src/i18n/locales/km.json b/apps/mobile/src/i18n/locales/km.json index 2831e9cf43..951b891c95 100644 --- a/apps/mobile/src/i18n/locales/km.json +++ b/apps/mobile/src/i18n/locales/km.json @@ -3202,9 +3202,6 @@ "openAgents": "បើកភ្នាក់ងារ", "running": "កំពុងដំណើរការ", "needsInput": "ត្រូវការបញ្ចូល", - "reconnecting": "កំពុងភ្ជាប់ឡើងវិញ", - "channelName": "ភ្នាក់ងារសកម្ម", - "activityKitDisabledTitle": "សកម្មភាពបន្តផ្ទាល់ត្រូវបានបិទ", - "activityKitDisabledBody": "បើកសកម្មភាពបន្តផ្ទាល់នៅក្នុងការកំណត់ ដើម្បីមើលភ្នាក់ងារសកម្មនៅលើអេក្រង់ចាក់សោ។" + "reconnecting": "កំពុងភ្ជាប់ឡើងវិញ" } } diff --git a/apps/mobile/src/i18n/locales/kn.json b/apps/mobile/src/i18n/locales/kn.json index c91b75e06e..aa081b63d1 100644 --- a/apps/mobile/src/i18n/locales/kn.json +++ b/apps/mobile/src/i18n/locales/kn.json @@ -3202,9 +3202,6 @@ "openAgents": "ಏಜೆಂಟ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ", "running": "ಚಾಲನೆಯಲ್ಲಿದೆ", "needsInput": "ಇನ್‌ಪುಟ್ ಅಗತ್ಯವಿದೆ", - "reconnecting": "ಮರುಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ", - "channelName": "ಸಕ್ರಿಯ ಏಜೆಂಟ್‌ಗಳು", - "activityKitDisabledTitle": "ನೇರ ಚಟುವಟಿಕೆಗಳು ಆಫ್ ಆಗಿವೆ", - "activityKitDisabledBody": "ಲಾಕ್ ಪರದೆಯಲ್ಲಿ ಸಕ್ರಿಯ ಏಜೆಂಟ್‌ಗಳನ್ನು ನೋಡಲು ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ನೇರ ಚಟುವಟಿಕೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ." + "reconnecting": "ಮರುಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ" } } diff --git a/apps/mobile/src/i18n/locales/ko.json b/apps/mobile/src/i18n/locales/ko.json index 9af46ce32a..de38c321ae 100644 --- a/apps/mobile/src/i18n/locales/ko.json +++ b/apps/mobile/src/i18n/locales/ko.json @@ -3202,9 +3202,6 @@ "openAgents": "에이전트 열기", "running": "실행 중", "needsInput": "입력 필요", - "reconnecting": "다시 연결 중", - "channelName": "활성 에이전트", - "activityKitDisabledTitle": "실시간 현황이 꺼져 있습니다", - "activityKitDisabledBody": "잠금 화면에서 활성 에이전트를 보려면 설정에서 실시간 현황을 켜세요." + "reconnecting": "다시 연결 중" } } diff --git a/apps/mobile/src/i18n/locales/lo.json b/apps/mobile/src/i18n/locales/lo.json index 0f5dc14831..8bc6075cd2 100644 --- a/apps/mobile/src/i18n/locales/lo.json +++ b/apps/mobile/src/i18n/locales/lo.json @@ -3202,9 +3202,6 @@ "openAgents": "ເປີດຕົວແທນ", "running": "ກຳລັງດຳເນີນການ", "needsInput": "ຕ້ອງການຂໍ້ມູນເຂົ້າ", - "reconnecting": "ກຳລັງເຊື່ອມຕໍ່ຄືນ", - "channelName": "ຕົວແທນທີ່ກຳລັງເຮັດວຽກ", - "activityKitDisabledTitle": "ກິດຈະກຳສົດປິດຢູ່", - "activityKitDisabledBody": "ເປີດກິດຈະກຳສົດໃນການຕັ້ງຄ່າ ເພື່ອເບິ່ງຕົວແທນທີ່ກຳລັງເຮັດວຽກໃນໜ້າຈໍລັອກ." + "reconnecting": "ກຳລັງເຊື່ອມຕໍ່ຄືນ" } } diff --git a/apps/mobile/src/i18n/locales/lt.json b/apps/mobile/src/i18n/locales/lt.json index 00b614f2ab..16f66764b0 100644 --- a/apps/mobile/src/i18n/locales/lt.json +++ b/apps/mobile/src/i18n/locales/lt.json @@ -3244,9 +3244,6 @@ "openAgents": "Atidaryti agentus", "running": "Vykdoma", "needsInput": "reikia įvesties", - "reconnecting": "Jungiamasi iš naujo", - "channelName": "Aktyvūs agentai", - "activityKitDisabledTitle": "Tiesioginės veiklos išjungtos", - "activityKitDisabledBody": "Nustatymuose įjunkite tiesiogines veiklas, kad užrakinimo ekrane matytumėte aktyvius agentus." + "reconnecting": "Jungiamasi iš naujo" } } diff --git a/apps/mobile/src/i18n/locales/lv.json b/apps/mobile/src/i18n/locales/lv.json index e9da3f9f1a..51f3ea955f 100644 --- a/apps/mobile/src/i18n/locales/lv.json +++ b/apps/mobile/src/i18n/locales/lv.json @@ -3223,9 +3223,6 @@ "openAgents": "Atvērt aģentus", "running": "DARBOJAS", "needsInput": "nepieciešama ievade", - "reconnecting": "Atkārtoti izveido savienojumu", - "channelName": "Aktīvie aģenti", - "activityKitDisabledTitle": "Tiešraides aktivitātes ir izslēgtas", - "activityKitDisabledBody": "Ieslēdz tiešraides aktivitātes iestatījumos, lai bloķēšanas ekrānā redzētu aktīvos aģentus." + "reconnecting": "Atkārtoti izveido savienojumu" } } diff --git a/apps/mobile/src/i18n/locales/mg.json b/apps/mobile/src/i18n/locales/mg.json index dbb5cdcf65..b88b5054e0 100644 --- a/apps/mobile/src/i18n/locales/mg.json +++ b/apps/mobile/src/i18n/locales/mg.json @@ -3202,9 +3202,6 @@ "openAgents": "Sokafy ny agent", "running": "MANDEHA", "needsInput": "mila fampidirana", - "reconnecting": "Mampifandray indray", - "channelName": "Agent mavitrika", - "activityKitDisabledTitle": "Tsy mandeha ny Hetsika Mivantana", - "activityKitDisabledBody": "Alefaso ao amin'ny Fikirana ny Hetsika Mivantana mba hahitana ny Agent Mavitrika eo amin'ny Efijery Fihidy." + "reconnecting": "Mampifandray indray" } } diff --git a/apps/mobile/src/i18n/locales/mi.json b/apps/mobile/src/i18n/locales/mi.json index 48614e612d..996cc80bd4 100644 --- a/apps/mobile/src/i18n/locales/mi.json +++ b/apps/mobile/src/i18n/locales/mi.json @@ -3202,9 +3202,6 @@ "openAgents": "Whakatuwheratia ngā māngai", "running": "Kei te oma", "needsInput": "e hiahia ana ki te whakaurunga", - "reconnecting": "Kei te hono anō", - "channelName": "Ngā māngai hohe", - "activityKitDisabledTitle": "Kua whakawetohia ngā Mahi Mataora", - "activityKitDisabledBody": "Whakakāngia ngā Mahi Mataora i Ngā tautuhinga kia kite i ngā Māngai Hohe i te Mata Maukati." + "reconnecting": "Kei te hono anō" } } diff --git a/apps/mobile/src/i18n/locales/mk.json b/apps/mobile/src/i18n/locales/mk.json index ba85de30a5..b99d7bde6b 100644 --- a/apps/mobile/src/i18n/locales/mk.json +++ b/apps/mobile/src/i18n/locales/mk.json @@ -3202,9 +3202,6 @@ "openAgents": "Отворете ги агентите", "running": "Во тек", "needsInput": "бара внес", - "reconnecting": "Повторно поврзување", - "channelName": "Активни агенти", - "activityKitDisabledTitle": "Активностите во живо се исклучени", - "activityKitDisabledBody": "Вклучете Активности во живо во Поставки за да ги видите активните агенти на заклучениот екран." + "reconnecting": "Повторно поврзување" } } diff --git a/apps/mobile/src/i18n/locales/ml.json b/apps/mobile/src/i18n/locales/ml.json index 9322d2271d..70513a7aab 100644 --- a/apps/mobile/src/i18n/locales/ml.json +++ b/apps/mobile/src/i18n/locales/ml.json @@ -3202,9 +3202,6 @@ "openAgents": "ഏജന്റുകളെ തുറക്കുക", "running": "പ്രവർത്തിക്കുന്നു", "needsInput": "ഇൻപുട്ട് ആവശ്യമാണ്", - "reconnecting": "വീണ്ടും ബന്ധിപ്പിക്കുന്നു", - "channelName": "സജീവ ഏജന്റുകൾ", - "activityKitDisabledTitle": "തത്സമയ പ്രവർത്തനങ്ങൾ ഓഫാണ്", - "activityKitDisabledBody": "ലോക്ക് സ്ക്രീനിൽ സജീവ ഏജന്റുകളെ കാണാൻ ക്രമീകരണങ്ങളിൽ തത്സമയ പ്രവർത്തനങ്ങൾ പ്രവർത്തനക്ഷമമാക്കുക." + "reconnecting": "വീണ്ടും ബന്ധിപ്പിക്കുന്നു" } } diff --git a/apps/mobile/src/i18n/locales/mn.json b/apps/mobile/src/i18n/locales/mn.json index b81eab872a..96bf6fb332 100644 --- a/apps/mobile/src/i18n/locales/mn.json +++ b/apps/mobile/src/i18n/locales/mn.json @@ -3202,9 +3202,6 @@ "openAgents": "Агентуудыг нээх", "running": "АЖИЛЛАЖ БАЙНА", "needsInput": "оролт шаардлагатай", - "reconnecting": "Дахин холбогдож байна", - "channelName": "Идэвхтэй агентууд", - "activityKitDisabledTitle": "Шууд үйл ажиллагаа унтраалттай байна", - "activityKitDisabledBody": "Түгжээтэй дэлгэц дээр Идэвхтэй агентуудыг харахын тулд Тохиргоо хэсэгт Шууд үйл ажиллагааг асаана уу." + "reconnecting": "Дахин холбогдож байна" } } diff --git a/apps/mobile/src/i18n/locales/mr.json b/apps/mobile/src/i18n/locales/mr.json index b938634c68..b2cfb3efd9 100644 --- a/apps/mobile/src/i18n/locales/mr.json +++ b/apps/mobile/src/i18n/locales/mr.json @@ -3202,9 +3202,6 @@ "openAgents": "एजंट्स उघडा", "running": "चालू आहे", "needsInput": "इनपुट आवश्यक", - "reconnecting": "पुन्हा जोडत आहे", - "channelName": "सक्रिय एजंट्स", - "activityKitDisabledTitle": "थेट क्रियाकलाप बंद आहेत", - "activityKitDisabledBody": "लॉक स्क्रीनवर सक्रिय एजंट्स पाहण्यासाठी सेटिंग्जमध्ये थेट क्रियाकलाप सुरू करा." + "reconnecting": "पुन्हा जोडत आहे" } } diff --git a/apps/mobile/src/i18n/locales/ms.json b/apps/mobile/src/i18n/locales/ms.json index f2383b0678..e43ee7f27c 100644 --- a/apps/mobile/src/i18n/locales/ms.json +++ b/apps/mobile/src/i18n/locales/ms.json @@ -3202,9 +3202,6 @@ "openAgents": "Buka ejen", "running": "Sedang berjalan", "needsInput": "perlu input", - "reconnecting": "Menyambung semula", - "channelName": "Ejen aktif", - "activityKitDisabledTitle": "Aktiviti Langsung dimatikan", - "activityKitDisabledBody": "Hidupkan Aktiviti Langsung dalam Tetapan untuk melihat Ejen Aktif pada Skrin Kunci." + "reconnecting": "Menyambung semula" } } diff --git a/apps/mobile/src/i18n/locales/mt.json b/apps/mobile/src/i18n/locales/mt.json index 9de9bda52b..35c4df9481 100644 --- a/apps/mobile/src/i18n/locales/mt.json +++ b/apps/mobile/src/i18n/locales/mt.json @@ -3265,9 +3265,6 @@ "openAgents": "Iftaħ l-aġenti", "running": "Għaddej", "needsInput": "jeħtieġ input", - "reconnecting": "Qed jerġa' jaqbad", - "channelName": "Aġenti attivi", - "activityKitDisabledTitle": "L-Attivitajiet Diretti huma mitfija", - "activityKitDisabledBody": "Ixgħel l-Attivitajiet Diretti fis-Settings biex tara l-Aġenti Attivi fuq l-Iskrin Imsakkar." + "reconnecting": "Qed jerġa' jaqbad" } } diff --git a/apps/mobile/src/i18n/locales/my.json b/apps/mobile/src/i18n/locales/my.json index 0acbfd71ca..edcc519920 100644 --- a/apps/mobile/src/i18n/locales/my.json +++ b/apps/mobile/src/i18n/locales/my.json @@ -3202,9 +3202,6 @@ "openAgents": "agent များကို ဖွင့်ပါ", "running": "လည်ပတ်နေသည်", "needsInput": "ထည့်သွင်းမှု လိုအပ်သည်", - "reconnecting": "ပြန်ချိတ်ဆက်နေသည်", - "channelName": "လုပ်ဆောင်နေသော agent များ", - "activityKitDisabledTitle": "တိုက်ရိုက်လှုပ်ရှားမှုများ ပိတ်ထားသည်", - "activityKitDisabledBody": "သော့ခတ်မျက်နှာပြင်တွင် လုပ်ဆောင်နေသော agent များကို ကြည့်ရန် ဆက်တင်များတွင် တိုက်ရိုက်လှုပ်ရှားမှုများကို ဖွင့်ပါ။" + "reconnecting": "ပြန်ချိတ်ဆက်နေသည်" } } diff --git a/apps/mobile/src/i18n/locales/nb.json b/apps/mobile/src/i18n/locales/nb.json index a4298c5f36..43761b6c59 100644 --- a/apps/mobile/src/i18n/locales/nb.json +++ b/apps/mobile/src/i18n/locales/nb.json @@ -3202,9 +3202,6 @@ "openAgents": "Åpne agenter", "running": "KJØRER", "needsInput": "trenger innspill", - "reconnecting": "Kobler til på nytt", - "channelName": "Aktive agenter", - "activityKitDisabledTitle": "Oppdateringer i sanntid er av", - "activityKitDisabledBody": "Slå på Oppdateringer i sanntid i Innstillinger for å se Aktive agenter på låst skjerm." + "reconnecting": "Kobler til på nytt" } } diff --git a/apps/mobile/src/i18n/locales/ne.json b/apps/mobile/src/i18n/locales/ne.json index ecf5f482fb..de1427f1cc 100644 --- a/apps/mobile/src/i18n/locales/ne.json +++ b/apps/mobile/src/i18n/locales/ne.json @@ -3202,9 +3202,6 @@ "openAgents": "एजेन्टहरू खोल्नुहोस्", "running": "चलिरहेको", "needsInput": "इनपुट चाहिन्छ", - "reconnecting": "पुनः जडान गर्दै", - "channelName": "सक्रिय एजेन्टहरू", - "activityKitDisabledTitle": "प्रत्यक्ष गतिविधिहरू बन्द छन्", - "activityKitDisabledBody": "लक स्क्रिनमा सक्रिय एजेन्टहरू हेर्न सेटिङ्समा प्रत्यक्ष गतिविधिहरू चालू गर्नुहोस्।" + "reconnecting": "पुनः जडान गर्दै" } } diff --git a/apps/mobile/src/i18n/locales/nl.json b/apps/mobile/src/i18n/locales/nl.json index 11b04a90b7..41325b46f0 100644 --- a/apps/mobile/src/i18n/locales/nl.json +++ b/apps/mobile/src/i18n/locales/nl.json @@ -3202,9 +3202,6 @@ "openAgents": "Agents openen", "running": "Bezig", "needsInput": "heeft invoer nodig", - "reconnecting": "Opnieuw verbinden", - "channelName": "Actieve agents", - "activityKitDisabledTitle": "Liveactiviteiten staan uit", - "activityKitDisabledBody": "Schakel liveactiviteiten in via Instellingen om actieve agents op het toegangsscherm te zien." + "reconnecting": "Opnieuw verbinden" } } diff --git a/apps/mobile/src/i18n/locales/om.json b/apps/mobile/src/i18n/locales/om.json index 7e7c873d39..1275746730 100644 --- a/apps/mobile/src/i18n/locales/om.json +++ b/apps/mobile/src/i18n/locales/om.json @@ -3202,9 +3202,6 @@ "openAgents": "Eejentoota banaa", "running": "Hojii irra jira", "needsInput": "seensa barbaada", - "reconnecting": "Irra deebi'ee walqabachaa jira", - "channelName": "Eejentoota hojii irra jiran", - "activityKitDisabledTitle": "Sochiiwwan Kallattii cufamaniiru", - "activityKitDisabledBody": "Eejentoota hojii irra jiran Iskiriinii Qulfii irratti arguuf, Qindaa'ina keessatti Sochiiwwan Kallattii banaa." + "reconnecting": "Irra deebi'ee walqabachaa jira" } } diff --git a/apps/mobile/src/i18n/locales/or.json b/apps/mobile/src/i18n/locales/or.json index 28dfd8f459..5f0ddff01a 100644 --- a/apps/mobile/src/i18n/locales/or.json +++ b/apps/mobile/src/i18n/locales/or.json @@ -3202,9 +3202,6 @@ "openAgents": "ଏଜେଣ୍ଟଗୁଡ଼ିକ ଖୋଲନ୍ତୁ", "running": "ଚାଲୁଛି", "needsInput": "ଇନପୁଟ୍ ଆବଶ୍ୟକ", - "reconnecting": "ପୁଣି ସଂଯୋଗ ହେଉଛି", - "channelName": "ସକ୍ରିୟ ଏଜେଣ୍ଟଗୁଡ଼ିକ", - "activityKitDisabledTitle": "ଲାଇଭ୍ କାର୍ଯ୍ୟକଳାପ ବନ୍ଦ ଅଛି", - "activityKitDisabledBody": "ଲକ୍ ସ୍କ୍ରିନ୍‌ରେ ସକ୍ରିୟ ଏଜେଣ୍ଟଗୁଡ଼ିକ ଦେଖିବାକୁ ସେଟିଂସ୍‌ରେ ଲାଇଭ୍ କାର୍ଯ୍ୟକଳାପ ଚାଲୁ କରନ୍ତୁ।" + "reconnecting": "ପୁଣି ସଂଯୋଗ ହେଉଛି" } } diff --git a/apps/mobile/src/i18n/locales/pa.json b/apps/mobile/src/i18n/locales/pa.json index dc384c98b0..693033ab74 100644 --- a/apps/mobile/src/i18n/locales/pa.json +++ b/apps/mobile/src/i18n/locales/pa.json @@ -3202,9 +3202,6 @@ "openAgents": "ਏਜੰਟ ਖੋਲ੍ਹੋ", "running": "ਚੱਲ ਰਿਹਾ ਹੈ", "needsInput": "ਇਨਪੁੱਟ ਦੀ ਲੋੜ ਹੈ", - "reconnecting": "ਮੁੜ ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ", - "channelName": "ਸਰਗਰਮ ਏਜੰਟ", - "activityKitDisabledTitle": "ਲਾਈਵ ਗਤੀਵਿਧੀਆਂ ਬੰਦ ਹਨ", - "activityKitDisabledBody": "ਲਾਕ ਸਕ੍ਰੀਨ 'ਤੇ ਸਰਗਰਮ ਏਜੰਟ ਦੇਖਣ ਲਈ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਲਾਈਵ ਗਤੀਵਿਧੀਆਂ ਚਾਲੂ ਕਰੋ।" + "reconnecting": "ਮੁੜ ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" } } diff --git a/apps/mobile/src/i18n/locales/pl.json b/apps/mobile/src/i18n/locales/pl.json index e0b439e9c0..cad09ac2c4 100644 --- a/apps/mobile/src/i18n/locales/pl.json +++ b/apps/mobile/src/i18n/locales/pl.json @@ -3244,9 +3244,6 @@ "openAgents": "Otwórz agentów", "running": "W toku", "needsInput": "wymaga danych", - "reconnecting": "Ponowne łączenie", - "channelName": "Aktywni agenci", - "activityKitDisabledTitle": "Wydarzenia na żywo są wyłączone", - "activityKitDisabledBody": "Włącz wydarzenia na żywo w Ustawieniach, aby widzieć aktywnych agentów na ekranie blokady." + "reconnecting": "Ponowne łączenie" } } diff --git a/apps/mobile/src/i18n/locales/ps.json b/apps/mobile/src/i18n/locales/ps.json index 023a93365b..93ecebe60c 100644 --- a/apps/mobile/src/i18n/locales/ps.json +++ b/apps/mobile/src/i18n/locales/ps.json @@ -3202,9 +3202,6 @@ "openAgents": "اجنټان پرانیزئ", "running": "روان", "needsInput": "ورودی ته اړتیا لري", - "reconnecting": "بیا نښلېږي", - "channelName": "فعال اجنټان", - "activityKitDisabledTitle": "ژوندي فعالیتونه بند دي", - "activityKitDisabledBody": "په قلف شوې پرده کې د فعالو اجنټانو د لیدلو لپاره په ترتیباتو کې ژوندي فعالیتونه فعال کړئ." + "reconnecting": "بیا نښلېږي" } } diff --git a/apps/mobile/src/i18n/locales/pt-BR.json b/apps/mobile/src/i18n/locales/pt-BR.json index ce93386fce..a90088c38b 100644 --- a/apps/mobile/src/i18n/locales/pt-BR.json +++ b/apps/mobile/src/i18n/locales/pt-BR.json @@ -3223,9 +3223,6 @@ "openAgents": "Abrir agentes", "running": "Em execução", "needsInput": "requer entrada", - "reconnecting": "Reconectando", - "channelName": "Agentes ativos", - "activityKitDisabledTitle": "As Atividades ao Vivo estão desativadas", - "activityKitDisabledBody": "Ative as Atividades ao Vivo em Ajustes para ver os agentes ativos na Tela Bloqueada." + "reconnecting": "Reconectando" } } diff --git a/apps/mobile/src/i18n/locales/pt.json b/apps/mobile/src/i18n/locales/pt.json index 73f9fcdd8c..b046797fb2 100644 --- a/apps/mobile/src/i18n/locales/pt.json +++ b/apps/mobile/src/i18n/locales/pt.json @@ -3223,9 +3223,6 @@ "openAgents": "Abrir agentes", "running": "EM EXECUÇÃO", "needsInput": "requer entrada", - "reconnecting": "A restabelecer ligação", - "channelName": "Agentes ativos", - "activityKitDisabledTitle": "As Atividades em tempo real estão desativadas", - "activityKitDisabledBody": "Ative as Atividades em tempo real nas Definições para ver os agentes ativos no Ecrã bloqueado." + "reconnecting": "A restabelecer ligação" } } diff --git a/apps/mobile/src/i18n/locales/ro.json b/apps/mobile/src/i18n/locales/ro.json index f1a3aa9777..edc32efe7a 100644 --- a/apps/mobile/src/i18n/locales/ro.json +++ b/apps/mobile/src/i18n/locales/ro.json @@ -3223,9 +3223,6 @@ "openAgents": "Deschide agenții", "running": "Rulează", "needsInput": "necesită introducere", - "reconnecting": "Se reconectează", - "channelName": "Agenți activi", - "activityKitDisabledTitle": "Activitățile live sunt dezactivate", - "activityKitDisabledBody": "Activează Activități live în Setări pentru a vedea Agenții activi pe ecranul de blocare." + "reconnecting": "Se reconectează" } } diff --git a/apps/mobile/src/i18n/locales/ru.json b/apps/mobile/src/i18n/locales/ru.json index 8412bf2ac9..9d0433771e 100644 --- a/apps/mobile/src/i18n/locales/ru.json +++ b/apps/mobile/src/i18n/locales/ru.json @@ -3244,9 +3244,6 @@ "openAgents": "Открыть агентов", "running": "Выполняется", "needsInput": "требует ввода", - "reconnecting": "Повторное подключение", - "channelName": "Активные агенты", - "activityKitDisabledTitle": "Эфир активности выключен", - "activityKitDisabledBody": "Включите Эфир активности в Настройках, чтобы видеть активных агентов на экране блокировки." + "reconnecting": "Повторное подключение" } } diff --git a/apps/mobile/src/i18n/locales/si.json b/apps/mobile/src/i18n/locales/si.json index f8bd34b3e0..b91444bd82 100644 --- a/apps/mobile/src/i18n/locales/si.json +++ b/apps/mobile/src/i18n/locales/si.json @@ -3202,9 +3202,6 @@ "openAgents": "නියෝජිතයන් විවෘත කරන්න", "running": "ධාවනය වෙමින්", "needsInput": "ආදානය අවශ්යයි", - "reconnecting": "නැවත සම්බන්ධ වෙමින්", - "channelName": "සක්‍රිය නියෝජිතයන්", - "activityKitDisabledTitle": "සජීවී ක්‍රියාකාරකම් අක්‍රියයි", - "activityKitDisabledBody": "අගුළු තිරයේ සක්‍රිය නියෝජිතයන් බැලීමට සැකසුම් තුළ සජීවී ක්‍රියාකාරකම් සක්‍රිය කරන්න." + "reconnecting": "නැවත සම්බන්ධ වෙමින්" } } diff --git a/apps/mobile/src/i18n/locales/sk.json b/apps/mobile/src/i18n/locales/sk.json index 5e66e53b27..5a2da6540d 100644 --- a/apps/mobile/src/i18n/locales/sk.json +++ b/apps/mobile/src/i18n/locales/sk.json @@ -3244,9 +3244,6 @@ "openAgents": "Otvoriť agentov", "running": "Prebieha", "needsInput": "vyžaduje vstup", - "reconnecting": "Opätovné pripájanie", - "channelName": "Aktívni agenti", - "activityKitDisabledTitle": "Živé aktivity sú vypnuté", - "activityKitDisabledBody": "Zapnite živé aktivity v Nastaveniach, aby sa aktívni agenti zobrazovali na zamknutej obrazovke." + "reconnecting": "Opätovné pripájanie" } } diff --git a/apps/mobile/src/i18n/locales/sl.json b/apps/mobile/src/i18n/locales/sl.json index 4cc7c05f56..6138aa0a4d 100644 --- a/apps/mobile/src/i18n/locales/sl.json +++ b/apps/mobile/src/i18n/locales/sl.json @@ -3244,9 +3244,6 @@ "openAgents": "Odprite agente", "running": "DELUJE", "needsInput": "potrebuje vnos", - "reconnecting": "Ponovno povezovanje", - "channelName": "Aktivni agenti", - "activityKitDisabledTitle": "Dejavnosti v živo so izklopljene", - "activityKitDisabledBody": "V Nastavitvah vklopite Dejavnosti v živo, da bodo Aktivni agenti prikazani na zaklenjenem zaslonu." + "reconnecting": "Ponovno povezovanje" } } diff --git a/apps/mobile/src/i18n/locales/so.json b/apps/mobile/src/i18n/locales/so.json index 218014c494..7b09b06741 100644 --- a/apps/mobile/src/i18n/locales/so.json +++ b/apps/mobile/src/i18n/locales/so.json @@ -3202,9 +3202,6 @@ "openAgents": "Fur wakiillada", "running": "Socodaya", "needsInput": "u baahan wax-soo-gal", - "reconnecting": "Dib u xiriirinaya", - "channelName": "Wakiillada firfircoon", - "activityKitDisabledTitle": "Hawlaha Tooska ah waa daman", - "activityKitDisabledBody": "Ku daar Hawlaha Tooska ah Dejinta si aad Wakiillada firfircoon ugu aragto Shaashadda Qufulka." + "reconnecting": "Dib u xiriirinaya" } } diff --git a/apps/mobile/src/i18n/locales/sq.json b/apps/mobile/src/i18n/locales/sq.json index dacf2759e9..e347f02620 100644 --- a/apps/mobile/src/i18n/locales/sq.json +++ b/apps/mobile/src/i18n/locales/sq.json @@ -3202,9 +3202,6 @@ "openAgents": "Hapni agjentët", "running": "Në ekzekutim", "needsInput": "ka nevojë për të dhëna", - "reconnecting": "Duke u rilidhur", - "channelName": "Agjentët aktivë", - "activityKitDisabledTitle": "Aktivitetet e drejtpërdrejta janë çaktivizuar", - "activityKitDisabledBody": "Aktivizoni Aktivitetet e drejtpërdrejta te Cilësimet për të parë Agjentët aktivë në Ekranin e kyçjes." + "reconnecting": "Duke u rilidhur" } } diff --git a/apps/mobile/src/i18n/locales/sr.json b/apps/mobile/src/i18n/locales/sr.json index 472e29f72b..9ff967432a 100644 --- a/apps/mobile/src/i18n/locales/sr.json +++ b/apps/mobile/src/i18n/locales/sr.json @@ -3223,9 +3223,6 @@ "openAgents": "Otvorite agente", "running": "U toku", "needsInput": "zahteva unos", - "reconnecting": "Ponovno povezivanje", - "channelName": "Aktivni agenti", - "activityKitDisabledTitle": "Aktivnosti uživo su isključene", - "activityKitDisabledBody": "Uključite Aktivnosti uživo u Podešavanjima da biste videli aktivne agente na zaključanom ekranu." + "reconnecting": "Ponovno povezivanje" } } diff --git a/apps/mobile/src/i18n/locales/sv.json b/apps/mobile/src/i18n/locales/sv.json index 5ddf047e59..23133bef21 100644 --- a/apps/mobile/src/i18n/locales/sv.json +++ b/apps/mobile/src/i18n/locales/sv.json @@ -3202,9 +3202,6 @@ "openAgents": "Öppna agenter", "running": "KÖRS", "needsInput": "kräver indata", - "reconnecting": "Återansluter", - "channelName": "Aktiva agenter", - "activityKitDisabledTitle": "Liveaktiviteter är avstängda", - "activityKitDisabledBody": "Aktivera liveaktiviteter i Inställningar för att se Aktiva agenter på låsskärmen." + "reconnecting": "Återansluter" } } diff --git a/apps/mobile/src/i18n/locales/sw.json b/apps/mobile/src/i18n/locales/sw.json index 4b57985a59..074cfb9315 100644 --- a/apps/mobile/src/i18n/locales/sw.json +++ b/apps/mobile/src/i18n/locales/sw.json @@ -3202,9 +3202,6 @@ "openAgents": "Fungua mawakala", "running": "Inaendelea", "needsInput": "inahitaji mchango", - "reconnecting": "Inaunganisha tena", - "channelName": "Mawakala wanaofanya kazi", - "activityKitDisabledTitle": "Shughuli za Moja kwa Moja zimezimwa", - "activityKitDisabledBody": "Washa Shughuli za Moja kwa Moja katika Mipangilio ili uone Mawakala wanaofanya kazi kwenye Skrini Iliyofungwa." + "reconnecting": "Inaunganisha tena" } } diff --git a/apps/mobile/src/i18n/locales/ta.json b/apps/mobile/src/i18n/locales/ta.json index 71c0233906..1888c8722e 100644 --- a/apps/mobile/src/i18n/locales/ta.json +++ b/apps/mobile/src/i18n/locales/ta.json @@ -3202,9 +3202,6 @@ "openAgents": "முகவர்களைத் திறக்கவும்", "running": "இயங்குகிறது", "needsInput": "உள்ளீடு தேவை", - "reconnecting": "மீண்டும் இணைக்கிறது", - "channelName": "செயலில் உள்ள முகவர்கள்", - "activityKitDisabledTitle": "நேரலைச் செயல்பாடுகள் முடக்கப்பட்டுள்ளன", - "activityKitDisabledBody": "பூட்டுத் திரையில் செயலில் உள்ள முகவர்களைக் காண அமைப்புகளில் நேரலைச் செயல்பாடுகளை இயக்கவும்." + "reconnecting": "மீண்டும் இணைக்கிறது" } } diff --git a/apps/mobile/src/i18n/locales/te.json b/apps/mobile/src/i18n/locales/te.json index 7c83980d8f..27fdafb1b4 100644 --- a/apps/mobile/src/i18n/locales/te.json +++ b/apps/mobile/src/i18n/locales/te.json @@ -3202,9 +3202,6 @@ "openAgents": "ఏజెంట్లను తెరవండి", "running": "నడుస్తోంది", "needsInput": "ఇన్పుట్ అవసరం", - "reconnecting": "మళ్లీ కనెక్ట్ అవుతోంది", - "channelName": "చురుకైన ఏజెంట్లు", - "activityKitDisabledTitle": "ప్రత్యక్ష కార్యకలాపాలు ఆఫ్‌లో ఉన్నాయి", - "activityKitDisabledBody": "లాక్ స్క్రీన్‌పై చురుకైన ఏజెంట్లను చూడటానికి సెట్టింగ్‌లలో ప్రత్యక్ష కార్యకలాపాలను ఆన్ చేయండి." + "reconnecting": "మళ్లీ కనెక్ట్ అవుతోంది" } } diff --git a/apps/mobile/src/i18n/locales/th.json b/apps/mobile/src/i18n/locales/th.json index b1cad084ff..be2437ae64 100644 --- a/apps/mobile/src/i18n/locales/th.json +++ b/apps/mobile/src/i18n/locales/th.json @@ -3202,9 +3202,6 @@ "openAgents": "เปิดเอเจนต์", "running": "กำลังทำงาน", "needsInput": "ต้องป้อนข้อมูล", - "reconnecting": "กำลังเชื่อมต่อใหม่", - "channelName": "เอเจนต์ที่กำลังทำงาน", - "activityKitDisabledTitle": "กิจกรรมสดปิดอยู่", - "activityKitDisabledBody": "เปิดกิจกรรมสดในการตั้งค่าเพื่อดูเอเจนต์ที่กำลังทำงานบนหน้าจอล็อค" + "reconnecting": "กำลังเชื่อมต่อใหม่" } } diff --git a/apps/mobile/src/i18n/locales/tr.json b/apps/mobile/src/i18n/locales/tr.json index ea85addb9b..7117b3d714 100644 --- a/apps/mobile/src/i18n/locales/tr.json +++ b/apps/mobile/src/i18n/locales/tr.json @@ -3202,9 +3202,6 @@ "openAgents": "Ajanları açın", "running": "Çalışıyor", "needsInput": "Girdi gerekli", - "reconnecting": "Yeniden bağlanılıyor", - "channelName": "Etkin ajanlar", - "activityKitDisabledTitle": "Canlı Etkinlikler kapalı", - "activityKitDisabledBody": "Etkin ajanları Kilit Ekranı'nda görmek için Ayarlar'dan Canlı Etkinlikler'i açın." + "reconnecting": "Yeniden bağlanılıyor" } } diff --git a/apps/mobile/src/i18n/locales/uk.json b/apps/mobile/src/i18n/locales/uk.json index 3a959178d8..01b6312b42 100644 --- a/apps/mobile/src/i18n/locales/uk.json +++ b/apps/mobile/src/i18n/locales/uk.json @@ -3244,9 +3244,6 @@ "openAgents": "Відкрити агентів", "running": "Виконується", "needsInput": "потребує вводу", - "reconnecting": "Повторне підключення", - "channelName": "Активні агенти", - "activityKitDisabledTitle": "Дії наживо вимкнено", - "activityKitDisabledBody": "Увімкніть «Дії наживо» в «Параметрах», щоб бачити активних агентів на замкненому екрані." + "reconnecting": "Повторне підключення" } } diff --git a/apps/mobile/src/i18n/locales/ur.json b/apps/mobile/src/i18n/locales/ur.json index 7acf73692f..4603f57ea1 100644 --- a/apps/mobile/src/i18n/locales/ur.json +++ b/apps/mobile/src/i18n/locales/ur.json @@ -3202,9 +3202,6 @@ "openAgents": "ایجنٹس کھولیں", "running": "چل رہا ہے", "needsInput": "ان پٹ درکار", - "reconnecting": "دوبارہ منسلک ہو رہا ہے", - "channelName": "فعال ایجنٹس", - "activityKitDisabledTitle": "لائیو سرگرمیاں بند ہیں", - "activityKitDisabledBody": "لاک اسکرین پر فعال ایجنٹس دیکھنے کے لیے ترتیبات میں لائیو سرگرمیاں فعال کریں۔" + "reconnecting": "دوبارہ منسلک ہو رہا ہے" } } diff --git a/apps/mobile/src/i18n/locales/uz.json b/apps/mobile/src/i18n/locales/uz.json index 550efd65b8..eaa3609da4 100644 --- a/apps/mobile/src/i18n/locales/uz.json +++ b/apps/mobile/src/i18n/locales/uz.json @@ -3202,9 +3202,6 @@ "openAgents": "Agentlarni oching", "running": "Ishlamoqda", "needsInput": "kiritish kerak", - "reconnecting": "Qayta ulanmoqda", - "channelName": "Faol agentlar", - "activityKitDisabledTitle": "Jonli faoliyatlar o'chirilgan", - "activityKitDisabledBody": "Qulflangan ekranda Faol agentlarni ko'rish uchun Sozlamalarda Jonli faoliyatlarni yoqing." + "reconnecting": "Qayta ulanmoqda" } } diff --git a/apps/mobile/src/i18n/locales/vi.json b/apps/mobile/src/i18n/locales/vi.json index 3f06694011..86c8419a80 100644 --- a/apps/mobile/src/i18n/locales/vi.json +++ b/apps/mobile/src/i18n/locales/vi.json @@ -3202,9 +3202,6 @@ "openAgents": "Mở tác nhân", "running": "ĐANG CHẠY", "needsInput": "cần nhập", - "reconnecting": "Đang kết nối lại", - "channelName": "Tác nhân đang hoạt động", - "activityKitDisabledTitle": "Hoạt động trực tiếp đang tắt", - "activityKitDisabledBody": "Bật Hoạt động trực tiếp trong Cài đặt để xem các tác nhân đang hoạt động trên Màn hình khóa." + "reconnecting": "Đang kết nối lại" } } diff --git a/apps/mobile/src/i18n/locales/yo.json b/apps/mobile/src/i18n/locales/yo.json index fb68588575..eb769c9306 100644 --- a/apps/mobile/src/i18n/locales/yo.json +++ b/apps/mobile/src/i18n/locales/yo.json @@ -3202,9 +3202,6 @@ "openAgents": "Ṣii awọn aṣoju", "running": "ǸJẸ́ ṢÍṢIṢẸ́", "needsInput": "nilo igbewọle", - "reconnecting": "Ti n tun sopọ", - "channelName": "Awọn aṣoju to n ṣiṣẹ", - "activityKitDisabledTitle": "Awọn Iṣẹ Lọwọlọwọ wa ni pipa", - "activityKitDisabledBody": "Tan Awọn Iṣẹ Lọwọlọwọ ninu Eto lati ri Awọn aṣoju to n ṣiṣẹ lori Iboju Titiipa." + "reconnecting": "Ti n tun sopọ" } } diff --git a/apps/mobile/src/i18n/locales/zh-Hans.json b/apps/mobile/src/i18n/locales/zh-Hans.json index 6f4c98633c..3fb3b32458 100644 --- a/apps/mobile/src/i18n/locales/zh-Hans.json +++ b/apps/mobile/src/i18n/locales/zh-Hans.json @@ -3202,9 +3202,6 @@ "openAgents": "打开代理", "running": "运行中", "needsInput": "需要输入", - "reconnecting": "正在重新连接", - "channelName": "活动代理", - "activityKitDisabledTitle": "实时活动已关闭", - "activityKitDisabledBody": "请在“设置”中开启“实时活动”,以在锁定屏幕上查看活动代理。" + "reconnecting": "正在重新连接" } } diff --git a/apps/mobile/src/i18n/locales/zh-Hant.json b/apps/mobile/src/i18n/locales/zh-Hant.json index 0ac6b71722..5d43030b65 100644 --- a/apps/mobile/src/i18n/locales/zh-Hant.json +++ b/apps/mobile/src/i18n/locales/zh-Hant.json @@ -3202,9 +3202,6 @@ "openAgents": "開啟代理", "running": "執行中", "needsInput": "需要輸入", - "reconnecting": "正在重新連線", - "channelName": "使用中的代理", - "activityKitDisabledTitle": "即時動態已關閉", - "activityKitDisabledBody": "請在「設定」中開啟「即時動態」,以在鎖定畫面上查看使用中的代理。" + "reconnecting": "正在重新連線" } } diff --git a/apps/mobile/src/i18n/locales/zu.json b/apps/mobile/src/i18n/locales/zu.json index 1fbf891bb2..89fb9ef2c9 100644 --- a/apps/mobile/src/i18n/locales/zu.json +++ b/apps/mobile/src/i18n/locales/zu.json @@ -3202,9 +3202,6 @@ "openAgents": "Vula ama-agent", "running": "IYASEBENZA", "needsInput": "idinga okokufaka", - "reconnecting": "Ixhuma kabusha", - "channelName": "Ama-agent asebenzayo", - "activityKitDisabledTitle": "Imisebenzi Ebukhoma ivaliwe", - "activityKitDisabledBody": "Vula Imisebenzi Ebukhoma ku-Izilungiselelo ukuze ubone Ama-agent asebenzayo Esikrinini Esikhiyiwe." + "reconnecting": "Ixhuma kabusha" } }