loki-mode 9.16.0 → 9.17.2
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/issue-providers.sh +0 -21
- package/autonomy/lib/verdict.py +4 -25
- package/autonomy/loki +4 -190
- package/autonomy/run.sh +64 -167
- package/autonomy/verify.sh +100 -12
- package/completions/_loki +0 -2
- package/completions/loki.bash +1 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/static/index.html +168 -2
- package/docs/VERIFICATION-COST.md +20 -0
- package/loki-ts/dist/loki.js +215 -215
- package/mcp/__init__.py +1 -1
- package/mcp/_sdk_loader.py +0 -25
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
package/autonomy/run.sh
CHANGED
|
@@ -2049,40 +2049,7 @@ except Exception:
|
|
|
2049
2049
|
pass
|
|
2050
2050
|
" 2>/dev/null || true)"
|
|
2051
2051
|
fi
|
|
2052
|
-
|
|
2053
|
-
_fields="${_fields} | \$${_cost}"
|
|
2054
|
-
# PROJECTED spend at the iteration cap, shown beside the actual.
|
|
2055
|
-
#
|
|
2056
|
-
# WHY. MAX_ITERATIONS (default 25) is the ONLY backstop on a run's cost:
|
|
2057
|
-
# LOKI_BUDGET_LIMIT defaults to "" and LOKI_MAX_DURATION to 0, both
|
|
2058
|
-
# documented at run.sh:1118-1121, and the runtime says so out loud at
|
|
2059
|
-
# :21333. That is a defensible default -- a run killed mid-flight at a
|
|
2060
|
-
# dollar threshold the user never chose is worse than one that finishes.
|
|
2061
|
-
# But it left the user unable to SEE where the run was heading: $4.20 at
|
|
2062
|
-
# iteration 3 of 25 reads as cheap right up to the moment it is not.
|
|
2063
|
-
#
|
|
2064
|
-
# Linear extrapolation, and labelled "proj" rather than presented as a
|
|
2065
|
-
# forecast: later iterations are usually cheaper than early ones (more
|
|
2066
|
-
# cache hits, smaller diffs), so this is an upper bound, not a promise.
|
|
2067
|
-
# Shown only when it would actually tell the user something -- from
|
|
2068
|
-
# iteration 2 (one data point cannot extrapolate) and only when the
|
|
2069
|
-
# projection is meaningfully above what has already been spent.
|
|
2070
|
-
if [ "${_iter:-0}" -ge 2 ] && [ "${_max:-0}" -gt 0 ]; then
|
|
2071
|
-
local _proj
|
|
2072
|
-
_proj="$(LOKI_C="$_cost" LOKI_I="$_iter" LOKI_M="$_max" python3 -c "
|
|
2073
|
-
import os
|
|
2074
|
-
try:
|
|
2075
|
-
c = float(os.environ['LOKI_C']); i = int(os.environ['LOKI_I']); m = int(os.environ['LOKI_M'])
|
|
2076
|
-
if i > 0 and m > i:
|
|
2077
|
-
p = c / i * m
|
|
2078
|
-
if p >= c * 1.5:
|
|
2079
|
-
print('%.2f' % p)
|
|
2080
|
-
except Exception:
|
|
2081
|
-
pass
|
|
2082
|
-
" 2>/dev/null || true)"
|
|
2083
|
-
[ -n "$_proj" ] && _fields="${_fields} (proj \$${_proj} at ${_max})"
|
|
2084
|
-
fi
|
|
2085
|
-
fi
|
|
2052
|
+
[ -n "$_cost" ] && _fields="${_fields} | \$${_cost}"
|
|
2086
2053
|
|
|
2087
2054
|
# Files changed (+ins/-del and file count) vs the run start SHA. Reuse the
|
|
2088
2055
|
# build_completion_summary diff approach incl. the .loki/.git exclude pathspec.
|
|
@@ -3349,34 +3316,10 @@ detect_complexity() {
|
|
|
3349
3316
|
file_count="${file_count:-0}"
|
|
3350
3317
|
file_count="${file_count//[^0-9]/}"
|
|
3351
3318
|
|
|
3352
|
-
# Check for external integrations
|
|
3353
|
-
#
|
|
3354
|
-
# THE EXCLUDES ARE LOAD-BEARING. This grep used to prune nothing while the
|
|
3355
|
-
# find eleven lines above it prunes node_modules/.git/vendor/dist/build --
|
|
3356
|
-
# same function, same intent, inconsistent implementation. With --include
|
|
3357
|
-
# "*.json" that meant ANY transitive dependency whose package.json mentions
|
|
3358
|
-
# azure, stripe or aws-sdk set has_external=true.
|
|
3359
|
-
#
|
|
3360
|
-
# And has_external does not merely block "simple": in the classifier below it
|
|
3361
|
-
# jumps straight to "complex", skipping "standard". So a one-liner in any
|
|
3362
|
-
# repo that has ever run npm install landed on the MOST expensive tier, which
|
|
3363
|
-
# then runs the architecture doc suite (up to 300s of silence per attempt)
|
|
3364
|
-
# and holds the council's forced minimum-iteration floor at 3 instead of 1.
|
|
3365
|
-
#
|
|
3366
|
-
# Reproduced from scratch before fixing: a project with ONE dependency naming
|
|
3367
|
-
# @azure/core classified complex; adding --exclude-dir=node_modules made the
|
|
3368
|
-
# identical project classify simple. It also fired TRUE on this repo.
|
|
3369
|
-
#
|
|
3370
|
-
# A prior incident matches exactly -- a coffee landing page took 1h34m over
|
|
3371
|
-
# 11 iterations because the simple fast-path never engaged. The fast path was
|
|
3372
|
-
# correctly built and correctly wired the whole time; this one missing prune
|
|
3373
|
-
# was what made it unreachable.
|
|
3319
|
+
# Check for external integrations
|
|
3374
3320
|
local has_external=false
|
|
3375
3321
|
if grep -rq "oauth\|SAML\|OIDC\|stripe\|twilio\|aws-sdk\|@google-cloud\|azure" \
|
|
3376
|
-
"$target_dir" --include="*.json" --include="*.ts" --include="*.js"
|
|
3377
|
-
--exclude-dir=node_modules --exclude-dir=.git --exclude-dir=vendor \
|
|
3378
|
-
--exclude-dir=dist --exclude-dir=build --exclude-dir=__pycache__ \
|
|
3379
|
-
--exclude-dir=.venv --exclude-dir=venv 2>/dev/null; then
|
|
3322
|
+
"$target_dir" --include="*.json" --include="*.ts" --include="*.js" 2>/dev/null; then
|
|
3380
3323
|
has_external=true
|
|
3381
3324
|
fi
|
|
3382
3325
|
|
|
@@ -11399,7 +11342,58 @@ enforce_test_coverage() {
|
|
|
11399
11342
|
if [ -f "${TARGET_DIR:-.}/package.json" ]; then
|
|
11400
11343
|
# BUG-EC-014: Wrap test runners with timeout to prevent hanging indefinitely
|
|
11401
11344
|
local gate_timeout="${LOKI_GATE_TIMEOUT:-300}" # 5 minutes default
|
|
11402
|
-
|
|
11345
|
+
# A DECLARED scripts.test wins over an installed package.
|
|
11346
|
+
#
|
|
11347
|
+
# The v7.41.x fix below already established that grep false-positives on
|
|
11348
|
+
# devDependencies -- read its comment -- but it only guarded the `else`
|
|
11349
|
+
# branch, so the three grep branches AHEAD of it still shadowed it. This
|
|
11350
|
+
# repo is the proof: jest is a devDependency with no jest config while
|
|
11351
|
+
# scripts.test runs `bash -n` + `node --test`, and the grep branch
|
|
11352
|
+
# hijacked the runner, ran jest over 895 non-jest files and failed every
|
|
11353
|
+
# one of them.
|
|
11354
|
+
#
|
|
11355
|
+
# _has_declared_test_script is true only when the project DECLARES a real
|
|
11356
|
+
# test script, which is the project's own statement of its runner. When
|
|
11357
|
+
# it declares nothing, the grep branches still apply exactly as before --
|
|
11358
|
+
# that is the legitimate case they were written for (a package that ships
|
|
11359
|
+
# a runner but no npm script).
|
|
11360
|
+
local _declared_test_script=""
|
|
11361
|
+
_declared_test_script=$(_LOKI_PKG="${TARGET_DIR:-.}/package.json" python3 -c "
|
|
11362
|
+
import json, os, sys
|
|
11363
|
+
try:
|
|
11364
|
+
with open(os.environ['_LOKI_PKG']) as f:
|
|
11365
|
+
d = json.load(f)
|
|
11366
|
+
except Exception:
|
|
11367
|
+
sys.exit(0)
|
|
11368
|
+
if not isinstance(d, dict):
|
|
11369
|
+
sys.exit(0)
|
|
11370
|
+
t = (d.get('scripts') or {}).get('test') or ''
|
|
11371
|
+
if 'no test specified' in t.lower():
|
|
11372
|
+
sys.exit(0)
|
|
11373
|
+
sys.stdout.write(t.strip())
|
|
11374
|
+
" 2>/dev/null || echo "")
|
|
11375
|
+
if [ -n "$_declared_test_script" ]; then
|
|
11376
|
+
# Run the DECLARED script. This body used to live in the trailing
|
|
11377
|
+
# `else`; a no-op `:` here would have terminated the if/elif chain
|
|
11378
|
+
# and left test_runner=none, turning a false BLOCK into a silently
|
|
11379
|
+
# unmeasured gate -- strictly worse than the bug being fixed.
|
|
11380
|
+
#
|
|
11381
|
+
# LOKI_TEST_COMMAND lets an operator override the invocation; the
|
|
11382
|
+
# default is the project's own `npm test`.
|
|
11383
|
+
local _test_cmd="${LOKI_TEST_COMMAND:-npm test}"
|
|
11384
|
+
# Label the runner by what the script invokes so evidence is honest
|
|
11385
|
+
# (node --test, vitest, jest, etc. all surface here).
|
|
11386
|
+
case "$_declared_test_script" in
|
|
11387
|
+
*"node --test"*|*"node:test"*) test_runner="node-test" ;;
|
|
11388
|
+
*vitest*) test_runner="vitest" ;;
|
|
11389
|
+
*jest*) test_runner="jest" ;;
|
|
11390
|
+
*mocha*) test_runner="mocha" ;;
|
|
11391
|
+
*) test_runner="npm-test" ;;
|
|
11392
|
+
esac
|
|
11393
|
+
local output
|
|
11394
|
+
output=$(cd "${TARGET_DIR:-.}" && timeout "$gate_timeout" sh -c "$_test_cmd" 2>&1) || test_passed=false
|
|
11395
|
+
details="$test_runner ($_test_cmd): $(echo "$output" | tail -5 | tr '\n' ' ')"
|
|
11396
|
+
elif grep -q '"vitest"' "${TARGET_DIR:-.}/package.json" 2>/dev/null; then
|
|
11403
11397
|
test_runner="vitest"
|
|
11404
11398
|
local output
|
|
11405
11399
|
output=$(cd "${TARGET_DIR:-.}" && timeout "$gate_timeout" npx vitest run --reporter=json 2>&1) || test_passed=false
|
|
@@ -11425,37 +11419,13 @@ enforce_test_coverage() {
|
|
|
11425
11419
|
# would false-positive on devDeps / unrelated keys), then run the
|
|
11426
11420
|
# configured command. This MUST sit before the monorepo/python/go/rust
|
|
11427
11421
|
# checks, all of which gate on test_runner=="none".
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
sys.exit(0)
|
|
11436
|
-
t = (d.get('scripts') or {}).get('test') or ''
|
|
11437
|
-
# npm's default placeholder; treat as 'no test'.
|
|
11438
|
-
if 'no test specified' in t.lower():
|
|
11439
|
-
sys.exit(0)
|
|
11440
|
-
sys.stdout.write(t.strip())
|
|
11441
|
-
" 2>/dev/null || echo "")
|
|
11442
|
-
if [ -n "$_pkg_test_script" ]; then
|
|
11443
|
-
# LOKI_TEST_COMMAND lets an operator override the invocation; the
|
|
11444
|
-
# default is the project's own `npm test`.
|
|
11445
|
-
local _test_cmd="${LOKI_TEST_COMMAND:-npm test}"
|
|
11446
|
-
# Label the runner by what the script invokes so evidence is
|
|
11447
|
-
# honest (node --test, vitest, jest, etc. all surface here).
|
|
11448
|
-
case "$_pkg_test_script" in
|
|
11449
|
-
*"node --test"*|*"node:test"*) test_runner="node-test" ;;
|
|
11450
|
-
*vitest*) test_runner="vitest" ;;
|
|
11451
|
-
*jest*) test_runner="jest" ;;
|
|
11452
|
-
*mocha*) test_runner="mocha" ;;
|
|
11453
|
-
*) test_runner="npm-test" ;;
|
|
11454
|
-
esac
|
|
11455
|
-
local output
|
|
11456
|
-
output=$(cd "${TARGET_DIR:-.}" && timeout "$gate_timeout" sh -c "$_test_cmd" 2>&1) || test_passed=false
|
|
11457
|
-
details="$test_runner ($_test_cmd): $(echo "$output" | tail -5 | tr '\n' ' ')"
|
|
11458
|
-
fi
|
|
11422
|
+
#
|
|
11423
|
+
# That handler now runs in the FIRST branch above, because a declared
|
|
11424
|
+
# script must win over an installed devDependency. Nothing is left to
|
|
11425
|
+
# do here: reaching this point means the project declares no test
|
|
11426
|
+
# script AND ships no recognised runner, so test_runner stays "none"
|
|
11427
|
+
# and the monorepo/python/go/rust detection below takes over.
|
|
11428
|
+
:
|
|
11459
11429
|
fi
|
|
11460
11430
|
fi
|
|
11461
11431
|
|
|
@@ -12355,14 +12325,6 @@ auto_generate_docs_if_needed() {
|
|
|
12355
12325
|
elif command -v timeout >/dev/null 2>&1; then
|
|
12356
12326
|
_doc_cmd=(timeout "${_doc_to}s")
|
|
12357
12327
|
fi
|
|
12358
|
-
# SAY WHAT IS HAPPENING BEFORE GOING QUIET. This call discards child output
|
|
12359
|
-
# and can run for the full timeout (default 300s), so without this line the
|
|
12360
|
-
# user sees a single "Auto-documentation" header and then minutes of nothing.
|
|
12361
|
-
# A silent gap reads as a hang: the observed incident was a build stuck ~55
|
|
12362
|
-
# min here with the work committed but never pushed, and nothing on screen
|
|
12363
|
-
# said which step owned the time. Naming the step and its cap turns an
|
|
12364
|
-
# apparent freeze into a bounded wait the user can reason about.
|
|
12365
|
-
log_info "Auto-documentation: generating architecture suite (no output until it finishes; up to ${_doc_to}s)"
|
|
12366
12328
|
if "${_doc_cmd[@]}" "$loki_bin" docs generate "$project_dir" >/dev/null 2>&1; then
|
|
12367
12329
|
:
|
|
12368
12330
|
else
|
|
@@ -12421,10 +12383,6 @@ run_magic_debate_gate() {
|
|
|
12421
12383
|
# verdict on genuinely thin input, not a spurious process block.
|
|
12422
12384
|
log_info "Magic Modules: running debate on '$latest_name'"
|
|
12423
12385
|
local debate_out debate_rc
|
|
12424
|
-
# Captured to a variable, so nothing reaches the screen for up to 300s. Same
|
|
12425
|
-
# reasoning as the doc suite above: name the step and its cap so a bounded
|
|
12426
|
-
# wait does not read as a hang.
|
|
12427
|
-
log_info "Magic debate: reviewing $latest_name (2 rounds, output shown when it finishes; up to 300s)"
|
|
12428
12386
|
debate_out=$(cd "$TARGET_DIR" && PYTHONPATH="$PROJECT_DIR" LOKI_PROVIDER="${PROVIDER_NAME:-claude}" \
|
|
12429
12387
|
timeout 300 "$PROJECT_DIR/autonomy/loki" magic debate "$latest_name" --rounds 2 2>&1) \
|
|
12430
12388
|
&& debate_rc=0 || debate_rc=$?
|
|
@@ -20089,11 +20047,8 @@ except Exception:
|
|
|
20089
20047
|
if [ -n "$gate_escalation_context" ]; then
|
|
20090
20048
|
_legacy_priority="${_legacy_priority}${_legacy_priority:+ }${gate_escalation_context}"
|
|
20091
20049
|
fi
|
|
20092
|
-
# Same cap, same reason, same default as the degraded path above --
|
|
20093
|
-
# a pasted spec has to be bounded, but the bound must not silently
|
|
20094
|
-
# eat requirements. 4000 bytes dropped anything past ~600 words.
|
|
20095
20050
|
if [ -n "$prd" ] && [ -f "$prd" ]; then
|
|
20096
|
-
_legacy_prd_content=$(head -c
|
|
20051
|
+
_legacy_prd_content=$(head -c 4000 "$prd")
|
|
20097
20052
|
fi
|
|
20098
20053
|
if [ $retry -eq 0 ]; then
|
|
20099
20054
|
if [ -n "$prd" ]; then
|
|
@@ -20144,35 +20099,9 @@ except Exception:
|
|
|
20144
20099
|
|
|
20145
20100
|
if [ "${PROVIDER_DEGRADED:-false}" = "true" ]; then
|
|
20146
20101
|
# Degraded providers: simpler wording, but still static-first.
|
|
20147
|
-
#
|
|
20148
|
-
# THE CAP IS NOW ANNOUNCED, NOT SILENT. This path PASTES the spec text
|
|
20149
|
-
# (a degraded provider cannot be told "read the file at this path" the
|
|
20150
|
-
# way claude/cline/opencode are at :20196), so it has to be bounded. It
|
|
20151
|
-
# was bounded at 4000 bytes with no notice: a requirement past ~600 words
|
|
20152
|
-
# was dropped mid-sentence and the model never knew a spec existed beyond
|
|
20153
|
-
# what it saw. Demonstrated on a 4229-byte spec -- the requirement on the
|
|
20154
|
-
# last line was simply absent from what the model received.
|
|
20155
|
-
#
|
|
20156
|
-
# That is the same class of defect spec-expand.sh:5-7 already names for
|
|
20157
|
-
# OpenAPI ("a 40-operation file loses 21 of 40 ops") and fixed for
|
|
20158
|
-
# contracts only. Markdown specs still had it, and only for the two
|
|
20159
|
-
# degraded providers -- so codex and aider users silently got a worse
|
|
20160
|
-
# build than claude users from the identical spec.
|
|
20161
|
-
#
|
|
20162
|
-
# Raised to 24000 (a large PRD fits whole) and, when the spec still
|
|
20163
|
-
# exceeds it, the model is TOLD so and given the path to read the rest.
|
|
20164
|
-
# An unannounced truncation makes the model confidently build the wrong
|
|
20165
|
-
# thing; an announced one makes it go look.
|
|
20166
|
-
local _prd_cap="${LOKI_DEGRADED_PRD_CAP:-24000}"
|
|
20167
20102
|
local prd_content=""
|
|
20168
|
-
local _prd_truncated=0
|
|
20169
20103
|
if [ -n "$prd" ] && [ -f "$prd" ]; then
|
|
20170
|
-
prd_content=$(head -c
|
|
20171
|
-
local _prd_bytes
|
|
20172
|
-
_prd_bytes=$(wc -c < "$prd" 2>/dev/null | tr -d ' ')
|
|
20173
|
-
if [ -n "$_prd_bytes" ] && [ "$_prd_bytes" -gt "$_prd_cap" ] 2>/dev/null; then
|
|
20174
|
-
_prd_truncated=1
|
|
20175
|
-
fi
|
|
20104
|
+
prd_content=$(head -c 4000 "$prd")
|
|
20176
20105
|
fi
|
|
20177
20106
|
|
|
20178
20107
|
local degraded_prd_anchor="Loki Mode"
|
|
@@ -20201,38 +20130,6 @@ except Exception:
|
|
|
20201
20130
|
[ -n "$queue_tasks" ] && printf 'Tasks: %s\n' "$queue_tasks"
|
|
20202
20131
|
if [ -n "$prd" ]; then
|
|
20203
20132
|
printf 'PRD contents: %s\n' "$prd_content"
|
|
20204
|
-
# Announce the cut. Silence here is what made the old 4000-byte cap
|
|
20205
|
-
# dangerous: the model treated a partial spec as the whole spec and
|
|
20206
|
-
# built confidently against requirements it had never seen. Naming
|
|
20207
|
-
# the file lets it read the remainder itself.
|
|
20208
|
-
if [ "${_prd_truncated:-0}" = "1" ]; then
|
|
20209
|
-
printf 'NOTE: the spec above is TRUNCATED at %s bytes. The full spec is at %s -- read it before deciding the work is complete.\n' \
|
|
20210
|
-
"$_prd_cap" "$prd"
|
|
20211
|
-
fi
|
|
20212
|
-
fi
|
|
20213
|
-
|
|
20214
|
-
# FIRST-PASS EXCELLENCE FOR DEGRADED PROVIDERS.
|
|
20215
|
-
#
|
|
20216
|
-
# This directive existed only for Claude. providers/claude.sh:322 injects
|
|
20217
|
-
# it via --append-system-prompt, a flag codex/aider do not have, so the
|
|
20218
|
-
# one mechanism built specifically to make a WEAKER model land complete
|
|
20219
|
-
# on iteration 1 reached only the strongest one. Measured before writing
|
|
20220
|
-
# this: grep for FIRST_PASS_EXCELLENCE returns 0 in codex.sh, aider.sh,
|
|
20221
|
-
# cline.sh and opencode.sh.
|
|
20222
|
-
#
|
|
20223
|
-
# It matters most exactly where it was missing. The premise (recorded
|
|
20224
|
-
# when the Claude version was built) is that iteration count is a proxy
|
|
20225
|
-
# for how much the first pass missed, and that for a weak model context
|
|
20226
|
-
# quality beats iteration count. Codex is also the free on-ramp, so the
|
|
20227
|
-
# users least able to absorb a bad build were the ones getting no help.
|
|
20228
|
-
#
|
|
20229
|
-
# Condensed rather than byte-mirrored: the Claude text is ~4.3KB of
|
|
20230
|
-
# system prompt, and these providers take it inline in the user turn
|
|
20231
|
-
# where budget is tighter. The four load-bearing instructions are kept --
|
|
20232
|
-
# build fully, wire the backend, verify by RUNNING, commit to one design.
|
|
20233
|
-
# Same iteration-1 gate and same env var, so one switch controls both.
|
|
20234
|
-
if [ "${LOKI_FIRST_PASS_EXCELLENCE:-1}" != "0" ] && [ "${iteration:-1}" -le 1 ] 2>/dev/null; then
|
|
20235
|
-
printf '%s\n' '[FIRST-PASS EXCELLENCE] Treat THIS pass as your one shot to ship a complete, working solution. The loop is a safety net, not a plan. 1) BUILD IT FULLY: no stubs, no TODOs, no placeholder or mock data where real logic belongs. If the spec implies a backend (auth, persistence, a form that submits), WIRE IT so it actually persists -- a UI whose buttons do nothing is the most common failure. 2) VERIFY BY RUNNING each acceptance path, not by reading the code. 3) DECIDE the architecture now rather than refactoring later. 4) Commit to ONE specific design; avoid the generic purple-gradient default look.'
|
|
20236
20133
|
fi
|
|
20237
20134
|
printf '</dynamic_context>\n'
|
|
20238
20135
|
return 0
|
package/autonomy/verify.sh
CHANGED
|
@@ -421,6 +421,30 @@ _verify_zero_tests_executed() {
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
# ---------------------------------------------------------------------------
|
|
424
|
+
# Reads scripts.test out of a package.json, or prints nothing.
|
|
425
|
+
#
|
|
426
|
+
# Parsed as JSON, never grepped: a substring search over the whole file is the
|
|
427
|
+
# exact bug this helper exists to remove, and re-introducing it one level down
|
|
428
|
+
# would be invisible. A malformed package.json prints nothing, which routes to
|
|
429
|
+
# runner=none -> INCONCLUSIVE, never to a guessed runner.
|
|
430
|
+
_verify_pkg_test_script() {
|
|
431
|
+
local tree="$1"
|
|
432
|
+
[ -f "$tree/package.json" ] || return 0
|
|
433
|
+
python3 -c '
|
|
434
|
+
import json,sys
|
|
435
|
+
try:
|
|
436
|
+
with open(sys.argv[1]) as fh:
|
|
437
|
+
d = json.load(fh)
|
|
438
|
+
except Exception:
|
|
439
|
+
sys.exit(0)
|
|
440
|
+
if not isinstance(d, dict):
|
|
441
|
+
sys.exit(0)
|
|
442
|
+
s = d.get("scripts")
|
|
443
|
+
if isinstance(s, dict) and isinstance(s.get("test"), str):
|
|
444
|
+
sys.stdout.write(s["test"])
|
|
445
|
+
' "$tree/package.json" 2>/dev/null || true
|
|
446
|
+
}
|
|
447
|
+
|
|
424
448
|
# Gate: tests (faithful port of enforce_test_coverage detection, run.sh:6624).
|
|
425
449
|
#
|
|
426
450
|
# Detection order mirrors the source: vitest -> jest -> mocha (package.json),
|
|
@@ -451,16 +475,52 @@ verify_gate_tests() {
|
|
|
451
475
|
local out=""
|
|
452
476
|
|
|
453
477
|
if [ -f "$tree/package.json" ]; then
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
478
|
+
# WHICH runner, decided by what `npm test` ACTUALLY INVOKES -- not by
|
|
479
|
+
# what happens to be installed.
|
|
480
|
+
#
|
|
481
|
+
# The old check was `grep '"jest"' package.json`, which matches a
|
|
482
|
+
# DEVDEPENDENCY entry. On this very repo that is exactly what happened:
|
|
483
|
+
# jest is a devDependency with NO jest config, while scripts.test runs
|
|
484
|
+
# bash -n plus `node --test`. So verify hijacked the runner, jest globbed
|
|
485
|
+
# 895 files that are not jest tests, every one reported "Your test suite
|
|
486
|
+
# must contain at least one test", and `loki verify` returned BLOCKED on
|
|
487
|
+
# a clean tree -- permanently, for a defect that does not exist.
|
|
488
|
+
#
|
|
489
|
+
# A false BLOCK on the flagship verification command is worse than a
|
|
490
|
+
# missed one: it trains users to ignore the verdict.
|
|
491
|
+
#
|
|
492
|
+
# scripts.test is the project's own declaration of its runner, so it is
|
|
493
|
+
# the signal with authority here. A declared script that names none of
|
|
494
|
+
# the three is still RUN, via `npm test` -- mirroring run.sh's npm-test
|
|
495
|
+
# fallback. Falling through instead would be its own defect: this repo
|
|
496
|
+
# declares `bash -n ... && node --test ...`, and without the fallback
|
|
497
|
+
# verify skipped it and ran PYTEST over a bash/node project.
|
|
498
|
+
local _test_script=""
|
|
499
|
+
_test_script="$(_verify_pkg_test_script "$tree")"
|
|
500
|
+
case "$_test_script" in
|
|
501
|
+
*vitest*)
|
|
502
|
+
runner="vitest"
|
|
503
|
+
out="$(cd "$tree" && $_vt npx vitest run 2>&1)" || rc=$? ;;
|
|
504
|
+
*jest*)
|
|
505
|
+
runner="jest"
|
|
506
|
+
out="$(cd "$tree" && $_vt npx jest --passWithNoTests --forceExit 2>&1)" || rc=$? ;;
|
|
507
|
+
*mocha*)
|
|
508
|
+
runner="mocha"
|
|
509
|
+
out="$(cd "$tree" && $_vt npx mocha 2>&1)" || rc=$? ;;
|
|
510
|
+
"")
|
|
511
|
+
: ;; # nothing declared: leave runner=none for the paths below
|
|
512
|
+
*"no test specified"*)
|
|
513
|
+
: ;; # npm's placeholder is not a test script
|
|
514
|
+
*)
|
|
515
|
+
# Labelled by what it invokes, so the evidence names the real
|
|
516
|
+
# runner rather than a generic "npm".
|
|
517
|
+
case "$_test_script" in
|
|
518
|
+
*"node --test"*|*"node:test"*) runner="node-test" ;;
|
|
519
|
+
*pytest*) runner="pytest" ;;
|
|
520
|
+
*) runner="npm-test" ;;
|
|
521
|
+
esac
|
|
522
|
+
out="$(cd "$tree" && $_vt npm test 2>&1)" || rc=$? ;;
|
|
523
|
+
esac
|
|
464
524
|
fi
|
|
465
525
|
|
|
466
526
|
if [ "$runner" = "none" ]; then
|
|
@@ -1060,10 +1120,38 @@ print("%d %d %d %d" % (crit, high, mod, low))
|
|
|
1060
1120
|
' 2>/dev/null || echo "")"
|
|
1061
1121
|
if [ -n "$sev" ]; then
|
|
1062
1122
|
read -r _c _h _m _l <<<"$sev"
|
|
1123
|
+
# SHIPPED vs dev. The audit above covers ALL dependencies,
|
|
1124
|
+
# which is right for a gate -- a compromised build tool is a
|
|
1125
|
+
# real risk -- but the FINDING must say which of the two it
|
|
1126
|
+
# is. This repo reports 4 high CVEs while `npm audit
|
|
1127
|
+
# --omit=dev` reports ZERO: nothing a user installs is
|
|
1128
|
+
# vulnerable. A receipt that says "4 high severity
|
|
1129
|
+
# vulnerabilities" with no such qualifier reads as "the
|
|
1130
|
+
# shipped product is vulnerable", which is a materially
|
|
1131
|
+
# different and false claim.
|
|
1132
|
+
#
|
|
1133
|
+
# Measured separately rather than subtracted: the two runs
|
|
1134
|
+
# count different dependency graphs, so arithmetic on the
|
|
1135
|
+
# totals would not be sound.
|
|
1136
|
+
local _prod_hc=""
|
|
1137
|
+
_prod_hc="$(cd "$tree" && npm audit --omit=dev --json 2>/dev/null | python3 -c '
|
|
1138
|
+
import sys, json
|
|
1139
|
+
try:
|
|
1140
|
+
v = json.load(sys.stdin).get("metadata", {}).get("vulnerabilities", {})
|
|
1141
|
+
except Exception:
|
|
1142
|
+
sys.exit(0)
|
|
1143
|
+
print(v.get("critical", 0) + v.get("high", 0))
|
|
1144
|
+
' 2>/dev/null || echo "")"
|
|
1145
|
+
local _scope_note=""
|
|
1146
|
+
if [ "$_prod_hc" = "0" ]; then
|
|
1147
|
+
_scope_note=" All are in devDependencies; \`npm audit --omit=dev\` reports 0 high/critical, so nothing in the shipped dependency tree is affected."
|
|
1148
|
+
elif [ -n "$_prod_hc" ]; then
|
|
1149
|
+
_scope_note=" $_prod_hc of these are in the SHIPPED (non-dev) dependency tree."
|
|
1150
|
+
fi
|
|
1063
1151
|
if [ "$_c" -gt 0 ] || [ "$_h" -gt 0 ]; then
|
|
1064
|
-
_verify_add_gate "dependency_audit" "fail" "npm-audit" "$_c critical, $_h high CVEs" "true"
|
|
1152
|
+
_verify_add_gate "dependency_audit" "fail" "npm-audit" "$_c critical, $_h high CVEs (shipped high/critical: ${_prod_hc:-unmeasured})" "true"
|
|
1065
1153
|
_verify_add_finding "High" "dependencies" "deterministic:npm-audit" "package-lock.json" "null" \
|
|
1066
|
-
"npm audit found $_c critical and $_h high severity vulnerabilities
|
|
1154
|
+
"npm audit found $_c critical and $_h high severity vulnerabilities.${_scope_note}"
|
|
1067
1155
|
elif [ "$_m" -gt 0 ]; then
|
|
1068
1156
|
_verify_add_gate "dependency_audit" "fail" "npm-audit" "$_m moderate CVEs" "true"
|
|
1069
1157
|
_verify_add_finding "Medium" "dependencies" "deterministic:npm-audit" "package-lock.json" "null" \
|
package/completions/_loki
CHANGED
|
@@ -172,8 +172,6 @@ function _loki_commands {
|
|
|
172
172
|
'share:Share session report as GitHub Gist'
|
|
173
173
|
'proof:Inspect/share proof-of-run artifacts'
|
|
174
174
|
'outcomes:What happened to the work AFTER the receipt (reverted/reworked/survived)'
|
|
175
|
-
'verdict:The five measured trust signals in one readable block'
|
|
176
|
-
'readiness:Can an agent verify its own work in this repo? (measured, not LLM-scored)'
|
|
177
175
|
'preview:Preview the locally-running app'
|
|
178
176
|
'deploy:Deploy the built product (CI/CD-aware)'
|
|
179
177
|
'context:Context window management'
|
package/completions/loki.bash
CHANGED
|
@@ -5,7 +5,7 @@ _loki_completion() {
|
|
|
5
5
|
_init_completion || return
|
|
6
6
|
|
|
7
7
|
# Main subcommands (must match autonomy/loki main case statement)
|
|
8
|
-
local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes
|
|
8
|
+
local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
|
|
9
9
|
local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
|
|
10
10
|
|
|
11
11
|
# 1. If we are on the first argument (subcommand)
|