loki-mode 8.6.0 → 8.8.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/deadline.py +38 -4
- package/autonomy/loki +195 -3
- package/autonomy/run.sh +181 -29
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +207 -19
- package/dashboard/static/index.html +10 -9
- package/docs/STRATEGY-2026-2028.md +124 -0
- package/docs/adoption-baseline-2026-07-31.md +84 -0
- package/docs/environment-variables.md +36 -4
- package/loki-ts/dist/loki.js +424 -414
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/providers/aider.sh +35 -3
- package/providers/claude.sh +12 -1
- package/providers/cline.sh +26 -3
- package/providers/model_catalog.json +17 -6
- package/providers/models.sh +58 -3
- package/providers/opencode.sh +8 -2
- package/references/multi-provider.md +9 -0
- package/skills/providers.md +35 -1
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": "8.
|
|
4
|
+
"version": "8.8.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 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "loki-mode",
|
|
4
4
|
"displayName": "Loki Mode",
|
|
5
|
-
"version": "8.
|
|
5
|
+
"version": "8.8.0",
|
|
6
6
|
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Autonomi",
|
package/providers/aider.sh
CHANGED
|
@@ -58,12 +58,18 @@ _aider_default_from_catalog() {
|
|
|
58
58
|
if [ -f "${script_dir}/models.sh" ]; then
|
|
59
59
|
# shellcheck source=./models.sh
|
|
60
60
|
source "${script_dir}/models.sh"
|
|
61
|
-
loki_latest_model aider development 2>/dev/null || echo "
|
|
61
|
+
loki_latest_model aider development 2>/dev/null || echo "openrouter/deepseek/deepseek-v3.2"
|
|
62
62
|
else
|
|
63
|
-
echo "
|
|
63
|
+
echo "openrouter/deepseek/deepseek-v3.2"
|
|
64
64
|
fi
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
# Resolution: provider-scoped env > catalog. The GLOBAL tier var
|
|
67
|
+
# (LOKI_MODEL_DEVELOPMENT) is deliberately NOT in this chain: aider takes
|
|
68
|
+
# litellm full model strings ("openrouter/deepseek/deepseek-v3.2"), while the
|
|
69
|
+
# global tier var carries Claude CLI aliases ("sonnet"), so a global set for
|
|
70
|
+
# one provider silently produced `aider --model sonnet`. Namespace-incompatible,
|
|
71
|
+
# not merely mis-ordered. Do not re-add it.
|
|
72
|
+
AIDER_DEFAULT_MODEL="${LOKI_AIDER_MODEL:-$(_aider_default_from_catalog)}"
|
|
67
73
|
PROVIDER_MODEL_PLANNING="$AIDER_DEFAULT_MODEL"
|
|
68
74
|
PROVIDER_MODEL_DEVELOPMENT="$AIDER_DEFAULT_MODEL"
|
|
69
75
|
PROVIDER_MODEL_FAST="$AIDER_DEFAULT_MODEL"
|
|
@@ -134,6 +140,32 @@ resolve_model_for_tier() {
|
|
|
134
140
|
echo "$AIDER_DEFAULT_MODEL"
|
|
135
141
|
}
|
|
136
142
|
|
|
143
|
+
# provider_invoke_argv <tier> <prompt> -- see providers/claude.sh for the full
|
|
144
|
+
# rationale. Populates _LOKI_INVOKE_ARGV so a caller can wrap it in `timeout`,
|
|
145
|
+
# which cannot exec a shell function.
|
|
146
|
+
#
|
|
147
|
+
# Unlike Codex, provider_get_tier_param here returns a real MODEL NAME, so it is
|
|
148
|
+
# safe to pass to --model. An empty value omits the flag rather than sending
|
|
149
|
+
# `--model ""`, which aider rejects.
|
|
150
|
+
provider_invoke_argv() {
|
|
151
|
+
local tier="${1:-development}"
|
|
152
|
+
local prompt="${2:-}"
|
|
153
|
+
local model
|
|
154
|
+
model="$(provider_get_tier_param "$tier" 2>/dev/null || printf '%s' "")"
|
|
155
|
+
_LOKI_INVOKE_ARGV=(aider --yes-always --no-auto-commits)
|
|
156
|
+
[ -n "$model" ] && _LOKI_INVOKE_ARGV+=(--model "$model")
|
|
157
|
+
# LOKI_AIDER_FLAGS is a documented operator knob (CLI --aider-flags) that
|
|
158
|
+
# provider_invoke honours. Dropping it here would make the timeout-safe path
|
|
159
|
+
# silently ignore operator config that the normal path applies. Deliberately
|
|
160
|
+
# word-split, matching provider_invoke's unquoted $extra_flags expansion.
|
|
161
|
+
if [ -n "${LOKI_AIDER_FLAGS:-}" ]; then
|
|
162
|
+
# shellcheck disable=SC2206
|
|
163
|
+
_LOKI_INVOKE_ARGV+=(${LOKI_AIDER_FLAGS})
|
|
164
|
+
fi
|
|
165
|
+
# Aider takes the prompt via --message, not positionally.
|
|
166
|
+
_LOKI_INVOKE_ARGV+=(--message "$prompt")
|
|
167
|
+
}
|
|
168
|
+
|
|
137
169
|
# Tier-aware invocation
|
|
138
170
|
# Aider uses a single model configured externally, tier has no effect
|
|
139
171
|
provider_invoke_with_tier() {
|
package/providers/claude.sh
CHANGED
|
@@ -381,7 +381,18 @@ provider_invoke_argv() {
|
|
|
381
381
|
local prompt="${2:-}"
|
|
382
382
|
_loki_build_claude_auto_flags "$tier" "${LOKI_COMPLEXITY:-standard}" ""
|
|
383
383
|
local model
|
|
384
|
-
|
|
384
|
+
# Use the SAME resolver as the non-argv path. This previously called
|
|
385
|
+
# loki_tier_route_model with one argument, but that function takes TWO
|
|
386
|
+
# (tier, model) and echoes its second arg when routing is off -- so it
|
|
387
|
+
# returned an EMPTY string with rc=0, the `||` fallback never fired, and
|
|
388
|
+
# every argv-based claude invocation shipped with no --model flag at all.
|
|
389
|
+
#
|
|
390
|
+
# Do NOT "fix" this by chaining provider_get_tier_param + loki_tier_route_model
|
|
391
|
+
# here: that reproduces base+route but SKIPS loki_apply_max_tier_clamp, so
|
|
392
|
+
# LOKI_MAX_TIER=sonnet still emitted `--model opus` and the cost ceiling was
|
|
393
|
+
# silently unenforced on exactly the timeout-safe seam. resolve_model_for_tier
|
|
394
|
+
# is base -> route -> clamp -> alt-provider in one call; keep both paths on it.
|
|
395
|
+
model="$(resolve_model_for_tier "$tier" 2>/dev/null || provider_get_tier_param "$tier")"
|
|
385
396
|
_LOKI_INVOKE_ARGV=(
|
|
386
397
|
claude --dangerously-skip-permissions
|
|
387
398
|
"${_LOKI_CLAUDE_AUTO_FLAGS[@]+"${_LOKI_CLAUDE_AUTO_FLAGS[@]}"}"
|
package/providers/cline.sh
CHANGED
|
@@ -59,12 +59,18 @@ _cline_default_from_catalog() {
|
|
|
59
59
|
if [ -f "${script_dir}/models.sh" ]; then
|
|
60
60
|
# shellcheck source=./models.sh
|
|
61
61
|
source "${script_dir}/models.sh"
|
|
62
|
-
loki_latest_model cline development 2>/dev/null || echo "
|
|
62
|
+
loki_latest_model cline development 2>/dev/null || echo "openrouter/deepseek/deepseek-v3.2"
|
|
63
63
|
else
|
|
64
|
-
echo "
|
|
64
|
+
echo "openrouter/deepseek/deepseek-v3.2"
|
|
65
65
|
fi
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
# Resolution: provider-scoped env > catalog. The GLOBAL tier var
|
|
68
|
+
# (LOKI_MODEL_DEVELOPMENT) is deliberately NOT in this chain: cline takes
|
|
69
|
+
# full routed model strings ("openrouter/deepseek/deepseek-v3.2"), while the
|
|
70
|
+
# global tier var carries Claude CLI aliases ("sonnet"), so a global set for
|
|
71
|
+
# one provider silently changed cline's model too. Namespace-incompatible,
|
|
72
|
+
# not merely mis-ordered. Do not re-add it.
|
|
73
|
+
CLINE_DEFAULT_MODEL="${LOKI_CLINE_MODEL:-$(_cline_default_from_catalog)}"
|
|
68
74
|
PROVIDER_MODEL_PLANNING="$CLINE_DEFAULT_MODEL"
|
|
69
75
|
PROVIDER_MODEL_DEVELOPMENT="$CLINE_DEFAULT_MODEL"
|
|
70
76
|
PROVIDER_MODEL_FAST="$CLINE_DEFAULT_MODEL"
|
|
@@ -132,6 +138,23 @@ resolve_model_for_tier() {
|
|
|
132
138
|
echo "$CLINE_DEFAULT_MODEL"
|
|
133
139
|
}
|
|
134
140
|
|
|
141
|
+
# provider_invoke_argv <tier> <prompt> -- see providers/claude.sh for the full
|
|
142
|
+
# rationale. Populates _LOKI_INVOKE_ARGV so a caller can wrap it in `timeout`,
|
|
143
|
+
# which cannot exec a shell function.
|
|
144
|
+
#
|
|
145
|
+
# Unlike Codex, provider_get_tier_param here returns a real MODEL NAME, so it is
|
|
146
|
+
# safe to pass to -m. An empty value omits the flag rather than sending `-m ""`,
|
|
147
|
+
# matching provider_invoke's existing model_args guard.
|
|
148
|
+
provider_invoke_argv() {
|
|
149
|
+
local tier="${1:-development}"
|
|
150
|
+
local prompt="${2:-}"
|
|
151
|
+
local model
|
|
152
|
+
model="$(provider_get_tier_param "$tier" 2>/dev/null || printf '%s' "")"
|
|
153
|
+
_LOKI_INVOKE_ARGV=(cline -y)
|
|
154
|
+
[ -n "$model" ] && _LOKI_INVOKE_ARGV+=(-m "$model")
|
|
155
|
+
_LOKI_INVOKE_ARGV+=("$prompt")
|
|
156
|
+
}
|
|
157
|
+
|
|
135
158
|
# Tier-aware invocation
|
|
136
159
|
# Cline uses a single model configured externally, tier has no effect
|
|
137
160
|
provider_invoke_with_tier() {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Canonical model catalog. Update this single file when a provider ships a new model. Providers/web-app/docs read from here.",
|
|
3
|
+
"_source_of_truth": "models[] is authoritative. Each entry's `tier` decides what that tier resolves to, first match wins, so order within models[] is load-bearing. The top-level latest_<tier> keys are DERIVED mirrors kept only for existing readers (dashboard/server.py, tools/probe-model-catalog.py, tests); never hand-edit them out of step with models[] -- tests/test-model-catalog-single-source.sh fails when they disagree. A provider whose models[] has no entry for a tier must declare `tier_fallback` saying which tier substitutes; there is no implicit fallback.",
|
|
3
4
|
"schema_version": 1,
|
|
4
5
|
"updated": "2026-07-29",
|
|
5
6
|
"providers": {
|
|
6
7
|
"claude": {
|
|
7
8
|
"latest_planning": "claude-opus-4-8",
|
|
8
9
|
"latest_development": "claude-sonnet-5",
|
|
9
|
-
"latest_fast": "claude-
|
|
10
|
+
"latest_fast": "claude-haiku-4-5",
|
|
10
11
|
"cli_aliases": {
|
|
11
12
|
"fable": "claude-fable-5",
|
|
12
13
|
"opus": "claude-opus-4-8",
|
|
@@ -51,6 +52,10 @@
|
|
|
51
52
|
"latest_planning": "gpt-5.3-codex",
|
|
52
53
|
"latest_development": "gpt-5.3-codex",
|
|
53
54
|
"latest_fast": "gpt-5.3-codex",
|
|
55
|
+
"tier_fallback": {
|
|
56
|
+
"development": "planning",
|
|
57
|
+
"fast": "planning"
|
|
58
|
+
},
|
|
54
59
|
"models": [
|
|
55
60
|
{
|
|
56
61
|
"id": "gpt-5.3-codex",
|
|
@@ -58,14 +63,16 @@
|
|
|
58
63
|
},
|
|
59
64
|
{
|
|
60
65
|
"id": "o3",
|
|
61
|
-
"tier": "planning"
|
|
66
|
+
"tier": "planning",
|
|
67
|
+
"notes": "Alternative planning model. NOT the default -- gpt-5.3-codex is listed first and first match wins."
|
|
62
68
|
},
|
|
63
69
|
{
|
|
64
70
|
"id": "o4-mini",
|
|
65
|
-
"tier": "
|
|
71
|
+
"tier": "legacy",
|
|
72
|
+
"notes": "Was tier=fast, which made the cheap tier resolve to o4-mini while latest_fast said gpt-5.3-codex. Codex does not differentiate tiers by model, so it is not any tier's default; parked at tier=legacy so it stays discoverable without being dispatched."
|
|
66
73
|
}
|
|
67
74
|
],
|
|
68
|
-
"notes": "Codex uses a single model with effort level (xhigh/high/low) for tier differentiation"
|
|
75
|
+
"notes": "Codex uses a single model with effort level (xhigh/high/low) for tier differentiation, so development and fast declare an explicit tier_fallback to planning rather than naming separate models."
|
|
69
76
|
},
|
|
70
77
|
"cline": {
|
|
71
78
|
"latest_planning": "openrouter/deepseek/deepseek-v3.2",
|
|
@@ -164,10 +171,14 @@
|
|
|
164
171
|
]
|
|
165
172
|
},
|
|
166
173
|
"generic": {
|
|
167
|
-
"_comment": "Fallback for ANY provider not named above (bring-your-own endpoint). All three tiers collapse to one open model, the same shape codex uses. Override per tier with LOKI_<PROVIDER>_MODEL_<TIER> or wholesale with LOKI_<PROVIDER>_MODEL.",
|
|
174
|
+
"_comment": "Fallback for ANY provider not named above (bring-your-own endpoint). All three tiers collapse to one open model, the same shape codex uses, declared via tier_fallback. Override per tier with LOKI_<PROVIDER>_MODEL_<TIER> or wholesale with LOKI_<PROVIDER>_MODEL.",
|
|
168
175
|
"latest_planning": "openrouter/deepseek/deepseek-v3.2",
|
|
169
176
|
"latest_development": "openrouter/deepseek/deepseek-v3.2",
|
|
170
|
-
"latest_fast": "openrouter/deepseek/deepseek-
|
|
177
|
+
"latest_fast": "openrouter/deepseek/deepseek-v3.2",
|
|
178
|
+
"tier_fallback": {
|
|
179
|
+
"planning": "development",
|
|
180
|
+
"fast": "development"
|
|
181
|
+
},
|
|
171
182
|
"models": [
|
|
172
183
|
{
|
|
173
184
|
"id": "openrouter/deepseek/deepseek-v3.2",
|
package/providers/models.sh
CHANGED
|
@@ -16,12 +16,40 @@
|
|
|
16
16
|
_LOKI_MODELS_SH_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
17
17
|
LOKI_MODEL_CATALOG="${LOKI_MODEL_CATALOG:-$_LOKI_MODELS_SH_DIR/model_catalog.json}"
|
|
18
18
|
|
|
19
|
+
# GENERIC CAPABILITY TIERS. Callers should ask for small|medium|high and never
|
|
20
|
+
# name a vendor model. medium is the DEFAULT.
|
|
21
|
+
#
|
|
22
|
+
# small -> the cheap/fast model (claude: haiku, codex: luna)
|
|
23
|
+
# medium -> the workhorse (claude: sonnet, codex: terra) [default]
|
|
24
|
+
# high -> the expensive/best (claude: opus, codex: sol)
|
|
25
|
+
#
|
|
26
|
+
# WHY AN ALIAS LAYER RATHER THAN A RENAME: the catalog already keys models by
|
|
27
|
+
# planning/development/fast and several call sites depend on those names. This
|
|
28
|
+
# maps onto them instead of duplicating the mapping, so there is exactly one
|
|
29
|
+
# place a model id is written down. Hardcoding "medium = claude-sonnet-5"
|
|
30
|
+
# anywhere would recreate the drift class this repo keeps paying for -- a doc
|
|
31
|
+
# that named a default matching neither the code nor the catalog, and a test
|
|
32
|
+
# that demanded open-weights providers resolve to a Claude model.
|
|
33
|
+
loki_tier_alias() {
|
|
34
|
+
case "${1:-medium}" in
|
|
35
|
+
small|fast) printf 'fast' ;;
|
|
36
|
+
high|planning|best) printf 'planning' ;;
|
|
37
|
+
medium|development|'') printf 'development' ;;
|
|
38
|
+
# Unknown value falls back to the DEFAULT rather than failing the run:
|
|
39
|
+
# a typo in a pipeline config should not change which model you pay for
|
|
40
|
+
# silently, and it should not halt a build either. The caller can see
|
|
41
|
+
# what resolved via loki_latest_model.
|
|
42
|
+
*) printf 'development' ;;
|
|
43
|
+
esac
|
|
44
|
+
}
|
|
45
|
+
|
|
19
46
|
# Return the "latest_<tier>" id for a provider from the catalog.
|
|
20
47
|
# Args: $1 provider (claude|codex|cline|aider)
|
|
21
|
-
# $2 tier (planning|development|fast)
|
|
48
|
+
# $2 tier (planning|development|fast, or small|medium|high)
|
|
22
49
|
loki_latest_model() {
|
|
23
50
|
local provider="${1:-claude}"
|
|
24
|
-
local tier
|
|
51
|
+
local tier
|
|
52
|
+
tier="$(loki_tier_alias "${2:-planning}")"
|
|
25
53
|
local tier_upper
|
|
26
54
|
tier_upper=$(printf '%s' "$tier" | tr '[:lower:]' '[:upper:]')
|
|
27
55
|
local provider_upper
|
|
@@ -74,7 +102,34 @@ if not p:
|
|
|
74
102
|
p = providers.get("generic")
|
|
75
103
|
if not p:
|
|
76
104
|
sys.exit(1)
|
|
77
|
-
|
|
105
|
+
|
|
106
|
+
# SINGLE SOURCE OF TRUTH: models[] is authoritative, top-level latest_<tier> is
|
|
107
|
+
# derived. Before this, both existed independently and drifted -- claude
|
|
108
|
+
# latest_fast said claude-sonnet-5 while models[tier=fast] said claude-haiku-4-5,
|
|
109
|
+
# so asking for the cheap tier silently billed the mid tier. models[] wins
|
|
110
|
+
# because it is the richer structure (context_window, max_output, open_weights,
|
|
111
|
+
# alias, notes per model); the top-level keys carry nothing it does not.
|
|
112
|
+
#
|
|
113
|
+
# ORDER IS LOAD-BEARING: a provider may declare several models at one tier
|
|
114
|
+
# (codex lists gpt-5.3-codex and o3 both as planning). First match wins, so the
|
|
115
|
+
# FIRST entry at a tier is the default. Reordering models[] changes what real
|
|
116
|
+
# builds dispatch. tests/test-model-catalog-single-source.sh pins this.
|
|
117
|
+
models = [m for m in p.get("models", []) if isinstance(m, dict)]
|
|
118
|
+
model = next((m.get("id") for m in models if m.get("tier") == tier), None)
|
|
119
|
+
|
|
120
|
+
if not model:
|
|
121
|
+
# No entry at this tier. Falling back must be DECLARED, never accidental:
|
|
122
|
+
# codex genuinely runs one model for every tier and varies reasoning effort
|
|
123
|
+
# instead, so an absent tier is legitimate for it -- but silently walking to
|
|
124
|
+
# some other tier is how you end up dispatching a model nobody chose. The
|
|
125
|
+
# provider states the substitution in the catalog as tier_fallback, and we
|
|
126
|
+
# honor only that.
|
|
127
|
+
fallback_tier = (p.get("tier_fallback") or {}).get(tier)
|
|
128
|
+
if fallback_tier:
|
|
129
|
+
model = next(
|
|
130
|
+
(m.get("id") for m in models if m.get("tier") == fallback_tier), None
|
|
131
|
+
)
|
|
132
|
+
|
|
78
133
|
if not model:
|
|
79
134
|
sys.exit(1)
|
|
80
135
|
print(model)
|
package/providers/opencode.sh
CHANGED
|
@@ -67,14 +67,20 @@ PROVIDER_DEGRADED=false
|
|
|
67
67
|
PROVIDER_MAX_OUTPUT_TOKENS=128000
|
|
68
68
|
|
|
69
69
|
# Model defaults. opencode names models "provider/model"; the catalog supplies
|
|
70
|
-
# the value and
|
|
70
|
+
# the value and LOKI_OPENCODE_MODEL overrides it.
|
|
71
71
|
_opencode_default_from_catalog() {
|
|
72
72
|
if type loki_latest_model >/dev/null 2>&1; then
|
|
73
73
|
loki_latest_model opencode development 2>/dev/null && return 0
|
|
74
74
|
fi
|
|
75
75
|
printf 'openrouter/deepseek/deepseek-v3.2'
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
# Resolution: provider-scoped env > catalog. The GLOBAL tier var
|
|
78
|
+
# (LOKI_MODEL_DEVELOPMENT) is deliberately NOT in this chain: opencode takes
|
|
79
|
+
# "provider/model" strings ("openrouter/deepseek/deepseek-v3.2"), while the
|
|
80
|
+
# global tier var carries Claude CLI aliases ("sonnet"), so a global set for
|
|
81
|
+
# one provider silently changed opencode's model too. Namespace-incompatible,
|
|
82
|
+
# not merely mis-ordered. Do not re-add it.
|
|
83
|
+
OPENCODE_DEFAULT_MODEL="${LOKI_OPENCODE_MODEL:-$(_opencode_default_from_catalog)}"
|
|
78
84
|
PROVIDER_MODEL_PLANNING="$OPENCODE_DEFAULT_MODEL"
|
|
79
85
|
PROVIDER_MODEL_DEVELOPMENT="$OPENCODE_DEFAULT_MODEL"
|
|
80
86
|
PROVIDER_MODEL_FAST="$OPENCODE_DEFAULT_MODEL"
|
|
@@ -71,6 +71,15 @@ PROVIDER_CONTEXT_WINDOW=1000000 # Max context tokens (Opus 4.7: 1M at standard p
|
|
|
71
71
|
PROVIDER_MAX_OUTPUT_TOKENS=128000
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
`PROVIDER_CONTEXT_WINDOW` also caps code-review size. The review diff and prompt
|
|
75
|
+
limits derive from it (assuming ~3 bytes per token and ~75% of the window for
|
|
76
|
+
input), so a small-window local model is not handed a prompt sized for a 200k
|
|
77
|
+
one. The derived value is only ever used to LOWER a cap, and a window at or above
|
|
78
|
+
roughly 188889 tokens clamps to the historical 400000/425000 byte defaults, so no
|
|
79
|
+
provider shipping today changes behavior (a provider that declares no window
|
|
80
|
+
keeps the defaults as well). `LOKI_REVIEW_MAX_DIFF_BYTES` and
|
|
81
|
+
`LOKI_REVIEW_MAX_PROMPT_BYTES` still override both.
|
|
82
|
+
|
|
74
83
|
#### Degraded Mode
|
|
75
84
|
```bash
|
|
76
85
|
PROVIDER_DEGRADED=false # true for Codex/Aider
|
package/skills/providers.md
CHANGED
|
@@ -73,6 +73,40 @@ Quality gates, the completion council, and the Evidence Receipt are all
|
|
|
73
73
|
model-agnostic: they assert on the artifact that was built, not on which model
|
|
74
74
|
built it. A cheaper or local model gets the same verification as Opus.
|
|
75
75
|
|
|
76
|
+
### Keeping the model catalog current
|
|
77
|
+
|
|
78
|
+
`providers/model_catalog.json` is hand-maintained and carries an `updated` date.
|
|
79
|
+
Models ship constantly, so the catalog rots. `loki doctor` reports its age in a
|
|
80
|
+
`Model catalog:` section and warns once it passes 90 days:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
WARN Last updated 2026-01-02 (210 days ago) -- may be missing newer models
|
|
84
|
+
Refresh: python3 tools/probe-model-catalog.py
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The warning is **advisory only**. It never changes an exit code and never fails
|
|
88
|
+
a build (`tests/test-model-catalog-staleness.sh` asserts exactly that). It also
|
|
89
|
+
makes no network call: doctor reads the local file's `updated` field and nothing
|
|
90
|
+
else, so air-gapped operation (`docs/air-gapped.md`) is unaffected.
|
|
91
|
+
|
|
92
|
+
To refresh:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python3 tools/probe-model-catalog.py # report new model IDs found in provider docs
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The probe reads public provider documentation and reports model IDs that are not
|
|
99
|
+
yet in the catalog. **It never rewrites the catalog.** You edit
|
|
100
|
+
`providers/model_catalog.json` by hand -- bump the relevant `latest_<tier>` and
|
|
101
|
+
add the model to `models[]` -- then set `updated` to today. Model adoption is a
|
|
102
|
+
human decision: cost, capability, and behavioural changes all need a person to
|
|
103
|
+
weigh them, and a model ID that does not exist would break every run that
|
|
104
|
+
selects it. Being stale is recoverable; a fabricated model ID is not.
|
|
105
|
+
|
|
106
|
+
The probe is also wired to a weekly CI job (`.github/workflows/model-catalog-probe.yml`)
|
|
107
|
+
that opens a draft PR with its findings. That job is CI-only -- it is not on any
|
|
108
|
+
runtime path.
|
|
109
|
+
|
|
76
110
|
## Claude Code (Default, Full Features)
|
|
77
111
|
|
|
78
112
|
**Best for:** All use cases. Full autonomous capability.
|
|
@@ -246,7 +280,7 @@ aider --message "$prompt" --yes-always --no-auto-commits --model model_name
|
|
|
246
280
|
**Environment Variables:**
|
|
247
281
|
| Variable | Description |
|
|
248
282
|
|----------|-------------|
|
|
249
|
-
| `LOKI_AIDER_MODEL` | Model to use
|
|
283
|
+
| `LOKI_AIDER_MODEL` | Model to use. Default comes from `providers/model_catalog.json` (`aider.latest_development`), not a hardcoded string. The global `LOKI_MODEL_*` tier vars do NOT apply to aider. |
|
|
250
284
|
| `LOKI_AIDER_FLAGS` | Extra aider flags (e.g., --architect) |
|
|
251
285
|
|
|
252
286
|
---
|