loki-mode 7.129.4 → 8.0.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 +81 -5
- package/SKILL.md +66 -5
- package/VERSION +1 -1
- package/autonomy/app-runner.sh +37 -0
- package/autonomy/completion-council.sh +862 -28
- package/autonomy/council-v2.sh +39 -0
- package/autonomy/crash.sh +3 -2
- package/autonomy/grill.sh +42 -0
- package/autonomy/hooks/validate-bash.sh +301 -4
- package/autonomy/lib/cockpit-render.sh +8 -2
- package/autonomy/lib/cr-rematerialize.py +101 -4
- package/autonomy/lib/deadline.py +957 -0
- package/autonomy/lib/dependency-setup.sh +205 -0
- package/autonomy/lib/done-recognition.sh +45 -0
- package/autonomy/lib/efficiency_cost.py +13 -6
- package/autonomy/lib/no_mock_scan.py +793 -0
- package/autonomy/lib/prd-enrich.sh +42 -0
- package/autonomy/lib/proof-analytics-props.py +69 -0
- package/autonomy/lib/proof-generator.py +282 -95
- package/autonomy/lib/proof-template.html +15 -12
- package/autonomy/lib/proof-verify.py +151 -102
- package/autonomy/lib/requirements_contract.py +848 -0
- package/autonomy/lib/sdk-mode.sh +103 -0
- package/autonomy/lib/secret-scan.sh +139 -0
- package/autonomy/lib/spec-expand.sh +208 -0
- package/autonomy/lib/tree_digest.py +266 -0
- package/autonomy/lib/voter-agents.sh +58 -8
- package/autonomy/lib/workspace_diff.py +124 -0
- package/autonomy/loki +189 -17
- package/autonomy/playwright-verify.sh +501 -0
- package/autonomy/prd-checklist.sh +17 -8
- package/autonomy/provider-offer.sh +111 -0
- package/autonomy/run.sh +4424 -678
- package/autonomy/sandbox.sh +42 -0
- package/autonomy/spec-interrogation.sh +148 -5
- package/autonomy/spec.sh +118 -1
- package/autonomy/telemetry.sh +133 -7
- package/autonomy/verify.sh +107 -0
- package/bin/loki +110 -3
- package/completions/_loki +10 -0
- package/completions/loki.bash +1 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/audit.py +96 -7
- package/dashboard/build_supervisor.py +1619 -0
- package/dashboard/control.py +8 -0
- package/dashboard/prompt_optimizer.py +7 -0
- package/dashboard/server.py +577 -22
- package/dashboard/static/trust.html +1 -1
- package/docs/ARCHITECTURE-OVERVIEW.md +207 -0
- package/docs/AUTONOMI-ECOSYSTEM.md +107 -0
- package/docs/INSTALLATION.md +19 -2
- package/docs/MODEL-EQUIVALENCE-HARNESS-PLAN.md +70 -0
- package/docs/PLANS-INDEX.md +33 -0
- package/docs/PRIVACY.md +29 -1
- package/docs/SIGNED-RECEIPTS.md +102 -0
- package/docs/SONNET5-DEFAULT-PLAN.md +1 -1
- package/docs/V8-ACCEPTANCE-TRIAGE-2026-07-24.md +114 -0
- package/docs/V8-AGENT-SDK-PLAN.md +692 -0
- package/docs/V8-COMPLEXITY-AUDIT-2026-07-24.md +45 -0
- package/docs/V8-MAJOR-RELEASE-PLAN.md +137 -0
- package/docs/V8-OVERNIGHT-PLAN-2026-07-25.md +82 -0
- package/docs/V8-RUNTIME-TRUTH-2026-07-25.md +232 -0
- package/docs/V8-SDK-RESEARCH-RAW.md +129 -0
- package/docs/alternative-installations.md +4 -4
- package/loki-ts/dist/loki.js +674 -285
- package/loki-ts/package.json +2 -0
- package/mcp/__init__.py +1 -1
- package/package.json +7 -6
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/providers/claude.sh +75 -0
- package/providers/codex.sh +85 -13
- package/references/sdk-mode.md +106 -0
- package/skills/model-selection.md +1 -1
- package/skills/quality-gates.md +22 -0
package/autonomy/loki
CHANGED
|
@@ -143,6 +143,15 @@ if [ -f "$_LOKI_SCRIPT_DIR/lib/config-map.sh" ]; then
|
|
|
143
143
|
source "$_LOKI_SCRIPT_DIR/lib/config-map.sh"
|
|
144
144
|
fi
|
|
145
145
|
|
|
146
|
+
# v8.1: one-switch SDK activation resolver (LOKI_SDK_MODE=off|judges|full,
|
|
147
|
+
# default off). Idempotent; run.sh sources+resolves it too. Sourced here so any
|
|
148
|
+
# in-CLI SDK judge site sees the resolved per-site flags. No-op when mode unset.
|
|
149
|
+
if [ -f "$_LOKI_SCRIPT_DIR/lib/sdk-mode.sh" ]; then
|
|
150
|
+
# shellcheck source=autonomy/lib/sdk-mode.sh
|
|
151
|
+
source "$_LOKI_SCRIPT_DIR/lib/sdk-mode.sh"
|
|
152
|
+
loki_sdk_resolve_mode
|
|
153
|
+
fi
|
|
154
|
+
|
|
146
155
|
# Resolve the script's real path (handles symlinks)
|
|
147
156
|
resolve_script_path() {
|
|
148
157
|
local script="$1"
|
|
@@ -824,6 +833,7 @@ show_help() {
|
|
|
824
833
|
echo "Usage: loki <command> [options]"
|
|
825
834
|
echo ""
|
|
826
835
|
echo "New here? Try these in order:"
|
|
836
|
+
echo " loki welcome Friendly first-run opener (what Loki is, next steps)"
|
|
827
837
|
echo " loki doctor Check your setup is ready (a few seconds)"
|
|
828
838
|
echo " loki quickstart Guided first build from your idea (no PRD needed)"
|
|
829
839
|
echo " loki start ./prd.md Build from a spec (PRD file, GitHub/GitLab/Jira/Azure issue, or no arg)"
|
|
@@ -856,6 +866,7 @@ show_help() {
|
|
|
856
866
|
echo " stop Stop execution immediately"
|
|
857
867
|
echo " pause Pause after current session"
|
|
858
868
|
echo " resume Resume paused execution"
|
|
869
|
+
echo " steer \"<note>\" Nudge a running build (needs LOKI_PROMPT_INJECTION=1)"
|
|
859
870
|
echo ""
|
|
860
871
|
echo "Verify / trust:"
|
|
861
872
|
echo " verify [base] Deterministic PR verification (CI-gate exit codes)"
|
|
@@ -910,6 +921,8 @@ show_help() {
|
|
|
910
921
|
echo " --budget USD Set cost budget limit (display in dashboard/status)"
|
|
911
922
|
echo " --bmad-project PATH Use BMAD Method project artifacts as input"
|
|
912
923
|
echo " --openspec PATH Use OpenSpec change directory as input"
|
|
924
|
+
echo " --config PATH Load settings from a config file (.yaml/.json/.env). Alias: --env-file, --vars"
|
|
925
|
+
echo " Precedence: CLI flag > env var > --config file > defaults. Secrets via \${VAR}."
|
|
913
926
|
echo ""
|
|
914
927
|
echo "Deprecated alias 'run': 'loki run <issue-ref>' still works as an alias"
|
|
915
928
|
echo "for 'loki start'. See its options with 'loki run --help'."
|
|
@@ -3328,6 +3341,52 @@ cmd_pause() {
|
|
|
3328
3341
|
fi
|
|
3329
3342
|
}
|
|
3330
3343
|
|
|
3344
|
+
# RUN-25 iter 22 (Wave D #3): `loki steer "<note>"` writes an operator directive
|
|
3345
|
+
# the running loop actually reads (.loki/HUMAN_INPUT.md), replacing the dead
|
|
3346
|
+
# steering.md hint the cockpit used to advertise (nothing read it). The loop only
|
|
3347
|
+
# INJECTS the note when LOKI_PROMPT_INJECTION=1 (default off, for safety), so we
|
|
3348
|
+
# write the note and tell the operator the exact flag -- honest: the control now
|
|
3349
|
+
# genuinely persists the directive instead of silently no-op'ing.
|
|
3350
|
+
cmd_steer() {
|
|
3351
|
+
case "${1:-}" in
|
|
3352
|
+
--help|-h|help|"")
|
|
3353
|
+
echo -e "${BOLD}Loki Mode -- steer the running build${NC}"
|
|
3354
|
+
echo ""
|
|
3355
|
+
echo "Usage: loki steer \"<guidance>\""
|
|
3356
|
+
echo ""
|
|
3357
|
+
echo "Writes your guidance to .loki/HUMAN_INPUT.md, which the running"
|
|
3358
|
+
echo "loop reads at the next iteration boundary (when LOKI_PROMPT_INJECTION=1)."
|
|
3359
|
+
echo ""
|
|
3360
|
+
echo " loki steer \"focus on the auth flow, skip the docs for now\""
|
|
3361
|
+
echo ""
|
|
3362
|
+
echo "The loop injects the note only when prompt injection is enabled:"
|
|
3363
|
+
echo " LOKI_PROMPT_INJECTION=1 loki start ... (or export it before starting)"
|
|
3364
|
+
[ -z "${1:-}" ] && return 2
|
|
3365
|
+
return 0
|
|
3366
|
+
;;
|
|
3367
|
+
esac
|
|
3368
|
+
if [ ! -d "$LOKI_DIR" ]; then
|
|
3369
|
+
echo -e "${YELLOW}No .loki directory found.${NC} Start a session first: loki start"
|
|
3370
|
+
return 1
|
|
3371
|
+
fi
|
|
3372
|
+
local note="$*"
|
|
3373
|
+
# Append with a timestamp so multiple steers accumulate; the reader consumes
|
|
3374
|
+
# and clears the file each iteration, so this is the pending-directive buffer.
|
|
3375
|
+
{
|
|
3376
|
+
printf '## Operator directive (%s)\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
3377
|
+
printf '%s\n\n' "$note"
|
|
3378
|
+
} >> "$LOKI_DIR/HUMAN_INPUT.md"
|
|
3379
|
+
emit_event session cli steer "chars=${#note}" 2>/dev/null || true
|
|
3380
|
+
echo -e "${GREEN}Steering note written to .loki/HUMAN_INPUT.md${NC}"
|
|
3381
|
+
if [ "${LOKI_PROMPT_INJECTION:-false}" != "1" ] && [ "${LOKI_PROMPT_INJECTION:-false}" != "true" ]; then
|
|
3382
|
+
echo -e "${YELLOW}Note: prompt injection is OFF, so the loop will NOT read this yet.${NC}"
|
|
3383
|
+
echo -e "${DIM}Enable it: export LOKI_PROMPT_INJECTION=1 (then the next iteration ingests the note).${NC}"
|
|
3384
|
+
else
|
|
3385
|
+
echo -e "${DIM}The next iteration will read and apply it.${NC}"
|
|
3386
|
+
fi
|
|
3387
|
+
return 0
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3331
3390
|
# Resume paused execution
|
|
3332
3391
|
cmd_resume() {
|
|
3333
3392
|
# v7.6.2 B-13 fix: --help must print help, not act on session state.
|
|
@@ -3835,6 +3894,8 @@ WHYJSON
|
|
|
3835
3894
|
_LOKI_WHY_STATE="$state_file" _LOKI_WHY_COMPLETION="$completion_file" \
|
|
3836
3895
|
_LOKI_WHY_HEAD_SHA="$_why_head_sha" \
|
|
3837
3896
|
_LOKI_WHY_LAST_ERROR="$loki_dir/state/LAST_ERROR.json" \
|
|
3897
|
+
_LOKI_WHY_UNCERTAINTY="$loki_dir/signals/UNCERTAINTY_ESCALATION" \
|
|
3898
|
+
_LOKI_WHY_CONVERGENCE="$loki_dir/council/convergence.log" \
|
|
3838
3899
|
_LOKI_WHY_HANDOFFS="$loki_dir/memory/handoffs" python3 - <<'WHYTXT'
|
|
3839
3900
|
import json, os, glob
|
|
3840
3901
|
def load(p):
|
|
@@ -3926,6 +3987,32 @@ print()
|
|
|
3926
3987
|
print(f" What happened: {meaning}")
|
|
3927
3988
|
print(f" What to do : {action}")
|
|
3928
3989
|
|
|
3990
|
+
# RUN-25 iter 22b/iter 23 (Wave D #6): when a run is PAUSED / live and actually
|
|
3991
|
+
# STUCK, name the REAL stall reason instead of the generic "resume". The runner
|
|
3992
|
+
# drops .loki/signals/UNCERTAINTY_ESCALATION when its proactive stuck-detector
|
|
3993
|
+
# fires (repeated no-progress / uncertainty), and council/convergence.log records
|
|
3994
|
+
# consecutive-no-change. Surface those so "why is it stuck" has a real answer, not
|
|
3995
|
+
# a generic resume hint. Reads existing files only (no new state, no spend).
|
|
3996
|
+
_unc = os.environ.get("_LOKI_WHY_UNCERTAINTY", "")
|
|
3997
|
+
if _unc and os.path.exists(_unc):
|
|
3998
|
+
print()
|
|
3999
|
+
print(" Stall reason : the runner's proactive stuck-detector escalated")
|
|
4000
|
+
print(" (UNCERTAINTY_ESCALATION): repeated no-progress or")
|
|
4001
|
+
print(" low-confidence iterations. It is not making headway on its own.")
|
|
4002
|
+
# Add the consecutive-no-change count from the convergence log tail if present.
|
|
4003
|
+
_conv = os.environ.get("_LOKI_WHY_CONVERGENCE", "")
|
|
4004
|
+
if _conv and os.path.exists(_conv):
|
|
4005
|
+
try:
|
|
4006
|
+
with open(_conv) as _cf:
|
|
4007
|
+
_tail = _cf.read().splitlines()[-1:]
|
|
4008
|
+
if _tail:
|
|
4009
|
+
print(f" Latest convergence signal: {_tail[0][:120]}")
|
|
4010
|
+
except Exception:
|
|
4011
|
+
pass
|
|
4012
|
+
print(" Unstick it : give the loop a concrete next step:")
|
|
4013
|
+
print(" loki steer \"<what to try next>\" (needs LOKI_PROMPT_INJECTION=1)")
|
|
4014
|
+
print(" or narrow the spec / fix the blocker, then loki resume.")
|
|
4015
|
+
|
|
3929
4016
|
# Surface the structured per-iteration error record, if one exists at
|
|
3930
4017
|
# .loki/state/LAST_ERROR.json (the run.sh writer that produces it ships in the
|
|
3931
4018
|
# SAME v7.92.0 release; on an older engine the file is simply absent and this
|
|
@@ -9372,14 +9459,40 @@ cmd_assets() {
|
|
|
9372
9459
|
esac
|
|
9373
9460
|
}
|
|
9374
9461
|
|
|
9375
|
-
# Guard: check if output file exists and warn before overwriting
|
|
9462
|
+
# Guard: check if output file exists and warn before overwriting.
|
|
9463
|
+
#
|
|
9464
|
+
# NON-INTERACTIVE SAFETY: this is the single chokepoint every export format
|
|
9465
|
+
# (_export_json / _export_markdown / _export_csv / _export_timeline) routes
|
|
9466
|
+
# through, so the guard lives here once rather than in four callers. Without it
|
|
9467
|
+
# the prompt BLOCKS FOREVER whenever stdin is not closed and not answerable -- a
|
|
9468
|
+
# CI lane, a pipeline, or a test harness that leaves stdin attached. That wedged
|
|
9469
|
+
# local-ci for 40+ minutes at tests/cli/test-alias-forwarding.sh (the runner sat
|
|
9470
|
+
# at 0.0% CPU in state S waiting on a read that could never be satisfied).
|
|
9471
|
+
#
|
|
9472
|
+
# Semantics mirror the established precedent at the BMAD prompt (search
|
|
9473
|
+
# LOKI_AUTO_CONFIRM): LOKI_AUTO_CONFIRM=true (falling back to CI) means ANSWER
|
|
9474
|
+
# YES, and echoes an auditable "y (auto-confirmed)" line. A non-interactive
|
|
9475
|
+
# shell WITHOUT that opt-in FAILS CLOSED: it refuses the overwrite and names the
|
|
9476
|
+
# env var, because silently clobbering a user's file in CI is the worse failure.
|
|
9477
|
+
# `read` is `|| reply=""` guarded so an EOF cannot abort a `set -e` caller.
|
|
9376
9478
|
_export_check_overwrite() {
|
|
9377
9479
|
local output="$1"
|
|
9378
9480
|
if [ -n "$output" ] && [ -f "$output" ]; then
|
|
9379
9481
|
echo -e "${YELLOW}Warning: File already exists: $output${NC}"
|
|
9482
|
+
local _auto="${LOKI_AUTO_CONFIRM:-${CI:-false}}"
|
|
9483
|
+
if [ "$_auto" = "true" ]; then
|
|
9484
|
+
echo -n "Overwrite? [y/N] "
|
|
9485
|
+
echo "y (auto-confirmed)"
|
|
9486
|
+
return 0
|
|
9487
|
+
fi
|
|
9488
|
+
if [ ! -t 0 ]; then
|
|
9489
|
+
echo "Export cancelled (non-interactive: refusing to overwrite '$output'." >&2
|
|
9490
|
+
echo "Set LOKI_AUTO_CONFIRM=true to overwrite, or choose a new output path.)" >&2
|
|
9491
|
+
return 1
|
|
9492
|
+
fi
|
|
9380
9493
|
echo -n "Overwrite? [y/N] "
|
|
9381
9494
|
local reply
|
|
9382
|
-
read -r reply
|
|
9495
|
+
read -r reply || reply=""
|
|
9383
9496
|
case "$reply" in
|
|
9384
9497
|
[yY]|[yY][eE][sS]) return 0 ;;
|
|
9385
9498
|
*) echo "Export cancelled."; return 1 ;;
|
|
@@ -10044,7 +10157,18 @@ cmd_config_edit() {
|
|
|
10044
10157
|
|
|
10045
10158
|
if [ -z "$config_file" ]; then
|
|
10046
10159
|
echo -e "${YELLOW}No config file found.${NC}"
|
|
10047
|
-
|
|
10160
|
+
# Non-interactive safety (same class as _export_check_overwrite): a bare
|
|
10161
|
+
# `read` here blocks forever when stdin is attached but unanswerable.
|
|
10162
|
+
# This prompt DEFAULTS TO YES, so auto-confirm and non-interactive both
|
|
10163
|
+
# take the create path; only the explicit interactive "n" declines.
|
|
10164
|
+
local create=""
|
|
10165
|
+
local _auto="${LOKI_AUTO_CONFIRM:-${CI:-false}}"
|
|
10166
|
+
if [ "$_auto" = "true" ] || [ ! -t 0 ]; then
|
|
10167
|
+
echo "Create one? [Y/n]: Y (non-interactive default; run 'loki config init' to control this)"
|
|
10168
|
+
create="Y"
|
|
10169
|
+
else
|
|
10170
|
+
read -r -p "Create one? [Y/n]: " create || create=""
|
|
10171
|
+
fi
|
|
10048
10172
|
create="${create:-Y}"
|
|
10049
10173
|
if [[ "$create" =~ ^[Yy] ]]; then
|
|
10050
10174
|
cmd_config_init
|
|
@@ -10555,15 +10679,25 @@ cmd_doctor() {
|
|
|
10555
10679
|
# Check if at least one provider is installed (detect_any_provider is the
|
|
10556
10680
|
# shared helper from provider-offer.sh, extracted from this exact loop).
|
|
10557
10681
|
if ! detect_any_provider; then
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
#
|
|
10562
|
-
#
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10682
|
+
# The bundled Claude Agent SDK runs the main loop with no separate CLI,
|
|
10683
|
+
# but ONLY when it is genuinely usable: binary extracted, credentials
|
|
10684
|
+
# present, and the SDK loop actually active. detect_bundled_sdk_provider
|
|
10685
|
+
# fails closed on any doubt, so when it returns false we emit today's
|
|
10686
|
+
# blocker verbatim. Mirrored in loki-ts/src/commands/doctor.ts.
|
|
10687
|
+
if declare -f detect_bundled_sdk_provider >/dev/null 2>&1 && detect_bundled_sdk_provider; then
|
|
10688
|
+
echo -e " ${GREEN}PASS${NC} Bundled Claude Agent SDK is usable -- no separate CLI needed"
|
|
10689
|
+
pass_count=$((pass_count + 1))
|
|
10690
|
+
else
|
|
10691
|
+
echo -e " ${RED}FAIL${NC} No AI provider CLI installed -- at least one is required"
|
|
10692
|
+
echo -e " ${YELLOW}Install: npm install -g @anthropic-ai/claude-code${NC}"
|
|
10693
|
+
fail_count=$((fail_count + 1))
|
|
10694
|
+
# v7.29.0: on a TTY (non-json), append the consent-gated install offer.
|
|
10695
|
+
# In report mode the helper is a no-op on non-TTY/CI, so the doctor
|
|
10696
|
+
# output stays byte-identical for the bun-parity matrix. --json never
|
|
10697
|
+
# reaches here (cmd_doctor dispatches to cmd_doctor_json earlier).
|
|
10698
|
+
if declare -f offer_provider_install >/dev/null 2>&1; then
|
|
10699
|
+
offer_provider_install report || true
|
|
10700
|
+
fi
|
|
10567
10701
|
fi
|
|
10568
10702
|
fi
|
|
10569
10703
|
echo ""
|
|
@@ -12977,7 +13111,12 @@ cmd_init() {
|
|
|
12977
13111
|
return 1
|
|
12978
13112
|
fi
|
|
12979
13113
|
fi
|
|
12980
|
-
|
|
13114
|
+
# stderr, not stdout: `loki init --json` writes a machine-readable
|
|
13115
|
+
# template list to stdout, and this human-facing banner was landing in
|
|
13116
|
+
# the middle of it -- the second `init` in a directory emitted invalid
|
|
13117
|
+
# JSON, so any tool parsing it broke on reinit but worked on first run.
|
|
13118
|
+
# Diagnostics belong on stderr; stdout is the data channel.
|
|
13119
|
+
echo -e "${YELLOW}Reinitializing existing .loki/ directory${NC}" >&2
|
|
12981
13120
|
fi
|
|
12982
13121
|
|
|
12983
13122
|
local version=$(get_version)
|
|
@@ -13286,7 +13425,10 @@ print(p or '')
|
|
|
13286
13425
|
return 1
|
|
13287
13426
|
fi
|
|
13288
13427
|
fi
|
|
13289
|
-
|
|
13428
|
+
# stderr for the same reason as the sibling banner above: stdout is the
|
|
13429
|
+
# machine-readable channel. Fixing only the path the failing test
|
|
13430
|
+
# exercised would leave this one to resurface the same bug.
|
|
13431
|
+
echo -e "${YELLOW}Reinitializing existing .loki/ directory in $target_dir${NC}" >&2
|
|
13290
13432
|
fi
|
|
13291
13433
|
|
|
13292
13434
|
# Build config JSON
|
|
@@ -16692,7 +16834,7 @@ def _resolve_session_pin(alias):
|
|
|
16692
16834
|
# override-path clamp). A 'sonnet' override dispatches sonnet.
|
|
16693
16835
|
# - No override (session pin): the runner maps alias -> tier ->
|
|
16694
16836
|
# resolve_model_for_tier -> clamp(model, real_tier). Use _resolve_session_pin.
|
|
16695
|
-
# A 'sonnet' session pin dispatches
|
|
16837
|
+
# A 'sonnet' session pin dispatches SONNET (development tier; CLAUDE_DEFAULT_DEVELOPMENT=sonnet since v7.104.0).
|
|
16696
16838
|
# This is the task-568 fix: price the actually-dispatched model on BOTH routes.
|
|
16697
16839
|
_is_override = (_session_model_source == '.loki/state/model-override')
|
|
16698
16840
|
if _is_override:
|
|
@@ -17508,7 +17650,17 @@ main() {
|
|
|
17508
17650
|
&& _loki_telemetry_enabled; then
|
|
17509
17651
|
_loki_disclose_telemetry_once
|
|
17510
17652
|
fi
|
|
17511
|
-
|
|
17653
|
+
# The command value is passed through the FIXED ALLOWLIST at the boundary
|
|
17654
|
+
# (_loki_known_command, autonomy/telemetry.sh): a real subcommand goes out
|
|
17655
|
+
# verbatim, anything else becomes the literal "other". Without this, the
|
|
17656
|
+
# honest typo `loki ./client-acme-merger-prd.md` put the raw file name on the
|
|
17657
|
+
# wire, because the payload builder is a pass-through dict, not an allowlist.
|
|
17658
|
+
# Fail closed: if the sanitizer is somehow undefined, send "other".
|
|
17659
|
+
if declare -f _loki_known_command >/dev/null 2>&1; then
|
|
17660
|
+
loki_telemetry "cli_command" "command=$(_loki_known_command "$command")" 2>/dev/null || true
|
|
17661
|
+
else
|
|
17662
|
+
loki_telemetry "cli_command" "command=other" 2>/dev/null || true
|
|
17663
|
+
fi
|
|
17512
17664
|
|
|
17513
17665
|
# Unified config-file pre-pass (#691). For session commands ONLY, honor
|
|
17514
17666
|
# LOKI_CONFIG_FILE / --config|--vars|--env-file and load the file BEFORE the
|
|
@@ -17581,6 +17733,9 @@ main() {
|
|
|
17581
17733
|
pause)
|
|
17582
17734
|
cmd_pause "$@"
|
|
17583
17735
|
;;
|
|
17736
|
+
steer)
|
|
17737
|
+
cmd_steer "$@"
|
|
17738
|
+
;;
|
|
17584
17739
|
resume)
|
|
17585
17740
|
cmd_resume "$@"
|
|
17586
17741
|
;;
|
|
@@ -29473,6 +29628,20 @@ except Exception:
|
|
|
29473
29628
|
fi
|
|
29474
29629
|
|
|
29475
29630
|
# Gate 2: Security Scan
|
|
29631
|
+
# Deny filter for the secret patterns below. This scanner sees RAW DIFF
|
|
29632
|
+
# LINES with no file context, so unlike the other two copies of this matcher
|
|
29633
|
+
# (autonomy/lib/secret-scan.sh, verify.sh) it cannot skip test paths -- it
|
|
29634
|
+
# has to judge the VALUE. A credential whose value literally says "test",
|
|
29635
|
+
# "example", "xxx", or "not-called" is a fixture; real leaked keys are not
|
|
29636
|
+
# named after their own fakeness.
|
|
29637
|
+
#
|
|
29638
|
+
# Concretely, this is what reported 2 CRITICAL findings on the v8 PR:
|
|
29639
|
+
# sk-ant-xxx (loki-ts/test/runner/oauth_dev.test.ts)
|
|
29640
|
+
# sk-ant-test-not-called (loki-ts/tests/runner/quality_gates.test.ts)
|
|
29641
|
+
# Both exist to prove the SDK path fails closed without a real key. Flagging
|
|
29642
|
+
# them trains people to ignore CRITICAL, which is how a real one gets missed.
|
|
29643
|
+
local _ci_secret_deny='(test|example|sample|fixture|dummy|fake|placeholder|redacted|changeme|your[-_]|xxx+|not[-_]called|\$\{|\$[A-Za-z_]|process\.env|os\.(environ|getenv)|<[^>]*>)'
|
|
29644
|
+
|
|
29476
29645
|
_ci_scan() {
|
|
29477
29646
|
local pattern="$1" sev="$2" cat="$3" finding="$4" suggestion="$5"
|
|
29478
29647
|
# Only scan added lines (starting with "+") to avoid flagging deleted code
|
|
@@ -29481,7 +29650,10 @@ except Exception:
|
|
|
29481
29650
|
local ml
|
|
29482
29651
|
ml=$(echo "$match" | cut -d: -f1)
|
|
29483
29652
|
_ci_add_finding "diff" "$ml" "$sev" "$cat" "$finding" "$suggestion"
|
|
29484
|
-
done < <(
|
|
29653
|
+
done < <(printf '%s\n' "$diff_content" \
|
|
29654
|
+
| grep -n '^+' \
|
|
29655
|
+
| grep -E "$pattern" 2>/dev/null \
|
|
29656
|
+
| grep -vEi "$_ci_secret_deny" 2>/dev/null || true)
|
|
29485
29657
|
}
|
|
29486
29658
|
|
|
29487
29659
|
# Hardcoded secrets
|