loki-mode 9.49.3 → 9.49.4

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 (78) hide show
  1. package/.github/workflows/loki-issue-to-pr.yml +1 -1
  2. package/README.md +2 -2
  3. package/SKILL.md +2 -2
  4. package/VERSION +1 -1
  5. package/autonomy/lib/proof-verify.py +4 -2
  6. package/dashboard/__init__.py +1 -1
  7. package/dashboard/server.py +21 -8
  8. package/docs/INSTALLATION.md +1 -1
  9. package/docs/P0-SWEEP-PLAN.md +1 -1
  10. package/docs/cursor-comparison.md +1 -1
  11. package/loki-ts/dist/loki.js +240 -240
  12. package/mcp/__init__.py +1 -1
  13. package/package.json +1 -1
  14. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  15. package/skills/quality-gates.md +1 -1
  16. package/web-app/dist/assets/{AdminPage-BA6etdwL.js → AdminPage-y6_b2Xo4.js} +1 -1
  17. package/web-app/dist/assets/{Avatar-CAtbcD23.js → Avatar-BFPkXhIJ.js} +1 -1
  18. package/web-app/dist/assets/{Badge-sb_JbKl0.js → Badge-BF3mhT57.js} +1 -1
  19. package/web-app/dist/assets/{Button-BamXbIWa.js → Button-BDp9aJ9q.js} +1 -1
  20. package/web-app/dist/assets/{CockpitPage-CUYizBtG.js → CockpitPage-5TKICCoA.js} +1 -1
  21. package/web-app/dist/assets/{ComparePage-BnmNTggM.js → ComparePage-BSuNsUHy.js} +1 -1
  22. package/web-app/dist/assets/{ErrorBoundary-BjX4_KOW.js → ErrorBoundary-ut3wrUmu.js} +1 -1
  23. package/web-app/dist/assets/{EvidenceReceiptPanel-Bq6pmNUw.js → EvidenceReceiptPanel-CcaBwEI-.js} +1 -1
  24. package/web-app/dist/assets/{GitHubIssuesPanel-DD8c6RVj.js → GitHubIssuesPanel-DJeYgzAa.js} +1 -1
  25. package/web-app/dist/assets/{GitHubPRsPanel-4NawTXRl.js → GitHubPRsPanel-DurZgSaj.js} +1 -1
  26. package/web-app/dist/assets/{HomePage-DTREi8OE.js → HomePage-Bs451LAy.js} +1 -1
  27. package/web-app/dist/assets/{LoginPage-DavfeQxC.js → LoginPage-HeRGJeZy.js} +1 -1
  28. package/web-app/dist/assets/{MagicPage-BIbTFlwh.js → MagicPage-CUKrqsZs.js} +1 -1
  29. package/web-app/dist/assets/{MetricsPage-Cn3UPKpU.js → MetricsPage-DvY1csgz.js} +1 -1
  30. package/web-app/dist/assets/{NotFoundPage-BVs_nuBv.js → NotFoundPage-BZDZ-B77.js} +1 -1
  31. package/web-app/dist/assets/{ProjectPage-CLplvOHe.js → ProjectPage-CqHkEZkl.js} +3 -3
  32. package/web-app/dist/assets/{ProjectsPage-BBFzBBG_.js → ProjectsPage-CLvCK1Y8.js} +1 -1
  33. package/web-app/dist/assets/{SettingsPage-WPsJ_K9r.js → SettingsPage-DP7OmdwP.js} +1 -1
  34. package/web-app/dist/assets/{ShowcasePage-ZaLaJl5J.js → ShowcasePage-BGS2jTGH.js} +1 -1
  35. package/web-app/dist/assets/{SystemSettingsPage-CCORP-uG.js → SystemSettingsPage-P_rlLh0x.js} +1 -1
  36. package/web-app/dist/assets/{TeamsPage-B6IGT_K2.js → TeamsPage-DxtBDcBC.js} +1 -1
  37. package/web-app/dist/assets/{TemplatesPage-RrYYC3CM.js → TemplatesPage-5e-faH3W.js} +1 -1
  38. package/web-app/dist/assets/{TerminalOutput-CeTvKnkl.js → TerminalOutput-togzv9Zl.js} +1 -1
  39. package/web-app/dist/assets/{activity-CySdOdDP.js → activity-Dreyb_Kr.js} +1 -1
  40. package/web-app/dist/assets/{bell-BHdr5yat.js → bell-DWH0drES.js} +1 -1
  41. package/web-app/dist/assets/{bot-BaB4q_4m.js → bot-78MoCuDl.js} +1 -1
  42. package/web-app/dist/assets/{check-BAgbzVrK.js → check-ikfylPst.js} +1 -1
  43. package/web-app/dist/assets/{chevron-left-CrVUY29C.js → chevron-left-DXUqkd8J.js} +1 -1
  44. package/web-app/dist/assets/{circle-alert-YrWgH49w.js → circle-alert-Da74VO8P.js} +1 -1
  45. package/web-app/dist/assets/{clock-DN8LAN-0.js → clock-mlGCFq8l.js} +1 -1
  46. package/web-app/dist/assets/{cloud-D6C4b1KY.js → cloud-B_Y5ZrIO.js} +1 -1
  47. package/web-app/dist/assets/{code-xml-xBWyKfux.js → code-xml-Bbom7YWV.js} +1 -1
  48. package/web-app/dist/assets/{database-BYqcS6kn.js → database-CLcofiVp.js} +1 -1
  49. package/web-app/dist/assets/{dollar-sign--WOh4k_m.js → dollar-sign-C5mkjXIM.js} +1 -1
  50. package/web-app/dist/assets/{file-code-corner-B0rLPjW1.js → file-code-corner-CVrkaOQP.js} +1 -1
  51. package/web-app/dist/assets/{file-plus-BNhUQ1TQ.js → file-plus-BV9240gX.js} +1 -1
  52. package/web-app/dist/assets/{folder-open-DliiOgFl.js → folder-open-D_6yV78G.js} +1 -1
  53. package/web-app/dist/assets/{globe-BV-hx0u_.js → globe-aZ0Zso4I.js} +1 -1
  54. package/web-app/dist/assets/{hammer-DhmP4GLw.js → hammer-EgOQNukG.js} +1 -1
  55. package/web-app/dist/assets/index-BSgmPwC0.css +1 -0
  56. package/web-app/dist/assets/{index-x0TESFYr.js → index-DvZ0Kk2x.js} +2 -2
  57. package/web-app/dist/assets/{layers-DXWDMSYj.js → layers-BOVKmepE.js} +1 -1
  58. package/web-app/dist/assets/{lightbulb-PBw6hhLS.js → lightbulb-DdASgqOG.js} +1 -1
  59. package/web-app/dist/assets/{loader-circle-B7KiGAVU.js → loader-circle-0K8yLgHl.js} +1 -1
  60. package/web-app/dist/assets/{lock-BUTRYdDQ.js → lock-XzlDMy8M.js} +1 -1
  61. package/web-app/dist/assets/{mail-DaLiNrPb.js → mail-COu670_W.js} +1 -1
  62. package/web-app/dist/assets/{minus-DB_hlGEk.js → minus-bQpUt9ff.js} +1 -1
  63. package/web-app/dist/assets/{package-CymmXoiQ.js → package-BtXTKusI.js} +1 -1
  64. package/web-app/dist/assets/{plus-Cp4AO4I2.js → plus-PxeiD8ju.js} +1 -1
  65. package/web-app/dist/assets/{refresh-cw-ygYTmZTE.js → refresh-cw-CjqzYczt.js} +1 -1
  66. package/web-app/dist/assets/{rotate-ccw-Ba3QxM9K.js → rotate-ccw-CY0os4DH.js} +1 -1
  67. package/web-app/dist/assets/{save-DPm96BnR.js → save-v8qep5cY.js} +1 -1
  68. package/web-app/dist/assets/{server-BapOz_vf.js → server-pIIs9vNp.js} +1 -1
  69. package/web-app/dist/assets/{shield-alert-DYzfVqWS.js → shield-alert-CsDqKXwA.js} +1 -1
  70. package/web-app/dist/assets/{thumbs-up-DxOTplej.js → thumbs-up-CAh3HVzH.js} +1 -1
  71. package/web-app/dist/assets/{trash-2-Br8dh_6B.js → trash-2-Dt6urTyY.js} +1 -1
  72. package/web-app/dist/assets/{trending-down-CxSP6MU3.js → trending-down-DrtBd_C9.js} +1 -1
  73. package/web-app/dist/assets/{trending-up-CXx0ywZb.js → trending-up-D-DbCHXm.js} +1 -1
  74. package/web-app/dist/assets/{upload-CpEO3qNc.js → upload-D4l6DCOt.js} +1 -1
  75. package/web-app/dist/assets/{usePolling-BuzXwjmC.js → usePolling-D0aoTX80.js} +1 -1
  76. package/web-app/dist/assets/{user-DUUYT47B.js → user-BQf_XJO_.js} +1 -1
  77. package/web-app/dist/index.html +2 -2
  78. package/web-app/dist/assets/index-D98GzaW8.css +0 -1
