loki-mode 9.50.3 → 9.50.4

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 v9.50.3
6
+ # Loki Mode v9.50.4
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
470
470
 
471
471
  ---
472
472
 
473
- **v9.50.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
473
+ **v9.50.4 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 9.50.3
1
+ 9.50.4
package/autonomy/run.sh CHANGED
@@ -4423,6 +4423,20 @@ build_completion_summary() {
4423
4423
  # checkable receipt. Name it.
4424
4424
  council_force_approved) outcome_label="Completed (force-approved)"
4425
4425
  notify_title="Run complete (force-approved)" ;;
4426
+ # The three gate-stuck terminals. Spelled out one literal arm each
4427
+ # rather than as a single wildcard pattern:
4428
+ # tests/test-completion-outcome-labels.sh derives outcomes from the call
4429
+ # sites and matches `^ *<outcome>\)`, so a wildcard arm renders correctly
4430
+ # at runtime while reading as "no label arm" to the guard. Literal arms
4431
+ # keep that guard strictly literal, which is what makes a NEWLY-added
4432
+ # outcome fail there instead of shipping as a raw enum. Same reason the
4433
+ # block above names each outcome.
4434
+ gate_stuck_static_analysis) outcome_label="Stopped (static analysis gate would not clear)"
4435
+ notify_title="Run stopped (gate not clearing)" ;;
4436
+ gate_stuck_mock_integrity) outcome_label="Stopped (mock integrity gate would not clear)"
4437
+ notify_title="Run stopped (gate not clearing)" ;;
4438
+ gate_stuck_mutation_integrity) outcome_label="Stopped (mutation integrity gate would not clear)"
4439
+ notify_title="Run stopped (gate not clearing)" ;;
4426
4440
  *) outcome_label="$outcome"; notify_title="Run finished" ;;
4427
4441
  esac
4428
4442
 
@@ -5244,6 +5258,12 @@ EOF
5244
5258
  force_stopped) _label="Stopped (not verified-complete)" ;;
5245
5259
  failed) _label="Failed" ;;
5246
5260
  intervention) _label="Needs input" ;;
5261
+ # Mirror of build_completion_summary's gate-stuck arms. Both must move
5262
+ # together or the card and COMPLETION.txt disagree on the same run.
5263
+ # Literal, not a glob, for the guard reason recorded there.
5264
+ gate_stuck_static_analysis) _label="Stopped (static analysis gate would not clear)" ;;
5265
+ gate_stuck_mock_integrity) _label="Stopped (mock integrity gate would not clear)" ;;
5266
+ gate_stuck_mutation_integrity) _label="Stopped (mutation integrity gate would not clear)" ;;
5247
5267
  *) _label="$_outcome" ;;
5248
5268
  esac
5249
5269
 
@@ -24221,6 +24241,14 @@ EOF
24221
24241
  "gate=static_analysis" \
24222
24242
  "consecutive=$sa_count" 2>/dev/null || true
24223
24243
  save_state "${retry:-0}" "gate_stuck_static_analysis" 20 2>/dev/null || true
24244
+ # Same rule as the COUNCIL_FORCE_STOPPED terminal below
24245
+ # ("No on_run_complete: a force-stop must never open a
24246
+ # 'done' PR"): a non-verified stop never opens a PR, but
24247
+ # it MUST still write COMPLETION.txt and ping. Without
24248
+ # this, the only terminal in run_autonomous that tells
24249
+ # the user nothing is the one that stopped because a
24250
+ # gate would not clear.
24251
+ emit_completion_summary gate_stuck_static_analysis
24224
24252
  return 20
24225
24253
  fi
24226
24254
  fi
@@ -24347,6 +24375,7 @@ EOF
24347
24375
  "gate=mock_integrity" \
24348
24376
  "consecutive=$mk_count" 2>/dev/null || true
24349
24377
  save_state "${retry:-0}" "gate_stuck_mock_integrity" 20 2>/dev/null || true
24378
+ emit_completion_summary gate_stuck_mock_integrity
24350
24379
  return 20
24351
24380
  fi
24352
24381
  ;;
@@ -24395,6 +24424,7 @@ EOF
24395
24424
  "gate=mutation_integrity" \
24396
24425
  "consecutive=$mt_count" 2>/dev/null || true
24397
24426
  save_state "${retry:-0}" "gate_stuck_mutation_integrity" 20 2>/dev/null || true
24427
+ emit_completion_summary gate_stuck_mutation_integrity
24398
24428
  return 20
24399
24429
  fi
24400
24430
  fi
@@ -27291,7 +27321,12 @@ except Exception:
27291
27321
  # The operator raises the cap (or narrows the spec) and submits a
27292
27322
  # NEW Job -- the same remedy as max_iterations_reached, which is why
27293
27323
  # it shares that code.
27294
- failed|max_iterations_reached|max_retries_exceeded|budget_exceeded|max_duration_reached|policy_blocked|inconclusive_spec_contradiction|force_stopped)
27324
+ # gate_stuck_* is deterministic for the same reason: the same gate
27325
+ # failed for the same reason N times, so a retry reaches the same
27326
+ # verdict. It already arrived here as 20 via save_state, but only
27327
+ # by falling through `*)`, which logs it as "crash, retryable" and
27328
+ # leaves a k8s podFailurePolicy reading a value nothing asserts.
27329
+ failed|max_iterations_reached|max_retries_exceeded|budget_exceeded|max_duration_reached|policy_blocked|inconclusive_spec_contradiction|force_stopped|gate_stuck_static_analysis|gate_stuck_mock_integrity|gate_stuck_mutation_integrity)
27295
27330
  result=20 ;;
27296
27331
  *)
27297
27332
  # Unknown/running/exited terminal: leave $result as-is (nonzero on a
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "9.50.3"
10
+ __version__ = "9.50.4"
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:** v9.50.3
5
+ **Version:** v9.50.4
6
6
 
7
7
  ---
8
8
 
@@ -70,31 +70,52 @@ Scope it honestly: perpetual mode auto-clears PAUSE and continues
70
70
  (`autonomy/run.sh:25470-25500`), except when the pause came from budget
71
71
  enforcement. Default mode does not auto-clear.
72
72
 
73
- ## 2. Three gates can terminate the run at exit 20, and that path never opens a PR
74
-
75
- `_loki_gate_stuck` (`autonomy/run.sh:11098`, threshold
76
- `LOKI_GATE_STUCK_THRESHOLD:-3`) compares a stable cause line across
77
- consecutive failures. When the same gate fails for the same reason three
78
- times, the run stops rather than grinding. It fires for three gates:
79
-
80
- - static analysis, `autonomy/run.sh:24094-24101`
81
- - mock integrity, `autonomy/run.sh:24220-24227`
82
- - mutation integrity, `autonomy/run.sh:24266-24275`
83
-
84
- Each does `save_state ... 20` and `return 20` out of `run_autonomous`.
85
-
86
- Stopping a non-converging loop is correct behavior and better than grinding.
87
- The one-run break is what the user is left holding: `on_run_complete`, the
88
- function that opens the PR, is called only from the success exits
89
- (`autonomy/run.sh:24803`, `:25025`, `:25661`). A `return 20` leaves
90
- `run_autonomous` before reaching any of them, so no PR is opened. The
91
- deliverable stays on the session branch and the user must discover and finish
92
- it by hand.
93
-
94
- This mirrors a deliberate decision elsewhere: the council force-stop path at
95
- `autonomy/run.sh:24770` carries the comment "No on_run_complete: a force-stop
96
- must never open a 'done' PR." The gate-stuck path inherits that outcome
97
- without stating it.
73
+ ## 2. Gate-stuck was the only terminal that told the user nothing (FIXED)
74
+
75
+ **Partly refuted, then fixed. The original framing of this finding was wrong,
76
+ and the correction is the useful part.**
77
+
78
+ `_loki_gate_stuck` (threshold `LOKI_GATE_STUCK_THRESHOLD:-3`) compares a stable
79
+ cause line across consecutive failures. When the same gate fails for the same
80
+ reason three times the run stops rather than grinding, for static analysis,
81
+ mock integrity and mutation integrity. Each does `save_state ... 20` then
82
+ `return 20` out of `run_autonomous`. (Line numbers are deliberately omitted:
83
+ every one this section originally cited had drifted before the fix landed.
84
+ Anchor on the function names.)
85
+
86
+ **What was wrong with the original finding.** It claimed "the deliverable stays
87
+ on the session branch and the user must discover and finish it by hand".
88
+ `commit_session_changes` is commit-always by design, including on failed runs,
89
+ and both it and `create_session_pr` are called from `main()` AFTER
90
+ `run_autonomous` returns 20. `create_session_pr` then calls `print_pr_advice`
91
+ (`autonomy/lib/git-pr-advisory.sh`), which prints the branch, the `git push -u`
92
+ line and the `gh pr create` line. So the work was already committed and the push
93
+ commands already printed. Two real bounds on that: `create_session_pr` returns
94
+ early when there are no commits, and `commit_session_changes` only commits on a
95
+ Loki-minted `loki/session-*` branch.
96
+
97
+ **The defect that was real.** Gate-stuck was the ONLY terminal in
98
+ `run_autonomous` that never called `emit_completion_summary`. Every other one
99
+ does, including the council force-stop. So it wrote no COMPLETION.txt, rendered
100
+ no completion card, and sent no notification. A `--bg` user got no ping and
101
+ nothing in the one file they are told to read, and `print_pr_advice` goes to a
102
+ stdout a detached run never shows them.
103
+
104
+ **Fixed:** all three branches now call `emit_completion_summary` with their own
105
+ outcome, each outcome has a literal label arm in both `build_completion_summary`
106
+ and `print_completion_card`, and the three statuses were added to the ENT-3
107
+ terminal-failure arm so exit 20 is classified by intent rather than reached by
108
+ fall-through (the log line previously read "crash, retryable").
109
+
110
+ **Not changed, deliberately:** no PR is opened. The council force-stop carries
111
+ the comment "No on_run_complete: a force-stop must never open a 'done' PR", and
112
+ that precedent bans the PR while mandating the summary in the same breath. This
113
+ applies the precedent rather than violating it.
114
+
115
+ Guards: `tests/test-completion-outcome-labels.sh` (now fast-tier; its literal
116
+ matcher is what caught a wildcard arm that rendered correctly but read as
117
+ unlabelled), `tests/test-exit-code-contract.sh` (10 to 13 assertions), and an
118
+ executed wiring assertion in `tests/test-terminal-next-step.sh`.
98
119
 
99
120
  ## 3. There is no cost-per-completed-task anywhere; only cost per iteration
100
121
 
@@ -271,7 +292,7 @@ Recorded so neither is raised again.
271
292
  | # | Break | Evidence | Pain |
272
293
  |---|---|---|---|
273
294
  | 1 | Gate escalation forces a PAUSE that waits forever, no timeout, no tty guard | `run.sh:24473-24477`, `run.sh:25793-25818`, defaults `run.sh:1513-1515` | Run stalls silently in `--bg`; the failing loop the demand names |
274
- | 2 | Gate-stuck exit 20 ends the run without opening a PR | `run.sh:11098`, `:24101`, `:24227`, `:24275`; PR only at `:24803`, `:25025`, `:25661` | Work exists on a branch the user must find and finish |
295
+ | 2 | FIXED. Gate-stuck was the only terminal that called no `emit_completion_summary`, so it wrote no COMPLETION.txt and sent no ping | `_loki_gate_stuck` branches in `run_autonomous`; label arms in `build_completion_summary` and `print_completion_card`; ENT-3 terminal arm | A `--bg` user got no notification and nothing in the file they are told to read. No PR, deliberately: same precedent as the council force-stop |
275
296
  | 3 | No cost-per-completed-task; only per-iteration | `loki:6447`, `:6518`; cost `loki:28461` and tasks `loki:28514` never divided | "Least cost per task" is unmeasurable today |
276
297
  | 4 | No task-value-per-dollar metric | 0 hits with positive control; `loki:28535-28545` is a fixed 15-min multiplier | The demand's headline metric does not exist |
