loki-mode 7.5.7 → 7.5.8

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 CHANGED
@@ -18,16 +18,19 @@
18
18
 
19
19
  ---
20
20
 
21
- > **How it works:** You provide a PRD. Loki Mode classifies complexity, assembles an agent team from 41 specialized types across 8 swarms, and runs autonomous RARV cycles (Reason - Act - Reflect - Verify) with 9 quality gates. Code is not "done" until it passes automated verification. Output is a Git repo with source, tests, configs, and audit logs.
21
+ > **How it works:** You provide a PRD. Loki Mode classifies complexity (`run.sh:detect_complexity()`), assembles an agent team from 41 specialized types across 8 swarms, and runs autonomous RARV cycles (Reason - Act - Reflect - Verify, see `run.sh:run_autonomous()`) with 11 quality gates (see `skills/quality-gates.md`). Code is not "done" until it passes automated verification. Output is a Git repo with source, tests, configs, and audit logs.
22
22
 
23
23
  ---
24
24
 
25
25
  ## Why Loki Mode?
26
26
 
27
27
  - **Truly autonomous** -- Describe what you want, walk away, come back to working code with tests
28
- - **Production quality built in** -- 9 quality gates, blind 3-reviewer code review, anti-sycophancy checks
28
+ - **Production quality built in** -- 11 quality gates (`skills/quality-gates.md`), blind 3-reviewer code review (`run.sh:run_code_review()`), anti-sycophancy checks
29
29
  - **Self-hosted and private** -- Your keys, your infrastructure, no data leaves your network
30
- - **5 AI providers** -- Claude, Codex, Gemini, Cline, Aider with automatic failover
30
+ - **5 AI providers** -- Claude, Codex, Gemini, Cline, Aider with automatic failover (`loki-ts/src/runner/providers.ts`)
31
+ - **Legacy system healing** -- `loki heal` archaeology/stabilize/isolate/modernize/validate phases (v6.67.0, see `skills/healing.md`)
32
+ - **Memory system** -- Episodic/semantic/procedural with vector search (v5.15.0, see `memory/engine.py`)
33
+ - **MCP server** -- 15 tools including ChromaDB code search (`mcp/server.py`)
31
34
  - **Full-stack output** -- Source code, tests, Docker configs, CI/CD pipelines, audit logs
32
35
  - **Open source** -- Free for personal, internal, and academic use. No vendor lock-in.
33
36
 
@@ -61,7 +64,7 @@ loki quick "build a landing page with a signup form"
61
64
  |--------|---------|-------|
62
65
  | **Bun (recommended)** | `bun install -g loki-mode` | Fastest. v8 will be Bun-only. |
63
66
  | **Homebrew** | `brew tap asklokesh/tap && brew install loki-mode` | Auto-installs Bun as a dep |
64
- | **Docker** | `docker pull asklokesh/loki-mode && docker run --rm asklokesh/loki-mode start prd.md` | Bun pre-installed in image |
67
+ | **Docker** | `docker pull asklokesh/loki-mode:7.5.7 && docker run --rm asklokesh/loki-mode:7.5.7 start prd.md` | Bun pre-installed in image |
65
68
  | **npm (compat)** | `npm install -g loki-mode` | Works without Bun (bash fallback). Migrate any time with `loki self-update --to bun`. |
66
69
 
67
70
  **Upgrading:**
@@ -121,7 +124,7 @@ The next major release sunsets the Bash runtime entirely. There is no firm calen
121
124
  | Method | Command |
122
125
  |--------|---------|
123
126
  | **Homebrew** | `brew tap asklokesh/tap && brew install loki-mode` |
124
- | **Docker** | `docker pull asklokesh/loki-mode` |
127
+ | **Docker** | `docker pull asklokesh/loki-mode:7.5.7` |
125
128
  | **Inside Claude Code** | `claude --dangerously-skip-permissions` then type "Loki Mode" |
126
129
  | **Git clone** | `git clone https://github.com/asklokesh/loki-mode.git` |
127
130
 
@@ -184,8 +187,8 @@ Every iteration: **Reason** (read state) - **Act** (execute, commit) - **Reflect
184
187
  </td>
185
188
  <td width="33%" valign="top">
186
189
 
187
- ### 9 Quality Gates
188
- Blind review, anti-sycophancy, severity blocking, mock/mutation detection. Code does not ship until all gates pass.
190
+ ### 11 Quality Gates
191
+ Blind review, anti-sycophancy, severity blocking, mock/mutation detection, backward compatibility (gate 10, v6.67.0), documentation coverage (gate 11, v7.5.0). Code does not ship until all gates pass.
189
192
 
190
193
  [Quality Gates](skills/quality-gates.md)
191
194
 
@@ -262,7 +265,7 @@ loki web # launches at http://localhost:57375
262
265
  |---------|:---------:|:--------:|:------:|:-------:|
263
266
  | Self-hosted / your keys | Yes | No | No | No |
