opencode-ralph-rlm 0.1.10 → 0.1.12
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/dist/ralph-rlm.js +94 -15
- package/package.json +1 -1
package/dist/ralph-rlm.js
CHANGED
|
@@ -23522,7 +23522,22 @@ var DEFAULT_TEMPLATES = {
|
|
|
23522
23522
|
"- The supervisor owns the lifecycle only. It never edits files or runs rlm_grep/rlm_slice.",
|
|
23523
23523
|
"- All implementation happens in RLM worker sessions spawned per attempt.",
|
|
23524
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."
|
|
23525
|
+
"- Stop the loop explicitly with ralph_end_supervision() when the user is done or verification passed.",
|
|
23526
|
+
"",
|
|
23527
|
+
"Onboarding checklist (supervisor):",
|
|
23528
|
+
"- If setup is incomplete, run ralph_doctor(autofix=true).",
|
|
23529
|
+
"- If PLAN.md is missing or weak, run ralph_quickstart_wizard(...) or ralph_bootstrap_plan(...) + ralph_validate_plan().",
|
|
23530
|
+
"- Start supervision with ralph_create_supervisor_session(start_loop=true) unless autoStartOnMainIdle is enabled.",
|
|
23531
|
+
"- Use ralph_supervision_status() to confirm current attempt and active sessions.",
|
|
23532
|
+
"",
|
|
23533
|
+
"Protocol files (purpose):",
|
|
23534
|
+
"- PLAN.md: goals, milestones, definition of done (authoritative).",
|
|
23535
|
+
"- RLM_INSTRUCTIONS.md: inner loop operating manual (authoritative, updated between attempts).",
|
|
23536
|
+
"- CURRENT_STATE.md: scratch for the current attempt only.",
|
|
23537
|
+
"- PREVIOUS_STATE.md: snapshot of last attempt's scratch.",
|
|
23538
|
+
"- NOTES_AND_LEARNINGS.md: durable learnings across attempts.",
|
|
23539
|
+
"- CONVERSATION.md / SUPERVISOR_LOG.md: status feed and timeline for the supervisor.",
|
|
23540
|
+
"- CONTEXT_FOR_RLM.md: large reference; workers must use rlm_grep/rlm_slice."
|
|
23526
23541
|
].join(`
|
|
23527
23542
|
`),
|
|
23528
23543
|
systemPromptAppend: "",
|
|
@@ -23536,7 +23551,12 @@ var DEFAULT_TEMPLATES = {
|
|
|
23536
23551
|
"- NOTES_AND_LEARNINGS.md \u2014 append-only durable learnings",
|
|
23537
23552
|
"- CONVERSATION.md \u2014 append-only supervisor-visible status feed",
|
|
23538
23553
|
"- CONTEXT_FOR_RLM.md \u2014 large reference; access via rlm_grep + rlm_slice",
|
|
23539
|
-
"- .opencode/agents/<name>/ \u2014 sub-agent state directories"
|
|
23554
|
+
"- .opencode/agents/<name>/ \u2014 sub-agent state directories",
|
|
23555
|
+
"",
|
|
23556
|
+
"## Loop control",
|
|
23557
|
+
"- ralph_create_supervisor_session(start_loop=true) to start",
|
|
23558
|
+
"- ralph_pause_supervision() / ralph_resume_supervision() to pause/resume",
|
|
23559
|
+
"- ralph_end_supervision() to stop and clean up"
|
|
23540
23560
|
].join(`
|
|
23541
23561
|
`),
|
|
23542
23562
|
continuePrompt: [
|
|
@@ -24238,6 +24258,27 @@ var RalphRLM = async ({ client, $, worktree }) => {
|
|
|
24238
24258
|
}).catch(() => {});
|
|
24239
24259
|
}
|
|
24240
24260
|
};
|
|
24261
|
+
const sendPromptWithFallback = async (sessionId, text, label, originSessionId) => {
|
|
24262
|
+
try {
|
|
24263
|
+
await client.session.promptAsync({
|
|
24264
|
+
path: { id: sessionId },
|
|
24265
|
+
body: { parts: [{ type: "text", text }] }
|
|
24266
|
+
});
|
|
24267
|
+
return true;
|
|
24268
|
+
} catch (err) {
|
|
24269
|
+
await notifySupervisor("supervisor", `${label} promptAsync failed: ${err instanceof Error ? err.message : String(err)}`, "warning", true, originSessionId);
|
|
24270
|
+
}
|
|
24271
|
+
try {
|
|
24272
|
+
await client.session.prompt({
|
|
24273
|
+
path: { id: sessionId },
|
|
24274
|
+
body: { parts: [{ type: "text", text }] }
|
|
24275
|
+
});
|
|
24276
|
+
return true;
|
|
24277
|
+
} catch (err) {
|
|
24278
|
+
await notifySupervisor("supervisor", `${label} prompt fallback failed: ${err instanceof Error ? err.message : String(err)}`, "error", true, originSessionId);
|
|
24279
|
+
return false;
|
|
24280
|
+
}
|
|
24281
|
+
};
|
|
24241
24282
|
const detectProjectDefaults = (root) => exports_Effect.gen(function* () {
|
|
24242
24283
|
const j = (f) => NodePath.join(root, f);
|
|
24243
24284
|
const hasBunLock = (yield* fileExists(j("bun.lockb"))) || (yield* fileExists(j("bun.lock")));
|
|
@@ -25134,6 +25175,10 @@ No pending questions found.`));
|
|
|
25134
25175
|
supervisor.activeReviewerSessionId = undefined;
|
|
25135
25176
|
supervisor.activeReviewerOutputPath = undefined;
|
|
25136
25177
|
await persistReviewerState();
|
|
25178
|
+
if (supervisor.ralphHandoffTimer) {
|
|
25179
|
+
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25180
|
+
supervisor.ralphHandoffTimer = undefined;
|
|
25181
|
+
}
|
|
25137
25182
|
if (args2.clear_binding === true) {
|
|
25138
25183
|
supervisor.sessionId = undefined;
|
|
25139
25184
|
}
|
|
@@ -25575,7 +25620,8 @@ Set a new goal and run again.
|
|
|
25575
25620
|
const st = sessionMap.get(sessionId);
|
|
25576
25621
|
sessionMap.delete(sessionId);
|
|
25577
25622
|
let didUpdate = false;
|
|
25578
|
-
|
|
25623
|
+
const clearedRalph = supervisor.currentRalphSessionId === sessionId;
|
|
25624
|
+
if (clearedRalph) {
|
|
25579
25625
|
supervisor.currentRalphSessionId = undefined;
|
|
25580
25626
|
didUpdate = true;
|
|
25581
25627
|
}
|
|
@@ -25591,6 +25637,10 @@ Set a new goal and run again.
|
|
|
25591
25637
|
didUpdate = true;
|
|
25592
25638
|
await persistReviewerState();
|
|
25593
25639
|
}
|
|
25640
|
+
if (supervisor.ralphHandoffTimer && clearedRalph) {
|
|
25641
|
+
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25642
|
+
supervisor.ralphHandoffTimer = undefined;
|
|
25643
|
+
}
|
|
25594
25644
|
if (didUpdate && st) {
|
|
25595
25645
|
await notifySupervisor(`${st.role}/attempt-${st.attempt}`, `${st.role} session ended (${reason}).`, "info", true, sessionId);
|
|
25596
25646
|
}
|
|
@@ -25651,10 +25701,18 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25651
25701
|
attempt: String(attempt),
|
|
25652
25702
|
nextAttempt: String(attempt + 1)
|
|
25653
25703
|
});
|
|
25654
|
-
await
|
|
25655
|
-
|
|
25656
|
-
|
|
25657
|
-
|
|
25704
|
+
const promptOk = await sendPromptWithFallback(workerId, promptText, `Worker prompt (attempt ${attempt})`, workerId);
|
|
25705
|
+
if (!promptOk) {
|
|
25706
|
+
mutateSession(workerId, (s) => {
|
|
25707
|
+
s.reportedStatus = "error";
|
|
25708
|
+
s.reportedStatusNote = "Worker prompt failed";
|
|
25709
|
+
});
|
|
25710
|
+
supervisor.currentWorkerSessionId = undefined;
|
|
25711
|
+
await client.session.abort({ path: { id: workerId } }).catch(() => {});
|
|
25712
|
+
await notifySupervisor(`worker/attempt-${attempt}`, "Worker prompt failed; supervision paused. Retry with ralph_create_supervisor_session(restart_if_done=true).", "error", true);
|
|
25713
|
+
supervisor.paused = true;
|
|
25714
|
+
throw new Error("Worker prompt failed");
|
|
25715
|
+
}
|
|
25658
25716
|
await notifySupervisor(`supervisor/attempt-${attempt}`, `Spawned worker session ${workerId}.`, "info", true);
|
|
25659
25717
|
return workerId;
|
|
25660
25718
|
};
|
|
@@ -25667,10 +25725,14 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25667
25725
|
if (st.workerSpawned) {
|
|
25668
25726
|
throw new Error("ralph_spawn_worker() has already been called for this attempt.");
|
|
25669
25727
|
}
|
|
25728
|
+
if (supervisor.ralphHandoffTimer) {
|
|
25729
|
+
clearTimeout(supervisor.ralphHandoffTimer);
|
|
25730
|
+
supervisor.ralphHandoffTimer = undefined;
|
|
25731
|
+
}
|
|
25732
|
+
const workerId = await spawnRlmWorker(st.attempt);
|
|
25670
25733
|
mutateSession(sessionID, (s) => {
|
|
25671
25734
|
s.workerSpawned = true;
|
|
25672
25735
|
});
|
|
25673
|
-
const workerId = await spawnRlmWorker(st.attempt);
|
|
25674
25736
|
await notifySupervisor(`ralph/attempt-${st.attempt}`, `Delegated coding to worker session ${workerId}.`, "info", true, sessionID);
|
|
25675
25737
|
return JSON.stringify({ ok: true, workerSessionId: workerId, attempt: st.attempt }, null, 2);
|
|
25676
25738
|
};
|
|
@@ -25688,10 +25750,29 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25688
25750
|
attempt: String(attempt),
|
|
25689
25751
|
nextAttempt: String(attempt + 1)
|
|
25690
25752
|
});
|
|
25691
|
-
await
|
|
25692
|
-
|
|
25693
|
-
|
|
25694
|
-
|
|
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);
|
|
25695
25776
|
await notifySupervisor(`supervisor/attempt-${attempt}`, `Spawned Ralph strategist session ${ralphId}.`, "info", true);
|
|
25696
25777
|
};
|
|
25697
25778
|
const handleWorkerIdle = async (workerSessionId) => {
|
|
@@ -25702,8 +25783,6 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25702
25783
|
const cfg = await run(getConfig());
|
|
25703
25784
|
if (!cfg.enabled)
|
|
25704
25785
|
return;
|
|
25705
|
-
if (!cfg.autoStartOnMainIdle)
|
|
25706
|
-
return;
|
|
25707
25786
|
const workerState = sessionMap.get(workerSessionId);
|
|
25708
25787
|
supervisor.currentWorkerSessionId = undefined;
|
|
25709
25788
|
if (!workerState?.reportedStatus) {
|
|
@@ -25823,7 +25902,7 @@ ${interpolate(templates.continuePrompt, { attempt: String(attemptN), verdict })}
|
|
|
25823
25902
|
},
|
|
25824
25903
|
"experimental.chat.system.transform": async (input, output) => {
|
|
25825
25904
|
output.system = output.system ?? [];
|
|
25826
|
-
const sessionID = input.sessionID ?? input.session_id;
|
|
25905
|
+
const sessionID = input.sessionID ?? input.session_id ?? input.session?.id;
|
|
25827
25906
|
const role = sessionMap.get(sessionID ?? "")?.role;
|
|
25828
25907
|
const base = role === "worker" || role === "subagent" ? templates.workerSystemPrompt : role === "ralph" ? templates.ralphSessionSystemPrompt : templates.systemPrompt;
|
|
25829
25908
|
const full = templates.systemPromptAppend ? `${base}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-ralph-rlm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
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",
|