mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-24 16:32:29 +00:00
fix(acp): preserve hidden thought replay on session load
This commit is contained in:
@@ -242,7 +242,7 @@ describe("acp session UX bridge behavior", () => {
|
||||
sessionStore.clearAllSessionsForTest();
|
||||
});
|
||||
|
||||
it("replays user and assistant text history on loadSession and returns initial controls", async () => {
|
||||
it("replays user text, assistant text, and hidden assistant thinking on loadSession", async () => {
|
||||
const sessionStore = createInMemorySessionStore();
|
||||
const connection = createAcpConnection();
|
||||
const sessionUpdate = connection.__sessionUpdateMock;
|
||||
@@ -283,7 +283,13 @@ describe("acp session UX bridge behavior", () => {
|
||||
return {
|
||||
messages: [
|
||||
{ role: "user", content: [{ type: "text", text: "Question" }] },
|
||||
{ role: "assistant", content: [{ type: "text", text: "Answer" }] },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "thinking", thinking: "Internal loop about NO_REPLY" },
|
||||
{ type: "text", text: "Answer" },
|
||||
],
|
||||
},
|
||||
{ role: "system", content: [{ type: "text", text: "ignore me" }] },
|
||||
{ role: "assistant", content: [{ type: "image", image: "skip" }] },
|
||||
],
|
||||
@@ -332,6 +338,13 @@ describe("acp session UX bridge behavior", () => {
|
||||
content: { type: "text", text: "Question" },
|
||||
},
|
||||
});
|
||||
expect(sessionUpdate).toHaveBeenCalledWith({
|
||||
sessionId: "agent:main:work",
|
||||
update: {
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
content: { type: "text", text: "Internal loop about NO_REPLY" },
|
||||
},
|
||||
});
|
||||
expect(sessionUpdate).toHaveBeenCalledWith({
|
||||
sessionId: "agent:main:work",
|
||||
update: {
|
||||
|
||||
@@ -134,6 +134,11 @@ type GatewayChatContentBlock = {
|
||||
thinking?: string;
|
||||
};
|
||||
|
||||
type ReplayChunk = {
|
||||
sessionUpdate: "user_message_chunk" | "agent_message_chunk" | "agent_thought_chunk";
|
||||
text: string;
|
||||
};
|
||||
|
||||
const SESSION_CREATE_RATE_LIMIT_DEFAULT_MAX_REQUESTS = 120;
|
||||
const SESSION_CREATE_RATE_LIMIT_DEFAULT_WINDOW_MS = 10_000;
|
||||
|
||||
@@ -261,25 +266,51 @@ function buildSessionPresentation(params: {
|
||||
return { configOptions, modes };
|
||||
}
|
||||
|
||||
function extractReplayText(content: unknown): string | undefined {
|
||||
if (typeof content === "string") {
|
||||
return content.length > 0 ? content : undefined;
|
||||
function extractReplayChunks(message: GatewayTranscriptMessage): ReplayChunk[] {
|
||||
const role = typeof message.role === "string" ? message.role : "";
|
||||
if (role !== "user" && role !== "assistant") {
|
||||
return [];
|
||||
}
|
||||
if (!Array.isArray(content)) {
|
||||
return undefined;
|
||||
if (typeof message.content === "string") {
|
||||
return message.content.length > 0
|
||||
? [
|
||||
{
|
||||
sessionUpdate: role === "user" ? "user_message_chunk" : "agent_message_chunk",
|
||||
text: message.content,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}
|
||||
const text = content
|
||||
.map((block) => {
|
||||
if (!block || typeof block !== "object" || Array.isArray(block)) {
|
||||
return "";
|
||||
}
|
||||
const typedBlock = block as { type?: unknown; text?: unknown };
|
||||
return typedBlock.type === "text" && typeof typedBlock.text === "string"
|
||||
? typedBlock.text
|
||||
: "";
|
||||
})
|
||||
.join("");
|
||||
return text.length > 0 ? text : undefined;
|
||||
if (!Array.isArray(message.content)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const replayChunks: ReplayChunk[] = [];
|
||||
for (const block of message.content) {
|
||||
if (!block || typeof block !== "object" || Array.isArray(block)) {
|
||||
continue;
|
||||
}
|
||||
const typedBlock = block as GatewayChatContentBlock;
|
||||
if (typedBlock.type === "text" && typeof typedBlock.text === "string" && typedBlock.text) {
|
||||
replayChunks.push({
|
||||
sessionUpdate: role === "user" ? "user_message_chunk" : "agent_message_chunk",
|
||||
text: typedBlock.text,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
role === "assistant" &&
|
||||
typedBlock.type === "thinking" &&
|
||||
typeof typedBlock.thinking === "string" &&
|
||||
typedBlock.thinking
|
||||
) {
|
||||
replayChunks.push({
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
text: typedBlock.thinking,
|
||||
});
|
||||
}
|
||||
}
|
||||
return replayChunks;
|
||||
}
|
||||
|
||||
function buildSessionMetadata(params: {
|
||||
@@ -1045,21 +1076,16 @@ export class AcpGatewayAgent implements Agent {
|
||||
transcript: ReadonlyArray<GatewayTranscriptMessage>,
|
||||
): Promise<void> {
|
||||
for (const message of transcript) {
|
||||
const role = typeof message.role === "string" ? message.role : "";
|
||||
if (role !== "user" && role !== "assistant") {
|
||||
continue;
|
||||
const replayChunks = extractReplayChunks(message);
|
||||
for (const chunk of replayChunks) {
|
||||
await this.connection.sessionUpdate({
|
||||
sessionId,
|
||||
update: {
|
||||
sessionUpdate: chunk.sessionUpdate,
|
||||
content: { type: "text", text: chunk.text },
|
||||
},
|
||||
});
|
||||
}
|
||||
const text = extractReplayText(message.content);
|
||||
if (!text) {
|
||||
continue;
|
||||
}
|
||||
await this.connection.sessionUpdate({
|
||||
sessionId,
|
||||
update: {
|
||||
sessionUpdate: role === "user" ? "user_message_chunk" : "agent_message_chunk",
|
||||
content: { type: "text", text },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user