loki-mode 11.76.0 → 11.78.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/lib/check_author.py +146 -0
- package/autonomy/lib/claude-flags.sh +5 -1
- package/autonomy/lib/mcp-config.sh +41 -1
- package/autonomy/lib/proof-analytics-props.py +115 -6
- package/autonomy/lib/proof-verify.py +22 -0
- package/autonomy/lib/telemetry-schema.json +52 -0
- package/autonomy/loki +398 -7
- package/autonomy/telemetry.sh +70 -31
- package/completions/_loki +19 -0
- package/completions/loki.bash +17 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/api_start.py +2 -10
- package/dashboard/secure_tmp.py +92 -0
- package/dashboard/telemetry.py +81 -15
- package/docs/ARCHITECTURE.md +6 -4
- package/docs/{v10/CLI-MODERN.md → CLI-INVENTORY.md} +6 -6
- package/docs/CLI-REFERENCE.md +3 -2
- package/docs/COMPARISON.md +6 -8
- package/docs/COMPONENTS.md +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/PRIVACY.md +47 -11
- package/docs/V10-RESEARCH.md +1 -1
- package/docs/architecture/ADR-001-runtime-migration.md +7 -7
- package/docs/cursor-comparison.md +1 -1
- package/docs/dev/architecture-reference.md +7 -2
- package/docs/dev/project-structure.md +2 -1
- package/docs/image-provenance.md +32 -7
- package/docs/slack.md +2 -0
- package/docs/v11/COST-HALF.md +2 -1
- package/loki-ts/dist/loki.js +1744 -1604
- package/loki-ts/package.json +2 -2
- package/mcp/__init__.py +1 -1
- package/mcp/requirements.txt +1 -1
- package/package.json +3 -3
- package/packages/control-plane/dist/server.js +20045 -19119
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/providers/aider.sh +7 -0
- package/providers/claude.sh +8 -1
- package/providers/cline.sh +3 -0
- package/providers/loader.sh +5 -0
- package/providers/model_catalog.json +3 -0
- package/providers/models.sh +59 -0
- package/providers/opencode.sh +9 -3
- package/skills/providers.md +1 -0
- package/web-app/dist/assets/{AdminPage-DoGPqZIa.js → AdminPage-BPF2Xehf.js} +1 -1
- package/web-app/dist/assets/{Avatar-Dyf7xJsz.js → Avatar-CSW9BhBU.js} +1 -1
- package/web-app/dist/assets/{Badge-Cm4dADew.js → Badge-wgm79wim.js} +1 -1
- package/web-app/dist/assets/{Button-LGxjldY5.js → Button-BcIAcPKr.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DuC9g48J.js → CockpitPage-BFi-0Kwj.js} +1 -1
- package/web-app/dist/assets/{ComparePage-DuTrmedK.js → ComparePage-B7Wibrfn.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-B0f7NEiT.js → ErrorBoundary-BYyOVh2c.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-DsE8B9Hw.js → EvidenceReceiptPanel-C9QcrjNl.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-aYflOqd4.js → GitHubIssuesPanel-CWbuBxVZ.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CCJGck6c.js → GitHubPRsPanel-qcBHK615.js} +1 -1
- package/web-app/dist/assets/{HomePage-BJU3PVkJ.js → HomePage-B1mYFU4t.js} +2 -2
- package/web-app/dist/assets/{LoginPage-UzYoF5CJ.js → LoginPage-CmIO1xL7.js} +1 -1
- package/web-app/dist/assets/{MagicPage-5X3hQ7lt.js → MagicPage-L7UP4qKP.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-CFsTNyOu.js → MetricsPage-DVPCjN1U.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-Ba0nMlqG.js → NotFoundPage-D3XZmCH5.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-fvU-EYZb.js → ProjectPage-BlaMzlVi.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-CVEKCorG.js → ProjectsPage-DN7W28Zq.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-JGbsNmqD.js → SettingsPage-2u9ODYB5.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-Dr2c_V-y.js → ShowcasePage-CoKuTfab.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-D9dCc4Ir.js → SystemSettingsPage-Crhj2XAU.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-B56u-bod.js → TeamsPage-DehMgnKh.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CUwqj6ge.js → TemplatesPage-DGtwfZgU.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Cm920SDi.js → TerminalOutput-7HZRtXoC.js} +1 -1
- package/web-app/dist/assets/{activity-DDF6juD-.js → activity-DAp5wswo.js} +1 -1
- package/web-app/dist/assets/{bell-7MW_oAcb.js → bell-Ncvnrz7d.js} +1 -1
- package/web-app/dist/assets/{bot-b63uz2Ov.js → bot-BWob3K3E.js} +1 -1
- package/web-app/dist/assets/{check-2oBMAALa.js → check-DReWA2vm.js} +1 -1
- package/web-app/dist/assets/{chevron-left-UuhI6pbU.js → chevron-left-CBJpzVU1.js} +1 -1
- package/web-app/dist/assets/{circle-alert-BikRzQA7.js → circle-alert-2c_vs5PW.js} +1 -1
- package/web-app/dist/assets/{clock-BdkTaAgO.js → clock-BrS_ZKKi.js} +1 -1
- package/web-app/dist/assets/{cloud-oKg24kTO.js → cloud-Czvuo51R.js} +1 -1
- package/web-app/dist/assets/{code-xml-CpDmdnYQ.js → code-xml-BWEqJ2I8.js} +1 -1
- package/web-app/dist/assets/{database-BT133u2w.js → database-CJ6M-oK-.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-khUEgmXm.js → dollar-sign-DSObKmVv.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-_TlPqN5g.js → file-code-corner-CVEW-gF5.js} +1 -1
- package/web-app/dist/assets/{file-plus-B5UT0SZU.js → file-plus-CAK0I0tE.js} +1 -1
- package/web-app/dist/assets/{globe-Chhrgg2K.js → globe-DeUgcsSZ.js} +1 -1
- package/web-app/dist/assets/{hammer-DJ6Z4tiA.js → hammer-B74PyjS0.js} +1 -1
- package/web-app/dist/assets/{index-BprNVRFx.js → index-DW3_HRqp.js} +3 -3
- package/web-app/dist/assets/{layers-CtYWuenz.js → layers-Dw7kaGyJ.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BRxnh_Va.js → loader-circle-B6xzlAjw.js} +1 -1
- package/web-app/dist/assets/{lock-btaZm2bN.js → lock-5ncCPzIG.js} +1 -1
- package/web-app/dist/assets/{package-BFU4d6gL.js → package-x59_dTx0.js} +1 -1
- package/web-app/dist/assets/{plus-BogXrZQi.js → plus-BcVBzjpS.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CsdQkR5n.js → refresh-cw-CNrJhks6.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-BeLxChlE.js → rotate-ccw-BpmS_ezI.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DAXSJc0i.js → scroll-text-CwBYNyoj.js} +1 -1
- package/web-app/dist/assets/{server-Cf_yJUqU.js → server-C4hnxkk6.js} +1 -1
- package/web-app/dist/assets/{shield-alert-B2gh1hM3.js → shield-alert-CSVUyWCM.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DxYjJ1v8.js → thumbs-up-DEU9fmDX.js} +1 -1
- package/web-app/dist/assets/{trash-2-BaAsSOYk.js → trash-2-DJKMevAm.js} +1 -1
- package/web-app/dist/assets/{trending-down-Kc-Tady7.js → trending-down-Dq3wAM59.js} +1 -1
- package/web-app/dist/assets/{trending-up-BDsns895.js → trending-up-DribptN4.js} +1 -1
- package/web-app/dist/assets/{upload-CCJzLwO9.js → upload-DUlZxsH1.js} +1 -1
- package/web-app/dist/assets/{usePolling-4aqc_A7F.js → usePolling-PecqVZ0k.js} +1 -1
- package/web-app/dist/assets/{user-BzoHXLDe.js → user-BcLYLDVU.js} +1 -1
- package/web-app/dist/index.html +1 -1
package/autonomy/telemetry.sh
CHANGED
|
@@ -52,6 +52,12 @@ _loki_telemetry_auto_off() {
|
|
|
52
52
|
return 1
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
# BASH_SOURCE is empty when sourced under zsh: skip the lookup rather than
|
|
56
|
+
# resolve dirname "" to the current directory.
|
|
57
|
+
_LOKI_TELEMETRY_SELF_DIR=""
|
|
58
|
+
if [ -n "${BASH_SOURCE[0]:-}" ]; then
|
|
59
|
+
_LOKI_TELEMETRY_SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd -P)" || _LOKI_TELEMETRY_SELF_DIR=""
|
|
60
|
+
fi
|
|
55
61
|
LOKI_POSTHOG_HOST="${LOKI_TELEMETRY_ENDPOINT:-https://us.i.posthog.com}"
|
|
56
62
|
LOKI_POSTHOG_KEY="phc_ya0vGBru41AJWtGNfZZ8H9W4yjoZy4KON0nnayS7s87"
|
|
57
63
|
|
|
@@ -208,48 +214,81 @@ _loki_known_blocker() {
|
|
|
208
214
|
esac
|
|
209
215
|
}
|
|
210
216
|
|
|
217
|
+
# _loki_os_family: raw `uname -s` collapsed to a fixed enum (MINGW64_NT-10.0-19045
|
|
218
|
+
# and friends are one family, not one value per Windows build).
|
|
219
|
+
_loki_os_family() {
|
|
220
|
+
case "${1:-}" in
|
|
221
|
+
Linux*) printf 'linux' ;;
|
|
222
|
+
Darwin*) printf 'darwin' ;;
|
|
223
|
+
MINGW*|MSYS*|CYGWIN*|Windows*) printf 'windows' ;;
|
|
224
|
+
*) printf 'other' ;;
|
|
225
|
+
esac
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_loki_arch_family() {
|
|
229
|
+
case "${1:-}" in
|
|
230
|
+
x86_64|amd64|x64) printf 'x64' ;;
|
|
231
|
+
arm64|aarch64) printf 'arm64' ;;
|
|
232
|
+
*) printf 'other' ;;
|
|
233
|
+
esac
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# _loki_traffic: who is driving this process, as the enum user | ci | internal,
|
|
237
|
+
# so analytics can filter bots and our own seats. internal wins, then ci (CI env
|
|
238
|
+
# vars or a non-interactive session), else user. Mirrors _traffic in
|
|
239
|
+
# dashboard/telemetry.py.
|
|
240
|
+
_loki_traffic() {
|
|
241
|
+
if [ "${LOKI_INTERNAL:-}" = "1" ] || [ "${LOKI_SEAT_TICK:-}" = "1" ] || [ -n "${LOKI_SEAT:-}" ] || [ -n "${LOKI_COMPANY_SEAT:-}" ]; then
|
|
242
|
+
printf 'internal'; return
|
|
243
|
+
fi
|
|
244
|
+
if [ -n "${CI:-}" ] || [ -n "${GITHUB_ACTIONS:-}" ] || [ -n "${GITLAB_CI:-}" ] \
|
|
245
|
+
|| [ -n "${BUILDKITE:-}" ] || [ -n "${JENKINS_URL:-}" ] \
|
|
246
|
+
|| [ -n "${TEAMCITY_VERSION:-}" ] || [ -n "${CONTINUOUS_INTEGRATION:-}" ]; then
|
|
247
|
+
printf 'ci'; return
|
|
248
|
+
fi
|
|
249
|
+
if [ -n "${LOKI_TTY_INTERACTIVE:-}" ]; then
|
|
250
|
+
[ "${LOKI_TTY_INTERACTIVE}" = "1" ] && printf 'user' || printf 'ci'
|
|
251
|
+
return
|
|
252
|
+
fi
|
|
253
|
+
if [ ! -t 1 ] && [ ! -t 0 ]; then printf 'ci'; return; fi
|
|
254
|
+
printf 'user'
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# loki_telemetry <event> [key=value...]: the ONE send path of the bash senders.
|
|
258
|
+
# Order is load-bearing: the opt-out gate runs before anything else, so with
|
|
259
|
+
# LOKI_TELEMETRY=off or DO_NOT_TRACK=1 no python, no curl and no network code is
|
|
260
|
+
# reached. Then every key and value is filtered through
|
|
261
|
+
# autonomy/lib/telemetry-schema.json (unlisted event: nothing is sent; unknown
|
|
262
|
+
# key or out-of-enum value: dropped). Callers' pairs are untrusted by design;
|
|
263
|
+
# the trusted base properties are appended last so a caller cannot override them.
|
|
211
264
|
loki_telemetry() {
|
|
212
265
|
_loki_telemetry_enabled || return 0
|
|
213
|
-
local event="$1"; shift
|
|
266
|
+
local event="${1:-}"; shift
|
|
267
|
+
[ -n "$event" ] || return 0
|
|
268
|
+
# The schema sits next to this file; prefer that over SCRIPT_DIR, which some
|
|
269
|
+
# callers leave unset when they only source this file.
|
|
270
|
+
local _lib="${_LOKI_TELEMETRY_SELF_DIR:-}/lib"
|
|
271
|
+
[ -f "$_lib/telemetry-schema.json" ] || _lib="${SCRIPT_DIR:-${SKILL_DIR:-}}/lib"
|
|
272
|
+
[ -f "$_lib/telemetry-schema.json" ] && [ -f "$_lib/proof-analytics-props.py" ] || return 0
|
|
214
273
|
local distinct_id
|
|
215
274
|
distinct_id=$(_loki_telemetry_id 2>/dev/null) || return 0
|
|
216
275
|
local version
|
|
217
|
-
version=$(cat "${SCRIPT_DIR:-${SKILL_DIR:-}}/VERSION" 2>/dev/null || cat "${SCRIPT_DIR:-${SKILL_DIR:-}}/../VERSION" 2>/dev/null || echo "unknown")
|
|
276
|
+
version=$(cat "${SCRIPT_DIR:-${SKILL_DIR:-}}/VERSION" 2>/dev/null || cat "${SCRIPT_DIR:-${SKILL_DIR:-}}/../VERSION" 2>/dev/null || cat "${_LOKI_TELEMETRY_SELF_DIR:-}/../VERSION" 2>/dev/null || echo "unknown")
|
|
218
277
|
version=$(echo "$version" | tr -d '[:space:]')
|
|
219
278
|
local channel
|
|
220
279
|
channel=$(_loki_detect_channel 2>/dev/null || echo "unknown")
|
|
221
|
-
local
|
|
222
|
-
|
|
223
|
-
arch=$(uname -m 2>/dev/null
|
|
280
|
+
local os_family arch
|
|
281
|
+
os_family=$(_loki_os_family "$(uname -s 2>/dev/null)")
|
|
282
|
+
arch=$(_loki_arch_family "$(uname -m 2>/dev/null)")
|
|
224
283
|
|
|
225
|
-
# Build JSON payload safely using Python to prevent injection.
|
|
226
284
|
# The extra key=value pairs are forwarded as "$@" -- one argv slot per pair,
|
|
227
|
-
# QUOTED
|
|
228
|
-
#
|
|
229
|
-
# containing a space was truncated (`prd=my file.md` -> `prd=my`, rest
|
|
230
|
-
# dropped) or, if a fragment itself contained `=`, forged an extra PostHog
|
|
231
|
-
# property KEY; and the unquoted expansion also globbed, so `spec=*.md`
|
|
232
|
-
# expanded against the cwd and injected file names into the payload. Callers
|
|
233
|
-
# already pass each pair as its own quoted word, so "$@" is the array and no
|
|
234
|
-
# caller signature changes.
|
|
285
|
+
# QUOTED, so a value with spaces or glob characters is neither split nor
|
|
286
|
+
# expanded. The python side splits on the first "=" only.
|
|
235
287
|
local payload
|
|
236
|
-
payload=$(python3 -
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if '=' in arg:
|
|
241
|
-
k, v = arg.split('=', 1)
|
|
242
|
-
props[k] = v
|
|
243
|
-
print(json.dumps({'api_key': '$LOKI_POSTHOG_KEY', 'event': sys.argv[5] if len(sys.argv) > 5 else '', 'distinct_id': '$distinct_id', 'properties': props}))
|
|
244
|
-
" "$os_name" "$arch" "$version" "$channel" "$@" 2>/dev/null) || return 0
|
|
245
|
-
# Re-inject event and distinct_id properly
|
|
246
|
-
payload=$(python3 -c "
|
|
247
|
-
import json, sys
|
|
248
|
-
d = json.loads(sys.argv[1])
|
|
249
|
-
d['event'] = sys.argv[2]
|
|
250
|
-
d['distinct_id'] = sys.argv[3]
|
|
251
|
-
print(json.dumps(d))
|
|
252
|
-
" "$payload" "$event" "$distinct_id" 2>/dev/null) || return 0
|
|
288
|
+
payload=$(python3 "$_lib/proof-analytics-props.py" --payload "$event" "$distinct_id" "$LOKI_POSTHOG_KEY" \
|
|
289
|
+
"$@" "os_family=$os_family" "arch=$arch" "version=$version" "channel=$channel" \
|
|
290
|
+
"traffic=$(_loki_traffic)" 2>/dev/null) || return 0
|
|
291
|
+
[ -n "$payload" ] || return 0
|
|
253
292
|
|
|
254
293
|
(curl -sS --max-time 3 -X POST "${LOKI_POSTHOG_HOST}/capture/" \
|
|
255
294
|
-H "Content-Type: application/json" \
|
package/completions/_loki
CHANGED
|
@@ -74,6 +74,15 @@ function _loki {
|
|
|
74
74
|
agent)
|
|
75
75
|
_loki_agent
|
|
76
76
|
;;
|
|
77
|
+
cost)
|
|
78
|
+
_arguments \
|
|
79
|
+
'--json[Output machine-readable JSON]' \
|
|
80
|
+
'--last[Show only the last N runs]:count:' \
|
|
81
|
+
'--month[Only runs generated in that month]:month (YYYY-MM):' \
|
|
82
|
+
'--csv[One CSV row per run]' \
|
|
83
|
+
'--detail[Per-iteration cache and trend breakdown]' \
|
|
84
|
+
'--help[Show help]'
|
|
85
|
+
;;
|
|
77
86
|
estimate)
|
|
78
87
|
_arguments \
|
|
79
88
|
'--json[Output machine-readable JSON]' \
|
|
@@ -86,6 +95,7 @@ function _loki {
|
|
|
86
95
|
'--json[Output machine-readable JSON]' \
|
|
87
96
|
'--badge[Print verified deploy badge]' \
|
|
88
97
|
'--probe[Re-probe last deploy health URL]' \
|
|
98
|
+
'--follow[Print one line per run event until the run ends]' \
|
|
89
99
|
'--help[Show help]'
|
|
90
100
|
;;
|
|
91
101
|
doctor)
|
|
@@ -203,6 +213,15 @@ function _loki {
|
|
|
203
213
|
code)
|
|
204
214
|
_loki_code
|
|
205
215
|
;;
|
|
216
|
+
github)
|
|
217
|
+
_arguments \
|
|
218
|
+
'1:subcommand:(status sync export pr publish protect)' \
|
|
219
|
+
'--dry-run[protect: show the branch-protection read, change nothing]' \
|
|
220
|
+
'--off[protect: restore the saved branch protection]' \
|
|
221
|
+
'--public[publish: create a public repository]' \
|
|
222
|
+
'--private[publish: create a private repository]' \
|
|
223
|
+
'--dir[publish: project directory]:directory:_directories'
|
|
224
|
+
;;
|
|
206
225
|
deploy)
|
|
207
226
|
_arguments \
|
|
208
227
|
'--dir[Project directory to scan]:directory:_directories' \
|
package/completions/loki.bash
CHANGED
|
@@ -125,6 +125,13 @@ _loki_completion() {
|
|
|
125
125
|
COMPREPLY=( $(compgen -W "${syslog_cmds}" -- "$cur") )
|
|
126
126
|
;;
|
|
127
127
|
|
|
128
|
+
cost)
|
|
129
|
+
if [[ "$cur" == -* ]]; then
|
|
130
|
+
COMPREPLY=( $(compgen -W "--json --last --month --csv --detail --help" -- "$cur") )
|
|
131
|
+
return 0
|
|
132
|
+
fi
|
|
133
|
+
;;
|
|
134
|
+
|
|
128
135
|
estimate)
|
|
129
136
|
if [[ "$cur" == -* ]]; then
|
|
130
137
|
COMPREPLY=( $(compgen -W "--json --model --provider --help" -- "$cur") )
|
|
@@ -134,7 +141,7 @@ _loki_completion() {
|
|
|
134
141
|
|
|
135
142
|
status)
|
|
136
143
|
if [[ "$cur" == -* ]]; then
|
|
137
|
-
COMPREPLY=( $(compgen -W "--json --badge --probe --help" -- "$cur") )
|
|
144
|
+
COMPREPLY=( $(compgen -W "--json --badge --probe --follow --help" -- "$cur") )
|
|
138
145
|
return 0
|
|
139
146
|
fi
|
|
140
147
|
;;
|
|
@@ -152,6 +159,15 @@ _loki_completion() {
|
|
|
152
159
|
fi
|
|
153
160
|
;;
|
|
154
161
|
|
|
162
|
+
github)
|
|
163
|
+
if [[ "$cur" == -* ]]; then
|
|
164
|
+
COMPREPLY=( $(compgen -W "--dry-run --off --public --private --dir --help" -- "$cur") )
|
|
165
|
+
return 0
|
|
166
|
+
fi
|
|
167
|
+
COMPREPLY=( $(compgen -W "status sync export pr publish protect" -- "$cur") )
|
|
168
|
+
return 0
|
|
169
|
+
;;
|
|
170
|
+
|
|
155
171
|
deploy)
|
|
156
172
|
if [[ "$cur" == -* ]]; then
|
|
157
173
|
COMPREPLY=( $(compgen -W "--execute --destination --canary --env-file --domain --health-url --health-match --auto-rollback --dir --no-clip --allow-unanchored --help" -- "$cur") )
|
package/dashboard/__init__.py
CHANGED
package/dashboard/api_start.py
CHANGED
|
@@ -32,7 +32,7 @@ from typing import Optional
|
|
|
32
32
|
from fastapi import APIRouter, Depends, HTTPException
|
|
33
33
|
from pydantic import BaseModel
|
|
34
34
|
|
|
35
|
-
from . import auth
|
|
35
|
+
from . import auth, secure_tmp
|
|
36
36
|
|
|
37
37
|
logger = logging.getLogger(__name__)
|
|
38
38
|
router = APIRouter(tags=["start"])
|
|
@@ -85,15 +85,7 @@ def _creds_dir() -> Path:
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
def _write_secret(name: str, value: str) -> None:
|
|
88
|
-
|
|
89
|
-
tmp = dest.with_name(".%s.%d.tmp" % (dest.name, os.getpid()))
|
|
90
|
-
fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
91
|
-
try:
|
|
92
|
-
os.fchmod(fd, 0o600)
|
|
93
|
-
os.write(fd, (value + "\n").encode())
|
|
94
|
-
finally:
|
|
95
|
-
os.close(fd)
|
|
96
|
-
os.replace(tmp, dest)
|
|
88
|
+
secure_tmp.write_secret_atomic(_secret_path(name), (value + "\n").encode())
|
|
97
89
|
|
|
98
90
|
|
|
99
91
|
def _read_secret(name: str) -> Optional[str]:
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Shared write-temp-then-rename for secrets, plus the stale-temp sweep.
|
|
2
|
+
|
|
3
|
+
One-line justification for a new file: no existing dashboard module owns
|
|
4
|
+
secret-temp creation and cleanup (build_supervisor._atomic_write is private to
|
|
5
|
+
execution state and does not sweep). A writer killed between the temp write and
|
|
6
|
+
the rename would otherwise leave the secret on disk forever.
|
|
7
|
+
|
|
8
|
+
Mirrors sweepStaleTmp / writeExclusiveTmp in loki-ts/src/util/atomic.ts and
|
|
9
|
+
_loki_mcp_sweep_stale_tmp in autonomy/lib/mcp-config.sh.
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import stat
|
|
14
|
+
import time
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
STALE_SECONDS = 60 * 60
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _pid_alive(pid: int) -> bool:
|
|
21
|
+
try:
|
|
22
|
+
os.kill(pid, 0)
|
|
23
|
+
except ProcessLookupError:
|
|
24
|
+
return False
|
|
25
|
+
except PermissionError:
|
|
26
|
+
return True # EPERM: the process exists, owned by someone else
|
|
27
|
+
except (OSError, OverflowError, ValueError):
|
|
28
|
+
return False
|
|
29
|
+
return True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def sweep_stale_tmp(directory, pattern: str) -> None:
|
|
33
|
+
"""Remove temps left by a dead writer.
|
|
34
|
+
|
|
35
|
+
`pattern` is fullmatched against each name in `directory` (never recursive)
|
|
36
|
+
and its first group is the writer pid. An entry is removed only when it is a
|
|
37
|
+
regular file (not a symlink), owned by this uid, not ours, and its pid is
|
|
38
|
+
dead or its mtime is over 60 minutes (pid reuse). A live writer's temp stays.
|
|
39
|
+
"""
|
|
40
|
+
rx = re.compile(pattern, re.DOTALL)
|
|
41
|
+
uid = os.getuid()
|
|
42
|
+
try:
|
|
43
|
+
entries = list(os.scandir(directory))
|
|
44
|
+
except OSError:
|
|
45
|
+
return
|
|
46
|
+
for entry in entries:
|
|
47
|
+
m = rx.fullmatch(entry.name)
|
|
48
|
+
if not m:
|
|
49
|
+
continue
|
|
50
|
+
pid = int(m.group(1))
|
|
51
|
+
if pid == os.getpid():
|
|
52
|
+
continue
|
|
53
|
+
if pid > 2**31 - 1:
|
|
54
|
+
continue # not a pid we could own; never signal or remove it
|
|
55
|
+
try:
|
|
56
|
+
st = os.lstat(entry.path)
|
|
57
|
+
if not stat.S_ISREG(st.st_mode) or st.st_uid != uid:
|
|
58
|
+
continue
|
|
59
|
+
if _pid_alive(pid) and time.time() - st.st_mtime <= STALE_SECONDS:
|
|
60
|
+
continue
|
|
61
|
+
os.unlink(entry.path)
|
|
62
|
+
except OSError:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def write_secret_atomic(dest, data: bytes, mode: int = 0o600) -> None:
|
|
67
|
+
"""Write `data` to `dest` via `.<name>.<pid>.tmp` then rename.
|
|
68
|
+
|
|
69
|
+
The temp is created O_CREAT|O_EXCL|O_NOFOLLOW so a planted symlink or file
|
|
70
|
+
is never written through. Stale temps of dead writers are swept first.
|
|
71
|
+
"""
|
|
72
|
+
dest = Path(dest)
|
|
73
|
+
sweep_stale_tmp(dest.parent, r"\.%s\.(\d+)\.tmp" % re.escape(dest.name))
|
|
74
|
+
tmp = dest.with_name(".%s.%d.tmp" % (dest.name, os.getpid()))
|
|
75
|
+
try:
|
|
76
|
+
os.unlink(tmp) # our own pid left over from a reused pid
|
|
77
|
+
except FileNotFoundError:
|
|
78
|
+
pass
|
|
79
|
+
fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, mode)
|
|
80
|
+
try:
|
|
81
|
+
try:
|
|
82
|
+
os.fchmod(fd, mode)
|
|
83
|
+
os.write(fd, data)
|
|
84
|
+
finally:
|
|
85
|
+
os.close(fd)
|
|
86
|
+
os.replace(tmp, dest)
|
|
87
|
+
except BaseException:
|
|
88
|
+
try:
|
|
89
|
+
os.unlink(tmp)
|
|
90
|
+
except OSError:
|
|
91
|
+
pass
|
|
92
|
+
raise
|
package/dashboard/telemetry.py
CHANGED
|
@@ -13,6 +13,7 @@ Force-on: LOKI_TELEMETRY=on OR ~/.loki/config: TELEMETRY_ENABLED=true
|
|
|
13
13
|
All calls are fire-and-forget, silent on failure, non-blocking.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
import importlib.util
|
|
16
17
|
import json
|
|
17
18
|
import os
|
|
18
19
|
import platform
|
|
@@ -153,27 +154,92 @@ def _get_version():
|
|
|
153
154
|
return "unknown"
|
|
154
155
|
|
|
155
156
|
|
|
157
|
+
_LIB = Path(__file__).resolve().parent.parent / "autonomy" / "lib"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _load_schema_module():
|
|
161
|
+
"""The one filter and schema live in autonomy/lib (shared with the bash
|
|
162
|
+
sender). Missing files mean nothing is sent: fail closed."""
|
|
163
|
+
spec = importlib.util.spec_from_file_location(
|
|
164
|
+
"loki_telemetry_schema", str(_LIB / "proof-analytics-props.py"))
|
|
165
|
+
mod = importlib.util.module_from_spec(spec)
|
|
166
|
+
spec.loader.exec_module(mod)
|
|
167
|
+
return mod
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _os_family():
|
|
171
|
+
name = platform.system().lower()
|
|
172
|
+
if name in ("linux", "darwin"):
|
|
173
|
+
return name
|
|
174
|
+
if name.startswith(("windows", "mingw", "msys", "cygwin")):
|
|
175
|
+
return "windows"
|
|
176
|
+
return "other"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _arch_family():
|
|
180
|
+
m = platform.machine().lower()
|
|
181
|
+
if m in ("x86_64", "amd64", "x64"):
|
|
182
|
+
return "x64"
|
|
183
|
+
if m in ("arm64", "aarch64"):
|
|
184
|
+
return "arm64"
|
|
185
|
+
return "other"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _traffic():
|
|
189
|
+
"""user | ci | internal. Mirrors _loki_traffic in autonomy/telemetry.sh."""
|
|
190
|
+
env = os.environ
|
|
191
|
+
if env.get("LOKI_INTERNAL") == "1" or env.get("LOKI_SEAT_TICK") == "1" or env.get("LOKI_SEAT") or env.get("LOKI_COMPANY_SEAT"):
|
|
192
|
+
return "internal"
|
|
193
|
+
for var in ("CI", "GITHUB_ACTIONS", "GITLAB_CI", "BUILDKITE", "JENKINS_URL",
|
|
194
|
+
"TEAMCITY_VERSION", "CONTINUOUS_INTEGRATION"):
|
|
195
|
+
if env.get(var):
|
|
196
|
+
return "ci"
|
|
197
|
+
sig = env.get("LOKI_TTY_INTERACTIVE")
|
|
198
|
+
if sig:
|
|
199
|
+
return "user" if sig == "1" else "ci"
|
|
200
|
+
try:
|
|
201
|
+
if not sys.stdout.isatty() and not sys.stdin.isatty():
|
|
202
|
+
return "ci"
|
|
203
|
+
except Exception:
|
|
204
|
+
pass
|
|
205
|
+
return "user"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def build_payload(event, properties=None):
|
|
209
|
+
"""Return the schema-filtered capture body, or None when the event is not
|
|
210
|
+
listed. Caller properties are untrusted; trusted base properties win."""
|
|
211
|
+
mod = _load_schema_module()
|
|
212
|
+
raw = dict(properties or {})
|
|
213
|
+
raw.update({
|
|
214
|
+
"os_family": _os_family(),
|
|
215
|
+
"arch": _arch_family(),
|
|
216
|
+
"version": _get_version(),
|
|
217
|
+
"channel": _detect_channel(),
|
|
218
|
+
"traffic": _traffic(),
|
|
219
|
+
})
|
|
220
|
+
props = mod.sanitize(mod.load_schema(), event, raw)
|
|
221
|
+
if props is None:
|
|
222
|
+
return None
|
|
223
|
+
return {
|
|
224
|
+
"api_key": _POSTHOG_KEY,
|
|
225
|
+
"event": event,
|
|
226
|
+
"distinct_id": _get_distinct_id(),
|
|
227
|
+
"properties": props,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
156
231
|
def send_telemetry(event, properties=None):
|
|
157
|
-
"""Send anonymous telemetry event. Non-blocking, silent on failure.
|
|
232
|
+
"""Send anonymous telemetry event. Non-blocking, silent on failure.
|
|
233
|
+
The opt-out gate runs first, before any schema or network code."""
|
|
158
234
|
if not _is_enabled():
|
|
159
235
|
return
|
|
160
236
|
|
|
161
237
|
def _send():
|
|
162
238
|
try:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
"channel": _detect_channel(),
|
|
168
|
-
}
|
|
169
|
-
if properties:
|
|
170
|
-
props.update(properties)
|
|
171
|
-
payload = json.dumps({
|
|
172
|
-
"api_key": _POSTHOG_KEY,
|
|
173
|
-
"event": event,
|
|
174
|
-
"distinct_id": _get_distinct_id(),
|
|
175
|
-
"properties": props,
|
|
176
|
-
}).encode()
|
|
239
|
+
body = build_payload(event, properties)
|
|
240
|
+
if body is None:
|
|
241
|
+
return
|
|
242
|
+
payload = json.dumps(body).encode()
|
|
177
243
|
req = Request(
|
|
178
244
|
f"{_POSTHOG_HOST}/capture/",
|
|
179
245
|
data=payload,
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -244,8 +244,9 @@ templates/ 21 PRD templates (saas, cli, discord-bot, ...)
|
|
|
244
244
|
agents/ Agent registry: types.json, hub install, managed registry
|
|
245
245
|
benchmarks/ SWE-bench and HumanEval harnesses
|
|
246
246
|
plugins/ Claude Code plugin packaging
|
|
247
|
-
.github/workflows/
|
|
248
|
-
|
|
247
|
+
.github/workflows/ release.yml, converge-dist-tags.yml,
|
|
248
|
+
sdk-freshness.yml (the pre-push gate is
|
|
249
|
+
scripts/local-ci.sh, run locally)
|
|
249
250
|
SKILL.md Slim core skill (progressive disclosure entry)
|
|
250
251
|
CLAUDE.md Project + agent operating instructions
|
|
251
252
|
VERSION / package.json Single source of version truth
|
|
@@ -324,8 +325,9 @@ docker/ Container distribution (Dockerfiles, compose)
|
|
|
324
325
|
TS providers module.
|
|
325
326
|
- **Docker / Docker Compose** for distribution and for the multi-service,
|
|
326
327
|
12-factor stacks Loki generates (web + database + cache with healthchecks).
|
|
327
|
-
- **GitHub Actions**
|
|
328
|
-
|
|
328
|
+
- **GitHub Actions** (`.github/workflows/`): `release.yml` (multi-channel
|
|
329
|
+
release), `converge-dist-tags.yml` and `sdk-freshness.yml`. The test suites
|
|
330
|
+
run locally through `scripts/local-ci.sh`, not in a workflow.
|
|
329
331
|
|
|
330
332
|
---
|
|
331
333
|
|
|
@@ -11,7 +11,7 @@ Classes: KEEP-MODERN (v10-native or engine-neutral), UPDATE (live but needs mode
|
|
|
11
11
|
- DROP-LEGACY: 31
|
|
12
12
|
- DELETE: 14
|
|
13
13
|
- Total rows: 144
|
|
14
|
-
- Registry paths (commands and nested subcommands):
|
|
14
|
+
- Registry paths (commands and nested subcommands): 214
|
|
15
15
|
|
|
16
16
|
## Commands
|
|
17
17
|
|
|
@@ -169,19 +169,19 @@ Classes: KEEP-MODERN (v10-native or engine-neutral), UPDATE (live but needs mode
|
|
|
169
169
|
- compliance: 1
|
|
170
170
|
- config: 4
|
|
171
171
|
- context: 5
|
|
172
|
-
- cost:
|
|
172
|
+
- cost: 7
|
|
173
173
|
- deploy: 1
|
|
174
174
|
- docs: 4
|
|
175
175
|
- enterprise: 2
|
|
176
176
|
- explain: 5
|
|
177
|
-
- export:
|
|
178
|
-
- github:
|
|
177
|
+
- export: 7
|
|
178
|
+
- github: 7
|
|
179
179
|
- import: 1
|
|
180
180
|
- init: 21
|
|
181
181
|
- issue: 2
|
|
182
182
|
- magic: 12
|
|
183
183
|
- memory: 7
|
|
184
|
-
- metrics:
|
|
184
|
+
- metrics: 7
|
|
185
185
|
- modernize: 3
|
|
186
186
|
- next: 1
|
|
187
187
|
- notify: 6
|
|
@@ -198,7 +198,7 @@ Classes: KEEP-MODERN (v10-native or engine-neutral), UPDATE (live but needs mode
|
|
|
198
198
|
- secrets: 3
|
|
199
199
|
- secure: 3
|
|
200
200
|
- setup-skill: 1
|
|
201
|
-
- share:
|
|
201
|
+
- share: 7
|
|
202
202
|
- ship: 1
|
|
203
203
|
- syslog: 3
|
|
204
204
|
- telemetry: 1
|
package/docs/CLI-REFERENCE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# CLI reference
|
|
2
2
|
|
|
3
|
-
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.
|
|
4
|
-
Hidden and legacy-only commands are omitted; see `docs/
|
|
3
|
+
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.78.0. Do not edit by hand.
|
|
4
|
+
Hidden and legacy-only commands are omitted; see `docs/CLI-INVENTORY.md` for the full inventory.
|
|
5
5
|
|
|
6
6
|
| Command | Description |
|
|
7
7
|
| --- | --- |
|
|
@@ -144,6 +144,7 @@ Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for th
|
|
|
144
144
|
| `loki assets export` | Export shareable assets |
|
|
145
145
|
| `loki export` | Export session data |
|
|
146
146
|
| `loki notify` | Notifications |
|
|
147
|
+
| `loki notify desktop` | Send a desktop notification (macOS or Linux notify-send) |
|
|
147
148
|
| `loki tour` | See a sample result |
|
|
148
149
|
| `loki wow` | Tiny real task, or the offline sample |
|
|
149
150
|
| `loki factory` | loki start with the autonomy policy printed |
|
package/docs/COMPARISON.md
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
| Feature | **Loki Mode** | **Devin** | **Codex** | **Cursor** | **Kiro** | **Antigravity** | **Amazon Q** | **OpenCode** |
|
|
33
33
|
|---------|--------------|-----------|-----------|------------|----------|-----------------|--------------|--------------|
|
|
34
|
-
| **Multi-Agent** | 41 prompt-defined
|
|
34
|
+
| **Multi-Agent** | Role inventory: 41 prompt-defined roles in 8 domains (`agents/types.json`), adopted per phase; parallel review council + optional worktree streams (Claude), sequential elsewhere | Single | Single | Up to 8 parallel | Background | Manager Surface | Multiple types | 4 built-in |
|
|
35
35
|
| **Orchestration** | Full orchestrator | N/A | N/A | Git worktree | Hooks | Manager view | Workflow | Subagents |
|
|
36
36
|
| **Parallel Exec** | 10+ Haiku, 4 impl (worktree) | No | No | 8 max | Yes | Yes | Yes | Yes |
|
|
37
37
|
| **Agent Domains** | Eng, Ops, Business, Data, Product, Growth, Review | N/A | N/A | N/A | N/A | N/A | 3 types | N/A |
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
| **Multi-Agent Verification** | Model diversity (Claude vs OpenAI, 54% improvement) | 3 blind reviewers + devil's advocate | Different approach (N/A for Claude Code - only Claude models) |
|
|
146
146
|
| **Quality Gates** | Built-in verification loops | 8 explicit gates + anti-sycophancy | **Loki Mode** |
|
|
147
147
|
| **Memory System** | Not documented | 3-tier episodic/semantic/procedural | **Loki Mode** |
|
|
148
|
-
| **Agent Specialization** | Custom Zen Agents | 41
|
|
148
|
+
| **Agent Specialization** | Custom Zen Agents | Role inventory: 41 prompt-defined roles in `agents/types.json` (a count of definitions, not a measured advantage) | Different approach |
|
|
149
149
|
| **CI Failure Analysis** | Explicit pattern with auto-fix | DevOps agent only | **ADOPTED from Zencoder** |
|
|
150
150
|
| **Review Comment Resolution** | Auto-apply simple changes | Manual review | **ADOPTED from Zencoder** |
|
|
151
151
|
| **Dependency Management** | Scheduled PRs, one group at a time | Mentioned only | **ADOPTED from Zencoder** |
|
|
@@ -174,10 +174,9 @@
|
|
|
174
174
|
|
|
175
175
|
1. **Quality Control**: 8 explicit gates + blind review + devil's advocate vs built-in loops
|
|
176
176
|
2. **Memory System**: 3-tier (episodic/semantic/procedural) with cross-project learning
|
|
177
|
-
3. **
|
|
178
|
-
4. **
|
|
179
|
-
5. **
|
|
180
|
-
6. **Research Foundation**: 10+ academic papers integrated vs proprietary
|
|
177
|
+
3. **Anti-Sycophancy**: CONSENSAGENT patterns prevent reviewer groupthink
|
|
178
|
+
4. **Autonomy Design**: Minimal human intervention from PRD to production
|
|
179
|
+
5. **Research Foundation**: 10+ academic papers integrated vs proprietary
|
|
181
180
|
|
|
182
181
|
### Where Zencoder EXCEEDS Loki Mode
|
|
183
182
|
|
|
@@ -230,7 +229,6 @@ These are patterns from competing projects that are **practically and scientific
|
|
|
230
229
|
| **Constitutional AI Integration** | Principles-based self-critique from Anthropic research | None have this |
|
|
231
230
|
| **Anti-Sycophancy (CONSENSAGENT)** | Blind review + devil's advocate prevents groupthink | None have this |
|
|
232
231
|
| **Provider Abstraction Layer** | Clean degradation from full-featured to sequential-only | Claude-only projects can't degrade |
|
|
233
|
-
| **41 Specialized Agent Roles** | Purpose-built role definitions in 8 domains vs generic; Loki gates every role's output through blind review + council | agents (108) has more but less organized |
|
|
234
232
|
| **Research Foundation** | 10+ academic papers integrated with citations | Most have no research backing |
|
|
235
233
|
|
|
236
234
|
### Superpowers Deep-Dive (35K+ Stars)
|
|
@@ -352,7 +350,7 @@ Tiered agent architecture with explicit escalation:
|
|
|
352
350
|
| Dimension | Loki Mode Advantage |
|
|
353
351
|
|-----------|-------------------|
|
|
354
352
|
| **Autonomy** | Designed for high autonomy with minimal human intervention |
|
|
355
|
-
| **Multi-Agent** | 41 prompt-defined
|
|
353
|
+
| **Multi-Agent** | Role inventory: 41 prompt-defined roles in 8 domains (`agents/types.json`), adopted per phase; parallel review council + optional worktree streams on Claude, sequential elsewhere. Output is gated by blind review + council |
|
|
356
354
|
| **Quality** | 8 gates + blind review + devil's advocate + property-based testing |
|
|
357
355
|
| **Research** | 10+ academic papers integrated vs proprietary/undisclosed |
|
|
358
356
|
| **Anti-Sycophancy** | Only agent with CONSENSAGENT-based blind review |
|
package/docs/COMPONENTS.md
CHANGED
|
@@ -324,7 +324,7 @@ The agent type registry and installation tooling.
|
|
|
324
324
|
phase.
|
|
325
325
|
|
|
326
326
|
**Key files:**
|
|
327
|
-
- `types.json` - Agent type definitions (41 agent types across 8 domains).
|
|
327
|
+
- `agents/types.json` - Agent type definitions (41 agent types across 8 domains).
|
|
328
328
|
- `hub_install.py` - Agent hub install.
|
|
329
329
|
- `managed_registry.py` - Managed agent registry.
|
|
330
330
|
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
|
|
4
4
|
|
|
5
|
-
**Version:** v11.
|
|
5
|
+
**Version:** v11.78.0
|
|
6
6
|
|
|
7
7
|
**Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
|
|
8
8
|
|