loreli 0.0.0 → 1.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 +670 -97
- package/bin/loreli.js +89 -0
- package/package.json +74 -14
- package/packages/README.md +101 -0
- package/packages/action/README.md +98 -0
- package/packages/action/src/index.js +656 -0
- package/packages/agent/README.md +517 -0
- package/packages/agent/src/backends/claude.js +287 -0
- package/packages/agent/src/backends/codex.js +278 -0
- package/packages/agent/src/backends/cursor.js +294 -0
- package/packages/agent/src/backends/index.js +329 -0
- package/packages/agent/src/base.js +138 -0
- package/packages/agent/src/cli.js +198 -0
- package/packages/agent/src/factory.js +119 -0
- package/packages/agent/src/index.js +12 -0
- package/packages/agent/src/models.js +141 -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/config/README.md +833 -0
- package/packages/config/src/defaults.js +134 -0
- package/packages/config/src/index.js +192 -0
- package/packages/config/src/schema.js +273 -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 +1558 -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 +237 -0
- package/packages/knowledge/src/index.js +412 -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 +279 -0
- package/packages/mcp/instructions.md +121 -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 +453 -0
- package/packages/mcp/scaffolding/mcp-configs/.codex/config.toml +3 -0
- package/packages/mcp/scaffolding/mcp-configs/.cursor/mcp.json +11 -0
- package/packages/mcp/scaffolding/mcp-configs/.mcp.json +11 -0
- package/packages/mcp/scaffolding/pull-request.md +23 -0
- package/packages/mcp/src/index.js +571 -0
- package/packages/mcp/src/tools/agents.js +429 -0
- package/packages/mcp/src/tools/context.js +199 -0
- package/packages/mcp/src/tools/github.js +1199 -0
- package/packages/mcp/src/tools/hitl.js +149 -0
- package/packages/mcp/src/tools/index.js +17 -0
- package/packages/mcp/src/tools/start.js +835 -0
- package/packages/mcp/src/tools/status.js +146 -0
- package/packages/mcp/src/tools/work.js +124 -0
- package/packages/orchestrator/README.md +192 -0
- package/packages/orchestrator/src/index.js +1226 -0
- package/packages/planner/README.md +168 -0
- package/packages/planner/src/index.js +1166 -0
- package/packages/review/README.md +129 -0
- package/packages/review/src/index.js +1283 -0
- package/packages/risk/README.md +119 -0
- package/packages/risk/src/index.js +428 -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 +452 -0
- package/packages/workflow/README.md +313 -0
- package/packages/workflow/src/index.js +481 -0
- package/packages/workflow/src/proof-of-life.js +74 -0
- package/packages/workspace/README.md +143 -0
- package/packages/workspace/src/index.js +1076 -0
- package/index.js +0 -8
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# loreli/review
|
|
2
|
+
|
|
3
|
+
Review workflow for Loreli's orchestration pipeline. Extends the `Workflow` base class to manage reviewer agents — PR scanning, review feedback forwarding, merge/HITL landing, stalemate escalation, and the signoff protocol.
|
|
4
|
+
|
|
5
|
+
## Research Findings
|
|
6
|
+
|
|
7
|
+
No existing npm packages cover adversarial cross-provider PR review with automated merge gating. This is domain-specific to Loreli's yin/yang agent model.
|
|
8
|
+
|
|
9
|
+
## Risk Gate
|
|
10
|
+
|
|
11
|
+
ReviewWorkflow's `scan()` uses a **label gate** to wait for risk assessment before dispatching reviewers. The `loreli/risk` package runs first in the reactor chain, applying `loreli:low-risk`, `loreli:medium-risk`, or `loreli:critical-risk` labels to each PR. Only PRs with a risk label pass through to reviewer dispatch.
|
|
12
|
+
|
|
13
|
+
- **No risk label** — PR is skipped (still awaiting risk assessment).
|
|
14
|
+
- **`loreli:low-risk`** — Normal reviewer dispatch.
|
|
15
|
+
- **`loreli:medium-risk`** — Reviewer dispatched with risk context attached to the prompt.
|
|
16
|
+
- **`loreli:critical-risk`** — PR added to `_completed` immediately; HITL escalation is handled by `loreli/risk`.
|
|
17
|
+
|
|
18
|
+
Setting `review.skipRiskAssessment: true` in `loreli.yml` disables the label gate entirely.
|
|
19
|
+
|
|
20
|
+
## API Reference
|
|
21
|
+
|
|
22
|
+
### `ReviewWorkflow` (extends Workflow)
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { ReviewWorkflow } from 'loreli/review';
|
|
26
|
+
|
|
27
|
+
const review = new ReviewWorkflow(orchestrator, hub);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Static Properties
|
|
31
|
+
|
|
32
|
+
| Property | Value | Description |
|
|
33
|
+
|----------|-------|-------------|
|
|
34
|
+
| `role` | `'reviewer'` | Agent role this workflow manages |
|
|
35
|
+
| `template` | `prompts/reviewer.md` | Mustache template for PR reviewer prompts (PR-only — plan review uses dedicated templates in `loreli/planner`) |
|
|
36
|
+
|
|
37
|
+
### Methods
|
|
38
|
+
|
|
39
|
+
#### `review.scan(repo)` → Promise\<Array\<{pr, reviewer}\>\>
|
|
40
|
+
|
|
41
|
+
Scan for new PRs created by action agents and dispatch reviewers. Uses branch naming convention (`{agentName}/issue-{number}`) to match PRs to agents. Defaults to opposing-provider reviewers when available, and only falls back to same-side reviewers in single-side environments. Requires a risk label on the PR unless `skipRiskAssessment` is true.
|
|
42
|
+
|
|
43
|
+
The `demand()` signal is topology-aware. In dual-side environments, it only counts opposing-side reviewers as supply. In single-side environments, same-side reviewers count as valid supply.
|
|
44
|
+
|
|
45
|
+
#### `review.forward(repo)` → Promise\<Array\<{pr, action}\>\>
|
|
46
|
+
|
|
47
|
+
Poll for `REQUEST_CHANGES` reviews on tracked PRs and relay feedback to the action agent. Enforces `maxRounds` escalation.
|
|
48
|
+
|
|
49
|
+
#### `review.land(repo)` → Promise\<Array\<{pr, merged, gated}\>\>
|
|
50
|
+
|
|
51
|
+
Detect approved reviews and auto-merge or trigger HITL. In dual-side mode, approval must come from the opposite side. In single-side mode, same-provider approval is allowed when reviewer and action identities are distinct.
|
|
52
|
+
|
|
53
|
+
When a PR merges into a non-default base branch (for example `loreli` while the repo default branch is `main`), GitHub does not auto-close linked issues from `Closes #N` keywords. `review.land()` performs a post-merge reconciliation step in this topology and closes any still-open linked issues to prevent duplicate re-dispatch loops.
|
|
54
|
+
|
|
55
|
+
#### `review.hitl(repo, pr)` → Promise\<{hitlAt, reviewers}\>
|
|
56
|
+
|
|
57
|
+
Activate Human In The Loop (HITL) for a PR. Requests review from configured humans, kills active agents, and records the HITL timestamp.
|
|
58
|
+
|
|
59
|
+
#### `review.signoff(repo, number, identity, role)` → Promise\<void\>
|
|
60
|
+
|
|
61
|
+
Post an approval comment using the hub's scoped identity.
|
|
62
|
+
|
|
63
|
+
### Reactor Handlers
|
|
64
|
+
|
|
65
|
+
The review workflow registers five handlers with the orchestrator's tick loop:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
review.reactor()
|
|
69
|
+
// → { 'review-hydrate': fn, scan: fn, forward: fn, land: fn, 'review-reap': fn }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
These are called sequentially on every tick to drive the PR lifecycle. Risk handlers (`loreli/risk`) must be registered **before** review handlers so labels are applied before `scan()` checks for them.
|
|
73
|
+
|
|
74
|
+
### Reviewer Eviction (Unified Proof-of-Life)
|
|
75
|
+
|
|
76
|
+
All eviction decisions in `scan()` route through the proof-of-life protocol as the single authority. Both local idle agents and foreign agents go through the same gate — `this.check()` from the base Workflow class.
|
|
77
|
+
|
|
78
|
+
**Immediate eviction** (no PoL needed — agent is definitively dead):
|
|
79
|
+
- **Dormant agents** — process exited, `agent.state === 'dormant'`.
|
|
80
|
+
- **Locally removed agents** — `orchestrator._removed.has(name)`, killed by stall detection.
|
|
81
|
+
|
|
82
|
+
**PoL-gated eviction** (agent might be alive — must verify first):
|
|
83
|
+
- **Foreign reviewers** (not in agents map) — `this.check()` posts a PoL request and waits for the owning orchestrator to respond.
|
|
84
|
+
- **Local idle reviewers** (`_lastActivity` > stall timeout) — `this.check()` posts a PoL request. The local responder calls `health()` (which uses `refresh()` for tmux activity detection) and posts a status-aware alive response. If `status: 'healthy'`, the agent is kept. If `status: 'unhealthy'`, the agent is evicted.
|
|
85
|
+
|
|
86
|
+
The `check()` method returns:
|
|
87
|
+
- `'active'` — agent proved alive (healthy alive response or recent GitHub activity). Skip eviction.
|
|
88
|
+
- `'requested'` / `'pending'` — PoL in progress. Skip eviction this tick, check again next tick.
|
|
89
|
+
- `'release'` — agent confirmed stalled (unhealthy alive response, expired request with no response, or timeout). Proceed with eviction.
|
|
90
|
+
|
|
91
|
+
This eliminates the previous competing systems where foreign agents were protected by PoL but local agents were evicted directly based on `_lastActivity` — causing false evictions of agents actively working but not updating the orchestrator's activity tracker.
|
|
92
|
+
|
|
93
|
+
### Release Markers
|
|
94
|
+
|
|
95
|
+
Every eviction path in `scan()` posts a `review-release` marker comment on the PR via `_releaseReviewer()`. This marker is critical for preventing the hydrate-evict loop: without it, `hydrate()` would re-discover the old `review-claim` on the next tick and re-add the entry to `_watched`, causing scan to evict again — forever.
|
|
96
|
+
|
|
97
|
+
`hydrate()` checks for a `review-release` marker after the `review-claim` for the same agent. If found, the claim is considered invalidated and the PR is skipped.
|
|
98
|
+
|
|
99
|
+
### Dead Action Agent Recovery
|
|
100
|
+
|
|
101
|
+
When `forward()` detects a `REQUEST_CHANGES` review but the original action agent is dead (not in the orchestrator's agents map), it calls `_restartAction()` instead of silently skipping the PR.
|
|
102
|
+
|
|
103
|
+
`_restartAction()` performs the following steps:
|
|
104
|
+
|
|
105
|
+
1. Fetches the PR to extract the linked issue number from the branch name (`{agent}/issue-{N}`).
|
|
106
|
+
2. Posts a themed comment on the PR explaining the restart.
|
|
107
|
+
3. Closes the PR via `hub.closePull()`.
|
|
108
|
+
4. Posts a `restart` marker on the linked issue — this releases the claim (recognized by `claimant()` in the action workflow) and resets the circuit breaker counter.
|
|
109
|
+
5. Removes `loreli:blocked` and `loreli:needs-attention` labels from the issue if present.
|
|
110
|
+
6. Removes the PR from `_watched` and adds it to `_completed`.
|
|
111
|
+
7. Kills the assigned reviewer.
|
|
112
|
+
|
|
113
|
+
On the next reactor tick, the action workflow's `_dispatch()` sees the unclaimed issue and assigns a fresh agent to start the work from scratch. This avoids the complexity of recovering stale branches, merge conflicts, or corrupted workspace state.
|
|
114
|
+
|
|
115
|
+
## Errors
|
|
116
|
+
|
|
117
|
+
| Error | When | Resolution |
|
|
118
|
+
|-------|------|------------|
|
|
119
|
+
| `No reviewers configured for HITL` | hitl() called without human reviewers | Configure reviewers in loreli.yml |
|
|
120
|
+
|
|
121
|
+
## Autonomous Operation
|
|
122
|
+
|
|
123
|
+
Reviewer agents run in headless tmux panes with no human operator. The `Workflow.render()` method automatically prepends a shared autonomous-mode preamble to every rendered prompt, instructing agents to never ask clarifying questions, skip interactive skills, and proceed with best judgment. See the `loreli/workflow` README for details on the preamble. The orchestrator's stall detection acts as a safety net for agents that block despite these directives.
|
|
124
|
+
|
|
125
|
+
## Scope Boundary
|
|
126
|
+
|
|
127
|
+
**In scope**: PR scanning (with risk label gate), review dispatch, feedback forwarding, merge landing, HITL, stalemate escalation, signoff protocol, reviewer prompt rendering.
|
|
128
|
+
|
|
129
|
+
**Out of scope**: Risk assessment (risk package), issue claiming (action package), planning (planner package), agent lifecycle (orchestrator).
|