Module: Collavre::Comment::ClaudeChannelPermission
- Extended by:
- ActiveSupport::Concern
- Included in:
- Collavre::Comment
- Defined in:
- app/models/collavre/comment/claude_channel_permission.rb
Overview
A native Claude Code tool-permission prompt surfaced into a topic as a structured approval comment (built by Api::V1::AgentsController#notify when the relayed notify carries a permission_request_id). It reuses the native approval comment UI — approver gate, approve button, ✅/🚫 decided label —but a decision does NOT execute a tool server-side (the tool runs inside the remote Claude Code process). Instead approve/deny relays the decision over the agent stream so the MCP plugin resolves the paused tool call.
Defined Under Namespace
Classes: AlreadyDecided
Constant Summary collapse
- ACTION_TYPE =
"claude_channel_permission"
Instance Method Summary collapse
-
#broadcast_claude_channel_permission_decision(behavior) ⇒ Object
Relay the decision to the suspended session over the agent stream.
-
#claude_channel_permission? ⇒ Boolean
True when this comment’s action payload is a Claude Channel permission prompt (vs. a native execute_tool/approve_tool action).
-
#claude_channel_permission_denied? ⇒ Boolean
The decision, once made, is persisted into the action payload so the rendered comment can distinguish ✅ approved from 🚫 denied.
- #claude_channel_permission_request_id ⇒ Object
-
#decide_claude_channel_permission!(behavior, by:) ⇒ Object
Atomically record the human’s allow/deny: stamp action_executed_at/by (which hides the buttons and marks the comment decided) and persist the decision into the action payload.
-
#rebroadcast_claude_channel_permission_decision ⇒ Object
Replay this comment’s already-recorded decision (used by the resubscribe path).
Instance Method Details
#broadcast_claude_channel_permission_decision(behavior) ⇒ Object
Relay the decision to the suspended session over the agent stream. The MCP plugin matches request_id against the prompts it surfaced, so sibling sessions sharing this (shared) agent ignore a request_id they never raised. task_id is intentionally absent: the decision only unblocks the paused tool call; the in-flight delegated task completes later via /reply.
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'app/models/collavre/comment/claude_channel_permission.rb', line 106 def (behavior) request_id = return false if request_id.blank? || user_id.blank? AgentChannel.broadcast_to_agent(user_id, { type: "permission_decision", request_id: request_id, behavior: behavior.to_s, agent_id: user_id }) true end |
#claude_channel_permission? ⇒ Boolean
True when this comment’s action payload is a Claude Channel permission prompt (vs. a native execute_tool/approve_tool action).
63 64 65 |
# File 'app/models/collavre/comment/claude_channel_permission.rb', line 63 def .present? end |
#claude_channel_permission_denied? ⇒ Boolean
The decision, once made, is persisted into the action payload so the rendered comment can distinguish ✅ approved from 🚫 denied.
73 74 75 |
# File 'app/models/collavre/comment/claude_channel_permission.rb', line 73 def &.dig("decision") == "deny" end |
#claude_channel_permission_request_id ⇒ Object
67 68 69 |
# File 'app/models/collavre/comment/claude_channel_permission.rb', line 67 def &.dig("request_id") end |
#decide_claude_channel_permission!(behavior, by:) ⇒ Object
Atomically record the human’s allow/deny: stamp action_executed_at/by (which hides the buttons and marks the comment decided) and persist the decision into the action payload. Raises AlreadyDecided if a decision was already recorded.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'app/models/collavre/comment/claude_channel_permission.rb', line 81 def (behavior, by:) behavior = behavior.to_s raise ArgumentError, "behavior must be allow or deny" unless %w[allow deny].include?(behavior) with_lock do reload raise AlreadyDecided if action_executed_at.present? payload = raise ArgumentError, "not a Claude Channel permission comment" unless payload payload["decision"] = behavior update!( action: JSON.pretty_generate(payload), action_executed_at: Time.current, action_executed_by: by ) end end |
#rebroadcast_claude_channel_permission_decision ⇒ Object
Replay this comment’s already-recorded decision (used by the resubscribe path). Unlike broadcast_claude_channel_permission_decision the behavior is read from the persisted payload rather than passed in, and only a comment that is both a permission prompt and decided re-broadcasts — a still- pending prompt has no decision to deliver.
124 125 126 127 128 129 130 131 |
# File 'app/models/collavre/comment/claude_channel_permission.rb', line 124 def return false unless decision = &.dig("decision") return false if decision.blank? (decision) end |