loki-mode 7.65.0 → 7.66.1

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: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.65.0
6
+ # Loki Mode v7.66.1
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -406,4 +406,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
406
406
 
407
407
  ---
408
408
 
409
- **v7.65.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
409
+ **v7.66.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.65.0
1
+ 7.66.1
@@ -75,6 +75,12 @@ COUNCIL_CONSECUTIVE_NO_CHANGE=0
75
75
  COUNCIL_DONE_SIGNALS=0
76
76
  COUNCIL_TOTAL_DONE_SIGNALS=0
77
77
  COUNCIL_LAST_DIFF_HASH=""
78
+ # bash-F1: distinguishes a genuine council approval from a force-stop safety
79
+ # valve (stagnation / done-signal flood). council_should_stop sets this to 1
80
+ # ONLY on the force-stop paths; run.sh reads it (same sourced shell) to avoid
81
+ # reporting a non-approved force-stop as a verified-complete product. Guarded
82
+ # default so set -u never trips before the function runs.
83
+ : "${COUNCIL_FORCE_STOPPED:=0}"
78
84
 
79
85
  #===============================================================================
80
86
  # v6.83.0 Phase 1: Managed Agents memory augmentation (opt-in).
@@ -2051,27 +2057,32 @@ ISSUES: CRITICAL:description (optional, one per line per issue)"
2051
2057
  # no VOTE and default a real APPROVE to REJECT. Capture the full
2052
2058
  # output; the downstream parse already greps VOTE/REASON/ISSUES.
2053
2059
  # CAVEMAN_DEFAULT_MODE=off suppression is preserved (see above).
2054
- verdict=$(echo "$prompt" | env CAVEMAN_DEFAULT_MODE=off claude "${_cm_argv[@]}" -p 2>/dev/null)
2060
+ # bash-F3: timeout-guard the provider subcall so a hung CLI can
2061
+ # not stall the whole council. Default 600s matches the Bun route
2062
+ # (council.ts LOKI_COUNCIL_TIMEOUT_MS=600000). A timeout yields
2063
+ # empty output, which the [ -z "$verdict" ] fallback below turns
2064
+ # into a conservative heuristic review.
2065
+ verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" env CAVEMAN_DEFAULT_MODE=off claude "${_cm_argv[@]}" -p 2>/dev/null)
2055
2066
  fi
2056
2067
  ;;
2057
2068
  codex)
2058
2069
  if command -v codex &>/dev/null; then
2059
- verdict=$(codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
2070
+ verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
2060
2071
  fi
2061
2072
  ;;
2062
2073
  gemini)
2063
2074
  if command -v gemini &>/dev/null; then
2064
- verdict=$(echo "$prompt" | gemini 2>/dev/null)
2075
+ verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" gemini 2>/dev/null)
2065
2076
  fi
2066
2077
  ;;
2067
2078
  cline)
2068
2079
  if command -v cline &>/dev/null; then
2069
- verdict=$(cline -y "$prompt" 2>/dev/null)
2080
+ verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" cline -y "$prompt" 2>/dev/null)
2070
2081
  fi
2071
2082
  ;;
2072
2083
  aider)
2073
2084
  if command -v aider &>/dev/null; then
2074
- verdict=$(aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
2085
+ verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
2075
2086
  fi
2076
2087
  ;;
2077
2088
  esac
@@ -2152,27 +2163,30 @@ REASON: your reasoning"
2152
2163
  # Inlined on `claude` only (does not cross the pipe). No-op absent.
2153
2164
  # v7.41.3 BUG A: full capture, no tail-truncation (see member
2154
2165
  # subcall note). CAVEMAN_DEFAULT_MODE=off suppression preserved.
2155
- verdict=$(echo "$prompt" | env CAVEMAN_DEFAULT_MODE=off claude "${_co_argv[@]}" -p 2>/dev/null)
2166
+ # bash-F3: timeout-guard the devil's-advocate subcall (same 600s
2167
+ # default as the member subcalls / Bun council.ts). An empty
2168
+ # verdict on timeout hits the conservative REJECT fallback below.
2169
+ verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" env CAVEMAN_DEFAULT_MODE=off claude "${_co_argv[@]}" -p 2>/dev/null)
2156
2170
  fi
2157
2171
  ;;
2158
2172
  codex)
2159
2173
  if command -v codex &>/dev/null; then
2160
- verdict=$(codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
2174
+ verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
2161
2175
  fi
2162
2176
  ;;
2163
2177
  gemini)
2164
2178
  if command -v gemini &>/dev/null; then
2165
- verdict=$(echo "$prompt" | gemini 2>/dev/null)
2179
+ verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" gemini 2>/dev/null)
2166
2180
  fi
2167
2181
  ;;
2168
2182
  cline)
2169
2183
  if command -v cline &>/dev/null; then
2170
- verdict=$(cline -y "$prompt" 2>/dev/null)
2184
+ verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" cline -y "$prompt" 2>/dev/null)
2171
2185
  fi
2172
2186
  ;;
2173
2187
  aider)
2174
2188
  if command -v aider &>/dev/null; then
2175
- verdict=$(aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
2189
+ verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
2176
2190
  fi
2177
2191
  ;;
2178
2192
  esac
@@ -2753,7 +2767,13 @@ council_evaluate() {
2753
2767
  fi
2754
2768
  fi
2755
2769
  if [ -z "$aggregate_result" ]; then
2756
- aggregate_result=$(council_aggregate_votes)
2770
+ # bash-F2: council_aggregate_votes emits log_info/log_warn lines on
2771
+ # stdout (run.sh log_* helpers do not redirect to stderr) before its
2772
+ # terminal `echo "$verdict"`. Capturing the whole stream and exact-
2773
+ # matching "COMPLETE" never succeeds, so the heuristic path could never
2774
+ # return COMPLETE. Take only the last line (the verdict token); any
2775
+ # degenerate empty output falls through to the safe not-COMPLETE default.
2776
+ aggregate_result=$(council_aggregate_votes | tail -n1)
2757
2777
  fi
2758
2778
 
2759
2779
  if [ "$aggregate_result" = "COMPLETE" ]; then
@@ -3011,6 +3031,11 @@ PYEOF
3011
3031
  #===============================================================================
3012
3032
 
3013
3033
  council_should_stop() {
3034
+ # bash-F1: reset the force-stop sentinel at entry. A return-0 from the
3035
+ # genuine approval path leaves this 0; only the safety-valve paths below
3036
+ # set it to 1 before their return-0.
3037
+ COUNCIL_FORCE_STOPPED=0
3038
+
3014
3039
  if [ "$COUNCIL_ENABLED" != "true" ]; then
3015
3040
  return 1 # Council disabled, don't stop
3016
3041
  fi
@@ -3101,6 +3126,7 @@ council_should_stop() {
3101
3126
  if [ "$COUNCIL_CONSECUTIVE_NO_CHANGE" -ge "$safety_limit" ]; then
3102
3127
  log_error "Safety valve: ${COUNCIL_CONSECUTIVE_NO_CHANGE} iterations with no changes exceeds safety limit ($safety_limit)"
3103
3128
  log_error "Forcing stop to prevent resource waste"
3129
+ COUNCIL_FORCE_STOPPED=1 # bash-F1: force-stop, NOT a council approval
3104
3130
  return 0 # FORCE STOP
3105
3131
  fi
3106
3132
  fi
@@ -3109,6 +3135,7 @@ council_should_stop() {
3109
3135
  if [ "$COUNCIL_TOTAL_DONE_SIGNALS" -ge "$COUNCIL_DONE_SIGNAL_LIMIT" ]; then
3110
3136
  log_error "Safety valve: Agent signaled 'done' $COUNCIL_TOTAL_DONE_SIGNALS times (limit: $COUNCIL_DONE_SIGNAL_LIMIT)"
3111
3137
  log_error "Forcing stop - agent believes work is complete"
3138
+ COUNCIL_FORCE_STOPPED=1 # bash-F1: force-stop, NOT a council approval
3112
3139
  return 0 # FORCE STOP
3113
3140
  fi
3114
3141
 
package/autonomy/run.sh CHANGED
@@ -15728,6 +15728,23 @@ else:
15728
15728
  ensure_completion_test_evidence || true
15729
15729
  fi
15730
15730
  if type council_should_stop &>/dev/null && council_should_stop; then
15731
+ # bash-F1: council_should_stop returns 0 from a genuine approval
15732
+ # AND from two force-stop safety valves (stagnation flood /
15733
+ # repeated done-signals). A force-stop is NOT a verified-complete
15734
+ # product, so it must not claim "PROJECT COMPLETE" or open a PR.
15735
+ # The sentinel set inside council_should_stop disambiguates.
15736
+ if [ "${COUNCIL_FORCE_STOPPED:-0}" = "1" ]; then
15737
+ echo ""
15738
+ log_header "COMPLETION COUNCIL: STOPPED WITHOUT APPROVAL"
15739
+ log_warn "Council force-stopped (stagnation or repeated done-signals); work is NOT verified-complete"
15740
+ log_info "Running memory consolidation..."
15741
+ run_memory_consolidation
15742
+ # No on_run_complete: a force-stop must never open a "done" PR.
15743
+ emit_completion_summary force_stopped
15744
+ save_state $retry "force_stopped" 0
15745
+ rm -f "$iter_output" 2>/dev/null
15746
+ return 0
15747
+ fi
15731
15748
  echo ""
15732
15749
  log_header "COMPLETION COUNCIL: PROJECT COMPLETE"
15733
15750
  log_info "Council voted to stop (convergence detected + requirements verified)"
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.65.0"
10
+ __version__ = "7.66.1"
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/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.65.0
5
+ **Version:** v7.66.1
6
6
 
7
7
  ---
8
8
 
@@ -395,7 +395,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
395
395
  # Run Loki Mode in Docker (Claude provider, API-key auth)
396
396
  docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
397
397
  -v $(pwd):/workspace -w /workspace \
398
- asklokesh/loki-mode:7.65.0 start ./my-spec.md
398
+ asklokesh/loki-mode:7.66.1 start ./my-spec.md
399
399
  ```
400
400
 
401
401
  ##### docker compose + .env (no host install)
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var r6=Object.defineProperty;var t6=($)=>$;function i6($,Q){this[$]=t6.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)r6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:i6.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var D1={};b(D1,{lokiDir:()=>j,homeLokiDir:()=>a$,findRepoRootForVersion:()=>n$,REPO_ROOT:()=>g});import{resolve as a,dirname as o$}from"path";import{fileURLToPath as e6}from"url";import{existsSync as j$}from"fs";import{homedir as $Q}from"os";function QQ(){let $=S1;for(let Q=0;Q<6;Q++){if(j$(a($,"VERSION"))&&j$(a($,"autonomy/run.sh")))return $;let Z=o$($);if(Z===$)break;$=Z}return a(S1,"..","..","..")}function n$($){let Q=$;for(let Z=0;Z<6;Z++){if(j$(a(Q,"VERSION"))&&j$(a(Q,"autonomy/run.sh")))return Q;let z=o$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function j(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function a$(){return a($Q(),".loki")}var S1,g;var C=P(()=>{S1=o$(e6(import.meta.url));g=QQ()});import{readFileSync as ZQ}from"fs";import{resolve as zQ,dirname as XQ}from"path";import{fileURLToPath as KQ}from"url";function F$(){if(Q$!==null)return Q$;let $="7.65.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=XQ(KQ(import.meta.url)),Z=n$(Q);Q$=ZQ(zQ(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var s$=P(()=>{C()});var b1={};b(b1,{runOrThrow:()=>qQ,run:()=>k,commandVersion:()=>JQ,commandExists:()=>f,ShellError:()=>r$});async function k($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function qQ($,Q={}){let Z=await k($,Q);if(Z.exitCode!==0)throw new r$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=VQ($),Z=await k(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function VQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function JQ($,Q="--version"){if(!await f($))return null;let z=await k([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var r$;var d=P(()=>{r$=class r$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function s($){return WQ?"":$}var WQ,T,S,_,wZ,I,R,h,V;var c=P(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=s("\x1B[0;31m"),S=s("\x1B[0;32m"),_=s("\x1B[1;33m"),wZ=s("\x1B[0;34m"),I=s("\x1B[0;36m"),R=s("\x1B[1m"),h=s("\x1B[2m"),V=s("\x1B[0m")});import{existsSync as wQ}from"fs";async function Z$(){if(Y$!==void 0)return Y$;let $="/opt/homebrew/bin/python3.12";if(wQ($))return Y$=$,$;let Q=await f("python3.12");if(Q)return Y$=Q,Q;let Z=await f("python3");return Y$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return k([Z,"-c",$],Q)}var Y$;var V$=P(()=>{d()});var e1={};b(e1,{runStatus:()=>uQ});import{existsSync as y,readFileSync as W$,readdirSync as d1,statSync as o1}from"fs";import{resolve as D,basename as DQ}from"path";import{homedir as CQ}from"os";function n1($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function a1($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*R$/Q);if(X>R$)X=R$;let q=R$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),J=n1($),U=n1(Q);return` ${R}${Z}${V} ${K}[${W}]${V} ${z}% (${J} / ${U})`}async function hQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${V}
2
+ var r6=Object.defineProperty;var t6=($)=>$;function i6($,Q){this[$]=t6.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)r6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:i6.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var D1={};b(D1,{lokiDir:()=>j,homeLokiDir:()=>a$,findRepoRootForVersion:()=>n$,REPO_ROOT:()=>g});import{resolve as a,dirname as o$}from"path";import{fileURLToPath as e6}from"url";import{existsSync as j$}from"fs";import{homedir as $Q}from"os";function QQ(){let $=S1;for(let Q=0;Q<6;Q++){if(j$(a($,"VERSION"))&&j$(a($,"autonomy/run.sh")))return $;let Z=o$($);if(Z===$)break;$=Z}return a(S1,"..","..","..")}function n$($){let Q=$;for(let Z=0;Z<6;Z++){if(j$(a(Q,"VERSION"))&&j$(a(Q,"autonomy/run.sh")))return Q;let z=o$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function j(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function a$(){return a($Q(),".loki")}var S1,g;var C=P(()=>{S1=o$(e6(import.meta.url));g=QQ()});import{readFileSync as ZQ}from"fs";import{resolve as zQ,dirname as XQ}from"path";import{fileURLToPath as KQ}from"url";function F$(){if(Q$!==null)return Q$;let $="7.66.1";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=XQ(KQ(import.meta.url)),Z=n$(Q);Q$=ZQ(zQ(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var s$=P(()=>{C()});var b1={};b(b1,{runOrThrow:()=>qQ,run:()=>k,commandVersion:()=>JQ,commandExists:()=>f,ShellError:()=>r$});async function k($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function qQ($,Q={}){let Z=await k($,Q);if(Z.exitCode!==0)throw new r$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=VQ($),Z=await k(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function VQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function JQ($,Q="--version"){if(!await f($))return null;let z=await k([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var r$;var d=P(()=>{r$=class r$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function s($){return WQ?"":$}var WQ,T,S,_,wZ,I,R,h,V;var c=P(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=s("\x1B[0;31m"),S=s("\x1B[0;32m"),_=s("\x1B[1;33m"),wZ=s("\x1B[0;34m"),I=s("\x1B[0;36m"),R=s("\x1B[1m"),h=s("\x1B[2m"),V=s("\x1B[0m")});import{existsSync as wQ}from"fs";async function Z$(){if(Y$!==void 0)return Y$;let $="/opt/homebrew/bin/python3.12";if(wQ($))return Y$=$,$;let Q=await f("python3.12");if(Q)return Y$=Q,Q;let Z=await f("python3");return Y$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return k([Z,"-c",$],Q)}var Y$;var V$=P(()=>{d()});var e1={};b(e1,{runStatus:()=>uQ});import{existsSync as y,readFileSync as W$,readdirSync as d1,statSync as o1}from"fs";import{resolve as D,basename as DQ}from"path";import{homedir as CQ}from"os";function n1($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function a1($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*R$/Q);if(X>R$)X=R$;let q=R$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),J=n1($),U=n1(Q);return` ${R}${Z}${V} ${K}[${W}]${V} ${z}% (${J} / ${U})`}async function hQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${V}
3
3
  `),process.stdout.write(`Install with:
4
4
  `),process.stdout.write(` brew install jq (macOS)
5
5
  `),process.stdout.write(` apt install jq (Debian/Ubuntu)
@@ -791,4 +791,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
791
791
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
792
792
  `),process.stderr.write(s6),2}}l1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var KZ=await XZ(Bun.argv.slice(2));process.exit(KZ);
793
793
 
794
- //# debugId=1BFA28C250C77A2E64756E2164756E21
794
+ //# debugId=3E79E75C7B6CF7FD64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.65.0'
60
+ __version__ = '7.66.1'
package/memory/storage.py CHANGED
@@ -317,10 +317,16 @@ class MemoryStorage:
317
317
  if lock_file is not None:
318
318
  fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
319
319
  lock_file.close()
320
- try:
321
- os.remove(lock_path)
322
- except OSError:
323
- pass
320
+ # Do NOT os.remove(lock_path) here. Unlinking the lock file on
321
+ # release is a flock+unlink inode-replacement race: with 3+
322
+ # contenders, holder A unlinks inode-1 after B (blocked on it)
323
+ # acquires it, then C opens the path, finds it gone, creates
324
+ # inode-2, and flocks inode-2 -- entering the critical section
325
+ # while B is still inside. That dropped index.json topics under
326
+ # concurrent store_pattern/store_episode (reproduced on Linux
327
+ # py3.13, 16 threads). Persistent lock files are the standard
328
+ # flock pattern; stale ones are GC'd by _cleanup_stale_locks,
329
+ # which is itself flock-safe (probe-before-unlink, wave-6).
324
330
 
325
331
  def _atomic_write(self, path: Path, data: dict) -> None:
326
332
  """
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "loki-mode",
3
3
  "mcpName": "io.github.asklokesh/loki-mode",
4
- "version": "7.65.0",
4
+ "version": "7.66.1",
5
5
  "description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
6
6
  "keywords": [
7
7
  "agent",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "loki-mode",
4
4
  "displayName": "Loki Mode",
5
- "version": "7.65.0",
5
+ "version": "7.66.1",
6
6
  "description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
7
7
  "author": {
8
8
  "name": "Autonomi",