Skip to content

Embedded: stop replaying historical session replies#52614

Closed
Openbling wants to merge 1 commit into
openclaw:mainfrom
Openbling-ai:codex/stop-replaying-historical-replies
Closed

Embedded: stop replaying historical session replies#52614
Openbling wants to merge 1 commit into
openclaw:mainfrom
Openbling-ai:codex/stop-replaying-historical-replies

Conversation

@Openbling

Copy link
Copy Markdown
Contributor

Summary

This prevents embedded subscribe from replaying historical assistant replies when attaching to an existing session.

Problem

When subscribeEmbeddedPiSession(...) subscribes to a session that already contains assistant messages, the initial replay can include historical assistant lifecycle events. Those events currently flow through the same outward-facing assistant delivery path as new replies.

That means a subscriber attaching to an existing session can emit an old assistant final again, even though it was already delivered before subscription.

Fix

Track whether embedded subscribe is still processing the initial replay and keep a set of preexisting session messages. While initial replay is in progress, and for messages already present when subscription started, assistant lifecycle handlers now ignore those events instead of treating them like fresh outward replies.

This keeps historical transcript state intact while preventing duplicate outward delivery.

Tests

Add a regression test that subscribes to an existing session containing a historical assistant reply, replays those historical events, then emits a new assistant reply. The subscriber should only emit the new reply.

Validation

  • pnpm test -- src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts -t "does not replay historical assistant finals when subscribing to an existing session"

Note

This PR is intentionally separate from the transcript-only gateway-injected filtering fix. Both touch embedded subscribe delivery, but they address different duplicate-reply failure modes.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Mar 23, 2026
@greptile-apps

greptile-apps Bot commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes duplicate outward delivery of historical assistant replies when subscribeEmbeddedPiSession attaches to a session that already has messages. The fix is clean and well-scoped: a synchronous initialReplayInProgress flag gates all event processing during the subscribe call, and a preexistingMessages set provides a secondary guard for the three message-lifecycle handlers in case a known message object surfaces post-subscribe.

Key observations:

  • The guard in handlers.ts (blocking all event types during replay) is the primary mechanism; the initialReplayInProgress branch inside resolveEligibleAssistantMessage in handlers.messages.ts is now unreachable dead code — only the preexistingMessages.has(message) check in that helper adds unique value.
  • The fix relies on session.subscribe firing all replay events synchronously before returning. The test harness validates this assumption, and the approach matches the described session contract.
  • The regression test is thorough: the toHaveLength(1) assertion on onAgentEvent calls implicitly verifies that historical agent_start/agent_end lifecycle events are also suppressed, not just assistant message finals.

Confidence Score: 5/5

  • Safe to merge — the fix is correctly scoped, the regression test covers the core scenario, and no existing behavior is broken.
  • The implementation is logically sound: a boolean flag and an object-identity Set together prevent historical replay events from triggering duplicate outward delivery. The only finding is a now-redundant initialReplayInProgress branch inside resolveEligibleAssistantMessage, which is harmless dead code. All other logic is unchanged.
  • No files require special attention.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-subscribe.handlers.messages.ts
Line: 48-50

Comment:
**Redundant `initialReplayInProgress` guard**

The `ctx.state.initialReplayInProgress` check here is now unreachable. The outer handler in `pi-embedded-subscribe.handlers.ts` already returns early for all event types when `initialReplayInProgress` is `true`, so `handleMessageStart`, `handleMessageUpdate`, and `handleMessageEnd` are never invoked during replay. Only the `preexistingMessages.has(message)` guard below provides unique value in `resolveEligibleAssistantMessage` — it protects against a preexisting message object appearing in a post-subscribe event.

Consider removing the redundant lines to keep the intent clear, or add a comment explaining the belt-and-suspenders rationale:

