loki-mode 11.4.2 → 11.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/chat.sh +6 -3
- package/autonomy/lib/engine10-push.sh +19 -1
- package/autonomy/lib/help-core.sh +1 -1
- package/autonomy/lib/proof-generator.py +23 -5
- package/autonomy/lib/proof-verify.py +607 -5
- package/autonomy/lib/run-summary.sh +17 -1
- package/autonomy/lib/setup-requirements.json +35 -0
- package/autonomy/loki +244 -237
- package/autonomy/notification-checker.py +1 -1
- package/autonomy/provider-offer.sh +95 -47
- package/autonomy/route.sh +38 -6
- package/autonomy/run.sh +42 -14
- package/autonomy/setup-wizard.sh +19 -10
- package/autonomy/verify.sh +40 -1
- package/completions/_loki +3 -1
- package/completions/loki.bash +9 -3
- package/dashboard/__init__.py +3 -10
- package/dashboard/control.py +5 -435
- package/docs/ARCHITECTURE.md +1 -2
- package/docs/CLI-CORE-VERBS.md +2 -6
- package/docs/CLI-REFERENCE.md +3 -1
- package/docs/COMPONENTS.md +0 -2
- package/docs/EVALUATING.md +27 -0
- package/docs/HARNESS-INTELLIGENCE-PLAN.md +20 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/KNOWN-ISSUES.md +10 -1
- package/docs/PROOF-PASSPORT.md +114 -0
- package/docs/alternative-installations.md +103 -0
- package/docs/authentication.md +19 -1
- package/docs/cost-controls.md +139 -0
- package/docs/dev/release-checklist.md +9 -8
- package/docs/enterprise/security.md +3 -234
- package/docs/environment-variables.md +36 -0
- package/docs/exit-codes.md +102 -2
- package/docs/spec-first-intent.md +29 -0
- package/docs/v10/ARCHITECT-CUTS.md +304 -0
- package/docs/v10/CLI-MODERN.md +2 -2
- package/docs/v10/DECISIONS.md +5 -5
- package/docs/v10/ENGINE.md +735 -267
- package/docs/v10/GUARDS.md +16 -14
- package/docs/v10/GUIDE.md +5 -7
- package/docs/v10/PROGRESS.md +12 -1
- package/docs/v11/MARKET-SLICES.md +1 -5
- package/integrations/openclaw/README.md +20 -0
- package/integrations/vibe-kanban.md +12 -50
- package/loki-ts/data/model-pricing.json +1 -0
- package/loki-ts/dist/loki.js +1156 -1103
- package/mcp/__init__.py +1 -1
- package/mcp/_sdk_loader.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +2063 -1032
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/plugins/loki-mode/README.md +41 -1
- package/plugins/loki-mode/commands/handoff.md +12 -0
- package/plugins/loki-mode/commands/loki-verify.md +9 -2
- package/plugins/loki-mode/commands/start.md +20 -0
- package/plugins/loki-mode/hooks/hooks.json +11 -0
- package/plugins/loki-mode/scripts/loki-stop-verify.sh +84 -0
- package/references/quality-control.md +1 -116
- package/skills/00-index.md +2 -2
- package/skills/artifacts.md +0 -22
- package/skills/production.md +2 -38
- package/web-app/dist/assets/{AdminPage-CYJ2BWRC.js → AdminPage-DhXdJNQk.js} +1 -1
- package/web-app/dist/assets/{Avatar-DdK4wynI.js → Avatar-ixVj76fM.js} +1 -1
- package/web-app/dist/assets/{Badge-CrX3MI7J.js → Badge-BGMSp22s.js} +1 -1
- package/web-app/dist/assets/{Button-709-wwGM.js → Button-pYJc32tG.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BapdTInO.js → CockpitPage-C1JBBouA.js} +1 -1
- package/web-app/dist/assets/{ComparePage-DT99pGvA.js → ComparePage-qD0NV4RK.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-B0uJSys8.js → ErrorBoundary-C-yxO58j.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-BtMFgElP.js → EvidenceReceiptPanel-ByPzuE1S.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-DjJuU941.js → GitHubIssuesPanel-BNv_2BEJ.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-HXfEX1BC.js → GitHubPRsPanel-CuLZQ9CS.js} +1 -1
- package/web-app/dist/assets/{HomePage-Cqyves-S.js → HomePage-CS_FW8Je.js} +2 -2
- package/web-app/dist/assets/{LoginPage-jUbtC7_Y.js → LoginPage-DwiNf1Wm.js} +1 -1
- package/web-app/dist/assets/{MagicPage-DHRsqjgR.js → MagicPage-CmQybLPZ.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-B-pED05R.js → MetricsPage-D9ERdpv8.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-CsbUs21R.js → NotFoundPage-D9ccgdbF.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-BtlLEQwB.js → ProjectPage-B9Jslmgv.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DaRbRjSp.js → ProjectsPage-DnU99Go2.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DALrtP3F.js → SettingsPage-DcTyx6wo.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-CoPHSNP0.js → ShowcasePage-jEY9ghSr.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-kOuSN50O.js → SystemSettingsPage-CSGltW-8.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DGWK0LMp.js → TeamsPage-gUuJXb9l.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-C-_10CwA.js → TemplatesPage-37Zhe--v.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Cz_vPbXL.js → TerminalOutput-B09hRwaR.js} +1 -1
- package/web-app/dist/assets/{activity-icIsXJVa.js → activity-q_4MY9J0.js} +1 -1
- package/web-app/dist/assets/{bell-DbPR2_4r.js → bell-X8OC_PFP.js} +1 -1
- package/web-app/dist/assets/{bot-DvjP3Yh5.js → bot-C-N1PUOD.js} +1 -1
- package/web-app/dist/assets/{check-D1OBxHeU.js → check-B2c76Jmp.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D09jDZ29.js → chevron-left-BF76YLNw.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DyZMLOcC.js → circle-alert-AeFKBD8h.js} +1 -1
- package/web-app/dist/assets/{clock-CdpdJTId.js → clock-CiXRf-FH.js} +1 -1
- package/web-app/dist/assets/{cloud-CoLTlmoj.js → cloud-DKQ8SRJ5.js} +1 -1
- package/web-app/dist/assets/{code-xml-DrkE2ZZY.js → code-xml-DEb6jXWP.js} +1 -1
- package/web-app/dist/assets/{database-CMQQuM2R.js → database-6W7e-E9c.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CuioDaOG.js → dollar-sign-Ds2GOuyW.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-Cx9j9K1S.js → file-code-corner-CZU0jg52.js} +1 -1
- package/web-app/dist/assets/{file-plus-CYD2zaaI.js → file-plus-DTVYnXCN.js} +1 -1
- package/web-app/dist/assets/{globe-6w-hnQkL.js → globe-0mUzKYDe.js} +1 -1
- package/web-app/dist/assets/{hammer-BpqtautL.js → hammer-BMHZg3YX.js} +1 -1
- package/web-app/dist/assets/{index-Dzs-9EgJ.js → index-BvOTvjax.js} +3 -3
- package/web-app/dist/assets/{layers-D5hd7jb7.js → layers-Df7TdcWB.js} +1 -1
- package/web-app/dist/assets/{loader-circle-C6Y9x_7H.js → loader-circle-kPsVfrSY.js} +1 -1
- package/web-app/dist/assets/{lock-D51L7rYh.js → lock-CXYcbEc5.js} +1 -1
- package/web-app/dist/assets/{package-BJF8hkP8.js → package-Cqhua1ID.js} +1 -1
- package/web-app/dist/assets/{plus-D5kUQ79n.js → plus-lseXhsn-.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-BKdlCa7F.js → refresh-cw-OZriI6gD.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-_bd_YI1e.js → rotate-ccw-CKCfjPg8.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DbkegIxF.js → scroll-text-DoVyyfKb.js} +1 -1
- package/web-app/dist/assets/{server-DAwqFIsq.js → server-DGy7TflW.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BuR1tPHu.js → shield-alert-BCzRcMXi.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-BF6p3mRd.js → thumbs-up-C_znJd5g.js} +1 -1
- package/web-app/dist/assets/{trash-2-BVpnkqqz.js → trash-2-BosCbeeA.js} +1 -1
- package/web-app/dist/assets/{trending-down-DpsDvRwW.js → trending-down-CkR5zDkV.js} +1 -1
- package/web-app/dist/assets/{trending-up-mFogTjt3.js → trending-up-DpUIfOH4.js} +1 -1
- package/web-app/dist/assets/{upload-4T8zpBsi.js → upload-CH9aKc8L.js} +1 -1
- package/web-app/dist/assets/{usePolling-DPXQv8WO.js → usePolling-pzxrHJeg.js} +1 -1
- package/web-app/dist/assets/{user-BYGIFaTm.js → user-B7OHa-VQ.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/dashboard/run.py +0 -63
- package/docs/openclaw-integration.md +0 -52
- package/docs/v10/HANDOFF-NEXT-WEEK.md +0 -40
- package/docs/v10/MORNING-BRIEF-1003.md +0 -25
- package/docs/v10/MORNING-BRIEF.md +0 -52
- package/docs/v10/TODO.md +0 -35
- package/mcp/resources.py +0 -351
- package/mcp/tools.py +0 -200
package/docs/PROOF-PASSPORT.md
CHANGED
|
@@ -29,3 +29,117 @@ trust limitations, but no repository or machine-specific paths.
|
|
|
29
29
|
Proof Passport does not currently sign the passport itself. Receipt signature
|
|
30
30
|
status is reported separately and must not be interpreted as a passport
|
|
31
31
|
signature.
|
|
32
|
+
|
|
33
|
+
## Verification record
|
|
34
|
+
|
|
35
|
+
Status: v1 (schema_version 1). Closes review finding LM-005: a passing outcome label could be misread when checks were not performed.
|
|
36
|
+
|
|
37
|
+
Implementation: the verification-record section of `autonomy/lib/proof-verify.py` (CLI: `proof-verify.py <label|summary|json> <file>`). Both routes of `loki verify` and `loki proof show` use this one module, so the bash and Bun paths cannot drift.
|
|
38
|
+
|
|
39
|
+
### Why three properties
|
|
40
|
+
|
|
41
|
+
A single "verified" word hides three different questions. The record answers each one separately and never lets one stand in for another.
|
|
42
|
+
|
|
43
|
+
1. **Integrity.** Is this record the one that was written, unaltered?
|
|
44
|
+
2. **Execution validity.** Did the checks run against the code in front of you, and in what environment?
|
|
45
|
+
3. **Requirement coverage.** Do the checks that ran actually cover what was asked for?
|
|
46
|
+
|
|
47
|
+
A signature alone proves neither execution validity nor requirement coverage. A signed record can describe checks run on a different commit, on a dirty tree, or against only some of the requirements. The signature says the bytes are unchanged. It says nothing about whether the claims in those bytes are current or complete.
|
|
48
|
+
|
|
49
|
+
### Property 1: integrity
|
|
50
|
+
|
|
51
|
+
| status | meaning |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `valid` | `verification.hash` matches the canonical bytes, gpg (when present) verifies, the recorded headline matches the headline re-derived from the facts, and the cost claim is coherent. |
|
|
54
|
+
| `invalid` | Any of the above failed. The label becomes `NOT VERIFIED (integrity failed ...)` regardless of what the checks say. |
|
|
55
|
+
| `not_recorded` | The record carries no hash. `.loki/verify/evidence.json` is always in this state: it is unhashed and unsigned. |
|
|
56
|
+
| `unchecked` | The integrity code could not run. |
|
|
57
|
+
|
|
58
|
+
The Ed25519 attestation is reported as present but is not verified inside the record. Use `loki proof verify --jwks` for that.
|
|
59
|
+
|
|
60
|
+
### Property 2: execution validity
|
|
61
|
+
|
|
62
|
+
Recorded with every receipt:
|
|
63
|
+
|
|
64
|
+
- `git.head_sha` and `git.dirty` (dirty ignores `.loki/`), plus `git.tree_sha256` when the generator computed it.
|
|
65
|
+
- `environment`: os, machine, python, node, bun and git versions, as found.
|
|
66
|
+
|
|
67
|
+
Status:
|
|
68
|
+
|
|
69
|
+
| status | meaning |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `fresh` | Recorded HEAD equals live HEAD and the tree digest (or clean flag) still matches. |
|
|
72
|
+
| `stale` | HEAD moved, or the working tree changed since the checks ran. Every previously passed check is reported as `stale`, and the label is `STALE (...)`. |
|
|
73
|
+
| `unknown` | Not enough was recorded to compare (older receipts, no git tree). Unknown is never promoted to fresh. The label carries `revision freshness unknown`. |
|
|
74
|
+
|
|
75
|
+
### Property 3: requirement coverage
|
|
76
|
+
|
|
77
|
+
Read from `.loki/state/requirements-map.json`:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{"requirements": [{"id": "R1", "text": "...", "checks": ["lint"], "status": "covered"}]}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
| status | meaning |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `recorded` | `total`, `covered`, `uncovered` and `uncovered_ids` are filled in. Uncovered requirements appear in the label. |
|
|
86
|
+
| `not_recorded` | No map exists. Coverage is unknown, not zero. Counts are `null`. |
|
|
87
|
+
| `unreadable` | The file exists but is not a valid map. Counts are `null`. |
|
|
88
|
+
|
|
89
|
+
`covered_but_check_not_passed` lists covered requirements whose named check did not pass. Matching check ids to gate names is best effort.
|
|
90
|
+
|
|
91
|
+
### Per-check status
|
|
92
|
+
|
|
93
|
+
Every check is exactly one of: `passed`, `failed`, `skipped`, `not_run`, `stale`.
|
|
94
|
+
|
|
95
|
+
Recorded words map as follows: pass-like words (`pass`, `passed`, `verified`, `ok`, `success`) become `passed`; fail-like words (`fail`, `failed`, `blocked`, `weakened`, `error`) become `failed`; `skipped` and `disabled` become `skipped`; `stale` stays `stale`. Everything else, including `inconclusive` and empty, becomes `not_run`. A check that did not reach a conclusion never reads as passed.
|
|
96
|
+
|
|
97
|
+
### Label rules
|
|
98
|
+
|
|
99
|
+
Evaluated in this order, first match wins:
|
|
100
|
+
|
|
101
|
+
1. Integrity `invalid`: `NOT VERIFIED (integrity failed: ...)`.
|
|
102
|
+
2. Any `failed` check: `NOT VERIFIED (failed: <ids>)`. A failed check never yields a passing label.
|
|
103
|
+
3. Record has no facts or no base verdict: `INCOMPLETE (...)`.
|
|
104
|
+
4. Base verdict is not `VERIFIED` or `VERIFIED WITH GAPS`: that verdict, unchanged.
|
|
105
|
+
5. Execution `stale`: `STALE (...)`.
|
|
106
|
+
6. Anything not run, recorded gaps, uncovered requirements, modified tests or unknown freshness: `VERIFIED (scoped: 5 of 7 checks ran; not run: lint, e2e)` with the applicable clauses.
|
|
107
|
+
7. Otherwise `VERIFIED`.
|
|
108
|
+
|
|
109
|
+
The bare word `VERIFIED` therefore means: integrity not failed, no failed checks, every check ran and passed, tree fresh, no uncovered requirements, no modified tests.
|
|
110
|
+
|
|
111
|
+
### Modified tests
|
|
112
|
+
|
|
113
|
+
`modified_tests` lists existing test files the change modified, deleted, renamed or type-changed, derived from `git diff --name-status` between the recorded base and head. Added test files are not listed. Status is `measured` or `unknown` (no base recorded or diff not computable). Modified tests appear in the label and in the summary because an implementer who edits the tests that judge the change weakens the evidence.
|
|
114
|
+
|
|
115
|
+
### Record shape
|
|
116
|
+
|
|
117
|
+
`loki proof show <id> --json` keeps the full proof. The verification record is produced by `proof-verify.py json`, embedded as `verification` in `.loki/verify/evidence.json`, and added as `verification` to the `loki proof verify` JSON. Fields are added, never renamed.
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"schema_version": 1,
|
|
122
|
+
"kind": "proof | evidence",
|
|
123
|
+
"label": "VERIFIED (scoped: ...)",
|
|
124
|
+
"base_verdict": "VERIFIED",
|
|
125
|
+
"integrity": {"status": "valid", "hash_ok": true, "signature": "none", "detail": "..."},
|
|
126
|
+
"execution": {"status": "fresh", "reason": "", "recorded": {}, "current": {}, "environment": {}},
|
|
127
|
+
"requirements": {"status": "recorded", "total": 3, "covered": 1, "uncovered": 2, "uncovered_ids": []},
|
|
128
|
+
"checks": [{"id": "lint", "status": "skipped", "detail": ""}],
|
|
129
|
+
"counts": {"passed": 1, "failed": 0, "skipped": 1, "not_run": 0, "stale": 0, "total": 2, "ran": 1},
|
|
130
|
+
"modified_tests": {"status": "measured", "files": [{"path": "tests/a.py", "change": "modified"}]},
|
|
131
|
+
"complete": true,
|
|
132
|
+
"record_path": ""
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Human output
|
|
137
|
+
|
|
138
|
+
`loki verify` (banner), `loki proof show <id>` and `loki proof verify <id> --human` print a compact summary of about 12 lines: the label, revision and freshness, environment, integrity, check counts with the checks that did not pass, requirement coverage, modified tests, and the signature caveat. Pass `--json` to `loki proof show` for the full record.
|
|
139
|
+
|
|
140
|
+
### Compatibility
|
|
141
|
+
|
|
142
|
+
- Exit codes are unchanged: `loki verify` keeps its verdict-based codes; `loki proof verify` keeps 0 clean, 1 tamper or drift, 2 unusable.
|
|
143
|
+
- Existing JSON fields are untouched. `verification` is additive.
|
|
144
|
+
- Receipts written before this contract lack `facts.environment` and `facts.git.dirty`. They report environment `not recorded` and may report freshness `unknown`.
|
|
145
|
+
- `loki proof show <id>` used to print the raw JSON. Scripts that parsed it must now pass `--json`.
|
|
@@ -115,3 +115,106 @@ mv loki-mode-${VERSION} ~/.claude/skills/loki-mode
|
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
**Best for:** Offline or air-gapped environments, pinned version deployments.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Setup requirements (LM-001)
|
|
122
|
+
|
|
123
|
+
This is the authoritative statement of what Loki Mode needs to run a first task. It is checked by tests against one shared table, `autonomy/lib/setup-requirements.json`. `loki doctor`, `loki doctor --requirements`, `loki doctor --json` (key `requirements`), `loki setup`, and the runtime "provider CLI missing" errors all read that table, so wording and fixes match.
|
|
124
|
+
|
|
125
|
+
Normal repository build dependencies (the toolchain of the project you point Loki at) are not Loki overhead and are not listed.
|
|
126
|
+
|
|
127
|
+
### How to read your machine
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
loki doctor --requirements # per path: ready or not ready, each item required or optional, present or missing, one-line fix
|
|
131
|
+
loki doctor --json # same data under "requirements", plus "first_task_ready"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
A first task is ready when every required "All paths" item is present and at least one execution path is ready. `first_task_ready` in the JSON reports exactly that. It checks presence on this machine; it does not prove that a provider login is valid or that an API key is accepted.
|
|
135
|
+
|
|
136
|
+
### Execution paths
|
|
137
|
+
|
|
138
|
+
- Claude Code CLI path: the default. `loki start` runs the `claude` binary you installed and logged in with `claude auth login`. `loki chat` (with the claude provider) and `loki edit` call the `claude` CLI directly.
|
|
139
|
+
- Anthropic API-key path (Agent SDK, no `claude` CLI): opt-in. It needs `LOKI_SDK_MODE=full` (resolved to `LOKI_SDK_LOOP=1` by `bin/loki`), Anthropic credentials, the bundled `@anthropic-ai/claude-agent-sdk` platform binary, and Bun. It runs only on the Bun route of `loki start`; commands that stay on the bash route (for example `loki demo`, `loki quick`) still need a provider CLI on PATH. Without `LOKI_SDK_MODE=full`, an API key alone does not replace the `claude` CLI for the main loop.
|
|
140
|
+
- Other provider CLIs: `codex` (degraded), `cline`, `aider`, `opencode`. Any one of them replaces the `claude` CLI for `loki start --provider <name>`. Loki never silently substitutes a different provider than the one requested.
|
|
141
|
+
|
|
142
|
+
### Install methods
|
|
143
|
+
|
|
144
|
+
| Install method | What it provides | What you still need |
|
|
145
|
+
| --- | --- | --- |
|
|
146
|
+
| `npm install -g loki-mode` | Loki, a bundled Bun binary and the Claude Agent SDK as npm optional dependencies | Node 20+, python3, jq, git, curl, and one execution path above |
|
|
147
|
+
| `npm install -g loki-mode --omit=optional` (slim) | Loki only; no bundled Bun, no Agent SDK | Everything above, plus Bun from https://bun.sh for the Bun route. Without Bun, `bin/loki` falls back to the bash CLI. The API-key path is unavailable |
|
|
148
|
+
| Homebrew (`brew install loki-mode`) | Loki | Same as npm, and install Bun yourself if you want the Bun route |
|
|
149
|
+
| git clone | Loki sources | Same as npm; see the Git Clone section above for the clone steps |
|
|
150
|
+
|
|
151
|
+
Supported platforms are the `os` entries in `package.json` (darwin, linux).
|
|
152
|
+
|
|
153
|
+
### Requirements by path
|
|
154
|
+
|
|
155
|
+
The tables below are generated from the shared source. Do not edit between the markers; run `UPDATE_CONTRACT=1 bun test tests/commands/doctor.test.ts` from `loki-ts` after editing the JSON, and the test fails if this block drifts.
|
|
156
|
+
|
|
157
|
+
<!-- BEGIN GENERATED REQUIREMENTS (loki-ts/tests/commands/doctor.test.ts) -->
|
|
158
|
+
|
|
159
|
+
### All paths
|
|
160
|
+
|
|
161
|
+
Needed by every Loki command.
|
|
162
|
+
|
|
163
|
+
| Requirement | Level | Check | Fix |
|
|
164
|
+
| --- | --- | --- | --- |
|
|
165
|
+
| Node.js 20 or newer | required | `node` on PATH, version 20.0 or newer | install Node 20+ from https://nodejs.org |
|
|
166
|
+
| Python 3.8 or newer | required | `python3` on PATH, version 3.8 or newer | install python3 with your package manager (brew install python3) |
|
|
167
|
+
| jq | required | `jq` on PATH | install jq with your package manager (brew install jq) |
|
|
168
|
+
| git | required | `git` on PATH | install git with your package manager (brew install git) |
|
|
169
|
+
| curl | required | `curl` on PATH | install curl with your package manager (brew install curl) |
|
|
170
|
+
| bash 4.0 or newer | optional | `bash` on PATH, version 4.0 or newer | install a newer bash (brew install bash); macOS ships 3.2 |
|
|
171
|
+
| Bun 1.3 or newer (bundled by npm install) | optional | `bun` on PATH, version 1.3 or newer | reinstall without --no-optional (npm bundles @oven/bun-*) or install from https://bun.sh; without it loki falls back to the slower bash CLI |
|
|
172
|
+
|
|
173
|
+
### Claude Code CLI path (default, loki start)
|
|
174
|
+
|
|
175
|
+
Loki drives the claude binary you installed and logged in.
|
|
176
|
+
|
|
177
|
+
| Requirement | Level | Check | Fix |
|
|
178
|
+
| --- | --- | --- | --- |
|
|
179
|
+
| claude CLI on PATH (then log in: claude auth login) | required | `claude` on PATH | npm install -g @anthropic-ai/claude-code |
|
|
180
|
+
|
|
181
|
+
### Anthropic API-key path (Agent SDK, no claude CLI)
|
|
182
|
+
|
|
183
|
+
Opt-in with LOKI_SDK_MODE=full; the bundled Agent SDK runs the loop over HTTPS.
|
|
184
|
+
|
|
185
|
+
| Requirement | Level | Check | Fix |
|
|
186
|
+
| --- | --- | --- | --- |
|
|
187
|
+
| SDK loop enabled (LOKI_SDK_MODE=full) | required | `LOKI_SDK_LOOP` is `1` or `true` | export LOKI_SDK_MODE=full |
|
|
188
|
+
| Anthropic credentials (API key, auth token or gateway URL) | required | one of `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL` is set | export ANTHROPIC_API_KEY=<your key> |
|
|
189
|
+
| Claude Agent SDK platform binary (optional npm dependency) | required | executable `@anthropic-ai/claude-agent-sdk-<platform>/claude` in an installed node_modules | reinstall without --no-optional: npm install -g loki-mode (the SDK is an optionalDependency) |
|
|
190
|
+
| Bun runtime (the SDK loop runs only on the Bun route) | required | `bun` on PATH, version 1.3 or newer | reinstall without --no-optional (npm bundles @oven/bun-*) or install from https://bun.sh |
|
|
191
|
+
|
|
192
|
+
### Other provider CLIs
|
|
193
|
+
|
|
194
|
+
codex (degraded), cline, aider, opencode: any one replaces the claude CLI.
|
|
195
|
+
|
|
196
|
+
| Requirement | Level | Check | Fix |
|
|
197
|
+
| --- | --- | --- | --- |
|
|
198
|
+
| codex CLI | optional | `codex` on PATH | npm install -g @openai/codex |
|
|
199
|
+
| cline CLI | optional | `cline` on PATH | npm install -g cline |
|
|
200
|
+
| aider CLI | optional | `aider` on PATH | pip install aider-chat |
|
|
201
|
+
| opencode CLI | optional | `opencode` on PATH | npm install -g opencode-ai |
|
|
202
|
+
|
|
203
|
+
### When an optional dependency is missing
|
|
204
|
+
|
|
205
|
+
| Missing | Effect |
|
|
206
|
+
| --- | --- |
|
|
207
|
+
| @anthropic-ai/claude-agent-sdk (or its platform binary) | The api-key-sdk path is unavailable and doctor reports it not ready. The claude-cli path and all other providers are unaffected. |
|
|
208
|
+
| @oven/bun-* (Bun) | bin/loki falls back to the bash CLI (slower). The api-key-sdk path is unavailable because the SDK loop runs only on Bun. |
|
|
209
|
+
|
|
210
|
+
<!-- END GENERATED REQUIREMENTS -->
|
|
211
|
+
|
|
212
|
+
### Install smoke test
|
|
213
|
+
|
|
214
|
+
`scripts/smoke-install.sh` packs the repository, installs the tarball into a temporary prefix, and runs `loki --version`, `loki doctor --json` and `loki route "add a health endpoint" --json`. It then repeats with Bun and the optional Agent SDK hidden. It is run by hand, is not part of CI, and writes a reproducible log. A run on one machine is evidence for that machine and OS only; it is not an OS matrix.
|
|
215
|
+
|
|
216
|
+
### Known behaviors
|
|
217
|
+
|
|
218
|
+
- Bun-less fallback: when Bun is absent, `loki doctor --json` runs a minimal bash evaluator (`loki_requirements_json` in `autonomy/provider-offer.sh`) over the same table. It checks presence only and does not compare versions, so the Node minimum is enforced only by the Bun doctor.
|
|
219
|
+
- `first_task_ready` does not verify that `claude auth login` was done or that an API key is valid.
|
|
220
|
+
- Observed once, with npm 11.17.0 on macOS arm64 and a local tarball: `npm install -g --omit=optional` still installed `@oven/bun-*` and the Claude Agent SDK packages. The smoke test removes them after install so the second case models a machine without them. This was not checked on other npm versions or from the registry.
|
package/docs/authentication.md
CHANGED
|
@@ -97,6 +97,20 @@ Scope hierarchy:
|
|
|
97
97
|
- `control` includes `write` and `read`
|
|
98
98
|
- `write` includes `read`
|
|
99
99
|
|
|
100
|
+
### Key Rotation and Revocation
|
|
101
|
+
|
|
102
|
+
Dashboard API keys (`/api/keys`) can be rotated with a grace period during which the old key stays valid (default 24 hours, `grace_period_hours`):
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
curl -X POST http://localhost:57374/api/keys/ci-pipeline/rotate \
|
|
106
|
+
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
|
107
|
+
-H "Content-Type: application/json" \
|
|
108
|
+
-d '{"grace_period_hours": 24}'
|
|
109
|
+
|
|
110
|
+
curl -X DELETE http://localhost:57374/api/keys/ci-pipeline \
|
|
111
|
+
-H "Authorization: Bearer $ADMIN_TOKEN"
|
|
112
|
+
```
|
|
113
|
+
|
|
100
114
|
### Roles
|
|
101
115
|
|
|
102
116
|
Predefined roles map to common access patterns:
|
|
@@ -177,7 +191,11 @@ Running with signature verification off provides no authentication security.
|
|
|
177
191
|
4. Provider redirects back with JWT
|
|
178
192
|
5. Dashboard validates JWT and grants access
|
|
179
193
|
|
|
180
|
-
OIDC-authenticated users
|
|
194
|
+
OIDC-authenticated users are NOT granted full access by default. The role is resolved from claims (see below) and its scopes are enforced through the same hierarchy as API tokens.
|
|
195
|
+
|
|
196
|
+
### OIDC Role Claims
|
|
197
|
+
|
|
198
|
+
Role claims are read case-insensitively; values may be a string, a space-separated string or a list. Sources: the claim named by `LOKI_OIDC_ROLES_CLAIM` (dotted paths such as `realm_access.roles` work), then `roles`, `groups`, `realm_access.roles` (Keycloak) and `cognito:groups` (AWS Cognito). Only values that exactly match a built-in role name (`admin`, `operator`, `viewer`, `auditor`) grant that role; other group names fall through to `LOKI_OIDC_DEFAULT_ROLE`. When several roles match, the highest privilege wins (`admin` > `operator` > `auditor` > `viewer`). An unset or unrecognized default falls back to `viewer`, never `admin`. When PyJWT and cryptography are installed, the signature (RS256/RS384/RS512), issuer, audience and expiry are verified against the issuer JWKS.
|
|
181
199
|
|
|
182
200
|
### Mixed Mode
|
|
183
201
|
|
package/docs/cost-controls.md
CHANGED
|
@@ -41,6 +41,127 @@ while spending almost nothing and completing no iteration, so neither of those
|
|
|
41
41
|
breakers ever trips. The wall-clock cap is the one that catches it. We added it
|
|
42
42
|
after a run burned $34 reaching an external timeout.
|
|
43
43
|
|
|
44
|
+
## Budget contract
|
|
45
|
+
|
|
46
|
+
The per-run spend cap: its default, how it is chosen, what it covers, where it is
|
|
47
|
+
enforced, how far a run can overshoot, what happens when a price is missing, and
|
|
48
|
+
what happens when a run is cancelled. The CLI help, the run-start line and this
|
|
49
|
+
section must agree; tests pin them.
|
|
50
|
+
|
|
51
|
+
### Default
|
|
52
|
+
|
|
53
|
+
The default cap is **100 USD per run**.
|
|
54
|
+
|
|
55
|
+
The number lives in one file, `loki-ts/data/model-pricing.json`
|
|
56
|
+
(`default_max_cost_usd`). Both routes read it:
|
|
57
|
+
|
|
58
|
+
- TypeScript: `loki-ts/src/e10ext/budget_cap.ts` (`DEFAULT_MAX_COST_USD`).
|
|
59
|
+
- Bash: `autonomy/run.sh` (`BUDGET_LIMIT`) and the help text through
|
|
60
|
+
`loki_budget_default_usd` in `autonomy/loki`.
|
|
61
|
+
|
|
62
|
+
`loki-ts/tests/engine10/budget_cap.test.ts` asserts that this document, the JSON
|
|
63
|
+
file, the TypeScript constant, the bash default and the CLI help all state the same
|
|
64
|
+
number. Change the JSON file and the document together.
|
|
65
|
+
|
|
66
|
+
Where the default applies:
|
|
67
|
+
|
|
68
|
+
| Route | Default applies | What the cap does |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| engine10 (`loki "<task>"`, the Loki 10 engine) | Only when an API key is set (`ANTHROPIC_API_KEY` or `OPENAI_API_KEY`). A subscription run (no key) has no dollar cap. | Ends the run `BUDGET_STOP`, exit 3. |
|
|
71
|
+
| Bash loop (`loki start`, `autonomy/run.sh`) | Always, key or not. | Pauses the run (writes `.loki/PAUSE` and `.loki/signals/BUDGET_EXCEEDED`); remove both to resume. |
|
|
72
|
+
| Bun SDK loop (`LOKI_SDK_LOOP`) | No default. Opt in with `BUDGET_LIMIT`. | Stops the loop when the limit is reached. |
|
|
73
|
+
|
|
74
|
+
The README and the engine guide have shown different figures in the past (100 and
|
|
75
|
+
20). The figure above is the code's, and the two guides follow it.
|
|
76
|
+
|
|
77
|
+
### Precedence
|
|
78
|
+
|
|
79
|
+
Highest first. The first one set wins.
|
|
80
|
+
|
|
81
|
+
1. `--max-cost USD` (engine10) or `--budget USD` (bash route). `loki start --budget`
|
|
82
|
+
is forwarded to engine10 as `--max-cost`.
|
|
83
|
+
2. The environment variable `LOKI_BUDGET_LIMIT`. On the bash route an empty value
|
|
84
|
+
(`LOKI_BUDGET_LIMIT=`) means unlimited.
|
|
85
|
+
3. Config file: `budgets.per_run` in `loki.yaml` (engine10), or the budget setting in
|
|
86
|
+
the settings file loaded by `autonomy/run.sh` (bash route).
|
|
87
|
+
4. The default above (subject to the API-key rule for engine10).
|
|
88
|
+
|
|
89
|
+
### Run-start line
|
|
90
|
+
|
|
91
|
+
Every start prints the active cap and where it came from.
|
|
92
|
+
|
|
93
|
+
- engine10: the start line ends the cap clause with `cap $X.XX (SOURCE)` where SOURCE
|
|
94
|
+
is `--max-cost`, `env`, `loki.yaml` or `default`, or `no dollar cap (subscription)`.
|
|
95
|
+
The default output stays within its 8-line limit.
|
|
96
|
+
- bash route: `Budget limit set: $X.XX (source: flag|env|config|default)`.
|
|
97
|
+
|
|
98
|
+
A cap set through the bash settings file is reported as `config`; the `--config`
|
|
99
|
+
file loader exports the variable, so it reports as `env`.
|
|
100
|
+
|
|
101
|
+
### Scope
|
|
102
|
+
|
|
103
|
+
- **Per run.** The cap covers one run, not a day or a repository.
|
|
104
|
+
- **Retries.** Every session counts, including retried stages and sessions started
|
|
105
|
+
after a provider failover. Each session writes its own cost record, and the run
|
|
106
|
+
total is the sum over every session started, so a retry adds to the total.
|
|
107
|
+
- **Child work.** Subagents and tool calls made inside a provider session are part of
|
|
108
|
+
that session's reported cost. Sessions run in a temporary directory (the Wall stage)
|
|
109
|
+
have their cost record copied into the run before the directory is removed.
|
|
110
|
+
- **Not counted.** The detached deep verify that may start after a PR opens is a
|
|
111
|
+
separate process and is not counted against the run cap. Sessions with no known
|
|
112
|
+
price are not counted either (see "Missing prices").
|
|
113
|
+
|
|
114
|
+
### Enforcement point and overshoot
|
|
115
|
+
|
|
116
|
+
engine10 checks the cap before each stage starts (`machine.ts`, `capReached`), using
|
|
117
|
+
the cost events recorded so far. A stage that is already running is not interrupted by
|
|
118
|
+
the dollar cap. The documented overshoot is therefore up to the cost of the stages in
|
|
119
|
+
flight when the cap is crossed (the plan and wall stages can run together). Time caps
|
|
120
|
+
and the per-session time limit still apply inside a stage.
|
|
121
|
+
|
|
122
|
+
The bash route checks after each iteration (`check_budget_limit`), so the overshoot is
|
|
123
|
+
up to one iteration.
|
|
124
|
+
|
|
125
|
+
### Missing prices
|
|
126
|
+
|
|
127
|
+
Cost is never reported as 0 when it was not measured.
|
|
128
|
+
|
|
129
|
+
- A session with no dollar figure records `usd: null` in its cost event. Token counts
|
|
130
|
+
are kept when the provider reported them.
|
|
131
|
+
- A model with no entry in the price table (`loki-ts/src/providers/price_table.ts`)
|
|
132
|
+
is not priced with a default guess. The cost record carries `total_cost_usd: null`,
|
|
133
|
+
`cost_basis: "unpriced"` and an `unmeasured_reason` naming the model.
|
|
134
|
+
- The bash iteration record omits `cost_usd` and writes `cost_unmeasured_reason`
|
|
135
|
+
instead of `"cost_usd": 0`.
|
|
136
|
+
- Unmeasured sessions never count toward the cap, so a run with unpriced models is not
|
|
137
|
+
stopped by a guess. The run summary says `not measured`, or `partial: $X for N of M
|
|
138
|
+
sessions` when only some sessions were priced. It never prints `$0.00` for a run
|
|
139
|
+
whose cost is unknown. The cap is only as good as the prices behind it: a run on a
|
|
140
|
+
model the table does not know is effectively uncapped, and the summary says so.
|
|
141
|
+
|
|
142
|
+
### Cancellation
|
|
143
|
+
|
|
144
|
+
SIGINT or SIGTERM during an engine10 run:
|
|
145
|
+
|
|
146
|
+
1. The supervisor forwards SIGTERM to the worker process group and keeps reading its
|
|
147
|
+
output.
|
|
148
|
+
2. The worker stops every live provider session. All providers share one spawn path
|
|
149
|
+
(`createSessionRunner`), so one rule covers them all: SIGTERM to the session's
|
|
150
|
+
process group, then SIGKILL after 2 seconds. No new session starts.
|
|
151
|
+
3. Each stopped session records the usage measured so far, with its unmeasured flag
|
|
152
|
+
when no dollar figure exists (`usd: null`).
|
|
153
|
+
4. If the worker has not exited after the cancel grace (10 seconds, set with
|
|
154
|
+
`LOKI_E10_CANCEL_GRACE_MS`), the supervisor SIGKILLs the group. A second signal
|
|
155
|
+
escalates immediately.
|
|
156
|
+
5. The supervisor opens no PR and makes no backstop commit, restores the branch the
|
|
157
|
+
run started on, and appends `run.completed` with `cancelled: true`,
|
|
158
|
+
`outcome: "CANCELLED"` and the cost folded from the events. `loki status` shows the
|
|
159
|
+
run as `cancelled`, distinct from `FAILED`.
|
|
160
|
+
6. The run summary is printed with `Outcome: CANCELLED` and the process exits **130**.
|
|
161
|
+
|
|
162
|
+
The test `loki-ts/tests/engine10/supervisor_signal.test.ts` drives this with a stub
|
|
163
|
+
provider that sleeps.
|
|
164
|
+
|
|
44
165
|
## Hitting a cap is a FAILURE, not a success
|
|
45
166
|
|
|
46
167
|
This is the part that matters for anyone automating against us.
|
|
@@ -86,3 +207,21 @@ What we do instead:
|
|
|
86
207
|
|
|
87
208
|
If a run cost more than you expected, `loki proof show <id>` tells you where it
|
|
88
209
|
went.
|
|
210
|
+
|
|
211
|
+
## Backend capability contract (LM-004)
|
|
212
|
+
|
|
213
|
+
One table in `loki-ts/src/providers/provider_guard.ts` (`CAPABILITIES`) records what each provider guarantees on engine10. `loki provider matrix` prints it (all engines), `loki provider info <name> --json` prints one provider's rows with the source files each value was read from, and `loki start` prints one disclosure line before the run when the provider falls short of Claude. Claude prints nothing extra. On the legacy bash engine only the invocation and sandbox flags are confirmed from code; every other value there is `unknown`, which means not confirmed, not absent.
|
|
214
|
+
|
|
215
|
+
- cost: `measured` = provider-reported; `estimated` = parsed or size-based and flagged estimated; `none` = reported as not measured.
|
|
216
|
+
- budget kill: `post-iteration` = no further stage starts once priced cost reaches the cap; `none` = no cap applies.
|
|
217
|
+
- cancel (pgroup): the session child is killed as a whole process group.
|
|
218
|
+
- guard scan: `native` = the provider's own hooks gate each call; `post-iteration` = diff scan after each iteration, reverting destructive changes.
|
|
219
|
+
- kill blocking: `native` = enforced; `none` = receipts list `kill blocking not enforced`.
|
|
220
|
+
|
|
221
|
+
| provider | engine | cost | budget kill | cancel (pgroup) | sandbox flags | receipt | resume | guard scan | kill blocking |
|
|
222
|
+
|---|---|---|---|---|---|---|---|---|---|
|
|
223
|
+
| claude | engine10 | measured | post-iteration | yes | none | yes | yes | native | native |
|
|
224
|
+
| codex | engine10 | estimated | post-iteration | yes | --sandbox workspace-write | yes | no | post-iteration | none |
|
|
225
|
+
| cline | engine10 | estimated | post-iteration | yes | none | yes | no | post-iteration | none |
|
|
226
|
+
| aider | engine10 | estimated | post-iteration | yes | --no-auto-commits | yes | no | post-iteration | none |
|
|
227
|
+
| opencode | engine10 | none | none | yes | none | yes | no | post-iteration | none |
|
|
@@ -4,9 +4,10 @@ Moved out of CLAUDE.md (S-78) to keep the root file short. Updated in this
|
|
|
4
4
|
pass to remove two items that `docs/v10/DECISIONS.md` D25 and
|
|
5
5
|
`docs/LOKI-10-BUILD-PROMPT.md` section 1 have superseded:
|
|
6
6
|
- `git add -A` in step 4 is replaced with staging by name (repo rule, always).
|
|
7
|
-
- Per-merge pushing
|
|
8
|
-
approved
|
|
9
|
-
|
|
7
|
+
- Per-merge pushing and the D25 release trains are superseded by D96 (every
|
|
8
|
+
approved slice is a release; see `docs/v10/OPERATING-MODEL.md`). Waiting for
|
|
9
|
+
Tests and Security Audit green on that exact SHA still applies to any push
|
|
10
|
+
made outside the merge-gate.
|
|
10
11
|
"Wait for user approval before commit" from the old workflow is superseded
|
|
11
12
|
by the standing authorization in `docs/LOKI-10-BUILD-PROMPT.md` section 1
|
|
12
13
|
for sessions operating under that program; outside it, ask as normal.
|
|
@@ -116,13 +117,13 @@ rm -f loki-mode-*.tgz
|
|
|
116
117
|
```
|
|
117
118
|
If any check fails, do not release; fix the root cause first.
|
|
118
119
|
|
|
119
|
-
### 4. Commit and land (release
|
|
120
|
+
### 4. Commit and land (every approved slice is a release, D96)
|
|
120
121
|
|
|
121
122
|
Stage files by name (never `git add -A` or `git add .`), commit with the
|
|
122
|
-
repo-local `asklokesh` identity, no co-author.
|
|
123
|
-
|
|
124
|
-
Security Audit to go green on that
|
|
125
|
-
releasing. GitHub Actions then creates the tag, the GitHub Release, publishes
|
|
123
|
+
repo-local `asklokesh` identity, no co-author. The merge-gate merges approved
|
|
124
|
+
slices to main and main publishes to npm `next` (D96); for a manual push
|
|
125
|
+
(`git push origin main`), wait for Tests and Security Audit to go green on that
|
|
126
|
+
exact SHA before bumping VERSION and releasing. GitHub Actions then creates the tag, the GitHub Release, publishes
|
|
126
127
|
npm, builds/pushes Docker, and updates the Homebrew tap. Do not manually
|
|
127
128
|
create tags.
|
|
128
129
|
|