277
298
  | 5 | No-flag issue run never uses its own PR block; PR depends on a guard chain with three silent no-ops, then teardown prints advice for the PR it already opened | `loki:10762` unreached (`loki:2602`, `:10330`); `run.sh:5279`, `:5282`, `:5287-5290`; `run.sh:9175` + `git-pr-advisory.sh:69-111` | Deliverable can vanish silently on the headline use case; contradictory closing instruction |
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var ht=Object.create;var{getPrototypeOf:gt,defineProperty:jG,getOwnPropertyNames:mt}=Object;var ut=Object.prototype.hasOwnProperty;function dt($){return this[$]}var pt,ct,lt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?pt??=new WeakMap:ct??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?ht(gt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of mt($))if(!ut.call(J,q))jG(J,q,{get:dt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var it=($)=>$;function at($,X){this[$]=it.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:at.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var HR={};B1(HR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as ot}from"url";import{existsSync as Zq}from"fs";import{homedir as st}from"os";function nt(){let $=UR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(UR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(st(),".loki")}var UR,L1;var k1=s(()=>{UR=LG(ot(import.meta.url));L1=nt()});import{readFileSync as rt}from"fs";import{resolve as tt,dirname as et}from"path";import{fileURLToPath as $e}from"url";function j9(){if(n3!==null)return n3;let $="9.50.3";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=et($e(import.meta.url)),Q=AG(X);n3=rt(tt(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var jR={};B1(jR,{runOrThrow:()=>Me,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>je,commandExists:()=>g5,ShellError:()=>TG,MAX_STDOUT_BYTES:()=>OR});async function Jq($,X=OR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function $1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([Jq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Me($,X={}){let Q=await $1($,X);if(Q.exitCode!==0)throw new TG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=Oe($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Oe($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function je($,X="--version"){if(!await g5($))return null;let z=await $1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var OR=16777216,TG;var y8=s(()=>{TG=class TG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Le?"":$}var Le,p0,$5,q1,F61,A1,f1,m5,r;var t7=s(()=>{Le=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),F61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as Ee}from"fs";async function Z2(){if(BQ!==void 0)return BQ;let $="/opt/homebrew/bin/python3.12";if(Ee($))return BQ=$,$;let X=await g5("python3.12");if(X)return BQ=X,X;let Q=await g5("python3");return BQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([Q,"-c",$],X)}var BQ;var m2=s(()=>{y8()});var hR={};B1(hR,{runStatus:()=>$00});import{existsSync as u5,readFileSync as C9,readdirSync as xR,statSync as kR}from"fs";import{resolve as A5,basename as le}from"path";import{homedir as ie}from"os";function SR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function yR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Vq/X);if(Z>Vq)Z=Vq;let K=Vq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=q1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=SR($),Y=SR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function oe(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
2
+ var ht=Object.create;var{getPrototypeOf:gt,defineProperty:jG,getOwnPropertyNames:mt}=Object;var ut=Object.prototype.hasOwnProperty;function dt($){return this[$]}var pt,ct,lt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?pt??=new WeakMap:ct??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?ht(gt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of mt($))if(!ut.call(J,q))jG(J,q,{get:dt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var it=($)=>$;function at($,X){this[$]=it.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:at.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var HR={};B1(HR,{lokiDir:()=>h0,homeLokiDir:()=>GQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as ot}from"url";import{existsSync as Zq}from"fs";import{homedir as st}from"os";function nt(){let $=UR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(UR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function GQ(){return h2(st(),".loki")}var UR,L1;var k1=s(()=>{UR=LG(ot(import.meta.url));L1=nt()});import{readFileSync as rt}from"fs";import{resolve as tt,dirname as et}from"path";import{fileURLToPath as $e}from"url";function j9(){if(n3!==null)return n3;let $="9.50.4";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=et($e(import.meta.url)),Q=AG(X);n3=rt(tt(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var jR={};B1(jR,{runOrThrow:()=>Me,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>je,commandExists:()=>g5,ShellError:()=>TG,MAX_STDOUT_BYTES:()=>OR});async function Jq($,X=OR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function $1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([Jq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Me($,X={}){let Q=await $1($,X);if(Q.exitCode!==0)throw new TG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=Oe($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Oe($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function je($,X="--version"){if(!await g5($))return null;let z=await $1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var OR=16777216,TG;var y8=s(()=>{TG=class TG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Le?"":$}var Le,p0,$5,q1,F61,A1,f1,m5,r;var t7=s(()=>{Le=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),F61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as Ee}from"fs";async function Z2(){if(BQ!==void 0)return BQ;let $="/opt/homebrew/bin/python3.12";if(Ee($))return BQ=$,$;let X=await g5("python3.12");if(X)return BQ=X,X;let Q=await g5("python3");return BQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([Q,"-c",$],X)}var BQ;var m2=s(()=>{y8()});var hR={};B1(hR,{runStatus:()=>$00});import{existsSync as u5,readFileSync as C9,readdirSync as xR,statSync as kR}from"fs";import{resolve as A5,basename as le}from"path";import{homedir as ie}from"os";function SR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function yR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Vq/X);if(Z>Vq)Z=Vq;let K=Vq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=q1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=SR($),Y=SR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function oe(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
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)
@@ -1221,7 +1221,7 @@ FINDINGS:
1221
1221
  FINDINGS:
1222
1222
  - [Critical] reviewer produced no output`;return z.stdout},H31=async()=>`VERDICT: ${Vt}
1223
1223
  FINDINGS:
1224
- - [Info] no reviewer CLI available; review skipped`,ir=64,L31;var At=s(()=>{A$();qY();w4();k1();y8();Zt={"security-sentinel":{keywords:["auth","login","password","token","api","sql","query","cookie","cors","csrf"],focus:"OWASP Top 10, injection, auth, secrets, input validation",checks:"injection (SQL, XSS, command, template), auth bypass, secrets in code, missing input validation, OWASP Top 10, insecure defaults",priority:0},"test-coverage-auditor":{keywords:["test","spec","coverage","assert","mock","fixture","expect","describe"],focus:"Missing tests, edge cases, error paths, boundary conditions",checks:"missing test cases, uncovered error paths, boundary conditions, mock correctness, test isolation, flaky test patterns",priority:1},"performance-oracle":{keywords:["database","query","cache","render","loop","fetch","load","index","join","pool"],focus:"N+1 queries, memory leaks, caching, bundle size, lazy loading",checks:"N+1 queries, unbounded loops, memory leaks, missing caching, excessive re-renders, large bundle imports, missing pagination",priority:2},"dependency-analyst":{keywords:["package","import","require","dependency","npm","pip","yarn","lock"],focus:"Outdated packages, CVEs, bloat, unused deps, license issues",checks:"outdated dependencies, known CVEs, unnecessary imports, dependency bloat, license compatibility, unused packages",priority:3},"legacy-healing-auditor":{keywords:["legacy","heal","migrate","cobol","fortran","refactor","modernize","deprecat","adapter","friction","characterization"],focus:"Behavioral preservation, friction safety, institutional knowledge retention",checks:"behavioral change without characterization test, removal of quirky code without friction map check, missing adapter layer for replaced components, institutional knowledge loss (deleted comments, removed error messages), breaking changes to undocumented APIs",priority:4}},Z31={name:"architecture-strategist",focus:"SOLID, coupling, cohesion, patterns, abstraction, dependency direction",checks:"SOLID violations, excessive coupling, wrong patterns, missing abstractions, dependency direction issues, god classes/functions"},K31={name:"maintainer-mergeability",focus:"Would a maintainer merge this PR as-is: scope discipline, dead/duplicated code, convention conformance",checks:"scope creep (changes unrelated to the stated task, drive-by edits, unrequested refactors), dead code (unreachable, unused, commented-out, leftover debug), duplicated logic that should reuse an existing helper, non-conformance to the surrounding code's conventions (naming, error handling, structure, formatting), and anything a careful human reviewer would ask to be changed before merging"};q31={simple:2,standard:2,complex:4};HG=Z1(L1,"loki-ts","data","code-review-schema.json");L31=/\[([^\]]+)\]\(([^)\s]+)\)/g});var ZR={};B1(ZR,{readHumanInput:()=>k31,handlePause:()=>b31,checkHumanIntervention:()=>S31});import{existsSync as F4,lstatSync as Ct,mkdirSync as $R,readFileSync as XR,renameSync as P31,statSync as Tt,unlinkSync as E31}from"fs";import{join as D4}from"path";function QR($){return $??h0()}function zR($){return{pause:D4($,"PAUSE"),pauseAtCheckpoint:D4($,"PAUSE_AT_CHECKPOINT"),humanInput:D4($,"HUMAN_INPUT.md"),councilReview:D4($,"signals","COUNCIL_REVIEW_REQUESTED"),stop:D4($,"STOP"),pausedMd:D4($,"PAUSED.md"),budgetExceeded:D4($,"signals","BUDGET_EXCEEDED"),logsDir:D4($,"logs")}}function e8($){try{E31($)}catch{}}function x31($){let X=(Q,z=2)=>String(Q).padStart(z,"0");return`${$.getUTCFullYear()}${X($.getUTCMonth()+1)}${X($.getUTCDate())}-${X($.getUTCHours())}${X($.getUTCMinutes())}${X($.getUTCSeconds())}`}function eF($,X,Q,z){try{$R(X,{recursive:!0})}catch{}let Z=D4(X,`${Q}-${x31(z)}.md`);try{return P31($,Z),Z}catch{return e8($),""}}function k31($={}){let X=QR($.lokiDirOverride),Q=zR(X).humanInput;if(!F4(Q))return null;let z;try{z=Ct(Q)}catch{return null}if(z.isSymbolicLink())return null;let Z;try{Z=Tt(Q)}catch{return null}if(Z.size>Dt)return null;try{return XR(Q,"utf8")}catch{return null}}function S31($={}){let X=QR($.lokiDirOverride),Q=zR(X),z=$.autonomyMode??"standard",Z=$.now??new Date;if(F4(Q.pause)){if(z==="perpetual"){if(F4(Q.budgetExceeded))return{action:"pause",reason:"Budget limit reached - execution paused"};return e8(Q.pause),e8(Q.pausedMd),{action:"continue",reason:"PAUSE file auto-cleared in perpetual mode"}}return{action:"pause",reason:"Execution paused via PAUSE file"}}if(F4(Q.pauseAtCheckpoint)){if(z==="checkpoint"){e8(Q.pauseAtCheckpoint);try{K7(Q.pause,"")}catch{}return{action:"pause",reason:"Execution paused at checkpoint"}}e8(Q.pauseAtCheckpoint)}if(F4(Q.humanInput)){let K=D7(Q.humanInput,()=>{if(!F4(Q.humanInput))return null;let J=null;try{J=Ct(Q.humanInput)}catch{J=null}if(J&&J.isSymbolicLink())return e8(Q.humanInput),{action:"continue",reason:"HUMAN_INPUT.md is a symlink - rejected for security"};if(!$.promptInjectionEnabled)return eF(Q.humanInput,Q.logsDir,"human-input-REJECTED",Z),{action:"continue",reason:"HUMAN_INPUT.md detected but prompt injection is DISABLED"};let q=0;try{q=Tt(Q.humanInput).size}catch{q=-1}if(q>Dt)return eF(Q.humanInput,Q.logsDir,"human-input-REJECTED-TOOLARGE",Z),{action:"continue",reason:"HUMAN_INPUT.md exceeds 1MB size limit, rejecting"};if(q>=0){let V="";try{V=XR(Q.humanInput,"utf8")}catch{return e8(Q.humanInput),{action:"continue",reason:"HUMAN_INPUT.md unreadable"}}if(V.length>0)return eF(Q.humanInput,Q.logsDir,"human-input",Z),{action:"input",payload:V,reason:"Human input detected"}}return null});if(K!==null)return K}if(F4(Q.councilReview))return e8(Q.councilReview),{action:"continue",reason:"Council force-review requested from dashboard"};if(F4(Q.stop))return e8(Q.stop),{action:"stop",reason:"STOP file detected - stopping execution"};return{action:"continue"}}async function b31($={}){let X=QR($.lokiDirOverride),Q=zR(X),z=$.pollIntervalMs??1000,Z=$.maxWaitMs,K=Date.now(),J=$.pausedMdBody??y31;try{$R(X,{recursive:!0}),K7(Q.pausedMd,J)}catch{}try{let q=D4(X,"state");$R(q,{recursive:!0});let V=D4(q,"interventions.json"),Y=0;if(F4(V))try{let U=JSON.parse(XR(V,"utf8"));if(typeof U?.count==="number"&&Number.isInteger(U.count)&&U.count>=0)Y=U.count}catch{}K7(V,JSON.stringify({count:Y+1,basis:"blocking pauses that waited on a human"})+`
1224
+ - [Info] no reviewer CLI available; review skipped`,ir=64,L31;var At=s(()=>{A$();qY();w4();k1();y8();Zt={"security-sentinel":{keywords:["auth","login","password","token","api","sql","query","cookie","cors","csrf"],focus:"OWASP Top 10, injection, auth, secrets, input validation",checks:"injection (SQL, XSS, command, template), auth bypass, secrets in code, missing input validation, OWASP Top 10, insecure defaults",priority:0},"test-coverage-auditor":{keywords:["test","spec","coverage","assert","mock","fixture","expect","describe"],focus:"Missing tests, edge cases, error paths, boundary conditions",checks:"missing test cases, uncovered error paths, boundary conditions, mock correctness, test isolation, flaky test patterns",priority:1},"performance-oracle":{keywords:["database","query","cache","render","loop","fetch","load","index","join","pool"],focus:"N+1 queries, memory leaks, caching, bundle size, lazy loading",checks:"N+1 queries, unbounded loops, memory leaks, missing caching, excessive re-renders, large bundle imports, missing pagination",priority:2},"dependency-analyst":{keywords:["package","import","require","dependency","npm","pip","yarn","lock"],focus:"Outdated packages, CVEs, bloat, unused deps, license issues",checks:"outdated dependencies, known CVEs, unnecessary imports, dependency bloat, license compatibility, unused packages",priority:3},"legacy-healing-auditor":{keywords:["legacy","heal","migrate","cobol","fortran","refactor","modernize","deprecat","adapter","friction","characterization"],focus:"Behavioral preservation, friction safety, institutional knowledge retention",checks:"behavioral change without characterization test, removal of quirky code without friction map check, missing adapter layer for replaced components, institutional knowledge loss (deleted comments, removed error messages), breaking changes to undocumented APIs",priority:4}},Z31={name:"architecture-strategist",focus:"SOLID, coupling, cohesion, patterns, abstraction, dependency direction",checks:"SOLID violations, excessive coupling, wrong patterns, missing abstractions, dependency direction issues, god classes/functions"},K31={name:"maintainer-mergeability",focus:"Would a maintainer merge this PR as-is: scope discipline, dead/duplicated code, convention conformance",checks:"scope creep (changes unrelated to the stated task, drive-by edits, unrequested refactors), dead code (unreachable, unused, commented-out, leftover debug), duplicated logic that should reuse an existing helper, non-conformance to the surrounding code's conventions (naming, error handling, structure, formatting), and anything a careful human reviewer would ask to be changed before merging"};q31={simple:2,standard:2,complex:4};HG=Z1(L1,"loki-ts","data","code-review-schema.json");L31=/\[([^\]]+)\]\(([^)\s]+)\)/g});var ZR={};B1(ZR,{readHumanInput:()=>k31,handlePause:()=>b31,checkHumanIntervention:()=>S31});import{existsSync as F4,lstatSync as Ct,mkdirSync as $R,readFileSync as XR,renameSync as P31,statSync as Tt,unlinkSync as E31}from"fs";import{join as D4}from"path";function QR($){return $??h0()}function zR($){return{pause:D4($,"PAUSE"),pauseAtCheckpoint:D4($,"PAUSE_AT_CHECKPOINT"),humanInput:D4($,"HUMAN_INPUT.md"),councilReview:D4($,"signals","COUNCIL_REVIEW_REQUESTED"),stop:D4($,"STOP"),pausedMd:D4($,"PAUSED.md"),budgetExceeded:D4($,"signals","BUDGET_EXCEEDED"),logsDir:D4($,"logs")}}function e8($){try{E31($)}catch{}}function x31($){let X=(Q,z=2)=>String(Q).padStart(z,"0");return`${$.getUTCFullYear()}${X($.getUTCMonth()+1)}${X($.getUTCDate())}-${X($.getUTCHours())}${X($.getUTCMinutes())}${X($.getUTCSeconds())}`}function eF($,X,Q,z){try{$R(X,{recursive:!0})}catch{}let Z=D4(X,`${Q}-${x31(z)}.md`);try{return P31($,Z),Z}catch{return e8($),""}}function k31($={}){let X=QR($.lokiDirOverride),Q=zR(X).humanInput;if(!F4(Q))return null;let z;try{z=Ct(Q)}catch{return null}if(z.isSymbolicLink())return null;let Z;try{Z=Tt(Q)}catch{return null}if(Z.size>Dt)return null;try{return XR(Q,"utf8")}catch{return null}}function S31($={}){let X=QR($.lokiDirOverride),Q=zR(X),z=$.autonomyMode??"standard",Z=$.now??new Date;if(F4(Q.pause)){if(z==="perpetual"){if(F4(Q.budgetExceeded))return{action:"pause",reason:"Budget limit reached - execution paused"};return e8(Q.pause),e8(Q.pausedMd),{action:"continue",reason:"PAUSE file auto-cleared in perpetual mode"}}return{action:"pause",reason:"Execution paused via PAUSE file"}}if(F4(Q.pauseAtCheckpoint)){if(z==="checkpoint"){e8(Q.pauseAtCheckpoint);try{K7(Q.pause,"")}catch{}return{action:"pause",reason:"Execution paused at checkpoint"}}e8(Q.pauseAtCheckpoint)}if(F4(Q.humanInput)){let K=D7(Q.humanInput,()=>{if(!F4(Q.humanInput))return null;let J=null;try{J=Ct(Q.humanInput)}catch{J=null}if(J&&J.isSymbolicLink())return e8(Q.humanInput),{action:"continue",reason:"HUMAN_INPUT.md is a symlink - rejected for security"};if(!$.promptInjectionEnabled)return eF(Q.humanInput,Q.logsDir,"human-input-REJECTED",Z),{action:"continue",reason:"HUMAN_INPUT.md detected but prompt injection is DISABLED"};let q=0;try{q=Tt(Q.humanInput).size}catch{q=-1}if(q>Dt)return eF(Q.humanInput,Q.logsDir,"human-input-REJECTED-TOOLARGE",Z),{action:"continue",reason:"HUMAN_INPUT.md exceeds 1MB size limit, rejecting"};if(q>=0){let V="";try{V=XR(Q.humanInput,"utf8")}catch{return e8(Q.humanInput),{action:"continue",reason:"HUMAN_INPUT.md unreadable"}}if(V.length>0)return eF(Q.humanInput,Q.logsDir,"human-input",Z),{action:"input",payload:V,reason:"Human input detected"}}return null});if(K!==null)return K}if(F4(Q.councilReview))return e8(Q.councilReview),{action:"continue",reason:"Council force-review requested from dashboard"};if(F4(Q.stop))return e8(Q.stop),{action:"stop",reason:"STOP file detected - stopping execution"};return{action:"continue"}}async function b31($={}){let X=QR($.lokiDirOverride),Q=zR(X),z=$.pollIntervalMs??1000,Z=$.maxWaitMs,K=Date.now(),J=$.pausedMdBody??y31;try{$R(X,{recursive:!0}),K7(Q.pausedMd,J)}catch(q){console.error(`handlePause: could not write ${Q.pausedMd}: ${q instanceof Error?q.message:String(q)}`)}try{let q=D4(X,"state");$R(q,{recursive:!0});let V=D4(q,"interventions.json"),Y=0;if(F4(V))try{let U=JSON.parse(XR(V,"utf8"));if(typeof U?.count==="number"&&Number.isInteger(U.count)&&U.count>=0)Y=U.count}catch{}K7(V,JSON.stringify({count:Y+1,basis:"blocking pauses that waited on a human"})+`
1225
1225
  `)}catch{}try{for(;;){if(F4(Q.stop))return e8(Q.stop),e8(Q.pausedMd),{outcome:"stop",timedOut:!1};if(!F4(Q.pause))return e8(Q.pausedMd),{outcome:"resumed",timedOut:!1};if(Z!==void 0&&Date.now()-K>=Z)return e8(Q.pausedMd),{outcome:"resumed",timedOut:!0};await new Promise((q)=>setTimeout(q,z))}}finally{e8(Q.pausedMd)}}var Dt=1048576,y31='# Loki Mode - Paused\n\nExecution is currently paused. Options:\n\n1. **Resume**: Press Enter in terminal or `rm .loki/PAUSE`\n2. **Add Instructions**: `echo "Focus on fixing the login bug" > .loki/HUMAN_INPUT.md`\n3. **Stop**: `touch .loki/STOP`\n\nCurrent state is saved. You can inspect:\n- `.loki/CONTINUITY.md` - Progress and context\n- `.loki/STATUS.txt` - Current status\n- `.loki/logs/` - Session logs\n';var KR=s(()=>{k1();I6();w4()});var wt={};B1(wt,{tryImport:()=>n7,taskClassForIteration:()=>It,runAutonomous:()=>m31,envBlockFlagOn:()=>BG,ent3ExitCode:()=>VQ,completionRefusalReason:()=>Ft,completionEvidenceRefusal:()=>Rt});import{existsSync as y2,mkdirSync as f31,writeFileSync as JR,statSync as _31,readFileSync as NG,unlinkSync as qR}from"fs";import{resolve as R4}from"path";function BG($){if($===void 0||$==="")return!1;return $==="true"||$==="1"}function Ft($,X,Q){if($)return"quality-gate battery crashed -- refusing completion this iteration (fail-closed)";if(X.blocked&&(X.failed.includes("code_review")||(X.cleared??[]).includes("code_review")))return"code_review BLOCK -- refusing completion this iteration";if(X.blocked&&X.failed.includes("semantic_tests")&&BG(Q.LOKI_GATE_SEMANTIC_TESTS_BLOCK))return"semantic_tests BLOCK (LOKI_GATE_SEMANTIC_TESTS_BLOCK) -- refusing completion this iteration";if(X.blocked&&X.failed.includes("invariants")&&BG(Q.LOKI_GATE_INVARIANTS_BLOCK))return"invariants BLOCK (LOKI_GATE_INVARIANTS_BLOCK) -- refusing completion this iteration";if(X.blocked&&X.failed.includes("test_coverage")&&BG(Q.LOKI_GATE_TEST_COVERAGE_BLOCK))return"test_coverage BLOCK (LOKI_GATE_TEST_COVERAGE_BLOCK) -- failing test suite refuses completion this iteration";return null}function Rt($,X=(z)=>{try{return NG(z,"utf8")}catch{return null}},Q=y2){let z=R4($,"queue","failed.json");if(!Q(z))return null;let Z=X(z);if(Z===null)return"completion refused: failure ledger present but unreadable (queue/failed.json)";let K=Z.trim();if(K===""||K==="[]"||K==="{}")return null;let J;try{J=JSON.parse(K)}catch{return"completion refused: failure ledger corrupt (queue/failed.json unparseable)"}if(Array.isArray(J)&&J.length>0)return`completion refused: ${J.length} unresolved task(s) in the failure ledger (queue/failed.json)`;if(J&&typeof J==="object"&&Object.keys(J).length>0)return"completion refused: unresolved entries in the failure ledger (queue/failed.json)";return null}function v31($,X){for(let Q of X)if(typeof $[Q]!=="function")return!1;return!0}async function h31($){switch($){case"./state.ts":return await Promise.resolve().then(() => (I6(),nM));case"./build_prompt.ts":return await Promise.resolve().then(() => (dS(),uS));case"./council.ts":return await Promise.resolve().then(() => (qy(),Jy));case"./providers.ts":return await Promise.resolve().then(() => (gF(),hF));case"./queues.ts":return await Promise.resolve().then(() => (ur(),mr));case"./budget.ts":return await Promise.resolve().then(() => (cG(),YI));case"./completion.ts":return await Promise.resolve().then(() => (lr(),cr));case"./quality_gates.ts":return await Promise.resolve().then(() => (At(),Lt));case"./intervention.ts":return await Promise.resolve().then(() => (KR(),ZR));case"./rarv.ts":return await Promise.resolve().then(() => (jZ(),NO));case"./checkpoint.ts":return await Promise.resolve().then(() => (wq(),oI));default:return}}async function n7($,X=[]){let Q;try{Q=await h31($)??await import($)}catch{return null}for(let z of X)if(typeof Q[z]!=="function"){let Z=Q[z]===void 0?"missing":`${typeof Q[z]}`;throw Error(`tryImport(${$}): required export '${z}' is ${Z} (expected function)`)}if(!v31(Q,X))throw Error(`tryImport(${$}): runtime contract validation failed`);return Q}function N9($,X){if($===null)throw Error(`[runner] FATAL: required module ${X} is not loadable; refusing to run with a degraded stub (see autonomous.ts module-resolution contract)`);return $}function It($,X){if(X>0)return"recovery";switch($){case"REASON":return"planning";case"ACT":return"implementation";case"REFLECT":return"review";case"VERIFY":return"verification";default:return"implementation"}}async function m31($){let X=$.prdPath,Q=lk({prdPath:$.prdPath,cwd:$.cwd,log:$.loggerStream?(Z)=>$.loggerStream.write(Z+`
1226
1226
  `):(Z)=>{console.log(Z)}});$.prdPath=Q.prdPath;let z=c31($);z.statePrdPath=X??"";try{return await u31($,z)}finally{await Dk(z).catch(()=>{})}}async function u31($,X){let Q=X.log,z=$.clock??Ck,Z=$.signals??g31;Q("[runner] Starting autonomous execution"),Q(`[runner] PRD: ${X.prdPath??"Codebase Analysis Mode"}`),Q(`[runner] provider=${X.provider} mode=${X.autonomyMode} model=${X.sessionModel}`),Q(`[runner] max_retries=${X.maxRetries} max_iterations=${X.maxIterations}`),l31(X),gk(),a31(X);let K=$.stateOverride?$.stateOverride:await n7("./state.ts",["loadStateForRunner","saveStateForRunner"]),J=await n7("./build_prompt.ts",["buildPromptForRunner"]),q=await n7("./council.ts",["councilInit"]),V=await n7("./providers.ts",["resolveProvider"]),Y=await n7("./queues.ts",["populateBmadQueue","populateOpenspecQueue","populateMirofishQueue","populatePrdQueue"]),U=await n7("./budget.ts",["checkBudgetLimitForRunner"]),H=await n7("./completion.ts",["checkCompletionPromise"]),W=await n7("./quality_gates.ts",["runQualityGates"]);await N9(K,"./state.ts").loadStateForRunner(X),await N9(q,"./council.ts").councilInit(X.prdPath);let G=N9(Y,"./queues.ts");await G.populateBmadQueue(X),await G.populateOpenspecQueue(X),await G.populateMirofishQueue(X),await G.populatePrdQueue(X);let N=$.council??N9(q,"./council.ts").defaultCouncil,M=$.providerOverride?$.providerOverride:await N9(V,"./providers.ts").resolveProvider(X.provider),A=N9(J,"./build_prompt.ts"),j=$.gatesOverride??N9(W,"./quality_gates.ts");if(X.iterationCount>=X.maxIterations)return Q(`[runner] max iterations already reached (${X.iterationCount}/${X.maxIterations})`),1;if(process.env.LOKI_REPO_PROFILE==="1")try{let L=await n7("./repo_profile.ts",["buildProfile"]);if(L){let C=process.env.LOKI_DIR,F=L.buildProfile({repoRoot:X.cwd,lokiDirOverride:C!==void 0&&C!==""?C:void 0});Q(`[runner] repo profile derived: ${F.facts.length} evidence-backed facts`)}}catch(L){Q(`[runner] repo profile build failed (non-fatal): ${L.message}`)}let B;while(X.retryCount<X.maxRetries){let L=await Z.checkHumanIntervention(X);if(L===1){Q("[runner] PAUSE signal -- waiting and re-checking"),await A7(K,X,"paused",0),await z.sleep(50);continue}if(L===2)return Q("[runner] STOP signal -- exiting cleanly"),await A7(K,X,"stopped",0),0;if(U?await U.checkBudgetLimitForRunner(X):await Z.isBudgetExceeded(X)){Q("[runner] budget limit exceeded -- pausing"),await A7(K,X,"budget_exceeded",0),await z.sleep(60000);continue}if($.policyCheck)try{if(!await $.policyCheck(X)){Q("[runner] policy engine denied iteration -- continuing without invoke"),await A7(K,X,"policy_blocked",0),await z.sleep(5000);continue}}catch(t){Q(`[runner] policy check threw: ${t.message}`)}if(X.iterationCount+=1,X.iterationCount>=X.maxIterations)return Q(`[runner] max iterations reached (${X.iterationCount}/${X.maxIterations})`),await A7(K,X,"max_iterations_reached",0),VQ("max_iterations_reached",0);let F;try{F=await A.buildPromptForRunner(X)}catch(t){Q(`[runner] buildPrompt threw: ${t.message} -- using stub`),F=`[stub-prompt-fallback iteration=${X.iterationCount} retry=${X.retryCount}]`}try{let t=await Promise.resolve().then(() => (jZ(),NO)),V0=t.getRarvPhaseName(X.iterationCount),B0=t.getRarvTier(X.iterationCount,{sessionModel:typeof X.sessionModel==="string"?X.sessionModel:void 0});if(Q(`[runner] RARV Phase: ${V0} -> Tier: ${B0}`),X.currentTier=B0,B0!=="fable"){let o=zS(It(V0,X.retryCount),String(X.currentTier),{iteration:X.iterationCount,env:{...process.env,LOKI_SESSION_MODEL:String(X.sessionModel)}});if(o.reason==="task_class"||o.reason==="session_ceiling"||o.reason==="explicit_override"){if(o.tier!==X.currentTier)Q(`[runner] capability router: ${X.currentTier} -> ${o.tier} (${o.reason})`);X.currentTier=o.tier}}if(B!==void 0)X.currentTier=B,B=void 0;try{(await Promise.resolve().then(() => (I6(),nM))).updateCurrentPhase(V0,{lokiDirOverride:X.lokiDir,iteration:X.iterationCount})}catch(o){Q(`[runner] updateCurrentPhase failed (non-fatal): ${o.message}`)}}catch(t){Q(`[runner] rarv module load failed (non-fatal): ${t.message}`)}Q(`[runner] Attempt ${X.retryCount+1}/${X.maxRetries} iteration=${X.iterationCount}`),await A7(K,X,"running",0);let I=z.now(),T=o31(X),D,R=!1;try{D=await M.invoke({provider:X.provider,prompt:F,tier:X.currentTier,cwd:X.cwd,iterationOutputPath:T,mainLoop:!0})}catch(t){let V0=t instanceof Error?t.message:String(t);R=tk(V0),Q(`[runner] provider invocation threw: ${V0}`),D={exitCode:1,capturedOutputPath:T}}let x=Math.max(0,Math.floor((z.now()-I)/1000)),k={exitCode:D.exitCode,durationSeconds:x,capturedOutputPath:D.capturedOutputPath};if(await A7(K,X,"exited",k.exitCode),k.exitCode===0)try{let t=await n7("./checkpoint.ts",["createCheckpoint"]);if(t)await t.createCheckpoint({iteration:X.iterationCount,taskId:X.prdPath??"codebase-analysis",taskDescription:`iteration ${X.iterationCount} success`,forceCreate:!0,lokiDirOverride:X.lokiDir})}catch(t){Q(`[runner] createCheckpoint failed (non-fatal): ${t.message}`)}let b={passed:[],failed:[],blocked:!1,escalated:!1},f=!1;try{b=await j.runQualityGates(X)}catch(t){f=!0,Q(`[runner] runQualityGates threw -- cannot verify completion this iteration; refusing completion (fail-closed) and continuing to iterate: ${t.message}`)}if(N.trackIteration)try{await N.trackIteration(k.capturedOutputPath??T)}catch(t){Q(`[runner] council.trackIteration failed: ${t.message}`)}if(k.exitCode===0){if(X.autonomyMode==="perpetual"){X.retryCount=0;continue}let t=Ft(f,b,process.env);if(t!==null){Q(`[runner] ${t}; continuing to next iteration`),X.retryCount=0;continue}let V0=Rt(X.lokiDir);if(V0!==null){Q(`[runner] ${V0}; continuing to next iteration`),X.retryCount=0;continue}try{if(await N.shouldStop(X))return Q("[runner] COMPLETION COUNCIL: project complete"),await A7(K,X,"council_approved",0),0}catch(o){Q(`[runner] council.shouldStop failed: ${o.message}`)}if(H?await H.checkCompletionPromise(X,k.capturedOutputPath??T).catch(()=>!1):await t31(X,k.capturedOutputPath??T))return Q("[runner] completion promise fulfilled"),await A7(K,X,"completion_promise_fulfilled",0),0;X.retryCount=0;continue}let m=r31(X);try{let t=D.capturedOutputPath,V0=t&&y2(t)?d31(t):"";if(V0||R||GO(X.lokiDir)){let B0=b.failed.includes("test_coverage")?1:void 0,o=nk({output:V0,attempts:X.retryCount,buildExitCode:B0,treeCorrupt:GO(X.lokiDir),providerUnavailable:R},{env:process.env});if(o.action==="stop"||o.action==="escalate")return Q(`[runner] recovery decision '${o.action}' (${o.reason}); stopping early to save budget instead of ${X.maxRetries-X.retryCount-1} further retries. Set LOKI_SMART_RETRY=0 to retry regardless.`),await A7(K,X,"failed",1),VQ("failed",1);if(o.action==="revise")Q(`[runner] recovery decision 'revise' (${o.reason}); re-attempting without backoff -- the build signal is actionable, not transient.`),m=0;if(o.action==="failover"){if(!o.requestTier)return Q("[runner] recovery failover omitted a tier; refusing unsafe retry"),await A7(K,X,"failed",1),VQ("failed",1);B=o.requestTier,m=0,Q(`[runner] recovery decision 'failover' (${o.reason}); requesting ${o.requestTier} tier without backoff`)}if(o.action==="checkpoint_rollback")try{let y0=await ek(X.lokiDir);m=0,Q(`[runner] recovery decision 'checkpoint_rollback' (${o.reason}); restored ${y0.restored} files from ${y0.checkpointId}`)}catch(y0){return Q(`[runner] checkpoint rollback failed closed: ${y0.message}; stopping`),await A7(K,X,"failed",1),VQ("failed",1)}}if(V0){let B0=await n7("./budget.ts",["isRateLimited","calculateRateLimitBackoff"]);if(B0&&B0.isRateLimited(V0)){let o=B0.calculateRateLimitBackoff();m=Math.max(m,o),Q(`[runner] rate-limit detected; backoff bumped to ${m}s`)}}}catch(t){Q(`[runner] rate-limit probe failed (non-fatal): ${t.message}`)}Q(`[runner] iteration failed (exit=${k.exitCode}); retry in ${m}s`),await z.sleep(m*1000),X.retryCount+=1}return Q(`[runner] max retries (${X.maxRetries}) exceeded`),await A7(K,X,"max_retries_exceeded",1),VQ("max_retries_exceeded",1)}function d31($){try{let X=NG($),Q=65536;return X.byteLength<=65536?X.toString("utf8"):X.subarray(X.byteLength-65536).toString("utf8")}catch{return""}}function p31($,X){let Q=process.env[$];if(Q===void 0||Q==="")return X;let z=Number.parseInt(Q,10);return Number.isFinite(z)?z:X}function c31($){let X=$.cwd??process.cwd(),Q=process.env.LOKI_DIR??R4(X,".loki"),z=(Z)=>{if($.loggerStream)$.loggerStream.write(Z+`
1227
1227
  `);else console.log(Z)};return{cwd:X,lokiDir:Q,prdPath:$.prdPath,provider:$.provider??"claude",maxRetries:$.maxRetries??5,maxIterations:$.maxIterations??p31("MAX_ITERATIONS",1000),baseWaitSeconds:$.baseWaitSeconds??30,maxWaitSeconds:$.maxWaitSeconds??3600,autonomyMode:$.autonomyMode??"checkpoint",sessionModel:$.sessionModel??"sonnet",budgetLimit:$.budgetLimit,completionPromise:$.completionPromise,iterationCount:0,retryCount:0,currentTier:$.sessionModel??"development",log:z}}function l31($){for(let X of["","logs","state","quality","queue","checklist"]){let Q=X?R4($.lokiDir,X):$.lokiDir;try{if(!y2(Q))f31(Q,{recursive:!0})}catch{}}i31($)}function i31($){let X=["PAUSE","PAUSE_AT_CHECKPOINT","PAUSED.md","STOP","COMPLETED","HUMAN_INPUT.md"];for(let Q of X){let z=R4($.lokiDir,Q);if(!y2(z))continue;try{qR(z)}catch{}}}function a31($){let X=R4($.lokiDir,"loki.pid"),Q=R4($.lokiDir,"runner-route");if(y2(X)){let J=0;try{J=Number.parseInt(NG(X,"utf8").trim(),10)}catch{}if(J>0&&J!==process.pid){let q=!1;try{process.kill(J,0),q=!0}catch{}if(q){let V="unknown";try{V=NG(Q,"utf8").trim()||"unknown"}catch{}let Y=`Another loki session is already running (PID ${J}, route: ${V}).
@@ -1229,7 +1229,7 @@ Stop it first with 'loki stop' or wait for it to finish.
1229
1229
  If you believe the lock is stale, remove '${X}' manually.`;throw $.log(`[runner] ERROR: ${Y}`),process.stderr.write(`${Y}
1230
1230
  `),Error("session-singleton: another loki runner is active")}$.log(`[runner] reaping stale ${X} (PID ${J} not alive)`)}}try{JR(X,`${process.pid}
1231
1231
  `),JR(Q,`bun
1232
- `)}catch(J){$.log(`[runner] WARN: could not write ${X}: ${J.message}`)}let z=!1,Z=()=>{if(z)return;z=!0;try{qR(X)}catch{}try{qR(Q)}catch{}},K=(J)=>{Z(),process.kill(process.pid,J)};return process.once("exit",Z),process.once("SIGINT",()=>K("SIGINT")),process.once("SIGTERM",()=>K("SIGTERM")),Z}function o31($){let X=R4($.lokiDir,"logs"),Q=R4(X,`iter-output-${$.iterationCount}-${Date.now()}.log`);try{JR(Q,"")}catch{}return Q}function VQ($,X){if(process.env.LOKI_DURABLE_STATE!=="1")return X;if(s31.has($))return 0;if(n31.has($))return 20;return X===0?1:X}async function A7($,X,Q,z){if($){try{await $.saveStateForRunner(X,Q,z)}catch(Z){X.log(`[runner] saveState failed: ${Z.message}`)}return}throw X.log("[runner] FATAL: src/runner/state.ts not loadable; refusing to write autonomy-state.json with stub schema"),Error("state.ts module is required but not loadable")}function r31($){let X=$.baseWaitSeconds*Math.pow(2,$.retryCount);return Math.min($.maxWaitSeconds,Math.max(0,X))}async function t31($,X){let Q=R4($.lokiDir,"signals","TASK_COMPLETION_CLAIMED");if(y2(Q))return!0;if(!$.completionPromise)return!1;if(!y2(X))return!1;try{if(_31(X).size===0)return!1;return(await $1(["grep","-Fq",$.completionPromise,X])).exitCode===0}catch{return!1}}var g31,s31,n31;var Pt=s(()=>{Tk();y8();Fk();A$();ik();rk();$S();ZS();g31={async checkHumanIntervention($){try{switch((await Promise.resolve().then(() => (KR(),ZR))).checkHumanIntervention({lokiDirOverride:$.lokiDir,autonomyMode:$.autonomyMode==="perpetual"?"perpetual":"standard"}).action){case"stop":return 2;case"pause":case"input":return 1;default:return 0}}catch{let X=R4($.lokiDir,"STOP"),Q=R4($.lokiDir,"PAUSE");if(y2(X))return 2;if(y2(Q))return 1;return 0}},async isBudgetExceeded(){return!1}};s31=new Set(["council_approved","council_force_approved","deterministic_gates_passed","completion_promise_fulfilled","paused","interrupted","stopped"]),n31=new Set(["failed","force_stopped","max_iterations_reached","max_retries_exceeded","budget_exceeded","max_duration_reached","policy_blocked","inconclusive_spec_contradiction"])});var bt={};B1(bt,{runStart:()=>z61,parseStartArgs:()=>yt});function S8($,X){let Q=$.indexOf(X);return Q>=0&&Q+1<$.length?$[Q+1]:void 0}function Q61(){let $=new Set(xt);for(let X of kt.keys())$.add(X);for(let X of St)$.add(X);return $.add("--help"),$.add("-h"),$}function Xq($){if($===void 0)return;let X=Number($);return Number.isFinite(X)&&X>0?X:void 0}function yt($,X=(Z)=>process.stderr.write(Z),Q=(Z)=>process.stdout.write(Z),z=(Z,K)=>{process.env[Z]=K}){if($.includes("--help")||$.includes("-h"))return Q(Et),0;let Z=Q61(),K,J=!1;for(let A=0;A<$.length;A++){let j=$[A];if(!j)continue;if(J){if(K===void 0)K=j;continue}if(j==="--"){J=!0;continue}if(j.startsWith("-")&&j!=="-"){let B=j.includes("=")?j.slice(0,j.indexOf("=")):j;if(!Z.has(B))return X(`start: flag ${B} is not supported by the Bun (LOKI_SDK_LOOP) runner.
1232
+ `)}catch(J){$.log(`[runner] WARN: could not write ${X}: ${J.message}`)}let z=!1,Z=()=>{if(z)return;z=!0;try{qR(X)}catch{}try{qR(Q)}catch{}},K=(J)=>{Z(),process.kill(process.pid,J)};return process.once("exit",Z),process.once("SIGINT",()=>K("SIGINT")),process.once("SIGTERM",()=>K("SIGTERM")),Z}function o31($){let X=R4($.lokiDir,"logs"),Q=R4(X,`iter-output-${$.iterationCount}-${Date.now()}.log`);try{JR(Q,"")}catch{}return Q}function VQ($,X){if(process.env.LOKI_DURABLE_STATE!=="1")return X;if(s31.has($))return 0;if(n31.has($))return 20;return X===0?1:X}async function A7($,X,Q,z){if($){try{await $.saveStateForRunner(X,Q,z)}catch(Z){X.log(`[runner] saveState failed: ${Z.message}`)}return}throw X.log("[runner] FATAL: src/runner/state.ts not loadable; refusing to write autonomy-state.json with stub schema"),Error("state.ts module is required but not loadable")}function r31($){let X=$.baseWaitSeconds*Math.pow(2,$.retryCount);return Math.min($.maxWaitSeconds,Math.max(0,X))}async function t31($,X){let Q=R4($.lokiDir,"signals","TASK_COMPLETION_CLAIMED");if(y2(Q))return!0;if(!$.completionPromise)return!1;if(!y2(X))return!1;try{if(_31(X).size===0)return!1;return(await $1(["grep","-Fq",$.completionPromise,X])).exitCode===0}catch{return!1}}var g31,s31,n31;var Pt=s(()=>{Tk();y8();Fk();A$();ik();rk();$S();ZS();g31={async checkHumanIntervention($){try{switch((await Promise.resolve().then(() => (KR(),ZR))).checkHumanIntervention({lokiDirOverride:$.lokiDir,autonomyMode:$.autonomyMode==="perpetual"?"perpetual":"standard"}).action){case"stop":return 2;case"pause":case"input":return 1;default:return 0}}catch{let X=R4($.lokiDir,"STOP"),Q=R4($.lokiDir,"PAUSE");if(y2(X))return 2;if(y2(Q))return 1;return 0}},async isBudgetExceeded(){return!1}};s31=new Set(["council_approved","council_force_approved","deterministic_gates_passed","completion_promise_fulfilled","paused","interrupted","stopped"]),n31=new Set(["failed","force_stopped","max_iterations_reached","max_retries_exceeded","budget_exceeded","max_duration_reached","policy_blocked","inconclusive_spec_contradiction","gate_stuck_static_analysis","gate_stuck_mock_integrity","gate_stuck_mutation_integrity"])});var bt={};B1(bt,{runStart:()=>z61,parseStartArgs:()=>yt});function S8($,X){let Q=$.indexOf(X);return Q>=0&&Q+1<$.length?$[Q+1]:void 0}function Q61(){let $=new Set(xt);for(let X of kt.keys())$.add(X);for(let X of St)$.add(X);return $.add("--help"),$.add("-h"),$}function Xq($){if($===void 0)return;let X=Number($);return Number.isFinite(X)&&X>0?X:void 0}function yt($,X=(Z)=>process.stderr.write(Z),Q=(Z)=>process.stdout.write(Z),z=(Z,K)=>{process.env[Z]=K}){if($.includes("--help")||$.includes("-h"))return Q(Et),0;let Z=Q61(),K,J=!1;for(let A=0;A<$.length;A++){let j=$[A];if(!j)continue;if(J){if(K===void 0)K=j;continue}if(j==="--"){J=!0;continue}if(j.startsWith("-")&&j!=="-"){let B=j.includes("=")?j.slice(0,j.indexOf("=")):j;if(!Z.has(B))return X(`start: flag ${B} is not supported by the Bun (LOKI_SDK_LOOP) runner.
1233
1233
  `),X(`Orchestration flags (--parallel, --github, --issue, --sandbox, --api, --bg, mirofish) run on the bash route automatically; if you reached this, run without LOKI_SDK_LOOP.
1234
1234
  `),2;let L=kt.get(B);if(L){z(L[0],L[1]);continue}if(St.has(B))continue;if(xt.has(B)&&!j.includes("="))A++;continue}if(K===void 0)K=j}let q=S8($,"--prd"),V=S8($,"--brief"),Y=q??V??K;if(!Y)return X(`start: a spec source (PRD path, --prd FILE, --brief TEXT, or issue ref) is required
1235
1235
  `),X(Et),2;let U=S8($,"--aider-model");if(U)z("LOKI_AIDER_MODEL",U);let H=S8($,"--aider-flags");if(H)z("LOKI_AIDER_FLAGS",H);let W=S8($,"--cline-model");if(W)z("LOKI_CLINE_MODEL",W);let G=S8($,"--provider");if(G&&!e31.has(G))return X(`start: unknown --provider '${G}'
@@ -1337,4 +1337,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
1337
1337
  `),2}case"start":{let{runStart:z}=await Promise.resolve().then(() => (ft(),bt));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
1338
1338
  `),process.stderr.write(_t),2}}PR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var J61=await K61(Bun.argv.slice(2));process.exit(J61);
1339
1339
 
1340
- //# debugId=6847A0F1DFFA16BD15E556FA99A4D261
1340
+ //# debugId=2D5176F5A8DEFD466C1C3885A0AC6CF2
package/mcp/__init__.py CHANGED
@@ -75,4 +75,4 @@ try:
75
75
  except ImportError:
76
76
  __all__ = ['mcp']
77
77
 
78
- __version__ = '9.50.3'
78
+ __version__ = '9.50.4'
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": "9.50.3",
4
+ "version": "9.50.4",
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, opencode).",
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": "9.50.3",
5
+ "version": "9.50.4",
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",