Based on claude-code-tools TmuxCLIController, this refactor: - Added DockerTmuxController class for robust tmux session management - Implements send_keys() with configurable delay_enter - Implements capture_pane() for output retrieval - Implements wait_for_prompt() for pattern-based completion detection - Implements wait_for_idle() for content-hash-based idle detection - Implements wait_for_shell_prompt() for shell prompt detection Also includes workflow improvements: - Pre-task git snapshot before agent execution - Post-task commit protocol in agent guidelines Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
170 lines
5.3 KiB
Python
Executable File
170 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Luzia Research Agent Loop - Interactive Research Task Processing
|
|
|
|
Continuously monitors incoming research tasks and routes them intelligently
|
|
based on security, speed, and complexity filters.
|
|
|
|
Can run in:
|
|
- Interactive mode: Process individual tasks
|
|
- Loop mode: Monitor queue continuously (default)
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
import time
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
sys.path.insert(0, '/opt/server-agents/orchestrator/lib')
|
|
|
|
from research_agent import LuziaResearchAgent, TaskFilter, ToolRouter
|
|
|
|
|
|
def run_interactive():
|
|
"""Interactive mode: process user input"""
|
|
agent = LuziaResearchAgent()
|
|
|
|
print("\n" + "=" * 70)
|
|
print("🔬 LUZIA RESEARCH AGENT - INTERACTIVE MODE")
|
|
print("=" * 70)
|
|
print("\nEnter research tasks to analyze. Type 'exit' to quit.\n")
|
|
|
|
while True:
|
|
try:
|
|
task = input("📋 Research task: ").strip()
|
|
|
|
if task.lower() in ['exit', 'quit', 'q']:
|
|
print("✅ Exiting research agent")
|
|
break
|
|
|
|
if not task:
|
|
print("⚠️ Empty task, try again\n")
|
|
continue
|
|
|
|
# Process the task
|
|
result = agent.process_research_task(task)
|
|
|
|
print(f"\n{result['analysis']['routing_summary']}")
|
|
|
|
if result['clarification']:
|
|
print("❓ Clarification Questions:")
|
|
for q in result['clarification']['questions']:
|
|
print(f" {q}")
|
|
|
|
print()
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n✅ Exiting research agent")
|
|
break
|
|
except Exception as e:
|
|
print(f"❌ Error: {e}\n")
|
|
|
|
|
|
def run_continuous():
|
|
"""Continuous loop mode: monitor and process tasks"""
|
|
agent = LuziaResearchAgent()
|
|
queue_file = Path("/opt/server-agents/state/research-queue.json")
|
|
|
|
agent.log("🔬 Luzia Research Agent started (continuous mode)")
|
|
|
|
while True:
|
|
try:
|
|
# Check if queue file exists
|
|
if not queue_file.exists():
|
|
time.sleep(10)
|
|
continue
|
|
|
|
# Read queue
|
|
with open(queue_file) as f:
|
|
queue_data = json.load(f)
|
|
|
|
pending = queue_data.get('pending', [])
|
|
if not pending:
|
|
time.sleep(10)
|
|
continue
|
|
|
|
# Process first pending task
|
|
task_entry = pending[0]
|
|
task_id = task_entry.get('id', str(uuid.uuid4()))
|
|
task = task_entry.get('task', '')
|
|
|
|
agent.log(f"📥 Processing task {task_id}: {task[:50]}...")
|
|
|
|
# Analyze and route
|
|
result = agent.process_research_task(task)
|
|
|
|
# Move to processed
|
|
queue_data['pending'].pop(0)
|
|
queue_data['processed'] = queue_data.get('processed', [])
|
|
queue_data['processed'].append({
|
|
'id': task_id,
|
|
'task': task,
|
|
'timestamp': datetime.now().isoformat(),
|
|
'analysis': result['analysis'],
|
|
'status': result['status'],
|
|
})
|
|
|
|
# Write back
|
|
with open(queue_file, 'w') as f:
|
|
json.dump(queue_data, f, indent=2)
|
|
|
|
agent.log(f"✅ Processed: {task[:50]}... → {result['analysis']['recommended_tool']}")
|
|
|
|
# Brief sleep between tasks
|
|
time.sleep(2)
|
|
|
|
except KeyboardInterrupt:
|
|
agent.log("🛑 Research agent stopped by user")
|
|
break
|
|
except Exception as e:
|
|
agent.log(f"❌ Error in loop: {e}")
|
|
time.sleep(5)
|
|
|
|
|
|
def show_demo():
|
|
"""Show demonstration of smart filtering"""
|
|
agent = LuziaResearchAgent()
|
|
|
|
demo_tasks = [
|
|
("quick answer: what is REST?", "Simple question, no urgency"),
|
|
("urgent critical vulnerability in auth system", "Critical security, needs immediate review"),
|
|
("research distributed caching approaches and tradeoffs", "Complex decision, needs exploration"),
|
|
("debug: zen-proxy max_tokens not working", "Error diagnosis, straightforward"),
|
|
("design: REST vs GraphQL for new API", "Architecture decision, multiple perspectives"),
|
|
]
|
|
|
|
print("\n" + "=" * 80)
|
|
print("🔬 LUZIA RESEARCH AGENT - SMART FILTER DEMONSTRATION")
|
|
print("=" * 80)
|
|
|
|
for task, context in demo_tasks:
|
|
print(f"\n📋 Task: {task}")
|
|
print(f" Context: {context}")
|
|
print(" " + "-" * 70)
|
|
|
|
analysis = agent.analyze_task(task)
|
|
print(f" Security: {analysis['security']}")
|
|
print(f" Speed: {analysis['speed']}")
|
|
print(f" Complexity: {analysis['complexity']}")
|
|
print(f" → Tool: {analysis['recommended_tool']}")
|
|
print(f" Reason: {analysis['reasoning']}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) > 1:
|
|
if sys.argv[1] == "--demo":
|
|
show_demo()
|
|
elif sys.argv[1] == "--interactive":
|
|
run_interactive()
|
|
elif sys.argv[1] == "--continuous":
|
|
run_continuous()
|
|
else:
|
|
print("Usage:")
|
|
print(" luzia_research_agent.py --demo Show demonstration")
|
|
print(" luzia_research_agent.py --interactive Interactive mode")
|
|
print(" luzia_research_agent.py --continuous Continuous queue monitoring")
|
|
else:
|
|
# Default: interactive mode
|
|
run_interactive()
|