fix(bridge): skip OpenClaw runtime-context envelope when picking prompt #3

Merged
hzhang merged 1 commits from fix/skip-runtime-context-message into main 2026-04-29 08:32:53 +00:00

View File

@@ -16,7 +16,22 @@ function stripOpenClawTimestampPrefix(raw: string): string {
}
/**
* Extract the latest user message from the OpenClaw request.
* Marker that prefixes the body of every OpenClaw runtime-context block.
* OpenClaw emits these as a separate `custom_message` in its session log; the
* OpenAI-completions adapter folds them into the request as an extra
* `role=user` message immediately after the actual user input. The bridge must
* skip them when picking the prompt to forward, otherwise Claude sees only the
* metadata envelope and reports the message as "empty".
*/
const RUNTIME_CONTEXT_MARKER =
"OpenClaw runtime context for the immediately preceding user message";
function isRuntimeContextMessage(text: string): boolean {
return text.trimStart().startsWith(RUNTIME_CONTEXT_MARKER);
}
/**
* Extract the latest user-authored message from the OpenClaw request.
*
* OpenClaw accumulates all user messages and sends the full array every turn,
* but assistant messages may be missing if the previous response wasn't streamed
@@ -26,15 +41,22 @@ function stripOpenClawTimestampPrefix(raw: string): string {
* OpenClaw prefixes user messages with a timestamp: "[Day YYYY-MM-DD HH:MM TZ] text"
* We strip the timestamp prefix before forwarding.
*
* Returns "" if no user messages exist or the latest user message is empty
* (e.g. a bare /new turn — see also extractRequestContext.bareSessionReset).
* OpenClaw also emits a runtime-context envelope as an extra `role=user`
* message after each real user message (chat_id, sender, etc.). We skip those
* when scanning for the prompt — see RUNTIME_CONTEXT_MARKER.
*
* Returns "" if no user-authored messages exist (e.g. a bare /new turn — see
* also extractRequestContext.bareSessionReset).
*/
export function extractLatestUserMessage(req: BridgeInboundRequest): string {
const userMessages = req.messages.filter((m) => m.role === "user");
if (userMessages.length === 0) return "";
const raw = messageText(userMessages[userMessages.length - 1]);
return stripOpenClawTimestampPrefix(raw);
for (let i = userMessages.length - 1; i >= 0; i -= 1) {
const raw = messageText(userMessages[i]);
if (!raw) continue;
if (isRuntimeContextMessage(raw)) continue;
return stripOpenClawTimestampPrefix(raw);
}
return "";
}
export type RequestContext = {