loki-mode 9.55.0 → 9.57.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.
Files changed (97) hide show
  1. package/.github/actions/issue-to-pr/action.yml +165 -29
  2. package/.github/workflows/loki-issue-to-pr.yml +137 -24
  3. package/SKILL.md +2 -2
  4. package/VERSION +1 -1
  5. package/autonomy/lib/proof-check.sh +9 -0
  6. package/autonomy/lib/proof-generator.py +12 -3
  7. package/autonomy/lib/workspace_diff.py +18 -7
  8. package/autonomy/loki +133 -3
  9. package/autonomy/run.sh +385 -22
  10. package/dashboard/__init__.py +1 -1
  11. package/dashboard/api_keys.py +12 -5
  12. package/dashboard/build_supervisor.py +25 -1
  13. package/dashboard/server.py +46 -24
  14. package/dashboard/static/index.html +57 -57
  15. package/docs/VERIFIED-COMPLETION-PLAN.md +7 -5
  16. package/docs/dev/architecture-reference.md +99 -0
  17. package/docs/dev/project-structure.md +134 -0
  18. package/docs/dev/release-checklist.md +157 -0
  19. package/docs/dev/research-foundation.md +29 -0
  20. package/docs/dev/tmp-cleanup.md +157 -0
  21. package/docs/environment-variables.md +1 -0
  22. package/docs/v10/BACKLOG.md +33 -0
  23. package/docs/v10/BOARD.md +97 -27
  24. package/docs/v10/DECISIONS.md +43 -0
  25. package/docs/v10/OPERATING-MODEL.md +47 -0
  26. package/docs/v10/PROGRESS.md +893 -0
  27. package/loki-ts/dist/loki.js +326 -325
  28. package/mcp/__init__.py +1 -1
  29. package/package.json +1 -1
  30. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  31. package/providers/claude.sh +1 -1
  32. package/providers/model_catalog.json +12 -5
  33. package/providers/models.sh +1 -1
  34. package/references/core-workflow.md +1 -1
  35. package/web-app/dist/assets/{AdminPage-CZrBkzRF.js → AdminPage-Oyx-w2Ch.js} +1 -1
  36. package/web-app/dist/assets/{Avatar-gQIp6FBI.js → Avatar-D60m65JB.js} +1 -1
  37. package/web-app/dist/assets/{Badge-D2_fBTFY.js → Badge-CqfZNaem.js} +1 -1
  38. package/web-app/dist/assets/{Button-D5qDd2ef.js → Button-BqjzE4fz.js} +1 -1
  39. package/web-app/dist/assets/CockpitPage-Czk7o_ou.js +47 -0
  40. package/web-app/dist/assets/ComparePage-BEfTrTb1.js +1 -0
  41. package/web-app/dist/assets/{ErrorBoundary-BRABI-BT.js → ErrorBoundary-BrLk5nkO.js} +1 -1
  42. package/web-app/dist/assets/EvidenceReceiptPanel-9vEpi5ir.js +1 -0
  43. package/web-app/dist/assets/{GitHubIssuesPanel-BC5GzKUN.js → GitHubIssuesPanel-D6izZx0w.js} +1 -1
  44. package/web-app/dist/assets/GitHubPRsPanel-BTkG2FgA.js +17 -0
  45. package/web-app/dist/assets/{HomePage-D_CDMoen.js → HomePage-nsr4kpN1.js} +2 -2
  46. package/web-app/dist/assets/{LoginPage-BBNA_N9t.js → LoginPage-BVblQrr9.js} +1 -1
  47. package/web-app/dist/assets/{MagicPage-CnFRZCMx.js → MagicPage-CmnSyLq7.js} +1 -1
  48. package/web-app/dist/assets/{MetricsPage-CPfLMRlh.js → MetricsPage-BVfwU81j.js} +1 -1
  49. package/web-app/dist/assets/{NotFoundPage-lA0XSGTz.js → NotFoundPage-AYQ6uqqI.js} +1 -1
  50. package/web-app/dist/assets/{ProjectPage-CNAPbr9z.js → ProjectPage-DMnbw9X9.js} +3 -3
  51. package/web-app/dist/assets/{ProjectsPage-CMtm8goN.js → ProjectsPage-CNVTAF2N.js} +1 -1
  52. package/web-app/dist/assets/{SettingsPage-BhIjVMYE.js → SettingsPage-D1_qasFD.js} +3 -3
  53. package/web-app/dist/assets/{ShowcasePage-BndtGeYo.js → ShowcasePage-2dBCjLXn.js} +1 -1
  54. package/web-app/dist/assets/{SystemSettingsPage-euXI-KXE.js → SystemSettingsPage-Djo6IE4u.js} +1 -1
  55. package/web-app/dist/assets/{TeamsPage-ClUKecun.js → TeamsPage-CY8p-yxD.js} +1 -1
  56. package/web-app/dist/assets/{TemplatesPage-BWHWVk0L.js → TemplatesPage-B6iFgH6t.js} +1 -1
  57. package/web-app/dist/assets/{TerminalOutput-Cv3N3Szf.js → TerminalOutput-B3yzZe-h.js} +1 -1
  58. package/web-app/dist/assets/{activity-dEhz-bXR.js → activity-CNfAjtRv.js} +1 -1
  59. package/web-app/dist/assets/{bell-B3XUMq1t.js → bell-CMETsHPH.js} +1 -1
  60. package/web-app/dist/assets/{bot-X-ssNlwR.js → bot-C9kXvk1i.js} +1 -1
  61. package/web-app/dist/assets/{check-C_9Nx7kb.js → check-BRK-zLB5.js} +1 -1
  62. package/web-app/dist/assets/{chevron-left-tvNswo3A.js → chevron-left-CxI5uD4g.js} +1 -1
  63. package/web-app/dist/assets/{circle-alert-CSB88nEs.js → circle-alert-CVFRPWpv.js} +1 -1
  64. package/web-app/dist/assets/{clock-C8p2e4NI.js → clock-CLYV8a_x.js} +1 -1
  65. package/web-app/dist/assets/{cloud-CKp2ZvhG.js → cloud-CiISiIOa.js} +1 -1
  66. package/web-app/dist/assets/{code-xml-dY70LCHD.js → code-xml-DHsgqwV-.js} +1 -1
  67. package/web-app/dist/assets/{database-DObn5fmn.js → database-DbvUqtz3.js} +1 -1
  68. package/web-app/dist/assets/{dollar-sign-DSu6n3K3.js → dollar-sign-Ct0lRPHw.js} +1 -1
  69. package/web-app/dist/assets/{file-code-corner-DpCnS73z.js → file-code-corner-D1sjFEh7.js} +1 -1
  70. package/web-app/dist/assets/{file-plus-D3IPKCf1.js → file-plus-DnmX18Fi.js} +1 -1
  71. package/web-app/dist/assets/{globe-C9QXkgH_.js → globe-bSpDNgoA.js} +1 -1
  72. package/web-app/dist/assets/{hammer-D_FtWe1y.js → hammer-Bj41Kvlq.js} +1 -1
  73. package/web-app/dist/assets/{index-CI54rara.css → index-BfQ0f3-S.css} +1 -1
  74. package/web-app/dist/assets/{index-Cz7q1bvC.js → index-CxajNJVu.js} +3 -3
  75. package/web-app/dist/assets/{layers-DsW7jnJX.js → layers-BNtBwYER.js} +1 -1
  76. package/web-app/dist/assets/{loader-circle-B3HspjKR.js → loader-circle-YY_VZofG.js} +1 -1
  77. package/web-app/dist/assets/{lock-DSI29pct.js → lock-rpjEPVoC.js} +1 -1
  78. package/web-app/dist/assets/{package-CLNxtA2x.js → package-desXU6XA.js} +1 -1
  79. package/web-app/dist/assets/{plus-DyI4WC4_.js → plus-7AoOU-I9.js} +1 -1
  80. package/web-app/dist/assets/{refresh-cw-D2dv0xA0.js → refresh-cw-oNj-Xn4B.js} +1 -1
  81. package/web-app/dist/assets/{rotate-ccw-CBvihA-a.js → rotate-ccw-DeiFjfVO.js} +1 -1
  82. package/web-app/dist/assets/{scroll-text-DvoOvAl_.js → scroll-text-Q9AzWiNV.js} +1 -1
  83. package/web-app/dist/assets/{server-DenWvSUt.js → server-DKgVwfek.js} +1 -1
  84. package/web-app/dist/assets/{shield-alert-40cX_FvG.js → shield-alert-G6x-bS-I.js} +1 -1
  85. package/web-app/dist/assets/{thumbs-up-BrjZ6QuV.js → thumbs-up-Qhk0yvw0.js} +1 -1
  86. package/web-app/dist/assets/{trash-2-C6evwkEH.js → trash-2-D23gvznl.js} +1 -1
  87. package/web-app/dist/assets/{trending-down-CfbZis3O.js → trending-down-CSQClPrr.js} +1 -1
  88. package/web-app/dist/assets/{trending-up-DWuEK3WC.js → trending-up-sjMSd1wc.js} +1 -1
  89. package/web-app/dist/assets/{upload-B9DFBcPG.js → upload-Dchovhld.js} +1 -1
  90. package/web-app/dist/assets/{usePolling-BryVC-T1.js → usePolling-DwLceXwF.js} +1 -1
  91. package/web-app/dist/assets/{user-DMRXwZiw.js → user-BqZM8CmD.js} +1 -1
  92. package/web-app/dist/index.html +2 -2
  93. package/web-app/server.py +52 -17
  94. package/web-app/dist/assets/CockpitPage-DVMk2RUK.js +0 -47
  95. package/web-app/dist/assets/ComparePage-NMLyE1xL.js +0 -1
  96. package/web-app/dist/assets/EvidenceReceiptPanel-D2LNKqUx.js +0 -1
  97. package/web-app/dist/assets/GitHubPRsPanel-BSlmoVTS.js +0 -17
