Skip to content

Non-conversational marker wakes can still pin a turn's speech locus (batched-wake path) #189

Open

Description

@Anarchid

A non-conversational marker (chat:edited, chat:deleted, chat:reaction-remove) that arrives as a channel incoming message can still choose the channel for the agent's next turn's speech.

  • A channel incoming message with triggerInference queues a wake that carries channelId, whatever its tags:
    for (const agentName of targetAgents) {
    if (!this.agents.has(agentName)) continue;
    this.pendingRequests.push({
    agentName,
    reason: 'mcpl:channel-incoming',
    source: event.serverId,
    timestamp: Date.now(),
    // Route this turn's auto-published speech back to THIS channel, not
    // the global most-recent-inbound locus (item-3 redux, trunk agents).
    channelId: event.channelId,
    counterparty: event.author?.id ? `${event.serverId}:user:${event.author.id}` : undefined,
    // Addressed messages outrank ambient chatter when a batched wake
    // picks the turn's frozen speech locus.
    addressed,
    });
  • When several wakes are batched, the channel of the most recent channel-bearing request wins unless an addressed message is present:
    let ambientReq: InferenceRequest | undefined;
    let addressedReq: InferenceRequest | undefined;
    for (const r of requests) {
    if (!r.channelId) continue;
    ambientReq = r;
    if (r.addressed) addressedReq = r;
    }
    const channelReq = addressedReq ?? ambientReq;
  • That channel becomes activeTriggerChannels and the frozen turn locus, which routeSpeech uses before defaultPublishChannel.

Repro shape: the agent is busy. An ambient message arrives on chanA, then a later edit marker arrives on chanB. The wake policy wakes on everything in chanB. zulip_mcp documents this setup: "a policy that wakes on everything in the channel wakes on them too". The next turn's reply to chanA is published in chanB.

Relation to #157 / #168: #168 stops markers from moving defaultPublishChannel, but this batched-wake path is separate and #168 doesn't cover it. Its changelog line ("a marker can no longer retarget outbound speech") overclaims until this is fixed too.

Suggested direction: leave channelId off the queued request when hasNonConversationalTag(event.tags) (from #168). The batch-selection comment already assumes this for reactions ("reactions — which never carry channelId").

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions