loki-mode 9.8.0 → 9.12.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.
Files changed (79) hide show
  1. package/README.md +19 -14
  2. package/SKILL.md +3 -2
  3. package/VERSION +1 -1
  4. package/autonomy/loki +122 -1
  5. package/autonomy/run.sh +49 -2
  6. package/dashboard/__init__.py +1 -1
  7. package/dashboard/api_evidence.py +411 -0
  8. package/dashboard/api_operator.py +283 -0
  9. package/dashboard/api_phases.py +262 -0
  10. package/dashboard/api_releases.py +242 -0
  11. package/dashboard/api_runs.py +477 -0
  12. package/dashboard/api_tests.py +444 -0
  13. package/dashboard/api_v2.py +47 -1
  14. package/dashboard/server.py +54 -0
  15. package/dashboard/static/index.html +246 -135
  16. package/docs/ARCHITECTURE-OVERVIEW.md +5 -3
  17. package/docs/CAPABILITY-BACKLOG.md +53 -0
  18. package/docs/COMPARISON.md +2 -2
  19. package/docs/COMPETITIVE-ANALYSIS.md +1 -1
  20. package/docs/COMPETITIVE-SCORECARD.md +422 -0
  21. package/docs/DASHBOARD-9.12-EVIDENCE.md +97 -0
  22. package/docs/DASHBOARD-ARCHITECTURE.md +423 -0
  23. package/docs/DEMOS.md +21 -23
  24. package/docs/HANDOFF-2026-08-03.md +439 -0
  25. package/docs/INSTALLATION.md +17 -10
  26. package/docs/OUTCOME-FRONTIER.md +536 -0
  27. package/docs/PROMPT-ABLATION-RESULT.md +97 -0
  28. package/docs/TOOLS.md +800 -0
  29. package/docs/alternative-installations.md +2 -3
  30. package/docs/audit-logging.md +44 -35
  31. package/docs/authentication.md +13 -2
  32. package/docs/authorization.md +87 -81
  33. package/docs/git-workflow.md +6 -3
  34. package/docs/metrics.md +15 -16
  35. package/docs/network-security.md +16 -13
  36. package/docs/openclaw-integration.md +36 -556
  37. package/docs/show-hn-post.md +2 -2
  38. package/docs/siem-integration.md +39 -36
  39. package/loki-ts/dist/loki.js +18 -18
  40. package/mcp/__init__.py +1 -1
  41. package/package.json +2 -2
  42. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  43. package/references/confidence-routing.md +18 -1
  44. package/references/invariant-checks.md +13 -8
  45. package/references/magic-rarv-integration.md +0 -1
  46. package/references/multi-provider.md +27 -5
  47. package/skills/healing.md +4 -2
  48. package/tools/audit-docs.py +488 -0
  49. package/tools/baseline-pin.py +19 -1
  50. package/tools/calibration-audit.py +523 -0
  51. package/tools/ci-gate.py +19 -1
  52. package/tools/cost-forecast.py +344 -0
  53. package/tools/cost-guard.py +19 -1
  54. package/tools/cost-history.py +19 -1
  55. package/tools/cost-per-outcome.py +394 -0
  56. package/tools/estimate-run.py +19 -1
  57. package/tools/evidence-freshness.py +307 -0
  58. package/tools/gate-init.py +19 -1
  59. package/tools/gate-report.py +19 -1
  60. package/tools/gate-simulate.py +570 -0
  61. package/tools/gate-trend.py +354 -0
  62. package/tools/model-advisor.py +52 -1
  63. package/tools/policy-load.py +19 -1
  64. package/tools/prompt-cost.py +363 -0
  65. package/tools/prompt-diff.py +448 -0
  66. package/tools/prompt-lint.py +448 -0
  67. package/tools/receipt-bundle.py +72 -2
  68. package/tools/receipt-diff.py +19 -1
  69. package/tools/receipt-find.py +19 -1
  70. package/tools/receipt-stats.py +380 -0
  71. package/tools/receipt-timeline.py +478 -0
  72. package/tools/receipt-verify-batch.py +291 -0
  73. package/tools/run-replay.py +19 -1
  74. package/tools/signing-status.py +19 -1
  75. package/tools/token-guard.py +19 -1
  76. package/tools/token-tax.py +375 -0
  77. package/tools/tool-index.py +19 -1
  78. package/tools/verification-tax.py +277 -0
  79. package/tools/verify-chain.py +361 -0
