mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 17:54:47 +00:00
fix(gateway): preserve partial transcript branches
This commit is contained in:
@@ -49,6 +49,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Config persistence: strip malformed pending final-delivery session fields on load so replay/recovery paths skip poisoned reply metadata instead of crashing on raw objects.
|
||||
- Config persistence: strip malformed plugin extension state and promoted session-slot ownership on load so corrupted session rows do not leak poisoned plugin metadata into replay/projection paths.
|
||||
- Gateway/sessions: ignore malformed compaction checkpoint rows during session projection so corrupted stores do not crash session list/describe responses or show bogus checkpoint counts.
|
||||
- Gateway/sessions: keep reachable transcript history when imported tree transcripts reference missing or legacy parent rows, preventing session history reads from going empty after a partial import.
|
||||
- Providers: reject malformed successful Runway, BytePlus, and Ollama embedding responses with provider-owned errors instead of raw parser/type failures, silent bad vectors, or long bogus polling.
|
||||
- Providers/images: reject malformed successful OpenAI-compatible, OpenAI, Google, fal, and OpenRouter image responses with provider-owned errors instead of raw shape failures, silent invalid base64 skips, or empty image results.
|
||||
- Providers/videos: reject malformed successful xAI, OpenRouter, and fal video create, poll, and result responses with provider-owned errors instead of raw parser failures or long bogus polling.
|
||||
|
||||
@@ -146,7 +146,7 @@ function buildActiveTreeEntries(params: {
|
||||
seen.add(currentId);
|
||||
const entry = params.byId.get(currentId);
|
||||
if (!entry) {
|
||||
return [];
|
||||
break;
|
||||
}
|
||||
out.push(entry);
|
||||
currentId = entry.parentId ?? undefined;
|
||||
@@ -206,10 +206,12 @@ async function buildSessionTranscriptIndex(
|
||||
record: parsed,
|
||||
};
|
||||
rawEntries.push(rawEntry);
|
||||
if (isTreeTranscriptRecord(parsed) && id) {
|
||||
hasTreeEntries = true;
|
||||
leafId = id;
|
||||
if (id) {
|
||||
byId.set(id, rawEntry);
|
||||
if (isTreeTranscriptRecord(parsed)) {
|
||||
hasTreeEntries = true;
|
||||
leafId = id;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -886,6 +886,72 @@ describe("readSessionMessages", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("keeps async active branch rows when imported parent links are incomplete", async () => {
|
||||
const sessionId = "test-session-tree-async-incomplete-parent";
|
||||
writeTranscript(tmpDir, sessionId, [
|
||||
{ type: "session", version: 3, id: sessionId },
|
||||
{
|
||||
type: "message",
|
||||
id: "legacy-user",
|
||||
message: { role: "user", content: "legacy prompt" },
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "tree-assistant",
|
||||
parentId: "legacy-user",
|
||||
message: { role: "assistant", content: "tree reply" },
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "orphan-tail",
|
||||
parentId: "missing-imported-parent",
|
||||
message: { role: "assistant", content: "reachable orphan tail" },
|
||||
},
|
||||
]);
|
||||
clearSessionTranscriptIndexCache();
|
||||
|
||||
const messages = await readSessionMessagesAsync(sessionId, storePath, undefined, {
|
||||
mode: "full",
|
||||
reason: "test imported partial tree selection",
|
||||
});
|
||||
|
||||
expect(messages.map((message) => (message as { content?: unknown }).content)).toEqual([
|
||||
"reachable orphan tail",
|
||||
]);
|
||||
expectMessageFields(messages[0], { openclaw: { id: "orphan-tail", seq: 1 } });
|
||||
});
|
||||
|
||||
test("keeps legacy async parents when tree transcripts reference pre-v3 rows", async () => {
|
||||
const sessionId = "test-session-tree-async-legacy-parent";
|
||||
writeTranscript(tmpDir, sessionId, [
|
||||
{ type: "session", version: 1, id: sessionId },
|
||||
{
|
||||
type: "message",
|
||||
id: "legacy-user",
|
||||
message: { role: "user", content: "legacy hello" },
|
||||
},
|
||||
{
|
||||
type: "message",
|
||||
id: "tree-assistant",
|
||||
parentId: "legacy-user",
|
||||
message: { role: "assistant", content: "tree hello" },
|
||||
},
|
||||
]);
|
||||
clearSessionTranscriptIndexCache();
|
||||
|
||||
const messages = await readSessionMessagesAsync(sessionId, storePath, undefined, {
|
||||
mode: "full",
|
||||
reason: "test legacy parent active tree selection",
|
||||
});
|
||||
|
||||
expect(messages.map((message) => (message as { content?: unknown }).content)).toEqual([
|
||||
"legacy hello",
|
||||
"tree hello",
|
||||
]);
|
||||
expectMessageFields(messages[0], { openclaw: { id: "legacy-user", seq: 1 } });
|
||||
expectMessageFields(messages[1], { openclaw: { id: "tree-assistant", seq: 2 } });
|
||||
});
|
||||
|
||||
test("caches async transcript indexes by file stats", async () => {
|
||||
const sessionId = "test-session-index-cache";
|
||||
writeTranscript(tmpDir, sessionId, [
|
||||
|
||||
Reference in New Issue
Block a user