loki-mode 7.5.6 → 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 +14 -10
- package/SKILL.md +54 -11
- package/VERSION +1 -1
- package/autonomy/app-runner.sh +31 -5
- package/autonomy/run.sh +31 -9
- package/autonomy/sandbox.sh +13 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +32 -19
- package/docs/INSTALLATION.md +16 -10
- package/docs/architecture/ADR-001-runtime-migration.md +26 -4
- package/docs/architecture/STATE-MACHINES.md +57 -0
- package/loki-ts/dist/loki.js +148 -147
- package/mcp/__init__.py +1 -1
- package/mcp/server.py +38 -8
- package/package.json +2 -2
- package/references/core-workflow.md +4 -0
- package/references/deployment.md +6 -0
- package/references/memory-system.md +1 -1
- package/references/production-patterns.md +6 -0
- package/references/prompt-repetition.md +2 -2
- package/references/quality-control.md +6 -0
- package/skills/agents.md +2 -2
- package/skills/healing.md +16 -0
- package/skills/providers.md +1 -1
- package/skills/quality-gates.md +18 -0
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
|
|
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** --
|
|
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
|
-
###
|
|
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
|
-
|
|
|
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** |
|
|
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.
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
331
|
+
---
|
|
317
332
|
|
|
318
|
-
|
|
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
|
-
|
|
|
321
|
-
|
|
|
322
|
-
|
|
|
323
|
-
|
|
|
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.
|
|
368
|
+
**v7.5.8 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.5.
|
|
1
|
+
7.5.8
|
package/autonomy/app-runner.sh
CHANGED
|
@@ -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
|
-
#
|
|
71
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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 - $
|
|
4066
|
-
-e "s|<div class=\"project-name\" id=\"project-name\">--|<div class=\"project-name\" id=\"project-name\">$
|
|
4067
|
-
-e "s|<div class=\"project-path\" id=\"project-path\" title=\"\">--|<div class=\"project-path\" id=\"project-path\" title=\"$
|
|
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
|
|
4426
|
-
<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
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
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=""
|
package/autonomy/sandbox.sh
CHANGED
|
@@ -1320,7 +1320,19 @@ sandbox_run() {
|
|
|
1320
1320
|
fi
|
|
1321
1321
|
|
|
1322
1322
|
log_info "Running command in sandbox: $cmd"
|
|
1323
|
-
|
|
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
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -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
|
-
|
|
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=
|
|
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 "
|
|
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
|
-
|
|
5891
|
-
|
|
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}")
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -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.
|
|
5
|
+
**Version:** v7.5.8
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## What's New in v7.5.
|
|
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.
|
|
15
|
-
- `counter_evidence.ts` --
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
24
|
-
`LOKI_HANDOFF_MD=
|
|
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:**
|
|
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
|
|
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
|
|