diff --git a/apps/gui/next-env.d.ts b/apps/gui/next-env.d.ts
index c4b7818..9edff1c 100644
--- a/apps/gui/next-env.d.ts
+++ b/apps/gui/next-env.d.ts
@@ -1,6 +1,6 @@
///
///
-import "./.next/dev/types/routes.d.ts";
+import "./.next/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/apps/gui/public/calendar_logo.png b/apps/gui/public/calendar_logo.png
new file mode 100644
index 0000000..11956fc
Binary files /dev/null and b/apps/gui/public/calendar_logo.png differ
diff --git a/apps/gui/public/clock_day_icon.png b/apps/gui/public/clock_day_icon.png
new file mode 100644
index 0000000..3a6dc2e
Binary files /dev/null and b/apps/gui/public/clock_day_icon.png differ
diff --git a/apps/gui/public/clock_night_icon.png b/apps/gui/public/clock_night_icon.png
new file mode 100644
index 0000000..37e08d4
Binary files /dev/null and b/apps/gui/public/clock_night_icon.png differ
diff --git a/apps/gui/public/map_pointer_icon.png b/apps/gui/public/map_pointer_icon.png
new file mode 100644
index 0000000..44b8b1c
Binary files /dev/null and b/apps/gui/public/map_pointer_icon.png differ
diff --git a/apps/gui/src/components/play/HandoffModal.tsx b/apps/gui/src/components/play/HandoffModal.tsx
index af97efa..6990c9a 100644
--- a/apps/gui/src/components/play/HandoffModal.tsx
+++ b/apps/gui/src/components/play/HandoffModal.tsx
@@ -128,7 +128,13 @@ export function HandoffModal({ entry, onClose }: HandoffModalProps) {
{chunks.map(
(
- chunk: { content: string; importance: number },
+ chunk: {
+ content: string;
+ importance: number;
+ quotes?: string[];
+ retainInBuffer?: boolean;
+ involvedEntityIds?: string[];
+ },
index: number,
) => (
void;
+ onSubmitAction: (e: React.FormEvent
) => void;
+ onPauseRequested: () => void;
+ onResumeRequested: () => void;
+ onStopRequested: () => void;
+}
+
+export function InteractDock({
+ snapshot,
+ loading,
+ playerInput,
+ setPlayerInput,
+ onSubmitAction,
+ onPauseRequested,
+ onResumeRequested,
+ onStopRequested,
+}: InteractDockProps) {
+ const router = useRouter();
+
+ return (
+
+ );
+}
diff --git a/apps/gui/src/components/play/InteractView.tsx b/apps/gui/src/components/play/InteractView.tsx
index d2e4692..dcfb2c5 100644
--- a/apps/gui/src/components/play/InteractView.tsx
+++ b/apps/gui/src/components/play/InteractView.tsx
@@ -1,10 +1,8 @@
"use client";
import * as React from "react";
-import { useRouter } from "next/navigation";
import type { SimSnapshot } from "@/lib/simulation-types";
import { Button } from "@/components/ui/button";
-import { Textarea } from "@/components/ui/textarea";
import { Spinner } from "@/components/ui/spinner";
import { cn } from "@/lib/utils";
import { hydrate } from "@omnia/voice";
@@ -15,6 +13,7 @@ import {
AlertDescription,
AlertTitle,
} from "@/components/ui/alert";
+import { InteractDock } from "./InteractDock";
function IntentTag({
intent,
@@ -117,7 +116,7 @@ function LogEntryCard({
entry: SimSnapshot["log"][number];
onShowPrompt: (entry: SimSnapshot["log"][number]) => void;
isPlayerCard: boolean;
- playerAlciases: Record;
+ playerAliases: Record;
playerId: string;
entities: SimSnapshot["entities"];
}) {
@@ -182,6 +181,9 @@ interface InteractViewProps {
onShowPrompt: (entry: SimSnapshot["log"][number]) => void;
onShowHandoff: (entry: SimSnapshot["log"][number]) => void;
logEndRef: React.RefObject;
+ onPauseRequested: () => void;
+ onResumeRequested: () => void;
+ onStopRequested: () => void;
}
export function InteractView({
@@ -194,16 +196,17 @@ export function InteractView({
onShowPrompt,
onShowHandoff,
logEndRef,
+ onPauseRequested,
+ onResumeRequested,
+ onStopRequested,
}: InteractViewProps) {
- const router = useRouter();
-
const playerEntity = snapshot.entities.find((e) => e.isPlayer);
return (
<>
{/* Scrollable Center Viewport */}
-
+
{snapshot.log.map((entry, i) => {
if (entry.isHandoff) {
return (
@@ -257,54 +260,16 @@ export function InteractView({
- {/* Sticky Chat / Interaction Input Footer */}
-
+
>
);
}
diff --git a/apps/gui/src/components/play/PlayView.tsx b/apps/gui/src/components/play/PlayView.tsx
index ed1b284..1ce667c 100644
--- a/apps/gui/src/components/play/PlayView.tsx
+++ b/apps/gui/src/components/play/PlayView.tsx
@@ -319,43 +319,6 @@ export function PlayView() {
- {/* Simulation Global Controls */}
-
- {snapshot.status !== "done" && snapshot.status !== "error" && (
- <>
- {snapshot.status === "running" &&
- (loading ? (
-
- ) : (
-
- ))}
-
- >
- )}
-
@@ -390,6 +353,14 @@ export function PlayView() {
onShowPrompt={setSelectedEntryForModal}
onShowHandoff={setSelectedHandoffForModal}
logEndRef={logEndRef}
+ onPauseRequested={() => {
+ pauseRequestedRef.current = true;
+ }}
+ onResumeRequested={() => runSteps(snapshot.id)}
+ onStopRequested={() => {
+ pauseRequestedRef.current = true;
+ router.push("/");
+ }}
/>
) : (
diff --git a/apps/gui/src/lib/simulation-types.ts b/apps/gui/src/lib/simulation-types.ts
index 81037f3..8b408dd 100644
--- a/apps/gui/src/lib/simulation-types.ts
+++ b/apps/gui/src/lib/simulation-types.ts
@@ -42,6 +42,9 @@ export interface HandoffResult {
chunks: {
content: string;
importance: number;
+ quotes?: string[];
+ retainInBuffer?: boolean;
+ involvedEntityIds?: string[];
}[];
}
@@ -103,4 +106,6 @@ export interface SimSnapshot {
entityIndex: number;
waitingEntity?: WaitingContext;
error?: string;
+ worldTime?: string;
+ currentLocation?: string;
}
diff --git a/apps/gui/src/lib/simulation/alias-handoff.ts b/apps/gui/src/lib/simulation/alias-handoff.ts
index f80bc03..7bb10c5 100644
--- a/apps/gui/src/lib/simulation/alias-handoff.ts
+++ b/apps/gui/src/lib/simulation/alias-handoff.ts
@@ -63,7 +63,7 @@ export async function runHandoffResolution(session: SimSession): Promise {
}
: undefined,
usage: lastCall?.usage,
- handoffResult: lastResult?.response || lastCall?.response,
+ handoffResult: (lastResult?.response || lastCall?.response) as any,
});
}
}
diff --git a/apps/gui/src/lib/simulation/simulation-manager.ts b/apps/gui/src/lib/simulation/simulation-manager.ts
index 4400075..fd7f5fe 100644
--- a/apps/gui/src/lib/simulation/simulation-manager.ts
+++ b/apps/gui/src/lib/simulation/simulation-manager.ts
@@ -483,6 +483,23 @@ export class SimulationManager {
};
});
+ // Get current location from the waiting entity
+ let currentLocation: string | undefined;
+ if (
+ worldState &&
+ session.entityIndex >= 0 &&
+ session.entityIndex < session.entities.length
+ ) {
+ const currentEntityInfo = session.entities[session.entityIndex];
+ const actualEntity = worldState.getEntity(currentEntityInfo.id);
+ if (actualEntity?.locationId) {
+ const location = worldState.getLocation(actualEntity.locationId);
+ if (location) {
+ currentLocation = location.id;
+ }
+ }
+ }
+
return {
id: session.worldInstanceId,
status: session.status,
@@ -495,6 +512,8 @@ export class SimulationManager {
entityIndex: session.entityIndex,
waitingEntity: session.waitingEntity,
error: session.error,
+ worldTime: worldState?.clock.get().toISOString(),
+ currentLocation,
};
}
}
diff --git a/apps/gui/src/lib/simulation/turn-executor.ts b/apps/gui/src/lib/simulation/turn-executor.ts
index edaa2b9..dce4a50 100644
--- a/apps/gui/src/lib/simulation/turn-executor.ts
+++ b/apps/gui/src/lib/simulation/turn-executor.ts
@@ -10,6 +10,7 @@ import type {
IntentInfo,
LogEntry,
WaitingContext,
+ ValidatorCall,
} from "../simulation-types";
// ---------------------------------------------------------------------------
@@ -88,7 +89,7 @@ async function processIntents(
prompt: {
systemPrompt: lastResult.systemPrompt || "",
userContext: lastResult.userContext || "",
- components: lastResult.promptComponents,
+ components: lastResult.components,
},
response: {
isValid: outcome.isValid,
diff --git a/apps/gui/src/lib/utils.ts b/apps/gui/src/lib/utils.ts
index 365058c..eee1091 100644
--- a/apps/gui/src/lib/utils.ts
+++ b/apps/gui/src/lib/utils.ts
@@ -4,3 +4,41 @@ import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+
+export function formatSimDate(isoString: string): string {
+ try {
+ const d = new Date(isoString);
+ if (isNaN(d.getTime())) return isoString;
+ const yyyy = d.getUTCFullYear();
+ const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
+ const dd = String(d.getUTCDate()).padStart(2, "0");
+ return `${yyyy}-${mm}-${dd}`;
+ } catch {
+ return isoString;
+ }
+}
+
+export function formatSimTimeHM(isoString: string): string {
+ try {
+ const d = new Date(isoString);
+ if (isNaN(d.getTime())) return isoString;
+ const hh = String(d.getUTCHours());
+ const mm = String(d.getUTCMinutes()).padStart(2, "0");
+ return `${hh}:${mm}`;
+ } catch {
+ return isoString;
+ }
+}
+
+export function getClockIcon(isoString: string): string {
+ try {
+ const d = new Date(isoString);
+ if (isNaN(d.getTime())) return "/clock_day_icon.png";
+ const hour = d.getUTCHours();
+ return hour >= 6 && hour < 18
+ ? "/clock_day_icon.png"
+ : "/clock_night_icon.png";
+ } catch {
+ return "/clock_day_icon.png";
+ }
+}