


























@@ -172,16 +172,37 @@ function formatTextPreview(texts: string[], maxChars = 800): string {
172172return combined.length > maxChars ? `${combined.slice(0, maxChars)}...` : combined;
173173}
174174175+function extractAssistantTexts(messages: unknown[]): string[] {
176+const texts: string[] = [];
177+for (const entry of messages) {
178+if (!entry || typeof entry !== "object") {
179+continue;
180+}
181+if ((entry as { role?: unknown }).role !== "assistant") {
182+continue;
183+}
184+const text = extractVisibleMessageText(entry);
185+if (typeof text === "string" && text.trim().length > 0) {
186+texts.push(text);
187+}
188+}
189+return texts;
190+}
191+175192async function waitForTrajectoryExportInstructionText(params: {
193+client: GatewayClient;
176194events: EventFrame[];
195+eventStartIndex: number;
177196expectedText: string;
178197runId: string;
198+sessionKey: string;
179199timeoutMs: number;
180200}): Promise<string> {
181201const deadline = Date.now() + params.timeoutMs;
182202let finalTexts: string[] = [];
183203while (Date.now() < deadline) {
184-finalTexts = params.events
204+const newEvents = params.events.slice(params.eventStartIndex);
205+finalTexts = newEvents
185206.map((event) => extractChatFinalText(event, params.runId))
186207.filter((text): text is string => typeof text === "string" && text.trim().length > 0);
187208const matchedText = finalTexts.find((text) => text.includes(params.expectedText));
@@ -192,9 +213,23 @@ async function waitForTrajectoryExportInstructionText(params: {
192213setTimeout(resolve, 500);
193214});
194215}
216+let assistantTexts: string[];
217+try {
218+const history = (await params.client.request(
219+"chat.history",
220+{
221+sessionKey: params.sessionKey,
222+limit: 24,
223+},
224+{ timeoutMs: 10_000 },
225+)) as { messages?: unknown[] };
226+assistantTexts = extractAssistantTexts(history.messages ?? []);
227+} catch {
228+assistantTexts = [];
229+}
195230throw new Error(
196231`timed out waiting for trajectory export instruction text for ${params.runId}; ` +
197-`events=${params.events.length}; finalTexts=${formatTextPreview(finalTexts)}`,
232+`events=${params.events.length}; finalTexts=${formatTextPreview(finalTexts)}; assistantTexts=${formatTextPreview(assistantTexts)}`,
198233);
199234}
200235@@ -210,6 +245,10 @@ function extractChatFinalText(event: EventFrame, runId: string): string | undefi
210245if (record.runId !== runId || record.state !== "final") {
211246return undefined;
212247}
248+return extractChatFinalRecordText(record);
249+}
250+251+function extractChatFinalRecordText(record: Record<string, unknown>): string | undefined {
213252const message = record.message;
214253if (!message || typeof message !== "object") {
215254return undefined;
@@ -395,6 +434,7 @@ describeLive("gateway live trajectory export", () => {
395434const bundleDir = path.join(workspaceDir, ".openclaw", "trajectory-exports", "bundle");
396435const beforeExport = new Set(await listDirectoryNames(tempDir));
397436const exportRunId = `chat-export-${randomUUID()}`;
437+const exportEventStartIndex = gatewayEvents.length;
398438logLiveStep("export:start", { bundleDir, exportRunId });
399439const exportResponse = (await client.request(
400440"chat.send",
@@ -415,9 +455,12 @@ describeLive("gateway live trajectory export", () => {
415455typeof exportResponse?.message === "object"
416456 ? extractVisibleMessageText(exportResponse.message)
417457 : await waitForTrajectoryExportInstructionText({
458+ client,
418459events: gatewayEvents,
460+eventStartIndex: exportEventStartIndex,
419461expectedText: "Trajectory exports can include",
420462runId: exportRunId,
463+ sessionKey,
421464timeoutMs: 60_000,
422465});
423466expect(finalText).toContain("Trajectory exports can include");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。