Skip to content

Inter-session message attribution incorrectly defaults to owner peer #35

Description

@abhibansal-sg

GitHub Issue: Inter-session message attribution incorrectly defaults to owner peer

Repository: plastic-labs/openclaw-honcho
Plugin version: 1.2.1
OpenClaw version: 2026.3.28

Summary

When agents communicate via sessions_send (inter-session messaging), the Honcho plugin attributes all inbound role: "user" messages to the owner peer instead of the sending agent's peer. This causes Honcho to model the owner as having authored messages they never wrote, polluting the owner's representation.

Environment

  • Multi-agent OpenClaw setup with 4 agents (main/Kai, atlas, nyx, mia)
  • Agents communicate via sessions_send for task delegation
  • Each agent has its own persistent session and Honcho peer (agent-main, agent-atlas, etc.)

Steps to Reproduce

  1. Agent A (e.g., agent:main) sends a message to Agent B (e.g., agent:atlas) via sessions_send
  2. Agent B processes the message and responds
  3. The agent_end capture hook fires and calls flushMessages()extractMessages()
  4. The inbound message from Agent A is attributed to the owner peer instead of Agent A's peer

Expected Behavior

The message should be attributed to Agent A's peer (agent-main), since Agent A is the actual sender.

Actual Behavior

The message is attributed to the owner peer, because extractMessages() maps all role: "user" messages to ownerPeer unconditionally:

const peer = role === "user" ? ownerPeer : agentPeer;

Root Cause Analysis

Provenance data exists but is not accessible

OpenClaw tags sessions_send messages with full provenance in the session transcript (.jsonl file):

{
  "role": "user",
  "content": [...],
  "provenance": {
    "kind": "inter_session",
    "sourceSessionKey": "agent:main:discord:channel:...",
    "sourceChannel": "discord",
    "sourceTool": "sessions_send"
  }
}

However, event.messages passed to plugin hooks strips the provenance field. Messages in the hook only contain ["role", "content", "timestamp"].

This means the plugin cannot access provenance from the hook event data directly.

Same issue affects subagent_announce messages

Subagent completion announcements also arrive as role: "user" with provenance.sourceTool === "subagent_announce" and are similarly mis-attributed to the owner.

Data from a real session:

  • 80 sessions_send messages attributed to owner (should be agent-main)
  • 18 subagent_announce messages attributed to owner (should be parent agent)

Additional gap: cron/heartbeat messages

Cron-initiated and heartbeat messages arrive as role: "user" with no provenance at all. These system-generated prompts get attributed to the owner, polluting the owner's representation with content like "Read HEARTBEAT.md..." and "[cron:...] WhatsApp Pulse — 15-min scan."

This cannot be fixed in the plugin without OpenClaw core tagging these messages with provenance.

Workaround (local patch applied)

We implemented a local workaround that reads provenance from the session transcript .jsonl file:

  1. flushMessages() constructs the transcript path from ctx.agentId + ctx.sessionId
  2. Reads the .jsonl file and builds a Map<timestamp, provenance> for inter-session user messages
  3. Matches event.messages entries by timestamp to find their provenance
  4. Resolves sender agent peers via getAgentPeer() and builds a peer override map
  5. Passes overrides to extractMessages() which uses them instead of defaulting to ownerPeer

This workaround is functional but has limitations:

  • Reads the entire transcript file on every flush (performance concern for large sessions)
  • Relies on timestamp matching (could theoretically have collisions)
  • Must be re-applied after plugin updates

Recommended Fix

Ideal (requires OpenClaw core change)

OpenClaw should include provenance in event.messages passed to plugin hooks. The data exists on disk — it just needs to not be stripped before hook delivery.

Plugin-side fix (if OpenClaw exposes provenance)

In extractMessages() or flushMessages():

// Before peer assignment, check provenance
if (role === "user" && m.provenance?.kind === "inter_session") {
    const senderAgentId = parseAgentIdFromSessionKey(m.provenance.sourceSessionKey);
    if (senderAgentId) {
        peer = await getAgentPeer(senderAgentId);
    }
}

Design decisions (validated with 4-round technical review)

  • sessions_send messages → attribute to sending agent peer
  • subagent_announce messages → attribute to parent agent (not ephemeral subagent)
  • All :subagent: session keys collapse to parent agent identity
  • Sender peers added to session with observeMe: true, observeOthers: true
  • Single merged addPeers() call with OR-merge for observation booleans
  • Cron/heartbeat attribution deferred (needs OpenClaw core provenance tagging)

Additional Issue: createMemorySearchTool error

After updating OpenClaw to 2026.3.28, the plugin throws:

plugin tool failed (openclaw-honcho): TypeError: Cannot read properties of undefined (reading 'createMemorySearchTool')

This appears to be a compatibility issue between plugin v1.2.1 and OpenClaw 2026.3.28's memory tool API. Core memory capture/context injection still works — only the QMD passthrough tool registration fails.

Impact

  • Owner peer representation polluted with agent-authored and system-generated content
  • Agent peer representations missing their actual inter-agent communication
  • Context injection feeds polluted data back into prompts (compounding effect)
  • Multi-agent setups with frequent sessions_send usage are most affected

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions