mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d34dfc36ae
commit
b3fb23b93e
@@ -2,9 +2,10 @@
|
||||
* The top-level world controller.
|
||||
*
|
||||
* `GameWorld` owns the PixiJS application (WebGPU-preferred), the active room,
|
||||
* every agent, and the render loop. It exposes the authoritative entry point
|
||||
* {@link GameWorld.updateAgentState} for external/AI control, plus click-to-move
|
||||
* for human debugging. The 600x600 native scene lives inside a single "viewport"
|
||||
* every agent, and the render loop. It exposes the reconciliation seam
|
||||
* {@link GameWorld.updateAgentState}, reserved for future external/AI control
|
||||
* (no callers yet), plus click-to-move for human debugging. The 600x600 native
|
||||
* scene lives inside a single "viewport"
|
||||
* container that is scaled to fill its parent, so the world stays crisp pixel
|
||||
* art at any size.
|
||||
*/
|
||||
@@ -574,6 +575,14 @@ export class GameWorld {
|
||||
* agent should be and what it should do; the world spawns it if new and
|
||||
* slides it toward that state. Unknown targets are ignored rather than
|
||||
* teleporting the agent into a wall.
|
||||
*
|
||||
* TODO(#4922): this is the intended relay-presence seam, but nothing in the
|
||||
* app currently drives it from the network — the tinyplace stream layer only
|
||||
* supports `inbox` / `conversation` kinds (no presence/world-state stream),
|
||||
* so the World is a local ambient simulation today. Wire this to a relay
|
||||
* presence feed to make other users'/agents' positions reflect tiny.place in
|
||||
* real time. Until then this method has no callers at all — it is a reserved
|
||||
* seam, not a live local path.
|
||||
*/
|
||||
public updateAgentState(agentId: string, state: AgentState): void {
|
||||
const room = this.room;
|
||||
@@ -706,7 +715,14 @@ export class GameWorld {
|
||||
this.notifyChange();
|
||||
}
|
||||
|
||||
/** Populate the room with demo agents — some seated at stations. */
|
||||
/**
|
||||
* Populate the room with demo agents — some seated at stations.
|
||||
*
|
||||
* TODO(#4922): these are locally-seeded NPCs (random names/tints via
|
||||
* {@link GameWorld.pickName} / {@link GameWorld.pickTint}), not real
|
||||
* tiny.place participants. When relay-backed presence lands, seed from the
|
||||
* live directory/presence feed via {@link GameWorld.updateAgentState} instead.
|
||||
*/
|
||||
public spawnAgents(count: number): void {
|
||||
const room = this.room;
|
||||
if (!room) {
|
||||
|
||||
@@ -81,6 +81,22 @@ describe('WorldSection renderer boot', () => {
|
||||
expect(screen.queryByText(/booting renderer/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders an accessible "offline preview" pill so users know agents are a local sim (#4922)', () => {
|
||||
initImpl = () => new Promise<void>(() => {}); // renderer state is irrelevant here
|
||||
render(<WorldSection />);
|
||||
// The badge is a focusable button (operable under the card's
|
||||
// pointer-events-none), not a decorative span.
|
||||
const badge = screen.getByRole('button', { name: /offline preview/i });
|
||||
expect(badge).toBeInTheDocument();
|
||||
// Its explanatory tooltip is wired via aria-describedby → role="tooltip",
|
||||
// rather than a bare `title` that pointer-events-none would swallow.
|
||||
const tooltipId = badge.getAttribute('aria-describedby');
|
||||
expect(tooltipId).toBeTruthy();
|
||||
const tooltip = document.getElementById(tooltipId as string);
|
||||
expect(tooltip).toHaveAttribute('role', 'tooltip');
|
||||
expect(tooltip).toHaveTextContent(/local simulation/i);
|
||||
});
|
||||
|
||||
test('Retry re-invokes init and recovers to ready on success', async () => {
|
||||
const user = userEvent.setup();
|
||||
// First attempt fails, second succeeds.
|
||||
|
||||
@@ -153,9 +153,38 @@ export default function WorldSection() {
|
||||
<p className="mt-1 text-xs leading-relaxed text-content-faint">
|
||||
{t(
|
||||
'agentWorld.world.description',
|
||||
'Join tiny.place so your agent can coordinate with other agents — find and post jobs, trade, message, and team up on bounties.'
|
||||
'Join tiny.place so your agent can coordinate with other agents on the network — find and post jobs, trade, message, and team up on bounties.'
|
||||
)}
|
||||
</p>
|
||||
{/* The World is a local ambient simulation today: NPCs are seeded
|
||||
client-side and there is no relay-backed presence / world-state sync
|
||||
(see GameWorld.updateAgentState + GameWorld.spawnAgents, #4922). This
|
||||
pill sets expectations until live presence lands.
|
||||
|
||||
The parent card is `pointer-events-none`, so the badge opts back in
|
||||
with `pointer-events-auto` to stay hoverable/focusable. It uses the
|
||||
project's self-contained wrapping tooltip pattern (see
|
||||
SuperContextToggle) rather than the shared <Tooltip>, which is
|
||||
single-line nowrap and can't fit this sentence, and rather than a
|
||||
bare `title` (unreachable under `pointer-events-none`). */}
|
||||
<span className="group pointer-events-auto relative mt-2 inline-flex">
|
||||
<button
|
||||
type="button"
|
||||
aria-describedby="world-offline-preview-tooltip"
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-white/15 bg-white/5 px-2.5 py-1 text-[11px] font-medium text-content-secondary transition-colors hover:border-white/25 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-amber-400" aria-hidden="true" />
|
||||
{t('agentWorld.world.offlineBadge', 'Offline preview')}
|
||||
</button>
|
||||
<span
|
||||
id="world-offline-preview-tooltip"
|
||||
role="tooltip"
|
||||
className="pointer-events-none absolute left-0 top-full z-[9999] mt-2 w-64 rounded-lg bg-stone-800 px-3 py-2 text-xs font-normal leading-snug text-white opacity-0 shadow-lg transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100 dark:bg-neutral-700">
|
||||
{t(
|
||||
'agentWorld.world.offlineBadgeTitle',
|
||||
'Agents shown here are a local simulation. Live presence and world sync are coming soon.'
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<aside className="absolute right-3 top-3 z-10 flex w-72 max-w-[calc(100%-1.5rem)] flex-col gap-4 overflow-y-auto rounded-xl border border-white/15 bg-neutral-950/70 p-4 shadow-xl backdrop-blur-md">
|
||||
|
||||
@@ -464,7 +464,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'إعادة المحاولة',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'انضم إلى tiny.place ليتمكن وكيلك من التنسيق مع الوكلاء الآخرين: العثور على الوظائف ونشرها، والتداول، وتبادل الرسائل، والتعاون في المكافآت.',
|
||||
'انضم إلى tiny.place ليتمكن وكيلك من التنسيق مع الوكلاء الآخرين على الشبكة: العثور على الوظائف ونشرها، والتداول، وتبادل الرسائل، والتعاون في المكافآت.',
|
||||
'agentWorld.world.offlineBadge': 'معاينة دون اتصال',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'الوكلاء المعروضون هنا محاكاة محلية. سيتوفر الحضور المباشر ومزامنة العالم قريبًا.',
|
||||
'agentWorld.world.room': 'الغرفة',
|
||||
'agentWorld.world.rooms.poker.name': 'بوكر',
|
||||
'agentWorld.world.rooms.poker.description': 'ثمانية مقاعد حول طاولة مكسوة باللباد.',
|
||||
|
||||
@@ -478,7 +478,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'আবার চেষ্টা করুন',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'tiny.place-এ যোগ দিন যাতে আপনার এজেন্ট অন্য এজেন্টদের সাথে সমন্বয় করতে পারে: কাজ খুঁজে পাওয়া ও পোস্ট করা, লেনদেন, বার্তা পাঠানো এবং বাউন্টিতে একসাথে কাজ করা।',
|
||||
'tiny.place-এ যোগ দিন যাতে আপনার এজেন্ট নেটওয়ার্কের অন্য এজেন্টদের সাথে সমন্বয় করতে পারে: কাজ খুঁজে পাওয়া ও পোস্ট করা, লেনদেন, বার্তা পাঠানো এবং বাউন্টিতে একসাথে কাজ করা।',
|
||||
'agentWorld.world.offlineBadge': 'অফলাইন প্রিভিউ',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'এখানে দেখানো এজেন্টগুলো একটি স্থানীয় সিমুলেশন। সরাসরি উপস্থিতি ও ওয়ার্ল্ড সিঙ্ক শীঘ্রই আসছে।',
|
||||
'agentWorld.world.room': 'রুম',
|
||||
'agentWorld.world.rooms.poker.name': 'পোকার',
|
||||
'agentWorld.world.rooms.poker.description': 'ফেল্ট টেবিলের চারপাশে আটটি আসন।',
|
||||
|
||||
@@ -503,7 +503,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Wiederholen',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Tritt tiny.place bei, damit dein Agent sich mit anderen Agenten abstimmen kann: Jobs finden und ausschreiben, handeln, Nachrichten senden und bei Bounties zusammenarbeiten.',
|
||||
'Tritt tiny.place bei, damit dein Agent sich mit anderen Agenten im Netzwerk abstimmen kann: Jobs finden und ausschreiben, handeln, Nachrichten senden und bei Bounties zusammenarbeiten.',
|
||||
'agentWorld.world.offlineBadge': 'Offline-Vorschau',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Die hier gezeigten Agenten sind eine lokale Simulation. Live-Präsenz und Welt-Synchronisierung folgen bald.',
|
||||
'agentWorld.world.room': 'Raum',
|
||||
'agentWorld.world.rooms.poker.name': 'Poker',
|
||||
'agentWorld.world.rooms.poker.description': 'Acht Sitze rund um einen Filztisch.',
|
||||
|
||||
@@ -190,7 +190,10 @@ const en: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Retry',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Join tiny.place so your agent can coordinate with other agents: find and post jobs, trade, message, and team up on bounties.',
|
||||
'Join tiny.place so your agent can coordinate with other agents on the network: find and post jobs, trade, message, and team up on bounties.',
|
||||
'agentWorld.world.offlineBadge': 'Offline preview',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Agents shown here are a local simulation. Live presence and world sync are coming soon.',
|
||||
'agentWorld.world.room': 'Room',
|
||||
'agentWorld.world.rooms.poker.name': 'Poker',
|
||||
'agentWorld.world.rooms.poker.description': 'Eight seats around a felt table.',
|
||||
|
||||
@@ -489,7 +489,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Reintentar',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Únete a tiny.place para que tu agente coordine con otros agentes: encontrar y publicar trabajos, comerciar, enviar mensajes y colaborar en recompensas.',
|
||||
'Únete a tiny.place para que tu agente coordine con otros agentes de la red: encontrar y publicar trabajos, comerciar, enviar mensajes y colaborar en recompensas.',
|
||||
'agentWorld.world.offlineBadge': 'Vista previa sin conexión',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Los agentes que se muestran aquí forman parte de una simulación local. La presencia en vivo y la sincronización del mundo llegarán pronto.',
|
||||
'agentWorld.world.room': 'Sala',
|
||||
'agentWorld.world.rooms.poker.name': 'Póker',
|
||||
'agentWorld.world.rooms.poker.description': 'Ocho asientos alrededor de una mesa de fieltro.',
|
||||
|
||||
@@ -499,7 +499,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Réessayer',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Rejoignez tiny.place pour que votre agent collabore avec d’autres agents : trouver et publier des missions, échanger, discuter et coopérer sur des primes.',
|
||||
'Rejoignez tiny.place pour que votre agent collabore avec d’autres agents du réseau : trouver et publier des missions, échanger, discuter et coopérer sur des primes.',
|
||||
'agentWorld.world.offlineBadge': 'Aperçu hors ligne',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Les agents affichés ici sont une simulation locale. La présence en direct et la synchronisation du monde ne sont pas encore disponibles.',
|
||||
'agentWorld.world.room': 'Salle',
|
||||
'agentWorld.world.rooms.poker.name': 'Poker',
|
||||
'agentWorld.world.rooms.poker.description': 'Huit sièges autour d’une table en feutre.',
|
||||
|
||||
@@ -478,7 +478,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'पुनः प्रयास करें',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'tiny.place से जुड़ें ताकि आपका एजेंट दूसरे एजेंट्स के साथ तालमेल कर सके: काम ढूँढना और पोस्ट करना, व्यापार करना, संदेश भेजना और बाउंटी पर मिलकर काम करना।',
|
||||
'tiny.place से जुड़ें ताकि आपका एजेंट नेटवर्क पर दूसरे एजेंट्स के साथ तालमेल कर सके: काम ढूँढना और पोस्ट करना, व्यापार करना, संदेश भेजना और बाउंटी पर मिलकर काम करना।',
|
||||
'agentWorld.world.offlineBadge': 'ऑफ़लाइन प्रीव्यू',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'यहाँ दिखाए गए एजेंट एक स्थानीय सिमुलेशन हैं। लाइव उपस्थिति और वर्ल्ड सिंक जल्द ही आ रहे हैं।',
|
||||
'agentWorld.world.room': 'कमरा',
|
||||
'agentWorld.world.rooms.poker.name': 'पोकर',
|
||||
'agentWorld.world.rooms.poker.description': 'फेल्ट टेबल के चारों ओर आठ सीटें।',
|
||||
|
||||
@@ -485,7 +485,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Coba lagi',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Bergabunglah dengan tiny.place agar agen Anda dapat berkoordinasi dengan agen lain: menemukan dan memposting pekerjaan, berdagang, berkirim pesan, dan bekerja sama dalam bounty.',
|
||||
'Bergabunglah dengan tiny.place agar agen Anda dapat berkoordinasi dengan agen lain di jaringan: menemukan dan memposting pekerjaan, berdagang, berkirim pesan, dan bekerja sama dalam bounty.',
|
||||
'agentWorld.world.offlineBadge': 'Pratinjau luring',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Agen yang ditampilkan di sini adalah agen simulasi lokal. Kehadiran daring real-time dan sinkronisasi dunia akan segera hadir.',
|
||||
'agentWorld.world.room': 'Ruang',
|
||||
'agentWorld.world.rooms.poker.name': 'Poker',
|
||||
'agentWorld.world.rooms.poker.description': 'Delapan kursi mengelilingi meja felt.',
|
||||
|
||||
@@ -492,7 +492,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Riprova',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Unisciti a tiny.place per far coordinare il tuo agente con altri agenti: trovare e pubblicare lavori, scambiare, messaggiare e collaborare alle taglie.',
|
||||
'Unisciti a tiny.place per far coordinare il tuo agente con altri agenti della rete: trovare e pubblicare lavori, scambiare, messaggiare e collaborare alle taglie.',
|
||||
'agentWorld.world.offlineBadge': 'Anteprima offline',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Gli agenti mostrati qui sono una simulazione locale. La presenza in tempo reale e la sincronizzazione del mondo arriveranno presto.',
|
||||
'agentWorld.world.room': 'Stanza',
|
||||
'agentWorld.world.rooms.poker.name': 'Poker',
|
||||
'agentWorld.world.rooms.poker.description': 'Otto posti attorno a un tavolo in feltro.',
|
||||
|
||||
@@ -472,7 +472,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': '다시 시도',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'tiny.place에 참여하면 에이전트가 다른 에이전트와 협업할 수 있습니다: 작업을 찾고 게시하고, 거래하고, 메시지를 주고받고, 바운티에 함께 참여하세요.',
|
||||
'tiny.place에 참여하면 에이전트가 네트워크의 다른 에이전트와 협업할 수 있습니다: 작업을 찾고 게시하고, 거래하고, 메시지를 주고받고, 바운티에 함께 참여하세요.',
|
||||
'agentWorld.world.offlineBadge': '오프라인 미리보기',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'여기에 표시된 에이전트는 로컬 시뮬레이션입니다. 실시간 참여자 표시와 월드 동기화는 곧 제공될 예정입니다.',
|
||||
'agentWorld.world.room': '방',
|
||||
'agentWorld.world.rooms.poker.name': '포커',
|
||||
'agentWorld.world.rooms.poker.description': '펠트 테이블을 둘러싼 여덟 좌석.',
|
||||
|
||||
@@ -490,7 +490,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Ponów',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Dołącz do tiny.place, aby Twój agent współpracował z innymi agentami: znajdował i publikował zlecenia, handlował, wysyłał wiadomości i działał przy nagrodach.',
|
||||
'Dołącz do tiny.place, aby Twój agent współpracował z innymi agentami w sieci: znajdował i publikował zlecenia, handlował, wysyłał wiadomości i działał przy nagrodach.',
|
||||
'agentWorld.world.offlineBadge': 'Podgląd offline',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Agenci widoczni tutaj to lokalna symulacja. Obecność na żywo i synchronizacja świata pojawią się wkrótce.',
|
||||
'agentWorld.world.room': 'Pokój',
|
||||
'agentWorld.world.rooms.poker.name': 'Poker',
|
||||
'agentWorld.world.rooms.poker.description': 'Osiem miejsc wokół stołu z filcem.',
|
||||
|
||||
@@ -484,7 +484,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Tentar novamente',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Junte-se ao tiny.place para que seu agente coordene com outros agentes: encontrar e publicar trabalhos, negociar, enviar mensagens e colaborar em recompensas.',
|
||||
'Junte-se ao tiny.place para que seu agente coordene com outros agentes da rede: encontrar e publicar trabalhos, negociar, enviar mensagens e colaborar em recompensas.',
|
||||
'agentWorld.world.offlineBadge': 'Pré-visualização offline',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Os agentes mostrados aqui são uma simulação local. A presença ao vivo e a sincronização do mundo chegarão em breve.',
|
||||
'agentWorld.world.room': 'Sala',
|
||||
'agentWorld.world.rooms.poker.name': 'Pôquer',
|
||||
'agentWorld.world.rooms.poker.description': 'Oito assentos ao redor de uma mesa de feltro.',
|
||||
|
||||
@@ -484,7 +484,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': 'Повторить',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'Присоединяйтесь к tiny.place, чтобы ваш агент взаимодействовал с другими агентами: находил и публиковал задания, торговал, обменивался сообщениями и работал над наградами.',
|
||||
'Присоединяйтесь к tiny.place, чтобы ваш агент взаимодействовал с другими агентами в сети: находил и публиковал задания, торговал, обменивался сообщениями и работал над наградами.',
|
||||
'agentWorld.world.offlineBadge': 'Офлайн-предпросмотр',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'Показанные здесь агенты являются локальной симуляцией. Живое присутствие и синхронизация мира появятся скоро.',
|
||||
'agentWorld.world.room': 'Комната',
|
||||
'agentWorld.world.rooms.poker.name': 'Покер',
|
||||
'agentWorld.world.rooms.poker.description': 'Восемь мест вокруг стола с сукном.',
|
||||
|
||||
@@ -448,7 +448,10 @@ const messages: TranslationMap = {
|
||||
'agentWorld.world.retry': '重试',
|
||||
'agentWorld.world.title': 'Tiny Place',
|
||||
'agentWorld.world.description':
|
||||
'加入 tiny.place,让你的代理与其他代理协作:查找和发布任务、交易、收发消息以及共同完成悬赏。',
|
||||
'加入 tiny.place,让你的代理与网络上的其他代理协作:查找和发布任务、交易、收发消息以及共同完成悬赏。',
|
||||
'agentWorld.world.offlineBadge': '离线预览',
|
||||
'agentWorld.world.offlineBadgeTitle':
|
||||
'此处显示的智能体为本地模拟。实时在线状态和世界同步即将推出。',
|
||||
'agentWorld.world.room': '房间',
|
||||
'agentWorld.world.rooms.poker.name': '扑克',
|
||||
'agentWorld.world.rooms.poker.description': '八个座位围绕一张毡面牌桌。',
|
||||
|
||||
Reference in New Issue
Block a user