loki-mode 5.49.4 → 5.51.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/README.md +2 -2
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +13 -0
- package/autonomy/council-v2.sh +302 -0
- package/autonomy/loki +92 -64
- package/autonomy/run.sh +228 -16
- package/dashboard/__init__.py +1 -1
- package/dashboard/api_keys.py +363 -0
- package/dashboard/api_v2.py +596 -0
- package/dashboard/models.py +88 -0
- package/dashboard/requirements.txt +8 -7
- package/dashboard/runs.py +384 -0
- package/dashboard/server.py +4 -0
- package/dashboard/tenants.py +281 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/alternative-installations.md +3 -3
- package/docs/enterprise/architecture.md +324 -0
- package/docs/enterprise/integration-cookbook.md +485 -0
- package/docs/enterprise/migration.md +268 -0
- package/docs/enterprise/performance.md +290 -0
- package/docs/enterprise/sdk-guide.md +557 -0
- package/docs/enterprise/security.md +390 -0
- package/mcp/__init__.py +1 -1
- package/memory/cross_project.py +108 -0
- package/memory/knowledge_graph.py +197 -0
- package/memory/rag_injector.py +81 -0
- package/package.json +4 -3
- package/src/audit/compliance.js +226 -0
- package/src/audit/index.js +147 -0
- package/src/audit/log.js +153 -0
- package/src/audit/residency.js +150 -0
- package/src/audit/subscriber.js +100 -0
- package/src/integrations/adapter.js +128 -0
- package/src/integrations/github/action-handler.js +267 -0
- package/src/integrations/github/reporter.js +485 -0
- package/src/integrations/github/templates/execution-summary.md +16 -0
- package/src/integrations/github/templates/quality-report.md +18 -0
- package/src/integrations/jira/api-client.js +152 -0
- package/src/integrations/jira/epic-converter.js +157 -0
- package/src/integrations/jira/index.js +45 -0
- package/src/integrations/jira/sync-manager.js +143 -0
- package/src/integrations/jira/webhook-handler.js +101 -0
- package/src/integrations/linear/client.js +382 -0
- package/src/integrations/linear/config.js +168 -0
- package/src/integrations/linear/sync.js +305 -0
- package/src/integrations/slack/adapter.js +142 -0
- package/src/integrations/slack/blocks.js +87 -0
- package/src/integrations/slack/commands.js +58 -0
- package/src/integrations/slack/index.js +2 -0
- package/src/integrations/slack/webhook-handler.js +32 -0
- package/src/integrations/sync-subscriber.js +249 -0
- package/src/integrations/teams/adapter.js +154 -0
- package/src/integrations/teams/cards.js +165 -0
- package/src/integrations/teams/index.js +2 -0
- package/src/integrations/teams/webhook.js +15 -0
- package/src/observability/index.js +144 -0
- package/src/observability/metrics.js +181 -0
- package/src/observability/otel-bridge.js +297 -0
- package/src/observability/otel.js +662 -0
- package/src/observability/spans.js +199 -0
- package/src/policies/approval.js +366 -0
- package/src/policies/check.js +38 -0
- package/src/policies/cost.js +296 -0
- package/src/policies/engine.js +615 -0
- package/src/policies/index.js +216 -0
- package/src/policies/types.js +303 -0
- package/src/protocols/a2a/agent-card.js +101 -0
- package/src/protocols/a2a/artifacts.js +61 -0
- package/src/protocols/a2a/client.js +167 -0
- package/src/protocols/a2a/index.js +40 -0
- package/src/protocols/a2a/streaming.js +120 -0
- package/src/protocols/a2a/task-manager.js +183 -0
- package/src/protocols/auth/oauth.js +220 -0
- package/src/protocols/mcp-circuit-breaker.js +165 -0
- package/src/protocols/mcp-client-manager.js +264 -0
- package/src/protocols/mcp-client.js +383 -0
- package/src/protocols/mcp-server.js +367 -0
- package/src/protocols/resources/continuity.js +49 -0
- package/src/protocols/resources/memory.js +128 -0
- package/src/protocols/tools/agent-metrics.js +164 -0
- package/src/protocols/tools/checkpoint-restore.js +225 -0
- package/src/protocols/tools/project-status.js +110 -0
- package/src/protocols/tools/quality-report.js +141 -0
- package/src/protocols/tools/start-project.js +165 -0
- package/src/protocols/transport/sse.js +191 -0
- package/src/protocols/transport/stdio.js +85 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[]()
|
|
12
12
|
[](benchmarks/)
|
|
13
13
|
|
|
14
|
-
**Current Version: v5.
|
|
14
|
+
**Current Version: v5.51.0**
|
|
15
15
|
|
|
16
16
|
**[Autonomi](https://www.autonomi.dev/)** | **[Documentation](https://www.autonomi.dev/docs)** | **[GitHub](https://github.com/asklokesh/loki-mode)**
|
|
17
17
|
|
|
@@ -85,7 +85,7 @@ gemini
|
|
|
85
85
|
### Verify Installation
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
loki --version # Should print 5.
|
|
88
|
+
loki --version # Should print 5.51.0
|
|
89
89
|
loki doctor # Check skill symlinks and provider availability
|
|
90
90
|
```
|
|
91
91
|
|
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.
|
|
6
|
+
# Loki Mode v5.51.0
|
|
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.
|
|
266
|
+
**v5.51.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.51.0
|
|
@@ -216,6 +216,19 @@ council_vote() {
|
|
|
216
216
|
local vote_dir="$COUNCIL_STATE_DIR/votes/iteration-$ITERATION_COUNT"
|
|
217
217
|
mkdir -p "$vote_dir"
|
|
218
218
|
|
|
219
|
+
# Council v2: true blind review with sycophancy detection
|
|
220
|
+
if [ "${LOKI_COUNCIL_VERSION:-1}" = "2" ]; then
|
|
221
|
+
# Source council v2 if not already loaded
|
|
222
|
+
if ! type council_v2_vote &>/dev/null; then
|
|
223
|
+
source "${BASH_SOURCE[0]%/*}/council-v2.sh"
|
|
224
|
+
fi
|
|
225
|
+
# Gather evidence first (shared function)
|
|
226
|
+
local evidence_file="$vote_dir/evidence.md"
|
|
227
|
+
council_gather_evidence "$evidence_file" "$prd_path"
|
|
228
|
+
council_v2_vote "$prd_path" "$evidence_file" "$vote_dir" "${ITERATION_COUNT:-0}"
|
|
229
|
+
return $?
|
|
230
|
+
fi
|
|
231
|
+
|
|
219
232
|
log_header "COMPLETION COUNCIL - Iteration $ITERATION_COUNT"
|
|
220
233
|
log_info "Convening ${COUNCIL_SIZE}-member council..."
|
|
221
234
|
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#===============================================================================
|
|
3
|
+
# Council v2 - True blind review with sycophancy detection
|
|
4
|
+
#
|
|
5
|
+
# Upgraded council review system that provides:
|
|
6
|
+
# 1. True blind review (isolated evidence packages, no cross-contamination)
|
|
7
|
+
# 2. Sycophancy detection via statistical analysis
|
|
8
|
+
# 3. Reviewer calibration tracking over time
|
|
9
|
+
# 4. Anti-sycophancy devil's advocate on high sycophancy scores
|
|
10
|
+
#
|
|
11
|
+
# Activated via: LOKI_COUNCIL_VERSION=2
|
|
12
|
+
#
|
|
13
|
+
# Environment Variables:
|
|
14
|
+
# LOKI_COUNCIL_SYCOPHANCY_THRESHOLD - Score above which to trigger devil's advocate (default: 0.6)
|
|
15
|
+
# LOKI_COUNCIL_VERSION - Set to "2" to activate this module
|
|
16
|
+
#
|
|
17
|
+
# Dependencies:
|
|
18
|
+
# - completion-council.sh (sourced by caller for shared functions)
|
|
19
|
+
# - swarm/sycophancy.py (sycophancy detection)
|
|
20
|
+
# - swarm/calibration.py (reviewer calibration)
|
|
21
|
+
#
|
|
22
|
+
#===============================================================================
|
|
23
|
+
|
|
24
|
+
COUNCIL_V2_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
25
|
+
|
|
26
|
+
#===============================================================================
|
|
27
|
+
# council_v2_vote() -- Main entry point for v2 voting
|
|
28
|
+
#
|
|
29
|
+
# 1. Create isolated evidence packages (no cross-contamination)
|
|
30
|
+
# 2. Launch parallel reviewers with isolated evidence
|
|
31
|
+
# 3. Collect votes independently
|
|
32
|
+
# 4. Run sycophancy detection via Python
|
|
33
|
+
# 5. Run calibration tracking
|
|
34
|
+
# 6. Apply anti-sycophancy if needed (devil's advocate)
|
|
35
|
+
# 7. Return final verdict
|
|
36
|
+
#===============================================================================
|
|
37
|
+
|
|
38
|
+
council_v2_vote() {
|
|
39
|
+
local prd_path="$1"
|
|
40
|
+
local evidence_file="$2"
|
|
41
|
+
local vote_dir="$3"
|
|
42
|
+
local iteration="${4:-0}"
|
|
43
|
+
|
|
44
|
+
local council_size="${COUNCIL_SIZE:-3}"
|
|
45
|
+
|
|
46
|
+
log_header "COMPLETION COUNCIL v2 - Iteration $iteration"
|
|
47
|
+
log_info "Convening ${council_size}-member blind review council..."
|
|
48
|
+
|
|
49
|
+
# Step 1: Create isolated evidence packages
|
|
50
|
+
local review_dirs=()
|
|
51
|
+
for i in $(seq 1 "$council_size"); do
|
|
52
|
+
local review_dir
|
|
53
|
+
review_dir=$(mktemp -d)
|
|
54
|
+
cp "$evidence_file" "$review_dir/evidence.md"
|
|
55
|
+
if [ -n "$prd_path" ] && [ -f "$prd_path" ]; then
|
|
56
|
+
cp "$prd_path" "$review_dir/prd.md"
|
|
57
|
+
fi
|
|
58
|
+
review_dirs+=("$review_dir")
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
# Step 2: Launch parallel reviewers
|
|
62
|
+
local vote_files=()
|
|
63
|
+
for i in $(seq 0 $((council_size - 1))); do
|
|
64
|
+
local role
|
|
65
|
+
case $i in
|
|
66
|
+
0) role="requirements_verifier" ;;
|
|
67
|
+
1) role="test_auditor" ;;
|
|
68
|
+
*) role="code_quality_reviewer" ;;
|
|
69
|
+
esac
|
|
70
|
+
local vote_file="${review_dirs[$i]}/vote.json"
|
|
71
|
+
vote_files+=("$vote_file")
|
|
72
|
+
|
|
73
|
+
# Each reviewer gets ONLY their evidence package (no other votes visible)
|
|
74
|
+
council_v2_run_reviewer "$role" "${review_dirs[$i]}" "$vote_file" &
|
|
75
|
+
done
|
|
76
|
+
|
|
77
|
+
# Wait for all reviewers to complete
|
|
78
|
+
wait
|
|
79
|
+
|
|
80
|
+
# Step 3: Collect votes
|
|
81
|
+
local votes_json="["
|
|
82
|
+
local first=true
|
|
83
|
+
local approve_count=0
|
|
84
|
+
local reject_count=0
|
|
85
|
+
for vote_file in "${vote_files[@]}"; do
|
|
86
|
+
if [ -f "$vote_file" ]; then
|
|
87
|
+
local vote_content
|
|
88
|
+
vote_content=$(cat "$vote_file")
|
|
89
|
+
if [ "$first" = "true" ]; then
|
|
90
|
+
first=false
|
|
91
|
+
else
|
|
92
|
+
votes_json="$votes_json,"
|
|
93
|
+
fi
|
|
94
|
+
votes_json="$votes_json$vote_content"
|
|
95
|
+
|
|
96
|
+
local verdict
|
|
97
|
+
verdict=$(echo "$vote_content" | python3 -c "import sys,json; print(json.load(sys.stdin).get('verdict','').upper())" 2>/dev/null || echo "UNKNOWN")
|
|
98
|
+
if [ "$verdict" = "APPROVE" ]; then
|
|
99
|
+
((approve_count++))
|
|
100
|
+
else
|
|
101
|
+
((reject_count++))
|
|
102
|
+
fi
|
|
103
|
+
fi
|
|
104
|
+
done
|
|
105
|
+
votes_json="$votes_json]"
|
|
106
|
+
|
|
107
|
+
log_info "Blind review results: $approve_count APPROVE / $reject_count REJECT"
|
|
108
|
+
|
|
109
|
+
# Step 4: Sycophancy detection
|
|
110
|
+
local sycophancy_score
|
|
111
|
+
sycophancy_score=$(python3 -c "
|
|
112
|
+
import sys
|
|
113
|
+
sys.path.insert(0, '${COUNCIL_V2_DIR}/../swarm')
|
|
114
|
+
from sycophancy import detect_sycophancy
|
|
115
|
+
import json
|
|
116
|
+
votes = json.loads(sys.argv[1])
|
|
117
|
+
print('{:.3f}'.format(detect_sycophancy(votes)))
|
|
118
|
+
" "$votes_json" 2>/dev/null || echo "0.000")
|
|
119
|
+
|
|
120
|
+
log_info "Sycophancy score: $sycophancy_score"
|
|
121
|
+
|
|
122
|
+
# Step 5: Anti-sycophancy check
|
|
123
|
+
if [ "$approve_count" -eq "$council_size" ]; then
|
|
124
|
+
local threshold="${LOKI_COUNCIL_SYCOPHANCY_THRESHOLD:-0.6}"
|
|
125
|
+
local should_challenge
|
|
126
|
+
should_challenge=$(python3 -c "print('yes' if float('$sycophancy_score') >= float('$threshold') else 'no')" 2>/dev/null || echo "no")
|
|
127
|
+
|
|
128
|
+
if [ "$should_challenge" = "yes" ]; then
|
|
129
|
+
log_warn "Sycophancy score $sycophancy_score >= $threshold -- adding devil's advocate"
|
|
130
|
+
# Run devil's advocate with fresh evidence (no visibility of other votes)
|
|
131
|
+
local da_dir
|
|
132
|
+
da_dir=$(mktemp -d)
|
|
133
|
+
cp "$evidence_file" "$da_dir/evidence.md"
|
|
134
|
+
[ -n "$prd_path" ] && [ -f "$prd_path" ] && cp "$prd_path" "$da_dir/prd.md"
|
|
135
|
+
local da_vote="$da_dir/vote.json"
|
|
136
|
+
council_v2_run_reviewer "devils_advocate" "$da_dir" "$da_vote"
|
|
137
|
+
|
|
138
|
+
if [ -f "$da_vote" ]; then
|
|
139
|
+
local da_verdict
|
|
140
|
+
da_verdict=$(cat "$da_vote" | python3 -c "import sys,json; print(json.load(sys.stdin).get('verdict','').upper())" 2>/dev/null || echo "REJECT")
|
|
141
|
+
if [ "$da_verdict" = "REJECT" ]; then
|
|
142
|
+
log_warn "Devil's advocate REJECTED unanimous approval"
|
|
143
|
+
approve_count=$((approve_count - 1))
|
|
144
|
+
reject_count=$((reject_count + 1))
|
|
145
|
+
else
|
|
146
|
+
log_info "Devil's advocate confirmed unanimous approval"
|
|
147
|
+
fi
|
|
148
|
+
fi
|
|
149
|
+
rm -rf "$da_dir"
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# Step 6: Calibration tracking
|
|
154
|
+
local final_decision
|
|
155
|
+
if [ "$approve_count" -ge "${COUNCIL_THRESHOLD:-2}" ]; then
|
|
156
|
+
final_decision="approve"
|
|
157
|
+
else
|
|
158
|
+
final_decision="reject"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
python3 -c "
|
|
162
|
+
import sys
|
|
163
|
+
sys.path.insert(0, '${COUNCIL_V2_DIR}/../swarm')
|
|
164
|
+
from calibration import CalibrationTracker
|
|
165
|
+
import json
|
|
166
|
+
tracker = CalibrationTracker('.loki/council/calibration.json')
|
|
167
|
+
votes = json.loads(sys.argv[1])
|
|
168
|
+
for i, v in enumerate(votes):
|
|
169
|
+
v['reviewer_id'] = 'reviewer-' + str(i + 1)
|
|
170
|
+
tracker.record_round(int(sys.argv[2]), votes, sys.argv[3])
|
|
171
|
+
tracker.save()
|
|
172
|
+
" "$votes_json" "$iteration" "$final_decision" 2>/dev/null || true
|
|
173
|
+
|
|
174
|
+
# Step 7: Save results
|
|
175
|
+
mkdir -p "$vote_dir"
|
|
176
|
+
echo "$votes_json" > "$vote_dir/all-votes.json"
|
|
177
|
+
cat > "$vote_dir/summary.json" << SUMMARY_EOF
|
|
178
|
+
{
|
|
179
|
+
"version": 2,
|
|
180
|
+
"approve": $approve_count,
|
|
181
|
+
"reject": $reject_count,
|
|
182
|
+
"sycophancy_score": $sycophancy_score,
|
|
183
|
+
"decision": "$final_decision",
|
|
184
|
+
"council_size": $council_size,
|
|
185
|
+
"iteration": $iteration
|
|
186
|
+
}
|
|
187
|
+
SUMMARY_EOF
|
|
188
|
+
|
|
189
|
+
log_info "Council v2 verdict: $approve_count APPROVE / $reject_count REJECT -> $final_decision (sycophancy: $sycophancy_score)"
|
|
190
|
+
|
|
191
|
+
# Emit event for dashboard
|
|
192
|
+
emit_event_json "council_vote" \
|
|
193
|
+
"version=2" \
|
|
194
|
+
"iteration=$iteration" \
|
|
195
|
+
"approve=$approve_count" \
|
|
196
|
+
"reject=$reject_count" \
|
|
197
|
+
"threshold=${COUNCIL_THRESHOLD:-2}" \
|
|
198
|
+
"sycophancy_score=$sycophancy_score" \
|
|
199
|
+
"result=$(echo "$final_decision" | tr '[:lower:]' '[:upper:]')" 2>/dev/null || true
|
|
200
|
+
|
|
201
|
+
# Cleanup isolated review directories
|
|
202
|
+
for dir in "${review_dirs[@]}"; do
|
|
203
|
+
rm -rf "$dir"
|
|
204
|
+
done
|
|
205
|
+
|
|
206
|
+
# Return result
|
|
207
|
+
if [ "$final_decision" = "approve" ]; then
|
|
208
|
+
return 0
|
|
209
|
+
else
|
|
210
|
+
return 1
|
|
211
|
+
fi
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#===============================================================================
|
|
215
|
+
# council_v2_run_reviewer() -- Run a single isolated reviewer
|
|
216
|
+
#
|
|
217
|
+
# Each reviewer receives only their evidence package and produces a JSON vote.
|
|
218
|
+
# No reviewer can see another reviewer's output (true blind review).
|
|
219
|
+
#===============================================================================
|
|
220
|
+
|
|
221
|
+
council_v2_run_reviewer() {
|
|
222
|
+
local role="$1"
|
|
223
|
+
local review_dir="$2"
|
|
224
|
+
local output_file="$3"
|
|
225
|
+
|
|
226
|
+
# Build review prompt based on role
|
|
227
|
+
local prompt
|
|
228
|
+
case "$role" in
|
|
229
|
+
requirements_verifier)
|
|
230
|
+
prompt="You are a requirements verification reviewer. Review the evidence and PRD below. Check if all requirements are implemented. Output a JSON object with keys: verdict (APPROVE or REJECT), reasoning (string), issues (array of {severity, description})."
|
|
231
|
+
;;
|
|
232
|
+
test_auditor)
|
|
233
|
+
prompt="You are a test auditor reviewer. Review the evidence below. Check test coverage, test quality, and assertion density. Output a JSON object with keys: verdict (APPROVE or REJECT), reasoning (string), issues (array of {severity, description})."
|
|
234
|
+
;;
|
|
235
|
+
code_quality_reviewer)
|
|
236
|
+
prompt="You are a code quality reviewer. Review the evidence below. Check for SOLID violations, security issues, performance problems. Output a JSON object with keys: verdict (APPROVE or REJECT), reasoning (string), issues (array of {severity, description})."
|
|
237
|
+
;;
|
|
238
|
+
devils_advocate)
|
|
239
|
+
prompt="You are a devil's advocate reviewer. Your job is to find reasons this code should NOT be approved. Be skeptical and contrarian. Output a JSON object with keys: verdict (APPROVE or REJECT), reasoning (string), issues (array of {severity, description})."
|
|
240
|
+
;;
|
|
241
|
+
*)
|
|
242
|
+
prompt="You are a general reviewer. Evaluate project completion. Output a JSON object with keys: verdict (APPROVE or REJECT), reasoning (string), issues (array of {severity, description})."
|
|
243
|
+
;;
|
|
244
|
+
esac
|
|
245
|
+
|
|
246
|
+
local evidence=""
|
|
247
|
+
[ -f "$review_dir/evidence.md" ] && evidence=$(cat "$review_dir/evidence.md")
|
|
248
|
+
local prd=""
|
|
249
|
+
[ -f "$review_dir/prd.md" ] && prd=$(head -100 "$review_dir/prd.md")
|
|
250
|
+
|
|
251
|
+
# Use the current provider to run the review
|
|
252
|
+
local full_prompt="$prompt
|
|
253
|
+
|
|
254
|
+
## Evidence
|
|
255
|
+
$evidence
|
|
256
|
+
|
|
257
|
+
## PRD (first 100 lines)
|
|
258
|
+
$prd
|
|
259
|
+
|
|
260
|
+
Respond ONLY with a valid JSON object. No markdown fencing."
|
|
261
|
+
|
|
262
|
+
local result
|
|
263
|
+
case "${PROVIDER_NAME:-claude}" in
|
|
264
|
+
claude)
|
|
265
|
+
if command -v claude &>/dev/null; then
|
|
266
|
+
result=$(echo "$full_prompt" | claude --model haiku -p 2>/dev/null || echo '{"verdict":"REJECT","reasoning":"review execution failed","issues":[]}')
|
|
267
|
+
else
|
|
268
|
+
result='{"verdict":"REJECT","reasoning":"reviewer CLI unavailable","issues":[]}'
|
|
269
|
+
fi
|
|
270
|
+
;;
|
|
271
|
+
codex)
|
|
272
|
+
if command -v codex &>/dev/null; then
|
|
273
|
+
result=$(codex exec -q "$full_prompt" 2>/dev/null || echo '{"verdict":"REJECT","reasoning":"review execution failed","issues":[]}')
|
|
274
|
+
else
|
|
275
|
+
result='{"verdict":"REJECT","reasoning":"reviewer CLI unavailable","issues":[]}'
|
|
276
|
+
fi
|
|
277
|
+
;;
|
|
278
|
+
gemini)
|
|
279
|
+
if command -v gemini &>/dev/null; then
|
|
280
|
+
result=$(echo "$full_prompt" | gemini 2>/dev/null || echo '{"verdict":"REJECT","reasoning":"review execution failed","issues":[]}')
|
|
281
|
+
else
|
|
282
|
+
result='{"verdict":"REJECT","reasoning":"reviewer CLI unavailable","issues":[]}'
|
|
283
|
+
fi
|
|
284
|
+
;;
|
|
285
|
+
*)
|
|
286
|
+
result='{"verdict":"REJECT","reasoning":"review not supported for this provider","issues":[]}'
|
|
287
|
+
;;
|
|
288
|
+
esac
|
|
289
|
+
|
|
290
|
+
# Extract JSON from result (handle markdown fencing)
|
|
291
|
+
local extracted
|
|
292
|
+
extracted=$(echo "$result" | sed -n '/^{/,/^}/p' | head -50)
|
|
293
|
+
if [ -z "$extracted" ]; then
|
|
294
|
+
# Try removing markdown fencing
|
|
295
|
+
extracted=$(echo "$result" | sed 's/^```json//;s/^```//' | sed -n '/^{/,/^}/p' | head -50)
|
|
296
|
+
fi
|
|
297
|
+
if [ -z "$extracted" ]; then
|
|
298
|
+
extracted='{"verdict":"REJECT","reasoning":"failed to parse review output","issues":[]}'
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
echo "$extracted" > "$output_file"
|
|
302
|
+
}
|
package/autonomy/loki
CHANGED
|
@@ -130,6 +130,81 @@ get_version() {
|
|
|
130
130
|
fi
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
# Ensure dashboard Python venv with all deps installed.
|
|
134
|
+
# Uses ~/.loki/dashboard-venv (persistent, writable, survives npm/brew upgrades).
|
|
135
|
+
# Sets DASHBOARD_PYTHON to the venv python3 path on success.
|
|
136
|
+
# Returns 0 on success, 1 on failure.
|
|
137
|
+
ensure_dashboard_venv() {
|
|
138
|
+
local dashboard_venv="$HOME/.loki/dashboard-venv"
|
|
139
|
+
DASHBOARD_PYTHON="python3"
|
|
140
|
+
|
|
141
|
+
# Use existing venv python if available
|
|
142
|
+
if [ -x "${dashboard_venv}/bin/python3" ]; then
|
|
143
|
+
DASHBOARD_PYTHON="${dashboard_venv}/bin/python3"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Check all required imports
|
|
147
|
+
if "$DASHBOARD_PYTHON" -c "import fastapi; import sqlalchemy; import aiosqlite" 2>/dev/null; then
|
|
148
|
+
return 0
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
echo -e "${YELLOW}Setting up dashboard virtualenv...${NC}"
|
|
152
|
+
|
|
153
|
+
# Create venv if missing or broken
|
|
154
|
+
if ! [ -x "${dashboard_venv}/bin/python3" ]; then
|
|
155
|
+
# Remove broken venv if exists
|
|
156
|
+
if [ -d "$dashboard_venv" ]; then
|
|
157
|
+
echo -e "${YELLOW}Removing broken dashboard venv...${NC}"
|
|
158
|
+
rm -rf "$dashboard_venv"
|
|
159
|
+
fi
|
|
160
|
+
mkdir -p "$HOME/.loki"
|
|
161
|
+
python3 -m venv "$dashboard_venv" 2>/dev/null || python3.13 -m venv "$dashboard_venv" 2>/dev/null || {
|
|
162
|
+
echo -e "${RED}Failed to create dashboard virtualenv${NC}"
|
|
163
|
+
echo "You may need to install python3-venv:"
|
|
164
|
+
echo " sudo apt install python3-venv (Debian/Ubuntu)"
|
|
165
|
+
echo " brew install python3 (macOS)"
|
|
166
|
+
return 1
|
|
167
|
+
}
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
DASHBOARD_PYTHON="${dashboard_venv}/bin/python3"
|
|
171
|
+
echo -e "${YELLOW}Installing dashboard dependencies...${NC}"
|
|
172
|
+
|
|
173
|
+
# Try pinned requirements first, then unpinned fallback
|
|
174
|
+
local req_file="${SKILL_DIR}/dashboard/requirements.txt"
|
|
175
|
+
local installed=false
|
|
176
|
+
if [ -f "$req_file" ]; then
|
|
177
|
+
if "${dashboard_venv}/bin/pip" install -r "$req_file" 2>&1 | tail -1; then
|
|
178
|
+
installed=true
|
|
179
|
+
else
|
|
180
|
+
echo -e "${YELLOW}Pinned deps failed, trying unpinned...${NC}"
|
|
181
|
+
fi
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
if [ "$installed" = false ]; then
|
|
185
|
+
# Install without greenlet first (it may need a C compiler).
|
|
186
|
+
# SQLAlchemy only requires greenlet for sync-in-async; aiosqlite is pure async.
|
|
187
|
+
if ! "${dashboard_venv}/bin/pip" install fastapi uvicorn pydantic websockets sqlalchemy aiosqlite 2>&1 | tail -1; then
|
|
188
|
+
echo -e "${RED}Failed to install dashboard dependencies${NC}"
|
|
189
|
+
echo "Try manually: ${dashboard_venv}/bin/pip install fastapi uvicorn sqlalchemy aiosqlite"
|
|
190
|
+
return 1
|
|
191
|
+
fi
|
|
192
|
+
# Try greenlet separately (optional, needs C compiler on some platforms)
|
|
193
|
+
"${dashboard_venv}/bin/pip" install greenlet 2>/dev/null || true
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# Verify imports work after install
|
|
197
|
+
if ! "$DASHBOARD_PYTHON" -c "import fastapi; import sqlalchemy; import aiosqlite" 2>/dev/null; then
|
|
198
|
+
echo -e "${RED}Dashboard dependencies installed but imports still fail${NC}"
|
|
199
|
+
echo "Try removing the venv and retrying:"
|
|
200
|
+
echo " rm -rf ${dashboard_venv}"
|
|
201
|
+
echo " loki dashboard start"
|
|
202
|
+
return 1
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
return 0
|
|
206
|
+
}
|
|
207
|
+
|
|
133
208
|
# Check if jq is available (called by functions that need it)
|
|
134
209
|
require_jq() {
|
|
135
210
|
if ! command -v jq &> /dev/null; then
|
|
@@ -1565,14 +1640,12 @@ cmd_dashboard_start() {
|
|
|
1565
1640
|
exit 1
|
|
1566
1641
|
fi
|
|
1567
1642
|
|
|
1568
|
-
#
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
python_cmd="${dashboard_venv}/bin/python3"
|
|
1573
|
-
elif ! command -v python3 &> /dev/null; then
|
|
1574
|
-
python_cmd="python"
|
|
1643
|
+
# Set up dashboard venv and python command
|
|
1644
|
+
if ! ensure_dashboard_venv; then
|
|
1645
|
+
echo -e "${RED}Cannot start dashboard without Python dependencies${NC}"
|
|
1646
|
+
return 1
|
|
1575
1647
|
fi
|
|
1648
|
+
local python_cmd="$DASHBOARD_PYTHON"
|
|
1576
1649
|
|
|
1577
1650
|
# Check if already running
|
|
1578
1651
|
if [ -f "$DASHBOARD_PID_FILE" ]; then
|
|
@@ -1618,34 +1691,6 @@ cmd_dashboard_start() {
|
|
|
1618
1691
|
tls_info=" (TLS enabled)"
|
|
1619
1692
|
fi
|
|
1620
1693
|
|
|
1621
|
-
# Ensure dashboard Python dependencies via virtualenv (PEP 668 safe)
|
|
1622
|
-
if ! "$python_cmd" -c "import fastapi" 2>/dev/null; then
|
|
1623
|
-
echo -e "${YELLOW}Setting up dashboard virtualenv...${NC}"
|
|
1624
|
-
local req_file="${SKILL_DIR}/dashboard/requirements.txt"
|
|
1625
|
-
if ! [ -d "$dashboard_venv" ]; then
|
|
1626
|
-
python3 -m venv "$dashboard_venv" 2>/dev/null || python3.13 -m venv "$dashboard_venv" 2>/dev/null || true
|
|
1627
|
-
fi
|
|
1628
|
-
if [ -x "${dashboard_venv}/bin/python3" ]; then
|
|
1629
|
-
python_cmd="${dashboard_venv}/bin/python3"
|
|
1630
|
-
echo -e "${YELLOW}Installing dashboard dependencies into venv...${NC}"
|
|
1631
|
-
if [ -f "$req_file" ]; then
|
|
1632
|
-
"${dashboard_venv}/bin/pip" install -q -r "$req_file" 2>/dev/null || {
|
|
1633
|
-
echo -e "${YELLOW}Pinned deps failed, installing core deps...${NC}"
|
|
1634
|
-
"${dashboard_venv}/bin/pip" install -q fastapi uvicorn pydantic websockets 2>/dev/null || true
|
|
1635
|
-
}
|
|
1636
|
-
else
|
|
1637
|
-
"${dashboard_venv}/bin/pip" install -q fastapi uvicorn pydantic websockets 2>/dev/null || true
|
|
1638
|
-
fi
|
|
1639
|
-
else
|
|
1640
|
-
# Fallback: try direct pip (may fail on PEP 668 systems)
|
|
1641
|
-
pip3 install -q fastapi uvicorn pydantic websockets 2>/dev/null || pip install -q fastapi uvicorn pydantic websockets 2>/dev/null || {
|
|
1642
|
-
echo -e "${RED}Failed to install dashboard dependencies${NC}"
|
|
1643
|
-
echo "Run manually: python3 -m venv ${dashboard_venv} && ${dashboard_venv}/bin/pip install fastapi uvicorn pydantic websockets"
|
|
1644
|
-
exit 1
|
|
1645
|
-
}
|
|
1646
|
-
fi
|
|
1647
|
-
fi
|
|
1648
|
-
|
|
1649
1694
|
echo -e "${GREEN}Starting dashboard server...${NC}"
|
|
1650
1695
|
echo -e "${CYAN}Host:${NC} $host"
|
|
1651
1696
|
echo -e "${CYAN}Port:${NC} $port"
|
|
@@ -3347,35 +3392,12 @@ cmd_api() {
|
|
|
3347
3392
|
fi
|
|
3348
3393
|
fi
|
|
3349
3394
|
|
|
3350
|
-
# Ensure dashboard Python dependencies
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
api_python="${dashboard_venv}/bin/python3"
|
|
3355
|
-
fi
|
|
3356
|
-
if ! "$api_python" -c "import fastapi" 2>/dev/null; then
|
|
3357
|
-
echo -e "${YELLOW}Setting up dashboard virtualenv...${NC}"
|
|
3358
|
-
if ! [ -d "$dashboard_venv" ]; then
|
|
3359
|
-
python3 -m venv "$dashboard_venv" 2>/dev/null || python3.13 -m venv "$dashboard_venv" 2>/dev/null || true
|
|
3360
|
-
fi
|
|
3361
|
-
if [ -x "${dashboard_venv}/bin/python3" ]; then
|
|
3362
|
-
api_python="${dashboard_venv}/bin/python3"
|
|
3363
|
-
local req_file="${SKILL_DIR}/dashboard/requirements.txt"
|
|
3364
|
-
if [ -f "$req_file" ]; then
|
|
3365
|
-
"${dashboard_venv}/bin/pip" install -q -r "$req_file" 2>/dev/null || {
|
|
3366
|
-
"${dashboard_venv}/bin/pip" install -q fastapi uvicorn pydantic websockets 2>/dev/null || true
|
|
3367
|
-
}
|
|
3368
|
-
else
|
|
3369
|
-
"${dashboard_venv}/bin/pip" install -q fastapi uvicorn pydantic websockets 2>/dev/null || true
|
|
3370
|
-
fi
|
|
3371
|
-
else
|
|
3372
|
-
pip3 install -q fastapi uvicorn pydantic websockets 2>/dev/null || {
|
|
3373
|
-
echo -e "${RED}Failed to install dashboard dependencies${NC}"
|
|
3374
|
-
echo "Run manually: python3 -m venv ${dashboard_venv} && ${dashboard_venv}/bin/pip install fastapi uvicorn pydantic websockets"
|
|
3375
|
-
exit 1
|
|
3376
|
-
}
|
|
3377
|
-
fi
|
|
3395
|
+
# Ensure dashboard Python dependencies
|
|
3396
|
+
if ! ensure_dashboard_venv; then
|
|
3397
|
+
echo -e "${RED}Cannot start dashboard without Python dependencies${NC}"
|
|
3398
|
+
exit 1
|
|
3378
3399
|
fi
|
|
3400
|
+
local api_python="$DASHBOARD_PYTHON"
|
|
3379
3401
|
|
|
3380
3402
|
# Start server
|
|
3381
3403
|
mkdir -p "$LOKI_DIR/logs" "$LOKI_DIR/dashboard"
|
|
@@ -3941,7 +3963,13 @@ SESSEOF
|
|
|
3941
3963
|
sleep 1
|
|
3942
3964
|
fi
|
|
3943
3965
|
|
|
3944
|
-
|
|
3966
|
+
# Set up dashboard venv
|
|
3967
|
+
if ! ensure_dashboard_venv; then
|
|
3968
|
+
echo -e "${YELLOW}Warning: Dashboard dependencies not available, continuing without dashboard${NC}"
|
|
3969
|
+
fi
|
|
3970
|
+
local demo_python="$DASHBOARD_PYTHON"
|
|
3971
|
+
|
|
3972
|
+
LOKI_DIR="$LOKI_DIR" PYTHONPATH="$SKILL_DIR" "$demo_python" -m uvicorn dashboard.server:app \
|
|
3945
3973
|
--host 127.0.0.1 --port 57374 --log-level warning &>/dev/null &
|
|
3946
3974
|
local dash_pid=$!
|
|
3947
3975
|
echo "$dash_pid" > "$LOKI_DIR/dashboard/dashboard.pid"
|