264
267
  | 5 AI provider failover | Yes | No | No | No |
265
- | 9 quality gates | Yes | No | No | No |
268
+ | 11 quality gates | Yes | No | No | No |
266
269
  | Blind code review | Yes | No | No | No |
267
270
  | Enterprise auth (SSO/RBAC) | Yes | No | Yes | No |
268
271
  | Air-gapped deployment | Yes | No | No | No |
@@ -295,8 +298,9 @@ Claude gets full features (subagents, parallelization, MCP, Task tool). Other pr
295
298
 
296
299
  | Command | Description |
297
300
  |---------|-------------|
298
- | `loki start [PRD]` | Start with optional PRD file |
301
+ | `loki start [PRD]` | Start with optional PRD file (also accepts an issue ref; replaces deprecated `loki run`) |
299
302
  | `loki stop` | Stop execution |
303
+ | `loki heal <path>` | Legacy system healing (archaeology, stabilize, isolate, modernize, validate -- v6.67.0) |
300
304
  | `loki pause` / `resume` | Pause/resume after current session |
301
305
  | `loki status` | Show current status |
302
306
  | `loki dashboard` | Open web dashboard |
@@ -383,7 +387,7 @@ See [benchmarks/](benchmarks/) for methodology.
383
387
  |------|-----------|---------------------|
384
388
  | **Code Gen** | Full-stack apps from PRDs | Complex domain logic may need human review |
385
389
  | **Deploy** | Generates configs, Dockerfiles, CI/CD | Does not deploy -- human runs deploy commands |
386
- | **Testing** | 9 automated quality gates | Test quality depends on AI assertions |
390
+ | **Testing** | 11 automated quality gates | Test quality depends on AI assertions |
387
391
  | **Providers** | 5 providers with auto-failover | Non-Claude providers lack parallel agents |
388
392
  | **Dashboard** | Real-time single-machine monitoring | No multi-node clustering |
389
393
 
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.5.7
6
+ # Loki Mode v7.5.8
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -89,7 +89,7 @@ These rules guide autonomous operation. Test results and code quality always tak
89
89
 
90
90
  ## Model Selection
91
91
 
92
- **Default (v5.3.0):** Haiku disabled for quality. Use `--allow-haiku` or `LOKI_ALLOW_HAIKU=true` to enable.
92
+ **Default since v5.3.0 (reaffirmed in v7.5.8):** Haiku disabled for quality. Use `--allow-haiku` or `LOKI_ALLOW_HAIKU=true` to enable.
93
93
 
94
94
  | Task Type | Tier | Claude (default) | Claude (--allow-haiku) | Codex (GPT-5.3) | Gemini |
95
95
  |-----------|------|------------------|------------------------|------------------|--------|
@@ -160,7 +160,9 @@ GROWTH ──[continuous improvement loop]──> GROWTH
160
160
 
161
161
  ---
162
162
 
163
- ## Module Loading Protocol
163
+ ## Module Loading Protocol (Skills)
164
+
165
+ This protocol governs **skill module** loading -- task-scoped instruction files in `skills/`. It is distinct from the Memory System Progressive Disclosure (see below), which governs persistent **memory layers** in `.loki/memory/`.
164
166
 
165
167
  ```
166
168
  1. Read skills/00-index.md (once per session)
@@ -179,6 +181,8 @@ GROWTH ──[continuous improvement loop]──> GROWTH
179
181
  5. When task category changes: Load new modules (old context discarded)
180
182
  ```
181
183
 
184
+ **Memory System Progressive Disclosure** is a separate 3-layer structure (`index.json` -> `timeline.json` -> `episodic/*.json`) for retrieving past episodes/patterns. See `skills/memory.md` and `references/memory-system.md`.
185
+
182
186
  ---
183
187
 
184
188
  ## Invocation
@@ -311,15 +315,54 @@ See `skills/memory.md` for the full integration guide.
311
315
 
312
316
  ---
313
317
 
314
- ## Planned Features
318
+ ## Phase 1 RARV-C Closure (v7.5.x)
319
+
320
+ The current track wires real evidence into RARV-C feedback. Documented here and in `loki internal --help`:
321
+
322
+ | Env Var | Effect |
323
+ |---------|--------|
324
+ | `LOKI_INJECT_FINDINGS=true` | Injects council findings + gate failures into the next REASON prompt |
325
+ | `LOKI_OVERRIDE_COUNCIL=true` | Promotes real provider judges over fakes when available |
326
+ | `LOKI_AUTO_LEARNINGS=true` | Auto-extracts learnings into semantic memory after VERIFY |
327
+ | `LOKI_HANDOFF_MD=true` | Emits a `handoff.md` continuity doc at session boundaries |
328
+
329
+ See `references/core-workflow.md` for the full RARV-C contract.
315
330
 
316
- The following features are documented in skill modules but not yet fully automated:
331
+ ---
317
332
 
