loki-mode 9.24.0 → 9.26.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/README.md +27 -2
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/config-map.sh +103 -0
- package/autonomy/loki +57 -19
- package/autonomy/quickstart.sh +4 -1
- package/autonomy/run.sh +46 -2
- package/autonomy/verify.sh +195 -5
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +505 -505
- package/loki-ts/package.json +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
package/autonomy/verify.sh
CHANGED
|
@@ -69,6 +69,151 @@ VERIFY_EXIT_ERROR=3
|
|
|
69
69
|
VERIFY_SCHEMA_VERSION="1.0"
|
|
70
70
|
|
|
71
71
|
# Resolve tool version from the VERSION file shipped alongside the repo.
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# LLM review stage (v9.26.0). Phase 2 of the spec; the deterministic MVP shipped
|
|
74
|
+
# without it and the evidence document said so honestly.
|
|
75
|
+
#
|
|
76
|
+
# WHY IT LIVES HERE AND NOT IN THE COUNCIL. This module deliberately does not
|
|
77
|
+
# source completion-council.sh (see the header): those functions are welded to
|
|
78
|
+
# the iteration loop's globals and diff base. Instead this calls the same
|
|
79
|
+
# raw-SDK bridge the council uses under the hood -- `loki internal sdk-judge`,
|
|
80
|
+
# a pure-HTTPS judge that prints JSON on exit 0 and NOTHING on any failure.
|
|
81
|
+
#
|
|
82
|
+
# FAIL-CLOSED, NEVER FAIL-QUIET. Every failure path records an honest status
|
|
83
|
+
# (unavailable, with the reason) rather than a pass. A verifier that reports
|
|
84
|
+
# "reviewed, no issues" when the reviewer never ran is worse than one that
|
|
85
|
+
# never had a reviewer.
|
|
86
|
+
#
|
|
87
|
+
# ADVISORY IN THIS RELEASE. The verdict and exit code are computed exactly as
|
|
88
|
+
# before; this only adds a section to the evidence document. `loki verify`
|
|
89
|
+
# exits 0/1/2 on the same conditions it always did, so a CI job gating on exit
|
|
90
|
+
# 0 sees no behavior change. Verdict influence is a separate, later flag.
|
|
91
|
+
_verify_llm_review() {
|
|
92
|
+
local out_dir="$1" merge_base="$2" head_sha="$3"
|
|
93
|
+
|
|
94
|
+
if [ "${VERIFY_NO_LLM:-0}" = "1" ]; then
|
|
95
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "skipped" "--no-llm requested" "" "0" ""
|
|
96
|
+
return 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
local loki_bin
|
|
100
|
+
loki_bin="$(command -v loki 2>/dev/null || true)"
|
|
101
|
+
if [ -z "$loki_bin" ]; then
|
|
102
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "the loki CLI is not on PATH, so the SDK judge bridge cannot be reached" "" "0" ""
|
|
103
|
+
return 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Bound the diff. A judge prompt is an input cost and an unbounded diff is
|
|
107
|
+
# both expensive and useless -- past some size the model cannot reason about
|
|
108
|
+
# it anyway. The cap is explicit in the reason string when it bites, so a
|
|
109
|
+
# truncated review is never silently presented as a whole-diff review.
|
|
110
|
+
local diff_cap="${LOKI_VERIFY_LLM_DIFF_BYTES:-200000}"
|
|
111
|
+
local diff_file; diff_file="$(mktemp "${TMPDIR:-/tmp}/loki-verify-llm-diff.XXXXXX")" || {
|
|
112
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "could not create a temp file for the diff" "" "0" ""
|
|
113
|
+
return 0
|
|
114
|
+
}
|
|
115
|
+
git diff --function-context "${merge_base}..${head_sha}" > "$diff_file" 2>/dev/null || true
|
|
116
|
+
local diff_bytes; diff_bytes=$(wc -c < "$diff_file" 2>/dev/null | tr -d ' ')
|
|
117
|
+
diff_bytes="${diff_bytes:-0}"
|
|
118
|
+
local truncated=""
|
|
119
|
+
if [ "$diff_bytes" -gt "$diff_cap" ]; then
|
|
120
|
+
head -c "$diff_cap" "$diff_file" > "${diff_file}.cut" 2>/dev/null && mv -f "${diff_file}.cut" "$diff_file"
|
|
121
|
+
truncated=" (diff truncated to ${diff_cap} bytes of ${diff_bytes})"
|
|
122
|
+
fi
|
|
123
|
+
if [ "$diff_bytes" -eq 0 ]; then
|
|
124
|
+
rm -f "$diff_file" 2>/dev/null || true
|
|
125
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "skipped" "no diff to review between the merge base and HEAD" "" "0" ""
|
|
126
|
+
return 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
local pf sf
|
|
130
|
+
pf="$(mktemp "${TMPDIR:-/tmp}/loki-verify-llm-prompt.XXXXXX")" || { rm -f "$diff_file"; return 0; }
|
|
131
|
+
sf="$(mktemp "${TMPDIR:-/tmp}/loki-verify-llm-schema.XXXXXX")" || { rm -f "$diff_file" "$pf"; return 0; }
|
|
132
|
+
|
|
133
|
+
# Context first: the diff is the bulk of the prompt and identical across any
|
|
134
|
+
# retry, so leading with it keeps the cacheable prefix stable.
|
|
135
|
+
{
|
|
136
|
+
printf 'Review this diff for correctness defects only.\n\n'
|
|
137
|
+
cat "$diff_file"
|
|
138
|
+
printf '\n\nYou are a code reviewer on a verification service. Report ONLY defects you can point at in this diff:\n'
|
|
139
|
+
printf -- '- a bug that produces a wrong result or a crash, with the input that triggers it\n'
|
|
140
|
+
printf -- '- a security hole reachable from untrusted input\n'
|
|
141
|
+
printf -- '- data loss or corruption\n\n'
|
|
142
|
+
printf 'Do NOT report style, naming, formatting, test coverage, or speculative refactors.\n'
|
|
143
|
+
printf 'If the diff has no such defect, return an empty findings array. An empty result is a\n'
|
|
144
|
+
printf 'legitimate and common answer; do not invent a finding to appear useful.\n'
|
|
145
|
+
} > "$pf"
|
|
146
|
+
|
|
147
|
+
cat > "$sf" <<'SCHEMA'
|
|
148
|
+
{
|
|
149
|
+
"type": "object",
|
|
150
|
+
"properties": {
|
|
151
|
+
"summary": { "type": "string" },
|
|
152
|
+
"findings": {
|
|
153
|
+
"type": "array",
|
|
154
|
+
"items": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"properties": {
|
|
157
|
+
"severity": { "type": "string", "enum": ["critical", "high", "medium", "low"] },
|
|
158
|
+
"file": { "type": "string" },
|
|
159
|
+
"message": { "type": "string" },
|
|
160
|
+
"why_it_breaks": { "type": "string" }
|
|
161
|
+
},
|
|
162
|
+
"required": ["severity", "message", "why_it_breaks"]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"required": ["summary", "findings"]
|
|
167
|
+
}
|
|
168
|
+
SCHEMA
|
|
169
|
+
|
|
170
|
+
model="${LOKI_VERIFY_LLM_MODEL:-claude-sonnet-5}"
|
|
171
|
+
local to_s="${LOKI_VERIFY_LLM_TIMEOUT_S:-120}"
|
|
172
|
+
local wrap=""
|
|
173
|
+
if command -v timeout >/dev/null 2>&1; then wrap="timeout $(( to_s + 15 ))"
|
|
174
|
+
elif command -v gtimeout >/dev/null 2>&1; then wrap="gtimeout $(( to_s + 15 ))"; fi
|
|
175
|
+
|
|
176
|
+
local out rc=0
|
|
177
|
+
out="$($wrap "$loki_bin" internal sdk-judge \
|
|
178
|
+
--prompt-file "$pf" --schema-file "$sf" \
|
|
179
|
+
--model "$model" --effort high \
|
|
180
|
+
--timeout-ms "$(( to_s * 1000 ))" 2>/dev/null)" || rc=$?
|
|
181
|
+
rm -f "$diff_file" "$pf" "$sf" 2>/dev/null || true
|
|
182
|
+
|
|
183
|
+
if [ "$rc" -ne 0 ] || [ -z "$out" ]; then
|
|
184
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "the SDK judge returned no result (no API key, transport failure, or timeout)${truncated}" "" "0" "$model"
|
|
185
|
+
return 0
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# Parse defensively: a malformed payload is "unavailable", never a pass.
|
|
189
|
+
local parsed
|
|
190
|
+
parsed="$(printf '%s' "$out" | python3 -c '
|
|
191
|
+
import json, sys
|
|
192
|
+
try:
|
|
193
|
+
d = json.load(sys.stdin)
|
|
194
|
+
fs = d.get("findings") or []
|
|
195
|
+
if not isinstance(fs, list):
|
|
196
|
+
raise ValueError("findings is not a list")
|
|
197
|
+
print("%d\t%s" % (len(fs), (d.get("summary") or "").replace("\t", " ").replace("\n", " ")[:300]))
|
|
198
|
+
except Exception as exc:
|
|
199
|
+
print("ERR\t%s" % type(exc).__name__)
|
|
200
|
+
' 2>/dev/null)" || parsed="ERR\tparse"
|
|
201
|
+
|
|
202
|
+
case "$parsed" in
|
|
203
|
+
ERR*)
|
|
204
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "the SDK judge returned a payload that did not parse${truncated}" "" "0" "$model" ;;
|
|
205
|
+
*)
|
|
206
|
+
findings_n="${parsed%%\t*}"
|
|
207
|
+
summary="${parsed#*\t}"
|
|
208
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "reviewed" "" "$summary" "$findings_n" "$model"
|
|
209
|
+
# Keep the raw payload beside the evidence document so a reader can
|
|
210
|
+
# check the review rather than take the summary on faith.
|
|
211
|
+
printf '%s' "$out" > "${out_dir}/llm-review.json" 2>/dev/null || true
|
|
212
|
+
;;
|
|
213
|
+
esac
|
|
214
|
+
return 0
|
|
215
|
+
}
|
|
216
|
+
|
|
72
217
|
_verify_tool_version() {
|
|
73
218
|
local here
|
|
74
219
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -1917,6 +2062,23 @@ verify_emit_evidence() {
|
|
|
1917
2062
|
repo_name="$(git config --get remote.origin.url 2>/dev/null | sed -E 's#.*[:/]([^/]+/[^/]+)(\.git)?$#\1#' || echo "local")"
|
|
1918
2063
|
[ -z "$repo_name" ] && repo_name="local"
|
|
1919
2064
|
|
|
2065
|
+
# LLM review (advisory this release: it does not change VERIFY_VERDICT or
|
|
2066
|
+
# VERIFY_EXIT, both already computed above).
|
|
2067
|
+
local _llm_line _llm_status _llm_reason _llm_summary _llm_n _llm_model
|
|
2068
|
+
_llm_line="$(_verify_llm_review "$out_dir" "${VERIFY_MERGE_BASE:-}" "${VERIFY_HEAD_SHA:-HEAD}" 2>/dev/null || true)"
|
|
2069
|
+
_llm_status="$(printf '%s' "$_llm_line" | cut -f1)"
|
|
2070
|
+
_llm_reason="$(printf '%s' "$_llm_line" | cut -f2)"
|
|
2071
|
+
_llm_summary="$(printf '%s' "$_llm_line" | cut -f3)"
|
|
2072
|
+
_llm_n="$(printf '%s' "$_llm_line" | cut -f4)"
|
|
2073
|
+
_llm_model="$(printf '%s' "$_llm_line" | cut -f5)"
|
|
2074
|
+
[ -n "$_llm_status" ] || _llm_status="unavailable"
|
|
2075
|
+
[ -n "$_llm_n" ] || _llm_n=0
|
|
2076
|
+
|
|
2077
|
+
_V_LLM_STATUS="$_llm_status" \
|
|
2078
|
+
_V_LLM_REASON="$_llm_reason" \
|
|
2079
|
+
_V_LLM_SUMMARY="$_llm_summary" \
|
|
2080
|
+
_V_LLM_N="$_llm_n" \
|
|
2081
|
+
_V_LLM_MODEL="$_llm_model" \
|
|
1920
2082
|
_VERIFY_OUT_DIR="$out_dir" \
|
|
1921
2083
|
_VERIFY_FINDINGS="$_VERIFY_FINDINGS_FILE" \
|
|
1922
2084
|
_VERIFY_GATES="$_VERIFY_GATES_FILE" \
|
|
@@ -2019,9 +2181,21 @@ doc = {
|
|
|
2019
2181
|
},
|
|
2020
2182
|
"deterministic_gates": gates,
|
|
2021
2183
|
"llm_review": {
|
|
2022
|
-
|
|
2023
|
-
"
|
|
2184
|
+
# status is one of: reviewed | skipped | unavailable.
|
|
2185
|
+
# "unavailable" is deliberately NOT "skipped": a reviewer that could not
|
|
2186
|
+
# run is a different fact from one that was not asked to, and collapsing
|
|
2187
|
+
# them would let a broken key read as a clean pass.
|
|
2188
|
+
"status": os.environ.get("_V_LLM_STATUS", "unavailable"),
|
|
2189
|
+
"reason": os.environ.get("_V_LLM_REASON", "") or None,
|
|
2190
|
+
"summary": os.environ.get("_V_LLM_SUMMARY", "") or None,
|
|
2191
|
+
"finding_count": int(os.environ.get("_V_LLM_N", "0") or 0),
|
|
2192
|
+
"model": os.environ.get("_V_LLM_MODEL", "") or None,
|
|
2193
|
+
# An LLM review is not reproducible the way a runner exit code is, and
|
|
2194
|
+
# the document says so rather than implying determinism it lacks.
|
|
2024
2195
|
"reproducible": False,
|
|
2196
|
+
# Advisory in this release: recorded, but never folded into the verdict
|
|
2197
|
+
# or the exit code. Promoting it is a separate, flagged change.
|
|
2198
|
+
"affects_verdict": False,
|
|
2025
2199
|
},
|
|
2026
2200
|
"findings": findings,
|
|
2027
2201
|
"suppressed": [],
|
|
@@ -2102,7 +2276,15 @@ lines.append("# Autonomi Verify report")
|
|
|
2102
2276
|
lines.append("")
|
|
2103
2277
|
lines.append("Verdict: **%s** (exit %d)" % (doc["verdict"], doc["exit_code"]))
|
|
2104
2278
|
lines.append("")
|
|
2105
|
-
|
|
2279
|
+
_llm = doc.get("llm_review") or {}
|
|
2280
|
+
_llm_status = _llm.get("status", "unavailable")
|
|
2281
|
+
if _llm_status == "reviewed":
|
|
2282
|
+
_llm_note = "LLM review: %d finding(s)" % _llm.get("finding_count", 0)
|
|
2283
|
+
elif _llm_status == "skipped":
|
|
2284
|
+
_llm_note = "LLM review: skipped"
|
|
2285
|
+
else:
|
|
2286
|
+
_llm_note = "LLM review: unavailable"
|
|
2287
|
+
lines.append("Tool: loki verify %s | %s" % (doc["produced_by"]["tool_version"], _llm_note))
|
|
2106
2288
|
lines.append("")
|
|
2107
2289
|
s = doc["subject"]
|
|
2108
2290
|
lines.append("## Subject")
|
|
@@ -2140,7 +2322,10 @@ else:
|
|
|
2140
2322
|
lines.append("")
|
|
2141
2323
|
lines.append("## LLM review")
|
|
2142
2324
|
lines.append("")
|
|
2143
|
-
|
|
2325
|
+
if _llm.get("reason"):
|
|
2326
|
+
lines.append("LLM review not run: %s" % _llm["reason"])
|
|
2327
|
+
elif _llm_status == "reviewed" and _llm.get("summary"):
|
|
2328
|
+
lines.append("LLM review: %s" % _llm["summary"])
|
|
2144
2329
|
lines.append("")
|
|
2145
2330
|
lines.append("Evidence JSON: %s" % ev_path)
|
|
2146
2331
|
lines.append("")
|
|
@@ -2183,7 +2368,9 @@ OPTIONS:
|
|
|
2183
2368
|
--block-on <list> Comma list of severities that BLOCK.
|
|
2184
2369
|
Default: critical,high (one notch looser than the
|
|
2185
2370
|
Loki build loop, which also blocks on medium).
|
|
2186
|
-
--no-llm
|
|
2371
|
+
--no-llm Skip the LLM review stage. The deterministic gates and the
|
|
2372
|
+
verdict are unchanged either way -- the review is advisory
|
|
2373
|
+
in this release and never alters the exit code.
|
|
2187
2374
|
--json Emit the evidence document to stdout so it can be piped
|
|
2188
2375
|
(`loki verify --json | jq .verdict`). The same document
|
|
2189
2376
|
is still written to <out>/evidence.json. The human
|
|
@@ -2793,6 +2980,9 @@ verify_main() {
|
|
|
2793
2980
|
--block-on)
|
|
2794
2981
|
block_on="$(printf '%s' "${2:-}" | tr '[:upper:]' '[:lower:]')"; shift 2 ;;
|
|
2795
2982
|
--no-llm)
|
|
2983
|
+
# Was a no-op accepted "for forward-compat" while no LLM stage
|
|
2984
|
+
# existed. Now it does what its name always implied.
|
|
2985
|
+
VERIFY_NO_LLM=1
|
|
2796
2986
|
shift ;;
|
|
2797
2987
|
--json)
|
|
2798
2988
|
# Emit the evidence document to STDOUT so a caller can pipe it.
|
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.26.1
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|