package/autonomy/loki CHANGED
@@ -36974,18 +36974,28 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
36974
36974
  echo -e "${YELLOW}NOT CHECKED${NC}: python3 is not on PATH, so the verifier could not run (exit 2)." >&2
36975
36975
  exit 2
36976
36976
  fi
36977
- local _pv_rc=0
36977
+ local _pv_rc=0 _pv_out="" _pv_base_rc=0
36978
+ # Captured, not printed directly, so that --jwks (below) can patch
36979
+ # it: BACKLOG 49 -- when --jwks later downgrades _pv_rc to 1
36980
+ # (ABSENT) or 2 (NOT CHECKED), this output's own "ok: true"/
36981
+ # VERIFIED header from the base hash/drift check must not still be
36982
+ # the last word on stdout. Unflagged (no --jwks), this is printed
36983
+ # byte-for-byte as the base verifier produced it, same as the Bun
36984
+ # route (proof.ts verifyProof) -- ok already agrees with the exit
36985
+ # code there, so there is nothing to patch.
36978
36986
  if [ "$_pv_human" -eq 1 ]; then
36979
- python3 -E "$verifier" --human "$pj" "${TARGET_DIR:-.}" || _pv_rc=$?
36987
+ _pv_out="$(python3 -E "$verifier" --human "$pj" "${TARGET_DIR:-.}")" || _pv_rc=$?
36980
36988
  else
