loki-mode 11.3.1 → 11.3.4

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 +69 -30
  6. package/bin/loki +30 -1
  7. package/completions/_loki +22 -0
  8. package/completions/loki.bash +17 -3
  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 +20 -0
  20. package/docs/v10/FAILURE-CLASSES.md +263 -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 +12 -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 +15752 -13978
  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-CKj0pDt0.js} +1 -1
  51. package/web-app/dist/assets/{Avatar-BJyjV7Zi.js → Avatar-BOV__pMM.js} +1 -1
  52. package/web-app/dist/assets/{Badge-D572l0uq.js → Badge-4a6mAYjU.js} +1 -1
  53. package/web-app/dist/assets/{Button-T4BnOZgl.js → Button-B3GM7oxm.js} +1 -1
  54. package/web-app/dist/assets/{CockpitPage-Cj_GFsNn.js → CockpitPage-Cwge3_kH.js} +1 -1
  55. package/web-app/dist/assets/{ComparePage-A8m4ZXak.js → ComparePage-CBQUGKsm.js} +1 -1
  56. package/web-app/dist/assets/{ErrorBoundary-hugCg5yg.js → ErrorBoundary-TM7lDjUJ.js} +1 -1
  57. package/web-app/dist/assets/{EvidenceReceiptPanel-CuJKDC2G.js → EvidenceReceiptPanel-DUOseTYA.js} +1 -1
  58. package/web-app/dist/assets/{GitHubIssuesPanel-BYGKjarC.js → GitHubIssuesPanel-hK_uML6T.js} +1 -1
  59. package/web-app/dist/assets/{GitHubPRsPanel-c7sFIJlf.js → GitHubPRsPanel-Bt0mug-u.js} +1 -1
  60. package/web-app/dist/assets/{HomePage-A1MIkG2P.js → HomePage-DcOogAai.js} +2 -2
  61. package/web-app/dist/assets/{LoginPage-8KQMRhtR.js → LoginPage-D41pjjhF.js} +1 -1
  62. package/web-app/dist/assets/{MagicPage-C05QW2Lx.js → MagicPage-iDcdAcL2.js} +1 -1
  63. package/web-app/dist/assets/{MetricsPage-Oukv5hme.js → MetricsPage-Tt4sfFD1.js} +1 -1
  64. package/web-app/dist/assets/{NotFoundPage-DKIwsawz.js → NotFoundPage-DPQMausn.js} +1 -1
  65. package/web-app/dist/assets/{ProjectPage-DwFRPLCG.js → ProjectPage-DdMz4frY.js} +3 -3
  66. package/web-app/dist/assets/{ProjectsPage-CgY4YYDX.js → ProjectsPage-Be0v4ace.js} +1 -1
  67. package/web-app/dist/assets/{SettingsPage-BpfcSjUA.js → SettingsPage-90yIh-mE.js} +1 -1
  68. package/web-app/dist/assets/{ShowcasePage-BqBakC4K.js → ShowcasePage-D5x5eczl.js} +1 -1
  69. package/web-app/dist/assets/{SystemSettingsPage-CZLu5VB0.js → SystemSettingsPage-UWeWT8KX.js} +1 -1
  70. package/web-app/dist/assets/{TeamsPage-D55S1jKz.js → TeamsPage-BtofFlme.js} +1 -1
  71. package/web-app/dist/assets/{TemplatesPage-D3SydvxN.js → TemplatesPage-DwB8Va2Z.js} +1 -1
  72. package/web-app/dist/assets/{TerminalOutput-hNg8G1-e.js → TerminalOutput-CGIgTmby.js} +1 -1
  73. package/web-app/dist/assets/{activity-D2C0VagO.js → activity-BZ77CO72.js} +1 -1
  74. package/web-app/dist/assets/{bell-CMvYi36w.js → bell-Bzpqv6un.js} +1 -1
  75. package/web-app/dist/assets/{bot-BvtQ6lmo.js → bot-yXo1B_-f.js} +1 -1
  76. package/web-app/dist/assets/{check-B13xkXed.js → check-BSxGjsta.js} +1 -1
  77. package/web-app/dist/assets/{chevron-left-emu6r49E.js → chevron-left-CqTRVUB5.js} +1 -1
  78. package/web-app/dist/assets/{circle-alert-CExlwjuy.js → circle-alert-88gdjY71.js} +1 -1
  79. package/web-app/dist/assets/{clock-D7Uas3oL.js → clock-XdFXoaJi.js} +1 -1
  80. package/web-app/dist/assets/{cloud-CkB4S86C.js → cloud-Tx1sJrXz.js} +1 -1
  81. package/web-app/dist/assets/{code-xml-SL9IIWIn.js → code-xml-DaH9CKWy.js} +1 -1
  82. package/web-app/dist/assets/{database-BQFkd7t1.js → database-BpvOmzij.js} +1 -1
  83. package/web-app/dist/assets/{dollar-sign-qHmnNYsU.js → dollar-sign-VC2UX2UE.js} +1 -1
  84. package/web-app/dist/assets/{file-code-corner-C2YUDP0K.js → file-code-corner--TF3VpoT.js} +1 -1
  85. package/web-app/dist/assets/{file-plus-C_ooIkDe.js → file-plus-DZpwU9pY.js} +1 -1
  86. package/web-app/dist/assets/{globe-CQ-GL485.js → globe-tbpeHRPV.js} +1 -1
  87. package/web-app/dist/assets/{hammer-U05L_D7V.js → hammer-CKLXn4vL.js} +1 -1
  88. package/web-app/dist/assets/{index-CooHd7I-.js → index-DnCsWpCh.js} +3 -3
  89. package/web-app/dist/assets/{layers-Buo17rjW.js → layers-B0fkGsk8.js} +1 -1
  90. package/web-app/dist/assets/{loader-circle-15vK3oV1.js → loader-circle-czscMzmQ.js} +1 -1
  91. package/web-app/dist/assets/{lock-BeXLebuJ.js → lock-CTtO6vc8.js} +1 -1
  92. package/web-app/dist/assets/{package-MVh_6-6R.js → package-DZeSY0-g.js} +1 -1
  93. package/web-app/dist/assets/{plus-Dh_nd9vS.js → plus-mE12CJVN.js} +1 -1
  94. package/web-app/dist/assets/{refresh-cw-C9cBJEAT.js → refresh-cw-9DSznasU.js} +1 -1
  95. package/web-app/dist/assets/{rotate-ccw-D3PAtzST.js → rotate-ccw-DcHI0M43.js} +1 -1
  96. package/web-app/dist/assets/{scroll-text-DZCoXQjo.js → scroll-text-BWLthSw-.js} +1 -1
  97. package/web-app/dist/assets/{server-OCOLI5J_.js → server-_I247LrS.js} +1 -1
  98. package/web-app/dist/assets/{shield-alert-oVhmf1_h.js → shield-alert-XRJYD_6c.js} +1 -1
  99. package/web-app/dist/assets/{thumbs-up-B-kbX4vN.js → thumbs-up-CxuTD3eO.js} +1 -1
  100. package/web-app/dist/assets/{trash-2-EL6moveW.js → trash-2-DuGxuVt8.js} +1 -1
  101. package/web-app/dist/assets/{trending-down-Cl3kjpsx.js → trending-down-Cg_zGt__.js} +1 -1
  102. package/web-app/dist/assets/{trending-up-DST7I6M2.js → trending-up-DpUJzIrV.js} +1 -1
  103. package/web-app/dist/assets/{upload-CUOT780G.js → upload-Db7YZ4zR.js} +1 -1
  104. package/web-app/dist/assets/{usePolling-Djmt90eb.js → usePolling-BGRYm-vq.js} +1 -1
  105. package/web-app/dist/assets/{user-BRBohA6J.js → user-CxHrowEP.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
@@ -31,9 +31,9 @@ The CEO is Lokesh Mure (Loki), the founder. Everyone else is an agent. The compa
31
31
  ## Release pipeline
32
32
  - Tier A fast gate on every change (target 60-120s): syntax checks, changed-file shellcheck, diff-selected tests, moat suite split per property. It is feedback, not release authority.
33
33
  - Tier B: every main commit has a full Tier B verdict on its exact tree, run on main or on the train that produced it (D55); never cancelled on main.
34
- - Release = a lookup: publish a commit whose tree already has a Tier B pass. A VERSION-only bump reuses its parent's verdict. Target: 2 min from verified to npm.
35
- - Clock cuts (D91): one release every 45 min from whatever is approved and green on main; no cut waits for a slice. Target per cut: >=3 features and >=5 enhancements or fixes, user-facing CHANGELOG. Approved slices merge to main continuously (tsc, impacted tests, global guards, P9); risky work merges flag-gated. A moat regression still blocks a cut.
36
- - If Tier B fails on main: stop releases, open a P0 fix-forward slice. Never unpublish; npm deprecate is allowed.
34
+ - Every approved slice is a release (D96): merge-gate merges to main, and main publishes a PATCH to npm `next` within 15 min. No trains, no batching. Target 3-10 releases per hour.
35
+ - `latest` is promotion only: the newest `next` whose SHA has a green hourly full-suite backstop and green Post-Release Smoke. A red backstop blocks promotion, not publishing.
36
+ - If the backstop fails on main: promotion stops, and a P0 fix-forward slice opens within the hour. Never unpublish; npm deprecate is allowed.
37
37
 
38
38
  ## Large tasks (for example "rebuild the dashboard")
39
39
  - The Architect splits the task into 10-40 slices behind a feature flag.
@@ -2297,3 +2297,15 @@ The swarm is stopped: .loki/V10-STOP exists, the loop has no wake-ups, and the c
2297
2297
  - PLAN-TIMEOUT engineer had no edits from 07:40Z to 08:14Z; re-pinged with a commit-now instruction.
2298
2298
 
2299
2299
  - 2026-10-08T10:27Z CoS: PLAN-TIMEOUT-1 merged by SHA b43536756 (TL APPROVE: fast-gate 10, train-verdict-reuse 40, select-tests 42, rc 0; git diff approved..HEAD^2 empty). Full local tier on 82eb1ad31 hit the 45-min cap (rc 124); reds classified env-only (receipt_attest needs cryptography; indexer + typecheck worktree-path only, green in main). bun 2004/0, util 175/0, moat rc 0 5/9. Lost ~1h: TL verdict sat in an idle teammate; next time read the subagent transcript after 10 min instead of waiting. Pushing the 11.3.1 train now.
2300
+
2301
+ ## 2026-10-08 12:21Z CoS
2302
+ - 11.3.1 (604145bbc, pushed) release run 37774482965 failed at required-ci NIGHTLY-BLOCK (D90): nightly 37686475225 red since 10-04 on one test, macOS bun cockpit "renders a real inline image ... via the bundled wasm" timed out after 5000ms (cold wasm+font load). npm 11.3.1 NOT published.
2303
+ - Fix-forward NIGHTLY-COCKPIT (row FC-50, beforeAll warm-up + 30s render timeout, assertions unchanged): built f7f48a120, cockpit 26/0; TL round 1 BLOCK only on FC-47 id collision, r2 is the rename, in review. Then: merge, push, dispatch nightly, FULL rerun of 37774482965.
2304
+ - 11.3.2 train on local main (unpushed), now e18e4ca11: 12 earlier slices plus 133-B4..ER-01-r2, 133-E3-r2, SIGS-DOC (run-all-tests.sh registration union by hand), VPR-1-r2. UNDO-2-r2 merge reverted locally (tripped raw-gh and full-env spawn guards), r3 in HIGH review.
2305
+ - Product-code conflicts sent to engineers: XV-1 (seal.ts), MARK-1 (pr.ts), MASS-2 (queue.ts/pr.ts). BLOCKs back to engineers: MCP-D (unauthenticated HTTP task creation), VPR-2-r2 (gh authorAssociation field does not exist), T5-ATTEMPTS-PR-r2 (stale help line, r3 in review).
2306
+
2307
+ ## 2026-10-08 20:27Z CoS
2308
+ - v11.3.3 (eee891534) is on main; release run 37837110730 failed at required-ci. Tests 37837111102 at the bump SHA is red, and bump-only parent reuse was refused: "normalizer: not on the allowlist: deploy/helm/autonomi/Chart.yaml" (release.sh also bumps 2 more Chart.yaml files and docs/CLI-REFERENCE.md). Parent 54a519a22 Tests green; nightly 37836989262 at 54a519a22 green.
2309
+ - CTO split: (A) BUMP-REUSE HIGH slice (allowlist + bump-set subset guard + replay), then ship as 11.3.4. (B) RELGATE-REDS: 6 latent reds that guards_for("VERSION") selected (watch-command v6.33.0, manifest-truncation rc=2, quick-receipt-order rc=124, autonomy-and-stop OPTOUT, node_lint web-app rc=127, moat P2.verify-exit-contract); ships separately and does not block A. SEL follow-up: guards_for literal-string selection, LOW.
2310
+ - Promote 11.3.2: auto run 37838898754 blocked on the smoke lookup (the pre-run-name smoke 37833604487 is not matched by display_title). Dispatched smoke 37839153237 (v11.3.2) then promote.
2311
+ - WALL-SMOKE-GUARD merged into train-1132 at 5be544484 (rides with 11.3.4). WALL-COLOR engineer building.
@@ -0,0 +1,77 @@
1
+ # Draft: an in-toto predicate for AI-agent code changes
2
+
3
+ Status: DRAFT proposal by the Loki Mode project. It is not accepted, adopted or standardized by in-toto or any other body, and nothing here claims otherwise. No upstream pull request exists, and none is referenced. Loki is the only implementation of this draft's source data (its receipt), so the draft is a reference for discussion, not an ecosystem convention.
4
+
5
+ This follows the shape the in-toto attestation project asks of new predicates (a name, a type identifier, a stated purpose, a field list, a worked example). Check the current in-toto "New Predicate Guidelines" before any submission; this document does not restate them.
6
+
7
+ ## Name and type identifier
8
+
9
+ - Name: Agent Change.
10
+ - Proposed predicateType: `https://autonomi.dev/loki/agent-change/v0`. This is an identifier only. It is a placeholder in the namespace Loki already uses for its DSSE export (`https://autonomi.dev/loki/receipt/v10`, see `docs/AGENT-CHANGE-RECEIPT.md`). It is not registered anywhere and is not claimed to resolve.
11
+ - Statement: in-toto Statement v1. Subject: the resulting git commit, `gitCommit` digest (and `gitTree`), as Loki's DSSE export already does.
12
+
13
+ ## Purpose
14
+
15
+ State, in a signable and machine-checkable form, what an automated agent changed in a repository, which task and models were involved, what was checked, what the outcome was, and, as a first-class list, what was not proven. A consumer (a reviewer, a policy engine, an admission check) can then decide whether to trust the change without reading the agent's transcript.
16
+
17
+ ## Fields
18
+
19
+ "Receipt source" names the field in the Loki receipt (documented in `docs/AGENT-CHANGE-RECEIPT.md`). "new" means the current receipt does not record it, so the draft would need the producer to add it.
20
+
21
+ | Predicate field | Meaning | Receipt source |
22
+ |---|---|---|
23
+ | `task.source` | `text` or `issue` | `task` |
24
+ | `task.sha256` | digest of the task text; the text is not included | `task` |
25
+ | `baseCommit` | commit the change starts from | `base_sha` |
26
+ | `headCommit` | resulting commit | `head_sha` |
27
+ | `diffSha256` | digest of the diff between them | `diff_sha256` |
28
+ | `agent.name` | agent product identity | new |
29
+ | `agent.provider` | model provider or CLI | `provider` |
30
+ | `models` | model identifiers used | `model` |
31
+ | `tools` | tools the agent used | new |
32
+ | `mcpServers` | MCP servers the agent could call | new |
33
+ | `checks` | list of `{name, cmd, result}` | `checks` |
34
+ | `outcome` | `VERIFIED`, `PARTIAL`, `ALREADY_SATISFIED`, `SPEC_CONFLICT` or `FAILED` | `verdict` |
35
+ | `notProven` | what the run did not prove | `not_proven` |
36
+ | `receiptSha256` | digest of the full producer receipt, when one exists | `receipt_sha256` |
37
+
38
+ Rules: `notProven` MUST be present, and an empty list is a claim that nothing was left unproven. A producer that cannot know a field omits it; it never writes a placeholder. `tools` and `mcpServers` are `new` because no existing receipt field carries them; until a producer records them they are omitted.
39
+
40
+ ## Worked example
41
+
42
+ Derived from the Loki test fixture `packages/control-plane/test/fixtures/runs/verified-pr/receipt.json` (a stubbed CLI-invoker run, so cost is unmetered). Hashes are copied from that fixture. The signature is omitted here; a real attestation is wrapped in a DSSE envelope.
43
+
44
+ ```json
45
+ {
46
+ "_type": "https://in-toto.io/Statement/v1",
47
+ "subject": [
48
+ { "name": "git+commit:7421fae2f14df16949a06dbcec18628bb45e50f0", "digest": { "gitCommit": "7421fae2f14df16949a06dbcec18628bb45e50f0" } }
49
+ ],
50
+ "predicateType": "https://autonomi.dev/loki/agent-change/v0",
51
+ "predicate": {
52
+ "task": { "source": "text", "sha256": "982a130e3cb8ef5642b3f3fa83f9e564126fba16a73537bb1251b4328a2fa3b0" },
53
+ "baseCommit": "ebc3d8091bfe6e75393a26c719aa7535247044f9",
54
+ "headCommit": "7421fae2f14df16949a06dbcec18628bb45e50f0",
55
+ "diffSha256": "eea5ea2cb26cec5473c1b2cec4195de8b17a1733b70ebbdcc4ede90eb58a7e30",
56
+ "agent": { "name": "loki", "provider": "claude" },
57
+ "models": ["sonnet"],
58
+ "checks": [
59
+ { "name": "bun:calc.test.ts", "cmd": "bun test calc.test.ts", "result": "pass" }
60
+ ],
61
+ "outcome": "VERIFIED",
62
+ "notProven": ["full suite", "app boot", "council", "security scan", "cost unmetered (CLI invoker; recorded as 0)"],
63
+ "receiptSha256": "04e777f6b9b3c854b5d1329ec1f8d96ee8ff78ac7aa432de63fbcf0d323b729e"
64
+ }
65
+ }
66
+ ```
67
+
68
+ ## Open questions
69
+
70
+ - Whether `models` should carry provider-qualified identifiers; the fixture records the short name `sonnet`.
71
+ - Whether `outcome` should be a smaller vocabulary than Loki's five verdicts.
72
+ - Whether `tools` and `mcpServers` belong in this predicate or in a separate inventory attestation.
73
+ - Relationship to SLSA provenance (Loki's SIGS-1 slice maps a receipt onto SLSA v1 instead); this draft complements it and does not replace it.
74
+
75
+ ## Reference implementation
76
+
77
+ Loki Mode produces the source receipt and can export a Statement v1 with its own predicate today (`loki verify --export-dsse`). Emitting this draft predicate is not implemented.
@@ -0,0 +1,55 @@
1
+ # MCP 2026-07-28: what applies to Loki, and who implements it
2
+
3
+ Slice MCP-0. Documentation only; no code changed. Written 2026-10-08.
4
+ Every claim is marked VERIFIED (read from the cited source on the fetch date) or UNVERIFIED (inferred, not run).
5
+
6
+ ## Sources
7
+
8
+ | Source | URL | Fetched |
9
+ |---|---|---|
10
+ | Spec "Key Changes" (changelog vs 2025-11-25) | https://modelcontextprotocol.io/specification/2026-07-28/changelog | 2026-10-08 |
11
+ | PyPI `mcp` JSON | https://pypi.org/pypi/mcp/json | 2026-10-08 |
12
+ | PyPI `mcp` 2.3.0 wheel and `mcp-types` 2.3.0 wheel (pip download, files read) | https://pypi.org/project/mcp/2.3.0/ | 2026-10-08 |
13
+ | npm `@modelcontextprotocol/sdk` (registry via `npm view`, tarball 1.32.1 read) | https://www.npmjs.com/package/@modelcontextprotocol/sdk | 2026-10-08 |
14
+ | npm `@modelcontextprotocol/server` and `@modelcontextprotocol/client` (`npm view`) | https://www.npmjs.com/package/@modelcontextprotocol/server | 2026-10-08 |
15
+
16
+ ## SDK versions found
17
+
18
+ - Python SDK latest version found: mcp 2.3.0, uploaded 2026-10-02 (VERIFIED, PyPI JSON). Also on PyPI: 2.2.0 (2026-09-07), 1.30.0 (2026-09-07). The installed copy on the build machine is mcp 1.27.0 (VERIFIED, `importlib.metadata`).
19
+ - Does Python mcp 2.3.0 implement 2026-07-28: its `mcp-types` 2.3.0 wheel lists "2026-07-28" in `MODERN_PROTOCOL_VERSIONS` (`mcp_types/version.py`), and the `mcp` wheel contains `server/_streamable_http_modern.py` and references to `server/discover` (VERIFIED, files read). Runtime behavior was not exercised: UNVERIFIED.
20
+ - Loki pins `mcp>=1.0.0,<2.0.0` in `mcp/requirements.txt`, so a fresh install can never pick up 2.x (VERIFIED, repo file). 2.3.0 requires Python >=3.10 and new dependencies (`mcp-types`, `httpx2`, `pyjwt`); compatibility with Loki's `mcp/_sdk_loader.py` is UNVERIFIED.
21
+ - npm `@modelcontextprotocol/sdk` latest: 1.32.1, modified 2026-10-05 (VERIFIED, `npm view`). Its README states it implements the spec up to 2025-11-25 and that 2026-07-28 support "is not planned for v1.x"; it lives in v2 as separate packages (VERIFIED, README in tarball).
22
+ - npm v2 packages `@modelcontextprotocol/server` and `@modelcontextprotocol/client`: latest 2.3.1, modified 2026-10-05 (VERIFIED, `npm view`). Whether they implement every item below: UNVERIFIED.
23
+
24
+ ## Conformance table
25
+
26
+ "Applies" means a requirement on a stdio tool server plus a client such as Loki's (`mcp/server.py`, `mcp/lsp_proxy.py`, `src/protocols/mcp-server.js`, `src/protocols/mcp-client.js`).
27
+
28
+ | Spec change (2026-07-28) | Applies to Loki | Status of claim |
29
+ |---|---|---|
30
+ | Stateless protocol: the `initialize` / `notifications/initialized` handshake is removed (initialize removal); each request carries protocol version and client capabilities in `_meta` (`io.modelcontextprotocol/protocolVersion`, `io.modelcontextprotocol/clientCapabilities`) | Yes, server and client | VERIFIED (changelog) Slice MCP-C: JS server done (no initialize needed, `_meta` version checked, no session stored); behind LOKI_MCP_2026_07=1. |
31
+ | `server/discover` RPC: servers MUST implement it; clients MAY call it first or use it as a stdio compatibility probe | Yes, server MUST | VERIFIED (changelog) Slice MCP-C: JS server done (`supportedVersions`, `serverInfo`, `capabilities`); result field names are our reading, UNVERIFIED against the spec schema. |
32
+ | Multi Round-Trip Requests (MRTR): `InputRequiredResult` (`resultType: "input_required"`) with `inputRequests`, client retries with `inputResponses`; replaces server-initiated `roots/list`, `sampling/createMessage`, `elicitation/create`. All results carry `resultType` | Yes if Loki ever asks the client for input; today it does not (UNVERIFIED, inferred from tool list) | Spec VERIFIED (changelog) |
33
+ | Tasks moved out of core to the extension `io.modelcontextprotocol/tasks` (polling via `tasks/get`, new `tasks/update`, no `tasks/list`) | Only if Loki exposes long-running tools as tasks; optional | VERIFIED (changelog) |
34
+ | OpenTelemetry trace context in `_meta`: `traceparent`, `tracestate`, `baggage` (traceparent in _meta) | Optional; useful with the OTEL-GENAI program | VERIFIED (changelog) |
35
+ | Deprecated: Roots, Sampling, Logging (`logging/setLevel` removed; level per request via `io.modelcontextprotocol/logLevel`), HTTP+SSE transport, OAuth Dynamic Client Registration (DCR) in favor of Client ID Metadata Documents | Loki implements none of Roots/Sampling/Logging as a server; DCR applies only to remote HTTP auth (`tests/test-mcp-http-auth.sh`) | VERIFIED (changelog); Loki usage UNVERIFIED beyond grep |
36
+ | Streamable HTTP: sessions and `Mcp-Session-Id` removed; `subscriptions/listen` replaces GET and `resources/subscribe`; `Mcp-Method` and `Mcp-Name` headers required on POST; SSE resumability removed | Only for the HTTP transport; stdio unaffected | VERIFIED (changelog) |
37
+ | `ping` and `notifications/roots/list_changed` removed; `ttlMs` and `cacheScope` required on list results; deterministic `tools/list` order; resource-not-found code now -32602 | Yes for the server's list endpoints | VERIFIED (changelog) |
38
+ | Error code renumbering: HeaderMismatch -32020, MissingRequiredClientCapability -32021, UnsupportedProtocolVersion -32022 | Yes once the stateless profile is implemented | VERIFIED (changelog) Slice MCP-C: -32022 UnsupportedProtocolVersion done; the other two codes are not used yet. |
39
+
40
+ ## Consequences for the plan
41
+
42
+ - Python side (MCP-B, MCP-C): do not hand-roll the 2026-07-28 profile. A pip release that carries it exists (2.3.0), so the path is an SDK bump behind a test, not new protocol code. Raising the `<2.0.0` ceiling needs a compatibility run first (UNVERIFIED today).
43
+ - JS side (MCP-A, MCP-C): the hand-rolled server cannot get 2026-07-28 from `@modelcontextprotocol/sdk` 1.x; the v2 packages are the candidate. Adding a dependency needs its own decision.
44
+ - MCP-A (legacy version negotiation fixes) is independent of this document.
45
+
46
+ ## MCP-C status (JS server)
47
+
48
+ - `server/discover` is answered always; the version list includes 2026-07-28 only when `LOKI_MCP_2026_07=1` (default off until every row above passes).
49
+ - `tools/list`, `tools/call` and the other methods work with no prior `initialize`. A `_meta["io.modelcontextprotocol/protocolVersion"]` outside the advertised list gets -32022 with `data.supported`.
50
+ - `initialize` for legacy clients is unchanged; its fallback stays 2025-11-25, never 2026-07-28. No session id is stored. `tools/call` keeps the existing auth check, which runs before the version check.
51
+ - Not done: `resultType`, `ttlMs`/`cacheScope`, MRTR, Tasks, `Mcp-Method` headers, `subscriptions/listen`. Python side: SDK-only, no hand-rolled protocol (see Consequences).
52
+
53
+ ## Method
54
+
55
+ Changelog text read from the fetched page on 2026-10-08. PyPI facts from the JSON API. Wheel and tarball contents read after `pip download --no-deps` and `npm pack` into scratch directories (not installed, not executed).
@@ -0,0 +1,41 @@
1
+ # VERIFY-PR sandbox: spec, threat model, runner contract (VPR-1)
2
+
3
+ Runner: `scripts/verify-pr-sandbox.sh`. Test: `tests/test-verify-pr-sandbox.sh`. Consumer: VPR-2 (`loki verify-pr`).
4
+
5
+ ## Rule of Two
6
+ The verify job holds untrusted input (the PR code). It holds NO token and has NO network. A separate job that never executes PR code holds `checks: write`, reads only the result file, and posts. This runner is the first job's execution boundary.
7
+
8
+ ## Contract
9
+ ```
10
+ verify-pr-sandbox.sh --repo DIR --out DIR --cmd 'SHELL CMD' --image IMG [--timeout S] [--max-output B] [--print-argv]
11
+ ```
12
+ - Exit 0: the check ran; read `result.json` (`status` COMPLETED, TIMEOUT or ERROR, plus `exit_code`).
13
+ - Exit 2: input refused. Exit 3: BLOCKED (docker absent, daemon down, image not local, resource exhaustion). BLOCKED says nothing about the PR and VPR-2 must never map it to a verdict.
14
+ - `OUT/result.json` and `OUT/output.txt` are the only files written, both by the host. `output.txt` is the check's captured output, capped at `--max-output` bytes, with the sentinel line removed; treat it as untrusted data. result.json fields: status, exit_code, output_truncated, detail, image, timeout_s.
15
+ - The image is never pulled (`--pull never`); dependencies must already be in the image or the caller reports NOT PROVEN.
16
+
17
+ ## Controls
18
+ | Control | Mechanism |
19
+ |---|---|
20
+ | No network | `--network none` |
21
+ | Read-only root | `--read-only`; `/work` (exec), `/cap`, `/tmp` are size-capped tmpfs |
22
+ | Non-root | `--user 65534:65534`, `--cap-drop ALL`, `no-new-privileges` |
23
+ | Caps | `--pids-limit 256`, `--memory 512m` (swap equal), `--cpus 1`, `--init` |
24
+ | Wall timeout | in-container `timeout -s KILL`, plus a host watchdog that runs `docker kill <exact name>` (never by pattern) |
25
+ | Env scrub | `docker run` inherits no host env; only PATH, HOME=/work, CI=1 are passed. GH_TOKEN, GITHUB_TOKEN, SSH_AUTH_SOCK never reach argv |
26
+ | Mounts | exactly one: the repo, read-only. No HOME, no docker socket, no output mount |
27
+ | Output | stdout is captured by the host, capped in-container (`--max-output`); the host writes result.json and output.txt |
28
+
29
+ Deviation from the card: there is no mounted output dir. A writable host mount would let the checked code fill the host disk, so the result travels on stdout and only the host writes `OUT`. Numeric flags are forced to base 10 (a leading zero is not octal) and every value flag requires a value. `--image` is restricted to `[A-Za-z0-9._/:@-]`.
30
+
31
+ ## Threat model
32
+ - Malicious package.json scripts / test code: run only inside the container, as nobody, no network.
33
+ - Git hooks: the repo is mounted read-only and the runner never runs git; checkout of base/head is VPR-2's job and must use `util/safe_git.ts`.
34
+ - Symlinks out of the repo: refused on the host before launch (absolute target, or relative target resolving outside the repo). The copy to `/work` happens inside the container, so a link resolves against the container filesystem only.
35
+ - Oversized output: capped by `--max-output`; `output_truncated` is set.
36
+ - Fork bomb: `--pids-limit 256`. Memory bomb: `--memory`. Disk fill: tmpfs size caps.
37
+ - Forged result: the checked code can print a fake `LOKI_VPR_END` line. A per-run 128-bit nonce is sent to the wrapper on stdin and the checked command runs with stdin from /dev/null; the host accepts only the last line carrying that nonce. Docker's default seccomp profile does NOT block ptrace or process_vm_readv on kernels >= 4.8, and the checked command runs as the same uid as the wrapper inside the container, so a hostile process can read the wrapper's memory and recover the nonce. The nonce therefore only stops accidental or naive forgery; it is not a security boundary. One cross-check is added: the wrapper exits with the check's code and the host rejects (status ERROR) a sentinel that disagrees with docker's own exit code. The PR code can still make its own check report success, which is why `result.json` is a signal about the check and not the final verdict (VPR-2 owns that, and it must not treat a bare COMPLETED exit 0 as proof). A custom seccomp profile denying ptrace is a possible hardening and is not shipped here.
38
+ - Docker pool exhaustion: reported as BLOCKED, never as a failing check.
39
+
40
+ ## Not covered (residual, tracked for VPR-2 review)
41
+ Kernel or runtime escapes (use gVisor or a microVM runtime for a hosted deployment); Docker Desktop shares the VM across containers; the hostile-container part of the test needs a daemon and a local image and SKIPs loudly otherwise.