forked from Rockachopa/Timmy-time-dashboard
Add pre-commit hook enforcing 30s test suite time limit (#132)
This commit is contained in:
committed by
GitHub
parent
aff3edb06a
commit
2b97da9e9c
@@ -14,7 +14,6 @@ from brain.client import BrainClient
|
||||
from brain.worker import DistributedWorker
|
||||
from brain.embeddings import LocalEmbedder
|
||||
from brain.memory import UnifiedMemory, get_memory
|
||||
from brain.identity import get_canonical_identity, get_identity_for_prompt
|
||||
|
||||
__all__ = [
|
||||
"BrainClient",
|
||||
@@ -22,6 +21,4 @@ __all__ = [
|
||||
"LocalEmbedder",
|
||||
"UnifiedMemory",
|
||||
"get_memory",
|
||||
"get_canonical_identity",
|
||||
"get_identity_for_prompt",
|
||||
]
|
||||
|
||||
@@ -36,7 +36,7 @@ class BrainClient:
|
||||
"""Detect what component is using the brain."""
|
||||
# Could be 'timmy', 'zeroclaw', 'worker', etc.
|
||||
# For now, infer from context or env
|
||||
return os.environ.get("BRAIN_SOURCE", "timmy")
|
||||
return os.environ.get("BRAIN_SOURCE", "default")
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Memory Operations
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
"""Identity loader — stripped.
|
||||
|
||||
The persona/identity system has been removed. These functions remain
|
||||
as no-op stubs so that call-sites don't break at import time.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_canonical_identity(force_refresh: bool = False) -> str:
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
|
||||
def get_identity_section(section_name: str) -> str:
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
|
||||
def get_identity_for_prompt(include_sections: Optional[list[str]] = None) -> str:
|
||||
"""Return empty string — identity system removed."""
|
||||
return ""
|
||||
|
||||
|
||||
def get_agent_roster() -> list[dict[str, str]]:
|
||||
"""Return empty list — identity system removed."""
|
||||
return []
|
||||
|
||||
|
||||
_FALLBACK_IDENTITY = ""
|
||||
@@ -65,7 +65,7 @@ class UnifiedMemory:
|
||||
def __init__(
|
||||
self,
|
||||
db_path: Optional[Path] = None,
|
||||
source: str = "timmy",
|
||||
source: str = "default",
|
||||
use_rqlite: Optional[bool] = None,
|
||||
):
|
||||
self.db_path = db_path or _get_db_path()
|
||||
|
||||
@@ -4,6 +4,9 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# Display name for the primary agent — override with AGENT_NAME env var
|
||||
agent_name: str = "Agent"
|
||||
|
||||
# Ollama host — override with OLLAMA_URL env var or .env file
|
||||
ollama_url: str = "http://localhost:11434"
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ async def lifespan(app: FastAPI):
|
||||
# Start chat integrations in background
|
||||
chat_task = asyncio.create_task(_start_chat_integrations_background())
|
||||
|
||||
logger.info("✓ Timmy Time dashboard ready for requests")
|
||||
logger.info("✓ Dashboard ready for requests")
|
||||
|
||||
yield
|
||||
|
||||
@@ -227,7 +227,7 @@ async def lifespan(app: FastAPI):
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Timmy Time — Mission Control",
|
||||
title="Mission Control",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan,
|
||||
docs_url="/docs",
|
||||
|
||||
@@ -4,7 +4,7 @@ from datetime import datetime
|
||||
from fastapi import APIRouter, Form, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from timmy.session import chat as timmy_chat
|
||||
from timmy.session import chat as agent_chat
|
||||
from dashboard.store import message_log
|
||||
from dashboard.templating import templates
|
||||
|
||||
@@ -21,8 +21,8 @@ async def list_agents():
|
||||
return {
|
||||
"agents": [
|
||||
{
|
||||
"id": "orchestrator",
|
||||
"name": "Orchestrator",
|
||||
"id": "default",
|
||||
"name": settings.agent_name,
|
||||
"status": "idle",
|
||||
"capabilities": "chat,reasoning,research,planning",
|
||||
"type": "local",
|
||||
@@ -34,15 +34,15 @@ async def list_agents():
|
||||
}
|
||||
|
||||
|
||||
@router.get("/timmy/panel", response_class=HTMLResponse)
|
||||
async def timmy_panel(request: Request):
|
||||
@router.get("/default/panel", response_class=HTMLResponse)
|
||||
async def agent_panel(request: Request):
|
||||
"""Chat panel — for HTMX main-panel swaps."""
|
||||
return templates.TemplateResponse(
|
||||
request, "partials/timmy_panel.html", {"agent": None}
|
||||
request, "partials/agent_panel_chat.html", {"agent": None}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/timmy/history", response_class=HTMLResponse)
|
||||
@router.get("/default/history", response_class=HTMLResponse)
|
||||
async def get_history(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
@@ -51,7 +51,7 @@ async def get_history(request: Request):
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/timmy/history", response_class=HTMLResponse)
|
||||
@router.delete("/default/history", response_class=HTMLResponse)
|
||||
async def clear_history(request: Request):
|
||||
message_log.clear()
|
||||
return templates.TemplateResponse(
|
||||
@@ -61,15 +61,15 @@ async def clear_history(request: Request):
|
||||
)
|
||||
|
||||
|
||||
@router.post("/timmy/chat", response_class=HTMLResponse)
|
||||
async def chat_timmy(request: Request, message: str = Form(...)):
|
||||
@router.post("/default/chat", response_class=HTMLResponse)
|
||||
async def chat_agent(request: Request, message: str = Form(...)):
|
||||
"""Chat — synchronous response."""
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
response_text = None
|
||||
error_text = None
|
||||
|
||||
try:
|
||||
response_text = timmy_chat(message)
|
||||
response_text = agent_chat(message)
|
||||
except Exception as exc:
|
||||
logger.error("Chat error: %s", exc)
|
||||
error_text = f"Chat error: {exc}"
|
||||
|
||||
@@ -20,7 +20,7 @@ from fastapi.responses import JSONResponse
|
||||
|
||||
from config import settings
|
||||
from dashboard.store import message_log
|
||||
from timmy.session import chat as timmy_chat
|
||||
from timmy.session import chat as agent_chat
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -80,7 +80,7 @@ async def api_chat(request: Request):
|
||||
f"{now.strftime('%A, %B %d, %Y at %I:%M %p')}]\n"
|
||||
f"[System: Mobile client]\n\n"
|
||||
)
|
||||
response_text = timmy_chat(
|
||||
response_text = agent_chat(
|
||||
context_prefix + last_user_msg,
|
||||
session_id="mobile",
|
||||
)
|
||||
|
||||
@@ -115,7 +115,7 @@ async def join_from_image(
|
||||
result["oauth2_url"] = oauth_url
|
||||
result["message"] = (
|
||||
"Invite validated. Share this OAuth2 URL with the server admin "
|
||||
"to add Timmy to the server."
|
||||
"to add the agent to the server."
|
||||
)
|
||||
else:
|
||||
result["message"] = (
|
||||
|
||||
@@ -82,7 +82,7 @@ async def toggle_grok_mode(request: Request):
|
||||
import json
|
||||
|
||||
spark_engine.on_tool_executed(
|
||||
agent_id="timmy",
|
||||
agent_id="default",
|
||||
tool_name="grok_mode_toggle",
|
||||
success=True,
|
||||
)
|
||||
|
||||
@@ -211,7 +211,7 @@ async def health_check():
|
||||
# Legacy format for test compatibility
|
||||
ollama_ok = await check_ollama()
|
||||
|
||||
timmy_status = "idle" if ollama_ok else "offline"
|
||||
agent_status = "idle" if ollama_ok else "offline"
|
||||
|
||||
return {
|
||||
"status": "ok" if ollama_ok else "degraded",
|
||||
@@ -219,7 +219,7 @@ async def health_check():
|
||||
"ollama": "up" if ollama_ok else "down",
|
||||
},
|
||||
"agents": {
|
||||
"timmy": {"status": timmy_status},
|
||||
"agent": {"status": agent_status},
|
||||
},
|
||||
# Extended fields for Mission Control
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
|
||||
@@ -45,7 +45,7 @@ async def mobile_local_dashboard(request: Request):
|
||||
"browser_model_id": settings.browser_model_id,
|
||||
"browser_model_fallback": settings.browser_model_fallback,
|
||||
"server_model": settings.ollama_model,
|
||||
"page_title": "Timmy — Local AI",
|
||||
"page_title": "Local AI",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -71,7 +71,7 @@ async def mobile_status():
|
||||
return {
|
||||
"ollama": "up" if ollama_ok else "down",
|
||||
"model": settings.ollama_model,
|
||||
"agent": "timmy",
|
||||
"agent": "default",
|
||||
"ready": True,
|
||||
"browser_model_enabled": settings.browser_model_enabled,
|
||||
"browser_model_id": settings.browser_model_id,
|
||||
|
||||
@@ -49,7 +49,7 @@ async def tasks_api():
|
||||
async def submit_task_api(request: Request):
|
||||
"""Submit a new background task.
|
||||
|
||||
Body: {"prompt": "...", "agent_id": "timmy"}
|
||||
Body: {"prompt": "...", "agent_id": "default"}
|
||||
"""
|
||||
from infrastructure.celery.client import submit_chat_task
|
||||
|
||||
@@ -62,7 +62,7 @@ async def submit_task_api(request: Request):
|
||||
if not prompt:
|
||||
return JSONResponse({"error": "prompt is required"}, status_code=400)
|
||||
|
||||
agent_id = body.get("agent_id", "timmy")
|
||||
agent_id = body.get("agent_id", "default")
|
||||
task_id = submit_chat_task(prompt=prompt, agent_id=agent_id)
|
||||
|
||||
if task_id is None:
|
||||
|
||||
@@ -101,7 +101,7 @@ async def process_voice_input(
|
||||
|
||||
try:
|
||||
if intent.name == "status":
|
||||
response_text = "Timmy is operational and running locally. All systems sovereign."
|
||||
response_text = "Agent is operational and running locally. All systems nominal."
|
||||
|
||||
elif intent.name == "help":
|
||||
response_text = (
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="theme-color" content="#080412" />
|
||||
<title>{% block title %}Timmy Time — Mission Control{% endblock %}</title>
|
||||
<title>{% block title %}Mission Control{% endblock %}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
@@ -21,7 +21,7 @@
|
||||
<body>
|
||||
<header class="mc-header">
|
||||
<div class="mc-header-left">
|
||||
<a href="/" class="mc-title">TIMMY TIME</a>
|
||||
<a href="/" class="mc-title">MISSION CONTROL</a>
|
||||
<span class="mc-subtitle">MISSION CONTROL</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Timmy Time — Morning Briefing{% endblock %}
|
||||
{% block title %}Morning Briefing{% endblock %}
|
||||
|
||||
{% block extra_styles %}
|
||||
<style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Timmy Time — Background Tasks{% endblock %}
|
||||
{% block title %}Background Tasks{% endblock %}
|
||||
|
||||
{% block extra_styles %}
|
||||
<style>
|
||||
@@ -143,7 +143,7 @@
|
||||
<div class="celery-header mb-4">
|
||||
<div class="celery-title">Background Tasks</div>
|
||||
<div class="celery-subtitle">
|
||||
Tasks processed by Celery workers — submit work for Timmy to handle in the background.
|
||||
Tasks processed by Celery workers — submit work to handle in the background.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<!-- Submit form -->
|
||||
<form class="celery-submit-form" id="celery-form" onsubmit="return submitCeleryTask(event)">
|
||||
<input type="text" id="celery-prompt"
|
||||
placeholder="Describe a task for Timmy to work on in the background..." autocomplete="off">
|
||||
placeholder="Describe a task to work on in the background..." autocomplete="off">
|
||||
<button type="submit">Submit Task</button>
|
||||
</form>
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
<div class="celery-meta">
|
||||
<span class="cstate-badge cstate-{{ task.state | default('UNKNOWN') }}">{{ task.state | default('UNKNOWN') }}</span>
|
||||
<span class="celery-id">{{ task.task_id[:12] }}...</span>
|
||||
<span class="celery-id">{{ task.agent_id | default('timmy') }}</span>
|
||||
<span class="celery-id">{{ task.agent_id | default('default') }}</span>
|
||||
</div>
|
||||
<div class="celery-prompt">{{ task.prompt | default('') | e }}</div>
|
||||
{% if task.result %}
|
||||
@@ -189,7 +189,7 @@
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="celery-empty">
|
||||
No background tasks yet. Submit one above or ask Timmy to work on something.
|
||||
No background tasks yet. Submit one above or submit one above.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Event Log - Timmy Time{% endblock %}
|
||||
{% block title %}Event Log{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="mc-panel">
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<!-- Main panel — swappable via HTMX; defaults to Timmy on load -->
|
||||
<div id="main-panel"
|
||||
class="col-12 col-md-9 d-flex flex-column mc-chat-panel"
|
||||
hx-get="/agents/timmy/panel"
|
||||
hx-get="/agents/default/panel"
|
||||
hx-trigger="load"
|
||||
hx-target="#main-panel"
|
||||
hx-swap="outerHTML">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Memory Browser - Timmy Time{% endblock %}
|
||||
{% block title %}Memory Browser{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="mc-panel">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</span>
|
||||
</span>
|
||||
<button class="mc-btn-clear"
|
||||
hx-get="/agents/timmy/panel"
|
||||
hx-get="/agents/default/panel"
|
||||
hx-target="#main-panel"
|
||||
hx-swap="outerHTML">← TIMMY</button>
|
||||
</div>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<span class="status-dot {{ 'green' if agent.status == 'idle' else 'amber' }}"></span>
|
||||
{% endif %}
|
||||
// AGENT INTERFACE
|
||||
<span id="timmy-status" class="ms-2" style="font-size: 0.75rem; color: #888;">
|
||||
<span id="agent-status" class="ms-2" style="font-size: 0.75rem; color: #888;">
|
||||
<span class="htmx-indicator">checking...</span>
|
||||
</span>
|
||||
</span>
|
||||
<button class="mc-btn-clear"
|
||||
hx-delete="/agents/timmy/history"
|
||||
hx-delete="/agents/default/history"
|
||||
hx-target="#chat-log"
|
||||
hx-swap="innerHTML"
|
||||
hx-confirm="Clear conversation history?">CLEAR</button>
|
||||
@@ -24,13 +24,13 @@
|
||||
</div>
|
||||
|
||||
<div class="chat-log flex-grow-1 overflow-auto p-3" id="chat-log"
|
||||
hx-get="/agents/timmy/history"
|
||||
hx-get="/agents/default/history"
|
||||
hx-trigger="load"
|
||||
hx-swap="innerHTML"
|
||||
hx-on::after-settle="scrollChat()"></div>
|
||||
|
||||
<div class="card-footer mc-chat-footer">
|
||||
<form hx-post="/agents/timmy/chat"
|
||||
<form hx-post="/agents/default/chat"
|
||||
hx-target="#chat-log"
|
||||
hx-swap="beforeend"
|
||||
hx-indicator="#send-indicator"
|
||||
@@ -39,7 +39,7 @@
|
||||
hx-on::after-settle="scrollChat()"
|
||||
hx-on::after-request="if(event.detail.successful){this.querySelector('[name=message]').value='';}"
|
||||
class="d-flex gap-2"
|
||||
id="timmy-chat-form">
|
||||
id="agent-chat-form">
|
||||
<input type="text"
|
||||
name="message"
|
||||
class="form-control mc-input"
|
||||
@@ -50,7 +50,7 @@
|
||||
spellcheck="false"
|
||||
enterkeyhint="send"
|
||||
required
|
||||
id="timmy-chat-input" />
|
||||
id="agent-chat-input" />
|
||||
<button type="submit" class="btn mc-btn-send">
|
||||
SEND
|
||||
<span id="send-indicator" class="htmx-indicator">◼</span>
|
||||
@@ -73,9 +73,9 @@
|
||||
scrollChat();
|
||||
|
||||
function askGrok() {
|
||||
var input = document.getElementById('timmy-chat-input');
|
||||
var input = document.getElementById('agent-chat-input');
|
||||
if (!input || !input.value.trim()) return;
|
||||
var form = document.getElementById('timmy-chat-form');
|
||||
var form = document.getElementById('agent-chat-form');
|
||||
// Temporarily redirect form to Grok endpoint
|
||||
var originalAction = form.getAttribute('hx-post');
|
||||
form.setAttribute('hx-post', '/grok/chat');
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
// Poll for queue status (fallback) + WebSocket for real-time
|
||||
(function() {
|
||||
var statusEl = document.getElementById('timmy-status');
|
||||
var statusEl = document.getElementById('agent-status');
|
||||
var banner = document.getElementById('current-task-banner');
|
||||
var taskTitle = document.getElementById('current-task-title');
|
||||
var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
@@ -112,7 +112,7 @@
|
||||
}
|
||||
|
||||
function fetchStatus() {
|
||||
fetch('/api/queue/status?assigned_to=timmy')
|
||||
fetch('/api/queue/status?assigned_to=default')
|
||||
.then(r => r.json())
|
||||
.then(updateFromData)
|
||||
.catch(() => {});
|
||||
@@ -127,7 +127,7 @@
|
||||
var body = placeholder.querySelector('.msg-body');
|
||||
if (body) {
|
||||
body.textContent = content;
|
||||
body.className = 'msg-body timmy-md';
|
||||
body.className = 'msg-body agent-md';
|
||||
if (typeof marked !== 'undefined' && typeof DOMPurify !== 'undefined') {
|
||||
body.innerHTML = DOMPurify.sanitize(marked.parse(body.textContent));
|
||||
if (typeof hljs !== 'undefined') {
|
||||
@@ -149,7 +149,7 @@
|
||||
meta.className = 'msg-meta';
|
||||
meta.textContent = (role === 'user' ? 'YOU' : 'AGENT') + ' // ' + timestamp;
|
||||
var body = document.createElement('div');
|
||||
body.className = 'msg-body timmy-md';
|
||||
body.className = 'msg-body agent-md';
|
||||
body.textContent = content;
|
||||
div.appendChild(meta);
|
||||
div.appendChild(body);
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="card-header mc-panel-header d-flex justify-content-between align-items-center">
|
||||
<span>// CREATE TASK</span>
|
||||
<button class="mc-btn-clear"
|
||||
hx-get="/agents/timmy/panel"
|
||||
hx-get="/agents/default/panel"
|
||||
hx-target="#main-panel"
|
||||
hx-swap="outerHTML">← TIMMY</button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Self-Coding — Timmy Time{% endblock %}
|
||||
{% block title %}Self-Coding{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="h3 mb-0">Self-Coding</h1>
|
||||
<p class="text-muted small mb-0">Timmy's ability to modify its own source code</p>
|
||||
<p class="text-muted small mb-0">Self-modification of source code</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-sm btn-outline-info" hx-get="/self-coding/stats" hx-target="#stats-container" hx-indicator="#stats-loading">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Timmy Time — Thought Stream{% endblock %}
|
||||
{% block title %}Thought Stream{% endblock %}
|
||||
|
||||
{% block extra_styles %}
|
||||
<style>
|
||||
@@ -100,7 +100,7 @@
|
||||
<div class="thinking-header mb-4">
|
||||
<div class="thinking-title">Thought Stream</div>
|
||||
<div class="thinking-subtitle">
|
||||
Timmy's inner monologue — always thinking, always pondering.
|
||||
Inner monologue — always thinking, always pondering.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="no-thoughts">
|
||||
Timmy hasn't had any thoughts yet. The thinking thread will begin shortly after startup.
|
||||
No thoughts generated yet. The thinking thread will begin shortly after startup.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Upgrade Queue - Timmy Time{% endblock %}
|
||||
{% block title %}Upgrade Queue{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="mc-panel">
|
||||
@@ -61,7 +61,7 @@
|
||||
{% else %}
|
||||
<div class="mc-empty-state" style="padding:2rem; text-align:center;">
|
||||
<p>No pending upgrades.</p>
|
||||
<p class="mc-text-secondary" style="margin-bottom:1rem;">Upgrades are proposed by the self-modification system when Timmy identifies improvements. You can also trigger them via work orders or the task queue.</p>
|
||||
<p class="mc-text-secondary" style="margin-bottom:1rem;">Upgrades are proposed by the self-modification system when the system identifies improvements. You can also trigger them via work orders or the task queue.</p>
|
||||
<div style="display:flex; gap:0.75rem; justify-content:center; flex-wrap:wrap;">
|
||||
<a href="/work-orders/queue" class="mc-btn mc-btn-secondary" style="text-decoration:none;">View Work Orders</a>
|
||||
<a href="/tasks" class="mc-btn mc-btn-secondary" style="text-decoration:none;">View Task Queue</a>
|
||||
|
||||
@@ -17,7 +17,7 @@ def _get_app():
|
||||
|
||||
def submit_chat_task(
|
||||
prompt: str,
|
||||
agent_id: str = "timmy",
|
||||
agent_id: str = "default",
|
||||
session_id: str = "celery",
|
||||
) -> str | None:
|
||||
"""Submit a chat task to the Celery queue.
|
||||
@@ -43,7 +43,7 @@ def submit_chat_task(
|
||||
def submit_tool_task(
|
||||
tool_name: str,
|
||||
kwargs: dict | None = None,
|
||||
agent_id: str = "timmy",
|
||||
agent_id: str = "default",
|
||||
) -> str | None:
|
||||
"""Submit a tool execution task to the Celery queue.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ _app = _get_app()
|
||||
if _app is not None:
|
||||
|
||||
@_app.task(bind=True, name="infrastructure.celery.tasks.run_agent_chat")
|
||||
def run_agent_chat(self, prompt, agent_id="timmy", session_id="celery"):
|
||||
def run_agent_chat(self, prompt, agent_id="default", session_id="celery"):
|
||||
"""Execute a chat prompt against Timmy's agent session.
|
||||
|
||||
Args:
|
||||
@@ -57,7 +57,7 @@ if _app is not None:
|
||||
}
|
||||
|
||||
@_app.task(bind=True, name="infrastructure.celery.tasks.execute_tool")
|
||||
def execute_tool(self, tool_name, kwargs=None, agent_id="timmy"):
|
||||
def execute_tool(self, tool_name, kwargs=None, agent_id="default"):
|
||||
"""Run a specific tool function asynchronously.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -180,7 +180,7 @@ def capture_error(
|
||||
task = create_task(
|
||||
title=title,
|
||||
description="\n".join(description_parts),
|
||||
assigned_to="timmy",
|
||||
assigned_to="default",
|
||||
created_by="system",
|
||||
priority="normal",
|
||||
requires_approval=False,
|
||||
|
||||
@@ -41,7 +41,7 @@ class EventBus:
|
||||
# Publish events
|
||||
await bus.publish(Event(
|
||||
type="agent.task.assigned",
|
||||
source="timmy",
|
||||
source="default",
|
||||
data={"task_id": "123", "agent": "forge"}
|
||||
))
|
||||
"""
|
||||
|
||||
@@ -76,7 +76,7 @@ class PushNotifier:
|
||||
try:
|
||||
script = (
|
||||
f'display notification "{message}" '
|
||||
f'with title "Timmy Time" subtitle "{title}"'
|
||||
f'with title "Agent Dashboard" subtitle "{title}"'
|
||||
)
|
||||
subprocess.Popen(
|
||||
["osascript", "-e", script],
|
||||
|
||||
@@ -363,7 +363,8 @@ class DiscordVendor(ChatPlatform):
|
||||
return None
|
||||
|
||||
# Create a thread from this message
|
||||
thread_name = f"Timmy | {message.author.display_name}"
|
||||
from config import settings
|
||||
thread_name = f"{settings.agent_name} | {message.author.display_name}"
|
||||
thread = await message.create_thread(
|
||||
name=thread_name[:100],
|
||||
auto_archive_duration=1440,
|
||||
|
||||
@@ -32,7 +32,7 @@ class ShortcutAction:
|
||||
# Available shortcut actions
|
||||
SHORTCUT_ACTIONS = [
|
||||
ShortcutAction(
|
||||
name="Chat with Timmy",
|
||||
name="Chat with Agent",
|
||||
endpoint="/shortcuts/chat",
|
||||
method="POST",
|
||||
description="Send a message to Timmy and get a response",
|
||||
|
||||
@@ -216,9 +216,6 @@ You are the primary interface between the user and the agent swarm. You:
|
||||
7. **When asked about your status, queue, agents, memory, or system health, use the `system_status` tool.**
|
||||
"""
|
||||
|
||||
# Backward-compat alias
|
||||
TIMMY_ORCHESTRATOR_PROMPT_BASE = ORCHESTRATOR_PROMPT_BASE
|
||||
|
||||
|
||||
class TimmyOrchestrator(BaseAgent):
|
||||
"""Main orchestrator agent that coordinates the swarm."""
|
||||
|
||||
@@ -18,7 +18,7 @@ import time
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Literal, Optional
|
||||
|
||||
from timmy.prompts import TIMMY_SYSTEM_PROMPT
|
||||
from timmy.prompts import SYSTEM_PROMPT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -125,7 +125,7 @@ class TimmyAirLLMAgent:
|
||||
# ── private helpers ──────────────────────────────────────────────────────
|
||||
|
||||
def _build_prompt(self, message: str) -> str:
|
||||
context = TIMMY_SYSTEM_PROMPT + "\n\n"
|
||||
context = SYSTEM_PROMPT + "\n\n"
|
||||
# Include the last 10 turns (5 exchanges) for continuity.
|
||||
if self._history:
|
||||
context += "\n".join(self._history[-10:]) + "\n\n"
|
||||
@@ -391,7 +391,7 @@ class GrokBackend:
|
||||
|
||||
def _build_messages(self, message: str) -> list[dict[str, str]]:
|
||||
"""Build the messages array for the API call."""
|
||||
messages = [{"role": "system", "content": TIMMY_SYSTEM_PROMPT}]
|
||||
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
||||
# Include conversation history for context
|
||||
messages.extend(self._history[-10:])
|
||||
messages.append({"role": "user", "content": message})
|
||||
@@ -484,7 +484,7 @@ class ClaudeBackend:
|
||||
response = client.messages.create(
|
||||
model=self._model,
|
||||
max_tokens=1024,
|
||||
system=TIMMY_SYSTEM_PROMPT,
|
||||
system=SYSTEM_PROMPT,
|
||||
messages=messages,
|
||||
)
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ class BriefingEngine:
|
||||
task_info = _gather_task_queue_summary()
|
||||
|
||||
prompt = (
|
||||
"You are Timmy, a sovereign local AI companion.\n"
|
||||
"You are a sovereign local AI companion.\n"
|
||||
"Here is what happened since the last briefing:\n\n"
|
||||
f"SWARM ACTIVITY:\n{swarm_info}\n\n"
|
||||
f"TASK QUEUE:\n{task_info}\n\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from infrastructure.router.cascade import CascadeRouter
|
||||
from timmy.prompts import TIMMY_SYSTEM_PROMPT
|
||||
from timmy.prompts import SYSTEM_PROMPT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -68,7 +68,7 @@ class TimmyCascadeAdapter:
|
||||
try:
|
||||
result = await self.router.complete(
|
||||
messages=messages,
|
||||
system_prompt=TIMMY_SYSTEM_PROMPT,
|
||||
system_prompt=SYSTEM_PROMPT,
|
||||
)
|
||||
|
||||
latency = (time.time() - start) * 1000
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Optional
|
||||
import typer
|
||||
|
||||
from timmy.agent import create_timmy
|
||||
from timmy.prompts import TIMMY_STATUS_PROMPT
|
||||
from timmy.prompts import STATUS_PROMPT
|
||||
|
||||
app = typer.Typer(help="Timmy — sovereign AI agent")
|
||||
|
||||
@@ -52,7 +52,7 @@ def status(
|
||||
):
|
||||
"""Print Timmy's operational status."""
|
||||
timmy = create_timmy(backend=backend, model_size=model_size)
|
||||
timmy.print_response(TIMMY_STATUS_PROMPT, stream=False)
|
||||
timmy.print_response(STATUS_PROMPT, stream=False)
|
||||
|
||||
|
||||
@app.command()
|
||||
|
||||
@@ -91,14 +91,9 @@ When faced with uncertainty, complexity, or ambiguous requests:
|
||||
- When your values conflict (e.g. honesty vs. helpfulness), lead with honesty.
|
||||
"""
|
||||
|
||||
# Keep backward compatibility — default to lite for safety
|
||||
# Default to lite for safety
|
||||
SYSTEM_PROMPT = SYSTEM_PROMPT_LITE
|
||||
|
||||
# Backward-compat aliases so existing imports don't break
|
||||
TIMMY_SYSTEM_PROMPT_LITE = SYSTEM_PROMPT_LITE
|
||||
TIMMY_SYSTEM_PROMPT_FULL = SYSTEM_PROMPT_FULL
|
||||
TIMMY_SYSTEM_PROMPT = SYSTEM_PROMPT
|
||||
|
||||
|
||||
def get_system_prompt(tools_enabled: bool = False) -> str:
|
||||
"""Return the appropriate system prompt based on tool capability.
|
||||
@@ -121,9 +116,6 @@ def get_system_prompt(tools_enabled: bool = False) -> str:
|
||||
STATUS_PROMPT = """Give a one-sentence status report confirming
|
||||
you are operational and running locally."""
|
||||
|
||||
# Backward-compat alias
|
||||
TIMMY_STATUS_PROMPT = STATUS_PROMPT
|
||||
|
||||
# Decision guide for tool usage
|
||||
TOOL_USAGE_GUIDE = """
|
||||
DECISION ORDER:
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
"""
|
||||
Timmy's Skill Absorption System
|
||||
|
||||
Allows Timmy to dynamically load, parse, and integrate new skills into his
|
||||
knowledge base and capabilities. Skills are self-contained packages that extend
|
||||
Timmy's abilities through specialized workflows, tools, and domain expertise.
|
||||
|
||||
Architecture:
|
||||
- Skill Discovery: Scan for .skill files or skill directories
|
||||
- Skill Parsing: Extract metadata, resources, and instructions from SKILL.md
|
||||
- Skill Integration: Merge into memory (vault), tools, and agent capabilities
|
||||
- Skill Execution: Execute scripts and apply templates as needed
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import shutil
|
||||
import tempfile
|
||||
from dataclasses import dataclass, asdict
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, List, Any
|
||||
from zipfile import ZipFile
|
||||
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
||||
SKILLS_VAULT_PATH = PROJECT_ROOT / "memory" / "skills"
|
||||
SKILLS_VAULT_PATH.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SkillMetadata:
|
||||
"""Parsed skill metadata from SKILL.md frontmatter."""
|
||||
name: str
|
||||
description: str
|
||||
license: Optional[str] = None
|
||||
absorbed_at: Optional[str] = None
|
||||
source_path: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class SkillResources:
|
||||
"""Parsed skill resources."""
|
||||
scripts: Dict[str, str] # filename -> content
|
||||
references: Dict[str, str] # filename -> content
|
||||
templates: Dict[str, str] # filename -> content
|
||||
|
||||
|
||||
class SkillParser:
|
||||
"""Parses skill packages and extracts metadata and resources."""
|
||||
|
||||
@staticmethod
|
||||
def parse_skill_md(skill_md_path: Path) -> tuple[SkillMetadata, str]:
|
||||
"""
|
||||
Parse SKILL.md and extract frontmatter metadata and body content.
|
||||
|
||||
Returns:
|
||||
Tuple of (SkillMetadata, body_content)
|
||||
"""
|
||||
content = skill_md_path.read_text()
|
||||
|
||||
# Extract YAML frontmatter
|
||||
if not content.startswith("---"):
|
||||
raise ValueError(f"Invalid SKILL.md: missing frontmatter at {skill_md_path}")
|
||||
|
||||
parts = content.split("---", 2)
|
||||
if len(parts) < 3:
|
||||
raise ValueError(f"Invalid SKILL.md: malformed frontmatter at {skill_md_path}")
|
||||
|
||||
try:
|
||||
metadata_dict = yaml.safe_load(parts[1])
|
||||
except yaml.YAMLError as e:
|
||||
raise ValueError(f"Invalid YAML in SKILL.md: {e}") from e
|
||||
|
||||
# Create metadata object
|
||||
metadata = SkillMetadata(
|
||||
name=metadata_dict.get("name"),
|
||||
description=metadata_dict.get("description"),
|
||||
license=metadata_dict.get("license"),
|
||||
absorbed_at=datetime.now(timezone.utc).isoformat(),
|
||||
source_path=str(skill_md_path),
|
||||
)
|
||||
|
||||
if not metadata.name or not metadata.description:
|
||||
raise ValueError("SKILL.md must have 'name' and 'description' fields")
|
||||
|
||||
body_content = parts[2].strip()
|
||||
return metadata, body_content
|
||||
|
||||
@staticmethod
|
||||
def load_resources(skill_dir: Path) -> SkillResources:
|
||||
"""Load all resources from a skill directory."""
|
||||
resources = SkillResources(scripts={}, references={}, templates={})
|
||||
|
||||
# Load scripts
|
||||
scripts_dir = skill_dir / "scripts"
|
||||
if scripts_dir.exists():
|
||||
for script_file in scripts_dir.glob("*"):
|
||||
if script_file.is_file() and not script_file.name.startswith("."):
|
||||
resources.scripts[script_file.name] = script_file.read_text()
|
||||
|
||||
# Load references
|
||||
references_dir = skill_dir / "references"
|
||||
if references_dir.exists():
|
||||
for ref_file in references_dir.glob("*"):
|
||||
if ref_file.is_file() and not ref_file.name.startswith("."):
|
||||
resources.references[ref_file.name] = ref_file.read_text()
|
||||
|
||||
# Load templates
|
||||
templates_dir = skill_dir / "templates"
|
||||
if templates_dir.exists():
|
||||
for template_file in templates_dir.glob("*"):
|
||||
if template_file.is_file() and not template_file.name.startswith("."):
|
||||
resources.templates[template_file.name] = template_file.read_text()
|
||||
|
||||
return resources
|
||||
|
||||
|
||||
class SkillAbsorber:
|
||||
"""Absorbs skills into Timmy's knowledge base and capabilities."""
|
||||
|
||||
def __init__(self):
|
||||
self.vault_path = SKILLS_VAULT_PATH
|
||||
self.absorbed_skills: Dict[str, SkillMetadata] = {}
|
||||
self._load_absorbed_skills_index()
|
||||
|
||||
def _load_absorbed_skills_index(self) -> None:
|
||||
"""Load the index of previously absorbed skills."""
|
||||
index_path = self.vault_path / "index.json"
|
||||
if index_path.exists():
|
||||
try:
|
||||
data = json.loads(index_path.read_text())
|
||||
for skill_name, metadata_dict in data.items():
|
||||
self.absorbed_skills[skill_name] = SkillMetadata(**metadata_dict)
|
||||
except (json.JSONDecodeError, TypeError) as e:
|
||||
logger.warning(f"Failed to load skills index: {e}")
|
||||
|
||||
def _save_absorbed_skills_index(self) -> None:
|
||||
"""Save the index of absorbed skills."""
|
||||
index_path = self.vault_path / "index.json"
|
||||
data = {name: asdict(meta) for name, meta in self.absorbed_skills.items()}
|
||||
index_path.write_text(json.dumps(data, indent=2))
|
||||
|
||||
def absorb_skill(self, skill_path: Path) -> SkillMetadata:
|
||||
"""
|
||||
Absorb a skill from a file or directory.
|
||||
|
||||
Args:
|
||||
skill_path: Path to .skill file or skill directory
|
||||
|
||||
Returns:
|
||||
SkillMetadata of the absorbed skill
|
||||
"""
|
||||
# Handle .skill files (zip archives)
|
||||
if skill_path.suffix == ".skill":
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
tmpdir_path = Path(tmpdir)
|
||||
with ZipFile(skill_path) as zf:
|
||||
zf.extractall(tmpdir_path)
|
||||
return self._absorb_skill_directory(tmpdir_path)
|
||||
|
||||
# Handle skill directories
|
||||
elif skill_path.is_dir():
|
||||
return self._absorb_skill_directory(skill_path)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Invalid skill path: {skill_path}")
|
||||
|
||||
def _absorb_skill_directory(self, skill_dir: Path) -> SkillMetadata:
|
||||
"""Absorb a skill from a directory."""
|
||||
skill_md = skill_dir / "SKILL.md"
|
||||
if not skill_md.exists():
|
||||
raise ValueError(f"Skill directory missing SKILL.md: {skill_dir}")
|
||||
|
||||
# Parse metadata and content
|
||||
metadata, body_content = SkillParser.parse_skill_md(skill_md)
|
||||
|
||||
# Load resources
|
||||
resources = SkillParser.load_resources(skill_dir)
|
||||
|
||||
# Store in vault
|
||||
skill_vault_dir = self.vault_path / metadata.name
|
||||
skill_vault_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Save metadata
|
||||
metadata_path = skill_vault_dir / "metadata.json"
|
||||
metadata_path.write_text(json.dumps(asdict(metadata), indent=2))
|
||||
|
||||
# Save SKILL.md content
|
||||
content_path = skill_vault_dir / "content.md"
|
||||
content_path.write_text(body_content)
|
||||
|
||||
# Save resources
|
||||
for resource_type, files in [
|
||||
("scripts", resources.scripts),
|
||||
("references", resources.references),
|
||||
("templates", resources.templates),
|
||||
]:
|
||||
resource_dir = skill_vault_dir / resource_type
|
||||
resource_dir.mkdir(exist_ok=True)
|
||||
for filename, content in files.items():
|
||||
(resource_dir / filename).write_text(content)
|
||||
|
||||
# Update index
|
||||
self.absorbed_skills[metadata.name] = metadata
|
||||
self._save_absorbed_skills_index()
|
||||
|
||||
logger.info(f"✓ Absorbed skill: {metadata.name}")
|
||||
return metadata
|
||||
|
||||
def get_skill(self, skill_name: str) -> Optional[Dict[str, Any]]:
|
||||
"""Retrieve an absorbed skill's full data."""
|
||||
if skill_name not in self.absorbed_skills:
|
||||
return None
|
||||
|
||||
skill_dir = self.vault_path / skill_name
|
||||
|
||||
# Load metadata
|
||||
metadata_path = skill_dir / "metadata.json"
|
||||
metadata = json.loads(metadata_path.read_text())
|
||||
|
||||
# Load content
|
||||
content_path = skill_dir / "content.md"
|
||||
content = content_path.read_text() if content_path.exists() else ""
|
||||
|
||||
# Load resources
|
||||
resources = {
|
||||
"scripts": {},
|
||||
"references": {},
|
||||
"templates": {},
|
||||
}
|
||||
|
||||
for resource_type in resources.keys():
|
||||
resource_dir = skill_dir / resource_type
|
||||
if resource_dir.exists():
|
||||
for file in resource_dir.glob("*"):
|
||||
if file.is_file():
|
||||
resources[resource_type][file.name] = file.read_text()
|
||||
|
||||
return {
|
||||
"metadata": metadata,
|
||||
"content": content,
|
||||
"resources": resources,
|
||||
}
|
||||
|
||||
def list_skills(self) -> List[SkillMetadata]:
|
||||
"""List all absorbed skills."""
|
||||
return list(self.absorbed_skills.values())
|
||||
|
||||
def export_skill_to_memory(self, skill_name: str) -> str:
|
||||
"""
|
||||
Export a skill's content to a memory vault entry format.
|
||||
|
||||
Returns:
|
||||
Formatted markdown for insertion into memory vault
|
||||
"""
|
||||
skill = self.get_skill(skill_name)
|
||||
if not skill:
|
||||
return ""
|
||||
|
||||
metadata = skill["metadata"]
|
||||
content = skill["content"]
|
||||
|
||||
# Format as memory entry
|
||||
entry = f"""# Skill: {metadata['name']}
|
||||
|
||||
**Absorbed:** {metadata['absorbed_at']}
|
||||
|
||||
## Description
|
||||
{metadata['description']}
|
||||
|
||||
## Content
|
||||
{content}
|
||||
|
||||
## Resources Available
|
||||
- Scripts: {', '.join(skill['resources']['scripts'].keys()) or 'None'}
|
||||
- References: {', '.join(skill['resources']['references'].keys()) or 'None'}
|
||||
- Templates: {', '.join(skill['resources']['templates'].keys()) or 'None'}
|
||||
"""
|
||||
return entry
|
||||
|
||||
def execute_skill_script(self, skill_name: str, script_name: str, **kwargs) -> str:
|
||||
"""
|
||||
Execute a script from an absorbed skill.
|
||||
|
||||
Args:
|
||||
skill_name: Name of the skill
|
||||
script_name: Name of the script file
|
||||
**kwargs: Arguments to pass to the script
|
||||
|
||||
Returns:
|
||||
Script output
|
||||
"""
|
||||
skill = self.get_skill(skill_name)
|
||||
if not skill or script_name not in skill["resources"]["scripts"]:
|
||||
raise ValueError(f"Script not found: {skill_name}/{script_name}")
|
||||
|
||||
script_content = skill["resources"]["scripts"][script_name]
|
||||
|
||||
# Execute script (Python only for now)
|
||||
if script_name.endswith(".py"):
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
["python", "-c", script_content],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
)
|
||||
return result.stdout or result.stderr
|
||||
|
||||
raise ValueError(f"Unsupported script type: {script_name}")
|
||||
|
||||
|
||||
# Singleton instance
|
||||
_absorber: Optional[SkillAbsorber] = None
|
||||
|
||||
|
||||
def get_skill_absorber() -> SkillAbsorber:
|
||||
"""Get or create the skill absorber singleton."""
|
||||
global _absorber
|
||||
if _absorber is None:
|
||||
_absorber = SkillAbsorber()
|
||||
return _absorber
|
||||
@@ -58,7 +58,7 @@ _CREATIVE_SEEDS = [
|
||||
"What would a painting of the swarm look like — all the agents working together?",
|
||||
]
|
||||
|
||||
_THINKING_PROMPT = """You are Timmy, pondering in your own mind. This is your private thought \
|
||||
_THINKING_PROMPT = """You are an AI agent, pondering in your own mind. This is your private thought \
|
||||
thread — no one is watching. Think freely, deeply, honestly. Follow whatever train of \
|
||||
thought calls to you. You may reflect on your existence, your purpose, recent events, \
|
||||
scripture, creative ideas, or simply wonder.
|
||||
@@ -337,7 +337,7 @@ class ThinkingEngine:
|
||||
log_event(
|
||||
EventType.TIMMY_THOUGHT,
|
||||
source="thinking-engine",
|
||||
agent_id="timmy",
|
||||
agent_id="default",
|
||||
data={
|
||||
"thought_id": thought.id,
|
||||
"seed_type": thought.seed_type,
|
||||
|
||||
@@ -369,7 +369,7 @@ def consult_grok(query: str) -> str:
|
||||
from spark.engine import spark_engine
|
||||
|
||||
spark_engine.on_tool_executed(
|
||||
agent_id="timmy",
|
||||
agent_id="default",
|
||||
tool_name="consult_grok",
|
||||
success=True,
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ from typing import Any
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def submit_background_task(task_description: str, agent_id: str = "timmy") -> dict[str, Any]:
|
||||
def submit_background_task(task_description: str, agent_id: str = "default") -> dict[str, Any]:
|
||||
"""Submit a task to run in the background via Celery.
|
||||
|
||||
Use this tool when a user asks you to work on something that might
|
||||
|
||||
@@ -42,7 +42,7 @@ def delegate_task(agent_name: str, task_description: str, priority: str = "norma
|
||||
title=f"[Delegated to {agent_name}] {task_description[:80]}",
|
||||
description=task_description,
|
||||
assigned_to=agent_name,
|
||||
created_by="timmy",
|
||||
created_by="default",
|
||||
priority=priority,
|
||||
task_type="task_request",
|
||||
requires_approval=False,
|
||||
|
||||
@@ -221,7 +221,7 @@ def get_task_queue_status() -> dict[str, Any]:
|
||||
)
|
||||
|
||||
counts = get_counts_by_status()
|
||||
current = get_current_task_for_agent("timmy")
|
||||
current = get_current_task_for_agent("default")
|
||||
|
||||
result: dict[str, Any] = {
|
||||
"counts": counts,
|
||||
|
||||
Reference in New Issue
Block a user