loki-mode 7.32.3 → 7.34.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 +4 -4
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +30 -2
- package/autonomy/context-tracker.py +7 -1
- package/autonomy/council-v2.sh +14 -1
- package/autonomy/grill.sh +27 -1
- package/autonomy/lib/claude-flags.sh +157 -0
- package/autonomy/loki +3 -3
- package/autonomy/run.sh +108 -4
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +26 -0
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/loki.js +4 -4
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/providers/claude.sh +25 -0
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
- **Intelligent `loki start`** -- For interactive foreground runs the dashboard auto-opens in the browser (cross-platform; skipped in CI, SSH-without-TTY, and piped runs; opt out with `LOKI_NO_AUTO_OPEN=1`). The completion summary shows "Your app is live at <url>" so you know exactly where to try what Loki just built. The autonomous loop passes Claude Code's `--effort`, `--max-budget-usd`, and `--fallback-model` on every iteration (each gated on CLI support and individual opt-out env vars) for better long-run unattended execution (v7.25.0).
|
|
37
37
|
- **Cross-project memory** -- Episodic/semantic/procedural memory with vector search; knowledge learned on one project surfaces on the next (v5.15.0+, see `memory/engine.py`)
|
|
38
38
|
- **Self-hosted and private** -- Your keys, your infrastructure, no data leaves your network
|
|
39
|
-
- **Legacy system healing** -- `loki heal` archaeology/stabilize/isolate/modernize/validate phases (v6.67.0, see `skills/healing.md`)
|
|
39
|
+
- **Legacy system healing** -- `loki modernize heal` archaeology/stabilize/isolate/modernize/validate phases (v6.67.0, see `skills/healing.md`)
|
|
40
40
|
- **MCP server** -- 34 tools (including ChromaDB code search) plus 3 resources and 2 prompts (`mcp/server.py`, with magic tools registered from `mcp/magic_tools.py` and the managed-memory tool from `mcp/managed_tools.py`). Of the 34, 33 are always available; `loki_memory_redact` is registered but only succeeds when `LOKI_MANAGED_AGENTS=true` and `LOKI_MANAGED_MEMORY=true`. Launch with `loki mcp` (bootstraps the Python MCP SDK on first run).
|
|
41
41
|
- **Full-stack output** -- Source code, tests, Docker Compose stacks (multi-service with healthchecks), CI/CD pipelines, audit logs
|
|
42
42
|
- **Provider-agnostic** -- runs on Claude, Codex, Cline, or Aider with automatic failover (`loki-ts/src/runner/providers.ts`); no vendor lock-in. Gemini CLI deprecated v7.5.18; Antigravity CLI coming soon.
|
|
@@ -366,17 +366,17 @@ Status legend: "E2E-verified" means we run real spec-to-code builds on it oursel
|
|
|
366
366
|
|---------|-------------|
|
|
367
367
|
| `loki start [PRD]` | Start with optional PRD file (also accepts an issue ref; replaces deprecated `loki run`). Auto-opens the dashboard in the browser for interactive runs and passes native `--effort`/`--max-budget-usd`/`--fallback-model` for resilience (v7.25.0) |
|
|
368
368
|
| `loki stop` | Stop execution |
|
|
369
|
-
| `loki heal <path>` | Legacy system healing (archaeology, stabilize, isolate, modernize, validate -- v6.67.0) |
|
|
369
|
+
| `loki modernize heal <path>` | Legacy system healing (archaeology, stabilize, isolate, modernize, validate -- v6.67.0; was: `loki heal`) |
|
|
370
370
|
| `loki pause` / `resume` | Pause/resume after current session |
|
|
371
371
|
| `loki status` | Show current status |
|
|
372
372
|
| `loki dashboard` | Open web dashboard |
|
|
373
|
-
| `loki preview`
|
|
373
|
+
| `loki preview` | Print running app URL and open in browser (Live App Preview, v7.24.0; was: `loki open`) |
|
|
374
374
|
| `loki web` | Launch Purple Lab web UI |
|
|
375
375
|
| `loki doctor` | Check environment and dependencies |
|
|
376
376
|
| `loki plan [PRD]` | Pre-execution analysis: complexity, cost, iterations |
|
|
377
377
|
| `loki review [--staged\|--diff]` | AI-powered code review with severity filtering |
|
|
378
378
|
| `loki test [--file\|--dir\|--changed]` | AI test generation (8 languages, 9 frameworks) |
|
|
379
|
-
| `loki onboard [path]` | Project analysis and CLAUDE.md generation |
|
|
379
|
+
| `loki analyze onboard [path]` | Project analysis and CLAUDE.md generation (was: `loki onboard`) |
|
|
380
380
|
| `loki import` | Import GitHub issues as tasks |
|
|
381
381
|
| `loki ci` | CI/CD quality gate integration |
|
|
382
382
|
| `loki failover` | Cross-provider auto-failover management |
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 11 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.
|
|
6
|
+
# Loki Mode v7.34.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -398,4 +398,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
398
398
|
|
|
399
399
|
---
|
|
400
400
|
|
|
401
|
-
**v7.
|
|
401
|
+
**v7.34.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.34.0
|
|
@@ -1759,7 +1759,23 @@ ISSUES: CRITICAL:description (optional, one per line per issue)"
|
|
|
1759
1759
|
claude)
|
|
1760
1760
|
if command -v claude &>/dev/null; then
|
|
1761
1761
|
local council_model="${PROVIDER_MODEL_FAST:-haiku}"
|
|
1762
|
-
|
|
1762
|
+
# EMBED 2 + 3 (v7.33.0). Council member completion vote. The
|
|
1763
|
+
# $prompt is fully self-contained (evidence + instructions +
|
|
1764
|
+
# strict VOTE/REASON/ISSUES output format, piped via stdin) and
|
|
1765
|
+
# the verdict is captured. So --bare (cheap, no hooks/LSP/CLAUDE.
|
|
1766
|
+
# md/MCP) and --disallowedTools (a voting reviewer must never
|
|
1767
|
+
# mutate the tree) both apply. Gated + opt-out
|
|
1768
|
+
# LOKI_BARE_SUBCALLS=0 / LOKI_REVIEW_TOOL_GUARD=0. Helpers may be
|
|
1769
|
+
# out of scope when this file is sourced standalone, so each is
|
|
1770
|
+
# type-guarded (degrades to the prior bare invocation).
|
|
1771
|
+
local _cm_argv=("--model" "$council_model")
|
|
1772
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
1773
|
+
_cm_argv+=("--bare")
|
|
1774
|
+
fi
|
|
1775
|
+
if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
|
|
1776
|
+
_cm_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
|
|
1777
|
+
fi
|
|
1778
|
+
verdict=$(echo "$prompt" | claude "${_cm_argv[@]}" -p 2>/dev/null | tail -20)
|
|
1763
1779
|
fi
|
|
1764
1780
|
;;
|
|
1765
1781
|
codex)
|
|
@@ -1842,7 +1858,19 @@ REASON: your reasoning"
|
|
|
1842
1858
|
claude)
|
|
1843
1859
|
if command -v claude &>/dev/null; then
|
|
1844
1860
|
local council_model="${PROVIDER_MODEL_FAST:-haiku}"
|
|
1845
|
-
|
|
1861
|
+
# EMBED 2 + 3 (v7.33.0). Contrarian (devil's-advocate) vote --
|
|
1862
|
+
# an adversarial reviewer. Self-contained $prompt via stdin,
|
|
1863
|
+
# verdict captured. --bare + --disallowedTools both apply (a
|
|
1864
|
+
# reviewer must never mutate the tree). Gated + opt-out
|
|
1865
|
+
# LOKI_BARE_SUBCALLS=0 / LOKI_REVIEW_TOOL_GUARD=0; type-guarded.
|
|
1866
|
+
local _co_argv=("--model" "$council_model")
|
|
1867
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
1868
|
+
_co_argv+=("--bare")
|
|
1869
|
+
fi
|
|
1870
|
+
if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
|
|
1871
|
+
_co_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
|
|
1872
|
+
fi
|
|
1873
|
+
verdict=$(echo "$prompt" | claude "${_co_argv[@]}" -p 2>/dev/null | tail -20)
|
|
1846
1874
|
fi
|
|
1847
1875
|
;;
|
|
1848
1876
|
codex)
|
|
@@ -304,7 +304,13 @@ def update_tracking(loki_dir, iteration, window_size, provider="claude",
|
|
|
304
304
|
if result["new_offset"] == last_offset:
|
|
305
305
|
return # Nothing new
|
|
306
306
|
|
|
307
|
-
# Update session ID and offset
|
|
307
|
+
# Update session ID and offset.
|
|
308
|
+
# v7.34.0 Phase 1: when LOKI_SESSION_STAMP=1, run.sh passes a
|
|
309
|
+
# per-iteration --session-id, so claude names the JSONL after that uuid
|
|
310
|
+
# and jsonl_path.stem is that uuid rather than a claude-minted one. This
|
|
311
|
+
# is just a string label for the tracking record; any value (uuid or
|
|
312
|
+
# otherwise) is fine here, so no reconcile/parse is needed and there is
|
|
313
|
+
# no crash path from the id differing.
|
|
308
314
|
tracking["session_id"] = jsonl_path.stem
|
|
309
315
|
tracking["updated_at"] = datetime.now(timezone.utc).isoformat()
|
|
310
316
|
offset_file.write_text(str(result["new_offset"]))
|
package/autonomy/council-v2.sh
CHANGED
|
@@ -263,7 +263,20 @@ Respond ONLY with a valid JSON object. No markdown fencing."
|
|
|
263
263
|
case "${PROVIDER_NAME:-claude}" in
|
|
264
264
|
claude)
|
|
265
265
|
if command -v claude &>/dev/null; then
|
|
266
|
-
|
|
266
|
+
# EMBED 2 + 3 (v7.33.0). Council-v2 reviewer verdict. $full_prompt
|
|
267
|
+
# is self-contained (evidence + PRD + strict JSON output, via
|
|
268
|
+
# stdin) and the JSON result is captured. --bare + --disallowedTools
|
|
269
|
+
# both apply (a reviewer must never mutate the tree). Gated +
|
|
270
|
+
# opt-out LOKI_BARE_SUBCALLS=0 / LOKI_REVIEW_TOOL_GUARD=0;
|
|
271
|
+
# type-guarded for standalone sourcing.
|
|
272
|
+
local _c2_argv=("--model" "haiku")
|
|
273
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
274
|
+
_c2_argv+=("--bare")
|
|
275
|
+
fi
|
|
276
|
+
if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
|
|
277
|
+
_c2_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
|
|
278
|
+
fi
|
|
279
|
+
result=$(echo "$full_prompt" | claude "${_c2_argv[@]}" -p 2>/dev/null || echo '{"verdict":"REJECT","reasoning":"review execution failed","issues":[]}')
|
|
267
280
|
else
|
|
268
281
|
result='{"verdict":"REJECT","reasoning":"reviewer CLI unavailable","issues":[]}'
|
|
269
282
|
fi
|
package/autonomy/grill.sh
CHANGED
|
@@ -38,6 +38,16 @@ GRILL_EXIT_ERROR=3
|
|
|
38
38
|
GRILL_DIR_DEFAULT=".loki/grill"
|
|
39
39
|
GRILL_REPORT_NAME="report.md"
|
|
40
40
|
|
|
41
|
+
# Source the claude-flags helper (idempotent, guarded) so the v7.33.0 embeds
|
|
42
|
+
# (--bare / --disallowedTools) are available in grill's standalone invocation
|
|
43
|
+
# path. Best-effort: if the file is missing the type-guards at the call site
|
|
44
|
+
# degrade to the prior bare invocation.
|
|
45
|
+
_grill_flags_helper="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/claude-flags.sh"
|
|
46
|
+
if [ -f "$_grill_flags_helper" ]; then
|
|
47
|
+
# shellcheck disable=SC1090
|
|
48
|
+
. "$_grill_flags_helper"
|
|
49
|
+
fi
|
|
50
|
+
|
|
41
51
|
_grill_log() { printf '[grill] %s\n' "$*" >&2; }
|
|
42
52
|
_grill_err() { printf '[grill][error] %s\n' "$*" >&2; }
|
|
43
53
|
|
|
@@ -178,8 +188,24 @@ grill_invoke_provider() {
|
|
|
178
188
|
local out
|
|
179
189
|
# Single-shot, non-interactive. Same pattern as the in-loop
|
|
180
190
|
# adversarial reviewer (run.sh:7807) and USAGE regen (run.sh:9832).
|
|
191
|
+
# EMBED 2 + 3 (v7.33.0). The devil's-advocate grill is a cheap
|
|
192
|
+
# self-contained adversarial subcall (the entire spec + interrogation
|
|
193
|
+
# instructions are in $prompt, piped via -p -; output captured). So:
|
|
194
|
+
# EMBED 2 (--bare): no hooks/LSP/CLAUDE.md/MCP needed; cheaper.
|
|
195
|
+
# Opt out LOKI_BARE_SUBCALLS=0.
|
|
196
|
+
# EMBED 3 (--disallowedTools): a grill agent interrogates a spec and
|
|
197
|
+
# must never mutate the tree (defense-in-depth). Deny Edit/Write/
|
|
198
|
+
# NotebookEdit + git mutations. Opt out LOKI_REVIEW_TOOL_GUARD=0.
|
|
199
|
+
# Type-guarded so an environment without the helper degrades cleanly.
|
|
200
|
+
local _gr_argv=("--dangerously-skip-permissions" "--model" "${LOKI_GRILL_MODEL:-sonnet}")
|
|
201
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
202
|
+
_gr_argv+=("--bare")
|
|
203
|
+
fi
|
|
204
|
+
if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
|
|
205
|
+
_gr_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
|
|
206
|
+
fi
|
|
181
207
|
out="$(printf '%s' "$prompt" \
|
|
182
|
-
| _grill_with_timeout "${LOKI_GRILL_TIMEOUT:-180}" claude
|
|
208
|
+
| _grill_with_timeout "${LOKI_GRILL_TIMEOUT:-180}" claude "${_gr_argv[@]}" -p - 2>/dev/null)"
|
|
183
209
|
if [ -z "$out" ]; then
|
|
184
210
|
_grill_err "provider returned no output (timeout or invocation error)"
|
|
185
211
|
return $GRILL_EXIT_ERROR
|
|
@@ -130,3 +130,160 @@ loki_claude_flag_supported() {
|
|
|
130
130
|
*) return 1 ;;
|
|
131
131
|
esac
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
# ---------- v7.33.0 cheap-subcall + reviewer-guard flag emitters ----------
|
|
135
|
+
# These centralize three Claude Code 2.1.170 embeds so every cheap NON-MAIN
|
|
136
|
+
# subcall site emits the same gated flags. Each emitter prints its flags to
|
|
137
|
+
# stdout (space-free per element via one-per-line is not needed; callers read
|
|
138
|
+
# them into an array with command substitution + word-splitting on the single
|
|
139
|
+
# token they emit, OR append the function output as additional argv elements).
|
|
140
|
+
# All are default-ON, opt-out via env, and gated on loki_claude_flag_supported
|
|
141
|
+
# so an older CLI degrades gracefully (emits nothing).
|
|
142
|
+
|
|
143
|
+
# EMBED 2 -- --bare (cheap NON-MAIN subcalls only). Minimal mode. Per
|
|
144
|
+
# `claude --help` it SKIPS hooks, LSP, plugin sync, attribution, auto-memory,
|
|
145
|
+
# background prefetches, keychain reads, and CLAUDE.md AUTO-discovery. It does
|
|
146
|
+
# NOT nullify explicit --mcp-config/--settings/--agents (the help lists those as
|
|
147
|
+
# the way to "explicitly provide context" UNDER --bare); what it drops is the
|
|
148
|
+
# IMPLICIT/auto-discovered context. Therefore --bare is ONLY safe on subcalls
|
|
149
|
+
# whose prompt is fully self-contained (the entire instruction set + context is
|
|
150
|
+
# passed via -p), and NEVER on the main RARV loop or on any call that relies on
|
|
151
|
+
# auto-discovered CLAUDE.md / hooks / auto-memory.
|
|
152
|
+
#
|
|
153
|
+
# AUTH GATE (critical): --bare sets CLAUDE_CODE_SIMPLE=1 and per `claude --help`
|
|
154
|
+
# reads Anthropic auth STRICTLY from ANTHROPIC_API_KEY or an apiKeyHelper via
|
|
155
|
+
# --settings -- "OAuth and keychain are never read". A subscription/OAuth user
|
|
156
|
+
# (no ANTHROPIC_API_KEY) gets "Not logged in" on every --bare subcall, which
|
|
157
|
+
# exits 0 with the error on stdout, so a council vote parses as the default
|
|
158
|
+
# REJECT and the loop silently corrupts. So --bare is enabled ONLY when an
|
|
159
|
+
# API-key auth path exists (ANTHROPIC_API_KEY set, or an apiKeyHelper configured
|
|
160
|
+
# in settings); otherwise it emits nothing and the subcall runs full-auth, the
|
|
161
|
+
# same as before this embed. Subscription users are thus unaffected by default.
|
|
162
|
+
# Default-ON (when auth-safe); opt out entirely with LOKI_BARE_SUBCALLS=0.
|
|
163
|
+
# Predicate so call sites can append "--bare" to their argv array uniformly:
|
|
164
|
+
# loki_subcall_bare_enabled && argv+=("--bare")
|
|
165
|
+
loki_subcall_bare_enabled() {
|
|
166
|
+
[ "${LOKI_BARE_SUBCALLS:-1}" = "0" ] && return 1
|
|
167
|
+
# API-key auth required (see AUTH GATE above). ANTHROPIC_API_KEY is the
|
|
168
|
+
# common case; apiKeyHelper covers the settings-configured helper. Anything
|
|
169
|
+
# else (OAuth/keychain subscription) must NOT use --bare. Trim the key so a
|
|
170
|
+
# whitespace-only value does not count as set (it would fail --bare auth).
|
|
171
|
+
local _key
|
|
172
|
+
_key="$(printf '%s' "${ANTHROPIC_API_KEY:-}" | tr -d '[:space:]')"
|
|
173
|
+
if [ -z "$_key" ] && ! _loki_apikey_helper_configured; then
|
|
174
|
+
return 1
|
|
175
|
+
fi
|
|
176
|
+
loki_claude_flag_supported "--bare"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# True when an apiKeyHelper is configured in any Claude settings source, which
|
|
180
|
+
# (unlike OAuth/keychain) IS honored under --bare. Best-effort, read-only.
|
|
181
|
+
_loki_apikey_helper_configured() {
|
|
182
|
+
local f
|
|
183
|
+
for f in "$HOME/.claude/settings.json" "$HOME/.config/claude/settings.json" \
|
|
184
|
+
"${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json" \
|
|
185
|
+
"$PWD/.claude/settings.json" "$PWD/.claude/settings.local.json"; do
|
|
186
|
+
[ -f "$f" ] || continue
|
|
187
|
+
# Require "apiKeyHelper" : "<non-empty value>" (a real key:value pair),
|
|
188
|
+
# not the bare token in a comment/prose. Tolerates whitespace around the
|
|
189
|
+
# colon. Not a full JSON parse, but rejects the obvious false-positives.
|
|
190
|
+
grep -Eq '"apiKeyHelper"[[:space:]]*:[[:space:]]*"[^"]+"' "$f" 2>/dev/null && return 0
|
|
191
|
+
done
|
|
192
|
+
return 1
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# EMBED 3 -- --disallowedTools (reviewer / adversarial subcalls). Motivation: a
|
|
196
|
+
# parallel agent once ran `git reset --hard` and wiped uncommitted work, so a
|
|
197
|
+
# reviewer/voter subcall should not casually mutate the tree. Per `claude --help`
|
|
198
|
+
# the value is a comma-or-space-separated list of tool names (e.g. "Bash(git *)
|
|
199
|
+
# Edit"); Bash(...) matching is command-PREFIX based (the `cmd:*` form).
|
|
200
|
+
#
|
|
201
|
+
# SCOPE / LIMITS (honest -- this is a denylist, NOT a sandbox):
|
|
202
|
+
# - Denies the direct file-mutation tools (Edit, Write, NotebookEdit).
|
|
203
|
+
# - Denies the common git MUTATION forms: the bare subcommand (`git reset:*`)
|
|
204
|
+
# AND the global-flag-prefixed evasions (`git -C:*`, `git --git-dir:*`,
|
|
205
|
+
# `git -c:*`) that slip a flag before the subcommand so the bare prefix
|
|
206
|
+
# does not match. Read-only git (diff/log/show/status) stays allowed.
|
|
207
|
+
# - It does NOT and cannot block every mutation path: a determined agent can
|
|
208
|
+
# still write via `Bash(echo > f)`, `sed -i`, `cp/mv/tee`, `python -c`, etc.
|
|
209
|
+
# This is a guardrail that raises the cost of the casual/common destructive
|
|
210
|
+
# command, not a guarantee the tree is immutable. The real safety net is
|
|
211
|
+
# that the integrator commits before any agent wave (see CLAUDE.md).
|
|
212
|
+
# The flag is variadic (<tools...>), so we emit ONE comma-separated token to
|
|
213
|
+
# avoid swallowing the following -p prompt as additional tool names.
|
|
214
|
+
# Default-ON; opt out with LOKI_REVIEW_TOOL_GUARD=0.
|
|
215
|
+
# Predicate + denylist so call sites append uniformly:
|
|
216
|
+
# if loki_review_guard_enabled; then
|
|
217
|
+
# argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
|
|
218
|
+
# fi
|
|
219
|
+
loki_review_guard_enabled() {
|
|
220
|
+
[ "${LOKI_REVIEW_TOOL_GUARD:-1}" = "0" ] && return 1
|
|
221
|
+
loki_claude_flag_supported "--disallowedTools"
|
|
222
|
+
}
|
|
223
|
+
loki_review_guard_denylist() {
|
|
224
|
+
# Comma-separated single token. Bash(cmd:*) is command-prefix matching.
|
|
225
|
+
# The `git -C:*` / `git --git-dir:*` / `git -c:*` entries close the
|
|
226
|
+
# global-flag-before-subcommand evasion of the bare git-mutation rules.
|
|
227
|
+
printf '%s' "Edit,Write,NotebookEdit,Bash(git commit:*),Bash(git reset:*),Bash(git push:*),Bash(git checkout:*),Bash(git clean:*),Bash(git rm:*),Bash(git stash:*),Bash(git -C:*),Bash(git --git-dir:*),Bash(git -c:*)"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
# ---------- v7.34.0 Claude session-id stamping (Phase 1, correlation-only) -----
|
|
231
|
+
# Derive a deterministic per-run UUID from the existing trust-run-id so the same
|
|
232
|
+
# run always maps to the same claude session UUID, and the bash + Bun routes
|
|
233
|
+
# produce BYTE-IDENTICAL uuids for the same run id. We use RFC-4122 UUIDv5
|
|
234
|
+
# (SHA-1 over a stable namespace + the name). The namespace below is a fixed,
|
|
235
|
+
# Loki-specific constant; never change it (changing it re-keys every run's uuid).
|
|
236
|
+
# The TS mirror is loki-ts/src/providers/claude_flags.ts claudeSessionUuid().
|
|
237
|
+
#
|
|
238
|
+
# Phase 1 is correlation-only: the uuid is written to a metadata file and (only
|
|
239
|
+
# when LOKI_SESSION_STAMP=1) emitted as a PER-ITERATION --session-id on the main
|
|
240
|
+
# loop. It NEVER pins one id across the run (that is Phase 2 continuity, which
|
|
241
|
+
# would accumulate transcript and compete with Loki's own injected memory).
|
|
242
|
+
LOKI_CLAUDE_SESSION_NS="b6f3c7a2-9d41-5e8b-9c2a-3f7d6e1a4b50"
|
|
243
|
+
|
|
244
|
+
# UUIDv5 over the Loki session namespace + an arbitrary name string. Pure
|
|
245
|
+
# (stdout only), deterministic, no side effects. Uses python3 (always present on
|
|
246
|
+
# the bash route; every other helper here already depends on it). Emits empty on
|
|
247
|
+
# any failure so the caller degrades to metadata-only without breaking the run.
|
|
248
|
+
_loki_uuid5() {
|
|
249
|
+
local name="${1:-}"
|
|
250
|
+
[ -z "$name" ] && return 0
|
|
251
|
+
command -v python3 >/dev/null 2>&1 || return 0
|
|
252
|
+
LOKI_CLAUDE_SESSION_NS="$LOKI_CLAUDE_SESSION_NS" _LOKI_UUID5_NAME="$name" \
|
|
253
|
+
python3 - <<'UUID5_PY' 2>/dev/null || true
|
|
254
|
+
import os, uuid
|
|
255
|
+
ns = uuid.UUID(os.environ["LOKI_CLAUDE_SESSION_NS"])
|
|
256
|
+
print(uuid.uuid5(ns, os.environ["_LOKI_UUID5_NAME"]))
|
|
257
|
+
UUID5_PY
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# The stable per-run claude session UUID: UUIDv5 of the trust-run-id. Same run
|
|
261
|
+
# id -> same uuid, on every route. Emits empty when no run id is resolvable.
|
|
262
|
+
_loki_claude_session_uuid() {
|
|
263
|
+
local run_id="${1:-${LOKI_TRUST_RUN_ID:-}}"
|
|
264
|
+
[ -z "$run_id" ] && return 0
|
|
265
|
+
_loki_uuid5 "$run_id"
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# The PER-ITERATION session UUID emitted on the main loop when LOKI_SESSION_STAMP=1.
|
|
269
|
+
# UUIDv5 of "<run-id>:<iteration>", so every iteration gets a DISTINCT, deterministic
|
|
270
|
+
# id. This is deliberately NOT the stable per-run uuid: a single pinned id reused
|
|
271
|
+
# across iterations would make claude RESUME (accumulate transcript), which is
|
|
272
|
+
# Phase 2 continuity, explicitly out of scope here. Distinct-per-iteration keeps
|
|
273
|
+
# each iteration a fresh stateless session (byte-identical default behavior to
|
|
274
|
+
# v7.33 except for the added correlation flag).
|
|
275
|
+
_loki_claude_iteration_session_uuid() {
|
|
276
|
+
local run_id="${1:-${LOKI_TRUST_RUN_ID:-}}"
|
|
277
|
+
local iteration="${2:-${ITERATION_COUNT:-0}}"
|
|
278
|
+
[ -z "$run_id" ] && return 0
|
|
279
|
+
_loki_uuid5 "${run_id}:${iteration}"
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
# Predicate: emit the per-iteration --session-id ARGV flag? CONSERVATIVE DEFAULT
|
|
283
|
+
# is OFF (metadata-file-only) so the default claude argv stays byte-identical to
|
|
284
|
+
# v7.33 (the UX-monotonicity requirement). Opt IN with LOKI_SESSION_STAMP=1.
|
|
285
|
+
# Gated on CLI support so an older claude degrades gracefully (no flag emitted).
|
|
286
|
+
loki_session_stamp_enabled() {
|
|
287
|
+
[ "${LOKI_SESSION_STAMP:-0}" = "1" ] || return 1
|
|
288
|
+
loki_claude_flag_supported "--session-id"
|
|
289
|
+
}
|
package/autonomy/loki
CHANGED
|
@@ -685,7 +685,7 @@ show_help() {
|
|
|
685
685
|
echo " modernize [cmd] Legacy modernization: heal|migrate"
|
|
686
686
|
echo ""
|
|
687
687
|
echo "Config:"
|
|
688
|
-
echo " config [cmd] Manage configuration
|
|
688
|
+
echo " config [cmd] Manage configuration (show|init|edit|path|set|get)"
|
|
689
689
|
echo " doctor [--json] Check system prerequisites and skill symlinks"
|
|
690
690
|
echo ""
|
|
691
691
|
echo " version Show version"
|
|
@@ -2888,8 +2888,8 @@ cmd_status() {
|
|
|
2888
2888
|
fi
|
|
2889
2889
|
|
|
2890
2890
|
echo ""
|
|
2891
|
-
echo -e "${DIM} Tip: loki context show - detailed token breakdown${NC}"
|
|
2892
|
-
echo -e "${DIM} Tip: loki code overview
|
|
2891
|
+
echo -e "${DIM} Tip: loki analyze context show - detailed token breakdown${NC}"
|
|
2892
|
+
echo -e "${DIM} Tip: loki analyze code overview - codebase intelligence${NC}"
|
|
2893
2893
|
}
|
|
2894
2894
|
|
|
2895
2895
|
# JSON output for loki status --json
|
package/autonomy/run.sh
CHANGED
|
@@ -3248,7 +3248,18 @@ Output ONLY the resolved file content with no conflict markers. No explanations.
|
|
|
3248
3248
|
|
|
3249
3249
|
case "${PROVIDER_NAME:-claude}" in
|
|
3250
3250
|
claude)
|
|
3251
|
-
|
|
3251
|
+
# EMBED 2 (v7.33.0): --bare on this cheap NON-MAIN subcall.
|
|
3252
|
+
# Reasoning: $conflict_prompt is fully self-contained -- it
|
|
3253
|
+
# carries the complete instruction set AND the entire conflicted
|
|
3254
|
+
# file content inline, and the agent's output is captured to a
|
|
3255
|
+
# variable (the shell, not the agent, writes the resolved file).
|
|
3256
|
+
# It needs no hooks, LSP, CLAUDE.md auto-discovery, or MCP, so
|
|
3257
|
+
# --bare is safe and cheaper. Gated + opt-out LOKI_BARE_SUBCALLS=0.
|
|
3258
|
+
local _cr_argv=("--dangerously-skip-permissions")
|
|
3259
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
3260
|
+
_cr_argv+=("--bare")
|
|
3261
|
+
fi
|
|
3262
|
+
resolution=$(claude "${_cr_argv[@]}" -p "$conflict_prompt" --output-format text 2>/dev/null)
|
|
3252
3263
|
;;
|
|
3253
3264
|
codex)
|
|
3254
3265
|
resolution=$(codex exec --full-auto --skip-git-repo-check "$conflict_prompt" 2>/dev/null)
|
|
@@ -7803,7 +7814,31 @@ BUILD_PROMPT
|
|
|
7803
7814
|
# Mythos 5 (Project Glasswing), not Fable. If a future change
|
|
7804
7815
|
# adds --model here, the security-sentinel reviewer must be
|
|
7805
7816
|
# pinned to opus, never fable.
|
|
7806
|
-
|
|
7817
|
+
# EMBED 2 + 3 (v7.33.0). This is a 3-reviewer council
|
|
7818
|
+
# subcall. $prompt_text is fully self-contained (built above
|
|
7819
|
+
# into $review_prompt_file with the diff, changed files,
|
|
7820
|
+
# checks, and strict VERDICT/FINDINGS output format), output
|
|
7821
|
+
# is captured to $review_output, and it deliberately does NOT
|
|
7822
|
+
# pass --model or go through buildAutoFlags. So:
|
|
7823
|
+
# EMBED 2 (--bare): the prompt needs no hooks/LSP/CLAUDE.md/
|
|
7824
|
+
# MCP discovery, so --bare is safe and cheaper. Opt out
|
|
7825
|
+
# LOKI_BARE_SUBCALLS=0.
|
|
7826
|
+
# EMBED 3 (--disallowedTools): raise the cost of a reviewer
|
|
7827
|
+
# casually mutating the tree (a parallel agent once ran
|
|
7828
|
+
# `git reset --hard` and wiped uncommitted work). Deny
|
|
7829
|
+
# Edit/Write/NotebookEdit + git mutation forms (incl. the
|
|
7830
|
+
# git -C / --git-dir evasions); read-only git stays allowed.
|
|
7831
|
+
# Guardrail, not a sandbox -- echo>/sed -i/etc. remain; the
|
|
7832
|
+
# real net is commit-before-agent-wave. Opt out
|
|
7833
|
+
# LOKI_REVIEW_TOOL_GUARD=0. See loki_review_guard_denylist.
|
|
7834
|
+
local _rv_argv=("--dangerously-skip-permissions")
|
|
7835
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
7836
|
+
_rv_argv+=("--bare")
|
|
7837
|
+
fi
|
|
7838
|
+
if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
|
|
7839
|
+
_rv_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
|
|
7840
|
+
fi
|
|
7841
|
+
claude "${_rv_argv[@]}" -p "$prompt_text" \
|
|
7807
7842
|
--output-format text > "$review_output" 2>/dev/null
|
|
7808
7843
|
;;
|
|
7809
7844
|
codex)
|
|
@@ -8016,7 +8051,25 @@ ADVERSARIAL_EOF
|
|
|
8016
8051
|
case "${PROVIDER_NAME:-claude}" in
|
|
8017
8052
|
claude)
|
|
8018
8053
|
if command -v claude &>/dev/null; then
|
|
8019
|
-
|
|
8054
|
+
# EMBED 2 + 3 (v7.33.0). Adversarial probe subcall.
|
|
8055
|
+
# $adversarial_prompt is fully self-contained (instructions +
|
|
8056
|
+
# changed files + diff inlined via the heredoc above) and output
|
|
8057
|
+
# is captured to $result_file. So:
|
|
8058
|
+
# EMBED 2 (--bare): no hooks/LSP/CLAUDE.md/MCP needed; cheaper.
|
|
8059
|
+
# Opt out LOKI_BARE_SUBCALLS=0.
|
|
8060
|
+
# EMBED 3 (--disallowedTools): keep an adversarial agent from
|
|
8061
|
+
# casually mutating the tree. Deny Edit/Write/NotebookEdit +
|
|
8062
|
+
# git mutation forms (incl. git -C / --git-dir evasions);
|
|
8063
|
+
# read-only git stays allowed. Guardrail, not a sandbox.
|
|
8064
|
+
# Opt out LOKI_REVIEW_TOOL_GUARD=0.
|
|
8065
|
+
local _adv_argv=("--dangerously-skip-permissions")
|
|
8066
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
8067
|
+
_adv_argv+=("--bare")
|
|
8068
|
+
fi
|
|
8069
|
+
if type loki_review_guard_enabled >/dev/null 2>&1 && loki_review_guard_enabled; then
|
|
8070
|
+
_adv_argv+=("--disallowedTools" "$(loki_review_guard_denylist)")
|
|
8071
|
+
fi
|
|
8072
|
+
claude "${_adv_argv[@]}" -p "$adversarial_prompt" \
|
|
8020
8073
|
--output-format text > "$result_file" 2>/dev/null || true
|
|
8021
8074
|
fi
|
|
8022
8075
|
;;
|
|
@@ -10041,9 +10094,21 @@ ${_commits}"
|
|
|
10041
10094
|
|
|
10042
10095
|
# Use haiku for cheap, fast generation. --dangerously-skip-permissions
|
|
10043
10096
|
# because this is a one-shot non-interactive call.
|
|
10097
|
+
# EMBED 2 (v7.33.0): --bare on this cheap NON-MAIN haiku subcall. The
|
|
10098
|
+
# USAGE.md-regen prompt ($_ic_prompt, piped via -p -) is fully self-contained
|
|
10099
|
+
# (project tree + manifests + entrypoint contents + commits inlined) and the
|
|
10100
|
+
# output is captured, not written by the agent. No hooks/LSP/CLAUDE.md/MCP
|
|
10101
|
+
# needed, so --bare is safe and cheaper. Opt out LOKI_BARE_SUBCALLS=0.
|
|
10102
|
+
# Always at least --dangerously-skip-permissions, so the array is never
|
|
10103
|
+
# empty (empty "${arr[@]}" under set -u errors on bash 3.2, stock macOS).
|
|
10104
|
+
local _ic_argv=("--dangerously-skip-permissions")
|
|
10105
|
+
if type loki_subcall_bare_enabled >/dev/null 2>&1 && loki_subcall_bare_enabled; then
|
|
10106
|
+
_ic_argv+=("--bare")
|
|
10107
|
+
fi
|
|
10108
|
+
_ic_argv+=("--model" "haiku")
|
|
10044
10109
|
local _ic_out
|
|
10045
10110
|
_ic_out=$(printf '%s' "$_ic_prompt" \
|
|
10046
|
-
| timeout 60 claude
|
|
10111
|
+
| timeout 60 claude "${_ic_argv[@]}" -p - 2>/dev/null \
|
|
10047
10112
|
| head -200)
|
|
10048
10113
|
# Sanity check: response must look like Markdown (starts with # or ##).
|
|
10049
10114
|
if [ -z "$_ic_out" ] || ! printf '%s' "$_ic_out" | head -1 | grep -qE '^#'; then
|
|
@@ -12259,6 +12324,26 @@ except Exception:
|
|
|
12259
12324
|
LOKI_TRUST_RUN_ID="$(_loki_trust_run_id --new)"
|
|
12260
12325
|
export LOKI_TRUST_RUN_ID
|
|
12261
12326
|
record_trust_event_bash "run_start" "start_sha=${_LOKI_RUN_START_SHA:-}" 2>/dev/null || true
|
|
12327
|
+
|
|
12328
|
+
# v7.34.0 Phase 1 (correlation-only): write a deterministic claude
|
|
12329
|
+
# session UUID derived from the trust-run-id to .loki/state/claude-session.json.
|
|
12330
|
+
# mode is "stamp" (Phase 1); Phase 2 continuity is a separate, opt-in arc.
|
|
12331
|
+
# Best-effort: the helper is in scope via providers/claude.sh sourcing
|
|
12332
|
+
# claude-flags.sh; if absent (e.g. non-claude provider, no python3) we
|
|
12333
|
+
# skip silently and never fail the run. The dashboard reads this file to
|
|
12334
|
+
# surface it for correlating the run with its Claude session JSONL.
|
|
12335
|
+
if type _loki_claude_session_uuid >/dev/null 2>&1; then
|
|
12336
|
+
local _loki_session_uuid
|
|
12337
|
+
_loki_session_uuid="$(_loki_claude_session_uuid "$LOKI_TRUST_RUN_ID")"
|
|
12338
|
+
if [ -n "$_loki_session_uuid" ]; then
|
|
12339
|
+
local _loki_session_created
|
|
12340
|
+
_loki_session_created="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
12341
|
+
mkdir -p ".loki/state" 2>/dev/null || true
|
|
12342
|
+
printf '{"run_id":"%s","claude_session_uuid":"%s","mode":"stamp","created_at":"%s"}\n' \
|
|
12343
|
+
"$LOKI_TRUST_RUN_ID" "$_loki_session_uuid" "$_loki_session_created" \
|
|
12344
|
+
> ".loki/state/claude-session.json" 2>/dev/null || true
|
|
12345
|
+
fi
|
|
12346
|
+
fi
|
|
12262
12347
|
fi
|
|
12263
12348
|
|
|
12264
12349
|
# Notify dashboard of active project directory (for AI Chat cross-directory usage)
|
|
@@ -12654,6 +12739,20 @@ except Exception as exc:
|
|
|
12654
12739
|
&& loki_claude_flag_supported "--include-partial-messages"; then
|
|
12655
12740
|
_loki_claude_argv+=("--include-partial-messages")
|
|
12656
12741
|
fi
|
|
12742
|
+
# v7.34.0 Phase 1 (correlation-only): per-iteration --session-id. OPT-IN
|
|
12743
|
+
# via LOKI_SESSION_STAMP=1 (CONSERVATIVE DEFAULT is OFF so the default
|
|
12744
|
+
# argv stays byte-identical to v7.33 -- the UX-monotonicity requirement).
|
|
12745
|
+
# The id is a DISTINCT, deterministic UUIDv5 of "<run-id>:<iteration>",
|
|
12746
|
+
# never one pinned id across the run: a reused id would make claude RESUME
|
|
12747
|
+
# and accumulate transcript (Phase 2 continuity, out of scope). This keeps
|
|
12748
|
+
# each iteration a fresh stateless session while making its ~/.claude
|
|
12749
|
+
# JSONL name predictable for dashboard correlation. Gated on CLI support.
|
|
12750
|
+
if type loki_session_stamp_enabled >/dev/null 2>&1 \
|
|
12751
|
+
&& loki_session_stamp_enabled; then
|
|
12752
|
+
local _loki_iter_session_uuid
|
|
12753
|
+
_loki_iter_session_uuid="$(_loki_claude_iteration_session_uuid "${LOKI_TRUST_RUN_ID:-}" "$ITERATION_COUNT")"
|
|
12754
|
+
[ -n "$_loki_iter_session_uuid" ] && _loki_claude_argv+=("--session-id" "$_loki_iter_session_uuid")
|
|
12755
|
+
fi
|
|
12657
12756
|
# ---- Bash<->Bun invocation-flag convergence ledger (v7.25.0) ----------
|
|
12658
12757
|
# The fixture corpus covers build_prompt/stats output, NOT this claude
|
|
12659
12758
|
# argv, so drift here is invisible to parity tests. Keep this ledger
|
|
@@ -12664,8 +12763,13 @@ except Exception as exc:
|
|
|
12664
12763
|
# today.
|
|
12665
12764
|
# Bash argv (canonical, live): --dangerously-skip-permissions --model M
|
|
12666
12765
|
# [--append-system-prompt] [--setting-sources] [--include-partial-messages]
|
|
12766
|
+
# [--session-id UUID (only when LOKI_SESSION_STAMP=1, v7.34.0)]
|
|
12667
12767
|
# [--effort] [--max-budget-usd] [--fallback-model] -p PROMPT
|
|
12668
12768
|
# --output-format stream-json --verbose
|
|
12769
|
+
# v7.34.0: --session-id is emitted ONLY on this MAIN loop, only under
|
|
12770
|
+
# LOKI_SESSION_STAMP=1, as a per-iteration distinct UUIDv5; the DEFAULT
|
|
12771
|
+
# argv (knob unset) is byte-identical to v7.33. Bun mirror lives in
|
|
12772
|
+
# loki-ts/src/runner/providers.ts (sessionStampArgv).
|
|
12669
12773
|
# Bun buildAutoFlags also emits: --exclude-dynamic-system-prompt-sections
|
|
12670
12774
|
# (cost-only), --mcp-config (bash gets MCP via --setting-sources +
|
|
12671
12775
|
# .mcp.json discovery; a how-difference, likely behavior-equivalent),
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -387,6 +387,11 @@ class StatusResponse(BaseModel):
|
|
|
387
387
|
mode: str = ""
|
|
388
388
|
provider: str = "claude"
|
|
389
389
|
current_task: str = ""
|
|
390
|
+
# v7.34.0 Phase 1: the deterministic per-run Claude session UUID derived from
|
|
391
|
+
# the trust-run-id (read from .loki/state/claude-session.json). Surfaced so a
|
|
392
|
+
# user can correlate the run with its Claude session JSONL (in ~/.claude/projects). Empty when
|
|
393
|
+
# the run predates this field or no claude session was stamped.
|
|
394
|
+
claude_session_id: str = ""
|
|
390
395
|
# Concurrent sessions (v6.4.0)
|
|
391
396
|
sessions: list[SessionInfo] = []
|
|
392
397
|
|
|
@@ -954,6 +959,26 @@ async def get_status() -> StatusResponse:
|
|
|
954
959
|
pending_tasks = 0
|
|
955
960
|
running_agents = 0
|
|
956
961
|
|
|
962
|
+
# v7.34.0 Phase 1: the deterministic per-run Claude session UUID, written at
|
|
963
|
+
# run-start by run.sh (correlation-only). Best-effort read; empty when the
|
|
964
|
+
# file is absent (run predates the field, or a non-claude provider).
|
|
965
|
+
claude_session_id = ""
|
|
966
|
+
claude_session_file = loki_dir / "state" / "claude-session.json"
|
|
967
|
+
if claude_session_file.exists():
|
|
968
|
+
try:
|
|
969
|
+
_cs = _safe_json_read(claude_session_file, {})
|
|
970
|
+
# Guard against a syntactically-valid non-object JSON (array, string,
|
|
971
|
+
# number) that would make .get() raise AttributeError, AND a
|
|
972
|
+
# non-string VALUE that would fail StatusResponse's str validation
|
|
973
|
+
# (both -> 500). The normal writer (run.sh) always emits an object
|
|
974
|
+
# with a string uuid, so this only triggers on external file
|
|
975
|
+
# corruption, but /api/status must never 500 on it.
|
|
976
|
+
if isinstance(_cs, dict):
|
|
977
|
+
_v = _cs.get("claude_session_uuid", "")
|
|
978
|
+
claude_session_id = _v if isinstance(_v, str) else ""
|
|
979
|
+
except (json.JSONDecodeError, OSError, KeyError, AttributeError):
|
|
980
|
+
pass
|
|
981
|
+
|
|
957
982
|
# Read dashboard state (with retry for concurrent writes)
|
|
958
983
|
_has_dashboard_state = False
|
|
959
984
|
if state_file.exists():
|
|
@@ -1206,6 +1231,7 @@ async def get_status() -> StatusResponse:
|
|
|
1206
1231
|
mode=mode,
|
|
1207
1232
|
provider=provider,
|
|
1208
1233
|
current_task=current_task,
|
|
1234
|
+
claude_session_id=claude_session_id,
|
|
1209
1235
|
sessions=active_session_list,
|
|
1210
1236
|
)
|
|
1211
1237
|
|
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.34.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -534,7 +534,7 @@ Loki Mode uses two network ports for different services:
|
|
|
534
534
|
LOKI_DASHBOARD_PORT=57374 loki dashboard start
|
|
535
535
|
|
|
536
536
|
# API port (default: 57374)
|
|
537
|
-
loki
|
|
537
|
+
loki api start --port 57374 # was: loki serve
|
|
538
538
|
```
|
|
539
539
|
|
|
540
540
|
### CORS Configuration
|
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.34.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)
|
|
@@ -42,8 +42,8 @@ var n6=Object.defineProperty;var a6=($)=>$;function s6($,Q){this[$]=a6.bind(null
|
|
|
42
42
|
`)}let J=D($,"state","context-usage.json");if(v(J)){let B=a1(J,"window_size",200000),O=a1(J,"used_tokens",0),M=0;if(B>0)M=Math.floor(O*100/B);process.stdout.write(`${w}Context:${q} ${M}% (${O} / ${B} tokens)
|
|
43
43
|
`)}let G=[D($,"dashboard","dashboard.pid"),D(NQ(),".loki","dashboard","dashboard.pid")].find((B)=>v(B))??"";if(G&&v(G)){let B=R$(G);if(B!==null&&k$(B)){let O=D(G,".."),M=(_,k)=>{let u=D(O,_);try{return v(u)?U$(u,"utf-8").trim()||k:k}catch{return k}},x=M("scheme","http"),N=M("host","127.0.0.1"),b=M("port",process.env.LOKI_DASHBOARD_PORT||"57374");if(N==="0.0.0.0")N="127.0.0.1";process.stdout.write(`${w}Dashboard:${q} ${x}://${N}:${b}/
|
|
44
44
|
`)}}return await hQ($),process.stdout.write(`
|
|
45
|
-
`),process.stdout.write(`${y} Tip: loki context show - detailed token breakdown${q}
|
|
46
|
-
`),process.stdout.write(`${y} Tip: loki code overview
|
|
45
|
+
`),process.stdout.write(`${y} Tip: loki analyze context show - detailed token breakdown${q}
|
|
46
|
+
`),process.stdout.write(`${y} Tip: loki analyze code overview - codebase intelligence${q}
|
|
47
47
|
`),0}async function hQ($){let Q=D($,"state"),Z=yQ(Q),z=D(Q,"relevant-learnings.json"),X=D($,"escalations"),W=Z.length>0,K=v(z),U=v(X);if(!W&&!K&&!U)return;if(process.stdout.write(`
|
|
48
48
|
${w}Phase 1 artifacts:${q}
|
|
49
49
|
`),W){let V=Z[Z.length-1],H=s1(V);if(H&&Array.isArray(H.findings)){let J={Critical:0,High:0,Medium:0,Low:0};for(let G of H.findings){let B=String(G.severity??"");if(B in J)J[B]=(J[B]??0)+1}let Y=Object.entries(J).filter(([,G])=>G>0).map(([G,B])=>`${B} ${G.toLowerCase()}`).join(", ");process.stdout.write(` Findings (iter ${H.iteration??"?"}): ${Y||"none"} -- ${H.findings.length} total
|
|
@@ -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=C89D42E5B326555264756E2164756E21
|
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.34.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 11 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
package/providers/claude.sh
CHANGED
|
@@ -190,6 +190,20 @@ _loki_build_claude_auto_flags() {
|
|
|
190
190
|
for _mcp_path in $_mcp_argv; do
|
|
191
191
|
_LOKI_CLAUDE_AUTO_FLAGS+=("$_mcp_path")
|
|
192
192
|
done
|
|
193
|
+
# EMBED 1 (v7.33.0): --strict-mcp-config. ONLY emitted alongside an
|
|
194
|
+
# actual --mcp-config bundle (never bare). Per `claude --help` it
|
|
195
|
+
# makes the agent load servers ONLY from --mcp-config, ignoring ALL
|
|
196
|
+
# other MCP sources (auto-discovered project .mcp.json AND any
|
|
197
|
+
# settings-injected configs). Note the bundle already includes the
|
|
198
|
+
# user's ~/.claude/mcp.json overlay explicitly, so the common
|
|
199
|
+
# user-MCP case is preserved; what is dropped is any MCP config not
|
|
200
|
+
# in the explicit bundle, making the run reproducible.
|
|
201
|
+
# Default-ON; opt out with LOKI_STRICT_MCP=0. Gated on CLI support so
|
|
202
|
+
# an older claude degrades gracefully.
|
|
203
|
+
if [ "${LOKI_STRICT_MCP:-1}" != "0" ] \
|
|
204
|
+
&& loki_claude_flag_supported "--strict-mcp-config"; then
|
|
205
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--strict-mcp-config")
|
|
206
|
+
fi
|
|
193
207
|
fi
|
|
194
208
|
fi
|
|
195
209
|
|
|
@@ -233,6 +247,17 @@ _loki_build_claude_auto_flags() {
|
|
|
233
247
|
&& loki_claude_flag_supported "--include-partial-messages"; then
|
|
234
248
|
_LOKI_CLAUDE_AUTO_FLAGS+=("--include-partial-messages")
|
|
235
249
|
fi
|
|
250
|
+
|
|
251
|
+
# --no-session-persistence (v7.34.0): boolean flag that disables Claude's
|
|
252
|
+
# own session persistence (it would otherwise write transcript JSONL under
|
|
253
|
+
# ~/.claude/projects). OPT-IN only via LOKI_NO_SESSION_PERSIST=1; DEFAULT OFF
|
|
254
|
+
# so this is zero behavior change (the flag is never emitted unless the user
|
|
255
|
+
# asks for it). Useful for ephemeral/CI runs that do not want on-disk
|
|
256
|
+
# transcripts. Gated on CLI support so an older claude degrades gracefully.
|
|
257
|
+
if [ "${LOKI_NO_SESSION_PERSIST:-0}" = "1" ] \
|
|
258
|
+
&& loki_claude_flag_supported "--no-session-persistence"; then
|
|
259
|
+
_LOKI_CLAUDE_AUTO_FLAGS+=("--no-session-persistence")
|
|
260
|
+
fi
|
|
236
261
|
}
|
|
237
262
|
|
|
238
263
|
# The system-prompt text that authorizes autonomous operation and resolves
|