loki-mode 11.3.1 → 11.3.2

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 (107) hide show
  1. package/SKILL.md +4 -4
  2. package/VERSION +1 -1
  3. package/autonomy/lib/efficiency_cost.py +8 -0
  4. package/autonomy/lib/engine10-push.sh +22 -8
  5. package/autonomy/loki +62 -30
  6. package/bin/loki +30 -1
  7. package/completions/_loki +6 -0
  8. package/completions/loki.bash +2 -2
  9. package/dashboard/__init__.py +1 -1
  10. package/dashboard/server.py +22 -14
  11. package/docs/AGENT-CHANGE-RECEIPT.md +2 -2
  12. package/docs/CLI-REFERENCE.md +5 -1
  13. package/docs/INSTALLATION.md +1 -1
  14. package/docs/OTEL-COLLECTOR.md +24 -0
  15. package/docs/ROUTING-EVIDENCE.md +32 -0
  16. package/docs/environment-variables.md +1 -0
  17. package/docs/spec-first-intent.md +28 -0
  18. package/docs/v10/CLI-MODERN.md +6 -3
  19. package/docs/v10/DECISIONS.md +15 -0
  20. package/docs/v10/FAILURE-CLASSES.md +95 -1
  21. package/docs/v10/GUARDS.md +18 -0
  22. package/docs/v10/GUIDE.md +7 -5
  23. package/docs/v10/METRICS.md +12 -0
  24. package/docs/v10/OPERATING-MODEL.md +3 -3
  25. package/docs/v10/PROGRESS.md +6 -0
  26. package/docs/v11/INTOTO-AGENT-CHANGE-PREDICATE.md +77 -0
  27. package/docs/v11/MCP-2026-07-28.md +55 -0
  28. package/docs/v11/VERIFY-PR-SANDBOX.md +41 -0
  29. package/loki-ts/dist/loki.js +1130 -969
  30. package/mcp/__init__.py +1 -1
  31. package/mcp/v10_tools.py +29 -0
  32. package/package.json +9 -11
  33. package/packages/control-plane/dist/ask-tools-server.js +8 -2
  34. package/packages/control-plane/dist/server.js +15728 -13988
  35. package/packages/control-plane/drizzle/0006_token_sessions.sql +1 -0
  36. package/packages/control-plane/drizzle/meta/0006_snapshot.json +807 -0
  37. package/packages/control-plane/drizzle/meta/_journal.json +7 -0
  38. package/packages/control-plane/src/db/schema.ts +1 -0
  39. package/packages/control-plane/src/server/routes/cost.ts +4 -3
  40. package/packages/control-plane/src/server/runs.ts +3 -3
  41. package/packages/control-plane/ui/dist/assets/index-DbLzr87u.js +190 -0
  42. package/packages/control-plane/ui/dist/index.html +1 -1
  43. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  44. package/providers/opencode.sh +2 -3
  45. package/src/observability/genai-spans.js +104 -0
  46. package/src/observability/otel-bridge.js +49 -1
  47. package/src/observability/otel.js +2 -2
  48. package/src/protocols/mcp-server.js +180 -5
  49. package/src/protocols/transport/sse.js +23 -2
  50. package/web-app/dist/assets/{AdminPage-CMkHNJbF.js → AdminPage-DuRQZsF9.js} +1 -1
  51. package/web-app/dist/assets/{Avatar-BJyjV7Zi.js → Avatar-7GtOQvsO.js} +1 -1
  52. package/web-app/dist/assets/{Badge-D572l0uq.js → Badge-CYgTnIxj.js} +1 -1
  53. package/web-app/dist/assets/{Button-T4BnOZgl.js → Button-rYSyIu7X.js} +1 -1
  54. package/web-app/dist/assets/{CockpitPage-Cj_GFsNn.js → CockpitPage-9adANY4o.js} +1 -1
  55. package/web-app/dist/assets/{ComparePage-A8m4ZXak.js → ComparePage-DEG2oA-R.js} +1 -1
  56. package/web-app/dist/assets/{ErrorBoundary-hugCg5yg.js → ErrorBoundary-CwtvY4xs.js} +1 -1
  57. package/web-app/dist/assets/{EvidenceReceiptPanel-CuJKDC2G.js → EvidenceReceiptPanel-DTT73pPB.js} +1 -1
  58. package/web-app/dist/assets/{GitHubIssuesPanel-BYGKjarC.js → GitHubIssuesPanel-CpWZLz2x.js} +1 -1
  59. package/web-app/dist/assets/{GitHubPRsPanel-c7sFIJlf.js → GitHubPRsPanel-H293R7E2.js} +1 -1
  60. package/web-app/dist/assets/{HomePage-A1MIkG2P.js → HomePage-B11Imp9k.js} +2 -2
  61. package/web-app/dist/assets/{LoginPage-8KQMRhtR.js → LoginPage-BZBgx5Jr.js} +1 -1
  62. package/web-app/dist/assets/{MagicPage-C05QW2Lx.js → MagicPage-B27S4_X8.js} +1 -1
  63. package/web-app/dist/assets/{MetricsPage-Oukv5hme.js → MetricsPage-CfbJWNDG.js} +1 -1
  64. package/web-app/dist/assets/{NotFoundPage-DKIwsawz.js → NotFoundPage-Tp_4zMSm.js} +1 -1
  65. package/web-app/dist/assets/{ProjectPage-DwFRPLCG.js → ProjectPage-CM4fgMsV.js} +3 -3
  66. package/web-app/dist/assets/{ProjectsPage-CgY4YYDX.js → ProjectsPage-DP2Fvsrd.js} +1 -1
  67. package/web-app/dist/assets/{SettingsPage-BpfcSjUA.js → SettingsPage-C4G9-EAy.js} +1 -1
  68. package/web-app/dist/assets/{ShowcasePage-BqBakC4K.js → ShowcasePage-Dj-n4dwu.js} +1 -1
  69. package/web-app/dist/assets/{SystemSettingsPage-CZLu5VB0.js → SystemSettingsPage-BqMLpuGt.js} +1 -1
  70. package/web-app/dist/assets/{TeamsPage-D55S1jKz.js → TeamsPage-mIH8H96m.js} +1 -1
  71. package/web-app/dist/assets/{TemplatesPage-D3SydvxN.js → TemplatesPage-BagW-v4n.js} +1 -1
  72. package/web-app/dist/assets/{TerminalOutput-hNg8G1-e.js → TerminalOutput-CTQRHhnK.js} +1 -1
  73. package/web-app/dist/assets/{activity-D2C0VagO.js → activity-DNQiBDDL.js} +1 -1
  74. package/web-app/dist/assets/{bell-CMvYi36w.js → bell-Coiwdad1.js} +1 -1
  75. package/web-app/dist/assets/{bot-BvtQ6lmo.js → bot-Cou-rTgR.js} +1 -1
  76. package/web-app/dist/assets/{check-B13xkXed.js → check-CsF-XtQp.js} +1 -1
  77. package/web-app/dist/assets/{chevron-left-emu6r49E.js → chevron-left-BJiy55Ht.js} +1 -1
  78. package/web-app/dist/assets/{circle-alert-CExlwjuy.js → circle-alert-D6-pQQWq.js} +1 -1
  79. package/web-app/dist/assets/{clock-D7Uas3oL.js → clock-BOGqwgt9.js} +1 -1
  80. package/web-app/dist/assets/{cloud-CkB4S86C.js → cloud-DsxhTtf5.js} +1 -1
  81. package/web-app/dist/assets/{code-xml-SL9IIWIn.js → code-xml-CxJDHUlE.js} +1 -1
  82. package/web-app/dist/assets/{database-BQFkd7t1.js → database-BJePscl_.js} +1 -1
  83. package/web-app/dist/assets/{dollar-sign-qHmnNYsU.js → dollar-sign-6qWGAkZo.js} +1 -1
  84. package/web-app/dist/assets/{file-code-corner-C2YUDP0K.js → file-code-corner-DcOtYeDb.js} +1 -1
  85. package/web-app/dist/assets/{file-plus-C_ooIkDe.js → file-plus-WuRyLFo0.js} +1 -1
  86. package/web-app/dist/assets/{globe-CQ-GL485.js → globe-r-xCP4DE.js} +1 -1
  87. package/web-app/dist/assets/{hammer-U05L_D7V.js → hammer-DTt0SsCB.js} +1 -1
  88. package/web-app/dist/assets/{index-CooHd7I-.js → index-BkbXyH8y.js} +3 -3
  89. package/web-app/dist/assets/{layers-Buo17rjW.js → layers-BvZhvSMY.js} +1 -1
  90. package/web-app/dist/assets/{loader-circle-15vK3oV1.js → loader-circle-3gosTei9.js} +1 -1
  91. package/web-app/dist/assets/{lock-BeXLebuJ.js → lock-CZZMxP0U.js} +1 -1
  92. package/web-app/dist/assets/{package-MVh_6-6R.js → package-C5A8CkJr.js} +1 -1
  93. package/web-app/dist/assets/{plus-Dh_nd9vS.js → plus-Btv7tTIF.js} +1 -1
  94. package/web-app/dist/assets/{refresh-cw-C9cBJEAT.js → refresh-cw-DsWLXgs_.js} +1 -1
  95. package/web-app/dist/assets/{rotate-ccw-D3PAtzST.js → rotate-ccw-DOVDjway.js} +1 -1
  96. package/web-app/dist/assets/{scroll-text-DZCoXQjo.js → scroll-text-smySzlrX.js} +1 -1
  97. package/web-app/dist/assets/{server-OCOLI5J_.js → server-b5PHsqAU.js} +1 -1
  98. package/web-app/dist/assets/{shield-alert-oVhmf1_h.js → shield-alert-BD7FDlh-.js} +1 -1
  99. package/web-app/dist/assets/{thumbs-up-B-kbX4vN.js → thumbs-up-CAsFzbyE.js} +1 -1
  100. package/web-app/dist/assets/{trash-2-EL6moveW.js → trash-2-BJ35qHgl.js} +1 -1
  101. package/web-app/dist/assets/{trending-down-Cl3kjpsx.js → trending-down-Ozrd26cZ.js} +1 -1
  102. package/web-app/dist/assets/{trending-up-DST7I6M2.js → trending-up-BhnMcM32.js} +1 -1
  103. package/web-app/dist/assets/{upload-CUOT780G.js → upload-JVHfdwoA.js} +1 -1
  104. package/web-app/dist/assets/{usePolling-Djmt90eb.js → usePolling-CTzbDV-e.js} +1 -1
  105. package/web-app/dist/assets/{user-BRBohA6J.js → user-Bks3-GYv.js} +1 -1
  106. package/web-app/dist/index.html +1 -1
  107. package/packages/control-plane/ui/dist/assets/index-DcBJXjCJ.js +0 -190
