loki-mode 9.37.0 → 9.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/efficiency_cost.py +32 -0
- package/autonomy/lib/proof-generator.py +32 -2
- package/autonomy/run.sh +11 -0
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
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.
|
|
6
|
+
# Loki Mode v9.38.0
|
|
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.
|
|
473
|
+
**v9.38.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.
|
|
1
|
+
9.38.0
|
|
@@ -127,6 +127,7 @@ def collect_efficiency(loki_dir):
|
|
|
127
127
|
}
|
|
128
128
|
model = ""
|
|
129
129
|
collected = False
|
|
130
|
+
_records = []
|
|
130
131
|
eff_dir = os.path.join(loki_dir, "metrics", "efficiency")
|
|
131
132
|
try:
|
|
132
133
|
names = sorted(os.listdir(eff_dir))
|
|
@@ -139,6 +140,7 @@ def collect_efficiency(loki_dir):
|
|
|
139
140
|
if not isinstance(rec, dict):
|
|
140
141
|
continue
|
|
141
142
|
collected = True
|
|
143
|
+
_records.append(rec)
|
|
142
144
|
cost["usd"] += _to_float(rec.get("cost_usd"))
|
|
143
145
|
cost["input_tokens"] += _to_int(rec.get("input_tokens"))
|
|
144
146
|
cost["output_tokens"] += _to_int(rec.get("output_tokens"))
|
|
@@ -171,11 +173,41 @@ def collect_efficiency(loki_dir):
|
|
|
171
173
|
"cache_read_tokens": cost["cache_read_tokens"],
|
|
172
174
|
"cache_creation_tokens": cost["cache_creation_tokens"],
|
|
173
175
|
})
|
|
176
|
+
# COST AND TOKENS ARE MEASURED SEPARATELY, so they must be reported
|
|
177
|
+
# separately. `_observed` is true when EITHER a cost or a token count was
|
|
178
|
+
# seen, which is right for "did we measure anything" -- but it let a run with
|
|
179
|
+
# real token counts and NO priced record report usd=0.0 available=True. The
|
|
180
|
+
# receipt then asserts the run cost nothing while its own token counts prove
|
|
181
|
+
# work happened, which is a fabricated fact of exactly the kind this module
|
|
182
|
+
# exists to prevent. Measured, before this fix:
|
|
183
|
+
# [{"input_tokens":100,"output_tokens":200}] -> usd=0.0 available=True
|
|
184
|
+
# A partially-priced run has the same shape: an iteration missing cost_usd
|
|
185
|
+
# contributes 0.0 to the sum and silently understates total spend.
|
|
186
|
+
#
|
|
187
|
+
# So usd is now null unless at least one record actually carried a cost, and
|
|
188
|
+
# cost_partial marks the case where some did and some did not. Tokens are
|
|
189
|
+
# unaffected: they keep reporting whatever was observed.
|
|
190
|
+
# PRESENCE of the key, not its value. An explicit {"cost_usd": 0} is a
|
|
191
|
+
# GENUINE measured zero and must stay 0.0 -- a free cache-hit iteration is a
|
|
192
|
+
# real observation, and nulling it would be its own dishonesty (existing
|
|
193
|
+
# contract: test_genuine_zero_cost_stays_zero_not_null). What is unknown is a
|
|
194
|
+
# record that never carried the field at all.
|
|
195
|
+
_any_cost = any("cost_usd" in r for r in _records)
|
|
196
|
+
_missing_cost = sum(1 for r in _records if "cost_usd" not in r)
|
|
174
197
|
if collected and _observed:
|
|
175
198
|
# Round usd to a sane precision but keep it precise (anti-pattern:
|
|
176
199
|
# round suspiciously-clean numbers). 4 decimals preserves odd values.
|
|
177
200
|
cost["usd"] = round(cost["usd"], 4)
|
|
178
201
|
cost["available"] = True
|
|
202
|
+
if not _any_cost:
|
|
203
|
+
# Tokens were observed but nothing was priced: unknown, not zero.
|
|
204
|
+
cost["usd"] = None
|
|
205
|
+
cost["cost_available"] = False
|
|
206
|
+
else:
|
|
207
|
+
cost["cost_available"] = True
|
|
208
|
+
# Some records priced, others not: the total is a LOWER BOUND.
|
|
209
|
+
cost["cost_partial"] = _missing_cost > 0
|
|
210
|
+
cost["cost_unpriced_records"] = _missing_cost
|
|
179
211
|
else:
|
|
180
212
|
# No record means unavailable, never an observed zero.
|
|
181
213
|
for key in (
|
|
@@ -173,9 +173,21 @@ def _collect_council(loki_dir):
|
|
|
173
173
|
) + (" [round %s]" % round_tag if round_tag else ""),
|
|
174
174
|
})
|
|
175
175
|
continue
|
|
176
|
+
# Skip non-reviewer records. The nested path above already refuses a row
|
|
177
|
+
# with a blank role AND blank vote ("noise, not a reviewer"); this flat
|
|
178
|
+
# path did not, so any council/*.json that is not a verdict file was
|
|
179
|
+
# rendered as a phantom reviewer. Observed on a real run: the glob picked
|
|
180
|
+
# up `evidence-gate-details.json` and `state.json`, inflating a genuine
|
|
181
|
+
# 3-voter council to a larger roster of mostly-empty rows. The receipt's
|
|
182
|
+
# council block is the central trust signal, so a padded roster
|
|
183
|
+
# overstates how much independent review actually happened.
|
|
184
|
+
_flat_role = str(rec.get("role") or rec.get("reviewer") or "")
|
|
185
|
+
_flat_vote = str(rec.get("vote") or rec.get("decision") or "")
|
|
186
|
+
if not _flat_role and not _flat_vote:
|
|
187
|
+
continue
|
|
176
188
|
reviewers.append({
|
|
177
|
-
"role":
|
|
178
|
-
"vote":
|
|
189
|
+
"role": _flat_role,
|
|
190
|
+
"vote": _flat_vote,
|
|
179
191
|
# Full text here; truncation to <=300 happens AFTER redaction so a
|
|
180
192
|
# secret straddling the cap cannot be sliced into a sub-pattern
|
|
181
193
|
# fragment that escapes the redactor.
|
|
@@ -1025,7 +1037,25 @@ def _empty_tree_sha(repo_dir):
|
|
|
1025
1037
|
|
|
1026
1038
|
Diffing against this yields "everything that currently exists", which is the
|
|
1027
1039
|
truthful baseline for a run that started from a repo with no commits.
|
|
1040
|
+
|
|
1041
|
+
REQUIRES AN ACTUAL REPOSITORY. `git hash-object -t tree /dev/null` is a pure
|
|
1042
|
+
hash computation: it succeeds OUTSIDE a repo too, returning the same
|
|
1043
|
+
constant 4b825dc6... So without this guard a run in a non-git directory got
|
|
1044
|
+
a well-formed, plausible-looking base_sha implying a real baseline had been
|
|
1045
|
+
captured, when no repository existed at all -- and every other git fact in
|
|
1046
|
+
the receipt (head_sha, tree_sha256, diff) was simultaneously empty. A
|
|
1047
|
+
fabricated-looking identifier beside empty siblings is worse than an honest
|
|
1048
|
+
empty string, because a reader checks the SHA and finds it valid.
|
|
1028
1049
|
"""
|
|
1050
|
+
try:
|
|
1051
|
+
inside = subprocess.run(
|
|
1052
|
+
["git", "rev-parse", "--is-inside-work-tree"],
|
|
1053
|
+
cwd=repo_dir, capture_output=True, text=True, timeout=10,
|
|
1054
|
+
)
|
|
1055
|
+
if inside.returncode != 0:
|
|
1056
|
+
return ""
|
|
1057
|
+
except Exception:
|
|
1058
|
+
return ""
|
|
1029
1059
|
try:
|
|
1030
1060
|
out = subprocess.run(
|
|
1031
1061
|
["git", "hash-object", "-t", "tree", os.devnull],
|
package/autonomy/run.sh
CHANGED
|
@@ -831,6 +831,17 @@ print(catalog["providers"]["claude"]["cli_aliases"].get(os.environ["_LOKI_SELECT
|
|
|
831
831
|
|
|
832
832
|
export LOKI_PHASE_UNIT_TESTS LOKI_PHASE_E2E_TESTS
|
|
833
833
|
export LOKI_PHASE_CODE_REVIEW LOKI_PHASE_SECURITY LOKI_PHASE_ACCESSIBILITY
|
|
834
|
+
# EXPORT THE PHASES THIS PROFILE TURNS OFF, not only the ones it leaves on.
|
|
835
|
+
# The receipt derives quality_gates.disabled_phases by scanning the
|
|
836
|
+
# environment for LOKI_PHASE_* set to false (proof-generator.py:470-478), so
|
|
837
|
+
# a phase that is disabled but never exported is INVISIBLE to it: the receipt
|
|
838
|
+
# reported disabled_phases [] and all_phases_enabled true while six phases
|
|
839
|
+
# were switched off. That defeats the field's stated purpose -- "a receipt
|
|
840
|
+
# must be able to say what was NOT checked" -- and makes a narrowed run look
|
|
841
|
+
# identical to a full one. The sibling loki_apply_scoped_change_profile
|
|
842
|
+
# already exports the phases it changes; this one exported only its enables.
|
|
843
|
+
export LOKI_PHASE_API_TESTS LOKI_PHASE_INTEGRATION LOKI_PHASE_PERFORMANCE
|
|
844
|
+
export LOKI_PHASE_REGRESSION LOKI_PHASE_UAT LOKI_PHASE_WEB_RESEARCH
|
|
834
845
|
export LOKI_COUNCIL_ENABLED LOKI_EVIDENCE_GATE LOKI_PROOF_GATE LOKI_PROOF
|
|
835
846
|
export LOKI_DASHBOARD LOKI_PARALLEL_MODE LOKI_MAX_PARALLEL_AGENTS
|
|
836
847
|
export LOKI_MAX_ITERATIONS LOKI_MAX_RETRIES LOKI_BASE_WAIT LOKI_MAX_WAIT
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -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.
|
|
5
|
+
**Version:** v9.38.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var _t=Object.create;var{getPrototypeOf:vt,defineProperty:jG,getOwnPropertyNames:ht}=Object;var gt=Object.prototype.hasOwnProperty;function mt($){return this[$]}var ut,dt,pt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?ut??=new WeakMap:dt??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?_t(vt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of ht($))if(!gt.call(J,q))jG(J,q,{get:mt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var ct=($)=>$;function lt($,X){this[$]=ct.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:lt.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var UR={};B1(UR,{lokiDir:()=>h0,homeLokiDir:()=>WQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as it}from"url";import{existsSync as Zq}from"fs";import{homedir as at}from"os";function ot(){let $=YR;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(YR,"..","..","..")}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 WQ(){return h2(at(),".loki")}var YR,L1;var k1=s(()=>{YR=LG(it(import.meta.url));L1=ot()});import{readFileSync as st}from"fs";import{resolve as nt,dirname as rt}from"path";import{fileURLToPath as tt}from"url";function j9(){if(n3!==null)return n3;let $="9.
|
|
2
|
+
var _t=Object.create;var{getPrototypeOf:vt,defineProperty:jG,getOwnPropertyNames:ht}=Object;var gt=Object.prototype.hasOwnProperty;function mt($){return this[$]}var ut,dt,pt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?ut??=new WeakMap:dt??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?_t(vt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of ht($))if(!gt.call(J,q))jG(J,q,{get:mt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var ct=($)=>$;function lt($,X){this[$]=ct.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:lt.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var UR={};B1(UR,{lokiDir:()=>h0,homeLokiDir:()=>WQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as it}from"url";import{existsSync as Zq}from"fs";import{homedir as at}from"os";function ot(){let $=YR;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(YR,"..","..","..")}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 WQ(){return h2(at(),".loki")}var YR,L1;var k1=s(()=>{YR=LG(it(import.meta.url));L1=ot()});import{readFileSync as st}from"fs";import{resolve as nt,dirname as rt}from"path";import{fileURLToPath as tt}from"url";function j9(){if(n3!==null)return n3;let $="9.38.0";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=rt(tt(import.meta.url)),Q=AG(X);n3=st(nt(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var OR={};B1(OR,{runOrThrow:()=>Be,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>Me,commandExists:()=>g5,ShellError:()=>TG,MAX_STDOUT_BYTES:()=>MR});async function Jq($,X=MR){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 Be($,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=Ne($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Ne($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function Me($,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 MR=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 Oe?"":$}var Oe,p0,$5,q1,D61,A1,f1,m5,r;var t7=s(()=>{Oe=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),D61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as we}from"fs";async function Z2(){if(GQ!==void 0)return GQ;let $="/opt/homebrew/bin/python3.12";if(we($))return GQ=$,$;let X=await g5("python3.12");if(X)return GQ=X,X;let Q=await g5("python3");return GQ=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 GQ;var m2=s(()=>{y8()});var vR={};B1(vR,{runStatus:()=>te});import{existsSync as u5,readFileSync as C9,readdirSync as ER,statSync as xR}from"fs";import{resolve as A5,basename as pe}from"path";import{homedir as ce}from"os";function kR($){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 SR($,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=kR($),Y=kR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function ie(){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)
|
|
@@ -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(() => (yt(),St));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
|
|
1338
1338
|
`),process.stderr.write(bt),2}}wR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var K61=await Z61(Bun.argv.slice(2));process.exit(K61);
|
|
1339
1339
|
|
|
1340
|
-
//# debugId=
|
|
1340
|
+
//# debugId=E2FFCEDA097D2FB9EF9268C108032EA0
|
package/mcp/__init__.py
CHANGED
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.
|
|
4
|
+
"version": "9.38.0",
|
|
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.
|
|
5
|
+
"version": "9.38.0",
|
|
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",
|