Class: Collavre::AiAgent::ClaudeChannelAdapter
- Inherits:
-
Object
- Object
- Collavre::AiAgent::ClaudeChannelAdapter
- Defined in:
- app/services/collavre/ai_agent/claude_channel_adapter.rb
Overview
Adapter for Claude Channel agents that communicate via MCP (ActionCable) instead of RubyLLM. Messages are delivered through AgentChannel WebSocket; responses arrive asynchronously via the reply API endpoint.
Defined Under Namespace
Classes: UndeliverableError
Instance Method Summary collapse
- #deliver ⇒ Object
-
#initialize(agent:, context:, task: nil) ⇒ ClaudeChannelAdapter
constructor
A new instance of ClaudeChannelAdapter.
Constructor Details
#initialize(agent:, context:, task: nil) ⇒ ClaudeChannelAdapter
Returns a new instance of ClaudeChannelAdapter.
11 12 13 14 15 16 |
# File 'app/services/collavre/ai_agent/claude_channel_adapter.rb', line 11 def initialize(agent:, context:, task: nil) @agent = agent @context = context @task = task @topic_id = context.dig("topic", "id") end |
Instance Method Details
#deliver ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'app/services/collavre/ai_agent/claude_channel_adapter.rb', line 18 def deliver unless @topic_id # Workflow subtasks build context without a topic (see # WorkflowExecutor#build_subtask_context). A Claude Channel agent # cannot service those — raise so AiAgentJob fails the task and the # parent workflow advances via fail_subtask! instead of hanging. raise UndeliverableError, "Claude Channel delivery requires a topic_id (agent=#{@agent.id})" end comment = find_comment payload = { type: "dispatch", agent_id: @agent.id, # task_id lets the MCP client echo it back via /reply so the server # can complete the exact dispatched task even when topic concurrency # > 1 allows multiple in-flight delegated tasks per topic. task_id: @task&.id, # session_topic marks whether this dispatch targets a Session-mapped # topic (topics.session_id present). One shared agent fans out to many # session topics; without this flag every session subscribed to # agent:user:<id> would also answer a sibling session's topic. The # plugin handles a session-topic dispatch only on its OWN session # topic; a non-session (work) topic may be handled by any session # (the server's atomic task claim dedups concurrent handlers). session_topic: session_topic?, comment: { id: @context.dig("comment", "id"), content: @context.dig("comment", "content"), author_id: @context.dig("sender", "id") || @context.dig("comment", "user_id"), author_name: @context.dig("sender", "name") || comment&.user&.display_name, topic_id: @topic_id, creative_id: @context.dig("creative", "id"), created_at: comment&.created_at&.iso8601 } } # Per-agent stream is the source of truth for MCP plugin clients: # they subscribe once by agent_id and receive every dispatch routed # to this agent regardless of which topic triggered it. The per-topic # stream is kept for legacy/UI viewers but is not how Claude Channel # plugins consume dispatches. AgentChannel.broadcast_to_agent(@agent.id, payload) AgentChannel.broadcast_to_topic(@topic_id, payload) end |