package/SKILL.md CHANGED
@@ -3,9 +3,9 @@ name: loki-mode
3
3
  description: An autonomous software factory that knows what it is supposed to deliver, and proves it did. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
4
4
  ---
5
5
 
6
- # Loki Mode v11.3.1
6
+ # Loki Mode v11.3.2
7
7
 
8
- **Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the legacy engine, which still ships and is being removed (`loki start owner/repo#N` still routes to it; prefer `loki owner/repo#N`).
8
+ **Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
9
9
 
10
10
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
11
11
 
@@ -200,7 +200,7 @@ This protocol governs **skill module** loading -- task-scoped instruction files
200
200
  <!-- generated:commands -->
201
201
  Commands (generated from `loki-ts/src/cli/registry.ts`; full reference in `docs/CLI-REFERENCE.md`):
202
202
 
203
- `version`, `status`, `doctor`, `provider`, `memory`, `rollback`, `proof`, `wiki`, `control`, `kpis`, `report`, `trust`, `crash`, `contract`, `start`, `slack`, `queue`, `answer`, `engine10`, `help`, `quick`, `quickstart`, `init`, `template`, `verify`, `keys`, `review`, `dashboard`, `config`, `mcp`, `acp`, `stop`, `pause`, `resume`, `why`, `next`, `logs`, `ship`, `deploy`, `import`, `github`, `issue`, `ci`, `modernize`, `share`, `assets`, `export`, `notify`, `tour`, `welcome`, `onboard`, `setup-skill`, `self-update`, `remote`, `cockpit`, `code`, `context`, `secrets`, `api`, `sandbox`, `docker`, `web`, `preview`, `telemetry`, `syslog`, `explain`, `docs`, `test`, `bench`, `voice`, `own`, `secure`, `compliance`, `enterprise`, `projects`, `audit`, `cost`, `metrics`, `sentrux`, `magic`
203
+ `version`, `status`, `doctor`, `provider`, `memory`, `rollback`, `proof`, `wiki`, `control`, `kpis`, `report`, `trust`, `crash`, `contract`, `start`, `slack`, `queue`, `issues`, `undo`, `verify-pr`, `answer`, `engine10`, `help`, `quick`, `quickstart`, `init`, `template`, `verify`, `keys`, `review`, `dashboard`, `config`, `mcp`, `acp`, `stop`, `pause`, `resume`, `why`, `next`, `logs`, `ship`, `deploy`, `import`, `github`, `issue`, `ci`, `modernize`, `share`, `assets`, `export`, `notify`, `tour`, `welcome`, `onboard`, `setup-skill`, `self-update`, `remote`, `cockpit`, `code`, `context`, `secrets`, `api`, `sandbox`, `docker`, `web`, `preview`, `telemetry`, `syslog`, `explain`, `docs`, `test`, `bench`, `voice`, `own`, `secure`, `compliance`, `enterprise`, `projects`, `audit`, `cost`, `metrics`, `sentrux`, `magic`
204
204
  <!-- /generated -->