318
- | Feature | Status | Notes |
333
+ ## Implemented Features
334
+
335
+ | Feature | Added | Notes |
336
+ |---------|-------|-------|
337
+ | Multi-provider support (5 providers) | v5.0.0 | claude, codex, gemini, cline, aider -- see `providers/` |
338
+ | CONTINUITY.md working memory | v5.35.0 | Auto-managed by run.sh, updated each iteration |
339
+ | Quality gates 3-reviewer system | v5.35.0 | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
340
+ | Memory System (episodic/semantic/procedural) | v5.15.0 | Full implementation in `memory/` |
341
+ | Context Window Tracking | v5.40.0 | Dashboard gauge, per-agent breakdown at `GET /api/context` |
342
+ | Notification Triggers | v5.40.0 | `GET/PUT /api/notifications/triggers` |
343
+ | GitHub integration | v5.42.2 | Import, sync-back, PR creation, export. CLI: `loki github`, API: `/api/github/*` |
344
+ | Legacy System Healing | v6.67.0 | `loki heal <path>` -- friction-as-semantics, characterization tests |
345
+ | Unified `loki start` | v6.84.0 | Auto-detects PRD vs issue input |
346
+ | Managed Agents (memory mirror) | v7.2.0 | Opt-in via `LOKI_MANAGED_AGENTS` -- see Managed Agents section |
347
+ | Bun runtime (Phase 1) | v7.3.0 | Read-only commands routed through `bin/loki`; `LOKI_LEGACY_BASH=1` to revert |
348
+ | Phase 1 RARV-C closure | v7.5.x | Findings injection, real judges, auto-learnings, handoff.md |
349
+
350
+ ## Planned / In-Progress Features
351
+
352
+ | Feature | Target | Notes |
319
353
  |---------|--------|-------|
320
- | CONTINUITY.md working memory | Implemented (v5.35.0) | Auto-managed by run.sh, updated each iteration |
321
- | GitHub integration | Implemented (v5.42.2) | Import, sync-back, PR creation, export. CLI: `loki github`, API: `/api/github/*` |
322
- | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
323
- | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
354
+ | Bun runtime (Phase 2+) | TBD | Migrate write-path commands; tracked on `feat/bun-migration` |
355
+ | Managed Agents multiagent path | TBD | `LOKI_EXPERIMENTAL_MANAGED_*` flags -- RESEARCH PREVIEW, not on live API |
356
+ | Benchmarks (HumanEval, SWE-bench) | TBD | Runner scripts and datasets exist in `benchmarks/`; no published results |
357
+ | `loki run` removal | next major | Currently a deprecated alias for `loki start` |
358
+
359
+ ## Deprecated
360
+
361
+ | Item | Deprecated In | Notes |
362
+ |------|---------------|-------|
363
+ | `loki run <issue>` | v6.84.0 | Alias for `loki start`. Will be removed in next major. |
364
+ | VSCode extension (`vscode-extension/`) | v7.2.0 | No longer actively maintained; dashboard web UI is the supported front-end. |
365
+
366
+ ---
324
367
 
325
- **v7.5.7 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
368
+ **v7.5.8 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.5.7
1
+ 7.5.8
@@ -64,11 +64,32 @@ _json_escape() {
64
64
  printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g' | tr -d '\n'
65
65
  }
66
66
 
