openclaw - 💡(How to fix) Fix Bug: before_prompt_build hook's prependContext/appendContext stripped into system prompt [2 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

The prependContext and appendContext returned by before_prompt_build hooks are not kept alongside the user message. Instead, resolveRuntimeContextPromptParts strips them out and places them into the system prompt area.

Root Cause

The resolveRuntimeContextPromptParts function in runtime-context-prompt.ts:

function resolveRuntimeContextPromptParts(params) {
  const transcriptPrompt = params.transcriptPrompt;
  if (transcriptPrompt === void 0 || transcriptPrompt === params.effectivePrompt)
    return { prompt: params.effectivePrompt };

  // transcriptPrompt ≠ effectivePrompt → stripping is triggered
  const runtimeContext = removeLastPromptOccurrence(
    params.effectivePrompt, transcriptPrompt
  )?.trim() || params.effectivePrompt.trim();

  return {
    prompt: transcriptPrompt,          // only raw user message remains
    runtimeContext,                     // hook-injected context was stripped
    // runtimeContext ends up in runtimeSystemContext → system prompt area
  };
}

The core issue:

  1. All channels (Feishu, Telegram, Signal, CLI, etc.) set transcriptPrompt = raw user message during agent run setup (source: transcriptCommandBody in get-reply-*.js, queued.transcriptPrompt in agent-runner.runtime-*.js).
  2. After before_prompt_build hooks execute, effectivePrompt = transcriptPrompt + prependContext + appendContext, which is never equal to transcriptPrompt.
  3. Therefore stripping is triggered 100% of the time, and all hook-injected context is moved into the system prompt area.

Fix Action

Fixed

Code Example

SYSTEM PROMPT: ... (original system prompt) + prependContext + appendContext  ← stripped here
PROMPT (to model): raw user message only  ← hook-injected context is GONE

---

SYSTEM PROMPT: ... (original system prompt only, no hook context)
PROMPT (to model): prependContext + "\n\n" + user message + "\n\n" + appendContext

---

function resolveRuntimeContextPromptParts(params) {
  const transcriptPrompt = params.transcriptPrompt;
  if (transcriptPrompt === void 0 || transcriptPrompt === params.effectivePrompt)
    return { prompt: params.effectivePrompt };

  // transcriptPrompt ≠ effectivePrompt → stripping is triggered
  const runtimeContext = removeLastPromptOccurrence(
    params.effectivePrompt, transcriptPrompt
  )?.trim() || params.effectivePrompt.trim();

  return {
    prompt: transcriptPrompt,          // only raw user message remains
    runtimeContext,                     // hook-injected context was stripped
    // runtimeContext ends up in runtimeSystemContext → system prompt area
  };
}
RAW_BUFFERClick to expand / collapse

Bug: before_prompt_build hook's prependContext / appendContext stripped into system prompt

Summary

The prependContext and appendContext returned by before_prompt_build hooks are not kept alongside the user message. Instead, resolveRuntimeContextPromptParts strips them out and places them into the system prompt area.

Actual behavior

SYSTEM PROMPT: ... (original system prompt) + prependContext + appendContext  ← stripped here
PROMPT (to model): raw user message only  ← hook-injected context is GONE

Expected behavior

SYSTEM PROMPT: ... (original system prompt only, no hook context)
PROMPT (to model): prependContext + "\n\n" + user message + "\n\n" + appendContext

Hook-injected context should be adjacent to the user message when sent to the model, so the model can associate the context with the user's question.

Requirements

The ideal behavior requires a three-layer separation:

  1. Hook context stays in the prompt areaprependContext / appendContext should be sent to the model alongside the user message in the same prompt area, not stripped into the system prompt.

  2. Not persisted in conversation history — Hook-injected content should NOT be written into the transcript / history. Only the user's raw message should be saved in history. Otherwise the history becomes bloated — injecting hundreds of lines (e.g. AGENTS.md) on every single turn will quickly overflow the context window.

  3. Not echoed to the user-facing UI — The user should only see their own raw message in webchat, Feishu, and other clients. Hook-injected prompts should only be injected during server-side prompt construction and only be visible to the model.

In summary:

LayerContent
Transcript / UI displayRaw user message only
Model promptUser message + hook-injected context
System promptSystem instructions (no hook prependContext / appendContext)

Root cause

The resolveRuntimeContextPromptParts function in runtime-context-prompt.ts:

function resolveRuntimeContextPromptParts(params) {
  const transcriptPrompt = params.transcriptPrompt;
  if (transcriptPrompt === void 0 || transcriptPrompt === params.effectivePrompt)
    return { prompt: params.effectivePrompt };

  // transcriptPrompt ≠ effectivePrompt → stripping is triggered
  const runtimeContext = removeLastPromptOccurrence(
    params.effectivePrompt, transcriptPrompt
  )?.trim() || params.effectivePrompt.trim();

  return {
    prompt: transcriptPrompt,          // only raw user message remains
    runtimeContext,                     // hook-injected context was stripped
    // runtimeContext ends up in runtimeSystemContext → system prompt area
  };
}

The core issue:

  1. All channels (Feishu, Telegram, Signal, CLI, etc.) set transcriptPrompt = raw user message during agent run setup (source: transcriptCommandBody in get-reply-*.js, queued.transcriptPrompt in agent-runner.runtime-*.js).
  2. After before_prompt_build hooks execute, effectivePrompt = transcriptPrompt + prependContext + appendContext, which is never equal to transcriptPrompt.
  3. Therefore stripping is triggered 100% of the time, and all hook-injected context is moved into the system prompt area.

Scope of impact

In selection-BmjEdnnA.js (lines ~14326-14450), the hook assembles effectivePrompt then calls resolveRuntimeContextPromptParts to split it.

This splitting logic affects all plugins that use before_prompt_build to return prependContext / appendContext. The hook-injected context is completely separated from the user message when sent to the model.

Relevant files

FileFunction / LinesRole
dist/runtime-context-prompt-*.js:18-34resolveRuntimeContextPromptPartsSplits effectivePrompt
dist/selection-*.js:14349-14355Hook assemblyprependContext/appendContexteffectivePrompt
dist/selection-*.js:14438-14447Calls resolveRuntimeContextPromptPartseffectivePrompt is split
dist/get-reply-*.jstranscriptPrompt: transcriptCommandBodyChannel-level transcriptPrompt source
dist/agent-runner-execution-*.jstranscriptPrompt: params.transcriptCommandBodyQueue execution transcriptPrompt source
dist/agent-runner.runtime-*.jstranscriptPrompt: queued.transcriptPromptRuntime transcriptPrompt passthrough

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

SYSTEM PROMPT: ... (original system prompt only, no hook context)
PROMPT (to model): prependContext + "\n\n" + user message + "\n\n" + appendContext

Hook-injected context should be adjacent to the user message when sent to the model, so the model can associate the context with the user's question.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Bug: before_prompt_build hook's prependContext/appendContext stripped into system prompt [2 pull requests]