opencode-ralph-rlm 0.1.12 → 0.1.14
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/README.md +38 -42
- package/dist/ralph-rlm.js +102 -101
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,8 +6,8 @@ New here? Start with [`GETTINGSTARTEDGUIDE.md`](GETTINGSTARTEDGUIDE.md).
|
|
|
6
6
|
|
|
7
7
|
Two techniques combine to make this work:
|
|
8
8
|
|
|
9
|
-
- **Ralph**
|
|
10
|
-
- **RLM** (Recursive Language Model worker)
|
|
9
|
+
- **Ralph** - the main session acting as strategist+supervisor. It reviews what failed, adjusts the plan and instructions, then delegates coding to a worker. It never writes code itself.
|
|
10
|
+
- **RLM** (Recursive Language Model worker) - a file-first coding session based on [arXiv:2512.24601](https://arxiv.org/abs/2512.24601). Each attempt gets a clean context window and loads all state from files rather than inheriting noise from prior turns.
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
## The problem this solves
|
|
@@ -43,9 +43,9 @@ Context windows are session-local and finite. Files are persistent, inspectable,
|
|
|
43
43
|
|
|
44
44
|
### Separation of strategy and execution
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Ralph keeps strategy and execution separate: **you (main session)** handle strategy and delegation, while **workers** implement. When a session that just wrote failing code is also responsible for diagnosing *why* it failed and planning the next approach, it pattern-matches against its own failed reasoning. It proposes variations on what didn't work rather than stepping back.
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
In this mode, the *worker* always gets a fresh window. It reads the failure record cold, without the accumulated baggage of having written the code. This mirrors how experienced engineering teams work: the reviewer of a failing PR is often not the one who writes the fix.
|
|
49
49
|
|
|
50
50
|
### The verify contract
|
|
51
51
|
|
|
@@ -63,52 +63,48 @@ The RLM paper demonstrates that full-file reads are expensive and often counterp
|
|
|
63
63
|
|
|
64
64
|
`NOTES_AND_LEARNINGS.md` and `RLM_INSTRUCTIONS.md` are the loop's long-term memory. They survive context resets and accumulate across attempts. The loop doesn't just retry — it gets smarter with each failure.
|
|
65
65
|
|
|
66
|
-
`RLM_INSTRUCTIONS.md` is the inner loop's operating manual. The
|
|
66
|
+
`RLM_INSTRUCTIONS.md` is the inner loop's operating manual. The main strategist (you) updates it between attempts when a pattern of failures reveals a gap in guidance. By attempt 10, the instructions encode everything learned from attempts 1-9.
|
|
67
67
|
|
|
68
68
|
This is why the approach scales to overnight runs. A fresh worker in attempt 10 starts with the accumulated knowledge of 9 prior attempts, encoded in protocol files, without the accumulated noise.
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
## How it works
|
|
72
72
|
|
|
73
|
-
###
|
|
73
|
+
### Two-level architecture (main session = strategist + supervisor)
|
|
74
74
|
|
|
75
75
|
```
|
|
76
|
-
You → main session (
|
|
76
|
+
You → main session (supervisor + strategist in one)
|
|
77
77
|
│
|
|
78
78
|
├─ attempt 1:
|
|
79
|
-
│ ├─
|
|
80
|
-
│
|
|
81
|
-
│ │ → ralph_spawn_worker() → STOP
|
|
79
|
+
│ ├─ strategist (you): ralph_load_context() → review failures → update PLAN.md
|
|
80
|
+
│ ├─ ralph_spawn_worker() → spawns RLM worker session W1
|
|
82
81
|
│ │
|
|
83
|
-
│ └─
|
|
82
|
+
│ └─ RLM worker session W1 ← fresh context
|
|
84
83
|
│ W1: ralph_load_context() → code → ralph_verify() → STOP
|
|
85
84
|
│
|
|
86
85
|
├─ plugin verifies on W1 idle
|
|
87
86
|
│ fail → roll state files → spawn attempt 2
|
|
88
87
|
│
|
|
89
88
|
├─ attempt 2:
|
|
90
|
-
│ ├─
|
|
91
|
-
│
|
|
92
|
-
│ │ → ralph_spawn_worker() → STOP
|
|
89
|
+
│ ├─ strategist (you): reads AGENT_CONTEXT_FOR_NEXT_RALPH.md → adjusts strategy
|
|
90
|
+
│ ├─ ralph_spawn_worker() → spawns RLM worker session W2
|
|
93
91
|
│ │
|
|
94
|
-
│ └─
|
|
92
|
+
│ └─ RLM worker session W2 ← fresh context
|
|
95
93
|
│ W2: loads compact state from files → code → STOP
|
|
96
94
|
│
|
|
97
95
|
└─ pass → done toast
|
|
98
96
|
```
|
|
99
97
|
|
|
100
|
-
Each
|
|
98
|
+
Each role has a distinct purpose and **fresh context window** where applicable:
|
|
101
99
|
|
|
102
100
|
| Role | Session | Context | Responsibility |
|
|
103
101
|
|---|---|---|---|
|
|
104
|
-
| **main** | Your conversation | Persistent |
|
|
105
|
-
| **ralph** | Per-attempt strategist | Fresh | Review failure, update PLAN.md / RLM_INSTRUCTIONS.md, call `ralph_spawn_worker()`. |
|
|
102
|
+
| **main** | Your conversation | Persistent | Supervisor + strategist. Review failures, update PLAN.md / RLM_INSTRUCTIONS.md, call `ralph_spawn_worker()`. |
|
|
106
103
|
| **worker** | Per-attempt coder | Fresh | `ralph_load_context()` → code → `ralph_verify()` → stop. |
|
|
107
104
|
|
|
108
105
|
### Roles and responsibilities (quick map)
|
|
109
106
|
|
|
110
|
-
- **Supervisor (main session):** orchestration and
|
|
111
|
-
- **Ralph strategist:** updates plan/instructions and delegates to a worker. No direct implementation.
|
|
107
|
+
- **Supervisor+strategist (main session):** orchestration, planning, and delegation. Never edits files or runs code directly.
|
|
112
108
|
- **RLM worker:** does the actual coding and verification for this attempt. One pass per session.
|
|
113
109
|
- **Sub-agent:** narrow task helper; updates its own state files under `.opencode/agents/<name>/`.
|
|
114
110
|
|
|
@@ -116,15 +112,13 @@ Each session role has a distinct purpose and **fresh context window**:
|
|
|
116
112
|
|
|
117
113
|
```
|
|
118
114
|
main idle
|
|
119
|
-
└─
|
|
120
|
-
└─
|
|
121
|
-
└─
|
|
122
|
-
└─
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
└─
|
|
126
|
-
└─ spawn Ralph(2)
|
|
127
|
-
└─ (repeat)
|
|
115
|
+
└─ strategist (you) calls ralph_spawn_worker()
|
|
116
|
+
└─ spawn Worker(1)
|
|
117
|
+
└─ Worker(1) calls ralph_verify() and goes idle
|
|
118
|
+
└─ plugin runs verify
|
|
119
|
+
├─ pass → done
|
|
120
|
+
└─ fail → roll state files
|
|
121
|
+
└─ strategist (you) handles next attempt
|
|
128
122
|
```
|
|
129
123
|
|
|
130
124
|
The plugin drives the loop from `session.idle` events. Neither Ralph nor the worker need to know about the outer loop — they just load context, do their job, and stop.
|
|
@@ -228,6 +222,8 @@ Create `.opencode/ralph.json`. All fields are optional — the plugin runs with
|
|
|
228
222
|
| `statusVerbosity` | `"normal"` | Supervisor status emission level: `minimal` (warnings/errors), `normal`, or `verbose`. |
|
|
229
223
|
| `maxAttempts` | `20` | Hard stop after this many failed verify attempts. |
|
|
230
224
|
| `heartbeatMinutes` | `15` | Warn if active strategist/worker has no progress for this many minutes. |
|
|
225
|
+
| `strategistHandoffMinutes` | `5` | Warn/retry if the strategist does not spawn a worker within this many minutes. |
|
|
226
|
+
| `strategistHandoffMaxRetries` | `2` | Max retries to respawn strategist after a missed handoff. |
|
|
231
227
|
| `verifyTimeoutMinutes` | `0` | Timeout for verify command in minutes. `0` disables timeouts. |
|
|
232
228
|
| `verify.command` | - | Shell command to run as an array, e.g. `["bun", "run", "verify"]`. If omitted, verify always returns `unknown`. |
|
|
233
229
|
| `verify.cwd` | `"."` | Working directory for the verify command, relative to the repo root. |
|
|
@@ -293,7 +289,7 @@ This repo now includes project-local agent files under `.opencode/agents/`:
|
|
|
293
289
|
- `.opencode/agents/security-auditor.md`
|
|
294
290
|
|
|
295
291
|
These profiles intentionally keep loop ownership in `ralph-rlm`.
|
|
296
|
-
Do not model
|
|
292
|
+
Do not model the strategist/worker roles as OpenCode primary/subagent replacements.
|
|
297
293
|
|
|
298
294
|
|
|
299
295
|
## Protocol files
|
|
@@ -522,9 +518,9 @@ Run the configured verify command. Returns `{ verdict: "pass"|"fail"|"unknown",
|
|
|
522
518
|
|
|
523
519
|
#### `ralph_spawn_worker()`
|
|
524
520
|
|
|
525
|
-
**
|
|
521
|
+
**Main strategist only.** Spawn a fresh RLM worker session for this attempt. Call this after reviewing protocol files and optionally updating `PLAN.md` / `RLM_INSTRUCTIONS.md`. Then stop — the plugin handles verification and prompts you for the next attempt if needed.
|
|
526
522
|
|
|
527
|
-
If you call this from
|
|
523
|
+
If you call this from an unbound session you will get: `ralph_spawn_worker() must be called from the bound supervisor session.` Bind first with `ralph_create_supervisor_session()`.
|
|
528
524
|
|
|
529
525
|
### Sub-agents
|
|
530
526
|
|
|
@@ -546,7 +542,7 @@ List all sub-agents registered in the current session with their name, goal, sta
|
|
|
546
542
|
|
|
547
543
|
### Supervisor communication
|
|
548
544
|
|
|
549
|
-
These tools let spawned sessions (
|
|
545
|
+
These tools let spawned sessions (RLM worker + sub-agents) communicate back to the main conversation at runtime. State is carried in `.opencode/pending_input.json` for question/response pairs, `SUPERVISOR_LOG.md` for structured status entries, and `CONVERSATION.md` for the readable event timeline.
|
|
550
546
|
|
|
551
547
|
User answers to `ralph_ask()` are persisted too: when you reply via `ralph_respond()`, the response is appended to `CONVERSATION.md`.
|
|
552
548
|
|
|
@@ -653,8 +649,8 @@ RALPH_BOOTSTRAP_RLM_INSTRUCTIONS="@/home/user/prompts/rlm-instructions.md"
|
|
|
653
649
|
| `RALPH_CONTEXT_GATE_ERROR` | — | Error message thrown when the agent tries a destructive tool before loading context. |
|
|
654
650
|
| `RALPH_WORKER_SYSTEM_PROMPT` | — | System prompt injected into every RLM worker session. Describes the one-pass contract. |
|
|
655
651
|
| `RALPH_WORKER_PROMPT` | `{{attempt}}` | Initial prompt sent to each spawned RLM worker session. |
|
|
656
|
-
| `RALPH_SESSION_SYSTEM_PROMPT` | — |
|
|
657
|
-
| `RALPH_SESSION_PROMPT` | `{{attempt}}` |
|
|
652
|
+
| `RALPH_SESSION_SYSTEM_PROMPT` | — | Legacy: system prompt for separate strategist sessions (unused in main-as-strategist mode). |
|
|
653
|
+
| `RALPH_SESSION_PROMPT` | `{{attempt}}` | Prompt sent to the main strategist session when an attempt starts. |
|
|
658
654
|
|
|
659
655
|
### Example: custom continue prompt from a file
|
|
660
656
|
|
|
@@ -697,7 +693,7 @@ Set `maxAttempts` high (25–50), write a detailed `PLAN.md` with a precise defi
|
|
|
697
693
|
|
|
698
694
|
1. Make an attempt.
|
|
699
695
|
2. Run verify.
|
|
700
|
-
3. On failure: roll state,
|
|
696
|
+
3. On failure: roll state, prompt the strategist (you) to diagnose and adjust, spawn the next worker.
|
|
701
697
|
4. Repeat until it passes or hits `maxAttempts`.
|
|
702
698
|
|
|
703
699
|
In the morning, check `SUPERVISOR_LOG.md` and `CONVERSATION.md` for the progress feed, `NOTES_AND_LEARNINGS.md` for what the loop learned, and `AGENT_CONTEXT_FOR_NEXT_RALPH.md` for where it stopped.
|
|
@@ -744,19 +740,19 @@ Parent agent:
|
|
|
744
740
|
|
|
745
741
|
Edit `RLM_INSTRUCTIONS.md` to add project-specific playbooks, register MCP tools, or adjust the debug workflow. Changes persist across attempts. Use `ralph_update_rlm_instructions()` from within a session, or edit the file directly.
|
|
746
742
|
|
|
747
|
-
The instructions file is the primary lever for improving loop performance. If the loop keeps making the same mistake, add a rule. If it keeps following an inefficient path, add a playbook. The
|
|
743
|
+
The instructions file is the primary lever for improving loop performance. If the loop keeps making the same mistake, add a rule. If it keeps following an inefficient path, add a playbook. The main strategist (you) updates these instructions between attempts based on what it observes in the failure record.
|
|
748
744
|
|
|
749
745
|
|
|
750
746
|
## Hooks installed
|
|
751
747
|
|
|
752
748
|
| Hook | What it does |
|
|
753
749
|
|---|---|
|
|
754
|
-
| `event: session.idle` | Routes idle events: **worker** → `handleWorkerIdle` (verify + continue loop); **
|
|
755
|
-
| `event: session.created` | Pre-allocates session state for known worker
|
|
750
|
+
| `event: session.idle` | Routes idle events: **worker** → `handleWorkerIdle` (verify + continue loop); **main/other** → `handleMainIdle` (kick off attempt 1). Also emits heartbeat/staleness warnings and supervisor status updates to `SUPERVISOR_LOG.md` and `CONVERSATION.md`. |
|
|
751
|
+
| `event: session.created` | Pre-allocates session state for known worker sessions. |
|
|
756
752
|
| `event: session.status` | Refreshes heartbeat/progress timestamps for active sessions and surfaces explicit session error statuses to the supervisor feed. |
|
|
757
|
-
| `experimental.chat.system.transform` |
|
|
753
|
+
| `experimental.chat.system.transform` | Two-way routing: **worker** → RLM file-first prompt; **main/other** → supervisor+strategist prompt. |
|
|
758
754
|
| `experimental.session.compacting` | Injects protocol file pointers into compaction context so state survives context compression. |
|
|
759
|
-
| `tool.execute.before` | Blocks destructive tools (`write`, `edit`, `bash`, `delete`, `move`, `rename`) in **worker and sub-agent sessions** until `ralph_load_context()` has been called.
|
|
755
|
+
| `tool.execute.before` | Blocks destructive tools (`write`, `edit`, `bash`, `delete`, `move`, `rename`) in **worker and sub-agent sessions** until `ralph_load_context()` has been called. |
|
|
760
756
|
|
|
761
757
|
|
|
762
758
|
## Background
|
|
@@ -765,7 +761,7 @@ The instructions file is the primary lever for improving loop performance. If th
|
|
|
765
761
|
|
|
766
762
|
The outer loop is named after the [Ralph Wiggum technique](https://www.geoffreyhuntley.com/ralph) — a `while` loop that feeds a prompt to an AI agent until it succeeds. The name reflects the philosophy: persistent, not clever. The loop doesn't try to be smart about when to give up. It tries, records what happened, and tries again with better instructions.
|
|
767
763
|
|
|
768
|
-
The key addition in this plugin over a naive Ralph implementation is the **separation of the strategist from the worker**.
|
|
764
|
+
The key addition in this plugin over a naive Ralph implementation is the **separation of the strategist from the worker**. The main session handles strategy and delegation, while each worker gets a fresh context to implement. This keeps planning clean while still benefiting from fresh worker windows.
|
|
769
765
|
|
|
770
766
|
### The RLM inner loop
|
|
771
767
|
|
package/dist/ralph-rlm.js
CHANGED
|
@@ -23417,6 +23417,8 @@ var RalphConfigSchema = exports_Schema.Struct({
|
|
|
23417
23417
|
statusVerbosity: exports_Schema.optional(exports_Schema.Union(exports_Schema.Literal("minimal"), exports_Schema.Literal("normal"), exports_Schema.Literal("verbose"))),
|
|
23418
23418
|
maxAttempts: exports_Schema.optional(exports_Schema.Number),
|
|
23419
23419
|
heartbeatMinutes: exports_Schema.optional(exports_Schema.Number),
|
|
23420
|
+
strategistHandoffMinutes: exports_Schema.optional(exports_Schema.Number),
|
|
23421
|
+
strategistHandoffMaxRetries: exports_Schema.optional(exports_Schema.Number),
|
|
23420
23422
|
verifyTimeoutMinutes: exports_Schema.optional(exports_Schema.Number),
|
|
23421
23423
|
verify: exports_Schema.optional(VerifyConfigSchema),
|
|
23422
23424
|
gateDestructiveToolsUntilContextLoaded: exports_Schema.optional(exports_Schema.Boolean),
|
|
@@ -23456,6 +23458,8 @@ var CONFIG_DEFAULTS = {
|
|
|
23456
23458
|
statusVerbosity: "normal",
|
|
23457
23459
|
maxAttempts: 20,
|
|
23458
23460
|
heartbeatMinutes: 15,
|
|
23461
|
+
strategistHandoffMinutes: 5,
|
|
23462
|
+
strategistHandoffMaxRetries: 2,
|
|
23459
23463
|
verifyTimeoutMinutes: 0,
|
|
23460
23464
|
gateDestructiveToolsUntilContextLoaded: true,
|
|
23461
23465
|
maxRlmSliceLines: 200,
|
|
@@ -23482,6 +23486,8 @@ function resolveConfig(raw) {
|
|
|
23482
23486
|
statusVerbosity: raw.statusVerbosity ?? CONFIG_DEFAULTS.statusVerbosity,
|
|
23483
23487
|
maxAttempts: toBoundedInt(raw.maxAttempts, CONFIG_DEFAULTS.maxAttempts, 1, 500),
|
|
23484
23488
|
heartbeatMinutes: toBoundedInt(raw.heartbeatMinutes, CONFIG_DEFAULTS.heartbeatMinutes, 1, 240),
|
|
23489
|
+
strategistHandoffMinutes: toBoundedInt(raw.strategistHandoffMinutes, CONFIG_DEFAULTS.strategistHandoffMinutes, 1, 60),
|
|
23490
|
+
strategistHandoffMaxRetries: toBoundedInt(raw.strategistHandoffMaxRetries, CONFIG_DEFAULTS.strategistHandoffMaxRetries, 0, 10),
|
|
23485
23491
|
verifyTimeoutMinutes: toBoundedInt(raw.verifyTimeoutMinutes, CONFIG_DEFAULTS.verifyTimeoutMinutes, 0, 240),
|
|
23486
23492
|
...verify !== undefined ? { verify } : {},
|
|
23487
23493
|
gateDestructiveToolsUntilContextLoaded: raw.gateDestructiveToolsUntilContextLoaded ?? CONFIG_DEFAULTS.gateDestructiveToolsUntilContextLoaded,
|
|
@@ -23504,9 +23510,9 @@ var DEFAULT_TEMPLATES = {
|
|
|
23504
23510
|
systemPrompt: [
|
|
23505
23511
|
"RALPH SUPERVISOR:",
|
|
23506
23512
|
"- You are the Ralph supervisor. You orchestrate RLM worker sessions; you do NOT write code yourself.",
|
|
23507
|
-
"- When the user gives you a goal, describe the task briefly and
|
|
23508
|
-
"- You
|
|
23509
|
-
" Supervisor = orchestration +
|
|
23513
|
+
"- When the user gives you a goal, describe the task briefly and then act as the strategist: call ralph_spawn_worker() to hand off.",
|
|
23514
|
+
"- You ARE the strategist in the main session, and you are NOT the RLM worker.",
|
|
23515
|
+
" Supervisor+strategist = orchestration + planning + delegation; RLM worker = implementation.",
|
|
23510
23516
|
"- Workers are spawned per-attempt with a fresh context window. They load state from protocol files.",
|
|
23511
23517
|
"- Protocol files (PLAN.md, RLM_INSTRUCTIONS.md, etc.) persist across all attempts \u2014 edit them to guide workers.",
|
|
23512
23518
|
"- After each worker attempt the plugin runs verify and either finishes or spawns the next worker.",
|
|
@@ -23514,6 +23520,7 @@ var DEFAULT_TEMPLATES = {
|
|
|
23514
23520
|
" When you receive one, call ralph_respond(id, answer) to unblock the session.",
|
|
23515
23521
|
"- Use ralph_doctor() to check setup, ralph_bootstrap_plan() to generate PLAN/TODOS,",
|
|
23516
23522
|
" ralph_create_supervisor_session() to bind/start explicitly, ralph_pause_supervision()/ralph_resume_supervision() to control execution, and ralph_end_supervision() to stop.",
|
|
23523
|
+
"- Only call loop-control tools (spawn, pause, resume, end) after the supervisor session is bound via ralph_create_supervisor_session().",
|
|
23517
23524
|
"- End supervision when verification has passed and the user confirms they are done, or when the user explicitly asks to stop the loop.",
|
|
23518
23525
|
"- Optional reviewer flow: worker marks readiness with ralph_request_review(); supervisor runs ralph_run_reviewer().",
|
|
23519
23526
|
"- Monitor progress in SUPERVISOR_LOG.md, CONVERSATION.md, or via toast notifications.",
|
|
@@ -23700,6 +23707,7 @@ var DEFAULT_TEMPLATES = {
|
|
|
23700
23707
|
"- You do NOT write code yourself; you are not the RLM worker.",
|
|
23701
23708
|
"- After reviewing state and optionally updating PLAN.md / RLM_INSTRUCTIONS.md,",
|
|
23702
23709
|
" call ralph_spawn_worker() to hand off to the RLM worker for this attempt.",
|
|
23710
|
+
"- You MUST call ralph_spawn_worker() exactly once per attempt.",
|
|
23703
23711
|
"- Then STOP. The plugin verifies independently and will spawn the next Ralph session if needed.",
|
|
23704
23712
|
"",
|
|
23705
23713
|
"Role boundaries:",
|
|
@@ -23720,7 +23728,7 @@ var DEFAULT_TEMPLATES = {
|
|
|
23720
23728
|
" guidance for the next worker based on patterns in the failures.",
|
|
23721
23729
|
"5. Optionally call ralph_set_status('running', 'strategy finalized').",
|
|
23722
23730
|
"6. Call ralph_report() summarizing strategy changes for this attempt.",
|
|
23723
|
-
"7. Call ralph_spawn_worker() to delegate the coding work to a fresh RLM worker.",
|
|
23731
|
+
"7. Call ralph_spawn_worker() to delegate the coding work to a fresh RLM worker (required).",
|
|
23724
23732
|
"8. STOP \u2014 the plugin handles verification and will spawn attempt {{nextAttempt}} if needed.",
|
|
23725
23733
|
"",
|
|
23726
23734
|
"You do not write code. Your value is strategic context adjustment between attempts.",
|
|
@@ -23728,7 +23736,10 @@ var DEFAULT_TEMPLATES = {
|
|
|
23728
23736
|
"Tool meaning:",
|
|
23729
23737
|
"- ralph_update_plan / ralph_update_rlm_instructions = durable strategy changes",
|
|
23730
23738
|
"- ralph_spawn_worker = handoff to implementation session",
|
|
23731
|
-
"- ralph_report = visible summary for the supervisor"
|
|
23739
|
+
"- ralph_report = visible summary for the supervisor",
|
|
23740
|
+
"",
|
|
23741
|
+
"Example flow:",
|
|
23742
|
+
'- ralph_load_context() \u2192 ralph_report("Strategy: update PLAN.md with constraint X") \u2192 ralph_spawn_worker() \u2192 STOP'
|
|
23732
23743
|
].join(`
|
|
23733
23744
|
`)
|
|
23734
23745
|
};
|
|
@@ -24279,6 +24290,49 @@ var RalphRLM = async ({ client, $, worktree }) => {
|
|
|
24279
24290
|
return false;
|
|
24280
24291
|
}
|
|
24281
24292
|
};
|
|
24293
|
+
const promptStrategistInMain = async (attempt) => {
|
|
24294
|
+
if (!supervisor.sessionId) {
|
|
24295
|
+
await notifySupervisor("supervisor", "Cannot prompt strategist: supervisor session not bound. Run ralph_create_supervisor_session().", "warning", true);
|
|
24296
|
+
return;
|
|
24297
|
+
}
|
|
24298
|
+
supervisor.awaitingStrategist = true;
|
|
24299
|
+
if (supervisor.ralphHandoffAttempt !== attempt) {
|
|
24300
|
+
supervisor.ralphHandoffAttempt = attempt;
|
|
24301
|
+
supervisor.ralphHandoffRetries = 0;
|
|
24302
|
+
}
|
|
24303
|
+
const promptText = interpolate(templates.ralphSessionPrompt, {
|
|
24304
|
+
attempt: String(attempt),
|
|
24305
|
+
nextAttempt: String(attempt + 1)
|
|
24306
|
+
});
|
|
24307
|
+
const ok = await sendPromptWithFallback(supervisor.sessionId, promptText, `Strategist prompt (attempt ${attempt})`, supervisor.sessionId);
|
|
24308
|
+
if (!ok) {
|
|
24309
|
+
supervisor.paused = true;
|
|
24310
|
+
await notifySupervisor(`supervisor/attempt-${attempt}`, "Strategist prompt failed; supervision paused. Retry with ralph_create_supervisor_session(restart_if_done=true).", "error", true, supervisor.sessionId);
|
|
24311
|
+
return;
|
|
24312
|
+
}
|
|
24313
|
+
if (supervisor.ralphHandoffTimer) {
|
|
24314
|
+
clearTimeout(supervisor.ralphHandoffTimer);
|
|
24315
|
+
}
|
|
24316
|
+
const cfg = await run(getConfig());
|
|
24317
|
+
const timeoutMs = Math.max(cfg.strategistHandoffMinutes, 1) * 60000;
|
|
24318
|
+
supervisor.ralphHandoffTimer = setTimeout(async () => {
|
|
24319
|
+
if (supervisor.done || supervisor.paused)
|
|
24320
|
+
return;
|
|
24321
|
+
if (!supervisor.awaitingStrategist)
|
|
24322
|
+
return;
|
|
24323
|
+
if (supervisor.ralphHandoffAttempt !== attempt)
|
|
24324
|
+
return;
|
|
24325
|
+
const retries = supervisor.ralphHandoffRetries ?? 0;
|
|
24326
|
+
if (retries < cfg.strategistHandoffMaxRetries) {
|
|
24327
|
+
supervisor.ralphHandoffRetries = retries + 1;
|
|
24328
|
+
await notifySupervisor(`supervisor/attempt-${attempt}`, `Strategist did not hand off; re-prompting main session (${retries + 1}/${cfg.strategistHandoffMaxRetries}).`, "warning", true, supervisor.sessionId);
|
|
24329
|
+
await promptStrategistInMain(attempt);
|
|
24330
|
+
return;
|
|
24331
|
+
}
|
|
24332
|
+
await notifySupervisor(`supervisor/attempt-${attempt}`, "Strategist did not hand off after retries; supervision paused. Use ralph_create_supervisor_session(restart_if_done=true) to retry.", "error", true, supervisor.sessionId);
|
|
24333
|
+
supervisor.paused = true;
|
|
24334
|
+
}, timeoutMs);
|
|
24335
|
+
};
|
|
24282
24336
|
const detectProjectDefaults = (root) => exports_Effect.gen(function* () {
|
|
24283
24337
|
const j = (f) => NodePath.join(root, f);
|
|
24284
24338
|
const hasBunLock = (yield* fileExists(j("bun.lockb"))) || (yield* fileExists(j("bun.lock")));
|
|
@@ -25122,7 +25176,7 @@ No pending questions found.`));
|
|
|
25122
25176
|
if (supervisor.done && args2.restart_if_done === true) {
|
|
25123
25177
|
supervisor.done = false;
|
|
25124
25178
|
supervisor.paused = false;
|
|
25125
|
-
supervisor.
|
|
25179
|
+
supervisor.awaitingStrategist = false;
|
|
25126
25180
|
supervisor.currentWorkerSessionId = undefined;
|
|
25127
25181
|
supervisor.activeReviewerName = undefined;
|
|
25128
25182
|
supervisor.activeReviewerAttempt = undefined;
|
|
@@ -25131,19 +25185,19 @@ No pending questions found.`));
|
|
|
25131
25185
|
await persistReviewerState();
|
|
25132
25186
|
await notifySupervisor("supervisor", "Supervisor done-state reset for a new run.", "info", true, sessionID);
|
|
25133
25187
|
}
|
|
25134
|
-
if (supervisor.
|
|
25188
|
+
if (supervisor.currentWorkerSessionId || supervisor.done) {
|
|
25135
25189
|
return JSON.stringify({
|
|
25136
25190
|
ok: true,
|
|
25137
25191
|
started: false,
|
|
25138
25192
|
message: "Loop is already running or completed for this process.",
|
|
25139
|
-
currentRalphSessionId: supervisor.currentRalphSessionId,
|
|
25140
25193
|
currentWorkerSessionId: supervisor.currentWorkerSessionId,
|
|
25141
25194
|
done: supervisor.done
|
|
25142
25195
|
}, null, 2);
|
|
25143
25196
|
}
|
|
25144
25197
|
supervisor.attempt = 1;
|
|
25198
|
+
supervisor.awaitingStrategist = false;
|
|
25145
25199
|
await notifySupervisor("supervisor", "Starting Ralph loop at attempt 1 (manual start).", "info", true, sessionID);
|
|
25146
|
-
await
|
|
25200
|
+
await promptStrategistInMain(1);
|
|
25147
25201
|
return JSON.stringify({ ok: true, started: true, attempt: 1 }, null, 2);
|
|
25148
25202
|
}
|
|
25149
25203
|
});
|
|
@@ -25159,6 +25213,7 @@ No pending questions found.`));
|
|
|
25159
25213
|
const reason = args2.reason?.trim();
|
|
25160
25214
|
supervisor.done = true;
|
|
25161
25215
|
supervisor.paused = true;
|
|
25216
|
+
supervisor.awaitingStrategist = false;
|
|
25162
25217
|
const sessionsToAbort = Array.from(sessionMap.keys());
|
|
25163
25218
|
for (const id of sessionsToAbort) {
|
|
25164
25219
|
await client.session.abort({ path: { id } }).catch(() => {});
|
|
@@ -25168,7 +25223,6 @@ No pending questions found.`));
|
|
|
25168
25223
|
}
|
|
25169
25224
|
stopAllCommands("supervision-ended");
|
|
25170
25225
|
sessionMap.clear();
|
|
25171
|
-
supervisor.currentRalphSessionId = undefined;
|
|
25172
25226
|
supervisor.currentWorkerSessionId = undefined;
|
|
25173
25227
|
supervisor.activeReviewerName = undefined;
|
|
25174
25228
|
supervisor.activeReviewerAttempt = undefined;
|
|
@@ -25192,7 +25246,7 @@ No pending questions found.`));
|
|
|
25192
25246
|
}
|
|
25193
25247
|
});
|
|
25194
25248
|
const tool_ralph_supervision_status = tool({
|
|
25195
|
-
description: "Get current supervision state (binding, attempt,
|
|
25249
|
+
description: "Get current supervision state (binding, attempt, awaiting strategist/worker, done flag).",
|
|
25196
25250
|
args: {},
|
|
25197
25251
|
async execute(_args, _ctx) {
|
|
25198
25252
|
return JSON.stringify({
|
|
@@ -25201,7 +25255,7 @@ No pending questions found.`));
|
|
|
25201
25255
|
attempt: supervisor.attempt,
|
|
25202
25256
|
done: supervisor.done,
|
|
25203
25257
|
paused: supervisor.paused ?? false,
|
|
25204
|
-
|
|
25258
|
+
awaitingStrategist: supervisor.awaitingStrategist ?? false,
|
|
25205
25259
|
currentWorkerSessionId: supervisor.currentWorkerSessionId ?? null,
|
|
25206
25260
|
activeReviewerName: supervisor.activeReviewerName ?? null,
|
|
25207
25261
|
activeReviewerAttempt: supervisor.activeReviewerAttempt ?? null,
|
|
@@ -25247,11 +25301,11 @@ No pending questions found.`));
|
|
|
25247
25301
|
message: "Loop is marked done. Use ralph_create_supervisor_session(restart_if_done=true)."
|
|
25248
25302
|
}, null, 2);
|
|
25249
25303
|
}
|
|
25250
|
-
if (supervisor.
|
|
25304
|
+
if (supervisor.currentWorkerSessionId) {
|
|
25251
25305
|
return JSON.stringify({ ok: true, resumed: true, started: false, message: "Loop already running." }, null, 2);
|
|
25252
25306
|
}
|
|
25253
25307
|
supervisor.attempt = Math.max(1, supervisor.attempt || 1);
|
|
25254
|
-
await
|
|
25308
|
+
await promptStrategistInMain(supervisor.attempt);
|
|
25255
25309
|
return JSON.stringify({ ok: true, resumed: true, started: true, attempt: supervisor.attempt }, null, 2);
|
|
25256
25310
|
}
|
|
25257
25311
|
});
|
|
@@ -25337,8 +25391,14 @@ Set a new goal and run again.
|
|
|
25337
25391
|
supervisor.done = false;
|
|
25338
25392
|
supervisor.paused = false;
|
|
25339
25393
|
supervisor.attempt = 0;
|
|
25340
|
-
supervisor.currentRalphSessionId = undefined;
|
|
25341
25394
|
supervisor.currentWorkerSessionId = undefined;
|
|
25395
|
+
supervisor.awaitingStrategist = false;
|
|
25396
|
+
supervisor.ralphHandoffRetries = 0;
|
|
25397
|
+
supervisor.ralphHandoffAttempt = undefined;
|
|
25398
|
+
if (supervisor.ralphHandoffTimer) {
|
|
25399
|
+
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25400
|
+
supervisor.ralphHandoffTimer = undefined;
|
|
25401
|
+
}
|
|
25342
25402
|
supervisor.activeReviewerName = undefined;
|
|
25343
25403
|
supervisor.activeReviewerAttempt = undefined;
|
|
25344
25404
|
supervisor.activeReviewerSessionId = undefined;
|
|
@@ -25428,7 +25488,7 @@ Set a new goal and run again.
|
|
|
25428
25488
|
supervisor.done = false;
|
|
25429
25489
|
supervisor.paused = false;
|
|
25430
25490
|
supervisor.attempt = 1;
|
|
25431
|
-
await
|
|
25491
|
+
await promptStrategistInMain(1);
|
|
25432
25492
|
actions.push("Started loop at attempt 1");
|
|
25433
25493
|
}
|
|
25434
25494
|
await appendConversationEntry("supervisor", `Quickstart completed for goal: ${args2.goal}`);
|
|
@@ -25613,18 +25673,12 @@ Set a new goal and run again.
|
|
|
25613
25673
|
s.lastProgressAt = now2;
|
|
25614
25674
|
});
|
|
25615
25675
|
};
|
|
25616
|
-
await maybeWarn(supervisor.currentRalphSessionId, "Strategist");
|
|
25617
25676
|
await maybeWarn(supervisor.currentWorkerSessionId, "Worker");
|
|
25618
25677
|
};
|
|
25619
25678
|
const clearSessionTracking = async (sessionId, reason) => {
|
|
25620
25679
|
const st = sessionMap.get(sessionId);
|
|
25621
25680
|
sessionMap.delete(sessionId);
|
|
25622
25681
|
let didUpdate = false;
|
|
25623
|
-
const clearedRalph = supervisor.currentRalphSessionId === sessionId;
|
|
25624
|
-
if (clearedRalph) {
|
|
25625
|
-
supervisor.currentRalphSessionId = undefined;
|
|
25626
|
-
didUpdate = true;
|
|
25627
|
-
}
|
|
25628
25682
|
if (supervisor.currentWorkerSessionId === sessionId) {
|
|
25629
25683
|
supervisor.currentWorkerSessionId = undefined;
|
|
25630
25684
|
didUpdate = true;
|
|
@@ -25637,7 +25691,7 @@ Set a new goal and run again.
|
|
|
25637
25691
|
didUpdate = true;
|
|
25638
25692
|
await persistReviewerState();
|
|
25639
25693
|
}
|
|
25640
|
-
if (supervisor.ralphHandoffTimer &&
|
|
25694
|
+
if (supervisor.ralphHandoffTimer && supervisor.sessionId === sessionId) {
|
|
25641
25695
|
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25642
25696
|
supervisor.ralphHandoffTimer = undefined;
|
|
25643
25697
|
}
|
|
@@ -25719,61 +25773,30 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25719
25773
|
tool_ralph_spawn_worker_impl = async (_args, ctx) => {
|
|
25720
25774
|
const sessionID = ctx.sessionID ?? "";
|
|
25721
25775
|
const st = sessionMap.get(sessionID);
|
|
25722
|
-
|
|
25723
|
-
|
|
25776
|
+
const isMainStrategist = st?.role === "main";
|
|
25777
|
+
if (!isMainStrategist && st?.role !== "ralph") {
|
|
25778
|
+
throw new Error("ralph_spawn_worker() must be called from the main strategist session.");
|
|
25724
25779
|
}
|
|
25725
|
-
if (
|
|
25726
|
-
throw new Error("ralph_spawn_worker()
|
|
25780
|
+
if (isMainStrategist && supervisor.sessionId && supervisor.sessionId !== sessionID) {
|
|
25781
|
+
throw new Error("ralph_spawn_worker() must be called from the bound supervisor session.");
|
|
25782
|
+
}
|
|
25783
|
+
if (supervisor.attempt < 1) {
|
|
25784
|
+
throw new Error("No active attempt. Start with ralph_create_supervisor_session(start_loop=true).");
|
|
25727
25785
|
}
|
|
25728
25786
|
if (supervisor.ralphHandoffTimer) {
|
|
25729
25787
|
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25730
25788
|
supervisor.ralphHandoffTimer = undefined;
|
|
25731
25789
|
}
|
|
25732
|
-
|
|
25790
|
+
supervisor.awaitingStrategist = false;
|
|
25791
|
+
const attempt = isMainStrategist ? supervisor.attempt : st.attempt;
|
|
25792
|
+
const workerId = await spawnRlmWorker(attempt);
|
|
25733
25793
|
mutateSession(sessionID, (s) => {
|
|
25734
25794
|
s.workerSpawned = true;
|
|
25735
25795
|
});
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
|
|
25739
|
-
|
|
25740
|
-
const result = await client.session.create({
|
|
25741
|
-
body: { title: `ralph-strategist-attempt-${attempt}` }
|
|
25742
|
-
});
|
|
25743
|
-
const ralphId = result.data?.id ?? `ralph-${Date.now()}`;
|
|
25744
|
-
supervisor.currentRalphSessionId = ralphId;
|
|
25745
|
-
sessionMap.set(ralphId, freshSession("ralph", attempt));
|
|
25746
|
-
mutateSession(ralphId, (s) => {
|
|
25747
|
-
s.lastProgressAt = Date.now();
|
|
25748
|
-
});
|
|
25749
|
-
const promptText = interpolate(templates.ralphSessionPrompt, {
|
|
25750
|
-
attempt: String(attempt),
|
|
25751
|
-
nextAttempt: String(attempt + 1)
|
|
25752
|
-
});
|
|
25753
|
-
const promptOk = await sendPromptWithFallback(ralphId, promptText, `Strategist prompt (attempt ${attempt})`, ralphId);
|
|
25754
|
-
if (!promptOk) {
|
|
25755
|
-
supervisor.currentRalphSessionId = undefined;
|
|
25756
|
-
await client.session.abort({ path: { id: ralphId } }).catch(() => {});
|
|
25757
|
-
await notifySupervisor(`supervisor/attempt-${attempt}`, "Strategist prompt failed; supervision paused. Retry with ralph_create_supervisor_session(restart_if_done=true).", "error", true);
|
|
25758
|
-
supervisor.paused = true;
|
|
25759
|
-
return;
|
|
25760
|
-
}
|
|
25761
|
-
if (supervisor.ralphHandoffTimer) {
|
|
25762
|
-
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25763
|
-
}
|
|
25764
|
-
const cfg = await run(getConfig());
|
|
25765
|
-
const timeoutMs = Math.max(cfg.heartbeatMinutes, 1) * 60000;
|
|
25766
|
-
supervisor.ralphHandoffTimer = setTimeout(async () => {
|
|
25767
|
-
if (supervisor.done || supervisor.paused)
|
|
25768
|
-
return;
|
|
25769
|
-
if (supervisor.currentRalphSessionId !== ralphId)
|
|
25770
|
-
return;
|
|
25771
|
-
const st = sessionMap.get(ralphId);
|
|
25772
|
-
if (st?.workerSpawned)
|
|
25773
|
-
return;
|
|
25774
|
-
await notifySupervisor(`ralph/attempt-${attempt}`, "Strategist did not hand off to a worker within the heartbeat window. Re-check prompt delivery or restart supervision.", "warning", true, ralphId);
|
|
25775
|
-
}, timeoutMs);
|
|
25776
|
-
await notifySupervisor(`supervisor/attempt-${attempt}`, `Spawned Ralph strategist session ${ralphId}.`, "info", true);
|
|
25796
|
+
supervisor.ralphHandoffRetries = 0;
|
|
25797
|
+
supervisor.ralphHandoffAttempt = attempt;
|
|
25798
|
+
await notifySupervisor(`supervisor/attempt-${attempt}`, `Delegated coding to worker session ${workerId}.`, "info", true, sessionID);
|
|
25799
|
+
return JSON.stringify({ ok: true, workerSessionId: workerId, attempt }, null, 2);
|
|
25777
25800
|
};
|
|
25778
25801
|
const handleWorkerIdle = async (workerSessionId) => {
|
|
25779
25802
|
if (supervisor.currentWorkerSessionId !== workerSessionId)
|
|
@@ -25792,6 +25815,11 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25792
25815
|
const { verdict, details } = await runAndParseVerify();
|
|
25793
25816
|
if (verdict === "pass") {
|
|
25794
25817
|
supervisor.done = true;
|
|
25818
|
+
supervisor.awaitingStrategist = false;
|
|
25819
|
+
if (supervisor.ralphHandoffTimer) {
|
|
25820
|
+
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25821
|
+
supervisor.ralphHandoffTimer = undefined;
|
|
25822
|
+
}
|
|
25795
25823
|
await run(writeFile(NodePath.join(worktree, FILES.NEXT_RALPH), interpolate(templates.doneFileContent, { timestamp: nowISO() })));
|
|
25796
25824
|
await client.tui.showToast({
|
|
25797
25825
|
body: { title: "Ralph: Done", message: "Verification passed. Loop complete.", variant: "success" }
|
|
@@ -25813,38 +25841,17 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25813
25841
|
await notifySupervisor(`worker/attempt-${supervisor.attempt}`, `Verification ${verdict}. Preparing next attempt.`, verdict === "fail" ? "warning" : "info", true, workerSessionId);
|
|
25814
25842
|
supervisor.attempt += 1;
|
|
25815
25843
|
await rolloverState(supervisor.attempt - 1, verdict, details);
|
|
25816
|
-
await
|
|
25817
|
-
};
|
|
25818
|
-
const handleRalphSessionIdle = async (ralphSessionId) => {
|
|
25819
|
-
if (supervisor.currentRalphSessionId !== ralphSessionId)
|
|
25820
|
-
return;
|
|
25821
|
-
if (supervisor.done)
|
|
25822
|
-
return;
|
|
25823
|
-
supervisor.currentRalphSessionId = undefined;
|
|
25824
|
-
const st = sessionMap.get(ralphSessionId);
|
|
25825
|
-
if (st && !st.reportedStatus) {
|
|
25826
|
-
await notifySupervisor(`ralph/attempt-${st.attempt}`, "No explicit strategist status reported before idle.", "info", true, ralphSessionId);
|
|
25827
|
-
}
|
|
25828
|
-
if (!st?.workerSpawned) {
|
|
25829
|
-
await client.tui.showToast({
|
|
25830
|
-
body: {
|
|
25831
|
-
title: "Ralph: no worker spawned",
|
|
25832
|
-
message: `Ralph session for attempt ${st?.attempt ?? supervisor.attempt} ended without calling ralph_spawn_worker().`,
|
|
25833
|
-
variant: "warning"
|
|
25834
|
-
}
|
|
25835
|
-
}).catch(() => {});
|
|
25836
|
-
await notifySupervisor(`ralph/attempt-${st?.attempt ?? supervisor.attempt}`, "Strategist went idle without spawning a worker.", "warning", true, ralphSessionId);
|
|
25837
|
-
}
|
|
25844
|
+
await promptStrategistInMain(supervisor.attempt);
|
|
25838
25845
|
};
|
|
25839
25846
|
const handleMainIdle = async (sessionID) => {
|
|
25840
25847
|
if (supervisor.done)
|
|
25841
25848
|
return;
|
|
25842
25849
|
if (supervisor.paused)
|
|
25843
25850
|
return;
|
|
25844
|
-
if (supervisor.currentRalphSessionId)
|
|
25845
|
-
return;
|
|
25846
25851
|
if (supervisor.currentWorkerSessionId)
|
|
25847
25852
|
return;
|
|
25853
|
+
if (supervisor.awaitingStrategist)
|
|
25854
|
+
return;
|
|
25848
25855
|
const cfg = await run(getConfig());
|
|
25849
25856
|
if (!cfg.enabled)
|
|
25850
25857
|
return;
|
|
@@ -25865,7 +25872,7 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25865
25872
|
}
|
|
25866
25873
|
supervisor.attempt = 1;
|
|
25867
25874
|
await notifySupervisor("supervisor", "Starting Ralph loop at attempt 1.", "info", true, sessionID);
|
|
25868
|
-
await
|
|
25875
|
+
await promptStrategistInMain(1);
|
|
25869
25876
|
};
|
|
25870
25877
|
return {
|
|
25871
25878
|
tool: {
|
|
@@ -25904,7 +25911,7 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25904
25911
|
output.system = output.system ?? [];
|
|
25905
25912
|
const sessionID = input.sessionID ?? input.session_id ?? input.session?.id;
|
|
25906
25913
|
const role = sessionMap.get(sessionID ?? "")?.role;
|
|
25907
|
-
const base = role === "worker" || role === "subagent" ? templates.workerSystemPrompt :
|
|
25914
|
+
const base = role === "worker" || role === "subagent" ? templates.workerSystemPrompt : templates.systemPrompt;
|
|
25908
25915
|
const full = templates.systemPromptAppend ? `${base}
|
|
25909
25916
|
${templates.systemPromptAppend}` : base;
|
|
25910
25917
|
output.system.push(full);
|
|
@@ -25946,8 +25953,6 @@ ${templates.systemPromptAppend}` : base;
|
|
|
25946
25953
|
const state = sessionMap.get(sessionID);
|
|
25947
25954
|
if (state?.role === "worker" && supervisor.currentWorkerSessionId !== sessionID)
|
|
25948
25955
|
return;
|
|
25949
|
-
if (state?.role === "ralph" && supervisor.currentRalphSessionId !== sessionID)
|
|
25950
|
-
return;
|
|
25951
25956
|
if (state) {
|
|
25952
25957
|
mutateSession(sessionID, (s) => {
|
|
25953
25958
|
s.lastProgressAt = Date.now();
|
|
@@ -25968,10 +25973,6 @@ ${templates.systemPromptAppend}` : base;
|
|
|
25968
25973
|
await handleWorkerIdle(sessionID).catch((err) => {
|
|
25969
25974
|
appLog("error", "handleWorkerIdle error", { error: String(err), sessionID });
|
|
25970
25975
|
});
|
|
25971
|
-
} else if (supervisor.currentRalphSessionId === sessionID) {
|
|
25972
|
-
await handleRalphSessionIdle(sessionID).catch((err) => {
|
|
25973
|
-
appLog("error", "handleRalphSessionIdle error", { error: String(err), sessionID });
|
|
25974
|
-
});
|
|
25975
25976
|
} else {
|
|
25976
25977
|
await handleMainIdle(sessionID).catch((err) => {
|
|
25977
25978
|
appLog("error", "handleMainIdle error", { error: String(err), sessionID });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-ralph-rlm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "OpenCode plugin: Ralph outer loop + RLM inner loop. Iterative AI development with file-first discipline and sub-agent support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/ralph-rlm.js",
|