loki-mode 5.57.0 → 5.58.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/autonomy/loki +125 -16
- package/autonomy/run.sh +149 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/migration_engine.py +3 -1
- 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 v5.
|
|
6
|
+
# Loki Mode v5.58.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.58.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.58.0
|
package/autonomy/loki
CHANGED
|
@@ -4854,7 +4854,7 @@ cmd_migrate_help() {
|
|
|
4854
4854
|
}
|
|
4855
4855
|
|
|
4856
4856
|
cmd_migrate_list() {
|
|
4857
|
-
python3 -c "
|
|
4857
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
4858
4858
|
import sys
|
|
4859
4859
|
sys.path.insert(0, '.')
|
|
4860
4860
|
from dashboard.migration_engine import list_migrations
|
|
@@ -4900,7 +4900,7 @@ cmd_migrate_status() {
|
|
|
4900
4900
|
return 1
|
|
4901
4901
|
fi
|
|
4902
4902
|
|
|
4903
|
-
python3 -c "
|
|
4903
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
4904
4904
|
import json, sys
|
|
4905
4905
|
sys.path.insert(0, '.')
|
|
4906
4906
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -4955,7 +4955,7 @@ print(data.get('id', ''))
|
|
|
4955
4955
|
" "$plan_file" 2>/dev/null || echo "")
|
|
4956
4956
|
|
|
4957
4957
|
if [ -n "$migration_id_for_plan" ]; then
|
|
4958
|
-
python3 -c "
|
|
4958
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
4959
4959
|
import sys
|
|
4960
4960
|
sys.path.insert(0, '.')
|
|
4961
4961
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -5121,7 +5121,7 @@ if manifests:
|
|
|
5121
5121
|
|
|
5122
5122
|
# Create migration via engine
|
|
5123
5123
|
local create_result
|
|
5124
|
-
create_result=$(python3 -c "
|
|
5124
|
+
create_result=$(PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
5125
5125
|
import json, sys
|
|
5126
5126
|
sys.path.insert(0, '.')
|
|
5127
5127
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -5215,7 +5215,7 @@ print(json.dumps({'id': manifest.id, 'dir': str(pipeline.migration_dir)}))
|
|
|
5215
5215
|
echo -e "${CYAN}[Phase: ${p}]${NC} Starting..."
|
|
5216
5216
|
|
|
5217
5217
|
# Start phase via engine
|
|
5218
|
-
python3 -c "
|
|
5218
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
5219
5219
|
import sys
|
|
5220
5220
|
sys.path.insert(0, '.')
|
|
5221
5221
|
from dashboard.migration_engine import MigrationPipeline
|
|
@@ -5226,31 +5226,140 @@ pipeline.start_phase(sys.argv[2])
|
|
|
5226
5226
|
return 1
|
|
5227
5227
|
}
|
|
5228
5228
|
|
|
5229
|
-
# Phase-specific logic
|
|
5229
|
+
# Phase-specific logic -- invoke Claude to do real work
|
|
5230
|
+
local phase_prompt=""
|
|
5230
5231
|
case "$p" in
|
|
5231
5232
|
understand)
|
|
5232
5233
|
echo -e " ${DIM}Analyzing codebase structure...${NC}"
|
|
5233
|
-
|
|
5234
|
-
|
|
5234
|
+
phase_prompt="You are performing the UNDERSTAND phase of a codebase migration.
|
|
5235
|
+
Target: ${target}
|
|
5236
|
+
Codebase: ${codebase_path}
|
|
5237
|
+
|
|
5238
|
+
Tasks:
|
|
5239
|
+
1. Analyze the full codebase structure (languages, frameworks, dependencies, architecture)
|
|
5240
|
+
2. Create the docs directory: mkdir -p ${migration_dir}/docs
|
|
5241
|
+
3. Write analysis documentation to ${migration_dir}/docs/analysis.md
|
|
5242
|
+
3. Identify migration seams (logical boundaries for incremental migration) and write them to ${migration_dir}/seams.json as a JSON array of objects with fields: id, name, description, files (array of file paths), dependencies (array of seam ids), priority (high/medium/low)
|
|
5243
|
+
|
|
5244
|
+
You MUST create both files. The migration cannot proceed without them.
|
|
5245
|
+
Write the analysis doc first, then the seams.json."
|
|
5235
5246
|
;;
|
|
5236
5247
|
guardrail)
|
|
5237
|
-
echo -e " ${DIM}
|
|
5238
|
-
|
|
5239
|
-
|
|
5248
|
+
echo -e " ${DIM}Setting up safety nets and characterization tests...${NC}"
|
|
5249
|
+
phase_prompt="You are performing the GUARDRAIL phase of a codebase migration.
|
|
5250
|
+
Target: ${target}
|
|
5251
|
+
Codebase: ${codebase_path}
|
|
5252
|
+
Migration dir: ${migration_dir}
|
|
5253
|
+
|
|
5254
|
+
Read ${migration_dir}/docs/analysis.md and ${migration_dir}/seams.json for context.
|
|
5255
|
+
|
|
5256
|
+
Tasks:
|
|
5257
|
+
1. Identify existing tests and create characterization tests that capture current behavior
|
|
5258
|
+
2. Write ${migration_dir}/features.json as a JSON array of objects with fields: id, name, description, test_command (shell command to verify), passes (boolean, set to true for existing passing behavior)
|
|
5259
|
+
3. Create a git checkpoint: cd ${codebase_path} && git stash || true
|
|
5260
|
+
|
|
5261
|
+
All features in features.json must have passes: true for the gate to pass."
|
|
5240
5262
|
;;
|
|
5241
5263
|
migrate)
|
|
5242
|
-
echo -e " ${DIM}Executing migration transforms
|
|
5243
|
-
|
|
5264
|
+
echo -e " ${DIM}Executing migration transforms...${NC}"
|
|
5265
|
+
phase_prompt="You are performing the MIGRATE phase of a codebase migration.
|
|
5266
|
+
Target: ${target}
|
|
5267
|
+
Codebase: ${codebase_path}
|
|
5268
|
+
Migration dir: ${migration_dir}
|
|
5269
|
+
|
|
5270
|
+
Read ${migration_dir}/docs/analysis.md, ${migration_dir}/seams.json, and ${migration_dir}/features.json for context.
|
|
5271
|
+
|
|
5272
|
+
Tasks:
|
|
5273
|
+
1. Create a migration plan and write it to ${migration_dir}/migration-plan.json with fields: target, source_path, steps (array of objects with: id, name, description, status set to 'completed' after you do the step)
|
|
5274
|
+
2. Execute the actual code migration transforms in ${codebase_path} -- convert code from the current framework/language to ${target}
|
|
5275
|
+
3. Update each step status to 'completed' as you finish it
|
|
5276
|
+
4. Work incrementally seam by seam from ${migration_dir}/seams.json
|
|
5277
|
+
|
|
5278
|
+
All steps must have status: completed for the gate to pass."
|
|
5244
5279
|
;;
|
|
5245
5280
|
verify)
|
|
5246
5281
|
echo -e " ${DIM}Running verification suite...${NC}"
|
|
5247
|
-
|
|
5248
|
-
|
|
5282
|
+
phase_prompt="You are performing the VERIFY phase of a codebase migration.
|
|
5283
|
+
Target: ${target}
|
|
5284
|
+
Codebase: ${codebase_path}
|
|
5285
|
+
Migration dir: ${migration_dir}
|
|
5286
|
+
|
|
5287
|
+
Tasks:
|
|
5288
|
+
1. Run syntax validation on all migrated files
|
|
5289
|
+
2. Run any test commands from ${migration_dir}/features.json to verify behavior is preserved
|
|
5290
|
+
3. Check for common migration issues (missing imports, broken references, etc.)
|
|
5291
|
+
4. Write a verification report to ${migration_dir}/docs/verification-report.md
|
|
5292
|
+
5. Summarize what was migrated, what works, and any remaining issues"
|
|
5249
5293
|
;;
|
|
5250
5294
|
esac
|
|
5251
5295
|
|
|
5296
|
+
# Invoke Claude to execute the phase
|
|
5297
|
+
local phase_exit=0
|
|
5298
|
+
if [ -n "$phase_prompt" ]; then
|
|
5299
|
+
local provider_name="${LOKI_PROVIDER:-claude}"
|
|
5300
|
+
case "$provider_name" in
|
|
5301
|
+
claude)
|
|
5302
|
+
(cd "$codebase_path" && claude --dangerously-skip-permissions -p "$phase_prompt" --output-format stream-json --verbose 2>&1) | \
|
|
5303
|
+
while IFS= read -r line; do
|
|
5304
|
+
# Extract text from stream-json
|
|
5305
|
+
if echo "$line" | python3 -c "
|
|
5306
|
+
import sys, json
|
|
5307
|
+
try:
|
|
5308
|
+
d = json.loads(sys.stdin.read())
|
|
5309
|
+
if d.get('type') == 'assistant':
|
|
5310
|
+
for item in d.get('message', {}).get('content', []):
|
|
5311
|
+
if item.get('type') == 'text':
|
|
5312
|
+
print(item.get('text', ''), end='')
|
|
5313
|
+
except Exception: pass
|
|
5314
|
+
" 2>/dev/null; then
|
|
5315
|
+
true
|
|
5316
|
+
fi
|
|
5317
|
+
done
|
|
5318
|
+
phase_exit=${PIPESTATUS[0]}
|
|
5319
|
+
;;
|
|
5320
|
+
codex)
|
|
5321
|
+
(cd "$codebase_path" && codex exec --full-auto "$phase_prompt" 2>&1) || phase_exit=$?
|
|
5322
|
+
;;
|
|
5323
|
+
gemini)
|
|
5324
|
+
(cd "$codebase_path" && gemini --approval-mode=yolo "$phase_prompt" 2>&1) || phase_exit=$?
|
|
5325
|
+
;;
|
|
5326
|
+
esac
|
|
5327
|
+
fi
|
|
5328
|
+
|
|
5329
|
+
# Verify phase gate artifacts exist before advancing
|
|
5330
|
+
local gate_ok=true
|
|
5331
|
+
case "$p" in
|
|
5332
|
+
understand)
|
|
5333
|
+
if [ ! -f "${migration_dir}/docs/analysis.md" ] || [ ! -f "${migration_dir}/seams.json" ]; then
|
|
5334
|
+
echo -e "${RED}Error: Phase 'understand' did not produce required artifacts${NC}"
|
|
5335
|
+
echo -e "${DIM} Expected: docs/analysis.md, seams.json${NC}"
|
|
5336
|
+
gate_ok=false
|
|
5337
|
+
fi
|
|
5338
|
+
;;
|
|
5339
|
+
guardrail)
|
|
5340
|
+
if [ ! -f "${migration_dir}/features.json" ]; then
|
|
5341
|
+
echo -e "${RED}Error: Phase 'guardrail' did not produce required artifacts${NC}"
|
|
5342
|
+
echo -e "${DIM} Expected: features.json${NC}"
|
|
5343
|
+
gate_ok=false
|
|
5344
|
+
fi
|
|
5345
|
+
;;
|
|
5346
|
+
migrate)
|
|
5347
|
+
if [ ! -f "${migration_dir}/migration-plan.json" ]; then
|
|
5348
|
+
echo -e "${RED}Error: Phase 'migrate' did not produce required artifacts${NC}"
|
|
5349
|
+
echo -e "${DIM} Expected: migration-plan.json${NC}"
|
|
5350
|
+
gate_ok=false
|
|
5351
|
+
fi
|
|
5352
|
+
;;
|
|
5353
|
+
esac
|
|
5354
|
+
|
|
5355
|
+
if [ "$gate_ok" != "true" ]; then
|
|
5356
|
+
echo -e "${YELLOW}Phase '$p' artifacts missing. Migration halted.${NC}"
|
|
5357
|
+
echo -e "${DIM}Re-run with: loki migrate <path> --target ${target} --phase ${p} --resume${NC}"
|
|
5358
|
+
return 1
|
|
5359
|
+
fi
|
|
5360
|
+
|
|
5252
5361
|
# Complete phase via engine
|
|
5253
|
-
python3 -c "
|
|
5362
|
+
PYTHONPATH="${SKILL_DIR:-.}" python3 -c "
|
|
5254
5363
|
import sys
|
|
5255
5364
|
sys.path.insert(0, '.')
|
|
5256
5365
|
from dashboard.migration_engine import MigrationPipeline
|
package/autonomy/run.sh
CHANGED
|
@@ -5556,6 +5556,76 @@ stop_dashboard() {
|
|
|
5556
5556
|
fi
|
|
5557
5557
|
}
|
|
5558
5558
|
|
|
5559
|
+
# Handle dashboard crash: restart silently without triggering pause handler
|
|
5560
|
+
# This prevents a killed dashboard from being misinterpreted as a user interrupt
|
|
5561
|
+
handle_dashboard_crash() {
|
|
5562
|
+
if [[ "${ENABLE_DASHBOARD:-true}" != "true" ]]; then
|
|
5563
|
+
return 0
|
|
5564
|
+
fi
|
|
5565
|
+
|
|
5566
|
+
local dashboard_pid_file=".loki/dashboard/dashboard.pid"
|
|
5567
|
+
if [[ ! -f "$dashboard_pid_file" ]]; then
|
|
5568
|
+
return 0
|
|
5569
|
+
fi
|
|
5570
|
+
|
|
5571
|
+
local dpid
|
|
5572
|
+
dpid=$(cat "$dashboard_pid_file" 2>/dev/null)
|
|
5573
|
+
if [[ -z "$dpid" ]]; then
|
|
5574
|
+
return 0
|
|
5575
|
+
fi
|
|
5576
|
+
|
|
5577
|
+
# Dashboard is still alive, nothing to do
|
|
5578
|
+
if kill -0 "$dpid" 2>/dev/null; then
|
|
5579
|
+
return 0
|
|
5580
|
+
fi
|
|
5581
|
+
|
|
5582
|
+
# Dashboard is dead -- restart it silently (with throttle)
|
|
5583
|
+
DASHBOARD_RESTART_COUNT=${DASHBOARD_RESTART_COUNT:-0}
|
|
5584
|
+
local max_restarts=${DASHBOARD_MAX_RESTARTS:-3}
|
|
5585
|
+
|
|
5586
|
+
if [ "$DASHBOARD_RESTART_COUNT" -ge "$max_restarts" ]; then
|
|
5587
|
+
log_warn "Dashboard restart limit reached ($max_restarts) - disabling dashboard for this session"
|
|
5588
|
+
ENABLE_DASHBOARD=false
|
|
5589
|
+
return 1
|
|
5590
|
+
fi
|
|
5591
|
+
|
|
5592
|
+
DASHBOARD_RESTART_COUNT=$((DASHBOARD_RESTART_COUNT + 1))
|
|
5593
|
+
log_info "Dashboard process $dpid exited, restarting silently (attempt $DASHBOARD_RESTART_COUNT/$max_restarts)..."
|
|
5594
|
+
emit_event_json "dashboard_crash" \
|
|
5595
|
+
"pid=$dpid" \
|
|
5596
|
+
"action=auto_restart" \
|
|
5597
|
+
"attempt=$DASHBOARD_RESTART_COUNT" \
|
|
5598
|
+
"autonomy_mode=$AUTONOMY_MODE"
|
|
5599
|
+
DASHBOARD_PID=""
|
|
5600
|
+
rm -f "$dashboard_pid_file"
|
|
5601
|
+
start_dashboard
|
|
5602
|
+
return 0
|
|
5603
|
+
}
|
|
5604
|
+
|
|
5605
|
+
# Check if a signal was caused by a child process dying (e.g., dashboard)
|
|
5606
|
+
# rather than an actual user interrupt. Returns 0 if it was a child crash
|
|
5607
|
+
# (handled silently), 1 if it was a real interrupt.
|
|
5608
|
+
is_child_process_signal() {
|
|
5609
|
+
# If dashboard PID is set and dashboard is now dead, this signal
|
|
5610
|
+
# was likely caused by the dashboard process exiting
|
|
5611
|
+
if [ -n "$DASHBOARD_PID" ] && ! kill -0 "$DASHBOARD_PID" 2>/dev/null; then
|
|
5612
|
+
handle_dashboard_crash
|
|
5613
|
+
return 0
|
|
5614
|
+
fi
|
|
5615
|
+
|
|
5616
|
+
# Check PID file as fallback
|
|
5617
|
+
if [ -f ".loki/dashboard/dashboard.pid" ]; then
|
|
5618
|
+
local dpid
|
|
5619
|
+
dpid=$(cat ".loki/dashboard/dashboard.pid" 2>/dev/null)
|
|
5620
|
+
if [ -n "$dpid" ] && ! kill -0 "$dpid" 2>/dev/null; then
|
|
5621
|
+
handle_dashboard_crash
|
|
5622
|
+
return 0
|
|
5623
|
+
fi
|
|
5624
|
+
fi
|
|
5625
|
+
|
|
5626
|
+
return 1
|
|
5627
|
+
}
|
|
5628
|
+
|
|
5559
5629
|
#===============================================================================
|
|
5560
5630
|
# Calculate Exponential Backoff
|
|
5561
5631
|
#===============================================================================
|
|
@@ -7171,6 +7241,9 @@ if __name__ == "__main__":
|
|
|
7171
7241
|
# Update session continuity file for next iteration / agent handoff
|
|
7172
7242
|
update_continuity
|
|
7173
7243
|
|
|
7244
|
+
# Checkpoint after each iteration (v5.57.0)
|
|
7245
|
+
create_checkpoint "iteration-${ITERATION_COUNT} complete" "iteration-${ITERATION_COUNT}"
|
|
7246
|
+
|
|
7174
7247
|
# Code review gate (v5.35.0)
|
|
7175
7248
|
if [ "$PHASE_CODE_REVIEW" = "true" ] && [ "$ITERATION_COUNT" -gt 0 ]; then
|
|
7176
7249
|
run_code_review || log_warn "Code review found issues - check .loki/quality/reviews/"
|
|
@@ -7240,6 +7313,9 @@ if __name__ == "__main__":
|
|
|
7240
7313
|
local goal_desc="${prd_path:-codebase-analysis}"
|
|
7241
7314
|
store_episode_trace "$task_id" "failure" "iteration" "$goal_desc" "$duration"
|
|
7242
7315
|
|
|
7316
|
+
# Checkpoint failed iteration state (v5.57.0)
|
|
7317
|
+
create_checkpoint "iteration-${ITERATION_COUNT} failed (exit=$exit_code)" "iteration-${ITERATION_COUNT}-fail"
|
|
7318
|
+
|
|
7243
7319
|
# Handle retry - check for rate limit first
|
|
7244
7320
|
local rate_limit_wait=$(detect_rate_limit "$log_file")
|
|
7245
7321
|
local wait_time
|
|
@@ -7299,6 +7375,15 @@ check_human_intervention() {
|
|
|
7299
7375
|
# handle_pause returns 1 if STOP was requested during the pause, so we must
|
|
7300
7376
|
# propagate that as return 2 (stop) instead of always returning 1 (continue).
|
|
7301
7377
|
if [ -f "$loki_dir/PAUSE" ]; then
|
|
7378
|
+
# In perpetual mode: auto-clear PAUSE files and continue without waiting
|
|
7379
|
+
if [ "$AUTONOMY_MODE" = "perpetual" ] || [ "$PERPETUAL_MODE" = "true" ]; then
|
|
7380
|
+
log_warn "PAUSE file detected but autonomy mode is perpetual - auto-clearing"
|
|
7381
|
+
notify_intervention_needed "PAUSE file auto-cleared in perpetual mode" 2>/dev/null || true
|
|
7382
|
+
rm -f "$loki_dir/PAUSE" "$loki_dir/PAUSED.md"
|
|
7383
|
+
# Restart dashboard if it crashed (likely cause of the PAUSE)
|
|
7384
|
+
handle_dashboard_crash
|
|
7385
|
+
return 0
|
|
7386
|
+
fi
|
|
7302
7387
|
log_warn "PAUSE file detected - pausing execution"
|
|
7303
7388
|
notify_intervention_needed "Execution paused via PAUSE file"
|
|
7304
7389
|
handle_pause
|
|
@@ -7311,6 +7396,26 @@ check_human_intervention() {
|
|
|
7311
7396
|
return 1
|
|
7312
7397
|
fi
|
|
7313
7398
|
|
|
7399
|
+
# Check for PAUSE_AT_CHECKPOINT (checkpoint mode deferred pause)
|
|
7400
|
+
if [ -f "$loki_dir/PAUSE_AT_CHECKPOINT" ]; then
|
|
7401
|
+
if [ "$AUTONOMY_MODE" = "checkpoint" ]; then
|
|
7402
|
+
log_warn "Checkpoint pause requested - pausing now"
|
|
7403
|
+
rm -f "$loki_dir/PAUSE_AT_CHECKPOINT"
|
|
7404
|
+
notify_intervention_needed "Execution paused at checkpoint"
|
|
7405
|
+
touch "$loki_dir/PAUSE"
|
|
7406
|
+
handle_pause
|
|
7407
|
+
local pause_result=$?
|
|
7408
|
+
rm -f "$loki_dir/PAUSE"
|
|
7409
|
+
if [ "$pause_result" -eq 1 ]; then
|
|
7410
|
+
return 2
|
|
7411
|
+
fi
|
|
7412
|
+
return 1
|
|
7413
|
+
else
|
|
7414
|
+
# Clean up stale checkpoint pause file
|
|
7415
|
+
rm -f "$loki_dir/PAUSE_AT_CHECKPOINT"
|
|
7416
|
+
fi
|
|
7417
|
+
fi
|
|
7418
|
+
|
|
7314
7419
|
# Check for HUMAN_INPUT.md (prompt injection)
|
|
7315
7420
|
# Security: Check it's a regular file (not symlink) to prevent symlink attacks
|
|
7316
7421
|
if [ -f "$loki_dir/HUMAN_INPUT.md" ] && [ ! -L "$loki_dir/HUMAN_INPUT.md" ]; then
|
|
@@ -7516,7 +7621,46 @@ except (json.JSONDecodeError, OSError): pass
|
|
|
7516
7621
|
# Re-enable signals for pause mode
|
|
7517
7622
|
trap cleanup INT TERM
|
|
7518
7623
|
|
|
7519
|
-
#
|
|
7624
|
+
# Check if this signal was caused by a child process dying (e.g., dashboard)
|
|
7625
|
+
# rather than an actual user interrupt. In that case, handle silently.
|
|
7626
|
+
if is_child_process_signal; then
|
|
7627
|
+
log_info "Child process exit detected, handled silently"
|
|
7628
|
+
INTERRUPT_COUNT=0
|
|
7629
|
+
return
|
|
7630
|
+
fi
|
|
7631
|
+
|
|
7632
|
+
# In perpetual/autonomous mode: NEVER pause, NEVER wait for input
|
|
7633
|
+
# Log the interrupt but continue the iteration loop immediately
|
|
7634
|
+
if [ "$AUTONOMY_MODE" = "perpetual" ] || [ "$PERPETUAL_MODE" = "true" ]; then
|
|
7635
|
+
INTERRUPT_COUNT=$((INTERRUPT_COUNT + 1))
|
|
7636
|
+
INTERRUPT_LAST_TIME=$current_time
|
|
7637
|
+
echo ""
|
|
7638
|
+
log_warn "Interrupt received in perpetual mode - ignoring (not pausing)"
|
|
7639
|
+
log_info "To stop: touch .loki/STOP or press Ctrl+C twice within 2 seconds"
|
|
7640
|
+
echo ""
|
|
7641
|
+
# Check and restart dashboard if it died
|
|
7642
|
+
handle_dashboard_crash
|
|
7643
|
+
# Do NOT reset INTERRUPT_COUNT -- let it accumulate so double-Ctrl+C escape works
|
|
7644
|
+
return
|
|
7645
|
+
fi
|
|
7646
|
+
|
|
7647
|
+
# In checkpoint mode: only pause at explicit checkpoint boundaries, not on
|
|
7648
|
+
# random signals. A signal during normal execution is treated as noise.
|
|
7649
|
+
if [ "$AUTONOMY_MODE" = "checkpoint" ]; then
|
|
7650
|
+
INTERRUPT_COUNT=$((INTERRUPT_COUNT + 1))
|
|
7651
|
+
INTERRUPT_LAST_TIME=$current_time
|
|
7652
|
+
echo ""
|
|
7653
|
+
log_warn "Interrupt received in checkpoint mode - will pause at next checkpoint"
|
|
7654
|
+
log_info "To stop immediately: press Ctrl+C again within 2 seconds"
|
|
7655
|
+
echo ""
|
|
7656
|
+
# Mark that a pause was requested for the next checkpoint
|
|
7657
|
+
touch "${TARGET_DIR:-.}/.loki/PAUSE_AT_CHECKPOINT"
|
|
7658
|
+
handle_dashboard_crash
|
|
7659
|
+
# Do NOT reset INTERRUPT_COUNT -- let it accumulate so double-Ctrl+C escape works
|
|
7660
|
+
return
|
|
7661
|
+
fi
|
|
7662
|
+
|
|
7663
|
+
# Supervised mode (or unrecognized): original behavior - pause and show options
|
|
7520
7664
|
INTERRUPT_COUNT=$((INTERRUPT_COUNT + 1))
|
|
7521
7665
|
INTERRUPT_LAST_TIME=$current_time
|
|
7522
7666
|
|
|
@@ -7937,6 +8081,10 @@ main() {
|
|
|
7937
8081
|
# Compound learnings into structured solution files (v5.30.0)
|
|
7938
8082
|
compound_session_to_solutions
|
|
7939
8083
|
|
|
8084
|
+
# Log checkpoint count before final checkpoint (v5.57.0)
|
|
8085
|
+
local cp_count=$(find .loki/state/checkpoints -maxdepth 1 -type d -name "cp-*" 2>/dev/null | wc -l | tr -d ' ')
|
|
8086
|
+
log_info "Session checkpoints: ${cp_count}"
|
|
8087
|
+
|
|
7940
8088
|
# Create session-end checkpoint (v5.34.0)
|
|
7941
8089
|
create_checkpoint "session end (iterations=$ITERATION_COUNT)" "session-end"
|
|
7942
8090
|
|
package/dashboard/__init__.py
CHANGED
|
@@ -307,12 +307,14 @@ class MigrationPipeline:
|
|
|
307
307
|
return phase_data.get("status", "pending")
|
|
308
308
|
|
|
309
309
|
def start_phase(self, phase: str) -> None:
|
|
310
|
-
"""Start a phase (transition from pending to in_progress)."""
|
|
310
|
+
"""Start a phase (transition from pending to in_progress). Idempotent if already in_progress."""
|
|
311
311
|
if phase not in PHASE_ORDER:
|
|
312
312
|
raise ValueError(f"Unknown phase: {phase}")
|
|
313
313
|
with self._lock:
|
|
314
314
|
manifest = self._load_manifest_unlocked()
|
|
315
315
|
current_status = manifest.phases[phase]["status"]
|
|
316
|
+
if current_status == "in_progress":
|
|
317
|
+
return # Already started, idempotent
|
|
316
318
|
if current_status != "pending":
|
|
317
319
|
raise RuntimeError(
|
|
318
320
|
f"Cannot start phase '{phase}': status is '{current_status}', expected 'pending'"
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED