loki-mode 6.81.0 → 6.82.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 +196 -55
- package/autonomy/run.sh +249 -44
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/mcp/server.py +105 -0
- 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.
|
|
6
|
+
# Loki Mode v6.82.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -269,4 +269,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
269
269
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
270
270
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
271
271
|
|
|
272
|
-
**v6.
|
|
272
|
+
**v6.82.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.
|
|
1
|
+
6.82.0
|
package/autonomy/loki
CHANGED
|
@@ -522,6 +522,7 @@ cmd_start() {
|
|
|
522
522
|
local mirofish_docker_image=""
|
|
523
523
|
local mirofish_bg=false
|
|
524
524
|
local mirofish_disabled=false
|
|
525
|
+
local no_plan=false # v6.81.1: --no-plan opts out of auto-plan display
|
|
525
526
|
|
|
526
527
|
while [[ $# -gt 0 ]]; do
|
|
527
528
|
case "$1" in
|
|
@@ -554,6 +555,7 @@ cmd_start() {
|
|
|
554
555
|
echo " --mirofish-timeout S Max pipeline wait in seconds (default: 3600)"
|
|
555
556
|
echo " --mirofish-bg Run MiroFish pipeline in background"
|
|
556
557
|
echo " --no-mirofish Disable MiroFish even if env var is set"
|
|
558
|
+
echo " --no-plan Skip auto-shown PRD analysis at startup"
|
|
557
559
|
echo " --yes, -y Skip confirmation prompts (auto-confirm)"
|
|
558
560
|
echo ""
|
|
559
561
|
echo "Environment Variables:"
|
|
@@ -776,6 +778,10 @@ cmd_start() {
|
|
|
776
778
|
mirofish_disabled=true
|
|
777
779
|
shift
|
|
778
780
|
;;
|
|
781
|
+
--no-plan)
|
|
782
|
+
no_plan=true
|
|
783
|
+
shift
|
|
784
|
+
;;
|
|
779
785
|
--budget)
|
|
780
786
|
if [[ -n "${2:-}" ]]; then
|
|
781
787
|
if ! echo "$2" | grep -qE '^[0-9]+(\.[0-9]+)?$'; then
|
|
@@ -1032,6 +1038,13 @@ cmd_start() {
|
|
|
1032
1038
|
fi
|
|
1033
1039
|
fi
|
|
1034
1040
|
|
|
1041
|
+
# v6.81.1: auto-show PRD analysis so users see complexity / cost / time
|
|
1042
|
+
# up front without needing a separate `loki plan` invocation. Skipped via
|
|
1043
|
+
# --no-plan or in non-TTY contexts (CI/pipe). No-op when PRD is absent.
|
|
1044
|
+
if [ -n "$prd_file" ]; then
|
|
1045
|
+
maybe_show_auto_plan "$prd_file" "$no_plan" || true
|
|
1046
|
+
fi
|
|
1047
|
+
|
|
1035
1048
|
# Load saved provider if not specified on command line
|
|
1036
1049
|
if [ -z "$provider" ]; then
|
|
1037
1050
|
if [ -f "$LOKI_DIR/state/provider" ]; then
|
|
@@ -4083,6 +4096,7 @@ cmd_run() {
|
|
|
4083
4096
|
echo " --complex Force complex complexity tier"
|
|
4084
4097
|
echo " --no-dashboard Disable web dashboard"
|
|
4085
4098
|
echo " --sandbox Run in Docker sandbox"
|
|
4099
|
+
echo " --no-plan Skip auto-shown PRD analysis at startup"
|
|
4086
4100
|
echo " --budget USD Set cost budget limit"
|
|
4087
4101
|
echo ""
|
|
4088
4102
|
echo "Progressive Isolation:"
|
|
@@ -4155,7 +4169,7 @@ cmd_run() {
|
|
|
4155
4169
|
start_args+=("--provider" "$provider_override")
|
|
4156
4170
|
shift
|
|
4157
4171
|
;;
|
|
4158
|
-
--parallel|--bg|--background|--simple|--complex|--no-dashboard|--sandbox)
|
|
4172
|
+
--parallel|--bg|--background|--simple|--complex|--no-dashboard|--sandbox|--no-plan)
|
|
4159
4173
|
start_args+=("$1")
|
|
4160
4174
|
shift
|
|
4161
4175
|
;;
|
|
@@ -4394,6 +4408,29 @@ INNER_SCRIPT_EOF
|
|
|
4394
4408
|
echo "$prd_content" > "$output_file"
|
|
4395
4409
|
echo -e "${GREEN}PRD generated:${NC} $output_file"
|
|
4396
4410
|
|
|
4411
|
+
# v6.81.1: auto-show PRD analysis once the issue-derived PRD is on disk,
|
|
4412
|
+
# so users see cost / complexity / time before execution begins. When the
|
|
4413
|
+
# user will continue into `cmd_start` below, cmd_start also has its own
|
|
4414
|
+
# auto-plan call -- but that second call respects --no-plan / TTY checks
|
|
4415
|
+
# and would typically re-emit the same analysis. We therefore pass
|
|
4416
|
+
# --no-plan along in start_args to prevent duplicate output. Passing
|
|
4417
|
+
# --no-plan here is safe because `cmd_run` itself already displayed the
|
|
4418
|
+
# plan.
|
|
4419
|
+
local _run_no_plan=false
|
|
4420
|
+
for _arg in "${start_args[@]+"${start_args[@]}"}"; do
|
|
4421
|
+
if [[ "$_arg" == "--no-plan" ]]; then
|
|
4422
|
+
_run_no_plan=true
|
|
4423
|
+
break
|
|
4424
|
+
fi
|
|
4425
|
+
done
|
|
4426
|
+
if [ -f "$output_file" ]; then
|
|
4427
|
+
maybe_show_auto_plan "$output_file" "$_run_no_plan" || true
|
|
4428
|
+
fi
|
|
4429
|
+
# Ensure cmd_start does not re-show the plan now that we just did.
|
|
4430
|
+
if [[ "$_run_no_plan" != "true" ]]; then
|
|
4431
|
+
start_args+=("--no-plan")
|
|
4432
|
+
fi
|
|
4433
|
+
|
|
4397
4434
|
# Start Loki Mode unless --no-start
|
|
4398
4435
|
if [[ "$no_start" == "true" ]]; then
|
|
4399
4436
|
echo ""
|
|
@@ -10524,58 +10561,25 @@ with open('$failover_file', 'w') as f: json.dump(d, f, indent=2)
|
|
|
10524
10561
|
done
|
|
10525
10562
|
}
|
|
10526
10563
|
|
|
10527
|
-
#
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
echo "Options:"
|
|
10544
|
-
echo " --json Machine-readable JSON output"
|
|
10545
|
-
echo " --verbose Show detailed per-iteration breakdown"
|
|
10546
|
-
echo " --help, -h Show this help"
|
|
10547
|
-
echo ""
|
|
10548
|
-
echo "Examples:"
|
|
10549
|
-
echo " loki plan ./prd.md"
|
|
10550
|
-
echo " loki plan ./prd.md --json"
|
|
10551
|
-
echo " loki plan ./prd.md --verbose"
|
|
10552
|
-
return 0
|
|
10553
|
-
;;
|
|
10554
|
-
--json) show_json=true; shift ;;
|
|
10555
|
-
--verbose) show_verbose=true; shift ;;
|
|
10556
|
-
*)
|
|
10557
|
-
if [ -z "$prd_file" ]; then
|
|
10558
|
-
prd_file="$1"
|
|
10559
|
-
fi
|
|
10560
|
-
shift
|
|
10561
|
-
;;
|
|
10562
|
-
esac
|
|
10563
|
-
done
|
|
10564
|
-
|
|
10565
|
-
if [ -z "$prd_file" ]; then
|
|
10566
|
-
echo -e "${RED}Usage: loki plan <PRD file>${NC}"
|
|
10567
|
-
echo "Run 'loki plan --help' for usage."
|
|
10564
|
+
# Reusable PRD plan analysis (v6.81.1)
|
|
10565
|
+
# Shared by `loki plan`, `loki start` (auto-plan), and `loki run` (auto-plan).
|
|
10566
|
+
# Arguments:
|
|
10567
|
+
# $1 - absolute PRD path
|
|
10568
|
+
# $2 - show_json (true|false)
|
|
10569
|
+
# $3 - show_verbose (true|false)
|
|
10570
|
+
# Respects:
|
|
10571
|
+
# LOKI_SESSION_MODEL - Pins ALL iterations to one tier (default: sonnet)
|
|
10572
|
+
# LOKI_LEGACY_TIER_SWITCHING - When 'true', restores per-iteration rotation
|
|
10573
|
+
show_prd_plan() {
|
|
10574
|
+
local prd_path="$1"
|
|
10575
|
+
local show_json="${2:-false}"
|
|
10576
|
+
local show_verbose="${3:-false}"
|
|
10577
|
+
|
|
10578
|
+
if [ -z "$prd_path" ] || [ ! -f "$prd_path" ]; then
|
|
10579
|
+
echo -e "${RED}show_prd_plan: PRD file not found: $prd_path${NC}" >&2
|
|
10568
10580
|
return 1
|
|
10569
10581
|
fi
|
|
10570
10582
|
|
|
10571
|
-
if [ ! -f "$prd_file" ]; then
|
|
10572
|
-
echo -e "${RED}PRD file not found: $prd_file${NC}"
|
|
10573
|
-
return 1
|
|
10574
|
-
fi
|
|
10575
|
-
|
|
10576
|
-
local prd_path
|
|
10577
|
-
prd_path="$(cd "$(dirname "$prd_file")" && pwd)/$(basename "$prd_file")"
|
|
10578
|
-
|
|
10579
10583
|
python3 -c "
|
|
10580
10584
|
import json, sys, os, re, math
|
|
10581
10585
|
|
|
@@ -10583,6 +10587,22 @@ prd_path = sys.argv[1]
|
|
|
10583
10587
|
show_json = sys.argv[2] == 'true'
|
|
10584
10588
|
show_verbose = sys.argv[3] == 'true'
|
|
10585
10589
|
|
|
10590
|
+
# v6.81.1: Respect LOKI_SESSION_MODEL (pinned by v6.81.0) so the estimate
|
|
10591
|
+
# matches what the main loop actually invokes. LOKI_LEGACY_TIER_SWITCHING=true
|
|
10592
|
+
# restores the legacy per-iteration Opus/Sonnet/Haiku rotation for users who
|
|
10593
|
+
# explicitly opt out.
|
|
10594
|
+
session_model_env = (os.environ.get('LOKI_SESSION_MODEL', 'sonnet') or 'sonnet').strip().lower()
|
|
10595
|
+
legacy_tier_switching = (os.environ.get('LOKI_LEGACY_TIER_SWITCHING', 'false') or 'false').strip().lower() == 'true'
|
|
10596
|
+
|
|
10597
|
+
# Map session model name to tier key used in tokens_per_tier below.
|
|
10598
|
+
# Unknown models fall through to 'development' (Sonnet) as a safe default.
|
|
10599
|
+
_session_tier_map = {
|
|
10600
|
+
'opus': 'planning',
|
|
10601
|
+
'sonnet': 'development',
|
|
10602
|
+
'haiku': 'fast',
|
|
10603
|
+
}
|
|
10604
|
+
session_tier = _session_tier_map.get(session_model_env, 'development')
|
|
10605
|
+
|
|
10586
10606
|
# Colors (disabled for JSON mode)
|
|
10587
10607
|
if show_json:
|
|
10588
10608
|
RED = GREEN = YELLOW = BLUE = CYAN = BOLD = DIM = NC = ''
|
|
@@ -10770,18 +10790,28 @@ tier_iterations = {'Opus': 0, 'Sonnet': 0, 'Haiku': 0}
|
|
|
10770
10790
|
for i in range(estimated_iterations):
|
|
10771
10791
|
rarv_step = i % 4
|
|
10772
10792
|
if rarv_step == 0:
|
|
10773
|
-
tier = 'planning'
|
|
10774
10793
|
phase_label = 'Reason (Planning)'
|
|
10775
10794
|
elif rarv_step == 1:
|
|
10776
|
-
tier = 'development'
|
|
10777
10795
|
phase_label = 'Act (Implementation)'
|
|
10778
10796
|
elif rarv_step == 2:
|
|
10779
|
-
tier = 'development'
|
|
10780
10797
|
phase_label = 'Reflect (Review)'
|
|
10781
10798
|
else:
|
|
10782
|
-
tier = 'fast'
|
|
10783
10799
|
phase_label = 'Verify (Testing)'
|
|
10784
10800
|
|
|
10801
|
+
if legacy_tier_switching:
|
|
10802
|
+
# Legacy pre-v6.81.0 behavior: rotate tier per RARV step.
|
|
10803
|
+
if rarv_step == 0:
|
|
10804
|
+
tier = 'planning'
|
|
10805
|
+
elif rarv_step in (1, 2):
|
|
10806
|
+
tier = 'development'
|
|
10807
|
+
else:
|
|
10808
|
+
tier = 'fast'
|
|
10809
|
+
else:
|
|
10810
|
+
# v6.81.0+: main loop is pinned to LOKI_SESSION_MODEL. Estimate
|
|
10811
|
+
# MUST use the same tier for all iterations to avoid quoting a
|
|
10812
|
+
# cost higher than the user will actually pay.
|
|
10813
|
+
tier = session_tier
|
|
10814
|
+
|
|
10785
10815
|
info = tokens_per_tier[tier]
|
|
10786
10816
|
model = info['model']
|
|
10787
10817
|
inp = info['input']
|
|
@@ -10957,7 +10987,20 @@ print(f' RARV cycles: {cycle_count} (4 iterations per cycle)')
|
|
|
10957
10987
|
opus_n = tier_iterations.get('Opus', 0)
|
|
10958
10988
|
sonnet_n = tier_iterations.get('Sonnet', 0)
|
|
10959
10989
|
haiku_n = tier_iterations.get('Haiku', 0)
|
|
10960
|
-
|
|
10990
|
+
if legacy_tier_switching:
|
|
10991
|
+
print(f' Model distribution: Opus x{opus_n} | Sonnet x{sonnet_n} | Haiku x{haiku_n}')
|
|
10992
|
+
print(f' {DIM}(legacy rotation: LOKI_LEGACY_TIER_SWITCHING=true){NC}')
|
|
10993
|
+
else:
|
|
10994
|
+
# Session-pinned: exactly one tier has all iterations.
|
|
10995
|
+
pinned_parts = []
|
|
10996
|
+
if opus_n > 0:
|
|
10997
|
+
pinned_parts.append(f'Opus x{opus_n}')
|
|
10998
|
+
if sonnet_n > 0:
|
|
10999
|
+
pinned_parts.append(f'Sonnet x{sonnet_n}')
|
|
11000
|
+
if haiku_n > 0:
|
|
11001
|
+
pinned_parts.append(f'Haiku x{haiku_n}')
|
|
11002
|
+
print(' Model distribution: ' + (' | '.join(pinned_parts) if pinned_parts else '(none)'))
|
|
11003
|
+
print(f' {DIM}(pinned via LOKI_SESSION_MODEL={session_model_env}; set LOKI_LEGACY_TIER_SWITCHING=true to rotate){NC}')
|
|
10961
11004
|
|
|
10962
11005
|
# Tokens
|
|
10963
11006
|
total_tok = total_input_tokens + total_output_tokens
|
|
@@ -10971,6 +11014,11 @@ ds = chr(36) # dollar sign
|
|
|
10971
11014
|
print(f'\n{CYAN}Cost Estimate{NC}')
|
|
10972
11015
|
print(f' {BOLD}Total: {ds}{total_cost:.2f}{NC}')
|
|
10973
11016
|
for model in ['Opus', 'Sonnet', 'Haiku']:
|
|
11017
|
+
# v6.81.1: suppress zero-cost tiers (e.g. session-pinned runs). Keeps
|
|
11018
|
+
# the breakdown focused on models actually used. Legacy mode still
|
|
11019
|
+
# shows all three because each tier has non-zero iterations.
|
|
11020
|
+
if tier_iterations.get(model, 0) == 0 and tier_totals.get(model, 0) == 0:
|
|
11021
|
+
continue
|
|
10974
11022
|
pct = (tier_totals[model] / total_cost * 100) if total_cost > 0 else 0
|
|
10975
11023
|
bar_len = int(pct / 5)
|
|
10976
11024
|
bar = '#' * bar_len + '.' * (20 - bar_len)
|
|
@@ -11025,6 +11073,99 @@ print()
|
|
|
11025
11073
|
" "$prd_path" "$show_json" "$show_verbose"
|
|
11026
11074
|
}
|
|
11027
11075
|
|
|
11076
|
+
# Dry-run PRD analysis and cost estimation (v6.18.0; refactored v6.81.1)
|
|
11077
|
+
# Thin wrapper around show_prd_plan() so the same analysis is reusable from
|
|
11078
|
+
# `loki start`, `loki run`, and other commands without duplicated code.
|
|
11079
|
+
cmd_plan() {
|
|
11080
|
+
local prd_file=""
|
|
11081
|
+
local show_json=false
|
|
11082
|
+
local show_verbose=false
|
|
11083
|
+
|
|
11084
|
+
while [[ $# -gt 0 ]]; do
|
|
11085
|
+
case "$1" in
|
|
11086
|
+
--help|-h)
|
|
11087
|
+
echo -e "${BOLD}loki plan${NC} - Dry-run PRD analysis and cost estimation"
|
|
11088
|
+
echo ""
|
|
11089
|
+
echo "Usage: loki plan <PRD> [options]"
|
|
11090
|
+
echo ""
|
|
11091
|
+
echo "Analyzes a PRD file without executing anything. Outputs complexity,"
|
|
11092
|
+
echo "estimated iterations, token usage, cost, and execution plan."
|
|
11093
|
+
echo ""
|
|
11094
|
+
echo "Options:"
|
|
11095
|
+
echo " --json Machine-readable JSON output"
|
|
11096
|
+
echo " --verbose Show detailed per-iteration breakdown"
|
|
11097
|
+
echo " --help, -h Show this help"
|
|
11098
|
+
echo ""
|
|
11099
|
+
echo "Environment Variables:"
|
|
11100
|
+
echo " LOKI_SESSION_MODEL Pin cost estimate to a single tier"
|
|
11101
|
+
echo " (opus|sonnet|haiku, default: sonnet)"
|
|
11102
|
+
echo " LOKI_LEGACY_TIER_SWITCHING Set to 'true' to restore the legacy"
|
|
11103
|
+
echo " Opus/Sonnet/Haiku per-iteration rotation"
|
|
11104
|
+
echo ""
|
|
11105
|
+
echo "Examples:"
|
|
11106
|
+
echo " loki plan ./prd.md"
|
|
11107
|
+
echo " loki plan ./prd.md --json"
|
|
11108
|
+
echo " loki plan ./prd.md --verbose"
|
|
11109
|
+
return 0
|
|
11110
|
+
;;
|
|
11111
|
+
--json) show_json=true; shift ;;
|
|
11112
|
+
--verbose) show_verbose=true; shift ;;
|
|
11113
|
+
*)
|
|
11114
|
+
if [ -z "$prd_file" ]; then
|
|
11115
|
+
prd_file="$1"
|
|
11116
|
+
fi
|
|
11117
|
+
shift
|
|
11118
|
+
;;
|
|
11119
|
+
esac
|
|
11120
|
+
done
|
|
11121
|
+
|
|
11122
|
+
if [ -z "$prd_file" ]; then
|
|
11123
|
+
echo -e "${RED}Usage: loki plan <PRD file>${NC}"
|
|
11124
|
+
echo "Run 'loki plan --help' for usage."
|
|
11125
|
+
return 1
|
|
11126
|
+
fi
|
|
11127
|
+
|
|
11128
|
+
if [ ! -f "$prd_file" ]; then
|
|
11129
|
+
echo -e "${RED}PRD file not found: $prd_file${NC}"
|
|
11130
|
+
return 1
|
|
11131
|
+
fi
|
|
11132
|
+
|
|
11133
|
+
local prd_path
|
|
11134
|
+
prd_path="$(cd "$(dirname "$prd_file")" && pwd)/$(basename "$prd_file")"
|
|
11135
|
+
|
|
11136
|
+
show_prd_plan "$prd_path" "$show_json" "$show_verbose"
|
|
11137
|
+
}
|
|
11138
|
+
|
|
11139
|
+
# Helper: decide whether to auto-show the plan from a major command.
|
|
11140
|
+
# v6.81.1: default ON, opt-out via --no-plan. Also auto-skipped when stdout is
|
|
11141
|
+
# not a TTY and --no-plan was not explicitly set (CI/automation contexts),
|
|
11142
|
+
# logging a one-line "plan skipped: non-interactive" to stderr for visibility.
|
|
11143
|
+
maybe_show_auto_plan() {
|
|
11144
|
+
local prd_path="$1"
|
|
11145
|
+
local no_plan_flag="${2:-false}" # user passed --no-plan explicitly
|
|
11146
|
+
|
|
11147
|
+
# Explicit opt-out wins.
|
|
11148
|
+
if [[ "$no_plan_flag" == "true" ]]; then
|
|
11149
|
+
return 0
|
|
11150
|
+
fi
|
|
11151
|
+
|
|
11152
|
+
# PRD missing: silently skip (other commands will emit their own errors).
|
|
11153
|
+
if [ -z "$prd_path" ] || [ ! -f "$prd_path" ]; then
|
|
11154
|
+
return 0
|
|
11155
|
+
fi
|
|
11156
|
+
|
|
11157
|
+
# Non-TTY heuristic: keep CI / pipe output clean. Log to stderr so it is
|
|
11158
|
+
# still surfaced in logs without corrupting stdout parsers.
|
|
11159
|
+
if [ ! -t 1 ]; then
|
|
11160
|
+
echo "plan skipped: non-interactive (pass --no-plan to silence, or run 'loki plan <PRD>' explicitly)" >&2
|
|
11161
|
+
return 0
|
|
11162
|
+
fi
|
|
11163
|
+
|
|
11164
|
+
local abs_prd
|
|
11165
|
+
abs_prd="$(cd "$(dirname "$prd_path")" && pwd)/$(basename "$prd_path")"
|
|
11166
|
+
show_prd_plan "$abs_prd" "false" "false"
|
|
11167
|
+
}
|
|
11168
|
+
|
|
11028
11169
|
# Main command dispatcher
|
|
11029
11170
|
main() {
|
|
11030
11171
|
if [ $# -eq 0 ]; then
|
package/autonomy/run.sh
CHANGED
|
@@ -3763,9 +3763,12 @@ track_iteration_complete() {
|
|
|
3763
3763
|
[ -z "$phase" ] && phase=$(python3 -c "import json; print(json.load(open('.loki/state/orchestrator.json')).get('currentPhase', 'unknown'))" 2>/dev/null || echo "unknown")
|
|
3764
3764
|
|
|
3765
3765
|
# Read token data from context tracker output (v5.42.0)
|
|
3766
|
+
# v6.82.0: also capture cache_read_tokens / cache_creation_tokens for
|
|
3767
|
+
# prompt-cache hit-rate analysis (S1.1 prompt restructure).
|
|
3766
3768
|
local iter_input=0 iter_output=0 iter_cost=0
|
|
3769
|
+
local iter_cache_read=0 iter_cache_creation=0
|
|
3767
3770
|
if [ -f ".loki/context/tracking.json" ]; then
|
|
3768
|
-
read iter_input iter_output iter_cost < <(python3 -c "
|
|
3771
|
+
read iter_input iter_output iter_cost iter_cache_read iter_cache_creation < <(python3 -c "
|
|
3769
3772
|
import json
|
|
3770
3773
|
try:
|
|
3771
3774
|
t = json.load(open('.loki/context/tracking.json'))
|
|
@@ -3773,11 +3776,17 @@ try:
|
|
|
3773
3776
|
match = [i for i in iters if i.get('iteration') == $iteration]
|
|
3774
3777
|
if match:
|
|
3775
3778
|
m = match[-1]
|
|
3776
|
-
print(
|
|
3779
|
+
print(
|
|
3780
|
+
m.get('input_tokens', 0),
|
|
3781
|
+
m.get('output_tokens', 0),
|
|
3782
|
+
m.get('cost_usd', 0),
|
|
3783
|
+
m.get('cache_read_tokens', 0),
|
|
3784
|
+
m.get('cache_creation_tokens', 0),
|
|
3785
|
+
)
|
|
3777
3786
|
else:
|
|
3778
|
-
print(0, 0, 0)
|
|
3779
|
-
except: print(0, 0, 0)
|
|
3780
|
-
" 2>/dev/null || echo "0 0 0")
|
|
3787
|
+
print(0, 0, 0, 0, 0)
|
|
3788
|
+
except: print(0, 0, 0, 0, 0)
|
|
3789
|
+
" 2>/dev/null || echo "0 0 0 0 0")
|
|
3781
3790
|
fi
|
|
3782
3791
|
|
|
3783
3792
|
cat > ".loki/metrics/efficiency/iteration-${iteration}.json" << EFF_EOF
|
|
@@ -3790,6 +3799,8 @@ except: print(0, 0, 0)
|
|
|
3790
3799
|
"status": "$status_str",
|
|
3791
3800
|
"input_tokens": ${iter_input:-0},
|
|
3792
3801
|
"output_tokens": ${iter_output:-0},
|
|
3802
|
+
"cache_read_tokens": ${iter_cache_read:-0},
|
|
3803
|
+
"cache_creation_tokens": ${iter_cache_creation:-0},
|
|
3793
3804
|
"cost_usd": ${iter_cost:-0},
|
|
3794
3805
|
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
3795
3806
|
}
|
|
@@ -7596,18 +7607,87 @@ except Exception:
|
|
|
7596
7607
|
return 0
|
|
7597
7608
|
}
|
|
7598
7609
|
|
|
7599
|
-
# Check if
|
|
7610
|
+
# Check if the loki_complete_task MCP tool was invoked in this iteration.
|
|
7611
|
+
# The tool writes a payload to .loki/signals/TASK_COMPLETION_CLAIMED with the
|
|
7612
|
+
# structured completion claim. When the signal exists, we read it, log the
|
|
7613
|
+
# structured event, and consume (remove) the file. Returns 0 on detection.
|
|
7614
|
+
#
|
|
7615
|
+
# Output on stdout: the JSON payload (for callers that want to log it).
|
|
7616
|
+
check_task_completion_signal() {
|
|
7617
|
+
local signal_file=".loki/signals/TASK_COMPLETION_CLAIMED"
|
|
7618
|
+
if [ ! -f "$signal_file" ]; then
|
|
7619
|
+
return 1
|
|
7620
|
+
fi
|
|
7621
|
+
|
|
7622
|
+
local payload
|
|
7623
|
+
payload=$(cat "$signal_file" 2>/dev/null || echo "")
|
|
7624
|
+
if [ -z "$payload" ]; then
|
|
7625
|
+
# Empty signal -- treat as noise and clean up
|
|
7626
|
+
rm -f "$signal_file" 2>/dev/null
|
|
7627
|
+
return 1
|
|
7628
|
+
fi
|
|
7629
|
+
|
|
7630
|
+
# Emit a structured event for observability (best-effort).
|
|
7631
|
+
local statement evidence confidence
|
|
7632
|
+
statement=$(python3 -c "
|
|
7633
|
+
import json, sys
|
|
7634
|
+
try:
|
|
7635
|
+
d = json.loads(sys.stdin.read())
|
|
7636
|
+
print(d.get('statement',''))
|
|
7637
|
+
except Exception:
|
|
7638
|
+
pass
|
|
7639
|
+
" <<< "$payload" 2>/dev/null || echo "")
|
|
7640
|
+
evidence=$(python3 -c "
|
|
7641
|
+
import json, sys
|
|
7642
|
+
try:
|
|
7643
|
+
d = json.loads(sys.stdin.read())
|
|
7644
|
+
print(d.get('evidence',''))
|
|
7645
|
+
except Exception:
|
|
7646
|
+
pass
|
|
7647
|
+
" <<< "$payload" 2>/dev/null || echo "")
|
|
7648
|
+
confidence=$(python3 -c "
|
|
7649
|
+
import json, sys
|
|
7650
|
+
try:
|
|
7651
|
+
d = json.loads(sys.stdin.read())
|
|
7652
|
+
print(d.get('confidence','medium'))
|
|
7653
|
+
except Exception:
|
|
7654
|
+
print('medium')
|
|
7655
|
+
" <<< "$payload" 2>/dev/null || echo "medium")
|
|
7656
|
+
|
|
7657
|
+
emit_event_json "task_completion_claim" \
|
|
7658
|
+
"statement=${statement:0:500}" \
|
|
7659
|
+
"confidence=${confidence}" \
|
|
7660
|
+
"evidence_length=${#evidence}"
|
|
7661
|
+
|
|
7662
|
+
# Return the payload on stdout
|
|
7663
|
+
printf '%s\n' "$payload"
|
|
7664
|
+
|
|
7665
|
+
# Consume the signal (next iteration would otherwise re-trigger)
|
|
7666
|
+
rm -f "$signal_file" 2>/dev/null
|
|
7667
|
+
return 0
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
# Check if completion promise is fulfilled in log output.
|
|
7671
|
+
#
|
|
7672
|
+
# As of v6.82.0, the default path is the MCP tool `loki_complete_task`
|
|
7673
|
+
# (detected via check_task_completion_signal above). The legacy grep-based
|
|
7674
|
+
# detection is retained behind LOKI_LEGACY_COMPLETION_MATCH=true for rollback.
|
|
7600
7675
|
check_completion_promise() {
|
|
7601
7676
|
local log_file="$1"
|
|
7602
7677
|
|
|
7603
|
-
#
|
|
7604
|
-
if
|
|
7678
|
+
# New default: structured signal from the loki_complete_task MCP tool.
|
|
7679
|
+
if check_task_completion_signal >/dev/null 2>&1; then
|
|
7605
7680
|
return 0
|
|
7606
7681
|
fi
|
|
7607
7682
|
|
|
7608
|
-
#
|
|
7609
|
-
if [
|
|
7610
|
-
|
|
7683
|
+
# Legacy grep fallback (opt-in via env flag for rollback).
|
|
7684
|
+
if [ "${LOKI_LEGACY_COMPLETION_MATCH:-false}" = "true" ]; then
|
|
7685
|
+
if grep -q "COMPLETION PROMISE FULFILLED" "$log_file" 2>/dev/null; then
|
|
7686
|
+
return 0
|
|
7687
|
+
fi
|
|
7688
|
+
if [ -n "$COMPLETION_PROMISE" ] && grep -qF "$COMPLETION_PROMISE" "$log_file" 2>/dev/null; then
|
|
7689
|
+
return 0
|
|
7690
|
+
fi
|
|
7611
7691
|
fi
|
|
7612
7692
|
|
|
7613
7693
|
return 1
|
|
@@ -8360,12 +8440,16 @@ build_prompt() {
|
|
|
8360
8440
|
# Ralph Wiggum Mode - Reason-Act-Reflect-VERIFY cycle with self-verification loop (Boris Cherny pattern)
|
|
8361
8441
|
local rarv_instruction="RALPH WIGGUM MODE ACTIVE. Use Reason-Act-Reflect-VERIFY cycle: 1) REASON - READ .loki/CONTINUITY.md including 'Mistakes & Learnings' section to avoid past errors. CHECK .loki/state/relevant-learnings.json for cross-project learnings from previous projects (mistakes to avoid, patterns to apply). Check .loki/state/ and .loki/queue/, identify next task. CHECK .loki/state/resources.json for system resource warnings - if CPU or memory is high, reduce parallel agent spawning or pause non-critical tasks. Limit to MAX_PARALLEL_AGENTS=${MAX_PARALLEL_AGENTS}. If queue empty, find new improvements. 2) ACT - Execute task, write code, commit changes atomically (git checkpoint). 3) REFLECT - Update .loki/CONTINUITY.md with progress, update state, identify NEXT improvement. Save valuable learnings for future projects. 4) VERIFY - Run automated tests (unit, integration, E2E), check compilation/build, verify against spec. IF VERIFICATION FAILS: a) Capture error details (stack trace, logs), b) Analyze root cause, c) UPDATE 'Mistakes & Learnings' in CONTINUITY.md with what failed, why, and how to prevent, d) Rollback to last good git checkpoint if needed, e) Apply learning and RETRY from REASON. If verification passes, mark task complete and continue. This self-verification loop achieves 2-3x quality improvement. CRITICAL: There is NEVER a 'finished' state - always find the next improvement, optimization, test, or feature."
|
|
8362
8442
|
|
|
8363
|
-
# Completion
|
|
8443
|
+
# Completion instruction (S0.2 -- structured tool call).
|
|
8444
|
+
# When PRD requirements are implemented, tests pass, and the checklist is
|
|
8445
|
+
# at or near 100%, the agent MUST invoke the `loki_complete_task` MCP tool
|
|
8446
|
+
# (defined in mcp/server.py) with completion_statement + evidence fields,
|
|
8447
|
+
# instead of emitting a prose completion string.
|
|
8364
8448
|
local completion_instruction=""
|
|
8365
8449
|
if [ -n "$COMPLETION_PROMISE" ]; then
|
|
8366
|
-
completion_instruction="COMPLETION_PROMISE: [$COMPLETION_PROMISE].
|
|
8450
|
+
completion_instruction="COMPLETION_PROMISE: [$COMPLETION_PROMISE]. When all PRD requirements are implemented, tests pass, and the PRD checklist is at or near 100%, invoke the loki_complete_task MCP tool with your completion_statement and evidence (cite tests that passed, checklist items verified, files created/modified). Do NOT emit a completion string in prose -- use the tool call."
|
|
8367
8451
|
else
|
|
8368
|
-
completion_instruction="NO COMPLETION PROMISE SET. Continue finding improvements. The Completion Council will evaluate your progress periodically. Iteration $iteration of max $MAX_ITERATIONS."
|
|
8452
|
+
completion_instruction="NO COMPLETION PROMISE SET. Continue finding improvements. The Completion Council will evaluate your progress periodically. Iteration $iteration of max $MAX_ITERATIONS. If you do decide the task is complete, invoke the loki_complete_task MCP tool with a structured statement and evidence rather than emitting prose."
|
|
8369
8453
|
fi
|
|
8370
8454
|
|
|
8371
8455
|
# Core autonomous instructions - NO questions, NO waiting, NEVER say done
|
|
@@ -8373,7 +8457,7 @@ build_prompt() {
|
|
|
8373
8457
|
if [ "$AUTONOMY_MODE" = "perpetual" ] || [ "$PERPETUAL_MODE" = "true" ]; then
|
|
8374
8458
|
autonomous_suffix="CRITICAL AUTONOMY RULES: 1) NEVER ask questions - just decide. 2) NEVER wait for confirmation - just act. 3) NEVER say 'done' or 'complete' - there's always more to improve. 4) NEVER stop voluntarily - if out of tasks, create new ones (add tests, optimize, refactor, add features). 5) Work continues PERPETUALLY. Even if PRD is implemented, find bugs, add tests, improve UX, optimize performance."
|
|
8375
8459
|
else
|
|
8376
|
-
autonomous_suffix="CRITICAL AUTONOMY RULES: 1) NEVER ask questions - just decide. 2) NEVER wait for confirmation - just act. 3) When all PRD requirements are implemented and tests pass,
|
|
8460
|
+
autonomous_suffix="CRITICAL AUTONOMY RULES: 1) NEVER ask questions - just decide. 2) NEVER wait for confirmation - just act. 3) When all PRD requirements are implemented and tests pass, invoke the loki_complete_task MCP tool (completion_statement='$COMPLETION_PROMISE' plus evidence + confidence). Do not emit completion prose. 4) If out of tasks but PRD is not fully implemented, continue working on remaining requirements. 5) Focus on completing PRD scope, not endless improvements."
|
|
8377
8461
|
fi
|
|
8378
8462
|
|
|
8379
8463
|
# Skill files are always copied to .loki/skills/ for all providers
|
|
@@ -8655,42 +8739,154 @@ except Exception:
|
|
|
8655
8739
|
fi
|
|
8656
8740
|
fi
|
|
8657
8741
|
|
|
8658
|
-
#
|
|
8659
|
-
#
|
|
8742
|
+
# S1.1 -- Static-first prompt assembly with cache-breakpoint marker.
|
|
8743
|
+
#
|
|
8744
|
+
# The prior shape (v<=6.81.x) concatenated ~13 dynamic blobs BEFORE the
|
|
8745
|
+
# 4-5 static instruction blobs, which destroyed Claude's prefix cache on
|
|
8746
|
+
# every iteration. The new layout places the stable instruction set first
|
|
8747
|
+
# (prd_anchor + RARV/SDLC/autonomy/memory instructions), emits a literal
|
|
8748
|
+
# [CACHE_BREAKPOINT] marker, then appends the volatile per-iteration
|
|
8749
|
+
# context inside a <dynamic_context> tag.
|
|
8750
|
+
#
|
|
8751
|
+
# The [CACHE_BREAKPOINT] marker is a documentation anchor today. When the
|
|
8752
|
+
# Claude CLI migration exposes cache_control, the orchestrator can split
|
|
8753
|
+
# the prompt at this marker and set cache_control on the prefix half.
|
|
8754
|
+
#
|
|
8755
|
+
# Rollback: set LOKI_LEGACY_PROMPT_ORDERING=true to restore the previous
|
|
8756
|
+
# dynamic-first concatenation order.
|
|
8757
|
+
|
|
8758
|
+
if [ "${LOKI_LEGACY_PROMPT_ORDERING:-false}" = "true" ]; then
|
|
8759
|
+
# Legacy dynamic-first ordering (pre-v6.82.0). Retained for rollback.
|
|
8760
|
+
if [ "${PROVIDER_DEGRADED:-false}" = "true" ]; then
|
|
8761
|
+
local _legacy_prd_content=""
|
|
8762
|
+
if [ -n "$prd" ] && [ -f "$prd" ]; then
|
|
8763
|
+
_legacy_prd_content=$(head -c 4000 "$prd")
|
|
8764
|
+
fi
|
|
8765
|
+
if [ $retry -eq 0 ]; then
|
|
8766
|
+
if [ -n "$prd" ]; then
|
|
8767
|
+
echo "You are a coding assistant. Read and implement the requirements from the PRD below. Write working code, run tests if possible, and commit changes. ${human_directive:+Priority: $human_directive} ${queue_tasks:+Tasks: $queue_tasks} PRD contents: $_legacy_prd_content"
|
|
8768
|
+
else
|
|
8769
|
+
echo "You are a coding assistant. Analyze this codebase and suggest improvements. Write working code and commit changes. ${human_directive:+Priority: $human_directive} ${queue_tasks:+Tasks: $queue_tasks}"
|
|
8770
|
+
fi
|
|
8771
|
+
else
|
|
8772
|
+
if [ -n "$prd" ]; then
|
|
8773
|
+
echo "You are a coding assistant. Continue working on iteration $iteration. Review what exists, implement remaining PRD requirements, fix any issues, add tests. ${human_directive:+Priority: $human_directive} ${queue_tasks:+Tasks: $queue_tasks} PRD contents: $_legacy_prd_content"
|
|
8774
|
+
else
|
|
8775
|
+
echo "You are a coding assistant. Continue working on iteration $iteration. Review what exists, improve code, fix bugs, add tests. ${human_directive:+Priority: $human_directive} ${queue_tasks:+Tasks: $queue_tasks}"
|
|
8776
|
+
fi
|
|
8777
|
+
fi
|
|
8778
|
+
else
|
|
8779
|
+
if [ $retry -eq 0 ]; then
|
|
8780
|
+
if [ -n "$prd" ]; then
|
|
8781
|
+
echo "Loki Mode with PRD at $prd. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
8782
|
+
else
|
|
8783
|
+
echo "Loki Mode. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $analysis_instruction $rarv_instruction $memory_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
8784
|
+
fi
|
|
8785
|
+
else
|
|
8786
|
+
if [ -n "$prd" ]; then
|
|
8787
|
+
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). PRD: $prd. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
8788
|
+
else
|
|
8789
|
+
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section Use .loki/generated-prd.md if exists. $rarv_instruction $memory_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
8790
|
+
fi
|
|
8791
|
+
fi
|
|
8792
|
+
fi
|
|
8793
|
+
return 0
|
|
8794
|
+
fi
|
|
8795
|
+
|
|
8796
|
+
# --- New static-first layout (v6.82.0+) ---
|
|
8797
|
+
#
|
|
8798
|
+
# assemble_prompt_static outputs the cache-stable prefix:
|
|
8799
|
+
# <loki_system>
|
|
8800
|
+
# {prd_anchor}
|
|
8801
|
+
# {rarv_instruction + sdlc_instruction + autonomous_suffix + memory_instruction}
|
|
8802
|
+
# </loki_system>
|
|
8803
|
+
# [CACHE_BREAKPOINT]
|
|
8804
|
+
#
|
|
8805
|
+
# assemble_prompt_dynamic outputs the volatile tail wrapped in
|
|
8806
|
+
# <dynamic_context iteration=".." retry=".."> ... </dynamic_context>.
|
|
8807
|
+
#
|
|
8808
|
+
# Keeping these as inline local helpers (nested functions via eval are
|
|
8809
|
+
# awkward in bash) -- we emit them as two contiguous printf blocks so the
|
|
8810
|
+
# logic is self-documenting and byte-reproducible.
|
|
8811
|
+
|
|
8660
8812
|
if [ "${PROVIDER_DEGRADED:-false}" = "true" ]; then
|
|
8813
|
+
# Degraded providers: simpler wording, but still static-first.
|
|
8661
8814
|
local prd_content=""
|
|
8662
8815
|
if [ -n "$prd" ] && [ -f "$prd" ]; then
|
|
8663
8816
|
prd_content=$(head -c 4000 "$prd")
|
|
8664
8817
|
fi
|
|
8665
8818
|
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8819
|
+
local degraded_prd_anchor="Loki Mode"
|
|
8820
|
+
[ -n "$prd" ] && degraded_prd_anchor="Loki Mode with PRD"
|
|
8821
|
+
|
|
8822
|
+
# STATIC PREFIX (cache-stable across iterations)
|
|
8823
|
+
printf '<loki_system>\n'
|
|
8824
|
+
printf '%s\n' "$degraded_prd_anchor"
|
|
8825
|
+
if [ -n "$prd" ]; then
|
|
8826
|
+
printf 'You are a coding assistant. Read and implement the requirements from the PRD. Write working code, run tests if possible, and commit changes.\n'
|
|
8672
8827
|
else
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8828
|
+
printf 'You are a coding assistant. Analyze this codebase and suggest improvements. Write working code and commit changes.\n'
|
|
8829
|
+
fi
|
|
8830
|
+
printf '</loki_system>\n'
|
|
8831
|
+
printf '[CACHE_BREAKPOINT]\n'
|
|
8832
|
+
|
|
8833
|
+
# DYNAMIC TAIL (changes every iteration)
|
|
8834
|
+
printf '<dynamic_context iteration="%s" retry="%s">\n' "$iteration" "$retry"
|
|
8835
|
+
[ -n "$human_directive" ] && printf 'Priority: %s\n' "$human_directive"
|
|
8836
|
+
[ -n "$queue_tasks" ] && printf 'Tasks: %s\n' "$queue_tasks"
|
|
8837
|
+
if [ -n "$prd" ]; then
|
|
8838
|
+
printf 'PRD contents: %s\n' "$prd_content"
|
|
8678
8839
|
fi
|
|
8840
|
+
printf '</dynamic_context>\n'
|
|
8841
|
+
return 0
|
|
8842
|
+
fi
|
|
8843
|
+
|
|
8844
|
+
# Full-featured providers (Claude, etc.)
|
|
8845
|
+
local prd_anchor
|
|
8846
|
+
if [ -n "$prd" ]; then
|
|
8847
|
+
prd_anchor="Loki Mode with PRD at $prd"
|
|
8679
8848
|
else
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8849
|
+
prd_anchor="Loki Mode"
|
|
8850
|
+
fi
|
|
8851
|
+
|
|
8852
|
+
# STATIC PREFIX (cache-stable across iterations).
|
|
8853
|
+
# Order is deterministic so the prefix is byte-identical for iter N and N+1.
|
|
8854
|
+
printf '<loki_system>\n'
|
|
8855
|
+
printf '%s\n' "$prd_anchor"
|
|
8856
|
+
printf '%s\n' "$rarv_instruction"
|
|
8857
|
+
printf '%s\n' "$sdlc_instruction"
|
|
8858
|
+
printf '%s\n' "$autonomous_suffix"
|
|
8859
|
+
printf '%s\n' "$memory_instruction"
|
|
8860
|
+
# For codebase-analysis mode (no PRD), analysis_instruction is part of the
|
|
8861
|
+
# static prefix so it remains cache-stable.
|
|
8862
|
+
if [ -z "$prd" ]; then
|
|
8863
|
+
printf '%s\n' "$analysis_instruction"
|
|
8864
|
+
fi
|
|
8865
|
+
printf '</loki_system>\n'
|
|
8866
|
+
printf '[CACHE_BREAKPOINT]\n'
|
|
8867
|
+
|
|
8868
|
+
# DYNAMIC TAIL -- all per-iteration context goes here.
|
|
8869
|
+
printf '<dynamic_context iteration="%s" retry="%s">\n' "$iteration" "$retry"
|
|
8870
|
+
if [ $retry -gt 0 ]; then
|
|
8871
|
+
if [ -n "$prd" ]; then
|
|
8872
|
+
printf 'Resume iteration #%s (retry #%s). PRD: %s\n' "$iteration" "$retry" "$prd"
|
|
8686
8873
|
else
|
|
8687
|
-
|
|
8688
|
-
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). PRD: $prd. $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section $rarv_instruction $memory_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
8689
|
-
else
|
|
8690
|
-
echo "Loki Mode - Resume iteration #$iteration (retry #$retry). $human_directive $gate_failure_context $queue_tasks $bmad_context $openspec_context $mirofish_context $magic_context $checklist_status $app_runner_info $playwright_info $memory_context_section Use .loki/generated-prd.md if exists. $rarv_instruction $memory_instruction $completion_instruction $sdlc_instruction $autonomous_suffix"
|
|
8691
|
-
fi
|
|
8874
|
+
printf 'Resume iteration #%s (retry #%s). Use .loki/generated-prd.md if exists.\n' "$iteration" "$retry"
|
|
8692
8875
|
fi
|
|
8693
8876
|
fi
|
|
8877
|
+
[ -n "$human_directive" ] && printf '%s\n' "$human_directive"
|
|
8878
|
+
[ -n "$gate_failure_context" ] && printf '%s\n' "$gate_failure_context"
|
|
8879
|
+
[ -n "$queue_tasks" ] && printf '%s\n' "$queue_tasks"
|
|
8880
|
+
[ -n "$bmad_context" ] && printf '%s\n' "$bmad_context"
|
|
8881
|
+
[ -n "$openspec_context" ] && printf '%s\n' "$openspec_context"
|
|
8882
|
+
[ -n "$mirofish_context" ] && printf '%s\n' "$mirofish_context"
|
|
8883
|
+
[ -n "$magic_context" ] && printf '%s\n' "$magic_context"
|
|
8884
|
+
[ -n "$checklist_status" ] && printf '%s\n' "$checklist_status"
|
|
8885
|
+
[ -n "$app_runner_info" ] && printf '%s\n' "$app_runner_info"
|
|
8886
|
+
[ -n "$playwright_info" ] && printf '%s\n' "$playwright_info"
|
|
8887
|
+
[ -n "$memory_context_section" ] && printf '%s\n' "$memory_context_section"
|
|
8888
|
+
printf '%s\n' "$completion_instruction"
|
|
8889
|
+
printf '</dynamic_context>\n'
|
|
8694
8890
|
}
|
|
8695
8891
|
|
|
8696
8892
|
#===============================================================================
|
|
@@ -10317,12 +10513,21 @@ if __name__ == "__main__":
|
|
|
10317
10513
|
return 0
|
|
10318
10514
|
fi
|
|
10319
10515
|
|
|
10320
|
-
#
|
|
10321
|
-
#
|
|
10322
|
-
|
|
10516
|
+
# Stop if either:
|
|
10517
|
+
# (a) the agent invoked the loki_complete_task MCP tool
|
|
10518
|
+
# (detected via .loki/signals/TASK_COMPLETION_CLAIMED), OR
|
|
10519
|
+
# (b) LOKI_LEGACY_COMPLETION_MATCH=true AND the completion
|
|
10520
|
+
# promise text appears in the iteration output.
|
|
10521
|
+
# The check_completion_promise() helper encapsulates both.
|
|
10522
|
+
# BUG-RUN-001: Use per-iteration output, not stale daily log.
|
|
10523
|
+
if check_completion_promise "$iter_output"; then
|
|
10323
10524
|
echo ""
|
|
10324
|
-
|
|
10325
|
-
|
|
10525
|
+
if [ -n "$COMPLETION_PROMISE" ]; then
|
|
10526
|
+
log_header "COMPLETION PROMISE FULFILLED: $COMPLETION_PROMISE"
|
|
10527
|
+
else
|
|
10528
|
+
log_header "TASK COMPLETION CLAIMED (via loki_complete_task)"
|
|
10529
|
+
fi
|
|
10530
|
+
log_info "Explicit completion signal detected."
|
|
10326
10531
|
# Run memory consolidation on successful completion
|
|
10327
10532
|
log_info "Running memory consolidation..."
|
|
10328
10533
|
run_memory_consolidation
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED
package/mcp/server.py
CHANGED
|
@@ -1005,6 +1005,111 @@ async def loki_consolidate_memory(since_hours: int = 24) -> str:
|
|
|
1005
1005
|
return json.dumps({"error": str(e)})
|
|
1006
1006
|
|
|
1007
1007
|
|
|
1008
|
+
@mcp.tool()
|
|
1009
|
+
async def loki_complete_task(
|
|
1010
|
+
completion_statement: str,
|
|
1011
|
+
evidence: str,
|
|
1012
|
+
confidence: str = "medium",
|
|
1013
|
+
) -> str:
|
|
1014
|
+
"""
|
|
1015
|
+
Declare that the current PRD / task is complete.
|
|
1016
|
+
|
|
1017
|
+
Replaces the legacy 'COMPLETION PROMISE FULFILLED: ...' prose string with a
|
|
1018
|
+
structured tool call. The orchestrator (run.sh) detects this via a signal
|
|
1019
|
+
file and stops the iteration loop gracefully.
|
|
1020
|
+
|
|
1021
|
+
Args:
|
|
1022
|
+
completion_statement: A short statement of what is complete (for example,
|
|
1023
|
+
"PRD requirements implemented, all tests passing, checklist 100%").
|
|
1024
|
+
evidence: Concrete evidence supporting the claim -- tests that passed,
|
|
1025
|
+
checklist items verified, files created/modified, metrics hit.
|
|
1026
|
+
confidence: One of 'high', 'medium', 'low' (default 'medium').
|
|
1027
|
+
'low' signals the orchestrator should still run the completion council.
|
|
1028
|
+
|
|
1029
|
+
Returns:
|
|
1030
|
+
JSON: {"recorded": true, "path": ".loki/events.jsonl"} on success,
|
|
1031
|
+
{"error": "..."} otherwise.
|
|
1032
|
+
"""
|
|
1033
|
+
_emit_tool_event_async(
|
|
1034
|
+
'loki_complete_task', 'start',
|
|
1035
|
+
parameters={
|
|
1036
|
+
'confidence': confidence,
|
|
1037
|
+
'statement_len': len(completion_statement or ''),
|
|
1038
|
+
'evidence_len': len(evidence or ''),
|
|
1039
|
+
},
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
# Validate inputs
|
|
1043
|
+
if not completion_statement or not completion_statement.strip():
|
|
1044
|
+
_emit_tool_event_async(
|
|
1045
|
+
'loki_complete_task', 'complete',
|
|
1046
|
+
result_status='error', error='completion_statement required')
|
|
1047
|
+
return json.dumps({"error": "completion_statement is required"})
|
|
1048
|
+
if not evidence or not evidence.strip():
|
|
1049
|
+
_emit_tool_event_async(
|
|
1050
|
+
'loki_complete_task', 'complete',
|
|
1051
|
+
result_status='error', error='evidence required')
|
|
1052
|
+
return json.dumps({"error": "evidence is required"})
|
|
1053
|
+
|
|
1054
|
+
confidence_norm = (confidence or 'medium').strip().lower()
|
|
1055
|
+
if confidence_norm not in ('high', 'medium', 'low'):
|
|
1056
|
+
confidence_norm = 'medium'
|
|
1057
|
+
|
|
1058
|
+
timestamp = datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z')
|
|
1059
|
+
|
|
1060
|
+
payload = {
|
|
1061
|
+
'type': 'task_completion_claim',
|
|
1062
|
+
'statement': completion_statement.strip(),
|
|
1063
|
+
'evidence': evidence.strip(),
|
|
1064
|
+
'confidence': confidence_norm,
|
|
1065
|
+
'timestamp': timestamp,
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
# Wrap event record with timestamp and type at the outer level so it matches
|
|
1069
|
+
# the shape of other events in .loki/events.jsonl.
|
|
1070
|
+
event_record = {
|
|
1071
|
+
'timestamp': timestamp,
|
|
1072
|
+
'type': 'task_completion_claim',
|
|
1073
|
+
'data': payload,
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
try:
|
|
1077
|
+
# Ensure .loki/ and .loki/signals/ exist under the project root
|
|
1078
|
+
loki_dir = safe_path_join('.loki')
|
|
1079
|
+
os.makedirs(loki_dir, exist_ok=True)
|
|
1080
|
+
signals_dir = safe_path_join('.loki', 'signals')
|
|
1081
|
+
os.makedirs(signals_dir, exist_ok=True)
|
|
1082
|
+
|
|
1083
|
+
events_path = safe_path_join('.loki', 'events.jsonl')
|
|
1084
|
+
with safe_open(events_path, 'a') as f:
|
|
1085
|
+
f.write(json.dumps(event_record) + '\n')
|
|
1086
|
+
|
|
1087
|
+
signal_path = safe_path_join('.loki', 'signals', 'TASK_COMPLETION_CLAIMED')
|
|
1088
|
+
with safe_open(signal_path, 'w') as f:
|
|
1089
|
+
f.write(json.dumps(payload, indent=2))
|
|
1090
|
+
|
|
1091
|
+
_emit_tool_event_async(
|
|
1092
|
+
'loki_complete_task', 'complete', result_status='success')
|
|
1093
|
+
return json.dumps({
|
|
1094
|
+
"recorded": True,
|
|
1095
|
+
"path": ".loki/events.jsonl",
|
|
1096
|
+
"signal": ".loki/signals/TASK_COMPLETION_CLAIMED",
|
|
1097
|
+
"confidence": confidence_norm,
|
|
1098
|
+
})
|
|
1099
|
+
except PathTraversalError as e:
|
|
1100
|
+
logger.error(f"Path traversal attempt blocked in loki_complete_task: {e}")
|
|
1101
|
+
_emit_tool_event_async(
|
|
1102
|
+
'loki_complete_task', 'complete',
|
|
1103
|
+
result_status='error', error='Access denied')
|
|
1104
|
+
return json.dumps({"error": "Access denied"})
|
|
1105
|
+
except Exception as e:
|
|
1106
|
+
logger.error(f"loki_complete_task failed: {e}")
|
|
1107
|
+
_emit_tool_event_async(
|
|
1108
|
+
'loki_complete_task', 'complete',
|
|
1109
|
+
result_status='error', error=str(e))
|
|
1110
|
+
return json.dumps({"error": str(e)})
|
|
1111
|
+
|
|
1112
|
+
|
|
1008
1113
|
# ============================================================
|
|
1009
1114
|
# RESOURCES - Data that can be read
|
|
1010
1115
|
# ============================================================
|