{"id":362363,"date":"2026-09-02T10:01:18","date_gmt":"2026-09-02T10:01:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/ai-agent-activity-log\/"},"modified":"2026-09-02T12:47:57","modified_gmt":"2026-09-02T12:47:57","slug":"digitizer-ai-agent-log","status":"publish","type":"plugin","link":"https:\/\/hi.wordpress.org\/plugins\/digitizer-ai-agent-log\/","author":9810718,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.0","stable_tag":"1.1.0","tested":"7.1","requires":"5.5","requires_php":"7.2","requires_plugins":null,"header_name":"Digitizer AI Agent Log","header_author":"Digitizer","header_description":"Records what automations changed on this site - anything that arrived over the REST API, WP-Cron, WP-CLI or XML-RPC. A change made by a person in wp-admin is not recorded at all, so the log is what the agents did and nothing else.","assets_banners_color":"5f6778","last_updated":"2026-09-02 12:47:57","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/Digitizers\/digitizer-ai-agent-log","header_author_uri":"https:\/\/www.digitizer.studio","rating":0,"author_block_rating":0,"active_installs":0,"downloads":64,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"benkalsky","date":"2026-09-02 10:00:58","revision":3677679},"1.0.1":{"tag":"1.0.1","author":"benkalsky","date":"2026-09-02 11:48:58","revision":3677910},"1.1.0":{"tag":"1.1.0","author":"benkalsky","date":"2026-09-02 12:47:57","revision":3678019}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3677679,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3677679,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3677679,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3677679,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.0.1","1.1.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3677910,"resolution":"1","location":"assets","locale":"","width":1600,"height":617}},"screenshots":{"1":"The log: what each change arrived on, which application password authenticated it, what it touched and which fields. The same entries are readable over the REST API."}},"plugin_section":[],"plugin_tags":[8531,2353,8534,23853,4574],"plugin_category":[],"plugin_contributors":[260321],"plugin_business_model":[],"class_list":["post-362363","plugin","type-plugin","status-publish","hentry","plugin_tags-activity-log","plugin_tags-ai","plugin_tags-audit-log","plugin_tags-rest-api","plugin_tags-wp-cli","plugin_contributors-benkalsky","plugin_committers-benkalsky"],"banners":{"banner":"https:\/\/ps.w.org\/digitizer-ai-agent-log\/assets\/banner-772x250.png?rev=3677679","banner_2x":"https:\/\/ps.w.org\/digitizer-ai-agent-log\/assets\/banner-1544x500.png?rev=3677679","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/digitizer-ai-agent-log\/assets\/icon-128x128.png?rev=3677679","icon_2x":"https:\/\/ps.w.org\/digitizer-ai-agent-log\/assets\/icon-256x256.png?rev=3677679","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/digitizer-ai-agent-log\/assets\/screenshot-1.png?rev=3677910","caption":"The log: what each change arrived on, which application password authenticated it, what it touched and which fields. The same entries are readable over the REST API."}],"raw_content":"<!--section=description-->\n<p>An AI agent, a headless front end, a sync script and a cron job all reach WordPress the same way a person never does: over an API. When something on the site is not what you left it as, the question is not \"who changed this\" but \"did a person change this, or did something else\".<\/p>\n\n<p>This plugin answers that question and only that question. It records changes that arrived over the REST API, WP-Cron, WP-CLI or XML-RPC. A change made by a person clicking in wp-admin is not recorded - not filtered out afterwards, not stored and hidden, simply never written.<\/p>\n\n<h4>Why not a general activity log<\/h4>\n\n<p>General activity logs record everything, which means the automated change you are looking for is one line in a thousand made by your own team. This one starts from the other end. If you already run an activity log, this sits beside it and answers a different question.<\/p>\n\n<h4>What it records<\/h4>\n\n<ul>\n<li><strong>The channel<\/strong> - <code>rest<\/code>, <code>cron<\/code>, <code>cli<\/code> or <code>xmlrpc<\/code>.<\/li>\n<li><strong>Which application password<\/strong> authenticated the request, by name, when there was one. Never a guess: when the name cannot be determined the field is empty rather than filled with a User-Agent or an IP that merely looks like an identity.<\/li>\n<li><strong>What changed<\/strong> - the object, and which fields were touched.<\/li>\n<li><strong>When<\/strong>, in UTC, shown in your site's timezone.<\/li>\n<\/ul>\n\n<h4>What it does not record<\/h4>\n\n<ul>\n<li><strong>Not the values.<\/strong> The names of the fields that changed, never their contents. A log of what your site contains is a second copy of your site.<\/li>\n<li><strong>Not people.<\/strong> A block editor save is a cookie-authenticated REST request, and it is recognised as a person and skipped. This is the plugin's central promise, and it is the case it was hardest to get right.<\/li>\n<li><strong>Not reads.<\/strong> Something polling the REST API would fill the table in a day and drown the writes that were the reason to look.<\/li>\n<li><strong>No IP addresses.<\/strong><\/li>\n<\/ul>\n\n<h4>Reading the log<\/h4>\n\n<p>On its own admin screen, filtered by channel, object type and date range. Or over the REST API at <code>digitizer-ai-agent-log\/v1\/activity<\/code>, which requires <code>manage_options<\/code>. There is deliberately no route that deletes: a log that can be erased through the API is a log an attacker erases on the way out.<\/p>\n\n<h4>Multisite<\/h4>\n\n<p>Each site keeps its own log, in its own table, and a run that switches between sites files each change under the site it happened on.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate. The log table is created the first time the plugin runs.<\/li>\n<li>Find the log under <strong>Agent Activity<\/strong> in the admin menu.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20it%20slow%20the%20site%20down%3F\"><h3>Does it slow the site down?<\/h3><\/dt>\n<dd><p>Changes are held in memory during the request and written once, at shutdown, and only when the request came in on a channel worth recording. A browser request writes nothing at all.<\/p><\/dd>\n<dt id=\"does%20it%20send%20anything%20anywhere%3F\"><h3>Does it send anything anywhere?<\/h3><\/dt>\n<dd><p>No. Nothing leaves the site.<\/p><\/dd>\n<dt id=\"how%20large%20does%20the%20table%20get%3F\"><h3>How large does the table get?<\/h3><\/dt>\n<dd><p>Old rows are pruned automatically, at most once an hour.<\/p><\/dd>\n<dt id=\"a%20plugin%20on%20my%20site%20rewrites%20its%20own%20settings%20and%20fills%20the%20log.%20can%20i%20silence%20it%3F\"><h3>A plugin on my site rewrites its own settings and fills the log. Can I silence it?<\/h3><\/dt>\n<dd><p>Yes, with a filter. Nothing is filtered out by default, because what is absent from this log is supposed to mean it did not happen over an API:<\/p>\n\n<pre><code>add_filter( 'digitizer_ai_agent_log_record', function ( $record, $entry ) {\n    if ( 'elementor_library' === $entry['object_subtype'] ) {\n        return false;\n    }\n    return $record;\n}, 10, 2 );\n<\/code><\/pre>\n\n<p>The entry carries <code>object_type<\/code>, <code>object_subtype<\/code>, <code>object_id<\/code>, <code>object_name<\/code>, <code>action<\/code>, <code>fields<\/code>, <code>blog_id<\/code>, <code>channel<\/code>, <code>app<\/code> and <code>user_id<\/code>, so a rule can match on who made the change as well as what it touched. On a network the filter runs inside the site the change happened on, so a callback may read that site's own options to decide.<\/p>\n\n<p>Note that such an entry is not a mistake: WordPress refuses an identical meta write before the plugin ever sees it, so a change that reaches the log did alter the row. It is simply a change you may not care about, and only your site can say which those are.<\/p><\/dd>\n<dt id=\"what%20happens%20when%20i%20uninstall%20it%3F\"><h3>What happens when I uninstall it?<\/h3><\/dt>\n<dd><p>The table is dropped and both of its options are deleted, on every site of a network. Nothing is left behind.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>A <code>digitizer_ai_agent_log_record<\/code> filter, applied to every entry once the channel and the application name are known, so a site can silence a writer it does not care about. Nothing is filtered by default.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>The plugin page gets its screenshot. No functional change.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First release.<\/li>\n<\/ul>","raw_excerpt":"Records what automations changed on your site - REST API, WP-Cron, WP-CLI, XML-RPC. Changes people make in wp-admin are not recorded at all.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/362363","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=362363"}],"author":[{"embeddable":true,"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/benkalsky"}],"wp:attachment":[{"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=362363"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=362363"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=362363"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=362363"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=362363"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/hi.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=362363"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}