205
205
 
206
206
  ---
@@ -483,4 +483,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
483
483
 
484
484
  ---
485
485
 
486
- **v11.3.1 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
486
+ **v11.3.2 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 11.3.1
1
+ 11.3.2
@@ -192,6 +192,9 @@ def collect_efficiency(loki_dir):
192
192
  # real observation, and nulling it would be its own dishonesty (existing
193
193
  # contract: test_genuine_zero_cost_stays_zero_not_null). What is unknown is a
194
194
  # record that never carried the field at all.
195
+ # FC-44: a record flagged tokens_measured:false (an ambiguous resumed session) carries no usage keys. One of them makes every
196
+ # token total NOT RECORDED (None); the flag keys are added only then, so a complete run's dict is unchanged.
197
+ _unmeasured_tokens = sum(1 for r in _records if r.get("tokens_measured") is False)
195
198
  _any_cost = any("cost_usd" in r for r in _records)
196
199
  _missing_cost = sum(1 for r in _records if "cost_usd" not in r)
197
200
  if collected and _observed:
@@ -208,6 +211,11 @@ def collect_efficiency(loki_dir):
208
211
  # Some records priced, others not: the total is a LOWER BOUND.
209
212
  cost["cost_partial"] = _missing_cost > 0
210
213
  cost["cost_unpriced_records"] = _missing_cost
214
+ if _unmeasured_tokens:
215
+ for key in ("input_tokens", "output_tokens", "cache_read_tokens", "cache_creation_tokens"):
216
+ cost[key] = None
217
+ cost["tokens_partial"] = True
218
+ cost["tokens_unmeasured_records"] = _unmeasured_tokens
211
219
  else:
212
220
  # No record means unavailable, never an observed zero.
213
221
  for key in (
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env bash
2
2
  # Loki 10 engine P4 push child (ENGINE.md sections 6 and 7).
3
3
  #
4
- # engine10-push.sh push-pr <repo-dir> <branch> <title> <body-file> [--draft]
4
+ # engine10-push.sh push-pr <repo-dir> <branch> <title> <body-file> [--draft] [--base <branch>]
5
+ # engine10-push.sh check-origin (exit 0 when the pinned origin is acceptable to push-pr, else the refusal; no network)
5
6
  # engine10-push.sh comment <pr-number> <body-file>
6
7
  # engine10-push.sh issue-comment <issue-ref> <body-file>
7
8
  # engine10-push.sh status <sha> <pending|success|failure|error> <description>
@@ -111,13 +112,26 @@ _e10_gh() { _loki_with_github_tokens _loki_run_neutral "$_e10_repo" command gh "
111
112
  mode="${1:-}"
112
113
  shift || true
113
114
  case "$mode" in
115
+ check-origin)
116
+ # The origin validation above already ran and died on a refusal; reaching here means push-pr would accept it.
117
+ exit 0
118
+ ;;
114
119
  push-pr)
115
- [ "$#" -ge 4 ] && [ "$#" -le 5 ] || die "usage: push-pr <repo-dir> <branch> <title> <body-file> [--draft]"
116
- dir="$1" branch="$2" title="$3" body="$4" draft=()
117
- if [ "$#" -eq 5 ]; then
118
- [ "$5" = "--draft" ] || die "unknown flag: $5"
119
- draft=(--draft)
120
- fi
120
+ [ "$#" -ge 4 ] && [ "$#" -le 7 ] || die "usage: push-pr <repo-dir> <branch> <title> <body-file> [--draft] [--base <branch>]"
121
+ dir="$1" branch="$2" title="$3" body="$4" draft=() base=()
122
+ shift 4
123
+ # MASS-2: --base stacks the PR on a parent slice's branch; a name git would not accept is refused.
124
+ while [ "$#" -gt 0 ]; do
125
+ case "$1" in
126
+ --draft) draft=(--draft); shift ;;
127
+ --base)
128
+ [ "$#" -ge 2 ] || die "--base needs a branch"
129
+ case "$2" in -* | '') die "invalid base branch: $2" ;; esac
130
+ git check-ref-format --branch "$2" >/dev/null 2>&1 || die "invalid base branch: $2"
131
+ base=(--base "$2"); shift 2 ;;
132
+ *) die "unknown flag: $1" ;;
133
+ esac
134
+ done
121
135
  [ -f "$body" ] || die "body file not found: $body"
122
136
  if [ -n "$_e10_local" ]; then
123
137
  _e10_local_push "$dir" "$branch" || die "push refused or failed (rc=$?)"
@@ -128,7 +142,7 @@ case "$mode" in
128
142
  url="$(_e10_gh pr list --repo "$_e10_repo" --head "$branch" --state open --json url --jq '.[0].url')" \
129
143
  || die "gh pr list failed"
130
144
  if [ -z "$url" ] || [ "$url" = "null" ]; then
131
- url="$(_e10_gh pr create --repo "$_e10_repo" --head "$branch" --title "$title" --body-file "$body" "${draft[@]}")" \
145
+ url="$(_e10_gh pr create --repo "$_e10_repo" --head "$branch" --title "$title" --body-file "$body" "${draft[@]}" "${base[@]}")" \
132
146
  || die "gh pr create failed"
133
147
  url="$(printf '%s\n' "$url" | tail -n 1)"
134
148
  fi
package/autonomy/loki CHANGED
@@ -1691,6 +1691,22 @@ _help_answer() {
1691
1691
  echo "and sets LOKI_NO_BROWSER=1."
1692
1692
  }
1693
1693
 
1694
+ # `loki help verify-pr`: a Bun-only command (loki-ts), so its help lives here.
1695
+ _help_verify_pr() {
1696
+ echo -e "${BOLD}loki verify-pr${NC} - Verify a PR against its linked issue (experimental, LOKI_VERIFY_PR=1)"
1697
+ echo ""
1698
+ echo "Usage: loki verify-pr <https://github.com/o/r/pull/N | o/r#N> [--out DIR]"
1699
+ echo ""
1700
+ echo "Derives a check from the PR's linked issue (Check: \`cmd\` or a loki-check fence)."
1701
+ echo "VERIFIED only if the check fails on the PR base, passes on its head, and the"
1702
+ echo "touched packages' suites pass. A PR with no linked issue, no check, a removed"
1703
+ echo "test or a non-discriminating check is NOT PROVEN. Writes verify-pr-result.json."
1704
+ echo "PR code runs only in the container sandbox (scripts/verify-pr-sandbox.sh); set"
1705
+ echo "LOKI_VPR_IMAGE to a locally present image."
1706
+ echo ""
1707
+ echo "Exit codes: 0 verified, 2 usage or sandbox blocked, 4 not proven."
1708
+ }
1709
+
1694
1710
  # Appended to `loki help verify`: the v10 DSSE export flag (docs/exit-codes.md).