```suggestion
  if (ctx.state.preexistingMessages.has(message)) {
    return null;
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Embedded: stop replaying historical sess..." | Re-trigger Greptile

Comment on lines +48 to +50
if (ctx.state.initialReplayInProgress) {
return null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant initialReplayInProgress guard

The ctx.state.initialReplayInProgress check here is now unreachable. The outer handler in pi-embedded-subscribe.handlers.ts already returns early for all event types when initialReplayInProgress is true, so handleMessageStart, handleMessageUpdate, and handleMessageEnd are never invoked during replay. Only the preexistingMessages.has(message) guard below provides unique value in resolveEligibleAssistantMessage — it protects against a preexisting message object appearing in a post-subscribe event.

Consider removing the redundant lines to keep the intent clear, or add a comment explaining the belt-and-suspenders rationale:

Suggested change
if (ctx.state.initialReplayInProgress) {
return null;
}
if (ctx.state.preexistingMessages.has(message)) {
return null;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-subscribe.handlers.messages.ts
Line: 48-50

Comment:
**Redundant `initialReplayInProgress` guard**

The `ctx.state.initialReplayInProgress` check here is now unreachable. The outer handler in `pi-embedded-subscribe.handlers.ts` already returns early for all event types when `initialReplayInProgress` is `true`, so `handleMessageStart`, `handleMessageUpdate`, and `handleMessageEnd` are never invoked during replay. Only the `preexistingMessages.has(message)` guard below provides unique value in `resolveEligibleAssistantMessage` — it protects against a preexisting message object appearing in a post-subscribe event.

Consider removing the redundant lines to keep the intent clear, or add a comment explaining the belt-and-suspenders rationale:

```suggestion
  if (ctx.state.preexistingMessages.has(message)) {
    return null;
  }
