opencode-ralph-rlm 0.1.8 → 0.1.10
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 +11 -1
- package/dist/ralph-rlm.js +143 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,13 @@ Each session role has a distinct purpose and **fresh context window**:
|
|
|
105
105
|
| **ralph** | Per-attempt strategist | Fresh | Review failure, update PLAN.md / RLM_INSTRUCTIONS.md, call `ralph_spawn_worker()`. |
|
|
106
106
|
| **worker** | Per-attempt coder | Fresh | `ralph_load_context()` → code → `ralph_verify()` → stop. |
|
|
107
107
|
|
|
108
|
+
### Roles and responsibilities (quick map)
|
|
109
|
+
|
|
110
|
+
- **Supervisor (main session):** orchestration and decisions only. Never edits files or runs code; uses `ralph_*` tools to control lifecycle.
|
|
111
|
+
- **Ralph strategist:** updates plan/instructions and delegates to a worker. No direct implementation.
|
|
112
|
+
- **RLM worker:** does the actual coding and verification for this attempt. One pass per session.
|
|
113
|
+
- **Sub-agent:** narrow task helper; updates its own state files under `.opencode/agents/<name>/`.
|
|
114
|
+
|
|
108
115
|
### The state machine
|
|
109
116
|
|
|
110
117
|
```
|
|
@@ -221,7 +228,8 @@ Create `.opencode/ralph.json`. All fields are optional — the plugin runs with
|
|
|
221
228
|
| `statusVerbosity` | `"normal"` | Supervisor status emission level: `minimal` (warnings/errors), `normal`, or `verbose`. |
|
|
222
229
|
| `maxAttempts` | `20` | Hard stop after this many failed verify attempts. |
|
|
223
230
|
| `heartbeatMinutes` | `15` | Warn if active strategist/worker has no progress for this many minutes. |
|
|
224
|
-
| `
|
|
231
|
+
| `verifyTimeoutMinutes` | `0` | Timeout for verify command in minutes. `0` disables timeouts. |
|
|
232
|
+
| `verify.command` | - | Shell command to run as an array, e.g. `["bun", "run", "verify"]`. If omitted, verify always returns `unknown`. |
|
|
225
233
|
| `verify.cwd` | `"."` | Working directory for the verify command, relative to the repo root. |
|
|
226
234
|
| `gateDestructiveToolsUntilContextLoaded` | `true` | Block `write`, `edit`, `bash`, etc. until `ralph_load_context()` has been called in the current attempt. |
|
|
227
235
|
| `maxRlmSliceLines` | `200` | Maximum lines a single `rlm_slice` call may return. |
|
|
@@ -439,6 +447,8 @@ Stop supervision for the current process. This prevents further auto-loop orches
|
|
|
439
447
|
|
|
440
448
|
- Use this when you want to pause/stop Ralph from spawning more sessions.
|
|
441
449
|
- Use this after verification passes and the user confirms they are done, or when the user asks to stop the loop.
|
|
450
|
+
- Active child sessions are aborted and any running verify command is stopped.
|
|
451
|
+
- Pass `delete_sessions: true` to delete child sessions after aborting them.
|
|
442
452
|
- Resume later with `ralph_create_supervisor_session(restart_if_done=true)`.
|
|
443
453
|
|
|
444
454
|
#### `ralph_supervision_status()`
|
package/dist/ralph-rlm.js
CHANGED
|
@@ -23417,6 +23417,7 @@ 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
|
+
verifyTimeoutMinutes: exports_Schema.optional(exports_Schema.Number),
|
|
23420
23421
|
verify: exports_Schema.optional(VerifyConfigSchema),
|
|
23421
23422
|
gateDestructiveToolsUntilContextLoaded: exports_Schema.optional(exports_Schema.Boolean),
|
|
23422
23423
|
maxRlmSliceLines: exports_Schema.optional(exports_Schema.Number),
|
|
@@ -23455,6 +23456,7 @@ var CONFIG_DEFAULTS = {
|
|
|
23455
23456
|
statusVerbosity: "normal",
|
|
23456
23457
|
maxAttempts: 20,
|
|
23457
23458
|
heartbeatMinutes: 15,
|
|
23459
|
+
verifyTimeoutMinutes: 0,
|
|
23458
23460
|
gateDestructiveToolsUntilContextLoaded: true,
|
|
23459
23461
|
maxRlmSliceLines: 200,
|
|
23460
23462
|
requireGrepBeforeLargeSlice: true,
|
|
@@ -23480,6 +23482,7 @@ function resolveConfig(raw) {
|
|
|
23480
23482
|
statusVerbosity: raw.statusVerbosity ?? CONFIG_DEFAULTS.statusVerbosity,
|
|
23481
23483
|
maxAttempts: toBoundedInt(raw.maxAttempts, CONFIG_DEFAULTS.maxAttempts, 1, 500),
|
|
23482
23484
|
heartbeatMinutes: toBoundedInt(raw.heartbeatMinutes, CONFIG_DEFAULTS.heartbeatMinutes, 1, 240),
|
|
23485
|
+
verifyTimeoutMinutes: toBoundedInt(raw.verifyTimeoutMinutes, CONFIG_DEFAULTS.verifyTimeoutMinutes, 0, 240),
|
|
23483
23486
|
...verify !== undefined ? { verify } : {},
|
|
23484
23487
|
gateDestructiveToolsUntilContextLoaded: raw.gateDestructiveToolsUntilContextLoaded ?? CONFIG_DEFAULTS.gateDestructiveToolsUntilContextLoaded,
|
|
23485
23488
|
maxRlmSliceLines,
|
|
@@ -23502,6 +23505,8 @@ var DEFAULT_TEMPLATES = {
|
|
|
23502
23505
|
"RALPH SUPERVISOR:",
|
|
23503
23506
|
"- You are the Ralph supervisor. You orchestrate RLM worker sessions; you do NOT write code yourself.",
|
|
23504
23507
|
"- When the user gives you a goal, describe the task briefly and stop \u2014 the plugin will spawn an RLM worker automatically.",
|
|
23508
|
+
"- You are NOT the Ralph strategist and NOT the RLM worker. Those are separate sessions.",
|
|
23509
|
+
" Supervisor = orchestration + decisions; Ralph strategist = planning + delegation; RLM worker = implementation.",
|
|
23505
23510
|
"- Workers are spawned per-attempt with a fresh context window. They load state from protocol files.",
|
|
23506
23511
|
"- Protocol files (PLAN.md, RLM_INSTRUCTIONS.md, etc.) persist across all attempts \u2014 edit them to guide workers.",
|
|
23507
23512
|
"- After each worker attempt the plugin runs verify and either finishes or spawns the next worker.",
|
|
@@ -23511,7 +23516,13 @@ var DEFAULT_TEMPLATES = {
|
|
|
23511
23516
|
" ralph_create_supervisor_session() to bind/start explicitly, ralph_pause_supervision()/ralph_resume_supervision() to control execution, and ralph_end_supervision() to stop.",
|
|
23512
23517
|
"- End supervision when verification has passed and the user confirms they are done, or when the user explicitly asks to stop the loop.",
|
|
23513
23518
|
"- Optional reviewer flow: worker marks readiness with ralph_request_review(); supervisor runs ralph_run_reviewer().",
|
|
23514
|
-
"- Monitor progress in SUPERVISOR_LOG.md, CONVERSATION.md, or via toast notifications."
|
|
23519
|
+
"- Monitor progress in SUPERVISOR_LOG.md, CONVERSATION.md, or via toast notifications.",
|
|
23520
|
+
"",
|
|
23521
|
+
"Big picture:",
|
|
23522
|
+
"- The supervisor owns the lifecycle only. It never edits files or runs rlm_grep/rlm_slice.",
|
|
23523
|
+
"- All implementation happens in RLM worker sessions spawned per attempt.",
|
|
23524
|
+
"- Strategy changes must be written to PLAN.md / RLM_INSTRUCTIONS.md so each new worker sees them.",
|
|
23525
|
+
"- Stop the loop explicitly with ralph_end_supervision() when the user is done or verification passed."
|
|
23515
23526
|
].join(`
|
|
23516
23527
|
`),
|
|
23517
23528
|
systemPromptAppend: "",
|
|
@@ -23562,7 +23573,11 @@ var DEFAULT_TEMPLATES = {
|
|
|
23562
23573
|
"- Keep CURRENT_STATE.md updated as you work (objective, hypothesis, actions, evidence, result).",
|
|
23563
23574
|
"- Write durable learnings to NOTES_AND_LEARNINGS.md (append-only).",
|
|
23564
23575
|
'- When you have completed the goal, write your final answer under a "{{doneHeading}}" heading in CURRENT_STATE.md.',
|
|
23565
|
-
"- Then output the exact phrase on its own line: {{doneSentinel}}"
|
|
23576
|
+
"- Then output the exact phrase on its own line: {{doneSentinel}}",
|
|
23577
|
+
"",
|
|
23578
|
+
"Role boundaries:",
|
|
23579
|
+
"- You are a child of the RLM worker. Do NOT act as the supervisor or Ralph strategist.",
|
|
23580
|
+
"- Do NOT write outside your stateDir unless explicitly instructed by your parent session."
|
|
23566
23581
|
].join(`
|
|
23567
23582
|
`),
|
|
23568
23583
|
subagentDoneSentinel: "SUB_AGENT_DONE",
|
|
@@ -23580,6 +23595,7 @@ var DEFAULT_TEMPLATES = {
|
|
|
23580
23595
|
"- Write durable learnings to NOTES_AND_LEARNINGS.md (append-only).",
|
|
23581
23596
|
"- Report meaningful progress with ralph_report() so the supervisor can track attempts in SUPERVISOR_LOG.md and CONVERSATION.md.",
|
|
23582
23597
|
"- Modify these instructions via ralph_update_rlm_instructions(patch, reason).",
|
|
23598
|
+
"- You are NOT the supervisor and NOT the Ralph strategist. You implement code for this attempt only.",
|
|
23583
23599
|
"",
|
|
23584
23600
|
"## Skills / MCP Registry (editable)",
|
|
23585
23601
|
"- (list tools, MCP servers, playbooks)",
|
|
@@ -23621,11 +23637,18 @@ var DEFAULT_TEMPLATES = {
|
|
|
23621
23637
|
"- CONTEXT_FOR_RLM.md is large. Access via rlm_grep + rlm_slice only. Never full-dump it.",
|
|
23622
23638
|
"- CURRENT_STATE.md is scratch for this attempt. Write your progress here.",
|
|
23623
23639
|
"- NOTES_AND_LEARNINGS.md is append-only. Write durable insights here.",
|
|
23640
|
+
"- You are the IMPLEMENTER for this attempt. Do the coding work; do NOT supervise or strategize beyond this attempt.",
|
|
23624
23641
|
"- Send status updates with ralph_report() at start, after major milestones, and before ralph_verify().",
|
|
23625
23642
|
"- Optionally call ralph_set_status(running|blocked|done|error, note) for explicit state handoff.",
|
|
23626
23643
|
"- When satisfied with your changes, call ralph_verify() once, then STOP.",
|
|
23627
23644
|
" The Ralph supervisor evaluates the result and will spawn the next attempt if needed.",
|
|
23628
|
-
"- Do NOT re-prompt yourself. One pass per session."
|
|
23645
|
+
"- Do NOT re-prompt yourself. One pass per session.",
|
|
23646
|
+
"",
|
|
23647
|
+
"Lifecycle + communication:",
|
|
23648
|
+
"- Start: ralph_load_context() \u2192 plan \u2192 ralph_report().",
|
|
23649
|
+
"- Work: edit files, keep CURRENT_STATE.md updated.",
|
|
23650
|
+
"- Finish: ralph_set_status('done', ...) \u2192 ralph_report() \u2192 ralph_verify() \u2192 STOP.",
|
|
23651
|
+
"- Questions: use ralph_ask() only if you are blocked on a human decision."
|
|
23629
23652
|
].join(`
|
|
23630
23653
|
`),
|
|
23631
23654
|
workerPrompt: [
|
|
@@ -23635,22 +23658,34 @@ var DEFAULT_TEMPLATES = {
|
|
|
23635
23658
|
"1. Call ralph_load_context() FIRST. It contains PLAN.md, RLM_INSTRUCTIONS.md, previous state, and all context.",
|
|
23636
23659
|
"2. Read AGENT_CONTEXT_FOR_NEXT_RALPH.md for the verdict and next step from the previous attempt.",
|
|
23637
23660
|
"3. Follow RLM_INSTRUCTIONS.md for project-specific playbooks.",
|
|
23661
|
+
"3.5 You are the IMPLEMENTER for this attempt (not the supervisor or strategist).",
|
|
23638
23662
|
"4. Immediately call ralph_report() with your plan for this attempt.",
|
|
23639
23663
|
"5. Optionally call ralph_set_status('running', '...') once you've scoped the approach.",
|
|
23640
23664
|
"6. Do the work. Write progress to CURRENT_STATE.md throughout and call ralph_report() at meaningful checkpoints.",
|
|
23641
23665
|
"7. When done, call ralph_set_status('done', '...') and ralph_report() with outcome + remaining risks, then call ralph_verify().",
|
|
23642
23666
|
"8. STOP \u2014 do not send further messages.",
|
|
23643
|
-
" The Ralph supervisor will handle the result and spawn attempt {{nextAttempt}} if needed."
|
|
23667
|
+
" The Ralph supervisor will handle the result and spawn attempt {{nextAttempt}} if needed.",
|
|
23668
|
+
"",
|
|
23669
|
+
"Tool meaning:",
|
|
23670
|
+
"- ralph_report = visible progress in SUPERVISOR_LOG.md + CONVERSATION.md",
|
|
23671
|
+
"- ralph_verify = run verify.command and end your attempt",
|
|
23672
|
+
"- rlm_grep/rlm_slice = safe access to large reference files",
|
|
23673
|
+
"- ralph_ask = ask the supervisor a blocking decision; you will wait for ralph_respond"
|
|
23644
23674
|
].join(`
|
|
23645
23675
|
`),
|
|
23646
23676
|
ralphSessionSystemPrompt: [
|
|
23647
23677
|
"RALPH STRATEGIST SESSION:",
|
|
23648
23678
|
"- You are Ralph, the strategic supervisor for one attempt.",
|
|
23649
23679
|
"- You review failures, update plans and instructions, then delegate coding to an RLM worker.",
|
|
23650
|
-
"- You do NOT write code yourself.",
|
|
23680
|
+
"- You do NOT write code yourself; you are not the RLM worker.",
|
|
23651
23681
|
"- After reviewing state and optionally updating PLAN.md / RLM_INSTRUCTIONS.md,",
|
|
23652
23682
|
" call ralph_spawn_worker() to hand off to the RLM worker for this attempt.",
|
|
23653
|
-
"- Then STOP. The plugin verifies independently and will spawn the next Ralph session if needed."
|
|
23683
|
+
"- Then STOP. The plugin verifies independently and will spawn the next Ralph session if needed.",
|
|
23684
|
+
"",
|
|
23685
|
+
"Role boundaries:",
|
|
23686
|
+
"- Do not edit source code or run tests directly.",
|
|
23687
|
+
"- Do update PLAN.md / RLM_INSTRUCTIONS.md if strategy needs adjustment.",
|
|
23688
|
+
"- Your output should be: strategy summary + worker spawned."
|
|
23654
23689
|
].join(`
|
|
23655
23690
|
`),
|
|
23656
23691
|
ralphSessionPrompt: [
|
|
@@ -23660,6 +23695,7 @@ var DEFAULT_TEMPLATES = {
|
|
|
23660
23695
|
"1. Call ralph_load_context() to review all protocol files.",
|
|
23661
23696
|
"2. Read AGENT_CONTEXT_FOR_NEXT_RALPH.md \u2014 what failed last time and why.",
|
|
23662
23697
|
"3. Review PLAN.md \u2014 is the goal still correct? Any constraints to add?",
|
|
23698
|
+
"3.5 You are the STRATEGIST for this attempt (not the supervisor or implementer).",
|
|
23663
23699
|
"4. Optionally call ralph_update_plan() or ralph_update_rlm_instructions() to improve",
|
|
23664
23700
|
" guidance for the next worker based on patterns in the failures.",
|
|
23665
23701
|
"5. Optionally call ralph_set_status('running', 'strategy finalized').",
|
|
@@ -23667,7 +23703,12 @@ var DEFAULT_TEMPLATES = {
|
|
|
23667
23703
|
"7. Call ralph_spawn_worker() to delegate the coding work to a fresh RLM worker.",
|
|
23668
23704
|
"8. STOP \u2014 the plugin handles verification and will spawn attempt {{nextAttempt}} if needed.",
|
|
23669
23705
|
"",
|
|
23670
|
-
"You do not write code. Your value is strategic context adjustment between attempts."
|
|
23706
|
+
"You do not write code. Your value is strategic context adjustment between attempts.",
|
|
23707
|
+
"",
|
|
23708
|
+
"Tool meaning:",
|
|
23709
|
+
"- ralph_update_plan / ralph_update_rlm_instructions = durable strategy changes",
|
|
23710
|
+
"- ralph_spawn_worker = handoff to implementation session",
|
|
23711
|
+
"- ralph_report = visible summary for the supervisor"
|
|
23671
23712
|
].join(`
|
|
23672
23713
|
`)
|
|
23673
23714
|
};
|
|
@@ -23762,7 +23803,26 @@ var writePendingInput = async (root, data) => {
|
|
|
23762
23803
|
await NodeFs.writeFile(p, JSON.stringify(data, null, 2), "utf8");
|
|
23763
23804
|
};
|
|
23764
23805
|
var sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
23765
|
-
|
|
23806
|
+
var activeCommands = new Set;
|
|
23807
|
+
var stopCommand = (cmd, reason) => {
|
|
23808
|
+
if (cmd.child.killed)
|
|
23809
|
+
return;
|
|
23810
|
+
cmd.timedOut = cmd.timedOut || reason === "timeout";
|
|
23811
|
+
try {
|
|
23812
|
+
cmd.child.kill();
|
|
23813
|
+
} catch {}
|
|
23814
|
+
cmd.killTimer = setTimeout(() => {
|
|
23815
|
+
try {
|
|
23816
|
+
cmd.child.kill("SIGKILL");
|
|
23817
|
+
} catch {}
|
|
23818
|
+
}, 2000);
|
|
23819
|
+
};
|
|
23820
|
+
var stopAllCommands = (reason) => {
|
|
23821
|
+
for (const cmd of activeCommands) {
|
|
23822
|
+
stopCommand(cmd, reason);
|
|
23823
|
+
}
|
|
23824
|
+
};
|
|
23825
|
+
async function runCommand(command, cwd, options) {
|
|
23766
23826
|
return await new Promise((resolve) => {
|
|
23767
23827
|
const child = spawn(command[0] ?? "", command.slice(1), {
|
|
23768
23828
|
cwd,
|
|
@@ -23772,6 +23832,18 @@ async function runCommand(command, cwd) {
|
|
|
23772
23832
|
});
|
|
23773
23833
|
let stdout = "";
|
|
23774
23834
|
let stderr = "";
|
|
23835
|
+
const entry = {
|
|
23836
|
+
child,
|
|
23837
|
+
label: options?.label ?? command.join(" "),
|
|
23838
|
+
startedAt: Date.now(),
|
|
23839
|
+
timedOut: false
|
|
23840
|
+
};
|
|
23841
|
+
activeCommands.add(entry);
|
|
23842
|
+
if (options?.timeoutMs && options.timeoutMs > 0) {
|
|
23843
|
+
entry.timeoutTimer = setTimeout(() => {
|
|
23844
|
+
stopCommand(entry, "timeout");
|
|
23845
|
+
}, options.timeoutMs);
|
|
23846
|
+
}
|
|
23775
23847
|
child.stdout?.on("data", (chunk3) => {
|
|
23776
23848
|
stdout += String(chunk3);
|
|
23777
23849
|
});
|
|
@@ -23779,11 +23851,23 @@ async function runCommand(command, cwd) {
|
|
|
23779
23851
|
stderr += String(chunk3);
|
|
23780
23852
|
});
|
|
23781
23853
|
child.on("error", (err) => {
|
|
23854
|
+
if (entry.timeoutTimer)
|
|
23855
|
+
clearTimeout(entry.timeoutTimer);
|
|
23856
|
+
if (entry.killTimer)
|
|
23857
|
+
clearTimeout(entry.killTimer);
|
|
23858
|
+
activeCommands.delete(entry);
|
|
23782
23859
|
resolve({ ok: false, code: null, stdout, stderr: `${stderr}
|
|
23783
23860
|
${String(err)}`.trim() });
|
|
23784
23861
|
});
|
|
23785
23862
|
child.on("close", (code) => {
|
|
23786
|
-
|
|
23863
|
+
if (entry.timeoutTimer)
|
|
23864
|
+
clearTimeout(entry.timeoutTimer);
|
|
23865
|
+
if (entry.killTimer)
|
|
23866
|
+
clearTimeout(entry.killTimer);
|
|
23867
|
+
activeCommands.delete(entry);
|
|
23868
|
+
const timeoutNote = entry.timedOut ? `
|
|
23869
|
+
[ralph] command timed out` : "";
|
|
23870
|
+
resolve({ ok: code === 0 && !entry.timedOut, code, stdout, stderr: `${stderr}${timeoutNote}`.trim() });
|
|
23787
23871
|
});
|
|
23788
23872
|
});
|
|
23789
23873
|
}
|
|
@@ -24295,9 +24379,11 @@ var RalphRLM = async ({ client, $, worktree }) => {
|
|
|
24295
24379
|
}
|
|
24296
24380
|
const verifyCmd = cfg.verify.command;
|
|
24297
24381
|
const cwd = NodePath.join(root, cfg.verify.cwd ?? ".");
|
|
24382
|
+
const timeoutMs = cfg.verifyTimeoutMinutes > 0 ? cfg.verifyTimeoutMinutes * 60000 : null;
|
|
24298
24383
|
return yield* exports_Effect.tryPromise({
|
|
24299
24384
|
try: async () => {
|
|
24300
|
-
const
|
|
24385
|
+
const options = timeoutMs ? { timeoutMs, label: "verify" } : { label: "verify" };
|
|
24386
|
+
const result = await runCommand(verifyCmd, cwd, options);
|
|
24301
24387
|
if (result.ok) {
|
|
24302
24388
|
return JSON.stringify({ verdict: "pass", output: result.stdout }, null, 2);
|
|
24303
24389
|
}
|
|
@@ -25024,6 +25110,7 @@ No pending questions found.`));
|
|
|
25024
25110
|
description: "Stop Ralph supervision for this process. Prevents further auto-loop orchestration until restarted.",
|
|
25025
25111
|
args: {
|
|
25026
25112
|
reason: tool.schema.string().optional().describe("Optional reason for ending supervision."),
|
|
25113
|
+
delete_sessions: tool.schema.boolean().optional().describe("Also delete child sessions after aborting them (default false)."),
|
|
25027
25114
|
clear_binding: tool.schema.boolean().optional().describe("Clear supervisor session binding after stop (default false).")
|
|
25028
25115
|
},
|
|
25029
25116
|
async execute(args2, ctx) {
|
|
@@ -25031,6 +25118,15 @@ No pending questions found.`));
|
|
|
25031
25118
|
const reason = args2.reason?.trim();
|
|
25032
25119
|
supervisor.done = true;
|
|
25033
25120
|
supervisor.paused = true;
|
|
25121
|
+
const sessionsToAbort = Array.from(sessionMap.keys());
|
|
25122
|
+
for (const id of sessionsToAbort) {
|
|
25123
|
+
await client.session.abort({ path: { id } }).catch(() => {});
|
|
25124
|
+
if (args2.delete_sessions) {
|
|
25125
|
+
await client.session.delete({ path: { id } }).catch(() => {});
|
|
25126
|
+
}
|
|
25127
|
+
}
|
|
25128
|
+
stopAllCommands("supervision-ended");
|
|
25129
|
+
sessionMap.clear();
|
|
25034
25130
|
supervisor.currentRalphSessionId = undefined;
|
|
25035
25131
|
supervisor.currentWorkerSessionId = undefined;
|
|
25036
25132
|
supervisor.activeReviewerName = undefined;
|
|
@@ -25451,6 +25547,8 @@ Set a new goal and run again.
|
|
|
25451
25547
|
}
|
|
25452
25548
|
});
|
|
25453
25549
|
const emitHeartbeatWarnings = async () => {
|
|
25550
|
+
if (supervisor.done || supervisor.paused)
|
|
25551
|
+
return;
|
|
25454
25552
|
const cfg = await run(getConfig());
|
|
25455
25553
|
const thresholdMs = cfg.heartbeatMinutes * 60000;
|
|
25456
25554
|
const now2 = Date.now();
|
|
@@ -25473,6 +25571,30 @@ Set a new goal and run again.
|
|
|
25473
25571
|
await maybeWarn(supervisor.currentRalphSessionId, "Strategist");
|
|
25474
25572
|
await maybeWarn(supervisor.currentWorkerSessionId, "Worker");
|
|
25475
25573
|
};
|
|
25574
|
+
const clearSessionTracking = async (sessionId, reason) => {
|
|
25575
|
+
const st = sessionMap.get(sessionId);
|
|
25576
|
+
sessionMap.delete(sessionId);
|
|
25577
|
+
let didUpdate = false;
|
|
25578
|
+
if (supervisor.currentRalphSessionId === sessionId) {
|
|
25579
|
+
supervisor.currentRalphSessionId = undefined;
|
|
25580
|
+
didUpdate = true;
|
|
25581
|
+
}
|
|
25582
|
+
if (supervisor.currentWorkerSessionId === sessionId) {
|
|
25583
|
+
supervisor.currentWorkerSessionId = undefined;
|
|
25584
|
+
didUpdate = true;
|
|
25585
|
+
}
|
|
25586
|
+
if (supervisor.activeReviewerSessionId === sessionId) {
|
|
25587
|
+
supervisor.activeReviewerSessionId = undefined;
|
|
25588
|
+
supervisor.activeReviewerName = undefined;
|
|
25589
|
+
supervisor.activeReviewerAttempt = undefined;
|
|
25590
|
+
supervisor.activeReviewerOutputPath = undefined;
|
|
25591
|
+
didUpdate = true;
|
|
25592
|
+
await persistReviewerState();
|
|
25593
|
+
}
|
|
25594
|
+
if (didUpdate && st) {
|
|
25595
|
+
await notifySupervisor(`${st.role}/attempt-${st.attempt}`, `${st.role} session ended (${reason}).`, "info", true, sessionId);
|
|
25596
|
+
}
|
|
25597
|
+
};
|
|
25476
25598
|
const runAndParseVerify = async () => {
|
|
25477
25599
|
const raw = await run(runVerify(worktree));
|
|
25478
25600
|
try {
|
|
@@ -25740,7 +25862,13 @@ ${templates.systemPromptAppend}` : base;
|
|
|
25740
25862
|
getSession(sessionID);
|
|
25741
25863
|
}
|
|
25742
25864
|
if (event?.type === "session.status" && sessionID) {
|
|
25865
|
+
if (supervisor.done || supervisor.paused)
|
|
25866
|
+
return;
|
|
25743
25867
|
const state = sessionMap.get(sessionID);
|
|
25868
|
+
if (state?.role === "worker" && supervisor.currentWorkerSessionId !== sessionID)
|
|
25869
|
+
return;
|
|
25870
|
+
if (state?.role === "ralph" && supervisor.currentRalphSessionId !== sessionID)
|
|
25871
|
+
return;
|
|
25744
25872
|
if (state) {
|
|
25745
25873
|
mutateSession(sessionID, (s) => {
|
|
25746
25874
|
s.lastProgressAt = Date.now();
|
|
@@ -25752,6 +25880,8 @@ ${templates.systemPromptAppend}` : base;
|
|
|
25752
25880
|
}
|
|
25753
25881
|
}
|
|
25754
25882
|
if (event?.type === "session.idle" && sessionID) {
|
|
25883
|
+
if (supervisor.done || supervisor.paused)
|
|
25884
|
+
return;
|
|
25755
25885
|
await emitHeartbeatWarnings().catch((err) => {
|
|
25756
25886
|
appLog("error", "heartbeat warning error", { error: String(err) });
|
|
25757
25887
|
});
|
|
@@ -25769,6 +25899,9 @@ ${templates.systemPromptAppend}` : base;
|
|
|
25769
25899
|
});
|
|
25770
25900
|
}
|
|
25771
25901
|
}
|
|
25902
|
+
if (sessionID && (event?.type === "session.closed" || event?.type === "session.ended" || event?.type === "session.deleted")) {
|
|
25903
|
+
await clearSessionTracking(sessionID, event.type);
|
|
25904
|
+
}
|
|
25772
25905
|
}
|
|
25773
25906
|
};
|
|
25774
25907
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-ralph-rlm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
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",
|