loki-mode 9.75.0 → 9.76.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/completion-council.sh +24 -6
- package/autonomy/lib/proof-check.sh +43 -8
- package/autonomy/lib/voter-agents.sh +42 -5
- package/autonomy/loki +3 -0
- package/autonomy/prd-checklist.sh +77 -16
- package/autonomy/run.sh +38 -3
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/v10/ARCHITECT-CUTS.md +359 -322
- package/docs/v10/BOARD.md +57 -37
- package/docs/v10/GUARDS.md +32 -21
- package/loki-ts/dist/loki.js +341 -338
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-CMGkmNnt.js → AdminPage-Bw8oPnII.js} +1 -1
- package/web-app/dist/assets/{Avatar-9mn8sJ-2.js → Avatar-BwEtc3RH.js} +1 -1
- package/web-app/dist/assets/{Badge-mSFLvvRX.js → Badge-C_-lfbUl.js} +1 -1
- package/web-app/dist/assets/{Button-B1v4CSkD.js → Button-BdDGKKeF.js} +1 -1
- package/web-app/dist/assets/CockpitPage-Dx9d4fEm.js +47 -0
- package/web-app/dist/assets/{ComparePage-C1BXuolM.js → ComparePage-BKtAhhIJ.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-C2sJyBhb.js → ErrorBoundary-BuG16GpG.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-a1irlBPD.js → EvidenceReceiptPanel-NATnqxKs.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-zM-WRxYB.js → GitHubIssuesPanel-BgOwdOpj.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-DvRa5Rxq.js → GitHubPRsPanel-CBJyHI1c.js} +1 -1
- package/web-app/dist/assets/{HomePage-CNwRexp5.js → HomePage-BHj17Epq.js} +2 -2
- package/web-app/dist/assets/{LoginPage-HKogNiEX.js → LoginPage-Dig4H-HO.js} +1 -1
- package/web-app/dist/assets/{MagicPage-u1mvdkUd.js → MagicPage-w6WJ35PH.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-CThdMJb5.js → MetricsPage-CvjSUOWn.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-B1v3lELS.js → NotFoundPage-PAcYmGkU.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-hDvSsdLF.js → ProjectPage-CBkT5JyW.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-CRAegSM6.js → ProjectsPage-Cs6yuWEU.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-Dt90bFxo.js → SettingsPage-BU9F40Yn.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-C5a9l8u_.js → ShowcasePage-jCNABTeT.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DtqsDXbx.js → SystemSettingsPage-Crmc-ryL.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-CooeoWxR.js → TeamsPage-DN8W-9m5.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CJhTxWrw.js → TemplatesPage-WvaXwZIl.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-DvjfbDxb.js → TerminalOutput-Bf_do2tO.js} +1 -1
- package/web-app/dist/assets/{activity-DK6KBQKA.js → activity-CMl9JvHl.js} +1 -1
- package/web-app/dist/assets/{bell-Cd8tQpN6.js → bell-CnjqTAUX.js} +1 -1
- package/web-app/dist/assets/{bot-Y30w8rML.js → bot-BBprDFVS.js} +1 -1
- package/web-app/dist/assets/{check-B5eUl1j9.js → check-DW9Hq4y4.js} +1 -1
- package/web-app/dist/assets/{chevron-left-qyvpFqze.js → chevron-left-DC9pMI2E.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DXtjVPDB.js → circle-alert-BJoll9YV.js} +1 -1
- package/web-app/dist/assets/{clock-dqQis2Q6.js → clock-BdHhRTHO.js} +1 -1
- package/web-app/dist/assets/{cloud-CzTURYO9.js → cloud-C54B7EO9.js} +1 -1
- package/web-app/dist/assets/{code-xml-DLbw1HTk.js → code-xml-DwQK2vpV.js} +1 -1
- package/web-app/dist/assets/{database-BIj38v85.js → database-DsWVFir7.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-9bVcHIkD.js → dollar-sign-DuRPNc2M.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-D57x6xD1.js → file-code-corner-C4j2P44-.js} +1 -1
- package/web-app/dist/assets/{file-plus-BXOrz3T5.js → file-plus-rnI2RZjF.js} +1 -1
- package/web-app/dist/assets/{globe-Cnq8h4Gt.js → globe-BbBhsaS5.js} +1 -1
- package/web-app/dist/assets/{hammer-iiLGlF28.js → hammer-BxcycH3E.js} +1 -1
- package/web-app/dist/assets/{index-uGGZ1Ij3.js → index-xGj3noKA.js} +2 -2
- package/web-app/dist/assets/{layers-gcGxxNEN.js → layers-CcAOhZeY.js} +1 -1
- package/web-app/dist/assets/{loader-circle-B4Ad11xq.js → loader-circle-DN_Xjd5e.js} +1 -1
- package/web-app/dist/assets/{lock-DvQNG5RI.js → lock-Dn8AMO-D.js} +1 -1
- package/web-app/dist/assets/{package-BtQBNbq2.js → package-uroTpCjn.js} +1 -1
- package/web-app/dist/assets/{plus-DKLiMXBZ.js → plus-CYJAEX02.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-B4Gu8e2m.js → refresh-cw-BUGsMChj.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-DATSPLYS.js → rotate-ccw-C_4D-XGl.js} +1 -1
- package/web-app/dist/assets/{scroll-text-GA8EtlY4.js → scroll-text-DrAJG0H3.js} +1 -1
- package/web-app/dist/assets/{server-DT_3kM4V.js → server-Di6lHsM9.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BEaqZEhO.js → shield-alert-DH1bNjkD.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DVvA-Fas.js → thumbs-up-Enk3ppj_.js} +1 -1
- package/web-app/dist/assets/{trash-2-BCyT5A9N.js → trash-2-CezqQT3x.js} +1 -1
- package/web-app/dist/assets/{trending-down-BLcNt39o.js → trending-down-B_ChI0U7.js} +1 -1
- package/web-app/dist/assets/{trending-up-BqfBp13A.js → trending-up-BtsvHd_-.js} +1 -1
- package/web-app/dist/assets/{upload-BxH0rjQU.js → upload-XiWa_WBM.js} +1 -1
- package/web-app/dist/assets/{usePolling-CokbBZIQ.js → usePolling-B6Hxwbk1.js} +1 -1
- package/web-app/dist/assets/{user-C_u9mNZM.js → user-oL_Oya-S.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/web-app/dist/assets/CockpitPage-BW0hYuzC.js +0 -47
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.76.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.76.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.
|
|
1
|
+
9.76.0
|
|
@@ -4368,19 +4368,34 @@ council_managed_should_stop() {
|
|
|
4368
4368
|
return 1
|
|
4369
4369
|
fi
|
|
4370
4370
|
|
|
4371
|
+
# S-196: every reader and the session heredoc run on the resolved -I -S
|
|
4372
|
+
# interpreter (see _loki_snapshot_py_tool); -E still loads user-site .pth
|
|
4373
|
+
# files. None resolvable -> never start the managed session: Bash fallback.
|
|
4374
|
+
# Deliberate consequence: -S hides site-packages, so the anthropic SDK is
|
|
4375
|
+
# never importable and is_enabled() is always False. The managed council
|
|
4376
|
+
# therefore always falls back to Bash voting (warned below). Reviving it
|
|
4377
|
+
# needs a pinned interpreter plus an SDK path that loads no .pth file;
|
|
4378
|
+
# a PATH or venv python3 without -S would let the agent forge the verdict.
|
|
4379
|
+
local _ms_py
|
|
4380
|
+
_ms_py="$(_loki_snapshot_py_tool)" || {
|
|
4381
|
+
log_warn "[Council] Managed completion council unavailable: no isolated -I -S interpreter; falling back to Bash voting"
|
|
4382
|
+
return 1
|
|
4383
|
+
}
|
|
4384
|
+
|
|
4371
4385
|
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
4372
|
-
local project_dir="${PROJECT_DIR:-$(pwd)}"
|
|
4373
4386
|
local round="${ITERATION_COUNT:-0}"
|
|
4374
4387
|
local verdicts_dir="$COUNCIL_STATE_DIR/verdicts"
|
|
4375
4388
|
mkdir -p "$verdicts_dir" 2>/dev/null || true
|
|
4376
4389
|
|
|
4377
4390
|
# Build session context: diff_summary, test_summary, pending_tasks.
|
|
4378
4391
|
# Kept deliberately small so we never choke the session budget.
|
|
4392
|
+
# S-196: diff the target project (same root as loki_dir and
|
|
4393
|
+
# LOKI_TARGET_DIR), never PROJECT_DIR, which is Loki's install tree.
|
|
4379
4394
|
local diff_summary=""
|
|
4380
|
-
diff_summary=$(cd "$
|
|
4395
|
+
diff_summary=$(cd "${TARGET_DIR:-.}" 2>/dev/null && git diff --stat 2>/dev/null | tail -20 | tr '\n' ' ' || echo "")
|
|
4381
4396
|
local test_summary=""
|
|
4382
4397
|
if [ -f "$loki_dir/quality/test-results.json" ]; then
|
|
4383
|
-
test_summary=$(_TRF="$loki_dir/quality/test-results.json"
|
|
4398
|
+
test_summary=$(_TRF="$loki_dir/quality/test-results.json" "$_ms_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
4384
4399
|
import json, os
|
|
4385
4400
|
try:
|
|
4386
4401
|
d = json.load(open(os.environ['_TRF']))
|
|
@@ -4391,7 +4406,7 @@ except Exception:
|
|
|
4391
4406
|
fi
|
|
4392
4407
|
local pending_tasks="[]"
|
|
4393
4408
|
if [ -f "$loki_dir/queue/pending.json" ]; then
|
|
4394
|
-
pending_tasks=$(_QF="$loki_dir/queue/pending.json"
|
|
4409
|
+
pending_tasks=$(_QF="$loki_dir/queue/pending.json" "$_ms_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
4395
4410
|
import json, os
|
|
4396
4411
|
try:
|
|
4397
4412
|
d = json.load(open(os.environ['_QF']))
|
|
@@ -4415,7 +4430,7 @@ except Exception:
|
|
|
4415
4430
|
_CC_LOKI_DIR="$loki_dir" \
|
|
4416
4431
|
LOKI_TARGET_DIR="${TARGET_DIR:-$(pwd)}" \
|
|
4417
4432
|
PROJECT_DIR="${PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)}" \
|
|
4418
|
-
|
|
4433
|
+
"$_ms_py" -I -S - <<'PYEOF' 2>/dev/null || exit_code=$?
|
|
4419
4434
|
import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
4420
4435
|
import json, os, sys, pathlib
|
|
4421
4436
|
|
|
@@ -4553,7 +4568,10 @@ PYEOF
|
|
|
4553
4568
|
return 0
|
|
4554
4569
|
fi
|
|
4555
4570
|
|
|
4556
|
-
|
|
4571
|
+
case "$exit_code" in
|
|
4572
|
+
2|3) log_warn "[Council] Managed completion council unavailable (exit=$exit_code): SDK not importable under the isolated -I -S interpreter; falling back to Bash voting" ;;
|
|
4573
|
+
*) log_warn "[Council] Managed completion council unavailable (exit=$exit_code); falling back to Bash voting" ;;
|
|
4574
|
+
esac
|
|
4557
4575
|
return 1
|
|
4558
4576
|
}
|
|
4559
4577
|
|
|
@@ -28,9 +28,41 @@
|
|
|
28
28
|
# guards on LOKI_PROVEN_PR_CHECK=1). It is also safe if called directly.
|
|
29
29
|
#
|
|
30
30
|
# Inline Python (D7): the call site's cwd is the agent's repo, so the proof
|
|
31
|
-
# readers
|
|
32
|
-
#
|
|
31
|
+
# readers drop '' and '.' from sys.path first; a committed json.py or
|
|
32
|
+
# sitecustomize.py cannot change the headline. Pinned by
|
|
33
33
|
# tests/test-proven-pr-check.sh (D7 section).
|
|
34
|
+
#
|
|
35
|
+
# BACKLOG 54 / S-203: python3 -E still loaded the user site-packages, where a
|
|
36
|
+
# .pth "import" line runs before any sys.path scrub and could forge json.load
|
|
37
|
+
# (a NOT VERIFIED proof read as VERIFIED). The readers resolve their
|
|
38
|
+
# interpreter through _loki_snapshot_py_tool and run it -I -S; when none
|
|
39
|
+
# resolves they print empty, as they do when python3 is absent. run.sh sources
|
|
40
|
+
# this file before it defines _loki_snapshot_py_tool, and its later definition
|
|
41
|
+
# replaces this copy, so the copy below only serves callers that source this
|
|
42
|
+
# file on its own (tests). run.sh is the source of truth: keep this copy
|
|
43
|
+
# byte-identical to it. Pinned by tests/test-proof-check-no-user-site-pth.sh
|
|
44
|
+
# and tests/test-council-py-tool-identity.sh.
|
|
45
|
+
declare -F _loki_snapshot_py_tool >/dev/null 2>&1 || \
|
|
46
|
+
_loki_snapshot_py_tool() {
|
|
47
|
+
local c
|
|
48
|
+
for c in /usr/bin/python3 /bin/python3; do
|
|
49
|
+
[ -x "$c" ] && [ ! -d "$c" ] && "$c" -I -S -c '' >/dev/null 2>&1 && { printf '%s\n' "$c"; return 0; }
|
|
50
|
+
done
|
|
51
|
+
local dir
|
|
52
|
+
local IFS=:
|
|
53
|
+
for dir in $PATH; do
|
|
54
|
+
case "$dir" in
|
|
55
|
+
/*) ;;
|
|
56
|
+
*) continue ;;
|
|
57
|
+
esac
|
|
58
|
+
if [ -x "$dir/python3" ] && [ ! -d "$dir/python3" ] \
|
|
59
|
+
&& "$dir/python3" -I -S -c '' >/dev/null 2>&1; then
|
|
60
|
+
printf '%s\n' "$dir/python3"
|
|
61
|
+
return 0
|
|
62
|
+
fi
|
|
63
|
+
done
|
|
64
|
+
return 1
|
|
65
|
+
}
|
|
34
66
|
|
|
35
67
|
# Double-source guard.
|
|
36
68
|
[ -n "${_PROOF_CHECK_SH:-}" ] && return 0
|
|
@@ -71,10 +103,11 @@ _proof_check_headline() {
|
|
|
71
103
|
local proof_path="${1:-}"
|
|
72
104
|
[ -n "$proof_path" ] || { printf '%s' ""; return 0; }
|
|
73
105
|
[ -f "$proof_path" ] || { printf '%s' ""; return 0; }
|
|
74
|
-
|
|
106
|
+
local _pc_py=""
|
|
107
|
+
_pc_py="$(_loki_snapshot_py_tool)" || { printf '%s' ""; return 0; }
|
|
75
108
|
|
|
76
109
|
local headline=""
|
|
77
|
-
headline="$(
|
|
110
|
+
headline="$("$_pc_py" -I -S - "$proof_path" <<'PY' 2>/dev/null || true
|
|
78
111
|
import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
79
112
|
import json, sys
|
|
80
113
|
try:
|
|
@@ -103,10 +136,11 @@ _proof_check_proof_head_sha() {
|
|
|
103
136
|
local proof_path="${1:-}"
|
|
104
137
|
[ -n "$proof_path" ] || { printf '%s' ""; return 0; }
|
|
105
138
|
[ -f "$proof_path" ] || { printf '%s' ""; return 0; }
|
|
106
|
-
|
|
139
|
+
local _pc_py=""
|
|
140
|
+
_pc_py="$(_loki_snapshot_py_tool)" || { printf '%s' ""; return 0; }
|
|
107
141
|
|
|
108
142
|
local sha=""
|
|
109
|
-
sha="$(
|
|
143
|
+
sha="$("$_pc_py" -I -S - "$proof_path" <<'PY' 2>/dev/null || true
|
|
110
144
|
import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
111
145
|
import json, sys
|
|
112
146
|
try:
|
|
@@ -138,10 +172,11 @@ _proof_check_run_id() {
|
|
|
138
172
|
local proof_path="${1:-}"
|
|
139
173
|
[ -n "$proof_path" ] || { printf '%s' ""; return 0; }
|
|
140
174
|
[ -f "$proof_path" ] || { printf '%s' ""; return 0; }
|
|
141
|
-
|
|
175
|
+
local _pc_py=""
|
|
176
|
+
_pc_py="$(_loki_snapshot_py_tool)" || { printf '%s' ""; return 0; }
|
|
142
177
|
|
|
143
178
|
local rid=""
|
|
144
|
-
rid="$(
|
|
179
|
+
rid="$("$_pc_py" -I -S - "$proof_path" <<'PY' 2>/dev/null || true
|
|
145
180
|
import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
146
181
|
import json, sys
|
|
147
182
|
try:
|
|
@@ -28,9 +28,18 @@
|
|
|
28
28
|
# to existing heuristic dispatch).
|
|
29
29
|
#
|
|
30
30
|
# Inline Python (D7): the dispatch runs with the cwd inside the agent's repo, so
|
|
31
|
-
# every python3 here
|
|
32
|
-
#
|
|
31
|
+
# every python3 here drops '' and '.' from sys.path before any other import; a
|
|
32
|
+
# committed json.py or sitecustomize.py cannot read the votes.
|
|
33
33
|
# Pinned by tests/test-voter-agents-json.sh (Case D).
|
|
34
|
+
#
|
|
35
|
+
# S-202 (BACKLOG 54): -E alone still loads the user site-packages, whose .pth
|
|
36
|
+
# "import" lines run before that scrub and can forge json.loads (3 REJECT
|
|
37
|
+
# findings read as a COMPLETE round). Every call site resolves its interpreter
|
|
38
|
+
# through _loki_snapshot_py_tool and runs it -I -S; when none resolves, the
|
|
39
|
+
# dispatch returns 1 and completion-council's fail-closed path applies. run.sh
|
|
40
|
+
# is the source of truth for the helper; the guarded copy below only serves
|
|
41
|
+
# callers that source this file on its own. Keep it byte-identical to run.sh's.
|
|
42
|
+
# Pinned by tests/test-voter-agents-no-user-site-pth.sh.
|
|
34
43
|
|
|
35
44
|
# Guard against double-source.
|
|
36
45
|
if [ "${__LOKI_VOTER_AGENTS_SH_LOADED:-0}" = "1" ]; then
|
|
@@ -38,6 +47,28 @@ if [ "${__LOKI_VOTER_AGENTS_SH_LOADED:-0}" = "1" ]; then
|
|
|
38
47
|
fi
|
|
39
48
|
__LOKI_VOTER_AGENTS_SH_LOADED=1
|
|
40
49
|
|
|
50
|
+
declare -F _loki_snapshot_py_tool >/dev/null 2>&1 || \
|
|
51
|
+
_loki_snapshot_py_tool() {
|
|
52
|
+
local c
|
|
53
|
+
for c in /usr/bin/python3 /bin/python3; do
|
|
54
|
+
[ -x "$c" ] && [ ! -d "$c" ] && "$c" -I -S -c '' >/dev/null 2>&1 && { printf '%s\n' "$c"; return 0; }
|
|
55
|
+
done
|
|
56
|
+
local dir
|
|
57
|
+
local IFS=:
|
|
58
|
+
for dir in $PATH; do
|
|
59
|
+
case "$dir" in
|
|
60
|
+
/*) ;;
|
|
61
|
+
*) continue ;;
|
|
62
|
+
esac
|
|
63
|
+
if [ -x "$dir/python3" ] && [ ! -d "$dir/python3" ] \
|
|
64
|
+
&& "$dir/python3" -I -S -c '' >/dev/null 2>&1; then
|
|
65
|
+
printf '%s\n' "$dir/python3"
|
|
66
|
+
return 0
|
|
67
|
+
fi
|
|
68
|
+
done
|
|
69
|
+
return 1
|
|
70
|
+
}
|
|
71
|
+
|
|
41
72
|
# Resolve repo root once. This file lives at autonomy/lib/voter-agents.sh.
|
|
42
73
|
__LOKI_VA_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
|
|
43
74
|
__LOKI_VA_REPO_ROOT="$(cd "${__LOKI_VA_LIB_DIR}/../.." 2>/dev/null && pwd)"
|
|
@@ -59,12 +90,14 @@ loki_voter_agents_json() {
|
|
|
59
90
|
fi
|
|
60
91
|
local prd="${LOKI_PRD_PATH:-}"
|
|
61
92
|
local tier="${LOKI_RARV_TIER:-development}"
|
|
93
|
+
local _va_py
|
|
94
|
+
_va_py="$(_loki_snapshot_py_tool)" || { printf '%s' '{}'; return 0; }
|
|
62
95
|
|
|
63
96
|
_VA_ITER="$iter" \
|
|
64
97
|
_VA_PRD="$prd" \
|
|
65
98
|
_VA_TIER="$tier" \
|
|
66
99
|
_VA_COMPLEXITY="${LOKI_COMPLEXITY:-standard}" \
|
|
67
|
-
|
|
100
|
+
"$_va_py" -I -S -c 'import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
68
101
|
import json, os
|
|
69
102
|
iter_n = os.environ.get("_VA_ITER", "0")
|
|
70
103
|
prd = os.environ.get("_VA_PRD", "")
|
|
@@ -133,7 +166,9 @@ print(json.dumps(agents, separators=(",", ":")))
|
|
|
133
166
|
# Arg $1: terse base findings summary used to brief the DA prompt.
|
|
134
167
|
loki_devils_advocate_json() {
|
|
135
168
|
local summary="${1:-}"
|
|
136
|
-
|
|
169
|
+
local _va_py
|
|
170
|
+
_va_py="$(_loki_snapshot_py_tool)" || { printf '%s' '{}'; return 0; }
|
|
171
|
+
_VA_SUMMARY="$summary" "$_va_py" -I -S -c 'import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
137
172
|
import json, os
|
|
138
173
|
summary = os.environ.get("_VA_SUMMARY", "")[:1000]
|
|
139
174
|
agents = {
|
|
@@ -355,13 +390,15 @@ loki_council_dispatch_agents() {
|
|
|
355
390
|
local verdicts_dir="$COUNCIL_STATE_DIR/verdicts"
|
|
356
391
|
local votes_dir="$COUNCIL_STATE_DIR/votes"
|
|
357
392
|
mkdir -p "$verdicts_dir" "$votes_dir" 2>/dev/null || return 1
|
|
393
|
+
local _va_py
|
|
394
|
+
_va_py="$(_loki_snapshot_py_tool)" || return 1
|
|
358
395
|
|
|
359
396
|
_VA_RESP="$response" \
|
|
360
397
|
_VA_ITER="$iteration" \
|
|
361
398
|
_VA_VDIR="$verdicts_dir" \
|
|
362
399
|
_VA_RFILE="$votes_dir/round-${iteration}.json" \
|
|
363
400
|
_VA_EXPECTED="${COUNCIL_SIZE:-3}" \
|
|
364
|
-
|
|
401
|
+
"$_va_py" -I -S -c 'import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
365
402
|
import json, os, sys
|
|
366
403
|
from datetime import datetime, timezone
|
|
367
404
|
|
package/autonomy/loki
CHANGED
|
@@ -1271,6 +1271,8 @@ loki_remote_verify_receipt() {
|
|
|
1271
1271
|
echo " the JWKS URL is not https -- only loopback is allowed over plain http)."
|
|
1272
1272
|
echo " Its integrity hash matches. Provenance is unproven here, not disproven."
|
|
1273
1273
|
echo " Install python3 cryptography, or re-run where the build server is reachable."
|
|
1274
|
+
echo " The verifier runs python3 -E, so cryptography supplied through PYTHONPATH is"
|
|
1275
|
+
echo " not seen; install it where the base interpreter finds it (site or user site)."
|
|
1274
1276
|
return 0
|
|
1275
1277
|
fi
|
|
1276
1278
|
|
|
@@ -37143,6 +37145,7 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
|
|
|
37143
37145
|
# verifier is a finding and is never softened to 2.
|
|
37144
37146
|
echo -e "${YELLOW}attestation: NOT CHECKED -- could not check against ${_pv_jwks} (key set unreadable,${NC}" >&2
|
|
37145
37147
|
echo -e "${YELLOW} verifier dependency missing, or the JWKS URL is not https -- only loopback is allowed over plain http).${NC}" >&2
|
|
37148
|
+
echo " The verifier runs python3 -E, so cryptography supplied through PYTHONPATH is not seen (a site or user-site install is)." >&2
|
|
37146
37149
|
echo " This is not a failure of the receipt. Nothing was proven either way (exit 2)." >&2
|
|
37147
37150
|
if [ "$_pv_rc" -eq 0 ]; then _pv_rc=2; fi
|
|
37148
37151
|
_pv_attestation_field="NOT CHECKED"
|
|
@@ -19,12 +19,23 @@
|
|
|
19
19
|
# LOKI_CHECKLIST_ENABLED - Enable/disable checklist (default: true)
|
|
20
20
|
#
|
|
21
21
|
# Inline Python (D7): verification runs with the cwd inside the agent's repo,
|
|
22
|
-
# so every python3 here
|
|
23
|
-
# committed sitecustomize.py) and every -c / - body drops '' and
|
|
24
|
-
# sys.path before any other import (a committed json.py would print
|
|
25
|
-
# verdict). Pinned by tests/moat/p2-honest-verdict.sh
|
|
22
|
+
# so every python3 here ignores PYTHON* env vars (an empty PYTHONPATH component
|
|
23
|
+
# would load a committed sitecustomize.py) and every -c / - body drops '' and
|
|
24
|
+
# '.' from sys.path before any other import (a committed json.py would print
|
|
25
|
+
# the verdict). Pinned by tests/moat/p2-honest-verdict.sh
|
|
26
26
|
# P2.checklist-verify-not-shadowed.
|
|
27
27
|
#
|
|
28
|
+
# S-204 (BACKLOG 53): -E alone still loads the user site-packages, whose .pth
|
|
29
|
+
# "import" lines run at startup and can patch json.dump, so checklist-verify.py
|
|
30
|
+
# wrote failing checks as verified into the results file the council hard gate
|
|
31
|
+
# trusts. Every call site resolves its interpreter through
|
|
32
|
+
# _loki_snapshot_py_tool and runs it -I -S; when none resolves, nothing is
|
|
33
|
+
# verified or summarized (the hard gate then blocks fail-closed). See that
|
|
34
|
+
# function's comment in run.sh for the rationale and accepted gaps. The copy
|
|
35
|
+
# below only serves callers that source this file before or without run.sh
|
|
36
|
+
# (tests); run.sh is the source of truth, keep it byte-identical. Pinned by
|
|
37
|
+
# tests/test-prd-checklist-no-user-site-pth.sh.
|
|
38
|
+
#
|
|
28
39
|
# Data:
|
|
29
40
|
# .loki/checklist/checklist.json - Full checklist with verification
|
|
30
41
|
# .loki/checklist/verification-results.json - Summary of last verification
|
|
@@ -37,6 +48,28 @@
|
|
|
37
48
|
#
|
|
38
49
|
#===============================================================================
|
|
39
50
|
|
|
51
|
+
declare -F _loki_snapshot_py_tool >/dev/null 2>&1 || \
|
|
52
|
+
_loki_snapshot_py_tool() {
|
|
53
|
+
local c
|
|
54
|
+
for c in /usr/bin/python3 /bin/python3; do
|
|
55
|
+
[ -x "$c" ] && [ ! -d "$c" ] && "$c" -I -S -c '' >/dev/null 2>&1 && { printf '%s\n' "$c"; return 0; }
|
|
56
|
+
done
|
|
57
|
+
local dir
|
|
58
|
+
local IFS=:
|
|
59
|
+
for dir in $PATH; do
|
|
60
|
+
case "$dir" in
|
|
61
|
+
/*) ;;
|
|
62
|
+
*) continue ;;
|
|
63
|
+
esac
|
|
64
|
+
if [ -x "$dir/python3" ] && [ ! -d "$dir/python3" ] \
|
|
65
|
+
&& "$dir/python3" -I -S -c '' >/dev/null 2>&1; then
|
|
66
|
+
printf '%s\n' "$dir/python3"
|
|
67
|
+
return 0
|
|
68
|
+
fi
|
|
69
|
+
done
|
|
70
|
+
return 1
|
|
71
|
+
}
|
|
72
|
+
|
|
40
73
|
# Configuration
|
|
41
74
|
CHECKLIST_ENABLED=${LOKI_CHECKLIST_ENABLED:-true}
|
|
42
75
|
CHECKLIST_INTERVAL=${LOKI_CHECKLIST_INTERVAL:-5}
|
|
@@ -200,12 +233,14 @@ checklist_oracle_triangulate() {
|
|
|
200
233
|
local status_token="NOOP"
|
|
201
234
|
local status_file=""
|
|
202
235
|
local status_tmp_root="${TMPDIR:-/tmp}"
|
|
236
|
+
local _cl_py
|
|
237
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 0
|
|
203
238
|
if status_file="$(mktemp "${status_tmp_root%/}/loki-oracle-status.XXXXXX" 2>/dev/null)"; then
|
|
204
239
|
if _ORACLE_SPEC="$CHECKLIST_PRD_PATH" \
|
|
205
240
|
_ORACLE_OUT="$findings_file" \
|
|
206
241
|
_ORACLE_PROJECT="$project_dir" \
|
|
207
242
|
_ORACLE_INSTALL_DIR="$oracle_install_dir" \
|
|
208
|
-
|
|
243
|
+
"$_cl_py" -I -S - > "$status_file" 2>/dev/null <<'ORACLE_PY'
|
|
209
244
|
import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
210
245
|
import json, os, re, sys, tempfile, glob
|
|
211
246
|
|
|
@@ -701,7 +736,9 @@ checklist_oracle_evidence() {
|
|
|
701
736
|
if [ ! -f "$findings_file" ]; then
|
|
702
737
|
return 0
|
|
703
738
|
fi
|
|
704
|
-
|
|
739
|
+
local _cl_py
|
|
740
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 0
|
|
741
|
+
_ORACLE_OUT="$findings_file" "$_cl_py" -I -S -c 'import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
705
742
|
import json, os
|
|
706
743
|
try:
|
|
707
744
|
with open(os.environ["_ORACLE_OUT"]) as f:
|
|
@@ -804,8 +841,9 @@ checklist_select_heldout() {
|
|
|
804
841
|
# Honest caveat: re-selection or partial-survival after a regen can reserve
|
|
805
842
|
# items the build loop already saw in earlier prompts (the hidden-from-loop
|
|
806
843
|
# guarantee is best-effort once the checklist ids change mid-run).
|
|
807
|
-
local status_token
|
|
808
|
-
|
|
844
|
+
local status_token _cl_py
|
|
845
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 0
|
|
846
|
+
status_token=$(_CHECKLIST_FILE="$CHECKLIST_FILE" _HELDOUT_FILE="$heldout_file" "$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
809
847
|
import json, os, sys, hashlib, tempfile
|
|
810
848
|
|
|
811
849
|
cl_path = os.environ['_CHECKLIST_FILE']
|
|
@@ -953,7 +991,9 @@ checklist_heldout_ids() {
|
|
|
953
991
|
if [ ! -f "$heldout_file" ]; then
|
|
954
992
|
return 0
|
|
955
993
|
fi
|
|
956
|
-
|
|
994
|
+
local _cl_py
|
|
995
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 0
|
|
996
|
+
_HELDOUT_FILE="$heldout_file" "$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
957
997
|
import json, os
|
|
958
998
|
try:
|
|
959
999
|
with open(os.environ['_HELDOUT_FILE']) as f:
|
|
@@ -997,9 +1037,17 @@ checklist_verify() {
|
|
|
997
1037
|
return 0
|
|
998
1038
|
fi
|
|
999
1039
|
|
|
1040
|
+
# Non-zero so council_reverify_checklist replaces any stale green results
|
|
1041
|
+
# with its fail-closed sentinel (run.sh's interval call ignores the status).
|
|
1042
|
+
local _cl_py
|
|
1043
|
+
if ! _cl_py="$(_loki_snapshot_py_tool)"; then
|
|
1044
|
+
log_warn "checklist verification skipped: no isolated python3 interpreter resolved"
|
|
1045
|
+
return 1
|
|
1046
|
+
fi
|
|
1047
|
+
|
|
1000
1048
|
log_step "Running PRD checklist verification..."
|
|
1001
1049
|
|
|
1002
|
-
|
|
1050
|
+
"$_cl_py" -I -S "$verify_script" \
|
|
1003
1051
|
--checklist "$CHECKLIST_FILE" \
|
|
1004
1052
|
--timeout "$CHECKLIST_TIMEOUT" 2>/dev/null || true
|
|
1005
1053
|
|
|
@@ -1027,11 +1075,13 @@ checklist_summary() {
|
|
|
1027
1075
|
echo ""
|
|
1028
1076
|
return 0
|
|
1029
1077
|
fi
|
|
1078
|
+
local _cl_py
|
|
1079
|
+
_cl_py="$(_loki_snapshot_py_tool)" || { echo ""; return 0; }
|
|
1030
1080
|
|
|
1031
1081
|
_CHECKLIST_RESULTS="$CHECKLIST_RESULTS_FILE" \
|
|
1032
1082
|
_CHECKLIST_WAIVERS="${CHECKLIST_DIR:-".loki/checklist"}/waivers.json" \
|
|
1033
1083
|
_CHECKLIST_HELDOUT="${CHECKLIST_DIR:-".loki/checklist"}/held-out.json" \
|
|
1034
|
-
|
|
1084
|
+
"$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
1035
1085
|
import json, sys, os
|
|
1036
1086
|
try:
|
|
1037
1087
|
fpath = os.environ.get('_CHECKLIST_RESULTS', '')
|
|
@@ -1128,13 +1178,16 @@ checklist_as_evidence() {
|
|
|
1128
1178
|
if [ ! -f "$CHECKLIST_RESULTS_FILE" ]; then
|
|
1129
1179
|
return 0
|
|
1130
1180
|
fi
|
|
1181
|
+
local _cl_py
|
|
1182
|
+
# No interpreter: the failed exec falls to "Checklist data unavailable" below.
|
|
1183
|
+
_cl_py="$(_loki_snapshot_py_tool)" || _cl_py="/nonexistent/python3"
|
|
1131
1184
|
|
|
1132
1185
|
{
|
|
1133
1186
|
echo ""
|
|
1134
1187
|
echo "## PRD Checklist Verification"
|
|
1135
1188
|
echo ""
|
|
1136
1189
|
|
|
1137
|
-
_CHECKLIST_RESULTS="$CHECKLIST_RESULTS_FILE"
|
|
1190
|
+
_CHECKLIST_RESULTS="$CHECKLIST_RESULTS_FILE" "$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
1138
1191
|
import json, os
|
|
1139
1192
|
try:
|
|
1140
1193
|
data = json.load(open(os.environ['_CHECKLIST_RESULTS']))
|
|
@@ -1170,7 +1223,9 @@ checklist_waiver_load() {
|
|
|
1170
1223
|
if [ ! -f "$waivers_file" ]; then
|
|
1171
1224
|
return 0
|
|
1172
1225
|
fi
|
|
1173
|
-
|
|
1226
|
+
local _cl_py
|
|
1227
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 0
|
|
1228
|
+
_WAIVERS_FILE="$waivers_file" "$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
1174
1229
|
import json, sys, os
|
|
1175
1230
|
try:
|
|
1176
1231
|
waivers_file = os.environ['_WAIVERS_FILE']
|
|
@@ -1193,8 +1248,10 @@ checklist_waiver_add() {
|
|
|
1193
1248
|
local waivers_file="${CHECKLIST_DIR:-".loki/checklist"}/waivers.json"
|
|
1194
1249
|
local timestamp
|
|
1195
1250
|
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
1251
|
+
local _cl_py
|
|
1252
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 1
|
|
1196
1253
|
|
|
1197
|
-
_WAIVERS_FILE="$waivers_file"
|
|
1254
|
+
_WAIVERS_FILE="$waivers_file" "$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
1198
1255
|
import json, os, sys
|
|
1199
1256
|
|
|
1200
1257
|
waivers_file = os.environ['_WAIVERS_FILE']
|
|
@@ -1246,8 +1303,10 @@ checklist_waiver_remove() {
|
|
|
1246
1303
|
echo "No waivers file found"
|
|
1247
1304
|
return 1
|
|
1248
1305
|
fi
|
|
1306
|
+
local _cl_py
|
|
1307
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 1
|
|
1249
1308
|
|
|
1250
|
-
_WAIVERS_FILE="$waivers_file"
|
|
1309
|
+
_WAIVERS_FILE="$waivers_file" "$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
1251
1310
|
import json, os, sys
|
|
1252
1311
|
|
|
1253
1312
|
waivers_file = os.environ['_WAIVERS_FILE']
|
|
@@ -1282,8 +1341,10 @@ checklist_waiver_list() {
|
|
|
1282
1341
|
echo "No waivers configured"
|
|
1283
1342
|
return 0
|
|
1284
1343
|
fi
|
|
1344
|
+
local _cl_py
|
|
1345
|
+
_cl_py="$(_loki_snapshot_py_tool)" || return 1
|
|
1285
1346
|
|
|
1286
|
-
_WAIVERS_FILE="$waivers_file"
|
|
1347
|
+
_WAIVERS_FILE="$waivers_file" "$_cl_py" -I -S -c "import sys; sys.path[:] = [p for p in sys.path if p not in ('', '.')]
|
|
1287
1348
|
import json, os
|
|
1288
1349
|
waivers_file = os.environ['_WAIVERS_FILE']
|
|
1289
1350
|
with open(waivers_file) as f:
|
package/autonomy/run.sh
CHANGED
|
@@ -10949,9 +10949,26 @@ _loki_untrack_agent_committed_user_files() {
|
|
|
10949
10949
|
fi
|
|
10950
10950
|
rm -f "$rec.added"
|
|
10951
10951
|
if [ ! -s "$rec.new" ]; then
|
|
10952
|
-
|
|
10953
|
-
#
|
|
10954
|
-
|
|
10952
|
+
rm -f "$rec.new"
|
|
10953
|
+
# S-194: the tree diff above cannot see a file an earlier (resumed)
|
|
10954
|
+
# session added and then untracked, but the branch history still
|
|
10955
|
+
# holds it and create_session_pr gates the push on this record. Keep
|
|
10956
|
+
# the record while that history holds any recorded path; on a failed
|
|
10957
|
+
# check, keep it too (fail closed).
|
|
10958
|
+
if [ -s "$rec" ]; then
|
|
10959
|
+
local range="${fork}..HEAD" held_paths=() p="" hist=""
|
|
10960
|
+
[ "$(git cat-file -t "$fork" 2>/dev/null)" = tree ] && range="HEAD"
|
|
10961
|
+
while IFS= read -r -d '' p; do held_paths+=("$p"); done < "$rec"
|
|
10962
|
+
if [ "${#held_paths[@]}" -eq 0 ] \
|
|
10963
|
+
|| ! hist="$(git -C "$top" --literal-pathspecs log -1 --format=%H --no-renames --diff-filter=A "$range" -- "${held_paths[@]}" 2>/dev/null)" \
|
|
10964
|
+
|| [ -n "$hist" ]; then
|
|
10965
|
+
return 0
|
|
10966
|
+
fi
|
|
10967
|
+
fi
|
|
10968
|
+
# BACKLOG 102: zero hits this session and no recorded path left in the
|
|
10969
|
+
# branch history -- clear a stale record left by an earlier session,
|
|
10970
|
+
# or it persists forever with nothing to read it.
|
|
10971
|
+
rm -f "$rec"
|
|
10955
10972
|
return 0
|
|
10956
10973
|
fi
|
|
10957
10974
|
mv -f "$rec.new" "$rec"
|
|
@@ -11198,6 +11215,24 @@ create_session_pr() {
|
|
|
11198
11215
|
return 0
|
|
11199
11216
|
fi
|
|
11200
11217
|
|
|
11218
|
+
# BACKLOG 108: _loki_untrack_agent_committed_user_files removed the user's
|
|
11219
|
+
# pre-existing files from the branch tip, but the commit that added them is
|
|
11220
|
+
# still in the branch history, so a push publishes them. Print the cleanup
|
|
11221
|
+
# before any push advice, and refuse the LOKI_AUTO_PR push. The fork is the
|
|
11222
|
+
# merge base with the recorded base, as that helper computes it; its
|
|
11223
|
+
# session-start-sha fallback cannot apply here, because without a merge
|
|
11224
|
+
# base commit_count is 0 and this function already returned.
|
|
11225
|
+
if [ -s .loki/state/agent-committed-user-files.z ]; then
|
|
11226
|
+
local _uc_fork="" _uc_names=""
|
|
11227
|
+
_uc_fork="$(git merge-base HEAD "$base" 2>/dev/null)" || _uc_fork=""
|
|
11228
|
+
_uc_names="$(_loki_nul_names .loki/state/agent-committed-user-files.z)"
|
|
11229
|
+
log_warn "The history of ${branch_name} still holds your pre-existing files the agent committed: ${_uc_names:-see .loki/state/agent-committed-user-files.z}. Before pushing, drop them from its history: git reset --soft ${_uc_fork:-<fork commit>} && git commit"
|
|
11230
|
+
if [ "${LOKI_AUTO_PR:-0}" = "1" ]; then
|
|
11231
|
+
log_warn "LOKI_AUTO_PR: not pushing ${branch_name}; its history holds your pre-existing files."
|
|
11232
|
+
return 1
|
|
11233
|
+
fi
|
|
11234
|
+
fi
|
|
11235
|
+
|
|
11201
11236
|
# DEFAULT: advisory only. Print the exact commands; never push, never PR.
|
|
11202
11237
|
if [ "${LOKI_AUTO_PR:-0}" != "1" ]; then
|
|
11203
11238
|
if declare -f print_pr_advice >/dev/null 2>&1; then
|
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.76.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|