@@ -0,0 +1,477 @@
1
+ """Read-only "runs" view over the filesystem .loki/ directory.
2
+
3
+ NOT dashboard/runs.py. That module is the SQLAlchemy Run/RunEvent control
4
+ plane (a database row per run, created by the dashboard). This module reads
5
+ the engine's own on-disk state under .loki/ -- what the runner actually wrote
6
+ -- and never touches a database. Same word, two different objects; do not
7
+ merge them.
8
+
9
+ WHY THIS EXISTS. The dashboard's primary object is a run, and it had zero
10
+ routes able to list one from engine state. These are pure functions taking a
11
+ loki_dir, so they are testable without FastAPI and can be mounted by whoever
12
+ owns server.py.
13
+
14
+ THE SOURCES, and what each can and cannot tell us:
15
+
16
+ .loki/metrics/trust-events.jsonl append-only, one record per trust event,
17
+ each carrying run_id (minted per run as run-<ts>-<pid>-<rand> by
18
+ _loki_trust_run_id in autonomy/run.sh). THE ONLY source that survives
19
+ across runs, and therefore the only reason list_runs can be plural.
20
+ .loki/state/trust-run-id the CURRENT run's minted id.
21
+ .loki/metrics/efficiency/iteration-*.json per-iteration cost and tokens.
22
+ WIPED at run start (autonomy/run.sh:6212), so these describe the current
23
+ run ONLY. A historical run's row therefore reports cost UNKNOWN -- not
24
+ zero -- because the evidence was deleted, which is a real absence of
25
+ measurement and must read as one.
26
+ .loki/state/completion.json terminal outcome of the last run.
27
+ .loki/loki-run.json run manifest (schema loki-run-manifest/v1).
28
+ .loki/PAUSE, .loki/STOP, .loki/session.json live status signals.
29
+
30
+ THE HONESTY RULE. An unmeasured cost is None, never 0.0. That predicate is
31
+ record_is_measured() in autonomy/lib/efficiency_cost.py and it is IMPORTED
32
+ here, not restated. Its own docstring explains why: "A second copy of this
33
+ predicate is how the honesty rule drifts: the four surfaces that once rendered
34
+ an unmeasured run as $0.00 each had their own idea of what counted as
35
+ measured." dashboard/server.py keeps a deliberate mirror; this module does not
36
+ add a third. If autonomy/lib is unreachable the cost fields read UNKNOWN and
37
+ the envelope says so -- a degraded read never invents a number.
38
+
39
+ Every returned envelope states `source` (the real paths read) and
40
+ `freshness_s` (age in seconds of the newest file that actually contributed,
41
+ None when nothing did), and carries an explicit `reason` when empty.
42
+ """
43
+
44
+ from __future__ import annotations
45
+
46
+ import json
47
+ import os
48
+ import time
49
+ from typing import Any, Optional
50
+
51
+ __all__ = ["list_runs", "get_run", "UNKNOWN"]
52
+
53
+ # What an unmeasured value reads as. Kept as a name so callers can render it
54
+ # without hardcoding None-means-unknown at each call site.
55
+ UNKNOWN = None
56
+
57
+ _RUN_ID_FILE = ("state", "trust-run-id")
58
+ _TRUST_EVENTS = ("metrics", "trust-events.jsonl")
59
+ _EFFICIENCY_DIR = ("metrics", "efficiency")
60
+ _COMPLETION = ("state", "completion.json")
61
+ _MANIFEST = "loki-run.json"
62
+ _SESSION = "session.json"
63
+
64
+ # Declared for the envelope so a caller can see exactly what was read, in the
65
+ # style already used at autonomy/loki:21677 (a real path, not a label).
66
+ _SOURCE_PATHS = (
67
+ ".loki/metrics/trust-events.jsonl",
68
+ ".loki/state/trust-run-id",
69
+ ".loki/metrics/efficiency/iteration-*.json",
70
+ ".loki/state/completion.json",
71
+ ".loki/loki-run.json",
72
+ ".loki/session.json",
73
+ )
74
+
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # tiny io helpers -- every one of them returns a default rather than raising,
78
+ # because a dashboard read must degrade, never 500 on a missing file.
79
+ # ---------------------------------------------------------------------------
80
+
81
+ def _p(loki_dir: str, *parts: str) -> str:
82
+ return os.path.join(loki_dir, *parts)
83
+
84
+
85
+ def _read_json(path: str, default: Any = None) -> Any:
86
+ try:
87
+ with open(path, "r", encoding="utf-8") as fh:
88
+ return json.load(fh)
89
+ except Exception:
90
+ return default
91
+
92
+
93
+ def _read_text(path: str) -> str:
94
+ try:
95
+ with open(path, "r", encoding="utf-8") as fh:
96
+ return fh.read().strip()
97
+ except Exception:
98
+ return ""
99
+
100
+
101
+ def _mtime(path: str) -> Optional[float]:
102
+ try:
103
+ return os.path.getmtime(path)
104
+ except OSError:
105
+ return None
106
+
107
+
108
+ def _freshness(mtimes: list, now: Optional[float] = None) -> Optional[int]:
109
+ """Age in seconds of the NEWEST file that contributed. None if none did.
110
+
111
+ None is load-bearing: "no file contributed" is not "contributed zero
112
+ seconds ago". Clamped at 0 because a file written during this call (or by
113
+ a clock that stepped) must not report a negative age.
114
+ """
115
+ real = [m for m in mtimes if m is not None]
116
+ if not real:
117
+ return None
118
+ return max(0, int((now if now is not None else time.time()) - max(real)))
119
+
120
+
121
+ # ---------------------------------------------------------------------------
122
+ # the canonical measured-ness predicate, imported not copied
123
+ # ---------------------------------------------------------------------------
124
+
125
+ _EFF_MOD: Any = None
126
+ _EFF_TRIED = False
127
+
128
+
129
+ def _efficiency_module():
130
+ """Load autonomy/lib/efficiency_cost.py by path. None if unreachable.
131
+
132
+ autonomy/lib is not an importable package (proof-generator.py has a hyphen
133
+ in its name), so server.py already loads siblings this way -- see
134
+ _trust_module() at dashboard/server.py:7967. Cached, including the failure,
135
+ so a missing file costs one stat per process rather than one per request.
136
+ """
137
+ global _EFF_MOD, _EFF_TRIED
138
+ if _EFF_TRIED:
139
+ return _EFF_MOD
140
+ _EFF_TRIED = True
141
+ repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
142
+ mod_path = os.path.join(repo_root, "autonomy", "lib", "efficiency_cost.py")
143
+ try:
144
+ import importlib.util as ilu
145
+ spec = ilu.spec_from_file_location("loki_efficiency_cost", mod_path)
146
+ if spec is None or spec.loader is None:
147
+ return None
148
+ mod = ilu.module_from_spec(spec)
149
+ spec.loader.exec_module(mod)
150
+ _EFF_MOD = mod
151
+ except Exception:
152
+ _EFF_MOD = None
153
+ return _EFF_MOD
154
+
155
+
156
+ # ---------------------------------------------------------------------------
157
+ # per-iteration cost, from the current run's efficiency records
158
+ # ---------------------------------------------------------------------------
159
+
160
+ def _iteration_records(loki_dir: str) -> list:
161
+ """[(iteration_int, record_dict, path)] sorted by iteration.
162
+
163
+ Sorted NUMERICALLY. A lexical sort puts iteration-10 before iteration-2,
164
+ which silently misorders any run past nine iterations.
165
+ """
166
+ eff_dir = _p(loki_dir, *_EFFICIENCY_DIR)
167
+ try:
168
+ names = os.listdir(eff_dir)
169
+ except OSError:
170
+ return []
171
+ out = []
172
+ for name in names:
173
+ if not (name.startswith("iteration-") and name.endswith(".json")):
174
+ continue
175
+ path = os.path.join(eff_dir, name)
176
+ rec = _read_json(path)
177
+ if not isinstance(rec, dict):
178
+ continue
179
+ try:
180
+ num = int(name[len("iteration-"):-len(".json")])
181
+ except ValueError:
182
+ continue
183
+ out.append((num, rec, path))
184
+ out.sort(key=lambda t: t[0])
185
+ return out
186
+
187
+
188
+ def _cost_for_current_run(loki_dir: str):
189
+ """(cost_usd, measured, note) for the run owning the efficiency dir.
190
+
191
+ cost_usd is None whenever we did not measure -- no records, all-zero
192
+ records, or the canonical module being unreachable. A genuine measured
193
+ zero (records that carry data and sum to 0.0) stays 0.0; that distinction
194
+ is the whole point of the predicate and is why collect_efficiency is
195
+ called rather than re-summed here.
196
+ """
197
+ mod = _efficiency_module()
198
+ if mod is None:
199
+ return UNKNOWN, False, "efficiency_cost module unreachable"
200
+ try:
201
+ cost, _model = mod.collect_efficiency(loki_dir)
202
+ except Exception as exc:
203
+ return UNKNOWN, False, "efficiency read failed: %s" % (exc,)
204
+ if not isinstance(cost, dict) or not cost.get("available"):
205
+ return UNKNOWN, False, "no measured efficiency record"
206
+ return cost.get("usd"), True, None
207
+
208
+
209
+ # ---------------------------------------------------------------------------
210
+ # status
211
+ # ---------------------------------------------------------------------------
212
+
213
+ def _current_status(loki_dir: str) -> str:
214
+ """Live status of the run occupying .loki/ right now.
215
+
216
+ Precedence copied from the CLI's own status reader (autonomy/loki:4981):
217
+ PAUSE file, then STOP file, then session.json status, then unknown. A
218
+ missing file yields "unknown" and never "completed" -- absence of a signal
219
+ is not evidence of success.
220
+ """
221
+ if os.path.isfile(_p(loki_dir, "PAUSE")):
222
+ return "paused"
223
+ if os.path.isfile(_p(loki_dir, "STOP")):
224
+ return "stopped"
225
+ session = _read_json(_p(loki_dir, _SESSION))
226
+ if isinstance(session, dict):
227
+ status = session.get("status")
228
+ if status:
229
+ return str(status)
230
+ return "unknown"
231
+
232
+
233
+ def _started_at(loki_dir: str, run_id: str, events: dict) -> Optional[str]:
234
+ """Earliest trust-event ts for this run. None when never recorded."""
235
+ stamps = [e.get("ts") for e in events.get(run_id, []) if e.get("ts")]
236
+ return min(stamps) if stamps else None
237
+
238
+
239
+ # ---------------------------------------------------------------------------
240
+ # trust events: the only cross-run source
241
+ # ---------------------------------------------------------------------------
242
+
243
+ def _events_by_run(loki_dir: str) -> dict:
244
+ """{run_id: [event, ...]} from trust-events.jsonl. Missing file -> {}.
245
+
246
+ Malformed lines are skipped rather than failing the whole read: the file
247
+ is appended to by a best-effort writer that can be killed mid-line, and one
248
+ torn tail must not blank every run before it.
249
+ """
250
+ path = _p(loki_dir, *_TRUST_EVENTS)
251
+ by_run: dict = {}
252
+ try:
253
+ with open(path, "r", encoding="utf-8") as fh:
254
+ for raw in fh:
255
+ raw = raw.strip()
256
+ if not raw:
257
+ continue
258
+ try:
259
+ rec = json.loads(raw)
260
+ except Exception:
261
+ continue
262
+ if not isinstance(rec, dict):
263
+ continue
264
+ rid = rec.get("run_id")
265
+ if not rid:
266
+ continue
267
+ by_run.setdefault(str(rid), []).append(rec)
268
+ except OSError:
269
+ return {}
270
+ return by_run
271
+
272
+
273
+ def _iterations_from_events(events: list) -> Optional[int]:
274
+ """Highest iteration seen in this run's events, or None if never recorded.
275
+
276
+ A LOWER BOUND, not a count. Trust events fire on trust events only, so a
277
+ run that did ten iterations and emitted an event on three reports 3. It is
278
+ the floor of what the run demonstrably did, which is why it is combined
279
+ with max() against the other sources rather than trusted alone.
280
+
281
+ Events carry iteration as an int (trust_metrics.record_trust_event coerces
282
+ it). A run whose events all report iteration 0 has no iteration evidence,
283
+ so this reports None rather than claiming the run did zero iterations.
284
+ """
285
+ seen = [e.get("iteration") for e in events
286
+ if isinstance(e.get("iteration"), int) and not isinstance(e.get("iteration"), bool)]
287
+ nonzero = [i for i in seen if i > 0]
288
+ return max(nonzero) if nonzero else None
289
+
290
+
291
+ # ---------------------------------------------------------------------------
292
+ # public API
293
+ # ---------------------------------------------------------------------------
294
+
295
+ def _row(loki_dir: str, run_id: str, is_current: bool, events: dict,
296
+ now: Optional[float] = None) -> dict:
297
+ """One run row. Cost is only ever attached to the CURRENT run.
298
+
299
+ The efficiency directory is wiped at run start, so its records belong to
300
+ whichever run holds .loki/ now. Attributing them to a historical run would
301
+ be a fabricated fact; that run reads cost UNKNOWN with a stated reason.
302
+ """
303
+ run_events = events.get(run_id, [])
304
+ contributing = [_mtime(_p(loki_dir, *_TRUST_EVENTS))]
305
+
306
+ completion = _read_json(_p(loki_dir, *_COMPLETION))
307
+ manifest = _read_json(_p(loki_dir, _MANIFEST))
308
+
309
+ iterations = _iterations_from_events(run_events)
310
+ cost_usd: Any = UNKNOWN
311
+ measured = False
312
+ note = "cost records are wiped at run start; only the current run has them"
313
+
314
+ if is_current:
315
+ cost_usd, measured, cost_note = _cost_for_current_run(loki_dir)
316
+ note = cost_note
317
+ recs = _iteration_records(loki_dir)
318
+ if recs:
319
+ contributing.extend(_mtime(path) for _n, _r, path in recs)
320
+ # The efficiency records are the sharper iteration evidence for the
321
+ # live run; events only see iterations that emitted a trust event.
322
+ #
323
+ # `or None` for the same reason cost is None when unmeasured: a run
324
+ # whose only record is iteration-0 has produced no evidence that any
325
+ # iteration completed, and a rendered "0 iterations" is a plausible
326
+ # -looking fact we did not measure. Every path into `iterations`
327
+ # applies this rule, so the field is never a fabricated zero.
328
+ iterations = max(iterations or 0, max(n for n, _r, _p2 in recs)) or None
329
+ if isinstance(manifest, dict) and isinstance(manifest.get("iterations"), int):
330
+ contributing.append(_mtime(_p(loki_dir, _MANIFEST)))
331
+ iterations = max(iterations or 0, manifest["iterations"]) or None
332
+
333
+ status = _current_status(loki_dir) if is_current else "unknown"
334
+ if isinstance(completion, dict) and completion.get("outcome"):
335
+ # A terminal outcome is the authoritative end state. Only the current
336
+ # run's .loki/ wrote it, so it never labels a historical run.
337
+ if is_current:
338
+ status = str(completion["outcome"])
339
+ contributing.append(_mtime(_p(loki_dir, *_COMPLETION)))
340
+
341
+ return {
342
+ "id": run_id,
343
+ "status": status,
344
+ "started_at": _started_at(loki_dir, run_id, events),
345
+ "iterations": iterations,
346
+ "cost_usd": cost_usd,
347
+ "measured": measured,
348
+ "cost_note": note,
349
+ "current": is_current,
350
+ "source": list(_SOURCE_PATHS),
351
+ "freshness_s": _freshness(contributing, now=now),
352
+ }
353
+
354
+
355
+ def list_runs(loki_dir: str, now: Optional[float] = None) -> dict:
356
+ """Every run discoverable from .loki/, newest first.
357
+
358
+ Returns an ENVELOPE, not a bare list, because the contract requires an
359
+ explicit reason when the result is empty and a list cannot carry one:
360
+
361
+ {"runs": [...], "source": [...], "freshness_s": int|None,
362
+ "reason": None|str}
363
+
364
+ reason is None when runs is non-empty. When runs is empty it states why in
365
+ words -- no .loki, no run id ever minted, and so on. An empty list is never
366
+ padded with a placeholder row.
367
+ """
368
+ envelope = {
369
+ "runs": [],
370
+ "source": list(_SOURCE_PATHS),
371
+ "freshness_s": None,
372
+ "reason": None,
373
+ }
374
+ if not loki_dir or not os.path.isdir(loki_dir):
375
+ envelope["reason"] = "no .loki directory at %s" % (loki_dir,)
376
+ return envelope
377
+
378
+ events = _events_by_run(loki_dir)
379
+ current_id = _read_text(_p(loki_dir, *_RUN_ID_FILE))
380
+
381
+ ids = list(events.keys())
382
+ if current_id and current_id not in ids:
383
+ ids.append(current_id)
384
+
385
+ if not ids:
386
+ envelope["reason"] = (
387
+ "no run id found: .loki/state/trust-run-id is absent and "
388
+ ".loki/metrics/trust-events.jsonl recorded no run_id"
389
+ )
390
+ return envelope
391
+
392
+ rows = [_row(loki_dir, rid, rid == current_id, events, now=now) for rid in ids]
393
+ # Newest first. started_at is None for a run that never recorded one, and
394
+ # those sort last rather than being dropped or dated.
395
+ rows.sort(key=lambda r: (r["started_at"] is not None, r["started_at"] or ""),
396
+ reverse=True)
397
+ envelope["runs"] = rows
398
+ envelope["freshness_s"] = _freshness(
399
+ [_mtime(_p(loki_dir, *_TRUST_EVENTS)), _mtime(_p(loki_dir, *_RUN_ID_FILE))],
400
+ now=now,
401
+ )
402
+ return envelope
403
+
404
+
405
+ def get_run(loki_dir: str, run_id: str, now: Optional[float] = None) -> dict:
406
+ """One run, plus per-iteration detail. Same envelope discipline.
407
+
408
+ {"run": {...}|None, "iterations": [...], "source": [...],
409
+ "freshness_s": int|None, "reason": None|str}
410
+
411
+ Per-iteration detail exists only for the CURRENT run (the records are
412
+ wiped at run start). For any other run `iterations` is empty and `reason`
413
+ says why, rather than returning invented rows.
414
+ """
415
+ envelope = {
416
+ "run": None,
417
+ "iterations": [],
418
+ "source": list(_SOURCE_PATHS),
419
+ "freshness_s": None,
420
+ "reason": None,
421
+ }
422
+ if not loki_dir or not os.path.isdir(loki_dir):
423
+ envelope["reason"] = "no .loki directory at %s" % (loki_dir,)
424
+ return envelope
425
+ if not run_id:
426
+ envelope["reason"] = "no run_id given"
427
+ return envelope
428
+
429
+ events = _events_by_run(loki_dir)
430
+ current_id = _read_text(_p(loki_dir, *_RUN_ID_FILE))
431
+ if run_id not in events and run_id != current_id:
432
+ envelope["reason"] = "run %s not found in .loki" % (run_id,)
433
+ return envelope
434
+
435
+ is_current = run_id == current_id
436
+ row = _row(loki_dir, run_id, is_current, events, now=now)
437
+ envelope["run"] = row
438
+ envelope["freshness_s"] = row["freshness_s"]
439
+
440
+ if not is_current:
441
+ envelope["reason"] = (
442
+ "per-iteration detail unavailable: .loki/metrics/efficiency is "
443
+ "wiped at run start and now belongs to run %s" % (current_id or "unknown",)
444
+ )
445
+ return envelope
446
+
447
+ mod = _efficiency_module()
448
+ recs = _iteration_records(loki_dir)
449
+ if not recs:
450
+ envelope["reason"] = (
451
+ "no per-iteration records at .loki/metrics/efficiency/iteration-*.json"
452
+ )
453
+ return envelope
454
+
455
+ for num, rec, path in recs:
456
+ # Per-iteration measured-ness uses the SAME canonical predicate as the
457
+ # aggregate, so one iteration cannot be called measured by a rule the
458
+ # total disagrees with.
459
+ if mod is None:
460
+ measured = False
461
+ else:
462
+ measured = bool(mod.record_is_measured(rec))
463
+ envelope["iterations"].append({
464
+ "iteration": num,
465
+ "measured": measured,
466
+ "cost_usd": rec.get("cost_usd") if measured else UNKNOWN,
467
+ "input_tokens": rec.get("input_tokens") if measured else UNKNOWN,
468
+ "output_tokens": rec.get("output_tokens") if measured else UNKNOWN,
469
+ "model": rec.get("model") or UNKNOWN,
470
+ "phase": rec.get("phase") or UNKNOWN,
471
+ "status": rec.get("status") or UNKNOWN,
472
+ "duration_ms": rec.get("duration_ms"),
473
+ "timestamp": rec.get("timestamp") or UNKNOWN,
474
+ "source": ".loki/metrics/efficiency/%s" % os.path.basename(path),
475
+ "freshness_s": _freshness([_mtime(path)], now=now),
476
+ })
477
+ return envelope