A new agent chat on another linked computer (as the web and Android can)
ci / ui (push) Waiting to run
ci / check (push) Failing after 3s
ci / start (push) Failing after 3s

Where the account has another computer linked (GET /devices), a chip before the folder
chooses it; this computer stays the default. Its folder is one its agent chats have worked
in, or a path typed in; the Terminal before the first message opens there too. Tests D4;
failing UI tests are also GitHub annotations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
HomerandClaude Opus 5.5 committed 2026-11-10 12:55:54 +00:00
1 parent 94e903ef70
commit 97c2301bab
5 files changed
+338 -15

No files matched your search

+64
View File
@@ -50,6 +50,7 @@ const LEMBAS_I18N = {
"Allow": "Povolit",
"Always": "Vždy",
"Always allow": "Vždy povolit",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Agentní konverzace pracuje ve složce na zařízení %(device)s přes tamního agenta LLeMbas.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Agentní konverzace pracuje ve složce na tomto počítači přes agenta vestavěného v této aplikaci.",
"Another device": "Jiné zařízení",
"Answer": "Odpovědět",
@@ -67,6 +68,9 @@ const LEMBAS_I18N = {
"Chat": "Konverzace",
"Chats": "Konverzace",
"Check for updates…": "Zkontrolovat aktualizace…",
"Choose": "Vybrat",
"Choose a folder first.": "Nejprve vyberte složku.",
"Choose a folder…": "Vybrat složku…",
"Choose a model to choose its effort.": "Vyberte model, abyste mohli zvolit jeho náročnost.",
"Close": "Zavřít",
"Close (LLeMbas stays in the tray)": "Zavřít (LLeMbas zůstane v oznamovací oblasti)",
@@ -80,6 +84,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Rezerva na zhuštění",
"Compacts before the next reply.": "Shrne se před další odpovědí.",
"Compacts by itself once the window is known.": "Shrne se samo, jakmile bude známé okno.",
"Computer": "Počítač",
"Connect": "Připojit",
"Connected.": "Připojeno.",
"Connected. This shell is also open in another tab, and they share a size.": "Připojeno. Tento shell je otevřený i na jiné kartě a sdílejí velikost.",
@@ -127,6 +132,7 @@ const LEMBAS_I18N = {
"Findings": "Zjištění",
"Finished %(n)s": "Dokončeno: %(n)s",
"Folder": "Složka",
"Folder on %(device)s": "Složka na zařízení %(device)s",
"Free space": "Volné místo",
"Hello": "Dobrý den",
"Hello, %(name)s": "Dobrý den, %(name)s",
@@ -302,6 +308,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Aplikace se k vašemu serveru přihlásí jednou, ve vašem prohlížeči, jako každé zařízení. Pak může číst a psát vaše konverzace, jako webové rozhraní.",
"The connection to the reply was lost: %(why)s": "Spojení s odpovědí se přerušilo: %(why)s",
"The first free scheduling model": "První volný model pro plánování",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "Úplná cesta ke složce na tom počítači. Jeho agent otevře jen složky, které dostal.",
"The instruction each run is given, on its own: say everything it needs.": "Instrukce, kterou dostane každé spuštění samostatně: uveďte vše, co potřebuje.",
"The last %(n)s runs failed: %(why)s": "Posledních %(n)s spuštění selhalo: %(why)s",
"The model's own": "Podle modelu",
@@ -340,6 +347,7 @@ const LEMBAS_I18N = {
"Tools": "Nástroje",
"Try again": "Zkusit znovu",
"Type a command or a chat's title": "Napište příkaz nebo název konverzace",
"Type a folder…": "Zadat složku…",
"Up to %(n)s at once": "Nejvýše %(n)s současně",
"Wait until the files are sent.": "Počkejte, až se soubory odešlou.",
"Waiting for a free model": "Čeká na volný model",
@@ -450,6 +458,7 @@ const LEMBAS_I18N = {
"Allow": "Erlauben",
"Always": "Immer",
"Always allow": "Immer erlauben",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Ein Agenten-Chat arbeitet in einem Ordner auf %(device)s, über den LLeMbas-Agenten dort.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Ein Agenten-Chat arbeitet in einem Ordner auf diesem Computer, über den in diese App eingebauten Agenten.",
"Another device": "Ein anderes Gerät",
"Answer": "Antworten",
@@ -467,6 +476,9 @@ const LEMBAS_I18N = {
"Chat": "Chat",
"Chats": "Chats",
"Check for updates…": "Nach Updates suchen…",
"Choose": "Auswählen",
"Choose a folder first.": "Wählen Sie zuerst einen Ordner.",
"Choose a folder…": "Ordner wählen…",
"Choose a model to choose its effort.": "Wählen Sie ein Modell, um seinen Aufwand zu wählen.",
"Close": "Schließen",
"Close (LLeMbas stays in the tray)": "Schließen (LLeMbas bleibt im Benachrichtigungsbereich)",
@@ -480,6 +492,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Puffer für die Verdichtung",
"Compacts before the next reply.": "Wird vor der nächsten Antwort zusammengefasst.",
"Compacts by itself once the window is known.": "Wird von selbst zusammengefasst, sobald das Fenster bekannt ist.",
"Computer": "Computer",
"Connect": "Verbinden",
"Connected.": "Verbunden.",
"Connected. This shell is also open in another tab, and they share a size.": "Verbunden. Diese Shell ist auch in einem anderen Tab offen, und beide teilen sich eine Größe.",
@@ -527,6 +540,7 @@ const LEMBAS_I18N = {
"Findings": "Erkenntnisse",
"Finished %(n)s": "Fertig: %(n)s",
"Folder": "Ordner",
"Folder on %(device)s": "Ordner auf %(device)s",
"Free space": "Freier Platz",
"Hello": "Hallo",
"Hello, %(name)s": "Hallo, %(name)s",
@@ -702,6 +716,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Die App meldet sich einmal an Ihrem Server an, in Ihrem Browser, wie jedes Gerät. Danach kann sie Ihre Chats lesen und schreiben, wie die Weboberfläche.",
"The connection to the reply was lost: %(why)s": "Die Verbindung zur Antwort ist abgebrochen: %(why)s",
"The first free scheduling model": "Das erste freie Planungsmodell",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "Der vollständige Pfad des Ordners auf diesem Computer. Sein Agent öffnet nur Ordner, die ihm freigegeben wurden.",
"The instruction each run is given, on its own: say everything it needs.": "Die Anweisung, die jeder Lauf für sich erhält: Sagen Sie alles, was er braucht.",
"The last %(n)s runs failed: %(why)s": "Die letzten %(n)s Läufe sind fehlgeschlagen: %(why)s",
"The model's own": "Die des Modells",
@@ -740,6 +755,7 @@ const LEMBAS_I18N = {
"Tools": "Werkzeuge",
"Try again": "Erneut versuchen",
"Type a command or a chat's title": "Befehl oder Chat-Titel eingeben",
"Type a folder…": "Ordner eingeben…",
"Up to %(n)s at once": "Bis zu %(n)s gleichzeitig",
"Wait until the files are sent.": "Warten Sie, bis die Dateien gesendet sind.",
"Waiting for a free model": "Wartet auf ein freies Modell",
@@ -850,6 +866,7 @@ const LEMBAS_I18N = {
"Allow": "Permitir",
"Always": "Siempre",
"Always allow": "Permitir siempre",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Un chat de agente trabaja en una carpeta de %(device)s, a través del agente LLeMbas de allí.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Un chat de agente trabaja en una carpeta de este equipo, a través del agente integrado en esta aplicación.",
"Another device": "Otro dispositivo",
"Answer": "Responder",
@@ -867,6 +884,9 @@ const LEMBAS_I18N = {
"Chat": "Chat",
"Chats": "Chats",
"Check for updates…": "Buscar actualizaciones…",
"Choose": "Elegir",
"Choose a folder first.": "Elija primero una carpeta.",
"Choose a folder…": "Elegir una carpeta…",
"Choose a model to choose its effort.": "Elija un modelo para elegir su esfuerzo.",
"Close": "Cerrar",
"Close (LLeMbas stays in the tray)": "Cerrar (LLeMbas sigue en la bandeja del sistema)",
@@ -880,6 +900,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Reserva para la compactación",
"Compacts before the next reply.": "Se compacta antes de la próxima respuesta.",
"Compacts by itself once the window is known.": "Se compacta sola en cuanto se conozca la ventana.",
"Computer": "Equipo",
"Connect": "Conectar",
"Connected.": "Conectado.",
"Connected. This shell is also open in another tab, and they share a size.": "Conectado. Este shell también está abierto en otra pestaña, y comparten tamaño.",
@@ -927,6 +948,7 @@ const LEMBAS_I18N = {
"Findings": "Hallazgos",
"Finished %(n)s": "Terminadas: %(n)s",
"Folder": "Carpeta",
"Folder on %(device)s": "Carpeta en %(device)s",
"Free space": "Espacio libre",
"Hello": "Hola",
"Hello, %(name)s": "Hola, %(name)s",
@@ -1102,6 +1124,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "La aplicación inicia sesión en su servidor una vez, en su navegador, como cualquier dispositivo. Después puede leer y escribir sus chats, como la interfaz web.",
"The connection to the reply was lost: %(why)s": "Se perdió la conexión con la respuesta: %(why)s",
"The first free scheduling model": "El primer modelo de programación libre",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "La ruta completa de la carpeta en ese equipo. Su agente solo abre las carpetas que se le han dado.",
"The instruction each run is given, on its own: say everything it needs.": "La instrucción que recibe cada ejecución por sí sola: diga todo lo que necesita.",
"The last %(n)s runs failed: %(why)s": "Las últimas %(n)s ejecuciones fallaron: %(why)s",
"The model's own": "La del modelo",
@@ -1140,6 +1163,7 @@ const LEMBAS_I18N = {
"Tools": "Herramientas",
"Try again": "Reintentar",
"Type a command or a chat's title": "Escriba un comando o el título de un chat",
"Type a folder…": "Escribir una carpeta…",
"Up to %(n)s at once": "Hasta %(n)s a la vez",
"Wait until the files are sent.": "Espere a que se envíen los archivos.",
"Waiting for a free model": "Esperando un modelo libre",
@@ -1250,6 +1274,7 @@ const LEMBAS_I18N = {
"Allow": "Autoriser",
"Always": "Toujours",
"Always allow": "Toujours autoriser",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Une discussion d'agent travaille dans un dossier de %(device)s, via l'agent LLeMbas qui s'y trouve.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Une discussion d'agent travaille dans un dossier de cet ordinateur, via l'agent intégré à cette application.",
"Another device": "Un autre appareil",
"Answer": "Répondre",
@@ -1267,6 +1292,9 @@ const LEMBAS_I18N = {
"Chat": "Conversation",
"Chats": "Conversations",
"Check for updates…": "Rechercher des mises à jour…",
"Choose": "Choisir",
"Choose a folder first.": "Choisissez d’abord un dossier.",
"Choose a folder…": "Choisir un dossier…",
"Choose a model to choose its effort.": "Choisissez un modèle pour choisir son effort.",
"Close": "Fermer",
"Close (LLeMbas stays in the tray)": "Fermer (LLeMbas reste dans la zone de notification)",
@@ -1280,6 +1308,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Réserve de compactage",
"Compacts before the next reply.": "Compacté avant la prochaine réponse.",
"Compacts by itself once the window is known.": "Compacté de lui-même dès que la fenêtre est connue.",
"Computer": "Ordinateur",
"Connect": "Se connecter",
"Connected.": "Connecté.",
"Connected. This shell is also open in another tab, and they share a size.": "Connecté. Ce shell est aussi ouvert dans un autre onglet, et ils partagent une taille.",
@@ -1327,6 +1356,7 @@ const LEMBAS_I18N = {
"Findings": "Constats",
"Finished %(n)s": "Terminées : %(n)s",
"Folder": "Dossier",
"Folder on %(device)s": "Dossier sur %(device)s",
"Free space": "Espace libre",
"Hello": "Bonjour",
"Hello, %(name)s": "Bonjour, %(name)s",
@@ -1502,6 +1532,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "L'application se connecte une fois à votre serveur, dans votre navigateur, comme tout appareil. Elle peut ensuite lire et écrire vos discussions, comme l'interface web.",
"The connection to the reply was lost: %(why)s": "La connexion à la réponse a été perdue : %(why)s",
"The first free scheduling model": "Le premier modèle de planification libre",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "Le chemin complet du dossier sur cet ordinateur. Son agent n’ouvre que les dossiers qui lui ont été confiés.",
"The instruction each run is given, on its own: say everything it needs.": "L'instruction donnée à chaque exécution, seule : dites tout ce dont elle a besoin.",
"The last %(n)s runs failed: %(why)s": "Les %(n)s dernières exécutions ont échoué : %(why)s",
"The model's own": "Celle du modèle",
@@ -1540,6 +1571,7 @@ const LEMBAS_I18N = {
"Tools": "Outils",
"Try again": "Réessayer",
"Type a command or a chat's title": "Tapez une commande ou le titre d'une conversation",
"Type a folder…": "Saisir un dossier…",
"Up to %(n)s at once": "Jusqu’à %(n)s à la fois",
"Wait until the files are sent.": "Attendez que les fichiers soient envoyés.",
"Waiting for a free model": "En attente d’un modèle libre",
@@ -1650,6 +1682,7 @@ const LEMBAS_I18N = {
"Allow": "Engedélyezés",
"Always": "Mindig",
"Always allow": "Mindig engedélyez",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Az ügynökcsevegés egy mappában dolgozik ezen: %(device)s, az ottani LLeMbas-ügynökön keresztül.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Az ügynökcsevegés egy mappában dolgozik ezen a számítógépen, az alkalmazásba épített ügynökön keresztül.",
"Another device": "Másik eszköz",
"Answer": "Válasz",
@@ -1667,6 +1700,9 @@ const LEMBAS_I18N = {
"Chat": "Csevegés",
"Chats": "Csevegések",
"Check for updates…": "Frissítések keresése…",
"Choose": "Kiválasztás",
"Choose a folder first.": "Először válasszon mappát.",
"Choose a folder…": "Mappa kiválasztása…",
"Choose a model to choose its effort.": "Válasszon modellt az erőfeszítés megadásához.",
"Close": "Bezárás",
"Close (LLeMbas stays in the tray)": "Bezárás (a LLeMbas a tálcán marad)",
@@ -1680,6 +1716,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Tömörítési tartalék",
"Compacts before the next reply.": "A következő válasz előtt tömörül.",
"Compacts by itself once the window is known.": "Magától tömörül, amint ismert az ablak.",
"Computer": "Számítógép",
"Connect": "Csatlakozás",
"Connected.": "Kapcsolódva.",
"Connected. This shell is also open in another tab, and they share a size.": "Kapcsolódva. Ez a shell egy másik lapon is nyitva van, és közös a méretük.",
@@ -1727,6 +1764,7 @@ const LEMBAS_I18N = {
"Findings": "Megállapítások",
"Finished %(n)s": "Kész: %(n)s",
"Folder": "Mappa",
"Folder on %(device)s": "Mappa ezen: %(device)s",
"Free space": "Szabad hely",
"Hello": "Üdv",
"Hello, %(name)s": "Üdv, %(name)s",
@@ -1902,6 +1940,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Az alkalmazás egyszer jelentkezik be a szerverére, a böngészőjében, mint bármely eszköz. Ezután olvashatja és írhatja a csevegéseit, mint a webes felület.",
"The connection to the reply was lost: %(why)s": "A kapcsolat a válasszal megszakadt: %(why)s",
"The first free scheduling model": "Az első szabad ütemezési modell",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "A mappa teljes elérési útja azon a számítógépen. Az ügynöke csak a neki megadott mappákat nyitja meg.",
"The instruction each run is given, on its own: say everything it needs.": "Az utasítás, amelyet minden futás önmagában kap: mondjon el mindent, amire szüksége van.",
"The last %(n)s runs failed: %(why)s": "Az utolsó %(n)s futás sikertelen: %(why)s",
"The model's own": "A modell sajátja",
@@ -1940,6 +1979,7 @@ const LEMBAS_I18N = {
"Tools": "Eszközök",
"Try again": "Újrapróbálás",
"Type a command or a chat's title": "Írjon be egy parancsot vagy egy csevegés címét",
"Type a folder…": "Mappa megadása…",
"Up to %(n)s at once": "Legfeljebb %(n)s egyszerre",
"Wait until the files are sent.": "Várjon, amíg a fájlok elküldődnek.",
"Waiting for a free model": "Szabad modellre vár",
@@ -2050,6 +2090,7 @@ const LEMBAS_I18N = {
"Allow": "Consenti",
"Always": "Sempre",
"Always allow": "Consenti sempre",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Una chat con agente lavora in una cartella su %(device)s, tramite l’agente LLeMbas che vi si trova.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Una chat con agente lavora in una cartella di questo computer, tramite l'agente integrato in questa app.",
"Another device": "Un altro dispositivo",
"Answer": "Rispondi",
@@ -2067,6 +2108,9 @@ const LEMBAS_I18N = {
"Chat": "Chat",
"Chats": "Chat",
"Check for updates…": "Cerca aggiornamenti…",
"Choose": "Scegli",
"Choose a folder first.": "Scegli prima una cartella.",
"Choose a folder…": "Scegli una cartella…",
"Choose a model to choose its effort.": "Scegli un modello per sceglierne l’impegno.",
"Close": "Chiudi",
"Close (LLeMbas stays in the tray)": "Chiudi (LLeMbas resta nell'area di notifica)",
@@ -2080,6 +2124,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Riserva per la compattazione",
"Compacts before the next reply.": "Si compatta prima della prossima risposta.",
"Compacts by itself once the window is known.": "Si compatta da sola appena la finestra è nota.",
"Computer": "Computer",
"Connect": "Connetti",
"Connected.": "Connesso.",
"Connected. This shell is also open in another tab, and they share a size.": "Connesso. Questa shell è aperta anche in un'altra scheda, e le due condividono la dimensione.",
@@ -2127,6 +2172,7 @@ const LEMBAS_I18N = {
"Findings": "Risultati",
"Finished %(n)s": "Terminate: %(n)s",
"Folder": "Cartella",
"Folder on %(device)s": "Cartella su %(device)s",
"Free space": "Spazio libero",
"Hello": "Ciao",
"Hello, %(name)s": "Ciao, %(name)s",
@@ -2302,6 +2348,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "L'app accede al suo server una volta, nel suo browser, come ogni dispositivo. Poi può leggere e scrivere le sue chat, come l'interfaccia web.",
"The connection to the reply was lost: %(why)s": "La connessione alla risposta si è interrotta: %(why)s",
"The first free scheduling model": "Il primo modello di pianificazione libero",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "Il percorso completo della cartella su quel computer. Il suo agente apre solo le cartelle che gli sono state date.",
"The instruction each run is given, on its own: say everything it needs.": "L’istruzione che ogni esecuzione riceve da sola: di’ tutto ciò che le serve.",
"The last %(n)s runs failed: %(why)s": "Le ultime %(n)s esecuzioni non sono riuscite: %(why)s",
"The model's own": "Quello del modello",
@@ -2340,6 +2387,7 @@ const LEMBAS_I18N = {
"Tools": "Strumenti",
"Try again": "Riprova",
"Type a command or a chat's title": "Digiti un comando o il titolo di una chat",
"Type a folder…": "Digita una cartella…",
"Up to %(n)s at once": "Fino a %(n)s alla volta",
"Wait until the files are sent.": "Attenda che i file siano inviati.",
"Waiting for a free model": "In attesa di un modello libero",
@@ -2450,6 +2498,7 @@ const LEMBAS_I18N = {
"Allow": "Pozwól",
"Always": "Zawsze",
"Always allow": "Zawsze zezwalaj",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Czat z agentem pracuje w folderze na %(device)s, przez działającego tam agenta LLeMbas.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Czat z agentem pracuje w folderze na tym komputerze, przez agenta wbudowanego w tę aplikację.",
"Another device": "Inne urządzenie",
"Answer": "Odpowiedź",
@@ -2467,6 +2516,9 @@ const LEMBAS_I18N = {
"Chat": "Czat",
"Chats": "Czaty",
"Check for updates…": "Sprawdź aktualizacje…",
"Choose": "Wybierz",
"Choose a folder first.": "Najpierw wybierz folder.",
"Choose a folder…": "Wybierz folder…",
"Choose a model to choose its effort.": "Wybierz model, aby wybrać jego wysiłek.",
"Close": "Zamknij",
"Close (LLeMbas stays in the tray)": "Zamknij (LLeMbas zostaje w zasobniku systemowym)",
@@ -2480,6 +2532,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Rezerwa na kompakcję",
"Compacts before the next reply.": "Zostanie streszczona przed następną odpowiedzią.",
"Compacts by itself once the window is known.": "Zostanie streszczona sama, gdy okno będzie znane.",
"Computer": "Komputer",
"Connect": "Połącz",
"Connected.": "Połączono.",
"Connected. This shell is also open in another tab, and they share a size.": "Połączono. Ta powłoka jest otwarta też w innej karcie i dzielą one rozmiar.",
@@ -2527,6 +2580,7 @@ const LEMBAS_I18N = {
"Findings": "Ustalenia",
"Finished %(n)s": "Zakończone: %(n)s",
"Folder": "Folder",
"Folder on %(device)s": "Folder na %(device)s",
"Free space": "Wolne miejsce",
"Hello": "Cześć",
"Hello, %(name)s": "Cześć, %(name)s",
@@ -2702,6 +2756,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Aplikacja loguje się do Twojego serwera raz, w przeglądarce, jak każde urządzenie. Potem może czytać i pisać Twoje czaty, jak interfejs WWW.",
"The connection to the reply was lost: %(why)s": "Połączenie z odpowiedzią zostało zerwane: %(why)s",
"The first free scheduling model": "Pierwszy wolny model do planowania",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "Pełna ścieżka folderu na tym komputerze. Jego agent otwiera tylko foldery, które mu udostępniono.",
"The instruction each run is given, on its own: say everything it needs.": "Instrukcja, którą każde uruchomienie dostaje samodzielnie: podaj wszystko, czego potrzebuje.",
"The last %(n)s runs failed: %(why)s": "Ostatnie uruchomienia nie powiodły się (%(n)s): %(why)s",
"The model's own": "Domyślne modelu",
@@ -2740,6 +2795,7 @@ const LEMBAS_I18N = {
"Tools": "Narzędzia",
"Try again": "Spróbuj ponownie",
"Type a command or a chat's title": "Wpisz polecenie lub tytuł czatu",
"Type a folder…": "Wpisz folder…",
"Up to %(n)s at once": "Do %(n)s naraz",
"Wait until the files are sent.": "Poczekaj, aż pliki zostaną wysłane.",
"Waiting for a free model": "Czeka na wolny model",
@@ -2850,6 +2906,7 @@ const LEMBAS_I18N = {
"Allow": "Povoliť",
"Always": "Vždy",
"Always allow": "Vždy povoliť",
"An agent chat works in a folder on %(device)s, through the LLeMbas agent there.": "Agentská konverzácia pracuje v priečinku na zariadení %(device)s cez tamojšieho agenta LLeMbas.",
"An agent chat works in a folder on this computer, through the agent built into this app.": "Agentská konverzácia pracuje v priečinku na tomto počítači cez agenta vstavaného v tejto aplikácii.",
"Another device": "Iné zariadenie",
"Answer": "Odpovedať",
@@ -2867,6 +2924,9 @@ const LEMBAS_I18N = {
"Chat": "Konverzácia",
"Chats": "Konverzácie",
"Check for updates…": "Skontrolovať aktualizácie…",
"Choose": "Vybrať",
"Choose a folder first.": "Najprv vyberte priečinok.",
"Choose a folder…": "Vybrať priečinok…",
"Choose a model to choose its effort.": "Vyberte model, aby ste mohli zvoliť jeho náročnosť.",
"Close": "Zavrieť",
"Close (LLeMbas stays in the tray)": "Zavrieť (LLeMbas zostane v oznamovacej oblasti)",
@@ -2880,6 +2940,7 @@ const LEMBAS_I18N = {
"Compaction buffer": "Rezerva na zhustenie",
"Compacts before the next reply.": "Zhrnie sa pred ďalšou odpoveďou.",
"Compacts by itself once the window is known.": "Zhrnie sa samo, keď bude okno známe.",
"Computer": "Počítač",
"Connect": "Pripojiť",
"Connected.": "Pripojené.",
"Connected. This shell is also open in another tab, and they share a size.": "Pripojené. Tento shell je otvorený aj v inej karte a zdieľajú veľkosť.",
@@ -2927,6 +2988,7 @@ const LEMBAS_I18N = {
"Findings": "Zistenia",
"Finished %(n)s": "Dokončené: %(n)s",
"Folder": "Priečinok",
"Folder on %(device)s": "Priečinok na zariadení %(device)s",
"Free space": "Voľné miesto",
"Hello": "Dobrý deň",
"Hello, %(name)s": "Dobrý deň, %(name)s",
@@ -3102,6 +3164,7 @@ const LEMBAS_I18N = {
"The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Aplikácia sa na váš server prihlási raz, vo vašom prehliadači, ako každé zariadenie. Potom môže čítať a písať vaše konverzácie, ako webové rozhranie.",
"The connection to the reply was lost: %(why)s": "Spojenie s odpoveďou sa prerušilo: %(why)s",
"The first free scheduling model": "Prvý voľný model na plánovanie",
"The folder's full path on that computer. Its agent opens only folders it has been given.": "Úplná cesta k priečinku na tom počítači. Jeho agent otvorí len priečinky, ktoré dostal.",
"The instruction each run is given, on its own: say everything it needs.": "Inštrukcia, ktorú dostane každé spustenie samostatne: uveďte všetko, čo potrebuje.",
"The last %(n)s runs failed: %(why)s": "Posledné spustenia zlyhali (%(n)s): %(why)s",
"The model's own": "Podľa modelu",
@@ -3140,6 +3203,7 @@ const LEMBAS_I18N = {
"Tools": "Nástroje",
"Try again": "Skúsiť znova",
"Type a command or a chat's title": "Napíšte príkaz alebo názov konverzácie",
"Type a folder…": "Zadať priečinok…",
"Up to %(n)s at once": "Najviac %(n)s naraz",
"Wait until the files are sent.": "Počkajte, kým sa súbory odošlú.",
"Waiting for a free model": "Čaká na voľný model",
+107 -15
View File
@@ -17,7 +17,7 @@ import { avatar, agentsControl, teamPossible, namedOnly, subagentsCap, noteSubag
import { readyIds, sending, sent, discard } from "./attach.js"
import { watchActivity, activityChanged, planList, activityButton, treeChanged } from "./activity.js"
import { agentBlock, agentReady, agentState, onAgentChange } from "./agent.js"
import { folderName } from "./list.js"
import { folderName, listed } from "./list.js"
import { unarchive } from "./archived.js"
import { isProgress, progressLine, progressTicks } from "./progress.js"
import { aimTerminal, terminalButton, terminalOpen, toggleTerminal } from "./terminal.js"
@@ -42,12 +42,46 @@ function deviceOf(id) {
return (ctx.devices?.() || []).find((d) => d.id === id) || null
}
/** The device a new agent chat goes to: this computer's, preferring the one linked right now. */
function draftDevice() {
/** This computer's device: the one linked right now, else the last one it was. */
function hereDevice() {
const mine = ctx.thisDevices()
return mine.find((id) => deviceOf(id)) || mine[0] || ""
}
/**
* The device a new agent chat goes to: the one chosen on its chip while it is still linked, else
* this computer's.
*/
function draftDevice() {
if (draft?.device && deviceOf(draft.device)) return draft.device
return hereDevice()
}
/** Whether the new agent chat works here (the agent built into this app) or on another device. */
function draftHere() {
const d = draftDevice()
return !d || ctx.thisDevices().includes(d)
}
/** The other linked devices of this account: where else a new agent chat can work. */
function otherDevices() {
const mine = ctx.thisDevices()
return (ctx.devices?.() || []).filter((d) => !mine.includes(d.id))
}
/**
* The folders an agent chat on another device has worked in: the sidebar's agent chats there,
* newest first. This app cannot browse another computer; these and a typed path are the choice.
*/
function knownFolders(device) {
const out = []
for (const c of listed()) {
const dir = c.kind === "agent" && c.device?.token_id === device ? c.device.directory : ""
if (dir && !out.includes(dir)) out.push(dir)
}
return out.slice(0, 8)
}
/**
* What the Terminal panel is for, as the page stands: a new agent chat whose device and folder
* are chosen (the shell before the first message), an open agent chat on a device that opens
@@ -58,7 +92,7 @@ function terminalTarget() {
if (draft.kind !== "agent" || !draft.dir) return null
const device = deviceOf(draftDevice())
if (!device?.terminal) return null
return { device: device.id, directory: draft.dir, name: device.name || t("This computer") }
return { device: device.id, directory: draft.dir, name: device.name || (draftHere() ? t("This computer") : t("Another device")) }
}
const chat = open?.chat
if (chat?.kind !== "agent" || !chat.device) return null
@@ -839,6 +873,51 @@ function folderChip(roots, value, onChange) {
})
}
/**
* Which computer a new agent chat works on: this one (the agent built into this app), or another
* linked device of the account, as `GET /devices` lists them. Shown only where there is another.
*/
function deviceChip(others) {
const here = draftHere()
const current = deviceOf(draftDevice())
const choose = (id) => {
if (id === draftDevice()) return
draft.device = id
const elsewhere = !ctx.thisDevices().includes(id)
draft.dir = elsewhere ? knownFolders(id)[0] || "" : ""
renderChips()
renderHello()
renderNotice()
}
const items = [{ label: t("This computer"), hint: ctx.status()?.host || "", checked: here, run: () => choose(hereDevice() || "") }]
for (const d of others) items.push({ label: d.name || d.host || d.id, hint: [d.host, d.platform].filter(Boolean).join(" · "), checked: !here && current?.id === d.id, run: () => choose(d.id) })
const label = here ? t("This computer") : current?.name || current?.host || t("Another device")
const b = chip({ ico: ICONS.terminal, label, title: `${t("Computer")}: ${label}`, cls: "cchip--device", onclick: (e) => menu(e.currentTarget, items) })
return b
}
/** The folder of a new agent chat on another device: one it has worked in, or one typed. */
function elsewhereFolderChip(device, value, onChange) {
const type = async () => {
const dir = await ask({ title: t("Folder on %(device)s", { device: deviceOf(device)?.name || t("Another device") }), text: t("The folder's full path on that computer. Its agent opens only folders it has been given."), value: value || "", ok: t("Choose"), cancel: t("Cancel") })
if (dir) onChange(dir)
}
const known = knownFolders(device)
if (!value && !known.length) return chip({ ico: ICONS.folder, label: t("Choose a folder…"), menuish: false, cls: "is-warn", onclick: type })
return chip({
ico: ICONS.folder,
label: value ? folderName(value) : t("Choose a folder…"),
title: value ? `${t("Folder")}: ${value}` : "",
cls: value ? "" : "is-warn",
onclick: (e) =>
menu(e.currentTarget, [
...known.map((r) => ({ label: folderName(r), hint: r, checked: r === value, run: () => r !== value && onChange(r) })),
known.length ? null : undefined,
{ label: t("Type a folder…"), run: type },
].filter((x) => x !== undefined)),
})
}
let chipsDrawn = 0 // which drawing is the latest: an older one's late answers are dropped
/** The chips for what the composer is writing to: a new chat (`draft`) or the open chat. */
@@ -852,9 +931,15 @@ async function renderChips() {
const model = models.find((m) => m.id === draft.model)
const parts = []
if (draft.kind === "agent") {
const roots = s?.remote?.enabled ? s.remote.roots : []
if (!roots.includes(draft.dir)) draft.dir = roots[0] || ""
parts.push(folderChip(roots, draft.dir, (d) => ((draft.dir = d), renderChips())))
const others = otherDevices()
if (others.length) parts.push(deviceChip(others))
if (draftHere()) {
const roots = s?.remote?.enabled ? s.remote.roots : []
if (!roots.includes(draft.dir)) draft.dir = roots[0] || ""
parts.push(folderChip(roots, draft.dir, (d) => ((draft.dir = d), renderChips())))
} else {
parts.push(elsewhereFolderChip(draftDevice(), draft.dir, (d) => ((draft.dir = d), renderChips())))
}
parts.push(modeChip(draft.mode, (m) => ((draft.mode = m), renderChips())))
}
const shell = terminalTarget()
@@ -923,7 +1008,7 @@ async function renderChips() {
function renderNotice() {
const box = $("link-notice")
const s = ctx.status()
const wants = draft ? draft.kind === "agent" : open?.chat?.kind === "agent" && onThisComputer(open.chat)
const wants = draft ? draft.kind === "agent" && draftHere() : open?.chat?.kind === "agent" && onThisComputer(open.chat)
const show = !!s && wants && !agentReady(s) && agentState(s).tone !== "busy"
box.hidden = !show
if (show) box.replaceChildren(agentBlock(s, { compact: !draft }))
@@ -1128,7 +1213,7 @@ export function closeChat() {
// --- A new chat ------------------------------------------------------------------------------------
let draft = null // { kind, dir, mode, model, effort, agents } while a new chat is being written
let draft = null // { kind, device, dir, mode, model, effort, agents } while a new chat is being written
let lastKind = "agent"
/** The kind a new chat starts as: the last one chosen (an agent chat, at first). */
@@ -1190,7 +1275,9 @@ function renderHello() {
h(
"p",
{ class: "hello__sub" },
kind === "agent"
kind === "agent" && !draftHere()
? t("An agent chat works in a folder on %(device)s, through the LLeMbas agent there.", { device: deviceOf(draftDevice())?.name || t("Another device") })
: kind === "agent"
? t("An agent chat works in a folder on this computer, through the agent built into this app.")
: t("A chat on %(server)s, with your account's models and files.", { server: server || t("Server") }),
),
@@ -1206,7 +1293,7 @@ export function newChat(kind) {
const models = ctx.models()
const start = models.find((m) => m.default) || models[0]
lastKind = kind || lastKind
draft = { kind: lastKind, dir: "", mode: "edit", model: start?.id || "", effort: levelsOf(start).start, agents: null }
draft = { kind: lastKind, device: "", dir: "", mode: "edit", model: start?.id || "", effort: levelsOf(start).start, agents: null }
clear($("messages"))
renderCards({ id: "" }, [])
alertIn("")
@@ -1233,11 +1320,16 @@ async function createChat(text, attachments) {
if (agents && (agents.min || agents.max)) body.agents = agents
if (draft.kind === "agent") {
const device = draftDevice()
if (!device || !agentReady(ctx.status())) {
renderNotice()
throw { detail: agentState(ctx.status()).sentence }
if (draftHere()) {
if (!device || !agentReady(ctx.status())) {
renderNotice()
throw { detail: agentState(ctx.status()).sentence }
}
if (!draft.dir) throw { detail: t("No folder yet: add one under Settings, Project folders.") }
} else {
if (!deviceOf(device)) throw { detail: t("This device is not linked right now.") }
if (!draft.dir) throw { detail: t("Choose a folder first.") }
}
if (!draft.dir) throw { detail: t("No folder yet: add one under Settings, Project folders.") }
Object.assign(body, { kind: "agent", device, directory: draft.dir, mode: draft.mode || "edit" })
}
const r = await post("/chats", body)