diff --git a/packages/adapters/openclaw/src/server/execute.ts b/packages/adapters/openclaw/src/server/execute.ts index 95949964..f3f2ee44 100644 --- a/packages/adapters/openclaw/src/server/execute.ts +++ b/packages/adapters/openclaw/src/server/execute.ts @@ -110,6 +110,60 @@ function buildWakeText(payload: WakePayload, paperclipEnv: Record 0 ? `${trimmedBase}\n\n${wakeText}` : wakeText; +} + +function buildOpenResponsesWakeInputMessage(wakeText: string): Record { + return { + type: "message", + role: "user", + content: [ + { + type: "input_text", + text: wakeText, + }, + ], + }; +} + +function appendWakeTextToOpenResponsesInput(input: unknown, wakeText: string): unknown { + if (typeof input === "string") { + return appendWakeText(input, wakeText); + } + + if (Array.isArray(input)) { + return [...input, buildOpenResponsesWakeInputMessage(wakeText)]; + } + + if (typeof input === "object" && input !== null) { + const parsed = parseObject(input); + const content = parsed.content; + if (typeof content === "string") { + return { + ...parsed, + content: appendWakeText(content, wakeText), + }; + } + if (Array.isArray(content)) { + return { + ...parsed, + content: [ + ...content, + { + type: "input_text", + text: wakeText, + }, + ], + }; + } + return [parsed, buildOpenResponsesWakeInputMessage(wakeText)]; + } + + return wakeText; +} + function isTextRequiredResponse(responseText: string): boolean { const parsed = parseOpenClawResponse(responseText); const parsedError = parsed && typeof parsed.error === "string" ? parsed.error : null; @@ -478,6 +532,9 @@ export async function execute(ctx: AdapterExecutionContext): Promise = isOpenResponses ? { @@ -487,9 +544,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise { expect(headers["x-openclaw-session-key"]).toBe("paperclip"); }); + it("appends wake text when OpenResponses input is provided as a message object", async () => { + const fetchMock = vi.fn().mockResolvedValue( + sseResponse([ + "event: response.completed\n", + 'data: {"type":"response.completed","status":"completed"}\n\n', + ]), + ); + vi.stubGlobal("fetch", fetchMock); + + const result = await execute( + buildContext({ + url: "https://agent.example/v1/responses", + method: "POST", + payloadTemplate: { + model: "openclaw", + input: { + type: "message", + role: "user", + content: [ + { + type: "input_text", + text: "start with this context", + }, + ], + }, + }, + }), + ); + + expect(result.exitCode).toBe(0); + const body = JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body ?? "{}")) as Record; + const input = body.input as Record; + expect(input.type).toBe("message"); + expect(input.role).toBe("user"); + expect(Array.isArray(input.content)).toBe(true); + + const content = input.content as Record[]; + expect(content).toHaveLength(2); + expect(content[0]).toEqual({ + type: "input_text", + text: "start with this context", + }); + expect(content[1]).toEqual( + expect.objectContaining({ + type: "input_text", + }), + ); + expect(String(content[1]?.text ?? "")).toContain("PAPERCLIP_RUN_ID=run-123"); + }); + it("fails when SSE endpoint does not return text/event-stream", async () => { const fetchMock = vi.fn().mockResolvedValue( new Response(JSON.stringify({ ok: false, error: "unexpected payload" }), {