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 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.81.0
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.81.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
272
+ **v6.82.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.81.0
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
- # Dry-run PRD analysis and cost estimation (v6.18.0)
10528
- cmd_plan() {
10529
- local prd_file=""
10530
- local show_json=false
10531
- local show_verbose=false
10532
-
10533
- while [[ $# -gt 0 ]]; do
10534
- case "$1" in
10535
- --help|-h)
10536
- echo -e "${BOLD}loki plan${NC} - Dry-run PRD analysis and cost estimation"
10537
- echo ""
10538
- echo "Usage: loki plan <PRD> [options]"
10539
- echo ""
10540
- echo "Analyzes a PRD file without executing anything. Outputs complexity,"
10541
- echo "estimated iterations, token usage, cost, and execution plan."
10542
- echo ""
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
- print(f' Model distribution: Opus x{opus_n} | Sonnet x{sonnet_n} | Haiku x{haiku_n}')
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(m.get('input_tokens', 0), m.get('output_tokens', 0), m.get('cost_usd', 0))
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 completion promise is fulfilled in log output
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
- # Check for the completion promise phrase in recent log output
7604
- if grep -q "COMPLETION PROMISE FULFILLED" "$log_file" 2>/dev/null; then
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
- # Check for custom completion promise text
7609
- if [ -n "$COMPLETION_PROMISE" ] && grep -qF "$COMPLETION_PROMISE" "$log_file" 2>/dev/null; then
7610
- return 0
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 promise instruction (only if set)
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]. ONLY output 'COMPLETION PROMISE FULFILLED: $COMPLETION_PROMISE' when this EXACT condition is met."
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, output the completion promise text EXACTLY: '$COMPLETION_PROMISE'. 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."
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
- # Degraded providers with small models need simplified prompts
8659
- # Full RARV/SDLC instructions overwhelm models < 30B parameters
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
- if [ $retry -eq 0 ]; then
8667
- if [ -n "$prd" ]; then
8668
- 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: $prd_content"
8669
- else
8670
- 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}"
8671
- fi
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
- if [ -n "$prd" ]; then
8674
- 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: $prd_content"
8675
- else
8676
- 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}"
8677
- fi
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
- if [ $retry -eq 0 ]; then
8681
- if [ -n "$prd" ]; then
8682
- 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"
8683
- else
8684
- 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"
8685
- fi
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
- if [ -n "$prd" ]; then
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
- # Only stop if EXPLICIT completion promise text was output
10321
- # BUG-RUN-001: Use per-iteration output, not stale daily log
10322
- if [ -n "$COMPLETION_PROMISE" ] && check_completion_promise "$iter_output"; then
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
- log_header "COMPLETION PROMISE FULFILLED: $COMPLETION_PROMISE"
10325
- log_info "Explicit completion promise detected in output."
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
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.81.0"
10
+ __version__ = "6.82.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v6.81.0
5
+ **Version:** v6.82.0
6
6
 
7
7
  ---
8
8
 
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '6.81.0'
60
+ __version__ = '6.82.0'
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
  # ============================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.81.0",
3
+ "version": "6.82.0",
4
4
  "description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "keywords": [
6
6
  "agent",