loki-mode 7.46.0 → 7.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +113 -0
- package/autonomy/run.sh +90 -4
- package/autonomy/spec-interrogation.sh +549 -0
- package/dashboard/__init__.py +1 -1
- package/dashboard/auth.py +117 -2
- package/docs/ACKNOWLEDGEMENTS.md +1 -1
- package/docs/COMPETITIVE-ANALYSIS.md +1 -1
- package/docs/INSTALLATION.md +2 -2
- package/docs/OPEN-CORE-BOUNDARY.md +6 -5
- package/docs/P2-SPEC-ROBUSTNESS-PLAN.md +192 -0
- package/docs/R9-OPEN-CORE-HOOKS-PLAN.md +2 -2
- package/docs/auto-claude-comparison.md +2 -2
- package/docs/certification/README.md +1 -1
- package/docs/competitive/bolt-new-analysis.md +1 -1
- package/docs/competitive/emergence-others-analysis.md +6 -6
- package/docs/competitive/replit-lovable-analysis.md +4 -4
- package/docs/enterprise/security.md +43 -3
- package/docs/show-hn-post.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-CKUOsWZW.js → AdminPage-CcCJ0Sjt.js} +1 -1
- package/web-app/dist/assets/{Avatar-CL9Id9Hi.js → Avatar-DK8kmayw.js} +1 -1
- package/web-app/dist/assets/{Badge-B12zwlD7.js → Badge-4uAWnemi.js} +1 -1
- package/web-app/dist/assets/{Button-CFLVoduT.js → Button-BBMk33tk.js} +1 -1
- package/web-app/dist/assets/ComparePage-bt9rwvST.js +1 -0
- package/web-app/dist/assets/{GitHubIssuesPanel-CSitxtAX.js → GitHubIssuesPanel-WDbH47UM.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BIT06FRo.js → GitHubPRsPanel-C2CiYtTx.js} +1 -1
- package/web-app/dist/assets/{HomePage-pU_0fGny.js → HomePage-BQk-MUjn.js} +4 -4
- package/web-app/dist/assets/{LoginPage-DTZtt2Yb.js → LoginPage-DMOZVGGL.js} +1 -1
- package/web-app/dist/assets/{MagicPage-10zfra8o.js → MagicPage-Bzp2Nt1z.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-C-wiKUkv.js → MetricsPage-C39JVdsw.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-BDkcmhYe.js → NotFoundPage-6vT_U9UL.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-CiCavQ8n.js → ProjectPage-BfFcZp-E.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-BLCXQwwC.js → ProjectsPage-CPMBf8Wt.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-PkxtaMyg.js → SettingsPage-BnNN6ETl.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-iECp8Tha.js → ShowcasePage-WDrMf-cx.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DS6Anno1.js → SystemSettingsPage-DX4jb2e8.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-ls6h6bNL.js → TeamsPage-BCfqcXzu.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-Bk0QzlPt.js → TemplatesPage-CZvmimDj.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-4-1hWCtZ.js → TerminalOutput-BlRqFwWV.js} +1 -1
- package/web-app/dist/assets/{activity-DH3ih2nS.js → activity-CacZsUyr.js} +1 -1
- package/web-app/dist/assets/{bell-Gn17S6uv.js → bell-DK2qtHnk.js} +1 -1
- package/web-app/dist/assets/{bot-Cbycc3VE.js → bot-CkcUtHad.js} +1 -1
- package/web-app/dist/assets/{check-nIAqa-kf.js → check-CbCPjX3M.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D2jcWDll.js → chevron-left-5NUKWw3i.js} +1 -1
- package/web-app/dist/assets/{circle-alert-CpL4Bhvt.js → circle-alert-S7uFoxC2.js} +1 -1
- package/web-app/dist/assets/{clock-IW4Wq86N.js → clock-CaQRrIrs.js} +1 -1
- package/web-app/dist/assets/{cloud-Cn8nNuH2.js → cloud-DBAX6c0r.js} +1 -1
- package/web-app/dist/assets/{code-xml-BiJBteXf.js → code-xml-De5-EXv3.js} +1 -1
- package/web-app/dist/assets/{copy-CnqkyNsi.js → copy-CUkT6k1v.js} +1 -1
- package/web-app/dist/assets/{database-CKSReqa5.js → database-BAWf1Gwt.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CDzDY64R.js → dollar-sign-Ji8zk86R.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Box4IwG1.js → file-code-corner-ChtXoBwS.js} +1 -1
- package/web-app/dist/assets/{file-plus-DpGqlXF8.js → file-plus-bFa37P76.js} +1 -1
- package/web-app/dist/assets/{folder-open-B57dAoBv.js → folder-open-DhXpXscO.js} +1 -1
- package/web-app/dist/assets/{git-commit-horizontal-BVbucmO5.js → git-commit-horizontal-DVPeDQ3j.js} +1 -1
- package/web-app/dist/assets/{globe-BkOnKl4x.js → globe-BPZgPeeu.js} +1 -1
- package/web-app/dist/assets/{hammer-DRbIQ4QU.js → hammer-jLCaujYH.js} +1 -1
- package/web-app/dist/assets/{index-CM_b_EhP.js → index-B-0iHBPO.js} +2 -2
- package/web-app/dist/assets/{layers-B78BiFiU.js → layers-B1vsrsFW.js} +1 -1
- package/web-app/dist/assets/{lightbulb-B-Itbm9g.js → lightbulb-C-uLoq9Y.js} +1 -1
- package/web-app/dist/assets/{loader-circle-Oq6NQhW2.js → loader-circle-JTfD-ZuM.js} +1 -1
- package/web-app/dist/assets/{lock-DbJ9zxbw.js → lock-G9rxD4gZ.js} +1 -1
- package/web-app/dist/assets/{mail-CzMRod6m.js → mail-BJ0PTN_V.js} +1 -1
- package/web-app/dist/assets/{package-WZ5osvej.js → package-CXClfLOO.js} +1 -1
- package/web-app/dist/assets/{plus-j08lFR-K.js → plus-EoL5OCB7.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CIr7E-g2.js → refresh-cw-BjREUnVq.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-gwoXxDeE.js → rotate-ccw-DahWX07H.js} +1 -1
- package/web-app/dist/assets/{save-B8fV_ZpE.js → save-Dek3gCn1.js} +1 -1
- package/web-app/dist/assets/{server-D5dO1paz.js → server-D6V1BAia.js} +1 -1
- package/web-app/dist/assets/{shield-alert-Du08zhdg.js → shield-alert-BtTK5Sxb.js} +1 -1
- package/web-app/dist/assets/{trash-2-DEKSVae5.js → trash-2-BT5o_g0r.js} +1 -1
- package/web-app/dist/assets/{trending-down-DBiXUtxJ.js → trending-down-D4Jk7KF3.js} +1 -1
- package/web-app/dist/assets/{trending-up-BgmK_tHq.js → trending-up-EQFTzhEo.js} +1 -1
- package/web-app/dist/assets/{upload-IaViyeVD.js → upload-JfI5lCSE.js} +1 -1
- package/web-app/dist/assets/{usePolling-PiRLqNu6.js → usePolling-BnhPUuGd.js} +1 -1
- package/web-app/dist/assets/{user-BB5J8wAF.js → user-DSUiUYtj.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/web-app/dist/assets/ComparePage-Dg0UdZAk.js +0 -1
package/dashboard/auth.py
CHANGED
|
@@ -477,6 +477,111 @@ def _base64url_decode(data: str) -> bytes:
|
|
|
477
477
|
return base64.urlsafe_b64decode(data)
|
|
478
478
|
|
|
479
479
|
|
|
480
|
+
# Role precedence (highest privilege first). When a token carries multiple
|
|
481
|
+
# recognized role claims, the highest-privilege match wins.
|
|
482
|
+
_ROLE_PRECEDENCE = ("admin", "operator", "auditor", "viewer")
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _normalize_claim_values(value) -> set[str]:
|
|
486
|
+
"""Normalize an OIDC claim value into a lowercased set of strings.
|
|
487
|
+
|
|
488
|
+
Claim values may be a single string, a space-separated string, or a
|
|
489
|
+
list of strings (different providers use different shapes). All are
|
|
490
|
+
flattened into a set of lowercased tokens for matching against ROLES.
|
|
491
|
+
"""
|
|
492
|
+
out: set[str] = set()
|
|
493
|
+
if value is None:
|
|
494
|
+
return out
|
|
495
|
+
if isinstance(value, str):
|
|
496
|
+
for part in value.split():
|
|
497
|
+
if part:
|
|
498
|
+
out.add(part.strip().lower())
|
|
499
|
+
elif isinstance(value, (list, tuple, set)):
|
|
500
|
+
for item in value:
|
|
501
|
+
if isinstance(item, str):
|
|
502
|
+
s = item.strip().lower()
|
|
503
|
+
if s:
|
|
504
|
+
out.add(s)
|
|
505
|
+
return out
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _collect_role_claims(claims: dict) -> set[str]:
|
|
509
|
+
"""Collect candidate role/group values from standard OIDC claim shapes.
|
|
510
|
+
|
|
511
|
+
Recognized sources (case-insensitive values flattened into one set):
|
|
512
|
+
- A configurable claim named by LOKI_OIDC_ROLES_CLAIM (supports a dotted
|
|
513
|
+
path for nested claims, e.g. "realm_access.roles" for Keycloak).
|
|
514
|
+
- "roles" (generic)
|
|
515
|
+
- "groups" (generic)
|
|
516
|
+
- "realm_access.roles" (Keycloak)
|
|
517
|
+
- "cognito:groups" (AWS Cognito)
|
|
518
|
+
|
|
519
|
+
Note: "groups"/"cognito:groups" typically carry arbitrary group names,
|
|
520
|
+
not Loki role names. Only values that exactly match one of the four
|
|
521
|
+
built-in role names (admin/operator/viewer/auditor, case-insensitive)
|
|
522
|
+
grant a role. Everything else is ignored and the default role applies.
|
|
523
|
+
"""
|
|
524
|
+
candidates: set[str] = set()
|
|
525
|
+
|
|
526
|
+
def _read_dotted(path: str):
|
|
527
|
+
node = claims
|
|
528
|
+
for key in path.split("."):
|
|
529
|
+
if isinstance(node, dict) and key in node:
|
|
530
|
+
node = node[key]
|
|
531
|
+
else:
|
|
532
|
+
return None
|
|
533
|
+
return node
|
|
534
|
+
|
|
535
|
+
configured = os.environ.get("LOKI_OIDC_ROLES_CLAIM", "").strip()
|
|
536
|
+
sources = []
|
|
537
|
+
if configured:
|
|
538
|
+
sources.append(configured)
|
|
539
|
+
sources.extend(["roles", "groups", "realm_access.roles", "cognito:groups"])
|
|
540
|
+
|
|
541
|
+
for src in sources:
|
|
542
|
+
if "." in src:
|
|
543
|
+
val = _read_dotted(src)
|
|
544
|
+
else:
|
|
545
|
+
val = claims.get(src)
|
|
546
|
+
candidates |= _normalize_claim_values(val)
|
|
547
|
+
|
|
548
|
+
return candidates
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _default_oidc_role() -> str:
|
|
552
|
+
"""Return the configured default OIDC role, validated against ROLES.
|
|
553
|
+
|
|
554
|
+
Defaults to the least-privileged role ("viewer"). If LOKI_OIDC_DEFAULT_ROLE
|
|
555
|
+
is set to an unrecognized value, falls back to "viewer" (never admin).
|
|
556
|
+
"""
|
|
557
|
+
configured = os.environ.get("LOKI_OIDC_DEFAULT_ROLE", "").strip().lower()
|
|
558
|
+
if configured in ROLES:
|
|
559
|
+
return configured
|
|
560
|
+
return "viewer"
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def _scopes_from_claims(claims: dict) -> tuple[list[str], str]:
|
|
564
|
+
"""Map OIDC token claims to Loki scopes via the existing ROLES mapping.
|
|
565
|
+
|
|
566
|
+
Returns a tuple of (scopes, role_name). If no recognized role claim is
|
|
567
|
+
present, the safe default role (viewer, or LOKI_OIDC_DEFAULT_ROLE) is
|
|
568
|
+
applied. This function NEVER returns ["*"]/admin by default: full access
|
|
569
|
+
is granted only when an explicit admin role claim is present.
|
|
570
|
+
"""
|
|
571
|
+
candidate_values = _collect_role_claims(claims)
|
|
572
|
+
|
|
573
|
+
matched_role = None
|
|
574
|
+
for role in _ROLE_PRECEDENCE:
|
|
575
|
+
if role in candidate_values:
|
|
576
|
+
matched_role = role
|
|
577
|
+
break
|
|
578
|
+
|
|
579
|
+
if matched_role is None:
|
|
580
|
+
matched_role = _default_oidc_role()
|
|
581
|
+
|
|
582
|
+
return resolve_scopes(matched_role), matched_role
|
|
583
|
+
|
|
584
|
+
|
|
480
585
|
def validate_oidc_token(token_str: str) -> Optional[dict]:
|
|
481
586
|
"""Validate an OIDC JWT token.
|
|
482
587
|
|
|
@@ -489,6 +594,12 @@ def validate_oidc_token(token_str: str) -> Optional[dict]:
|
|
|
489
594
|
- Audience matches OIDC_AUDIENCE or OIDC_CLIENT_ID
|
|
490
595
|
- Token is not expired
|
|
491
596
|
|
|
597
|
+
On success, role/group claims are mapped to Loki roles (admin/operator/
|
|
598
|
+
viewer/auditor) via _scopes_from_claims. When no recognized role claim is
|
|
599
|
+
present, the least-privileged default role (viewer, configurable via
|
|
600
|
+
LOKI_OIDC_DEFAULT_ROLE) is applied. OIDC users are never granted ["*"]
|
|
601
|
+
unless an explicit admin role claim is present.
|
|
602
|
+
|
|
492
603
|
SECURITY CRITICAL: Without PyJWT, JWT signatures are NOT cryptographically
|
|
493
604
|
verified. An attacker can forge tokens with arbitrary claims. For any
|
|
494
605
|
production deployment, you MUST install PyJWT + cryptography so that
|
|
@@ -529,11 +640,13 @@ def validate_oidc_token(token_str: str) -> Optional[dict]:
|
|
|
529
640
|
issuer=OIDC_ISSUER,
|
|
530
641
|
)
|
|
531
642
|
|
|
643
|
+
scopes, role = _scopes_from_claims(decoded)
|
|
532
644
|
return {
|
|
533
645
|
"id": decoded.get("sub", ""),
|
|
534
646
|
"name": decoded.get("name", decoded.get("email", decoded.get("sub", ""))),
|
|
535
647
|
"email": decoded.get("email", ""),
|
|
536
|
-
"scopes":
|
|
648
|
+
"scopes": scopes, # mapped from OIDC role/group claims
|
|
649
|
+
"role": role,
|
|
537
650
|
"auth_method": "oidc",
|
|
538
651
|
"issuer": decoded.get("iss"),
|
|
539
652
|
}
|
|
@@ -602,11 +715,13 @@ def validate_oidc_token(token_str: str) -> Optional[dict]:
|
|
|
602
715
|
return None
|
|
603
716
|
|
|
604
717
|
# Return user info from claims
|
|
718
|
+
scopes, role = _scopes_from_claims(claims)
|
|
605
719
|
return {
|
|
606
720
|
"id": claims.get("sub", ""),
|
|
607
721
|
"name": claims.get("name", claims.get("email", claims.get("sub", ""))),
|
|
608
722
|
"email": claims.get("email", ""),
|
|
609
|
-
"scopes":
|
|
723
|
+
"scopes": scopes, # mapped from OIDC role/group claims
|
|
724
|
+
"role": role,
|
|
610
725
|
"auth_method": "oidc",
|
|
611
726
|
"issuer": claims.get("iss"),
|
|
612
727
|
}
|
package/docs/ACKNOWLEDGEMENTS.md
CHANGED
|
@@ -336,7 +336,7 @@ Based on research synthesis, the following improvements are planned:
|
|
|
336
336
|
|
|
337
337
|
This acknowledgements file documents the research and resources that influenced Loki Mode's design. All referenced works retain their original licenses and copyrights.
|
|
338
338
|
|
|
339
|
-
Loki Mode itself is released under the
|
|
339
|
+
Loki Mode itself is released under the Business Source License 1.1 (BUSL-1.1), a source-available license.
|
|
340
340
|
|
|
341
341
|
---
|
|
342
342
|
|
|
@@ -45,7 +45,7 @@ GSD is the closest competitor -- a context engineering system that spawns fresh
|
|
|
45
45
|
| **Enterprise Security** | `--dangerously-skip-permissions` | MCP sandboxed | Sandboxed | Audit logs, RBAC | Staged autonomy | Sandboxed |
|
|
46
46
|
| **Cross-Project Learning** | No | AgentDB | No | No | No | Limited |
|
|
47
47
|
| **Observability** | Dashboard + STATUS.txt | Real-time tracing | Logs | Full tracing | Built-in | Full |
|
|
48
|
-
| **Pricing** | Free (
|
|
48
|
+
| **Pricing** | Free (source-available) | Free (OSS) | Free (OSS) | $25+/mo | $20-400/mo | $20-500/mo |
|
|
49
49
|
| **Production Ready** | Experimental | Production | Production | Production | Production | Production |
|
|
50
50
|
| **Resource Monitoring** | Yes (v2.18.5) | Unknown | No | No | No | No |
|
|
51
51
|
| **State Recovery** | Yes (checkpoints) | Yes (AgentDB) | Limited | Yes | Git worktrees | Yes |
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v7.
|
|
5
|
+
**Version:** v7.47.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -389,7 +389,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
|
|
|
389
389
|
# Run Loki Mode in Docker (Claude provider, API-key auth)
|
|
390
390
|
docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
|
|
391
391
|
-v $(pwd):/workspace -w /workspace \
|
|
392
|
-
asklokesh/loki-mode:7.
|
|
392
|
+
asklokesh/loki-mode:7.47.0 start ./my-spec.md
|
|
393
393
|
```
|
|
394
394
|
|
|
395
395
|
##### docker compose + .env (no host install)
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# Loki Mode open-core boundary
|
|
2
2
|
|
|
3
|
-
Loki Mode is and stays
|
|
4
|
-
|
|
3
|
+
Loki Mode is and stays source-available (BUSL-1.1) and free to self-host. This
|
|
4
|
+
document draws the line between what is free forever and what
|
|
5
|
+
hosted/paid/enterprise plans would add on top. R9 ships
|
|
5
6
|
the SEAMS for that line; it does not ship a hosted backend, a license server, or
|
|
6
7
|
any paywall on existing functionality.
|
|
7
8
|
|
|
8
9
|
## Principle
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
today runs locally, free, with no account, no license key,
|
|
12
|
-
to any Loki service. Hosted/paid features are ADDITIVE convenience and
|
|
11
|
+
The free self-hosted tier is fully functional with zero hosted backend. Every
|
|
12
|
+
capability Loki has today runs locally, free, with no account, no license key,
|
|
13
|
+
and no network call to any Loki service. Hosted/paid features are ADDITIVE convenience and
|
|
13
14
|
team/enterprise layers, never a removal or gating of something that is free
|
|
14
15
|
today.
|
|
15
16
|
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# P2 Spec Robustness Plan (P2-1 spec interrogation gate + P2-2 assumption ledger)
|
|
2
|
+
|
|
3
|
+
Status: design for implementation. No version bump, no commit in this arc.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Loki must stay accurate even when the input spec is WRONG, ambiguous, or
|
|
8
|
+
incomplete. Today two building blocks already detect spec defects but neither
|
|
9
|
+
feeds the autonomous loop:
|
|
10
|
+
|
|
11
|
+
- `autonomy/grill.sh` invokes the provider once with a Devil's-Advocate prompt
|
|
12
|
+
and writes 10-15 hardest spec questions to `.loki/grill/report.md`. It is
|
|
13
|
+
CLI-only (`grep grill autonomy/run.sh` = 0 invocations) and nothing reads its
|
|
14
|
+
output.
|
|
15
|
+
- `autonomy/prd-analyzer.py` detects missing PRD dimensions and has a
|
|
16
|
+
deterministic `_make_assumption()` map, writing `.loki/prd-observations.md`,
|
|
17
|
+
which nothing reads. Its interactive Q&A is inert in non-TTY (autonomous) runs.
|
|
18
|
+
|
|
19
|
+
The fix: run interrogation automatically in DISCOVERY, classify the findings,
|
|
20
|
+
record every spec gap as a first-class ASSUMPTION in a tracked ledger, BLOCK
|
|
21
|
+
completion while high-severity assumptions are unconfirmed-and-unacknowledged,
|
|
22
|
+
and surface the ledger in the proof-of-done output. Defects are SURFACED as
|
|
23
|
+
recorded assumptions, never silently autocorrected.
|
|
24
|
+
|
|
25
|
+
## Core design decision: auto-acknowledgment lifecycle (prevents the trap)
|
|
26
|
+
|
|
27
|
+
A naive "block completion while any high-severity assumption is unconfirmed"
|
|
28
|
+
hard-blocks EVERY ambiguous run to max-iterations, because in autonomous
|
|
29
|
+
(non-TTY) mode no human can ever set `confirmed=yes`. We never reach the
|
|
30
|
+
"done, plus here is what I assumed" output the goal demands.
|
|
31
|
+
|
|
32
|
+
Resolution: split the gate from the lifecycle.
|
|
33
|
+
|
|
34
|
+
- The gate `council_assumption_ledger_gate` is a PURE function of ledger state.
|
|
35
|
+
It blocks iff an entry has `severity=high AND confirmed=false AND
|
|
36
|
+
acknowledged=false`.
|
|
37
|
+
- The auto-acknowledgment lifecycle lives in run.sh (NOT in the gate). Once an
|
|
38
|
+
assumption has been written into the ledger AND injected into the build prompt
|
|
39
|
+
at least once, run.sh marks it `acknowledged=true`. Default-on; opt-out
|
|
40
|
+
`LOKI_ASSUMPTIONS_REQUIRE_CONFIRM=1` keeps a human-in-the-loop path where only
|
|
41
|
+
`confirmed=true` clears the block.
|
|
42
|
+
|
|
43
|
+
This is the OPPOSITE of silent autocorrect: the assumption is recorded,
|
|
44
|
+
injected into the agent's prompt, and surfaced in proof-of-done. Acknowledgment
|
|
45
|
+
records "Loki has SEEN this gap and proceeded with a stated default", not "Loki
|
|
46
|
+
hid it". The gate still has teeth on the first iteration (high-sev unacknowledged
|
|
47
|
+
blocks) and in the require-confirm path.
|
|
48
|
+
|
|
49
|
+
## Severity rule (deterministic, no LLM)
|
|
50
|
+
|
|
51
|
+
grill emits no severity. Classify by section / keyword on the read side:
|
|
52
|
+
|
|
53
|
+
- HIGH: security blind spots; scale/reliability blind spots; missing or
|
|
54
|
+
untestable acceptance criteria; any line containing contradiction keywords
|
|
55
|
+
(contradict, conflict, inconsistent, mutually exclusive).
|
|
56
|
+
- MEDIUM: ambiguities; unstated assumptions; underspecified behavior; all
|
|
57
|
+
prd-analyzer missing-dimension assumptions.
|
|
58
|
+
|
|
59
|
+
This guarantees a HIGH tier exists (so the gate has teeth) and is fully
|
|
60
|
+
deterministic (so tests are reproducible).
|
|
61
|
+
|
|
62
|
+
## Taxonomy mapping (classification, read-side only)
|
|
63
|
+
|
|
64
|
+
grill section -> finding class:
|
|
65
|
+
- "Ambiguities and missing acceptance criteria" -> ambiguous (HIGH if the line
|
|
66
|
+
references acceptance criteria / testable / measurable; else MEDIUM)
|
|
67
|
+
- "Unstated assumptions" -> underspecified (MEDIUM)
|
|
68
|
+
- "Security blind spots" -> missing (HIGH)
|
|
69
|
+
- "Scale and reliability blind spots" -> missing (HIGH)
|
|
70
|
+
- any line with a contradiction keyword (any section) -> contradictory (HIGH)
|
|
71
|
+
- prd-analyzer missing dimensions -> missing (MEDIUM, deterministic default)
|
|
72
|
+
|
|
73
|
+
"None identified." lines are skipped (no fabricated findings).
|
|
74
|
+
|
|
75
|
+
grill output contract is NOT changed (it is parsed by the loki-grill skill).
|
|
76
|
+
We classify a COPY of its markdown; grill.sh stays byte-identical.
|
|
77
|
+
|
|
78
|
+
## No-fabrication rule for ledger content
|
|
79
|
+
|
|
80
|
+
A grill finding is a QUESTION, not a resolution. The ledger `assumption` field
|
|
81
|
+
for a grill-derived gap is an honest "spec gives no answer; proceeding with the
|
|
82
|
+
implementer default for <area>" plus `affects=<area>`. We do NOT invent a
|
|
83
|
+
specific resolution the build will not actually follow. prd-analyzer assumptions
|
|
84
|
+
reuse its existing deterministic `_make_assumption()` text verbatim.
|
|
85
|
+
|
|
86
|
+
## Ledger schema (`.loki/assumptions/`)
|
|
87
|
+
|
|
88
|
+
One JSON file per assumption: `.loki/assumptions/<id>.json`, plus a
|
|
89
|
+
human-readable `.loki/assumptions/ledger.md` rollup regenerated on each write.
|
|
90
|
+
Each entry:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"id": "a-0001",
|
|
95
|
+
"gap": "<the spec defect / unanswered question, verbatim>",
|
|
96
|
+
"assumption": "<honest stated default Loki proceeds with>",
|
|
97
|
+
"why": "<why this assumption / where the gap came from: grill|prd-analyzer>",
|
|
98
|
+
"severity": "high|medium",
|
|
99
|
+
"class": "ambiguous|contradictory|underspecified|missing",
|
|
100
|
+
"affects": "<area, e.g. security, acceptance-criteria, data-model>",
|
|
101
|
+
"source": "grill|prd-analyzer",
|
|
102
|
+
"confirmed": false,
|
|
103
|
+
"acknowledged": false,
|
|
104
|
+
"created_at": "<iso8601>"
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Stable id = `a-` + zero-padded counter over existing files (idempotent: a second
|
|
109
|
+
DISCOVERY run with the same findings does not duplicate; dedupe on the `gap`
|
|
110
|
+
text hash).
|
|
111
|
+
|
|
112
|
+
## Build surface (files + functions)
|
|
113
|
+
|
|
114
|
+
1. NEW `autonomy/spec-interrogation.sh` (sourced by run.sh; standalone-testable):
|
|
115
|
+
- `spec_interrogation_classify_report <report.md path>`: pure classifier.
|
|
116
|
+
Reads grill markdown, emits one TSV/JSON finding per question line with
|
|
117
|
+
class + severity. Takes a file so a fixture report drives the test with no
|
|
118
|
+
`claude` call.
|
|
119
|
+
- `spec_interrogation_severity_for <section> <line>`: deterministic severity.
|
|
120
|
+
- `spec_ledger_write <gap> <assumption> <why> <severity> <class> <affects>
|
|
121
|
+
<source>`: idempotent writer (dedupe on gap hash) -> `.loki/assumptions/`.
|
|
122
|
+
- `spec_ledger_rebuild_md`: regenerate `.loki/assumptions/ledger.md`.
|
|
123
|
+
- `spec_ledger_high_unresolved_count`: count entries with
|
|
124
|
+
`severity=high AND confirmed=false AND acknowledged=false` (gate input;
|
|
125
|
+
also reused by the summary).
|
|
126
|
+
- `spec_ledger_acknowledge_all`: set `acknowledged=true` on all entries
|
|
127
|
+
(auto-ack lifecycle helper; default path).
|
|
128
|
+
- `spec_interrogation_run <spec_path>`: orchestrator. Default-on
|
|
129
|
+
(`LOKI_SPEC_GRILL=0` opts out). Provider-aware: source grill.sh, call
|
|
130
|
+
`grill_check_provider`; if provider absent, log honest message, skip the
|
|
131
|
+
grill subcall (NO fabricated questions), but STILL fold prd-analyzer
|
|
132
|
+
missing-dimension assumptions into the ledger so degrade surfaces something
|
|
133
|
+
non-blocking. On provider present: run `grill_main` (writes report.md),
|
|
134
|
+
classify it, write ledger entries. Always non-fatal to the run.
|
|
135
|
+
|
|
136
|
+
2. `autonomy/run.sh` DISCOVERY (~13056, after prd-analyzer + council_init,
|
|
137
|
+
before the iteration loop): source spec-interrogation.sh and call
|
|
138
|
+
`spec_interrogation_run "$prd_path"`. This is the grep-able grill invocation
|
|
139
|
+
the task requires. Best-effort (`|| true`), never blocks startup.
|
|
140
|
+
|
|
141
|
+
3. `autonomy/run.sh` auto-ack lifecycle: after the build prompt is constructed
|
|
142
|
+
each iteration (assumptions are injected into the prompt via build_prompt),
|
|
143
|
+
call `spec_ledger_acknowledge_all` UNLESS
|
|
144
|
+
`LOKI_ASSUMPTIONS_REQUIRE_CONFIRM=1`. Inject the high-severity assumption
|
|
145
|
+
list into the build prompt (so the agent sees the gaps it must respect).
|
|
146
|
+
|
|
147
|
+
4. `autonomy/completion-council.sh` `council_assumption_ledger_gate` (new),
|
|
148
|
+
slotted into `council_evaluate` right after `council_evidence_gate`
|
|
149
|
+
(mirrors 2510-2513). Same defensive `COUNCIL_STATE_DIR` default, opt-out
|
|
150
|
+
`LOKI_ASSUMPTION_GATE=0`. Blocks iff `spec_ledger_high_unresolved_count > 0`.
|
|
151
|
+
Writes `.loki/council/assumption-block.json` on block, removes it on pass.
|
|
152
|
+
Also wired into the completion-promise route in run.sh (~14525 pattern) and
|
|
153
|
+
the code_review gate chain (~15013) so the promise path cannot bypass it.
|
|
154
|
+
|
|
155
|
+
5. `autonomy/run.sh` `build_completion_summary` (~2637): emit an
|
|
156
|
+
"Assumptions recorded: N (M high-severity)" block into COMPLETION.txt and the
|
|
157
|
+
ledger list, plus the count into completion.json. So "done" means "done, plus
|
|
158
|
+
here are the N places your spec was ambiguous and what I assumed."
|
|
159
|
+
|
|
160
|
+
6. NEW `tests/test-spec-interrogation.sh` (bash convention, ok/bad counters,
|
|
161
|
+
source the module, mktemp fixtures):
|
|
162
|
+
- (a) classifier on a fixture grill report writes classified findings to the
|
|
163
|
+
ledger (ambiguous/contradictory/underspecified/missing + high/medium).
|
|
164
|
+
- (b) a ledger with one high/confirmed:false/acknowledged:false entry makes
|
|
165
|
+
`council_assumption_ledger_gate` return 1 (BLOCK) and write
|
|
166
|
+
assumption-block.json.
|
|
167
|
+
- (c) clean spec (no high-sev entries, or all acknowledged) -> gate returns 0
|
|
168
|
+
(no spurious block), no block file.
|
|
169
|
+
- (d) no provider -> `spec_interrogation_run` degrades cleanly: honest
|
|
170
|
+
message, prd-analyzer assumptions still folded (medium, non-blocking), run
|
|
171
|
+
proceeds, gate passes.
|
|
172
|
+
|
|
173
|
+
## Gate reachability (resolved open question)
|
|
174
|
+
|
|
175
|
+
The existing gates fire from THREE sites: `council_evaluate` (~2510), the
|
|
176
|
+
completion-promise route (~14525), and the code_review gate chain (~15013). The
|
|
177
|
+
new gate is wired into all three so high-sev unacknowledged assumptions cannot
|
|
178
|
+
slip through the promise path.
|
|
179
|
+
|
|
180
|
+
## Opt-out knobs (all default-on, intelligent)
|
|
181
|
+
|
|
182
|
+
- `LOKI_SPEC_GRILL=0` -> skip interrogation entirely.
|
|
183
|
+
- `LOKI_ASSUMPTION_GATE=0` -> gate is pass-through.
|
|
184
|
+
- `LOKI_ASSUMPTIONS_REQUIRE_CONFIRM=1` -> require human `confirmed=true`
|
|
185
|
+
(disables auto-ack); the human-in-the-loop path.
|
|
186
|
+
|
|
187
|
+
No "user must decide the type" knob. Classification + severity are automatic.
|
|
188
|
+
|
|
189
|
+
## Constraints
|
|
190
|
+
|
|
191
|
+
No emojis, no em dashes, no version bump, no commit, no push. Provider-aware,
|
|
192
|
+
degrade cleanly, no fabricated questions when provider absent.
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Status: SEAMS implemented (this worktree). NOT a live hosted backend.
|
|
4
4
|
|
|
5
5
|
R9 in the competitive-stickiness arc is the open-core monetization layer: keep
|
|
6
|
-
Loki fully
|
|
7
|
-
and paid plans would attach later. R9 ships the seams only. There is no Loki
|
|
6
|
+
Loki fully source-available (BUSL-1.1) and free to self-host, while adding the
|
|
7
|
+
SEAMS where hosted, enterprise, and paid plans would attach later. R9 ships the seams only. There is no Loki
|
|
8
8
|
hosted service, no license-verification backend, and no paid gate on any
|
|
9
9
|
existing feature. Every honest stub is labeled as such.
|
|
10
10
|
|
|
@@ -239,7 +239,7 @@ Loki Mode now incorporates proven patterns from Cursor's large-scale agent deplo
|
|
|
239
239
|
3. **Anti-Sycophancy** - Blind review prevents false positives
|
|
240
240
|
4. **Full SDLC** - Business, marketing, growth automation
|
|
241
241
|
5. **Published Benchmarks** - Verify claims with reproducible tests
|
|
242
|
-
6. **
|
|
242
|
+
6. **Source-available (BUSL-1.1)** - Inspect and self-host the full code
|
|
243
243
|
|
|
244
244
|
---
|
|
245
245
|
|
|
@@ -256,7 +256,7 @@ Loki Mode now incorporates proven patterns from Cursor's large-scale agent deplo
|
|
|
256
256
|
- Full spec-to-product lifecycle (not just coding)
|
|
257
257
|
- 41 specialized agent roles
|
|
258
258
|
- Anti-sycophancy measures
|
|
259
|
-
-
|
|
259
|
+
- Source-available (BUSL-1.1) license
|
|
260
260
|
- No subscription requirement
|
|
261
261
|
- Verified benchmarks
|
|
262
262
|
|
|
@@ -73,7 +73,7 @@ docs/certification/
|
|
|
73
73
|
|
|
74
74
|
## Cost and Licensing
|
|
75
75
|
|
|
76
|
-
This certification program is **free and
|
|
76
|
+
This certification program is **free and source-available**, released under the same license as Loki Mode (BUSL-1.1). No registration or payment is required.
|
|
77
77
|
|
|
78
78
|
## Version
|
|
79
79
|
|
|
@@ -529,7 +529,7 @@ The "vibe coding" market -- AI tools that generate code from natural language --
|
|
|
529
529
|
| Lovable | Lovable | $100M ARR in 8 months | Design-quality prototypes |
|
|
530
530
|
| Vercel | v0 | Part of Vercel ($3.5B+) | UI component generator |
|
|
531
531
|
| Replit | Replit | $1.16B valuation | Browser IDE + AI |
|
|
532
|
-
| Autonomi | Loki Mode |
|
|
532
|
+
| Autonomi | Loki Mode | Source-available (BUSL-1.1), early stage | PRD-to-production system |
|
|
533
533
|
|
|
534
534
|
### 11.4 Strategic Implication for Loki Mode
|
|
535
535
|
|
|
@@ -280,7 +280,7 @@ Developers who value open-source tooling, speed, and terminal-native workflows.
|
|
|
280
280
|
| Feature | Emergence AI (Agent-E) | Rork | Claude Code CLI | Codex CLI | Loki Mode |
|
|
281
281
|
|---------|:---------------------:|:----:|:--------------:|:---------:|:---------:|
|
|
282
282
|
| **Primary Focus** | Web automation | Mobile apps | Coding assistant | Coding assistant | PRD-to-deploy |
|
|
283
|
-
| **Open Source** | Partial (Agent-E only) | No | Source-available | Yes (Apache-2.0) |
|
|
283
|
+
| **Open Source / Source model** | Partial (Agent-E only) | No | Source-available | Yes (Apache-2.0) | Source-available (BUSL-1.1) |
|
|
284
284
|
| **Multi-Provider** | Yes (OpenAI, Azure, Ollama) | Yes (Gemini, Claude) | Partial (Claude models via Bedrock/Vertex/Foundry) | No (GPT only) | Yes (5 providers, 3+ model families) |
|
|
285
285
|
| **Multi-Agent** | Yes (2-agent model) | No | Yes (coordinated teams) | Yes (experimental) | Yes (41 agent types) |
|
|
286
286
|
| **Autonomous Iteration** | No (task-level) | No | Partial (/loop, /schedule) | No (requires prompting) | Yes (RARV loop + completion council) |
|
|
@@ -454,7 +454,7 @@ This positioning highlights three unique capabilities no competitor offers toget
|
|
|
454
454
|
| Dimension | Codex CLI | Loki Mode |
|
|
455
455
|
|-----------|-----------|-----------|
|
|
456
456
|
| Autonomy | Assisted (human prompts each task) | Fully autonomous |
|
|
457
|
-
|
|
|
457
|
+
| Source model | Open source (Apache-2.0) | Source-available (BUSL-1.1) |
|
|
458
458
|
| Speed | 240+ tokens/sec | Depends on provider |
|
|
459
459
|
| Providers | OpenAI only | 5 providers |
|
|
460
460
|
| Multi-agent | Experimental (isolated) | 41 agent types, 8 domains |
|
|
@@ -467,8 +467,8 @@ This positioning highlights three unique capabilities no competitor offers toget
|
|
|
467
467
|
| Focus | Web/workflow automation | Software development |
|
|
468
468
|
| Pricing | Enterprise contracts | Free + API costs |
|
|
469
469
|
| Self-hosted | VPC option | Fully self-hosted |
|
|
470
|
-
|
|
|
471
|
-
| **Loki Mode advantage:** | Purpose-built for software,
|
|
470
|
+
| Source-available | Partial | Yes (BUSL-1.1) |
|
|
471
|
+
| **Loki Mode advantage:** | Purpose-built for software, source-available, accessible pricing |
|
|
472
472
|
|
|
473
473
|
#### vs. Rork
|
|
474
474
|
| Dimension | Rork | Loki Mode |
|
|
@@ -485,7 +485,7 @@ This positioning highlights three unique capabilities no competitor offers toget
|
|
|
485
485
|
"You already use AI for coding. Loki Mode makes it autonomous -- give it a PRD, and it handles planning, implementation, testing, code review, and deployment. Keep using Claude or Codex under the hood."
|
|
486
486
|
|
|
487
487
|
**For engineering leaders evaluating AI tooling:**
|
|
488
|
-
"Loki Mode is the only
|
|
488
|
+
"Loki Mode is the only source-available system with enterprise-grade quality gates (8 gates, 3-reviewer blind review, anti-sycophancy checks) that runs autonomously on any AI provider. Self-hosted, no vendor lock-in."
|
|
489
489
|
|
|
490
490
|
**For startups and solo developers:**
|
|
491
491
|
"Go from idea to deployed product overnight. Write a PRD, invoke Loki Mode, and let it build, test, and deploy while you sleep. Works with your existing Claude or OpenAI API key."
|
|
@@ -558,7 +558,7 @@ The most significant near-term competitive threat is Anthropic's Agent SDK (http
|
|
|
558
558
|
**However, Loki Mode's structural advantages remain:**
|
|
559
559
|
1. **Multi-provider:** Agent SDK is Claude-only. Loki Mode works with any provider.
|
|
560
560
|
2. **Battle-tested pipeline:** 8 quality gates, completion council, healing, memory -- these took months to build and validate. A new Agent SDK project starts from zero.
|
|
561
|
-
3. **
|
|
561
|
+
3. **Source-available and self-hosted:** No dependency on Anthropic's platform decisions.
|
|
562
562
|
4. **Research foundation:** Built on patterns from OpenAI, DeepMind, Anthropic, and academic research. Not just engineering, but applied AI safety research (Constitutional AI, anti-sycophancy, alignment faking detection).
|
|
563
563
|
|
|
564
564
|
---
|
|
@@ -26,8 +26,8 @@ Replit and Lovable represent two dominant players in the "vibe coding" / AI app
|
|
|
26
26
|
|
|
27
27
|
| Metric | Replit | Lovable | Loki Mode |
|
|
28
28
|
|--------|--------|---------|-----------|
|
|
29
|
-
| Valuation | $9B (Mar 2026) | $6.6B (Dec 2025) |
|
|
30
|
-
| ARR | $265M+ (targeting $1B) | $400M+ (Feb 2026) | N/A (
|
|
29
|
+
| Valuation | $9B (Mar 2026) | $6.6B (Dec 2025) | Source-available (BUSL-1.1) |
|
|
30
|
+
| ARR | $265M+ (targeting $1B) | $400M+ (Feb 2026) | N/A (source-available) |
|
|
31
31
|
| Users | 50M+ registered | 8M+ users | Developer community |
|
|
32
32
|
| Total Funding | $650M+ | $653M | $0 |
|
|
33
33
|
| Employees | ~1,000+ | ~817 | Solo maintainer |
|
|
@@ -376,7 +376,7 @@ Replit Agent has evolved rapidly through four major versions:
|
|
|
376
376
|
|
|
377
377
|
| Metric | Replit Agent | Lovable.dev | Loki Mode |
|
|
378
378
|
|--------|:-----------:|:-----------:|:---------:|
|
|
379
|
-
| Free tier | Yes (limited) | Yes (30 credits/mo) | Yes (
|
|
379
|
+
| Free tier | Yes (limited) | Yes (30 credits/mo) | Yes (source-available, free to self-host) |
|
|
380
380
|
| Entry paid | $20/mo (Core) | $25/mo (Pro) | $0 (bring your own API key) |
|
|
381
381
|
| Team plan | $100/mo (Pro) | $50/mo (Business) | $0 |
|
|
382
382
|
| Cost model | Effort-based credits | Per-prompt credits | API key costs only |
|
|
@@ -585,7 +585,7 @@ The term "vibe coding" (coined by Andrej Karpathy) has driven explosive growth i
|
|
|
585
585
|
|
|
586
586
|
**Medium-term (12 months):** Moderate threat -- as Replit/Lovable improve production quality and add enterprise features, they will attract more professional developers. Loki Mode must ship deployment, dashboard, and Figma integration.
|
|
587
587
|
|
|
588
|
-
**Long-term (24 months):** High convergence risk -- all platforms will trend toward autonomous, production-grade, multi-agent systems. Loki Mode's quality gates, memory system, and brownfield support will be critical differentiators. The
|
|
588
|
+
**Long-term (24 months):** High convergence risk -- all platforms will trend toward autonomous, production-grade, multi-agent systems. Loki Mode's quality gates, memory system, and brownfield support will be critical differentiators. The source-available, self-hostable model is an enduring advantage if the community grows.
|
|
589
589
|
|
|
590
590
|
---
|
|
591
591
|
|
|
@@ -68,11 +68,51 @@ For organizations using identity providers (Okta, Auth0, Azure AD), Loki Mode su
|
|
|
68
68
|
```bash
|
|
69
69
|
export LOKI_OIDC_ISSUER="https://your-idp.okta.com/oauth2/default"
|
|
70
70
|
export LOKI_OIDC_CLIENT_ID="your-client-id"
|
|
71
|
-
|
|
72
|
-
export
|
|
71
|
+
# Optional: audience override (defaults to LOKI_OIDC_CLIENT_ID)
|
|
72
|
+
export LOKI_OIDC_AUDIENCE="your-api-audience"
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
The system validates OIDC tokens
|
|
75
|
+
The system validates OIDC bearer tokens (presented as `Authorization: Bearer <jwt>`)
|
|
76
|
+
against the issuer's JWKS endpoint. When PyJWT + cryptography are installed, the
|
|
77
|
+
JWT signature is cryptographically verified (RS256/RS384/RS512) along with the
|
|
78
|
+
issuer, audience, and expiry. Without PyJWT, tokens are rejected unless
|
|
79
|
+
`LOKI_OIDC_SKIP_SIGNATURE_VERIFY=true` is set explicitly (insecure, local testing
|
|
80
|
+
only).
|
|
81
|
+
|
|
82
|
+
**Claims-to-roles mapping:**
|
|
83
|
+
|
|
84
|
+
On a valid token, role/group claims are mapped to the four built-in Loki roles
|
|
85
|
+
(`admin`, `operator`, `viewer`, `auditor`). The resolved role's scopes are then
|
|
86
|
+
enforced through the same scope hierarchy used for API tokens. OIDC users are
|
|
87
|
+
NOT granted full access by default.
|
|
88
|
+
|
|
89
|
+
Recognized claim sources (case-insensitive; values may be a string,
|
|
90
|
+
space-separated string, or list):
|
|
91
|
+
|
|
92
|
+
| Claim | Provider |
|
|
93
|
+
|-------|----------|
|
|
94
|
+
| value of `LOKI_OIDC_ROLES_CLAIM` (supports dotted paths, e.g. `realm_access.roles`) | configurable |
|
|
95
|
+
| `roles` | generic |
|
|
96
|
+
| `groups` | generic |
|
|
97
|
+
| `realm_access.roles` | Keycloak |
|
|
98
|
+
| `cognito:groups` | AWS Cognito |
|
|
99
|
+
|
|
100
|
+
Only claim values that exactly match a built-in role name (`admin`, `operator`,
|
|
101
|
+
`viewer`, `auditor`) grant that role. Arbitrary group names (common in `groups`
|
|
102
|
+
or `cognito:groups`) do not map to a role and fall through to the default.
|
|
103
|
+
|
|
104
|
+
When multiple recognized roles are present, the highest-privilege match wins
|
|
105
|
+
(precedence: `admin` > `operator` > `auditor` > `viewer`).
|
|
106
|
+
|
|
107
|
+
**Default role (when no recognized role claim is present):**
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Defaults to the least-privileged role (viewer). Never admin.
|
|
111
|
+
export LOKI_OIDC_DEFAULT_ROLE="viewer"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
If `LOKI_OIDC_DEFAULT_ROLE` is unset or set to an unrecognized value, it falls
|
|
115
|
+
back to `viewer`. The default is never `admin` / full access.
|
|
76
116
|
|
|
77
117
|
## Authorization
|
|
78
118
|
|
package/docs/show-hn-post.md
CHANGED
|
@@ -24,7 +24,7 @@ I built Loki Mode because I got tired of the copy-paste loop between AI coding a
|
|
|
24
24
|
|
|
25
25
|
**Test suite:** 683 npm tests, 631 pytest tests, 16 shell tests. Self-reported HumanEval score of 162/164 (98.78%).
|
|
26
26
|
|
|
27
|
-
Built solo.
|
|
27
|
+
Built solo. BUSL-1.1 source-available.
|
|
28
28
|
|
|
29
29
|
## Try it
|
|
30
30
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>m});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,m;var C=L(()=>{N1=l$(t6(import.meta.url));m=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.
|
|
2
|
+
var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null,Q)}var h=($,Q)=>{for(var Z in Q)n6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:s6.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var K$=import.meta.require;var S1={};h(S1,{lokiDir:()=>P,homeLokiDir:()=>o$,findRepoRootForVersion:()=>d$,REPO_ROOT:()=>m});import{resolve as n,dirname as l$}from"path";import{fileURLToPath as t6}from"url";import{existsSync as P$}from"fs";import{homedir as r6}from"os";function i6(){let $=N1;for(let Q=0;Q<6;Q++){if(P$(n($,"VERSION"))&&P$(n($,"autonomy/run.sh")))return $;let Z=l$($);if(Z===$)break;$=Z}return n(N1,"..","..","..")}function d$($){let Q=$;for(let Z=0;Z<6;Z++){if(P$(n(Q,"VERSION"))&&P$(n(Q,"autonomy/run.sh")))return Q;let z=l$(Q);if(z===Q)break;Q=z}return n($,"..","..","..")}function P(){return process.env.LOKI_DIR??n(process.cwd(),".loki")}function o$(){return n(r6(),".loki")}var N1,m;var C=L(()=>{N1=l$(t6(import.meta.url));m=i6()});import{readFileSync as e6}from"fs";import{resolve as $Q,dirname as QQ}from"path";import{fileURLToPath as ZQ}from"url";function F$(){if($$!==null)return $$;let $="7.47.0";if(typeof $==="string"&&$.length>0)return $$=$,$$;try{let Q=QQ(ZQ(import.meta.url)),Z=d$(Q);$$=e6($Q(Z,"VERSION"),"utf-8").trim()}catch{$$="unknown"}return $$}var $$=null;var n$=L(()=>{C()});var C1={};h(C1,{runOrThrow:()=>zQ,run:()=>j,commandVersion:()=>KQ,commandExists:()=>f,ShellError:()=>a$});async function j($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[W,K,U]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:W,stderr:K,exitCode:U}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function zQ($,Q={}){let Z=await j($,Q);if(Z.exitCode!==0)throw new a$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=XQ($),Z=await j(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function XQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function KQ($,Q="--version"){if(!await f($))return null;let z=await j([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var a$;var d=L(()=>{a$=class a$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function a($){return WQ?"":$}var WQ,T,S,I,TZ,w,R,y,q;var c=L(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=a("\x1B[0;31m"),S=a("\x1B[0;32m"),I=a("\x1B[1;33m"),TZ=a("\x1B[0;34m"),w=a("\x1B[0;36m"),R=a("\x1B[1m"),y=a("\x1B[2m"),q=a("\x1B[0m")});import{existsSync as TQ}from"fs";async function Q$(){if(B$!==void 0)return B$;let $="/opt/homebrew/bin/python3.12";if(TQ($))return B$=$,$;let Q=await f("python3.12");if(Q)return B$=Q,Q;let Z=await f("python3");return B$=Z,Z}async function Z$($,Q={}){let Z=await Q$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return j([Z,"-c",$],Q)}var B$;var W$=L(()=>{d()});var t1={};h(t1,{runStatus:()=>gQ});import{existsSync as v,readFileSync as U$,readdirSync as l1,statSync as d1}from"fs";import{resolve as D,basename as xQ}from"path";import{homedir as NQ}from"os";async function DQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${q}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -789,4 +789,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
789
789
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
790
790
|
`),process.stderr.write(o6),2}}p1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var ZZ=await QZ(Bun.argv.slice(2));process.exit(ZZ);
|
|
791
791
|
|
|
792
|
-
//# debugId=
|
|
792
|
+
//# debugId=3DD935606FD979EE64756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
3
|
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.47.0",
|
|
5
5
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|