mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 02:06:43 +00:00
feat: ask_user follow-ups — harness convergence, channel finalization + reactions, native cards, docked web panel (#110372)
* feat(ask-user): follow-up harness slice * feat(ask-user): follow-up channels slice * feat(ask-user): follow-up native slice * feat(ui): dock question panel above composer with stepper and compact stream summaries * docs: refresh follow-up integration maps * test(ui): align terminal summary proof * fix(infra): echo declared option answers in terminal status when free-text is allowed * fix(infra): keep reaction answering when display labels are formatter-adjusted * fix(agents): settle plain-text claims only after question registration commits * fix(agents,apps): commit-ordered claim persistence, claim-aware prompt delivery, non-blocking question refresh * fix(harness,infra): reaction-appropriate question copy, caller presentations honored * fix(native,infra,agents): local-expiry eviction, value-addressed reactions, reserve-before-request * fix(infra,android): dual-mode question resolver for compact callbacks; reset terminal retention on replayed pending * fix(harness,discord): claim-aware prompt delivery in run helper; escape finalization labels * fix(macos): merge transient-content visibility with question cards after main sync * fix: repair ask user follow-up CI * test: update limited bootstrap scope expectation * fix: retain shared question card API * chore: refresh native i18n inventory
This commit is contained in:
@@ -5409,6 +5409,22 @@
|
||||
"未激活"
|
||||
]
|
||||
},
|
||||
{
|
||||
"locale": "es",
|
||||
"source": "Not selected",
|
||||
"translations": [
|
||||
"No seleccionada",
|
||||
"No seleccionado"
|
||||
]
|
||||
},
|
||||
{
|
||||
"locale": "sv",
|
||||
"source": "Not selected",
|
||||
"translations": [
|
||||
"Inte vald",
|
||||
"Inte valt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"locale": "de",
|
||||
"source": "Notifications",
|
||||
@@ -7584,6 +7600,30 @@
|
||||
"Захищено (TLS)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"locale": "es",
|
||||
"source": "Selected",
|
||||
"translations": [
|
||||
"Seleccionada",
|
||||
"Seleccionado"
|
||||
]
|
||||
},
|
||||
{
|
||||
"locale": "fa",
|
||||
"source": "Selected",
|
||||
"translations": [
|
||||
"انتخاب شده",
|
||||
"انتخابشده"
|
||||
]
|
||||
},
|
||||
{
|
||||
"locale": "sv",
|
||||
"source": "Selected",
|
||||
"translations": [
|
||||
"Vald",
|
||||
"Valt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"locale": "tr",
|
||||
"source": "Selected",
|
||||
|
||||
+371
-235
File diff suppressed because it is too large
Load Diff
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "مرفق غير مدعوم"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "إجابة أخرى"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "إرسال"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "تمت الإجابة"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "تمت الإجابة في مكان آخر"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "انتهت الصلاحية"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "تم الإلغاء"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "قيد الانتظار"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "جارٍ الإرسال…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "تنتهي الصلاحية خلال ${seconds / 60}د ${seconds % 60}ث"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "تنتهي الصلاحية خلال ${seconds}ث"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "توسيع الخطة"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "إجابة أخرى"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "غير محدد"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "محدد"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "إرسال"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "جارٍ الإرسال…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "تنتهي الصلاحية خلال \\(seconds / 60)د \\(seconds % 60)ث"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "تنتهي الصلاحية خلال \\(seconds)ث"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Nicht unterstützter Anhang"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Andere Antwort"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Absenden"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Beantwortet"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Anderswo beantwortet"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Abgelaufen"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Abgebrochen"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Ausstehend"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Wird gesendet…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Läuft in ${seconds / 60}m ${seconds % 60}s ab"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Läuft in ${seconds}s ab"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Plan ausklappen"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Andere Antwort"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Nicht ausgewählt"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Ausgewählt"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Absenden"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Wird gesendet…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Läuft in \\(seconds / 60)m \\(seconds % 60)s ab"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Läuft in \\(seconds)s ab"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Adjunto no compatible"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Otra respuesta"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Enviar"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Respondida"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Respondida en otro lugar"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Caducada"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Cancelada"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Pendiente"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Enviando…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Caduca en ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Caduca en ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Expandir el plan"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Otra respuesta"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "No seleccionada"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Seleccionada"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Enviar"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Enviando…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Caduca en \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Caduca en \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "پیوست پشتیبانینشده"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "پاسخ دیگر"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "ارسال"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "پاسخ داده شده"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "در جای دیگری پاسخ داده شده"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "منقضی شده"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "لغو شده"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "در انتظار"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "در حال ارسال…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "انقضا تا ${seconds / 60}m ${seconds % 60}s دیگر"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "انقضا تا ${seconds}s دیگر"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "بازکردن برنامه"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "پاسخ دیگر"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "انتخاب نشده"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "انتخاب شده"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "ارسال"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "در حال ارسال…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "انقضا تا \\(seconds / 60)m \\(seconds % 60)s دیگر"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "انقضا تا \\(seconds)s دیگر"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Pièce jointe non prise en charge"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Autre réponse"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Envoyer"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Répondu"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Répondu ailleurs"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Expiré"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Annulé"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "En attente"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Envoi…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Expire dans ${seconds / 60} min ${seconds % 60} s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Expire dans ${seconds} s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Développer le plan"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Autre réponse"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Non sélectionné"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Sélectionné"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Envoyer"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Envoi…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Expire dans \\(seconds / 60) min \\(seconds % 60) s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Expire dans \\(seconds) s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "असमर्थित अटैचमेंट"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "अन्य उत्तर"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "सबमिट करें"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "उत्तर दिया गया"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "कहीं और उत्तर दिया गया"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "समय समाप्त"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "रद्द किया गया"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "लंबित"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "सबमिट किया जा रहा है…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "${seconds / 60}m ${seconds % 60}s में समाप्त होगा"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "${seconds}s में समाप्त होगा"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "योजना विस्तृत करें"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "अन्य उत्तर"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "चयनित नहीं"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "चयनित"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "सबमिट करें"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "सबमिट किया जा रहा है…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "\\(seconds / 60)m \\(seconds % 60)s में समाप्त होगा"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "\\(seconds)s में समाप्त होगा"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Lampiran tidak didukung"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Jawaban lain"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Kirim"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Sudah dijawab"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Dijawab di tempat lain"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Kedaluwarsa"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Dibatalkan"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Menunggu"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Mengirim…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Kedaluwarsa dalam ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Kedaluwarsa dalam ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Luaskan rencana"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Jawaban lain"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Tidak dipilih"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Dipilih"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Kirim"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Mengirim…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Kedaluwarsa dalam \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Kedaluwarsa dalam \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Allegato non supportato"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Altra risposta"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Invia"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Risposto"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Risposto altrove"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Scaduto"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Annullato"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "In attesa"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Invio in corso…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Scade tra ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Scade tra ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Espandi il piano"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Altra risposta"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Non selezionato"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Selezionato"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Invia"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Invio in corso…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Scade tra \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Scade tra \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "サポートされていない添付ファイル"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "その他の回答"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "送信"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "回答済み"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "他で回答済み"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "期限切れ"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "キャンセル済み"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "保留中"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "送信中…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "期限切れまで${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "期限切れまで${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "計画を展開"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "その他の回答"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "未選択"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "選択済み"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "送信"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "送信中…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "期限切れまで\\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "期限切れまで\\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "지원되지 않는 첨부 파일"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "기타 답변"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "제출"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "응답 완료"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "다른 곳에서 응답 완료"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "만료됨"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "취소됨"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "대기 중"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "제출 중…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "${seconds / 60}분 ${seconds % 60}초 후 만료"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "${seconds}초 후 만료"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "계획 펼치기"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "기타 답변"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "선택되지 않음"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "선택됨"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "제출"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "제출 중…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "\\(seconds / 60)분 \\(seconds % 60)초 후 만료"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "\\(seconds)초 후 만료"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Niet-ondersteunde bijlage"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Ander antwoord"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Indienen"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Beantwoord"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Elders beantwoord"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Verlopen"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Geannuleerd"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "In afwachting"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Bezig met indienen…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Verloopt over ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Verloopt over ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Plan uitklappen"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Ander antwoord"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Niet geselecteerd"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Geselecteerd"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Indienen"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Bezig met indienen…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Verloopt over \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Verloopt over \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Nieobsługiwany załącznik"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Inna odpowiedź"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Prześlij"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Udzielono odpowiedzi"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Odpowiedziano gdzie indziej"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Wygasło"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Anulowano"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Oczekuje"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Przesyłanie…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Wygasa za ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Wygasa za ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Rozwiń plan"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Inna odpowiedź"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Nie wybrano"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Wybrano"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Prześlij"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Przesyłanie…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Wygasa za \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Wygasa za \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Anexo não compatível"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Outra resposta"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Enviar"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Respondido"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Respondido em outro lugar"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Expirado"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Cancelado"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Pendente"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Enviando…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Expira em ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Expira em ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Expandir plano"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Outra resposta"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Não selecionado"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Selecionado"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Enviar"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Enviando…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Expira em \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Expira em \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Неподдерживаемое вложение"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Другой ответ"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Отправить"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Отвечено"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Отвечено в другом месте"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Срок истёк"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Отменено"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Ожидает ответа"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Отправка…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Истекает через ${seconds / 60} мин ${seconds % 60} с"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Истекает через ${seconds} с"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Развернуть план"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Другой ответ"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Не выбрано"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Выбрано"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Отправить"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Отправка…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Истекает через \\(seconds / 60) мин \\(seconds % 60) с"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Истекает через \\(seconds) с"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Bilaga stöds inte"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Annat svar"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Skicka"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Besvarad"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Besvarad på annat håll"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Utgången"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Avbruten"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Väntande"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Skickar…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Upphör om ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Upphör om ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Visa plan"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Annat svar"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Inte valt"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Valt"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Skicka"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Skickar…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Upphör om \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Upphör om \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "ไฟล์แนบที่ไม่รองรับ"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "คำตอบอื่น"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "ส่ง"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "ตอบแล้ว"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "ตอบจากที่อื่นแล้ว"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "หมดอายุแล้ว"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "ยกเลิกแล้ว"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "รอดำเนินการ"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "กำลังส่ง…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "หมดอายุใน ${seconds / 60} นาที ${seconds % 60} วินาที"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "หมดอายุใน ${seconds} วินาที"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "ขยายแผน"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "คำตอบอื่น"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "ไม่ได้เลือก"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "เลือกแล้ว"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "ส่ง"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "กำลังส่ง…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "หมดอายุใน \\(seconds / 60) นาที \\(seconds % 60) วินาที"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "หมดอายุใน \\(seconds) วินาที"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Desteklenmeyen ek"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Diğer yanıt"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Gönder"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Yanıtlandı"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Başka yerde yanıtlandı"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Süresi doldu"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "İptal edildi"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Bekliyor"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Gönderiliyor…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Sona ermesine ${seconds / 60} dk ${seconds % 60} sn"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Sona ermesine ${seconds} sn"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Planı genişlet"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Diğer yanıt"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Seçilmedi"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Seçildi"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Gönder"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Gönderiliyor…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Sona ermesine \\(seconds / 60) dk \\(seconds % 60) sn"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Sona ermesine \\(seconds) sn"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Непідтримуване вкладення"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Інша відповідь"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Надіслати"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Відповідь надано"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Відповідь надано в іншому місці"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Термін дії минув"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Скасовано"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Очікує"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Надсилання…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Залишилося ${seconds / 60} хв ${seconds % 60} с"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Залишилося ${seconds} с"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Розгорнути план"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Інша відповідь"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Не вибрано"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Вибрано"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Надіслати"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Надсилання…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Залишилося \\(seconds / 60) хв \\(seconds % 60) с"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Залишилося \\(seconds) с"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "Tệp đính kèm không được hỗ trợ"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "Câu trả lời khác"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "Gửi"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "Đã trả lời"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "Đã trả lời ở nơi khác"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "Đã hết hạn"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "Đã hủy"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "Đang chờ"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "Đang gửi…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "Hết hạn sau ${seconds / 60}m ${seconds % 60}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "Hết hạn sau ${seconds}s"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "Mở rộng kế hoạch"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "Câu trả lời khác"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "Chưa chọn"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "Đã chọn"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "Gửi"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "Đang gửi…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "Hết hạn sau \\(seconds / 60)m \\(seconds % 60)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "Hết hạn sau \\(seconds)s"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "不支持的附件"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "其他答案"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "提交"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "已回答"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "已在其他地方回答"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "已过期"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "已取消"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "待处理"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "正在提交…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "${seconds / 60}分${seconds % 60}秒后过期"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "${seconds}秒后过期"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "展开计划"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "其他答案"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "未选择"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "已选择"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "提交"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "正在提交…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "\\(seconds / 60)分\\(seconds % 60)秒后过期"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "\\(seconds)秒后过期"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7343,6 +7343,56 @@
|
||||
"source": "Unsupported attachment",
|
||||
"translated": "不支援的附件"
|
||||
},
|
||||
{
|
||||
"id": "native.android.bf40f9f9a508155d",
|
||||
"source": "Other answer",
|
||||
"translated": "其他答案"
|
||||
},
|
||||
{
|
||||
"id": "native.android.fa81739f52e7697f",
|
||||
"source": "Submit",
|
||||
"translated": "提交"
|
||||
},
|
||||
{
|
||||
"id": "native.android.f68cd7a84be4089e",
|
||||
"source": "Answered",
|
||||
"translated": "已回答"
|
||||
},
|
||||
{
|
||||
"id": "native.android.a0dc883947c2f980",
|
||||
"source": "Answered elsewhere",
|
||||
"translated": "已在其他地方回答"
|
||||
},
|
||||
{
|
||||
"id": "native.android.1a0e6b33059a63aa",
|
||||
"source": "Expired",
|
||||
"translated": "已過期"
|
||||
},
|
||||
{
|
||||
"id": "native.android.d6d8fc83cec68418",
|
||||
"source": "Cancelled",
|
||||
"translated": "已取消"
|
||||
},
|
||||
{
|
||||
"id": "native.android.73f64d0d54b7f9c0",
|
||||
"source": "Pending",
|
||||
"translated": "待處理"
|
||||
},
|
||||
{
|
||||
"id": "native.android.92dfa7979d04989c",
|
||||
"source": "Submitting…",
|
||||
"translated": "正在提交…"
|
||||
},
|
||||
{
|
||||
"id": "native.android.c5e9ee99f9d9c612",
|
||||
"source": "Expires in ${seconds / 60}m ${seconds % 60}s",
|
||||
"translated": "將於 ${seconds / 60}m ${seconds % 60}s 後過期"
|
||||
},
|
||||
{
|
||||
"id": "native.android.2ca602fbdf96f1e7",
|
||||
"source": "Expires in ${seconds}s",
|
||||
"translated": "將於 ${seconds}s 後過期"
|
||||
},
|
||||
{
|
||||
"id": "native.android.118e8d239b18895e",
|
||||
"source": "Model",
|
||||
@@ -22913,6 +22963,41 @@
|
||||
"source": "Expand plan",
|
||||
"translated": "展開計畫"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.da4494f9e875169d",
|
||||
"source": "Other answer",
|
||||
"translated": "其他答案"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e9c3855562d8e4e9",
|
||||
"source": "Not selected",
|
||||
"translated": "未選取"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.bd4335213464d4be",
|
||||
"source": "Selected",
|
||||
"translated": "已選取"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.e3095b651fc46b52",
|
||||
"source": "Submit",
|
||||
"translated": "提交"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.6c1adc16e0a846e5",
|
||||
"source": "Submitting…",
|
||||
"translated": "正在提交…"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.698bdb0d6537edb6",
|
||||
"source": "Expires in \\(seconds / 60)m \\(seconds % 60)s",
|
||||
"translated": "將於 \\(seconds / 60)m \\(seconds % 60)s 後過期"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.30e1c342b3345290",
|
||||
"source": "Expires in \\(seconds)s",
|
||||
"translated": "將於 \\(seconds)s 後過期"
|
||||
},
|
||||
{
|
||||
"id": "native.apple.7cd6fb01b9f5d14a",
|
||||
"source": "User",
|
||||
|
||||
@@ -7,6 +7,7 @@ import ai.openclaw.app.chat.ChatMessage
|
||||
import ai.openclaw.app.chat.ChatOutboxItem
|
||||
import ai.openclaw.app.chat.ChatPendingToolCall
|
||||
import ai.openclaw.app.chat.ChatPlanStep
|
||||
import ai.openclaw.app.chat.ChatQuestionPrompt
|
||||
import ai.openclaw.app.chat.ChatSessionEntry
|
||||
import ai.openclaw.app.chat.ChatThinkingLevelSelection
|
||||
import ai.openclaw.app.chat.ChatWidgetResource
|
||||
@@ -614,6 +615,7 @@ class MainViewModel private constructor(
|
||||
val chatModelCatalog: StateFlow<List<GatewayModelSummary>> = runtimeState(initial = emptyList()) { it.chatModelCatalog }
|
||||
val chatStreamingAssistantText: StateFlow<String?> = runtimeState(initial = null) { it.chatStreamingAssistantText }
|
||||
val chatPendingToolCalls: StateFlow<List<ChatPendingToolCall>> = runtimeState(initial = emptyList()) { it.chatPendingToolCalls }
|
||||
val chatQuestions: StateFlow<List<ChatQuestionPrompt>> = runtimeState(initial = emptyList()) { it.chatQuestions }
|
||||
val chatPlanSteps: StateFlow<List<ChatPlanStep>> = runtimeState(initial = emptyList()) { it.chatPlanSteps }
|
||||
val chatSessions: StateFlow<List<ChatSessionEntry>> = runtimeState(initial = emptyList()) { it.chatSessions }
|
||||
val pendingRunCount: StateFlow<Int> = runtimeState(initial = 0) { it.pendingRunCount }
|
||||
@@ -1648,6 +1650,13 @@ class MainViewModel private constructor(
|
||||
ensureRuntime().deleteChatOutboxCommand(id)
|
||||
}
|
||||
|
||||
fun resolveChatQuestion(
|
||||
id: String,
|
||||
answers: Map<String, List<String>>,
|
||||
) {
|
||||
ensureRuntime().resolveChatQuestion(id, answers)
|
||||
}
|
||||
|
||||
suspend fun listBackgroundTasks(agentId: String): List<BackgroundTask> = ensureRuntime().listBackgroundTasks(agentId)
|
||||
|
||||
suspend fun getBackgroundTask(taskId: String): BackgroundTask = ensureRuntime().getBackgroundTask(taskId)
|
||||
|
||||
@@ -11,6 +11,7 @@ import ai.openclaw.app.chat.ChatMessage
|
||||
import ai.openclaw.app.chat.ChatOutboxItem
|
||||
import ai.openclaw.app.chat.ChatPendingToolCall
|
||||
import ai.openclaw.app.chat.ChatPlanStep
|
||||
import ai.openclaw.app.chat.ChatQuestionPrompt
|
||||
import ai.openclaw.app.chat.ChatSessionDeletion
|
||||
import ai.openclaw.app.chat.ChatSessionEntry
|
||||
import ai.openclaw.app.chat.ChatThinkingLevelSelection
|
||||
@@ -2412,6 +2413,7 @@ class NodeRuntime private constructor(
|
||||
val chatModelCatalog: StateFlow<List<GatewayModelSummary>> = chat.modelCatalog
|
||||
val chatStreamingAssistantText: StateFlow<String?> = chat.streamingAssistantText
|
||||
val chatPendingToolCalls: StateFlow<List<ChatPendingToolCall>> = chat.pendingToolCalls
|
||||
val chatQuestions: StateFlow<List<ChatQuestionPrompt>> = chat.questions
|
||||
val chatPlanSteps: StateFlow<List<ChatPlanStep>> = chat.planSteps
|
||||
val chatSessions: StateFlow<List<ChatSessionEntry>> = chat.sessions
|
||||
val pendingRunCount: StateFlow<Int> = chat.pendingRunCount
|
||||
@@ -2426,6 +2428,11 @@ class NodeRuntime private constructor(
|
||||
|
||||
fun deleteChatOutboxCommand(id: String) = chat.deleteOutboxCommand(id)
|
||||
|
||||
fun resolveChatQuestion(
|
||||
id: String,
|
||||
answers: Map<String, List<String>>,
|
||||
) = chat.resolveQuestion(id, answers)
|
||||
|
||||
private fun applyScreenshotFixture() {
|
||||
check(BuildConfig.DEBUG) { "Android screenshot fixtures require a debug build" }
|
||||
_serverName.value = "OpenClaw Gateway"
|
||||
|
||||
@@ -5,6 +5,8 @@ import ai.openclaw.app.gateway.GatewayRequestDefinitiveFailure
|
||||
import ai.openclaw.app.gateway.GatewayRequestNotEnqueued
|
||||
import ai.openclaw.app.gateway.GatewayRequestOutcomeUnknown
|
||||
import ai.openclaw.app.gateway.GatewaySession
|
||||
import ai.openclaw.app.gateway.QuestionListResult
|
||||
import ai.openclaw.app.gateway.QuestionRecord
|
||||
import ai.openclaw.app.gateway.parseChatSendAck
|
||||
import ai.openclaw.app.i18n.NativeText
|
||||
import ai.openclaw.app.i18n.nativeText
|
||||
@@ -26,6 +28,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
@@ -250,6 +253,19 @@ class ChatController internal constructor(
|
||||
private val _pendingToolCalls = MutableStateFlow<List<ChatPendingToolCall>>(emptyList())
|
||||
val pendingToolCalls: StateFlow<List<ChatPendingToolCall>> = _pendingToolCalls.asStateFlow()
|
||||
|
||||
private val _questions = MutableStateFlow<List<ChatQuestionPrompt>>(emptyList())
|
||||
val questions: StateFlow<List<ChatQuestionPrompt>> = _questions.asStateFlow()
|
||||
private val questionStateLock = Any()
|
||||
private var questionStateRevision = 0L
|
||||
private var questionRefreshGeneration = 0L
|
||||
|
||||
private data class QuestionEvictionJob(
|
||||
val job: Job,
|
||||
val observedAtMs: Long?,
|
||||
)
|
||||
|
||||
private val questionEvictionJobs = mutableMapOf<String, QuestionEvictionJob>()
|
||||
|
||||
private val _planSteps = MutableStateFlow<List<ChatPlanStep>>(emptyList())
|
||||
val planSteps: StateFlow<List<ChatPlanStep>> = _planSteps.asStateFlow()
|
||||
|
||||
@@ -517,6 +533,7 @@ class ChatController internal constructor(
|
||||
}
|
||||
|
||||
private fun refreshConnectedGateway() {
|
||||
refreshQuestions()
|
||||
if (!restoreRunStateOnReconnect) {
|
||||
refresh()
|
||||
return
|
||||
@@ -546,6 +563,7 @@ class ChatController internal constructor(
|
||||
)
|
||||
clearLiveHistoryMarker()
|
||||
_sessions.value = emptyList()
|
||||
clearQuestions()
|
||||
applyThinkingMetadata(null)
|
||||
sessionsListArchived = false
|
||||
unreadPatchSessionKey = null
|
||||
@@ -2030,6 +2048,7 @@ class ChatController internal constructor(
|
||||
}
|
||||
}
|
||||
"health" -> {
|
||||
refreshQuestions()
|
||||
if (restoreRunStateOnReconnect) {
|
||||
refreshHistoryForRecovery(forceHealth = true, completesReconnectRecovery = true)
|
||||
} else {
|
||||
@@ -2043,6 +2062,7 @@ class ChatController internal constructor(
|
||||
pendingToolCallsById.clear()
|
||||
publishPendingToolCalls()
|
||||
_streamingAssistantText.value = null
|
||||
refreshQuestions()
|
||||
refreshHistoryForRecovery()
|
||||
}
|
||||
"chat" -> {
|
||||
@@ -2064,6 +2084,250 @@ class ChatController internal constructor(
|
||||
if (payloadJson.isNullOrBlank()) return
|
||||
handleAgentEvent(payloadJson)
|
||||
}
|
||||
"question.requested" -> {
|
||||
if (payloadJson.isNullOrBlank()) return
|
||||
handleQuestionRequested(payloadJson)
|
||||
}
|
||||
"question.resolved" -> {
|
||||
if (payloadJson.isNullOrBlank()) return
|
||||
handleQuestionResolved(payloadJson)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resolveQuestion(
|
||||
id: String,
|
||||
answers: Map<String, List<String>>,
|
||||
) {
|
||||
val gatewayId = currentCacheScope()?.gatewayId
|
||||
updateQuestions { prompts ->
|
||||
prompts.map { prompt ->
|
||||
if (prompt.record.id == id) prompt.copy(submitting = true, errorText = null) else prompt
|
||||
}
|
||||
}
|
||||
scope.launch {
|
||||
try {
|
||||
val params =
|
||||
buildJsonObject {
|
||||
put("id", JsonPrimitive(id))
|
||||
put(
|
||||
"answers",
|
||||
buildJsonObject {
|
||||
put(
|
||||
"answers",
|
||||
buildJsonObject {
|
||||
answers.forEach { (questionId, values) ->
|
||||
put(questionId, buildJsonObject { put("answers", JsonArray(values.map(::JsonPrimitive))) })
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
requestGatewayBound(gatewayId, "question.resolve", params.toString())
|
||||
updateQuestions { prompts ->
|
||||
prompts.map { prompt ->
|
||||
if (prompt.record.id == id) {
|
||||
prompt.copy(
|
||||
record = prompt.record.copy(status = "answered"),
|
||||
submitting = false,
|
||||
answeredLocally = true,
|
||||
terminalObservedAtMs = prompt.terminalObservedAtMs ?: System.currentTimeMillis(),
|
||||
)
|
||||
} else {
|
||||
prompt
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: Throwable) {
|
||||
updateQuestions { prompts ->
|
||||
prompts.map { prompt ->
|
||||
if (prompt.record.id == id) prompt.copy(submitting = false, errorText = error.message ?: "Question failed") else prompt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshQuestions() {
|
||||
val gatewayScope = currentCacheScope()
|
||||
val (refreshGeneration, stateRevision) =
|
||||
synchronized(questionStateLock) {
|
||||
questionRefreshGeneration += 1
|
||||
questionRefreshGeneration to questionStateRevision
|
||||
}
|
||||
scope.launch {
|
||||
try {
|
||||
val response = requestGatewayBound(gatewayScope?.gatewayId, "question.list", "{}")
|
||||
if (gatewayScope != currentCacheScope()) return@launch
|
||||
val records = json.decodeFromString<QuestionListResult>(response).questions
|
||||
synchronized(questionStateLock) {
|
||||
if (refreshGeneration != questionRefreshGeneration || stateRevision != questionStateRevision) {
|
||||
return@synchronized
|
||||
}
|
||||
val current = _questions.value
|
||||
val existing = current.associateBy { it.record.id }
|
||||
val nowMs = System.currentTimeMillis()
|
||||
val listedIds = records.mapTo(mutableSetOf()) { it.id }
|
||||
val retainedTerminal = current.filter { it.record.id !in listedIds && it.shouldRetainAfterList(nowMs) }
|
||||
val next =
|
||||
records.map { record ->
|
||||
existing[record.id]?.let { prompt ->
|
||||
prompt.copy(
|
||||
record = record,
|
||||
submitting = prompt.submitting && record.status == "pending",
|
||||
terminalObservedAtMs =
|
||||
if (record.status == "pending" && nowMs < record.expiresAtMs) {
|
||||
null
|
||||
} else {
|
||||
prompt.terminalObservedAtMs ?: nowMs
|
||||
},
|
||||
)
|
||||
} ?: ChatQuestionPrompt(
|
||||
record = record,
|
||||
terminalObservedAtMs = nowMs.takeIf { record.status != "pending" },
|
||||
)
|
||||
} + retainedTerminal
|
||||
if (next != current) {
|
||||
_questions.value = next
|
||||
questionStateRevision += 1
|
||||
}
|
||||
syncQuestionEvictionsLocked()
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
// Older gateways and tokens without operator.questions keep ordinary chat usable.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleQuestionRequested(payloadJson: String) {
|
||||
val record = runCatching { json.decodeFromString<QuestionRecord>(payloadJson) }.getOrNull() ?: return
|
||||
updateQuestions { prompts ->
|
||||
if (prompts.any { it.record.id == record.id }) {
|
||||
prompts.map { prompt ->
|
||||
if (prompt.record.id == record.id) {
|
||||
prompt.copy(
|
||||
record = record,
|
||||
submitting = prompt.submitting && record.status == "pending",
|
||||
// A replayed pending event must drop stale terminal retention or
|
||||
// the zero-delay eviction loop respins without ever removing it.
|
||||
terminalObservedAtMs =
|
||||
if (record.status == "pending") null else prompt.terminalObservedAtMs,
|
||||
)
|
||||
} else {
|
||||
prompt
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prompts + ChatQuestionPrompt(record)
|
||||
}
|
||||
}
|
||||
refreshQuestions()
|
||||
}
|
||||
|
||||
private fun handleQuestionResolved(payloadJson: String) {
|
||||
val payload = runCatching { json.parseToJsonElement(payloadJson).jsonObject }.getOrNull() ?: return
|
||||
val id = payload["id"].asStringOrNull() ?: return
|
||||
val status = payload["status"].asStringOrNull() ?: return
|
||||
val nowMs = System.currentTimeMillis()
|
||||
updateQuestions { prompts ->
|
||||
prompts.map { prompt ->
|
||||
if (prompt.record.id == id) {
|
||||
prompt.copy(
|
||||
record = prompt.record.copy(status = status),
|
||||
submitting = false,
|
||||
terminalObservedAtMs = prompt.terminalObservedAtMs ?: nowMs,
|
||||
)
|
||||
} else {
|
||||
prompt
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshQuestions()
|
||||
}
|
||||
|
||||
private fun updateQuestions(transform: (List<ChatQuestionPrompt>) -> List<ChatQuestionPrompt>) {
|
||||
synchronized(questionStateLock) {
|
||||
val current = _questions.value
|
||||
val next = transform(current)
|
||||
if (next == current) return
|
||||
_questions.value = next
|
||||
questionStateRevision += 1
|
||||
syncQuestionEvictionsLocked()
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncQuestionEvictionsLocked(nowMs: Long = System.currentTimeMillis()) {
|
||||
val currentById = _questions.value.associateBy { it.record.id }
|
||||
questionEvictionJobs.entries.removeAll { (id, scheduled) ->
|
||||
val prompt = currentById[id]
|
||||
if (prompt == null || scheduled.observedAtMs != prompt.terminalObservedAtMs) {
|
||||
scheduled.job.cancel()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
for (prompt in _questions.value) {
|
||||
if (questionEvictionJobs.containsKey(prompt.record.id)) continue
|
||||
val id = prompt.record.id
|
||||
val observedAt = prompt.terminalObservedAtMs
|
||||
val remainingMs =
|
||||
if (observedAt == null && prompt.record.status == "pending" && prompt.record.expiresAtMs != Long.MAX_VALUE) {
|
||||
(prompt.record.expiresAtMs - nowMs).coerceAtLeast(0)
|
||||
} else if (observedAt != null) {
|
||||
(QUESTION_TERMINAL_RETENTION_MS - (nowMs - observedAt)).coerceAtLeast(0)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
val job =
|
||||
scope.launch(start = CoroutineStart.LAZY) {
|
||||
delay(remainingMs)
|
||||
synchronized(questionStateLock) {
|
||||
questionEvictionJobs.remove(id)
|
||||
val current = _questions.value
|
||||
if (observedAt == null) {
|
||||
val next =
|
||||
current.map {
|
||||
if (it.record.id == id && it.record.status == "pending" && it.terminalObservedAtMs == null) {
|
||||
it.copy(terminalObservedAtMs = System.currentTimeMillis())
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
if (next != current) {
|
||||
_questions.value = next
|
||||
questionStateRevision += 1
|
||||
}
|
||||
syncQuestionEvictionsLocked()
|
||||
return@synchronized
|
||||
}
|
||||
val next =
|
||||
current.filterNot {
|
||||
it.record.id == id &&
|
||||
it.terminalObservedAtMs == observedAt &&
|
||||
(it.status() == ChatQuestionStatus.Expired || it.record.status != "pending")
|
||||
}
|
||||
if (next != current) {
|
||||
_questions.value = next
|
||||
questionStateRevision += 1
|
||||
} else {
|
||||
syncQuestionEvictionsLocked()
|
||||
}
|
||||
}
|
||||
}
|
||||
questionEvictionJobs[id] = QuestionEvictionJob(job, observedAt)
|
||||
job.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearQuestions() {
|
||||
synchronized(questionStateLock) {
|
||||
questionRefreshGeneration += 1
|
||||
if (_questions.value.isEmpty()) return
|
||||
_questions.value = emptyList()
|
||||
questionStateRevision += 1
|
||||
syncQuestionEvictionsLocked()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package ai.openclaw.app.chat
|
||||
|
||||
import ai.openclaw.app.gateway.Question
|
||||
import ai.openclaw.app.gateway.QuestionRecord
|
||||
|
||||
internal const val QUESTION_TERMINAL_RETENTION_MS = 15_000L
|
||||
|
||||
enum class ChatQuestionStatus {
|
||||
Pending,
|
||||
Submitting,
|
||||
Answered,
|
||||
AnsweredElsewhere,
|
||||
Expired,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
data class ChatQuestionPrompt(
|
||||
val record: QuestionRecord,
|
||||
val submitting: Boolean = false,
|
||||
val answeredLocally: Boolean = false,
|
||||
val errorText: String? = null,
|
||||
val terminalObservedAtMs: Long? = null,
|
||||
) {
|
||||
fun status(nowMs: Long = System.currentTimeMillis()): ChatQuestionStatus =
|
||||
when (record.status) {
|
||||
"answered" -> if (answeredLocally) ChatQuestionStatus.Answered else ChatQuestionStatus.AnsweredElsewhere
|
||||
"cancelled" -> ChatQuestionStatus.Cancelled
|
||||
"expired" -> ChatQuestionStatus.Expired
|
||||
else ->
|
||||
when {
|
||||
nowMs >= record.expiresAtMs -> ChatQuestionStatus.Expired
|
||||
submitting -> ChatQuestionStatus.Submitting
|
||||
else -> ChatQuestionStatus.Pending
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldRetainAfterList(nowMs: Long): Boolean = terminalObservedAtMs?.let { nowMs - it < QUESTION_TERMINAL_RETENTION_MS } == true
|
||||
}
|
||||
|
||||
data class ChatQuestionDraft(
|
||||
val selectedOptions: Map<String, Set<String>> = emptyMap(),
|
||||
val otherText: Map<String, String> = emptyMap(),
|
||||
) {
|
||||
fun toggle(
|
||||
question: Question,
|
||||
label: String,
|
||||
): ChatQuestionDraft {
|
||||
if (question.options.none { it.label == label }) return this
|
||||
val selected = selectedOptions[question.id].orEmpty()
|
||||
val next =
|
||||
if (question.multiSelect == true) {
|
||||
if (label in selected) selected - label else selected + label
|
||||
} else if (selected == setOf(label)) {
|
||||
emptySet()
|
||||
} else {
|
||||
setOf(label)
|
||||
}
|
||||
return copy(
|
||||
selectedOptions = selectedOptions + (question.id to next),
|
||||
otherText = if (question.multiSelect != true && next.isNotEmpty()) otherText + (question.id to "") else otherText,
|
||||
)
|
||||
}
|
||||
|
||||
fun setOther(
|
||||
question: Question,
|
||||
value: String,
|
||||
): ChatQuestionDraft {
|
||||
if (question.options.isNotEmpty() && question.isOther != true) return this
|
||||
val clearOptions = question.multiSelect != true && value.isNotBlank()
|
||||
return copy(
|
||||
selectedOptions = if (clearOptions) selectedOptions + (question.id to emptySet()) else selectedOptions,
|
||||
otherText = otherText + (question.id to value),
|
||||
)
|
||||
}
|
||||
|
||||
fun answers(questions: List<Question>): Map<String, List<String>>? {
|
||||
val result = linkedMapOf<String, List<String>>()
|
||||
for (question in questions) {
|
||||
val selected = selectedOptions[question.id].orEmpty()
|
||||
val values = question.options.mapNotNull { option -> option.label.takeIf { it in selected } }.toMutableList()
|
||||
otherText[question.id]?.trim()?.takeIf { it.isNotEmpty() }?.let(values::add)
|
||||
if (values.isEmpty()) return null
|
||||
result[question.id] = values
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
internal fun questionsForSession(
|
||||
prompts: List<ChatQuestionPrompt>,
|
||||
sessionKey: String,
|
||||
mainSessionKey: String,
|
||||
activeAgentId: String,
|
||||
): List<ChatQuestionPrompt> {
|
||||
val main = mainSessionKey.trim().ifEmpty { "main" }
|
||||
val current = sessionKey.trim().let { if (it == "main") main else it }
|
||||
val activeAgent = activeAgentId.trim().lowercase()
|
||||
return prompts.filter { prompt ->
|
||||
val key = prompt.record.sessionKey?.trim() ?: return@filter true
|
||||
val sessionMatches = key == sessionKey || key == current || (key == "main" && current == main)
|
||||
val promptAgent =
|
||||
prompt.record.agentId
|
||||
?.trim()
|
||||
.orEmpty()
|
||||
.lowercase()
|
||||
sessionMatches && (promptAgent.isEmpty() || activeAgent.isEmpty() || promptAgent == activeAgent)
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,46 @@ data class GatewayNodeInvokeRequest(
|
||||
val idempotencyKey: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class QuestionOption(
|
||||
val label: String,
|
||||
val description: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Question(
|
||||
val id: String,
|
||||
val header: String,
|
||||
val question: String,
|
||||
val options: List<QuestionOption>,
|
||||
val multiSelect: Boolean? = null,
|
||||
val isOther: Boolean? = null,
|
||||
val isSecret: Boolean? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class QuestionAnswers(
|
||||
val answers: Map<String, QuestionAnswersAnswersValue>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class QuestionRecord(
|
||||
val id: String,
|
||||
val questions: List<Question>,
|
||||
val agentId: String? = null,
|
||||
val sessionKey: String? = null,
|
||||
val createdAtMs: Long,
|
||||
val expiresAtMs: Long,
|
||||
val status: String,
|
||||
val answers: QuestionAnswers? = null,
|
||||
val resolvedBy: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class QuestionListResult(
|
||||
val questions: List<QuestionRecord>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GatewayEventFrameStateVersion(
|
||||
val presence: Long,
|
||||
@@ -85,6 +125,11 @@ data class GatewayNodeInvokeResultParamsError(
|
||||
val message: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class QuestionAnswersAnswersValue(
|
||||
val answers: List<String>,
|
||||
)
|
||||
|
||||
enum class GatewayMethod(
|
||||
val rawValue: String,
|
||||
) {
|
||||
|
||||
@@ -1082,6 +1082,7 @@ class GatewaySession(
|
||||
setOf(
|
||||
"operator.admin",
|
||||
"operator.approvals",
|
||||
"operator.questions",
|
||||
"operator.read",
|
||||
"operator.talk.secrets",
|
||||
"operator.write",
|
||||
|
||||
@@ -152,6 +152,8 @@ internal val nativeStringResourceIds: Map<String, Int> =
|
||||
"Android can scan or paste an existing setup code, but this gateway does not expose setup-code generation to the app yet. Generate the QR/code on the gateway host with openclaw qr, then scan it here or paste the setup code below." to R.string.native_58268edca12a81a1,
|
||||
"Android setup guide" to R.string.native_14638daf580b6ee0,
|
||||
"Another cron action is still finishing." to R.string.native_4072c873764625e9,
|
||||
"Answered" to R.string.native_665590354e719bcf,
|
||||
"Answered elsewhere" to R.string.native_071c496aa34a4fd5,
|
||||
"App Filter" to R.string.native_eaf3a81a6411115f,
|
||||
"App language" to R.string.native_b8352b44a588721c,
|
||||
"App list stays on this phone." to R.string.native_aab53a916682c720,
|
||||
@@ -229,6 +231,7 @@ internal val nativeStringResourceIds: Map<String, Int> =
|
||||
"Camera enabled" to R.string.native_29d5c526bab8bf50,
|
||||
"Cancel" to R.string.cancel,
|
||||
"Cancel voice note" to R.string.native_fae8fa3bc050ab7e,
|
||||
"Cancelled" to R.string.native_d353a99eb4556847,
|
||||
"Cannot reach gateway" to R.string.native_d8f247890b029a85,
|
||||
"Canvas" to R.string.native_3824a9f4dafe92c6,
|
||||
"Canvas Status" to R.string.native_584162ac73e99ee6,
|
||||
@@ -480,7 +483,10 @@ internal val nativeStringResourceIds: Map<String, Int> =
|
||||
"Exec approval requests will appear here while this phone is connected." to R.string.native_e1db8571dfe6dfcf,
|
||||
"Expand link preview" to R.string.native_c184b2ada7abc688,
|
||||
"Expand plan checklist" to R.string.native_d30f33731adb6b40,
|
||||
"Expired" to R.string.native_424a2551d356754c,
|
||||
"Expires \${duration}" to R.string.native_48377001408f13be,
|
||||
"Expires in \${seconds / 60}m \${seconds % 60}s" to R.string.native_9dca3c2b01b40c0b,
|
||||
"Expires in \${seconds}s" to R.string.native_e9e3a4b23345cb57,
|
||||
"Expression" to R.string.native_c67415bcff328a59,
|
||||
"Extra" to R.string.native_2e4aaf05df1cbfb3,
|
||||
"Failed" to R.string.native_031a8f0f659df890,
|
||||
@@ -864,6 +870,7 @@ internal val nativeStringResourceIds: Map<String, Int> =
|
||||
"Optional description" to R.string.native_c7b7b7f01360f6a5,
|
||||
"Optional override" to R.string.native_217119a61e20f1f2,
|
||||
"Optional path" to R.string.native_1ed1c61b19068fa4,
|
||||
"Other answer" to R.string.native_773e7729b237d4ea,
|
||||
"Output" to R.string.native_b2439bcb8dee14b6,
|
||||
"Overview" to R.string.native_d4b1ea5708dd5329,
|
||||
"PDF" to R.string.native_1d393b0081b632c5,
|
||||
@@ -1171,6 +1178,8 @@ internal val nativeStringResourceIds: Map<String, Int> =
|
||||
"Stop Dictation" to R.string.native_bf399ffa03617a96,
|
||||
"Sub-agent" to R.string.native_429f6057535c1ea3,
|
||||
"Subagents" to R.string.native_88296ab3d6665a8e,
|
||||
"Submit" to R.string.native_155f816c0407310c,
|
||||
"Submitting…" to R.string.native_49195f559e4a9f5b,
|
||||
"Subsystem" to R.string.native_63734ae10b7e46e9,
|
||||
"Suggest Task" to R.string.native_49bb137d12031cbc,
|
||||
"Summarize recent sessions and next steps." to R.string.native_11e47358a692d81b,
|
||||
|
||||
@@ -44,6 +44,7 @@ class ConnectionManager(
|
||||
// sessions.patch (model switching); stored tokens keep their granted scopes.
|
||||
"operator.admin",
|
||||
"operator.approvals",
|
||||
"operator.questions",
|
||||
"operator.read",
|
||||
"operator.talk.secrets",
|
||||
"operator.write",
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
package ai.openclaw.app.ui.chat
|
||||
|
||||
import ai.openclaw.app.chat.ChatQuestionDraft
|
||||
import ai.openclaw.app.chat.ChatQuestionPrompt
|
||||
import ai.openclaw.app.chat.ChatQuestionStatus
|
||||
import ai.openclaw.app.gateway.Question
|
||||
import ai.openclaw.app.i18n.nativeString
|
||||
import ai.openclaw.app.ui.design.ClawTheme
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
internal fun ChatQuestionCard(
|
||||
prompt: ChatQuestionPrompt,
|
||||
onSubmit: (String, Map<String, List<String>>) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var draft by remember(prompt.record.id) { mutableStateOf(ChatQuestionDraft()) }
|
||||
var nowMs by remember(prompt.record.id) { mutableLongStateOf(System.currentTimeMillis()) }
|
||||
val status = prompt.status(nowMs)
|
||||
val pending = status == ChatQuestionStatus.Pending
|
||||
LaunchedEffect(prompt.record.id, prompt.record.expiresAtMs, status) {
|
||||
while (status == ChatQuestionStatus.Pending || status == ChatQuestionStatus.Submitting) {
|
||||
delay(1000)
|
||||
nowMs = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
|
||||
Surface(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(ClawTheme.radii.sheet),
|
||||
color = ClawTheme.colors.surfaceRaised,
|
||||
border = BorderStroke(1.dp, ClawTheme.colors.border),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||
) {
|
||||
prompt.record.questions.forEach { question ->
|
||||
QuestionSection(
|
||||
question = question,
|
||||
draft = draft,
|
||||
enabled = pending,
|
||||
onDraftChanged = { draft = it },
|
||||
)
|
||||
}
|
||||
QuestionFooter(
|
||||
prompt = prompt,
|
||||
draft = draft,
|
||||
status = status,
|
||||
nowMs = nowMs,
|
||||
onSubmit = onSubmit,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QuestionSection(
|
||||
question: Question,
|
||||
draft: ChatQuestionDraft,
|
||||
enabled: Boolean,
|
||||
onDraftChanged: (ChatQuestionDraft) -> Unit,
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = question.header.uppercase(),
|
||||
style = ClawTheme.type.caption,
|
||||
color = ClawTheme.colors.primary,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
Text(text = question.question, style = ClawTheme.type.body, color = ClawTheme.colors.text)
|
||||
question.options.forEach { option ->
|
||||
val selected = option.label in draft.selectedOptions[question.id].orEmpty()
|
||||
Surface(
|
||||
onClick = { onDraftChanged(draft.toggle(question, option.label)) },
|
||||
enabled = enabled,
|
||||
shape = RoundedCornerShape(ClawTheme.radii.row),
|
||||
color = if (selected) ClawTheme.colors.surfacePressed else ClawTheme.colors.surface,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 6.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
if (question.multiSelect == true) {
|
||||
Checkbox(checked = selected, onCheckedChange = null, enabled = enabled)
|
||||
} else {
|
||||
RadioButton(selected = selected, onClick = null, enabled = enabled)
|
||||
}
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(text = option.label, style = ClawTheme.type.body, color = ClawTheme.colors.text)
|
||||
option.description?.takeIf { it.isNotBlank() }?.let { description ->
|
||||
Text(text = description, style = ClawTheme.type.caption, color = ClawTheme.colors.textMuted)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (question.options.isEmpty() || question.isOther == true) {
|
||||
OutlinedTextField(
|
||||
value = draft.otherText[question.id].orEmpty(),
|
||||
onValueChange = { onDraftChanged(draft.setOther(question, it)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = enabled,
|
||||
label = { Text(nativeString("Other answer")) },
|
||||
minLines = 1,
|
||||
maxLines = 4,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QuestionFooter(
|
||||
prompt: ChatQuestionPrompt,
|
||||
draft: ChatQuestionDraft,
|
||||
status: ChatQuestionStatus,
|
||||
nowMs: Long,
|
||||
onSubmit: (String, Map<String, List<String>>) -> Unit,
|
||||
) {
|
||||
val answers = draft.answers(prompt.record.questions)
|
||||
if (status == ChatQuestionStatus.Pending || status == ChatQuestionStatus.Submitting) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = questionCountdown(prompt.record.expiresAtMs, nowMs),
|
||||
style = ClawTheme.type.caption,
|
||||
color = ClawTheme.colors.textMuted,
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
Button(
|
||||
onClick = { answers?.let { onSubmit(prompt.record.id, it) } },
|
||||
enabled = answers != null && status == ChatQuestionStatus.Pending,
|
||||
) {
|
||||
Text(if (status == ChatQuestionStatus.Submitting) nativeString("Submitting…") else nativeString("Submit"))
|
||||
}
|
||||
}
|
||||
prompt.errorText?.let { error ->
|
||||
Text(text = error, style = ClawTheme.type.caption, color = ClawTheme.colors.danger)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
text =
|
||||
when (status) {
|
||||
ChatQuestionStatus.Answered -> nativeString("Answered")
|
||||
ChatQuestionStatus.AnsweredElsewhere -> nativeString("Answered elsewhere")
|
||||
ChatQuestionStatus.Expired -> nativeString("Expired")
|
||||
ChatQuestionStatus.Cancelled -> nativeString("Cancelled")
|
||||
ChatQuestionStatus.Pending -> nativeString("Pending")
|
||||
ChatQuestionStatus.Submitting -> nativeString("Submitting…")
|
||||
},
|
||||
style = ClawTheme.type.caption,
|
||||
color = ClawTheme.colors.textMuted,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// nativeString is the non-composable resource accessor (nativeStringResource
|
||||
// is the @Composable variant), so this helper is safe outside composition.
|
||||
private fun questionCountdown(
|
||||
expiresAtMs: Long,
|
||||
nowMs: Long,
|
||||
): String {
|
||||
val seconds = ((expiresAtMs - nowMs).coerceAtLeast(0) + 999) / 1000
|
||||
return if (seconds >= 60) {
|
||||
nativeString("Expires in \${seconds / 60}m \${seconds % 60}s", seconds / 60, seconds % 60)
|
||||
} else {
|
||||
nativeString("Expires in \${seconds}s", seconds)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import ai.openclaw.app.chat.ChatOutboxItem
|
||||
import ai.openclaw.app.chat.ChatPendingToolCall
|
||||
import ai.openclaw.app.chat.ChatPlanStep
|
||||
import ai.openclaw.app.chat.ChatPlanStepStatus
|
||||
import ai.openclaw.app.chat.ChatQuestionPrompt
|
||||
import ai.openclaw.app.chat.ChatSessionEntry
|
||||
import ai.openclaw.app.chat.ChatThinkingLevelOption
|
||||
import ai.openclaw.app.chat.ChatThinkingLevelSelection
|
||||
@@ -21,6 +22,7 @@ import ai.openclaw.app.chat.MessageSpeechPhase
|
||||
import ai.openclaw.app.chat.MessageSpeechState
|
||||
import ai.openclaw.app.chat.VoiceNoteRecorderState
|
||||
import ai.openclaw.app.chat.chatOutboxQueueFailureText
|
||||
import ai.openclaw.app.chat.questionsForSession
|
||||
import ai.openclaw.app.chat.resolveChatComposerOwner
|
||||
import ai.openclaw.app.chat.resolveGatewayDefaultAgentId
|
||||
import ai.openclaw.app.currentAppLanguage
|
||||
@@ -185,6 +187,7 @@ fun ChatScreen(
|
||||
val thinkingLevelSelection by viewModel.chatThinkingLevelSelection.collectAsState()
|
||||
val streamingAssistantText by viewModel.chatStreamingAssistantText.collectAsState()
|
||||
val pendingToolCalls by viewModel.chatPendingToolCalls.collectAsState()
|
||||
val questions by viewModel.chatQuestions.collectAsState()
|
||||
val planSteps by viewModel.chatPlanSteps.collectAsState()
|
||||
val sessions by viewModel.chatSessions.collectAsState()
|
||||
val chatCommands by viewModel.chatCommands.collectAsState()
|
||||
@@ -523,6 +526,7 @@ fun ChatScreen(
|
||||
historyLoading = historyLoading,
|
||||
pendingRunCount = pendingRunCount,
|
||||
pendingToolCalls = pendingToolCalls,
|
||||
questions = questionsForSession(questions, sessionKey, mainSessionKey, activeAgentId),
|
||||
streamingAssistantText = streamingAssistantText,
|
||||
healthOk = healthOk,
|
||||
gatewayOffline = gatewayOffline,
|
||||
@@ -540,6 +544,7 @@ fun ChatScreen(
|
||||
),
|
||||
onRetryOutbox = viewModel::retryChatOutboxCommand,
|
||||
onDeleteOutbox = viewModel::deleteChatOutboxCommand,
|
||||
onResolveQuestion = viewModel::resolveChatQuestion,
|
||||
onStarterPrompt = { prompt -> inputDrafts[composerOwner] = prompt },
|
||||
onReplyMessage = { value -> viewModel.setChatReplyDraft(value, composerOwner) },
|
||||
speechState = messageSpeechState,
|
||||
@@ -963,6 +968,7 @@ private fun ChatMessageList(
|
||||
historyLoading: Boolean,
|
||||
pendingRunCount: Int,
|
||||
pendingToolCalls: List<ChatPendingToolCall>,
|
||||
questions: List<ChatQuestionPrompt>,
|
||||
streamingAssistantText: String?,
|
||||
healthOk: Boolean,
|
||||
gatewayOffline: Boolean,
|
||||
@@ -970,6 +976,7 @@ private fun ChatMessageList(
|
||||
recoveryOutboxItems: List<ChatOutboxItem>,
|
||||
onRetryOutbox: (String) -> Unit,
|
||||
onDeleteOutbox: (String) -> Unit,
|
||||
onResolveQuestion: (String, Map<String, List<String>>) -> Unit,
|
||||
onStarterPrompt: (String) -> Unit,
|
||||
onReplyMessage: (String) -> Unit,
|
||||
speechState: MessageSpeechState?,
|
||||
@@ -978,7 +985,7 @@ private fun ChatMessageList(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val timeline =
|
||||
remember(messages, pendingRunCount, pendingToolCalls, streamingAssistantText, outboxItems, recoveryOutboxItems) {
|
||||
remember(messages, pendingRunCount, pendingToolCalls, questions, streamingAssistantText, outboxItems, recoveryOutboxItems) {
|
||||
buildChatTimeline(
|
||||
messages = messages,
|
||||
pendingRunCount = pendingRunCount,
|
||||
@@ -986,6 +993,7 @@ private fun ChatMessageList(
|
||||
streamingAssistantText = streamingAssistantText,
|
||||
outboxItems = outboxItems,
|
||||
recoveryOutboxItems = recoveryOutboxItems,
|
||||
questions = questions,
|
||||
)
|
||||
}
|
||||
val readerScroll =
|
||||
@@ -1041,6 +1049,8 @@ private fun ChatMessageList(
|
||||
),
|
||||
)
|
||||
is ChatTimelineItem.PendingTools -> ToolBubble(toolCalls = item.toolCalls)
|
||||
is ChatTimelineItem.QuestionPrompt ->
|
||||
ChatQuestionCard(prompt = item.prompt, onSubmit = onResolveQuestion)
|
||||
is ChatTimelineItem.StreamingAssistant ->
|
||||
ChatBubble(
|
||||
messageId = null,
|
||||
|
||||
@@ -4,6 +4,7 @@ import ai.openclaw.app.chat.ChatMessage
|
||||
import ai.openclaw.app.chat.ChatOutboxItem
|
||||
import ai.openclaw.app.chat.ChatOutboxStatus
|
||||
import ai.openclaw.app.chat.ChatPendingToolCall
|
||||
import ai.openclaw.app.chat.ChatQuestionPrompt
|
||||
import ai.openclaw.app.chat.OUTBOX_OWNER_CHANGED_ERROR
|
||||
import ai.openclaw.app.resolveAgentIdFromMainSessionKey
|
||||
|
||||
@@ -34,6 +35,10 @@ internal sealed class ChatTimelineItem {
|
||||
val toolCalls: List<ChatPendingToolCall>,
|
||||
) : ChatTimelineItem()
|
||||
|
||||
data class QuestionPrompt(
|
||||
val prompt: ChatQuestionPrompt,
|
||||
) : ChatTimelineItem()
|
||||
|
||||
object Thinking : ChatTimelineItem()
|
||||
}
|
||||
|
||||
@@ -53,11 +58,13 @@ internal fun buildChatTimeline(
|
||||
streamingAssistantText: String?,
|
||||
outboxItems: List<ChatOutboxItem> = emptyList(),
|
||||
recoveryOutboxItems: List<ChatOutboxItem> = emptyList(),
|
||||
questions: List<ChatQuestionPrompt> = emptyList(),
|
||||
): ChatTimeline {
|
||||
val stream = streamingAssistantText?.trim()?.takeIf { it.isNotEmpty() }
|
||||
val items =
|
||||
buildList {
|
||||
// reverseLayout: index 0 renders bottom-most; queued commands are the newest user input.
|
||||
questions.asReversed().forEach { prompt -> add(ChatTimelineItem.QuestionPrompt(prompt)) }
|
||||
outboxItems.asReversed().forEach { item -> add(ChatTimelineItem.OutboxCommand(item)) }
|
||||
recoveryOutboxItems.asReversed().forEach { item -> add(ChatTimelineItem.RecoveryOutboxCommand(item)) }
|
||||
if (recoveryOutboxItems.isNotEmpty()) add(ChatTimelineItem.OutboxRecoveryHeader(recoveryOutboxItems.size))
|
||||
@@ -99,7 +106,14 @@ internal fun buildChatTimeline(
|
||||
latestUserMessageId = latestUserMessage?.id,
|
||||
latestUserMessageVersion = latestUserMessage?.let(::stableMessageVersion),
|
||||
latestContentVersion =
|
||||
latestContentVersion(messages, pendingRunCount, pendingToolCalls, stream, outboxItems + recoveryOutboxItems),
|
||||
latestContentVersion(
|
||||
messages,
|
||||
pendingRunCount,
|
||||
pendingToolCalls,
|
||||
stream,
|
||||
outboxItems + recoveryOutboxItems,
|
||||
questions,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -188,6 +202,7 @@ private fun latestContentVersion(
|
||||
pendingToolCalls: List<ChatPendingToolCall>,
|
||||
stream: String?,
|
||||
outboxItems: List<ChatOutboxItem> = emptyList(),
|
||||
questions: List<ChatQuestionPrompt> = emptyList(),
|
||||
): String {
|
||||
val latest = messages.lastOrNull()
|
||||
return buildString {
|
||||
@@ -232,6 +247,15 @@ private fun latestContentVersion(
|
||||
append(item.status)
|
||||
append(';')
|
||||
}
|
||||
append(":questions=")
|
||||
questions.forEach { prompt ->
|
||||
append(prompt.record.id)
|
||||
append(',')
|
||||
append(prompt.record.status)
|
||||
append(',')
|
||||
append(prompt.submitting)
|
||||
append(';')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,6 +266,7 @@ internal fun chatTimelineItemKey(item: ChatTimelineItem): String =
|
||||
is ChatTimelineItem.RecoveryOutboxCommand -> "outbox-recovery:${item.item.id}"
|
||||
is ChatTimelineItem.OutboxRecoveryHeader -> "outbox-recovery-header"
|
||||
is ChatTimelineItem.PendingTools -> "tools"
|
||||
is ChatTimelineItem.QuestionPrompt -> "question:${item.prompt.record.id}"
|
||||
is ChatTimelineItem.StreamingAssistant -> "stream"
|
||||
ChatTimelineItem.Thinking -> "thinking"
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مصفوفة JSON للأمر argv"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تعذّرت قراءة هذه الصورة. اختر لقطة شاشة واضحة أو صورة لرمز QR من openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تعذّر %1$s مقترح ورشة عمل Skills."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تمت الإجابة في مكان آخر"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"سجّل Gateway الموافقة مرة واحدة."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"لا يتحقق OpenClaw من الموقع إلا عندما يطلب Gateway المقترن ذلك. في شاشة Android التالية، اختر %1$s للسماح بعمليات التحقق أثناء عمل التطبيق في الخلفية."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"الأجهزة المقترنة"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s معلّقة"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"اسم الجهاز"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"إرسال"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"الموقع"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مثال America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"الجلسة المستهدفة"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"لا توجد عمليات أتمتة مطابقة."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"يمكن للهاتف الوصول إلى Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"انتهت الصلاحية"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"أعمال OpenClaw المجدولة من Gateway لديك."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"في انتظار الموافقة على الجهاز"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مشكلة"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ابحث في بيانات السجل الوصفية. يتحقق Gateway من الثقة مرة أخرى قبل أي تنزيل."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"استخدم عنوان IP خاصًا بشبكة LAN للإعداد المحلي، أو فعّل Tailscale Serve / اعرض عنوان URL للبوابة باستخدام wss:// للوصول عن بُعد."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"جارٍ الإرسال…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"الحساب %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"بحث"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"سيؤدي هذا إلى عزل \"%1$s\" وتحديث حالة ورشة Skills من Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تسجيل ملاحظة صوتية"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"في قائمة الانتظار"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تمت الإجابة"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"السماح بأدوات الكاميرا عند طلبها."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"المشكلات"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"التنبيه الصوتي"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"صِل Gateway للبدء"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"إشعار أمان"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"إجابة أخرى"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تجاهل تحذير الصورة المشتركة"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"قيّم Gateway إصدارًا مختلفًا من ClawHub. راجع Skill مرة أخرى قبل التثبيت."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"فتح وصول النظام"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تحقق"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"قراءة موقع هذا الهاتف"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مفتاح المهارة"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تنتهي الصلاحية خلال %1$sد %2$sث"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تم تثبيت %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"بوابات Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"وضع إعادة التوجيه"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s أخرى"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"توسيع قائمة التحقق الخاصة بالخطة"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"المتصفح"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تم الإلغاء"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"التشغيل معلّق"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ذكاء اصطناعي شخصي على أجهزتك"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"المزوّدون والنماذج"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"العقدة"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تنتهي الصلاحية خلال %1$sث"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"الموجّه غير متاح"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"السجلات"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"اتصل بـ Gateway لفحص مقترحات ورشة عمل Skills."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Befehl-argv-JSON-Array"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dieses Bild konnte nicht gelesen werden. Wähle einen deutlichen Screenshot oder ein deutliches Bild des QR-Codes aus openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Der Skill-Workshop-Vorschlag konnte nicht mit der Aktion %1$s bearbeitet werden."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Anderswo beantwortet"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway hat die Genehmigung einmalig erfasst."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw überprüft den Standort nur, wenn Ihr gekoppeltes Gateway ihn anfordert. Wählen Sie auf dem nächsten Android-Bildschirm %1$s aus, um Überprüfungen zuzulassen, während die App im Hintergrund ausgeführt wird."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gekoppelte Geräte"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s ausstehend"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gerätename"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Absenden"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Standort"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"z. B. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sitzungsziel"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Keine passenden Automatisierungen."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Smartphone kann die Gateway erreichen"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Abgelaufen"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Geplante OpenClaw-Arbeit von Ihrer Gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Warten auf Gerätefreigabe"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problem"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Durchsuchen Sie die Registry-Metadaten. Das Gateway überprüft die Vertrauenswürdigkeit vor jedem Download erneut."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verwenden Sie für die lokale Einrichtung eine private LAN-IP oder aktivieren Sie Tailscale Serve bzw. stellen Sie für den Remotezugriff eine wss://-Gateway-URL bereit."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Wird gesendet…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Konto %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suchen"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dadurch wird \"%1$s\" unter Quarantäne gestellt und der Status von Skill Workshop über das Gateway aktualisiert."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sprachnachricht aufnehmen"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"In Warteschlange"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Beantwortet"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kameratools auf Anfrage zulassen."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Probleme"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sprachaktivierung"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway verbinden, um zu starten"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sicherheitshinweis"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Andere Antwort"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Warnung zu geteiltem Bild schließen"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Das Gateway hat eine andere ClawHub-Version geprüft. Prüfen Sie den Skill vor der Installation erneut."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Systemzugriff öffnen"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prüfen"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Standort dieses Telefons abrufen"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill-Schlüssel"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Läuft in %1$sm %2$ss ab"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s wurde installiert."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Weiterleitungsmodus"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s weitere"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Plan-Checkliste ausklappen"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Browser"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Abgebrochen"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ausführung ausstehend"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Persönliche KI auf deinen Geräten"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Anbieter & Modelle"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Knoten"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Läuft in %1$ss ab"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prompt nicht verfügbar"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Protokolle"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verbinden Sie das Gateway, um Vorschläge aus Skill Workshop zu prüfen."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Array JSON de argv del comando"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"No se pudo leer esa imagen. Selecciona una captura de pantalla clara o una imagen del código QR de openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"No se pudo %1$s la propuesta de Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Respondida en otro lugar"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway registró la aprobación una vez."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw solo comprueba la ubicación cuando el Gateway vinculado la solicita. En la siguiente pantalla de Android, elige %1$s para permitir las comprobaciones mientras la aplicación está en segundo plano."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dispositivos emparejados"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s pendientes"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nombre del dispositivo"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Enviar"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ubicación"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"p. ej. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Destino de la sesión"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"No hay automatizaciones coincidentes."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"El teléfono puede acceder al Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Caducada"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Trabajo de OpenClaw programado desde tu gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Esperando la aprobación del dispositivo"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problema"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Busca en los metadatos del registro. El Gateway vuelve a verificar la confianza antes de cualquier descarga."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Use una dirección IP de LAN privada para la configuración local, o habilite Tailscale Serve / exponga una URL de gateway wss:// para el acceso remoto."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Enviando…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cuenta %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Buscar"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Esto pondrá \"%1$s\" en cuarentena y actualizará el estado de Skill Workshop desde el Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Grabar nota de voz"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"En cola"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Respondida"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Permitir las herramientas de cámara cuando se soliciten."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problemas"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Activación por voz"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Conecta el Gateway para comenzar"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Aviso de seguridad"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Otra respuesta"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Descartar advertencia de imagen compartida"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"El Gateway evaluó una versión diferente de ClawHub. Revisa de nuevo la Skill antes de instalarla."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Abrir acceso del sistema"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Comprobar"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Leer la ubicación de este teléfono"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Clave de Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Caduca en %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Se instaló %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Modo de reenvío"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s más"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expandir la lista de verificación del plan"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Navegador"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cancelada"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ejecución pendiente"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"IA personal en tus dispositivos"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Proveedores y modelos"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nodo"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Caduca en %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Indicación no disponible"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Registros"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Conecta el Gateway para consultar las propuestas de Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"آرایه JSON دستور argv"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"خواندن این تصویر ممکن نبود. یک اسکرینشات یا تصویر واضح از کد QR ایجادشده با openclaw qr انتخاب کنید."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"پیشنهاد Skill Workshop را نمیتوان %1$s کرد."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"در جای دیگری پاسخ داده شده"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway تأیید را یکبار ثبت کرد."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw فقط زمانی موقعیت مکانی را بررسی میکند که Gateway جفتشده شما آن را درخواست کند. در صفحه بعدی Android، %1$s را انتخاب کنید تا بررسیها هنگام اجرای برنامه در پسزمینه مجاز باشند."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"دستگاههای جفتشده"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s در انتظار"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"نام دستگاه"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ارسال"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"موقعیت مکانی"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مثلاً America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مقصد نشست"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"خودکارسازی منطبقی یافت نشد."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"تلفن میتواند به Gateway دسترسی داشته باشد"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"منقضی شده"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"کارهای زمانبندیشده OpenClaw از Gateway شما."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"در انتظار تأیید دستگاه"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مشکل"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"فراداده رجیستری را جستجو کنید. Gateway پیش از هرگونه دانلود، اعتماد را دوباره تأیید میکند."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"برای راهاندازی محلی از یک IP خصوصی LAN استفاده کنید، یا برای دسترسی از راه دور Tailscale Serve را فعال کنید / یک نشانی URL از نوع wss:// برای Gateway در دسترس قرار دهید."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"در حال ارسال…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"حساب %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"جستجو"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"این کار \"%1$s\" را قرنطینه میکند و وضعیت Skill Workshop را از Gateway تازهسازی میکند."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ضبط یادداشت صوتی"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"در صف"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"پاسخ داده شده"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"در صورت درخواست، ابزارهای دوربین را مجاز کنید."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مشکلات"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"بیدارباش صوتی"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"برای شروع، Gateway را متصل کنید"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"اعلان امنیتی"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"پاسخ دیگر"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"رد کردن هشدار تصویر اشتراکی"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway نسخه دیگری از ClawHub را ارزیابی کرده است. پیش از نصب، Skill را دوباره بررسی کنید."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"باز کردن دسترسی سیستم"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"بررسی"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"خواندن موقعیت مکانی این تلفن"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"کلید مهارت"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"انقضا تا %1$sm %2$ss دیگر"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s نصب شد."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gatewayها"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"حالت بازارسال"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s مورد دیگر"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"بازکردن فهرست بررسی برنامه"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"مرورگر"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"لغو شده"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"اجرای در انتظار"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"هوش مصنوعی شخصی روی دستگاههای شما"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ارائهدهندگان و مدلها"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"گره"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"انقضا تا %1$ss دیگر"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"درخواست در دسترس نیست"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"گزارشها"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"برای بررسی پیشنهادهای Skill Workshop، به Gateway متصل شوید."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tableau JSON argv de la commande"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Impossible de lire cette image. Choisissez une capture d’écran ou une image nette du code QR fourni par openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Impossible d’effectuer l’action %1$s sur la proposition Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Répondu ailleurs"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Le Gateway a enregistré l\'approbation une fois."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw vérifie uniquement la localisation lorsque votre Gateway associé la demande. Sur l’écran Android suivant, choisissez %1$s pour autoriser les vérifications lorsque l’application est en arrière-plan."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Appareils associés"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s en attente"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nom de l’appareil"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Envoyer"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Localisation"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ex. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cible de session"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Aucune automatisation correspondante."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Le téléphone peut joindre la Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expiré"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Travail OpenClaw planifié depuis votre Gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"En attente de l’approbation de l’appareil"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problème"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Recherchez dans les métadonnées du registre. Le Gateway vérifie à nouveau la fiabilité avant tout téléchargement."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Utilisez une adresse IP de réseau local privé pour la configuration locale, ou activez Tailscale Serve / exposez une URL de gateway en wss:// pour l’accès à distance."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Envoi…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Compte %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Rechercher"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cela mettra \"%1$s\" en quarantaine et actualisera l’état de Skill Workshop depuis le Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Enregistrer une note vocale"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"En attente"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Répondu"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Autoriser les outils de caméra sur demande."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problèmes"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Réveil vocal"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Connectez le Gateway pour commencer"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Avis de sécurité"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Autre réponse"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ignorer l\'avertissement d\'image partagée"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Le Gateway a évalué une autre version de ClawHub. Examinez à nouveau la compétence avant de l’installer."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ouvrir l’accès système"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Vérifier"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Lire la position de ce téléphone"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Clé du Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expire dans %1$s min %2$s s"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s a été installé."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Mode de transfert"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s de plus"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Développer la liste de contrôle du plan"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Navigateur"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Annulé"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Exécution en attente"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"IA personnelle sur vos appareils"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Fournisseurs et modèles"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nœud"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expire dans %1$s s"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Invite indisponible"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Journaux"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Connectez le Gateway pour examiner les propositions de Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"कमांड argv JSON ऐरे"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"उस इमेज को पढ़ा नहीं जा सका। openclaw qr से मिले QR का स्पष्ट स्क्रीनशॉट या इमेज चुनें।"</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshop प्रस्ताव को %1$s नहीं किया जा सका।"</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"कहीं और उत्तर दिया गया"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway ने स्वीकृति एक बार दर्ज की।"</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw केवल तभी स्थान की जाँच करता है, जब आपका युग्मित Gateway इसका अनुरोध करता है। ऐप के बैकग्राउंड में होने पर जाँच की अनुमति देने के लिए अगली Android स्क्रीन पर %1$s चुनें।"</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"पेयर किए गए डिवाइस"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s लंबित"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"डिवाइस का नाम"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"सबमिट करें"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"स्थान"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"जैसे America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"सेशन लक्ष्य"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"कोई मेल खाता ऑटोमेशन नहीं मिला।"</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"फ़ोन Gateway तक पहुँच सकता है"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"समय समाप्त"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"आपके gateway से निर्धारित OpenClaw कार्य।"</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"डिवाइस की स्वीकृति की प्रतीक्षा है"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"समस्या"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"रजिस्ट्री मेटाडेटा खोजें। किसी भी डाउनलोड से पहले Gateway विश्वसनीयता को फिर से सत्यापित करता है।"</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"स्थानीय सेटअप के लिए निजी LAN IP का उपयोग करें, या रिमोट एक्सेस के लिए Tailscale Serve सक्षम करें / wss:// Gateway URL उपलब्ध कराएँ।"</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"सबमिट किया जा रहा है…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"खाता %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"खोजें"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"यह \"%1$s\" को क्वारंटीन करेगा और Gateway से Skill Workshop की स्थिति रीफ़्रेश करेगा।"</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"वॉइस नोट रिकॉर्ड करें"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"कतार में"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"उत्तर दिया गया"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"अनुरोध किए जाने पर कैमरा टूल्स की अनुमति दें।"</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"समस्याएँ"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"वॉइस वेक"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"शुरू करने के लिए Gateway कनेक्ट करें"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"सुरक्षा सूचना"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"अन्य उत्तर"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"साझा-छवि चेतावनी खारिज करें"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway ने किसी अन्य ClawHub रिलीज़ का मूल्यांकन किया था। इंस्टॉल करने से पहले Skill की दोबारा समीक्षा करें।"</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"सिस्टम एक्सेस खोलें"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"जाँचें"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"इस फ़ोन का स्थान पढ़ें"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill कुंजी"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$sm %2$ss में समाप्त होगा"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s इंस्टॉल किया गया।"</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"फ़ॉरवर्डिंग मोड"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s और"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"योजना चेकलिस्ट विस्तृत करें"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Browser"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"रद्द किया गया"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"रन लंबित है"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"आपके डिवाइसों पर निजी AI"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"प्रदाता और मॉडल"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"नोड"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$ss में समाप्त होगा"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"प्रॉम्प्ट उपलब्ध नहीं है"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"लॉग"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshop प्रस्तावों की जाँच करने के लिए Gateway कनेक्ट करें।"</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Larik JSON argv perintah"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gambar tersebut tidak dapat dibaca. Pilih tangkapan layar atau gambar QR yang jelas dari openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tidak dapat %1$s proposal Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dijawab di tempat lain"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway mencatat persetujuan sekali."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw hanya memeriksa lokasi saat Gateway yang disandingkan memintanya. Di layar Android berikutnya, pilih %1$s untuk mengizinkan pemeriksaan saat aplikasi berjalan di latar belakang."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Perangkat yang Dipasangkan"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s tertunda"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nama perangkat"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kirim"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Lokasi"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"mis. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Target sesi"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tidak ada otomatisasi yang cocok."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ponsel dapat menjangkau Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kedaluwarsa"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Pekerjaan OpenClaw terjadwal dari gateway Anda."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Menunggu persetujuan perangkat"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Masalah"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cari metadata registri. Gateway memverifikasi kembali kepercayaan sebelum pengunduhan apa pun."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gunakan IP LAN privat untuk penyiapan lokal, atau aktifkan Tailscale Serve / ekspos URL gateway wss:// untuk akses jarak jauh."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Mengirim…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Akun %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cari"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tindakan ini akan mengarantina \"%1$s\" dan memuat ulang status Skill Workshop dari Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Rekam catatan suara"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dalam antrean"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sudah dijawab"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Izinkan alat kamera saat diminta."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Masalah"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Bangun Suara"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Hubungkan Gateway untuk memulai"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Pemberitahuan keamanan"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Jawaban lain"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tutup peringatan gambar yang dibagikan"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway mengevaluasi rilis ClawHub yang berbeda. Tinjau kembali skill sebelum menginstalnya."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Buka Akses Sistem"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Periksa"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Baca lokasi ponsel ini"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kunci Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kedaluwarsa dalam %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s telah diinstal."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Mode Penerusan"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s lagi"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Luaskan daftar periksa rencana"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Browser"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dibatalkan"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Eksekusi Tertunda"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"AI pribadi di perangkat Anda"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Penyedia & Model"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Node"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kedaluwarsa dalam %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Perintah tidak tersedia"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Log"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Hubungkan Gateway untuk memeriksa proposal Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Array JSON argv del comando"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Impossibile leggere l\'immagine. Scegli uno screenshot nitido o un\'immagine del QR generato da openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Impossibile %1$s la proposta di Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Risposto altrove"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Il Gateway ha registrato l\'approvazione una volta."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw controlla la posizione solo quando il Gateway associato la richiede. Nella schermata Android successiva, scegli %1$s per consentire i controlli mentre l\'app è in background."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dispositivi associati"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s in sospeso"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nome dispositivo"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Invia"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Posizione"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"es. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Destinazione della sessione"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nessuna automazione corrispondente."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Il telefono può raggiungere il Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Scaduto"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Lavoro OpenClaw pianificato dal tuo Gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"In attesa dell\'approvazione del dispositivo"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problema"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cerca nei metadati del registro. Il Gateway verifica nuovamente l\'attendibilità prima di qualsiasi download."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Usa un IP LAN privato per la configurazione locale oppure abilita Tailscale Serve / esponi un URL del gateway wss:// per l\'accesso remoto."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Invio in corso…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Account %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cerca"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verrà messa in quarantena \"%1$s\" e lo stato di Skill Workshop verrà aggiornato dal gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Registra nota vocale"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"In coda"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Risposto"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Consenti gli strumenti della fotocamera quando richiesto."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problemi"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Attivazione vocale"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Connetti il gateway per iniziare"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Avviso di sicurezza"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Altra risposta"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ignora avviso immagine condivisa"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Il Gateway ha valutato una release diversa di ClawHub. Esamina nuovamente la skill prima di installarla."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Apri accesso di sistema"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Controlla"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Leggi la posizione di questo telefono"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Chiave della skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Scade tra %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s installata."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Modalità di inoltro"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s altri"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Espandi l\'elenco di controllo del piano"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Browser"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Annullato"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Esecuzione in sospeso"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"AI personale sui tuoi dispositivi"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Provider e modelli"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nodo"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Scade tra %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prompt non disponibile"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Log"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Connetti il Gateway per esaminare le proposte di Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"コマンド argv JSON配列"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"この画像を読み取れませんでした。openclaw qr のQRが鮮明に写っているスクリーンショットまたは画像を選択してください。"</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshopの提案を%1$sできませんでした。"</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"他で回答済み"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway が承認を1回記録しました。"</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClawは、ペアリング済みのGatewayから要求された場合にのみ位置情報を確認します。アプリがバックグラウンドにある間も確認できるようにするには、次のAndroid画面で%1$sを選択してください。"</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ペアリング済みデバイス"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s件保留中"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"デバイス名"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"送信"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"位置情報"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"例: America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"セッションターゲット"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"一致する自動化はありません。"</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"スマートフォンが Gateway に到達できること"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"期限切れ"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway からスケジュールされた OpenClaw の作業。"</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"デバイスの承認を待っています"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"問題"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"レジストリのメタデータを検索します。Gatewayはダウンロード前に信頼性を再度検証します。"</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ローカル設定にはプライベートLAN IPを使用するか、リモートアクセス用にTailscale Serveを有効にするか、wss:// Gateway URLを公開してください。"</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"送信中…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"アカウント %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"検索"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"「%1$s」を隔離し、GatewayからSkill Workshopの状態を更新します。"</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ボイスノートを録音"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"待機中"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"回答済み"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"要求された場合にカメラツールを許可します。"</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"問題"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"音声ウェイク"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"開始するにはGatewayに接続してください"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"セキュリティに関する通知"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"その他の回答"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"共有画像の警告を閉じる"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gatewayが別のClawHubリリースを評価しました。インストールする前に、Skillをもう一度確認してください。"</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"システムアクセスを開く"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"確認"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"このスマートフォンの位置情報を読み取る"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skillキー"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"期限切れまで%1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$sをインストールしました。"</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"転送モード"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s 件追加"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"計画チェックリストを展開"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ブラウザ"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"キャンセル済み"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"実行保留中"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"あなたのデバイス上のパーソナル AI"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"プロバイダーとモデル"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ノード"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"期限切れまで%1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"プロンプトを利用できません"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ログ"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshopの提案を確認するには、Gatewayに接続してください。"</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"명령 argv JSON 배열"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"해당 이미지를 읽을 수 없습니다. openclaw qr의 QR이 선명하게 보이는 스크린샷이나 이미지를 선택하세요."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshop 제안을 %1$s하지 못했습니다."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"다른 곳에서 응답 완료"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway가 한 번 승인을 기록했습니다."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw는 페어링된 Gateway가 요청할 때만 위치를 확인합니다. 앱이 백그라운드에 있는 동안에도 위치를 확인할 수 있도록 다음 Android 화면에서 %1$s을 선택하세요."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"페어링된 기기"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s개 대기 중"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"기기 이름"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"제출"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"위치"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"예: America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"세션 대상"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"일치하는 자동화가 없습니다."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"휴대폰에서 Gateway에 연결할 수 있음"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"만료됨"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway에서 예약된 OpenClaw 작업."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"기기 승인 대기 중"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"문제"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"레지스트리 메타데이터를 검색합니다. Gateway는 다운로드 전에 신뢰성을 다시 확인합니다."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"로컬 설정에는 사설 LAN IP를 사용하거나, 원격 액세스를 위해 Tailscale Serve를 활성화하거나 wss:// Gateway URL을 노출하세요."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"제출 중…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"계정 %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"검색"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"이 작업은 \"%1$s\"을(를) 격리하고 Gateway에서 Skill Workshop 상태를 새로 고칩니다."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"음성 메모 녹음"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"대기 중"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"응답 완료"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"요청 시 카메라 도구를 허용합니다."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"문제"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"음성 깨우기"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"시작하려면 Gateway를 연결하세요"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"보안 알림"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"기타 답변"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"공유 이미지 경고 닫기"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway가 다른 ClawHub 릴리스를 평가했습니다. 설치하기 전에 해당 Skill을 다시 검토하세요."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"시스템 접근 권한 열기"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"확인"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"이 휴대전화의 위치 확인"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill 키"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s분 %2$s초 후 만료"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s을(를) 설치했습니다."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"전달 모드"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s개 더"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"계획 체크리스트 펼치기"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"브라우저"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"취소됨"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"실행 대기 중"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"기기에서 사용하는 개인 AI"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"제공업체 및 모델"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"노드"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s초 후 만료"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"프롬프트를 사용할 수 없음"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"로그"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshop 제안을 확인하려면 Gateway를 연결하세요."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Commando argv JSON-array"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kan die afbeelding niet lezen. Kies een duidelijke schermafbeelding of afbeelding van de QR-code van openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kan het Skill Workshop-voorstel niet %1$s."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Elders beantwoord"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway heeft eenmalig goedkeuring vastgelegd."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw controleert je locatie alleen wanneer je gekoppelde Gateway daarom vraagt. Kies op het volgende Android-scherm %1$s om controles toe te staan terwijl de app op de achtergrond actief is."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gekoppelde apparaten"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s openstaand"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Apparaatnaam"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Indienen"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Locatie"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"bijv. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sessiedoel"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Geen overeenkomende automatiseringen."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Telefoon kan de Gateway bereiken"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verlopen"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gepland OpenClaw-werk vanaf je Gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Wachten op apparaatgoedkeuring"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Probleem"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Doorzoek registermetadata. De Gateway verifieert de betrouwbaarheid opnieuw vóór elke download."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gebruik een privé-IP-adres op het LAN voor lokale configuratie, of schakel Tailscale Serve in / maak een wss://-gateway-URL beschikbaar voor externe toegang."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Bezig met indienen…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Account %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Zoeken"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Hiermee wordt \"%1$s\" in quarantaine geplaatst en wordt de status van Skill Workshop vernieuwd vanuit de gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Spraakbericht opnemen"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"In wachtrij"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Beantwoord"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cameratools toestaan wanneer daarom wordt gevraagd."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problemen"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Spraakactivering"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verbind de Gateway om te beginnen"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Beveiligingsmelding"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ander antwoord"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Waarschuwing gedeelde afbeelding sluiten"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"De Gateway heeft een andere ClawHub-release beoordeeld. Bekijk de skill opnieuw voordat u deze installeert."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Systeemtoegang openen"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Controleren"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Locatie van deze telefoon lezen"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill-sleutel"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verloopt over %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s is geïnstalleerd."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Doorstuurmodus"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s meer"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Checklist van plan uitklappen"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Browser"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Geannuleerd"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Uitvoering in behandeling"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Persoonlijke AI op je apparaten"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Providers en modellen"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Knooppunt"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verloopt over %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prompt niet beschikbaar"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Logboeken"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verbind de Gateway om voorstellen van Skill Workshop te bekijken."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tablica JSON argv polecenia"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nie udało się odczytać tego obrazu. Wybierz wyraźny zrzut ekranu lub obraz kodu QR z openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nie udało się wykonać działania %1$s dla propozycji Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Odpowiedziano gdzie indziej"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway zarejestrował zatwierdzenie jednorazowo."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw sprawdza lokalizację tylko wtedy, gdy zażąda tego sparowany Gateway. Na następnym ekranie systemu Android wybierz %1$s, aby zezwolić na sprawdzanie, gdy aplikacja działa w tle."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sparowane urządzenia"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s oczekujących"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nazwa urządzenia"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prześlij"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Lokalizacja"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"np. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cel sesji"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Brak pasujących automatyzacji."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Telefon może połączyć się z Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Wygasło"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Zaplanowane zadania OpenClaw z Twojego gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Oczekiwanie na zatwierdzenie urządzenia"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problem"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Przeszukuj metadane rejestru. Gateway ponownie weryfikuje zaufanie przed każdym pobraniem."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Do konfiguracji lokalnej użyj prywatnego adresu IP w sieci LAN albo włącz Tailscale Serve / udostępnij adres URL bramy Gateway z protokołem wss://, aby umożliwić dostęp zdalny."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Przesyłanie…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Konto %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Szukaj"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Spowoduje to poddanie propozycji „%1$s” kwarantannie i odświeżenie stanu Skill Workshop z gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nagraj notatkę głosową"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"W kolejce"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Udzielono odpowiedzi"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Zezwalaj na używanie narzędzi aparatu na żądanie."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problemy"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Wybudzanie głosem"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Połącz Gateway, aby rozpocząć"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Informacja o bezpieczeństwie"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Inna odpowiedź"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Odrzuć ostrzeżenie o udostępnionym obrazie"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway ocenił inne wydanie ClawHub. Sprawdź umiejętność ponownie przed instalacją."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Otwórz dostęp systemowy"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sprawdź"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Odczytuj lokalizację tego telefonu"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Klucz Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Wygasa za %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Zainstalowano %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tryb przekazywania"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s więcej"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Rozwiń listę kontrolną planu"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Przeglądarka"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Anulowano"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Oczekujące uruchomienie"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Osobista AI na Twoich urządzeniach"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dostawcy i modele"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Węzeł"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Wygasa za %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Polecenie niedostępne"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dzienniki"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Połącz Gateway, aby sprawdzić propozycje Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Array JSON argv do comando"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Não foi possível ler essa imagem. Escolha uma captura de tela nítida ou uma imagem do QR gerado por openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Não foi possível %1$s a proposta do Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Respondido em outro lugar"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"O Gateway registrou a aprovação uma vez."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"O OpenClaw só verifica a localização quando o Gateway pareado solicita. Na próxima tela do Android, escolha %1$s para permitir verificações enquanto o app estiver em segundo plano."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dispositivos pareados"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s pendentes"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nome do dispositivo"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Enviar"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Localização"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ex.: America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Destino da sessão"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nenhuma automação correspondente."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"O telefone pode acessar o Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expirado"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Trabalho agendado do OpenClaw a partir do seu gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Aguardando aprovação do dispositivo"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problema"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Pesquise os metadados do registro. O Gateway verifica novamente a confiabilidade antes de qualquer download."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Use um IP de LAN privada para a configuração local ou ative o Tailscale Serve / exponha uma URL de gateway wss:// para acesso remoto."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Enviando…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Conta %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Pesquisar"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Isso colocará \"%1$s\" em quarentena e atualizará o estado do Skill Workshop com base no gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gravar nota de voz"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Na fila"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Respondido"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Permitir ferramentas de câmera quando solicitado."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problemas"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ativação por voz"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Conecte o Gateway para começar"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Aviso de segurança"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Outra resposta"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dispensar aviso de imagem compartilhada"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"O Gateway avaliou uma versão diferente do ClawHub. Revise a skill novamente antes de instalar."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Abrir acesso do sistema"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Verificar"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ler a localização deste telefone"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Chave da Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expira em %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s foi instalada."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Modo de encaminhamento"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s mais"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expandir lista de verificação do plano"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Navegador"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cancelado"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Execução pendente"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"IA pessoal nos seus dispositivos"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Provedores e modelos"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nó"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expira em %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prompt indisponível"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Logs"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Conecte o Gateway para inspecionar as propostas do Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"JSON-массив argv команды"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Не удалось прочитать это изображение. Выберите чёткий снимок экрана или изображение QR-кода из openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Не удалось выполнить действие %1$s с предложением Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Отвечено в другом месте"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway зафиксировал одобрение однократно."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw проверяет местоположение, только когда его запрашивает сопряжённый Gateway. На следующем экране Android выберите %1$s, чтобы разрешить проверки, когда приложение работает в фоновом режиме."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Сопряженные устройства"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s ожидают подтверждения"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Имя устройства"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Отправить"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Местоположение"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"например America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Целевая сессия"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Подходящие автоматизации не найдены."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Телефон может подключиться к Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Срок истёк"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Запланированная работа OpenClaw с вашего gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ожидание подтверждения устройства"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Проблема"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ищите в метаданных реестра. Gateway повторно проверяет надёжность перед каждой загрузкой."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Для локальной настройки используйте частный IP-адрес локальной сети, а для удалённого доступа включите Tailscale Serve или откройте доступ к URL-адресу Gateway с wss://."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Отправка…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Учётная запись %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Поиск"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Предложение \"%1$s\" будет помещено в карантин, а состояние Skill Workshop будет обновлено из Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Записать голосовое сообщение"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"В очереди"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Отвечено"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Разрешать доступ к камере по запросу."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Проблемы"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Голосовая активация"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Подключите Gateway, чтобы начать"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Уведомление о безопасности"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Другой ответ"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Закрыть предупреждение об общем изображении"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway проверил другой выпуск ClawHub. Ещё раз просмотрите навык перед установкой."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Открыть системный доступ"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Проверить"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Получать местоположение этого телефона"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ключ Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Истекает через %1$s мин %2$s с"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Установлен %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Режим пересылки"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s еще"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Развернуть контрольный список плана"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Браузер"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Отменено"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Запуск ожидается"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Персональный ИИ на ваших устройствах"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Провайдеры и модели"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Узел"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Истекает через %1$s с"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Запрос недоступен"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Журналы"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Подключите Gateway, чтобы просмотреть предложения Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kommando argv JSON-array"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Det gick inte att läsa bilden. Välj en tydlig skärmbild eller bild av QR-koden från openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Det gick inte att %1$s förslaget från Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Besvarad på annat håll"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway registrerade godkännandet en gång."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw kontrollerar bara platsen när din parkopplade Gateway begär det. På nästa Android-skärm väljer du %1$s för att tillåta kontroller medan appen körs i bakgrunden."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Parkopplade enheter"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s väntande"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Enhetsnamn"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skicka"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Plats"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"t.ex. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sessionsmål"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Inga matchande automatiseringar."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Telefonen kan nå Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Utgången"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Schemalagt OpenClaw-arbete från din gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Väntar på enhetsgodkännande"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problem"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sök i registrets metadata. Gateway verifierar tilliten igen före varje nedladdning."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Använd en privat LAN-IP-adress för lokal konfiguration eller aktivera Tailscale Serve / exponera en Gateway-URL med wss:// för fjärråtkomst."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skickar…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Konto %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sök"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Detta sätter \"%1$s\" i karantän och uppdaterar statusen för Skill Workshop från Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Spela in röstmeddelande"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"I kö"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Besvarad"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tillåt kameraverktyg på begäran."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Problem"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Röstaktivering"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Anslut Gateway för att börja"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Säkerhetsmeddelande"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Annat svar"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Avfärda varning om delad bild"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway utvärderade en annan ClawHub-version. Granska denna skill igen innan du installerar."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Öppna systemåtkomst"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kontrollera"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Läs av den här telefonens plats"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill-nyckel"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Upphör om %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Installerade %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Vidarebefordringsläge"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s till"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Visa planens checklista"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Webbläsare"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Avbruten"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Körning väntar"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Personlig AI på dina enheter"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Leverantörer och modeller"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nod"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Upphör om %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prompten är inte tillgänglig"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Loggar"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Anslut Gateway för att granska förslag från Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"อาร์เรย์ JSON ของ argv คำสั่ง"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ไม่สามารถอ่านรูปภาพนั้นได้ โปรดเลือกภาพหน้าจอหรือรูปภาพคิวอาร์จาก openclaw qr ที่ชัดเจน"</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ไม่สามารถ %1$s ข้อเสนอ Skill Workshop ได้"</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ตอบจากที่อื่นแล้ว"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway บันทึกการอนุมัติหนึ่งครั้ง"</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw จะตรวจสอบตำแหน่งเมื่อ Gateway ที่จับคู่ไว้ร้องขอเท่านั้น ในหน้าจอ Android ถัดไป ให้เลือก %1$s เพื่ออนุญาตให้ตรวจสอบขณะที่แอปทำงานอยู่เบื้องหลัง"</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"อุปกรณ์ที่จับคู่แล้ว"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"รอดำเนินการ %1$s รายการ"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ชื่ออุปกรณ์"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ส่ง"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ตำแหน่งที่ตั้ง"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"เช่น America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"เป้าหมายเซสชัน"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ไม่พบระบบอัตโนมัติที่ตรงกัน"</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"โทรศัพท์สามารถเข้าถึง Gateway ได้"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"หมดอายุแล้ว"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"งาน OpenClaw ที่กำหนดเวลาไว้จาก gateway ของคุณ"</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"กำลังรอการอนุมัติอุปกรณ์"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ปัญหา"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ค้นหาข้อมูลเมตาของรีจิสทรี โดย Gateway จะตรวจสอบความน่าเชื่อถืออีกครั้งก่อนดาวน์โหลด"</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ใช้ IP ของ LAN ส่วนตัวสำหรับการตั้งค่าภายในเครื่อง หรือเปิดใช้งาน Tailscale Serve / เปิดเผย URL ของ Gateway แบบ wss:// สำหรับการเข้าถึงจากระยะไกล"</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"กำลังส่ง…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"บัญชี %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ค้นหา"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"การดำเนินการนี้จะกักกัน \"%1$s\" และรีเฟรชสถานะ Skill Workshop จาก Gateway"</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"บันทึกข้อความเสียง"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"อยู่ในคิว"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ตอบแล้ว"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"อนุญาตให้ใช้เครื่องมือกล้องเมื่อมีการร้องขอ"</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ปัญหา"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"การปลุกด้วยเสียง"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"เชื่อมต่อ Gateway เพื่อเริ่มต้น"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ประกาศด้านความปลอดภัย"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"คำตอบอื่น"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ปิดคำเตือนรูปภาพที่แชร์"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway ประเมิน ClawHub รุ่นอื่น โปรดตรวจสอบ Skill อีกครั้งก่อนติดตั้ง"</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"เปิดการเข้าถึงระบบ"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ตรวจสอบ"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"อ่านตำแหน่งของโทรศัพท์เครื่องนี้"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"คีย์ของ Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"หมดอายุใน %1$s นาที %2$s วินาที"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ติดตั้ง %1$s แล้ว"</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"โหมดการส่งต่อ"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s เพิ่มเติม"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ขยายรายการตรวจสอบแผน"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"เบราว์เซอร์"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ยกเลิกแล้ว"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"รอการเรียกใช้"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"AI ส่วนตัวบนอุปกรณ์ของคุณ"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ผู้ให้บริการและโมเดล"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"โหนด"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"หมดอายุใน %1$s วินาที"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ไม่มีพรอมต์"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"บันทึก"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"เชื่อมต่อ Gateway เพื่อตรวจสอบข้อเสนอ Skill Workshop"</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Komut argv JSON dizisi"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Bu görüntü okunamadı. openclaw qr tarafından oluşturulan QR kodunun net bir ekran görüntüsünü veya görselini seçin."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshop teklifi için %1$s işlemi gerçekleştirilemedi."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Başka yerde yanıtlandı"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway onayı bir kez kaydetti."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw, konumu yalnızca eşleştirilmiş Gateway\'iniz istediğinde kontrol eder. Uygulama arka plandayken kontrollere izin vermek için bir sonraki Android ekranında %1$s seçeneğini belirleyin."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Eşlenmiş Cihazlar"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s bekliyor"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cihaz adı"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gönder"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Konum"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"örn. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Oturum hedefi"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Eşleşen otomasyon yok."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Telefon Gateway\'ye erişebiliyor"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Süresi doldu"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway\'inizden zamanlanmış OpenClaw işleri."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cihaz onayı bekleniyor"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sorun"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kayıt defteri meta verilerinde arama yapın. Gateway, herhangi bir indirmeden önce güveni tekrar doğrular."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Yerel kurulum için özel bir LAN IP\'si kullanın veya uzaktan erişim için Tailscale Serve\'ü etkinleştirin / bir wss:// gateway URL\'sini kullanıma açın."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gönderiliyor…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Hesap %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ara"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Bu işlem \"%1$s\" teklifini karantinaya alacak ve Skill Workshop durumunu Gateway\'den yenileyecek."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sesli not kaydet"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sıraya alındı"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Yanıtlandı"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"İstendiğinde kamera araçlarına izin ver."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sorunlar"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sesle Uyandırma"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Başlamak için Gateway\'i bağlayın"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Güvenlik bildirimi"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Diğer yanıt"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Paylaşılan görüntü uyarısını kapat"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway farklı bir ClawHub sürümünü değerlendirdi. Yüklemeden önce Skill\'i tekrar inceleyin."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sistem Erişimini Aç"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kontrol et"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Bu telefonun konumunu okuyun"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Anahtarı"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sona ermesine %1$s dk %2$s sn"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s yüklendi."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway\'ler"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Yönlendirme Modu"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s daha"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Plan kontrol listesini genişlet"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tarayıcı"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"İptal edildi"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Çalıştırma Bekliyor"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cihazlarınızda kişisel AI"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sağlayıcılar ve Modeller"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Düğüm"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sona ermesine %1$s sn"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"İstem kullanılamıyor"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Günlükler"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Workshop önerilerini incelemek için Gateway\'e bağlanın."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"JSON-масив argv команди"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Не вдалося прочитати це зображення. Виберіть чіткий знімок екрана або зображення QR-коду з openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Не вдалося %1$s пропозицію Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Відповідь надано в іншому місці"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway зафіксував схвалення один раз."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw перевіряє місцезнаходження, лише коли цього запитує підключений Gateway. На наступному екрані Android виберіть %1$s, щоб дозволити перевірки, коли застосунок працює у фоновому режимі."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Спарені пристрої"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s очікують"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Назва пристрою"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Надіслати"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Місцезнаходження"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"напр. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ціль сеансу"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Відповідних автоматизацій не знайдено."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Телефон може підключитися до Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Термін дії минув"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Запланована робота OpenClaw з вашого gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Очікування схвалення пристрою"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Проблема"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Пошук у метаданих реєстру. Gateway повторно перевіряє надійність перед будь-яким завантаженням."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Для локального налаштування використовуйте приватну IP-адресу локальної мережі або ввімкніть Tailscale Serve / надайте доступ до URL-адреси шлюзу wss:// для віддаленого доступу."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Надсилання…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Обліковий запис %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Пошук"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Буде ізольовано \"%1$s\", а стан Майстерні навичок оновлено з Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Записати голосове повідомлення"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"У черзі"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Відповідь надано"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Дозволяти інструменти камери за запитом."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Проблеми"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Голосова активація"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Підключіть Gateway, щоб почати"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Повідомлення про безпеку"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Інша відповідь"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Закрити попередження про спільне зображення"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway перевірив інший випуск ClawHub. Перегляньте навичку ще раз перед установленням."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Відкрити системний доступ"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Перевірити"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Отримувати дані про місцезнаходження цього телефону"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ключ навички"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Залишилося %1$s хв %2$s с"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Установлено %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Шлюзи"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Режим пересилання"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s ще"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Розгорнути контрольний список плану"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Браузер"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Скасовано"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Очікує запуску"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Персональний ШІ на ваших пристроях"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Постачальники й моделі"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Вузол"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Залишилося %1$s с"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Запит недоступний"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Журнали"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Підключіть Gateway, щоб переглянути пропозиції Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Mảng JSON argv của lệnh"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Không thể đọc hình ảnh đó. Hãy chọn ảnh chụp màn hình rõ nét hoặc hình ảnh mã QR từ openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Không thể %1$s đề xuất Skill Workshop."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đã trả lời ở nơi khác"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway đã ghi nhận phê duyệt một lần."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw chỉ kiểm tra vị trí khi Gateway đã ghép nối yêu cầu. Trên màn hình Android tiếp theo, hãy chọn %1$s để cho phép kiểm tra khi ứng dụng chạy trong nền."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Thiết bị đã ghép đôi"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s đang chờ"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tên thiết bị"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gửi"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Vị trí"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"ví dụ: America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đích phiên"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Không có tác vụ tự động nào phù hợp."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Điện thoại có thể truy cập Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đã hết hạn"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Công việc OpenClaw đã lên lịch từ gateway của bạn."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đang chờ phê duyệt thiết bị"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sự cố"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tìm kiếm siêu dữ liệu trong registry. Gateway sẽ xác minh lại độ tin cậy trước mọi lượt tải xuống."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sử dụng địa chỉ IP LAN riêng để thiết lập cục bộ, hoặc bật Tailscale Serve / công khai URL Gateway wss:// để truy cập từ xa."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đang gửi…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tài khoản %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Tìm kiếm"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Thao tác này sẽ cách ly \"%1$s\" và làm mới trạng thái Skill Workshop từ Gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Ghi ghi chú thoại"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đang chờ"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đã trả lời"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cho phép dùng công cụ camera khi được yêu cầu."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sự cố"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đánh thức bằng giọng nói"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kết nối Gateway để bắt đầu"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Thông báo bảo mật"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Câu trả lời khác"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Bỏ qua cảnh báo hình ảnh được chia sẻ"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway đã đánh giá một bản phát hành ClawHub khác. Hãy xem lại skill trước khi cài đặt."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Mở quyền truy cập hệ thống"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kiểm tra"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đọc vị trí của điện thoại này"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Khóa Skill"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Hết hạn sau %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đã cài đặt %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Chế độ chuyển tiếp"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s nữa"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Mở rộng danh sách kiểm tra kế hoạch"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Trình duyệt"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Đã hủy"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Lượt chạy đang chờ"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"AI cá nhân trên các thiết bị của bạn"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nhà cung cấp & Mô hình"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nút"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Hết hạn sau %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Câu lệnh không khả dụng"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Nhật ký"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Kết nối Gateway để kiểm tra các đề xuất Skill Workshop."</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"命令 argv JSON 数组"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"无法读取该图片。请选择清晰的截图或 openclaw qr 所生成二维码的图片。"</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"无法 %1$s Skill Workshop 提案。"</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已在其他地方回答"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway 已记录一次批准。"</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw 仅在已配对的 Gateway 请求时检查位置。在下一个 Android 屏幕上,选择 %1$s,以允许应用在后台运行时进行检查。"</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已配对设备"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s 项待审批"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"设备名称"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"提交"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"位置"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"例如 America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"会话目标"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"没有匹配的自动化。"</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"手机可以连接到 Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已过期"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"来自你的 gateway 的已计划 OpenClaw 工作。"</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"正在等待设备批准"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"问题"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"搜索注册表元数据。Gateway 会在下载前再次验证信任状态。"</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"本地设置请使用专用局域网 IP;远程访问请启用 Tailscale Serve,或公开一个 wss:// Gateway URL。"</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"正在提交…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"账户 %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"搜索"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"这将隔离“%1$s”,并从 Gateway 刷新 Skill Workshop 状态。"</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"录制语音留言"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已排队"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已回答"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"在请求时允许使用相机工具。"</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"问题"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"语音唤醒"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"连接 Gateway 以开始"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"安全提示"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"其他答案"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"关闭共享图片警告"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway 评估了不同的 ClawHub 版本。请重新检查该 Skill 后再安装。"</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"打开系统访问权限"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"检查"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"读取此手机的位置信息"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill 键"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s分%2$s秒后过期"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已安装 %1$s。"</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"转发模式"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s 个更多"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"展开计划清单"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"浏览器"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已取消"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"运行待处理项"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"您设备上的个人 AI"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"提供商与模型"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"节点"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s秒后过期"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"提示词不可用"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"日志"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"连接 Gateway 以检查 Skill Workshop 提案。"</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"命令 argv JSON 陣列"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"無法讀取該圖片。請選擇清晰的螢幕截圖或 openclaw qr 所產生的 QR 圖片。"</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"無法%1$s Skill Workshop 提案。"</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已在其他地方回答"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway 已記錄核准一次。"</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw 只會在已配對的 Gateway 要求時檢查位置。在下一個 Android 畫面上,選擇 %1$s,以允許應用程式在背景執行時檢查位置。"</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已配對裝置"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s 個待核准項目"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"裝置名稱"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"提交"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"位置"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"例如 America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"工作階段目標"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"沒有相符的自動化。"</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"手機可以連線到 Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已過期"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"來自您 gateway 的已排程 OpenClaw 工作。"</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"正在等待裝置核准"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"問題"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"搜尋登錄中繼資料。Gateway 會在下載前再次驗證信任狀態。"</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"本機設定請使用私人區域網路 IP;若要遠端存取,請啟用 Tailscale Serve,或公開一個 wss:// Gateway URL。"</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"正在提交…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"帳戶 %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"搜尋"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"這將隔離「%1$s」,並從 Gateway 重新整理 Skill Workshop 狀態。"</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"錄製語音留言"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已排入佇列"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已回答"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"在要求時允許使用相機工具。"</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"問題"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"語音喚醒"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"連接 Gateway 以開始"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"安全性通知"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"其他答案"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"關閉分享圖片警告"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway 評估了不同的 ClawHub 發行版本。請在安裝前再次檢閱此技能。"</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"開啟系統存取權限"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"檢查"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"讀取此手機的位置"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill 金鑰"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"將於 %1$sm %2$ss 後過期"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已安裝 %1$s。"</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"轉發模式"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... 另有 %1$s 個"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"展開計畫檢查清單"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"瀏覽器"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"已取消"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"執行待處理項目"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"您裝置上的個人 AI"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"提供者與模型"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"節點"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"將於 %1$ss 後過期"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"提示詞無法使用"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"記錄"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"請連線至 Gateway 以檢查 Skill Workshop 提案。"</string>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<string name="native_0692ff7848b2cfc2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Command argv JSON array"</string>
|
||||
<string name="native_06ee275ccdbbf862" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Could not read that image. Choose a clear screenshot or image of the QR from openclaw qr."</string>
|
||||
<string name="native_06fcc3f73af64b5b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Could not %1$s Skill Workshop proposal."</string>
|
||||
<string name="native_071c496aa34a4fd5" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Answered elsewhere"</string>
|
||||
<string name="native_0728ba404eb8f8bd" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway recorded approval once."</string>
|
||||
<string name="native_073c1634c496cdb6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"status"</string>
|
||||
<string name="native_07bcb0f3c324a358" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"OpenClaw only checks location when your paired Gateway requests it. On the next Android screen, choose %1$s to allow checks while the app is in the background."</string>
|
||||
@@ -129,6 +130,7 @@
|
||||
<string name="native_1485bc093cdbef6d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Paired Devices"</string>
|
||||
<string name="native_1520b570498730a0" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s pending"</string>
|
||||
<string name="native_155106be1173d757" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Device name"</string>
|
||||
<string name="native_155f816c0407310c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Submit"</string>
|
||||
<string name="native_15b61974b2707a7b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Location"</string>
|
||||
<string name="native_16196f98cecd6839" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"e.g. America/New_York"</string>
|
||||
<string name="native_16524d7409d37507" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session target"</string>
|
||||
@@ -382,6 +384,7 @@
|
||||
<string name="native_41cb8a2ca2cf1e36" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"No matching automations."</string>
|
||||
<string name="native_41df9f586511e084" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Phone can reach the Gateway"</string>
|
||||
<string name="native_41ed52921661c7f0" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateway"</string>
|
||||
<string name="native_424a2551d356754c" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expired"</string>
|
||||
<string name="native_4287d54ad5e78985" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Scheduled OpenClaw work from your gateway."</string>
|
||||
<string name="native_429f6057535c1ea3" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Sub-agent"</string>
|
||||
<string name="native_42e3a99c21767def" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Waiting for device approval"</string>
|
||||
@@ -411,6 +414,7 @@
|
||||
<string name="native_48dc76dfa23ae03f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Issue"</string>
|
||||
<string name="native_48f1756061ba5373" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Search registry metadata. The Gateway verifies trust again before any download."</string>
|
||||
<string name="native_4902797187db2367" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Use a private LAN IP for local setup, or enable Tailscale Serve / expose a wss:// gateway URL for remote access."</string>
|
||||
<string name="native_49195f559e4a9f5b" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Submitting…"</string>
|
||||
<string name="native_4928615473a8ea60" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Account %1$s"</string>
|
||||
<string name="native_49bb137d12031cbc" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Suggest Task"</string>
|
||||
<string name="native_49c266baaaa70981" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Search"</string>
|
||||
@@ -584,6 +588,7 @@
|
||||
<string name="native_65b001ea12846d0c" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"This will quarantine \"%1$s\" and refresh Skill Workshop state from the gateway."</string>
|
||||
<string name="native_65d45123f8f45969" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Record voice note"</string>
|
||||
<string name="native_661ff40a07e037bb" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Queued"</string>
|
||||
<string name="native_665590354e719bcf" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Answered"</string>
|
||||
<string name="native_6657cc73af00feac" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Allow camera tools when requested."</string>
|
||||
<string name="native_666067dd376e5d45" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Issues"</string>
|
||||
<string name="native_666c8bc0319f3b54" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Voice Wake"</string>
|
||||
@@ -702,6 +707,7 @@
|
||||
<string name="native_7703e4288a7ca2db" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Session Settings"</string>
|
||||
<string name="native_7718167ea9c39713" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Connect gateway to start"</string>
|
||||
<string name="native_7728331d12fcfc1a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Security notice"</string>
|
||||
<string name="native_773e7729b237d4ea" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Other answer"</string>
|
||||
<string name="native_7765ad1890a192a8" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Dismiss shared-image warning"</string>
|
||||
<string name="native_779aa3df405560c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"The Gateway evaluated a different ClawHub release. Review the skill again before installing."</string>
|
||||
<string name="native_77ab6bb71f77154d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Open System Access"</string>
|
||||
@@ -916,6 +922,7 @@
|
||||
<string name="native_9d60841e0a7891df" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Check"</string>
|
||||
<string name="native_9d988c13c67e88e6" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Read this phone\'s location"</string>
|
||||
<string name="native_9db0df0afb47b562" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Skill Key"</string>
|
||||
<string name="native_9dca3c2b01b40c0b" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expires in %1$sm %2$ss"</string>
|
||||
<string name="native_9e37aa0704a3cf44" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Installed %1$s."</string>
|
||||
<string name="native_9e4635769f0aaf0d" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Gateways"</string>
|
||||
<string name="native_9e9bef85bb320b81" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Forwarding Mode"</string>
|
||||
@@ -1213,6 +1220,7 @@
|
||||
<string name="native_d2ee46edf00c8eb6" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"... +%1$s more"</string>
|
||||
<string name="native_d30f33731adb6b40" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expand plan checklist"</string>
|
||||
<string name="native_d31de1a5c5c8ba2a" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Browser"</string>
|
||||
<string name="native_d353a99eb4556847" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Cancelled"</string>
|
||||
<string name="native_d354ee936f59115e" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"screen record"</string>
|
||||
<string name="native_d3974fd6d3b6c81f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Run Pending"</string>
|
||||
<string name="native_d3f75e24411053c4" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Personal AI on your devices"</string>
|
||||
@@ -1335,6 +1343,7 @@
|
||||
<string name="native_e89bdf093e8a6756" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Providers & Models"</string>
|
||||
<string name="native_e93372533f323b2f" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Node"</string>
|
||||
<string name="native_e98feb46bd5d7792" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"%1$s "</string>
|
||||
<string name="native_e9e3a4b23345cb57" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Expires in %1$ss"</string>
|
||||
<string name="native_ea14eb224e89a810" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Prompt unavailable"</string>
|
||||
<string name="native_ea2100dc89ae9fe2" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Logs"</string>
|
||||
<string name="native_ea5cddf7a04d7b94" formatted="false" tools:ignore="Typos,TypographyDashes,TypographyEllipsis">"Connect the gateway to inspect Skill Workshop proposals."</string>
|
||||
|
||||
@@ -291,6 +291,7 @@ class GatewayBootstrapAuthTest {
|
||||
listOf(
|
||||
"operator.admin",
|
||||
"operator.approvals",
|
||||
"operator.questions",
|
||||
"operator.read",
|
||||
"operator.talk.secrets",
|
||||
"operator.write",
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
package ai.openclaw.app.chat
|
||||
|
||||
import ai.openclaw.app.gateway.Question
|
||||
import ai.openclaw.app.gateway.QuestionListResult
|
||||
import ai.openclaw.app.gateway.QuestionOption
|
||||
import ai.openclaw.app.gateway.QuestionRecord
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceTimeBy
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class ChatQuestionTest {
|
||||
private val question =
|
||||
Question(
|
||||
id = "meal",
|
||||
header = "Meal",
|
||||
question = "Choose dinner",
|
||||
options = listOf(QuestionOption("Pizza"), QuestionOption("Tacos")),
|
||||
multiSelect = true,
|
||||
isOther = true,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun multiSelectAnswersFollowDeclaredOrderAndIncludeOther() {
|
||||
val draft =
|
||||
ChatQuestionDraft()
|
||||
.toggle(question, "Tacos")
|
||||
.toggle(question, "Pizza")
|
||||
.setOther(question, " Salad ")
|
||||
|
||||
assertEquals(mapOf("meal" to listOf("Pizza", "Tacos", "Salad")), draft.answers(listOf(question)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun statusDistinguishesLocalRemoteAndExpiry() {
|
||||
val record = record(status = "pending", expiresAtMs = 2_000)
|
||||
assertEquals(ChatQuestionStatus.Expired, ChatQuestionPrompt(record).status(nowMs = 2_000))
|
||||
assertEquals(
|
||||
ChatQuestionStatus.AnsweredElsewhere,
|
||||
ChatQuestionPrompt(record.copy(status = "answered")).status(nowMs = 1_000),
|
||||
)
|
||||
assertEquals(
|
||||
ChatQuestionStatus.Answered,
|
||||
ChatQuestionPrompt(record.copy(status = "answered"), answeredLocally = true).status(nowMs = 1_000),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun terminalPromptRetentionMatchesGatewayGrace() {
|
||||
val prompt =
|
||||
ChatQuestionPrompt(
|
||||
record = record(status = "answered"),
|
||||
terminalObservedAtMs = 1_000,
|
||||
)
|
||||
|
||||
assertTrue(prompt.shouldRetainAfterList(15_999))
|
||||
assertFalse(prompt.shouldRetainAfterList(16_000))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sessionFilterKeepsGlobalAndCurrentPrompts() {
|
||||
val prompts =
|
||||
listOf(
|
||||
ChatQuestionPrompt(record(sessionKey = null)),
|
||||
ChatQuestionPrompt(record(id = "current", sessionKey = "agent:main:main")),
|
||||
ChatQuestionPrompt(record(id = "other", sessionKey = "agent:main:other")),
|
||||
ChatQuestionPrompt(record(id = "foreign-main", sessionKey = "main", agentId = "other")),
|
||||
)
|
||||
val visible = questionsForSession(prompts, "main", "agent:main:main", "main")
|
||||
assertEquals(listOf("ask_123", "current"), visible.map { it.record.id })
|
||||
assertTrue(visible.all { it.status(1_000) == ChatQuestionStatus.Pending })
|
||||
}
|
||||
|
||||
@Test
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun staleQuestionListCannotOverwriteNewerEvent() =
|
||||
runTest {
|
||||
val listStarted = CompletableDeferred<Unit>()
|
||||
val listResponse = CompletableDeferred<String>()
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
var listCallCount = 0
|
||||
val controller =
|
||||
ChatController(
|
||||
scope = this,
|
||||
json = json,
|
||||
requestGateway = { method, _ ->
|
||||
if (method == "question.list") {
|
||||
listCallCount += 1
|
||||
if (listCallCount == 1) {
|
||||
listStarted.complete(Unit)
|
||||
listResponse.await()
|
||||
} else {
|
||||
json.encodeToString(QuestionListResult(listOf(record(id = "ask_new"))))
|
||||
}
|
||||
} else {
|
||||
"{}"
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
controller.handleGatewayEvent("health", null)
|
||||
runCurrent()
|
||||
listStarted.await()
|
||||
controller.handleGatewayEvent("question.requested", json.encodeToString(record(id = "ask_new")))
|
||||
listResponse.complete(json.encodeToString(QuestionListResult(listOf(record(id = "ask_old")))))
|
||||
advanceUntilIdle()
|
||||
|
||||
assertEquals(listOf("ask_new"), controller.questions.value.map { it.record.id })
|
||||
}
|
||||
|
||||
@Test
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun pendingRefreshPreservesSubmissionLock() =
|
||||
runTest {
|
||||
val resolveStarted = CompletableDeferred<Unit>()
|
||||
val resolveResponse = CompletableDeferred<String>()
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
val pending = record(expiresAtMs = Long.MAX_VALUE)
|
||||
val controller =
|
||||
ChatController(
|
||||
scope = this,
|
||||
json = json,
|
||||
requestGateway = { method, _ ->
|
||||
when (method) {
|
||||
"question.list" -> json.encodeToString(QuestionListResult(listOf(pending.copy(createdAtMs = 2_000))))
|
||||
"question.resolve" -> {
|
||||
resolveStarted.complete(Unit)
|
||||
resolveResponse.await()
|
||||
}
|
||||
else -> "{}"
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
controller.handleGatewayEvent("question.requested", json.encodeToString(pending))
|
||||
controller.resolveQuestion(pending.id, mapOf("meal" to listOf("Pizza")))
|
||||
runCurrent()
|
||||
resolveStarted.await()
|
||||
controller.handleGatewayEvent("health", null)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(
|
||||
ChatQuestionStatus.Submitting,
|
||||
controller.questions.value
|
||||
.single()
|
||||
.status(nowMs = 3_000),
|
||||
)
|
||||
assertFalse(
|
||||
controller.questions.value
|
||||
.single()
|
||||
.answeredLocally,
|
||||
)
|
||||
resolveResponse.complete("{}")
|
||||
advanceUntilIdle()
|
||||
}
|
||||
|
||||
@Test
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun resolvedEventReconcilesAfterDiscardingOlderList() =
|
||||
runTest {
|
||||
val firstListStarted = CompletableDeferred<Unit>()
|
||||
val firstListResponse = CompletableDeferred<String>()
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
var listCallCount = 0
|
||||
val controller =
|
||||
ChatController(
|
||||
scope = this,
|
||||
json = json,
|
||||
requestGateway = { method, _ ->
|
||||
if (method != "question.list") {
|
||||
"{}"
|
||||
} else {
|
||||
listCallCount += 1
|
||||
if (listCallCount == 1) {
|
||||
firstListStarted.complete(Unit)
|
||||
firstListResponse.await()
|
||||
} else {
|
||||
json.encodeToString(QuestionListResult(listOf(record(id = "ask_other"))))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
controller.handleGatewayEvent("health", null)
|
||||
runCurrent()
|
||||
firstListStarted.await()
|
||||
controller.handleGatewayEvent(
|
||||
"question.resolved",
|
||||
"""{"id":"ask_done","status":"answered"}""",
|
||||
)
|
||||
runCurrent()
|
||||
firstListResponse.complete(json.encodeToString(QuestionListResult(listOf(record(id = "ask_done")))))
|
||||
advanceUntilIdle()
|
||||
|
||||
assertEquals(listOf("ask_other"), controller.questions.value.map { it.record.id })
|
||||
}
|
||||
|
||||
@Test
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun questionListRetainsRecentlyResolvedCard() =
|
||||
runTest {
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
val pending = record(id = "ask_done", expiresAtMs = Long.MAX_VALUE)
|
||||
val controller =
|
||||
ChatController(
|
||||
scope = this,
|
||||
json = json,
|
||||
requestGateway = { _, _ -> json.encodeToString(QuestionListResult(emptyList())) },
|
||||
)
|
||||
|
||||
controller.handleGatewayEvent("question.requested", json.encodeToString(pending))
|
||||
controller.handleGatewayEvent(
|
||||
"question.resolved",
|
||||
"""{"id":"ask_done","status":"answered"}""",
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertEquals(listOf("ask_done"), controller.questions.value.map { it.record.id })
|
||||
assertEquals(
|
||||
ChatQuestionStatus.AnsweredElsewhere,
|
||||
controller.questions.value
|
||||
.single()
|
||||
.status(),
|
||||
)
|
||||
|
||||
advanceTimeBy(QUESTION_TERMINAL_RETENTION_MS)
|
||||
runCurrent()
|
||||
|
||||
assertTrue(controller.questions.value.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun locallyExpiredQuestionIsEvictedAfterTerminalGrace() =
|
||||
runTest {
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
val controller = ChatController(scope = this, json = json, requestGateway = { _, _ -> "{}" })
|
||||
val pending = record(expiresAtMs = 1_000)
|
||||
|
||||
controller.handleGatewayEvent("question.requested", json.encodeToString(pending))
|
||||
advanceTimeBy(QUESTION_TERMINAL_RETENTION_MS + 1_001)
|
||||
runCurrent()
|
||||
|
||||
assertTrue(controller.questions.value.isEmpty())
|
||||
}
|
||||
|
||||
private fun record(
|
||||
id: String = "ask_123",
|
||||
status: String = "pending",
|
||||
expiresAtMs: Long = Long.MAX_VALUE,
|
||||
sessionKey: String? = "agent:main:main",
|
||||
agentId: String? = "main",
|
||||
) = QuestionRecord(
|
||||
id = id,
|
||||
questions = listOf(question),
|
||||
agentId = agentId,
|
||||
sessionKey = sessionKey,
|
||||
createdAtMs = 1_000,
|
||||
expiresAtMs = expiresAtMs,
|
||||
status = status,
|
||||
)
|
||||
}
|
||||
@@ -433,6 +433,7 @@ class ConnectionManagerTest {
|
||||
listOf(
|
||||
"operator.admin",
|
||||
"operator.approvals",
|
||||
"operator.questions",
|
||||
"operator.read",
|
||||
"operator.talk.secrets",
|
||||
"operator.write",
|
||||
|
||||
@@ -114105,6 +114105,142 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Other answer": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Other answer"
|
||||
}
|
||||
},
|
||||
"zh-CN": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "其他答案"
|
||||
}
|
||||
},
|
||||
"zh-TW": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "其他答案"
|
||||
}
|
||||
},
|
||||
"pt-BR": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Outra resposta"
|
||||
}
|
||||
},
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Andere Antwort"
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Otra respuesta"
|
||||
}
|
||||
},
|
||||
"ja-JP": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "その他の回答"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "기타 답변"
|
||||
}
|
||||
},
|
||||
"fr": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Autre réponse"
|
||||
}
|
||||
},
|
||||
"hi": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "अन्य उत्तर"
|
||||
}
|
||||
},
|
||||
"ar": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "إجابة أخرى"
|
||||
}
|
||||
},
|
||||
"it": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Altra risposta"
|
||||
}
|
||||
},
|
||||
"tr": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Diğer yanıt"
|
||||
}
|
||||
},
|
||||
"uk": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Інша відповідь"
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Jawaban lain"
|
||||
}
|
||||
},
|
||||
"pl": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Inna odpowiedź"
|
||||
}
|
||||
},
|
||||
"th": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "คำตอบอื่น"
|
||||
}
|
||||
},
|
||||
"vi": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Câu trả lời khác"
|
||||
}
|
||||
},
|
||||
"nl": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Ander antwoord"
|
||||
}
|
||||
},
|
||||
"fa": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "پاسخ دیگر"
|
||||
}
|
||||
},
|
||||
"ru": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Другой ответ"
|
||||
}
|
||||
},
|
||||
"sv": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Annat svar"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Out-of-app approval alerts need notification permission.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
@@ -159665,6 +159801,278 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Submit": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Submit"
|
||||
}
|
||||
},
|
||||
"zh-CN": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "提交"
|
||||
}
|
||||
},
|
||||
"zh-TW": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "提交"
|
||||
}
|
||||
},
|
||||
"pt-BR": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Enviar"
|
||||
}
|
||||
},
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Absenden"
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Enviar"
|
||||
}
|
||||
},
|
||||
"ja-JP": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "送信"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "제출"
|
||||
}
|
||||
},
|
||||
"fr": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Envoyer"
|
||||
}
|
||||
},
|
||||
"hi": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "सबमिट करें"
|
||||
}
|
||||
},
|
||||
"ar": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "إرسال"
|
||||
}
|
||||
},
|
||||
"it": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Invia"
|
||||
}
|
||||
},
|
||||
"tr": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Gönder"
|
||||
}
|
||||
},
|
||||
"uk": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Надіслати"
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Kirim"
|
||||
}
|
||||
},
|
||||
"pl": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Prześlij"
|
||||
}
|
||||
},
|
||||
"th": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "ส่ง"
|
||||
}
|
||||
},
|
||||
"vi": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Gửi"
|
||||
}
|
||||
},
|
||||
"nl": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Indienen"
|
||||
}
|
||||
},
|
||||
"fa": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "ارسال"
|
||||
}
|
||||
},
|
||||
"ru": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Отправить"
|
||||
}
|
||||
},
|
||||
"sv": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Skicka"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Submitting…": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Submitting…"
|
||||
}
|
||||
},
|
||||
"zh-CN": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "正在提交…"
|
||||
}
|
||||
},
|
||||
"zh-TW": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "正在提交…"
|
||||
}
|
||||
},
|
||||
"pt-BR": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Enviando…"
|
||||
}
|
||||
},
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Wird gesendet…"
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Enviando…"
|
||||
}
|
||||
},
|
||||
"ja-JP": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "送信中…"
|
||||
}
|
||||
},
|
||||
"ko": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "제출 중…"
|
||||
}
|
||||
},
|
||||
"fr": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Envoi…"
|
||||
}
|
||||
},
|
||||
"hi": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "सबमिट किया जा रहा है…"
|
||||
}
|
||||
},
|
||||
"ar": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "جارٍ الإرسال…"
|
||||
}
|
||||
},
|
||||
"it": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Invio in corso…"
|
||||
}
|
||||
},
|
||||
"tr": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Gönderiliyor…"
|
||||
}
|
||||
},
|
||||
"uk": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Надсилання…"
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Mengirim…"
|
||||
}
|
||||
},
|
||||
"pl": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Przesyłanie…"
|
||||
}
|
||||
},
|
||||
"th": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "กำลังส่ง…"
|
||||
}
|
||||
},
|
||||
"vi": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Đang gửi…"
|
||||
}
|
||||
},
|
||||
"nl": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Bezig met indienen…"
|
||||
}
|
||||
},
|
||||
"fa": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "در حال ارسال…"
|
||||
}
|
||||
},
|
||||
"ru": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Отправка…"
|
||||
}
|
||||
},
|
||||
"sv": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Skickar…"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Summarize the current OpenClaw status and tell me what needs attention.": {
|
||||
"localizations": {
|
||||
"en": {
|
||||
|
||||
@@ -499,6 +499,15 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
|
||||
return (try? JSONDecoder().decode(OpenClawGatewayHealthOK.self, from: res))?.ok ?? true
|
||||
}
|
||||
|
||||
func listQuestions() async throws -> [QuestionRecord] {
|
||||
let data = try await self.gateway.request(OpenClawChatGatewayRequests.questionList())
|
||||
return try JSONDecoder().decode(QuestionListResult.self, from: data).questions
|
||||
}
|
||||
|
||||
func resolveQuestion(id: String, answers: [String: [String]]) async throws {
|
||||
_ = try await self.gateway.request(OpenClawChatGatewayRequests.resolveQuestion(id: id, answers: answers))
|
||||
}
|
||||
|
||||
func events() -> AsyncStream<OpenClawChatTransportEvent> {
|
||||
AsyncStream { continuation in
|
||||
let task = Task {
|
||||
|
||||
@@ -4869,10 +4869,10 @@ extension NodeAppModel {
|
||||
if includeAdminScope {
|
||||
scopes.append("operator.admin")
|
||||
}
|
||||
// Preserve reconnect compatibility for older paired operator tokens that were
|
||||
// approved before iOS requested operator.approvals by default.
|
||||
// Older paired tokens request a scope upgrade before interactive prompts can arrive.
|
||||
if includeApprovalScope {
|
||||
scopes.append("operator.approvals")
|
||||
scopes.append("operator.questions")
|
||||
}
|
||||
return GatewayConnectOptions(
|
||||
role: "operator",
|
||||
|
||||
@@ -321,11 +321,13 @@ private func waitForActiveGateway(stableID: String, appModel: NodeAppModel) asyn
|
||||
#expect(withoutApprovalScope.scopes.contains("operator.read"))
|
||||
#expect(withoutApprovalScope.scopes.contains("operator.write"))
|
||||
#expect(!withoutApprovalScope.scopes.contains("operator.approvals"))
|
||||
#expect(!withoutApprovalScope.scopes.contains("operator.questions"))
|
||||
#expect(withoutApprovalScope.scopes.contains("operator.talk.secrets"))
|
||||
#expect(!withoutApprovalScope.scopesAreExplicit)
|
||||
#expect(withoutApprovalScope.caps == [OpenClawGatewayClientCapability.inlineWidgets])
|
||||
|
||||
#expect(withApprovalScope.scopes.contains("operator.approvals"))
|
||||
#expect(withApprovalScope.scopes.contains("operator.questions"))
|
||||
#expect(withAdminScope.scopes.contains("operator.admin"))
|
||||
}
|
||||
|
||||
|
||||
@@ -505,6 +505,16 @@ struct MacGatewayChatTransport: OpenClawChatTransport {
|
||||
try await GatewayConnection.shared.healthOK(timeoutMs: timeoutMs)
|
||||
}
|
||||
|
||||
func listQuestions() async throws -> [QuestionRecord] {
|
||||
let data = try await GatewayConnection.shared.request(OpenClawChatGatewayRequests.questionList())
|
||||
return try JSONDecoder().decode(QuestionListResult.self, from: data).questions
|
||||
}
|
||||
|
||||
func resolveQuestion(id: String, answers: [String: [String]]) async throws {
|
||||
_ = try await GatewayConnection.shared.request(
|
||||
OpenClawChatGatewayRequests.resolveQuestion(id: id, answers: answers))
|
||||
}
|
||||
|
||||
func waitForRunCompletion(
|
||||
runId rawRunId: String,
|
||||
timeoutMs: Int) async -> OpenClawChatRunObservation
|
||||
|
||||
@@ -3,5 +3,6 @@ let defaultOperatorConnectScopes: [String] = [
|
||||
"operator.read",
|
||||
"operator.write",
|
||||
"operator.approvals",
|
||||
"operator.questions",
|
||||
"operator.pairing",
|
||||
]
|
||||
|
||||
@@ -139,6 +139,18 @@ public enum OpenClawChatGatewayPayloadCodec {
|
||||
as: OpenClawAgentEventPayload.self)
|
||||
else { return nil }
|
||||
return .agent(agent)
|
||||
case "question.requested":
|
||||
guard let payload = frame.payload,
|
||||
let question = try? GatewayPayloadDecoding.decode(payload, as: QuestionRecord.self)
|
||||
else { return nil }
|
||||
return .questionRequested(question)
|
||||
case "question.resolved":
|
||||
guard let payload = frame.payload,
|
||||
let resolved = try? GatewayPayloadDecoding.decode(
|
||||
payload,
|
||||
as: OpenClawQuestionResolvedEvent.self)
|
||||
else { return nil }
|
||||
return .questionResolved(resolved)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -80,6 +80,24 @@ public enum OpenClawChatGatewayRequests {
|
||||
OpenClawChatGatewayRequest(method: "models.list", timeoutMs: self.defaultTimeoutMs)
|
||||
}
|
||||
|
||||
public static func questionList() -> OpenClawChatGatewayRequest {
|
||||
OpenClawChatGatewayRequest(method: "question.list", timeoutMs: self.defaultTimeoutMs)
|
||||
}
|
||||
|
||||
public static func resolveQuestion(
|
||||
id: String,
|
||||
answers: [String: [String]]) -> OpenClawChatGatewayRequest
|
||||
{
|
||||
let values = answers.mapValues { AnyCodable(["answers": AnyCodable($0)]) }
|
||||
return OpenClawChatGatewayRequest(
|
||||
method: "question.resolve",
|
||||
params: [
|
||||
"id": AnyCodable(id),
|
||||
"answers": AnyCodable(["answers": AnyCodable(values)]),
|
||||
],
|
||||
timeoutMs: self.mutationTimeoutMs)
|
||||
}
|
||||
|
||||
public static func sessionsList(
|
||||
limit: Int?,
|
||||
search: String?,
|
||||
|
||||
@@ -0,0 +1,496 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import SwiftUI
|
||||
|
||||
public enum OpenClawQuestionCardStatus: Sendable, Equatable {
|
||||
case pending
|
||||
case submitting
|
||||
case answered
|
||||
case answeredElsewhere
|
||||
case expired
|
||||
case cancelled
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
public final class OpenClawQuestionCardModel: Identifiable {
|
||||
private static let terminalRetentionSeconds: TimeInterval = 15
|
||||
|
||||
public let id: String
|
||||
public private(set) var record: QuestionRecord
|
||||
public private(set) var isSubmitting = false
|
||||
public private(set) var wasAnsweredLocally = false
|
||||
public private(set) var errorText: String?
|
||||
public private(set) var selectedOptions: [String: Set<String>] = [:]
|
||||
public private(set) var otherText: [String: String] = [:]
|
||||
private var terminalObservedAt: Date?
|
||||
|
||||
public init(record: QuestionRecord) {
|
||||
self.id = record.id
|
||||
self.record = record
|
||||
self.terminalObservedAt = record.status == .pending ? nil : Date()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func apply(record: QuestionRecord, at date: Date = Date()) -> Bool {
|
||||
guard record.id == self.id, !Self.recordsMatch(self.record, record) else { return false }
|
||||
self.record = record
|
||||
self.isSubmitting = self.isSubmitting && record.status == .pending
|
||||
self.terminalObservedAt = record.status == .pending ? nil : (self.terminalObservedAt ?? date)
|
||||
return true
|
||||
}
|
||||
|
||||
public func status(at date: Date = Date()) -> OpenClawQuestionCardStatus {
|
||||
switch self.record.status {
|
||||
case .answered:
|
||||
return self.wasAnsweredLocally ? .answered : .answeredElsewhere
|
||||
case .cancelled:
|
||||
return .cancelled
|
||||
case .expired:
|
||||
return .expired
|
||||
case .pending:
|
||||
if date.timeIntervalSince1970 * 1000 >= Double(self.record.expiresatms) {
|
||||
return .expired
|
||||
}
|
||||
return self.isSubmitting ? .submitting : .pending
|
||||
}
|
||||
}
|
||||
|
||||
public func remainingSeconds(at date: Date = Date()) -> Int {
|
||||
max(0, Int(ceil(Double(self.record.expiresatms) / 1000 - date.timeIntervalSince1970)))
|
||||
}
|
||||
|
||||
public func toggleOption(questionID: String, label: String) {
|
||||
guard let question = self.record.questions.first(where: { $0.id == questionID }),
|
||||
question.options.contains(where: { $0.label == label }),
|
||||
self.status() == .pending
|
||||
else { return }
|
||||
var selected = self.selectedOptions[questionID] ?? []
|
||||
if question.multiselect == true {
|
||||
if selected.contains(label) {
|
||||
selected.remove(label)
|
||||
} else {
|
||||
selected.insert(label)
|
||||
}
|
||||
} else {
|
||||
selected = selected == [label] ? [] : [label]
|
||||
if !selected.isEmpty {
|
||||
self.otherText[questionID] = ""
|
||||
}
|
||||
}
|
||||
self.selectedOptions[questionID] = selected
|
||||
self.errorText = nil
|
||||
}
|
||||
|
||||
public func setOtherText(questionID: String, value: String) {
|
||||
guard let question = self.record.questions.first(where: { $0.id == questionID }),
|
||||
question.options.isEmpty || question.isother == true,
|
||||
self.status() == .pending
|
||||
else { return }
|
||||
self.otherText[questionID] = value
|
||||
if question.multiselect != true, !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||
self.selectedOptions[questionID] = []
|
||||
}
|
||||
self.errorText = nil
|
||||
}
|
||||
|
||||
public var canSubmit: Bool {
|
||||
self.status() == .pending && self.answers() != nil
|
||||
}
|
||||
|
||||
public func beginSubmission() -> [String: [String]]? {
|
||||
guard let answers = self.answers(), self.status() == .pending else { return nil }
|
||||
self.isSubmitting = true
|
||||
self.errorText = nil
|
||||
return answers
|
||||
}
|
||||
|
||||
public func markAnsweredLocally(at date: Date = Date()) {
|
||||
self.wasAnsweredLocally = true
|
||||
self.isSubmitting = false
|
||||
self.terminalObservedAt = self.terminalObservedAt ?? date
|
||||
self.record = QuestionRecord(
|
||||
id: self.record.id,
|
||||
questions: self.record.questions,
|
||||
agentid: self.record.agentid,
|
||||
sessionkey: self.record.sessionkey,
|
||||
createdatms: self.record.createdatms,
|
||||
expiresatms: self.record.expiresatms,
|
||||
status: .answered,
|
||||
answers: self.answers().map { answers in
|
||||
QuestionAnswers(answers: answers.mapValues { values in
|
||||
AnyCodable(["answers": values])
|
||||
})
|
||||
},
|
||||
resolvedby: self.record.resolvedby)
|
||||
}
|
||||
|
||||
public func apply(resolved: OpenClawQuestionResolvedEvent, at date: Date = Date()) {
|
||||
guard resolved.id == self.id else { return }
|
||||
self.isSubmitting = false
|
||||
self.terminalObservedAt = self.terminalObservedAt ?? date
|
||||
self.record = QuestionRecord(
|
||||
id: self.record.id,
|
||||
questions: self.record.questions,
|
||||
agentid: self.record.agentid,
|
||||
sessionkey: self.record.sessionkey,
|
||||
createdatms: self.record.createdatms,
|
||||
expiresatms: self.record.expiresatms,
|
||||
status: resolved.status,
|
||||
answers: resolved.answers,
|
||||
resolvedby: self.record.resolvedby)
|
||||
}
|
||||
|
||||
public func failSubmission(_ message: String) {
|
||||
self.isSubmitting = false
|
||||
self.errorText = message
|
||||
}
|
||||
|
||||
func shouldRetainAfterList(at date: Date) -> Bool {
|
||||
guard let terminalObservedAt else { return false }
|
||||
return date.timeIntervalSince(terminalObservedAt) < Self.terminalRetentionSeconds
|
||||
}
|
||||
|
||||
func terminalRetentionDelay(at date: Date) -> TimeInterval? {
|
||||
guard let terminalObservedAt else { return nil }
|
||||
return max(0, Self.terminalRetentionSeconds - date.timeIntervalSince(terminalObservedAt))
|
||||
}
|
||||
|
||||
func observeLocalExpiry(at date: Date) -> Bool {
|
||||
guard self.record.status == .pending, self.terminalObservedAt == nil,
|
||||
date.timeIntervalSince1970 * 1000 >= Double(self.record.expiresatms)
|
||||
else { return false }
|
||||
self.terminalObservedAt = date
|
||||
return true
|
||||
}
|
||||
|
||||
func localExpiryDelay(at date: Date) -> TimeInterval? {
|
||||
guard self.record.status == .pending, self.terminalObservedAt == nil else { return nil }
|
||||
return max(0, Double(self.record.expiresatms) / 1000 - date.timeIntervalSince1970)
|
||||
}
|
||||
|
||||
private func answers() -> [String: [String]]? {
|
||||
var result: [String: [String]] = [:]
|
||||
for question in self.record.questions {
|
||||
let selected = self.selectedOptions[question.id] ?? []
|
||||
var values = question.options.compactMap { selected.contains($0.label) ? $0.label : nil }
|
||||
if let other = self.otherText[question.id]?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
!other.isEmpty
|
||||
{
|
||||
values.append(other)
|
||||
}
|
||||
guard !values.isEmpty else { return nil }
|
||||
result[question.id] = values
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static func recordsMatch(_ lhs: QuestionRecord, _ rhs: QuestionRecord) -> Bool {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = [.sortedKeys]
|
||||
return (try? encoder.encode(lhs)) == (try? encoder.encode(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
// periphery:ignore - Public SwiftUI component for package consumers; native apps embed it through the shared list.
|
||||
public struct OpenClawQuestionCard: View {
|
||||
@Bindable private var model: OpenClawQuestionCardModel
|
||||
private let onSubmit: @MainActor @Sendable (OpenClawQuestionCardModel) async -> Void
|
||||
|
||||
// periphery:ignore - Public construction is part of the exported SwiftUI component API.
|
||||
public init(
|
||||
model: OpenClawQuestionCardModel,
|
||||
onSubmit: @escaping @MainActor @Sendable (OpenClawQuestionCardModel) async -> Void)
|
||||
{
|
||||
self.model = model
|
||||
self.onSubmit = onSubmit
|
||||
}
|
||||
|
||||
// periphery:ignore - SwiftUI View witness must stay public for the exported component.
|
||||
public var body: some View {
|
||||
TimelineView(.periodic(from: .now, by: 1)) { context in
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
ForEach(self.model.record.questions, id: \.id) { question in
|
||||
self.questionSection(question, now: context.date)
|
||||
}
|
||||
self.footer(now: context.date)
|
||||
}
|
||||
.padding(16)
|
||||
.background(OpenClawChatTheme.subtleCard, in: RoundedRectangle(cornerRadius: 16))
|
||||
.overlay(RoundedRectangle(cornerRadius: 16).stroke(.secondary.opacity(0.2)))
|
||||
}
|
||||
}
|
||||
|
||||
private func questionSection(_ question: Question, now: Date) -> some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(question.header.uppercased())
|
||||
.font(OpenClawChatTypography.captionSemiBold)
|
||||
.foregroundStyle(OpenClawChatTheme.accent)
|
||||
Text(question.question)
|
||||
.font(OpenClawChatTypography.body)
|
||||
ForEach(question.options, id: \.label) { option in
|
||||
self.optionRow(question: question, option: option, now: now)
|
||||
}
|
||||
if question.options.isEmpty || question.isother == true {
|
||||
TextField(
|
||||
"Other answer",
|
||||
text: Binding(
|
||||
get: { self.model.otherText[question.id] ?? "" },
|
||||
set: { self.model.setOtherText(questionID: question.id, value: $0) }),
|
||||
axis: .vertical)
|
||||
.font(OpenClawChatTypography.body)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.disabled(self.model.status(at: now) != .pending)
|
||||
.accessibilityLabel("Other answer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func optionRow(question: Question, option: QuestionOption, now: Date) -> some View {
|
||||
let selected = self.model.selectedOptions[question.id]?.contains(option.label) == true
|
||||
return Button {
|
||||
self.model.toggleOption(questionID: question.id, label: option.label)
|
||||
} label: {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
Image(systemName: selected
|
||||
? (question.multiselect == true ? "checkmark.square.fill" : "largecircle.fill.circle")
|
||||
: (question.multiselect == true ? "square" : "circle"))
|
||||
.foregroundStyle(selected ? OpenClawChatTheme.accent : .secondary)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(option.label).font(OpenClawChatTypography.body)
|
||||
if let description = option.description, !description.isEmpty {
|
||||
Text(description)
|
||||
.font(OpenClawChatTypography.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(self.model.status(at: now) != .pending)
|
||||
.accessibilityLabel(option.label)
|
||||
.accessibilityValue(selected ? "Selected" : "Not selected")
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func footer(now: Date) -> some View {
|
||||
let status = self.model.status(at: now)
|
||||
if status == .pending || status == .submitting {
|
||||
HStack {
|
||||
Text(self.countdownText(now: now))
|
||||
.font(OpenClawChatTypography.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
Button(status == .submitting ? "Submitting…" : "Submit") {
|
||||
Task { await self.onSubmit(self.model) }
|
||||
}
|
||||
.font(OpenClawChatTypography.body(size: 14, weight: .semibold, relativeTo: .callout))
|
||||
.buttonStyle(.borderedProminent)
|
||||
.disabled(!self.model.canSubmit || status == .submitting)
|
||||
}
|
||||
if let errorText = self.model.errorText {
|
||||
Text(errorText)
|
||||
.font(OpenClawChatTypography.caption)
|
||||
.foregroundStyle(OpenClawChatTheme.danger)
|
||||
}
|
||||
} else {
|
||||
Label(self.terminalText(status), systemImage: self.terminalIcon(status))
|
||||
.font(OpenClawChatTypography.captionSemiBold)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
private func countdownText(now: Date) -> String {
|
||||
let seconds = self.model.remainingSeconds(at: now)
|
||||
return seconds >= 60 ? "Expires in \(seconds / 60)m \(seconds % 60)s" : "Expires in \(seconds)s"
|
||||
}
|
||||
|
||||
private func terminalText(_ status: OpenClawQuestionCardStatus) -> String {
|
||||
switch status {
|
||||
case .answered: "Answered"
|
||||
case .answeredElsewhere: "Answered elsewhere"
|
||||
case .expired: "Expired"
|
||||
case .cancelled: "Cancelled"
|
||||
case .pending, .submitting: "Pending"
|
||||
}
|
||||
}
|
||||
|
||||
private func terminalIcon(_ status: OpenClawQuestionCardStatus) -> String {
|
||||
switch status {
|
||||
case .answered, .answeredElsewhere: "checkmark.circle.fill"
|
||||
case .expired: "clock.badge.xmark"
|
||||
case .cancelled: "xmark.circle"
|
||||
case .pending, .submitting: "clock"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
struct OpenClawQuestionCards: View {
|
||||
let viewModel: OpenClawChatViewModel
|
||||
|
||||
var body: some View {
|
||||
ForEach(self.viewModel.visibleQuestionCards) { card in
|
||||
OpenClawQuestionCard(model: card) { [weak viewModel = self.viewModel] model in
|
||||
await viewModel?.submitQuestion(model)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenClawChatViewModel {
|
||||
public var visibleQuestionCards: [OpenClawQuestionCardModel] {
|
||||
self.questionCards.filter { card in
|
||||
guard let key = card.record.sessionkey else { return true }
|
||||
return self.matchesCurrentSessionKey(
|
||||
incoming: key,
|
||||
agentId: card.record.agentid,
|
||||
current: self.sessionKey)
|
||||
}
|
||||
}
|
||||
|
||||
func refreshQuestions() async {
|
||||
self.questionRefreshGeneration &+= 1
|
||||
let refreshGeneration = self.questionRefreshGeneration
|
||||
let stateRevision = self.questionStateRevision
|
||||
do {
|
||||
let records = try await self.transport.listQuestions()
|
||||
guard refreshGeneration == self.questionRefreshGeneration,
|
||||
stateRevision == self.questionStateRevision
|
||||
else { return }
|
||||
let existing = Dictionary(uniqueKeysWithValues: self.questionCards.map { ($0.id, $0) })
|
||||
let listedIDs = Set(records.map(\.id))
|
||||
let now = Date()
|
||||
let retainedTerminal = self.questionCards.filter { model in
|
||||
!listedIDs.contains(model.id) && model.shouldRetainAfterList(at: now)
|
||||
}
|
||||
var changed = records.count + retainedTerminal.count != self.questionCards.count
|
||||
self.questionCards = records.map { record in
|
||||
if let model = existing[record.id] {
|
||||
changed = model.apply(record: record) || changed
|
||||
return model
|
||||
}
|
||||
changed = true
|
||||
return OpenClawQuestionCardModel(record: record)
|
||||
} + retainedTerminal
|
||||
self.syncQuestionEvictions()
|
||||
if changed {
|
||||
self.questionStateRevision &+= 1
|
||||
self.markTimelineChanged()
|
||||
}
|
||||
} catch let error as GatewayResponseError where Self.questionListIsUnavailable(error) {
|
||||
guard refreshGeneration == self.questionRefreshGeneration,
|
||||
stateRevision == self.questionStateRevision,
|
||||
!self.questionCards.isEmpty
|
||||
else { return }
|
||||
self.questionCards = []
|
||||
self.syncQuestionEvictions()
|
||||
self.questionStateRevision &+= 1
|
||||
self.markTimelineChanged()
|
||||
} catch {
|
||||
// Question recovery is best-effort; chat bootstrap remains usable without this scope.
|
||||
}
|
||||
}
|
||||
|
||||
private nonisolated static func questionListIsUnavailable(_ error: GatewayResponseError) -> Bool {
|
||||
guard error.code == "INVALID_REQUEST" else { return false }
|
||||
return error.message == "unknown method: question.list" ||
|
||||
error.message == "missing scope: operator.questions"
|
||||
}
|
||||
|
||||
func upsertQuestion(_ record: QuestionRecord) {
|
||||
if let model = self.questionCards.first(where: { $0.id == record.id }) {
|
||||
guard model.apply(record: record) else { return }
|
||||
} else {
|
||||
self.questionCards.append(OpenClawQuestionCardModel(record: record))
|
||||
}
|
||||
self.questionStateRevision &+= 1
|
||||
self.syncQuestionEvictions()
|
||||
self.markTimelineChanged()
|
||||
}
|
||||
|
||||
func resolveQuestionEvent(_ event: OpenClawQuestionResolvedEvent) {
|
||||
self.questionCards.first(where: { $0.id == event.id })?.apply(resolved: event)
|
||||
self.questionStateRevision &+= 1
|
||||
self.syncQuestionEvictions()
|
||||
self.markTimelineChanged()
|
||||
}
|
||||
|
||||
func reconcileQuestionsAfterEvent() {
|
||||
// Invalidate a list snapshot captured before this event, then fetch the
|
||||
// authoritative set so other pending cards from that snapshot are not lost.
|
||||
self.questionRefreshGeneration &+= 1
|
||||
Task { [weak self] in await self?.refreshQuestions() }
|
||||
}
|
||||
|
||||
func submitQuestion(_ model: OpenClawQuestionCardModel) async {
|
||||
guard let answers = model.beginSubmission() else { return }
|
||||
self.questionStateRevision &+= 1
|
||||
do {
|
||||
try await self.transport.resolveQuestion(id: model.id, answers: answers)
|
||||
model.markAnsweredLocally()
|
||||
self.questionStateRevision &+= 1
|
||||
self.syncQuestionEvictions()
|
||||
self.markTimelineChanged()
|
||||
} catch {
|
||||
model.failSubmission(error.localizedDescription)
|
||||
self.questionStateRevision &+= 1
|
||||
}
|
||||
}
|
||||
|
||||
func evictQuestionIfTerminalGraceElapsed(
|
||||
_ model: OpenClawQuestionCardModel,
|
||||
at date: Date = Date())
|
||||
{
|
||||
guard self.questionCards.first(where: { $0.id == model.id }) === model else { return }
|
||||
if model.observeLocalExpiry(at: date) {
|
||||
self.questionStateRevision &+= 1
|
||||
self.markTimelineChanged()
|
||||
}
|
||||
guard
|
||||
!model.shouldRetainAfterList(at: date),
|
||||
model.status(at: date) == .expired || model.record.status != .pending
|
||||
else {
|
||||
self.syncQuestionEvictions(at: date)
|
||||
return
|
||||
}
|
||||
self.questionCards.removeAll { $0 === model }
|
||||
self.questionEvictionTasks.removeValue(forKey: model.id)?.cancel()
|
||||
self.questionEvictionDeadlines.removeValue(forKey: model.id)
|
||||
self.questionStateRevision &+= 1
|
||||
self.markTimelineChanged()
|
||||
}
|
||||
|
||||
private func syncQuestionEvictions(at date: Date = Date()) {
|
||||
let modelsByID = Dictionary(uniqueKeysWithValues: self.questionCards.map { ($0.id, $0) })
|
||||
let cancelledIDs = self.questionEvictionTasks.keys.filter { modelsByID[$0] == nil }
|
||||
for id in cancelledIDs {
|
||||
self.questionEvictionTasks.removeValue(forKey: id)?.cancel()
|
||||
self.questionEvictionDeadlines.removeValue(forKey: id)
|
||||
}
|
||||
for model in self.questionCards {
|
||||
guard let delay = model.terminalRetentionDelay(at: date) ?? model.localExpiryDelay(at: date)
|
||||
else { continue }
|
||||
let deadline = date.addingTimeInterval(delay)
|
||||
if let scheduled = self.questionEvictionDeadlines[model.id],
|
||||
abs(scheduled.timeIntervalSince(deadline)) < 0.01
|
||||
{
|
||||
continue
|
||||
}
|
||||
self.questionEvictionTasks.removeValue(forKey: model.id)?.cancel()
|
||||
self.questionEvictionDeadlines[model.id] = deadline
|
||||
self.questionEvictionTasks[model.id] = Task { [weak self, weak model] in
|
||||
try? await Task.sleep(for: .seconds(delay))
|
||||
guard !Task.isCancelled, let self, let model else { return }
|
||||
self.questionEvictionTasks.removeValue(forKey: model.id)
|
||||
self.questionEvictionDeadlines.removeValue(forKey: model.id)
|
||||
self.evictQuestionIfTerminalGraceElapsed(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import OpenClawProtocol
|
||||
|
||||
public enum OpenClawChatTransportEvent: Sendable {
|
||||
case health(ok: Bool)
|
||||
@@ -7,9 +8,24 @@ public enum OpenClawChatTransportEvent: Sendable {
|
||||
case chat(OpenClawChatEventPayload)
|
||||
case sessionMessage(OpenClawSessionMessageEventPayload)
|
||||
case agent(OpenClawAgentEventPayload)
|
||||
case questionRequested(QuestionRecord)
|
||||
case questionResolved(OpenClawQuestionResolvedEvent)
|
||||
case seqGap
|
||||
}
|
||||
|
||||
public struct OpenClawQuestionResolvedEvent: Codable, Sendable {
|
||||
public let id: String
|
||||
public let status: QuestionStatus
|
||||
public let answers: QuestionAnswers?
|
||||
|
||||
// periphery:ignore - Package consumers construct transport events; native apps decode them.
|
||||
public init(id: String, status: QuestionStatus, answers: QuestionAnswers? = nil) {
|
||||
self.id = id
|
||||
self.status = status
|
||||
self.answers = answers
|
||||
}
|
||||
}
|
||||
|
||||
public struct OpenClawChatSessionsChangedEvent: Codable, Sendable, Equatable {
|
||||
public let sessionKey: String?
|
||||
public let agentId: String?
|
||||
@@ -319,6 +335,8 @@ public protocol OpenClawChatTransport: Sendable {
|
||||
func acquireSessionSettingsRouteLease() async -> OpenClawChatSessionSettingsRouteLease?
|
||||
|
||||
func requestHealth(timeoutMs: Int) async throws -> Bool
|
||||
func listQuestions() async throws -> [QuestionRecord]
|
||||
func resolveQuestion(id: String, answers: [String: [String]]) async throws
|
||||
func waitForRunCompletion(runId: String, timeoutMs: Int) async -> OpenClawChatRunObservation
|
||||
func events() -> AsyncStream<OpenClawChatTransportEvent>
|
||||
func resolveInlineWidgetResource(
|
||||
@@ -332,6 +350,17 @@ public protocol OpenClawChatTransport: Sendable {
|
||||
}
|
||||
|
||||
extension OpenClawChatTransport {
|
||||
public func listQuestions() async throws -> [QuestionRecord] {
|
||||
[]
|
||||
}
|
||||
|
||||
public func resolveQuestion(id _: String, answers _: [String: [String]]) async throws {
|
||||
throw NSError(
|
||||
domain: "OpenClawChatTransport",
|
||||
code: 0,
|
||||
userInfo: [NSLocalizedDescriptionKey: "question.resolve not supported by this transport"])
|
||||
}
|
||||
|
||||
public func requestFullMessage(sessionKey _: String, messageID _: String) async throws -> OpenClawChatMessage? {
|
||||
nil
|
||||
}
|
||||
|
||||
@@ -409,6 +409,8 @@ public struct OpenClawChatView: View {
|
||||
self.messageRow(for: msg, contextWindowTokens: contextWindowTokens)
|
||||
}
|
||||
|
||||
OpenClawQuestionCards(viewModel: self.viewModel)
|
||||
|
||||
if self.viewModel.hasBlockingRunActivity, !self.hasVisibleStreamingAssistantText {
|
||||
ChatTypingIndicatorBubble(
|
||||
style: self.style,
|
||||
@@ -688,7 +690,8 @@ public struct OpenClawChatView: View {
|
||||
private var hasVisibleTransientContent: Bool {
|
||||
self.viewModel.hasBlockingRunActivity ||
|
||||
(self.displayOptions.contains(.toolActivity) && !self.viewModel.pendingToolCalls.isEmpty) ||
|
||||
self.hasVisibleStreamingAssistantText
|
||||
self.hasVisibleStreamingAssistantText ||
|
||||
!self.viewModel.visibleQuestionCards.isEmpty
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
@@ -24,7 +24,11 @@ extension OpenClawChatViewModel {
|
||||
func handleTransportEvent(_ evt: OpenClawChatTransportEvent) {
|
||||
switch evt {
|
||||
case let .health(ok):
|
||||
let reconnected = ok && !self.healthOK
|
||||
applyTransportHealth(ok)
|
||||
if reconnected {
|
||||
Task { [weak self] in await self?.refreshQuestions() }
|
||||
}
|
||||
case .tick:
|
||||
let context = self.currentSessionSnapshot()
|
||||
Task { await self.pollHealthIfNeeded(force: false, sessionSnapshot: context) }
|
||||
@@ -38,6 +42,12 @@ extension OpenClawChatViewModel {
|
||||
self.handleSessionMessageEvent(message)
|
||||
case let .agent(agent):
|
||||
self.handleAgentEvent(agent)
|
||||
case let .questionRequested(question):
|
||||
self.upsertQuestion(question)
|
||||
self.reconcileQuestionsAfterEvent()
|
||||
case let .questionResolved(resolved):
|
||||
self.resolveQuestionEvent(resolved)
|
||||
self.reconcileQuestionsAfterEvent()
|
||||
case .seqGap:
|
||||
self.errorText = nil
|
||||
self.invalidateHistorySnapshots()
|
||||
@@ -47,6 +57,9 @@ extension OpenClawChatViewModel {
|
||||
self.updateStreamingAssistantText(nil)
|
||||
self.clearPlan()
|
||||
let context = self.beginHistoryRequest()
|
||||
// Question refresh is best-effort and must not delay transcript
|
||||
// recovery behind a slow gateway round trip.
|
||||
Task { await self.refreshQuestions() }
|
||||
Task {
|
||||
await self.refreshHistoryAfterRun(historyRequest: context)
|
||||
await self.pollHealthIfNeeded(force: true, sessionSnapshot: context.session)
|
||||
|
||||
@@ -88,6 +88,11 @@ public final class OpenClawChatViewModel {
|
||||
}
|
||||
|
||||
public private(set) var pendingRunCount: Int = 0
|
||||
public internal(set) var questionCards: [OpenClawQuestionCardModel] = []
|
||||
var questionRefreshGeneration: UInt64 = 0
|
||||
var questionStateRevision: UInt64 = 0
|
||||
var questionEvictionTasks: [String: Task<Void, Never>] = [:]
|
||||
var questionEvictionDeadlines: [String: Date] = [:]
|
||||
var hasActiveSessionRunWithoutChatSnapshot = false
|
||||
|
||||
public private(set) var sessionKey: String {
|
||||
@@ -483,12 +488,15 @@ public final class OpenClawChatViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
isolated deinit {
|
||||
self.eventTask?.cancel()
|
||||
self.bootstrapTask?.cancel()
|
||||
self.outboxRetryTask?.cancel()
|
||||
self.outboxChangesTask?.cancel()
|
||||
self.activeSessionRunIndicatorTimeoutTask?.cancel()
|
||||
for (_, task) in self.questionEvictionTasks {
|
||||
task.cancel()
|
||||
}
|
||||
for (_, task) in self.pendingRunOwnerTasks {
|
||||
task.cancel()
|
||||
}
|
||||
@@ -875,6 +883,8 @@ extension OpenClawChatViewModel {
|
||||
await self.syncActiveSessionSubscription(startingWith: context.session.key)
|
||||
guard self.isCurrentBootstrap(context) else { return }
|
||||
|
||||
Task { [weak self] in await self?.refreshQuestions() }
|
||||
|
||||
let payload = try await transport.requestHistory(sessionKey: context.session.key)
|
||||
guard self.isCurrentBootstrap(context) else { return }
|
||||
_ = self.applyHistoryPayload(
|
||||
|
||||
@@ -801,6 +801,7 @@ extension GatewayChannelActor {
|
||||
let allowedOperatorScopes: Set = [
|
||||
"operator.admin",
|
||||
"operator.approvals",
|
||||
"operator.questions",
|
||||
"operator.read",
|
||||
"operator.talk.secrets",
|
||||
"operator.write",
|
||||
|
||||
@@ -52,6 +52,7 @@ extension GatewayChannelActor {
|
||||
"operator.read",
|
||||
"operator.write",
|
||||
"operator.approvals",
|
||||
"operator.questions",
|
||||
"operator.pairing",
|
||||
]
|
||||
|
||||
|
||||
@@ -195,6 +195,20 @@ struct ChatGatewayRequestTests {
|
||||
#expect(String(decoding: encoded, as: UTF8.self).contains("a.png"))
|
||||
}
|
||||
|
||||
@Test func `question resolve request preserves nested answer contract`() throws {
|
||||
let request = OpenClawChatGatewayRequests.resolveQuestion(
|
||||
id: "ask_123",
|
||||
answers: ["meal": ["Pizza", "Salad"]])
|
||||
|
||||
#expect(request.method == "question.resolve")
|
||||
let data = try JSONEncoder().encode(request.params)
|
||||
let object = try #require(JSONSerialization.jsonObject(with: data) as? [String: Any])
|
||||
let answers = try #require(object["answers"] as? [String: Any])
|
||||
let values = try #require(answers["answers"] as? [String: Any])
|
||||
let meal = try #require(values["meal"] as? [String: Any])
|
||||
#expect(meal["answers"] as? [String] == ["Pizza", "Salad"])
|
||||
}
|
||||
|
||||
@Test func `long running requests share exact gateway timeout margins`() {
|
||||
#expect(OpenClawChatGatewayRequests.agentWait(runID: "run-1", timeoutMs: 1).timeoutMs == 5001)
|
||||
#expect(OpenClawChatGatewayRequests.agentWait(runID: "run-1", timeoutMs: 30000).timeoutMs == 35000)
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import Foundation
|
||||
import OpenClawProtocol
|
||||
import Testing
|
||||
@testable import OpenClawChatUI
|
||||
|
||||
@MainActor
|
||||
private func questionRecord(
|
||||
multiSelect: Bool = false,
|
||||
isOther: Bool = true,
|
||||
createdAtMs: Int = 1_000_000,
|
||||
expiresAtMs: Int = 4_000_000_000_000,
|
||||
status: QuestionStatus = .pending) -> QuestionRecord
|
||||
{
|
||||
QuestionRecord(
|
||||
id: "ask_123",
|
||||
questions: [
|
||||
Question(
|
||||
id: "meal",
|
||||
header: "Meal",
|
||||
question: "Choose dinner",
|
||||
options: [
|
||||
QuestionOption(label: "Pizza", description: "Fast"),
|
||||
QuestionOption(label: "Tacos"),
|
||||
],
|
||||
multiselect: multiSelect,
|
||||
isother: isOther),
|
||||
],
|
||||
agentid: "main",
|
||||
sessionkey: "agent:main:main",
|
||||
createdatms: createdAtMs,
|
||||
expiresatms: expiresAtMs,
|
||||
status: status)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func `question card single select and other are exclusive`() {
|
||||
let model = OpenClawQuestionCardModel(record: questionRecord())
|
||||
model.toggleOption(questionID: "meal", label: "Pizza")
|
||||
#expect(model.beginSubmission() == ["meal": ["Pizza"]])
|
||||
model.failSubmission("retry")
|
||||
|
||||
model.setOtherText(questionID: "meal", value: " Salad ")
|
||||
#expect(model.selectedOptions["meal"]?.isEmpty == true)
|
||||
#expect(model.beginSubmission() == ["meal": ["Salad"]])
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func `question card multi select uses declared option order`() {
|
||||
let model = OpenClawQuestionCardModel(record: questionRecord(multiSelect: true))
|
||||
model.toggleOption(questionID: "meal", label: "Tacos")
|
||||
model.toggleOption(questionID: "meal", label: "Pizza")
|
||||
#expect(model.beginSubmission() == ["meal": ["Pizza", "Tacos"]])
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func `question card maps expiry and answer origin`() {
|
||||
let now = Date(timeIntervalSince1970: 1500)
|
||||
let expired = OpenClawQuestionCardModel(record: questionRecord(expiresAtMs: 1_499_000))
|
||||
#expect(expired.status(at: now) == .expired)
|
||||
#expect(expired.remainingSeconds(at: now) == 0)
|
||||
|
||||
let remote = OpenClawQuestionCardModel(record: questionRecord())
|
||||
remote.apply(resolved: OpenClawQuestionResolvedEvent(id: remote.id, status: .answered))
|
||||
#expect(remote.status(at: Date(timeIntervalSince1970: 1500)) == .answeredElsewhere)
|
||||
|
||||
let local = OpenClawQuestionCardModel(record: questionRecord())
|
||||
local.markAnsweredLocally()
|
||||
local.apply(resolved: OpenClawQuestionResolvedEvent(id: local.id, status: .answered))
|
||||
#expect(local.status(at: Date(timeIntervalSince1970: 1500)) == .answered)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func `question card pending refresh preserves submission`() {
|
||||
let model = OpenClawQuestionCardModel(record: questionRecord(expiresAtMs: Int.max))
|
||||
model.toggleOption(questionID: "meal", label: "Pizza")
|
||||
#expect(model.beginSubmission() != nil)
|
||||
|
||||
#expect(model.apply(record: questionRecord(createdAtMs: 2_000_000, expiresAtMs: Int.max)))
|
||||
#expect(model.status(at: Date(timeIntervalSince1970: 1500)) == .submitting)
|
||||
|
||||
#expect(model.apply(record: questionRecord(createdAtMs: 2_000_000, expiresAtMs: Int.max, status: .answered)))
|
||||
#expect(model.status(at: Date(timeIntervalSince1970: 1500)) == .answeredElsewhere)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func `question card retains terminal feedback for gateway grace`() {
|
||||
let observedAt = Date(timeIntervalSince1970: 1500)
|
||||
let model = OpenClawQuestionCardModel(record: questionRecord())
|
||||
model.apply(resolved: .init(id: model.id, status: .answered), at: observedAt)
|
||||
|
||||
#expect(model.shouldRetainAfterList(at: observedAt.addingTimeInterval(14)))
|
||||
#expect(!model.shouldRetainAfterList(at: observedAt.addingTimeInterval(15)))
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func `question card locally expired state enters terminal retention`() {
|
||||
let expiresAt = Date(timeIntervalSince1970: 1500)
|
||||
let model = OpenClawQuestionCardModel(record: questionRecord(expiresAtMs: 1_500_000))
|
||||
|
||||
#expect(model.observeLocalExpiry(at: expiresAt))
|
||||
#expect(model.shouldRetainAfterList(at: expiresAt.addingTimeInterval(14)))
|
||||
#expect(!model.shouldRetainAfterList(at: expiresAt.addingTimeInterval(15)))
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func `question card stores local answers in gateway record shape`() throws {
|
||||
let model = OpenClawQuestionCardModel(record: questionRecord())
|
||||
model.toggleOption(questionID: "meal", label: "Pizza")
|
||||
model.markAnsweredLocally()
|
||||
|
||||
let data = try JSONEncoder().encode(model.record.answers)
|
||||
let json = try #require(String(data: data, encoding: .utf8))
|
||||
#expect(json.contains("\"meal\":{\"answers\":[\"Pizza\"]}"))
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import OpenClawKit
|
||||
import OpenClawProtocol
|
||||
import Testing
|
||||
@testable import OpenClawChatUI
|
||||
|
||||
@@ -612,6 +613,7 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
|
||||
private let waitForRunCompletionHook:
|
||||
(@Sendable (String, Int) async -> OpenClawChatRunObservation)?
|
||||
private let acquireSessionSettingsRouteLeaseHook: (@Sendable () async -> Void)?
|
||||
private let listQuestionsHook: (@Sendable () async throws -> [QuestionRecord])?
|
||||
private let healthResponses: [Bool]
|
||||
|
||||
private let stream: AsyncStream<OpenClawChatTransportEvent>
|
||||
@@ -643,6 +645,7 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
|
||||
sendMessageStatus: String = "ok",
|
||||
waitForRunCompletionHook: (@Sendable (String, Int) async -> OpenClawChatRunObservation)? = nil,
|
||||
acquireSessionSettingsRouteLeaseHook: (@Sendable () async -> Void)? = nil,
|
||||
listQuestionsHook: (@Sendable () async throws -> [QuestionRecord])? = nil,
|
||||
healthResponses: [Bool] = [true])
|
||||
{
|
||||
self.historyResponses = historyResponses
|
||||
@@ -668,6 +671,7 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
|
||||
self.sendMessageStatus = sendMessageStatus
|
||||
self.waitForRunCompletionHook = waitForRunCompletionHook
|
||||
self.acquireSessionSettingsRouteLeaseHook = acquireSessionSettingsRouteLeaseHook
|
||||
self.listQuestionsHook = listQuestionsHook
|
||||
self.healthResponses = healthResponses
|
||||
var cont: AsyncStream<OpenClawChatTransportEvent>.Continuation!
|
||||
self.stream = AsyncStream { c in
|
||||
@@ -955,6 +959,10 @@ private final class TestChatTransport: @unchecked Sendable, OpenClawChatTranspor
|
||||
return self.healthResponses.last ?? true
|
||||
}
|
||||
|
||||
func listQuestions() async throws -> [QuestionRecord] {
|
||||
try await self.listQuestionsHook?() ?? []
|
||||
}
|
||||
|
||||
func waitForRunCompletion(
|
||||
runId: String,
|
||||
timeoutMs: Int) async -> OpenClawChatRunObservation
|
||||
@@ -1196,8 +1204,210 @@ extension TestChatTransportState {
|
||||
}
|
||||
}
|
||||
|
||||
private actor QuestionListGate {
|
||||
private var continuation: CheckedContinuation<[QuestionRecord], Never>?
|
||||
|
||||
var isWaiting: Bool {
|
||||
self.continuation != nil
|
||||
}
|
||||
|
||||
func wait() async -> [QuestionRecord] {
|
||||
await withCheckedContinuation { continuation in
|
||||
self.continuation = continuation
|
||||
}
|
||||
}
|
||||
|
||||
func resume(with records: [QuestionRecord]) {
|
||||
self.continuation?.resume(returning: records)
|
||||
self.continuation = nil
|
||||
}
|
||||
}
|
||||
|
||||
private actor QuestionListEventRace {
|
||||
private var firstContinuation: CheckedContinuation<[QuestionRecord], Never>?
|
||||
private var callCount = 0
|
||||
private let currentRecords: [QuestionRecord]
|
||||
|
||||
init(currentRecords: [QuestionRecord]) {
|
||||
self.currentRecords = currentRecords
|
||||
}
|
||||
|
||||
var firstIsWaiting: Bool {
|
||||
self.firstContinuation != nil
|
||||
}
|
||||
|
||||
var calls: Int {
|
||||
self.callCount
|
||||
}
|
||||
|
||||
func request() async -> [QuestionRecord] {
|
||||
self.callCount += 1
|
||||
if self.callCount == 1 {
|
||||
return await withCheckedContinuation { continuation in
|
||||
self.firstContinuation = continuation
|
||||
}
|
||||
}
|
||||
return self.currentRecords
|
||||
}
|
||||
|
||||
func resumeFirst(with records: [QuestionRecord]) {
|
||||
self.firstContinuation?.resume(returning: records)
|
||||
self.firstContinuation = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func chatQuestionRecord(
|
||||
id: String,
|
||||
status: QuestionStatus = .pending,
|
||||
expiresAtMs: Int = 4_000_000_000_000) -> QuestionRecord
|
||||
{
|
||||
QuestionRecord(
|
||||
id: id,
|
||||
questions: [
|
||||
Question(
|
||||
id: "choice",
|
||||
header: "Choice",
|
||||
question: "Choose",
|
||||
options: [QuestionOption(label: "One"), QuestionOption(label: "Two")]),
|
||||
],
|
||||
agentid: "main",
|
||||
sessionkey: "main",
|
||||
createdatms: 1,
|
||||
expiresatms: expiresAtMs,
|
||||
status: status)
|
||||
}
|
||||
|
||||
@Suite(.serialized)
|
||||
struct ChatViewModelTests {
|
||||
@Test @MainActor func `locally expired question is evicted after terminal grace`() {
|
||||
let viewModel = OpenClawChatViewModel(
|
||||
sessionKey: "main",
|
||||
transport: TestChatTransport(historyResponses: []))
|
||||
let expiresAt = Date(timeIntervalSince1970: 1500)
|
||||
viewModel.upsertQuestion(chatQuestionRecord(id: "ask_local", expiresAtMs: 1_500_000))
|
||||
let model = viewModel.questionCards[0]
|
||||
|
||||
viewModel.evictQuestionIfTerminalGraceElapsed(model, at: expiresAt)
|
||||
#expect(viewModel.questionCards.map(\.id) == ["ask_local"])
|
||||
|
||||
viewModel.evictQuestionIfTerminalGraceElapsed(
|
||||
model,
|
||||
at: expiresAt.addingTimeInterval(15))
|
||||
#expect(viewModel.questionCards.isEmpty)
|
||||
}
|
||||
|
||||
@Test @MainActor func `stale question list cannot overwrite a newer event`() async throws {
|
||||
let gate = QuestionListGate()
|
||||
let transport = TestChatTransport(
|
||||
historyResponses: [],
|
||||
listQuestionsHook: { await gate.wait() })
|
||||
let viewModel = OpenClawChatViewModel(sessionKey: "main", transport: transport)
|
||||
let refresh = Task { await viewModel.refreshQuestions() }
|
||||
try await waitUntil("question list request") { await gate.isWaiting }
|
||||
|
||||
viewModel.upsertQuestion(chatQuestionRecord(id: "ask_new"))
|
||||
await gate.resume(with: [chatQuestionRecord(id: "ask_old")])
|
||||
await refresh.value
|
||||
|
||||
#expect(viewModel.questionCards.map(\.id) == ["ask_new"])
|
||||
}
|
||||
|
||||
@Test @MainActor func `definitive question list rejection clears stale cards`() async {
|
||||
let transport = TestChatTransport(
|
||||
historyResponses: [],
|
||||
listQuestionsHook: {
|
||||
throw GatewayResponseError(
|
||||
method: "question.list",
|
||||
code: "INVALID_REQUEST",
|
||||
message: "unknown method: question.list",
|
||||
details: nil)
|
||||
})
|
||||
let viewModel = OpenClawChatViewModel(sessionKey: "main", transport: transport)
|
||||
viewModel.upsertQuestion(chatQuestionRecord(id: "ask_stale"))
|
||||
|
||||
await viewModel.refreshQuestions()
|
||||
|
||||
#expect(viewModel.questionCards.isEmpty)
|
||||
}
|
||||
|
||||
@Test @MainActor func `transient question list rejection preserves event cards`() async {
|
||||
let transport = TestChatTransport(
|
||||
historyResponses: [],
|
||||
listQuestionsHook: {
|
||||
throw GatewayResponseError(
|
||||
method: "question.list",
|
||||
code: "UNAVAILABLE",
|
||||
message: "try again",
|
||||
details: nil)
|
||||
})
|
||||
let viewModel = OpenClawChatViewModel(sessionKey: "main", transport: transport)
|
||||
viewModel.upsertQuestion(chatQuestionRecord(id: "ask_live"))
|
||||
|
||||
await viewModel.refreshQuestions()
|
||||
|
||||
#expect(viewModel.questionCards.map(\.id) == ["ask_live"])
|
||||
}
|
||||
|
||||
@Test @MainActor func `question recovery does not block bootstrap history`() async throws {
|
||||
let questionGate = QuestionListGate()
|
||||
let historyCalls = AsyncCounter()
|
||||
let transport = TestChatTransport(
|
||||
historyResponses: [historyPayload()],
|
||||
requestHistoryHook: { _ in _ = await historyCalls.increment() },
|
||||
listQuestionsHook: { await questionGate.wait() })
|
||||
let viewModel = OpenClawChatViewModel(sessionKey: "main", transport: transport)
|
||||
|
||||
viewModel.load()
|
||||
try await waitUntil("question recovery request") { await questionGate.isWaiting }
|
||||
try await waitUntil("history during question recovery") { await historyCalls.current() == 1 }
|
||||
await questionGate.resume(with: [])
|
||||
}
|
||||
|
||||
@Test @MainActor func `resolved event reconciles after discarding older question list`() async throws {
|
||||
let race = QuestionListEventRace(currentRecords: [chatQuestionRecord(id: "ask_other")])
|
||||
let transport = TestChatTransport(
|
||||
historyResponses: [],
|
||||
listQuestionsHook: { await race.request() })
|
||||
let viewModel = OpenClawChatViewModel(sessionKey: "main", transport: transport)
|
||||
let initialRefresh = Task { await viewModel.refreshQuestions() }
|
||||
try await waitUntil("first question list request") { await race.firstIsWaiting }
|
||||
|
||||
viewModel.handleTransportEvent(.questionResolved(.init(id: "ask_done", status: .answered)))
|
||||
try await waitUntil("question event reconciliation") { await race.calls == 2 }
|
||||
await race.resumeFirst(with: [chatQuestionRecord(id: "ask_done")])
|
||||
await initialRefresh.value
|
||||
for _ in 0..<100 where viewModel.questionCards.map(\.id) != ["ask_other"] {
|
||||
await Task.yield()
|
||||
}
|
||||
#expect(viewModel.questionCards.map(\.id) == ["ask_other"])
|
||||
}
|
||||
|
||||
@Test @MainActor func `question list retains recently resolved card`() async {
|
||||
let transport = TestChatTransport(
|
||||
historyResponses: [],
|
||||
listQuestionsHook: { [] })
|
||||
let viewModel = OpenClawChatViewModel(sessionKey: "main", transport: transport)
|
||||
viewModel.upsertQuestion(chatQuestionRecord(id: "ask_done"))
|
||||
viewModel.resolveQuestionEvent(.init(id: "ask_done", status: .answered))
|
||||
|
||||
await viewModel.refreshQuestions()
|
||||
|
||||
#expect(viewModel.questionCards.map(\.id) == ["ask_done"])
|
||||
#expect(viewModel.questionCards.first?.status() == .answeredElsewhere)
|
||||
}
|
||||
|
||||
@Test @MainActor func `question card is evicted after terminal grace`() {
|
||||
let transport = TestChatTransport(historyResponses: [])
|
||||
let viewModel = OpenClawChatViewModel(sessionKey: "main", transport: transport)
|
||||
viewModel.upsertQuestion(chatQuestionRecord(id: "ask_done"))
|
||||
viewModel.resolveQuestionEvent(.init(id: "ask_done", status: .answered))
|
||||
let model = viewModel.questionCards[0]
|
||||
|
||||
viewModel.evictQuestionIfTerminalGraceElapsed(model, at: Date().addingTimeInterval(16))
|
||||
|
||||
#expect(viewModel.questionCards.isEmpty)
|
||||
}
|
||||
|
||||
@Test func `context usage fraction validates freshness and token bounds`() {
|
||||
func fraction(total: Int?, fresh: Bool? = true, context: Int?) -> Double? {
|
||||
OpenClawChatViewModel.chatContextUsageFraction(
|
||||
|
||||
@@ -13,7 +13,7 @@ e5e67ddf3cab38fcbf9220bc3160715897e2709d9a9ff6ff36f1ecc9453c2367 module/agent-c
|
||||
30452bae2a689fb75dcb6dba9ef11e5b95f9cbe0c62eb190a0fcb82d0a19ae52 module/agent-core
|
||||
74daa746deb548379d3f0d6eac3c4d082df1034c4360cc03bf51fee0f10a2e4d module/agent-harness
|
||||
e09226afd443cee02ed648f032f53bfeb60585617bb523a649995764d3c38da9 module/agent-harness-exec-review-runtime
|
||||
6131781d5c77140711114835b64e087cc81eb87fde6644dd6b2b1f3dafb28d32 module/agent-harness-runtime
|
||||
b4360e05090c276641edd472d29e504828b59de335e9d5711d67053c7d0f7bd5 module/agent-harness-runtime
|
||||
ec22d7a039fb58d0b8343ad149322960d3d8ca58b3f4c70f2fa8a099f8186d0c module/agent-harness-task-runtime
|
||||
5f63bf587bf3547d59d0dc5d0dc2fee54745aa6edaab4aa3ae700dba03443edb module/agent-harness-tool-runtime
|
||||
5168648cd946abad8a92822889f13ceacc87ed502314a66190d0b1eb8ebe76ea module/agent-media-payload
|
||||
@@ -104,7 +104,7 @@ d4ade9c9530585638e57602fcbe3ba1caa21a8d1ce88d87019d2d7048e3988b4 module/core
|
||||
e6fc74e18435cd4639801d9bd1f224154d1cd9405cdb3ba91a24b67c0792e9d2 module/dangerous-name-runtime
|
||||
4af19d59c2f18674e7d7f7dc1b358b644dc707e6bd601dc47168bd9e4a669940 module/dedupe-runtime
|
||||
feb743a5f25297abc7bb47d22ef060e0ed54b0cff69ac50885681dfade970f8e module/delivery-queue-runtime
|
||||
9f87be19881a070787f650740d84551cc5fe0ffc2b073330cf4c55eb8e31c991 module/device-bootstrap
|
||||
7edf89b42d01805f8c1c5c7d9909ff82c0a2e9a5051a8001f84fa05fb8e0a751 module/device-bootstrap
|
||||
6215d3af5923bf5a616d73062534968b69f448e3e30adc64ae9caebdd1a46d71 module/diagnostic-runtime
|
||||
9885951a437128dd3ebea5a54dcd59e6e9f4a1ba78b166c2c4aa0a286f108fe5 module/direct-dm
|
||||
9cdf7fbc6551e0373d6ab007128a266b200b5c8916fc4f73ffa77192cb0c3e9b module/direct-dm-access
|
||||
@@ -236,7 +236,7 @@ fffda485ad0492168b0b58671a5fe232de028c64cd1c0e36ab710ba7d58bf9d3 module/provide
|
||||
69e205f87efbee176778ef5810c104dc0528e2545cafcf31b6cc8ea360e06020 module/provider-zai-endpoint
|
||||
56151035047a69e6163d5578023d00f51a2413b777f3784af88e06261c039345 module/proxy-capture
|
||||
08f9f6abbe7f01fab7f96d611058f0de55996026a0e90cfd014fd2fdbe62ba0a module/qa-runner-runtime
|
||||
99f8046623351bb03bf54f45691d437604ba27c4418d05b6c7d4da91f30a6179 module/question-gateway-runtime
|
||||
aa2a56b4448c8ebdec9d06aac95d809995f533093d42fa32cd75e1d852967245 module/question-gateway-runtime
|
||||
5e6ac90d11f2f6ed764f5c53ef98110551ff4beaa0d5dcc343a3e7d97e41844d module/realtime-bootstrap-context
|
||||
6f551c1da49e6652f4af775798f45d3f3d5949c884c2e1967fc6254d34e9867e module/realtime-transcription
|
||||
5f63f3b22b2a88f05da90e99b0c474dd450b6ea3e5d4145cbf50517bff8466cf module/realtime-voice
|
||||
|
||||
@@ -641,6 +641,13 @@ All actions are enabled by default; use `channels.imessage.actions` to turn indi
|
||||
- Legacy text-style tapbacks (`Liked "…"` plain text from very old Apple clients) cannot resolve approvals because they carry no message GUID; reaction resolution requires the structured tapback metadata that current macOS / iOS clients emit.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Question reactions (1️⃣ / 2️⃣ / 3️⃣ / 4️⃣)">
|
||||
For an `ask_user` prompt with one non-secret, single-select question and one to four options, OpenClaw adds numbered emoji choices. React to the delivered prompt with the matching number to answer it. The reaction must carry the stable GUID of the bot-authored message; OpenClaw then maps the number to the canonical option through the Gateway. Stale or duplicate taps are ignored.
|
||||
|
||||
Multi-question, multi-select, and free-text prompts remain text-reply-only. Question reactions follow normal iMessage DM/group admission rules. They are recognized even when general `reactionNotifications` is `"off"`, without turning unrelated reactions into agent events.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Config writes
|
||||
|
||||
@@ -339,6 +339,10 @@ Signal exec and plugin approval prompts use the top-level `approvals.exec` and `
|
||||
|
||||
Approval reaction resolution requires explicit Signal approvers from `channels.signal.allowFrom`, `channels.signal.defaultTo`, or the matching account-level fields. Direct same-chat exec approval prompts can still suppress the duplicate local `/approve` fallback without explicit approvers; no-approver group approvals keep the local fallback visible.
|
||||
|
||||
## Question reactions
|
||||
|
||||
For an `ask_user` prompt with one non-secret, single-select question and one to four options, Signal shows `1️⃣` through `4️⃣` beside the option labels. React to the delivered prompt with the matching number to answer it. OpenClaw verifies the reaction targets the bot-authored message, then maps the number to the canonical option through the Gateway. Stale or duplicate taps are ignored. Multi-question, multi-select, and free-text prompts remain text-reply-only; normal Signal DM/group admission rules authorize the sender.
|
||||
|
||||
## Delivery targets (CLI/cron)
|
||||
|
||||
- DMs: `signal:+15551234567` (or plain E.164).
|
||||
|
||||
@@ -228,6 +228,10 @@ WhatsApp can render exec and plugin approval prompts as `👍`/`👎` reactions,
|
||||
|
||||
WhatsApp approval reactions require explicit approvers in `allowFrom` (or `"*"`). `defaultTo` sets ordinary default message targets, not an approver list. Manual `/approve` commands still pass the normal WhatsApp sender-authorization path before approval resolution.
|
||||
|
||||
## Question reactions
|
||||
|
||||
For an `ask_user` prompt with one non-secret, single-select question and one to four options, WhatsApp shows `1️⃣` through `4️⃣` beside the option labels. React to the delivered prompt with the matching number to answer it. OpenClaw maps the number to the canonical option through the Gateway; stale or duplicate taps are ignored. Multi-question, multi-select, and free-text prompts remain text-reply-only. Normal WhatsApp DM/group admission rules authorize the reacting sender.
|
||||
|
||||
## Plugin hooks and privacy
|
||||
|
||||
Inbound WhatsApp messages can carry personal content, phone numbers, group identifiers, sender names, and session correlation fields. WhatsApp does not broadcast inbound `message_received` hook payloads to plugins unless you opt in:
|
||||
|
||||
@@ -859,6 +859,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Lifecycle status reactions
|
||||
- H2: Reactions (message tool)
|
||||
- H2: Approval reactions
|
||||
- H2: Question reactions
|
||||
- H2: Delivery targets (CLI/cron)
|
||||
- H2: Aliases
|
||||
- H2: Troubleshooting
|
||||
@@ -1053,6 +1054,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Runtime model
|
||||
- H2: Call the current requester with MeowCaller (experimental)
|
||||
- H2: Approval prompts
|
||||
- H2: Question reactions
|
||||
- H2: Plugin hooks and privacy
|
||||
- H2: Access control and activation
|
||||
- H2: Configured ACP bindings
|
||||
@@ -5126,6 +5128,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H3: 8. Voice + expanded Android command surface
|
||||
- H3: 9. Workspace files (read-only)
|
||||
- H2: Review command approvals
|
||||
- H2: Answer agent questions
|
||||
- H2: Assistant entrypoints
|
||||
- H2: Notification forwarding
|
||||
- H2: Related
|
||||
@@ -5185,6 +5188,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
|
||||
- H2: Quick start (pair + connect)
|
||||
- H2: Health summaries
|
||||
- H2: Review command approvals
|
||||
- H2: Answer agent questions
|
||||
- H2: Optional direct Apple Watch node
|
||||
- H2: Relay-backed push for official builds
|
||||
- H2: Background alive beacons
|
||||
|
||||
@@ -360,6 +360,15 @@ Gateways that predate the unified approval methods fall back to the shipped
|
||||
exec-specific methods. Pending review still works, but retained terminal state
|
||||
and the richer cross-surface result require an updated Gateway.
|
||||
|
||||
## Answer agent questions
|
||||
|
||||
Chat shows pending Gateway questions as native cards for operator connections
|
||||
with `operator.questions` (or `operator.admin`). Cards support single- and
|
||||
multi-select options, option descriptions, free-text **Other** answers, and an
|
||||
expiry countdown. Reconnects reload pending questions from the Gateway. A card
|
||||
locks when this device answers it, another surface answers it first, or the
|
||||
question expires or is cancelled.
|
||||
|
||||
## Assistant entrypoints
|
||||
|
||||
Android supports launching OpenClaw from the system assistant trigger (Google Assistant). Holding the home button (or another `ACTION_ASSIST` trigger) opens the app; saying "Hey Google, ask OpenClaw `<prompt>`" matches the app's declared App Actions query pattern and hands the prompt into the chat composer without auto-sending it.
|
||||
|
||||
@@ -138,6 +138,15 @@ unified approval methods fall back to the shipped exec-specific methods;
|
||||
retained terminal state and richer cross-surface results require an updated
|
||||
Gateway.
|
||||
|
||||
## Answer agent questions
|
||||
|
||||
Chat shows pending Gateway questions as native cards for operator connections
|
||||
with `operator.questions` (or `operator.admin`). Cards support single- and
|
||||
multi-select options, option descriptions, free-text **Other** answers, and an
|
||||
expiry countdown. Reconnects reload pending questions from the Gateway. A card
|
||||
locks when this device answers it, another surface answers it first, or the
|
||||
question expires or is cancelled.
|
||||
|
||||
## Optional direct Apple Watch node
|
||||
|
||||
Direct mode gives the watch its own signed node identity and Gateway connection.
|
||||
|
||||
@@ -11,7 +11,7 @@ The full chat window is a native split view:
|
||||
|
||||
- **Sessions sidebar**: searchable session list with pinned and recent sections. Spawned child sessions nest beneath their parent when the Gateway provides hierarchy metadata; collapsed parents summarize running, failed, and unread descendants. Context menus support rename, pin, fork, read/unread, archive/restore, copy session key, and delete. A toolbar button (or Cmd-N) creates a real new session via `sessions.create`.
|
||||
- **Window toolbar**: context-usage ring (tokens and session cost, with a compact action), model controls, and a session actions menu. Models are grouped by provider with the default provider first, while pinned and recent models remain at the top. The controls can inherit or override the model's thinking level, choose tool-call verbosity, and toggle Fast responses. The menu can rename or fork the current session and update its pin, read, or archive state. **Sessions…** (Shift-Cmd-S) opens the Active/Archived manager for gateway search, rename, pin, archive, and restore. Separate menu checkmarks show or hide assistant reasoning and tool activity; both are on by default and remembered across launches.
|
||||
- **Transcript and composer**: assistant messages render as plain text with an avatar, user messages as accent bubbles. Empty chats offer desktop starter prompts. Typing `/` opens slash-command autocomplete backed by `commands.list`, with arrow/Tab/Return/Escape keyboard navigation. Right-click a message to copy its visible Markdown without hidden reasoning. Truncated assistant messages also offer **Open Full Message**, which loads a selectable Markdown reader. Use **Listen** for gateway TTS with a local speech fallback.
|
||||
- **Transcript and composer**: assistant messages render as plain text with an avatar, user messages as accent bubbles. Pending agent questions render as native cards with single- or multi-select options, free-text **Other** answers, expiry countdowns, and shared terminal state. Empty chats offer desktop starter prompts. Typing `/` opens slash-command autocomplete backed by `commands.list`, with arrow/Tab/Return/Escape keyboard navigation. Right-click a message to copy its visible Markdown without hidden reasoning. Truncated assistant messages also offer **Open Full Message**, which loads a selectable Markdown reader. Use **Listen** for gateway TTS with a local speech fallback.
|
||||
- **Voice controls**: the composer can start or stop the existing macOS Talk Mode without replacing its menu-bar overlay. While Talk Mode is active, the composer shows its listening/thinking/speaking state, live audio activity, and an expandable rolling transcript. Right-click the Talk button to choose **System Default** or a connected microphone; this is the same microphone selection used by Voice Wake and push-to-talk. If a selected microphone disconnects, the active Talk session falls back to the system default and tries the selection again the next time Talk Mode starts. A separate microphone action records a voice note when Talk Mode does not own audio capture.
|
||||
|
||||
The anchored compact chat panel from the menu bar keeps the compact single-column layout with the same model, thinking, verbosity, and Fast controls inline, plus starter prompts, Talk Mode, voice notes, and Listen. Assistant reasoning and tool activity remain hidden in this compact surface.
|
||||
@@ -46,7 +46,7 @@ Disable the feature entirely with **Settings → General → Quick Chat**; the s
|
||||
|
||||
## How it is wired
|
||||
|
||||
- Data plane: Gateway WS methods `chat.history`, `chat.message.get`, `chat.send`, `chat.abort`, `chat.inject`, and events `chat`, `agent`, `presence`, `tick`, `health`.
|
||||
- Data plane: Gateway WS methods `chat.history`, `chat.message.get`, `chat.send`, `chat.abort`, `chat.inject`, plus `question.list` and `question.resolve`, and events `chat`, `agent`, `presence`, `tick`, `health`; question cards follow `question.requested` and `question.resolved` events and refresh from `question.list` after reconnects.
|
||||
- `chat.history` returns a display-normalized transcript: inline directive tags are stripped from visible text, plain-text tool-call XML payloads (`<tool_call>`, `<function_call>`, `<tool_calls>`, `<function_calls>`, including truncated blocks) and leaked model control tokens are stripped, pure silent-token assistant rows such as exact `NO_REPLY`/`no_reply` are omitted, and oversized rows can be replaced with a truncated placeholder.
|
||||
- Session: defaults to the primary session as above; the UI can switch between sessions.
|
||||
- Unread state: after a session activates and its live history loads successfully, the app clears that session's unread marker. Failed history loads do not clear it; a transient patch failure retries on the next activation.
|
||||
|
||||
@@ -227,12 +227,14 @@ cwd creates a fresh approval.
|
||||
|
||||
Codex MCP tool approval elicitations route through OpenClaw's plugin approval
|
||||
flow when Codex marks `_meta.codex_approval_kind` as `"mcp_tool_call"`. Codex
|
||||
`request_user_input` prompts are sent back to the originating chat. A single
|
||||
non-secret choice uses typed channel buttons when the channel supports them,
|
||||
and the Control UI shows non-secret questions as a structured card. The next
|
||||
queued follow-up message answers that native server request instead of being
|
||||
steered as extra context. Secret questions stay on the warned text-reply path.
|
||||
Other MCP elicitation requests fail closed.
|
||||
`request_user_input` registers a provider-neutral gateway question for the
|
||||
originating session. The Control UI renders the gateway question card, and a
|
||||
single non-secret choice uses typed channel buttons when the channel supports
|
||||
them. Button taps, Control UI answers, and the next queued plain-text reply all
|
||||
resolve the same gateway record before OpenClaw returns the app-server answer.
|
||||
Codex auto-resolution and attempt aborts bound the wait and cancel the record.
|
||||
Secret questions stay entirely on the warned text-reply path. Other MCP
|
||||
elicitation requests fail closed.
|
||||
|
||||
For the general plugin approval flow that carries these prompts, see
|
||||
[Plugin permission requests](/plugins/plugin-permission-requests).
|
||||
|
||||
@@ -279,10 +279,10 @@ keeps `/btw` behavior identical to other non-Codex runtimes.
|
||||
surface.
|
||||
- PI session state does not migrate when an agent switches to `copilot`.
|
||||
Selection is per attempt; existing PI sessions remain valid.
|
||||
- `ask_user` uses the same OpenClaw prompt-and-reply path as the Codex
|
||||
harness: when the Copilot SDK asks for user input, OpenClaw posts a
|
||||
blocking prompt to the active channel/TUI, and the next queued user
|
||||
message resolves the SDK request.
|
||||
- `ask_user` uses the provider-neutral gateway question runtime. The Control
|
||||
UI shows the same question card as other OpenClaw questions, supported
|
||||
channels render choice buttons, and the next queued plain-text message
|
||||
resolves that gateway record before the SDK request returns.
|
||||
|
||||
## Permissions and ask_user
|
||||
|
||||
@@ -361,10 +361,11 @@ a configured `github-copilot` auth profile). When the resolved mode is
|
||||
`useLoggedInUser`, the session-level field is omitted so the SDK keeps
|
||||
deriving identity from the logged-in identity.
|
||||
|
||||
`ask_user` uses `SessionConfig.onUserInputRequest`. The bridge accepts choice
|
||||
indexes or labels for fixed-choice requests, accepts free-form answers when
|
||||
the SDK request allows them, and cancels a pending request when the OpenClaw
|
||||
attempt is aborted.
|
||||
`ask_user` uses `SessionConfig.onUserInputRequest`. The bridge registers SDK
|
||||
choices or option-less free-text prompts as gateway questions, accepts choice
|
||||
indexes or labels for fixed-choice requests, and accepts free-form answers
|
||||
when the SDK request allows them. Aborting the OpenClaw attempt cancels the
|
||||
gateway record and returns an empty SDK answer.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -145,8 +145,12 @@ Button semantics:
|
||||
`ask_user` question. Like `approval`, this is an OpenClaw runtime action;
|
||||
agents and plugins must not synthesize question IDs. Telegram, Discord, and
|
||||
Slack map it to transport-private native callbacks and resolve the choice
|
||||
through the Gateway. Other channels degrade the controls to label text, and
|
||||
the user can answer with a plain-text reply.
|
||||
through the Gateway. When the question becomes answered, expired, or
|
||||
cancelled, those channels edit the delivered message, remove its actions,
|
||||
and append the terminal status. WhatsApp, Signal, and iMessage render up to
|
||||
four single-select choices as `1️⃣` through `4️⃣` reactions. Other question
|
||||
shapes degrade to label text, and the user can answer with a plain-text
|
||||
reply.
|
||||
- `action.type: "url"` opens a normal link.
|
||||
- `action.type: "web-app"` launches a channel-native web app. Set `url` for a
|
||||
URL-backed app or `widgetId` for an OpenClaw-hosted widget whose launch
|
||||
|
||||
@@ -19,7 +19,10 @@ runs do not receive it.
|
||||
|
||||
You can answer from any supported conversation surface:
|
||||
|
||||
- The web Control UI shows one unified question card.
|
||||
- The web Control UI docks a question panel directly above the composer. For
|
||||
multi-question prompts, the panel shows one question at a time and advances
|
||||
through a short stepper. After resolution, the panel closes and the chat
|
||||
keeps only a compact answer summary.
|
||||
- Telegram, Discord, and Slack render native buttons for a single-choice,
|
||||
single-question prompt.
|
||||
- A plain-text reply works on any channel. Reply with a number, an option label,
|
||||
@@ -29,8 +32,8 @@ OpenClaw always enables a free-text **Other** answer. The agent must not add an
|
||||
`Other` option to the authored option list.
|
||||
|
||||
Prompts that cannot use native buttons, including multi-question and
|
||||
multi-select prompts, degrade to readable text. The Control UI keeps the full
|
||||
structured card.
|
||||
multi-select prompts, degrade to readable text on channels. The Control UI
|
||||
keeps the full structured stepper.
|
||||
|
||||
## Timeout and no answer
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
|
||||
export type CodexSteeringQueueOptions = {
|
||||
debounceMs?: number;
|
||||
images?: EmbeddedRunAttemptParams["images"];
|
||||
isInboundUserMessage?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { setActiveEmbeddedRun } from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import {
|
||||
cancelPendingAgentQuestionForSession,
|
||||
claimPendingAgentQuestionAnswer,
|
||||
embeddedAgentLog,
|
||||
setActiveEmbeddedRun,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import {
|
||||
interruptCodexTurnBestEffort,
|
||||
retireCodexAppServerClientAfterTimedOutTurn,
|
||||
@@ -146,8 +151,31 @@ export async function activateCodexAttemptTurn(
|
||||
const handle = {
|
||||
kind: "embedded" as const,
|
||||
runId: params.runId,
|
||||
queueMessage: async (text: string, optionsLocal?: CodexSteeringQueueOptions) =>
|
||||
activeSteeringQueue.queue(text, optionsLocal),
|
||||
queueMessage: async (text: string, optionsLocal?: CodexSteeringQueueOptions) => {
|
||||
const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
|
||||
if (isInboundUserMessage && !optionsLocal?.images?.length) {
|
||||
const claimed = await claimPendingAgentQuestionAnswer({
|
||||
sessionKey: params.sessionKey ?? params.sessionId,
|
||||
text,
|
||||
});
|
||||
if (claimed) {
|
||||
return;
|
||||
}
|
||||
} else if (isInboundUserMessage) {
|
||||
try {
|
||||
await cancelPendingAgentQuestionForSession({
|
||||
sessionKey: params.sessionKey ?? params.sessionId,
|
||||
resolvedBy: "image-reply",
|
||||
});
|
||||
} catch (error) {
|
||||
// Cleanup failure must not drop the user's image turn.
|
||||
embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", {
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
await activeSteeringQueue.queue(text, optionsLocal);
|
||||
},
|
||||
isStreaming: () => !state.completed && !runAbortController.signal.aborted,
|
||||
isStopped: () => state.completed || state.timedOut || runAbortController.signal.aborted,
|
||||
isAbortable: () =>
|
||||
|
||||
@@ -20,12 +20,44 @@ import {
|
||||
const activeRunRegistrationMocks = vi.hoisted(() => ({
|
||||
clearActiveEmbeddedRun: vi.fn(),
|
||||
setActiveEmbeddedRun: vi.fn(),
|
||||
questionWaiters: new Map<string, (value: unknown) => void>(),
|
||||
cancelQuestionError: undefined as Error | undefined,
|
||||
}));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/agent-harness-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/agent-harness-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
cancelPendingAgentQuestionForSession: async (
|
||||
...args: Parameters<typeof actual.cancelPendingAgentQuestionForSession>
|
||||
) => {
|
||||
const error = activeRunRegistrationMocks.cancelQuestionError;
|
||||
activeRunRegistrationMocks.cancelQuestionError = undefined;
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return await actual.cancelPendingAgentQuestionForSession(...args);
|
||||
},
|
||||
callGatewayTool: async (...args: Parameters<typeof actual.callGatewayTool>) => {
|
||||
const [method, , rawParams] = args;
|
||||
const params = rawParams as { id?: string; answers?: unknown; cancel?: boolean } | undefined;
|
||||
if (method === "question.request") {
|
||||
return { id: params?.id, expiresAtMs: Date.now() + 60_000 };
|
||||
}
|
||||
if (method === "question.waitAnswer") {
|
||||
return await new Promise((resolve) => {
|
||||
activeRunRegistrationMocks.questionWaiters.set(params?.id ?? "", resolve);
|
||||
});
|
||||
}
|
||||
if (method === "question.resolve") {
|
||||
const result = params?.cancel
|
||||
? { status: "cancelled" as const }
|
||||
: { status: "answered" as const, answers: params?.answers };
|
||||
activeRunRegistrationMocks.questionWaiters.get(params?.id ?? "")?.(result);
|
||||
return result;
|
||||
}
|
||||
return await actual.callGatewayTool(...args);
|
||||
},
|
||||
clearActiveEmbeddedRun: (
|
||||
...args: Parameters<typeof actual.clearActiveEmbeddedRun>
|
||||
): ReturnType<typeof actual.clearActiveEmbeddedRun> => {
|
||||
@@ -165,6 +197,57 @@ describe("runCodexAppServerAttempt steering", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("still steers an image when gateway question cancellation fails", async () => {
|
||||
const { requests, waitForMethod, completeTurn, notify } = createStartedThreadHarness();
|
||||
const params = createSteeringParams();
|
||||
const run = runCodexAppServerAttempt(params, {
|
||||
pluginConfig: { appServer: { mode: "yolo" } },
|
||||
});
|
||||
await waitForMethod("turn/start");
|
||||
let handle:
|
||||
| {
|
||||
queueMessage: (
|
||||
text: string,
|
||||
options?: Parameters<typeof queueActiveRunMessageForTest>[2],
|
||||
) => Promise<void>;
|
||||
}
|
||||
| undefined;
|
||||
await vi.waitFor(() => {
|
||||
handle = activeRunRegistrationMocks.setActiveEmbeddedRun.mock.calls.findLast(
|
||||
(call) => call[0] === params.sessionId,
|
||||
)?.[1] as typeof handle;
|
||||
expect(handle).toBeDefined();
|
||||
}, fastWait);
|
||||
|
||||
activeRunRegistrationMocks.cancelQuestionError = new Error("gateway unavailable");
|
||||
const delivered = handle!.queueMessage("inspect this", {
|
||||
debounceMs: 0,
|
||||
images: [{ type: "image", data: PNG_1X1, mimeType: "image/png" }],
|
||||
isInboundUserMessage: true,
|
||||
});
|
||||
await vi.waitFor(
|
||||
() => expect(requests.map((entry) => entry.method)).toContain("turn/steer"),
|
||||
fastWait,
|
||||
);
|
||||
const steer = requests.findLast((entry) => entry.method === "turn/steer");
|
||||
const clientUserMessageId = (steer?.params as { clientUserMessageId?: string } | undefined)
|
||||
?.clientUserMessageId;
|
||||
if (!clientUserMessageId) {
|
||||
throw new Error("turn/steer clientUserMessageId missing");
|
||||
}
|
||||
await notify({
|
||||
method: "item/completed",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: { id: "steered-user-message", type: "userMessage", clientId: clientUserMessageId },
|
||||
},
|
||||
});
|
||||
await delivered;
|
||||
await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
});
|
||||
|
||||
it("accepts message-tool-only steering for active Codex app-server source replies", async () => {
|
||||
const { requests, waitForMethod, completeTurn } = createStartedThreadHarness();
|
||||
const params = createSteeringParams();
|
||||
@@ -344,7 +427,7 @@ describe("runCodexAppServerAttempt steering", () => {
|
||||
let handleRequest:
|
||||
| ((request: { id: string; method: string; params?: unknown }) => Promise<unknown>)
|
||||
| undefined;
|
||||
const request = vi.fn(async (method: string) => {
|
||||
const request = vi.fn(async (method: string, _params?: unknown) => {
|
||||
if (method === "thread/start") {
|
||||
return threadStartResult();
|
||||
}
|
||||
@@ -408,18 +491,30 @@ describe("runCodexAppServerAttempt steering", () => {
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1), fastWait);
|
||||
await waitAndQueueActiveRunMessage(params.sessionId, "2");
|
||||
await waitAndQueueActiveRunMessage(params.sessionId, "tool progress", { debounceMs: 0 });
|
||||
await vi.waitFor(
|
||||
() => expect(request.mock.calls.map(([method]) => method)).toContain("turn/steer"),
|
||||
fastWait,
|
||||
);
|
||||
const sourceSteer = request.mock.calls.findLast(([method]) => method === "turn/steer");
|
||||
const sourceMessageId = (sourceSteer?.[1] as { clientUserMessageId?: string } | undefined)
|
||||
?.clientUserMessageId;
|
||||
if (!sourceMessageId) {
|
||||
throw new Error("source turn/steer clientUserMessageId missing");
|
||||
}
|
||||
await notify({
|
||||
method: "item/completed",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
item: { id: "source-message", type: "userMessage", clientId: sourceMessageId },
|
||||
},
|
||||
});
|
||||
await waitAndQueueActiveRunMessage(params.sessionId, "2", { isInboundUserMessage: true });
|
||||
await expect(response).resolves.toEqual({
|
||||
answers: { mode: { answers: ["Deep"] } },
|
||||
});
|
||||
const requestCalls = request.mock.calls as unknown as Array<[string, unknown]>;
|
||||
expect(
|
||||
requestCalls.some(
|
||||
([method, callParams]) =>
|
||||
method === "turn/steer" &&
|
||||
(callParams as { expectedTurnId?: string } | undefined)?.expectedTurnId === "turn-1",
|
||||
),
|
||||
).toBe(false);
|
||||
expect(request.mock.calls.filter(([method]) => method === "turn/steer")).toHaveLength(1);
|
||||
|
||||
await notify({
|
||||
method: "turn/completed",
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import crypto from "node:crypto";
|
||||
import type { AgentHarnessUserInputQuestion } from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import type { MessagePresentation } from "openclaw/plugin-sdk/interactive-runtime";
|
||||
|
||||
export type CodexUserInputActionAnswer =
|
||||
| { type: "choice"; optionIndex: number }
|
||||
| { type: "answers"; answers: Record<string, string> };
|
||||
|
||||
type PendingAnswer = (answer: CodexUserInputActionAnswer) => boolean;
|
||||
|
||||
const pendingAnswers = new Map<string, PendingAnswer>();
|
||||
|
||||
export function registerCodexUserInputActions(answer: PendingAnswer): {
|
||||
token: string;
|
||||
dispose: () => void;
|
||||
} {
|
||||
const token = crypto.randomUUID();
|
||||
pendingAnswers.set(token, answer);
|
||||
return {
|
||||
token,
|
||||
dispose: () => pendingAnswers.delete(token),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveCodexUserInputAction(
|
||||
token: string,
|
||||
answer: CodexUserInputActionAnswer,
|
||||
): boolean {
|
||||
const pending = pendingAnswers.get(token);
|
||||
if (!pending || !pending(answer)) {
|
||||
return false;
|
||||
}
|
||||
pendingAnswers.delete(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function buildCodexUserInputPresentation(
|
||||
questions: readonly AgentHarnessUserInputQuestion[],
|
||||
token: string,
|
||||
): MessagePresentation | undefined {
|
||||
const question = questions.length === 1 ? questions[0] : undefined;
|
||||
if (!question || question.isSecret || !question.options?.length) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
title: question.header,
|
||||
tone: "info",
|
||||
blocks: [
|
||||
{
|
||||
type: "buttons",
|
||||
buttons: question.options.map((option, index) => {
|
||||
const action = {
|
||||
type: "command",
|
||||
command: `/codex answer ${token} choice:${index}`,
|
||||
} as const;
|
||||
return index === 0
|
||||
? { label: option.label, action, style: "primary" as const }
|
||||
: { label: option.label, action };
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function parseCodexUserInputActionAnswer(
|
||||
encoded: string,
|
||||
): CodexUserInputActionAnswer | undefined {
|
||||
const choiceMatch = encoded.match(/^choice:(\d+)$/u);
|
||||
if (choiceMatch) {
|
||||
const optionIndex = Number(choiceMatch[1]);
|
||||
return Number.isSafeInteger(optionIndex) ? { type: "choice", optionIndex } : undefined;
|
||||
}
|
||||
if (!encoded.startsWith("answers:")) {
|
||||
return undefined;
|
||||
}
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(decodeURIComponent(encoded.slice("answers:".length)));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
return undefined;
|
||||
}
|
||||
const entries = Object.entries(parsed);
|
||||
if (entries.length === 0 || entries.some(([key, value]) => !key || typeof value !== "string")) {
|
||||
return undefined;
|
||||
}
|
||||
return { type: "answers", answers: Object.fromEntries(entries) };
|
||||
}
|
||||
@@ -1,517 +1,257 @@
|
||||
// Codex tests cover user input bridge plugin behavior.
|
||||
import type { EmbeddedRunAttemptParams } from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
// Codex tests cover gateway-backed request_user_input behavior.
|
||||
import {
|
||||
claimPendingAgentQuestionAnswer,
|
||||
type AgentHarnessQuestionGatewayCall,
|
||||
type EmbeddedRunAttemptParams,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { resolveCodexUserInputAction } from "./user-input-actions.js";
|
||||
import { createCodexUserInputBridge } from "./user-input-bridge.js";
|
||||
|
||||
function createParams(): EmbeddedRunAttemptParams {
|
||||
type GatewayCallRecord = { method: string; opts: unknown; params: unknown };
|
||||
|
||||
function createParams(signal?: AbortSignal): EmbeddedRunAttemptParams {
|
||||
return {
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:session-1",
|
||||
agentId: "main",
|
||||
timeoutMs: 90_000,
|
||||
onBlockReply: vi.fn(),
|
||||
onAgentEvent: vi.fn(),
|
||||
abortSignal: signal,
|
||||
} as unknown as EmbeddedRunAttemptParams;
|
||||
}
|
||||
|
||||
function expectFirstBlockReplyText(params: EmbeddedRunAttemptParams): string {
|
||||
const onBlockReply = params.onBlockReply;
|
||||
if (onBlockReply === undefined) {
|
||||
throw new Error("Expected onBlockReply callback");
|
||||
}
|
||||
const payload = vi.mocked(onBlockReply).mock.calls[0]?.[0];
|
||||
if (typeof payload?.text !== "string") {
|
||||
throw new Error("Expected first block reply text");
|
||||
}
|
||||
return payload.text;
|
||||
function createGatewayStub() {
|
||||
const calls: GatewayCallRecord[] = [];
|
||||
let settleWait: ((value: unknown) => void) | undefined;
|
||||
const wait = new Promise<unknown>((resolve) => {
|
||||
settleWait = resolve;
|
||||
});
|
||||
const call: AgentHarnessQuestionGatewayCall = async (method, opts, params) => {
|
||||
calls.push({ method, opts, params });
|
||||
if (method === "question.request") {
|
||||
return { id: (params as { id: string }).id, expiresAtMs: Date.now() + 90_000 };
|
||||
}
|
||||
if (method === "question.waitAnswer") {
|
||||
return await wait;
|
||||
}
|
||||
if (method === "question.resolve") {
|
||||
const resolveParams = params as {
|
||||
answers?: { answers: Record<string, { answers: string[] }> };
|
||||
cancel?: boolean;
|
||||
};
|
||||
const result = resolveParams.cancel
|
||||
? { status: "cancelled" as const }
|
||||
: { status: "answered" as const, answers: resolveParams.answers! };
|
||||
settleWait?.(result);
|
||||
return result;
|
||||
}
|
||||
throw new Error(`unexpected gateway method: ${method}`);
|
||||
};
|
||||
return { call, calls };
|
||||
}
|
||||
|
||||
function requestParams(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-1",
|
||||
questions: [
|
||||
{
|
||||
id: "choice",
|
||||
header: "Mode",
|
||||
question: "Pick a mode",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [
|
||||
{ label: "Fast", description: "Use less reasoning" },
|
||||
{ label: "Deep", description: "Use more reasoning" },
|
||||
],
|
||||
},
|
||||
],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("Codex app-server user input bridge", () => {
|
||||
it("prompts the originating chat and resolves request_user_input from the next queued message", async () => {
|
||||
it("registers, presents, claims, and returns gateway answers", async () => {
|
||||
const params = createParams();
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-1",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-1",
|
||||
questions: [
|
||||
{
|
||||
id: "choice",
|
||||
header: "Mode",
|
||||
question: "Pick a mode",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [
|
||||
{ label: "Fast", description: "Use less reasoning" },
|
||||
{ label: "Deep", description: "Use more reasoning" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
expect(expectFirstBlockReplyText(params)).toContain("Pick a mode");
|
||||
expect(bridge.handleQueuedMessage("2")).toBe(true);
|
||||
|
||||
await expect(response).resolves.toEqual({
|
||||
answers: { choice: { answers: ["Deep"] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("emits a web question card and typed channel actions", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-actions",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-actions",
|
||||
questions: [
|
||||
{
|
||||
id: "mode",
|
||||
header: "Mode",
|
||||
question: "Pick a mode",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [{ label: "Fast" }, { label: "Deep" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const response = bridge.handleRequest({ id: "input-1", params: requestParams() });
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
expect(params.onAgentEvent).toHaveBeenCalledWith({
|
||||
stream: "question",
|
||||
data: expect.objectContaining({ phase: "requested", itemId: "tool-actions" }),
|
||||
});
|
||||
const presentation = vi.mocked(params.onBlockReply!).mock.calls[0]?.[0].presentation;
|
||||
const block = presentation?.blocks[0];
|
||||
expect(block?.type).toBe("buttons");
|
||||
if (block?.type !== "buttons") {
|
||||
throw new Error("expected typed question buttons");
|
||||
|
||||
const request = gateway.calls.find((entry) => entry.method === "question.request");
|
||||
if (!request) {
|
||||
throw new Error("expected question.request");
|
||||
}
|
||||
const action = block.buttons[1]?.action;
|
||||
expect(action?.type).toBe("command");
|
||||
if (action?.type !== "command") {
|
||||
throw new Error("expected command action");
|
||||
}
|
||||
const match = action.command.match(/^\/codex answer ([0-9a-f-]+) choice:1$/u);
|
||||
expect(match).not.toBeNull();
|
||||
expect(resolveCodexUserInputAction(match![1]!, { type: "choice", optionIndex: 1 })).toBe(true);
|
||||
|
||||
await expect(response).resolves.toEqual({ answers: { mode: { answers: ["Deep"] } } });
|
||||
expect(params.onAgentEvent).toHaveBeenLastCalledWith({
|
||||
stream: "question",
|
||||
data: expect.objectContaining({ phase: "resolved", itemId: "tool-actions" }),
|
||||
expect(request?.params).toMatchObject({
|
||||
sessionKey: "agent:main:session-1",
|
||||
agentId: "main",
|
||||
timeoutMs: 90_000,
|
||||
questions: requestParams().questions,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps numeric labels distinct from typed option indexes", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
const payload = vi.mocked(params.onBlockReply!).mock.calls[0]![0];
|
||||
expect(payload.channelData).toEqual({
|
||||
askUser: { questionId: (request.params as { id: string }).id },
|
||||
});
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-numeric-label",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-numeric-label",
|
||||
questions: [
|
||||
{
|
||||
id: "mode",
|
||||
header: "Mode",
|
||||
question: "Pick a mode",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [{ label: "2" }, { label: "Deep" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
const event = vi
|
||||
.mocked(params.onAgentEvent!)
|
||||
.mock.calls.find(([payload]) => payload.stream === "question")?.[0];
|
||||
const actionId =
|
||||
event && typeof event.data === "object" && event.data && "actionToken" in event.data
|
||||
? event.data.actionToken
|
||||
: undefined;
|
||||
expect(typeof actionId).toBe("string");
|
||||
expect(
|
||||
resolveCodexUserInputAction(String(actionId), {
|
||||
type: "answers",
|
||||
answers: { mode: "2" },
|
||||
}),
|
||||
).toBe(true);
|
||||
await expect(response).resolves.toEqual({ answers: { mode: { answers: ["2"] } } });
|
||||
});
|
||||
|
||||
it("preserves case-distinct structured option labels", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-case-label",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-case-label",
|
||||
questions: [
|
||||
{
|
||||
id: "mode",
|
||||
header: "Mode",
|
||||
question: "Pick a mode",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [{ label: "FAST" }, { label: " fast " }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
const event = vi
|
||||
.mocked(params.onAgentEvent!)
|
||||
.mock.calls.find(([payload]) => payload.stream === "question")?.[0];
|
||||
const actionId =
|
||||
event && typeof event.data === "object" && event.data && "actionToken" in event.data
|
||||
? event.data.actionToken
|
||||
: undefined;
|
||||
expect(
|
||||
resolveCodexUserInputAction(String(actionId), {
|
||||
type: "answers",
|
||||
answers: { mode: " fast " },
|
||||
}),
|
||||
).toBe(true);
|
||||
await expect(response).resolves.toEqual({ answers: { mode: { answers: [" fast "] } } });
|
||||
});
|
||||
|
||||
it("preserves reserved question ids in structured answers", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-reserved-id",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-reserved-id",
|
||||
questions: [
|
||||
{
|
||||
id: "__proto__",
|
||||
header: "Mode",
|
||||
question: "Pick a mode",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [{ label: "Safe" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
const event = vi
|
||||
.mocked(params.onAgentEvent!)
|
||||
.mock.calls.find(([payload]) => payload.stream === "question")?.[0];
|
||||
const actionId =
|
||||
event && typeof event.data === "object" && event.data && "actionToken" in event.data
|
||||
? event.data.actionToken
|
||||
: undefined;
|
||||
expect(
|
||||
resolveCodexUserInputAction(String(actionId), {
|
||||
type: "answers",
|
||||
answers: Object.fromEntries([["__proto__", "Safe"]]),
|
||||
}),
|
||||
).toBe(true);
|
||||
await expect(response).resolves.toEqual({
|
||||
answers: Object.fromEntries([["__proto__", { answers: ["Safe"] }]]),
|
||||
});
|
||||
});
|
||||
|
||||
it("does not expose secret questions as channel buttons", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-secret",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-secret",
|
||||
questions: [
|
||||
{
|
||||
id: "secret",
|
||||
header: "Secret",
|
||||
question: "Enter it",
|
||||
isOther: true,
|
||||
isSecret: true,
|
||||
options: [{ label: "Stored value" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
expect(vi.mocked(params.onBlockReply!).mock.calls[0]?.[0].presentation).toBeUndefined();
|
||||
expect(params.onAgentEvent).toHaveBeenCalledWith({
|
||||
stream: "question",
|
||||
data: expect.not.objectContaining({ actionToken: expect.anything() }),
|
||||
});
|
||||
expect(bridge.handleQueuedMessage("private")).toBe(true);
|
||||
await response;
|
||||
});
|
||||
|
||||
it("does not let a captured handle settle a replacement request", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
const question = {
|
||||
id: "answer",
|
||||
header: "Answer",
|
||||
question: "Continue?",
|
||||
isOther: true,
|
||||
isSecret: false,
|
||||
options: null,
|
||||
};
|
||||
const firstResponse = bridge.handleRequest({
|
||||
id: "input-first",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-first",
|
||||
questions: [question],
|
||||
},
|
||||
});
|
||||
const firstHandle = bridge.claimPendingRequest();
|
||||
expect(firstHandle).toBeDefined();
|
||||
|
||||
const secondResponse = bridge.handleRequest({
|
||||
id: "input-second",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-second",
|
||||
questions: [question],
|
||||
},
|
||||
});
|
||||
|
||||
await expect(firstResponse).resolves.toEqual({ answers: {} });
|
||||
expect(firstHandle?.cancel()).toBe(false);
|
||||
expect(bridge.claimPendingRequest()).toBeDefined();
|
||||
bridge.cancelPending();
|
||||
await expect(secondResponse).resolves.toEqual({ answers: {} });
|
||||
});
|
||||
|
||||
it("maps keyed multi-question replies to Codex answer ids", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-2",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-1",
|
||||
questions: [
|
||||
{
|
||||
id: "repo",
|
||||
header: "Repository",
|
||||
question: "Which repo?",
|
||||
isOther: true,
|
||||
isSecret: false,
|
||||
options: null,
|
||||
},
|
||||
{
|
||||
id: "scope",
|
||||
header: "Scope",
|
||||
question: "Which scope?",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [{ label: "Tests", description: "Only tests" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
expect(bridge.handleQueuedMessage("repo: openclaw\nscope: Tests")).toBe(true);
|
||||
|
||||
await expect(response).resolves.toEqual({
|
||||
answers: {
|
||||
repo: { answers: ["openclaw"] },
|
||||
scope: { answers: ["Tests"] },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects free-form option replies when Other is disabled", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-options",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-1",
|
||||
questions: [
|
||||
{
|
||||
id: "mode",
|
||||
header: "Mode",
|
||||
question: "Pick a mode",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [{ label: "Fast", description: "Use less reasoning" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
expect(bridge.handleQueuedMessage("banana")).toBe(true);
|
||||
|
||||
await expect(response).resolves.toEqual({
|
||||
answers: { mode: { answers: [] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("escapes prompt question and option text before chat display", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-escaped",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-1",
|
||||
questions: [
|
||||
{
|
||||
id: "mode",
|
||||
header: "Mode <@U123>",
|
||||
question: "Pick [trusted](https://evil) @here",
|
||||
isOther: false,
|
||||
isSecret: false,
|
||||
options: [{ label: "Fast <@U123>", description: "Use [less](https://evil)" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
const text = expectFirstBlockReplyText(params);
|
||||
expect(text).toContain("Mode <\uff20U123>");
|
||||
expect(text).toContain("Pick \uff3btrusted\uff3d\uff08https://evil\uff09 \uff20here");
|
||||
expect(text).toContain(
|
||||
"Fast <\uff20U123> - Use \uff3bless\uff3d\uff08https://evil\uff09",
|
||||
);
|
||||
expect(text).not.toContain("<@U123>");
|
||||
expect(text).not.toContain("[trusted](https://evil)");
|
||||
expect(text).not.toContain("@here");
|
||||
|
||||
expect(bridge.handleQueuedMessage("1")).toBe(true);
|
||||
await expect(response).resolves.toEqual({
|
||||
answers: { mode: { answers: ["Fast <@U123>"] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("clears pending prompts when Codex resolves the server request itself", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
});
|
||||
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-3",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-1",
|
||||
questions: [
|
||||
{
|
||||
id: "answer",
|
||||
header: "Answer",
|
||||
question: "Continue?",
|
||||
isOther: true,
|
||||
isSecret: false,
|
||||
options: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
bridge.handleNotification({
|
||||
method: "serverRequest/resolved",
|
||||
params: { threadId: "thread-1", requestId: "input-3" },
|
||||
});
|
||||
|
||||
await expect(response).resolves.toEqual({ answers: {} });
|
||||
expect(bridge.handleQueuedMessage("too late")).toBe(false);
|
||||
});
|
||||
|
||||
it("resolves malformed empty question prompts without waiting for chat input", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
expect(payload.presentationTextMode).toBe("fallback");
|
||||
expect(payload.text).toContain("Reply with the number or option text.");
|
||||
expect(payload.text).not.toContain("your own answer");
|
||||
const buttons = payload.presentation?.blocks.find((block) => block.type === "buttons");
|
||||
expect(buttons?.type === "buttons" ? buttons.buttons[1]?.action : undefined).toMatchObject({
|
||||
type: "question",
|
||||
optionValue: "Deep",
|
||||
});
|
||||
|
||||
await expect(
|
||||
bridge.handleRequest({
|
||||
id: "input-empty",
|
||||
params: {
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
itemId: "tool-1",
|
||||
questions: [],
|
||||
},
|
||||
claimPendingAgentQuestionAnswer({ sessionKey: params.sessionKey, text: "2" }),
|
||||
).resolves.toBe(true);
|
||||
await expect(response).resolves.toEqual({ answers: { choice: { answers: ["Deep"] } } });
|
||||
});
|
||||
|
||||
it("cancels the gateway record on run abort", async () => {
|
||||
const controller = new AbortController();
|
||||
const params = createParams(controller.signal);
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
signal: controller.signal,
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
const response = bridge.handleRequest({ id: "input-abort", params: requestParams() });
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
controller.abort();
|
||||
|
||||
await expect(response).resolves.toEqual({ answers: {} });
|
||||
expect(gateway.calls).toContainEqual(
|
||||
expect.objectContaining({
|
||||
method: "question.resolve",
|
||||
params: expect.objectContaining({ cancel: true, resolvedBy: "run-abort" }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not register a gateway question after the run already aborted", async () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
const params = createParams(controller.signal);
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
signal: controller.signal,
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
|
||||
await expect(
|
||||
bridge.handleRequest({ id: "input-already-aborted", params: requestParams() }),
|
||||
).resolves.toEqual({ answers: {} });
|
||||
expect(gateway.calls).toEqual([]);
|
||||
expect(params.onBlockReply).not.toHaveBeenCalled();
|
||||
expect(bridge.handleQueuedMessage("late answer")).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps secret questions on the warned text-only path", async () => {
|
||||
const params = createParams();
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-secret",
|
||||
params: requestParams({
|
||||
questions: [
|
||||
{
|
||||
id: "token",
|
||||
header: "Secret",
|
||||
question: "Enter token",
|
||||
isOther: true,
|
||||
isSecret: true,
|
||||
options: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
|
||||
const payload = vi.mocked(params.onBlockReply!).mock.calls[0]![0];
|
||||
expect(payload.text).toContain("This channel may show your reply");
|
||||
expect(payload.channelData).toBeUndefined();
|
||||
expect(gateway.calls).toHaveLength(0);
|
||||
expect(bridge.claimPendingRequest()?.answer("private")).toBe(true);
|
||||
await expect(response).resolves.toEqual({ answers: { token: { answers: ["private"] } } });
|
||||
});
|
||||
|
||||
it("cancels the matching gateway record on serverRequest/resolved", async () => {
|
||||
const params = createParams();
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
const response = bridge.handleRequest({ id: 42, params: requestParams() });
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
bridge.handleNotification({
|
||||
method: "serverRequest/resolved",
|
||||
params: { threadId: "thread-1", requestId: 42 },
|
||||
});
|
||||
|
||||
await expect(response).resolves.toEqual({ answers: {} });
|
||||
expect(gateway.calls).toContainEqual(
|
||||
expect.objectContaining({
|
||||
method: "question.resolve",
|
||||
params: expect.objectContaining({ cancel: true, resolvedBy: "run-abort" }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("passes Codex autoResolutionMs and option-less free text through", async () => {
|
||||
const params = createParams();
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCodexUserInputBridge({
|
||||
paramsForRun: params,
|
||||
threadId: "thread-1",
|
||||
turnId: "turn-1",
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
const response = bridge.handleRequest({
|
||||
id: "input-free",
|
||||
params: requestParams({
|
||||
autoResolutionMs: 60_000,
|
||||
questions: [
|
||||
{
|
||||
id: "notes",
|
||||
header: "Notes",
|
||||
question: "What should change?",
|
||||
isOther: true,
|
||||
isSecret: false,
|
||||
options: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
expect(gateway.calls[0]?.params).toMatchObject({
|
||||
timeoutMs: 60_000,
|
||||
questions: [expect.objectContaining({ id: "notes", options: [] })],
|
||||
});
|
||||
await claimPendingAgentQuestionAnswer({ sessionKey: params.sessionKey, text: "Refactor it" });
|
||||
await expect(response).resolves.toEqual({
|
||||
answers: { notes: { answers: ["Refactor it"] } },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* Bridges Codex item/tool user-input requests to OpenClaw messaging prompts and
|
||||
* turns replies into app-server answer payloads.
|
||||
*/
|
||||
/** Bridges Codex request_user_input calls to gateway questions and secret text prompts. */
|
||||
import {
|
||||
buildAgentHarnessUserInputAnswers,
|
||||
callGatewayTool,
|
||||
deliverAgentHarnessUserInputPrompt,
|
||||
embeddedAgentLog,
|
||||
emptyAgentHarnessUserInputAnswers,
|
||||
runAgentHarnessGatewayQuestion,
|
||||
type AgentHarnessQuestionGatewayCall,
|
||||
type AgentHarnessUserInputOption,
|
||||
type AgentHarnessUserInputQuestion,
|
||||
type EmbeddedRunAttemptParams,
|
||||
@@ -18,24 +18,24 @@ import {
|
||||
type JsonObject,
|
||||
type JsonValue,
|
||||
} from "./protocol.js";
|
||||
import { emitCodexAppServerEvent } from "./run-attempt-lifecycle.js";
|
||||
import {
|
||||
buildCodexUserInputPresentation,
|
||||
registerCodexUserInputActions,
|
||||
type CodexUserInputActionAnswer,
|
||||
} from "./user-input-actions.js";
|
||||
|
||||
type PendingUserInput = {
|
||||
const DEFAULT_USER_INPUT_TIMEOUT_MS = 15 * 60_000;
|
||||
|
||||
type PendingSecretUserInput = {
|
||||
requestId: number | string;
|
||||
threadId: string;
|
||||
turnId: string;
|
||||
itemId: string;
|
||||
questions: AgentHarnessUserInputQuestion[];
|
||||
claimed: boolean;
|
||||
resolve: (value: JsonValue) => void;
|
||||
cleanup: () => void;
|
||||
};
|
||||
|
||||
type PendingGatewayUserInput = {
|
||||
requestId: number | string;
|
||||
threadId: string;
|
||||
abort: AbortController;
|
||||
};
|
||||
|
||||
type CodexUserInputBridge = {
|
||||
handleRequest: (request: {
|
||||
id: number | string;
|
||||
@@ -47,7 +47,6 @@ type CodexUserInputBridge = {
|
||||
cancel: () => boolean;
|
||||
}
|
||||
| undefined;
|
||||
handleQueuedMessage: (text: string) => boolean;
|
||||
handleNotification: (notification: CodexServerNotification) => void;
|
||||
cancelPending: () => void;
|
||||
};
|
||||
@@ -58,27 +57,34 @@ export function createCodexUserInputBridge(params: {
|
||||
threadId: string;
|
||||
turnId: string;
|
||||
signal?: AbortSignal;
|
||||
gatewayCall?: AgentHarnessQuestionGatewayCall;
|
||||
}): CodexUserInputBridge {
|
||||
let pending: PendingUserInput | undefined;
|
||||
let sensitiveInput: PendingSecretUserInput | undefined;
|
||||
let pendingGateway: PendingGatewayUserInput | undefined;
|
||||
const gatewayCall = params.gatewayCall ?? callGatewayTool;
|
||||
|
||||
const resolvePending = (value: JsonValue) => {
|
||||
const current = pending;
|
||||
const resolveSecret = (value: JsonValue) => {
|
||||
const current = sensitiveInput;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
pending = undefined;
|
||||
sensitiveInput = undefined;
|
||||
current.cleanup();
|
||||
current.resolve(value);
|
||||
};
|
||||
|
||||
const resolvePendingIfCurrent = (current: PendingUserInput, value: JsonValue): boolean => {
|
||||
if (pending !== current) {
|
||||
const resolveSecretIfCurrent = (current: PendingSecretUserInput, value: JsonValue): boolean => {
|
||||
if (sensitiveInput !== current) {
|
||||
return false;
|
||||
}
|
||||
resolvePending(value);
|
||||
resolveSecret(value);
|
||||
return true;
|
||||
};
|
||||
|
||||
const cancelGateway = () => {
|
||||
pendingGateway?.abort.abort(new Error("Codex user input request cancelled"));
|
||||
};
|
||||
|
||||
return {
|
||||
async handleRequest(request) {
|
||||
const requestParams = readUserInputParams(request.params);
|
||||
@@ -92,94 +98,114 @@ export function createCodexUserInputBridge(params: {
|
||||
return emptyUserInputResponse();
|
||||
}
|
||||
|
||||
resolvePending(emptyUserInputResponse());
|
||||
resolveSecret(emptyUserInputResponse());
|
||||
cancelGateway();
|
||||
|
||||
return new Promise<JsonValue>((resolve) => {
|
||||
const abortListener = () => resolvePending(emptyUserInputResponse());
|
||||
let disposeAction = () => {};
|
||||
const cleanup = () => {
|
||||
params.signal?.removeEventListener("abort", abortListener);
|
||||
disposeAction();
|
||||
emitQuestionEvent(params.paramsForRun, "resolved", requestParams.itemId);
|
||||
};
|
||||
const current: PendingUserInput = {
|
||||
requestId: request.id,
|
||||
threadId: requestParams.threadId,
|
||||
turnId: requestParams.turnId,
|
||||
itemId: requestParams.itemId,
|
||||
questions: requestParams.questions,
|
||||
claimed: false,
|
||||
resolve,
|
||||
cleanup,
|
||||
};
|
||||
const actionRegistration = requestParams.questions.some((question) => question.isSecret)
|
||||
? undefined
|
||||
: registerCodexUserInputActions((answer) => {
|
||||
const response = buildUserInputActionResponse(requestParams.questions, answer);
|
||||
return response ? resolvePendingIfCurrent(current, response) : false;
|
||||
});
|
||||
disposeAction = actionRegistration?.dispose ?? disposeAction;
|
||||
pending = current;
|
||||
params.signal?.addEventListener("abort", abortListener, { once: true });
|
||||
if (params.signal?.aborted) {
|
||||
resolvePending(emptyUserInputResponse());
|
||||
return;
|
||||
}
|
||||
emitQuestionEvent(
|
||||
params.paramsForRun,
|
||||
"requested",
|
||||
requestParams.itemId,
|
||||
requestParams.questions,
|
||||
actionRegistration?.token,
|
||||
);
|
||||
void deliverUserInputPrompt(
|
||||
params.paramsForRun,
|
||||
requestParams.questions,
|
||||
actionRegistration?.token,
|
||||
).catch((error: unknown) => {
|
||||
embeddedAgentLog.warn("failed to deliver codex user input prompt", { error });
|
||||
if (requestParams.questions.some((question) => question.isSecret)) {
|
||||
return new Promise<JsonValue>((resolve) => {
|
||||
const abortListener = () => resolveSecret(emptyUserInputResponse());
|
||||
const cleanup = () => params.signal?.removeEventListener("abort", abortListener);
|
||||
const current: PendingSecretUserInput = {
|
||||
requestId: request.id,
|
||||
threadId: requestParams.threadId,
|
||||
questions: requestParams.questions,
|
||||
claimed: false,
|
||||
resolve,
|
||||
cleanup,
|
||||
};
|
||||
sensitiveInput = current;
|
||||
params.signal?.addEventListener("abort", abortListener, { once: true });
|
||||
if (params.signal?.aborted) {
|
||||
resolveSecret(emptyUserInputResponse());
|
||||
return;
|
||||
}
|
||||
void deliverAgentHarnessUserInputPrompt(params.paramsForRun, requestParams.questions, {
|
||||
formatText: formatCodexDisplayText,
|
||||
intro: "Codex needs input:",
|
||||
}).catch((error: unknown) => {
|
||||
embeddedAgentLog.warn("failed to deliver secret codex user input prompt", { error });
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const abort = new AbortController();
|
||||
const abortFromRun = () => abort.abort(params.signal?.reason);
|
||||
params.signal?.addEventListener("abort", abortFromRun, { once: true });
|
||||
if (params.signal?.aborted) {
|
||||
abortFromRun();
|
||||
}
|
||||
pendingGateway = { requestId: request.id, threadId: requestParams.threadId, abort };
|
||||
try {
|
||||
const result = await runAgentHarnessGatewayQuestion({
|
||||
questions: requestParams.questions,
|
||||
sessionKey: params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId,
|
||||
agentId: params.paramsForRun.agentId,
|
||||
timeoutMs:
|
||||
requestParams.autoResolutionMs ??
|
||||
params.paramsForRun.timeoutMs ??
|
||||
DEFAULT_USER_INPUT_TIMEOUT_MS,
|
||||
gatewayCall,
|
||||
delivery: params.paramsForRun,
|
||||
promptOptions: {
|
||||
formatText: formatCodexDisplayText,
|
||||
intro: "Codex needs input:",
|
||||
},
|
||||
signal: abort.signal,
|
||||
});
|
||||
return result.status === "answered"
|
||||
? (result.answers as unknown as JsonObject)
|
||||
: emptyUserInputResponse();
|
||||
} catch (error) {
|
||||
embeddedAgentLog.warn("failed to bridge codex user input through gateway", { error });
|
||||
return emptyUserInputResponse();
|
||||
} finally {
|
||||
params.signal?.removeEventListener("abort", abortFromRun);
|
||||
if (pendingGateway?.abort === abort) {
|
||||
pendingGateway = undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
claimPendingRequest() {
|
||||
const current = pending;
|
||||
const current = sensitiveInput;
|
||||
if (!current || current.claimed) {
|
||||
return undefined;
|
||||
}
|
||||
current.claimed = true;
|
||||
return {
|
||||
answer: (text) =>
|
||||
resolvePendingIfCurrent(current, buildUserInputResponse(current.questions, text)),
|
||||
cancel: () => resolvePendingIfCurrent(current, emptyUserInputResponse()),
|
||||
resolveSecretIfCurrent(current, buildUserInputResponse(current.questions, text)),
|
||||
cancel: () => resolveSecretIfCurrent(current, emptyUserInputResponse()),
|
||||
};
|
||||
},
|
||||
handleQueuedMessage(text) {
|
||||
const current = pending;
|
||||
if (!current || current.claimed) {
|
||||
return false;
|
||||
}
|
||||
resolvePending(buildUserInputResponse(current.questions, text));
|
||||
return true;
|
||||
},
|
||||
handleNotification(notification) {
|
||||
if (notification.method !== "serverRequest/resolved" || !pending) {
|
||||
if (notification.method !== "serverRequest/resolved") {
|
||||
return;
|
||||
}
|
||||
const notificationParams = isJsonObject(notification.params)
|
||||
? notification.params
|
||||
: undefined;
|
||||
const requestId = notificationParams ? readRequestId(notificationParams) : undefined;
|
||||
if (!notificationParams || requestId === undefined) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
notificationParams &&
|
||||
readString(notificationParams, "threadId") === pending.threadId &&
|
||||
requestId !== undefined &&
|
||||
String(requestId) === String(pending.requestId)
|
||||
sensitiveInput &&
|
||||
readString(notificationParams, "threadId") === sensitiveInput.threadId &&
|
||||
String(requestId) === String(sensitiveInput.requestId)
|
||||
) {
|
||||
resolvePending(emptyUserInputResponse());
|
||||
resolveSecret(emptyUserInputResponse());
|
||||
}
|
||||
if (
|
||||
pendingGateway &&
|
||||
readString(notificationParams, "threadId") === pendingGateway.threadId &&
|
||||
String(requestId) === String(pendingGateway.requestId)
|
||||
) {
|
||||
pendingGateway.abort.abort(new Error("Codex server request resolved"));
|
||||
}
|
||||
},
|
||||
cancelPending() {
|
||||
resolvePending(emptyUserInputResponse());
|
||||
resolveSecret(emptyUserInputResponse());
|
||||
cancelGateway();
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -190,6 +216,7 @@ function readUserInputParams(value: JsonValue | undefined):
|
||||
turnId: string;
|
||||
itemId: string;
|
||||
questions: AgentHarnessUserInputQuestion[];
|
||||
autoResolutionMs?: number;
|
||||
}
|
||||
| undefined {
|
||||
if (!isJsonObject(value)) {
|
||||
@@ -203,9 +230,19 @@ function readUserInputParams(value: JsonValue | undefined):
|
||||
return undefined;
|
||||
}
|
||||
const questions = questionsRaw
|
||||
.map(readQuestion)
|
||||
.map((rawQuestion) => {
|
||||
const question = readQuestion(rawQuestion);
|
||||
if (question && isJsonObject(rawQuestion) && rawQuestion.multiSelect === true) {
|
||||
question.multiSelect = true;
|
||||
}
|
||||
return question;
|
||||
})
|
||||
.filter((question): question is AgentHarnessUserInputQuestion => Boolean(question));
|
||||
return { threadId, turnId, itemId, questions };
|
||||
const autoResolutionMs =
|
||||
typeof value.autoResolutionMs === "number" && value.autoResolutionMs > 0
|
||||
? value.autoResolutionMs
|
||||
: undefined;
|
||||
return { threadId, turnId, itemId, questions, autoResolutionMs };
|
||||
}
|
||||
|
||||
function readQuestion(value: JsonValue): AgentHarnessUserInputQuestion | undefined {
|
||||
@@ -247,78 +284,13 @@ function readOption(value: JsonValue): AgentHarnessUserInputOption | undefined {
|
||||
return label ? { label, description } : undefined;
|
||||
}
|
||||
|
||||
async function deliverUserInputPrompt(
|
||||
params: EmbeddedRunAttemptParams,
|
||||
questions: AgentHarnessUserInputQuestion[],
|
||||
actionToken?: string,
|
||||
): Promise<void> {
|
||||
await deliverAgentHarnessUserInputPrompt(params, questions, {
|
||||
formatText: formatCodexDisplayText,
|
||||
intro: "Codex needs input:",
|
||||
presentation: actionToken ? buildCodexUserInputPresentation(questions, actionToken) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function emitQuestionEvent(
|
||||
params: EmbeddedRunAttemptParams,
|
||||
phase: "requested" | "resolved",
|
||||
itemId: string,
|
||||
questions?: AgentHarnessUserInputQuestion[],
|
||||
actionToken?: string,
|
||||
): void {
|
||||
void emitCodexAppServerEvent(params, {
|
||||
stream: "question",
|
||||
data: {
|
||||
phase,
|
||||
itemId,
|
||||
source: "codex-app-server",
|
||||
...(questions ? { questions } : {}),
|
||||
...(actionToken ? { actionToken } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function buildUserInputResponse(
|
||||
questions: AgentHarnessUserInputQuestion[],
|
||||
inputText: string,
|
||||
): JsonObject {
|
||||
// Multi-question replies may use "header: answer" or numbered lines. Keep the
|
||||
// parser permissive so chat-channel replies remain ergonomic.
|
||||
return buildAgentHarnessUserInputAnswers(questions, inputText) as unknown as JsonObject;
|
||||
}
|
||||
|
||||
function buildUserInputActionResponse(
|
||||
questions: AgentHarnessUserInputQuestion[],
|
||||
action: CodexUserInputActionAnswer,
|
||||
): JsonObject | undefined {
|
||||
if (action.type === "choice") {
|
||||
const question = questions.length === 1 ? questions[0] : undefined;
|
||||
const option = question?.options?.[action.optionIndex];
|
||||
return question && option
|
||||
? ({ answers: { [question.id]: { answers: [option.label] } } } as JsonObject)
|
||||
: undefined;
|
||||
}
|
||||
if (
|
||||
Object.keys(action.answers).length !== questions.length ||
|
||||
!questions.every((question) => Object.hasOwn(action.answers, question.id))
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
const answerEntries: Array<[string, { answers: string[] }]> = [];
|
||||
for (const question of questions) {
|
||||
const rawAnswer = action.answers[question.id];
|
||||
if (!rawAnswer?.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
const exactOption = question.options?.find((option) => option.label === rawAnswer);
|
||||
if (!exactOption && question.options?.length && !question.isOther) {
|
||||
return undefined;
|
||||
}
|
||||
answerEntries.push([question.id, { answers: [exactOption?.label ?? rawAnswer] }]);
|
||||
}
|
||||
return { answers: Object.fromEntries(answerEntries) } as unknown as JsonObject;
|
||||
}
|
||||
|
||||
function emptyUserInputResponse(): JsonObject {
|
||||
return emptyAgentHarnessUserInputAnswers() as unknown as JsonObject;
|
||||
}
|
||||
|
||||
@@ -38,10 +38,6 @@ import {
|
||||
type CodexAppServerBindingStore,
|
||||
type CodexAppServerThreadBinding,
|
||||
} from "./app-server/session-binding.js";
|
||||
import {
|
||||
parseCodexUserInputActionAnswer,
|
||||
resolveCodexUserInputAction,
|
||||
} from "./app-server/user-input-actions.js";
|
||||
import { readCodexAccountAuthOverview } from "./command-account.js";
|
||||
import { canMutateCodexHost, CODEX_NATIVE_EXECUTION_AUTH_ERROR } from "./command-authorization.js";
|
||||
import {
|
||||
@@ -382,24 +378,6 @@ export async function handleCodexSubcommand(
|
||||
if (normalized === "help") {
|
||||
return { text: buildHelp() };
|
||||
}
|
||||
if (normalized === "answer") {
|
||||
if (rest.length !== 2) {
|
||||
return { text: "Usage: /codex answer <request-token> <choice>" };
|
||||
}
|
||||
if (!canMutateCodexHost(ctx)) {
|
||||
return { text: CODEX_NATIVE_EXECUTION_AUTH_ERROR };
|
||||
}
|
||||
const [token = "", encodedAnswer = ""] = rest;
|
||||
const answer = parseCodexUserInputActionAnswer(encodedAnswer);
|
||||
if (!answer) {
|
||||
return { text: "Invalid Codex answer encoding." };
|
||||
}
|
||||
return {
|
||||
text: resolveCodexUserInputAction(token, answer)
|
||||
? "Answered Codex."
|
||||
: "That Codex question is no longer pending.",
|
||||
};
|
||||
}
|
||||
if (
|
||||
CODEX_NATIVE_CONTROL_SUBCOMMANDS.has(normalized) &&
|
||||
!returnsBeforeNativeCodexExecution(normalized, rest) &&
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
testCodexAppServerBindingStore,
|
||||
} from "./app-server/session-binding.test-helpers.js";
|
||||
import { resetSharedCodexAppServerClientForTests } from "./app-server/shared-client.js";
|
||||
import { registerCodexUserInputActions } from "./app-server/user-input-actions.js";
|
||||
import { codexDiagnosticsFeedbackState } from "./command-diagnostics-state.js";
|
||||
import { handleCodexCommand } from "./command-dispatch.js";
|
||||
import type { CodexCommandDepsOverride } from "./command-handlers.js";
|
||||
@@ -387,39 +386,6 @@ describe("codex command", () => {
|
||||
expect(handler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("authorizes and decodes request-scoped Codex answers", async () => {
|
||||
const answer = vi.fn(() => true);
|
||||
const registration = registerCodexUserInputActions(answer);
|
||||
await expect(
|
||||
handleCodexCommand(
|
||||
createContext(
|
||||
`answer ${registration.token} answers:%7B%22mode%22%3A%22Deep%20mode%22%7D`,
|
||||
undefined,
|
||||
{
|
||||
senderIsOwner: false,
|
||||
},
|
||||
),
|
||||
{ deps: createDeps() },
|
||||
),
|
||||
).resolves.toEqual({
|
||||
text: "Only an owner or operator.admin can control Codex native execution.",
|
||||
});
|
||||
expect(answer).not.toHaveBeenCalled();
|
||||
|
||||
await expect(
|
||||
handleCodexCommand(
|
||||
createContext(`answer ${registration.token} answers:%7B%22mode%22%3A%22Deep%20mode%22%7D`),
|
||||
{
|
||||
deps: createDeps(),
|
||||
},
|
||||
),
|
||||
).resolves.toEqual({ text: "Answered Codex." });
|
||||
expect(answer).toHaveBeenCalledWith({
|
||||
type: "answers",
|
||||
answers: { mode: "Deep mode" },
|
||||
});
|
||||
});
|
||||
|
||||
it("renders the top-level Codex menu as portable native slash commands", async () => {
|
||||
const result = await handleCodexCommand(createContext(""), { deps: createDeps() });
|
||||
|
||||
|
||||
@@ -22,6 +22,51 @@ import { runCopilotAttempt } from "./attempt.js";
|
||||
import type { CopilotClientPool } from "./runtime.js";
|
||||
import type { createCopilotToolBridge } from "./tool-bridge.js";
|
||||
|
||||
const gatewayQuestionMock = vi.hoisted(() => ({
|
||||
waiters: new Map<string, (value: unknown) => void>(),
|
||||
cancelError: undefined as Error | undefined,
|
||||
warn: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/agent-harness-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/agent-harness-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
embeddedAgentLog: { ...actual.embeddedAgentLog, warn: gatewayQuestionMock.warn },
|
||||
cancelPendingAgentQuestionForSession: async (
|
||||
...args: Parameters<typeof actual.cancelPendingAgentQuestionForSession>
|
||||
) => {
|
||||
const error = gatewayQuestionMock.cancelError;
|
||||
gatewayQuestionMock.cancelError = undefined;
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return await actual.cancelPendingAgentQuestionForSession(...args);
|
||||
},
|
||||
callGatewayTool: async (...args: Parameters<typeof actual.callGatewayTool>) => {
|
||||
const [method, , rawParams] = args;
|
||||
const params = rawParams as { id?: string; answers?: unknown; cancel?: boolean } | undefined;
|
||||
if (method === "question.request") {
|
||||
return { id: params?.id, expiresAtMs: Date.now() + 60_000 };
|
||||
}
|
||||
if (method === "question.waitAnswer") {
|
||||
return await new Promise((resolve) => {
|
||||
gatewayQuestionMock.waiters.set(params?.id ?? "", resolve);
|
||||
});
|
||||
}
|
||||
if (method === "question.resolve") {
|
||||
const result = params?.cancel
|
||||
? { status: "cancelled" as const }
|
||||
: { status: "answered" as const, answers: params?.answers };
|
||||
gatewayQuestionMock.waiters.get(params?.id ?? "")?.(result);
|
||||
gatewayQuestionMock.waiters.delete(params?.id ?? "");
|
||||
return result;
|
||||
}
|
||||
return await actual.callGatewayTool(...args);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
type CopilotToolBridgeInput = Parameters<typeof createCopilotToolBridge>[0];
|
||||
|
||||
const TINY_PNG_BASE64 =
|
||||
@@ -1332,12 +1377,19 @@ describe("runCopilotAttempt", () => {
|
||||
const session = await sessionCreated.promise;
|
||||
await vi.waitFor(() => expect(session.sendAndWait).toHaveBeenCalledTimes(1));
|
||||
|
||||
gatewayQuestionMock.cancelError = new Error("gateway unavailable");
|
||||
expect(abortAgentHarnessRun("session-1")).toBe(true);
|
||||
const result = await runPromise;
|
||||
|
||||
expect(session.abort).toHaveBeenCalledTimes(1);
|
||||
expect(result.aborted).toBe(true);
|
||||
expect(result.externalAbort).toBe(true);
|
||||
await vi.waitFor(() =>
|
||||
expect(gatewayQuestionMock.warn).toHaveBeenCalledWith(
|
||||
"failed to cancel copilot gateway question during shutdown",
|
||||
expect.objectContaining({ error: expect.any(Error) }),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("abort path (signal already aborted)", async () => {
|
||||
@@ -1642,7 +1694,9 @@ describe("runCopilotAttempt", () => {
|
||||
const attempt = runCopilotAttempt(makeParams({ onBlockReply }), { pool });
|
||||
|
||||
await vi.waitFor(() => expect(onBlockReply).toHaveBeenCalledTimes(1));
|
||||
expect(queueAgentHarnessMessage("session-1", "2")).toBe(true);
|
||||
expect(queueAgentHarnessMessage("session-1", "tool progress")).toBe(true);
|
||||
await waitForEventLoopTurn();
|
||||
expect(queueAgentHarnessMessage("session-1", "2", { isInboundUserMessage: true })).toBe(true);
|
||||
const result = await attempt;
|
||||
|
||||
const cfg = requireCreateSessionConfig(sdk);
|
||||
|
||||
@@ -9,7 +9,10 @@ import type {
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import {
|
||||
buildAgentHookContextChannelFields,
|
||||
cancelPendingAgentQuestionForSession,
|
||||
claimPendingAgentQuestionAnswer,
|
||||
detectAndLoadAgentHarnessPromptImages,
|
||||
embeddedAgentLog,
|
||||
getModelProviderRequestTransport,
|
||||
isHostScopedAgentToolActive,
|
||||
resolveAgentHarnessBeforePromptBuildResult,
|
||||
@@ -898,10 +901,28 @@ export async function runCopilotAttempt(
|
||||
isAborted: () => aborted,
|
||||
});
|
||||
|
||||
const cancelGatewayQuestionBestEffort = (resolvedBy: string) => {
|
||||
void cancelPendingAgentQuestionForSession({
|
||||
sessionKey: input.sessionKey ?? input.sessionId,
|
||||
resolvedBy,
|
||||
}).catch((error: unknown) => {
|
||||
embeddedAgentLog.warn("failed to cancel copilot gateway question during shutdown", {
|
||||
error,
|
||||
});
|
||||
});
|
||||
};
|
||||
const activeRunHandle = {
|
||||
kind: "embedded" as const,
|
||||
queueMessage: async (text: string) => {
|
||||
if (userInputBridge.handleQueuedMessage(text)) {
|
||||
// This backend intentionally omits supportsQueueMessageImages, so the reply
|
||||
// registry rejects attachment turns before this text-only callback runs.
|
||||
queueMessage: async (text: string, options?: { isInboundUserMessage?: boolean }) => {
|
||||
if (
|
||||
options?.isInboundUserMessage === true &&
|
||||
(await claimPendingAgentQuestionAnswer({
|
||||
sessionKey: input.sessionKey ?? input.sessionId,
|
||||
text,
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
throw new Error("Copilot runtime is not waiting for user input.");
|
||||
@@ -910,10 +931,12 @@ export async function runCopilotAttempt(
|
||||
isCompacting: () => bridge?.isCompacting() ?? false,
|
||||
sourceReplyDeliveryMode: input.sourceReplyDeliveryMode,
|
||||
cancel: () => {
|
||||
cancelGatewayQuestionBestEffort("run-cancel");
|
||||
userInputBridge.cancelPending();
|
||||
abortActiveSession();
|
||||
},
|
||||
abort: () => {
|
||||
cancelGatewayQuestionBestEffort("run-abort");
|
||||
userInputBridge.cancelPending();
|
||||
abortActiveSession();
|
||||
},
|
||||
|
||||
@@ -1,75 +1,126 @@
|
||||
// Copilot tests cover SDK ask_user bridge behavior.
|
||||
import type { EmbeddedRunAttemptParams } from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
// Copilot tests cover SDK ask_user gateway behavior.
|
||||
import {
|
||||
claimPendingAgentQuestionAnswer,
|
||||
type AgentHarnessQuestionGatewayCall,
|
||||
type EmbeddedRunAttemptParams,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createCopilotUserInputBridge } from "./user-input-bridge.js";
|
||||
|
||||
type GatewayCallRecord = { method: string; opts: unknown; params: unknown };
|
||||
|
||||
function createParams(): EmbeddedRunAttemptParams {
|
||||
return {
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:session-1",
|
||||
agentId: "main",
|
||||
timeoutMs: 75_000,
|
||||
onBlockReply: vi.fn(),
|
||||
} as unknown as EmbeddedRunAttemptParams;
|
||||
}
|
||||
|
||||
function expectFirstBlockReplyText(params: EmbeddedRunAttemptParams): string {
|
||||
const onBlockReply = params.onBlockReply;
|
||||
if (!onBlockReply) {
|
||||
throw new Error("Expected onBlockReply callback");
|
||||
}
|
||||
const payload = vi.mocked(onBlockReply).mock.calls[0]?.[0];
|
||||
if (typeof payload?.text !== "string") {
|
||||
throw new Error("Expected first block reply text");
|
||||
}
|
||||
return payload.text;
|
||||
function createGatewayStub() {
|
||||
const calls: GatewayCallRecord[] = [];
|
||||
let settleWait: ((value: unknown) => void) | undefined;
|
||||
const wait = new Promise<unknown>((resolve) => {
|
||||
settleWait = resolve;
|
||||
});
|
||||
const call: AgentHarnessQuestionGatewayCall = async (method, opts, params) => {
|
||||
calls.push({ method, opts, params });
|
||||
if (method === "question.request") {
|
||||
return { id: (params as { id: string }).id, expiresAtMs: Date.now() + 75_000 };
|
||||
}
|
||||
if (method === "question.waitAnswer") {
|
||||
return await wait;
|
||||
}
|
||||
if (method === "question.resolve") {
|
||||
const resolved = params as {
|
||||
answers?: { answers: Record<string, { answers: string[] }> };
|
||||
cancel?: boolean;
|
||||
};
|
||||
const result = resolved.cancel
|
||||
? { status: "cancelled" as const }
|
||||
: { status: "answered" as const, answers: resolved.answers! };
|
||||
settleWait?.(result);
|
||||
return result;
|
||||
}
|
||||
throw new Error(`unexpected gateway method: ${method}`);
|
||||
};
|
||||
return { call, calls };
|
||||
}
|
||||
|
||||
describe("Copilot user input bridge", () => {
|
||||
it("prompts through OpenClaw and resolves the SDK request from the next queued message", async () => {
|
||||
it("registers, presents, claims, and returns a selected option", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCopilotUserInputBridge({ paramsForRun: params });
|
||||
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCopilotUserInputBridge({
|
||||
paramsForRun: params,
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
const response = bridge.onUserInputRequest(
|
||||
{
|
||||
question: "Pick a mode",
|
||||
choices: ["Fast", "Deep"],
|
||||
allowFreeform: false,
|
||||
},
|
||||
{ question: "Pick a mode", choices: ["Fast", "Deep"], allowFreeform: false },
|
||||
{ sessionId: "sdk-session-1" },
|
||||
);
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
const request = gateway.calls[0];
|
||||
if (!request) {
|
||||
throw new Error("expected question.request");
|
||||
}
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
expect(expectFirstBlockReplyText(params)).toContain("Pick a mode");
|
||||
expect(bridge.handleQueuedMessage("2")).toBe(true);
|
||||
expect(request.params).toMatchObject({
|
||||
sessionKey: "agent:main:session-1",
|
||||
agentId: "main",
|
||||
timeoutMs: 75_000,
|
||||
questions: [
|
||||
expect.objectContaining({ id: "answer", options: [{ label: "Fast" }, { label: "Deep" }] }),
|
||||
],
|
||||
});
|
||||
const payload = vi.mocked(params.onBlockReply!).mock.calls[0]![0];
|
||||
expect(payload.channelData).toEqual({
|
||||
askUser: { questionId: (request.params as { id: string }).id },
|
||||
});
|
||||
expect(payload.presentationTextMode).toBe("fallback");
|
||||
expect(payload.text).toContain("Reply with the number or option text.");
|
||||
expect(payload.text).not.toContain("your own answer");
|
||||
|
||||
await expect(
|
||||
claimPendingAgentQuestionAnswer({ sessionKey: params.sessionKey, text: "2" }),
|
||||
).resolves.toBe(true);
|
||||
await expect(response).resolves.toEqual({ answer: "Deep", wasFreeform: false });
|
||||
});
|
||||
|
||||
it("returns free-form answers when Copilot allows them", async () => {
|
||||
it("supports option-less free-form questions", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCopilotUserInputBridge({ paramsForRun: params });
|
||||
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCopilotUserInputBridge({
|
||||
paramsForRun: params,
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
const response = bridge.onUserInputRequest(
|
||||
{
|
||||
question: "Which branch?",
|
||||
allowFreeform: true,
|
||||
},
|
||||
{ question: "Which branch?", allowFreeform: true },
|
||||
{ sessionId: "sdk-session-1" },
|
||||
);
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
expect(bridge.handleQueuedMessage("fix/harness-parity")).toBe(true);
|
||||
|
||||
await expect(response).resolves.toEqual({
|
||||
answer: "fix/harness-parity",
|
||||
wasFreeform: true,
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
expect(gateway.calls[0]?.params).toMatchObject({
|
||||
questions: [expect.objectContaining({ options: [] })],
|
||||
});
|
||||
expect(vi.mocked(params.onBlockReply!).mock.calls[0]![0].presentation).toBeUndefined();
|
||||
|
||||
await claimPendingAgentQuestionAnswer({
|
||||
sessionKey: params.sessionKey,
|
||||
text: "fix/harness-parity",
|
||||
});
|
||||
await expect(response).resolves.toEqual({ answer: "fix/harness-parity", wasFreeform: true });
|
||||
});
|
||||
|
||||
it("escapes SDK-controlled prompt text before channel delivery", async () => {
|
||||
it("escapes SDK-controlled prompt text before delivery", async () => {
|
||||
const params = createParams();
|
||||
const bridge = createCopilotUserInputBridge({ paramsForRun: params });
|
||||
|
||||
void bridge.onUserInputRequest(
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCopilotUserInputBridge({
|
||||
paramsForRun: params,
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
const response = bridge.onUserInputRequest(
|
||||
{
|
||||
question: "Pick [trusted](https://evil) <@U123> @here\u202e",
|
||||
choices: ["One @everyone", "Two `code`"],
|
||||
@@ -77,9 +128,8 @@ describe("Copilot user input bridge", () => {
|
||||
},
|
||||
{ sessionId: "sdk-session-1" },
|
||||
);
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
const text = expectFirstBlockReplyText(params);
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
const text = vi.mocked(params.onBlockReply!).mock.calls[0]![0].text ?? "";
|
||||
expect(text).not.toContain("@here");
|
||||
expect(text).not.toContain("@everyone");
|
||||
expect(text).not.toContain("<@U123>");
|
||||
@@ -87,35 +137,65 @@ describe("Copilot user input bridge", () => {
|
||||
expect(text).not.toContain("`code`");
|
||||
expect(text).toContain("\uff20here");
|
||||
expect(text).toContain("\uff3btrusted\uff3d");
|
||||
const presentation = vi.mocked(params.onBlockReply!).mock.calls[0]![0].presentation;
|
||||
const visiblePresentation = presentation?.blocks.map((block) =>
|
||||
block.type === "text"
|
||||
? block.text
|
||||
: block.type === "buttons"
|
||||
? block.buttons.map((button) => button.label).join(" ")
|
||||
: "",
|
||||
);
|
||||
expect(JSON.stringify(visiblePresentation)).not.toContain("@here");
|
||||
expect(JSON.stringify(visiblePresentation)).not.toContain("@everyone");
|
||||
expect(JSON.stringify(visiblePresentation)).not.toContain("<@U123>");
|
||||
expect(JSON.stringify(visiblePresentation)).not.toContain("[trusted](https://evil)");
|
||||
bridge.cancelPending();
|
||||
await response;
|
||||
});
|
||||
|
||||
it("rejects queued messages when no ask_user request is pending", () => {
|
||||
const bridge = createCopilotUserInputBridge({ paramsForRun: createParams() });
|
||||
|
||||
expect(bridge.handleQueuedMessage("late")).toBe(false);
|
||||
});
|
||||
|
||||
it("resolves pending requests with an empty answer when aborted", async () => {
|
||||
it("cancels the gateway record and returns an empty answer on abort", async () => {
|
||||
const params = createParams();
|
||||
const controller = new AbortController();
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCopilotUserInputBridge({
|
||||
paramsForRun: params,
|
||||
signal: controller.signal,
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
|
||||
const response = bridge.onUserInputRequest(
|
||||
{
|
||||
question: "Continue?",
|
||||
choices: ["Yes", "No"],
|
||||
allowFreeform: false,
|
||||
},
|
||||
{ question: "Continue?", choices: ["Yes", "No"], allowFreeform: false },
|
||||
{ sessionId: "sdk-session-1" },
|
||||
);
|
||||
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1));
|
||||
await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledOnce());
|
||||
controller.abort();
|
||||
|
||||
await expect(response).resolves.toEqual({ answer: "", wasFreeform: true });
|
||||
expect(bridge.handleQueuedMessage("1")).toBe(false);
|
||||
expect(gateway.calls).toContainEqual(
|
||||
expect.objectContaining({
|
||||
method: "question.resolve",
|
||||
params: expect.objectContaining({ cancel: true, resolvedBy: "run-abort" }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not register a gateway question after the run already aborted", async () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
const params = createParams();
|
||||
const gateway = createGatewayStub();
|
||||
const bridge = createCopilotUserInputBridge({
|
||||
paramsForRun: params,
|
||||
signal: controller.signal,
|
||||
gatewayCall: gateway.call,
|
||||
});
|
||||
|
||||
await expect(
|
||||
bridge.onUserInputRequest(
|
||||
{ question: "Continue?", choices: ["Yes", "No"], allowFreeform: false },
|
||||
{ sessionId: "sdk-session-1" },
|
||||
),
|
||||
).resolves.toEqual({ answer: "", wasFreeform: true });
|
||||
expect(gateway.calls).toEqual([]);
|
||||
expect(params.onBlockReply).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user