loki-mode 6.68.2 → 6.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/web-app/server.py +170 -0
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v6.
|
|
6
|
+
# Loki Mode v6.69.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -268,4 +268,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
268
268
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
269
269
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
270
270
|
|
|
271
|
-
**v6.
|
|
271
|
+
**v6.69.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.
|
|
1
|
+
6.69.0
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
package/web-app/server.py
CHANGED
|
@@ -1088,6 +1088,7 @@ class DevServerManager:
|
|
|
1088
1088
|
|
|
1089
1089
|
asyncio.create_task(self._monitor_output(session_id))
|
|
1090
1090
|
asyncio.create_task(self._monitor_backend_output(session_id))
|
|
1091
|
+
asyncio.create_task(self._continuous_log_monitor(session_id))
|
|
1091
1092
|
|
|
1092
1093
|
# Wait for either frontend or backend port (up to 30s)
|
|
1093
1094
|
for _ in range(60):
|
|
@@ -1238,6 +1239,7 @@ class DevServerManager:
|
|
|
1238
1239
|
self.servers[session_id] = server_info
|
|
1239
1240
|
|
|
1240
1241
|
asyncio.create_task(self._monitor_output(session_id))
|
|
1242
|
+
asyncio.create_task(self._continuous_log_monitor(session_id))
|
|
1241
1243
|
|
|
1242
1244
|
# For Docker projects, also start the service health monitor
|
|
1243
1245
|
if framework == "docker":
|
|
@@ -1408,6 +1410,170 @@ class DevServerManager:
|
|
|
1408
1410
|
if fe_proc and fe_proc.poll() is not None:
|
|
1409
1411
|
info["status"] = "error"
|
|
1410
1412
|
|
|
1413
|
+
async def _continuous_log_monitor(self, session_id: str) -> None:
|
|
1414
|
+
"""Continuously monitor dev server output for errors and auto-fix.
|
|
1415
|
+
|
|
1416
|
+
This runs alongside _monitor_output and watches for error patterns
|
|
1417
|
+
in the accumulated log lines. When errors are detected while the
|
|
1418
|
+
process is still running (not yet crashed), it triggers the AI
|
|
1419
|
+
provider to diagnose and fix the issue proactively.
|
|
1420
|
+
"""
|
|
1421
|
+
info = self.servers.get(session_id)
|
|
1422
|
+
if not info:
|
|
1423
|
+
return
|
|
1424
|
+
|
|
1425
|
+
last_error_check: float = 0
|
|
1426
|
+
error_cooldown = 30 # Don't check more than every 30 seconds
|
|
1427
|
+
|
|
1428
|
+
while True:
|
|
1429
|
+
info = self.servers.get(session_id)
|
|
1430
|
+
if not info:
|
|
1431
|
+
break
|
|
1432
|
+
|
|
1433
|
+
await asyncio.sleep(5)
|
|
1434
|
+
|
|
1435
|
+
now = time.time()
|
|
1436
|
+
if now - last_error_check < error_cooldown:
|
|
1437
|
+
continue
|
|
1438
|
+
|
|
1439
|
+
# Check recent output for error indicators
|
|
1440
|
+
recent_lines = info.get("output_lines", [])[-30:]
|
|
1441
|
+
if not recent_lines:
|
|
1442
|
+
continue
|
|
1443
|
+
|
|
1444
|
+
recent_text = "\n".join(recent_lines)
|
|
1445
|
+
|
|
1446
|
+
# Look for error indicators in the output
|
|
1447
|
+
error_indicators = [
|
|
1448
|
+
"Error:", "ERROR", "FATAL", "error:", "failed",
|
|
1449
|
+
"TypeError:", "SyntaxError:", "ModuleNotFoundError:",
|
|
1450
|
+
"Cannot find module", "ENOENT", "EACCES", "EADDRINUSE",
|
|
1451
|
+
"Connection refused", "Build failed", "Compilation failed",
|
|
1452
|
+
"npm ERR!", "pip install failed",
|
|
1453
|
+
]
|
|
1454
|
+
|
|
1455
|
+
has_error = any(indicator in recent_text for indicator in error_indicators)
|
|
1456
|
+
|
|
1457
|
+
# Also check if the process exited
|
|
1458
|
+
proc = info.get("process")
|
|
1459
|
+
process_dead = proc and proc.poll() is not None
|
|
1460
|
+
|
|
1461
|
+
if not has_error and not process_dead:
|
|
1462
|
+
continue
|
|
1463
|
+
|
|
1464
|
+
last_error_check = now
|
|
1465
|
+
|
|
1466
|
+
# Don't fix if already fixing (either from this monitor or _auto_fix)
|
|
1467
|
+
if info.get("_auto_fixing"):
|
|
1468
|
+
continue
|
|
1469
|
+
|
|
1470
|
+
# Don't fix if the _monitor_output auto-fix already handled it
|
|
1471
|
+
# (process exited = _monitor_output triggers _auto_fix, skip here)
|
|
1472
|
+
if process_dead:
|
|
1473
|
+
continue
|
|
1474
|
+
|
|
1475
|
+
project_dir = info.get("project_dir", ".")
|
|
1476
|
+
|
|
1477
|
+
logger.info("Error detected in dev server output for session %s, triggering AI fix", session_id)
|
|
1478
|
+
|
|
1479
|
+
# Broadcast to frontend that we are auto-fixing
|
|
1480
|
+
try:
|
|
1481
|
+
await _broadcast({
|
|
1482
|
+
"type": "auto_fix",
|
|
1483
|
+
"data": {
|
|
1484
|
+
"session_id": session_id,
|
|
1485
|
+
"status": "detecting",
|
|
1486
|
+
"message": "Error detected in dev server output. AI is analyzing...",
|
|
1487
|
+
}
|
|
1488
|
+
})
|
|
1489
|
+
except Exception:
|
|
1490
|
+
pass
|
|
1491
|
+
|
|
1492
|
+
# Gather context and trigger fix
|
|
1493
|
+
info["_auto_fixing"] = True
|
|
1494
|
+
try:
|
|
1495
|
+
# Get Docker context if applicable
|
|
1496
|
+
docker_ctx: dict = {}
|
|
1497
|
+
if info.get("framework") == "docker":
|
|
1498
|
+
try:
|
|
1499
|
+
docker_ctx = await _gather_docker_context(Path(project_dir))
|
|
1500
|
+
except Exception:
|
|
1501
|
+
pass
|
|
1502
|
+
|
|
1503
|
+
# Build AI prompt with full context
|
|
1504
|
+
compose_content = ""
|
|
1505
|
+
compose_file = Path(project_dir) / "docker-compose.yml"
|
|
1506
|
+
if not compose_file.exists():
|
|
1507
|
+
compose_file = Path(project_dir) / "docker-compose.yaml"
|
|
1508
|
+
if compose_file.exists():
|
|
1509
|
+
try:
|
|
1510
|
+
compose_content = compose_file.read_text(errors="replace")[:5000]
|
|
1511
|
+
except OSError:
|
|
1512
|
+
pass
|
|
1513
|
+
|
|
1514
|
+
error_lines = "\n".join(info.get("output_lines", [])[-50:])
|
|
1515
|
+
|
|
1516
|
+
fix_prompt = (
|
|
1517
|
+
"The dev server has errors. Analyze and fix them.\n\n"
|
|
1518
|
+
f"DEV SERVER OUTPUT (last 50 lines):\n{error_lines[:3000]}\n"
|
|
1519
|
+
)
|
|
1520
|
+
if docker_ctx:
|
|
1521
|
+
svc_status = docker_ctx.get("service_status", [])
|
|
1522
|
+
if svc_status:
|
|
1523
|
+
fix_prompt += f"\nDOCKER SERVICE STATUS: {json.dumps(svc_status)}\n"
|
|
1524
|
+
if compose_content:
|
|
1525
|
+
fix_prompt += f"\nDOCKER-COMPOSE.YML:\n{compose_content}\n"
|
|
1526
|
+
fix_prompt += (
|
|
1527
|
+
f"\nPROJECT DIRECTORY: {project_dir}\n\n"
|
|
1528
|
+
"INSTRUCTIONS:\n"
|
|
1529
|
+
"1. Analyze the error in the output above\n"
|
|
1530
|
+
"2. Fix the root cause (edit code, config, Dockerfile, etc.)\n"
|
|
1531
|
+
"3. The system will automatically restart/rebuild after your fix\n"
|
|
1532
|
+
"4. Make the fix work on any platform"
|
|
1533
|
+
)
|
|
1534
|
+
|
|
1535
|
+
# Trigger AI fix
|
|
1536
|
+
loki = _find_loki_cli()
|
|
1537
|
+
if loki:
|
|
1538
|
+
fix_env = {**os.environ, **_load_secrets()}
|
|
1539
|
+
fix_env["LOKI_MAX_ITERATIONS"] = "5"
|
|
1540
|
+
fix_env["LOKI_AUTO_FIX"] = "true"
|
|
1541
|
+
|
|
1542
|
+
await asyncio.to_thread(
|
|
1543
|
+
subprocess.run,
|
|
1544
|
+
[loki, "quick", fix_prompt],
|
|
1545
|
+
capture_output=True, text=True, cwd=project_dir, timeout=300,
|
|
1546
|
+
env=fix_env,
|
|
1547
|
+
)
|
|
1548
|
+
|
|
1549
|
+
# Rebuild if Docker
|
|
1550
|
+
if info.get("framework") == "docker":
|
|
1551
|
+
await asyncio.to_thread(
|
|
1552
|
+
subprocess.run,
|
|
1553
|
+
["docker", "compose", "up", "-d", "--build", "--no-deps"],
|
|
1554
|
+
capture_output=True, cwd=project_dir, timeout=120,
|
|
1555
|
+
)
|
|
1556
|
+
|
|
1557
|
+
# Broadcast fix complete
|
|
1558
|
+
try:
|
|
1559
|
+
await _broadcast({
|
|
1560
|
+
"type": "auto_fix",
|
|
1561
|
+
"data": {
|
|
1562
|
+
"session_id": session_id,
|
|
1563
|
+
"status": "completed",
|
|
1564
|
+
"message": "AI fix applied. Rebuilding...",
|
|
1565
|
+
}
|
|
1566
|
+
})
|
|
1567
|
+
except Exception:
|
|
1568
|
+
pass
|
|
1569
|
+
except Exception as e:
|
|
1570
|
+
logger.error("Continuous log monitor fix failed: %s", e)
|
|
1571
|
+
finally:
|
|
1572
|
+
# Re-fetch info in case it was replaced during the fix
|
|
1573
|
+
info = self.servers.get(session_id)
|
|
1574
|
+
if info:
|
|
1575
|
+
info["_auto_fixing"] = False
|
|
1576
|
+
|
|
1411
1577
|
async def _auto_fix(self, session_id: str, error_context: str) -> None:
|
|
1412
1578
|
"""Auto-fix a crashed dev server by invoking loki quick with the error."""
|
|
1413
1579
|
info = self.servers.get(session_id)
|
|
@@ -4305,6 +4471,10 @@ async def get_preview_info(session_id: str) -> JSONResponse:
|
|
|
4305
4471
|
info["preview_url"] = None
|
|
4306
4472
|
info["entry_file"] = None
|
|
4307
4473
|
|
|
4474
|
+
# Indicate whether AI-driven continuous log monitoring is active for this session
|
|
4475
|
+
server_info = dev_server_manager.servers.get(session_id)
|
|
4476
|
+
info["ai_detected"] = server_info is not None
|
|
4477
|
+
|
|
4308
4478
|
return JSONResponse(content=info)
|
|
4309
4479
|
|
|
4310
4480
|
|