loki-mode 9.12.5 → 9.16.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 -101
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/intent.sh +414 -0
- package/autonomy/issue-providers.sh +21 -0
- package/autonomy/lib/agent_readiness.py +202 -0
- package/autonomy/lib/claim_grounding.py +171 -0
- package/autonomy/lib/config-map.sh +10 -6
- package/autonomy/lib/decision_record.py +198 -0
- package/autonomy/lib/failure_memory.py +199 -0
- package/autonomy/lib/outcome_ledger.py +498 -0
- package/autonomy/lib/preedit_snapshot.py +216 -0
- package/autonomy/lib/verdict.py +204 -0
- package/autonomy/loki +378 -22
- package/autonomy/provider-offer.sh +25 -1
- package/autonomy/run.sh +516 -11
- package/autonomy/telemetry.sh +8 -1
- package/autonomy/verify.sh +10 -1
- package/completions/_loki +4 -0
- package/completions/loki.bash +2 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/api_operator.py +15 -2
- package/dashboard/api_v2.py +6 -1
- package/dashboard/control.py +62 -10
- package/dashboard/run.py +13 -2
- package/dashboard/scim.py +221 -0
- package/dashboard/server.py +48 -5
- package/docs/GATE-FAILURE-TRIAGE.md +254 -0
- package/docs/LOOP-CANDIDATE-PROPOSAL-v1.md +167 -0
- package/docs/LOOP-HARNESS-AUDIT.md +684 -0
- package/docs/VERIFICATION-COST.md +103 -0
- package/docs/WANG-PRINCIPLES-PLAN.md +1 -1
- package/loki-ts/dist/loki.js +402 -398
- package/mcp/__init__.py +1 -1
- package/mcp/_sdk_loader.py +25 -0
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/tools/loop-harness-report.py +216 -0
package/mcp/__init__.py
CHANGED
package/mcp/_sdk_loader.py
CHANGED
|
@@ -147,6 +147,31 @@ def _load_real_fastmcp():
|
|
|
147
147
|
for _k in ("mcp", "mcp.server"):
|
|
148
148
|
if _k in _saved_local:
|
|
149
149
|
sys.modules[_k] = _saved_local[_k]
|
|
150
|
+
elif _k in sys.modules:
|
|
151
|
+
# The SDK claimed a name the local package had NOT yet loaded,
|
|
152
|
+
# so there is no local module to re-pin and the old code left
|
|
153
|
+
# the SDK's module cached under it PERMANENTLY, process-wide.
|
|
154
|
+
#
|
|
155
|
+
# Measured (issue #190, python3.12 + SDK, from the repo root):
|
|
156
|
+
# any caller that reaches the loader before our own
|
|
157
|
+
# mcp/server.py is imported -- mcp/lsp_proxy.py, or a test that
|
|
158
|
+
# imports mcp._sdk_loader directly -- leaves the SDK's
|
|
159
|
+
# site-packages/mcp/server/__init__.py under "mcp.server". Every
|
|
160
|
+
# later `from mcp import server` in that process then silently
|
|
161
|
+
# gets the SDK instead of ours. That is how
|
|
162
|
+
# tests/test_mcp_agent_metrics_honesty.py skipped for its full
|
|
163
|
+
# life: it passed 7/7 alone but hit its capability guard in a
|
|
164
|
+
# full run, because tests/mcp/* ran first and poisoned the name.
|
|
165
|
+
#
|
|
166
|
+
# Deleting is correct where re-pinning is impossible: it leaves
|
|
167
|
+
# the name UNCLAIMED, so the next `import mcp.server` resolves
|
|
168
|
+
# through the local package exactly as it would have. Verified
|
|
169
|
+
# this does not disturb the SDK's own runtime subtree --
|
|
170
|
+
# mcp.types, mcp.server.lowlevel and mcp.server.fastmcp.* stay
|
|
171
|
+
# cached and FastMCP still instantiates, because FastMCP holds
|
|
172
|
+
# direct references to its dependencies rather than re-resolving
|
|
173
|
+
# "mcp.server" by name.
|
|
174
|
+
del sys.modules[_k]
|
|
150
175
|
for _k, _v in _saved_local.items():
|
|
151
176
|
# Restore any other purely-local submodules that the SDK import did
|
|
152
177
|
# not legitimately replace (e.g. mcp.magic_tools, mcp.tools).
|
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": "9.
|
|
4
|
+
"version": "9.16.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": "9.
|
|
5
|
+
"version": "9.16.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",
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""loop-harness-v1: what the verifiers in a run actually recorded.
|
|
3
|
+
|
|
4
|
+
READ-ONLY. Opens .loki/events.jsonl and nothing else. Writes nothing, spawns
|
|
5
|
+
nothing, and touches no runtime path. Rollback is deleting this file.
|
|
6
|
+
|
|
7
|
+
WHY THIS REPORTS MOSTLY UNKNOWN, AND WHY THAT IS THE POINT. A loop-harness
|
|
8
|
+
manifest wants, per verifier invocation: eligibility, the deterministic
|
|
9
|
+
criterion, retry and timeout caps, latency, tokens, cash cost, the verdict,
|
|
10
|
+
whether it changed the terminal outcome, false-positive review, and a rollback
|
|
11
|
+
switch.
|
|
12
|
+
|
|
13
|
+
Measured at v9.12.5, the runtime emits almost none of that:
|
|
14
|
+
|
|
15
|
+
code_review_complete review_id, source, iteration
|
|
16
|
+
review_verification_failed reason, iteration, implementation_retry
|
|
17
|
+
_evidence_gate_and_surface nothing structured
|
|
18
|
+
_invariant_gate_and_surface nothing structured
|
|
19
|
+
_semantic_gate_and_surface nothing structured
|
|
20
|
+
|
|
21
|
+
So this reader CANNOT derive most columns. It reports them UNKNOWN rather than
|
|
22
|
+
defaulting them, because a manifest that fills a cost column with 0.0 or an
|
|
23
|
+
eligibility column with "yes" is asserting a measurement nobody took -- and a
|
|
24
|
+
fabricated manifest about verification is worse than no manifest at all.
|
|
25
|
+
|
|
26
|
+
The UNKNOWNs are the deliverable. They say exactly which instrumentation is
|
|
27
|
+
missing, so a later decision to add it is evidenced rather than assumed.
|
|
28
|
+
|
|
29
|
+
Exit codes follow this repo's convention:
|
|
30
|
+
0 verifier records were found and reported
|
|
31
|
+
2 the trace could not be read
|
|
32
|
+
3 nothing to check -- no verifier events in this workspace
|
|
33
|
+
64 usage error
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import argparse
|
|
37
|
+
import json
|
|
38
|
+
import os
|
|
39
|
+
import sys
|
|
40
|
+
|
|
41
|
+
sys.dont_write_bytecode = True
|
|
42
|
+
|
|
43
|
+
UNKNOWN = "UNKNOWN"
|
|
44
|
+
|
|
45
|
+
# Event types that represent a verifier doing something. Derived from
|
|
46
|
+
# autonomy/run.sh's emit_event_json call sites, not invented here.
|
|
47
|
+
_VERIFIER_EVENTS = {
|
|
48
|
+
"code_review_start",
|
|
49
|
+
"code_review_complete",
|
|
50
|
+
"code_review_council_complete",
|
|
51
|
+
"review_verification_failed",
|
|
52
|
+
"managed_review_council_ok",
|
|
53
|
+
"gate_stuck",
|
|
54
|
+
"policy_denied",
|
|
55
|
+
"task_completion_claim",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Fields a loop-harness manifest wants, and where each one comes from today.
|
|
59
|
+
# "" means no emitter records it, so the column reads UNKNOWN for every row.
|
|
60
|
+
_FIELD_SOURCES = {
|
|
61
|
+
"verifier": "event type",
|
|
62
|
+
"iteration": "iteration",
|
|
63
|
+
"verdict": "partial: only the failure path records a reason",
|
|
64
|
+
"eligible": "",
|
|
65
|
+
"criterion": "",
|
|
66
|
+
"retry_cap": "",
|
|
67
|
+
"timeout_cap": "",
|
|
68
|
+
"latency_ms": "",
|
|
69
|
+
"tokens": "",
|
|
70
|
+
"cost_usd": "",
|
|
71
|
+
"changed_terminal_outcome": "",
|
|
72
|
+
"false_positive_reviewed": "",
|
|
73
|
+
"rollback_switch": "",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class _Parser(argparse.ArgumentParser):
|
|
78
|
+
"""Usage errors exit 64, not argparse's default 2.
|
|
79
|
+
|
|
80
|
+
In this repo 2 means "could NOT be checked" -- a real answer about the
|
|
81
|
+
subject. A mistyped flag is not that.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def error(self, message):
|
|
85
|
+
self.print_usage(sys.stderr)
|
|
86
|
+
sys.stderr.write("%s: error: %s\n" % (self.prog, message))
|
|
87
|
+
raise SystemExit(64)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _events(path):
|
|
91
|
+
"""Every well-formed record, oldest first. A torn line is skipped.
|
|
92
|
+
|
|
93
|
+
events.jsonl is appended to by concurrent shell writers, so a partial
|
|
94
|
+
final line is normal operation and must not blank out the history behind
|
|
95
|
+
it.
|
|
96
|
+
"""
|
|
97
|
+
out = []
|
|
98
|
+
try:
|
|
99
|
+
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
|
100
|
+
for line in fh:
|
|
101
|
+
line = line.strip()
|
|
102
|
+
if not line:
|
|
103
|
+
continue
|
|
104
|
+
try:
|
|
105
|
+
rec = json.loads(line)
|
|
106
|
+
except (ValueError, json.JSONDecodeError):
|
|
107
|
+
continue
|
|
108
|
+
if isinstance(rec, dict):
|
|
109
|
+
out.append(rec)
|
|
110
|
+
except OSError:
|
|
111
|
+
return None
|
|
112
|
+
return out
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _row(rec):
|
|
116
|
+
"""One verifier invocation, with every underivable field UNKNOWN."""
|
|
117
|
+
data = rec.get("data") if isinstance(rec.get("data"), dict) else {}
|
|
118
|
+
etype = rec.get("type") or UNKNOWN
|
|
119
|
+
|
|
120
|
+
verdict = UNKNOWN
|
|
121
|
+
if etype in ("code_review_complete", "managed_review_council_ok"):
|
|
122
|
+
# Completion is not a verdict: these fire when the council FINISHED,
|
|
123
|
+
# not when it approved. Recording "pass" here would invent an outcome.
|
|
124
|
+
verdict = UNKNOWN
|
|
125
|
+
elif etype == "review_verification_failed":
|
|
126
|
+
verdict = data.get("reason") or "failed"
|
|
127
|
+
elif etype == "policy_denied":
|
|
128
|
+
verdict = "denied"
|
|
129
|
+
|
|
130
|
+
row = {
|
|
131
|
+
"verifier": etype,
|
|
132
|
+
"timestamp": rec.get("timestamp") or UNKNOWN,
|
|
133
|
+
"iteration": data.get("iteration", UNKNOWN),
|
|
134
|
+
"verdict": verdict,
|
|
135
|
+
}
|
|
136
|
+
for field, source in _FIELD_SOURCES.items():
|
|
137
|
+
if field in row:
|
|
138
|
+
continue
|
|
139
|
+
row[field] = UNKNOWN if not source else data.get(field, UNKNOWN)
|
|
140
|
+
return row
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def report(loki_dir):
|
|
144
|
+
"""The manifest envelope. Empty results always carry a reason."""
|
|
145
|
+
path = os.path.join(loki_dir, "events.jsonl")
|
|
146
|
+
env = {
|
|
147
|
+
"report": "loop-harness-v1",
|
|
148
|
+
"source": [path],
|
|
149
|
+
"rows": [],
|
|
150
|
+
"count": 0,
|
|
151
|
+
"unmeasured_fields": sorted(f for f, s in _FIELD_SOURCES.items()
|
|
152
|
+
if not s),
|
|
153
|
+
"reason": None,
|
|
154
|
+
}
|
|
155
|
+
if not os.path.isdir(loki_dir):
|
|
156
|
+
env["reason"] = "no .loki directory at %s" % loki_dir
|
|
157
|
+
return env, 3
|
|
158
|
+
if not os.path.isfile(path):
|
|
159
|
+
env["reason"] = ("%s does not exist, so no verifier ever recorded "
|
|
160
|
+
"anything here" % path)
|
|
161
|
+
return env, 3
|
|
162
|
+
|
|
163
|
+
records = _events(path)
|
|
164
|
+
if records is None:
|
|
165
|
+
env["reason"] = "%s could not be read" % path
|
|
166
|
+
return env, 2
|
|
167
|
+
|
|
168
|
+
rows = [_row(r) for r in records if r.get("type") in _VERIFIER_EVENTS]
|
|
169
|
+
if not rows:
|
|
170
|
+
# NOT an empty table. "no verifier events" and "the verifiers all
|
|
171
|
+
# passed" are different claims, and only one of them is supported.
|
|
172
|
+
env["reason"] = (
|
|
173
|
+
"no verifier events among %d records in this trace (the run "
|
|
174
|
+
"predates verifier tracing, or no verifier ran)" % len(records))
|
|
175
|
+
return env, 3
|
|
176
|
+
|
|
177
|
+
env["rows"] = rows
|
|
178
|
+
env["count"] = len(rows)
|
|
179
|
+
return env, 0
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def main(argv=None):
|
|
183
|
+
parser = _Parser(description="loop-harness-v1 verifier manifest (read-only)")
|
|
184
|
+
parser.add_argument("--loki-dir", default=os.environ.get("LOKI_DIR")
|
|
185
|
+
or os.path.join(os.getcwd(), ".loki"))
|
|
186
|
+
parser.add_argument("--json", action="store_true")
|
|
187
|
+
args = parser.parse_args(argv)
|
|
188
|
+
|
|
189
|
+
env, code = report(args.loki_dir)
|
|
190
|
+
|
|
191
|
+
if args.json:
|
|
192
|
+
print(json.dumps(env, indent=2))
|
|
193
|
+
return code
|
|
194
|
+
|
|
195
|
+
print("loop-harness-v1 source: %s" % ", ".join(env["source"]))
|
|
196
|
+
if not env["rows"]:
|
|
197
|
+
print(" nothing to check: %s" % env["reason"])
|
|
198
|
+
else:
|
|
199
|
+
print(" %-30s %-10s %s" % ("VERIFIER", "ITERATION", "VERDICT"))
|
|
200
|
+
for r in env["rows"]:
|
|
201
|
+
print(" %-30s %-10s %s"
|
|
202
|
+
% (r["verifier"], r["iteration"], r["verdict"]))
|
|
203
|
+
print(" %d verifier records" % env["count"])
|
|
204
|
+
print("")
|
|
205
|
+
print(" NOT RECORDED BY THE RUNTIME (every row reads UNKNOWN):")
|
|
206
|
+
for f in env["unmeasured_fields"]:
|
|
207
|
+
print(" - %s" % f)
|
|
208
|
+
print(" These are the fields a loop-harness manifest needs and the")
|
|
209
|
+
print(" runtime does not emit. They are reported UNKNOWN rather than")
|
|
210
|
+
print(" defaulted, because a fabricated verification manifest is worse")
|
|
211
|
+
print(" than an absent one.")
|
|
212
|
+
return code
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
raise SystemExit(main())
|