feat: background task dispatch system with live UI panel

- 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.
This commit is contained in:
Shane
2026-08-22 18:06:23 -04:00
parent d372adca7d
commit 44e8d05e2c
8 changed files with 457 additions and 48 deletions
+17
View File
@@ -64,6 +64,23 @@
<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>