mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-03 10:06:05 +08:00
## Summary AI agents have 13 moderation tools (close_topic, delete_topic, edit_tags, edit_post, etc.) that currently execute immediately without human oversight. This adds an optional approval queue that routes these tool actions through Discourse's review queue for moderator approval before execution. - **New `require_approval` toggle** on AI agents — when enabled, moderation tool calls are intercepted and sent to the review queue instead of executing immediately - **Review queue integration** — moderators see the agent name, tool name, parameters, and a rendered snippet of the triggering post, then approve or reject - **Loop prevention** — approved tool execution is wrapped in `DiscourseAutomation.set_active_automation` to prevent automation re-trigger loops (e.g., `edit_tags` → `topic_tags_changed` → automation fires again) ### New files - `AiToolAction` model — stores tool name, parameters (JSONB), agent/bot user refs, and triggering post ID - `ReviewableAiToolAction` — Reviewable subclass with approve (executes tool) and reject (discards) actions - `ReviewableAiToolActionSerializer` — serializes target tool data and payload context - Review queue frontend component — displays tool action details and post snippet - Two migrations: `ai_tool_actions` table and `require_approval` column on `ai_agents` ### Modified files - `Tool` base class gains `requires_approval?` (default `false`), overridden to `true` on all 13 moderation tools - `Bot#invoke_tool` — intercepts tools when both tool and agent opt in to approval - Agent admin editor — new "Require approval" checkbox - Agent REST model — `require_approval` added to attribute whitelists for save payloads - Serializer, controller, plugin.rb — wired up for the new field and reviewable type
37 lines
1,008 B
Ruby
37 lines
1,008 B
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe AiToolAction do
|
|
fab!(:ai_agent)
|
|
|
|
before { enable_current_plugin }
|
|
|
|
it "validates presence of tool_name" do
|
|
action = AiToolAction.new(bot_user_id: -1, ai_agent: ai_agent)
|
|
expect(action).not_to be_valid
|
|
expect(action.errors[:tool_name]).to be_present
|
|
end
|
|
|
|
it "validates presence of bot_user_id" do
|
|
action = AiToolAction.new(tool_name: "close_topic", ai_agent: ai_agent)
|
|
expect(action).not_to be_valid
|
|
expect(action.errors[:bot_user_id]).to be_present
|
|
end
|
|
|
|
it "creates a valid record" do
|
|
action =
|
|
AiToolAction.create!(
|
|
tool_name: "close_topic",
|
|
tool_parameters: {
|
|
topic_id: 1,
|
|
closed: true,
|
|
reason: "test",
|
|
},
|
|
ai_agent: ai_agent,
|
|
bot_user_id: -1,
|
|
)
|
|
|
|
expect(action).to be_persisted
|
|
expect(action.tool_name).to eq("close_topic")
|
|
expect(action.tool_parameters).to eq("topic_id" => 1, "closed" => true, "reason" => "test")
|
|
end
|
|
end
|