loki-mode 7.95.0 → 7.97.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/hooks/migration-hooks.sh +74 -10
- package/autonomy/run.sh +104 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/static/index.html +162 -156
- package/docs/INSTALLATION.md +1 -1
- package/events/bus.py +11 -1
- package/events/bus.ts +7 -1
- package/events/emit.sh +35 -10
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/mcp/learning_collector.py +18 -0
- package/mcp/server.py +18 -3
- package/memory/consolidation.py +55 -64
- package/memory/engine.py +60 -57
- package/memory/rag_injector.py +71 -6
- package/memory/retrieval.py +42 -12
- package/memory/storage.py +124 -18
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.
|
|
6
|
+
# Loki Mode v7.97.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
408
408
|
|
|
409
409
|
---
|
|
410
410
|
|
|
411
|
-
**v7.
|
|
411
|
+
**v7.97.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.97.0
|
|
@@ -162,7 +162,14 @@ hook_pre_file_edit() {
|
|
|
162
162
|
[[ -z "$file_path" ]] && return 0
|
|
163
163
|
local snap_base
|
|
164
164
|
snap_base=$(_migration_snapshot_dir)
|
|
165
|
-
|
|
165
|
+
# Enforce the pairing contract: if the snapshot cannot be captured, BLOCK the
|
|
166
|
+
# edit. Proceeding would leave post_file_edit's block_and_rollback path with
|
|
167
|
+
# no snapshot to restore -- a test failure would then leave the broken edit
|
|
168
|
+
# in place (silent revert failure). Fail loud/closed here instead.
|
|
169
|
+
if ! _heal_snapshot_save "$snap_base" "$file_path"; then
|
|
170
|
+
echo "HOOK_BLOCKED: could not capture a pre-edit snapshot for ${file_path}; refusing to edit without a revert path. Check that ${snap_base}/snapshots is writable."
|
|
171
|
+
return 1
|
|
172
|
+
fi
|
|
166
173
|
return 0
|
|
167
174
|
}
|
|
168
175
|
|
|
@@ -348,6 +355,19 @@ hook_pre_healing_modify() {
|
|
|
348
355
|
[[ "${LOKI_HEAL_MODE:-false}" != "true" ]] && return 0
|
|
349
356
|
[[ -z "$file_path" ]] && return 0
|
|
350
357
|
|
|
358
|
+
# Fail CLOSED if the friction map is absent. Without it the friction safety
|
|
359
|
+
# gate below cannot run, so deleting (or never producing) friction-map.json
|
|
360
|
+
# would otherwise BYPASS protection and let unclassified institutional
|
|
361
|
+
# knowledge be destroyed -- and _heal_snapshot_save would still run, masking
|
|
362
|
+
# the gap. The map is produced by the archaeology phase (the phase gate
|
|
363
|
+
# blocks archaeology -> stabilize until friction-map.json has >=1 entry), so
|
|
364
|
+
# any modify in stabilize/isolate/modernize legitimately has one. Block
|
|
365
|
+
# before any friction check or snapshot proceeds.
|
|
366
|
+
if [[ ! -f "$heal_dir/friction-map.json" ]]; then
|
|
367
|
+
echo "HOOK_BLOCKED: friction-map.json missing at ${heal_dir}/friction-map.json. Run the archaeology phase first (loki heal <path> --phase archaeology) to catalog friction before modifying files in healing mode."
|
|
368
|
+
return 1
|
|
369
|
+
fi
|
|
370
|
+
|
|
351
371
|
# Check if file has friction points
|
|
352
372
|
if [[ -f "$heal_dir/friction-map.json" ]]; then
|
|
353
373
|
local blocked
|
|
@@ -405,7 +425,15 @@ print('OK')
|
|
|
405
425
|
# Capture a pre-edit snapshot so post_healing_modify can revert ONLY the
|
|
406
426
|
# healing edit on test failure (not unrelated uncommitted changes, and not
|
|
407
427
|
# via git checkout which discards everything). Keyed by file path.
|
|
408
|
-
|
|
428
|
+
#
|
|
429
|
+
# Enforce the pairing contract: if the snapshot cannot be captured, BLOCK the
|
|
430
|
+
# edit. Proceeding would leave the edit with no revert path -- a later test
|
|
431
|
+
# failure would then leave the broken edit in place while honestly reporting
|
|
432
|
+
# "no snapshot" (silent revert failure). Fail loud/closed here instead.
|
|
433
|
+
if ! _heal_snapshot_save "$heal_dir" "$file_path"; then
|
|
434
|
+
echo "HOOK_BLOCKED: could not capture a pre-edit snapshot for ${file_path}; refusing to modify it without a revert path. Check that ${heal_dir}/snapshots is writable."
|
|
435
|
+
return 1
|
|
436
|
+
fi
|
|
409
437
|
|
|
410
438
|
return 0
|
|
411
439
|
}
|
|
@@ -425,27 +453,63 @@ _heal_snapshot_path() {
|
|
|
425
453
|
# healing edit will CREATE it), write a sentinel marker instead so the revert
|
|
426
454
|
# path knows to remove the file rather than restore content.
|
|
427
455
|
#
|
|
428
|
-
# Pairing contract: hook_pre_healing_modify (which calls
|
|
429
|
-
# file before hook_post_healing_modify reverts it
|
|
456
|
+
# Pairing contract (ENFORCED, fail-closed): hook_pre_healing_modify (which calls
|
|
457
|
+
# this) MUST run for a file before hook_post_healing_modify reverts it, AND that
|
|
458
|
+
# pre call MUST leave behind a revertable snapshot. The snapshot is refreshed on
|
|
430
459
|
# every pre call, so a post without a matching fresh pre could restore a stale
|
|
431
460
|
# blob. On the success path the snapshot is intentionally left in place; the
|
|
432
461
|
# next pre overwrites it.
|
|
462
|
+
#
|
|
463
|
+
# Returns 0 ONLY when a revertable snapshot demonstrably exists on disk after
|
|
464
|
+
# this call: exactly one of {content snapshot, ".absent" marker}. Returns 1 on
|
|
465
|
+
# ANY failure (cannot create the snapshot dir, cp/sentinel write failed, the
|
|
466
|
+
# wrong marker survived, or both/neither markers exist). A 1 return MUST block
|
|
467
|
+
# the edit at the caller -- otherwise the edit proceeds with no revert path and a
|
|
468
|
+
# later test failure leaves the broken edit in place (the silent-revert-failure
|
|
469
|
+
# this guard exists to prevent). NEVER return 0 on a failure path.
|
|
433
470
|
_heal_snapshot_save() {
|
|
434
471
|
local heal_dir="$1"
|
|
435
472
|
local file_path="$2"
|
|
473
|
+
# Empty file_path: nothing to snapshot. Callers treat empty file_path as a
|
|
474
|
+
# no-op (return 0 before reaching here in the hooks); not a contract breach.
|
|
436
475
|
[[ -z "$file_path" ]] && return 0
|
|
437
476
|
local snap_dir="$heal_dir/snapshots"
|
|
438
|
-
mkdir -p "$snap_dir" 2>/dev/null ||
|
|
477
|
+
if ! mkdir -p "$snap_dir" 2>/dev/null || [[ ! -d "$snap_dir" ]]; then
|
|
478
|
+
return 1
|
|
479
|
+
fi
|
|
439
480
|
local snap
|
|
440
481
|
snap=$(_heal_snapshot_path "$heal_dir" "$file_path")
|
|
441
482
|
if [[ -f "$file_path" ]]; then
|
|
442
|
-
|
|
443
|
-
|
|
483
|
+
# Capture content, then drop any stale absent-marker so EXACTLY the
|
|
484
|
+
# content snapshot survives. Fail closed if either step fails.
|
|
485
|
+
if ! cp "$file_path" "$snap" 2>/dev/null; then
|
|
486
|
+
return 1
|
|
487
|
+
fi
|
|
488
|
+
if ! rm -f "$snap.absent" 2>/dev/null; then
|
|
489
|
+
return 1
|
|
490
|
+
fi
|
|
491
|
+
# Verify exactly the content snapshot exists and the absent-marker is
|
|
492
|
+
# gone. A lingering marker is a contract violation (restore checks the
|
|
493
|
+
# content snapshot first, but the inconsistency must not be tolerated).
|
|
494
|
+
if [[ ! -f "$snap" || -f "$snap.absent" ]]; then
|
|
495
|
+
return 1
|
|
496
|
+
fi
|
|
444
497
|
else
|
|
445
498
|
# File does not exist pre-edit: record an "absent" marker, drop any
|
|
446
|
-
# stale content snapshot.
|
|
447
|
-
|
|
448
|
-
|
|
499
|
+
# stale content snapshot. CRITICAL: if the stale content snapshot is not
|
|
500
|
+
# removed, restore checks the content snapshot FIRST and would restore
|
|
501
|
+
# content for a file that should have been REMOVED. Fail closed if the
|
|
502
|
+
# stale snapshot cannot be cleared or the marker cannot be written.
|
|
503
|
+
if ! rm -f "$snap" 2>/dev/null; then
|
|
504
|
+
return 1
|
|
505
|
+
fi
|
|
506
|
+
if ! : > "$snap.absent" 2>/dev/null; then
|
|
507
|
+
return 1
|
|
508
|
+
fi
|
|
509
|
+
# Verify exactly the absent-marker exists and no content snapshot does.
|
|
510
|
+
if [[ ! -f "$snap.absent" || -f "$snap" ]]; then
|
|
511
|
+
return 1
|
|
512
|
+
fi
|
|
449
513
|
fi
|
|
450
514
|
return 0
|
|
451
515
|
}
|
package/autonomy/run.sh
CHANGED
|
@@ -3801,6 +3801,82 @@ effective_session_cap() {
|
|
|
3801
3801
|
return 0
|
|
3802
3802
|
}
|
|
3803
3803
|
|
|
3804
|
+
# Auto-flag parity for parallel worktree Claude sessions (wave-5 fix).
|
|
3805
|
+
# The main RARV loop applies adaptive cost/resilience flags to its claude
|
|
3806
|
+
# invocation (run.sh:16007-16030 effort/max-budget/fallback) plus an MCP
|
|
3807
|
+
# bundle, but the parallel worktree spawn shipped a bare invocation that missed
|
|
3808
|
+
# them entirely. This populates the global _LOKI_WT_AUTO_FLAGS array with ONLY
|
|
3809
|
+
# the flags that are safe on the plain (non stream-json) worktree invocation:
|
|
3810
|
+
# --effort adaptive reasoning depth (dev tier)
|
|
3811
|
+
# --max-budget-usd per-call hard backstop
|
|
3812
|
+
# --fallback-model resilience to model overload/unavailability
|
|
3813
|
+
# --mcp-config (+ --strict-mcp-config) the same MCP bundle the Bun
|
|
3814
|
+
# route emits; a SUPERSET of the bash main loop, which does
|
|
3815
|
+
# NOT emit --mcp-config (see ledger run.sh:15990-15996).
|
|
3816
|
+
# Worktree dev streams benefit from the bundled MCP servers.
|
|
3817
|
+
# We deliberately do NOT replicate the stream-json-coupled flags
|
|
3818
|
+
# (--include-hook-events, --include-partial-messages) nor --output-format /
|
|
3819
|
+
# --session-id: this invocation logs free-form text, not parsed stream-json, so
|
|
3820
|
+
# those would be invalid or unwanted here. Each flag is gated on CLI support +
|
|
3821
|
+
# its opt-out env var, matching the main loop. Extracted from spawn_worktree_session
|
|
3822
|
+
# so it is unit-testable (tests/test-worktree-auto-flags.sh).
|
|
3823
|
+
_loki_build_worktree_claude_flags() {
|
|
3824
|
+
_LOKI_WT_AUTO_FLAGS=()
|
|
3825
|
+
# Non-claude providers never receive these flags.
|
|
3826
|
+
if [ "${PROVIDER_NAME:-claude}" != "claude" ]; then
|
|
3827
|
+
return 0
|
|
3828
|
+
fi
|
|
3829
|
+
# Dev-tier model param drives the fallback derivation (worktree streams are
|
|
3830
|
+
# development work). Resolve via the provider helper when available.
|
|
3831
|
+
local _loki_wt_primary=""
|
|
3832
|
+
if type provider_get_tier_param >/dev/null 2>&1; then
|
|
3833
|
+
_loki_wt_primary="$(provider_get_tier_param development 2>/dev/null || true)"
|
|
3834
|
+
fi
|
|
3835
|
+
if [ "${LOKI_AUTO_EFFORT:-on}" != "off" ] \
|
|
3836
|
+
&& type loki_effort_for_tier >/dev/null 2>&1 \
|
|
3837
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
3838
|
+
&& loki_claude_flag_supported "--effort"; then
|
|
3839
|
+
local _loki_wt_effort
|
|
3840
|
+
_loki_wt_effort="$(loki_effort_for_tier development "${DETECTED_COMPLEXITY:-${LOKI_COMPLEXITY:-standard}}")"
|
|
3841
|
+
[ -n "$_loki_wt_effort" ] && _LOKI_WT_AUTO_FLAGS+=("--effort" "$_loki_wt_effort")
|
|
3842
|
+
fi
|
|
3843
|
+
if [ "${LOKI_AUTO_BUDGET:-on}" != "off" ] \
|
|
3844
|
+
&& type loki_remaining_budget >/dev/null 2>&1 \
|
|
3845
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
3846
|
+
&& loki_claude_flag_supported "--max-budget-usd"; then
|
|
3847
|
+
local _loki_wt_rem
|
|
3848
|
+
_loki_wt_rem="$(loki_remaining_budget)"
|
|
3849
|
+
[ -n "$_loki_wt_rem" ] && _LOKI_WT_AUTO_FLAGS+=("--max-budget-usd" "$_loki_wt_rem")
|
|
3850
|
+
fi
|
|
3851
|
+
if [ "${LOKI_AUTO_FALLBACK:-on}" != "off" ] \
|
|
3852
|
+
&& [ -n "$_loki_wt_primary" ] \
|
|
3853
|
+
&& type loki_fallback_for_primary >/dev/null 2>&1 \
|
|
3854
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
3855
|
+
&& loki_claude_flag_supported "--fallback-model"; then
|
|
3856
|
+
local _loki_wt_fb
|
|
3857
|
+
_loki_wt_fb="$(loki_fallback_for_primary "$_loki_wt_primary")"
|
|
3858
|
+
[ -n "$_loki_wt_fb" ] && _LOKI_WT_AUTO_FLAGS+=("--fallback-model" "$_loki_wt_fb")
|
|
3859
|
+
fi
|
|
3860
|
+
if type loki_mcp_config_argv >/dev/null 2>&1 \
|
|
3861
|
+
&& type loki_claude_flag_supported >/dev/null 2>&1 \
|
|
3862
|
+
&& loki_claude_flag_supported "--mcp-config"; then
|
|
3863
|
+
local _loki_wt_mcp
|
|
3864
|
+
if _loki_wt_mcp="$(loki_mcp_config_argv)" && [ -n "$_loki_wt_mcp" ]; then
|
|
3865
|
+
_LOKI_WT_AUTO_FLAGS+=("--mcp-config")
|
|
3866
|
+
local _loki_wt_mcp_path
|
|
3867
|
+
for _loki_wt_mcp_path in $_loki_wt_mcp; do
|
|
3868
|
+
_LOKI_WT_AUTO_FLAGS+=("$_loki_wt_mcp_path")
|
|
3869
|
+
done
|
|
3870
|
+
# --strict-mcp-config only alongside a real bundle, never bare.
|
|
3871
|
+
if [ "${LOKI_STRICT_MCP:-1}" != "0" ] \
|
|
3872
|
+
&& loki_claude_flag_supported "--strict-mcp-config"; then
|
|
3873
|
+
_LOKI_WT_AUTO_FLAGS+=("--strict-mcp-config")
|
|
3874
|
+
fi
|
|
3875
|
+
fi
|
|
3876
|
+
fi
|
|
3877
|
+
return 0
|
|
3878
|
+
}
|
|
3879
|
+
|
|
3804
3880
|
# Spawn a Claude session in a worktree
|
|
3805
3881
|
spawn_worktree_session() {
|
|
3806
3882
|
local stream_name="$1"
|
|
@@ -3843,6 +3919,11 @@ spawn_worktree_session() {
|
|
|
3843
3919
|
|
|
3844
3920
|
log_step "Spawning ${PROVIDER_DISPLAY_NAME:-Claude} session: $stream_name"
|
|
3845
3921
|
|
|
3922
|
+
# Build the worktree auto-flag set (effort/max-budget/fallback/mcp-config)
|
|
3923
|
+
# BEFORE the ( subshell so it is inherited (a `bash -c` would not). Populates
|
|
3924
|
+
# the global _LOKI_WT_AUTO_FLAGS array. See _loki_build_worktree_claude_flags.
|
|
3925
|
+
_loki_build_worktree_claude_flags
|
|
3926
|
+
|
|
3846
3927
|
(
|
|
3847
3928
|
cd "$worktree_path" || exit 1
|
|
3848
3929
|
_wt_exit=0
|
|
@@ -3858,12 +3939,17 @@ spawn_worktree_session() {
|
|
|
3858
3939
|
if type loki_caveman_activate_env >/dev/null 2>&1; then
|
|
3859
3940
|
_loki_wt_cm="$(loki_caveman_activate_env)"
|
|
3860
3941
|
fi
|
|
3942
|
+
# Expand the auto-flag array with the bash-3.2 empty-array guard
|
|
3943
|
+
# (${arr[@]+...}) so a bare "${arr[@]}" under set -u does not
|
|
3944
|
+
# abort with "unbound variable" when no flags were collected.
|
|
3861
3945
|
if [ -n "$_loki_wt_cm" ]; then
|
|
3862
3946
|
CAVEMAN_DEFAULT_MODE="$_loki_wt_cm" claude --dangerously-skip-permissions \
|
|
3947
|
+
"${_LOKI_WT_AUTO_FLAGS[@]+"${_LOKI_WT_AUTO_FLAGS[@]}"}" \
|
|
3863
3948
|
-p "Loki Mode: $task_prompt. Read .loki/CONTINUITY.md for context." \
|
|
3864
3949
|
>> "$log_file" 2>&1 || _wt_exit=$?
|
|
3865
3950
|
else
|
|
3866
3951
|
claude --dangerously-skip-permissions \
|
|
3952
|
+
"${_LOKI_WT_AUTO_FLAGS[@]+"${_LOKI_WT_AUTO_FLAGS[@]}"}" \
|
|
3867
3953
|
-p "Loki Mode: $task_prompt. Read .loki/CONTINUITY.md for context." \
|
|
3868
3954
|
>> "$log_file" 2>&1 || _wt_exit=$?
|
|
3869
3955
|
fi
|
|
@@ -17773,7 +17859,24 @@ check_human_intervention() {
|
|
|
17773
17859
|
if type ensure_completion_test_evidence &>/dev/null; then
|
|
17774
17860
|
ensure_completion_test_evidence || true
|
|
17775
17861
|
fi
|
|
17776
|
-
|
|
17862
|
+
# TRUST MOAT (fail-CLOSED, parity with the default route ~17153): each gate
|
|
17863
|
+
# arm below is `type fn && ! fn`, so a missing gate fn (council library
|
|
17864
|
+
# failed to source) silently skips that gate and the chain can reach
|
|
17865
|
+
# council_vote and force-approve completion UNGATED -- a fake-green. Probe
|
|
17866
|
+
# the core gate fns first; if any is missing, refuse the force-review
|
|
17867
|
+
# approval and fall through to continue iterating. No-op on healthy loads.
|
|
17868
|
+
_frgate_loadable=1
|
|
17869
|
+
for _frgate_fn in council_checklist_gate council_evidence_gate council_heldout_gate council_assumption_ledger_gate; do
|
|
17870
|
+
if ! type "$_frgate_fn" &>/dev/null; then
|
|
17871
|
+
log_error "Council force-review: gate unavailable: ${_frgate_fn} (council library incomplete)."
|
|
17872
|
+
_frgate_loadable=0
|
|
17873
|
+
fi
|
|
17874
|
+
done
|
|
17875
|
+
if [ "$_frgate_loadable" -eq 0 ]; then
|
|
17876
|
+
log_error "Council force-review: required gates missing; refusing force approval (fail-closed)."
|
|
17877
|
+
unset _frgate_loadable _frgate_fn
|
|
17878
|
+
elif type council_checklist_gate &>/dev/null && ! council_checklist_gate; then
|
|
17879
|
+
unset _frgate_loadable _frgate_fn
|
|
17777
17880
|
log_info "Council force-review: blocked by checklist hard gate"
|
|
17778
17881
|
elif type council_evidence_gate &>/dev/null && ! _evidence_gate_and_surface; then
|
|
17779
17882
|
log_info "Council force-review: blocked by evidence hard gate"
|