fix(agents): restore runtime context fallback for heartbeat prompts#91817
fix(agents): restore runtime context fallback for heartbeat prompts#91817solodmd wants to merge 4 commits into
Conversation
resolveRuntimeContextPromptParts lost the fallback during the pi-embedded-runner to embedded-agent-runner refactor. When transcriptPrompt is not found in effectivePrompt (e.g. heartbeat placeholder vs exec event), use extracted text as implicit runtimeContext. Verification: pnpm test runtime-context-prompt.test.ts (22/22 pass)
|
Codex review: needs maintainer review before merge. Reviewed June 10, 2026, 3:49 AM ET / 07:49 UTC. Summary PR surface: Source +27, Tests +75. Total +102 across 3 files. Reproducibility: yes. at source level: current main returns the heartbeat transcript placeholder without Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Land the focused heartbeat-only fallback after normal maintainer review and CI, keeping the runtime context ephemeral and avoiding new config or persistence surfaces. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main returns the heartbeat transcript placeholder without Is this the best way to solve the issue? Yes; this is the best observed fix shape because the fallback is restored in the prompt-splitting helper but is enabled only by the heartbeat call site. Non-heartbeat mismatches remain unchanged, and hook-expanded model prompts are handled without duplicating the event text. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against db5b883a9cb7. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +27, Tests +75. Total +102 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Verification evidence1. Temporary logging code added to attempt.tsconst runtimeContextForHook = promptSubmission.runtimeOnly
? undefined
: promptSubmission.runtimeContext?.trim();
// TEMP: verify exec-event runtime context fix
if (params.trigger === "heartbeat" && runtimeContextForHook) {
console.log("[runtime-context-v] hook present, chars:", runtimeContextForHook.length);
}
const runtimeContextMessageForCurrentTurn =
buildRuntimeContextCustomMessage(runtimeContextForHook);
// TEMP: verify custom message was created
if (params.trigger === "heartbeat") {
console.log("[runtime-context-v] custom msg created:", !!runtimeContextMessageForCurrentTurn);
}2. Gateway log output (after fix)3. How to reproduce
The 303 characters captured contain the structured exec completion event (id, exit code, stdout/stderr output). |
|
Closing after maintainer autoreview because the proposed fallback is not safe at this boundary. The patch makes the model consume the unmatched heartbeat/exec-event payload through a hidden runtime-context message while the current During review we also found and fixed narrower issues around media prompt trust, explicit runtime-context composition, and hook-expanded prompt duplication, but the remaining problem requires an explicit contract for exposing all current-turn model-visible context to pre-model gates. That is broader than this PR and should be designed at the hook/input boundary rather than restored as a generic prompt fallback. |
Summary
pi-embedded-runner→embedded-agent-runnerrefactor,resolveRuntimeContextPromptPartslost a legacy fallback. WhentranscriptPromptis defined but not a substring ofeffectivePrompt(e.g. heartbeat placeholder[OpenClaw heartbeat poll]vs exec event output), the effective text should be used as implicitruntimeContext. Without it,buildRuntimeContextCustomMessageskips creating thecustom_message, and the model doesn't receive the runtime context on heartbeat turns.runtimeContext→buildRuntimeContextCustomMessage→custom_message.attempt.tsconfirmedruntimeContextForHookis populated (303 chars) andcustom_messageis created.transcriptPrompt !== undefined && hiddenRuntimeContext === undefined && extracted.runtimeContext === undefined— verify it correctly distinguishes "not found in text" from "found but no surrounding context."Linked context
No related issue directly — discovered while investigating exec background completion notification behavior.
This is a refactoring omission: the fallback was present in the old
pi-embedded-runnerand lost during the rename/restructure toembedded-agent-runner.pi-embedded-runnertoembedded-agent-runner, where the fallback was dropped.resolveRuntimeContextPromptPartsfor hook context handling.Real behavior proof
custom_messageinto model messages.agent model: zte/Qwen3-235B-A22B, ran exec command, triggered heartbeat.console.logat thebuildRuntimeContextCustomMessagecall site, rebuilt, ran gateway, executedexec sleep 5 background=true, waited for heartbeat wake.runtimeContextForHookcontains the exec event text (303 chars),buildRuntimeContextCustomMessagesuccessfully creates thecustom_message.messagesForCurrentPrompt.runtime-context-prompt.test.tsprovides structural coverage of the extraction logic.runtimeContextForHookisundefined→buildRuntimeContextCustomMessage(undefined)returnsundefined→ no custom_message created. This is the current behavior onmain.Tests and validation
pnpm test src/agents/embedded-agent-runner/run/runtime-context-prompt.test.ts: 22/22 pass."uses effective text as implicit runtime context when transcriptPrompt is not found"— covers the exact heartbeat/exec-event fallback scenario.Risk checklist
No— internal runtime context; only affects model-visible messages on exec-event heartbeat turns (restoring behavior that existed in pi-embedded-runner).NoNotranscriptPrompt !== undefined && hiddenRuntimeContext === undefined && extracted.runtimeContext === undefined— if a future caller passes atranscriptPromptthat unintentionally doesn't matcheffectivePrompt, the entire effective text becomesruntimeContext. This is narrowly scoped and was the previous behavior.Current review state