```

How can I resolve this? If you propose a fix, please make it concise.

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 29, 2026
@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

The PR remains useful but is not merge-ready: current main still lacks the requested attach-time historical replay guard, while the branch is stale against newer transcript-only assistant suppression and still lacks after-fix real behavior proof for a supported replaying embedded session path.

Canonical path: Close this PR as superseded by #45489.

So I’m closing this here and keeping the remaining discussion on #45489.

Review details

Best possible solution:

Close this PR as superseded by #45489.

Do we have a high-confidence way to reproduce the issue?

No high-confidence real runtime reproduction is established. The synthetic PR harness demonstrates the failure shape for a replaying subscribe implementation, but the discussion still lacks proof of a supported current embedded replay source producing the duplicate outward delivery.

Is this the best way to solve the issue?

Unclear as submitted. The intended boundary is reasonable, but the branch needs rebase work to preserve current transcript-only suppression and maintainer confirmation or real proof for the synchronous replay contract.

Security review:

Security review cleared: The diff only changes embedded subscribe TypeScript logic and colocated tests, with no concrete security or supply-chain concern found.

What I checked:

  • linked superseding PR: fix: prevent delivery-mirror re-delivery and raise Slack chunk limit #45489 (fix: prevent delivery-mirror re-delivery and raise Slack chunk limit) is merged at 2026-03-23T21:11:19Z.
  • cluster evidence: the durable review links that PR in the work cluster or recommended risk path.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • steipete: Prior ClawSweeper context ties this person to recent maintenance of embedded subscribe delivery and replay-state paths. (role: recent area contributor; confidence: high; commits: 5e2f6ce2943e, 5435591f6a1a, 761b71e268a7; files: src/agents/pi-embedded-subscribe.ts, src/agents/pi-embedded-subscribe.handlers.messages.ts, src/agents/pi-embedded-subscribe.handlers.types.ts)
  • Eva: Provided history connects this person to replay-invalid lifecycle and compaction retry work in the same embedded replay/lifecycle area. (role: adjacent replay and lifecycle contributor; confidence: medium; commits: 6b100ca559b1, 7f54cf73e259, f9a5e0a64ff0; files: src/agents/pi-embedded-subscribe.ts, src/agents/pi-embedded-subscribe.handlers.types.ts, src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts)
  • mpz4life: Related merged work updated embedded event dispatch and lifecycle naming, which is the path this PR would gate during initial replay. (role: recent dispatcher/lifecycle contributor; confidence: low; commits: 0a761a9eac2d; files: src/agents/pi-embedded-subscribe.handlers.ts, src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 996d07ee466c.

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes:

The PR snapshots preexisting embedded session messages, suppresses initial replay/preexisting assistant lifecycle delivery, and adds a regression test that only fresh assistant replies emit after subscription.

Maintainer follow-up before merge:

This is an open implementation PR with a narrow plausible fix already attached; the remaining action is maintainer replay-contract review, rebase/conflict resolution, and normal validation rather than a separate automated repair branch.

Review details

Best possible solution:

Land or replace a narrow embedded subscribe fix that snapshots preexisting session messages, suppresses only historical replay/preexisting assistant outward delivery, preserves fresh assistant replies and current transcript-only filters, and keeps focused regression coverage for the attach-to-existing-session case.

Acceptance criteria:

  • pnpm test -- src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts -t "does not replay historical assistant finals when subscribing to an existing session"
  • pnpm check:changed

What I checked:

  • Current main lacks replay state: EmbeddedPiSubscribeState initialization has assistant/output, tool, replay, and liveness fields, but no preexistingMessages set or initialReplayInProgress flag; rg found no current hits for those proposed names or the regression test title. (src/agents/pi-embedded-subscribe.ts:126, e46dccb35374)
  • Current main subscribes directly: subscribeEmbeddedPiSession still calls params.session.subscribe(createEmbeddedPiSessionEventHandler(ctx)) directly, with no snapshot before subscribing and no replay-window close after subscribe returns. (src/agents/pi-embedded-subscribe.ts:933, e46dccb35374)
  • Current dispatcher has no replay short-circuit: createEmbeddedPiSessionEventHandler immediately switches on event type and schedules handlers; there is no top-level guard for synchronous initial replay events. (src/agents/pi-embedded-subscribe.handlers.ts:75, e46dccb35374)
  • Current assistant handlers do not filter preexisting messages: message_start, message_update, and message_end reject non-assistant and transcript-only OpenClaw assistant messages, but they do not check whether the message was already present when subscription began. (src/agents/pi-embedded-subscribe.handlers.messages.ts:384, e46dccb35374)
  • Existing coverage is adjacent only: Current tests cover non-streaming assistant delivery and repeated message_end de-duplication after live subscription, but no current-main regression test covers historical assistant replay during subscription attach. (src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts:645, e46dccb35374)
  • PR diff is narrowly scoped: The PR patch changes only embedded subscribe TypeScript source/types and one colocated regression test; there are no workflow, lockfile, dependency, package-publishing, install/build/release, generated/vendor, or secrets-handling changes in the provided file list. (81db13631bad)

Likely related people:

  • steipete: Prior ClawSweeper context ties steipete to repeated recent maintenance of the embedded subscribe delivery and replay-state path, and local shallow blame attributes the current central files to Peter Steinberger in this checkout. (role: recent maintainer / adjacent owner; confidence: high; commits: 5e2f6ce2943e, 5435591f6a1a, 761b71e268a7; files: src/agents/pi-embedded-subscribe.ts, src/agents/pi-embedded-subscribe.handlers.messages.ts, src/agents/pi-embedded-subscribe.handlers.types.ts)
  • Eva: Provided ClawSweeper history context connects Eva to replay-invalid lifecycle and compaction retry work in the same embedded replay/lifecycle area. (role: adjacent replay/phase maintainer; confidence: medium; commits: 6b100ca559b1, 7f54cf73e259, f9a5e0a64ff0; files: src/agents/pi-embedded-subscribe.ts, src/agents/pi-embedded-subscribe.handlers.types.ts, src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts)
  • mpz4life: Provided ClawSweeper context links mpz4life to recent embedded event dispatcher and lifecycle naming work in the path that this PR would gate during initial replay. (role: recent dispatcher/lifecycle maintainer; confidence: low; commits: 0a761a9eac2d; files: src/agents/pi-embedded-subscribe.handlers.ts, src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts)

Remaining risk / open question:

  • The PR is stale/unmergeable against current main and must preserve the newer transcript-only assistant filtering in src/agents/pi-embedded-subscribe.handlers.messages.ts during rebase.
  • The proposed initialReplayInProgress guard assumes any event delivered synchronously during session.subscribe is historical replay; the current @mariozechner/pi-coding-agent AgentSession.subscribe implementation does not itself replay, so maintainers should confirm the replaying integration contract before merging.

Codex review notes: model gpt-5.5, reasoning high; reviewed against e46dccb35374.

@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Apr 30, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels May 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 19, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant