loki-mode 8.0.3 → 8.2.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 +54 -10
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +45 -4
- package/autonomy/council-v2.sh +114 -16
- package/autonomy/grill.sh +32 -0
- package/autonomy/lib/done-recognition.sh +16 -4
- package/autonomy/lib/fast_verify.py +346 -0
- package/autonomy/lib/no_mock_scan.py +21 -1
- package/autonomy/lib/prd-enrich.sh +12 -1
- package/autonomy/lib/proof-generator.py +173 -6
- package/autonomy/lib/proof-template.html +77 -12
- package/autonomy/lib/proof-verify.py +52 -0
- package/autonomy/loki +246 -7
- package/autonomy/run.sh +14 -0
- package/autonomy/verify.sh +12 -6
- package/dashboard/__init__.py +1 -1
- package/events/emit.sh +105 -18
- package/loki-ts/dist/loki.js +319 -316
- package/mcp/__init__.py +30 -12
- package/mcp/server.py +150 -0
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/providers/claude.sh +72 -0
- package/providers/codex.sh +13 -0
- package/providers/loader.sh +10 -4
- package/providers/model_catalog.json +114 -17
- package/providers/models.sh +26 -3
- package/providers/opencode.sh +145 -0
- package/references/design-archetypes.md +85 -0
package/events/emit.sh
CHANGED
|
@@ -44,36 +44,79 @@ safe_append_event_jsonl() {
|
|
|
44
44
|
if command -v flock >/dev/null 2>&1; then
|
|
45
45
|
# flock path: bind FD 9 to the sentinel file (created if absent),
|
|
46
46
|
# take an exclusive lock, append, release on subshell exit.
|
|
47
|
+
#
|
|
48
|
+
# `-w 5` is LOAD-BEARING: a bare `flock -x 9` blocks FOREVER. A holder
|
|
49
|
+
# that is killed mid-write (a reaped CI run, a Ctrl-C'd build) leaves
|
|
50
|
+
# every subsequent emit hung, and because emit.sh is spawned per CLI
|
|
51
|
+
# invocation those hangs ACCUMULATE. Measured 2026-07-29: 59 orphaned
|
|
52
|
+
# emit.sh processes, the oldest alive 21 HOURS, on a machine at 0% idle.
|
|
53
|
+
# Observability must never outlive the thing it observes.
|
|
47
54
|
(
|
|
48
|
-
flock -x 9
|
|
55
|
+
flock -w 5 -x 9 || exit 1
|
|
49
56
|
printf '%s\n' "$line" >> "$events_path"
|
|
50
57
|
) 9>"$lock_target"
|
|
51
|
-
|
|
58
|
+
local frc=$?
|
|
59
|
+
if [ "$frc" -ne 0 ]; then
|
|
60
|
+
# Lock unavailable within the timeout: append unlocked rather than
|
|
61
|
+
# block. A rare interleaved line is strictly better than a hung CLI.
|
|
62
|
+
printf '%s\n' "$line" >> "$events_path" 2>/dev/null || true
|
|
63
|
+
fi
|
|
64
|
+
return 0
|
|
52
65
|
fi
|
|
53
66
|
|
|
54
|
-
# Fallback: mkdir-based mutex. mkdir is atomic on POSIX.
|
|
67
|
+
# Fallback: mkdir-based mutex. mkdir is atomic on POSIX. macOS ships NO
|
|
68
|
+
# flock(1), so this is the path real users take -- it must be the robust one.
|
|
55
69
|
local lock_dir="${events_path}.lockdir"
|
|
56
70
|
local attempts=0
|
|
57
|
-
local max_attempts=
|
|
71
|
+
local max_attempts=100 # ~1s at 10ms sleep
|
|
72
|
+
local stale_after=30
|
|
58
73
|
while ! mkdir "$lock_dir" 2>/dev/null; do
|
|
74
|
+
# COUNT EVERY ITERATION, before any `continue` can skip the increment.
|
|
75
|
+
#
|
|
76
|
+
# This was the v8.1.0 fix's blind spot and it kept the P0 alive. Both
|
|
77
|
+
# `continue` paths below (lock vanished mid-stat; stale lock reclaimed)
|
|
78
|
+
# jumped PAST the increment that used to live at the bottom of the loop,
|
|
79
|
+
# so `max_attempts` was unreachable on those paths and the loop spun
|
|
80
|
+
# forever. Under concurrent emits the stale-reclaim path fires over and
|
|
81
|
+
# over, which is precisely the pathological case.
|
|
82
|
+
#
|
|
83
|
+
# MEASURED 2026-07-30, AFTER the v8.1.0 fix shipped: 63 orphaned
|
|
84
|
+
# emit.sh processes, oldest alive 10h51m, each burning ~5% CPU, machine
|
|
85
|
+
# at load 63 on 14 cores. Every orphan started after the fix landed.
|
|
86
|
+
# An unbounded wait whose only exit is a counter must increment that
|
|
87
|
+
# counter on EVERY path, or the bound is decorative.
|
|
59
88
|
attempts=$((attempts + 1))
|
|
60
89
|
if [ "$attempts" -ge "$max_attempts" ]; then
|
|
61
|
-
#
|
|
62
|
-
local age
|
|
63
|
-
age=$(( $(date +%s) - $(stat -f%m "$lock_dir" 2>/dev/null \
|
|
64
|
-
|| stat -c%Y "$lock_dir" 2>/dev/null \
|
|
65
|
-
|| echo 0) ))
|
|
66
|
-
if [ "$age" -gt 30 ]; then
|
|
67
|
-
rmdir "$lock_dir" 2>/dev/null || rm -rf "$lock_dir" 2>/dev/null || true
|
|
68
|
-
attempts=0
|
|
69
|
-
continue
|
|
70
|
-
fi
|
|
71
|
-
# Give up -- best-effort write so observability never blocks.
|
|
90
|
+
# Give up fast -- best-effort write so observability never blocks.
|
|
72
91
|
printf '%s\n' "$line" >> "$events_path" 2>/dev/null || true
|
|
73
|
-
return
|
|
92
|
+
return 0
|
|
93
|
+
fi
|
|
94
|
+
# Check staleness EVERY iteration, not only after exhausting attempts.
|
|
95
|
+
# The old code waited for all 500 attempts before its first staleness
|
|
96
|
+
# check, so a stale lock cost ~5s AND ~500 forked sleep helpers per
|
|
97
|
+
# event; under concurrent emits the locks kept going stale and the
|
|
98
|
+
# processes piled up. Measured before this fix: 10s and ~500 forks for
|
|
99
|
+
# a SINGLE append against an already-stale lock.
|
|
100
|
+
local age=0
|
|
101
|
+
local mtime
|
|
102
|
+
mtime=$(stat -f%m "$lock_dir" 2>/dev/null || stat -c%Y "$lock_dir" 2>/dev/null || echo "")
|
|
103
|
+
if [ -n "$mtime" ]; then
|
|
104
|
+
age=$(( $(date +%s) - mtime ))
|
|
105
|
+
else
|
|
106
|
+
# Lock vanished between the failed mkdir and the stat: retry at once.
|
|
107
|
+
continue
|
|
74
108
|
fi
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
if [ "$age" -gt "$stale_after" ]; then
|
|
110
|
+
rmdir "$lock_dir" 2>/dev/null || rm -rf "$lock_dir" 2>/dev/null || true
|
|
111
|
+
continue
|
|
112
|
+
fi
|
|
113
|
+
# (attempt counting and the give-up branch moved to the TOP of the loop
|
|
114
|
+
# so no `continue` can bypass them)
|
|
115
|
+
# Sleep ~10ms WITHOUT forking when the shell supports fractional sleep
|
|
116
|
+
# (bash's `read -t` needs no external process). perl/sleep are fallbacks.
|
|
117
|
+
read -r -t 0.01 _unused_ < /dev/null 2>/dev/null \
|
|
118
|
+
|| perl -e 'select(undef,undef,undef,0.01)' 2>/dev/null \
|
|
119
|
+
|| sleep 1
|
|
77
120
|
done
|
|
78
121
|
# Critical section.
|
|
79
122
|
printf '%s\n' "$line" >> "$events_path"
|
|
@@ -90,6 +133,50 @@ fi
|
|
|
90
133
|
|
|
91
134
|
set -euo pipefail
|
|
92
135
|
|
|
136
|
+
#-----------------------------------------------------------------------------
|
|
137
|
+
# SELF-REAPER: telemetry must never outlive the thing it observes.
|
|
138
|
+
#-----------------------------------------------------------------------------
|
|
139
|
+
# WHY A WATCHDOG AND NOT ANOTHER LOCK FIX. v8.1.0 fixed a specific unbounded
|
|
140
|
+
# wait (bare `flock -x`, and a staleness check that ran only after 500 attempts).
|
|
141
|
+
# It was a real fix and it was not sufficient: MEASURED 2026-07-30, AFTER that
|
|
142
|
+
# release, 63 orphaned emit.sh processes were alive on this machine, the oldest
|
|
143
|
+
# 10h51m, each burning ~5% CPU, contributing to load 63 on 14 cores. Every one
|
|
144
|
+
# of them started AFTER the fix landed, so whatever wedges emit.sh is not (only)
|
|
145
|
+
# the path that was fixed.
|
|
146
|
+
#
|
|
147
|
+
# The lesson is that patching each discovered hang is an arms race we keep
|
|
148
|
+
# losing, because a hang anywhere in this script has the same user-visible cost.
|
|
149
|
+
# emit.sh is FIRE-AND-FORGET telemetry: nothing waits on its result, and a
|
|
150
|
+
# dropped event is strictly cheaper than a wedged process. So instead of proving
|
|
151
|
+
# no path can block, we cap the lifetime of EVERY path.
|
|
152
|
+
#
|
|
153
|
+
# A background timer SIGKILLs this process after LOKI_EMIT_MAX_SECONDS (default
|
|
154
|
+
# 10). It is deliberately blunt: no cleanup hook, no graceful drain, because the
|
|
155
|
+
# failure mode being defended against is precisely "graceful paths did not run".
|
|
156
|
+
# The killer is disowned so it never becomes a job the parent shell waits on,
|
|
157
|
+
# and it exits immediately when the main process finishes normally.
|
|
158
|
+
#
|
|
159
|
+
# Set LOKI_EMIT_MAX_SECONDS=0 to disable (useful only when debugging emit.sh
|
|
160
|
+
# itself -- an operator who disables it is choosing the orphan risk knowingly).
|
|
161
|
+
_emit_max="${LOKI_EMIT_MAX_SECONDS:-10}"
|
|
162
|
+
case "$_emit_max" in ''|*[!0-9]*) _emit_max=10 ;; esac
|
|
163
|
+
if [ "$_emit_max" -gt 0 ]; then
|
|
164
|
+
_emit_target=$$
|
|
165
|
+
(
|
|
166
|
+
# Poll rather than one long sleep so the watchdog exits promptly on the
|
|
167
|
+
# normal path instead of lingering for the full window.
|
|
168
|
+
_waited=0
|
|
169
|
+
while [ "$_waited" -lt "$_emit_max" ]; do
|
|
170
|
+
sleep 1
|
|
171
|
+
kill -0 "$_emit_target" 2>/dev/null || exit 0
|
|
172
|
+
_waited=$((_waited + 1))
|
|
173
|
+
done
|
|
174
|
+
kill -9 "$_emit_target" 2>/dev/null || true
|
|
175
|
+
) >/dev/null 2>&1 &
|
|
176
|
+
# Disown so the watchdog is not a tracked job of the caller's shell.
|
|
177
|
+
disown 2>/dev/null || true
|
|
178
|
+
fi
|
|
179
|
+
|
|
93
180
|
# Configuration
|
|
94
181
|
LOKI_DIR="${LOKI_DIR:-.loki}"
|
|
95
182
|
EVENTS_DIR="$LOKI_DIR/events/pending"
|