Skip to content

fix(outbound): thread sessionKey into message_sending hook context#19

Closed
zeroaltitude wants to merge 1 commit intomainfrom
fix/message-sending-session-key
Closed

fix(outbound): thread sessionKey into message_sending hook context#19
zeroaltitude wants to merge 1 commit intomainfrom
fix/message-sending-session-key

Conversation

@zeroaltitude
Copy link
Copy Markdown
Owner

What

Thread sessionKey into the PluginHookMessageContext passed to runMessageSending from the outbound delivery path.

Why

applyMessageSendingHook in src/infra/outbound/deliver.ts constructed the hook context with only { channelId, accountId, conversationId } and dropped the sessionKey that deliverOutboundPayloads already had in scope as sessionKeyForInternalHooks (resolved at line 950 from params.mirror?.sessionKey ?? params.session?.key).

PluginHookMessageContext already declares sessionKey?: string, and the inbound auto-reply/dispatch.ts path already populates it via deriveInboundMessageHookContexttoPluginMessageContext. This brings the outbound deliver path in line with the type contract and the inbound dispatch path.

Concrete regression observed

openclaw-provenance correlates a per-turn signal across two hooks:

  • agent_end writes to finalTaintBySession.set(sessionKey, …) so the developer-mode footer payload survives until delivery.
  • message_sending does finalTaintBySession.get(sessionKey) and appends the footer to outbound content.

With sessionKey missing on the message_sending side, the handler fell back to "unknown" and the lookup always missed, so the developer-mode taint trajectory footer never appeared on outbound replies. Confirmed in the live gateway log on integration: agent_end fires per turn but finalTaintBySession.get returns undefined in every message_sending invocation.

Change

  • applyMessageSendingHook accepts an optional sessionKey.
  • The single caller in deliverOutboundPayloads passes sessionKeyForInternalHooks.
  • Two new unit tests in deliver.test.ts cover the present-and-absent cases.

Tests

pnpm test src/infra/outbound/deliver.test.ts → 55/55 passing (53 prior + 2 new).

Risk

Low. Additive only — the field is optional in the type, no plugins currently rely on it being absent. The inbound dispatch path was already passing sessionKey; this aligns the outbound path with that existing behavior.

The outbound delivery path in applyMessageSendingHook constructed a
PluginHookMessageContext containing only { channelId, accountId,
conversationId } and dropped the sessionKey that the surrounding
deliverOutboundPayloads scope already had as
sessionKeyForInternalHooks. Plugins receiving message_sending therefore
saw ctx.sessionKey === undefined for every reply.

This breaks any plugin that needs to correlate a per-turn signal
emitted in agent_end with the matching outbound delivery in
message_sending. The concrete observed regression is the
openclaw-provenance developer-mode taint footer: agent_end populates a
finalTaintBySession map keyed by sessionKey, and message_sending looks
it up by sessionKey before appending the footer. With the key missing
on the message_sending side, the lookup always returned undefined and
no footer was ever appended.

PluginHookMessageContext already declares sessionKey?: string and the
auto-reply dispatch path in src/auto-reply/dispatch.ts already
threads it through deriveInboundMessageHookContext +
toPluginMessageContext. This change brings deliver.ts in line.

- applyMessageSendingHook now takes an optional sessionKey and forwards
  it on the runMessageSending context.
- The single caller in deliverOutboundPayloads passes
  sessionKeyForInternalHooks, which is already resolved earlier in the
  function from params.mirror?.sessionKey ?? params.session?.key.
- Two new unit tests cover the present-and-absent cases.

Closes the deliver.ts side of the agent_end → message_sending
correlation gap. No behavior change for plugins that don't read
ctx.sessionKey.
@zeroaltitude
Copy link
Copy Markdown
Owner Author

Re-targeted upstream as openclaw#73706.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant