✨ Add the file to plugin and MCP audit events (#12972)

* ✨ Add the file and MCP session to plugin audit events

Events sent by plugins and the MCP plugin only carried their origin, so
the multi-file and dashboard MCP workflows could not be measured, nor
duplicated events from several open tabs spotted.

The context of se/event and se/add-event now carries the file-id of the
file the action ran in (absent on the dashboard) and, for the MCP, the
mcp-session-id of the tab connection that ran it.

Closes #12902

AI-assisted-by: claude-opus-5-5

* ♻️ Drop the MCP session id from plugin audit events

Every audit event already carries the session-id of the tab in its
context, and the MCP session id is only a hash of it, so it adds
nothing to group events by tab.

AI-assisted-by: claude-opus-5-5

* 🐛 Use a reified event in the system events test

ptk/data-event does not implement IWithMeta, so add-event failed on it.
Plugin actions emit reified events, which carry metadata.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
andrés gonzález 2026-11-11 16:18:15 +01:00 committed by GitHub
parent ca616a39ed
commit 0d05cfaf5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 22 deletions

View File

@ -10,31 +10,35 @@
[app.main.store :as st]
[app.plugins.register :as r]))
(defn- get-origin
[plugin-id]
(if (= plugin-id r/mcp-plugin-id) "mcp" "plugin"))
(defn- get-context
"Returns the audit context of an action run by a plugin: the file it ran
in (none on the dashboard) and, for plugins other than the MCP, the
plugin name and URL. Events drop the nil values."
[state plugin-id]
(let [file-id (:current-file-id state)]
(if (= plugin-id r/mcp-plugin-id)
{:file-id file-id}
(let [plugin-data (r/get-plugin-data state plugin-id)]
{:file-id file-id
:plugin-name (:name plugin-data)
:plugin-url (:url plugin-data)}))))
;; Formats an event from the plugin system
(defn event
[plugin-id name & {:as props}]
(if (= plugin-id r/mcp-plugin-id)
(-> props
(assoc ::ev/name name)
(assoc ::ev/origin "mcp")
(ev/event))
(let [plugin-data (r/get-plugin-data @st/state plugin-id)]
(-> props
(assoc ::ev/name name)
(assoc ::ev/origin "plugin")
(assoc ::ev/context
{:plugin-name (:name plugin-data)
:plugin-url (:url plugin-data)})
(ev/event)))))
(-> props
(assoc ::ev/name name)
(assoc ::ev/origin (get-origin plugin-id))
(assoc ::ev/context (get-context @st/state plugin-id))
(ev/event)))
(defn add-event
[event plugin-id]
(let [plugin-data (r/get-plugin-data @st/state plugin-id)]
(with-meta
event
(if (= plugin-id r/mcp-plugin-id)
{::ev/origin "mcp"}
{::ev/origin "plugin"
::ev/context {:plugin-name (:name plugin-data)
:plugin-url (:url plugin-data)}}))))
(with-meta
event
{::ev/origin (get-origin plugin-id)
::ev/context (get-context @st/state plugin-id)}))

View File

@ -0,0 +1,52 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.plugins.system-events-test
(:require
[app.main.data.event :as ev]
[app.main.store :as st]
[app.plugins.register :as r]
[app.plugins.system-events :as se]
[cljs.test :as t :include-macros true]
[potok.v2.core :as ptk]))
(def ^:private plugin-id "00000000-0000-0000-0000-000000000000")
(defn- set-state
[file-id]
(set! st/state
(atom {:current-file-id file-id
:profile {:props {:plugins {:data {plugin-id {:name "Plugin"
:url "https://plugin.test"}}}}}})))
(t/deftest mcp-event-carries-file
(let [file-id (random-uuid)]
(set-state file-id)
(let [data (deref (se/event r/mcp-plugin-id "create-version" :file-id file-id))]
(t/is (= "mcp" (::ev/origin data)))
(t/is (= {:file-id file-id} (::ev/context data)))
(t/is (= file-id (:file-id data))))))
(t/deftest plugin-event-carries-file-and-plugin
(let [file-id (random-uuid)]
(set-state file-id)
(t/is (= {:file-id file-id
:plugin-name "Plugin"
:plugin-url "https://plugin.test"}
(::ev/context (deref (se/event plugin-id "add-flow")))))))
(t/deftest mcp-add-event-carries-file
(let [file-id (random-uuid)]
(set-state file-id)
(t/is (= {::ev/origin "mcp"
::ev/context {:file-id file-id}}
(meta (se/add-event (ptk/reify ::test) r/mcp-plugin-id))))))
(t/deftest event-has-no-file-on-the-dashboard
(set-state nil)
(t/is (nil? (-> (deref (se/event r/mcp-plugin-id "create-project"))
::ev/context
:file-id))))

View File

@ -101,6 +101,7 @@
[frontend-tests.plugins.read-only-test]
[frontend-tests.plugins.register-test]
[frontend-tests.plugins.shape-bugfixes-test]
[frontend-tests.plugins.system-events-test]
[frontend-tests.plugins.text-test]
[frontend-tests.plugins.tokens-test]
[frontend-tests.plugins.user-test]
@ -301,6 +302,7 @@
'frontend-tests.plugins.read-only-test
'frontend-tests.plugins.register-test
'frontend-tests.plugins.shape-bugfixes-test
'frontend-tests.plugins.system-events-test
'frontend-tests.plugins.text-test
'frontend-tests.plugins.tokens-test
'frontend-tests.plugins.user-test