loki-mode 6.37.5 → 6.37.7
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/autonomy/completion-council.sh +3 -2
- package/autonomy/loki +91 -108
- package/autonomy/run.sh +6 -0
- package/completions/_loki +27 -11
- package/completions/loki.bash +9 -4
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +82 -4
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
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.37.
|
|
6
|
+
# Loki Mode v6.37.7
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
267
267
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
268
268
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
269
269
|
|
|
270
|
-
**v6.37.
|
|
270
|
+
**v6.37.7 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.37.
|
|
1
|
+
6.37.7
|
|
@@ -42,8 +42,9 @@ COUNCIL_ENABLED=${LOKI_COUNCIL_ENABLED:-true}
|
|
|
42
42
|
COUNCIL_SIZE=${LOKI_COUNCIL_SIZE:-3}
|
|
43
43
|
COUNCIL_THRESHOLD=${LOKI_COUNCIL_THRESHOLD:-2}
|
|
44
44
|
COUNCIL_CHECK_INTERVAL=${LOKI_COUNCIL_CHECK_INTERVAL:-5}
|
|
45
|
-
# Guard against
|
|
46
|
-
if [ "$COUNCIL_CHECK_INTERVAL" -
|
|
45
|
+
# Guard against invalid interval (must be positive integer)
|
|
46
|
+
if ! [[ "$COUNCIL_CHECK_INTERVAL" =~ ^[1-9][0-9]*$ ]]; then
|
|
47
|
+
echo "Warning: invalid COUNCIL_CHECK_INTERVAL '$COUNCIL_CHECK_INTERVAL', using default 5" >&2
|
|
47
48
|
COUNCIL_CHECK_INTERVAL=5
|
|
48
49
|
fi
|
|
49
50
|
COUNCIL_MIN_ITERATIONS=${LOKI_COUNCIL_MIN_ITERATIONS:-3}
|
package/autonomy/loki
CHANGED
|
@@ -410,7 +410,7 @@ show_help() {
|
|
|
410
410
|
echo " api [cmd] Dashboard/API server (start|stop|status)"
|
|
411
411
|
echo " sandbox [cmd] Docker sandbox (start|stop|status|logs|shell|build)"
|
|
412
412
|
echo " notify [cmd] Send notifications (test|slack|discord|webhook|status)"
|
|
413
|
-
echo "
|
|
413
|
+
echo " telemetry [cmd] OpenTelemetry management (status|enable|disable|stop|start)"
|
|
414
414
|
echo " import Import GitHub issues as tasks"
|
|
415
415
|
echo " github [cmd] GitHub integration (sync|export|pr|status)"
|
|
416
416
|
echo " config [cmd] Manage configuration (show|init|edit|path|set|get)"
|
|
@@ -1144,8 +1144,9 @@ cmd_stop() {
|
|
|
1144
1144
|
if [ -n "$target_session" ]; then
|
|
1145
1145
|
if is_session_running "$target_session"; then
|
|
1146
1146
|
_stop_session_by_id "$target_session"
|
|
1147
|
+
echo "Stopped session: $target_session"
|
|
1147
1148
|
else
|
|
1148
|
-
echo
|
|
1149
|
+
echo "No active session found"
|
|
1149
1150
|
fi
|
|
1150
1151
|
return 0
|
|
1151
1152
|
fi
|
|
@@ -3035,6 +3036,10 @@ cmd_web_start() {
|
|
|
3035
3036
|
echo -e "${RED}Error: Port must be between 1 and 65535, got $port${NC}"
|
|
3036
3037
|
exit 1
|
|
3037
3038
|
fi
|
|
3039
|
+
if lsof -Pi :"$port" -sTCP:LISTEN -t >/dev/null 2>&1; then
|
|
3040
|
+
echo -e "${RED}Error: Port $port is already in use${NC}"
|
|
3041
|
+
exit 1
|
|
3042
|
+
fi
|
|
3038
3043
|
|
|
3039
3044
|
# Validate --prd file if provided
|
|
3040
3045
|
if [ -n "$prd_file" ]; then
|
|
@@ -4449,12 +4454,12 @@ cmd_watch() {
|
|
|
4449
4454
|
esac
|
|
4450
4455
|
done
|
|
4451
4456
|
|
|
4452
|
-
# Validate numeric arguments
|
|
4453
|
-
if ! [[ "$poll_interval" =~ ^[0-9]
|
|
4457
|
+
# Validate numeric arguments (no leading zeros, no decimals, positive integers only)
|
|
4458
|
+
if ! [[ "$poll_interval" =~ ^[1-9][0-9]*$ ]]; then
|
|
4454
4459
|
echo -e "${RED}Invalid --interval: $poll_interval (expected positive integer)${NC}"
|
|
4455
4460
|
return 1
|
|
4456
4461
|
fi
|
|
4457
|
-
if ! [[ "$debounce" =~ ^[0-9]
|
|
4462
|
+
if ! [[ "$debounce" =~ ^[1-9][0-9]*$ ]] && [ "$debounce" != "0" ]; then
|
|
4458
4463
|
echo -e "${RED}Invalid --debounce: $debounce (expected non-negative integer)${NC}"
|
|
4459
4464
|
return 1
|
|
4460
4465
|
fi
|
|
@@ -9739,7 +9744,8 @@ main() {
|
|
|
9739
9744
|
cmd_enterprise "$@"
|
|
9740
9745
|
;;
|
|
9741
9746
|
voice)
|
|
9742
|
-
|
|
9747
|
+
echo "Voice mode is planned for a future release. Track progress at github.com/asklokesh/loki-mode/issues/85"
|
|
9748
|
+
exit 0
|
|
9743
9749
|
;;
|
|
9744
9750
|
secrets)
|
|
9745
9751
|
cmd_secrets "$@"
|
|
@@ -13171,95 +13177,9 @@ for check, passed in checks.items():
|
|
|
13171
13177
|
|
|
13172
13178
|
# Voice input commands
|
|
13173
13179
|
cmd_voice() {
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
# Check fallback locations for voice script
|
|
13178
|
-
if [ ! -f "$VOICE_SCRIPT" ]; then
|
|
13179
|
-
# Try relative to loki CLI location
|
|
13180
|
-
local loki_dir
|
|
13181
|
-
loki_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13182
|
-
VOICE_SCRIPT="$loki_dir/voice.sh"
|
|
13183
|
-
fi
|
|
13184
|
-
|
|
13185
|
-
if [ ! -f "$VOICE_SCRIPT" ]; then
|
|
13186
|
-
echo -e "${RED}Error: Voice module not found${NC}"
|
|
13187
|
-
echo "Expected at: $SKILL_DIR/autonomy/voice.sh"
|
|
13188
|
-
echo ""
|
|
13189
|
-
echo "Voice input requires the voice.sh module."
|
|
13190
|
-
echo "This feature may not be available in all installations."
|
|
13191
|
-
exit 1
|
|
13192
|
-
fi
|
|
13193
|
-
|
|
13194
|
-
case "$subcommand" in
|
|
13195
|
-
status)
|
|
13196
|
-
"$VOICE_SCRIPT" status
|
|
13197
|
-
;;
|
|
13198
|
-
listen)
|
|
13199
|
-
echo -e "${BOLD}Starting voice input...${NC}"
|
|
13200
|
-
local text
|
|
13201
|
-
text=$("$VOICE_SCRIPT" listen)
|
|
13202
|
-
if [ -n "$text" ]; then
|
|
13203
|
-
echo ""
|
|
13204
|
-
echo -e "${GREEN}Transcribed text:${NC}"
|
|
13205
|
-
echo "$text"
|
|
13206
|
-
fi
|
|
13207
|
-
;;
|
|
13208
|
-
dictate)
|
|
13209
|
-
local output="${2:-prd-voice.md}"
|
|
13210
|
-
echo -e "${BOLD}Starting guided PRD dictation...${NC}"
|
|
13211
|
-
echo ""
|
|
13212
|
-
"$VOICE_SCRIPT" dictate "$output"
|
|
13213
|
-
if [ -f "$output" ]; then
|
|
13214
|
-
echo ""
|
|
13215
|
-
echo -e "${GREEN}PRD created: $output${NC}"
|
|
13216
|
-
echo ""
|
|
13217
|
-
echo "Start Loki Mode with:"
|
|
13218
|
-
echo " loki start $output"
|
|
13219
|
-
fi
|
|
13220
|
-
;;
|
|
13221
|
-
speak)
|
|
13222
|
-
shift
|
|
13223
|
-
if [ $# -eq 0 ]; then
|
|
13224
|
-
echo -e "${RED}Usage: loki voice speak MESSAGE${NC}"
|
|
13225
|
-
exit 1
|
|
13226
|
-
fi
|
|
13227
|
-
"$VOICE_SCRIPT" speak "$*"
|
|
13228
|
-
;;
|
|
13229
|
-
start)
|
|
13230
|
-
# Dictate PRD and start Loki Mode
|
|
13231
|
-
local prd_file="${2:-prd-voice-$(date +%Y%m%d%H%M%S).md}"
|
|
13232
|
-
echo -e "${BOLD}Voice-activated PRD creation...${NC}"
|
|
13233
|
-
"$VOICE_SCRIPT" dictate "$prd_file"
|
|
13234
|
-
if [ -f "$prd_file" ]; then
|
|
13235
|
-
echo ""
|
|
13236
|
-
echo -e "${GREEN}PRD created. Starting Loki Mode...${NC}"
|
|
13237
|
-
cmd_start "$prd_file"
|
|
13238
|
-
fi
|
|
13239
|
-
;;
|
|
13240
|
-
--help|-h|help)
|
|
13241
|
-
echo -e "${BOLD}loki voice${NC} - Voice input for PRD creation"
|
|
13242
|
-
echo ""
|
|
13243
|
-
echo "Usage: loki voice <command> [options]"
|
|
13244
|
-
echo ""
|
|
13245
|
-
echo "Commands:"
|
|
13246
|
-
echo " status Check voice input capabilities"
|
|
13247
|
-
echo " listen Listen and transcribe voice input"
|
|
13248
|
-
echo " dictate [FILE] Guided PRD dictation (default: prd-voice.md)"
|
|
13249
|
-
echo " speak MESSAGE Text-to-speech output"
|
|
13250
|
-
echo " start [FILE] Dictate PRD and start Loki Mode immediately"
|
|
13251
|
-
echo ""
|
|
13252
|
-
echo "Requirements:"
|
|
13253
|
-
echo " macOS: Enable Dictation in System Settings > Keyboard"
|
|
13254
|
-
echo " Or: Set OPENAI_API_KEY for Whisper API transcription"
|
|
13255
|
-
echo " Or: pip install openai-whisper for local transcription"
|
|
13256
|
-
;;
|
|
13257
|
-
*)
|
|
13258
|
-
echo -e "${RED}Unknown voice command: $subcommand${NC}"
|
|
13259
|
-
echo "Run 'loki voice help' for usage."
|
|
13260
|
-
exit 1
|
|
13261
|
-
;;
|
|
13262
|
-
esac
|
|
13180
|
+
# Voice mode is planned for a future release (#85)
|
|
13181
|
+
echo "Voice mode is planned for a future release. Track progress at github.com/asklokesh/loki-mode/issues/85"
|
|
13182
|
+
return 0
|
|
13263
13183
|
}
|
|
13264
13184
|
|
|
13265
13185
|
# Enterprise features (optional - requires env vars)
|
|
@@ -13747,9 +13667,22 @@ cmd_telemetry() {
|
|
|
13747
13667
|
echo -e "${BOLD}Telemetry Status${NC}"
|
|
13748
13668
|
echo ""
|
|
13749
13669
|
|
|
13670
|
+
# Check persistent opt-out (~/.loki/config)
|
|
13671
|
+
local global_config="${HOME}/.loki/config"
|
|
13672
|
+
local persistently_disabled=false
|
|
13673
|
+
if [ -f "$global_config" ] && grep -q "^TELEMETRY_DISABLED=true" "$global_config" 2>/dev/null; then
|
|
13674
|
+
persistently_disabled=true
|
|
13675
|
+
fi
|
|
13676
|
+
|
|
13677
|
+
if [ "$persistently_disabled" = true ]; then
|
|
13678
|
+
echo -e " Opt-out: ${RED}disabled persistently${NC} (loki telemetry start to re-enable)"
|
|
13679
|
+
fi
|
|
13680
|
+
|
|
13750
13681
|
local endpoint="${LOKI_OTEL_ENDPOINT:-}"
|
|
13751
|
-
if [ -n "$endpoint" ]; then
|
|
13682
|
+
if [ -n "$endpoint" ] && [ "$persistently_disabled" = false ]; then
|
|
13752
13683
|
echo -e " Endpoint: ${GREEN}$endpoint${NC}"
|
|
13684
|
+
elif [ "$persistently_disabled" = true ]; then
|
|
13685
|
+
echo -e " Endpoint: ${YELLOW}ignored (opted out)${NC}"
|
|
13753
13686
|
else
|
|
13754
13687
|
echo -e " Endpoint: ${YELLOW}not configured${NC}"
|
|
13755
13688
|
fi
|
|
@@ -13782,6 +13715,15 @@ try {
|
|
|
13782
13715
|
|
|
13783
13716
|
enable)
|
|
13784
13717
|
local endpoint="${1:-http://localhost:4318}"
|
|
13718
|
+
|
|
13719
|
+
# Check persistent opt-out
|
|
13720
|
+
local global_config="${HOME}/.loki/config"
|
|
13721
|
+
if [ -f "$global_config" ] && grep -q "^TELEMETRY_DISABLED=true" "$global_config" 2>/dev/null; then
|
|
13722
|
+
echo -e "${YELLOW}Telemetry is persistently disabled.${NC}"
|
|
13723
|
+
echo "Run 'loki telemetry start' first to re-enable."
|
|
13724
|
+
return 1
|
|
13725
|
+
fi
|
|
13726
|
+
|
|
13785
13727
|
echo -e "${BOLD}Enabling telemetry${NC}"
|
|
13786
13728
|
|
|
13787
13729
|
# Save to config
|
|
@@ -13825,15 +13767,49 @@ with open('$config_file', 'w') as f:
|
|
|
13825
13767
|
echo " Unset LOKI_OTEL_ENDPOINT in your shell for immediate effect."
|
|
13826
13768
|
;;
|
|
13827
13769
|
|
|
13770
|
+
stop)
|
|
13771
|
+
# Persistent opt-out across all sessions
|
|
13772
|
+
local global_config="${HOME}/.loki/config"
|
|
13773
|
+
mkdir -p "${HOME}/.loki"
|
|
13774
|
+
|
|
13775
|
+
# Remove existing TELEMETRY_DISABLED line if present, then add
|
|
13776
|
+
if [ -f "$global_config" ]; then
|
|
13777
|
+
grep -v "^TELEMETRY_DISABLED=" "$global_config" > "${global_config}.tmp" 2>/dev/null || true
|
|
13778
|
+
mv "${global_config}.tmp" "$global_config"
|
|
13779
|
+
fi
|
|
13780
|
+
echo "TELEMETRY_DISABLED=true" >> "$global_config"
|
|
13781
|
+
|
|
13782
|
+
echo -e "${BOLD}Telemetry permanently disabled${NC}"
|
|
13783
|
+
echo ""
|
|
13784
|
+
echo " Opt-out saved to: $global_config"
|
|
13785
|
+
echo " This persists across all sessions and new runs."
|
|
13786
|
+
echo " Run 'loki telemetry start' to re-enable."
|
|
13787
|
+
;;
|
|
13788
|
+
|
|
13789
|
+
start)
|
|
13790
|
+
# Remove persistent opt-out
|
|
13791
|
+
local global_config="${HOME}/.loki/config"
|
|
13792
|
+
if [ -f "$global_config" ]; then
|
|
13793
|
+
grep -v "^TELEMETRY_DISABLED=" "$global_config" > "${global_config}.tmp" 2>/dev/null || true
|
|
13794
|
+
mv "${global_config}.tmp" "$global_config"
|
|
13795
|
+
fi
|
|
13796
|
+
|
|
13797
|
+
echo -e "${BOLD}Telemetry opt-out removed${NC}"
|
|
13798
|
+
echo ""
|
|
13799
|
+
echo " Telemetry can now be enabled with 'loki telemetry enable [endpoint]'."
|
|
13800
|
+
;;
|
|
13801
|
+
|
|
13828
13802
|
--help|-h|help)
|
|
13829
|
-
echo -e "${BOLD}loki telemetry${NC} - OpenTelemetry management
|
|
13803
|
+
echo -e "${BOLD}loki telemetry${NC} - OpenTelemetry management"
|
|
13830
13804
|
echo ""
|
|
13831
13805
|
echo "Usage: loki telemetry <command>"
|
|
13832
13806
|
echo ""
|
|
13833
13807
|
echo "Commands:"
|
|
13834
13808
|
echo " status Show current telemetry config"
|
|
13835
13809
|
echo " enable [endpoint] Enable OTEL (default: http://localhost:4318)"
|
|
13836
|
-
echo " disable Disable OTEL"
|
|
13810
|
+
echo " disable Disable OTEL for current project"
|
|
13811
|
+
echo " stop Permanently opt out of telemetry across all sessions"
|
|
13812
|
+
echo " start Remove persistent opt-out, re-enable telemetry"
|
|
13837
13813
|
echo ""
|
|
13838
13814
|
;;
|
|
13839
13815
|
*)
|
|
@@ -13980,6 +13956,13 @@ cmd_remote() {
|
|
|
13980
13956
|
# Emit event
|
|
13981
13957
|
emit_event session cli remote_start "prd=${prd_abs:-none}"
|
|
13982
13958
|
|
|
13959
|
+
# Check SSH directory exists (needed for remote connections)
|
|
13960
|
+
if [ ! -d "$HOME/.ssh" ]; then
|
|
13961
|
+
echo -e "${RED}Error: ~/.ssh directory not found${NC}"
|
|
13962
|
+
echo "Create it with: mkdir -p ~/.ssh && chmod 700 ~/.ssh"
|
|
13963
|
+
return 1
|
|
13964
|
+
fi
|
|
13965
|
+
|
|
13983
13966
|
# Always use bypassPermissions so Loki Mode can operate autonomously
|
|
13984
13967
|
rc_flags+=("--permission-mode" "bypassPermissions")
|
|
13985
13968
|
|
|
@@ -14636,19 +14619,19 @@ cmd_agent() {
|
|
|
14636
14619
|
local filter_swarm="${1:-}"
|
|
14637
14620
|
echo -e "${BOLD}Agent Types${NC}"
|
|
14638
14621
|
echo ""
|
|
14639
|
-
python3 << PYEOF
|
|
14640
|
-
import json, sys
|
|
14622
|
+
TYPES_FILE="$types_file" FILTER_SWARM="$filter_swarm" FILTER_SWARM2="${2:-}" python3 << 'PYEOF'
|
|
14623
|
+
import json, sys, os
|
|
14641
14624
|
|
|
14642
|
-
with open("
|
|
14625
|
+
with open(os.environ["TYPES_FILE"]) as f:
|
|
14643
14626
|
agents = json.load(f)
|
|
14644
14627
|
|
|
14645
|
-
filter_swarm = "
|
|
14628
|
+
filter_swarm = os.environ.get("FILTER_SWARM", "")
|
|
14646
14629
|
if filter_swarm and filter_swarm.startswith("--swarm"):
|
|
14647
14630
|
# Handle --swarm=X or --swarm X
|
|
14648
14631
|
if "=" in filter_swarm:
|
|
14649
14632
|
filter_swarm = filter_swarm.split("=")[1]
|
|
14650
14633
|
else:
|
|
14651
|
-
filter_swarm = "
|
|
14634
|
+
filter_swarm = os.environ.get("FILTER_SWARM2", "")
|
|
14652
14635
|
|
|
14653
14636
|
swarms = {}
|
|
14654
14637
|
for a in agents:
|
|
@@ -14678,13 +14661,13 @@ PYEOF
|
|
|
14678
14661
|
echo -e "${RED}Usage: loki agent info <type>${NC}"
|
|
14679
14662
|
return 1
|
|
14680
14663
|
fi
|
|
14681
|
-
python3 << PYEOF
|
|
14682
|
-
import json, sys
|
|
14664
|
+
TYPES_FILE="$types_file" AGENT_TYPE="$agent_type" python3 << 'PYEOF'
|
|
14665
|
+
import json, sys, os
|
|
14683
14666
|
|
|
14684
|
-
with open("
|
|
14667
|
+
with open(os.environ["TYPES_FILE"]) as f:
|
|
14685
14668
|
agents = json.load(f)
|
|
14686
14669
|
|
|
14687
|
-
target = "
|
|
14670
|
+
target = os.environ["AGENT_TYPE"]
|
|
14688
14671
|
found = None
|
|
14689
14672
|
for a in agents:
|
|
14690
14673
|
if a["type"] == target:
|
|
@@ -15757,7 +15740,7 @@ cmd_explain() {
|
|
|
15757
15740
|
|
|
15758
15741
|
while [[ $# -gt 0 ]]; do
|
|
15759
15742
|
case "$1" in
|
|
15760
|
-
--json) output_json=true; shift ;;
|
|
15743
|
+
--json) output_json=true; require_jq || return 1; shift ;;
|
|
15761
15744
|
--brief) output_brief=true; shift ;;
|
|
15762
15745
|
--save) output_save=true; shift ;;
|
|
15763
15746
|
--help|-h)
|
package/autonomy/run.sh
CHANGED
|
@@ -602,6 +602,12 @@ if [ -f "${SCRIPT_DIR}/playwright-verify.sh" ]; then
|
|
|
602
602
|
fi
|
|
603
603
|
|
|
604
604
|
# Anonymous usage telemetry (opt-out: LOKI_TELEMETRY_DISABLED=true or DO_NOT_TRACK=1)
|
|
605
|
+
# Also check persistent opt-out from ~/.loki/config (#77)
|
|
606
|
+
if [ -f "${HOME}/.loki/config" ] && grep -q "^TELEMETRY_DISABLED=true" "${HOME}/.loki/config" 2>/dev/null; then
|
|
607
|
+
LOKI_TELEMETRY_DISABLED=true
|
|
608
|
+
export LOKI_TELEMETRY_DISABLED
|
|
609
|
+
unset LOKI_OTEL_ENDPOINT
|
|
610
|
+
fi
|
|
605
611
|
TELEMETRY_SCRIPT="$SCRIPT_DIR/telemetry.sh"
|
|
606
612
|
if [ -f "$TELEMETRY_SCRIPT" ]; then
|
|
607
613
|
# shellcheck source=telemetry.sh
|
package/completions/_loki
CHANGED
|
@@ -44,8 +44,11 @@ function _loki {
|
|
|
44
44
|
projects)
|
|
45
45
|
_loki_projects
|
|
46
46
|
;;
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
telemetry)
|
|
48
|
+
_loki_telemetry
|
|
49
|
+
;;
|
|
50
|
+
agent)
|
|
51
|
+
_loki_agent
|
|
49
52
|
;;
|
|
50
53
|
status)
|
|
51
54
|
_arguments \
|
|
@@ -121,7 +124,8 @@ function _loki_commands {
|
|
|
121
124
|
'dogfood:Self-development statistics'
|
|
122
125
|
'projects:Project registry commands'
|
|
123
126
|
'enterprise:Enterprise feature commands'
|
|
124
|
-
'
|
|
127
|
+
'telemetry:OpenTelemetry management'
|
|
128
|
+
'agent:Agent type dispatch'
|
|
125
129
|
'doctor:Check system prerequisites'
|
|
126
130
|
'onboard:Analyze repo and generate CLAUDE.md'
|
|
127
131
|
'metrics:Session productivity report'
|
|
@@ -294,17 +298,29 @@ function _loki_projects {
|
|
|
294
298
|
_describe -t cmds 'projects subcommand' cmds
|
|
295
299
|
}
|
|
296
300
|
|
|
297
|
-
function
|
|
301
|
+
function _loki_telemetry {
|
|
302
|
+
local -a cmds
|
|
303
|
+
cmds=(
|
|
304
|
+
'status:Show telemetry config'
|
|
305
|
+
'enable:Enable OTEL'
|
|
306
|
+
'disable:Disable OTEL for current project'
|
|
307
|
+
'stop:Permanently opt out across all sessions'
|
|
308
|
+
'start:Remove persistent opt-out'
|
|
309
|
+
'help:Telemetry help'
|
|
310
|
+
)
|
|
311
|
+
_describe -t cmds 'telemetry subcommand' cmds
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function _loki_agent {
|
|
298
315
|
local -a cmds
|
|
299
316
|
cmds=(
|
|
300
|
-
'
|
|
301
|
-
'
|
|
302
|
-
'
|
|
303
|
-
'
|
|
304
|
-
'
|
|
305
|
-
'help:Voice help'
|
|
317
|
+
'list:List agent types'
|
|
318
|
+
'info:Show agent type details'
|
|
319
|
+
'run:Run agent with prompt'
|
|
320
|
+
'start:Start agent in background'
|
|
321
|
+
'help:Agent help'
|
|
306
322
|
)
|
|
307
|
-
_describe -t cmds '
|
|
323
|
+
_describe -t cmds 'agent subcommand' cmds
|
|
308
324
|
}
|
|
309
325
|
|
|
310
326
|
function _loki_reset {
|
package/completions/loki.bash
CHANGED
|
@@ -5,7 +5,7 @@ _loki_completion() {
|
|
|
5
5
|
_init_completion || return
|
|
6
6
|
|
|
7
7
|
# Main subcommands (must match autonomy/loki main case statement)
|
|
8
|
-
local main_commands="start quick demo init stop pause resume status dashboard logs serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise
|
|
8
|
+
local main_commands="start quick demo init stop pause resume status dashboard logs serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets doctor watchdog audit metrics syslog onboard share explain plan report test ci watch telemetry agent version completions help"
|
|
9
9
|
|
|
10
10
|
# 1. If we are on the first argument (subcommand)
|
|
11
11
|
if [[ $cword -eq 1 ]]; then
|
|
@@ -83,9 +83,14 @@ _loki_completion() {
|
|
|
83
83
|
COMPREPLY=( $(compgen -W "${projects_cmds}" -- "$cur") )
|
|
84
84
|
;;
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
local
|
|
88
|
-
COMPREPLY=( $(compgen -W "${
|
|
86
|
+
telemetry)
|
|
87
|
+
local telemetry_cmds="status enable disable stop start help"
|
|
88
|
+
COMPREPLY=( $(compgen -W "${telemetry_cmds}" -- "$cur") )
|
|
89
|
+
;;
|
|
90
|
+
|
|
91
|
+
agent)
|
|
92
|
+
local agent_cmds="list info run start help"
|
|
93
|
+
COMPREPLY=( $(compgen -W "${agent_cmds}" -- "$cur") )
|
|
89
94
|
;;
|
|
90
95
|
|
|
91
96
|
syslog)
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -2662,10 +2662,43 @@ async def pause_session():
|
|
|
2662
2662
|
"""Pause the current session by creating PAUSE file."""
|
|
2663
2663
|
if not _control_limiter.check("control"):
|
|
2664
2664
|
raise HTTPException(status_code=429, detail="Rate limit exceeded")
|
|
2665
|
-
|
|
2665
|
+
|
|
2666
|
+
loki_dir = _get_loki_dir()
|
|
2667
|
+
pid_file = loki_dir / "loki.pid"
|
|
2668
|
+
|
|
2669
|
+
# Verify loki process is running before attempting pause
|
|
2670
|
+
process_running = False
|
|
2671
|
+
if pid_file.exists():
|
|
2672
|
+
try:
|
|
2673
|
+
pid = int(pid_file.read_text().strip())
|
|
2674
|
+
os.kill(pid, 0) # Signal 0: check existence without killing
|
|
2675
|
+
process_running = True
|
|
2676
|
+
except (ValueError, OSError, ProcessLookupError):
|
|
2677
|
+
pass
|
|
2678
|
+
|
|
2679
|
+
pause_file = loki_dir / "PAUSE"
|
|
2666
2680
|
pause_file.parent.mkdir(parents=True, exist_ok=True)
|
|
2667
2681
|
pause_file.write_text(datetime.now(timezone.utc).isoformat())
|
|
2668
|
-
|
|
2682
|
+
|
|
2683
|
+
if not process_running:
|
|
2684
|
+
return JSONResponse(
|
|
2685
|
+
status_code=503,
|
|
2686
|
+
content={"success": False, "message": "Session process is not running; pause signal may have no effect"},
|
|
2687
|
+
)
|
|
2688
|
+
|
|
2689
|
+
# Poll up to 5s to confirm process is still alive with PAUSE file present
|
|
2690
|
+
for _ in range(10):
|
|
2691
|
+
try:
|
|
2692
|
+
os.kill(pid, 0)
|
|
2693
|
+
return {"success": True, "message": "Session paused", "process_verified": True}
|
|
2694
|
+
except OSError:
|
|
2695
|
+
break
|
|
2696
|
+
await asyncio.sleep(0.5)
|
|
2697
|
+
|
|
2698
|
+
return JSONResponse(
|
|
2699
|
+
status_code=503,
|
|
2700
|
+
content={"success": False, "message": "Session process exited unexpectedly after pause signal"},
|
|
2701
|
+
)
|
|
2669
2702
|
|
|
2670
2703
|
|
|
2671
2704
|
@app.post("/api/control/resume", dependencies=[Depends(auth.require_scope("control"))])
|
|
@@ -2673,13 +2706,58 @@ async def resume_session():
|
|
|
2673
2706
|
"""Resume a paused session by removing PAUSE/STOP files."""
|
|
2674
2707
|
if not _control_limiter.check("control"):
|
|
2675
2708
|
raise HTTPException(status_code=429, detail="Rate limit exceeded")
|
|
2709
|
+
|
|
2710
|
+
loki_dir = _get_loki_dir()
|
|
2711
|
+
pid_file = loki_dir / "loki.pid"
|
|
2712
|
+
|
|
2713
|
+
# Verify loki process is running before attempting resume
|
|
2714
|
+
process_running = False
|
|
2715
|
+
pid = 0
|
|
2716
|
+
if pid_file.exists():
|
|
2717
|
+
try:
|
|
2718
|
+
pid = int(pid_file.read_text().strip())
|
|
2719
|
+
os.kill(pid, 0) # Signal 0: check existence without killing
|
|
2720
|
+
process_running = True
|
|
2721
|
+
except (ValueError, OSError, ProcessLookupError):
|
|
2722
|
+
pass
|
|
2723
|
+
|
|
2724
|
+
if not process_running:
|
|
2725
|
+
# Still remove the files for cleanup, but return 503
|
|
2726
|
+
for fname in ["PAUSE", "STOP"]:
|
|
2727
|
+
fpath = loki_dir / fname
|
|
2728
|
+
try:
|
|
2729
|
+
fpath.unlink(missing_ok=True)
|
|
2730
|
+
except Exception:
|
|
2731
|
+
pass
|
|
2732
|
+
return JSONResponse(
|
|
2733
|
+
status_code=503,
|
|
2734
|
+
content={"success": False, "message": "Session did not respond to resume signal"},
|
|
2735
|
+
)
|
|
2736
|
+
|
|
2676
2737
|
for fname in ["PAUSE", "STOP"]:
|
|
2677
|
-
fpath =
|
|
2738
|
+
fpath = loki_dir / fname
|
|
2678
2739
|
try:
|
|
2679
2740
|
fpath.unlink(missing_ok=True)
|
|
2680
2741
|
except Exception:
|
|
2681
2742
|
pass
|
|
2682
|
-
|
|
2743
|
+
|
|
2744
|
+
# Poll up to 5s to verify the process is still running and acknowledged the resume
|
|
2745
|
+
for _ in range(10):
|
|
2746
|
+
try:
|
|
2747
|
+
os.kill(pid, 0)
|
|
2748
|
+
if not (loki_dir / "PAUSE").exists():
|
|
2749
|
+
return {"success": True, "message": "Session resumed", "process_verified": True}
|
|
2750
|
+
except OSError:
|
|
2751
|
+
return JSONResponse(
|
|
2752
|
+
status_code=503,
|
|
2753
|
+
content={"success": False, "message": "Session did not respond to resume signal"},
|
|
2754
|
+
)
|
|
2755
|
+
await asyncio.sleep(0.5)
|
|
2756
|
+
|
|
2757
|
+
return JSONResponse(
|
|
2758
|
+
status_code=503,
|
|
2759
|
+
content={"success": False, "message": "Session did not respond to resume signal"},
|
|
2760
|
+
)
|
|
2683
2761
|
|
|
2684
2762
|
|
|
2685
2763
|
@app.post("/api/control/stop", dependencies=[Depends(auth.require_scope("control"))])
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED