loki-mode 11.4.2 → 11.5.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/chat.sh +6 -3
- package/autonomy/lib/engine10-push.sh +19 -1
- package/autonomy/lib/help-core.sh +1 -1
- package/autonomy/lib/proof-generator.py +23 -5
- package/autonomy/lib/proof-verify.py +607 -5
- package/autonomy/lib/run-summary.sh +17 -1
- package/autonomy/lib/setup-requirements.json +35 -0
- package/autonomy/loki +232 -36
- package/autonomy/provider-offer.sh +95 -47
- package/autonomy/route.sh +38 -6
- package/autonomy/run.sh +42 -14
- package/autonomy/setup-wizard.sh +19 -10
- package/autonomy/verify.sh +40 -1
- package/completions/_loki +3 -1
- package/completions/loki.bash +9 -3
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +3 -1
- package/docs/EVALUATING.md +27 -0
- package/docs/HARNESS-INTELLIGENCE-PLAN.md +20 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/KNOWN-ISSUES.md +10 -1
- package/docs/PROOF-PASSPORT.md +114 -0
- package/docs/alternative-installations.md +103 -0
- package/docs/cost-controls.md +139 -0
- package/docs/environment-variables.md +36 -0
- package/docs/exit-codes.md +102 -2
- package/docs/spec-first-intent.md +29 -0
- package/docs/v10/CLI-MODERN.md +2 -2
- package/docs/v10/ENGINE.md +32 -0
- package/loki-ts/data/model-pricing.json +1 -0
- package/loki-ts/dist/loki.js +1095 -1043
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +1721 -707
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/plugins/loki-mode/README.md +41 -1
- package/plugins/loki-mode/commands/handoff.md +12 -0
- package/plugins/loki-mode/commands/loki-verify.md +9 -2
- package/plugins/loki-mode/commands/start.md +20 -0
- package/plugins/loki-mode/hooks/hooks.json +11 -0
- package/plugins/loki-mode/scripts/loki-stop-verify.sh +84 -0
- package/web-app/dist/assets/{AdminPage-CYJ2BWRC.js → AdminPage-Cbu3pXL8.js} +1 -1
- package/web-app/dist/assets/{Avatar-DdK4wynI.js → Avatar-DGqGGcGx.js} +1 -1
- package/web-app/dist/assets/{Badge-CrX3MI7J.js → Badge-Bhlipt6v.js} +1 -1
- package/web-app/dist/assets/{Button-709-wwGM.js → Button-CpN2sO61.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BapdTInO.js → CockpitPage-BqeDJwdL.js} +1 -1
- package/web-app/dist/assets/{ComparePage-DT99pGvA.js → ComparePage-DUieA2ba.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-B0uJSys8.js → ErrorBoundary-DTWsYrOm.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-BtMFgElP.js → EvidenceReceiptPanel-BU_B9X19.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-DjJuU941.js → GitHubIssuesPanel-DEZoLOsP.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-HXfEX1BC.js → GitHubPRsPanel-BEsnrq7Y.js} +1 -1
- package/web-app/dist/assets/{HomePage-Cqyves-S.js → HomePage-BoUVuq7h.js} +2 -2
- package/web-app/dist/assets/{LoginPage-jUbtC7_Y.js → LoginPage-CmOED7vb.js} +1 -1
- package/web-app/dist/assets/{MagicPage-DHRsqjgR.js → MagicPage-DATZitrZ.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-B-pED05R.js → MetricsPage-DGL1v0I-.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-CsbUs21R.js → NotFoundPage-C2WgOjZd.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-BtlLEQwB.js → ProjectPage-BMi1tepj.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DaRbRjSp.js → ProjectsPage-vc3y5YPh.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DALrtP3F.js → SettingsPage-kbq0Ftvi.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-CoPHSNP0.js → ShowcasePage-Bn6Q8Kbg.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-kOuSN50O.js → SystemSettingsPage-Basmhs_b.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DGWK0LMp.js → TeamsPage-BTqOc4TR.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-C-_10CwA.js → TemplatesPage-2P9002VL.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Cz_vPbXL.js → TerminalOutput-CAoSPBrg.js} +1 -1
- package/web-app/dist/assets/{activity-icIsXJVa.js → activity-BcWVWCqU.js} +1 -1
- package/web-app/dist/assets/{bell-DbPR2_4r.js → bell-CYRqqgZ6.js} +1 -1
- package/web-app/dist/assets/{bot-DvjP3Yh5.js → bot-CU7gdn3g.js} +1 -1
- package/web-app/dist/assets/{check-D1OBxHeU.js → check-DLlCpz9V.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D09jDZ29.js → chevron-left-CE9Cb30C.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DyZMLOcC.js → circle-alert-uou4bjua.js} +1 -1
- package/web-app/dist/assets/{clock-CdpdJTId.js → clock-C_dSJcDX.js} +1 -1
- package/web-app/dist/assets/{cloud-CoLTlmoj.js → cloud-DZ22ITF6.js} +1 -1
- package/web-app/dist/assets/{code-xml-DrkE2ZZY.js → code-xml-BrJS0hhS.js} +1 -1
- package/web-app/dist/assets/{database-CMQQuM2R.js → database-D2HOmxtl.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CuioDaOG.js → dollar-sign-CQ1vK_6q.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Cx9j9K1S.js → file-code-corner-w2vpGm5d.js} +1 -1
- package/web-app/dist/assets/{file-plus-CYD2zaaI.js → file-plus-DlF6wjkD.js} +1 -1
- package/web-app/dist/assets/{globe-6w-hnQkL.js → globe-Dy3lBJg1.js} +1 -1
- package/web-app/dist/assets/{hammer-BpqtautL.js → hammer-C3xIiRXN.js} +1 -1
- package/web-app/dist/assets/{index-Dzs-9EgJ.js → index-4t-_IW2y.js} +3 -3
- package/web-app/dist/assets/{layers-D5hd7jb7.js → layers-Y8Fs8qXI.js} +1 -1
- package/web-app/dist/assets/{loader-circle-C6Y9x_7H.js → loader-circle-CxgN1v4m.js} +1 -1
- package/web-app/dist/assets/{lock-D51L7rYh.js → lock-DFFNbXDs.js} +1 -1
- package/web-app/dist/assets/{package-BJF8hkP8.js → package-5e6qCTUg.js} +1 -1
- package/web-app/dist/assets/{plus-D5kUQ79n.js → plus-C0Go_ytj.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-BKdlCa7F.js → refresh-cw-D4i-NW3o.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-_bd_YI1e.js → rotate-ccw-YlKkiUW6.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DbkegIxF.js → scroll-text-4qXOHTap.js} +1 -1
- package/web-app/dist/assets/{server-DAwqFIsq.js → server-BB6zXNp9.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BuR1tPHu.js → shield-alert-D4tzjB1H.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-BF6p3mRd.js → thumbs-up-DZiRwdFj.js} +1 -1
- package/web-app/dist/assets/{trash-2-BVpnkqqz.js → trash-2-Dv_LkRIK.js} +1 -1
- package/web-app/dist/assets/{trending-down-DpsDvRwW.js → trending-down-BJYp9PQe.js} +1 -1
- package/web-app/dist/assets/{trending-up-mFogTjt3.js → trending-up-BTcXLklK.js} +1 -1
- package/web-app/dist/assets/{upload-4T8zpBsi.js → upload-fcHqwOxr.js} +1 -1
- package/web-app/dist/assets/{usePolling-DPXQv8WO.js → usePolling-BeJYw_4L.js} +1 -1
- package/web-app/dist/assets/{user-BYGIFaTm.js → user-BcDwUCsO.js} +1 -1
- package/web-app/dist/index.html +1 -1
|
@@ -80,6 +80,60 @@ detect_any_provider() {
|
|
|
80
80
|
return 1
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
# sdk_binary_present: true (0) when an executable Claude Agent SDK platform binary exists
|
|
84
|
+
# in an installed node_modules. Shared by detect_bundled_sdk_provider and
|
|
85
|
+
# loki_requirements_json.
|
|
86
|
+
sdk_binary_present() {
|
|
87
|
+
# Search the node_modules trees Loki is actually installed into. The root is
|
|
88
|
+
# derived from THIS FILE's location (self-containment contract, see header):
|
|
89
|
+
# SKILL_DIR belongs to autonomy/loki and is NOT exported, so it would be
|
|
90
|
+
# empty when doctor.ts spawns this script standalone -- which would silently
|
|
91
|
+
# answer "no SDK" on the Bun route while the bash route said yes, exactly the
|
|
92
|
+
# kind of route split the parity gate exists to prevent.
|
|
93
|
+
# LOKI_SDK_NODE_MODULES is a TEST-ONLY seam (never set in production) so the
|
|
94
|
+
# shell tests can point at a fixture tree without mutating real node_modules.
|
|
95
|
+
local _po_root
|
|
96
|
+
_po_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)" || _po_root=""
|
|
97
|
+
# Array, not a word-split string: install roots can contain spaces.
|
|
98
|
+
local _po_search=()
|
|
99
|
+
if [ -n "${LOKI_SDK_NODE_MODULES:-}" ]; then
|
|
100
|
+
# When the test seam is set it is AUTHORITATIVE (not merely first): the
|
|
101
|
+
# real tree must not be consulted as a fallback, or a fixture asserting
|
|
102
|
+
# "no usable SDK" would silently pass by finding the repo's own binary.
|
|
103
|
+
_po_search=("$LOKI_SDK_NODE_MODULES")
|
|
104
|
+
elif [ -n "$_po_root" ]; then
|
|
105
|
+
# Two layouts, both real:
|
|
106
|
+
# repo clone -> <root>/node_modules, <root>/loki-ts/node_modules
|
|
107
|
+
# npm install -> npm HOISTS the SDK out of node_modules/loki-mode/
|
|
108
|
+
# up to the installing tree's top-level
|
|
109
|
+
# node_modules/ (verified by packing this repo and
|
|
110
|
+
# installing the tarball). Without the hoisted
|
|
111
|
+
# candidates the predicate silently returns 1 for
|
|
112
|
+
# every npm-installed user, i.e. the feature would
|
|
113
|
+
# be dead for exactly the audience it targets.
|
|
114
|
+
# <root> is .../node_modules/loki-mode, so ../ is the @scope-less
|
|
115
|
+
# top-level node_modules and ../../ covers a scoped install.
|
|
116
|
+
_po_search=(
|
|
117
|
+
"$_po_root/node_modules"
|
|
118
|
+
"$_po_root/loki-ts/node_modules"
|
|
119
|
+
"$_po_root/.."
|
|
120
|
+
"$_po_root/../.."
|
|
121
|
+
)
|
|
122
|
+
fi
|
|
123
|
+
local _nm
|
|
124
|
+
for _nm in ${_po_search+"${_po_search[@]}"}; do
|
|
125
|
+
[ -n "$_nm" ] || continue
|
|
126
|
+
set -- "$_nm"/@anthropic-ai/claude-agent-sdk-*/claude
|
|
127
|
+
# Unmatched globs stay literal in bash, so test the expansion for real.
|
|
128
|
+
while [ "$#" -gt 0 ]; do
|
|
129
|
+
[ -x "$1" ] && return 0
|
|
130
|
+
shift
|
|
131
|
+
done
|
|
132
|
+
done
|
|
133
|
+
return 1
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
83
137
|
# detect_bundled_sdk_provider: true (0) only when the bundled Claude Agent SDK
|
|
84
138
|
# can ACTUALLY run this machine's main loop with no separate CLI install.
|
|
85
139
|
#
|
|
@@ -130,53 +184,7 @@ detect_bundled_sdk_provider() {
|
|
|
130
184
|
return 1
|
|
131
185
|
fi
|
|
132
186
|
|
|
133
|
-
|
|
134
|
-
# derived from THIS FILE's location (self-containment contract, see header):
|
|
135
|
-
# SKILL_DIR belongs to autonomy/loki and is NOT exported, so it would be
|
|
136
|
-
# empty when doctor.ts spawns this script standalone -- which would silently
|
|
137
|
-
# answer "no SDK" on the Bun route while the bash route said yes, exactly the
|
|
138
|
-
# kind of route split the parity gate exists to prevent.
|
|
139
|
-
# LOKI_SDK_NODE_MODULES is a TEST-ONLY seam (never set in production) so the
|
|
140
|
-
# shell tests can point at a fixture tree without mutating real node_modules.
|
|
141
|
-
local _po_root
|
|
142
|
-
_po_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)" || _po_root=""
|
|
143
|
-
# Array, not a word-split string: install roots can contain spaces.
|
|
144
|
-
local _po_search=()
|
|
145
|
-
if [ -n "${LOKI_SDK_NODE_MODULES:-}" ]; then
|
|
146
|
-
# When the test seam is set it is AUTHORITATIVE (not merely first): the
|
|
147
|
-
# real tree must not be consulted as a fallback, or a fixture asserting
|
|
148
|
-
# "no usable SDK" would silently pass by finding the repo's own binary.
|
|
149
|
-
_po_search=("$LOKI_SDK_NODE_MODULES")
|
|
150
|
-
elif [ -n "$_po_root" ]; then
|
|
151
|
-
# Two layouts, both real:
|
|
152
|
-
# repo clone -> <root>/node_modules, <root>/loki-ts/node_modules
|
|
153
|
-
# npm install -> npm HOISTS the SDK out of node_modules/loki-mode/
|
|
154
|
-
# up to the installing tree's top-level
|
|
155
|
-
# node_modules/ (verified by packing this repo and
|
|
156
|
-
# installing the tarball). Without the hoisted
|
|
157
|
-
# candidates the predicate silently returns 1 for
|
|
158
|
-
# every npm-installed user, i.e. the feature would
|
|
159
|
-
# be dead for exactly the audience it targets.
|
|
160
|
-
# <root> is .../node_modules/loki-mode, so ../ is the @scope-less
|
|
161
|
-
# top-level node_modules and ../../ covers a scoped install.
|
|
162
|
-
_po_search=(
|
|
163
|
-
"$_po_root/node_modules"
|
|
164
|
-
"$_po_root/loki-ts/node_modules"
|
|
165
|
-
"$_po_root/.."
|
|
166
|
-
"$_po_root/../.."
|
|
167
|
-
)
|
|
168
|
-
fi
|
|
169
|
-
local _nm
|
|
170
|
-
for _nm in ${_po_search+"${_po_search[@]}"}; do
|
|
171
|
-
[ -n "$_nm" ] || continue
|
|
172
|
-
set -- "$_nm"/@anthropic-ai/claude-agent-sdk-*/claude
|
|
173
|
-
# Unmatched globs stay literal in bash, so test the expansion for real.
|
|
174
|
-
while [ "$#" -gt 0 ]; do
|
|
175
|
-
[ -x "$1" ] && return 0
|
|
176
|
-
shift
|
|
177
|
-
done
|
|
178
|
-
done
|
|
179
|
-
return 1
|
|
187
|
+
sdk_binary_present
|
|
180
188
|
}
|
|
181
189
|
|
|
182
190
|
# _po_assume_yes: true when the user has opted into unattended confirmation.
|
|
@@ -545,6 +553,46 @@ print(json.dumps({"selected": selected, "providers": rows}, indent=2))
|
|
|
545
553
|
) || return 1
|
|
546
554
|
}
|
|
547
555
|
|
|
556
|
+
# loki_requirements_json: print the setup requirements report (shared table
|
|
557
|
+
# autonomy/lib/setup-requirements.json) as one JSON object, matching the "requirements"
|
|
558
|
+
# key of the Bun doctor (loki-ts/src/commands/doctor.ts). Used by the minimal
|
|
559
|
+
# `loki doctor --json` fallback when the Bun doctor cannot run. Presence only: unlike the
|
|
560
|
+
# Bun evaluator it does not compare versions.
|
|
561
|
+
loki_requirements_json() {
|
|
562
|
+
local table statuses="" id kind target values st v
|
|
563
|
+
table="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)/lib/setup-requirements.json"
|
|
564
|
+
if [ ! -f "$table" ] || ! command -v jq >/dev/null 2>&1; then
|
|
565
|
+
printf 'null\n'
|
|
566
|
+
return 0
|
|
567
|
+
fi
|
|
568
|
+
while IFS=$'\t' read -r id kind target values; do
|
|
569
|
+
st=missing
|
|
570
|
+
case "$kind" in
|
|
571
|
+
command) command -v "$target" >/dev/null 2>&1 && st=present ;;
|
|
572
|
+
env_any)
|
|
573
|
+
for v in ${target//,/ }; do [ -n "${!v:-}" ] && st=present; done ;;
|
|
574
|
+
env_value)
|
|
575
|
+
for v in ${target//,/ }; do
|
|
576
|
+
case ",${values}," in *",${!v:-},"*) [ -n "${!v:-}" ] && st=present ;; esac
|
|
577
|
+
done ;;
|
|
578
|
+
sdk_binary) sdk_binary_present && st=present ;;
|
|
579
|
+
esac
|
|
580
|
+
statuses="${statuses}${id} ${st}"$'\n'
|
|
581
|
+
done < <(jq -r '.requirements[] | [.id, .kind, (.target | if type == "array" then join(",") else . end), ((.values // []) | join(","))] | @tsv' "$table")
|
|
582
|
+
|
|
583
|
+
jq -n --rawfile st <(printf '%s' "$statuses") --slurpfile t "$table" '
|
|
584
|
+
($st | split("\n") | map(select(length > 0) | split(" ") | {(.[0]): .[1]}) | add) as $s
|
|
585
|
+
| $t[0] as $tab
|
|
586
|
+
| {paths: [ $tab.paths[] as $p
|
|
587
|
+
| ([ $tab.requirements[] | select(.path == $p.id)
|
|
588
|
+
| {id, label, level, status: $s[.id], detail: (if $s[.id] == "present" then null else "not found by presence check" end), fix} ]) as $items
|
|
589
|
+
| ([ $items[] | select(.level == "required") ]) as $req
|
|
590
|
+
| {id: $p.id, title: $p.title,
|
|
591
|
+
ready: (if ($req | length) > 0 then ($req | all(.status == "present")) else ($items | any(.status == "present")) end),
|
|
592
|
+
items: $items} ]}
|
|
593
|
+
| . + {first_task_ready: ((.paths[] | select(.id == "common") | .ready) and ([.paths[] | select(.id != "common") | .ready] | any))}'
|
|
594
|
+
}
|
|
595
|
+
|
|
548
596
|
# Executed directly (doctor.ts child_process bridge, or manual): run the offer.
|
|
549
597
|
# When sourced by autonomy/loki, this block does not run.
|
|
550
598
|
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
package/autonomy/route.sh
CHANGED
|
@@ -45,7 +45,7 @@ loki_route_resolve() {
|
|
|
45
45
|
local verb="$1"; shift
|
|
46
46
|
R_DECIDED=0; R_E10=0; R_HARD=0; R_ENGINE=""; R_REASON=""; R_INPUT_TYPE=""; R_INPUT=""
|
|
47
47
|
R_PROVIDER=""; R_MODEL=""; R_NOTICE=""; R_ERR=""; R_EXPLAIN=0; R_JSON=0; R_SESSION_MODEL=""; R_VERB=""
|
|
48
|
-
R_E10_ARGS=(); R_CLEAN=()
|
|
48
|
+
R_E10_ARGS=(); R_CLEAN=(); R_ADAPTER=""; R_CAPDIFF=()
|
|
49
49
|
|
|
50
50
|
local a engine_req="" help=0 legacy_flag="" session_model="" provider_flag=""
|
|
51
51
|
local -a positionals=() passthru=()
|
|
@@ -208,21 +208,53 @@ loki_route_resolve() {
|
|
|
208
208
|
R_REASON="default: $R_INPUT_TYPE input runs on engine10 regardless of flag order"
|
|
209
209
|
fi
|
|
210
210
|
fi
|
|
211
|
+
_route_adapter_and_diffs "$legacy_flag"
|
|
211
212
|
return 0
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
# Sets R_ADAPTER and R_CAPDIFF[] from R_ENGINE / R_PROVIDER. Empty R_CAPDIFF means the
|
|
216
|
+
# full Claude Code path on the Loki 10 engine. Statements mirror providers/*.sh
|
|
217
|
+
# (PROVIDER_HAS_TASK_TOOL=false for every non-claude provider).
|
|
218
|
+
_route_adapter_and_diffs() {
|
|
219
|
+
local legacy_flag="$1"
|
|
220
|
+
R_CAPDIFF=()
|
|
221
|
+
if [ "$R_ENGINE" = "legacy" ]; then
|
|
222
|
+
R_ADAPTER="legacy-bash:$R_PROVIDER"
|
|
223
|
+
R_CAPDIFF+=("runs the previous bash loop (autonomy/run.sh) instead of the Loki 10 supervisor")
|
|
224
|
+
if [ -n "$legacy_flag" ]; then
|
|
225
|
+
R_CAPDIFF+=("honors $legacy_flag, which engine10 does not support")
|
|
226
|
+
fi
|
|
227
|
+
else
|
|
228
|
+
R_ADAPTER="engine10:$R_PROVIDER"
|
|
229
|
+
fi
|
|
230
|
+
case "$R_PROVIDER" in
|
|
231
|
+
claude) ;;
|
|
232
|
+
codex|cline|aider|opencode)
|
|
233
|
+
R_CAPDIFF+=("provider $R_PROVIDER has no Claude Code Task-tool subagents (providers/$R_PROVIDER.sh)") ;;
|
|
234
|
+
*)
|
|
235
|
+
R_CAPDIFF+=("provider $R_PROVIDER has no engine10 invoker") ;;
|
|
236
|
+
esac
|
|
237
|
+
}
|
|
238
|
+
|
|
214
239
|
# Print the route (human or --json). Returns 0.
|
|
215
240
|
loki_route_print() {
|
|
216
241
|
local shown="$R_INPUT"
|
|
217
242
|
if [ "${#shown}" -gt 72 ]; then shown="$(printf '%s' "$shown" | cut -c1-69)..."; fi
|
|
218
243
|
if [ "$R_JSON" = "1" ]; then
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
"$
|
|
244
|
+
local diffs="" d
|
|
245
|
+
for d in ${R_CAPDIFF[@]+"${R_CAPDIFF[@]}"}; do
|
|
246
|
+
diffs="${diffs:+$diffs,}\"$(_route_json_str "$d")\""
|
|
247
|
+
done
|
|
248
|
+
printf '{"engine":"%s","adapter":"%s","provider":"%s","model":"%s","input_type":"%s","input":"%s","reason":"%s","capability_differences":[%s]}\n' \
|
|
249
|
+
"$(_route_json_str "$R_ENGINE")" "$(_route_json_str "$R_ADAPTER")" "$(_route_json_str "$R_PROVIDER")" "$(_route_json_str "$R_MODEL")" \
|
|
250
|
+
"$(_route_json_str "$R_INPUT_TYPE")" "$(_route_json_str "$shown")" "$(_route_json_str "$R_REASON")" "$diffs"
|
|
222
251
|
else
|
|
223
|
-
printf 'engine: %s\nprovider: %s\nmodel: %s\ninput: %s (%s)\nreason: %s\n' \
|
|
224
|
-
"$R_ENGINE" "$R_PROVIDER" "$R_MODEL" "$R_INPUT_TYPE" "$shown" "$R_REASON"
|
|
252
|
+
printf 'engine: %s\nadapter: %s\nprovider: %s\nmodel: %s\ninput: %s (%s)\nreason: %s\n' \
|
|
253
|
+
"$R_ENGINE" "$R_ADAPTER" "$R_PROVIDER" "$R_MODEL" "$R_INPUT_TYPE" "$shown" "$R_REASON"
|
|
254
|
+
local d
|
|
255
|
+
for d in ${R_CAPDIFF[@]+"${R_CAPDIFF[@]}"}; do printf 'differs: %s\n' "$d"; done
|
|
225
256
|
fi
|
|
257
|
+
return 0
|
|
226
258
|
}
|
|
227
259
|
|
|
228
260
|
# Entry used by bin/loki. Args: the full original argv. Sets R_* and returns:
|
package/autonomy/run.sh
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
# LOKI_RESOURCE_MEM_THRESHOLD - Memory % threshold to warn (default: 80)
|
|
32
32
|
#
|
|
33
33
|
# Budget / Cost Limits (opt-in):
|
|
34
|
-
# LOKI_BUDGET_LIMIT - Max USD spend before auto-pause (default: empty = unlimited)
|
|
34
|
+
# LOKI_BUDGET_LIMIT - Max USD spend before auto-pause (default: loki-ts/data/model-pricing.json default_max_cost_usd, 100 USD; empty = unlimited)
|
|
35
35
|
# Example: "50.00" pauses session when estimated cost >= $50
|
|
36
36
|
#
|
|
37
37
|
# Security & Autonomy Controls (Enterprise):
|
|
@@ -532,7 +532,7 @@ for key, env_var in mapping.items():
|
|
|
532
532
|
val = get_nested(data, key) or data.get(key) or data.get(key.replace('.', '_'))
|
|
533
533
|
if val and isinstance(val, str):
|
|
534
534
|
safe_val = shlex.quote(val)
|
|
535
|
-
print(f'[ -z \"\${{{env_var}:-}}\" ] && export {env_var}={safe_val}')
|
|
535
|
+
print(f'[ -z \"\${{{env_var}:-}}\" ] && export {env_var}={safe_val}' + (\" && export LOKI_BUDGET_SOURCE=config\" if env_var == 'LOKI_BUDGET_LIMIT' else ''))
|
|
536
536
|
" 2>/dev/null)" 2>/dev/null || true
|
|
537
537
|
}
|
|
538
538
|
_LOKI_SETTINGS_FILE="${TARGET_DIR:-.}/.loki/config/settings.json" _load_json_settings
|
|
@@ -565,7 +565,23 @@ RESOURCE_MEM_THRESHOLD=${LOKI_RESOURCE_MEM_THRESHOLD:-80} # Memory % thresho
|
|
|
565
565
|
# it never kills work or discards a deliverable. The user removes .loki/PAUSE
|
|
566
566
|
# to resume, or raises LOKI_BUDGET_LIMIT. Set LOKI_BUDGET_LIMIT="" to restore
|
|
567
567
|
# the old unlimited behavior explicitly.
|
|
568
|
-
|
|
568
|
+
# LM-002: the default value is read from default_max_cost_usd in loki-ts/data/model-pricing.json, the same
|
|
569
|
+
# file the Loki 10 engine reads (docs/cost-controls.md). "100.00" is only the
|
|
570
|
+
# fallback when that file is absent, and budget_cap.test.ts pins it to the file.
|
|
571
|
+
_loki_budget_default="$(sed -n 's/.*"default_max_cost_usd"[[:space:]]*:[[:space:]]*\([0-9][0-9.]*\).*/\1/p' \
|
|
572
|
+
"${SCRIPT_DIR}/../loki-ts/data/model-pricing.json" 2>/dev/null | head -n 1)"
|
|
573
|
+
case "$_loki_budget_default" in
|
|
574
|
+
'' | *[!0-9.]*) _loki_budget_default="100.00" ;;
|
|
575
|
+
*) _loki_budget_default="$(awk -v v="$_loki_budget_default" 'BEGIN{printf "%.2f", v}')" ;;
|
|
576
|
+
esac
|
|
577
|
+
# Where the active cap came from: flag (loki start --budget), config (settings.json),
|
|
578
|
+
# env (LOKI_BUDGET_LIMIT set by the caller), or default. Printed once at run start.
|
|
579
|
+
if [ "${LOKI_BUDGET_LIMIT+set}" = "set" ]; then
|
|
580
|
+
BUDGET_SOURCE="${LOKI_BUDGET_SOURCE:-env}"
|
|
581
|
+
else
|
|
582
|
+
BUDGET_SOURCE="default"
|
|
583
|
+
fi
|
|
584
|
+
BUDGET_LIMIT=${LOKI_BUDGET_LIMIT-"$_loki_budget_default"} # USD. Note `-` not `:-`: an
|
|
569
585
|
# explicit empty value means the
|
|
570
586
|
# operator chose unlimited, and is
|
|
571
587
|
# honored.
|
|
@@ -7536,7 +7552,7 @@ EOF
|
|
|
7536
7552
|
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
7537
7553
|
}
|
|
7538
7554
|
BUDGET_EOF
|
|
7539
|
-
log_info "Budget limit set: \$$BUDGET_LIMIT"
|
|
7555
|
+
log_info "Budget limit set: \$$BUDGET_LIMIT (source: ${BUDGET_SOURCE:-default})"
|
|
7540
7556
|
fi
|
|
7541
7557
|
|
|
7542
7558
|
# v7.0.0 Phase 2: remote->local hydrate. Runs ONCE at session boot (not
|
|
@@ -9041,21 +9057,25 @@ _read_iteration_cost() {
|
|
|
9041
9057
|
if [ -f "$result_cost_file" ]; then
|
|
9042
9058
|
python3 -c "
|
|
9043
9059
|
import json
|
|
9060
|
+
def _c(v):
|
|
9061
|
+
return v if isinstance(v, (int, float)) and not isinstance(v, bool) else 'unmeasured'
|
|
9044
9062
|
try:
|
|
9045
9063
|
d = json.load(open('$result_cost_file'))
|
|
9046
9064
|
print(
|
|
9047
9065
|
d.get('input_tokens', 0) or 0,
|
|
9048
9066
|
d.get('output_tokens', 0) or 0,
|
|
9049
|
-
d.get('total_cost_usd'
|
|
9067
|
+
_c(d.get('total_cost_usd')),
|
|
9050
9068
|
d.get('cache_read_tokens', 0) or 0,
|
|
9051
9069
|
d.get('cache_creation_tokens', 0) or 0,
|
|
9052
9070
|
)
|
|
9053
9071
|
except Exception:
|
|
9054
|
-
print(0, 0,
|
|
9055
|
-
" 2>/dev/null || echo "0 0
|
|
9072
|
+
print(0, 0, 'unmeasured', 0, 0)
|
|
9073
|
+
" 2>/dev/null || echo "0 0 unmeasured 0 0"
|
|
9056
9074
|
elif [ -f ".loki/context/tracking.json" ]; then
|
|
9057
9075
|
python3 -c "
|
|
9058
9076
|
import json
|
|
9077
|
+
def _c(v):
|
|
9078
|
+
return v if isinstance(v, (int, float)) and not isinstance(v, bool) else 'unmeasured'
|
|
9059
9079
|
try:
|
|
9060
9080
|
t = json.load(open('.loki/context/tracking.json'))
|
|
9061
9081
|
iters = t.get('per_iteration', [])
|
|
@@ -9065,17 +9085,17 @@ try:
|
|
|
9065
9085
|
print(
|
|
9066
9086
|
m.get('input_tokens', 0),
|
|
9067
9087
|
m.get('output_tokens', 0),
|
|
9068
|
-
m.get('cost_usd'
|
|
9088
|
+
_c(m.get('cost_usd')),
|
|
9069
9089
|
m.get('cache_read_tokens', 0),
|
|
9070
9090
|
m.get('cache_creation_tokens', 0),
|
|
9071
9091
|
)
|
|
9072
9092
|
else:
|
|
9073
|
-
print(0, 0,
|
|
9093
|
+
print(0, 0, 'unmeasured', 0, 0)
|
|
9074
9094
|
except Exception:
|
|
9075
|
-
print(0, 0,
|
|
9076
|
-
" 2>/dev/null || echo "0 0
|
|
9095
|
+
print(0, 0, 'unmeasured', 0, 0)
|
|
9096
|
+
" 2>/dev/null || echo "0 0 unmeasured 0 0"
|
|
9077
9097
|
else
|
|
9078
|
-
echo "0 0
|
|
9098
|
+
echo "0 0 unmeasured 0 0"
|
|
9079
9099
|
fi
|
|
9080
9100
|
}
|
|
9081
9101
|
|
|
@@ -9192,6 +9212,15 @@ track_iteration_complete() {
|
|
|
9192
9212
|
$iter_cost_line
|
|
9193
9213
|
ITER_COST_EOF
|
|
9194
9214
|
|
|
9215
|
+
# Unknown cost is omitted with a reason, never written as 0 (readers treat absence as unmeasured).
|
|
9216
|
+
local iter_cost_json
|
|
9217
|
+
case "${iter_cost:-unmeasured}" in
|
|
9218
|
+
unmeasured) iter_cost_json=' "cost_unmeasured_reason": "no dollar figure recorded for this iteration",
|
|
9219
|
+
' ;;
|
|
9220
|
+
*) iter_cost_json=" \"cost_usd\": ${iter_cost},
|
|
9221
|
+
" ;;
|
|
9222
|
+
esac
|
|
9223
|
+
|
|
9195
9224
|
cat > ".loki/metrics/efficiency/iteration-${iteration}.json" << EFF_EOF
|
|
9196
9225
|
{
|
|
9197
9226
|
"iteration": $iteration,
|
|
@@ -9204,8 +9233,7 @@ ITER_COST_EOF
|
|
|
9204
9233
|
"output_tokens": ${iter_output:-0},
|
|
9205
9234
|
"cache_read_tokens": ${iter_cache_read:-0},
|
|
9206
9235
|
"cache_creation_tokens": ${iter_cache_creation:-0},
|
|
9207
|
-
"
|
|
9208
|
-
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
9236
|
+
${iter_cost_json} "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
9209
9237
|
}
|
|
9210
9238
|
EFF_EOF
|
|
9211
9239
|
|
package/autonomy/setup-wizard.sh
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# file. `loki welcome` (intro), `loki quickstart` (first build) and
|
|
5
5
|
# `loki doctor` (health) remain separate entry points.
|
|
6
6
|
#
|
|
7
|
-
# REQUIRED (doctor fails without them): node >=
|
|
7
|
+
# REQUIRED (doctor fails without them): node >= 20, python3 >= 3.8, jq, git, curl
|
|
8
8
|
# AT LEAST ONE of: a provider CLI (claude, codex, cline, aider, opencode) or the
|
|
9
9
|
# bundled Claude Agent SDK with credentials. Claude Code is the default.
|
|
10
10
|
# OPTIONAL: bun (bundled by npm), bash >= 4, codex/cline/aider/opencode, ollama.
|
|
@@ -31,6 +31,14 @@ _lsw_confirm() {
|
|
|
31
31
|
case "$ans" in y|Y|yes|YES) return 0 ;; *) return 1 ;; esac
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
_lsw_fix() {
|
|
35
|
+
# _lsw_fix ID FALLBACK -> the one-line fix from the shared requirements table
|
|
36
|
+
# (autonomy/lib/setup-requirements.json), the same text loki doctor prints.
|
|
37
|
+
local out=""
|
|
38
|
+
out="$(jq -r --arg id "$1" '.requirements[] | select(.id == $id) | .fix' "${SKILL_DIR}/autonomy/lib/setup-requirements.json" 2>/dev/null | head -n 1)" || out=""
|
|
39
|
+
printf '%s' "${out:-$2}"
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
_lsw_tool() {
|
|
35
43
|
# _lsw_tool NAME LABEL INSTALL_HINT
|
|
36
44
|
if command -v "$1" >/dev/null 2>&1; then
|
|
@@ -79,12 +87,12 @@ cmd_setup() {
|
|
|
79
87
|
echo "Loki Mode setup"
|
|
80
88
|
echo ""
|
|
81
89
|
echo "1. Required tools"
|
|
82
|
-
_lsw_tool node REQUIRED "
|
|
83
|
-
_lsw_tool python3 REQUIRED "
|
|
84
|
-
_lsw_tool jq REQUIRED "
|
|
85
|
-
_lsw_tool git REQUIRED "
|
|
86
|
-
_lsw_tool curl REQUIRED "
|
|
87
|
-
_lsw_tool bun OPTIONAL "bundled by npm; else see https://bun.sh" || true
|
|
90
|
+
_lsw_tool node REQUIRED "$(_lsw_fix node "install Node 20+ from https://nodejs.org")" || true
|
|
91
|
+
_lsw_tool python3 REQUIRED "$(_lsw_fix python3 "install python3")" || true
|
|
92
|
+
_lsw_tool jq REQUIRED "$(_lsw_fix jq "install jq")" || true
|
|
93
|
+
_lsw_tool git REQUIRED "$(_lsw_fix git "install git")" || true
|
|
94
|
+
_lsw_tool curl REQUIRED "$(_lsw_fix curl "install curl")" || true
|
|
95
|
+
_lsw_tool bun OPTIONAL "$(_lsw_fix bun "bundled by npm; else see https://bun.sh")" || true
|
|
88
96
|
if [ "$_LSW_MISSING" -gt 0 ]; then
|
|
89
97
|
echo ""
|
|
90
98
|
echo "Install the missing REQUIRED tools above with your package manager (setup never installs system packages), then rerun: loki setup"
|
|
@@ -95,7 +103,7 @@ cmd_setup() {
|
|
|
95
103
|
echo "2. Provider CLI (Claude Code is the default and the only Tier-1 provider)"
|
|
96
104
|
local p
|
|
97
105
|
for p in codex cline aider opencode; do
|
|
98
|
-
_lsw_tool "$p" OPTIONAL "see 'loki doctor' for the command" || true
|
|
106
|
+
_lsw_tool "$p" OPTIONAL "$(_lsw_fix "$p" "see 'loki doctor' for the command")" || true
|
|
99
107
|
done
|
|
100
108
|
if command -v claude >/dev/null 2>&1; then
|
|
101
109
|
printf ' [ OK ] %-10s DEFAULT\n' claude
|
|
@@ -108,7 +116,7 @@ cmd_setup() {
|
|
|
108
116
|
fi
|
|
109
117
|
fi
|
|
110
118
|
else
|
|
111
|
-
echo " [MISS] claude DEFAULT. Install: npm install -g @anthropic-ai/claude-code (then: claude auth login)"
|
|
119
|
+
echo " [MISS] claude DEFAULT. Install: $(_lsw_fix claude "npm install -g @anthropic-ai/claude-code") (then: claude auth login)"
|
|
112
120
|
if [ "$check_only" = 0 ] && [ -f "${SKILL_DIR}/autonomy/provider-offer.sh" ]; then
|
|
113
121
|
if _lsw_confirm "Run that install command now?"; then
|
|
114
122
|
# shellcheck disable=SC1091
|
|
@@ -118,7 +126,8 @@ cmd_setup() {
|
|
|
118
126
|
fi
|
|
119
127
|
if ! command -v claude >/dev/null 2>&1; then
|
|
120
128
|
echo " Claude Code is not mandatory if another provider above is installed,"
|
|
121
|
-
echo " or if the
|
|
129
|
+
echo " or if you use the Anthropic API-key path (LOKI_SDK_MODE=full with ANTHROPIC_API_KEY)."
|
|
130
|
+
echo " Requirements per path: loki doctor --requirements"
|
|
122
131
|
fi
|
|
123
132
|
fi
|
|
124
133
|
|
package/autonomy/verify.sh
CHANGED
|
@@ -2268,6 +2268,7 @@ verify_emit_evidence() {
|
|
|
2268
2268
|
_V_SCOPE_SOFT_NET="${VERIFY_SCOPE_SOFT_NET_LINES:-}" \
|
|
2269
2269
|
_V_SCOPE_MAX_FILES="${VERIFY_SCOPE_MAX_FILES:-}" \
|
|
2270
2270
|
_V_SCOPE_MAX_NET="${VERIFY_SCOPE_MAX_NET_LINES:-}" \
|
|
2271
|
+
_V_LIBDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib" \
|
|
2271
2272
|
_verify_py - <<'PYEOF'
|
|
2272
2273
|
import json, os, hashlib, sys
|
|
2273
2274
|
|
|
@@ -2401,6 +2402,27 @@ if _scope_verdict:
|
|
|
2401
2402
|
"thresholds": _thresholds,
|
|
2402
2403
|
}
|
|
2403
2404
|
|
|
2405
|
+
# Verification record (docs/PROOF-PASSPORT.md): integrity, execution
|
|
2406
|
+
# validity (revision + environment) and requirement coverage, kept separate, plus
|
|
2407
|
+
# per-check status and a scoped label. Additive; verdict/exit_code are untouched.
|
|
2408
|
+
# A failure to build it is recorded, never swallowed into a clean-looking doc.
|
|
2409
|
+
try:
|
|
2410
|
+
sys.path.insert(0, os.environ["_V_LIBDIR"])
|
|
2411
|
+
import importlib.util
|
|
2412
|
+
_spec = importlib.util.spec_from_file_location(
|
|
2413
|
+
"loki_proof_verify", os.path.join(os.environ["_V_LIBDIR"], "proof-verify.py"))
|
|
2414
|
+
_pv_mod = importlib.util.module_from_spec(_spec)
|
|
2415
|
+
_spec.loader.exec_module(_pv_mod)
|
|
2416
|
+
doc["verification"] = _pv_mod.build_for_evidence(
|
|
2417
|
+
doc, repo_dir=".", loki_dir=os.environ.get("LOKI_DIR") or ".loki",
|
|
2418
|
+
record_path="", fresh_capture=True)
|
|
2419
|
+
# base_verdict duplicates doc["verdict"]; record_path would pin the output
|
|
2420
|
+
# directory into a document that must be byte-identical across directories.
|
|
2421
|
+
doc["verification"].pop("base_verdict", None)
|
|
2422
|
+
except Exception as _vr_exc:
|
|
2423
|
+
doc["verification"] = {"schema_version": "1.0", "error": "verification record unavailable: %s" % _vr_exc,
|
|
2424
|
+
"label": doc["verdict"] + " (verification record unavailable)"}
|
|
2425
|
+
|
|
2404
2426
|
os.makedirs(out_dir, exist_ok=True)
|
|
2405
2427
|
ev_path = os.path.join(out_dir, "evidence.json")
|
|
2406
2428
|
with open(ev_path, "w") as f:
|
|
@@ -2437,6 +2459,10 @@ lines.append("# Autonomi Verify report")
|
|
|
2437
2459
|
lines.append("")
|
|
2438
2460
|
lines.append("Verdict: **%s** (exit %d)" % (doc["verdict"], doc["exit_code"]))
|
|
2439
2461
|
lines.append("")
|
|
2462
|
+
_vr_label = (doc.get("verification") or {}).get("label")
|
|
2463
|
+
if _vr_label:
|
|
2464
|
+
lines.append("Verification: %s" % _vr_label)
|
|
2465
|
+
lines.append("")
|
|
2440
2466
|
_llm = doc.get("llm_review") or {}
|
|
2441
2467
|
_llm_status = _llm.get("status", "unavailable")
|
|
2442
2468
|
if _llm_status == "reviewed":
|
|
@@ -3419,7 +3445,20 @@ verify_main() {
|
|
|
3419
3445
|
# Under --json stdout carries the evidence document alone, so the human
|
|
3420
3446
|
# banner is redirected to stderr rather than dropped: an operator watching a
|
|
3421
3447
|
# terminal still sees the verdict, and `| jq` still parses.
|
|
3422
|
-
|
|
3448
|
+
# Scoped label + compact reviewer summary from the verification record just
|
|
3449
|
+
# written to evidence.json. The exit code is unchanged; only the wording is
|
|
3450
|
+
# stricter: a skipped or not-run check never leaves a bare "VERIFIED".
|
|
3451
|
+
local _v_lib _v_label _v_summary
|
|
3452
|
+
_v_lib="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib"
|
|
3453
|
+
_v_label="$VERIFY_VERDICT"
|
|
3454
|
+
if [ -f "$_v_lib/proof-verify.py" ]; then
|
|
3455
|
+
_v_summary="$(_verify_py "$_v_lib/proof-verify.py" summary "$out_dir/evidence.json" --embedded 2>/dev/null)" || _v_summary=""
|
|
3456
|
+
if [ -n "$_v_summary" ]; then
|
|
3457
|
+
_v_label="${_v_summary%%$'\n'*}"
|
|
3458
|
+
printf '%s\n' "${_v_summary#*$'\n'}" >&$_v_banner_fd
|
|
3459
|
+
fi
|
|
3460
|
+
fi
|
|
3461
|
+
printf 'VERDICT: %s\n' "$_v_label" >&$_v_banner_fd
|
|
3423
3462
|
printf 'Evidence: %s/evidence.json\n' "$out_dir" >&$_v_banner_fd
|
|
3424
3463
|
printf 'Report: %s/report.md\n' "$out_dir" >&$_v_banner_fd
|
|
3425
3464
|
# --hosted only: surface the enrichment in human output so the extra signal
|
package/completions/_loki
CHANGED
|
@@ -378,7 +378,8 @@ function _loki_provider {
|
|
|
378
378
|
'show:Display current provider'
|
|
379
379
|
'set:Set default provider'
|
|
380
380
|
'list:List available providers'
|
|
381
|
-
'info:Get provider information'
|
|
381
|
+
'info:Get provider information (--json: capability contract)'
|
|
382
|
+
'matrix:Show the capability matrix'
|
|
382
383
|
'help:Provider help'
|
|
383
384
|
)
|
|
384
385
|
_describe -t cmds 'provider subcommand' cmds
|
|
@@ -389,6 +390,7 @@ function _loki_plan {
|
|
|
389
390
|
'--json[Machine-readable JSON output]' \
|
|
390
391
|
'--verbose[Detailed per-iteration breakdown]' \
|
|
391
392
|
'--spec[Write editable acceptance criteria to .loki/specs/<slug>.md]' \
|
|
393
|
+
'--requirements[Print the last run requirement-to-check map (read-only)]' \
|
|
392
394
|
'*:prd file:_files'
|
|
393
395
|
}
|
|
394
396
|
|
package/completions/loki.bash
CHANGED
|
@@ -67,7 +67,7 @@ _loki_completion() {
|
|
|
67
67
|
;;
|
|
68
68
|
|
|
69
69
|
provider)
|
|
70
|
-
local provider_cmds="show set list info help"
|
|
70
|
+
local provider_cmds="show set list info matrix help"
|
|
71
71
|
COMPREPLY=( $(compgen -W "${provider_cmds}" -- "$cur") )
|
|
72
72
|
;;
|
|
73
73
|
|
|
@@ -123,9 +123,15 @@ _loki_completion() {
|
|
|
123
123
|
fi
|
|
124
124
|
;;
|
|
125
125
|
|
|
126
|
+
setup-skill)
|
|
127
|
+
if [[ "$cur" == -* ]]; then
|
|
128
|
+
COMPREPLY=( $(compgen -W "--claude-code --print --project --stop-hook --settings --help" -- "$cur") )
|
|
129
|
+
return 0
|
|
130
|
+
fi
|
|
131
|
+
;;
|
|
126
132
|
doctor)
|
|
127
133
|
if [[ "$cur" == -* ]]; then
|
|
128
|
-
COMPREPLY=( $(compgen -W "--json --fix --help" -- "$cur") )
|
|
134
|
+
COMPREPLY=( $(compgen -W "--json --fix --repo --requirements --help" -- "$cur") )
|
|
129
135
|
return 0
|
|
130
136
|
fi
|
|
131
137
|
;;
|
|
@@ -197,7 +203,7 @@ _loki_completion() {
|
|
|
197
203
|
|
|
198
204
|
plan)
|
|
199
205
|
if [[ "$cur" == -* ]]; then
|
|
200
|
-
COMPREPLY=( $(compgen -W "--json --verbose --spec --help" -- "$cur") )
|
|
206
|
+
COMPREPLY=( $(compgen -W "--json --verbose --spec --requirements --help" -- "$cur") )
|
|
201
207
|
return 0
|
|
202
208
|
fi
|
|
203
209
|
;;
|
package/dashboard/__init__.py
CHANGED
package/docs/CLI-REFERENCE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLI reference
|
|
2
2
|
|
|
3
|
-
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.
|
|
3
|
+
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.5.1. Do not edit by hand.
|
|
4
4
|
Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for the full inventory.
|
|
5
5
|
|
|
6
6
|
| Command | Description |
|
|
@@ -12,6 +12,8 @@ Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for th
|
|
|
12
12
|
| `loki provider show` | Show current provider |
|
|
13
13
|
| `loki provider list` | List providers and install status |
|
|
14
14
|
| `loki provider set` | Switch the active provider |
|
|
15
|
+
| `loki provider info` | Provider details; --json prints the capability contract |
|
|
16
|
+
| `loki provider matrix` | Capability matrix per provider and engine |
|
|
15
17
|
| `loki memory` | Cross-project learnings |
|
|
16
18
|
| `loki memory list` | All learnings |
|
|
17
19
|
| `loki memory index` | Show or rebuild the memory index |
|
package/docs/EVALUATING.md
CHANGED
|
@@ -184,6 +184,33 @@ per-call cost was not recorded.
|
|
|
184
184
|
|
|
185
185
|
---
|
|
186
186
|
|
|
187
|
+
## 8. Harness attribution: what Loki adds to the same model
|
|
188
|
+
|
|
189
|
+
Question: what does Loki add to the same underlying coding agent, and is the benefit worth its cost? Status: pilot. Six tasks, small n. A pilot reports variance; it is not proof of superiority. Running it is a separate, paid step.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
loki bench attribution --dry-run # validate task set, print plan, no model call
|
|
193
|
+
loki bench attribution --model <model-id> --execute # run, spends API budget
|
|
194
|
+
loki bench attribution --report <result.json> # re-render the markdown report
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Source checkout only. Implementation: `benchmarks/bench/attribution.py`, reusing `runner._apply_overlay`, `report._median` and `benchmarks/bench/adapters` (process-tree reaping, the Claude JSON parser, the Loki adapter and its cost collection). Options: `--tasks id,id`, `--repeats N` (default 1), `--model ID` (default `claude-sonnet-5`, or `LOKI_BENCH_MODEL`), `--budget-usd X` (default 5), `--timeout SECONDS` (default 900, capped by each task's `agent_timeout_s`), `--out-dir DIR`, `--tasks-dir DIR`. Without `--execute` it only prints the plan and never spawns `claude` or `loki`.
|
|
198
|
+
|
|
199
|
+
Arms and controls: `raw_claude` runs `claude -p <prompt> --output-format json --model M --dangerously-skip-permissions --max-budget-usd B`; `loki` runs `loki start <prompt-file> --provider claude --no-dashboard --yes` with `LOKI_SESSION_MODEL=M` and `LOKI_BUDGET_LIMIT=B`. Both get a byte-identical seed in a fresh git repo with one fixed "seed" commit, the same prompt text (the PRD file for Loki is kept outside the workspace), the same wall-clock cap with the process tree reaped on timeout, separate scratch directories, a scoped child environment and `LOKI_NO_BROWSER=1`. The arm that goes first alternates per task and repeat. Every result JSON pins `claude --version`, `loki version`, the Loki commit SHA, Python version, model, budget, timeout, repeats, and per task the sha256 of the seed tree, acceptance tree and prompt. Disclosed difference: Loki runs its own planning, review and verification loop; that overhead is what is measured. Provider comparisons are out of scope so model and harness effects are not mixed.
|
|
200
|
+
|
|
201
|
+
Tasks live under `benchmarks/attribution/tasks/<id>/` (2 bug fixes, 2 features, 1 refactor, 1 integration): `task.json`, `prompt.md` (the only instruction either arm sees), `seed/` (workspace), `acceptance/` (hidden test, copied in only after the run, overwriting same-named files) and `reference/` (known-good overlay, used only by tests). Exit code 0 from `acceptance.cmd` means accepted. The loader rejects a task whose acceptance test also appears in the seed. Neither arm grades itself: no council, RARV-C or LLM judge takes part in scoring.
|
|
202
|
+
|
|
203
|
+
Metrics, per arm and per task class, computed by `compute_metrics`; failures, timeouts and harness errors stay in every denominator:
|
|
204
|
+
|
|
205
|
+
- Acceptance rate: accepted / assigned, with a 95% Wilson interval.
|
|
206
|
+
- False-completion rate: (reported success and not accepted) / reported success; null when nothing reported success. Reported success means `claude -p` exits 0 with `is_error` false, or `loki start` exits 0.
|
|
207
|
+
- Cost per accepted task: total measured spend across all attempts / accepted. Null (`undefined_no_accepted`) when none accepted. A run with no cost figure is `unmeasured`, never zero: some unmeasured gives `partial_lower_bound` (per-accepted null, lower bound only), all unmeasured gives `unmeasured`. Cost is API-dollar accounting; subscription use is not converted.
|
|
208
|
+
- Elapsed time: median over accepted runs, with completion rate, timeout count and error count; median over all runs is also given.
|
|
209
|
+
|
|
210
|
+
Not measured: human effort, recovery success, review quality. Output: `--out-dir` (default `benchmarks/attribution/results/`) receives `attribution-<UTC stamp>.json` and the matching `.md`. Publish failures, timeouts and exclusions with the report; do not drop rows. Do not claim superiority from overlapping intervals on a handful of runs; use the pilot to size a larger run with `--repeats` above 1. Tests: `python3 benchmarks/bench/tests/test_attribution.py` (no model call).
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
187
214
|
## What we do not have
|
|
188
215
|
|
|
189
216
|
Stating this plainly, because you will find it out anyway and it is cheaper for
|
|
@@ -133,6 +133,26 @@ Cap overrides (only meaningful when feature 8 is on):
|
|
|
133
133
|
Profile knobs: `LOKI_REPO_PROFILE_TTL_SECONDS` (86400),
|
|
134
134
|
`LOKI_REPO_PROFILE_MAX_CHARS` (2000).
|
|
135
135
|
|
|
136
|
+
## Repo commands and baseline (REPOMEM, same module)
|
|
137
|
+
|
|
138
|
+
`loki-ts/src/runner/repo_profile.ts` also owns the commands profile the engine10 path uses (`loki start`). The goal: the user never explains how the repo builds and tests, and a failure that was already there never blocks the run. It is not gated by `LOKI_REPO_PROFILE`; disable it with `LOKI_REPO_BASELINE=0`. Both profiles share one manifest hash (`manifestHash`: sha256 over name and content of package.json, requirements.txt, Makefile, pyproject.toml, tox.ini, pytest.ini, setup.cfg, go.mod, Cargo.toml and `.github/workflows/*.yml`).
|
|
139
|
+
|
|
140
|
+
Artifact: `.loki/state/repo-profile.json` (version 1, `CommandProfile`), written by engine10 intake.
|
|
141
|
+
|
|
142
|
+
- `revision`: HEAD the baseline ran on. `manifest_hash`: as above.
|
|
143
|
+
- `commands[]`: `{kind: test|lint|typecheck|build, cmd, source, validated, exit_code, duration_s, failing_ids}`. `validated` is true only when the command ran to completion on the untouched tree, false when it could not run (missing, timed out), null when discovered but not executed. Only the first test command is executed.
|
|
144
|
+
- `baseline`: `{ran, command, exit_code, duration_s, timed_out, failing_ids, attributable, reason?}`. `attributable` means exit 0, or non-zero with parseable failing ids (`failIds`).
|
|
145
|
+
|
|
146
|
+
Lifecycle:
|
|
147
|
+
|
|
148
|
+
1. Intake (after the dirty-tree refusal and the `.loki/` exclude, before any agent edit): same manifest hash and HEAD reuses the file; same hash and a new HEAD keeps the commands and re-runs only the baseline; a changed hash rediscovers and re-runs (`ensureProfile`).
|
|
149
|
+
2. Baseline: `sh -c <test cmd>` in the repo, 60s timeout (SIGKILL), scoped env (`plainTestEnv`, `PYTHONDONTWRITEBYTECODE=1`, `CI=1`). Untracked files the run created are removed so the diff stays the agent's.
|
|
150
|
+
3. Implementer brief (`briefContext`, e10ext/context.ts): a "Repo commands" block and the known-red test ids (`profileBrief`). Bytes depend only on the profile content (no timestamps).
|
|
151
|
+
4. Verify (`verifyStage`): `attribute()` marks a failing check pre-existing only when the profile revision equals the run base sha, the baseline was attributable, the check is neither a Wall/task-named target nor in a file the diff touched, and every one of its failing ids is in the baseline. Those join `pre_red` and `pre_red_checks`. Everything else that fails is introduced and blocks as before; a check with no parsed ids is introduced. Verify data gains `baseline_line` (`baseline: N pre-existing failing check(s) (not blocking), M introduced (blocking)`) and `introduced_checks`; seal lists the line under NOT PROVEN only when a baseline failure was excused.
|
|
152
|
+
5. Surface: `loki doctor --repo` (and `--repo --json`) prints the profile, read-only, from the current directory.
|
|
153
|
+
|
|
154
|
+
`e10ext/repomemory.ts` still holds the last verified command, flaky list and failure causes (dynamic per-repo cache data, not discovery).
|
|
155
|
+
|
|
136
156
|
## Acceptance
|
|
137
157
|
|
|
138
158
|
1. With all flags unset, `bun test tests/parity/` stays 60/60 byte-exact.
|