@@ -61,7 +61,7 @@ jobs:
61
61
  # Pinned to a TAG, not @main. A moving ref would hand every user
62
62
  # whatever main holds at the moment their issue is labeled, including a
63
63
  # half-landed release. Bump this line deliberately.
64
- uses: asklokesh/loki-mode/.github/actions/issue-to-pr@v9.49.3
64
+ uses: asklokesh/loki-mode/.github/actions/issue-to-pr@v9.49.4
65
65
  env:
66
66
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
67
67
  with:
package/README.md CHANGED
@@ -532,14 +532,14 @@ Required:
532
532
 
533
533
  - An agent provider CLI: [Claude Code](https://docs.claude.com/en/docs/claude-code) (`claude`, Tier 1, recommended and E2E-verified - the provider Loki Mode is built for). Cline, Codex, Aider, and opencode are supported as experimental providers (wiring in place; not yet E2E-verified by us). Loki cannot run a build without one of these installed and authenticated.
534
534
  - Python 3.10+ (`python3`) for the dashboard, memory system, and orchestration helpers.
535
+ - Node.js 18+ (`node`) for the npm install path and the bundled runtime.
536
+ - `jq` for the JSON that the shell flows and the quality gates parse.
535
537
  - Git 2.x (`git`) for checkpoints and worktrees.
536
538
  - `curl` for installation and network calls.
537
539
 
538
540
  Recommended:
539
541
 
540
542
  - Bun 1.3.0+ (`bun`) for the fast runtime (the recommended install path above installs it).
541
- - Node.js 18+ and npm if you install via npm instead of Bun.
542
- - `jq` for nicer JSON handling in shell flows.
543
543
  - Docker if you want Loki's App Runner to run containerized projects, or to run Loki itself from the published image.
544
544
 
545
545
  You also need credentials for whichever provider you use (for Claude Code, an authenticated `claude` login or `ANTHROPIC_API_KEY`). `loki doctor` flags a missing or unauthenticated provider as the first thing to fix.
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v9.49.3
6
+ # Loki Mode v9.49.4
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
470
470
 
471
471
  ---
472
472
 
473
- **v9.49.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
473
+ **v9.49.4 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 9.49.3
1
+ 9.49.4
@@ -324,7 +324,8 @@ def _compute_headline(facts, degraded):
324
324
  or tests.get("status") == "failed"
325
325
  or build.get("status") == "failed"
326
326
  or any(g.get("status") == "failed"
327
- for g in (facts.get("quality_gates") or []))
327
+ for g in (facts.get("quality_gates") or [])
328
+ if _is_exogenous(g))
328
329
  or sec_high
329
330
  or fn_failed
330
331
  )
@@ -342,7 +343,8 @@ def _compute_headline(facts, degraded):
342
343
  tests.get("status") == "verified"
343
344
  or build.get("status") == "verified"
344
345
  or any(g.get("status") == "passed"
345
- for g in (facts.get("quality_gates") or []))
346
+ for g in (facts.get("quality_gates") or [])
347
+ if _is_exogenous(g))
346
348
  )
347
349
  if any_verified and degraded:
348
350
  return "VERIFIED WITH GAPS"
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "9.49.3"
10
+ __version__ = "9.49.4"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -261,16 +261,16 @@ def _real_client_host(request: Request):
261
261
  client = getattr(request, "client", None)
262
262
  host = getattr(client, "host", None) if client else None
263
263
  if host is None:
264
- return None
264
+ return None, False
265
265
  if host in _trusted_proxies():
266
266
  fwd = request.headers.get("x-forwarded-for", "")
267
267
  first = fwd.split(",")[0].strip()
268
268
  if first:
269
- return first
270
- return host
269
+ return first, True
270
+ return host, False
271
271
 
272
272
 
273
- def _is_local_caller(host) -> bool:
273
+ def _is_local_caller(host, from_forwarded: bool = False) -> bool:
274
274
  """True only for a caller we can positively identify as non-routable.
275
275
 
276
276
  A peer that is not an IP literal (ASGI test transports report
@@ -285,7 +285,18 @@ def _is_local_caller(host) -> bool:
285
285
  try:
286
286
  return ipaddress.ip_address(host).is_loopback
287
287
  except ValueError:
288
- return True
288
+ # A DIRECT peer that is not an IP literal is genuinely local: ASGI test
289
+ # transports report "testclient" and UDS transports report names, and
290
+ # refusing those closes no hole while breaking 17 tests.
291
+ #
292
+ # A FORWARDED value is different in kind. It arrived inside a request
293
+ # header, so treating an unparseable one as local lets any caller behind
294
+ # a trusted proxy pass the local check by sending a non-IP token.
295
+ # Measured: with LOKI_TRUSTED_PROXIES set, "not-an-ip", "unknown" and
296
+ # "testclient" all returned local, while an empty value correctly did
297
+ # not. "unknown" is a literal some proxies emit when the client address
298
+ # is unavailable, so this is reachable without an attacker choosing it.
299
+ return not from_forwarded
289
300
 
290
301
 
291
302
  def require_local_or_authenticated(request: Request) -> None:
@@ -316,13 +327,13 @@ def require_local_or_authenticated(request: Request) -> None:
316
327
  """
