+ {t(
+ 'settings.composio.directOnlyDesc',
+ 'Managed Composio auth is unavailable here. Enter your own Composio API key or skip this for now.'
+ )}
+
+
+ )}
{/* API key field — only when Direct is selected */}
{mode === 'direct' && (
diff --git a/app/src/components/settings/panels/DevicesComingSoonPanel.tsx b/app/src/components/settings/panels/DevicesComingSoonPanel.tsx
new file mode 100644
index 000000000..a2f76fca9
--- /dev/null
+++ b/app/src/components/settings/panels/DevicesComingSoonPanel.tsx
@@ -0,0 +1,52 @@
+import { useT } from '../../../lib/i18n/I18nContext';
+import EmptyStateCard from '../../EmptyStateCard';
+import SettingsHeader from '../components/SettingsHeader';
+import { useSettingsNavigation } from '../hooks/useSettingsNavigation';
+
+const DevicesComingSoonPanel = () => {
+ const { t } = useT();
+ const { navigateBack, breadcrumbs } = useSettingsNavigation();
+
+ return (
+
+
+
+
+ }
+ title="Devices"
+ description="Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices."
+ footer={
+
+ Coming Soon
+
+ }
+ />
+
{t('common.loading')}
@@ -142,7 +157,7 @@ const SearchPanel = () => {
className="bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 overflow-hidden"
role="radiogroup"
aria-label={t('settings.search.engineAria')}>
- {ENGINES.map((opt, idx) => {
+ {visibleEngines.map((opt, idx) => {
const selected = opt.id === selectedEngine;
const configured = isConfigured(opt.id);
const blocked = opt.requiresKey && !configured && selected;
diff --git a/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx b/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx
index 0da2614b1..89acbfa4a 100644
--- a/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx
+++ b/app/src/components/settings/panels/__tests__/ComposioPanel.test.tsx
@@ -16,6 +16,10 @@ vi.mock('../../../../utils/tauriCommands', () => ({
openhumanComposioClearApiKey: hoisted.clearApiKey,
}));
+const coreStateMock = vi.fn(() => ({ snapshot: { sessionToken: 'cloud.jwt.remote' } }));
+
+vi.mock('../../../../providers/CoreStateProvider', () => ({ useCoreState: () => coreStateMock() }));
+
vi.mock('../../hooks/useSettingsNavigation', () => ({
useSettingsNavigation: () => ({
navigateBack: vi.fn(),
@@ -41,6 +45,7 @@ const directModeWithKey = { result: { mode: 'direct' as const, api_key_set: true
describe('ComposioPanel', () => {
beforeEach(() => {
vi.clearAllMocks();
+ coreStateMock.mockReturnValue({ snapshot: { sessionToken: 'cloud.jwt.remote' } });
hoisted.getMode.mockResolvedValue(backendMode);
hoisted.setApiKey.mockResolvedValue({ result: { stored: true, mode: 'direct' }, logs: [] });
hoisted.clearApiKey.mockResolvedValue({ result: { cleared: true, mode: 'backend' }, logs: [] });
@@ -104,6 +109,20 @@ describe('ComposioPanel', () => {
expect(screen.getByLabelText('Composio API key')).toBeInTheDocument();
});
+ test('defaults to direct-only API-key mode when managed auth is unavailable', async () => {
+ coreStateMock.mockReturnValue({ snapshot: { sessionToken: 'header.payload.local' } });
+ const Panel = await importPanel();
+ renderWithProviders();
+ await waitFor(() => expect(screen.queryByText('Loading…')).toBeNull());
+
+ expect(
+ screen.queryByLabelText('Managed (OpenHuman handles it for you)')
+ ).not.toBeInTheDocument();
+ expect(screen.queryByLabelText('Direct (bring your own API key)')).not.toBeInTheDocument();
+ expect(screen.getByLabelText('Composio API key')).toBeInTheDocument();
+ expect(screen.getByText(/Managed Composio auth is unavailable here/i)).toBeInTheDocument();
+ });
+
test('saving Direct mode with a key calls setApiKey and masks the field', async () => {
const Panel = await importPanel();
renderWithProviders();
diff --git a/app/src/hooks/__tests__/useBackendReachable.test.tsx b/app/src/hooks/__tests__/useBackendReachable.test.tsx
new file mode 100644
index 000000000..3463c5166
--- /dev/null
+++ b/app/src/hooks/__tests__/useBackendReachable.test.tsx
@@ -0,0 +1,36 @@
+import { renderHook, waitFor } from '@testing-library/react';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { useBackendReachable } from '../useBackendReachable';
+
+vi.mock('../../services/backendUrl', () => ({
+ getBackendUrl: vi.fn().mockResolvedValue('http://localhost:5005'),
+}));
+
+describe('useBackendReachable', () => {
+ beforeEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('reports reachable when fetch resolves with any HTTP response', async () => {
+ vi.spyOn(globalThis, 'fetch').mockResolvedValue(new Response(null, { status: 200 }));
+
+ const { result } = renderHook(() => useBackendReachable());
+ expect(result.current).toBe('probing');
+ await waitFor(() => expect(result.current).toBe('reachable'));
+ });
+
+ it('reports reachable on 4xx (host answered)', async () => {
+ vi.spyOn(globalThis, 'fetch').mockResolvedValue(new Response(null, { status: 404 }));
+
+ const { result } = renderHook(() => useBackendReachable());
+ await waitFor(() => expect(result.current).toBe('reachable'));
+ });
+
+ it('reports unreachable when fetch rejects (network error)', async () => {
+ vi.spyOn(globalThis, 'fetch').mockRejectedValue(new TypeError('Failed to fetch'));
+
+ const { result } = renderHook(() => useBackendReachable());
+ await waitFor(() => expect(result.current).toBe('unreachable'));
+ });
+});
diff --git a/app/src/hooks/useBackendReachable.ts b/app/src/hooks/useBackendReachable.ts
new file mode 100644
index 000000000..fb48ae629
--- /dev/null
+++ b/app/src/hooks/useBackendReachable.ts
@@ -0,0 +1,59 @@
+import createDebug from 'debug';
+import { useEffect, useState } from 'react';
+
+import { getBackendUrl } from '../services/backendUrl';
+
+const log = createDebug('app:backend-probe');
+
+export type BackendProbeStatus = 'probing' | 'reachable' | 'unreachable';
+
+const PROBE_TIMEOUT_MS = 2500;
+
+/**
+ * Probes the configured backend URL once on mount and reports whether it is
+ * reachable. The "Continue locally" CTA on the Welcome screen is gated on
+ * `unreachable` so users only see it when the backend OAuth flow can't be
+ * completed (per issue #2037 AC).
+ *
+ * We treat any successful HTTP response (incl. 4xx) as reachable — the goal is
+ * to confirm the host is online, not that a specific route exists.
+ */
+export function useBackendReachable(): BackendProbeStatus {
+ const [status, setStatus] = useState('probing');
+
+ useEffect(() => {
+ let cancelled = false;
+ const controller = new AbortController();
+ const timeoutId = window.setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
+
+ void (async () => {
+ try {
+ const base = await getBackendUrl();
+ log('[probe] fetching %s/health', base);
+ const response = await fetch(`${base}/health`, {
+ method: 'GET',
+ signal: controller.signal,
+ cache: 'no-store',
+ });
+ if (cancelled) return;
+ // Any HTTP response (even 404) means the host answered — it's online.
+ log('[probe] response status=%d → reachable', response.status);
+ setStatus('reachable');
+ } catch (err) {
+ if (cancelled) return;
+ log('[probe] failed → unreachable: %o', err);
+ setStatus('unreachable');
+ } finally {
+ window.clearTimeout(timeoutId);
+ }
+ })();
+
+ return () => {
+ cancelled = true;
+ controller.abort();
+ window.clearTimeout(timeoutId);
+ };
+ }, []);
+
+ return status;
+}
diff --git a/app/src/lib/composio/hooks.test.ts b/app/src/lib/composio/hooks.test.ts
index 14c41fc4f..4333df6c0 100644
--- a/app/src/lib/composio/hooks.test.ts
+++ b/app/src/lib/composio/hooks.test.ts
@@ -4,6 +4,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
const mockListToolkits = vi.fn();
const mockListConnections = vi.fn();
const mockListAgentReadyToolkits = vi.fn();
+const mockOpenhumanComposioGetMode = vi.fn();
+let sessionToken = 'jwt-abc';
vi.mock('./composioApi', () => ({
listToolkits: () => mockListToolkits(),
@@ -11,10 +13,27 @@ vi.mock('./composioApi', () => ({
listAgentReadyToolkits: () => mockListAgentReadyToolkits(),
}));
+vi.mock('../coreState/store', async () => {
+ const actual = await vi.importActual('../coreState/store');
+ return { ...actual, getCoreStateSnapshot: () => ({ snapshot: { sessionToken } }) };
+});
+
+vi.mock('../../utils/tauriCommands', async () => {
+ const actual = await vi.importActual(
+ '../../utils/tauriCommands'
+ );
+ return { ...actual, openhumanComposioGetMode: () => mockOpenhumanComposioGetMode() };
+});
+
describe('useComposioIntegrations', () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
+ sessionToken = 'jwt-abc';
+ mockOpenhumanComposioGetMode.mockResolvedValue({
+ result: { mode: 'backend', api_key_set: true },
+ logs: [],
+ });
});
it('keeps toolkit cards visible when connections fetch fails', async () => {
@@ -50,6 +69,27 @@ describe('useComposioIntegrations', () => {
expect(result.current.connectionByToolkit.size).toBe(0);
expect(result.current.error).toBe('backend unreachable');
});
+
+ it('skips toolkit fetch and polling for local sessions without a composio api key', async () => {
+ sessionToken = 'header.payload.local';
+ mockOpenhumanComposioGetMode.mockResolvedValue({
+ result: { mode: 'direct', api_key_set: false },
+ logs: [],
+ });
+
+ const { useComposioIntegrations } = await import('./hooks');
+ const { result } = renderHook(() => useComposioIntegrations(10));
+
+ await waitFor(() => {
+ expect(result.current.loading).toBe(false);
+ });
+
+ expect(result.current.toolkits).toEqual([]);
+ expect(result.current.connectionByToolkit.size).toBe(0);
+ expect(result.current.error).toBeNull();
+ expect(mockListToolkits).not.toHaveBeenCalled();
+ expect(mockListConnections).not.toHaveBeenCalled();
+ });
});
describe('useAgentReadyComposioToolkits', () => {
diff --git a/app/src/lib/composio/hooks.ts b/app/src/lib/composio/hooks.ts
index 2372b64fe..6d593ec21 100644
--- a/app/src/lib/composio/hooks.ts
+++ b/app/src/lib/composio/hooks.ts
@@ -1,5 +1,8 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { isLocalSessionToken } from '../../utils/localSession';
+import { openhumanComposioGetMode } from '../../utils/tauriCommands';
+import { getCoreStateSnapshot } from '../coreState/store';
import { listAgentReadyToolkits, listConnections, listToolkits } from './composioApi';
import { canonicalizeComposioToolkitSlug } from './toolkitSlug';
import type { ComposioConnection } from './types';
@@ -33,10 +36,14 @@ export interface UseComposioIntegrationsResult {
* explicit `refresh()` because the allowlist is stable.
*/
export function useComposioIntegrations(pollIntervalMs = 5_000): UseComposioIntegrationsResult {
+ const isLocalSession = isLocalSessionToken(getCoreStateSnapshot().snapshot.sessionToken);
const [toolkits, setToolkits] = useState([]);
const [connections, setConnections] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
+ const [fetchEnabled, setFetchEnabled] = useState(() =>
+ isLocalSession ? null : true
+ );
const mountedRef = useRef(true);
useEffect(() => {
@@ -46,7 +53,38 @@ export function useComposioIntegrations(pollIntervalMs = 5_000): UseComposioInte
};
}, []);
+ const resolveFetchEnabled = useCallback(async (): Promise => {
+ if (!isLocalSession) {
+ if (mountedRef.current) setFetchEnabled(true);
+ return true;
+ }
+ try {
+ const res = await openhumanComposioGetMode();
+ const enabled = Boolean(res.result?.api_key_set);
+ if (mountedRef.current) setFetchEnabled(enabled);
+ return enabled;
+ } catch (err) {
+ console.warn(
+ '[composio] failed to resolve direct-mode api key status:',
+ err instanceof Error ? err.message : String(err)
+ );
+ if (mountedRef.current) setFetchEnabled(false);
+ return false;
+ }
+ }, [isLocalSession]);
+
const refresh = useCallback(async () => {
+ const enabled = fetchEnabled ?? (await resolveFetchEnabled());
+ if (!enabled) {
+ if (mountedRef.current) {
+ setToolkits([]);
+ setConnections([]);
+ setError(null);
+ setLoading(false);
+ }
+ return;
+ }
+
let nextError: string | null = null;
try {
const [toolkitsResult, connectionsResult] = await Promise.allSettled([
@@ -81,12 +119,12 @@ export function useComposioIntegrations(pollIntervalMs = 5_000): UseComposioInte
} finally {
if (mountedRef.current) setLoading(false);
}
- }, []);
+ }, [fetchEnabled, resolveFetchEnabled]);
// Initial fetch + polling.
useEffect(() => {
void refresh();
- if (pollIntervalMs <= 0) return;
+ if (pollIntervalMs <= 0 || fetchEnabled !== true) return;
const id = window.setInterval(() => {
void listConnections()
.then(resp => {
@@ -101,7 +139,7 @@ export function useComposioIntegrations(pollIntervalMs = 5_000): UseComposioInte
});
}, pollIntervalMs);
return () => window.clearInterval(id);
- }, [refresh, pollIntervalMs]);
+ }, [refresh, pollIntervalMs, fetchEnabled]);
// [composio-cache] Listen for a window-level "config changed" event
// emitted by ComposioPanel when the user flips backend ↔ direct or
@@ -115,11 +153,26 @@ export function useComposioIntegrations(pollIntervalMs = 5_000): UseComposioInte
useEffect(() => {
const onConfigChanged = () => {
console.debug('[composio-cache] window:composio:config-changed → refresh()');
+ if (isLocalSession) {
+ void resolveFetchEnabled().then(enabled => {
+ if (enabled) {
+ void refresh();
+ return;
+ }
+ if (mountedRef.current) {
+ setToolkits([]);
+ setConnections([]);
+ setError(null);
+ setLoading(false);
+ }
+ });
+ return;
+ }
void refresh();
};
window.addEventListener('composio:config-changed', onConfigChanged);
return () => window.removeEventListener('composio:config-changed', onConfigChanged);
- }, [refresh]);
+ }, [isLocalSession, refresh, resolveFetchEnabled]);
const connectionByToolkit = useMemo(() => {
const map = new Map();
diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts
index 14470ead2..86bfde069 100644
--- a/app/src/lib/i18n/ar.ts
+++ b/app/src/lib/i18n/ar.ts
@@ -7,6 +7,25 @@ import type { TranslationMap } from './types';
// Arabic (العربية) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const ar: TranslationMap = { ...ar1, ...ar2, ...ar3, ...ar4, ...ar5 };
+const ar: TranslationMap = {
+ ...ar1,
+ ...ar2,
+ ...ar3,
+ ...ar4,
+ ...ar5,
+ 'skills.composio.noApiKeyTitle': 'لم يتم إعداد مفتاح Composio API',
+ 'skills.composio.noApiKeyDescription':
+ 'يستخدم الوضع المحلي مفتاح Composio API الخاص بك. افتح الإعدادات ← الخيارات المتقدمة ← Composio لإضافته قبل توصيل التكاملات هنا.',
+ 'skills.composio.noApiKeyCta': 'افتح في الإعدادات',
+ 'channels.localManagedUnavailable': 'القنوات المُدارة غير متاحة للمستخدمين المحليين.',
+ 'rewards.localUnavailable':
+ 'تسجيل الدخول المحلي لا يمنح مكافآت أو قسائم أو رصيد إحالة. لكسب المكافآت، سجّل الخروج ثم تابِع بتسجيل الدخول باستخدام حساب OpenHuman.',
+ 'rewards.localUnavailableCta': 'افتح إعدادات الحساب',
+ 'settings.search.localManagedUnavailable':
+ 'بحث OpenHuman المُدار غير متاح للمستخدمين المحليين. أضف مفتاح Parallel أو Brave الخاص بك لتفعيل البحث على الويب.',
+ 'devices.comingSoonDescription':
+ 'إقران الأجهزة قريبًا. ستكون هذه الصفحة مخصصة لإقران أجهزة iPhone وإدارة الأجهزة المتصلة.',
+ 'welcome.continueLocallyExperimental': 'المتابعة محليًا (تجريبي)',
+};
export default ar;
diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts
index c0366f2f2..75c5e7802 100644
--- a/app/src/lib/i18n/bn.ts
+++ b/app/src/lib/i18n/bn.ts
@@ -7,6 +7,25 @@ import type { TranslationMap } from './types';
// Bengali (বাংলা) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const bn: TranslationMap = { ...bn1, ...bn2, ...bn3, ...bn4, ...bn5 };
+const bn: TranslationMap = {
+ ...bn1,
+ ...bn2,
+ ...bn3,
+ ...bn4,
+ ...bn5,
+ 'skills.composio.noApiKeyTitle': 'কোনো Composio API Key কনফিগার করা নেই',
+ 'skills.composio.noApiKeyDescription':
+ 'লোকাল মোডে আপনার নিজের Composio API key ব্যবহার হয়। এখানে ইন্টিগ্রেশন যুক্ত করার আগে Settings → Advanced → Composio খুলে একটি key যোগ করুন।',
+ 'skills.composio.noApiKeyCta': 'সেটিংসে খুলুন',
+ 'channels.localManagedUnavailable': 'লোকাল ব্যবহারকারীদের জন্য ম্যানেজড চ্যানেল উপলভ্য নয়।',
+ 'rewards.localUnavailable':
+ 'লোকাল লগইনে কোনো রিওয়ার্ড, কুপন বা রেফারেল ক্রেডিট মেলে না। রিওয়ার্ড পেতে লগ আউট করে একটি OpenHuman অ্যাকাউন্ট দিয়ে সাইন ইন করুন।',
+ 'rewards.localUnavailableCta': 'অ্যাকাউন্ট সেটিংস খুলুন',
+ 'settings.search.localManagedUnavailable':
+ 'লোকাল ব্যবহারকারীদের জন্য OpenHuman Managed সার্চ উপলভ্য নয়। ওয়েব সার্চ চালু করতে আপনার নিজের Parallel বা Brave API key যোগ করুন।',
+ 'devices.comingSoonDescription':
+ 'ডিভাইস পেয়ারিং শীঘ্রই আসছে। এই পেজে iPhone পেয়ারিং এবং সংযুক্ত ডিভাইস ম্যানেজ করা যাবে।',
+ 'welcome.continueLocallyExperimental': 'লোকালি চালিয়ে যান (প্রায়োগিক)',
+};
export default bn;
diff --git a/app/src/lib/i18n/chunks/ar-1.ts b/app/src/lib/i18n/chunks/ar-1.ts
index 806d87fcb..ae45d140b 100644
--- a/app/src/lib/i18n/chunks/ar-1.ts
+++ b/app/src/lib/i18n/chunks/ar-1.ts
@@ -84,6 +84,8 @@ const ar1: TranslationMap = {
'settings.clearAppDataDesc': 'تسجيل الخروج وحذف جميع البيانات المحلية للتطبيق نهائيًا',
'settings.logOut': 'تسجيل الخروج',
'settings.logOutDesc': 'تسجيل الخروج من حسابك',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'اللغة',
'settings.languageDesc': 'لغة عرض واجهة التطبيق',
'settings.alerts': 'التنبيهات',
@@ -511,6 +513,18 @@ const ar1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'مسار التنقل',
'settings.betaBuild': 'إصدار تجريبي - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/ar-3.ts b/app/src/lib/i18n/chunks/ar-3.ts
index d2462092d..1d22c8ed9 100644
--- a/app/src/lib/i18n/chunks/ar-3.ts
+++ b/app/src/lib/i18n/chunks/ar-3.ts
@@ -211,6 +211,9 @@ const ar3: TranslationMap = {
'about.update.status.default': 'التحقق من التحديثات',
'welcome.connectionFailed': 'فشل الاتصال: {status} {statusText}',
'welcome.connectionFailedMsg': 'فشل الاتصال: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'فتح جلسة محادثة مباشرة مع الوكيل.',
'channels.activeRouteValue': '{channel} عبر {authMode}',
'privacy.dataKind.messages': 'الرسائل',
@@ -398,6 +401,7 @@ const ar3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default ar3;
diff --git a/app/src/lib/i18n/chunks/bn-1.ts b/app/src/lib/i18n/chunks/bn-1.ts
index 1fad0d088..caad72ca3 100644
--- a/app/src/lib/i18n/chunks/bn-1.ts
+++ b/app/src/lib/i18n/chunks/bn-1.ts
@@ -84,6 +84,8 @@ const bn1: TranslationMap = {
'settings.clearAppDataDesc': 'সাইন আউট করুন এবং সব লোকাল ডেটা স্থায়ীভাবে মুছুন',
'settings.logOut': 'লগ আউট',
'settings.logOutDesc': 'আপনার অ্যাকাউন্ট থেকে সাইন আউট করুন',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'ভাষা',
'settings.languageDesc': 'অ্যাপ ইন্টারফেসের প্রদর্শন ভাষা',
'settings.alerts': 'সতর্কতা',
@@ -520,6 +522,18 @@ const bn1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'ব্রেডক্রাম্ব নেভিগেশন',
'settings.betaBuild': 'বেটা বিল্ড - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/bn-3.ts b/app/src/lib/i18n/chunks/bn-3.ts
index 0c9a8b5a2..711bf29ca 100644
--- a/app/src/lib/i18n/chunks/bn-3.ts
+++ b/app/src/lib/i18n/chunks/bn-3.ts
@@ -215,6 +215,9 @@ const bn3: TranslationMap = {
'about.update.status.default': 'আপডেট পরীক্ষা করুন',
'welcome.connectionFailed': 'সংযোগ ব্যর্থ: {status} {statusText}',
'welcome.connectionFailedMsg': 'সংযোগ ব্যর্থ: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'এজেন্টের সাথে সরাসরি চ্যাট সেশন খুলুন।',
'channels.activeRouteValue': '{authMode} এর মাধ্যমে {channel}',
'privacy.dataKind.messages': 'বার্তা',
@@ -401,6 +404,7 @@ const bn3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default bn3;
diff --git a/app/src/lib/i18n/chunks/de-1.ts b/app/src/lib/i18n/chunks/de-1.ts
index 177b3f315..bc1d65960 100644
--- a/app/src/lib/i18n/chunks/de-1.ts
+++ b/app/src/lib/i18n/chunks/de-1.ts
@@ -532,6 +532,20 @@ const de1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'Navigationspfad',
'settings.betaBuild': 'Beta-Build - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/de-3.ts b/app/src/lib/i18n/chunks/de-3.ts
index 42a08fa11..2a7ff892f 100644
--- a/app/src/lib/i18n/chunks/de-3.ts
+++ b/app/src/lib/i18n/chunks/de-3.ts
@@ -413,6 +413,10 @@ const de3: TranslationMap = {
'channels.web.description': 'Chatte über die integrierte Web-Oberfläche.',
'channels.web.authMode.managed_dm.description':
'Nutze den eingebetteten Web-Chat — keine Einrichtung erforderlich.',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
};
export default de3;
diff --git a/app/src/lib/i18n/chunks/en-1.ts b/app/src/lib/i18n/chunks/en-1.ts
index 16d12a86e..ff664c76b 100644
--- a/app/src/lib/i18n/chunks/en-1.ts
+++ b/app/src/lib/i18n/chunks/en-1.ts
@@ -285,6 +285,8 @@ const en1: TranslationMap = {
'settings.clearAppDataDesc': 'Sign out and permanently clear all local app data',
'settings.logOut': 'Log out',
'settings.logOutDesc': 'Sign out of your account',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Language',
'settings.languageDesc': 'Display language for the app interface',
'settings.alerts': 'Alerts',
@@ -444,6 +446,10 @@ const en1: TranslationMap = {
'skills.integrations': 'Composio Integrations',
'skills.integrationsSubtitle':
'Cloud-based OAuth connections — sign in with your account and Composio brokers the tokens so agents can read and act on your behalf. No API keys to manage.',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
'skills.tabs.composio': 'Composio',
'skills.tabs.channels': 'Channels',
'skills.tabs.mcp': 'MCP Servers',
@@ -467,6 +473,9 @@ const en1: TranslationMap = {
'rewards.title': 'Rewards',
'rewards.referrals': 'Referrals',
'rewards.coupons': 'Redeem',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
'rewards.credits': 'Credits',
'rewards.referralCode': 'Your referral code',
'rewards.copyCode': 'Copy code',
@@ -596,6 +605,7 @@ const en1: TranslationMap = {
'channels.configure': 'Configure Channel',
'channels.setup': 'Setup',
'channels.noChannels': 'No channels configured',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
'channels.addChannel': 'Add Channel',
'channels.status.connected': 'Connected',
'channels.status.disconnected': 'Disconnected',
@@ -937,6 +947,8 @@ const en1: TranslationMap = {
'settings.search.engineManagedLabel': 'OpenHuman Managed',
'settings.search.engineManagedDesc':
'Default. Routed through the OpenHuman backend — no API key required.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
'settings.search.engineParallelLabel': 'Parallel',
'settings.search.engineParallelDesc':
'Direct Parallel API: search, extract, chat, research, enrich, dataset tools.',
@@ -980,6 +992,8 @@ const en1: TranslationMap = {
'devices.betaBadge': 'Beta',
'devices.betaText':
'This feature is currently in beta. Pair iOS phones with this OpenHuman to use them as a remote client.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'devices.title': 'Devices',
'devices.pairIphone': 'Pair iPhone',
'devices.noPaired': 'No paired devices',
diff --git a/app/src/lib/i18n/chunks/en-3.ts b/app/src/lib/i18n/chunks/en-3.ts
index 1f9291f86..c71ecc7e5 100644
--- a/app/src/lib/i18n/chunks/en-3.ts
+++ b/app/src/lib/i18n/chunks/en-3.ts
@@ -214,6 +214,10 @@ const en3: TranslationMap = {
'about.update.status.default': 'Check for updates',
'welcome.connectionFailed': 'Connection failed: {status} {statusText}',
'welcome.connectionFailedMsg': 'Connection failed: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'Open a direct chat session with the agent.',
'channels.activeRouteValue': '{channel} via {authMode}',
'privacy.dataKind.messages': 'Messages',
diff --git a/app/src/lib/i18n/chunks/es-1.ts b/app/src/lib/i18n/chunks/es-1.ts
index 0ece7b6a4..b835e6944 100644
--- a/app/src/lib/i18n/chunks/es-1.ts
+++ b/app/src/lib/i18n/chunks/es-1.ts
@@ -86,6 +86,8 @@ const es1: TranslationMap = {
'Cerrar sesión y eliminar permanentemente todos los datos locales de la app',
'settings.logOut': 'Cerrar sesión',
'settings.logOutDesc': 'Salir de tu cuenta',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Idioma',
'settings.languageDesc': 'Idioma de visualización de la interfaz de la app',
'settings.alerts': 'Alertas',
@@ -532,6 +534,18 @@ const es1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'Ruta de navegación',
'settings.betaBuild': 'Compilación beta - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/es-3.ts b/app/src/lib/i18n/chunks/es-3.ts
index 14b8efa23..bcea0ebba 100644
--- a/app/src/lib/i18n/chunks/es-3.ts
+++ b/app/src/lib/i18n/chunks/es-3.ts
@@ -218,6 +218,9 @@ const es3: TranslationMap = {
'about.update.status.default': 'Buscar actualizaciones',
'welcome.connectionFailed': 'Conexión fallida: {status} {statusText}',
'welcome.connectionFailedMsg': 'Conexión fallida: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'Abre una sesión de chat directo con el agente.',
'channels.activeRouteValue': '{channel} vía {authMode}',
'privacy.dataKind.messages': 'Mensajes',
@@ -406,6 +409,7 @@ const es3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default es3;
diff --git a/app/src/lib/i18n/chunks/fr-1.ts b/app/src/lib/i18n/chunks/fr-1.ts
index c17d13789..21ceb8d4f 100644
--- a/app/src/lib/i18n/chunks/fr-1.ts
+++ b/app/src/lib/i18n/chunks/fr-1.ts
@@ -86,6 +86,8 @@ const fr1: TranslationMap = {
'Se déconnecter et supprimer définitivement toutes les données locales',
'settings.logOut': 'Se déconnecter',
'settings.logOutDesc': 'Se déconnecter de ton compte',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Langue',
'settings.languageDesc': "Langue d'affichage de l'interface",
'settings.alerts': 'Alertes',
@@ -534,6 +536,18 @@ const fr1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'Fil d’Ariane',
'settings.betaBuild': 'Build bêta - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/fr-3.ts b/app/src/lib/i18n/chunks/fr-3.ts
index 943571519..6f0a77ce3 100644
--- a/app/src/lib/i18n/chunks/fr-3.ts
+++ b/app/src/lib/i18n/chunks/fr-3.ts
@@ -219,6 +219,9 @@ const fr3: TranslationMap = {
'about.update.status.default': 'Rechercher des mises à jour',
'welcome.connectionFailed': 'Connexion échouée : {status} {statusText}',
'welcome.connectionFailedMsg': 'Connexion échouée : {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': "Ouvrir une session de chat direct avec l'agent.",
'channels.activeRouteValue': '{channel} via {authMode}',
'privacy.dataKind.messages': 'Messages',
@@ -407,6 +410,7 @@ const fr3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default fr3;
diff --git a/app/src/lib/i18n/chunks/hi-1.ts b/app/src/lib/i18n/chunks/hi-1.ts
index f83ea7543..e92dd8645 100644
--- a/app/src/lib/i18n/chunks/hi-1.ts
+++ b/app/src/lib/i18n/chunks/hi-1.ts
@@ -83,6 +83,8 @@ const hi1: TranslationMap = {
'settings.clearAppDataDesc': 'साइन आउट करें और सारा लोकल ऐप डेटा हमेशा के लिए मिटाएं',
'settings.logOut': 'लॉग आउट',
'settings.logOutDesc': 'अपने अकाउंट से साइन आउट करें',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'भाषा',
'settings.languageDesc': 'ऐप इंटरफेस की डिस्प्ले भाषा',
'settings.alerts': 'अलर्ट',
@@ -517,6 +519,18 @@ const hi1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'ब्रेडक्रंब नेविगेशन',
'settings.betaBuild': 'बीटा बिल्ड - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/hi-3.ts b/app/src/lib/i18n/chunks/hi-3.ts
index 0ab30ff9e..3f11035bd 100644
--- a/app/src/lib/i18n/chunks/hi-3.ts
+++ b/app/src/lib/i18n/chunks/hi-3.ts
@@ -214,6 +214,9 @@ const hi3: TranslationMap = {
'about.update.status.default': 'अपडेट चेक करें',
'welcome.connectionFailed': 'कनेक्शन विफल: {status} {statusText}',
'welcome.connectionFailedMsg': 'कनेक्शन विफल: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'एजेंट के साथ डायरेक्ट चैट सेशन खोलें।',
'channels.activeRouteValue': '{channel} द्वारा {authMode}',
'privacy.dataKind.messages': 'मैसेज',
@@ -403,6 +406,7 @@ const hi3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default hi3;
diff --git a/app/src/lib/i18n/chunks/id-1.ts b/app/src/lib/i18n/chunks/id-1.ts
index d27f59ec0..391cb32e0 100644
--- a/app/src/lib/i18n/chunks/id-1.ts
+++ b/app/src/lib/i18n/chunks/id-1.ts
@@ -83,6 +83,8 @@ const id1: TranslationMap = {
'settings.clearAppDataDesc': 'Keluar dan hapus permanen semua data aplikasi lokal',
'settings.logOut': 'Keluar',
'settings.logOutDesc': 'Keluar dari akun Anda',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Bahasa',
'settings.languageDesc': 'Bahasa tampilan untuk antarmuka aplikasi',
'settings.alerts': 'Peringatan',
@@ -523,6 +525,18 @@ const id1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'Jejak navigasi',
'settings.betaBuild': 'Build beta - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/id-3.ts b/app/src/lib/i18n/chunks/id-3.ts
index 72b9d5c9f..923ce7e57 100644
--- a/app/src/lib/i18n/chunks/id-3.ts
+++ b/app/src/lib/i18n/chunks/id-3.ts
@@ -215,6 +215,9 @@ const id3: TranslationMap = {
'about.update.status.default': 'Periksa pembaruan',
'welcome.connectionFailed': 'Koneksi gagal: {status} {statusText}',
'welcome.connectionFailedMsg': 'Koneksi gagal: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'Buka sesi chat langsung dengan agen.',
'channels.activeRouteValue': '{channel} lewat {authMode}',
'privacy.dataKind.messages': 'Pesan',
@@ -406,6 +409,7 @@ const id3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default id3;
diff --git a/app/src/lib/i18n/chunks/it-1.ts b/app/src/lib/i18n/chunks/it-1.ts
index 2d6634cb5..e90953786 100644
--- a/app/src/lib/i18n/chunks/it-1.ts
+++ b/app/src/lib/i18n/chunks/it-1.ts
@@ -85,6 +85,8 @@ const it1: TranslationMap = {
"Disconnetti e cancella permanentemente tutti i dati locali dell'app",
'settings.logOut': 'Disconnetti',
'settings.logOutDesc': 'Disconnetti dal tuo account',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Lingua',
'settings.languageDesc': "Lingua di visualizzazione dell'interfaccia dell'app",
'settings.alerts': 'Avvisi',
@@ -527,6 +529,18 @@ const it1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'Percorso di navigazione',
'settings.betaBuild': 'Build beta - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/it-3.ts b/app/src/lib/i18n/chunks/it-3.ts
index 2a918283f..de00a982f 100644
--- a/app/src/lib/i18n/chunks/it-3.ts
+++ b/app/src/lib/i18n/chunks/it-3.ts
@@ -218,6 +218,9 @@ const it3: TranslationMap = {
'about.update.status.default': 'Verifica aggiornamenti',
'welcome.connectionFailed': 'Connessione fallita: {status} {statusText}',
'welcome.connectionFailedMsg': 'Connessione fallita: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': "Apri una sessione di chat diretta con l'agente.",
'channels.activeRouteValue': '{channel} via {authMode}',
'privacy.dataKind.messages': 'Messaggi',
@@ -406,6 +409,7 @@ const it3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default it3;
diff --git a/app/src/lib/i18n/chunks/ko-1.ts b/app/src/lib/i18n/chunks/ko-1.ts
index d689f7595..215a5f59d 100644
--- a/app/src/lib/i18n/chunks/ko-1.ts
+++ b/app/src/lib/i18n/chunks/ko-1.ts
@@ -83,6 +83,8 @@ const ko1: TranslationMap = {
'settings.clearAppDataDesc': '로그아웃하고 모든 로컬 앱 데이터를 영구적으로 삭제',
'settings.logOut': '로그아웃',
'settings.logOutDesc': '계정에서 로그아웃',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': '언어',
'settings.languageDesc': '앱 인터페이스 표시 언어',
'settings.alerts': '알림',
@@ -520,6 +522,18 @@ const ko1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': '이동 경로',
'settings.betaBuild': '베타 빌드 - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/ko-3.ts b/app/src/lib/i18n/chunks/ko-3.ts
index 7543d247d..774500eca 100644
--- a/app/src/lib/i18n/chunks/ko-3.ts
+++ b/app/src/lib/i18n/chunks/ko-3.ts
@@ -213,6 +213,9 @@ const ko3: TranslationMap = {
'about.update.status.default': '업데이트 확인',
'welcome.connectionFailed': '연결 실패: {status} {statusText}',
'welcome.connectionFailedMsg': '연결 실패: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': '에이전트와 직접 채팅 세션을 엽니다.',
'channels.activeRouteValue': '{channel} via {authMode}',
'privacy.dataKind.messages': '메시지',
@@ -403,5 +406,6 @@ const ko3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default ko3;
diff --git a/app/src/lib/i18n/chunks/pt-1.ts b/app/src/lib/i18n/chunks/pt-1.ts
index a05921b98..242109a74 100644
--- a/app/src/lib/i18n/chunks/pt-1.ts
+++ b/app/src/lib/i18n/chunks/pt-1.ts
@@ -85,6 +85,8 @@ const pt1: TranslationMap = {
'settings.clearAppDataDesc': 'Sair e excluir permanentemente todos os dados locais do app',
'settings.logOut': 'Sair',
'settings.logOutDesc': 'Sair da sua conta',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Idioma',
'settings.languageDesc': 'Idioma de exibição da interface do app',
'settings.alerts': 'Alertas',
@@ -532,6 +534,18 @@ const pt1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'Trilha de navegação',
'settings.betaBuild': 'Build beta - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/pt-3.ts b/app/src/lib/i18n/chunks/pt-3.ts
index b43cf61bd..9cdd6fd41 100644
--- a/app/src/lib/i18n/chunks/pt-3.ts
+++ b/app/src/lib/i18n/chunks/pt-3.ts
@@ -217,6 +217,9 @@ const pt3: TranslationMap = {
'about.update.status.default': 'Verificar atualizações',
'welcome.connectionFailed': 'Falha na conexão: {status} {statusText}',
'welcome.connectionFailedMsg': 'Falha na conexão: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'Abrir uma sessão de chat direto com o agente.',
'channels.activeRouteValue': '{channel} via {authMode}',
'privacy.dataKind.messages': 'Mensagens',
@@ -405,6 +408,7 @@ const pt3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default pt3;
diff --git a/app/src/lib/i18n/chunks/ru-1.ts b/app/src/lib/i18n/chunks/ru-1.ts
index 3064c5397..6b6b02fd2 100644
--- a/app/src/lib/i18n/chunks/ru-1.ts
+++ b/app/src/lib/i18n/chunks/ru-1.ts
@@ -85,6 +85,8 @@ const ru1: TranslationMap = {
'settings.clearAppDataDesc': 'Выйти из аккаунта и удалить все локальные данные приложения',
'settings.logOut': 'Выйти',
'settings.logOutDesc': 'Выйти из своего аккаунта',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Язык',
'settings.languageDesc': 'Язык отображения интерфейса',
'settings.alerts': 'Оповещения',
@@ -522,6 +524,18 @@ const ru1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': 'Навигационная цепочка',
'settings.betaBuild': 'Бета-сборка — v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/ru-3.ts b/app/src/lib/i18n/chunks/ru-3.ts
index d4c47e3ee..635fca70a 100644
--- a/app/src/lib/i18n/chunks/ru-3.ts
+++ b/app/src/lib/i18n/chunks/ru-3.ts
@@ -215,6 +215,9 @@ const ru3: TranslationMap = {
'about.update.status.default': 'Проверить обновления',
'welcome.connectionFailed': 'Ошибка подключения: {status} {statusText}',
'welcome.connectionFailedMsg': 'Ошибка подключения: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
'chat.agentChatDesc': 'Открыть прямой чат с агентом.',
'channels.activeRouteValue': '{channel} через {authMode}',
'privacy.dataKind.messages': 'Сообщения',
@@ -402,6 +405,7 @@ const ru3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': 'Chat via the built-in web UI.',
'channels.web.authMode.managed_dm.description': 'Use the embedded web chat — no setup required.',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default ru3;
diff --git a/app/src/lib/i18n/chunks/zh-CN-1.ts b/app/src/lib/i18n/chunks/zh-CN-1.ts
index c9108b56f..2b8e769a5 100644
--- a/app/src/lib/i18n/chunks/zh-CN-1.ts
+++ b/app/src/lib/i18n/chunks/zh-CN-1.ts
@@ -83,6 +83,8 @@ const zhCN1: TranslationMap = {
'settings.clearAppDataDesc': '退出登录并永久清除所有本地应用数据',
'settings.logOut': '退出登录',
'settings.logOutDesc': '退出当前账户',
+ 'settings.exitLocalSession': '退出本地会话',
+ 'settings.exitLocalSessionDesc': '返回登录页面',
'settings.language': '语言',
'settings.languageDesc': '应用界面显示语言',
'settings.alerts': '通知',
@@ -504,6 +506,18 @@ const zhCN1: TranslationMap = {
'Must be a positive integer (use the Unlimited preset for no limit).',
'autonomy.presetUnlimited': 'Unlimited (default)',
'triggers.toggleFailed': '{action} failed for {trigger}: {message}',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'common.breadcrumb': '面包屑导航',
'settings.betaBuild': '测试版构建 - v{version}',
'migration.vendor.openclaw': 'OpenClaw',
diff --git a/app/src/lib/i18n/chunks/zh-CN-3.ts b/app/src/lib/i18n/chunks/zh-CN-3.ts
index 0e8a794db..a417beb00 100644
--- a/app/src/lib/i18n/chunks/zh-CN-3.ts
+++ b/app/src/lib/i18n/chunks/zh-CN-3.ts
@@ -205,6 +205,9 @@ const zhCN3: TranslationMap = {
'about.update.status.default': '检查更新',
'welcome.connectionFailed': '连接失败: {status} {statusText}',
'welcome.connectionFailedMsg': '连接失败: {message}',
+ 'welcome.continueLocally': '本地继续',
+ 'welcome.localSessionStarting': '正在启动本地会话...',
+ 'welcome.localSessionDesc': '使用离线本地配置文件,跳过 TinyHumans OAuth。',
'chat.agentChatDesc': '与智能体进行直接对话。',
'channels.activeRouteValue': '{channel} 通过 {authMode}',
'privacy.dataKind.messages': '消息',
@@ -396,6 +399,7 @@ const zhCN3: TranslationMap = {
'channels.web.displayName': 'Web',
'channels.web.description': '通过内置的 Web UI 聊天。',
'channels.web.authMode.managed_dm.description': '使用嵌入式 Web 聊天 — 无需设置。',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
};
export default zhCN3;
diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts
index 7b2432db1..3950a6739 100644
--- a/app/src/lib/i18n/de.ts
+++ b/app/src/lib/i18n/de.ts
@@ -7,6 +7,25 @@ import type { TranslationMap } from './types';
// German (Deutsch) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const de: TranslationMap = { ...de1, ...de2, ...de3, ...de4, ...de5 };
+const de: TranslationMap = {
+ ...de1,
+ ...de2,
+ ...de3,
+ ...de4,
+ ...de5,
+ 'skills.composio.noApiKeyTitle': 'Kein Composio-API-Schlüssel konfiguriert',
+ 'skills.composio.noApiKeyDescription':
+ 'Im lokalen Modus wird dein eigener Composio-API-Schlüssel verwendet. Öffne Einstellungen → Erweitert → Composio, um einen Schlüssel hinzuzufügen, bevor du hier Integrationen verbindest.',
+ 'skills.composio.noApiKeyCta': 'In den Einstellungen öffnen',
+ 'channels.localManagedUnavailable': 'Verwaltete Kanäle sind für lokale Benutzer nicht verfügbar.',
+ 'rewards.localUnavailable':
+ 'Ein lokaler Login sammelt keine Belohnungen, Gutscheine oder Empfehlungsguthaben. Melde dich ab und anschließend mit einem OpenHuman-Konto an, wenn Belohnungen zählen sollen.',
+ 'rewards.localUnavailableCta': 'Kontoeinstellungen öffnen',
+ 'settings.search.localManagedUnavailable':
+ 'Die von OpenHuman verwaltete Suche ist für lokale Benutzer nicht verfügbar. Füge deinen eigenen Parallel- oder Brave-API-Schlüssel hinzu, um die Websuche zu aktivieren.',
+ 'devices.comingSoonDescription':
+ 'Gerätekopplung kommt bald. Diese Seite wird für das Koppeln von iPhones und die Verwaltung verbundener Geräte zuständig sein.',
+ 'welcome.continueLocallyExperimental': 'Lokal fortfahren (Experimentell)',
+};
export default de;
diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts
index 55a09d8ec..05b1e01c2 100644
--- a/app/src/lib/i18n/en.ts
+++ b/app/src/lib/i18n/en.ts
@@ -90,6 +90,8 @@ const en: TranslationMap = {
'settings.clearAppDataDesc': 'Sign out and permanently clear all local app data',
'settings.logOut': 'Log out',
'settings.logOutDesc': 'Sign out of your account',
+ 'settings.exitLocalSession': 'Exit local session',
+ 'settings.exitLocalSessionDesc': 'Return to the sign-in screen',
'settings.language': 'Language',
'settings.betaBuild': 'Beta build - v{version}',
'settings.languageDesc': 'Display language for the app interface',
@@ -251,6 +253,10 @@ const en: TranslationMap = {
'skills.integrations': 'Composio Integrations',
'skills.integrationsSubtitle':
'Cloud-based OAuth connections — sign in with your account and Composio brokers the tokens so agents can read and act on your behalf. No API keys to manage.',
+ 'skills.composio.noApiKeyTitle': 'No Composio API Key Configured',
+ 'skills.composio.noApiKeyDescription':
+ 'Local mode uses your own Composio API key. Open Settings → Advanced → Composio to add one before connecting integrations here.',
+ 'skills.composio.noApiKeyCta': 'Open in Settings',
'skills.tabs.composio': 'Composio',
'skills.tabs.channels': 'Channels',
'skills.tabs.mcp': 'MCP Servers',
@@ -277,6 +283,9 @@ const en: TranslationMap = {
'rewards.title': 'Rewards',
'rewards.referrals': 'Referrals',
'rewards.coupons': 'Redeem',
+ 'rewards.localUnavailable':
+ 'Local login does not earn rewards, coupons, or referral credit. To earn rewards, log out and continue by signing in with an OpenHuman account.',
+ 'rewards.localUnavailableCta': 'Open Account Settings',
'rewards.credits': 'Credits',
'rewards.referralCode': 'Your referral code',
'rewards.copyCode': 'Copy code',
@@ -420,6 +429,7 @@ const en: TranslationMap = {
'channels.configure': 'Configure Channel',
'channels.setup': 'Setup',
'channels.noChannels': 'No channels configured',
+ 'channels.localManagedUnavailable': 'Managed channels are not available for local users.',
'channels.addChannel': 'Add Channel',
'channels.status.connected': 'Connected',
'channels.status.disconnected': 'Disconnected',
@@ -584,6 +594,8 @@ const en: TranslationMap = {
'settings.search.engineManagedLabel': 'OpenHuman Managed',
'settings.search.engineManagedDesc':
'Default. Routed through the OpenHuman backend — no API key required.',
+ 'settings.search.localManagedUnavailable':
+ 'OpenHuman Managed search is not available for local users. Add your own Parallel or Brave API key to enable web search.',
'settings.search.engineParallelLabel': 'Parallel',
'settings.search.engineParallelDesc':
'Direct Parallel API: search, extract, chat, research, enrich, dataset tools.',
@@ -627,6 +639,8 @@ const en: TranslationMap = {
'devices.betaBadge': 'Beta',
'devices.betaText':
'This feature is currently in beta. Pair iOS phones with this OpenHuman to use them as a remote client.',
+ 'devices.comingSoonDescription':
+ 'Device pairing is coming soon. This page will be the home for pairing iPhones and managing connected devices.',
'devices.title': 'Devices',
'devices.pairIphone': 'Pair iPhone',
'devices.noPaired': 'No paired devices',
@@ -1660,6 +1674,10 @@ const en: TranslationMap = {
// Welcome: connection error messages
'welcome.connectionFailed': 'Connection failed: {status} {statusText}',
'welcome.connectionFailedMsg': 'Connection failed: {message}',
+ 'welcome.continueLocally': 'Continue locally',
+ 'welcome.continueLocallyExperimental': 'Continue Locally (Experimental)',
+ 'welcome.localSessionStarting': 'Starting local session...',
+ 'welcome.localSessionDesc': 'Uses an offline local profile and skips TinyHumans OAuth.',
// Chat: Agent chat panel description
'chat.agentChatDesc': 'Open a direct chat session with the agent.',
diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts
index fa3153525..bbed6eb01 100644
--- a/app/src/lib/i18n/es.ts
+++ b/app/src/lib/i18n/es.ts
@@ -7,6 +7,26 @@ import type { TranslationMap } from './types';
// Spanish (Español) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const es: TranslationMap = { ...es1, ...es2, ...es3, ...es4, ...es5 };
+const es: TranslationMap = {
+ ...es1,
+ ...es2,
+ ...es3,
+ ...es4,
+ ...es5,
+ 'skills.composio.noApiKeyTitle': 'No hay una API key de Composio configurada',
+ 'skills.composio.noApiKeyDescription':
+ 'El modo local usa tu propia API key de Composio. Abre Ajustes → Avanzado → Composio para añadir una antes de conectar integraciones aquí.',
+ 'skills.composio.noApiKeyCta': 'Abrir en Ajustes',
+ 'channels.localManagedUnavailable':
+ 'Los canales gestionados no están disponibles para usuarios locales.',
+ 'rewards.localUnavailable':
+ 'El acceso local no obtiene recompensas, cupones ni crédito por referidos. Cierra sesión y continúa iniciando sesión con una cuenta de OpenHuman si quieres que las recompensas cuenten.',
+ 'rewards.localUnavailableCta': 'Abrir ajustes de la cuenta',
+ 'settings.search.localManagedUnavailable':
+ 'La búsqueda gestionada por OpenHuman no está disponible para usuarios locales. Añade tu propia API key de Parallel o Brave para habilitar la búsqueda web.',
+ 'devices.comingSoonDescription':
+ 'El emparejamiento de dispositivos llegará pronto. Esta página será el lugar para emparejar iPhones y gestionar dispositivos conectados.',
+ 'welcome.continueLocallyExperimental': 'Continuar localmente (Experimental)',
+};
export default es;
diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts
index ca2795040..c67cf74fc 100644
--- a/app/src/lib/i18n/fr.ts
+++ b/app/src/lib/i18n/fr.ts
@@ -7,6 +7,26 @@ import type { TranslationMap } from './types';
// French (Français) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const fr: TranslationMap = { ...fr1, ...fr2, ...fr3, ...fr4, ...fr5 };
+const fr: TranslationMap = {
+ ...fr1,
+ ...fr2,
+ ...fr3,
+ ...fr4,
+ ...fr5,
+ 'skills.composio.noApiKeyTitle': 'Aucune clé API Composio configurée',
+ 'skills.composio.noApiKeyDescription':
+ 'Le mode local utilise votre propre clé API Composio. Ouvrez Paramètres → Avancé → Composio pour en ajouter une avant de connecter des intégrations ici.',
+ 'skills.composio.noApiKeyCta': 'Ouvrir dans les paramètres',
+ 'channels.localManagedUnavailable':
+ 'Les canaux gérés ne sont pas disponibles pour les utilisateurs locaux.',
+ 'rewards.localUnavailable':
+ 'La connexion locale ne permet pas de gagner des récompenses, des coupons ou du crédit de parrainage. Déconnecte-toi puis connecte-toi avec un compte OpenHuman si tu veux que les récompenses comptent.',
+ 'rewards.localUnavailableCta': 'Ouvrir les paramètres du compte',
+ 'settings.search.localManagedUnavailable':
+ 'La recherche gérée par OpenHuman n’est pas disponible pour les utilisateurs locaux. Ajoutez votre propre clé API Parallel ou Brave pour activer la recherche web.',
+ 'devices.comingSoonDescription':
+ 'L’appairage des appareils arrive bientôt. Cette page servira à appairer des iPhone et à gérer les appareils connectés.',
+ 'welcome.continueLocallyExperimental': 'Continuer en local (Expérimental)',
+};
export default fr;
diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts
index 56694e538..17ccc0547 100644
--- a/app/src/lib/i18n/hi.ts
+++ b/app/src/lib/i18n/hi.ts
@@ -7,6 +7,25 @@ import type { TranslationMap } from './types';
// Hindi (हिन्दी) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const hi: TranslationMap = { ...hi1, ...hi2, ...hi3, ...hi4, ...hi5 };
+const hi: TranslationMap = {
+ ...hi1,
+ ...hi2,
+ ...hi3,
+ ...hi4,
+ ...hi5,
+ 'skills.composio.noApiKeyTitle': 'कोई Composio API key कॉन्फ़िगर नहीं है',
+ 'skills.composio.noApiKeyDescription':
+ 'लोकल मोड आपकी अपनी Composio API key का उपयोग करता है। यहाँ integrations जोड़ने से पहले Settings → Advanced → Composio खोलकर key जोड़ें।',
+ 'skills.composio.noApiKeyCta': 'Settings में खोलें',
+ 'channels.localManagedUnavailable': 'लोकल उपयोगकर्ताओं के लिए managed channels उपलब्ध नहीं हैं।',
+ 'rewards.localUnavailable':
+ 'लोकल लॉगिन पर rewards, coupons या referral credit नहीं मिलते। rewards पाने के लिए लॉग आउट करें और OpenHuman खाते से साइन इन करें।',
+ 'rewards.localUnavailableCta': 'Account Settings खोलें',
+ 'settings.search.localManagedUnavailable':
+ 'लोकल उपयोगकर्ताओं के लिए OpenHuman Managed search उपलब्ध नहीं है। वेब सर्च चालू करने के लिए अपनी Parallel या Brave API key जोड़ें।',
+ 'devices.comingSoonDescription':
+ 'डिवाइस पेयरिंग जल्द आ रही है। यह पेज iPhone पेयर करने और जुड़े हुए डिवाइस प्रबंधित करने का स्थान होगा।',
+ 'welcome.continueLocallyExperimental': 'लोकल रूप से जारी रखें (प्रायोगिक)',
+};
export default hi;
diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts
index 0e55de204..ab5e04e30 100644
--- a/app/src/lib/i18n/id.ts
+++ b/app/src/lib/i18n/id.ts
@@ -7,6 +7,25 @@ import type { TranslationMap } from './types';
// Bahasa Indonesia translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const id: TranslationMap = { ...id1, ...id2, ...id3, ...id4, ...id5 };
+const id: TranslationMap = {
+ ...id1,
+ ...id2,
+ ...id3,
+ ...id4,
+ ...id5,
+ 'skills.composio.noApiKeyTitle': 'Belum ada API key Composio yang dikonfigurasi',
+ 'skills.composio.noApiKeyDescription':
+ 'Mode lokal menggunakan API key Composio milik Anda sendiri. Buka Pengaturan → Lanjutan → Composio untuk menambahkannya sebelum menghubungkan integrasi di sini.',
+ 'skills.composio.noApiKeyCta': 'Buka di Pengaturan',
+ 'channels.localManagedUnavailable': 'Channel terkelola tidak tersedia untuk pengguna lokal.',
+ 'rewards.localUnavailable':
+ 'Login lokal tidak mendapatkan reward, kupon, atau kredit referral. Keluar lalu lanjutkan dengan masuk menggunakan akun OpenHuman jika Anda ingin reward dihitung.',
+ 'rewards.localUnavailableCta': 'Buka Pengaturan Akun',
+ 'settings.search.localManagedUnavailable':
+ 'Pencarian OpenHuman Managed tidak tersedia untuk pengguna lokal. Tambahkan API key Parallel atau Brave Anda sendiri untuk mengaktifkan pencarian web.',
+ 'devices.comingSoonDescription':
+ 'Pemasangan perangkat akan segera hadir. Halaman ini akan menjadi tempat untuk memasangkan iPhone dan mengelola perangkat yang terhubung.',
+ 'welcome.continueLocallyExperimental': 'Lanjutkan Secara Lokal (Eksperimental)',
+};
export default id;
diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts
index 15cdab7d9..0c4ac79c7 100644
--- a/app/src/lib/i18n/it.ts
+++ b/app/src/lib/i18n/it.ts
@@ -7,6 +7,26 @@ import type { TranslationMap } from './types';
// Italian (Italiano) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const it: TranslationMap = { ...it1, ...it2, ...it3, ...it4, ...it5 };
+const it: TranslationMap = {
+ ...it1,
+ ...it2,
+ ...it3,
+ ...it4,
+ ...it5,
+ 'skills.composio.noApiKeyTitle': 'Nessuna chiave API Composio configurata',
+ 'skills.composio.noApiKeyDescription':
+ 'La modalità locale usa la tua chiave API Composio. Apri Impostazioni → Avanzate → Composio per aggiungerne una prima di collegare le integrazioni qui.',
+ 'skills.composio.noApiKeyCta': 'Apri nelle impostazioni',
+ 'channels.localManagedUnavailable':
+ 'I canali gestiti non sono disponibili per gli utenti locali.',
+ 'rewards.localUnavailable':
+ "L'accesso locale non guadagna ricompense, coupon o credito referral. Esci e continua accedendo con un account OpenHuman se vuoi che le ricompense vengano conteggiate.",
+ 'rewards.localUnavailableCta': 'Apri le impostazioni account',
+ 'settings.search.localManagedUnavailable':
+ 'La ricerca gestita da OpenHuman non è disponibile per gli utenti locali. Aggiungi la tua chiave API Parallel o Brave per abilitare la ricerca web.',
+ 'devices.comingSoonDescription':
+ "L'abbinamento dei dispositivi arriverà presto. Questa pagina sarà il punto centrale per abbinare gli iPhone e gestire i dispositivi connessi.",
+ 'welcome.continueLocallyExperimental': 'Continua Localmente (Sperimentale)',
+};
export default it;
diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts
index 3d21380ba..a11c0e30d 100644
--- a/app/src/lib/i18n/ko.ts
+++ b/app/src/lib/i18n/ko.ts
@@ -2110,6 +2110,19 @@ const ko: TranslationMap = {
'settings.localModel.status.ollamaDocs': 'Ollama 문서',
'settings.localModel.status.thenRetry':
'설정 지침을 확인한 다음 런타임에 연결할 수 있게 되면 다시 시도하세요.',
+ 'skills.composio.noApiKeyTitle': 'Composio API 키가 설정되지 않았습니다',
+ 'skills.composio.noApiKeyDescription':
+ '로컬 모드는 사용자의 Composio API 키를 사용합니다. 여기서 통합을 연결하기 전에 설정 → 고급 → Composio에서 키를 추가하세요.',
+ 'skills.composio.noApiKeyCta': '설정에서 열기',
+ 'channels.localManagedUnavailable': '로컬 사용자에게는 관리형 채널을 사용할 수 없습니다.',
+ 'rewards.localUnavailable':
+ '로컬 로그인으로는 보상, 쿠폰 또는 추천 크레딧을 받을 수 없습니다. 보상을 받으려면 로그아웃한 뒤 OpenHuman 계정으로 로그인하세요.',
+ 'rewards.localUnavailableCta': '계정 설정 열기',
+ 'settings.search.localManagedUnavailable':
+ '로컬 사용자에게는 OpenHuman Managed 검색을 사용할 수 없습니다. 웹 검색을 사용하려면 Parallel 또는 Brave API 키를 추가하세요.',
+ 'devices.comingSoonDescription':
+ '기기 페어링은 곧 제공됩니다. 이 페이지는 iPhone 페어링과 연결된 기기 관리를 위한 공간이 됩니다.',
+ 'welcome.continueLocallyExperimental': '로컬로 계속하기 (실험적)',
};
export default ko;
diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts
index 578fb41fe..ecabdb95e 100644
--- a/app/src/lib/i18n/pt.ts
+++ b/app/src/lib/i18n/pt.ts
@@ -7,6 +7,26 @@ import type { TranslationMap } from './types';
// Portuguese (Português) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const pt: TranslationMap = { ...pt1, ...pt2, ...pt3, ...pt4, ...pt5 };
+const pt: TranslationMap = {
+ ...pt1,
+ ...pt2,
+ ...pt3,
+ ...pt4,
+ ...pt5,
+ 'skills.composio.noApiKeyTitle': 'Nenhuma chave de API do Composio configurada',
+ 'skills.composio.noApiKeyDescription':
+ 'O modo local usa sua própria chave de API do Composio. Abra Configurações → Avançado → Composio para adicionar uma antes de conectar integrações aqui.',
+ 'skills.composio.noApiKeyCta': 'Abrir nas Configurações',
+ 'channels.localManagedUnavailable':
+ 'Canais gerenciados não estão disponíveis para usuários locais.',
+ 'rewards.localUnavailable':
+ 'O login local não rende recompensas, cupons nem crédito de indicação. Saia e continue entrando com uma conta OpenHuman se quiser que as recompensas contem.',
+ 'rewards.localUnavailableCta': 'Abrir configurações da conta',
+ 'settings.search.localManagedUnavailable':
+ 'A busca gerenciada pela OpenHuman não está disponível para usuários locais. Adicione sua própria chave de API do Parallel ou Brave para habilitar a busca na web.',
+ 'devices.comingSoonDescription':
+ 'O pareamento de dispositivos está chegando em breve. Esta página será o lugar para parear iPhones e gerenciar dispositivos conectados.',
+ 'welcome.continueLocallyExperimental': 'Continuar Localmente (Experimental)',
+};
export default pt;
diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts
index 4a89a0137..c404e4644 100644
--- a/app/src/lib/i18n/ru.ts
+++ b/app/src/lib/i18n/ru.ts
@@ -7,6 +7,25 @@ import type { TranslationMap } from './types';
// Russian (Русский) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const ru: TranslationMap = { ...ru1, ...ru2, ...ru3, ...ru4, ...ru5 };
+const ru: TranslationMap = {
+ ...ru1,
+ ...ru2,
+ ...ru3,
+ ...ru4,
+ ...ru5,
+ 'skills.composio.noApiKeyTitle': 'Ключ API Composio не настроен',
+ 'skills.composio.noApiKeyDescription':
+ 'Локальный режим использует ваш собственный ключ API Composio. Откройте Настройки → Дополнительно → Composio, чтобы добавить ключ перед подключением интеграций здесь.',
+ 'skills.composio.noApiKeyCta': 'Открыть в настройках',
+ 'channels.localManagedUnavailable': 'Управляемые каналы недоступны для локальных пользователей.',
+ 'rewards.localUnavailable':
+ 'Локальный вход не приносит награды, купоны или реферальный кредит. Выйдите и войдите с аккаунтом OpenHuman, если хотите, чтобы награды начислялись.',
+ 'rewards.localUnavailableCta': 'Открыть настройки аккаунта',
+ 'settings.search.localManagedUnavailable':
+ 'Поиск OpenHuman Managed недоступен для локальных пользователей. Добавьте свой ключ API Parallel или Brave, чтобы включить веб-поиск.',
+ 'devices.comingSoonDescription':
+ 'Сопряжение устройств скоро появится. Эта страница будет местом для подключения iPhone и управления подключёнными устройствами.',
+ 'welcome.continueLocallyExperimental': 'Продолжить локально (Экспериментально)',
+};
export default ru;
diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts
index c02fa656a..60419abd2 100644
--- a/app/src/lib/i18n/zh-CN.ts
+++ b/app/src/lib/i18n/zh-CN.ts
@@ -7,6 +7,24 @@ import type { TranslationMap } from './types';
// Simplified Chinese (简体中文) translations. Each chunk maps to chunks/en-N.ts.
// Missing keys fall back to English via I18nContext.resolveEn().
-const zhCN: TranslationMap = { ...zhCN1, ...zhCN2, ...zhCN3, ...zhCN4, ...zhCN5 };
+const zhCN: TranslationMap = {
+ ...zhCN1,
+ ...zhCN2,
+ ...zhCN3,
+ ...zhCN4,
+ ...zhCN5,
+ 'skills.composio.noApiKeyTitle': '尚未配置 Composio API 密钥',
+ 'skills.composio.noApiKeyDescription':
+ '本地模式使用你自己的 Composio API 密钥。在此连接集成之前,请打开 设置 → 高级 → Composio 添加一个密钥。',
+ 'skills.composio.noApiKeyCta': '在设置中打开',
+ 'channels.localManagedUnavailable': '本地用户无法使用托管频道。',
+ 'rewards.localUnavailable':
+ '本地登录不会获得奖励、优惠券或推荐积分。若要累计奖励,请先登出,然后使用 OpenHuman 账号登录。',
+ 'rewards.localUnavailableCta': '打开账户设置',
+ 'settings.search.localManagedUnavailable':
+ '本地用户无法使用 OpenHuman 托管搜索。请添加你自己的 Parallel 或 Brave API 密钥以启用网页搜索。',
+ 'devices.comingSoonDescription': '设备配对即将推出。此页面将用于配对 iPhone 并管理已连接设备。',
+ 'welcome.continueLocallyExperimental': '本地继续(实验性)',
+};
export default zhCN;
diff --git a/app/src/pages/Rewards.tsx b/app/src/pages/Rewards.tsx
index 2077e1ac7..f49f5f398 100644
--- a/app/src/pages/Rewards.tsx
+++ b/app/src/pages/Rewards.tsx
@@ -1,13 +1,17 @@
import createDebug from 'debug';
import { useCallback, useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import EmptyStateCard from '../components/EmptyStateCard';
import PillTabBar from '../components/PillTabBar';
import RewardsCommunityTab from '../components/rewards/RewardsCommunityTab';
import RewardsRedeemTab from '../components/rewards/RewardsRedeemTab';
import RewardsReferralsTab from '../components/rewards/RewardsReferralsTab';
import { useT } from '../lib/i18n/I18nContext';
+import { useCoreState } from '../providers/CoreStateProvider';
import { rewardsApi } from '../services/api/rewardsApi';
import type { RewardsSnapshot } from '../types/rewards';
+import { isLocalSessionToken } from '../utils/localSession';
type RewardsTab = 'referrals' | 'redeem' | 'rewards';
@@ -25,8 +29,11 @@ function errorMessage(err: unknown): string {
const Rewards = () => {
const { t } = useT();
+ const navigate = useNavigate();
+ const { snapshot: coreSnapshot } = useCoreState();
+ const isLocalSession = isLocalSessionToken(coreSnapshot.sessionToken);
const [selectedTab, setSelectedTab] = useState('rewards');
- const [snapshot, setSnapshot] = useState(null);
+ const [rewardsSnapshot, setRewardsSnapshot] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
@@ -37,7 +44,7 @@ const Rewards = () => {
try {
const result = await rewardsApi.getMyRewards();
if (signal?.cancelled) return;
- setSnapshot(result);
+ setRewardsSnapshot(result);
log(
'snapshot applied unlockedCount=%d totalCount=%d',
result.summary.unlockedCount,
@@ -47,7 +54,7 @@ const Rewards = () => {
const message = errorMessage(err);
log('snapshot load failed error=%s', message);
if (signal?.cancelled) return;
- setSnapshot(null);
+ setRewardsSnapshot(null);
setError(message);
} finally {
if (!signal?.cancelled) {
@@ -57,12 +64,15 @@ const Rewards = () => {
}, []);
useEffect(() => {
+ if (isLocalSession) {
+ return;
+ }
const signal = { cancelled: false };
void loadRewards(signal);
return () => {
signal.cancelled = true;
};
- }, [loadRewards]);
+ }, [isLocalSession, loadRewards]);
const handleTabChange = useCallback((next: RewardsTab) => {
log('tab changed next=%s', next);
@@ -74,6 +84,37 @@ const Rewards = () => {
void loadRewards();
}, [loadRewards]);
+ if (isLocalSession) {
+ return (
+