loki-mode 5.52.0 → 5.52.1
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/README.md +2 -54
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/hooks/validate-bash.sh +5 -2
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/mcp/server.py +230 -0
- package/package.json +1 -1
- /package/dashboard/{secrets.py → app_secrets.py} +0 -0
package/README.md
CHANGED
|
@@ -473,60 +473,8 @@ Go get coffee. It'll be deployed when you get back.
|
|
|
473
473
|
|
|
474
474
|
## Architecture
|
|
475
475
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
PRD["PRD Document"] --> REASON
|
|
479
|
-
|
|
480
|
-
subgraph RARVC["RARV+C Cycle"]
|
|
481
|
-
direction TB
|
|
482
|
-
REASON["1. Reason"] --> ACT["2. Act"]
|
|
483
|
-
ACT --> REFLECT["3. Reflect"]
|
|
484
|
-
REFLECT --> VERIFY["4. Verify"]
|
|
485
|
-
VERIFY -->|"pass"| COMPOUND["5. Compound"]
|
|
486
|
-
VERIFY -->|"fail"| REASON
|
|
487
|
-
COMPOUND --> REASON
|
|
488
|
-
end
|
|
489
|
-
|
|
490
|
-
subgraph PROVIDERS["Provider Layer"]
|
|
491
|
-
CLAUDE["Claude Code<br/>(full features)"]
|
|
492
|
-
CODEX["Codex CLI<br/>(degraded)"]
|
|
493
|
-
GEMINI["Gemini CLI<br/>(degraded)"]
|
|
494
|
-
end
|
|
495
|
-
|
|
496
|
-
ACT --> PROVIDERS
|
|
497
|
-
|
|
498
|
-
subgraph AGENTS["Agent Swarms (41 types)"]
|
|
499
|
-
ENG["Engineering (8)"]
|
|
500
|
-
OPS["Operations (8)"]
|
|
501
|
-
BIZ["Business (8)"]
|
|
502
|
-
DATA["Data (3)"]
|
|
503
|
-
PROD["Product (3)"]
|
|
504
|
-
GROWTH["Growth (4)"]
|
|
505
|
-
REVIEW["Review (3)"]
|
|
506
|
-
ORCH["Orchestration (4)"]
|
|
507
|
-
end
|
|
508
|
-
|
|
509
|
-
PROVIDERS --> AGENTS
|
|
510
|
-
|
|
511
|
-
subgraph INFRA["Infrastructure"]
|
|
512
|
-
DASHBOARD["Dashboard<br/>(FastAPI + Web UI)<br/>TLS/HTTPS, OIDC, RBAC"]
|
|
513
|
-
MEMORY["Memory System<br/>(Episodic/Semantic/Procedural)"]
|
|
514
|
-
COUNCIL["Completion Council<br/>(3-member voting)"]
|
|
515
|
-
QUEUE["Task Queue<br/>(.loki/queue/)"]
|
|
516
|
-
METRICS["Metrics Export<br/>(Prometheus/OpenMetrics)"]
|
|
517
|
-
AUDIT["Audit Trail<br/>(SHA-256 integrity chain)"]
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
AGENTS --> QUEUE
|
|
521
|
-
VERIFY --> COUNCIL
|
|
522
|
-
REFLECT --> MEMORY
|
|
523
|
-
COMPOUND --> MEMORY
|
|
524
|
-
AGENTS --> AUDIT
|
|
525
|
-
DASHBOARD -.->|"reads"| QUEUE
|
|
526
|
-
DASHBOARD -.->|"reads"| MEMORY
|
|
527
|
-
DASHBOARD -.->|"reads"| AUDIT
|
|
528
|
-
DASHBOARD -.->|"exposes"| METRICS
|
|
529
|
-
```
|
|
476
|
+
<img width="6961" height="6302" alt="architecture" src="https://github.com/user-attachments/assets/d9954dd2-5cb6-4b1c-8cd2-67f68141dffa" />
|
|
477
|
+
|
|
530
478
|
|
|
531
479
|
**Key components:**
|
|
532
480
|
- **RARV+C Cycle** -- Reason, Act, Reflect, Verify, Compound. Every iteration follows this loop. Failed verification triggers retry from Reason.
|
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 v5.52.
|
|
6
|
+
# Loki Mode v5.52.1
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -263,4 +263,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
263
263
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
264
264
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
265
265
|
|
|
266
|
-
**v5.52.
|
|
266
|
+
**v5.52.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.52.
|
|
1
|
+
5.52.1
|
|
@@ -39,6 +39,9 @@ BLOCKED_PATTERNS=(
|
|
|
39
39
|
"rm .*\.loki/session\.lock"
|
|
40
40
|
"> \.loki/council/"
|
|
41
41
|
"> \.loki/config\.yaml"
|
|
42
|
+
# Fork bomb patterns
|
|
43
|
+
":\(\)\{.*\|.*&"
|
|
44
|
+
":\(\) *\{.*\|.*&"
|
|
42
45
|
)
|
|
43
46
|
|
|
44
47
|
# Safe path patterns that override blocked pattern matches
|
|
@@ -59,7 +62,7 @@ for pattern in "${BLOCKED_PATTERNS[@]}"; do
|
|
|
59
62
|
fi
|
|
60
63
|
done
|
|
61
64
|
"$is_safe" && continue
|
|
62
|
-
printf '%s' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Blocked: potentially dangerous command pattern detected"}}'
|
|
65
|
+
printf '%s' '{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Blocked: potentially dangerous command pattern detected"}}'
|
|
63
66
|
exit 2
|
|
64
67
|
fi
|
|
65
68
|
done
|
|
@@ -71,6 +74,6 @@ printf '%s' "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"command\":$(ech
|
|
|
71
74
|
echo >> "$LOG_DIR/bash-audit.jsonl"
|
|
72
75
|
|
|
73
76
|
# Allow command
|
|
74
|
-
printf '%s' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
|
|
77
|
+
printf '%s' '{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}}'
|
|
75
78
|
|
|
76
79
|
exit 0
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -46,7 +46,7 @@ from .models import (
|
|
|
46
46
|
from . import registry
|
|
47
47
|
from . import auth
|
|
48
48
|
from . import audit
|
|
49
|
-
from . import
|
|
49
|
+
from . import app_secrets as secrets_mod
|
|
50
50
|
from . import telemetry as _telemetry
|
|
51
51
|
from .control import atomic_write_json
|
|
52
52
|
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED
package/mcp/server.py
CHANGED
|
@@ -979,6 +979,236 @@ async def get_pending_tasks() -> str:
|
|
|
979
979
|
return json.dumps({"error": "Access denied", "pending_tasks": [], "count": 0})
|
|
980
980
|
|
|
981
981
|
|
|
982
|
+
# ============================================================
|
|
983
|
+
# ENTERPRISE TOOLS (P0-1)
|
|
984
|
+
# ============================================================
|
|
985
|
+
|
|
986
|
+
@mcp.tool()
|
|
987
|
+
async def loki_start_project(prd_content: str = "", prd_path: str = "") -> str:
|
|
988
|
+
"""
|
|
989
|
+
Start a new Loki Mode project from a PRD.
|
|
990
|
+
|
|
991
|
+
Args:
|
|
992
|
+
prd_content: Inline PRD content (takes priority over prd_path)
|
|
993
|
+
prd_path: Path to a PRD file on disk
|
|
994
|
+
|
|
995
|
+
Returns:
|
|
996
|
+
JSON with project initialization status
|
|
997
|
+
"""
|
|
998
|
+
_emit_tool_event_async('loki_start_project', 'start', parameters={'prd_path': prd_path})
|
|
999
|
+
try:
|
|
1000
|
+
content = prd_content
|
|
1001
|
+
if not content and prd_path:
|
|
1002
|
+
resolved = safe_path_join('.', prd_path)
|
|
1003
|
+
if os.path.exists(resolved):
|
|
1004
|
+
with safe_open(resolved, 'r') as f:
|
|
1005
|
+
content = f.read()
|
|
1006
|
+
else:
|
|
1007
|
+
return json.dumps({"error": f"PRD file not found: {prd_path}"})
|
|
1008
|
+
|
|
1009
|
+
if not content:
|
|
1010
|
+
return json.dumps({"error": "No PRD content or path provided"})
|
|
1011
|
+
|
|
1012
|
+
# Initialize project state
|
|
1013
|
+
os.makedirs('.loki/state', exist_ok=True)
|
|
1014
|
+
project = {
|
|
1015
|
+
"status": "initialized",
|
|
1016
|
+
"prd_length": len(content),
|
|
1017
|
+
"prd_path": prd_path or "inline",
|
|
1018
|
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
1019
|
+
}
|
|
1020
|
+
state_path = safe_path_join('.loki', 'state', 'project.json')
|
|
1021
|
+
with safe_open(state_path, 'w') as f:
|
|
1022
|
+
json.dump(project, f, indent=2)
|
|
1023
|
+
|
|
1024
|
+
_emit_tool_event_async('loki_start_project', 'complete', result_status='success')
|
|
1025
|
+
return json.dumps({"success": True, **project})
|
|
1026
|
+
except PathTraversalError as e:
|
|
1027
|
+
return json.dumps({"error": f"Access denied: {e}"})
|
|
1028
|
+
except Exception as e:
|
|
1029
|
+
logger.error(f"Start project failed: {e}")
|
|
1030
|
+
_emit_tool_event_async('loki_start_project', 'complete', result_status='error', error=str(e))
|
|
1031
|
+
return json.dumps({"error": str(e)})
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
@mcp.tool()
|
|
1035
|
+
async def loki_project_status() -> str:
|
|
1036
|
+
"""
|
|
1037
|
+
Get the current project status including RARV cycle state, agent activity, and task progress.
|
|
1038
|
+
|
|
1039
|
+
Returns:
|
|
1040
|
+
JSON with project status, phase, iteration, agents, and task counts
|
|
1041
|
+
"""
|
|
1042
|
+
_emit_tool_event_async('loki_project_status', 'start', parameters={})
|
|
1043
|
+
try:
|
|
1044
|
+
status = {}
|
|
1045
|
+
|
|
1046
|
+
# Read orchestrator state
|
|
1047
|
+
orch_path = safe_path_join('.loki', 'state', 'orchestrator.json')
|
|
1048
|
+
if os.path.exists(orch_path):
|
|
1049
|
+
with safe_open(orch_path, 'r') as f:
|
|
1050
|
+
status["orchestrator"] = json.load(f)
|
|
1051
|
+
|
|
1052
|
+
# Read project state
|
|
1053
|
+
proj_path = safe_path_join('.loki', 'state', 'project.json')
|
|
1054
|
+
if os.path.exists(proj_path):
|
|
1055
|
+
with safe_open(proj_path, 'r') as f:
|
|
1056
|
+
status["project"] = json.load(f)
|
|
1057
|
+
|
|
1058
|
+
# Read task queue summary
|
|
1059
|
+
queue_path = safe_path_join('.loki', 'state', 'task-queue.json')
|
|
1060
|
+
if os.path.exists(queue_path):
|
|
1061
|
+
with safe_open(queue_path, 'r') as f:
|
|
1062
|
+
queue = json.load(f)
|
|
1063
|
+
tasks = queue.get("tasks", [])
|
|
1064
|
+
status["tasks"] = {
|
|
1065
|
+
"total": len(tasks),
|
|
1066
|
+
"pending": sum(1 for t in tasks if t.get("status") == "pending"),
|
|
1067
|
+
"in_progress": sum(1 for t in tasks if t.get("status") == "in-progress"),
|
|
1068
|
+
"completed": sum(1 for t in tasks if t.get("status") == "completed"),
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if not status:
|
|
1072
|
+
status = {"status": "no_project", "message": "No active project found"}
|
|
1073
|
+
|
|
1074
|
+
_emit_tool_event_async('loki_project_status', 'complete', result_status='success')
|
|
1075
|
+
return json.dumps(status, default=str)
|
|
1076
|
+
except PathTraversalError:
|
|
1077
|
+
return json.dumps({"error": "Access denied"})
|
|
1078
|
+
except Exception as e:
|
|
1079
|
+
logger.error(f"Project status failed: {e}")
|
|
1080
|
+
_emit_tool_event_async('loki_project_status', 'complete', result_status='error', error=str(e))
|
|
1081
|
+
return json.dumps({"error": str(e)})
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
@mcp.tool()
|
|
1085
|
+
async def loki_agent_metrics() -> str:
|
|
1086
|
+
"""
|
|
1087
|
+
Get agent metrics including token usage, task completion rates, and timing.
|
|
1088
|
+
|
|
1089
|
+
Returns:
|
|
1090
|
+
JSON with per-agent metrics and aggregates
|
|
1091
|
+
"""
|
|
1092
|
+
_emit_tool_event_async('loki_agent_metrics', 'start', parameters={})
|
|
1093
|
+
try:
|
|
1094
|
+
metrics = {"agents": [], "aggregate": {}}
|
|
1095
|
+
|
|
1096
|
+
# Read efficiency metrics
|
|
1097
|
+
metrics_dir = safe_path_join('.loki', 'metrics', 'efficiency')
|
|
1098
|
+
if os.path.isdir(metrics_dir):
|
|
1099
|
+
for fname in os.listdir(metrics_dir):
|
|
1100
|
+
if fname.endswith('.json'):
|
|
1101
|
+
fpath = os.path.join(metrics_dir, fname)
|
|
1102
|
+
with open(fpath, 'r') as f:
|
|
1103
|
+
metrics["agents"].append(json.load(f))
|
|
1104
|
+
|
|
1105
|
+
# Read token economics
|
|
1106
|
+
econ_path = safe_path_join('.loki', 'metrics', 'token-economics.json')
|
|
1107
|
+
if os.path.exists(econ_path):
|
|
1108
|
+
with safe_open(econ_path, 'r') as f:
|
|
1109
|
+
metrics["token_economics"] = json.load(f)
|
|
1110
|
+
|
|
1111
|
+
metrics["agent_count"] = len(metrics["agents"])
|
|
1112
|
+
_emit_tool_event_async('loki_agent_metrics', 'complete', result_status='success')
|
|
1113
|
+
return json.dumps(metrics, default=str)
|
|
1114
|
+
except PathTraversalError:
|
|
1115
|
+
return json.dumps({"error": "Access denied"})
|
|
1116
|
+
except Exception as e:
|
|
1117
|
+
logger.error(f"Agent metrics failed: {e}")
|
|
1118
|
+
_emit_tool_event_async('loki_agent_metrics', 'complete', result_status='error', error=str(e))
|
|
1119
|
+
return json.dumps({"error": str(e)})
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
@mcp.tool()
|
|
1123
|
+
async def loki_checkpoint_restore(checkpoint_id: str = "") -> str:
|
|
1124
|
+
"""
|
|
1125
|
+
List available checkpoints or restore project state from a specific checkpoint.
|
|
1126
|
+
|
|
1127
|
+
Args:
|
|
1128
|
+
checkpoint_id: ID of checkpoint to restore (empty = list all)
|
|
1129
|
+
|
|
1130
|
+
Returns:
|
|
1131
|
+
JSON with available checkpoints or restoration result
|
|
1132
|
+
"""
|
|
1133
|
+
_emit_tool_event_async('loki_checkpoint_restore', 'start', parameters={'checkpoint_id': checkpoint_id})
|
|
1134
|
+
try:
|
|
1135
|
+
cp_dir = safe_path_join('.loki', 'state', 'checkpoints')
|
|
1136
|
+
if not os.path.isdir(cp_dir):
|
|
1137
|
+
return json.dumps({"checkpoints": [], "message": "No checkpoints directory"})
|
|
1138
|
+
|
|
1139
|
+
checkpoints = []
|
|
1140
|
+
for fname in sorted(os.listdir(cp_dir)):
|
|
1141
|
+
if fname.endswith('.json'):
|
|
1142
|
+
fpath = os.path.join(cp_dir, fname)
|
|
1143
|
+
with open(fpath, 'r') as f:
|
|
1144
|
+
cp = json.load(f)
|
|
1145
|
+
cp["id"] = fname.replace('.json', '')
|
|
1146
|
+
checkpoints.append(cp)
|
|
1147
|
+
|
|
1148
|
+
if not checkpoint_id:
|
|
1149
|
+
_emit_tool_event_async('loki_checkpoint_restore', 'complete', result_status='success')
|
|
1150
|
+
return json.dumps({"checkpoints": checkpoints, "count": len(checkpoints)})
|
|
1151
|
+
|
|
1152
|
+
# Find and restore specific checkpoint
|
|
1153
|
+
target = next((c for c in checkpoints if c["id"] == checkpoint_id), None)
|
|
1154
|
+
if not target:
|
|
1155
|
+
return json.dumps({"error": f"Checkpoint not found: {checkpoint_id}"})
|
|
1156
|
+
|
|
1157
|
+
# Write checkpoint state as current state
|
|
1158
|
+
state_path = safe_path_join('.loki', 'state', 'orchestrator.json')
|
|
1159
|
+
with safe_open(state_path, 'w') as f:
|
|
1160
|
+
json.dump(target, f, indent=2)
|
|
1161
|
+
|
|
1162
|
+
_emit_tool_event_async('loki_checkpoint_restore', 'complete', result_status='success')
|
|
1163
|
+
return json.dumps({"restored": True, "checkpoint_id": checkpoint_id})
|
|
1164
|
+
except PathTraversalError:
|
|
1165
|
+
return json.dumps({"error": "Access denied"})
|
|
1166
|
+
except Exception as e:
|
|
1167
|
+
logger.error(f"Checkpoint restore failed: {e}")
|
|
1168
|
+
_emit_tool_event_async('loki_checkpoint_restore', 'complete', result_status='error', error=str(e))
|
|
1169
|
+
return json.dumps({"error": str(e)})
|
|
1170
|
+
|
|
1171
|
+
|
|
1172
|
+
@mcp.tool()
|
|
1173
|
+
async def loki_quality_report() -> str:
|
|
1174
|
+
"""
|
|
1175
|
+
Get quality gate results including blind review scores, council verdicts, and test coverage.
|
|
1176
|
+
|
|
1177
|
+
Returns:
|
|
1178
|
+
JSON with quality gate status, review results, and coverage metrics
|
|
1179
|
+
"""
|
|
1180
|
+
_emit_tool_event_async('loki_quality_report', 'start', parameters={})
|
|
1181
|
+
try:
|
|
1182
|
+
report = {"gates": [], "council": None, "coverage": None}
|
|
1183
|
+
|
|
1184
|
+
# Read quality gate results
|
|
1185
|
+
gates_path = safe_path_join('.loki', 'state', 'quality-gates.json')
|
|
1186
|
+
if os.path.exists(gates_path):
|
|
1187
|
+
with safe_open(gates_path, 'r') as f:
|
|
1188
|
+
report["gates"] = json.load(f)
|
|
1189
|
+
|
|
1190
|
+
# Read council results
|
|
1191
|
+
council_path = safe_path_join('.loki', 'state', 'council-results.json')
|
|
1192
|
+
if os.path.exists(council_path):
|
|
1193
|
+
with safe_open(council_path, 'r') as f:
|
|
1194
|
+
report["council"] = json.load(f)
|
|
1195
|
+
|
|
1196
|
+
# Read coverage
|
|
1197
|
+
coverage_path = safe_path_join('.loki', 'metrics', 'coverage.json')
|
|
1198
|
+
if os.path.exists(coverage_path):
|
|
1199
|
+
with safe_open(coverage_path, 'r') as f:
|
|
1200
|
+
report["coverage"] = json.load(f)
|
|
1201
|
+
|
|
1202
|
+
_emit_tool_event_async('loki_quality_report', 'complete', result_status='success')
|
|
1203
|
+
return json.dumps(report, default=str)
|
|
1204
|
+
except PathTraversalError:
|
|
1205
|
+
return json.dumps({"error": "Access denied"})
|
|
1206
|
+
except Exception as e:
|
|
1207
|
+
logger.error(f"Quality report failed: {e}")
|
|
1208
|
+
_emit_tool_event_async('loki_quality_report', 'complete', result_status='error', error=str(e))
|
|
1209
|
+
return json.dumps({"error": str(e)})
|
|
1210
|
+
|
|
1211
|
+
|
|
982
1212
|
# ============================================================
|
|
983
1213
|
# PROMPTS - Pre-built prompt templates
|
|
984
1214
|
# ============================================================
|
package/package.json
CHANGED
|
File without changes
|