Skip to content

Commit e445ec7

Browse files
committed
fix: update root assets run.mjs with history/session fixes
1 parent d427dee commit e445ec7

1 file changed

Lines changed: 59 additions & 17 deletions

File tree

assets/openclaw-agent-local/run.mjs

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import { createInterface } from "node:readline";
1717
import { randomUUID, randomBytes } from "node:crypto";
18-
import { createReadStream } from "node:fs";
18+
import { createReadStream, readFileSync, writeFileSync } from "node:fs";
1919
import fs from "node:fs/promises";
2020
import path from "node:path";
2121

@@ -132,21 +132,18 @@ async function ensureSessionDir() {
132132
}
133133

134134
/** Read only the first line of a file without loading the entire content. */
135-
async function readFirstLine(filePath) {
136-
return new Promise((resolve, reject) => {
137-
const stream = createReadStream(filePath, { encoding: "utf-8" });
138-
const rl = createInterface({ input: stream, crlfDelay: Infinity });
139-
rl.once("line", (line) => {
140-
rl.close();
141-
stream.destroy();
142-
resolve(line);
143-
});
144-
rl.once("close", () => resolve(null));
145-
stream.once("error", reject);
146-
});
135+
function readFirstLine(filePath) {
136+
try {
137+
const buf = readFileSync(filePath, { encoding: "utf-8" });
138+
const nl = buf.indexOf("\n");
139+
const cr = buf.indexOf("\r");
140+
const end2 = cr >= 0 && (nl < 0 || cr < nl) ? cr : nl >= 0 ? nl : buf.length;
141+
return buf.slice(0, end2) || null;
142+
} catch { return null; }
147143
}
148144

149-
async function getOrCreateSession() {
145+
146+
async function getOrCreateSession(requestedSessionId) {
150147
await ensureSessionDir();
151148

152149
// Find session files and sort by mtime (most recent first)
@@ -169,6 +166,7 @@ async function getOrCreateSession() {
169166
if (!firstLine) continue;
170167
const header = JSON.parse(firstLine);
171168
if (header.type === "session" && header.id) {
169+
if (requestedSessionId && header.id !== requestedSessionId) continue;
172170
return { sessionId: header.id, sessionFile };
173171
}
174172
} catch {
@@ -180,7 +178,7 @@ async function getOrCreateSession() {
180178
}
181179

182180
// Create a new session
183-
const sessionId = randomUUID();
181+
const sessionId = requestedSessionId || randomUUID();
184182
const sessionFile = path.join(SESSION_DIR, `session-${sessionId}.jsonl`);
185183
const header = {
186184
type: "session",
@@ -352,9 +350,39 @@ function abortActiveRun() {
352350
// Agent execution
353351
// ---------------------------------------------------------------------------
354352

353+
354+
// ── Conversation history (independent of SessionManager) ──────────
355+
function getHistoryPath(sessionFile) {
356+
return sessionFile.replace(/\.jsonl$/, '.history.json');
357+
}
358+
function loadHistory(sessionFile, maxTurns) {
359+
maxTurns = maxTurns || 20;
360+
try {
361+
const raw = readFileSync(getHistoryPath(sessionFile), "utf-8");
362+
const arr = JSON.parse(raw);
363+
return Array.isArray(arr) ? arr.slice(-maxTurns) : [];
364+
} catch { return []; }
365+
}
366+
function saveHistory(sessionFile, turns) {
367+
try {
368+
writeFileSync(getHistoryPath(sessionFile), JSON.stringify(turns), "utf-8");
369+
process.stderr.write("[openclaw-agent] history saved: " + turns.length + " turns\n");
370+
} catch (e) {
371+
process.stderr.write("[openclaw-agent] saveHistory failed: " + e.message + "\n");
372+
}
373+
}
374+
function buildHistoryPrompt(turns) {
375+
if (!turns || turns.length === 0) return "";
376+
return "<conversation_history>\n" +
377+
turns.map(t => "[" + t.role + "]: " + t.text).join("\n") +
378+
"\n</conversation_history>\n\nContinuing the above conversation. User says:\n";
379+
}
380+
381+
let requestedSessionId = null;
382+
355383
async function runAgent(prompt, images) {
356384
if (!currentSessionId) {
357-
const session = await getOrCreateSession();
385+
const session = await getOrCreateSession(requestedSessionId);
358386
currentSessionId = session.sessionId;
359387
currentSessionFile = session.sessionFile;
360388
}
@@ -419,11 +447,16 @@ async function runAgent(prompt, images) {
419447
};
420448
}
421449

450+
// Inject conversation history into prompt
451+
const prevTurns = loadHistory(currentSessionFile, 20);
452+
const historyPrefix = buildHistoryPrompt(prevTurns);
453+
const effectivePrompt = historyPrefix ? historyPrefix + prompt : prompt;
454+
422455
const result = await runEmbeddedPiAgent({
423456
sessionId: currentSessionId,
424457
sessionFile: currentSessionFile,
425458
workspaceDir: WORKSPACE_DIR,
426-
prompt,
459+
prompt: effectivePrompt,
427460
images: images || undefined,
428461
provider: PROVIDER,
429462
model: MODEL,
@@ -527,6 +560,14 @@ async function runAgent(prompt, images) {
527560
emitTextDelta(metaText);
528561
}
529562

563+
// Save conversation turn to separate history file
564+
{
565+
const hist = loadHistory(currentSessionFile, 50);
566+
hist.push({ role: "user", text: prompt });
567+
if (lastPartialText) hist.push({ role: "assistant", text: lastPartialText.trim() });
568+
saveHistory(currentSessionFile, hist);
569+
}
570+
530571
emitMessageEnd();
531572

532573
// Check for errors
@@ -587,6 +628,7 @@ async function main() {
587628
MODEL = msg.model;
588629
}
589630
if (msg.provider) PROVIDER = msg.provider;
631+
if (msg.sessionId) requestedSessionId = msg.sessionId;
590632
process.stderr.write("[openclaw-agent] config received via stdin\n");
591633
continue;
592634
}

0 commit comments

Comments
 (0)