Skip to content

Slack-specific prompt logic leaks into the adapter host runtime #186

Description

@akrentsel

Summary

compose_inbound_wakeup_prompt in the host branches on adapter_type == "slack" and appends Slack-specific prompt text, reading two metadata keys (dmTarget, isActiveThread) that the Slack worker invents. This puts protocol-specific behavior in the host, coupled to the worker by untyped string keys across a language boundary.

The forthcoming architecture doc in #171 states the rule this breaks:

The host owns supervision, routing, durable message records, retries, and conversation wakeups. Protocol-specific behavior stays in each worker.

Where

Host side — crates/executor/src/adapter/runtime.rs:910-925:

if config.adapter_type == "slack" {
    if let Some(dm_target) = metadata.get("dmTarget").and_then(|value| value.as_str()) {
        prompt.push_str(&format!(
            "\n\nSlack sender DM target: `{dm_target}`. Use this only for appropriate private follow-up; do not use DM to bypass safety policy.",
        ));
    }
    if metadata.get("isActiveThread").and_then(|value| value.as_bool()) == Some(true) {
        prompt.push_str(
            "\n\nThis Slack message is from an active thread, but it may be ambient conversation. Only call send_adapter_message if the message appears directed at Exo, ...",
        );
    }
}

Worker side — examples/exo/adapters/slack/worker.ts:771,775 emits isActiveThread and dmTarget into the event metadata.

Related instances of the same pattern (not the subject of this issue, but relevant if a general fix is preferred):

  • crates/executor/src/adapter/tools.rs:904 — Slack dm: target validation

Suggested fix

metadata is already free-form JSON fully controlled by the worker, so the guidance can travel with it. Have workers emit their own prompt notes and let the host append them generically:

{
  "type": "message",
  "target": "C123:1700000000.000000",
  "text": "...",
  "metadata": {
    "promptNotes": [
      "This Slack message is from an active thread, but it may be ambient conversation. Only call send_adapter_message if ..."
    ]
  }
}

Host side, replacing the adapter_type == "slack" block:

if let Some(notes) = metadata.get("promptNotes").and_then(|v| v.as_array()) {
    for note in notes.iter().filter_map(|n| n.as_str()) {
        prompt.push_str("\n\n");
        prompt.push_str(note);
    }
}

Activity

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

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