1695
1711
  _help_verify_dsse() {
1696
1712
  echo ""
@@ -1763,6 +1779,7 @@ show_help() {
1763
1779
  echo " review [opts] Standalone code review with quality gates"
1764
1780
  echo " ultracode \"task\" Run a task as a native Claude Code Dynamic Workflow (opt-in, Claude-only)"
1765
1781
  echo " trust [cmd] Visible trust trajectory; 'trust detail' for metrics"
1782
+ echo " undo <run> [--plan] Plan or apply (LOKI_UNDO=1) an undo of a run; verifies the receipt"
1766
1783
  echo ""
1767
1784
  echo "Observe:"
1768
1785
  echo " dashboard [cmd] Operations UI server (start|stop|status|url|open)"
@@ -1798,13 +1815,9 @@ show_help() {
1798
1815
  echo ""
1799
1816
  echo "Options for 'start':"
1800
1817
  echo " --provider NAME AI provider: claude (default), codex, cline, aider, opencode"
1801
- echo " --parallel Enable parallel mode with git worktrees"
1802
- echo " --bg, --background Run in background mode"
1803
1818
  echo " --simple Force simple complexity tier (3 phases)"
1804
1819
  echo " --complex Force complex complexity tier (8 phases)"
1805
- echo " --github Enable GitHub issue import"
1806
1820
  echo " --no-dashboard Disable web dashboard"
1807
- echo " --sandbox Run in Docker sandbox for isolation (default: off; requires Docker)"
1808
1821
  echo " --isolation LVL Per-run isolation dial: none (default) | worktree | docker"
1809
1822
  echo " --skip-memory Skip loading memory context at startup"
1810
1823
  echo " --fresh-prd Regenerate the PRD from the codebase (no-PRD runs; ignores the reusable generated PRD)"
@@ -1824,8 +1837,6 @@ show_help() {
1824
1837
  echo " loki start ./prd.md # Start with PRD file"
1825
1838
  echo " loki start owner/repo#123 # GitHub issue (auto-fetches)"
1826
1839
  echo " loki start PROJ-456 # Jira issue"
1827
- echo " loki start --bg ./prd.md # Start in background"
1828
- echo " loki start --parallel ./prd.md # Parallel mode (git worktrees)"
1829
1840
  echo " loki quick \"add dark mode\" # Single-task mode (Loki 10 engine)"
1830
1841
  echo " loki tour # See a real sample result (no provider, no key, no spend)"
1831
1842
  echo " loki demo # Build a sample todo app end to end"
@@ -1898,7 +1909,7 @@ show_help() {
1898
1909
  echo " merge resume review rollback run sandbox secrets secure self-update sentrux"
1899
1910
  echo " serve setup-skill share ship spec start state stats status steer stop"
1900
1911
  echo " syslog telemetry template test tour trigger trust trust-metrics"
1901
- echo " ultracode update verify version voice watch watchdog web welcome why"
1912
+ echo " ultracode undo update verify version voice watch watchdog web welcome why"
1902
1913
  echo " wiki workspace worktree (wt)"
1903
1914
  echo " control"
1904
1915
  echo ""
@@ -2563,17 +2574,13 @@ cmd_start() {
2563
2574
  echo " Token from LOKI_REMOTE_TOKEN / LOKI_REMOTE_TOKEN_FILE"
2564
2575
  echo " (never an argument). Env: LOKI_REMOTE_URL."
2565
2576
  echo " --provider NAME AI provider: claude (default), codex, cline, aider, opencode"
2566
- echo " --parallel Enable parallel mode with git worktrees"
2567
2577
  echo " --allow-haiku Enable Haiku model for the fast tier (default: disabled)"
2568
- echo " --bg, --background Run in background mode"
2569
2578
  echo " --simple Force simple complexity tier (3 phases)"
2570
2579
  echo " --complex Force complex complexity tier (8 phases)"
2571
- echo " --github Enable GitHub issue import"
2572
2580
  echo " --no-dashboard Disable web dashboard"
2573
2581
  echo " --api Start dashboard API server alongside the build"
2574
- echo " --sandbox Run in Docker sandbox (default: off; requires Docker)"
2575
2582
  echo " --isolation LEVEL Per-run isolation: none (default) | worktree | docker."
2576
- echo " Unified dial; --parallel = worktree, --sandbox = docker."
2583
+ echo " Unified dial."
2577
2584
  echo " --attempts N Run N (1-5, default 1) independent attempts in separate git worktrees;"
2578
2585
  echo " the most executed passing checks wins, losers are recorded on the receipt"
2579
2586
  echo " --skip-memory Skip loading memory context at startup"
@@ -2608,7 +2615,6 @@ cmd_start() {
2608
2615
  echo " --worktree, -w Git worktree isolation (separate branch)"
2609
2616
  echo " --pr Worktree + auto-create PR"
2610
2617
  echo " --ship Worktree + PR + auto-merge"
2611
- echo " --detach, -d Run in background"
2612
2618
  echo ""
2613
2619
  echo "Preview without spending (any input mode):"
2614
2620
  echo " loki plan <PRD> Analyze a spec WITHOUT executing: complexity,"
@@ -2637,7 +2643,6 @@ cmd_start() {
2637
2643
  echo " loki start 123 # ISSUE mode (current repo GitHub issue)"
2638
2644
  echo " loki start PROJ-456 # ISSUE mode (Jira)"
2639
2645
  echo " loki start owner/repo#789 # ISSUE mode (GitHub specific repo)"
2640
- echo " loki start --prd ./prd.md --parallel # Explicit PRD, parallel mode"
2641
2646
  echo " loki start --issue 123 --ship # Explicit issue, full automation"
2642
2647
  echo " loki start --provider codex # Use OpenAI Codex CLI"
2643
2648
  echo " loki start --bmad-project ./my-project # Start from BMAD artifacts"
@@ -6424,22 +6429,35 @@ except Exception:
6424
6429
  fi
6425
6430
  fi
6426
6431
 
6427
- # v7.82: completion-aware one-liner. When a finished run left a completion
6428
- # record, show its outcome and point at \`loki why\` for the full story. Reads
6429
- # the persisted completion.json (no recompute); silent when absent.
6430
- if [ -f "$LOKI_DIR/state/completion.json" ]; then
6431
- local last_outcome
6432
- last_outcome=$(_LOKI_CJ="$LOKI_DIR/state/completion.json" python3 -c "
6433
- import json, os
6432
+ # 133-F4: newest sealed receipt's verdict, read from the receipt field only
6433
+ # (never recomputed). Missing, empty or unparseable receipts never read as
6434
+ # VERIFIED. This replaces the old completion.json one-liner (Bun never had it).
6435
+ local receipt_line
6436
+ receipt_line=$(_LOKI_RUNS="$LOKI_DIR/runs" python3 -c "
6437
+ import json, os, re
6438
+ runs = os.environ['_LOKI_RUNS']
6434
6439
  try:
6435
- d = json.load(open(os.environ['_LOKI_CJ']))
6436
- print(d.get('outcome', '') or '')
6440
+ ids = sorted(d for d in os.listdir(runs) if os.path.isdir(os.path.join(runs, d)))
6437
6441
  except Exception:
6438
- print('')" 2>/dev/null)
6439
- if [ -n "$last_outcome" ]; then
6440
- echo -e "${DIM}Last run: ${last_outcome} - run 'loki why' for details${NC}"
6441
- fi
6442
- fi
6442
+ ids = []
6443
+ if not ids:
6444
+ print('Last run: no receipt (verdict NOT AVAILABLE)')
6445
+ else:
6446
+ try:
6447
+ d = json.load(open(os.path.join(runs, ids[-1], 'receipt.json')))
6448
+ v = d['verdict']
6449
+ h = d['receipt_sha256']
6450
+ if not (isinstance(v, str) and re.fullmatch(r'[A-Z_]+', v) and isinstance(h, str)):
6451
+ raise ValueError
6452
+ h = h[7:] if h.startswith('sha256:') else h
6453
+ if not re.fullmatch(r'[0-9a-f]{12,}', h):
6454
+ raise ValueError
6455
+ print('Last run: %s (receipt sha256:%s)' % (v, h[:12]))
6456
+ except Exception:
6457
+ print('Last run: receipt unreadable')
6458
+ " 2>/dev/null)
6459
+ [ -n "$receipt_line" ] || receipt_line="Last run: receipt unreadable"
6460
+ echo -e "${DIM}${receipt_line}${NC}"
6443
6461
 
6444
6462
  echo ""
6445
6463
  echo -e "${DIM} Tip: loki analyze context show - detailed token breakdown${NC}"
@@ -12394,6 +12412,9 @@ cmd_export() {
12394
12412
  echo " csv Task queue as CSV"
12395
12413
  echo " timeline Iteration timeline as JSON"
12396
12414
  echo ""
12415
+ echo "Flags:"
12416
+ echo " --sarif [run-id] Write .loki/runs/<id>/findings.sarif (SARIF 2.1.0; needs bun)"
12417
+ echo ""
12397
12418
  echo "Examples:"
12398
12419
  echo " loki export json # Print to stdout"
12399
12420
  echo " loki export json session-export.json # Save to file"
@@ -12416,6 +12437,10 @@ cmd_export() {
12416
12437
  require_jq || return 1
12417
12438
  _export_timeline "$output_path"
12418
12439
  ;;
12440
+ --sarif)
12441
+ echo -e "${RED}loki export --sarif needs bun (https://bun.sh).${NC}" >&2
12442
+ return 1
12443
+ ;;
12419
12444
  *)
12420
12445
  echo -e "${RED}Unknown format: $format${NC}"
12421
12446
  echo "Supported: json, markdown, csv, timeline"
@@ -21201,6 +21226,11 @@ main() {
21201
21226
  mcp)
21202
21227
  cmd_mcp "$@"
21203
21228
  ;;
21229
+ undo)
21230
+ # UNDO-1: Bun-only (verifies the receipt with the Bun verifier); bin/loki routes here only when bun is absent.
21231
+ echo "loki undo needs the Bun runtime (install bun, then re-run: loki undo <run-id> --plan)" >&2
21232
+ exit 2
21233
+ ;;
21204
21234
  acp)
21205
21235
  # ACP agent over stdio; stdout is protocol-only. See docs/acp-editors.md.
21206
21236
  LOKI_ACP_LOKI="${LOKI_ACP_LOKI:-$_LOKI_SCRIPT_DIR/loki}" exec python3 "$_LOKI_SCRIPT_DIR/acp-agent.py"
@@ -21413,6 +21443,8 @@ main() {
21413
21443
  show_help
21414
21444
  elif [ "$_help_target" = "answer" ]; then
21415
21445
  _help_answer
21446
+ elif [ "$_help_target" = "verify-pr" ]; then
21447
+ _help_verify_pr
21416
21448
  else
21417
21449
  LOKI_HELP_ONLY=1 "$0" "$_help_target" --help "$@"
21418
21450
  local _help_rc=$?
@@ -30164,7 +30196,7 @@ except: pass
30164
30196
 
30165
30197
  # Detect shell scripts
30166
30198
  local shell_count=0
30167
- shell_count=$(find "$target_path" -maxdepth 2 -name "*.sh" -type f 2>/dev/null | wc -l | tr -d ' ')
30199
+ shell_count=$(find "$target_path" -maxdepth 2 -name "*.sh" -type f 2>/dev/null | wc -l | tr -d ' ' || true)
30168
30200
  if [ "$shell_count" -gt 0 ]; then
30169
30201
  languages="$languages Bash"
30170
30202
  fi
@@ -30602,7 +30634,7 @@ Files: $src_count source, $test_count test, $doc_count docs"
30602
30634
  if [ -d "$target_path/$dir" ]; then
30603
30635
  # Count files in directory
30604
30636
  local dir_count
30605
- dir_count=$(echo "$tree_output" | grep "^${dir}/" | wc -l | tr -d ' ')
30637
+ dir_count=$(echo "$tree_output" | grep "^${dir}/" | wc -l | tr -d ' ' || true)
30606
30638
  output="$output
30607
30639
  $dir/ ($dir_count files)"
30608
30640
  else
package/bin/loki CHANGED
@@ -467,6 +467,21 @@ if [ "${1:-}" = "start" ]; then
467
467
  unset _loki_att_args _loki_att_n _loki_att_skip _loki_att_a
468
468
  fi
469
469
 
470
+ # SPEC-FIRST-INTENT: `start --spec FILE` and `start --spec-first` are parsed by the Bun start command,
471
+ # which forwards them to engine10; they never reach the positional-task route or the legacy loop.
472
+ if [ "${1:-}" = "start" ]; then
473
+ for _loki_sp_a in "${@:2}"; do
474
+ case "$_loki_sp_a" in
475
+ --spec|--spec=*|--spec-first)
476
+ _lb=$(_loki_bun)
477
+ [ -n "$_lb" ] || { echo "loki: --spec needs bun (install from https://bun.sh)." >&2; exit 1; }
478
+ exec "$_lb" "$BUN_CLI" "$@"
479
+ ;;
480
+ esac
481
+ done
482
+ unset _loki_sp_a
483
+ fi
484
+
470
485
  # Loki 10 engine (D29, the only engine from 11.0.0). Routes `loki "<task>"`,
471
486
  # `loki owner/repo#N`, `loki quick "<task>"`, `keys` and `verify --pubkey` to engine10.
472
487
  # shellcheck disable=SC2194
@@ -477,6 +492,13 @@ case "v10" in
477
492
  _e10_args=("$@")
478
493
  case "${1:-}" in
479
494
  keys) _e10=1; _e10_hard=1 ;;
495
+ # SARIF-2: `export --sarif` has no bash implementation; it runs on bun only.
496
+ export)
497
+ for _sa in "$@"; do
498
+ case "$_sa" in --sarif) _e10=1; _e10_hard=1; _e10_args=(export-sarif "${@:2}") ;; esac
499
+ done
500
+ unset _sa
501
+ ;;
480
502
  status|verify|dashboard)
481
503
  if [ "$1" = "verify" ]; then
482
504
  for _pk in "$@"; do
@@ -495,6 +517,13 @@ case "v10" in
495
517
  unset _r10 _p10 _d
496
518
  fi
497
519
  ;;
520
+ # SPEC-FIRST-INTENT: only `plan ... --spec` is a v10 command; every other plan call stays on the legacy CLI.
521
+ plan)
522
+ for _pa in "$@"; do
523
+ [ "$_pa" = "--spec" ] && { _e10=1; _e10_hard=1; }
524
+ done
525
+ unset _pa
526
+ ;;
498
527
  ""|-*) _e10=0 ;;
499
528
  # quick: the lean small path, no PR (legacy quick never opened one). A flag-first
500
529
  # call (--help, -v) stays on the legacy quick.
@@ -710,7 +739,7 @@ fi
710
739
  # Two-token routes (provider show/list, memory list/index) match on the first
711
740
  # token only; the Bun dispatcher handles subcommand routing internally.
712
741
  case "${1:-}" in
713
- version|--version|-v|status|stats|doctor|provider|memory|rollback|internal|kpis|trust|proof|receipt|wiki|crash|control|contract|slack|queue|answer|completion|__complete)
742
+ version|--version|-v|status|stats|doctor|provider|memory|rollback|internal|kpis|trust|proof|receipt|wiki|crash|control|contract|slack|queue|issues|answer|undo|verify-pr|completion|__complete)
714
743
  # v7.5.2: rollback added (wires loki-ts/src/commands/rollback.ts).
715
744
  # v7.5.3: internal added for autonomy/run.sh phase1-hooks calls.
716
745
  # v7.5.28: kpis added (Phase K MVP: read-only KPI snapshot).
package/completions/_loki CHANGED
@@ -209,7 +209,10 @@ function _loki_commands {
209
209
  'otel:OpenTelemetry management'
210
210
  'wiki:Wiki commands'
211
211
  'answer:Resume a BLOCKED run with an answer'
212
+ 'verify-pr:Verify a PR against its linked issue in a sandbox'
212
213
  'queue:Overnight issue queue with a morning digest'
214
+ 'issues:Run every open issue in a repo and open PRs'
215
+ 'undo:Show what undoing a run would do (read-only plan)'
213
216
  'control:Control plane (preview, LOKI_CONTROL=1)'
214
217
  'workspace:Multi-project workspaces (preview, LOKI_WORKSPACES=1)'
215
218
  'backlog:Run every matching open issue, N in parallel'
@@ -337,6 +340,9 @@ function _loki_memory {
337
340
  'pattern:Manage patterns'
338
341
  'skill:Manage skills'
339
342
  'vectors:Vector search'
343
+ 'lessons:List PR lessons (--json)'
344
+ 'learn:Learn from a merged PR'
345
+ 'forget:Delete a PR lesson'
340
346
  'help:Memory help'
341
347
  )
342
348
  _describe -t cmds 'memory subcommand' cmds
@@ -5,7 +5,7 @@ _loki_completion() {
5
5
  _init_completion || return
6
6
 
7
7
  # Main subcommands (must match autonomy/loki main case statement)
8
- local main_commands="start backlog quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash control workspace open otel preview quickstart rc rollback merge self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help keys answer queue acp"
8
+ local main_commands="start backlog quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash control workspace open otel preview quickstart rc rollback merge self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help keys answer queue issues undo acp verify-pr"
9
9
 
10
10
  # 1. If we are on the first argument (subcommand)
11
11
  if [[ $cword -eq 1 ]]; then
@@ -57,7 +57,7 @@ _loki_completion() {
57
57
  ;;
58
58
 
59
59
  memory)
60
- local memory_cmds="list show search stats export clear dedupe index timeline consolidate economics retrieve episode pattern skill vectors help"
60
+ local memory_cmds="list show search stats export clear dedupe index timeline consolidate economics retrieve lessons learn forget episode pattern skill vectors help"
61
61
  COMPREPLY=( $(compgen -W "${memory_cmds}" -- "$cur") )
62
62
  ;;
63
63
 
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "11.3.1"
10
+ __version__ = "11.3.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -7889,6 +7889,9 @@ def _compute_cost_snapshot() -> dict:
7889
7889
  cost_recorded = False
7890
7890
  # Tokens were measured (tracker fallback) but no USD figure was recorded.
7891
7891
  cost_unknown = False
7892
+ # FC-44: sessions whose usage could not be recorded (efficiency record flagged tokens_measured:false). Any of them makes
7893
+ # every token and dollar total NOT RECORDED; the response key appears only then.
7894
+ unmeasured_sessions = 0
7892
7895
 
7893
7896
  # Read efficiency files (one JSON file per iteration/task).
7894
7897
  # Use the iteration-*.json pattern so this reader sees the same
@@ -7906,6 +7909,9 @@ def _compute_cost_snapshot() -> dict:
7906
7909
  if not isinstance(data, dict):
7907
7910
  continue
7908
7911
  measured = _record_is_measured(data)
7912
+ rec_unmeasured = data.get("tokens_measured") is False
7913
+ if rec_unmeasured:
7914
+ unmeasured_sessions += 1
7909
7915
  if measured:
7910
7916
  cost_recorded = True
7911
7917
  # Tokens are measured apart from cost: a record that priced an
@@ -7941,17 +7947,18 @@ def _compute_cost_snapshot() -> dict:
7941
7947
  for bucket, name in ((by_phase, phase), (by_model, model)):
7942
7948
  if name not in bucket:
7943
7949
  bucket[name] = {"input_tokens": 0, "output_tokens": 0, "cost_usd": 0.0,
7944
- "measured": False, "tokens_measured": False}
7950
+ "measured": False, "tokens_measured": False, "unmeasured": False}
7945
7951
  bucket[name]["input_tokens"] += inp
7946
7952
  bucket[name]["output_tokens"] += out
7947
7953
  bucket[name]["cost_usd"] += cost
7948
7954
  bucket[name]["measured"] = bucket[name]["measured"] or measured
7949
7955
  bucket[name]["tokens_measured"] = bucket[name]["tokens_measured"] or tokens_measured
7956
+ bucket[name]["unmeasured"] = bucket[name]["unmeasured"] or rec_unmeasured
7950
7957
  except (json.JSONDecodeError, KeyError, TypeError, AttributeError):
7951
7958
  pass
7952
7959
 
7953
7960
  # Fallback: read from context tracking if efficiency files have no token data
7954
- if total_input == 0 and total_output == 0:
7961
+ if total_input == 0 and total_output == 0 and not unmeasured_sessions:
7955
7962
  ctx_file = loki_dir / "context" / "tracking.json"
7956
7963
  if ctx_file.exists():
7957
7964
  try:
@@ -8024,25 +8031,26 @@ def _compute_cost_snapshot() -> dict:
8024
8031
  # set of measured records that genuinely sums to zero still renders 0.0 --
8025
8032
  # the direction that would otherwise blank real data (the v8.72.0 trap).
8026
8033
  return {
8027
- "total_input_tokens": total_input if cost_recorded else None,
8028
- "total_output_tokens": total_output if cost_recorded else None,
8029
- "total_cache_read_tokens": total_cache_read if cost_recorded else None,
8030
- "total_cache_creation_tokens": total_cache_creation if cost_recorded else None,
8034
+ **({"unmeasured_sessions": unmeasured_sessions} if unmeasured_sessions else {}),
8035
+ "total_input_tokens": total_input if cost_recorded and not unmeasured_sessions else None,
8036
+ "total_output_tokens": total_output if cost_recorded and not unmeasured_sessions else None,
8037
+ "total_cache_read_tokens": total_cache_read if cost_recorded and not unmeasured_sessions else None,
8038
+ "total_cache_creation_tokens": total_cache_creation if cost_recorded and not unmeasured_sessions else None,
8031
8039
  "total_tokens": (
8032
8040
  total_input + total_output + total_cache_read + total_cache_creation
8033
- ) if cost_recorded else None,
8041
+ ) if cost_recorded and not unmeasured_sessions else None,
8034
8042
  "cache_hit_ratio": round(total_cache_read / _read_in, 4) if _read_in > 0 else None,
8035
- "estimated_cost_usd": round(estimated_cost, 6) if cost_recorded and not cost_unknown else None,
8043
+ "estimated_cost_usd": round(estimated_cost, 6) if cost_recorded and not cost_unknown and not unmeasured_sessions else None,
8036
8044
  "cost_recorded": cost_recorded,
8037
8045
  "by_phase": {k: {
8038
- "input_tokens": v["input_tokens"] if v.get("tokens_measured") else None,
8039
- "output_tokens": v["output_tokens"] if v.get("tokens_measured") else None,
8040
- "cost_usd": round(v["cost_usd"], 6) if v.get("measured") else None,
8046
+ "input_tokens": v["input_tokens"] if v.get("tokens_measured") and not v.get("unmeasured") else None,
8047
+ "output_tokens": v["output_tokens"] if v.get("tokens_measured") and not v.get("unmeasured") else None,
8048
+ "cost_usd": round(v["cost_usd"], 6) if v.get("measured") and not v.get("unmeasured") else None,
8041
8049
  } for k, v in by_phase.items()},
8042
8050
  "by_model": {k: {
8043
- "input_tokens": v["input_tokens"] if v.get("tokens_measured") else None,
8044
- "output_tokens": v["output_tokens"] if v.get("tokens_measured") else None,
8045
- "cost_usd": round(v["cost_usd"], 6) if v.get("measured") else None,
8051
+ "input_tokens": v["input_tokens"] if v.get("tokens_measured") and not v.get("unmeasured") else None,
8052
+ "output_tokens": v["output_tokens"] if v.get("tokens_measured") and not v.get("unmeasured") else None,
8053
+ "cost_usd": round(v["cost_usd"], 6) if v.get("measured") and not v.get("unmeasured") else None,
8046
8054
  } for k, v in by_model.items()},
8047
8055
  "budget_limit": budget_limit,
8048
8056
  "budget_used": round(budget_used, 6) if budget_limit is not None and budget_used is not None else None,
@@ -13,8 +13,8 @@ An Agent Change Receipt is the JSON file a Loki 10 run seals at `.loki/runs/<run
13
13
  - `not_proven`: list of strings naming what the run did not prove.
14
14
  - `verdict`: one of `VERIFIED`, `PARTIAL`, `ALREADY_SATISFIED`, `SPEC_CONFLICT`, `FAILED` (`types.ts:44`).
15
15
  - `evidence`: list of strings (search hits and citation for an ALREADY_SATISFIED verdict).
16
- - `cost`: `{ usd (number or null), input_tokens, output_tokens, measured_sessions, total_sessions, partial_usd, source? }`.
17
- - `time`: `{ wall_s, stages }`.
16
+ - `cost`: `{ usd (number or null), input_tokens, output_tokens, cache_read_tokens?, cache_creation_tokens?, sdk_duration_ms?, measured_sessions, total_sessions, partial_usd, source? }`.
17
+ - `time`: `{ wall_s, total_s?, stages, stage_s? }`. `stages` is a disjoint partition of `total_s`: `setup` (first event to machine start), each stage, a parallel group as one bucket (for example `plan+wall`), `orchestration` gaps and `seal`, so a consumer may simply add them. `stage_s` holds each stage's own seconds (parallel members overlap, so they can add to more than their bucket). `wall_s` is the stage buckets without setup, orchestration and seal. `total_s` is elapsed seconds from the run's first event to seal (PR stage excluded); treat it as NOT RECORDED unless every bucket is a non-negative number and they sum to it within 1%. `total_s`, `stage_s` and the cache token keys are additive: older receipts omit them, which reads NOT RECORDED, never 0.
18
18
  - `events_sha256`: digest of the run event log.
19
19
  - `receipt_sha256`: sha256 of the canonical JSON with `verification` and `receipt_sha256` removed (`loki-ts/src/engine10/verify_cmd.ts:19`).
20
20
  - `verification`: `{ jwt, kid }`. `jwt` is a compact EdDSA (Ed25519) token binding the receipt hash; both are null when the receipt is unsigned (`stages/seal.ts:92-95`).
@@ -1,6 +1,6 @@
1
1
  # CLI reference
2
2
 
3
- Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.3.1. Do not edit by hand.
3
+ Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.3.2. Do not edit by hand.
4
4
  Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for the full inventory.
5
5
 
6
6
  | Command | Description |
@@ -61,6 +61,10 @@ Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for th
61
61
  | `loki queue add` | Queue issue refs |
62
62
  | `loki queue list` | Show the queue |
63
63
  | `loki queue run` | Process the queue |
64
+ | `loki issues` | Run every open issue in a repo and open PRs |
65
+ | `loki issues run` | Triage and run open issues; split too-large ones into stacked PRs |
66
+ | `loki undo` | Plan or apply an undo of a run |
67
+ | `loki verify-pr` | Verify a PR against its linked issue in a sandbox |
64
68
  | `loki answer` | Resume a BLOCKED run with an answer |
65
69
  | `loki engine10` | v10 engine router |
66
70
  | `loki engine10 run` | Run the v10 engine |
@@ -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.3.1
5
+ **Version:** v11.3.2
6
6
 
7
7
  **Engine note:** `loki "<task>"`, `loki owner/repo#N` and `loki quick` run the Loki 10 engine. `loki start` still routes to the older engine, which is being removed; prefer `loki owner/repo#N`. See the [user guide](v10/GUIDE.md).
8
8
 
@@ -0,0 +1,24 @@
1
+ # Loki traces in Grafana, Honeycomb and Datadog
2
+
3
+ Loki exports OTLP/HTTP JSON traces to `LOKI_OTEL_ENDPOINT` (path `/v1/traces`). With `LOKI_OTEL_GENAI=1` the engine10 events map to `gen_ai` spans: `invoke_agent loki` for the run, `stage <name>` per stage, `execute_tool <name>` per tool call. Prompt and completion content never reach a span, and unknown token usage is absent, not 0.
4
+
5
+ The collector config `config/otel-collector/loki-collector.yaml` fans those spans out to three backends.
6
+
7
+ ## Use it
8
+
9
+ 1. Set the credentials for the vendors you use (the config reads them from the environment, nothing is stored in the file):
10
+ - Grafana Cloud: `GRAFANA_OTLP_ENDPOINT`, `GRAFANA_OTLP_AUTH` (the full header value, `Basic <base64 instanceId:token>`).
11
+ - Honeycomb: `HONEYCOMB_API_KEY` (ingest key).
12
+ - Datadog: `DD_API_KEY`, `DD_SITE` (for example `datadoghq.com`). Needs a collector build that includes the Datadog exporter (collector-contrib).
13
+ 2. Start the collector: `otelcol-contrib --config config/otel-collector/loki-collector.yaml`.
14
+ 3. Point Loki at it: `LOKI_OTEL_ENDPOINT=http://localhost:4318 LOKI_OTEL_GENAI=1 loki start ...`.
15
+
16
+ To use a subset of vendors, edit only `service.pipelines.traces.exporters` and list the vendors you use. The collector validates every defined exporter even when it is not listed, so the required fields of the other vendors carry inert defaults (`https://unset.invalid` for the Grafana endpoint, `unset` for the Datadog key, `datadoghq.com` for the Datadog site). Those defaults are never used by a vendor that is not in the list, and are not credentials. A listed vendor with its variables unset sends to the inert default and fails at export time, so set the variables for every vendor you list.
17
+
18
+ ## Receipt link
19
+
20
+ When tracing was on, the receipt carries a signed `trace_id` (see `docs/AGENT-CHANGE-RECEIPT.md`). Search for that id in the backend to open the run's trace.
21
+
22
+ ## Offline check
23
+
24
+ `bash tests/test-otel-collector-config.sh` parses the config, asserts the receiver, processor, exporters and traces pipeline, asserts every credential is an `${env:NAME}` placeholder (an optional `:-default` is allowed and must not be key-shaped), asserts that every required field still resolves to a non-empty default when a vendor's variables are unset, and maps the deterministic fixture in `tests/fixtures/otel/` (fixed run id and timestamps) to the golden span list. It makes no network call. When `otelcol-contrib` is on PATH it also runs `otelcol-contrib validate` with only `HONEYCOMB_API_KEY` set; that step is skipped, not passed, when the binary is absent. It does not contact a vendor; import into each vendor UI is not tested.
@@ -0,0 +1,32 @@
1
+ # Routing evidence
2
+
3
+ Measurements cited by provider and routing code. Each entry records the number,
4
+ its source and the date it was taken. Code comments point here instead of
5
+ carrying the figures.
6
+
7
+ ## Cost parity: MiniMax M2.5 vs Claude Opus 4.6
8
+
9
+ Status: a SINGLE internal run, not re-measured. Do not use it to drive routing
10
+ or in any external claim until it is re-derived (issue 210).
11
+
12
+ - Benchmark: SWE-bench, uniform scaffold.
13
+ - Harness: mini-swe-agent v2.0.0.
14
+ - Date: 2026-02-17.
15
+ - MiniMax M2.5: resolved 75.8 at $36.64 total.
16
+ - Claude Opus 4.6: resolved 75.6 at $275.76 total.
17
+ - Derived: an open model matching frontier accuracy at ~7.5x lower cost.
18
+ - Source: cited from the cost-context comment that previously lived in
19
+ providers/opencode.sh; competitive context in docs/COMPETITIVE-INTEL-2026-09.md
20
+ item 3.
21
+
22
+ ## opencode viability
23
+
24
+ Date verified: 2026-07-29/30.
25
+
26
+ - Source opencode.ai/docs/providers documents 75+ providers and custom
27
+ OpenAI-compatible endpoints.
28
+ - Source anomalyco/opencode: 190,871 stars, release v1.18.9 (2026-07-28),
29
+ pushed the same week.
30
+ - Source aider: last release v0.86.0 from 2025-08-09.
31
+ - Roo Code is archived; Continue is read-only.
32
+ - CLI surface checked against the installed opencode v1.17.9.
@@ -135,6 +135,7 @@ banners you might expect. These variables control the remaining `[INFO]` and
135
135
  | `LOKI_DURABLE_STATE` | `0` | `1` enables durable state **and** the richer process-exit contract that lets Kubernetes distinguish a deterministic failure from a crash. See [exit codes](./exit-codes.md). |
136
136
  | `LOKI_SDK_LOOP` | unset | Routes the run through the Bun/TypeScript runner instead of bash. Both implement the same exit contract. |
137
137
  | `LOKI_ALLOW_AGENT_GITHUB_TOKEN` | unset | By default `GH_TOKEN`, `GITHUB_TOKEN`, `GH_ENTERPRISE_TOKEN`, and `GITHUB_ENTERPRISE_TOKEN` are withheld from agent sessions. The two routes differ: on the bash route (`autonomy/run.sh`) the withheld tokens are re-granted only for Loki's own trusted calls, namely the `gh()` CLI wrapper, the `_loki_net` helper used by the optional local-PR path in `on_run_complete()`, the session-end `create_session_pr` call, and `autonomy/lib/proof-check.sh`'s network probe. On the Bun route (`loki-ts/src/runner/github_token.ts`'s `withholdGithubTokens()`) the tokens are dropped from the process outright, with no re-grant and no push/PR step of its own. Exactly `1` lets the agent inherit the tokens as before, on either route; that is a Rule of Two exposure, and a one-line stderr warning is printed. |
138
+ | `LOKI_AI_MARKING` | unset (off) | `1` (exactly) adds machine-readable trailers to the Loki commit (`AI-Generated: true`, `AI-Provider`, `AI-Model`, `Loki-Receipt`, after `Loki-Run`) and one `AI-Generated: true (...)` marker line under the verdict in the PR body. Values are single-line and bounded. Off, the commit and PR body are byte-identical to before. |
138
139
 
139
140
  `LOKI_DURABLE_STATE=1` is the one to set in a Job or task definition. Without
140
141
  it every failure collapses to exit 1 and the platform cannot tell "re-running