Merge pull request 'fix(bridge): skip OpenClaw runtime-context envelope when picking prompt' (#3) from fix/skip-runtime-context-message into main

Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
h z
2026-04-29 08:32:52 +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, * 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 * but assistant messages may be missing if the previous response wasn't streamed
@@ -26,16 +41,23 @@ function stripOpenClawTimestampPrefix(raw: string): string {
* OpenClaw prefixes user messages with a timestamp: "[Day YYYY-MM-DD HH:MM TZ] text" * OpenClaw prefixes user messages with a timestamp: "[Day YYYY-MM-DD HH:MM TZ] text"
* We strip the timestamp prefix before forwarding. * We strip the timestamp prefix before forwarding.
* *
* Returns "" if no user messages exist or the latest user message is empty * OpenClaw also emits a runtime-context envelope as an extra `role=user`
* (e.g. a bare /new turn — see also extractRequestContext.bareSessionReset). * 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 { export function extractLatestUserMessage(req: BridgeInboundRequest): string {
const userMessages = req.messages.filter((m) => m.role === "user"); const userMessages = req.messages.filter((m) => m.role === "user");
if (userMessages.length === 0) return ""; for (let i = userMessages.length - 1; i >= 0; i -= 1) {
const raw = messageText(userMessages[i]);
const raw = messageText(userMessages[userMessages.length - 1]); if (!raw) continue;
if (isRuntimeContextMessage(raw)) continue;
return stripOpenClawTimestampPrefix(raw); return stripOpenClawTimestampPrefix(raw);
} }
return "";
}
export type RequestContext = { export type RequestContext = {
agentId: string; agentId: string;