317
328
  if auth.ENTERPRISE_AUTH_ENABLED or auth.OIDC_ENABLED:
318
329
  return
319
- host = _real_client_host(request)
330
+ host, host_from_forwarded = _real_client_host(request)
320
331
  if host is None:
321
332
  raise HTTPException(
322
333
  status_code=403,
323
334
  detail="control requires an identifiable client; enable "
324
335
  "LOKI_ENTERPRISE_AUTH to allow remote access")
325
- if _is_local_caller(host):
336
+ if _is_local_caller(host, host_from_forwarded):
326
337
  return
327
338
  raise HTTPException(
328
339
  status_code=403,
@@ -1211,14 +1222,16 @@ class WebSocketBoundaryMiddleware:
1211
1222
  if not (auth.ENTERPRISE_AUTH_ENABLED or auth.OIDC_ENABLED):
1212
1223
  client = scope.get("client")
1213
1224
  host = client[0] if client else None
1225
+ host_from_forwarded = False
1214
1226
  if host in _trusted_proxies():
1215
1227
  for raw_name, raw_value in scope.get("headers", []):
1216
1228
  if raw_name == b"x-forwarded-for":
1217
1229
  first = raw_value.decode("latin-1").split(",")[0].strip()
1218
1230
  if first:
1219
1231
  host = first
1232
+ host_from_forwarded = True
1220
1233
  break
1221
- if not _is_local_caller(host):
1234
+ if not _is_local_caller(host, host_from_forwarded):
1222
1235
  # 1008 = policy violation. Closing with a code beats an
1223
1236
  # accept-then-drop, which reads to a client as a flaky network.
1224
1237
  await send({"type": "websocket.close", "code": 1008})
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v9.49.3
5
+ **Version:** v9.49.4
6
6
 
7
7
  ---
8
8
 
@@ -42,7 +42,7 @@ Honest count after this sweep: 8 gates.
42
42
  | 5 | Mock Integrity Detector | enforce_mock_integrity -> tests/detect-mock-problems.sh | Yes (HIGH blocks) | LOKI_GATE_MOCK=false |
43
43
  | 6 | Test Mutation Detector | enforce_mutation_integrity -> tests/detect-test-mutations.sh | Yes (HIGH blocks) | LOKI_GATE_MUTATION=false |
44
44
  | 7 | Documentation Coverage | run_doc_quality_gate (run.sh:7388) | Yes | LOKI_GATE_DOC_COVERAGE=false |
45
- | 8 | Magic Modules Debate | run_magic_debate_gate (run.sh:7495) | Yes (BLOCK sev) | LOKI_GATE_MAGIC_DEBATE=false |
45
+ | 8 | Magic Modules Debate | run_magic_debate_gate (run.sh:7495) | No, advisory by default. NOTE added 2026-09-13: this row said "Yes (BLOCK sev)" and that was never true on either route; bash enforces only with LOKI_GATE_MAGIC_DEBATE_BLOCKING=true and Bun self-skips unless LOKI_GATE_MAGIC_DEBATE="true". Line numbers in this document are as grep-verified in June 2026 and are left unchanged. | LOKI_GATE_MAGIC_DEBATE=false |
46
46
 
47
47
  Conditional auditor (documented separately, NOT numbered): Backward-Compatibility
48
48
  / legacy-healing-auditor (healing mode only). Removed: Input/Output Guardrails.
@@ -76,7 +76,7 @@ velocity_quality_balance:
76
76
  5. Mock Integrity Detector - HIGH blocks
77
77
  6. Test Mutation Detector - HIGH blocks
78
78
  7. Documentation Coverage
79
- 8. Magic Modules Debate - BLOCK severity
79
+ 8. Magic Modules Debate - advisory by default on both routes (bash needs LOKI_GATE_MAGIC_DEBATE_BLOCKING=true; Bun self-skips unless LOKI_GATE_MAGIC_DEBATE="true")
80
80
 
81
81
  Conditional auditor (not numbered): backward-compatibility / legacy-healing-auditor (healing mode only).
82
82