67
- # Validate a command string: reject shell metacharacters that enable injection
67
+ # Validate a command string: reject shell metacharacters that enable injection.
68
+ #
69
+ # Trust contract:
70
+ # - $_APP_RUNNER_METHOD is either (a) auto-detected by app_runner_init from a
71
+ # fixed set of internal templates (npm run dev, docker compose up -d, etc.)
72
+ # or (b) supplied verbatim by the operator via the LOKI_APP_COMMAND env
73
+ # variable. Auto-detected strings are trusted; LOKI_APP_COMMAND is an
74
+ # external operator-controlled input that may be set in CI, .envrc, or by
75
+ # a hostile parent process.
76
+ # - This validator is the only line of defense for the LOKI_APP_COMMAND path.
77
+ # It runs BEFORE the value is assigned to _APP_RUNNER_METHOD and BEFORE
78
+ # it is interpolated into `bash -lc -- "$_APP_RUNNER_METHOD"` at startup.
79
+ # - Allow only a strict whitelist: A-Z a-z 0-9 _ . / - = and a single ASCII
80
+ # space (0x20). Tabs, newlines, glob, redirects, command separators, and
81
+ # command substitution are all rejected.
68
82
  _validate_app_command() {
69
83
  local cmd="$1"
70
- # Allow alphanumeric, spaces, hyphens, underscores, dots, slashes, colons, equals
71
- # Reject semicolons, pipes, backticks, $(), &&, ||, redirects used for injection
84
+ # Strict whitelist: alphanumerics, underscore, dot, slash, hyphen, equals,
85
+ # and ASCII space. Anything else (including tabs, newlines, ;, |, &, <, >,
86
+ # $, `, (, ), {, }, [, ], *, ?, ~, ", ', \) is rejected.
87
+ if [[ ! "$cmd" =~ ^[A-Za-z0-9_./=\ -]+$ ]]; then
88
+ log_error "App Runner: command rejected (only [A-Za-z0-9_./= -] allowed): $cmd"
89
+ return 1
90
+ fi
91
+ # Belt-and-braces: also explicitly reject the legacy injection set so a
92
+ # future regex regression cannot silently re-allow these.
72
93
  if echo "$cmd" | grep -qE '[;|`$]|&&|\|\||>>|<<'; then
73
94
  log_error "App Runner: command rejected (unsafe characters): $cmd"
74
95
  return 1
@@ -440,7 +461,10 @@ app_runner_start() {
440
461
  # Use setsid with a PID file so we capture the actual child PID (the process
441
462
  # group leader) rather than the subshell PID, which would orphan the app.
442
463
  local _pgid_file="$_APP_RUNNER_DIR/app.pgid.$$"
443
- (cd "$dir" && setsid bash -c 'echo $$ > "'"$_pgid_file"'"; exec '"$_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
464
+ # Note: $_APP_RUNNER_METHOD has passed _validate_app_command (whitelist).
465
+ # The `--` after `bash -lc` prevents flag injection if the assembled
466
+ # script string ever begins with a `-`.
467
+ (cd "$dir" && setsid bash -lc -- 'echo $$ > "'"$_pgid_file"'"; exec '"$_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
444
468
  local _subshell_pid=$!
445
469
  # Wait briefly for the pgid file to appear, then read the real PGID
446
470
  local _pgid_wait=0
@@ -456,7 +480,9 @@ app_runner_start() {
456
480
  rm -f "$_pgid_file"
457
481
  else
458
482
  _APP_RUNNER_HAS_SETSID=false
459
- (cd "$dir" && bash -c "$_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
483
+ # Note: $_APP_RUNNER_METHOD has passed _validate_app_command (whitelist).
484
+ # The `--` after `bash -lc` prevents flag injection.
485
+ (cd "$dir" && bash -lc -- "$_APP_RUNNER_METHOD" >> "$_APP_RUNNER_DIR/app.log" 2>&1) &
460
486
  _APP_RUNNER_PID=$!
461
487
  fi
462
488
  # Register with central PID registry if available
package/autonomy/run.sh CHANGED
@@ -4061,10 +4061,19 @@ generate_dashboard() {
4061
4061
  local project_path=$(pwd)
4062
4062
 
4063
4063
  if [ -f "$skill_dashboard" ]; then
4064
+ # v7.5.8: Escape sed-special chars in project_name/project_path before
4065
+ # interpolating into the substitution RHS. Without this, a project
4066
+ # whose path contains '|' (the chosen sed delimiter), '&' (RHS
4067
+ # backref), '\' or '/' would either break the substitution or allow
4068
+ # attacker-controlled text to be smuggled into the served HTML.
4069
+ local project_name_sed project_path_sed
4070
+ project_name_sed=$(printf '%s' "$project_name" | sed -e 's/[\\&|/]/\\&/g')
4071
+ project_path_sed=$(printf '%s' "$project_path" | sed -e 's/[\\&|/]/\\&/g')
4072
+
4064
4073
  # Copy and inject project info
4065
- sed -e "s|Loki Mode</title>|Loki Mode - $project_name</title>|g" \
4066
- -e "s|<div class=\"project-name\" id=\"project-name\">--|<div class=\"project-name\" id=\"project-name\">$project_name|g" \
4067
- -e "s|<div class=\"project-path\" id=\"project-path\" title=\"\">--|<div class=\"project-path\" id=\"project-path\" title=\"$project_path\">$project_path|g" \
4074
+ sed -e "s|Loki Mode</title>|Loki Mode - ${project_name_sed}</title>|g" \
4075
+ -e "s|<div class=\"project-name\" id=\"project-name\">--|<div class=\"project-name\" id=\"project-name\">${project_name_sed}|g" \
4076
+ -e "s|<div class=\"project-path\" id=\"project-path\" title=\"\">--|<div class=\"project-path\" id=\"project-path\" title=\"${project_path_sed}\">${project_path_sed}|g" \
4068
4077
  "$skill_dashboard" > .loki/dashboard/index.html
4069
4078
  log_info "Dashboard copied from skill installation"
4070
4079
  log_info "Project: $project_name ($project_path)"
@@ -4422,8 +4431,8 @@ generate_dashboard() {
4422
4431
  <div class="agent-status ${status}">${status}</div>
4423
4432
  <div class="agent-work">${currentTask}</div>
4424
4433
  <div class="agent-meta">
4425
- <span>⏱ ${duration}</span>
4426
- <span>✓ ${tasksCount} tasks</span>
4434
+ <span>${duration}</span>
4435
+ <span>${tasksCount} tasks</span>
4427
4436
  </div>
4428
4437
  </div>
4429
4438
  `;
@@ -5757,10 +5766,23 @@ enforce_test_coverage() {
5757
5766
  if [ "$is_monorepo" = "true" ]; then
5758
5767
  # Allow env override
5759
5768
  if [ -n "${LOKI_MONOREPO_TEST_CMD:-}" ]; then
5760
- test_runner="monorepo-custom"
5761
- local output
5762
- output=$(cd "${TARGET_DIR:-.}" && eval "$LOKI_MONOREPO_TEST_CMD" 2>&1) || test_passed=false
5763
- details="monorepo-custom: $(echo "$output" | tail -3 | tr '\n' ' ')"
5769
+ # v7.5.8: Strict whitelist before eval (mirrors app-runner.sh
5770
+ # _validate_app_command hardening). Reject anything outside
5771
+ # [A-Za-z0-9_./= -] so command separators (; | & `), redirects,
5772
+ # subshells, and command substitution can't be smuggled in via
5773
+ # an env var. Failing input is treated as inconclusive (gate
5774
+ # skipped) rather than executed.
5775
+ if [[ ! "$LOKI_MONOREPO_TEST_CMD" =~ ^[A-Za-z0-9_./=\ -]+$ ]] || \
5776
+ echo "$LOKI_MONOREPO_TEST_CMD" | grep -qE '[;|`$]|&&|\|\||>>|<<'; then
5777
+ log_error "LOKI_MONOREPO_TEST_CMD rejected (only [A-Za-z0-9_./= -] allowed): $LOKI_MONOREPO_TEST_CMD"
5778
+ test_runner="monorepo-custom-rejected"
5779
+ details="monorepo-custom: rejected by whitelist (gate skipped, inconclusive)"
5780
+ else
5781
+ test_runner="monorepo-custom"
5782
+ local output
5783
+ output=$(cd "${TARGET_DIR:-.}" && eval "$LOKI_MONOREPO_TEST_CMD" 2>&1) || test_passed=false
5784
+ details="monorepo-custom: $(echo "$output" | tail -3 | tr '\n' ' ')"
5785
+ fi
5764
5786
  else
5765
5787
  # Scan workspace packages for test runners
5766
5788
  local workspace_runner=""
@@ -1320,7 +1320,19 @@ sandbox_run() {
1320
1320
  fi
1321
1321
 
1322
1322
  log_info "Running command in sandbox: $cmd"
1323
- docker exec -it "$CONTAINER_NAME" bash -c "cd /workspace && $cmd"
1323
+ # Trust contract: $cmd is the operator's CLI argv (`loki sandbox run <cmd>`),
1324
+ # and the operator is the same person who is running the sandbox container
1325
+ # on their own machine. Shell metacharacters are intentionally allowed so
1326
+ # the operator can run pipelines, redirects, etc. inside the sandbox -- but
1327
+ # the sandbox is the security boundary, not this shell-out. We:
1328
+ # 1. set the working dir via `docker exec -w` instead of `cd && $cmd`,
1329
+ # so the user command cannot break out of the cd via metacharacters
1330
+ # that confuse the wrapper (e.g. unmatched quotes affecting cd);
1331
+ # 2. pass `--` to bash so the user string cannot be re-interpreted as a
1332
+ # bash flag if it begins with `-`;
1333
+ # 3. pass the user string as a separate argv element to bash -lc so it
1334
+ # is treated as the script body, not concatenated into a wrapper.
1335
+ docker exec -it -w /workspace "$CONTAINER_NAME" bash -lc -- "$cmd"
1324
1336
  }
1325
1337
 
1326
1338
  # Start a dev server inside the sandbox
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.5.7"
10
+ __version__ = "7.5.8"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -622,15 +622,21 @@ app = FastAPI(
622
622
  # Set LOKI_DASHBOARD_CORS to override (comma-separated origins).
623
623
  _cors_default = "http://localhost:57374,http://127.0.0.1:57374"
624
624
  _cors_raw = os.environ.get("LOKI_DASHBOARD_CORS", _cors_default)
625
- if _cors_raw.strip() == "*":
625
+ _cors_origins = [o.strip() for o in _cors_raw.split(",") if o.strip()]
626
+ if "*" in _cors_origins or _cors_raw.strip() == "*":
627
+ if os.environ.get("LOKI_ENV") == "production":
628
+ raise RuntimeError(
629
+ "Wildcard CORS ('*') is not allowed in production. "
630
+ "Set LOKI_DASHBOARD_CORS to a specific origin list, "
631
+ "or set LOKI_ENV != production."
632
+ )
626
633
  logger.warning(
627
634
  "LOKI_DASHBOARD_CORS is set to '*' -- all origins are allowed. "
628
635
  "This is insecure for production deployments."
629
636
  )
630
- _cors_origins = _cors_raw.split(",")
631
637
  app.add_middleware(
632
638
  CORSMiddleware,
633
- allow_origins=[o.strip() for o in _cors_origins if o.strip()],
639
+ allow_origins=_cors_origins,
634
640
  allow_credentials=True,
635
641
  allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
636
642
  allow_headers=["Content-Type", "Authorization", "X-Requested-With"],
@@ -2244,7 +2250,7 @@ def _sanitize_agent_id(agent_id: str) -> str:
2244
2250
  )
2245
2251
  return agent_id
2246
2252
 
2247
- @app.get("/api/memory/summary")
2253
+ @app.get("/api/memory/summary", dependencies=[Depends(auth.require_scope("read"))])
2248
2254
  async def get_memory_summary():
2249
2255
  """Get memory system summary from .loki/memory/."""
2250
2256
  # Try SQLite backend first for accurate counts
@@ -2371,7 +2377,7 @@ async def list_episodes(limit: int = Query(default=50, ge=1, le=1000)):
2371
2377
  return episodes
2372
2378
 
2373
2379
 
2374
- @app.get("/api/memory/episodes/{episode_id}")
2380
+ @app.get("/api/memory/episodes/{episode_id}", dependencies=[Depends(auth.require_scope("read"))])
2375
2381
  async def get_episode(episode_id: str):
2376
2382
  """Get a specific episodic memory entry."""
2377
2383
  # Try SQLite first
@@ -2432,7 +2438,7 @@ async def list_patterns():
2432
2438
  return []
2433
2439
 
2434
2440
 
2435
- @app.get("/api/memory/patterns/{pattern_id}")
2441
+ @app.get("/api/memory/patterns/{pattern_id}", dependencies=[Depends(auth.require_scope("read"))])
2436
2442
  async def get_pattern(pattern_id: str):
2437
2443
  """Get a specific semantic pattern."""
2438
2444
  patterns = await list_patterns()
@@ -2471,7 +2477,7 @@ async def list_skills():
2471
2477
  return skills
2472
2478
 
2473
2479
 
2474
- @app.get("/api/memory/skills/{skill_id}")
2480
+ @app.get("/api/memory/skills/{skill_id}", dependencies=[Depends(auth.require_scope("read"))])
2475
2481
  async def get_skill(skill_id: str):
2476
2482
  """Get a specific procedural skill."""
2477
2483
  loki_dir = _get_loki_dir()
@@ -2510,7 +2516,7 @@ async def consolidate_memory(hours: int = 24):
2510
2516
  return {"status": "ok", "message": f"Consolidation for last {hours}h", "consolidated": 0, "patternsCreated": 0, "patternsMerged": 0, "episodesProcessed": 0}
2511
2517
 
2512
2518
 
2513
- @app.post("/api/memory/retrieve")
2519
+ @app.post("/api/memory/retrieve", dependencies=[Depends(auth.require_scope("control"))])
2514
2520
  async def retrieve_memory(query: dict = None):
2515
2521
  """Search memories by query."""
2516
2522
  return {"results": [], "query": query}
@@ -2678,7 +2684,7 @@ def _read_learning_signals(signal_type: Optional[str] = None, limit: int = 50) -
2678
2684
  return signals[:limit]
2679
2685
 
2680
2686
 
2681
- @app.get("/api/learning/metrics")
2687
+ @app.get("/api/learning/metrics", dependencies=[Depends(auth.require_scope("read"))])
2682
2688
  async def get_learning_metrics(
2683
2689
  timeRange: str = Query("7d", pattern=r"^\d{1,4}[hdm]$"),
2684
2690
  signalType: Optional[str] = None,
@@ -2747,7 +2753,7 @@ async def get_learning_metrics(
2747
2753
  }
2748
2754
 
2749
2755
 
2750
- @app.get("/api/learning/trends")
2756
+ @app.get("/api/learning/trends", dependencies=[Depends(auth.require_scope("read"))])
2751
2757
  async def get_learning_trends(
2752
2758
  timeRange: str = Query("7d", pattern=r"^\d{1,4}[hdm]$"),
2753
2759
  signalType: Optional[str] = None,
@@ -2767,7 +2773,7 @@ async def get_learning_trends(
2767
2773
  return {"dataPoints": data_points, "maxValue": max_val, "period": timeRange}
2768
2774
 
2769
2775
 
2770
- @app.get("/api/learning/signals")
2776
+ @app.get("/api/learning/signals", dependencies=[Depends(auth.require_scope("read"))])
2771
2777
  async def get_learning_signals(
2772
2778
  timeRange: str = Query("7d", pattern=r"^\d{1,4}[hdm]$"),
2773
2779
  signalType: Optional[str] = None,
@@ -2793,7 +2799,7 @@ async def get_learning_signals(
2793
2799
  return combined[offset:offset + limit]
2794
2800
 
2795
2801
 
2796
- @app.get("/api/learning/aggregation")
2802
+ @app.get("/api/learning/aggregation", dependencies=[Depends(auth.require_scope("read"))])
2797
2803
  async def get_learning_aggregation():
2798
2804
  """Get latest learning aggregation result, merging file-based aggregation with live signals."""
2799
2805
  result = {"preferences": [], "error_patterns": [], "success_patterns": [], "tool_efficiencies": []}
@@ -2976,7 +2982,7 @@ async def trigger_aggregation():
2976
2982
  return result
2977
2983
 
2978
2984
 
2979
- @app.get("/api/learning/preferences")
2985
+ @app.get("/api/learning/preferences", dependencies=[Depends(auth.require_scope("read"))])
2980
2986
  async def get_learning_preferences(limit: int = Query(default=50, ge=1, le=1000)):
2981
2987
  """Get aggregated user preferences from events and learning signals directory."""
2982
2988
  events = _read_events("30d")
@@ -2988,7 +2994,7 @@ async def get_learning_preferences(limit: int = Query(default=50, ge=1, le=1000)
2988
2994
  return combined[:limit]
2989
2995
 
2990
2996
 
2991
- @app.get("/api/learning/errors")
2997
+ @app.get("/api/learning/errors", dependencies=[Depends(auth.require_scope("read"))])
2992
2998
  async def get_learning_errors(limit: int = Query(default=50, ge=1, le=1000)):
2993
2999
  """Get aggregated error patterns from events and learning signals directory."""
2994
3000
  events = _read_events("30d")
@@ -3000,7 +3006,7 @@ async def get_learning_errors(limit: int = Query(default=50, ge=1, le=1000)):
3000
3006
  return combined[:limit]
3001
3007
 
3002
3008
 
3003
- @app.get("/api/learning/success")
3009
+ @app.get("/api/learning/success", dependencies=[Depends(auth.require_scope("read"))])
3004
3010
  async def get_learning_success(limit: int = Query(default=50, ge=1, le=1000)):
3005
3011
  """Get aggregated success patterns from events and learning signals directory."""
3006
3012
  events = _read_events("30d")
@@ -3012,7 +3018,7 @@ async def get_learning_success(limit: int = Query(default=50, ge=1, le=1000)):
3012
3018
  return combined[:limit]
3013
3019
 
3014
3020
 
3015
- @app.get("/api/learning/tools")
3021
+ @app.get("/api/learning/tools", dependencies=[Depends(auth.require_scope("read"))])
3016
3022
  async def get_tool_efficiency(limit: int = Query(default=50, ge=1, le=1000)):
3017
3023
  """Get tool efficiency rankings from events and learning signals directory."""
3018
3024
  events = _read_events("30d")
@@ -5882,13 +5888,20 @@ async def list_escalations():
5882
5888
  @app.get("/api/escalations/{filename}")
5883
5889
  async def get_escalation(filename: str):
5884
5890
  """Read one handoff document. Path-traversal-safe."""
5885
- if "/" in filename or "\\" in filename or filename.startswith("."):
5891
+ if "\\" in filename or filename.startswith("."):
5886
5892
  raise HTTPException(status_code=400, detail="invalid filename")
5887
5893
  if not filename.endswith(".md"):
5888
5894
  raise HTTPException(status_code=400,
5889
5895
  detail="only .md handoffs are served")
5890
- base = _get_loki_dir()
5891
- path = base / "escalations" / filename
5896
+ escalations_dir = str(_get_loki_dir() / "escalations")
5897
+ # Resolve symlinks on both sides to catch symlink traversal that the
5898
+ # router-level "/" rejection misses (e.g. a symlink whose target
5899
+ # escapes the escalations directory).
5900
+ target = os.path.realpath(os.path.join(escalations_dir, filename))
5901
+ base = os.path.realpath(escalations_dir)
5902
+ if not target.startswith(base + os.sep):
5903
+ raise HTTPException(status_code=400, detail="Invalid filename")
5904
+ path = _Path(target)
5892
5905
  if not path.exists():
5893
5906
  raise HTTPException(status_code=404,
5894
5907
  detail=f"handoff not found: {filename}")
@@ -2,26 +2,32 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.5.7
5
+ **Version:** v7.5.8
6
6
 
7
7
  ---
8
8
 
9
- ## What's New in v7.5.0
9
+ ## What's New in v7.5.x
10
+
11
+ ### Phase 1 RARV-C closure (shipped v7.5.0, default-on as of v7.5.3)
12
+ Phase 1 closure features now activate automatically when you run
13
+ `loki start` -- no env flags required. Power users can opt out by
14
+ setting any flag to `0`.
10
15
 
11
- ### Phase 1 RARV-C closure (Bun route, default-off feature flags)
12
16
  - `findings_injector.ts` -- structured per-finding records (severity, file,
13
17
  line, reviewer) injected into the next iteration's prompt instead of bare
14
- comma-separated tokens. Enable with `LOKI_INJECT_FINDINGS=1`.
15
- - `counter_evidence.ts` -- 3-judge override council. Drop a
18
+ comma-separated tokens. Default: ON. Opt out with `LOKI_INJECT_FINDINGS=0`.
19
+ - `counter_evidence.ts` -- override council. Drop a
16
20
  `.loki/state/counter-evidence-<iter>.json` to dispute reviewer findings;
17
- 2-of-3 approval lifts the BLOCK. Enable with `LOKI_OVERRIDE_COUNCIL=1`
18
- (requires `LOKI_INJECT_FINDINGS=1`).
21
+ approval lifts the BLOCK. Default: ON. Opt out with `LOKI_OVERRIDE_COUNCIL=0`.
19
22
  - `learnings_writer.ts` -- automatic structured learnings to
20
23
  `.loki/state/relevant-learnings.json` on every code_review failure.
21
- Enable with `LOKI_AUTO_LEARNINGS=1`.
24
+ Default: ON. Opt out with `LOKI_AUTO_LEARNINGS=0`.
22
25
  - `escalation_handoff.ts` -- structured human-handoff doc to
23
- `.loki/escalations/handoff-*.md` before PAUSE. Enable with
24
- `LOKI_HANDOFF_MD=1`.
26
+ `.loki/escalations/handoff-*.md` before PAUSE. Default: ON. Opt out
27
+ with `LOKI_HANDOFF_MD=0`.
28
+ - `loki status` shows a "Phase 1 artifacts" section when findings,
29
+ learnings, or escalations exist (added v7.5.5; see the `phase1` block
30
+ in `loki status --json`).
25
31
  - See `skills/quality-gates.md` for full schema and reachability notes.
26
32
 
27
33
  ### Earlier highlights still in scope
@@ -1,7 +1,28 @@
1
1
  # ADR-001: Migrate Loki orchestrator off bash
2
2
 
3
- **Status:** Proposed (feature/bun-migration branch)
4
- **Date:** 2026-04-25
3
+ **Status:** Accepted -- Phases 1-5 shipped; Phase 6 (bash sunset) gated on 30-day clean soak
4
+ **Date:** 2026-04-25 (proposed) / 2026-04-29 (status updated for v7.5.7)
5
+ **Last reviewed:** 2026-04-29 against v7.5.7
6
+
7
+ ## Phase status (as of v7.5.7, 2026-04-29)
8
+
9
+ | Phase | Description | Status | Shipped in |
10
+ |---|---|---|---|
11
+ | 1 | Scaffold `loki-ts/`, `ts-version` POC, CI matrix | DONE | v7.0.x |
12
+ | 2 | Read-only sub-commands ported (provider show, status, stats, memory list) | DONE | v7.1.x |
13
+ | 3 | Build/release tooling on Bun (`bun publish`, `bun build`, `bun test`) | DONE | v7.2.0 |
14
+ | 4 | `build_prompt` + `run_autonomous` outer loop ported; `LOKI_LEGACY_BASH=1` fallback live | DONE | v7.3.0 |
15
+ | 5 | `council_should_stop`, `run_code_review` ported; side-by-side parity verified | DONE | v7.3.0 -- v7.4.x |
16
+ | 6 | Sunset bash: remove `LOKI_LEGACY_BASH`, delete `autonomy/run.sh` + `autonomy/loki` | **GATED** | Pending 30-day clean soak from v7.3.0 GA |
17
+
18
+ **Phase 6 entry criteria (per founder call 2026-04-25, release_strategy memory):**
19
+ - 30 consecutive days with zero parity-diff regressions on the Bun route
20
+ - All 14 CLI tests passing on both routes (`tests/test-cli-commands.sh`)
21
+ - Bun-parity matrix green on every workflow (added after v7.4.18 doctor-text drift bug)
22
+ - No `LOKI_LEGACY_BASH=1` fallback invocations observed in telemetry over the soak window
23
+ - Local CI gate (`scripts/local-ci.sh`) green on every push
24
+
25
+ When all criteria are met, Phase 6 PR (#159 per release_strategy) opens for council review.
5
26
  **Decision driver:** `autonomy/run.sh` is 11,327 lines of bash and `autonomy/loki` is 22,304 lines. Both are fragile, hard to refactor, untyped, and the upstream RARV-C audit (v6.81 plan) flagged them as the single biggest architectural debt.
6
27
 
7
28
  ## Context: facts the user asked me to verify
@@ -162,10 +183,11 @@ Reasons:
162
183
  - Port `council_should_stop` and `run_code_review` to TypeScript
163
184
  - Same validation discipline
164
185
 
165
- ### Phase 6: Sunset bash (only after 30 days clean in production)
186
+ ### Phase 6: Sunset bash (only after 30 days clean in production) -- GATED as of v7.5.7
166
187
  - Remove `LOKI_LEGACY_BASH` flag
167
188
  - Delete `autonomy/run.sh` and `autonomy/loki` (keep in git history)
168
- - Bash Bun migration complete
189
+ - Bash -> Bun migration complete
190
+ - **Status (2026-04-29):** awaiting 30-day clean soak. Soak clock started with v7.3.0 GA. See "Phase status" table at top of this ADR for entry criteria.
169
191
 
170
192
  ## What we can do better for releases (Anthropic owns Bun → leverage)
171
193