36981
- python3 -E "$verifier" "$pj" "${TARGET_DIR:-.}" || _pv_rc=$?
36989
+ _pv_out="$(python3 -E "$verifier" "$pj" "${TARGET_DIR:-.}")" || _pv_rc=$?
36982
36990
  fi
36991
+ _pv_base_rc="$_pv_rc"
36983
36992
 
36984
36993
  # The attestation check runs AFTER, and can only make the verdict
36985
36994
  # worse, never better. A valid attestation does not excuse a failed
36986
36995
  # integrity hash: the signature proves who produced the bytes, not
36987
36996
  # that the bytes are internally consistent. Folding the two together
36988
36997
  # would let a signed-but-tampered receipt exit 0.
36998
+ local _pv_attestation_field="n/a"
36989
36999
  if [ -n "$_pv_jwks" ]; then
36990
37000
  local _pv_att
36991
37001
  _pv_att="$(loki_proof_attestation_check "$pj" "$_pv_jwks")"
@@ -36994,12 +37004,14 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
36994
37004
  echo "attestation: VERIFIED against ${_pv_jwks}" >&2
36995
37005
  echo " Proven: these bytes were signed by the holder of a key in that set." >&2
36996
37006
  echo " NOT proven: that the code is correct. A receipt records which checks ran." >&2
37007
+ _pv_attestation_field="VERIFIED"
36997
37008
  ;;
36998
37009
  bad)
36999
37010
  echo -e "${RED}attestation: FAILED -- the token does not cover these bytes.${NC}" >&2
37000
37011
  echo " Either the receipt was altered after signing, or the attestation" >&2
37001
37012
  echo " belongs to a different run. Do not trust this receipt." >&2
37002
37013
  _pv_rc=1
37014
+ _pv_attestation_field="FAILED"
37003
37015
  ;;
37004
37016
  none)
37005
37017
  # Exit 1: the caller supplied a key set and asked who
@@ -37011,6 +37023,7 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
37011
37023
  echo " gpg-signed; this says only that it has no JWKS attestation." >&2
37012
37024
  echo " Not verified: a key set was supplied, but the receipt is unsigned (exit 1)." >&2
37013
37025
  _pv_rc=1
37026
+ _pv_attestation_field="ABSENT"
37014
37027
  ;;
37015
37028
  *)
37016
37029
  # Unreachable key set, unreadable file, missing python
@@ -37021,9 +37034,126 @@ print(json.dumps(json.load(open(os.environ['LOKI_PROOF_JSON'])), indent=2))
37021
37034
  echo -e "${YELLOW}attestation: NOT CHECKED -- could not check against ${_pv_jwks} (key set unreadable, or verifier dependency missing).${NC}" >&2
37022
37035
  echo " This is not a failure of the receipt. Nothing was proven either way (exit 2)." >&2
37023
37036
  if [ "$_pv_rc" -eq 0 ]; then _pv_rc=2; fi
37037
+ _pv_attestation_field="NOT CHECKED"
37024
37038
  ;;
37025
37039
  esac
37026
37040
  fi
37041
+ # Only patch when --jwks put a THIRD input (the attestation) into
37042
+ # the verdict. With no --jwks, ok/the header already agree with the
37043
+ # exit code (both come straight from the base verifier), and
37044
+ # output is printed byte-for-byte: unflagged output is identical
37045
+ # to before this change, on both the bash and Bun routes.
37046
+ if [ -n "$_pv_jwks" ]; then
37047
+ # Rewrite the base verifier's output so ok/verdict (JSON) or
37048
+ # the header line (--human) always agree with $_pv_rc, the
37049
+ # exit code this command actually returns. verdict reads
37050
+ # doc["hash_ok"] (the parsed JSON) AND the attestation field
37051
+ # together: hash_ok False is TAMPERED, and a bad signature
37052
+ # against otherwise-good bytes is ALSO TAMPERED (the token
37053
+ # proves the bytes came from an untrusted source, which is the
37054
+ # same class of finding as a rewritten hash); diff_drift True
37055
+ # (or unverifiable, e.g. wrong TARGET_DIR) is drift, NOT
37056
+ # tampering -- loki_remote_verify_receipt draws the same line
37057
+ # -- so drift with an otherwise-valid or absent attestation
37058
+ # must stay FAILED, never TAMPERED.
37059
+ # -E plus the explicit sys.path strip BEFORE importing
37060
+ # json/os: PYTHONPATH and a committed sitecustomize.py must
37061
+ # not run here, same guard as the attestation check above
37062
+ # (loki_proof_attestation_check). The strip runs first so a
37063
+ # shadow json.py planted in the invocation cwd cannot
37064
+ # intercept this import either.
37065
+ _V_BASE_RC="$_pv_base_rc" _V_RC="$_pv_rc" _V_ATTESTATION="$_pv_attestation_field" _V_HUMAN="$_pv_human" python3 -E -c '
37066
+ import sys; sys.path[:] = [p for p in sys.path if p not in ("", ".")]
37067
+ import json, os
37068
+
37069
+ raw = sys.stdin.read()
37070
+ base_rc = int(os.environ["_V_BASE_RC"])
37071
+ rc = int(os.environ["_V_RC"])
37072
+ attestation = os.environ["_V_ATTESTATION"]
37073
+ human = os.environ["_V_HUMAN"] == "1"
37074
+
37075
+ # --human never gets the JSON report (render_reasons only prints a header
37076
+ # plus prose), so doc stays None there and hash_ok cannot be consulted; the
37077
+ # JSON branch below parses raw first so it CAN consult doc.get("hash_ok").
37078
+ doc = None
37079
+ if not human:
37080
+ try:
37081
+ doc = json.loads(raw)
37082
+ except ValueError:
37083
+ doc = None
37084
+
37085
+ if rc == 0:
37086
+ verdict = "VERIFIED"
37087
+ elif rc == 2:
37088
+ # Exit 2 is always "could not check" (missing verifier, malformed
37089
+ # proof.json, or --jwks NOT CHECKED) -- never an accusation.
37090
+ verdict = "NOT CHECKED"
37091
+ elif base_rc != 0:
37092
+ # The base check itself found something: a real tamper/drift finding, or
37093
+ # it could not run at all (malformed proof.json, doc is None). A definite
37094
+ # hash mismatch is TAMPERED. So is a bad signature against otherwise-good
37095
+ # bytes (attestation FAILED): the token proves these bytes were not
37096
+ # produced by a trusted signer, which is the same accusation a rewritten
37097
+ # hash makes, just caught by a different mechanism -- a drifted tree
37098
+ # checked against the WRONG key still means "do not trust this receipt,"
37099
+ # not merely "could not confirm it." Anything else here (drift, drift-
37100
+ # unverifiable, cost/headline incoherence, or a base verifier error) is
37101
+ # FAILED, the same word "loki proof verify --human" already uses for a
37102
+ # non-tamper finding, rather than an accusation the data does not
37103
+ # support. --human never sees doc (it is prose, not JSON), so it relies
37104
+ # on the attestation field alone here; the JSON branch below can also
37105
+ # consult hash_ok.
37106
+ verdict = "TAMPERED" if (attestation == "FAILED" or (doc is not None and doc.get("hash_ok") is False)) else "FAILED"
37107
+ else:
37108
+ # Base check passed (rc would have been 0 otherwise); --jwks is what
37109
+ # pushed the final rc to 1. Only two attestation outcomes do that: a bad
37110
+ # signature (a tamper finding, once the base bytes are known good) or no
37111
+ # signature at all (ABSENT, a fact about the receipt, not an accusation).
37112
+ verdict = "TAMPERED" if attestation == "FAILED" else "ABSENT"
37113
+
37114
+ if human:
37115
+ # The --human vocabulary (render_reasons) is only three words: VERIFIED /
37116
+ # COULD NOT CHECK / FAILED. verdict (five words, shared with the JSON
37117
+ # branch) maps onto it rather than being printed raw, so a TAMPERED or
37118
+ # ABSENT finding still reads as the header word this flag readers
37119
+ # already parse for a failure. --human has no fifth word of its own for
37120
+ # TAMPERED; a real integrity/attestation failure and a non-tamper finding
37121
+ # both print "FAILED" to a human reader, same as before this fix -- only
37122
+ # the JSON verdict field distinguishes the two.
37123
+ header = {
37124
+ "VERIFIED": "VERIFIED",
37125
+ "NOT CHECKED": "COULD NOT CHECK",
37126
+ }.get(verdict, "FAILED")
37127
+ # Only the header line can disagree with rc (it reflects the base check
37128
+ # alone, computed before --jwks); replace just that line so the
37129
+ # per-reason " - ..." detail from the base verifier survives untouched.
37130
+ lines = raw.split("\n")
37131
+ if lines and lines[0].strip() in ("VERIFIED", "COULD NOT CHECK", "FAILED"):
37132
+ lines[0] = header
37133
+ sys.stdout.write("\n".join(lines))
37134
+ sys.exit(0)
37135
+
37136
+ if doc is None:
37137
+ # Base verifier printed something unparseable (should not happen; the
37138
+ # base verifier always emits JSON, even on its own internal error). Pass
37139
+ # it through unchanged rather than fabricate a report over data we cannot
37140
+ # read.
37141
+ sys.stdout.write(raw)
37142
+ sys.exit(0)
37143
+
37144
+ doc["verdict"] = verdict
37145
+ doc["attestation"] = attestation
37146
+ # ok is superseded by verdict, which cannot silently drift from the exit
37147
+ # code the way a second boolean could. Kept for existing readers, but forced
37148
+ # to agree with rc instead of repeating the base verifier own possibly-
37149
+ # passing opinion, so a stdout-only consumer reading "ok" is never told the
37150
+ # opposite of what this process just exited with.
37151
+ doc["ok"] = (rc == 0)
37152
+ print(json.dumps(doc, indent=2))
37153
+ ' <<<"$_pv_out" || printf '%s\n' "$_pv_out"
37154
+ else
37155
+ printf '%s\n' "$_pv_out"
37156
+ fi
37027
37157
  exit "$_pv_rc"
37028
37158
  ;;
37029
37159
  open)