loreli 0.0.0 → 2.0.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/LICENSE +1 -1
- package/README.md +710 -97
- package/bin/loreli.js +89 -0
- package/package.json +77 -14
- package/packages/README.md +101 -0
- package/packages/action/README.md +98 -0
- package/packages/action/prompts/action.md +172 -0
- package/packages/action/src/index.js +684 -0
- package/packages/agent/README.md +606 -0
- package/packages/agent/src/backends/claude.js +387 -0
- package/packages/agent/src/backends/codex.js +351 -0
- package/packages/agent/src/backends/cursor.js +371 -0
- package/packages/agent/src/backends/index.js +486 -0
- package/packages/agent/src/base.js +138 -0
- package/packages/agent/src/cli.js +275 -0
- package/packages/agent/src/discover.js +396 -0
- package/packages/agent/src/factory.js +124 -0
- package/packages/agent/src/index.js +12 -0
- package/packages/agent/src/models.js +159 -0
- package/packages/agent/src/output.js +62 -0
- package/packages/agent/src/session.js +162 -0
- package/packages/agent/src/trace.js +186 -0
- package/packages/classify/README.md +136 -0
- package/packages/classify/prompts/blocker.md +12 -0
- package/packages/classify/prompts/feedback.md +14 -0
- package/packages/classify/prompts/pane-state.md +20 -0
- package/packages/classify/src/index.js +81 -0
- package/packages/config/README.md +898 -0
- package/packages/config/src/defaults.js +145 -0
- package/packages/config/src/index.js +223 -0
- package/packages/config/src/schema.js +291 -0
- package/packages/config/src/validate.js +160 -0
- package/packages/context/README.md +165 -0
- package/packages/context/src/index.js +198 -0
- package/packages/hub/README.md +338 -0
- package/packages/hub/src/base.js +154 -0
- package/packages/hub/src/github.js +1597 -0
- package/packages/hub/src/index.js +79 -0
- package/packages/hub/src/labels.js +48 -0
- package/packages/identity/README.md +288 -0
- package/packages/identity/src/index.js +620 -0
- package/packages/identity/src/themes/avatar.js +217 -0
- package/packages/identity/src/themes/digimon.js +217 -0
- package/packages/identity/src/themes/dragonball.js +217 -0
- package/packages/identity/src/themes/lotr.js +217 -0
- package/packages/identity/src/themes/marvel.js +217 -0
- package/packages/identity/src/themes/pokemon.js +217 -0
- package/packages/identity/src/themes/starwars.js +217 -0
- package/packages/identity/src/themes/transformers.js +217 -0
- package/packages/identity/src/themes/zelda.js +217 -0
- package/packages/knowledge/README.md +217 -0
- package/packages/knowledge/src/index.js +243 -0
- package/packages/log/README.md +93 -0
- package/packages/log/src/index.js +252 -0
- package/packages/marker/README.md +200 -0
- package/packages/marker/src/index.js +184 -0
- package/packages/mcp/README.md +323 -0
- package/packages/mcp/instructions.md +126 -0
- package/packages/mcp/scaffolding/.agents/skills/loreli-context/SKILL.md +89 -0
- package/packages/mcp/scaffolding/ISSUE_TEMPLATE/config.yml +2 -0
- package/packages/mcp/scaffolding/ISSUE_TEMPLATE/loreli.yml +83 -0
- package/packages/mcp/scaffolding/loreli.yml +491 -0
- package/packages/mcp/scaffolding/mcp-configs/.codex/config.toml +4 -0
- package/packages/mcp/scaffolding/mcp-configs/.cursor/mcp.json +14 -0
- package/packages/mcp/scaffolding/mcp-configs/.mcp.json +14 -0
- package/packages/mcp/scaffolding/pull-request.md +23 -0
- package/packages/mcp/src/index.js +600 -0
- package/packages/mcp/src/tools/agent-context.js +44 -0
- package/packages/mcp/src/tools/agents.js +450 -0
- package/packages/mcp/src/tools/context.js +200 -0
- package/packages/mcp/src/tools/github.js +1163 -0
- package/packages/mcp/src/tools/hitl.js +162 -0
- package/packages/mcp/src/tools/index.js +18 -0
- package/packages/mcp/src/tools/refactor.js +227 -0
- package/packages/mcp/src/tools/repo.js +44 -0
- package/packages/mcp/src/tools/start.js +904 -0
- package/packages/mcp/src/tools/status.js +149 -0
- package/packages/mcp/src/tools/work.js +134 -0
- package/packages/orchestrator/README.md +192 -0
- package/packages/orchestrator/src/index.js +1492 -0
- package/packages/planner/README.md +251 -0
- package/packages/planner/prompts/plan-reviewer.md +109 -0
- package/packages/planner/prompts/planner.md +191 -0
- package/packages/planner/prompts/tiebreaker-reviewer.md +71 -0
- package/packages/planner/src/index.js +1381 -0
- package/packages/review/README.md +129 -0
- package/packages/review/prompts/reviewer.md +158 -0
- package/packages/review/src/index.js +1403 -0
- package/packages/risk/README.md +178 -0
- package/packages/risk/prompts/risk.md +272 -0
- package/packages/risk/src/index.js +439 -0
- package/packages/session/README.md +165 -0
- package/packages/session/src/index.js +215 -0
- package/packages/test-utils/README.md +96 -0
- package/packages/test-utils/src/index.js +354 -0
- package/packages/tmux/README.md +261 -0
- package/packages/tmux/src/index.js +501 -0
- package/packages/workflow/README.md +317 -0
- package/packages/workflow/prompts/preamble.md +14 -0
- package/packages/workflow/src/index.js +660 -0
- package/packages/workflow/src/proof-of-life.js +74 -0
- package/packages/workspace/README.md +143 -0
- package/packages/workspace/src/index.js +1127 -0
- package/index.js +0 -8
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
# loreli/config
|
|
2
|
+
|
|
3
|
+
Centralized configuration for Loreli. Reads `loreli.yml` from target repositories, merges with start parameters and environment variables, and provides typed defaults for every configurable value in the system.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart TD
|
|
9
|
+
BP["Start Params"] --> Merge["Config.merge()"]
|
|
10
|
+
YML["loreli.yml (repo)"] --> Load["Config.load()"]
|
|
11
|
+
Load --> Resolve["Config.get(path)"]
|
|
12
|
+
Merge --> Resolve
|
|
13
|
+
DOT[".env file"] --> LoadEnv["loadEnv()"]
|
|
14
|
+
LoadEnv --> PEnv["process.env"]
|
|
15
|
+
PEnv --> Resolve
|
|
16
|
+
DEF["Built-in Defaults"] --> Resolve
|
|
17
|
+
Resolve --> Value["Resolved Value"]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Config values are resolved through four layers, highest priority first:
|
|
21
|
+
|
|
22
|
+
1. **Start tool params** -- explicit per-invocation overrides via `config.merge()`
|
|
23
|
+
2. **`loreli.yml`** -- repo-level config read via `hub.read()` during `config.load()`
|
|
24
|
+
3. **Environment variables** -- shell env and `.env` file (via `loadEnv()`)
|
|
25
|
+
4. **Built-in defaults** -- hardcoded in `defaults.js`
|
|
26
|
+
|
|
27
|
+
## API Reference
|
|
28
|
+
|
|
29
|
+
### `new Config()`
|
|
30
|
+
|
|
31
|
+
Creates a config instance with empty layers. All `get()` calls return built-in defaults until `load()` or `merge()` is called.
|
|
32
|
+
|
|
33
|
+
### `config.load(hub, repo)` -> `Promise<boolean>`
|
|
34
|
+
|
|
35
|
+
Read `loreli.yml` from the target repo root via `hub.read()`. Returns `true` if the file was found, `false` otherwise. When the file is missing, defaults apply gracefully.
|
|
36
|
+
|
|
37
|
+
The following example demonstrates loading config from a repo, which is the first step in start:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { Config } from 'loreli/config';
|
|
41
|
+
|
|
42
|
+
const config = new Config();
|
|
43
|
+
const found = await config.load(hub, 'owner/repo');
|
|
44
|
+
// found === true if loreli.yml exists
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `config.loadLocal(path?)` -> `boolean`
|
|
48
|
+
|
|
49
|
+
Read local `loreli.yml` from disk (default `./loreli.yml`) using the same parser and schema validation as `load()`. Returns `true` when the file exists and parses, `false` otherwise.
|
|
50
|
+
|
|
51
|
+
The following example demonstrates loading config in standalone CLI contexts where no Hub instance is available yet:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { Config } from 'loreli/config';
|
|
55
|
+
|
|
56
|
+
const config = new Config();
|
|
57
|
+
const found = config.loadLocal('loreli.yml');
|
|
58
|
+
// found === true when local loreli.yml exists
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `config.merge(overrides)`
|
|
62
|
+
|
|
63
|
+
Apply a plain object on top of all other layers. Nested objects are shallow-merged one level deep. Values are validated through the schema -- invalid types are silently discarded.
|
|
64
|
+
|
|
65
|
+
The following example shows how start params override file-level config:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
config.merge({ theme: 'pokemon', reviewers: ['alice'] });
|
|
69
|
+
config.get('theme'); // 'pokemon' (overridden)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `config.get(path)` -> `*`
|
|
73
|
+
|
|
74
|
+
Resolve a config value through all layers using dot-notation. Returns `undefined` for completely unknown paths.
|
|
75
|
+
|
|
76
|
+
The following example demonstrates accessing nested and top-level values:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
config.get('theme'); // 'transformers'
|
|
80
|
+
config.get('repo'); // 'owner/repo' (when configured)
|
|
81
|
+
config.get('merge.method'); // 'squash'
|
|
82
|
+
config.get('timeouts.stall'); // 600000
|
|
83
|
+
config.get('reviewers'); // []
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `config.toJSON()` -> `object`
|
|
87
|
+
|
|
88
|
+
Serialize the fully resolved config to a plain object, walking every default key and resolving each through the layer chain. Used for session persistence.
|
|
89
|
+
|
|
90
|
+
### `config.found` -> `boolean`
|
|
91
|
+
|
|
92
|
+
Whether `loreli.yml` was found during the last `load()` call. Used by start to decide if the file needs scaffolding.
|
|
93
|
+
|
|
94
|
+
### `loadEnv(path?)` -> `boolean`
|
|
95
|
+
|
|
96
|
+
Load a `.env` file into `process.env` using Node's built-in `process.loadEnvFile()`. Variables already present in the shell environment are **not** overwritten, so shell exports always win.
|
|
97
|
+
|
|
98
|
+
Returns `true` if the file was loaded, `false` if it does not exist. Any other I/O error is re-thrown.
|
|
99
|
+
|
|
100
|
+
The following example demonstrates loading a `.env` file before creating a `Config` instance, which is the recommended pattern for local development and testing. The loaded variables flow into `Config.get()` through the existing env layer — no extra wiring needed:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
import { loadEnv, Config } from 'loreli/config';
|
|
104
|
+
|
|
105
|
+
loadEnv(); // loads .env from cwd (silent no-op if missing)
|
|
106
|
+
const config = new Config();
|
|
107
|
+
config.get('github.token'); // resolves GITHUB_TOKEN from .env
|
|
108
|
+
config.get('log.level'); // resolves LORELI_LOG_LEVEL from .env
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The following example shows loading from an explicit path, which is useful in tests or CI where the `.env` file lives in a non-standard location:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
import { loadEnv } from 'loreli/config';
|
|
115
|
+
|
|
116
|
+
const found = loadEnv('/path/to/project/.env.test');
|
|
117
|
+
// found === true if the file existed
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
> **Note:** `loadEnv` uses Node's built-in `process.loadEnvFile()` (available since Node 20.12). No external dependencies are required. The project targets Node >= 24.
|
|
121
|
+
|
|
122
|
+
## `loreli.yml` Schema
|
|
123
|
+
|
|
124
|
+
This file lives in the **target repository** root (the repo agents work on). It is scaffolded by start if absent.
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# loreli.yml - repo-level orchestration config
|
|
128
|
+
# See https://www.npmjs.com/package/loreli for full documentation.
|
|
129
|
+
#
|
|
130
|
+
# Duration fields support both numbers (milliseconds) and strings parsed by `ms`.
|
|
131
|
+
# Prefer strings like 10m, 1h, 3d for readability.
|
|
132
|
+
|
|
133
|
+
# --- Identity ---
|
|
134
|
+
# theme
|
|
135
|
+
# What: Chooses naming/theme vocabulary for agent identities and system messages.
|
|
136
|
+
# Impact: Cosmetic only; does not change orchestration behavior or model quality.
|
|
137
|
+
# Signal: Humans find agent identity/readability poor in PRs, comments, or dashboards.
|
|
138
|
+
# Change when: You want agent names/messages to match your team's preferred style.
|
|
139
|
+
theme: transformers # string or list: transformers | pokemon | marvel | digimon | starwars | lotr | dragonball | avatar | zelda
|
|
140
|
+
# theme: # list = randomize theme per work item
|
|
141
|
+
# - transformers
|
|
142
|
+
# - pokemon
|
|
143
|
+
# - marvel
|
|
144
|
+
|
|
145
|
+
# --- Agent defaults ---
|
|
146
|
+
# model
|
|
147
|
+
# What: Default model tier when tools do not specify a model explicitly.
|
|
148
|
+
# Impact: Higher tiers are usually stronger but slower/more expensive.
|
|
149
|
+
# Signal: Repeated low-quality outputs at current tier, or cost/latency pressure from higher tiers.
|
|
150
|
+
# Change when: You want a global quality/cost baseline shift for all agents.
|
|
151
|
+
model: balanced # fast | balanced | powerful | exact model string
|
|
152
|
+
|
|
153
|
+
# repo
|
|
154
|
+
# What: Optional repository slug fallback for standalone tool contexts before start runs.
|
|
155
|
+
# Impact: Enables tools like `loreli tools context`, `start_work`, and `hitl` to resolve repository scope without session hydration.
|
|
156
|
+
# Signal: CLI tools report "No repository configured" outside agent/start sessions.
|
|
157
|
+
# Change when: You regularly run Loreli tools directly from a shell and want a persistent repo default.
|
|
158
|
+
# repo: owner/repo
|
|
159
|
+
|
|
160
|
+
# --- Merge gate ---
|
|
161
|
+
# reviewers
|
|
162
|
+
# What: GitHub usernames for HITL review requests.
|
|
163
|
+
# Impact: Non-empty reviewers typically means more human approval steps.
|
|
164
|
+
# Signal: PRs merge without enough human oversight, or reviewer assignment is missing expected owners.
|
|
165
|
+
# Change when: You want specific humans looped into merges.
|
|
166
|
+
reviewers: [] # empty = autonomous merge path
|
|
167
|
+
|
|
168
|
+
merge:
|
|
169
|
+
# merge.method
|
|
170
|
+
# What: Git merge strategy used by Loreli when merging approved PRs.
|
|
171
|
+
# Impact: Changes commit history shape (single squash commit vs merge commit vs rebase history).
|
|
172
|
+
# Signal: Repository policy violations or maintainer feedback about history shape.
|
|
173
|
+
# Change when: Your repo has a strict merge policy.
|
|
174
|
+
method: squash # squash | merge | rebase
|
|
175
|
+
|
|
176
|
+
# merge.hitl
|
|
177
|
+
# What: Enables/disables Human In The Loop merge gating.
|
|
178
|
+
# Impact: true blocks final merge on human approval; false allows full automation.
|
|
179
|
+
# Signal: Unexpected autonomous merges (need true) or merge throughput too slow due to manual gates (consider false).
|
|
180
|
+
# Change when: You need more safety (set true) or more autonomy/speed (set false).
|
|
181
|
+
hitl: false
|
|
182
|
+
|
|
183
|
+
# merge.base
|
|
184
|
+
# What: Base branch target for agent PRs.
|
|
185
|
+
# Impact: Controls where agent changes accumulate (for example staging branch vs main).
|
|
186
|
+
# Signal: Main branch receiving agent merges too early, or release managers asking for a promotion/staging lane.
|
|
187
|
+
# Change when: You want agents to merge into an integration branch before promotion to main.
|
|
188
|
+
base: loreli
|
|
189
|
+
|
|
190
|
+
# --- PR quality gates ---
|
|
191
|
+
pr:
|
|
192
|
+
validation:
|
|
193
|
+
# pr.validation.command
|
|
194
|
+
# What: Shell command run before `pr/create` is allowed.
|
|
195
|
+
# Impact: Non-zero exit blocks PR creation; stronger checks reduce bad PRs but increase latency.
|
|
196
|
+
# Signal: PRs repeatedly fail CI after creation (tighten command), or PR creation is bottlenecked by long prechecks (lighten command).
|
|
197
|
+
# Change when: You want a different gate (for example lint+test, unit-only, or build-only).
|
|
198
|
+
command: npm test
|
|
199
|
+
|
|
200
|
+
selfReview:
|
|
201
|
+
# pr.selfReview.enabled
|
|
202
|
+
# What: Requires a preview step before actual PR creation.
|
|
203
|
+
# Impact: Adds one extra tool step but catches obvious metadata/diff mistakes early.
|
|
204
|
+
# Signal: Frequent wrong-base/wrong-scope PRs (enable), or operators report friction from two-step create flow (disable).
|
|
205
|
+
# Change when: You want faster single-step PR creation (set false) or stricter guardrails (true).
|
|
206
|
+
enabled: true
|
|
207
|
+
|
|
208
|
+
# --- HITL behavior ---
|
|
209
|
+
hitl:
|
|
210
|
+
# hitl.timeout
|
|
211
|
+
# What: Max idle age for HITL-owned claims before stale-claim eviction logic can act.
|
|
212
|
+
# Impact: Lower values recycle stuck ownership faster; higher values preserve ownership longer.
|
|
213
|
+
# Signal: Claims remain stuck for days (decrease), or valid long human review cycles get evicted too early (increase).
|
|
214
|
+
# Change when: Human review cadence is slower/faster than current default.
|
|
215
|
+
# Special: null disables stale-claim eviction in HITL mode.
|
|
216
|
+
timeout: 3d
|
|
217
|
+
|
|
218
|
+
# --- Stall and lifecycle timeouts ---
|
|
219
|
+
timeouts:
|
|
220
|
+
# timeouts.stall
|
|
221
|
+
# What: No-activity threshold for stall escalation.
|
|
222
|
+
# Impact: >stall triggers tier-1, >2x triggers tier-2 warning, >3x triggers kill.
|
|
223
|
+
# Signal: Agents killed during legitimate long runs (increase), or stalled agents consume slots too long (decrease).
|
|
224
|
+
# Change when: Agents are killed too aggressively on long tasks (increase),
|
|
225
|
+
# or stuck agents linger too long (decrease).
|
|
226
|
+
stall: 10m
|
|
227
|
+
|
|
228
|
+
# timeouts.shutdown
|
|
229
|
+
# What: Grace window for cooperative shutdown before force stop.
|
|
230
|
+
# Impact: Lower values free resources faster; higher values allow cleaner agent exits.
|
|
231
|
+
# Signal: Frequent force-kill shutdowns while agents are still finishing final output (increase),
|
|
232
|
+
# or shutdown hangs tying up capacity (decrease).
|
|
233
|
+
# Change when: Agents need more time to finalize output before shutdown.
|
|
234
|
+
shutdown: 1m
|
|
235
|
+
|
|
236
|
+
# timeouts.poll
|
|
237
|
+
# What: Poll cadence during shutdown waiting loop.
|
|
238
|
+
# Impact: Lower values detect stop faster but increase loop overhead.
|
|
239
|
+
# Signal: Slow recognition of completed shutdowns (decrease), or excessive polling/log churn during shutdown (increase).
|
|
240
|
+
# Change when: You want quicker shutdown detection or lower polling churn.
|
|
241
|
+
poll: 2s
|
|
242
|
+
|
|
243
|
+
# timeouts.rapidDeath
|
|
244
|
+
# What: Startup watch window used to detect immediate backend failures.
|
|
245
|
+
# Impact: Helps mark unhealthy backends degraded sooner after repeated instant failures.
|
|
246
|
+
# Signal: Backends crash quickly but are not degraded soon enough (increase), or degraded too aggressively from transient startup blips (decrease).
|
|
247
|
+
# Change when: Startup failures are missed (increase) or false positives occur (decrease).
|
|
248
|
+
rapidDeath: 15s
|
|
249
|
+
|
|
250
|
+
# timeouts.proxyDiscovery
|
|
251
|
+
# What: HTTP timeout for proxy model discovery calls used by claude/codex.
|
|
252
|
+
# Impact: Lower values fail fast on unhealthy proxies; higher values tolerate slower proxy endpoints.
|
|
253
|
+
# Signal: Discovery often logs timeout failures on healthy but slow networks (increase),
|
|
254
|
+
# or startup blocks too long on dead proxy endpoints (decrease).
|
|
255
|
+
# Change when: Proxy-backed environments need slower/faster discovery behavior.
|
|
256
|
+
proxyDiscovery: 5s
|
|
257
|
+
|
|
258
|
+
# timeouts.nudge
|
|
259
|
+
# What: Enables/disables tier-1 "you appear stalled" message.
|
|
260
|
+
# Impact: true may interrupt deep work; false keeps escalation signals without message interruption.
|
|
261
|
+
# Signal: Agents frequently context-switch into status reporting mid-task (disable), or teams need explicit stall prompts (enable).
|
|
262
|
+
# Change when: Long-running autonomous tasks should not be interrupted.
|
|
263
|
+
nudge: true
|
|
264
|
+
|
|
265
|
+
# --- Reactor polling ---
|
|
266
|
+
watch:
|
|
267
|
+
# watch.interval
|
|
268
|
+
# What: Main orchestration tick interval.
|
|
269
|
+
# Impact: Lower = faster reaction to new issues/PR state changes, higher API/process load.
|
|
270
|
+
# Signal: Claim/review actions happen too slowly after repo events (decrease), or API rate pressure/churn is high (increase).
|
|
271
|
+
# Change when: You need faster responsiveness or lower background churn.
|
|
272
|
+
interval: 1m
|
|
273
|
+
|
|
274
|
+
# watch.maxRounds
|
|
275
|
+
# What: Maximum re-review/rework loop cycles before escalation.
|
|
276
|
+
# Impact: Higher values allow deeper autonomous iteration; lower values force earlier human escalation.
|
|
277
|
+
# Signal: PRs escalate before they can converge autonomously (increase), or loops churn too long without convergence (decrease).
|
|
278
|
+
# Change when: PRs routinely need more/less autonomous correction rounds.
|
|
279
|
+
maxRounds: 7
|
|
280
|
+
|
|
281
|
+
# watch.maxClaims
|
|
282
|
+
# What: Max simultaneous issue claims per action agent.
|
|
283
|
+
# Impact: Higher increases per-agent throughput but can dilute focus and increase context switching.
|
|
284
|
+
# Signal: Idle action agents with unclaimed work (increase), or agents juggle too many tasks and miss follow-through (decrease).
|
|
285
|
+
# Change when: Agents are underutilized (increase) or overloaded (decrease).
|
|
286
|
+
maxClaims: 3
|
|
287
|
+
|
|
288
|
+
# --- Workflow policy (per-role) ---
|
|
289
|
+
# workflows.{role}
|
|
290
|
+
# What: Per-role model, scaling, trace, prompt, and risk-skip overrides.
|
|
291
|
+
# Impact: Fine-grained control over each workflow without global changes.
|
|
292
|
+
# Signal: One role needs different model/scaling/trace than others.
|
|
293
|
+
# Change when: You want role-specific tuning.
|
|
294
|
+
# Resolution: workflows.{role}.model → global model → 'balanced'
|
|
295
|
+
# Each role may also set: prompt (file path), trace.{enabled,maxOutputChars}
|
|
296
|
+
# Risk additionally supports: skip (skips mandatory risk verdict checks)
|
|
297
|
+
workflows:
|
|
298
|
+
action:
|
|
299
|
+
model: balanced
|
|
300
|
+
maxAgents: 3
|
|
301
|
+
reviewer:
|
|
302
|
+
model: balanced
|
|
303
|
+
maxAgents: 2
|
|
304
|
+
trace:
|
|
305
|
+
enabled: true
|
|
306
|
+
maxOutputChars: 4000
|
|
307
|
+
risk:
|
|
308
|
+
model: fast
|
|
309
|
+
maxAgents: 3
|
|
310
|
+
skip: false
|
|
311
|
+
trace:
|
|
312
|
+
enabled: true
|
|
313
|
+
maxOutputChars: 2000
|
|
314
|
+
planner:
|
|
315
|
+
model: powerful
|
|
316
|
+
maxAgents: 1
|
|
317
|
+
trace:
|
|
318
|
+
enabled: true
|
|
319
|
+
maxOutputChars: 4000
|
|
320
|
+
|
|
321
|
+
# --- Scaling policy (global) ---
|
|
322
|
+
scaling:
|
|
323
|
+
# scaling.maxAgents
|
|
324
|
+
# What: Global cap for active non-dormant agents.
|
|
325
|
+
# Impact: Upper bound on parallelism and compute/resource consumption.
|
|
326
|
+
# Signal: Backlog grows with idle infrastructure (increase), or host/API limits are saturated (decrease).
|
|
327
|
+
# Change when: You need more throughput (increase) or tighter resource limits (decrease).
|
|
328
|
+
maxAgents: 8
|
|
329
|
+
|
|
330
|
+
# scaling.maxPerTick
|
|
331
|
+
# What: Spawn budget per reactor tick.
|
|
332
|
+
# Impact: Higher values ramp up faster but can spike load.
|
|
333
|
+
# Signal: Slow recovery from empty capacity (increase), or sudden spawn bursts causing instability (decrease).
|
|
334
|
+
# Change when: Startup/recovery is too slow (increase) or too bursty (decrease).
|
|
335
|
+
maxPerTick: 2
|
|
336
|
+
|
|
337
|
+
# scaling.cooldown
|
|
338
|
+
# What: Minimum delay before spawning another agent of the same role.
|
|
339
|
+
# Impact: Prevents spawn thrashing when demand oscillates.
|
|
340
|
+
# Signal: Repeated rapid spawn/stop cycles for same role (increase), or role fill speed too slow during sustained demand (decrease).
|
|
341
|
+
# Change when: You see role churn/flapping or need faster re-scaling.
|
|
342
|
+
cooldown: 30s
|
|
343
|
+
|
|
344
|
+
# --- Logging ---
|
|
345
|
+
log:
|
|
346
|
+
# log.level
|
|
347
|
+
# What: Minimum severity written to logs.
|
|
348
|
+
# Impact: Lower levels (debug/trace) increase observability and log volume.
|
|
349
|
+
# Signal: Hard-to-diagnose behavior with insufficient log detail (lower level), or noisy oversized logs (raise level).
|
|
350
|
+
# Change when: Debugging production behavior or reducing noise.
|
|
351
|
+
level: info # error | warn | info | debug | trace
|
|
352
|
+
|
|
353
|
+
# log.maxSize
|
|
354
|
+
# What: Per-file rotation size threshold in bytes.
|
|
355
|
+
# Impact: Larger files reduce rotation frequency but grow disk usage per file.
|
|
356
|
+
# Signal: Too-frequent rotations splitting incidents (increase), or very large log files hard to handle (decrease).
|
|
357
|
+
# Change when: You want fewer rotations or smaller log files.
|
|
358
|
+
maxSize: 10485760 # 10 MB
|
|
359
|
+
|
|
360
|
+
# log.maxFiles
|
|
361
|
+
# What: Number of rotated files retained.
|
|
362
|
+
# Impact: Higher values keep more history and consume more disk.
|
|
363
|
+
# Signal: Needed historical context disappears before investigation (increase), or disk pressure from logs (decrease).
|
|
364
|
+
# Change when: You need longer local forensic history.
|
|
365
|
+
maxFiles: 3
|
|
366
|
+
|
|
367
|
+
# --- Labels ---
|
|
368
|
+
labels:
|
|
369
|
+
# labels.track
|
|
370
|
+
# What: Adds Loreli/provider/model tracking labels to issues and PRs.
|
|
371
|
+
# Impact: Better observability/filtering in GitHub labels, but more label traffic.
|
|
372
|
+
# Signal: Need to filter/report by provider/role/model (enable), or label clutter complaints from maintainers (disable).
|
|
373
|
+
# Change when: You want a cleaner label surface (set false).
|
|
374
|
+
track: true
|
|
375
|
+
|
|
376
|
+
# labels.extra
|
|
377
|
+
# What: Extra labels applied to all Loreli-created items.
|
|
378
|
+
# Impact: Improves triage/routing automation via label-based workflows.
|
|
379
|
+
# Signal: Manual relabeling happens repeatedly after Loreli creates items.
|
|
380
|
+
# Change when: You need team/project labels attached automatically.
|
|
381
|
+
extra: []
|
|
382
|
+
|
|
383
|
+
# --- Agent tool restrictions ---
|
|
384
|
+
agents:
|
|
385
|
+
disallowedTools:
|
|
386
|
+
# agents.disallowedTools
|
|
387
|
+
# What: Commands agents cannot execute directly.
|
|
388
|
+
# Impact: Helps prevent bypassing MCP guardrails and policy checks.
|
|
389
|
+
# Signal: Agents attempt raw GitHub/API shell tooling that bypasses Loreli controls.
|
|
390
|
+
# Change when: You need stricter/looser command policy.
|
|
391
|
+
- gh
|
|
392
|
+
- curl
|
|
393
|
+
|
|
394
|
+
# --- Backend model/env overrides ---
|
|
395
|
+
# backends.{name}.models
|
|
396
|
+
# What: Per-backend tier/provider model routing overrides.
|
|
397
|
+
# Impact: Changes model selection without code changes — overrides both
|
|
398
|
+
# runtime discovery and static defaults. Required for LiteLLM/proxy setups.
|
|
399
|
+
# Signal: Specific backend underperforms at current tier/provider mapping,
|
|
400
|
+
# or you're behind a proxy that uses different model names.
|
|
401
|
+
# Change when: You need backend-specific model tuning or custom proxy routing.
|
|
402
|
+
# Resolution: config override > runtime discovery > static defaults > pass-through
|
|
403
|
+
# Note: When cursor-agent is available, models are auto-discovered at startup
|
|
404
|
+
# via `--list-models`. Config overrides always take precedence over discovery.
|
|
405
|
+
#
|
|
406
|
+
# backends.{name}.env
|
|
407
|
+
# What: Env vars injected into backend launcher scripts.
|
|
408
|
+
# Impact: Controls API base URLs, auth endpoints, and backend runtime options.
|
|
409
|
+
# Signal: Backend needs proxy routing/custom endpoint/auth flag and current environment inheritance is insufficient.
|
|
410
|
+
# Change when: You use proxies/self-hosted gateways/custom backend flags.
|
|
411
|
+
#
|
|
412
|
+
# backends:
|
|
413
|
+
# claude:
|
|
414
|
+
# env:
|
|
415
|
+
# ANTHROPIC_BASE_URL: https://your-proxy.example.com/v1
|
|
416
|
+
# models:
|
|
417
|
+
# fast:
|
|
418
|
+
# anthropic: claude-haiku-4-5-20251001
|
|
419
|
+
# balanced:
|
|
420
|
+
# anthropic: claude-sonnet-4-5-20250929
|
|
421
|
+
# powerful:
|
|
422
|
+
# anthropic: claude-opus-4-5-20251101
|
|
423
|
+
# codex:
|
|
424
|
+
# env:
|
|
425
|
+
# OPENAI_BASE_URL: https://your-proxy.example.com/v1
|
|
426
|
+
# models:
|
|
427
|
+
# fast:
|
|
428
|
+
# openai: gpt-5-mini
|
|
429
|
+
# balanced:
|
|
430
|
+
# openai: gpt-5.1-codex
|
|
431
|
+
# powerful:
|
|
432
|
+
# openai: gpt-5.2-pro
|
|
433
|
+
# cursor:
|
|
434
|
+
# models:
|
|
435
|
+
# fast:
|
|
436
|
+
# anthropic: sonnet-4.5
|
|
437
|
+
# openai: gpt-5.3-codex-low
|
|
438
|
+
# balanced:
|
|
439
|
+
# anthropic: sonnet-4.5-thinking
|
|
440
|
+
# openai: gpt-5.3-codex
|
|
441
|
+
# powerful:
|
|
442
|
+
# anthropic: opus-4.6-thinking
|
|
443
|
+
# openai: gpt-5.1-codex-max
|
|
444
|
+
|
|
445
|
+
# --- Trace capture (global defaults) ---
|
|
446
|
+
# Per-role trace overrides live under workflows.{role}.trace
|
|
447
|
+
trace:
|
|
448
|
+
# trace.enabled
|
|
449
|
+
# What: Master switch for workflow trace collection.
|
|
450
|
+
# Impact: Off reduces trace artifacts; on improves debuggability.
|
|
451
|
+
# Signal: Missing forensic context during incidents (enable), or trace storage overhead too high (disable).
|
|
452
|
+
# Change when: You need to reduce trace volume or increase diagnostics.
|
|
453
|
+
enabled: true
|
|
454
|
+
|
|
455
|
+
# trace.includeOutput
|
|
456
|
+
# What: Include model/tool output snippets in traces.
|
|
457
|
+
# Impact: Better debugging context but larger logs and potentially sensitive output in traces.
|
|
458
|
+
# Signal: Investigations lack enough output context (enable), or traces include too much sensitive/noisy content (disable).
|
|
459
|
+
# Change when: You want minimal traces or stricter output hygiene.
|
|
460
|
+
includeOutput: true
|
|
461
|
+
|
|
462
|
+
# trace.maxOutputChars
|
|
463
|
+
# What: Global cap of captured output chars per trace item.
|
|
464
|
+
# Impact: Higher captures richer context but grows trace size.
|
|
465
|
+
# Signal: Trace snippets are cut before key error details appear (increase), or traces are too large/noisy (decrease).
|
|
466
|
+
# Change when: Useful context is being truncated too aggressively.
|
|
467
|
+
maxOutputChars: 8000
|
|
468
|
+
|
|
469
|
+
# --- Proof of life ---
|
|
470
|
+
proofOfLife:
|
|
471
|
+
# proofOfLife.timeout
|
|
472
|
+
# What: Wait window for liveness responses before stale claim eviction actions.
|
|
473
|
+
# Impact: Lower values reclaim work faster; higher values are more tolerant of slow responses.
|
|
474
|
+
# Signal: Active claims get evicted while agents are still responsive (increase), or dead claims linger too long (decrease).
|
|
475
|
+
# Change when: Claims are reclaimed too quickly or too slowly.
|
|
476
|
+
timeout: 5m
|
|
477
|
+
|
|
478
|
+
# --- Workspace lifecycle ---
|
|
479
|
+
workspace:
|
|
480
|
+
# workspace.cleanup
|
|
481
|
+
# What: Deletes per-agent workspace directories on kill/shutdown.
|
|
482
|
+
# Impact: true saves disk space; false preserves artifacts for debugging.
|
|
483
|
+
# Signal: Disk growth from stale workspaces (enable), or need post-run forensic inspection (disable).
|
|
484
|
+
# Change when: You want cleaner disks (true) or post-mortem inspection (false).
|
|
485
|
+
cleanup: false
|
|
486
|
+
|
|
487
|
+
# --- Session cleanup ---
|
|
488
|
+
cleanup:
|
|
489
|
+
# cleanup.retention
|
|
490
|
+
# What: Age threshold for pruning old sessions.
|
|
491
|
+
# Impact: Lower values reduce disk usage; higher values keep history longer.
|
|
492
|
+
# Signal: Session storage growth pressure (decrease), or historical debugging context disappears too soon (increase).
|
|
493
|
+
# Change when: You need more/less historical session retention.
|
|
494
|
+
retention: 12h
|
|
495
|
+
|
|
496
|
+
# cleanup.autoprune
|
|
497
|
+
# What: Runs prune/sweep automatically at start.
|
|
498
|
+
# Impact: true keeps storage tidy without manual cleanup.
|
|
499
|
+
# Signal: Accumulated stale files/sessions between runs (enable), or need strict manual lifecycle control (disable).
|
|
500
|
+
# Change when: You want explicit/manual cleanup control (set false).
|
|
501
|
+
autoprune: true
|
|
502
|
+
|
|
503
|
+
# --- Feedback and knowledge capture ---
|
|
504
|
+
feedback:
|
|
505
|
+
# feedback.enabled
|
|
506
|
+
# What: Master switch for feedback marker capture and knowledge aggregation.
|
|
507
|
+
# Impact: false disables automated pattern accumulation.
|
|
508
|
+
# Signal: Need to temporarily pause all feedback mining/noise.
|
|
509
|
+
# Change when: You want to pause feedback mining entirely.
|
|
510
|
+
enabled: true
|
|
511
|
+
|
|
512
|
+
# feedback.threshold
|
|
513
|
+
# What: Repeat-count threshold before a feedback pattern is promoted/escalated.
|
|
514
|
+
# Impact: Lower values promote patterns sooner; higher values reduce noisy promotions.
|
|
515
|
+
# Signal: Too many low-signal promotions (increase), or important repeated issues taking too long to surface (decrease).
|
|
516
|
+
# Change when: Pattern promotion is too noisy (increase) or too slow (decrease).
|
|
517
|
+
threshold: 5
|
|
518
|
+
|
|
519
|
+
# feedback.categories
|
|
520
|
+
# What: Allowed classifier buckets for captured feedback.
|
|
521
|
+
# Impact: Removed categories are ignored by pattern promotion.
|
|
522
|
+
# Signal: Certain feedback classes are irrelevant/noisy for your team.
|
|
523
|
+
# Change when: You want to focus feedback mining on specific quality dimensions.
|
|
524
|
+
categories:
|
|
525
|
+
- naming
|
|
526
|
+
- architecture
|
|
527
|
+
- testing
|
|
528
|
+
- documentation
|
|
529
|
+
- performance
|
|
530
|
+
- security
|
|
531
|
+
|
|
532
|
+
# feedback.hitl
|
|
533
|
+
# What: Controls Human In The Loop escalation for feedback-driven PRs at merge time.
|
|
534
|
+
# Impact: true gates all feedback PRs on human approval; false allows full automation; array gates only listed categories.
|
|
535
|
+
# Signal: Feedback-driven changes landing without review (set true or list categories), or unnecessary merge friction (set false).
|
|
536
|
+
# Change when: You want human oversight on specific feedback categories (e.g. architecture, security) while letting others auto-merge.
|
|
537
|
+
hitl: false
|
|
538
|
+
|
|
539
|
+
# --- Tmux ---
|
|
540
|
+
tmux:
|
|
541
|
+
# tmux.session
|
|
542
|
+
# What: Shared tmux session name where agent panes run.
|
|
543
|
+
# Impact: Changing this isolates Loreli panes into a different tmux session namespace.
|
|
544
|
+
# Signal: Multiple orchestrations collide in same tmux namespace.
|
|
545
|
+
# Change when: Running multiple isolated Loreli orchestrations on one machine.
|
|
546
|
+
session: loreli
|
|
547
|
+
|
|
548
|
+
# tmux.capture
|
|
549
|
+
# What: Number of pane history lines Loreli reads when inspecting output.
|
|
550
|
+
# Impact: Higher values improve diagnostics but increase capture overhead.
|
|
551
|
+
# Signal: Error root cause appears just above currently captured history window.
|
|
552
|
+
# Change when: Important error context scrolls out of captured history.
|
|
553
|
+
capture: 500
|
|
554
|
+
|
|
555
|
+
# --- GitHub auth override ---
|
|
556
|
+
# github.token
|
|
557
|
+
# What: Fallback GitHub token in config (env token is preferred).
|
|
558
|
+
# Impact: Enables Hub API auth when env injection is unavailable.
|
|
559
|
+
# Signal: Hub/API auth failures in environments where you cannot set env vars.
|
|
560
|
+
# Change when: You cannot provide GITHUB_TOKEN via environment.
|
|
561
|
+
# github:
|
|
562
|
+
# token: ghp_your_token_here
|
|
563
|
+
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## Configurable Base Branch
|
|
567
|
+
|
|
568
|
+
By default agents create pull requests against `main`. The `merge.base` setting redirects all agent work to a dedicated branch, keeping `main` untouched until a human is ready to promote.
|
|
569
|
+
|
|
570
|
+
### Why
|
|
571
|
+
|
|
572
|
+
Agents run autonomously — often overnight or in batch. Pointing them at a dedicated branch (e.g. `loreli`) creates a natural safety boundary: agent-produced PRs merge into `loreli`, and a single human-reviewed PR promotes the batch from `loreli` into `main`. This prevents unreviewed agent code from landing on `main` directly.
|
|
573
|
+
|
|
574
|
+
### How to Enable
|
|
575
|
+
|
|
576
|
+
Set `merge.base` in the target repository's `loreli.yml`:
|
|
577
|
+
|
|
578
|
+
```yaml
|
|
579
|
+
merge:
|
|
580
|
+
base: loreli
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
The scaffolding template (`loreli.yml` generated by start) already defaults to `loreli`. Repositories that omit `merge.base` or set it to `main` retain the current behavior — agents PR directly against `main`.
|
|
584
|
+
|
|
585
|
+
### What It Affects
|
|
586
|
+
|
|
587
|
+
The setting flows through every system boundary that references a base branch:
|
|
588
|
+
|
|
589
|
+
| System | Behavior |
|
|
590
|
+
|--------|----------|
|
|
591
|
+
| **Workspace reset** (`reset()`) | Agent workspaces are force-recreated from `origin/<base>` instead of `origin/main` when recycling between issues. The base ref is explicitly fetched since agent clones use `--single-branch`. |
|
|
592
|
+
| **PR creation** (`pr` tool) | PRs opened by action agents target `<base>` instead of `main`. The base is resolved from config and can be overridden per-call via the `base` argument. |
|
|
593
|
+
| **Relay auto-PR** (action workflow) | When the action workflow auto-creates a PR on behalf of a dormant agent, it uses the configured base. |
|
|
594
|
+
| **Review checkout** (`checkout()`) | Reviewer workspaces sync the configured base branch alongside the PR head so diffs are computed against the correct parent. |
|
|
595
|
+
| **Re-review dispatch** (review workflow) | Re-review prompts include the configured base in PR metadata so reviewers evaluate against the correct target. |
|
|
596
|
+
|
|
597
|
+
### Resolution Order
|
|
598
|
+
|
|
599
|
+
`merge.base` follows the standard config resolution chain:
|
|
600
|
+
|
|
601
|
+
1. **Start params** — `config.merge({ merge: { base: 'develop' } })`
|
|
602
|
+
2. **`loreli.yml`** — `merge.base: loreli`
|
|
603
|
+
3. **Built-in default** — `main`
|
|
604
|
+
|
|
605
|
+
Every call site uses `cfg?.get?.('merge.base') ?? 'main'` as the resolution pattern, ensuring the system degrades gracefully when config is unavailable.
|
|
606
|
+
|
|
607
|
+
### Workflow
|
|
608
|
+
|
|
609
|
+
The intended workflow with `merge.base: loreli`:
|
|
610
|
+
|
|
611
|
+
```
|
|
612
|
+
main ──────────────────────────────────────── human review ──▶ main
|
|
613
|
+
│ ▲
|
|
614
|
+
└──▶ loreli ──▶ agent PRs merge here ──────────────┘
|
|
615
|
+
│ ▲ ▲
|
|
616
|
+
├── agent-0/issue-1 ───┘ (auto-merge)
|
|
617
|
+
└── agent-1/issue-2 ───┘ (auto-merge)
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Agents branch from `loreli`, open PRs against `loreli`, and their work is auto-merged (or HITL-reviewed) into `loreli`. A separate human-created PR from `loreli → main` rolls up all agent work for final review.
|
|
621
|
+
|
|
622
|
+
## PR Quality Gates
|
|
623
|
+
|
|
624
|
+
`pr` config controls pre-PR quality enforcement in the `pr` MCP tool (`action: create`).
|
|
625
|
+
|
|
626
|
+
```yaml
|
|
627
|
+
pr:
|
|
628
|
+
validation:
|
|
629
|
+
command: npm test
|
|
630
|
+
selfReview:
|
|
631
|
+
enabled: true
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
- `pr.validation.command`: defaults to `npm test` and runs in the action agent workspace before PR creation. Non-zero exit blocks `pr/create` and returns command output.
|
|
635
|
+
- `pr.selfReview.enabled`: defaults to `true` and switches `pr/create` into a two-step flow. First call returns a diff/stat preview. Second call must pass `confirm: true` to proceed.
|
|
636
|
+
|
|
637
|
+
## Feedback HITL
|
|
638
|
+
|
|
639
|
+
`feedback.hitl` controls per-category Human In The Loop escalation for feedback-driven PRs at merge time. It accepts three shapes:
|
|
640
|
+
|
|
641
|
+
- **`false`** (default) — no HITL on feedback-driven PRs; they merge after agent sign-off.
|
|
642
|
+
- **`true`** — HITL on all feedback-driven PRs; every such PR requires human approval before merge.
|
|
643
|
+
- **`['architecture', 'security']`** — HITL only for listed categories; PRs driven by feedback in those categories require human approval; others auto-merge after agent sign-off.
|
|
644
|
+
|
|
645
|
+
`merge.hitl` takes global precedence. When `merge.hitl` is `true`, all PRs (including feedback-driven ones) require human approval regardless of `feedback.hitl`.
|
|
646
|
+
|
|
647
|
+
## Built-in Defaults
|
|
648
|
+
|
|
649
|
+
Every configurable value has a default in `defaults.js`. These are the lowest-priority layer and apply when no other layer provides a value. Backend model defaults may be overridden at runtime by auto-discovery (see [Model Discovery](#model-discovery)):
|
|
650
|
+
|
|
651
|
+
| Key | Default | Description |
|
|
652
|
+
|-----|---------|-------------|
|
|
653
|
+
| `repo` | `undefined` | Optional repository slug fallback (`owner/name`) for standalone tool contexts |
|
|
654
|
+
| `theme` | `transformers` | Agent naming theme (`string` or `string[]` for random per work item) |
|
|
655
|
+
| `reviewers` | `[]` | Human reviewers (empty = auto-merge) |
|
|
656
|
+
| `model` | `balanced` | Default model alias |
|
|
657
|
+
| `backends.claude.models.fast.anthropic` | `claude-haiku-4-5-20251001` | Claude fast tier |
|
|
658
|
+
| `backends.claude.models.balanced.anthropic` | `claude-sonnet-4-5-20250929` | Claude balanced tier |
|
|
659
|
+
| `backends.claude.models.powerful.anthropic` | `claude-opus-4-5-20251101` | Claude powerful tier |
|
|
660
|
+
| `backends.codex.models.fast.openai` | `gpt-5-mini` | Codex fast tier |
|
|
661
|
+
| `backends.codex.models.balanced.openai` | `gpt-5.1-codex` | Codex balanced tier |
|
|
662
|
+
| `backends.codex.models.powerful.openai` | `gpt-5.2-pro` | Codex powerful tier |
|
|
663
|
+
| `backends.cursor.models.fast.anthropic` | `sonnet-4.5` | Cursor fast anthropic |
|
|
664
|
+
| `backends.cursor.models.balanced.anthropic` | `sonnet-4.5-thinking` | Cursor balanced anthropic |
|
|
665
|
+
| `backends.cursor.models.powerful.anthropic` | `opus-4.6-thinking` | Cursor powerful anthropic |
|
|
666
|
+
| `backends.{name}.env` | *(none)* | Backend-specific env var overrides (flat string map) |
|
|
667
|
+
| `agents.disallowedTools` | `['gh', 'curl']` | Commands agents cannot execute |
|
|
668
|
+
| `scaling.maxAgents` | `8` | Global cap — max agents across all roles |
|
|
669
|
+
| `scaling.maxPerTick` | `2` | Max new agents spawned per reactor tick |
|
|
670
|
+
| `scaling.cooldown` | `30000` | Min time between spawns for same role (ms) |
|
|
671
|
+
| `merge.method` | `squash` | PR merge method |
|
|
672
|
+
| `merge.hitl` | `false` | HITL mode: `false` = auto-merge, `true` = human reviewers |
|
|
673
|
+
| `feedback.hitl` | `false` | Per-category HITL for feedback-driven PRs: `false` = none, `true` = all, `string[]` = listed categories only |
|
|
674
|
+
| `merge.base` | `main` | Default PR base branch (scaffolding template sets `loreli` for safety — agents work on a dedicated branch, not main) |
|
|
675
|
+
| `pr.validation.command` | `npm test` | Default shell command run before `pr/create`; non-zero exit blocks PR creation |
|
|
676
|
+
| `pr.selfReview.enabled` | `true` | Require two-step self-review flow (`create` preview, then `create` with `confirm=true`) before PR creation |
|
|
677
|
+
| `workflows.action.model` | `balanced` | Action agent model tier |
|
|
678
|
+
| `workflows.action.maxAgents` | `3` | Max concurrent action agents |
|
|
679
|
+
| `workflows.reviewer.model` | `balanced` | Reviewer agent model tier |
|
|
680
|
+
| `workflows.reviewer.maxAgents` | `2` | Max concurrent reviewer agents |
|
|
681
|
+
| `workflows.reviewer.trace.enabled` | `true` | Reviewer trace capture |
|
|
682
|
+
| `workflows.reviewer.trace.maxOutputChars` | `4000` | Reviewer trace output cap |
|
|
683
|
+
| `workflows.risk.model` | `fast` | Risk agent model tier |
|
|
684
|
+
| `workflows.risk.maxAgents` | `3` | Max concurrent risk agents |
|
|
685
|
+
| `workflows.risk.skip` | `false` | Skip mandatory risk verdict checks |
|
|
686
|
+
| `workflows.risk.trace.enabled` | `true` | Risk trace capture |
|
|
687
|
+
| `workflows.risk.trace.maxOutputChars` | `2000` | Risk trace output cap |
|
|
688
|
+
| `workflows.planner.model` | `powerful` | Planner agent model tier |
|
|
689
|
+
| `workflows.planner.maxAgents` | `1` | Max concurrent planner agents |
|
|
690
|
+
| `workflows.planner.trace.enabled` | `true` | Planner trace capture |
|
|
691
|
+
| `workflows.planner.trace.maxOutputChars` | `4000` | Planner trace output cap |
|
|
692
|
+
| `workflows.{role}.prompt` | `undefined` | Custom prompt file for role (relative to repo root) |
|
|
693
|
+
| `timeouts.stall` | `600000` | Agent stall detection (ms) |
|
|
694
|
+
| `timeouts.shutdown` | `60000` | Graceful shutdown timeout (ms) |
|
|
695
|
+
| `timeouts.poll` | `2000` | Poll interval (ms) |
|
|
696
|
+
| `timeouts.rapidDeath` | `15000` | Spawn-window backend failure detection delay (ms) |
|
|
697
|
+
| `timeouts.proxyDiscovery` | `5000` | Proxy model discovery HTTP timeout (ms) |
|
|
698
|
+
| `timeouts.nudge` | `true` | Enable/disable tier-1 stall nudge messages |
|
|
699
|
+
| `log.level` | `info` | Console log level |
|
|
700
|
+
| `log.maxSize` | `10485760` | Max log file size (bytes) |
|
|
701
|
+
| `log.maxFiles` | `3` | Rotated log file count |
|
|
702
|
+
| `proofOfLife.timeout` | `300000` | Proof-of-life response timeout (ms, default 5m) |
|
|
703
|
+
| `cleanup.retention` | `43200000` | Prune sessions older than this (ms, default 12h) |
|
|
704
|
+
| `cleanup.autoprune` | `true` | Run prune at start |
|
|
705
|
+
| `tmux.session` | `loreli` | Tmux session name |
|
|
706
|
+
| `tmux.capture` | `500` | Pane capture history lines |
|
|
707
|
+
|
|
708
|
+
## Environment Variable Mapping
|
|
709
|
+
|
|
710
|
+
Only a subset of config values can be overridden via environment variables. These variables can come from the shell or from a `.env` file loaded via `loadEnv()`:
|
|
711
|
+
|
|
712
|
+
| Config Path | Environment Variable | Purpose |
|
|
713
|
+
|-------------|---------------------|---------|
|
|
714
|
+
| `repo` | `LORELI_REPO` | Repository fallback (`owner/name`) for tool contexts before `start` |
|
|
715
|
+
| `log.level` | `LORELI_LOG_LEVEL` | Override default log level |
|
|
716
|
+
| `github.token` | `GITHUB_TOKEN` | GitHub API token for hub |
|
|
717
|
+
|
|
718
|
+
Environment variables sit between the file layer and built-in defaults in resolution priority. Shell-set variables take precedence over `.env` file values.
|
|
719
|
+
|
|
720
|
+
Additional env vars recognized by other packages but not routed through `Config.get()`:
|
|
721
|
+
|
|
722
|
+
| Variable | Package | Purpose |
|
|
723
|
+
|----------|---------|---------|
|
|
724
|
+
| `LORELI_HOME` | mcp, agent, log | Override `~/.loreli/` base storage directory |
|
|
725
|
+
| `LORELI_TEST_REPO` | tests | GitHub repo for integration tests (`owner/name`) |
|
|
726
|
+
|
|
727
|
+
Copy `.env.example` from the project root to `.env` for local development. See the root README for the full list.
|
|
728
|
+
|
|
729
|
+
## Backend Environment Variables
|
|
730
|
+
|
|
731
|
+
Each backend can declare environment variable overrides in `loreli.yml` under `backends.{name}.env`. These are exported in launcher scripts before spawning the CLI agent in tmux.
|
|
732
|
+
|
|
733
|
+
At runtime, the agent package's `models.env()` function merges two layers:
|
|
734
|
+
|
|
735
|
+
1. **Inherited** — `process.env` vars matching the backend's known prefixes are collected automatically
|
|
736
|
+
2. **Config overrides** — `backends.{name}.env` from `loreli.yml` or `config.merge()` take precedence on key collision
|
|
737
|
+
|
|
738
|
+
| Backend | Inherited Prefixes |
|
|
739
|
+
|---------|-------------------|
|
|
740
|
+
| `claude` | `ANTHROPIC_*`, `CLAUDE_*` |
|
|
741
|
+
| `codex` | `OPENAI_*`, `CODEX_*` |
|
|
742
|
+
| `cursor` | `ANTHROPIC_*`, `OPENAI_*`, `CLAUDE_*`, `CURSOR_*` |
|
|
743
|
+
|
|
744
|
+
This means critical variables like `ANTHROPIC_BASE_URL` (proxy URL), `ANTHROPIC_AUTH_TOKEN`, and `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` are forwarded from the orchestrator's environment into tmux-spawned agents without explicit config — but can be overridden in `loreli.yml` when needed:
|
|
745
|
+
|
|
746
|
+
```yaml
|
|
747
|
+
backends:
|
|
748
|
+
claude:
|
|
749
|
+
env:
|
|
750
|
+
ANTHROPIC_BASE_URL: https://your-proxy.example.com/v1
|
|
751
|
+
codex:
|
|
752
|
+
env:
|
|
753
|
+
OPENAI_BASE_URL: https://your-proxy.example.com/v1
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
The `env` section is a flat string-to-string map. Non-string values and empty objects are silently discarded by schema validation.
|
|
757
|
+
|
|
758
|
+
## Model Discovery
|
|
759
|
+
|
|
760
|
+
At startup, `BackendRegistry.discover()` probes available backends for their supported models. This enables automatic tier classification without relying solely on static defaults that may become stale.
|
|
761
|
+
|
|
762
|
+
### Resolution Chain
|
|
763
|
+
|
|
764
|
+
Model aliases (`fast`, `balanced`, `powerful`) resolve through four layers:
|
|
765
|
+
|
|
766
|
+
1. **Config override** — `backends.{name}.models.{alias}.{provider}` from `loreli.yml`. Always wins. Required for LiteLLM/proxy setups where model names differ from upstream.
|
|
767
|
+
2. **Runtime discovery** — models discovered from backend CLIs and configured proxy endpoints. `cursor-agent` uses `--list-models`; `claude`/`codex` query OpenAI-compatible model listing (`/v1/models` with `/models` fallback) when their base URL overrides are configured.
|
|
768
|
+
3. **Static defaults** — built-in values from `defaults.js`. When discovery data is available, static fallbacks are validated against the discovered list. Invalid IDs trigger a warning and fall back to the backend's default discovered model.
|
|
769
|
+
4. **Pass-through** — exact model strings bypass resolution entirely.
|
|
770
|
+
|
|
771
|
+
### Discovery by Backend
|
|
772
|
+
|
|
773
|
+
| Backend | Method | Notes |
|
|
774
|
+
|---------|--------|-------|
|
|
775
|
+
| `cursor-agent` | `--list-models` CLI flag | Parseable output, classified into tiers per provider |
|
|
776
|
+
| `claude` | Proxy model listing (`/v1/models` / `/models`) when `ANTHROPIC_BASE_URL` is configured | Auth key order: `ANTHROPIC_API_KEY`, then `OPENAI_API_KEY` |
|
|
777
|
+
| `codex` | Proxy model listing (`/v1/models` / `/models`) when `OPENAI_BASE_URL` is configured | Auth key order: `OPENAI_API_KEY`, then `ANTHROPIC_API_KEY` |
|
|
778
|
+
|
|
779
|
+
### Validation
|
|
780
|
+
|
|
781
|
+
When discovery data is available, resolved model IDs are validated against the discovered model list. This catches stale defaults, typos, and models removed by providers. When validation fails, the backend's default model is used and a warning is logged.
|
|
782
|
+
|
|
783
|
+
### LiteLLM / Proxy Override
|
|
784
|
+
|
|
785
|
+
When backends route through a LiteLLM proxy or custom gateway, override model names in `loreli.yml`:
|
|
786
|
+
|
|
787
|
+
```yaml
|
|
788
|
+
backends:
|
|
789
|
+
claude:
|
|
790
|
+
env:
|
|
791
|
+
ANTHROPIC_BASE_URL: https://your-litellm.example.com/v1
|
|
792
|
+
models:
|
|
793
|
+
fast:
|
|
794
|
+
anthropic: litellm/haiku
|
|
795
|
+
balanced:
|
|
796
|
+
anthropic: litellm/sonnet
|
|
797
|
+
powerful:
|
|
798
|
+
anthropic: litellm/opus
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
Config overrides take precedence over both discovery and static defaults. See [packages/agent/README.md](../agent/README.md) for the full model resolution API reference.
|
|
802
|
+
|
|
803
|
+
## Tool Blocking
|
|
804
|
+
|
|
805
|
+
Agents can bypass Loreli's MCP guardrails (stamping, role guards, label enforcement) by using raw CLI tools like `gh` or `curl` to interact with GitHub directly. The `agents.disallowedTools` config prevents this by blocking specified commands across all CLI backends.
|
|
806
|
+
|
|
807
|
+
### How It Works
|
|
808
|
+
|
|
809
|
+
The deny list is enforced through each backend's native mechanism:
|
|
810
|
+
|
|
811
|
+
| Backend | Enforcement | Source |
|
|
812
|
+
|---------|-------------|--------|
|
|
813
|
+
| Claude Code | `--disallowedTools` CLI flag + `PreToolUse` hooks | [Hooks guide](https://code.claude.com/docs/en/hooks-guide) |
|
|
814
|
+
| Cursor Agent | `beforeShellExecution` hooks (`.cursor/hooks.json`) | [Cursor hooks](https://cursor.com/docs/agent/hooks) |
|
|
815
|
+
| Codex CLI | `rules.prefix_rules` via `-c` flags | [Codex config](https://developers.openai.com/codex/config-reference) |
|
|
816
|
+
|
|
817
|
+
When `prepare()` scaffolds an agent workspace with a non-empty deny list, it creates/updates three enforcement files:
|
|
818
|
+
|
|
819
|
+
- **`.loreli/deny.sh`** — Common deny script used by both Claude and Cursor hooks. Reads JSON from stdin, extracts the command, and exits with code 2 (block) if the first token matches a denied command. Exit code 2 is the universal block signal recognized by both Claude Code and Cursor Agent. Always overwritten when the deny list changes.
|
|
820
|
+
- **`.claude/settings.local.json`** — Claude Code project hooks config referencing `deny.sh` for `PreToolUse` events on Bash tools. **Merged** into existing file — user-defined hooks and non-hook settings (e.g. `permissions`) are preserved.
|
|
821
|
+
- **`.cursor/hooks.json`** — Cursor Agent hooks config referencing `deny.sh` for `beforeShellExecution` events with a regex matcher. **Merged** into existing file — user-defined hooks are preserved.
|
|
822
|
+
|
|
823
|
+
Loreli's hook entry is identified by its reference to `deny.sh` in the command string. On subsequent runs, the existing Loreli entry is updated in place (e.g. when the deny list changes) rather than duplicated.
|
|
824
|
+
|
|
825
|
+
### Customizing the Deny List
|
|
826
|
+
|
|
827
|
+
Override the default deny list in `loreli.yml`:
|
|
828
|
+
|
|
829
|
+
```yaml
|
|
830
|
+
agents:
|
|
831
|
+
disallowedTools:
|
|
832
|
+
- gh
|
|
833
|
+
- curl
|
|
834
|
+
- wget
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
To disable tool blocking entirely, set an empty array:
|
|
838
|
+
|
|
839
|
+
```yaml
|
|
840
|
+
agents:
|
|
841
|
+
disallowedTools: []
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
## Input Validation (`check`)
|
|
845
|
+
|
|
846
|
+
The `check` module provides strict validators for tool arguments, preventing invalid inputs before any side effects occur. All validators throw descriptive errors on invalid input and return the validated value on success.
|
|
847
|
+
|
|
848
|
+
```js
|
|
849
|
+
import { check } from 'loreli/config';
|
|
850
|
+
|
|
851
|
+
check.repo('owner/repo'); // returns 'owner/repo'
|
|
852
|
+
check.repo('invalid'); // throws: Invalid repository format
|
|
853
|
+
|
|
854
|
+
check.name('optimus-0'); // returns 'optimus-0'
|
|
855
|
+
check.name('x'.repeat(65)); // throws: Agent name too long
|
|
856
|
+
|
|
857
|
+
check.role('action'); // returns 'action'
|
|
858
|
+
check.role('admin'); // throws: Invalid role
|
|
859
|
+
|
|
860
|
+
check.theme('transformers'); // returns 'transformers'
|
|
861
|
+
check.theme(['pokemon', 'marvel']); // returns ['pokemon', 'marvel']
|
|
862
|
+
check.provider('anthropic'); // returns 'anthropic'
|
|
863
|
+
check.positive(42, 'PR'); // returns 42
|
|
864
|
+
check.positive(-1, 'count'); // throws: count must be a positive number
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
### API
|
|
868
|
+
|
|
869
|
+
| Function | Rule | Error |
|
|
870
|
+
|----------|------|-------|
|
|
871
|
+
| `check.repo(str)` | `owner/name` format, alphanumeric + `.` `-` `_` | `Invalid repository format` |
|
|
872
|
+
| `check.name(str)` | `[A-Za-z0-9_-]+`, max 64 chars | `Agent name too long` / `Invalid agent name` |
|
|
873
|
+
| `check.role(str)` | `planner` \| `action` \| `reviewer` | `Invalid role` |
|
|
874
|
+
| `check.theme(str\|str[])` | Valid theme name(s); arrays must be non-empty | `Invalid theme` / `Theme list must not be empty` |
|
|
875
|
+
| `check.provider(str)` | `openai` \| `anthropic` \| `cursor-openai` \| `cursor-anthropic` | `Invalid provider` |
|
|
876
|
+
| `check.positive(n, label)` | Finite positive number | `{label} must be a positive number` |
|
|
877
|
+
|
|
878
|
+
All validators follow the pattern: required string → format check → return validated value. `null`, `undefined`, and empty strings are rejected with "required" errors.
|
|
879
|
+
|
|
880
|
+
## Schema Validation
|
|
881
|
+
|
|
882
|
+
The `validate(raw)` function normalizes raw YAML content:
|
|
883
|
+
|
|
884
|
+
- Strips unknown keys
|
|
885
|
+
- Validates types (strings, numbers, arrays of strings)
|
|
886
|
+
- `theme` accepts both a string and an array of strings (single-element arrays collapse to a string)
|
|
887
|
+
- Returns only known, valid fields
|
|
888
|
+
|
|
889
|
+
Invalid values are silently discarded, allowing partial `loreli.yml` files to work correctly.
|
|
890
|
+
|
|
891
|
+
## Error Handling
|
|
892
|
+
|
|
893
|
+
| Scenario | Behavior |
|
|
894
|
+
|----------|----------|
|
|
895
|
+
| `loreli.yml` missing | `load()` returns `false`, defaults apply |
|
|
896
|
+
| Malformed YAML | `load()` returns `false`, defaults apply |
|
|
897
|
+
| Invalid type in config | `validate()` strips the key, default applies |
|
|
898
|
+
| Unknown keys in YAML | Silently ignored |
|