diff --git a/app/src-tauri/src/directory_picker.rs b/app/src-tauri/src/directory_picker.rs
new file mode 100644
index 0000000000..55e4406d3b
--- /dev/null
+++ b/app/src-tauri/src/directory_picker.rs
@@ -0,0 +1,76 @@
+//! Native directory chooser for memory-source configuration (#5831).
+//!
+//! The folder memory-source used to be picked with an
+//! ` ` in the renderer. That element does
+//! not expose a filesystem path: Chromium only carries one on the
+//! non-standard `File.path` attribute, and only when the renderer has
+//! filesystem-aware integration. Outside that renderer the handler fell back
+//! to `webkitRelativePath.split('/')[0]` — the chosen directory's **name**
+//! with its location discarded — and stored it. The source then looked
+//! configured and could never sync, failing once per cycle forever with
+//! `folder does not exist: docs`.
+//!
+//! A native dialog has no such gap: it returns an absolute path in every
+//! renderer, on every platform.
+//!
+//! ## Why `rfd` and not `tauri-plugin-dialog`
+//!
+//! This mirrors [`crate::artifact_commands::save_artifact_via_dialog`]
+//! (#3162), which is already in this shell and already talks to the OS
+//! dialog APIs through `rfd`. Reusing it means **no new dependency, no new
+//! plugin, and no capability-allowlist entry** — the crate is declared in
+//! `Cargo.toml` with `default-features = false` plus `xdg-portal`, which is
+//! what keeps Linux off GTK. Adding the dialog plugin for one command would
+//! widen the dependency graph for a capability the shell already has.
+//!
+//! ## Trust boundary
+//!
+//! Deliberately none. Unlike the artifact commands — which re-validate that
+//! a renderer-supplied path sits inside the artifacts tree, because there
+//! the renderer *supplies* the path — this command takes no input and
+//! returns only what the user chose in an OS-owned dialog. The renderer
+//! cannot steer it at a directory, and picking a folder to index is the
+//! user's decision to make anywhere on their disk.
+
+/// Open the OS-native directory chooser and return the absolute path of the
+/// directory the user selected.
+///
+/// Returns:
+/// - `Ok(Some(path))` — the absolute path chosen.
+/// - `Ok(None)` — the user dismissed the dialog. Not an error; the caller
+/// leaves the field untouched.
+/// - `Err(_)` — the dialog could not run (for example, no xdg-desktop
+/// portal on a headless Linux host), or it somehow yielded a relative
+/// path. The caller surfaces this rather than storing anything.
+#[tauri::command]
+pub async fn pick_directory_via_dialog() -> Result, String> {
+ // On Linux this is the xdg-desktop portal (no GTK link); on macOS and
+ // Windows the system panel. The await resolves when the user picks or
+ // cancels.
+ let handle = rfd::AsyncFileDialog::new().pick_folder().await;
+
+ let Some(dir) = handle else {
+ log::info!("[directory_picker] pick_directory_via_dialog cancelled by user");
+ return Ok(None);
+ };
+
+ let path = dir.path().to_path_buf();
+
+ // The OS dialogs all hand back absolute paths, so this is a belt-and-braces
+ // check rather than a branch we expect to take. It exists because a
+ // relative value reaching the store is the entire defect this command was
+ // written to remove: an error here is recoverable and visible, whereas a
+ // stored relative path is neither.
+ if !path.is_absolute() {
+ return Err(format!(
+ "the directory chooser returned a non-absolute path: {}",
+ path.display()
+ ));
+ }
+
+ log::info!(
+ "[directory_picker] pick_directory_via_dialog chose {}",
+ path.display()
+ );
+ Ok(Some(path.display().to_string()))
+}
diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs
index fc802f91fe..2112642e46 100644
--- a/app/src-tauri/src/lib.rs
+++ b/app/src-tauri/src/lib.rs
@@ -43,6 +43,7 @@ mod core_rpc;
mod deep_link_ipc;
#[cfg(target_os = "windows")]
mod deep_link_ipc_windows;
+mod directory_picker;
// Cross-platform module: the registry-reading function is windows-only, but
// the parsing helpers compile (and test) everywhere so `cargo test` on the
// developer host covers them.
@@ -3371,6 +3372,7 @@ pub fn run() {
// and the Save-As fallback needs it there (CodeRabbit on #4127).
artifact_commands::save_artifact_via_dialog,
artifact_commands::download_artifact_to_downloads,
+ directory_picker::pick_directory_via_dialog,
// Structured WhatsApp data (store lives shell-side).
whatsapp_data::whatsapp_data_list_chats,
whatsapp_data::whatsapp_data_list_messages,
diff --git a/app/src/components/intelligence/AddMemorySourceFields.tsx b/app/src/components/intelligence/AddMemorySourceFields.tsx
index eea9a5e48c..95f5596ca0 100644
--- a/app/src/components/intelligence/AddMemorySourceFields.tsx
+++ b/app/src/components/intelligence/AddMemorySourceFields.tsx
@@ -19,6 +19,7 @@ import type { ComposioConnection } from '../../lib/composio/types';
import { useT } from '../../lib/i18n/I18nContext';
import type { SourceKind } from '../../services/memorySourcesService';
import TextField from '../ui/TextField';
+import { directoryPathFromPickedFiles, pickDirectoryNatively } from './folderPicker';
const log = debug('intelligence:add-memory-source-dialog');
@@ -60,6 +61,39 @@ interface FolderFieldProps {
function FolderField({ label, value, onChange }: FolderFieldProps) {
const { t } = useT();
+ const fallbackInputRef = useRef(null);
+ const [pickError, setPickError] = useState(null);
+
+ // Never store a value that cannot work as a path. `no-absolute-path` is the
+ // #5831 case: a directory was chosen but the renderer would not say where it
+ // is, so the only honest outcomes are an error or nothing — not the bare
+ // directory name the old handler saved, which produced a source that looked
+ // configured and could never sync.
+ const applyResult = (result: { ok: true; path: string } | { ok: false; reason: string }) => {
+ if (result.ok) {
+ setPickError(null);
+ onChange(result.path);
+ return;
+ }
+ if (result.reason === 'no-absolute-path') {
+ log('folder picker produced no absolute path; refusing to store a bare name');
+ setPickError(t('memorySources.folderPathUnavailable'));
+ }
+ // `cancelled` leaves the field exactly as it was, silently.
+ };
+
+ const handleBrowse = async () => {
+ const native = await pickDirectoryNatively();
+ if (native.ok || native.reason !== 'unavailable') {
+ applyResult(native);
+ return;
+ }
+ // No native chooser here (a browser context, or no portal). Fall back to
+ // the directory input, which may still carry a real path.
+ setPickError(null);
+ fallbackInputRef.current?.click();
+ };
+
return (
{label}
@@ -67,43 +101,41 @@ function FolderField({ label, value, onChange }: FolderFieldProps) {
onChange(e.target.value)}
+ onChange={e => {
+ setPickError(null);
+ onChange(e.target.value);
+ }}
placeholder={t('memorySources.folderPathPlaceholder')}
/>
-
{t('memorySources.browse')}
- {
- const files = e.target.files;
- if (!files || files.length === 0) return;
- // Chromium exposes the chosen directory path on the first file's `path`
- // attribute when the renderer has filesystem-aware integration (CEF).
- // Fall back to webkitRelativePath split if `path` isn't available.
- const first = files[0] as File & { path?: string };
- if (first.path) {
- // first.path is the absolute path to the file. Derive the directory
- // by trimming the relative portion (everything after the chosen root).
- const rel = first.webkitRelativePath || first.name;
- const abs = first.path;
- const idx = abs.lastIndexOf(rel);
- onChange(idx > 0 ? abs.slice(0, idx).replace(/\/$/, '') : abs);
- } else if (first.webkitRelativePath) {
- onChange(first.webkitRelativePath.split('/')[0]);
- }
- }}
- />
-
+
+ {
+ applyResult(directoryPathFromPickedFiles(e.target.files));
+ // Clear so re-picking the same directory fires `change` again.
+ e.target.value = '';
+ }}
+ />
+ {pickError ? (
+
+ {pickError}
+
+ ) : null}
);
}
diff --git a/app/src/components/intelligence/__tests__/folderPicker.test.ts b/app/src/components/intelligence/__tests__/folderPicker.test.ts
new file mode 100644
index 0000000000..6d328b5b90
--- /dev/null
+++ b/app/src/components/intelligence/__tests__/folderPicker.test.ts
@@ -0,0 +1,136 @@
+/**
+ * #5831 — the folder picker must never hand back a value that cannot work
+ * as a path.
+ *
+ * The defect these tests pin: when `File.path` was unavailable the handler
+ * fell back to `webkitRelativePath.split('/')[0]`, storing the chosen
+ * directory's NAME with its location discarded. A source built from that
+ * value can never sync, and it fails once per cycle forever rather than once,
+ * visibly, at the moment of choosing.
+ */
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { directoryPathFromPickedFiles, pickDirectoryNatively } from '../folderPicker';
+
+const hoisted = vi.hoisted(() => ({ invoke: vi.fn(), isTauri: vi.fn() }));
+
+vi.mock('../../../utils/tauriCommands/common', () => ({
+ safeInvoke: hoisted.invoke,
+ isTauri: hoisted.isTauri,
+}));
+
+/** A `webkitdirectory` file entry, with `path` present only when asked for. */
+function pickedFile(relativePath: string, absolutePath?: string): File {
+ const file = new File(['x'], relativePath.split('/').pop() ?? 'f.md');
+ Object.defineProperty(file, 'webkitRelativePath', { value: relativePath });
+ if (absolutePath !== undefined) {
+ Object.defineProperty(file, 'path', { value: absolutePath });
+ }
+ return file;
+}
+
+function fileList(...files: File[]): FileList {
+ const indexed: Record = {};
+ files.forEach((file, i) => {
+ indexed[i] = file;
+ });
+ return {
+ ...indexed,
+ length: files.length,
+ item: (i: number) => files[i] ?? null,
+ } as unknown as FileList;
+}
+
+describe('directoryPathFromPickedFiles', () => {
+ it('refuses to produce a path when the renderer does not expose File.path', () => {
+ // THE REGRESSION. The old handler returned 'docs' here. A source stored
+ // with that value produced `folder does not exist: docs` on every sync,
+ // indefinitely, and no absolute path is recoverable from this input.
+ const result = directoryPathFromPickedFiles(fileList(pickedFile('docs/readme.md')));
+
+ expect(result).toEqual({ ok: false, reason: 'no-absolute-path' });
+ });
+
+ it('never returns the bare directory name for any nesting depth', () => {
+ for (const relative of ['docs/a.md', 'docs/deep/b.md', 'docs/deep/deeper/c.md']) {
+ const result = directoryPathFromPickedFiles(fileList(pickedFile(relative)));
+
+ expect(result.ok).toBe(false);
+ // Belt and braces: assert the specific bad value can never come back,
+ // not merely that this input failed.
+ expect(JSON.stringify(result)).not.toContain('"docs"');
+ }
+ });
+
+ it('derives the containing directory when File.path is present', () => {
+ const result = directoryPathFromPickedFiles(
+ fileList(pickedFile('notes/readme.md', '/Users/you/notes/readme.md'))
+ );
+
+ expect(result).toEqual({ ok: true, path: '/Users/you' });
+ });
+
+ it('derives a Windows directory without mangling the separators', () => {
+ const result = directoryPathFromPickedFiles(
+ fileList(pickedFile('notes/readme.md', 'C:\\Users\\you\\notes\\readme.md'))
+ );
+
+ // `webkitRelativePath` is forward-slashed while the absolute path is not,
+ // so the relative portion does not appear verbatim and the whole path is
+ // kept. That is a usable absolute path, which is the property that matters.
+ expect(result).toEqual({ ok: true, path: 'C:\\Users\\you\\notes\\readme.md' });
+ });
+
+ it('treats an empty selection as a cancellation rather than an error', () => {
+ expect(directoryPathFromPickedFiles(null)).toEqual({ ok: false, reason: 'cancelled' });
+ expect(directoryPathFromPickedFiles(fileList())).toEqual({ ok: false, reason: 'cancelled' });
+ });
+});
+
+describe('pickDirectoryNatively', () => {
+ beforeEach(() => {
+ hoisted.invoke.mockReset();
+ hoisted.isTauri.mockReset();
+ });
+
+ it('reports unavailable outside the desktop shell, without invoking', async () => {
+ hoisted.isTauri.mockReturnValue(false);
+
+ await expect(pickDirectoryNatively()).resolves.toEqual({ ok: false, reason: 'unavailable' });
+ expect(hoisted.invoke).not.toHaveBeenCalled();
+ });
+
+ it('returns the absolute path the native dialog chose', async () => {
+ hoisted.isTauri.mockReturnValue(true);
+ hoisted.invoke.mockResolvedValue('/Users/you/notes');
+
+ await expect(pickDirectoryNatively()).resolves.toEqual({ ok: true, path: '/Users/you/notes' });
+ expect(hoisted.invoke).toHaveBeenCalledWith('pick_directory_via_dialog');
+ });
+
+ it('treats a null return as a cancellation', async () => {
+ hoisted.isTauri.mockReturnValue(true);
+ hoisted.invoke.mockResolvedValue(null);
+
+ await expect(pickDirectoryNatively()).resolves.toEqual({ ok: false, reason: 'cancelled' });
+ });
+
+ it('falls back to unavailable when the dialog cannot run', async () => {
+ // Headless Linux with no xdg-desktop portal lands here, and wants the
+ // fallback input rather than an error.
+ hoisted.isTauri.mockReturnValue(true);
+ hoisted.invoke.mockRejectedValue(new Error('no portal'));
+
+ await expect(pickDirectoryNatively()).resolves.toEqual({ ok: false, reason: 'unavailable' });
+ });
+
+ it('refuses a blank path rather than storing it', async () => {
+ hoisted.isTauri.mockReturnValue(true);
+ hoisted.invoke.mockResolvedValue(' ');
+
+ await expect(pickDirectoryNatively()).resolves.toEqual({
+ ok: false,
+ reason: 'no-absolute-path',
+ });
+ });
+});
diff --git a/app/src/components/intelligence/folderPicker.ts b/app/src/components/intelligence/folderPicker.ts
new file mode 100644
index 0000000000..aad9941058
--- /dev/null
+++ b/app/src/components/intelligence/folderPicker.ts
@@ -0,0 +1,110 @@
+/**
+ * Folder selection for the folder memory-source (#5831).
+ *
+ * The rule this module exists to enforce: **never hand back a value that
+ * cannot work as a path.** A folder source is stored verbatim and read back
+ * by the memory driver, so a value that is not an absolute path produces a
+ * source that looks configured and can never sync — failing once per cycle,
+ * indefinitely, with an error the user cannot connect to the picker they
+ * used. Refusing at the point of choosing is strictly better: it is one
+ * failure, immediately, next to the control that caused it.
+ *
+ * Two selection paths, in preference order:
+ *
+ * 1. {@link pickDirectoryNatively} — the OS directory chooser, via the
+ * `pick_directory_via_dialog` Tauri command. Returns an absolute path in
+ * every renderer and on every platform. This is the durable answer.
+ * 2. {@link directoryPathFromPickedFiles} — the ` `
+ * fallback for a browser context, where no native dialog exists. It can
+ * only produce a real path when Chromium exposes the non-standard
+ * `File.path`; when it does not, this returns a failure rather than the
+ * bare directory name the old code stored.
+ */
+import { safeInvoke as invoke, isTauri } from '../../utils/tauriCommands/common';
+
+/**
+ * Why a folder selection produced no usable path.
+ *
+ * - `cancelled` — the user dismissed the chooser. Not an error; the caller
+ * leaves the field as it was and says nothing.
+ * - `unavailable` — no native chooser here (a browser context, or the
+ * dialog could not run). The caller offers the fallback input.
+ * - `no-absolute-path` — a directory was chosen but the renderer would not
+ * say where it is. **This is the case that must never be stored.**
+ */
+export type FolderPickFailure = 'cancelled' | 'unavailable' | 'no-absolute-path';
+
+export type FolderPickResult =
+ | { ok: true; path: string }
+ | { ok: false; reason: FolderPickFailure };
+
+/**
+ * Derive the chosen directory's absolute path from a `webkitdirectory`
+ * `FileList`.
+ *
+ * Chromium exposes the absolute path of each file on the non-standard
+ * `File.path` attribute only when the renderer has filesystem-aware
+ * integration. When present, the directory is that path with the file's
+ * `webkitRelativePath` (`/<...>/`) trimmed off the end.
+ *
+ * When it is absent, all that remains is `webkitRelativePath`, whose first
+ * segment is the directory's **name** — not its location. The old handler
+ * stored that name and it could never resolve, which is #5831. So this
+ * returns `no-absolute-path` instead: the caller must surface it, not save it.
+ */
+export function directoryPathFromPickedFiles(files: FileList | null): FolderPickResult {
+ if (!files || files.length === 0) {
+ return { ok: false, reason: 'cancelled' };
+ }
+
+ const first = files[0] as File & { path?: string };
+ const absolute = first.path;
+
+ if (!absolute) {
+ // `webkitRelativePath` is deliberately NOT consulted as a fallback. Its
+ // first segment is a name with the location discarded; storing it is the
+ // defect, not a degraded-but-usable answer.
+ return { ok: false, reason: 'no-absolute-path' };
+ }
+
+ const relative = first.webkitRelativePath || first.name;
+ const cut = absolute.lastIndexOf(relative);
+ // `cut > 0` keeps a pathological `lastIndexOf` result (0, or -1 when the
+ // relative portion is somehow absent) from truncating the path to nothing.
+ const directory = cut > 0 ? absolute.slice(0, cut) : absolute;
+ const trimmed = directory.replace(/[/\\]+$/, '');
+
+ // A trailing-separator trim can empty a root-level selection ("/" -> ""),
+ // and an empty path is exactly the unusable value this module refuses.
+ if (trimmed.length === 0) {
+ return { ok: true, path: directory };
+ }
+ return { ok: true, path: trimmed };
+}
+
+/**
+ * Open the OS-native directory chooser.
+ *
+ * Resolves `unavailable` outside the desktop shell, and also when the
+ * command throws — a headless Linux host with no xdg-desktop portal reaches
+ * the same place as a browser tab, and both want the fallback input rather
+ * than an error.
+ */
+export async function pickDirectoryNatively(): Promise {
+ if (!isTauri()) {
+ return { ok: false, reason: 'unavailable' };
+ }
+
+ try {
+ const chosen = await invoke('pick_directory_via_dialog');
+ if (chosen == null) {
+ return { ok: false, reason: 'cancelled' };
+ }
+ if (chosen.trim().length === 0) {
+ return { ok: false, reason: 'no-absolute-path' };
+ }
+ return { ok: true, path: chosen };
+ } catch {
+ return { ok: false, reason: 'unavailable' };
+ }
+}
diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts
index 63de2d781c..bbbe6bb080 100644
--- a/app/src/lib/i18n/ar.ts
+++ b/app/src/lib/i18n/ar.ts
@@ -2747,6 +2747,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'قريباً',
'memorySources.composioListFailed': 'فشل في تحميل الأتصالات Xqx0x.',
'memorySources.browse': '(بروز)...',
+ 'memorySources.folderPathUnavailable':
+ 'تعذّر تحديد موقع هذا المجلد. اكتب مساره الكامل بدلاً من ذلك.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts
index 04b37813d2..b913310913 100644
--- a/app/src/lib/i18n/bn.ts
+++ b/app/src/lib/i18n/bn.ts
@@ -2813,6 +2813,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'শীঘ্রই আসছে',
'memorySources.composioListFailed': 'Xqxqx সংযোগ লোড করতে ব্যর্থ।',
'memorySources.browse': 'ব্রাউজ করুন...',
+ 'memorySources.folderPathUnavailable':
+ 'সেই ফোল্ডারটি কোথায় আছে তা নির্ণয় করা যায়নি। বদলে এর সম্পূর্ণ পাথ লিখুন।',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '*ড্যাম',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts
index bb679f37b6..b1a43b5dbc 100644
--- a/app/src/lib/i18n/de.ts
+++ b/app/src/lib/i18n/de.ts
@@ -2891,6 +2891,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Demnächst',
'memorySources.composioListFailed': 'Fehler beim Laden der Composio-Verbindungen.',
'memorySources.browse': 'Durchsuchen…',
+ 'memorySources.folderPathUnavailable':
+ 'Der Speicherort dieses Ordners ließ sich nicht ermitteln. Gib stattdessen den vollständigen Pfad ein.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': 'Md. ',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts
index dd3774e19a..840e71a9df 100644
--- a/app/src/lib/i18n/en.ts
+++ b/app/src/lib/i18n/en.ts
@@ -3061,6 +3061,8 @@ const en: TranslationMap = {
'memorySources.comingSoon': 'Coming soon',
'memorySources.composioListFailed': 'Failed to load Composio connections.',
'memorySources.browse': 'Browse…',
+ 'memorySources.folderPathUnavailable':
+ 'Could not determine where that folder is. Type its full path instead.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts
index eb71b79274..a629c8b363 100644
--- a/app/src/lib/i18n/es.ts
+++ b/app/src/lib/i18n/es.ts
@@ -2866,6 +2866,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Próximamente',
'memorySources.composioListFailed': 'Error al cargar las conexiones Composio.',
'memorySources.browse': 'Examinar…',
+ 'memorySources.folderPathUnavailable':
+ 'No se pudo determinar dónde está esa carpeta. Escribe su ruta completa.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts
index a626e6f338..72448c1e21 100644
--- a/app/src/lib/i18n/fr.ts
+++ b/app/src/lib/i18n/fr.ts
@@ -2889,6 +2889,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Bientôt disponible',
'memorySources.composioListFailed': 'Échec du chargement des connexions Composio.',
'memorySources.browse': 'Parcourir…',
+ 'memorySources.folderPathUnavailable':
+ 'Impossible de déterminer où se trouve ce dossier. Saisissez plutôt son chemin complet.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts
index 825b0a54a2..c1df0e9e0e 100644
--- a/app/src/lib/i18n/hi.ts
+++ b/app/src/lib/i18n/hi.ts
@@ -2811,6 +2811,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'जल्द आ रहा है',
'memorySources.composioListFailed': 'Composio कनेक्शन लोड करने में विफल रहा।',
'memorySources.browse': 'ब्राउज़ करें',
+ 'memorySources.folderPathUnavailable':
+ 'यह पता नहीं चल सका कि वह फ़ोल्डर कहाँ है। इसके बजाय उसका पूरा पथ लिखें।',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts
index ede8f92239..363b99bea2 100644
--- a/app/src/lib/i18n/id.ts
+++ b/app/src/lib/i18n/id.ts
@@ -2823,6 +2823,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Segera hadir',
'memorySources.composioListFailed': 'Gagal memuat koneksi Composio.',
'memorySources.browse': 'Jelajahi...',
+ 'memorySources.folderPathUnavailable':
+ 'Tidak dapat menentukan lokasi folder tersebut. Ketik jalur lengkapnya.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '* * /*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts
index cb0ff7dc3b..45f5484fbd 100644
--- a/app/src/lib/i18n/it.ts
+++ b/app/src/lib/i18n/it.ts
@@ -2865,6 +2865,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Prossimamente',
'memorySources.composioListFailed': 'Impossibile caricare le connessioni Composio.',
'memorySources.browse': 'Sfoglia…',
+ 'memorySources.folderPathUnavailable':
+ 'Non è stato possibile determinare dove si trova quella cartella. Digita invece il percorso completo.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts
index 3f9a749fb7..8d7b0982fe 100644
--- a/app/src/lib/i18n/ko.ts
+++ b/app/src/lib/i18n/ko.ts
@@ -2777,6 +2777,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': '출시 예정',
'memorySources.composioListFailed': 'Composio 연결을 불러오지 못했습니다.',
'memorySources.browse': '찾아보기…',
+ 'memorySources.folderPathUnavailable':
+ '해당 폴더의 위치를 확인할 수 없습니다. 대신 전체 경로를 입력하세요.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts
index dc6649b9dd..cc850564e5 100644
--- a/app/src/lib/i18n/pl.ts
+++ b/app/src/lib/i18n/pl.ts
@@ -2842,6 +2842,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Wkrótce',
'memorySources.composioListFailed': 'Nie udało się wczytać połączeń Composio.',
'memorySources.browse': 'Przeglądaj…',
+ 'memorySources.folderPathUnavailable':
+ 'Nie udało się ustalić, gdzie znajduje się ten folder. Wpisz zamiast tego jego pełną ścieżkę.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts
index 06427b7959..eb9b7c3be6 100644
--- a/app/src/lib/i18n/pt.ts
+++ b/app/src/lib/i18n/pt.ts
@@ -2861,6 +2861,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Em breve',
'memorySources.composioListFailed': 'Falha ao carregar as conexões Composio.',
'memorySources.browse': 'Navegar…',
+ 'memorySources.folderPathUnavailable':
+ 'Não foi possível determinar onde essa pasta está. Digite o caminho completo.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts
index 38607343f3..94299cc7c7 100644
--- a/app/src/lib/i18n/ru.ts
+++ b/app/src/lib/i18n/ru.ts
@@ -2831,6 +2831,8 @@ const messages: TranslationMap = {
'memorySources.comingSoon': 'Скоро',
'memorySources.composioListFailed': 'Не удалось загрузить соединения Composio.',
'memorySources.browse': 'Просматривать…',
+ 'memorySources.folderPathUnavailable':
+ 'Не удалось определить расположение этой папки. Введите полный путь.',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',
diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts
index c2486840a8..cf13995a94 100644
--- a/app/src/lib/i18n/zh-CN.ts
+++ b/app/src/lib/i18n/zh-CN.ts
@@ -2659,6 +2659,7 @@ const messages: TranslationMap = {
'memorySources.comingSoon': '即将推出',
'memorySources.composioListFailed': '加载 Composio 连接失败。',
'memorySources.browse': '浏览…',
+ 'memorySources.folderPathUnavailable': '无法确定该文件夹的位置。请直接输入完整路径。',
'memorySources.folderPathPlaceholder': '/Users/you/notes',
'memorySources.globPatternPlaceholder': '**/*.md',
'memorySources.repoUrlPlaceholder': 'https://github.com/org/repo',