loki-mode 11.17.11 → 11.17.13
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/cost-summary.py +5 -2
- package/autonomy/lib/efficiency_cost.py +13 -0
- package/autonomy/lib/proof-generator.py +5 -0
- package/autonomy/lib/proof-template.html +21 -6
- package/autonomy/lib/trust_trajectory.py +11 -1
- package/autonomy/loki +42 -28
- package/completions/_loki +1 -0
- package/completions/loki.bash +1 -1
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/v10/BOARD.md +79 -11
- package/docs/v10/FAILURE-CLASSES.md +63 -0
- package/docs/v10/PROGRESS.md +2 -0
- package/loki-ts/dist/loki.js +951 -947
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +237 -230
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-CFqo-FpE.js → AdminPage-CwtSZac4.js} +1 -1
- package/web-app/dist/assets/{Avatar-CxDqlcAZ.js → Avatar-BfguNHDC.js} +1 -1
- package/web-app/dist/assets/{Badge-CjZcSHSd.js → Badge-DQGEOIqb.js} +1 -1
- package/web-app/dist/assets/{Button-BsTd60w2.js → Button-CRb-wJga.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BivdM9_z.js → CockpitPage-DnA9wewN.js} +1 -1
- package/web-app/dist/assets/{ComparePage-DEsuYGnF.js → ComparePage-DX6F_G0l.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-Q2L0QzKv.js → ErrorBoundary-VVIdgfkb.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-Da0-q_vi.js → EvidenceReceiptPanel-FxknNXxU.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-MSIQvU9a.js → GitHubIssuesPanel-DqiraoJP.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-Dw-t46_A.js → GitHubPRsPanel-BYkTsRfQ.js} +1 -1
- package/web-app/dist/assets/{HomePage-k_r93vg_.js → HomePage-BjpWZdl4.js} +2 -2
- package/web-app/dist/assets/{LoginPage-C-TlmbEy.js → LoginPage-DdBnFY4Y.js} +1 -1
- package/web-app/dist/assets/{MagicPage-Dv_lGAX0.js → MagicPage-ihQmO1wm.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-BINwnc6C.js → MetricsPage-Dq5YARm1.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-DGbdLAhm.js → NotFoundPage-CLzwsmxk.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-D4GhODo7.js → ProjectPage-Dm8X4ntm.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-C9iCGUvI.js → ProjectsPage-DI3PhBwa.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-08SiNkKI.js → SettingsPage-D3bL3Ll-.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-5cq8wXvh.js → ShowcasePage-ypNyQAKe.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-ch0Ex4iS.js → SystemSettingsPage-a9KPi49-.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-eE9Bevpx.js → TeamsPage-B2KmQjeS.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-BLqK2AOM.js → TemplatesPage-BnK5NB62.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Bw6lAjMM.js → TerminalOutput-CYbPu51X.js} +1 -1
- package/web-app/dist/assets/{activity-mqeYLy0X.js → activity-DBIfCcER.js} +1 -1
- package/web-app/dist/assets/{bell-RrSQYwnk.js → bell-B9HN4QkY.js} +1 -1
- package/web-app/dist/assets/{bot-BAYArzbY.js → bot-DamtEiBN.js} +1 -1
- package/web-app/dist/assets/{check-DpZMgeCp.js → check-DXifl-uv.js} +1 -1
- package/web-app/dist/assets/{chevron-left-HdiZ5i2v.js → chevron-left-BrlJBb8m.js} +1 -1
- package/web-app/dist/assets/{circle-alert-CIGI9S7n.js → circle-alert-CVKCTAb9.js} +1 -1
- package/web-app/dist/assets/{clock-DyGC5H7u.js → clock-HQ6E5gSp.js} +1 -1
- package/web-app/dist/assets/{cloud-CCV2xWgX.js → cloud-B15Ysqug.js} +1 -1
- package/web-app/dist/assets/{code-xml-DZgWjvzk.js → code-xml-hqFIp0_h.js} +1 -1
- package/web-app/dist/assets/{database-kzfwKPqD.js → database-CcSm0M_K.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-jepNmrzR.js → dollar-sign-acxpddMc.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-BaJhHJXN.js → file-code-corner-B5IIv12W.js} +1 -1
- package/web-app/dist/assets/{file-plus-rONhSBM8.js → file-plus-CkbMFWJb.js} +1 -1
- package/web-app/dist/assets/{globe-DEKjsEXs.js → globe-CWmZ3xTF.js} +1 -1
- package/web-app/dist/assets/{hammer-LFrBT_V4.js → hammer-BSaCsCAo.js} +1 -1
- package/web-app/dist/assets/{index-DTi6txFh.js → index-BCf0fCNu.js} +3 -3
- package/web-app/dist/assets/{layers-D0AF3hqY.js → layers-CL82nJ3b.js} +1 -1
- package/web-app/dist/assets/{loader-circle-5MojTTeG.js → loader-circle-DBhm8R7M.js} +1 -1
- package/web-app/dist/assets/{lock-DKTO02bm.js → lock-wWcqImfq.js} +1 -1
- package/web-app/dist/assets/{package-0-plMCjY.js → package-BaZa22IO.js} +1 -1
- package/web-app/dist/assets/{plus-CjhYDJ6n.js → plus-TFOQPEeK.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CLC6VIEp.js → refresh-cw-BOKfYIta.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-DInfsBjr.js → rotate-ccw-DrH5oGwW.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DHh5UjH-.js → scroll-text-aY8_wIJd.js} +1 -1
- package/web-app/dist/assets/{server-D0j8HyOw.js → server-C6FQ01rB.js} +1 -1
- package/web-app/dist/assets/{shield-alert-D8wHooc9.js → shield-alert-DurYyWUo.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-Cda_qfHU.js → thumbs-up-wUuSs67Q.js} +1 -1
- package/web-app/dist/assets/{trash-2-eyrU5HYs.js → trash-2-DAob0LKZ.js} +1 -1
- package/web-app/dist/assets/{trending-down-Br_sStsY.js → trending-down-yKL9mp2O.js} +1 -1
- package/web-app/dist/assets/{trending-up-DGmTcXWO.js → trending-up-BD55eCE3.js} +1 -1
- package/web-app/dist/assets/{upload-CvCVXqWR.js → upload-0IJxnT31.js} +1 -1
- package/web-app/dist/assets/{usePolling-DT-cLGOG.js → usePolling-DuBiboP5.js} +1 -1
- package/web-app/dist/assets/{user-tzdYvO9g.js → user-BDw_rGaY.js} +1 -1
- package/web-app/dist/index.html +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: An autonomous software factory that knows what it is supposed to deliver, and proves it did. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v11.17.
|
|
6
|
+
# Loki Mode v11.17.13
|
|
7
7
|
|
|
8
8
|
**Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
|
|
9
9
|
|
|
@@ -483,4 +483,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
483
483
|
|
|
484
484
|
---
|
|
485
485
|
|
|
486
|
-
**v11.17.
|
|
486
|
+
**v11.17.13 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
11.17.
|
|
1
|
+
11.17.13
|
|
@@ -50,7 +50,7 @@ _HERE = os.path.dirname(os.path.abspath(__file__))
|
|
|
50
50
|
if _HERE not in sys.path:
|
|
51
51
|
sys.path.insert(0, _HERE)
|
|
52
52
|
|
|
53
|
-
from efficiency_cost import record_is_measured # noqa: E402
|
|
53
|
+
from efficiency_cost import cost_block_is_partial, record_is_measured # noqa: E402
|
|
54
54
|
|
|
55
55
|
# iteration_attribution.py already reads and sorts the efficiency dir, skipping
|
|
56
56
|
# malformed records. Importing it keeps ONE reader of that directory; a third
|
|
@@ -167,6 +167,8 @@ def summarize(workspace="."):
|
|
|
167
167
|
"fully_measured": exists > 0 and measured == exists,
|
|
168
168
|
"total_cost_usd": round(total_usd, 4) if have_cost else None,
|
|
169
169
|
"cost_iterations_counted": len(cost_points),
|
|
170
|
+
"cost_partial": cost_block_is_partial({
|
|
171
|
+
"cost_partial": have_cost and (len(cost_points) < measured or measured < exists)}),
|
|
170
172
|
"avg_cost_per_iteration": (
|
|
171
173
|
round(total_usd / len(cost_points), 4) if have_cost else None),
|
|
172
174
|
"cache_hit_ratio": _ratio(
|
|
@@ -262,7 +264,8 @@ def render(s):
|
|
|
262
264
|
s["iterations_found"], s["iterations_measured"],
|
|
263
265
|
"" if s["fully_measured"] else " <- PARTIAL" if s["iterations_found"]
|
|
264
266
|
else ""))
|
|
265
|
-
L.append("Total cost: " + _usd(s["total_cost_usd"])
|
|
267
|
+
L.append("Total cost: " + _usd(s["total_cost_usd"])
|
|
268
|
+
+ (" <- LOWER BOUND" if s.get("cost_partial") else ""))
|
|
266
269
|
L.append("Avg / iter: " + _usd(s["avg_cost_per_iteration"]))
|
|
267
270
|
L.append("Cache ratio: " + _pct(s["cache_hit_ratio"])
|
|
268
271
|
+ " (cache_read / (input + cache_read))")
|
|
@@ -238,6 +238,19 @@ _LEDGER_TOKEN_KEYS = (
|
|
|
238
238
|
)
|
|
239
239
|
|
|
240
240
|
|
|
241
|
+
def cost_block_is_partial(cost):
|
|
242
|
+
"""The one partial-cost check (FC-COST-PARTIAL-b): True when a cost block's
|
|
243
|
+
dollar total is a lower bound or unknown, so consumers label it and never
|
|
244
|
+
print it as complete."""
|
|
245
|
+
if not isinstance(cost, dict):
|
|
246
|
+
return False
|
|
247
|
+
if cost.get("cost_partial") is True or cost.get("cost_available") is False:
|
|
248
|
+
return True
|
|
249
|
+
return any(
|
|
250
|
+
isinstance(cost.get(k), int) and not isinstance(cost.get(k), bool) and cost.get(k) > 0
|
|
251
|
+
for k in ("ledger_invalid_lines", "cost_unpriced_records"))
|
|
252
|
+
|
|
253
|
+
|
|
241
254
|
def read_child_ledger(loki_dir):
|
|
242
255
|
"""Read .loki/metrics/child-sessions.jsonl, one JSON object per child session.
|
|
243
256
|
|
|
@@ -37,6 +37,7 @@ if _HERE not in sys.path:
|
|
|
37
37
|
|
|
38
38
|
import proof_redact # noqa: E402
|
|
39
39
|
from efficiency_cost import collect_efficiency as _collect_efficiency # noqa: E402
|
|
40
|
+
from efficiency_cost import cost_block_is_partial as _cost_is_partial # noqa: E402
|
|
40
41
|
import effort_estimator # noqa: E402
|
|
41
42
|
from tree_digest import MANIFEST_VERSION, compute_tree_digest # noqa: E402
|
|
42
43
|
from workspace_diff import collect_workspace_diff # noqa: E402
|
|
@@ -2133,6 +2134,8 @@ def _build_social_hook(proof):
|
|
|
2133
2134
|
council ratio. When cost was not collected, omit the cost (never fabricate
|
|
2134
2135
|
a number, never print "$0.00")."""
|
|
2135
2136
|
usd = _fmt_usd_hook((proof.get("cost") or {}).get("usd"))
|
|
2137
|
+
if usd is not None and _cost_is_partial(proof.get("cost")):
|
|
2138
|
+
usd = "at least " + usd
|
|
2136
2139
|
lead = ("Built autonomously for " + usd) if usd is not None \
|
|
2137
2140
|
else "Built autonomously by Loki Mode"
|
|
2138
2141
|
parts = [lead]
|
|
@@ -2184,6 +2187,8 @@ def _render_fallback_html(proof):
|
|
|
2184
2187
|
|
|
2185
2188
|
usd_val = cost.get("usd", None)
|
|
2186
2189
|
usd_disp = _fmt_usd_hook(usd_val)
|
|
2190
|
+
if usd_disp is not None and _cost_is_partial(cost):
|
|
2191
|
+
usd_disp = "at least " + usd_disp
|
|
2187
2192
|
|
|
2188
2193
|
rows = []
|
|
2189
2194
|
rows.append("<h1>Loki Mode -- Proof of Run</h1>")
|
|
@@ -803,14 +803,28 @@
|
|
|
803
803
|
sec.classList.remove("hide");
|
|
804
804
|
}
|
|
805
805
|
|
|
806
|
+
// Mirror of cost_block_is_partial (autonomy/lib/efficiency_cost.py).
|
|
807
|
+
function costIsPartial(p) {
|
|
808
|
+
var c = g(p, "cost", null);
|
|
809
|
+
if (!c || typeof c !== "object") return false;
|
|
810
|
+
if (c.cost_partial === true || c.cost_available === false) return true;
|
|
811
|
+
var ks = ["ledger_invalid_lines", "cost_unpriced_records"];
|
|
812
|
+
for (var i = 0; i < ks.length; i++) {
|
|
813
|
+
var v = c[ks[i]];
|
|
814
|
+
if (typeof v === "number" && isFinite(v) && Math.floor(v) === v && v > 0) return true;
|
|
815
|
+
}
|
|
816
|
+
return false;
|
|
817
|
+
}
|
|
818
|
+
|
|
806
819
|
function renderHero(p) {
|
|
807
820
|
// cost.usd is null when cost was not collected for this run. g() coerces
|
|
808
821
|
// null to its default, so read with a null default and branch BEFORE
|
|
809
822
|
// fmtUsd: a skeptic seeing a zero-dollar total assumes the artifact is fake.
|
|
810
823
|
var usdRaw = g(p, "cost.usd", null);
|
|
824
|
+
var partial = costIsPartial(p);
|
|
811
825
|
var fc = num(g(p, "files_changed.count", 0));
|
|
812
826
|
var parts = [__PROOF_RECEIPT_TITLE_JSON__];
|
|
813
|
-
if (usdRaw !== null) parts.push(fmtUsd(usdRaw));
|
|
827
|
+
if (usdRaw !== null) parts.push((partial ? "at least " : "") + fmtUsd(usdRaw));
|
|
814
828
|
else parts.push("cost not recorded");
|
|
815
829
|
parts.push(fc + " file" + (fc === 1 ? "" : "s") + " changed");
|
|
816
830
|
var cs = councilSummary(p);
|
|
@@ -825,7 +839,7 @@
|
|
|
825
839
|
if (prov || model) sub.push("via " + esc(prov) + (model ? " / " + esc(model) : ""));
|
|
826
840
|
document.getElementById("heroSub").innerHTML = sub.join(" · ");
|
|
827
841
|
|
|
828
|
-
renderHeroStats(p, usdRaw, fc, cs);
|
|
842
|
+
renderHeroStats(p, usdRaw, fc, cs, partial);
|
|
829
843
|
|
|
830
844
|
var actions = document.getElementById("heroActions");
|
|
831
845
|
var html = "";
|
|
@@ -872,7 +886,7 @@
|
|
|
872
886
|
actions.innerHTML = html + share;
|
|
873
887
|
}
|
|
874
888
|
|
|
875
|
-
function renderHeroStats(p, usdRaw, fc, cs) {
|
|
889
|
+
function renderHeroStats(p, usdRaw, fc, cs, partial) {
|
|
876
890
|
var el = document.getElementById("heroStats");
|
|
877
891
|
if (!el) return;
|
|
878
892
|
var cells = [];
|
|
@@ -895,7 +909,7 @@
|
|
|
895
909
|
}
|
|
896
910
|
cells.push(["Verified", vMain, esc(vSub), cs ? "ok" : ""]);
|
|
897
911
|
// Cost: never render a zero-dollar total when cost was not collected.
|
|
898
|
-
if (usdRaw !== null) cells.push(["Cost", fmtUsd(usdRaw), "total for this run", ""]);
|
|
912
|
+
if (usdRaw !== null) cells.push(["Cost", (partial ? "at least " : "") + fmtUsd(usdRaw), partial ? "lower bound, some cost not priced" : "total for this run", ""]);
|
|
899
913
|
else cells.push(["Cost", "not recorded", "no cost data", ""]);
|
|
900
914
|
// Duration.
|
|
901
915
|
var dur = g(p, "wall_clock_sec", null);
|
|
@@ -962,6 +976,7 @@
|
|
|
962
976
|
function renderCost(p) {
|
|
963
977
|
var card = document.getElementById("costCard");
|
|
964
978
|
var usdRaw = g(p, "cost.usd", null);
|
|
979
|
+
var partial = costIsPartial(p);
|
|
965
980
|
function tokenValue(path) {
|
|
966
981
|
var raw = g(p, path, null);
|
|
967
982
|
return raw === null ? "not recorded" : fmtInt(raw);
|
|
@@ -981,8 +996,8 @@
|
|
|
981
996
|
// a zero-dollar total (which reads as fake/broken to a skeptic).
|
|
982
997
|
var top;
|
|
983
998
|
if (usdRaw !== null) {
|
|
984
|
-
top = '<div class="cost-top"><span class="cost-usd">' + fmtUsd(usdRaw) +
|
|
985
|
-
'</span><span class="cost-label">total cost for this run, itemized below</span></div>';
|
|
999
|
+
top = '<div class="cost-top"><span class="cost-usd">' + (partial ? "at least " : "") + fmtUsd(usdRaw) +
|
|
1000
|
+
'</span><span class="cost-label">' + (partial ? "lower bound: some cost was not priced or recorded" : "total cost for this run, itemized below") + '</span></div>';
|
|
986
1001
|
} else {
|
|
987
1002
|
top = '<div class="cost-top"><span class="cost-label">Cost was not recorded for this run. ' +
|
|
988
1003
|
'Token usage is itemized below.</span></div>';
|
|
@@ -171,6 +171,12 @@ def _interventions_value(proof):
|
|
|
171
171
|
_IV_TYPES = ("steer", "input", "authorization", "repair")
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
_LIB_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
175
|
+
if _LIB_DIR not in sys.path:
|
|
176
|
+
sys.path.insert(0, _LIB_DIR)
|
|
177
|
+
from efficiency_cost import cost_block_is_partial # noqa: E402
|
|
178
|
+
|
|
179
|
+
|
|
174
180
|
def _cost_value(proof):
|
|
175
181
|
usd = _obj(proof.get("cost")).get("usd")
|
|
176
182
|
if usd is None or isinstance(usd, bool):
|
|
@@ -236,6 +242,7 @@ def _load_runs(loki_dir):
|
|
|
236
242
|
"verdict": verdict,
|
|
237
243
|
"false_verified": false_verified,
|
|
238
244
|
"cost_usd": _cost_value(proof),
|
|
245
|
+
"cost_partial": cost_block_is_partial(_obj(proof.get("cost"))),
|
|
239
246
|
"interventions_by_type": by_type,
|
|
240
247
|
"waited_s": waited_s,
|
|
241
248
|
"active_s": active_s,
|
|
@@ -336,6 +343,9 @@ def outcome_effort(series):
|
|
|
336
343
|
cost_note = "no runs"
|
|
337
344
|
elif accepted == 0:
|
|
338
345
|
cost_note = "no accepted run, cost per accepted is undefined"
|
|
346
|
+
elif any(r.get("cost_partial") for r in series):
|
|
347
|
+
cost_note = "cost partial for %d of %d runs, a lower bound is not a total" % (
|
|
348
|
+
sum(1 for r in series if r.get("cost_partial")), len(series))
|
|
339
349
|
elif unmeasured:
|
|
340
350
|
cost_note = "cost unmeasured for %d of %d runs, a partial sum is not a total" % (unmeasured, len(series))
|
|
341
351
|
else:
|
|
@@ -449,7 +459,7 @@ def compute_trajectory(loki_dir):
|
|
|
449
459
|
"regressing_axes": regressing_axes,
|
|
450
460
|
"series": series,
|
|
451
461
|
"notes": notes,
|
|
452
|
-
"outcome_effort": outcome_effort(
|
|
462
|
+
"outcome_effort": outcome_effort(runs),
|
|
453
463
|
}
|
|
454
464
|
|
|
455
465
|
|
package/autonomy/loki
CHANGED
|
@@ -397,6 +397,24 @@ _loki_update_cache_path() {
|
|
|
397
397
|
echo "$HOME/.loki/cache/update-check-bash.json"
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
+
# _loki_verdict_badge <headline> <url> -> prints a "Verified by Loki" markdown
|
|
401
|
+
# badge for an honesty.headline, or nothing. Exact-match only: VERIFIED
|
|
402
|
+
# (brightgreen), VERIFIED WITH GAPS (orange), NOT VERIFIED (red). Any other
|
|
403
|
+
# headline, or an empty url, omits the badge (never green, never forced red).
|
|
404
|
+
_loki_verdict_badge() {
|
|
405
|
+
local headline="${1:-}" url="${2:-}" message color
|
|
406
|
+
url="${url#"${url%%[![:space:]]*}"}"; url="${url%"${url##*[![:space:]]}"}"
|
|
407
|
+
[ -n "$url" ] || return 0
|
|
408
|
+
case "$headline" in
|
|
409
|
+
"VERIFIED") message="verified"; color="brightgreen" ;;
|
|
410
|
+
"VERIFIED WITH GAPS") message="verified with gaps"; color="orange" ;;
|
|
411
|
+
"NOT VERIFIED") message="not verified"; color="red" ;;
|
|
412
|
+
*) return 0 ;;
|
|
413
|
+
esac
|
|
414
|
+
printf '[](%s)\n' \
|
|
415
|
+
"$message" "${message// /%20}" "$color" "$url"
|
|
416
|
+
}
|
|
417
|
+
|
|
400
418
|
# _loki_semver_newer <latest> <current> -> 0 (true) iff latest is strictly newer.
|
|
401
419
|
# Rejects anything that is not a plain MAJOR.MINOR.PATCH (e.g. "unknown",
|
|
402
420
|
# prereleases): on any unparseable input it returns 1 (never nudge).
|
|
@@ -9973,7 +9991,7 @@ _deploy_latest_receipt() {
|
|
|
9973
9991
|
return 0
|
|
9974
9992
|
}
|
|
9975
9993
|
|
|
9976
|
-
# _deploy_record <dir> <command> <exit_code> <run_id> <anchor_sha> [state] [health_status] [health_url] [rb_attempted] [rb_exit] [receipt_sha256] [started_at] [finished_at] [target] [canary] [post_rollback_health] [health_http_code] [health_match] [tree_sha] [destination]
|
|
9994
|
+
# _deploy_record <dir> <command> <exit_code> <run_id> <anchor_sha> [state] [health_status] [health_url] [rb_attempted] [rb_exit] [receipt_sha256] [started_at] [finished_at] [target] [canary] [post_rollback_health] [health_http_code] [health_match] [tree_sha] [destination] [deployed_url] [health_url_source]
|
|
9977
9995
|
# Writes the executed deploy's own record, then signs it (loki.deploy/1, Ed25519 via the
|
|
9978
9996
|
# receipt signer in loki-ts/src/util/receipt_signer.ts) so it is bound to the receipt that
|
|
9979
9997
|
# authorized it; `loki verify <record>` re-checks it offline. An unsigned record is reported
|
|
@@ -9997,6 +10015,7 @@ _deploy_record() {
|
|
|
9997
10015
|
local rb_attempted="${9:-false}" rb_exit="${10:-}"
|
|
9998
10016
|
local receipt_sha="${11:-}" started_at="${12:-}" finished_at="${13:-}" target="${14:-}" canary="${15:-false}"
|
|
9999
10017
|
local post_rb="${16:-unchecked}" hcode_rec="${17:-}" hmatch="${18:-}" tree_sha="${19:-}" dest_rec="${20:-}"
|
|
10018
|
+
local deployed_url="${21:-}" hsrc="${22:-}"
|
|
10000
10019
|
local ddir="$dir/.loki/deploys"
|
|
10001
10020
|
[ -d "${LOKI_DIR:-}" ] && ddir="${LOKI_DIR}/deploys"
|
|
10002
10021
|
mkdir -p "$ddir" 2>/dev/null || return 0
|
|
@@ -10004,7 +10023,7 @@ _deploy_record() {
|
|
|
10004
10023
|
DR_CMD="$command" DR_RC="$rc" DR_RUN="$run_id" DR_ANCHOR="$anchor" DR_OUT="$f" \
|
|
10005
10024
|
DR_STATE="$anchor_state" DR_HS="$health_status" DR_HU="$health_url" \
|
|
10006
10025
|
DR_RBA="$rb_attempted" DR_RBX="$rb_exit" DR_RSHA="$receipt_sha" DR_T0="$started_at" \
|
|
10007
|
-
DR_T1="$finished_at" DR_TGT="$target" DR_CAN="$canary" DR_PRB="$post_rb" DR_HC="$hcode_rec" DR_HM="$hmatch" DR_TREE="$tree_sha" DR_DEST="$dest_rec" \
|
|
10026
|
+
DR_T1="$finished_at" DR_TGT="$target" DR_CAN="$canary" DR_PRB="$post_rb" DR_HC="$hcode_rec" DR_HM="$hmatch" DR_TREE="$tree_sha" DR_DEST="$dest_rec" DR_DURL="$deployed_url" DR_HSRC="$hsrc" \
|
|
10008
10027
|
python3 -c '
|
|
10009
10028
|
import json, os, datetime
|
|
10010
10029
|
state = os.environ.get("DR_STATE") or "anchored"
|
|
@@ -10034,6 +10053,8 @@ json.dump({
|
|
|
10034
10053
|
"health_match": os.environ.get("DR_HM", ""),
|
|
10035
10054
|
"tree_sha": os.environ.get("DR_TREE", ""),
|
|
10036
10055
|
"destination": os.environ.get("DR_DEST", ""),
|
|
10056
|
+
"deployed_url": os.environ.get("DR_DURL", ""),
|
|
10057
|
+
"health_url_source": os.environ.get("DR_HSRC", ""),
|
|
10037
10058
|
"health_http_code": int(os.environ["DR_HC"]) if os.environ.get("DR_HC", "").isdigit() else None,
|
|
10038
10059
|
}, open(os.environ["DR_OUT"], "w"), indent=2)
|
|
10039
10060
|
' 2>/dev/null || return 0
|
|
@@ -10339,6 +10360,8 @@ _deploy_try_execute() {
|
|
|
10339
10360
|
|
|
10340
10361
|
local rc=0 started_at finished_at purl="" canary_out=""
|
|
10341
10362
|
started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
10363
|
+
local deployed_url="" health_src=""
|
|
10364
|
+
[ -n "$health_url" ] && health_src="flag"
|
|
10342
10365
|
local health_status="unchecked" rb_attempted=false rb_exit="" hcode="" rb_hcode="" post_rb="unchecked"
|
|
10343
10366
|
if [ "$canary_kind" = vercel ]; then
|
|
10344
10367
|
# Staged: a preview deploy (no --prod), probe the preview URL, promote only if healthy.
|
|
@@ -10349,7 +10372,7 @@ _deploy_try_execute() {
|
|
|
10349
10372
|
printf '%s\n' "$canary_out"
|
|
10350
10373
|
purl="$(grep -Eo 'https?://[^[:space:]]+' <<<"$canary_out" | tail -n 1)"
|
|
10351
10374
|
if [ "$rc" -eq 0 ] && [ -n "$purl" ]; then
|
|
10352
|
-
health_url="$purl"
|
|
10375
|
+
health_url="$purl"; deployed_url="$purl"; health_src="captured"
|
|
10353
10376
|
echo "Probing canary health: ${health_url}"
|
|
10354
10377
|
if hcode="$(_deploy_probe_health "$health_url" "$health_match")"; then
|
|
10355
10378
|
health_status="healthy"
|
|
@@ -10365,7 +10388,16 @@ _deploy_try_execute() {
|
|
|
10365
10388
|
rc=4
|
|
10366
10389
|
fi
|
|
10367
10390
|
else
|
|
10368
|
-
( cd "$dir" && eval "$exec_cmd" ) || rc=$?
|
|
10391
|
+
canary_out="$( cd "$dir" && eval "$exec_cmd" )" || rc=$?
|
|
10392
|
+
printf '%s\n' "$canary_out"
|
|
10393
|
+
if [ "$rc" -eq 0 ]; then
|
|
10394
|
+
# Same last-https-URL parse as the canary path; never invented.
|
|
10395
|
+
deployed_url="$(grep -Eo 'https?://[^[:space:]]+' <<<"$canary_out" | tail -n 1)" || deployed_url=""
|
|
10396
|
+
if [ -n "$deployed_url" ]; then
|
|
10397
|
+
echo "Live at: ${deployed_url}"
|
|
10398
|
+
if [ -z "$health_url" ]; then health_url="$deployed_url"; health_src="captured"; fi
|
|
10399
|
+
fi
|
|
10400
|
+
fi
|
|
10369
10401
|
fi
|
|
10370
10402
|
|
|
10371
10403
|
# Health probe only after a deploy that exited 0. Without a URL, or after a
|
|
@@ -10410,7 +10442,7 @@ _deploy_try_execute() {
|
|
|
10410
10442
|
rsha="$(python3 -c 'import json,sys;print((json.load(open(sys.argv[1])).get("verification") or {}).get("hash") or "")' "$pj" 2>/dev/null || printf '')"
|
|
10411
10443
|
rec="$(_deploy_record "$dir" "$exec_cmd" "$rc" "${run_id:-unknown}" "${anchor:-unknown}" \
|
|
10412
10444
|
"$anchor_state" "$health_status" "$health_url" "$rb_attempted" "$rb_exit" \
|
|
10413
|
-
"$rsha" "$started_at" "$finished_at" "${command%% *}" "$canary" "$post_rb" "$hcode" "$health_match" "$(git -C "$dir" rev-parse 'HEAD^{tree}' 2>/dev/null || true)" "$dest")"
|
|
10445
|
+
"$rsha" "$started_at" "$finished_at" "${command%% *}" "$canary" "$post_rb" "$hcode" "$health_match" "$(git -C "$dir" rev-parse 'HEAD^{tree}' 2>/dev/null || true)" "$dest" "$deployed_url" "$health_src")"
|
|
10414
10446
|
echo ""
|
|
10415
10447
|
if [ "$rc" -eq 0 ]; then
|
|
10416
10448
|
echo -e "${GREEN}Deploy command exited 0.${NC}"
|
|
@@ -38275,39 +38307,21 @@ PYEOF
|
|
|
38275
38307
|
# The badge links to the gist we just published (no new egress).
|
|
38276
38308
|
# shields.io static/v1 query endpoint avoids structural hyphens.
|
|
38277
38309
|
local badge
|
|
38278
|
-
badge=$(LOKI_PROOF_JSON="$pj"
|
|
38310
|
+
badge=$(LOKI_PROOF_JSON="$pj" "$_share_py3" -E - <<'PYEOF' 2>/dev/null || true
|
|
38279
38311
|
# The cwd is the checkout being shared: drop it from sys.path before any
|
|
38280
38312
|
# other import, or a committed json.py/os.py could answer this badge text.
|
|
38281
38313
|
import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
|
|
38282
38314
|
import json, os
|
|
38283
|
-
from urllib.parse import quote
|
|
38284
38315
|
try:
|
|
38285
38316
|
d = json.load(open(os.environ["LOKI_PROOF_JSON"]))
|
|
38286
38317
|
except Exception:
|
|
38287
38318
|
d = {}
|
|
38288
|
-
|
|
38289
|
-
|
|
38290
|
-
|
|
38291
|
-
# overrides. Unmapped headline -> no entry -> badge omitted.
|
|
38292
|
-
mapping = {
|
|
38293
|
-
"VERIFIED": ("verified", "brightgreen"),
|
|
38294
|
-
"VERIFIED WITH GAPS": ("verified with gaps", "orange"),
|
|
38295
|
-
"NOT VERIFIED": ("not verified", "red"),
|
|
38296
|
-
}
|
|
38297
|
-
entry = mapping.get(headline)
|
|
38298
|
-
url = os.environ.get("LOKI_GIST_URL", "").strip()
|
|
38299
|
-
# Omit the badge for any unmapped headline (never green, never forced red),
|
|
38300
|
-
# and when we have no link target to point at.
|
|
38301
|
-
if entry and url:
|
|
38302
|
-
message, color = entry
|
|
38303
|
-
badge_img = (
|
|
38304
|
-
"https://img.shields.io/static/v1?label=Loki"
|
|
38305
|
-
"&message=" + quote(message)
|
|
38306
|
-
+ "&color=" + color
|
|
38307
|
-
)
|
|
38308
|
-
print("[]({url})".format(msg=message, img=badge_img, url=url))
|
|
38319
|
+
h = (d.get("honesty") or {}).get("headline")
|
|
38320
|
+
if isinstance(h, str):
|
|
38321
|
+
print(h)
|
|
38309
38322
|
PYEOF
|
|
38310
38323
|
)
|
|
38324
|
+
badge=$(_loki_verdict_badge "$badge" "$LOKI_LAST_GIST_URL")
|
|
38311
38325
|
if [ -n "$badge" ]; then
|
|
38312
38326
|
echo ""
|
|
38313
38327
|
echo "Markdown badge (paste into a README or PR):"
|
package/completions/_loki
CHANGED
|
@@ -242,6 +242,7 @@ function _loki_commands {
|
|
|
242
242
|
'secure:Secure-by-default gate (findings and waivers)'
|
|
243
243
|
'own:Finish-and-own ownership handoff'
|
|
244
244
|
'backup:Back up dashboard state (verified archive)'
|
|
245
|
+
'restore:Restore dashboard state from a verified backup archive'
|
|
245
246
|
'telemetry:OpenTelemetry management'
|
|
246
247
|
'agent:Agent type dispatch'
|
|
247
248
|
'doctor:Check system prerequisites'
|
package/completions/loki.bash
CHANGED
|
@@ -150,7 +150,7 @@ _loki_completion() {
|
|
|
150
150
|
|
|
151
151
|
deploy)
|
|
152
152
|
if [[ "$cur" == -* ]]; then
|
|
153
|
-
COMPREPLY=( $(compgen -W "--execute --canary --health-url --health-match --auto-rollback --dir --no-clip --allow-unanchored --help" -- "$cur") )
|
|
153
|
+
COMPREPLY=( $(compgen -W "--execute --destination --canary --health-url --health-match --auto-rollback --dir --no-clip --allow-unanchored --help" -- "$cur") )
|
|
154
154
|
return 0
|
|
155
155
|
fi
|
|
156
156
|
;;
|
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.17.
|
|
3
|
+
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.17.13. 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 |
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
|
|
4
4
|
|
|
5
|
-
**Version:** v11.17.
|
|
5
|
+
**Version:** v11.17.13
|
|
6
6
|
|
|
7
7
|
**Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
|
|
8
8
|
|