Clean up generated files and fix 6 dashboard bugs (#142)
* chore: gitignore local/generated files and remove from tracking Remove user-specific files (MEMORY.md, user_profile.md, prompts.py) from source control. Add patterns for credentials, backups, and generated content to .gitignore. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve 6 dashboard bugs — chat, /bugs, /swarm/events, WebSocket, marketplace, sidebar 1. Chat non-functional: CSRF middleware silently blocked HTMX POSTs. Added CSRF token transmission via hx-headers in base.html. 2. /bugs → 500: Route missing template vars (total, stats, filter_status). 3. /swarm/events → 500: Called .event_type.value on a plain str (SparkEvent.event_type is str, not enum). Also fixed timestamp and source field mismatches in the template. 4. WebSocket reconnect loop: No WS endpoint existed at /swarm/live, only an HTTP GET. Added @router.websocket("/live") using ws_manager. 5. Marketplace "Agent not found": Nav links /marketplace/ui matched the /{agent_id} catch-all. Added explicit /marketplace/ui route with enriched template context. 6. Agents sidebar "LOADING...": /swarm/agents/sidebar endpoint was missing. Added route returning the existing sidebar partial. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: restore src/timmy/prompts.py to source control prompts.py is imported by timmy.agent and is production code, not a user-local file. Re-add to tracking and remove from .gitignore. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Trip T <trip@local> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
b615595100
commit
3bf7187482
17
.gitignore
vendored
17
.gitignore
vendored
@@ -17,6 +17,10 @@ env/
|
|||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
|
discord_credentials.txt
|
||||||
|
|
||||||
|
# Backup / temp files
|
||||||
|
*~
|
||||||
|
|
||||||
# SQLite — never commit databases or WAL/SHM artifacts
|
# SQLite — never commit databases or WAL/SHM artifacts
|
||||||
*.db
|
*.db
|
||||||
@@ -55,6 +59,19 @@ src/data/
|
|||||||
*.swo
|
*.swo
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
|
# Local content — user-specific or generated
|
||||||
|
MEMORY.md
|
||||||
|
memory/self/user_profile.md
|
||||||
|
TIMMYTIME
|
||||||
|
introduction.txt
|
||||||
|
messages.txt
|
||||||
|
morning_briefing.txt
|
||||||
|
markdown_report.md
|
||||||
|
data/timmy_soul.jsonl
|
||||||
|
scripts/migrate_to_zeroclaw.py
|
||||||
|
src/infrastructure/db_pool.py
|
||||||
|
workspace/
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
|
|||||||
52
MEMORY.md
52
MEMORY.md
@@ -1,52 +0,0 @@
|
|||||||
# Timmy Hot Memory
|
|
||||||
|
|
||||||
> Working RAM — always loaded, ~300 lines max, pruned monthly
|
|
||||||
> Last updated: 2026-02-26
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current Status
|
|
||||||
|
|
||||||
**Agent State:** Operational
|
|
||||||
**Mode:** Development
|
|
||||||
**Active Tasks:** 0
|
|
||||||
**Pending Decisions:** None
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Standing Rules
|
|
||||||
|
|
||||||
1. **Sovereignty First** — No cloud dependencies
|
|
||||||
2. **Local-Only Inference** — Ollama on localhost
|
|
||||||
3. **Privacy by Design** — Telemetry disabled
|
|
||||||
4. **Tool Minimalism** — Use tools only when necessary
|
|
||||||
5. **Memory Discipline** — Write handoffs at session end
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Agent Roster
|
|
||||||
|
|
||||||
| Agent | Role | Status |
|
|
||||||
|-------|------|--------|
|
|
||||||
| Timmy | Core | Active |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## User Profile
|
|
||||||
|
|
||||||
**Name:** Not
|
|
||||||
|
|
||||||
|
|
||||||
## Key Decisions
|
|
||||||
|
|
||||||
(none yet)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Pending Actions
|
|
||||||
|
|
||||||
- [ ] Learn user's name
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Prune date: 2026-02-25*
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# User Profile
|
|
||||||
|
|
||||||
> Learned information about the user. Updated continuously.
|
|
||||||
|
|
||||||
## Basic Information
|
|
||||||
|
|
||||||
**Name:** Not
|
|
||||||
**Location:** (unknown)
|
|
||||||
**Occupation:** (unknown)
|
|
||||||
**Technical Level:** (to be assessed)
|
|
||||||
|
|
||||||
## Interests & Expertise
|
|
||||||
|
|
||||||
- (to be learned from conversations)
|
|
||||||
|
|
||||||
## Preferences
|
|
||||||
|
|
||||||
### Communication
|
|
||||||
- Response style: (default: concise, technical)
|
|
||||||
- Detail level: (default: medium)
|
|
||||||
- Humor: (default: minimal)
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
- Auto-tool usage: (default: minimal)
|
|
||||||
- Confirmation required for: shell commands, file writes
|
|
||||||
|
|
||||||
### Memory
|
|
||||||
- Personalization: Enabled
|
|
||||||
- Context retention: 20 messages (working), 100 (short-term)
|
|
||||||
|
|
||||||
## Important Facts
|
|
||||||
|
|
||||||
- (to be extracted from conversations)
|
|
||||||
|
|
||||||
## Relationship History
|
|
||||||
|
|
||||||
- First session: 2026-02-25
|
|
||||||
- Total sessions: 1
|
|
||||||
- Key milestones: (none yet)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Last updated: 2026-02-27*
|
|
||||||
@@ -47,28 +47,41 @@ async def api_list_agents():
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/marketplace")
|
@router.get("/marketplace")
|
||||||
|
async def marketplace_json(request: Request):
|
||||||
|
"""Marketplace JSON API (backward compat)."""
|
||||||
|
return await api_list_agents()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/marketplace/ui", response_class=HTMLResponse)
|
||||||
async def marketplace_ui(request: Request):
|
async def marketplace_ui(request: Request):
|
||||||
"""Marketplace page — returns JSON for API requests, HTML for browser."""
|
"""Marketplace HTML page."""
|
||||||
# Check if client wants JSON (common test clients don't set Accept header)
|
|
||||||
accept = request.headers.get("accept", "")
|
|
||||||
# Return JSON if Accept header indicates JSON OR if no preference (default to JSON for API)
|
|
||||||
if "application/json" in accept or accept == "*/*" or not accept:
|
|
||||||
return await api_list_agents()
|
|
||||||
|
|
||||||
# Browser request - return HTML
|
|
||||||
try:
|
try:
|
||||||
brain = BrainClient()
|
brain = BrainClient()
|
||||||
tasks = await brain.get_pending_tasks(limit=20)
|
tasks = await brain.get_pending_tasks(limit=20)
|
||||||
except Exception:
|
except Exception:
|
||||||
tasks = []
|
tasks = []
|
||||||
|
|
||||||
|
# Enrich agents with fields the template expects
|
||||||
|
enriched = []
|
||||||
|
for agent in AGENT_CATALOG:
|
||||||
|
a = dict(agent)
|
||||||
|
a.setdefault("status", a.get("default_status", "active"))
|
||||||
|
a.setdefault("tasks_completed", 0)
|
||||||
|
a.setdefault("total_earned", 0)
|
||||||
|
enriched.append(a)
|
||||||
|
|
||||||
|
active = sum(1 for a in enriched if a["status"] == "active")
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"marketplace.html",
|
"marketplace.html",
|
||||||
{
|
{
|
||||||
"agents": AGENT_CATALOG,
|
"agents": enriched,
|
||||||
"pending_tasks": tasks,
|
"pending_tasks": tasks,
|
||||||
"message": "Personas deprecated — use Brain Task Queue",
|
"message": "Personas deprecated — use Brain Task Queue",
|
||||||
|
"page_title": "Agent Marketplace",
|
||||||
|
"active_count": active,
|
||||||
|
"planned_count": 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Request
|
from fastapi import APIRouter, Request, WebSocket, WebSocketDisconnect
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
from spark.engine import spark_engine
|
from spark.engine import spark_engine
|
||||||
from dashboard.templating import templates
|
from dashboard.templating import templates
|
||||||
|
from infrastructure.ws_manager.handler import ws_manager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -31,13 +32,13 @@ async def swarm_events(
|
|||||||
if agent_id:
|
if agent_id:
|
||||||
events = [e for e in events if e.agent_id == agent_id]
|
events = [e for e in events if e.agent_id == agent_id]
|
||||||
if event_type:
|
if event_type:
|
||||||
events = [e for e in events if e.event_type.value == event_type]
|
events = [e for e in events if e.event_type == event_type]
|
||||||
|
|
||||||
# Prepare summary and event types for template
|
# Prepare summary and event types for template
|
||||||
summary = {}
|
summary = {}
|
||||||
event_types = set()
|
event_types = set()
|
||||||
for e in events:
|
for e in events:
|
||||||
etype = e.event_type.value
|
etype = e.event_type
|
||||||
event_types.add(etype)
|
event_types.add(etype)
|
||||||
summary[etype] = summary.get(etype, 0) + 1
|
summary[etype] = summary.get(etype, 0) + 1
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ async def swarm_live(request: Request):
|
|||||||
"""Live swarm activity page."""
|
"""Live swarm activity page."""
|
||||||
status = spark_engine.status()
|
status = spark_engine.status()
|
||||||
events = spark_engine.get_timeline(limit=20)
|
events = spark_engine.get_timeline(limit=20)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"swarm_live.html",
|
"swarm_live.html",
|
||||||
@@ -69,3 +70,34 @@ async def swarm_live(request: Request):
|
|||||||
"events": events,
|
"events": events,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.websocket("/live")
|
||||||
|
async def swarm_ws(websocket: WebSocket):
|
||||||
|
"""WebSocket endpoint for live swarm updates."""
|
||||||
|
await ws_manager.connect(websocket)
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
await websocket.receive_text()
|
||||||
|
except WebSocketDisconnect:
|
||||||
|
ws_manager.disconnect(websocket)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/agents/sidebar", response_class=HTMLResponse)
|
||||||
|
async def agents_sidebar(request: Request):
|
||||||
|
"""Sidebar partial showing agent status for the home page."""
|
||||||
|
from config import settings
|
||||||
|
|
||||||
|
agents = [
|
||||||
|
{
|
||||||
|
"id": "default",
|
||||||
|
"name": settings.agent_name,
|
||||||
|
"status": "idle",
|
||||||
|
"type": "local",
|
||||||
|
"capabilities": "chat,reasoning,research,planning",
|
||||||
|
"last_seen": None,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request, "partials/swarm_agents_sidebar.html", {"agents": agents}
|
||||||
|
)
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ async def mission_control(request: Request):
|
|||||||
|
|
||||||
@router.get("/bugs", response_class=HTMLResponse)
|
@router.get("/bugs", response_class=HTMLResponse)
|
||||||
async def bugs_page(request: Request):
|
async def bugs_page(request: Request):
|
||||||
return templates.TemplateResponse(request, "bugs.html", {"bugs": []})
|
return templates.TemplateResponse(request, "bugs.html", {
|
||||||
|
"bugs": [], "total": 0, "stats": {}, "filter_status": None,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@router.get("/self-coding", response_class=HTMLResponse)
|
@router.get("/self-coding", response_class=HTMLResponse)
|
||||||
|
|||||||
@@ -15,6 +15,14 @@
|
|||||||
<link rel="stylesheet" href="/static/style.css?v=5" />
|
<link rel="stylesheet" href="/static/style.css?v=5" />
|
||||||
{% block extra_styles %}{% endblock %}
|
{% block extra_styles %}{% endblock %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.3/dist/htmx.min.js" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.3/dist/htmx.min.js" crossorigin="anonymous"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var match = document.cookie.match(/csrf_token=([^;]+)/);
|
||||||
|
if (match) {
|
||||||
|
document.body.setAttribute('hx-headers', JSON.stringify({"X-CSRF-Token": match[1]}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -63,14 +63,14 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for event in events %}
|
{% for event in events %}
|
||||||
<tr class="event-row" data-type="{{ event.event_type.value }}">
|
<tr class="event-row" data-type="{{ event.event_type }}">
|
||||||
<td class="event-time">{{ event.timestamp[11:19] }}</td>
|
<td class="event-time">{{ event.created_at[11:19] }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="mc-badge mc-badge-{{ event.event_type.value.split('.')[0] }}">
|
<span class="mc-badge mc-badge-{{ event.event_type.split('.')[0] }}">
|
||||||
{{ event.event_type.value }}
|
{{ event.event_type }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ event.source }}</td>
|
<td>{{ event.agent_id or '-' }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if event.task_id %}
|
{% if event.task_id %}
|
||||||
<a href="/swarm/events?task_id={{ event.task_id }}">{{ event.task_id[:8] }}...</a>
|
<a href="/swarm/events?task_id={{ event.task_id }}">{{ event.task_id[:8] }}...</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user