- agent/task_registry.py: file-based JSONL event registry (cross-process) - agent/task_worker.py: autonomous LLM loop with weather/time/memory/web tools - agent/dispatch_mcp.py: MCP tool exposing dispatch_task to the main agent - agent/agent.py: registers dispatch toolset, polls task events → room data - web: slide-out task panel (FAB button + badge), live step streaming via data channel topic 'tasks', status dots (running/completed/failed) - Dockerfile: copies new task_*.py and dispatch_mcp.py files The dispatch MCP runs in its own process; events flow through /tmp/tasks/events.jsonl which the main agent tails every second and forwards to the browser. Tasks run up to 10 LLM iterations with tool calls.
88 lines
5.0 KiB
HTML
88 lines
5.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<meta name="theme-color" content="#09090b">
|
|
<title>Hope</title>
|
|
<link rel="manifest" href="manifest.json">
|
|
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
|
<link rel="stylesheet" href="style.css">
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<h1>Hope</h1>
|
|
<p class="subtitle">Your voice assistant</p>
|
|
</header>
|
|
|
|
<div class="orb-stage">
|
|
<div class="orb" id="orb" data-state="idle">
|
|
<div class="orb-core"></div>
|
|
<div class="orb-ring"></div>
|
|
</div>
|
|
<div class="orb-labels">
|
|
<span class="state-label" id="stateLabel">Idle</span>
|
|
<span class="thinking-chip" id="thinkingChip" hidden>thinking…</span>
|
|
<span class="barge-hint" id="bargeHint" hidden>tap or speak to interrupt</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="controls">
|
|
<button id="startBtn" class="btn btn-primary">Start Conversation</button>
|
|
<button id="stopBtn" class="btn btn-danger" disabled>Stop</button>
|
|
<button id="muteBtn" class="btn btn-icon" aria-label="Mute microphone" title="Mute microphone" disabled>
|
|
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="8" y1="22" x2="16" y2="22"></line></svg>
|
|
</button>
|
|
<button id="settingsBtn" class="btn btn-icon" aria-label="Voice settings" title="Voice settings">
|
|
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="status" id="status">Disconnected</div>
|
|
<div class="mic-meter" id="micLevel" aria-hidden="true"><span class="mic-meter-fill" id="micMeterFill"></span></div>
|
|
|
|
<div class="transcript" id="transcript">
|
|
<div class="transcript-header">
|
|
<span>Conversation</span>
|
|
<button id="clearBtn" class="btn btn-ghost" title="Clear conversation">Clear</button>
|
|
</div>
|
|
<div id="messages"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings sheet (voice picker) -->
|
|
<div class="sheet-backdrop" id="sheetBackdrop" hidden></div>
|
|
<div class="settings-sheet" id="settingsSheet" role="dialog" aria-modal="true" aria-label="Voice settings">
|
|
<div class="sheet-handle"></div>
|
|
<div class="sheet-header">
|
|
<h2>Voice</h2>
|
|
<button id="closeSheetBtn" class="btn btn-ghost" aria-label="Close">
|
|
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"></path></svg>
|
|
</button>
|
|
</div>
|
|
<div class="voice-list" id="voiceList"></div>
|
|
</div>
|
|
|
|
<!-- Task panel (background tasks) -->
|
|
<button id="tasksBtn" class="btn btn-icon tasks-fab" aria-label="Background tasks" title="Background tasks">
|
|
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><path d="M9 9h6M9 13h6M9 17h4"></path></svg>
|
|
<span class="tasks-badge" id="tasksBadge" hidden>0</span>
|
|
</button>
|
|
<div class="tasks-panel" id="tasksPanel">
|
|
<div class="tasks-header">
|
|
<h2>Background Tasks</h2>
|
|
<button id="closeTasksBtn" class="btn btn-ghost" aria-label="Close">
|
|
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"></path></svg>
|
|
</button>
|
|
</div>
|
|
<div class="tasks-list" id="tasksList">
|
|
<p class="tasks-empty">No tasks yet. Ask Hope to research something.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="livekit-client.umd.js"></script>
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|