=== Agentic Daisy AI Agent Firewall: earlier releases === The changelog for the current releases is in readme.txt. This file keeps every earlier release, newest first, because WordPress.org shows only the first 5,000 characters of a readme's changelog. == Changelog == = 2.0.1 = * Changed: each part of Agent Firewall is now its own page in the admin menu - Activity Ledger, Approvals, Agents, Policies, Exposure Scan and a new Settings page - instead of tabs on one screen. Every page has its own address you can bookmark, the number of held actions shows on the Approvals item, and the kill switch is at the top of every page. * Changed: the alert, webhook and ledger retention settings moved from the Activity Ledger to the Settings page, so the ledger opens with only the ledger. Pages also load faster: the Activity Ledger now makes 4 requests to your server when it opens, down from as many as 15, and nothing is fetched twice. * Links from earlier alert emails, webhooks and digests still work and open the right page. * Fixed: the webhook setting said it posts "the same two events the email alert above covers". It posts six: an action held for approval, one still waiting after a day, an agent tripping its rate limit, an agent denied for reaching outside its scopes or while the kill switch is engaged, an AI tool changing definition, and the audit ledger failing its daily check. The setting now lists them. = 2.0.0 = * New: Agent Firewall Pro and Agency, paid editions sold separately (see "Pro and Agency" in readme.txt). This free plugin keeps every feature it had and gains the fixes below; nothing in it was moved behind a paywall, and it shows no upgrade prompts. * Fixed: the webhook URL check now refuses an IPv6 address on this server or its private network (such as `http://[::1]/`) and `.localhost` names when you save, as it already did for IPv4. WordPress's own send-time check stopped these from being contacted, so this only makes the error appear at save rather than as deliveries that never arrive. * Security: the kill switch now stops agents reading the site over the REST API, as it always said it did. Writes were refused, but a stopped agent could still read posts, users and other data while you investigated. Each refused read is recorded in the audit ledger; an agent can still check the status of its own held action. * Fixed: on a multisite network, if another plugin switched sites during a request, the firewall kept reading and writing the original site's policies, agents and audit ledger. It now always uses the site WordPress is on. * Fixed: deleting the plugin from a multisite network removed only the main site's data, even when other sites had asked for theirs to be deleted. Every site is now visited, and each keeps or loses its data by its own choice. * Security: the agent block on the plugin's own controls compared route names letter for letter, but WordPress does not - so an agent whose linked user is an administrator could reach the settings (including the webhook signing secret), the audit ledger, the policies and the approval queue by writing the address in different capitals. Every casing is now refused, and a held request to those controls is never carried out on approval in any casing either. * Security: an agent whose linked user is an administrator could read the plugin's settings - including the webhook signing secret - and the AI exposure scan, and could reach the settings with writes that your policies would then judge like any other. Both are now closed to agents outright, as the rest of the firewall's own controls already were. A test now fails if any of the plugin's routes is ever left open to agents again. * New: the audit ledger verifies itself every day. If its tamper-evident chain stops verifying - an entry changed, inserted or removed outside the plugin - the site administrator is emailed with the first entry that fails, and the webhook is told, once and then daily until it verifies again. Until now that was only noticed by someone running `wp agent-firewall verify`. The email rides the existing alert checkbox. * New: an approval queue nobody is working is no longer silent. When a held action has waited more than a day, a reminder goes to your webhook and, if you ticked "Email me when an action is held for my approval", to your inbox - at most once a day, and always while there is still time to decide before held actions expire. * Fixed: approving the same held action twice at once - two administrators clicking Approve together, or a double-submitted click - could carry it out twice. A held action is now claimed before it runs, so the second approval is told it has already been decided. If an approval fails partway, the action returns to the queue to be approved again or rejected. = 1.8.1 = * Fixed: approving a held action now carries it out. Only deleting or partly editing a post could be approved before: a held change to a user, a category or tag, a plugin, a theme or any custom content type failed with "This action type cannot be executed automatically" and sat in the queue until it expired, and an approved post edit applied its title, content, excerpt, status and slug while silently dropping everything else - categories, tags, featured image, author, date. Approval now sends the agent's request again exactly as it was, as the agent's linked user, so WordPress validates it and checks that user's permissions afresh. An approved post, page or media edit can now be undone, as an allowed one could. * New: an approval WordPress refuses because the agent's user is not allowed to make the change is marked "refused" and leaves the queue, rather than waiting to expire. * Changed: REST writes are judged after WordPress's own checks rather than before them. A write the agent's user is not allowed to make, or whose input WordPress rejects, is answered by WordPress straight away and recorded as "refused" instead of being held for an approval that could never succeed - an Editor's agent asking to rename the site used to wait in the queue. Writes to routes that do not exist get WordPress's "not found" rather than being held, and the audit ledger no longer records "allowed" for a write WordPress then refused. * New: an agent write another plugin answers before the firewall could evaluate it is recorded as "bypassed" instead of passing unseen, and counts against the agent's rate limit. * New: recorded refusals are capped at 20 per agent for each kind of target every ten minutes, for REST writes and abilities together. The last row before the cap says when recording resumes. * Fixed: an approval stopped because a person had changed the target first showed in the Activity Ledger as "No rule matched, so a built-in default applied", as if a policy had denied it. It now says it was approved and not carried out, and why. * Fixed: a long value in a held action's payload was cut off at the edge of its box; it now wraps, so the whole request can be read before approving it. * New: the approval queue names what each action targets - "About us, page #5", linked to its edit screen - instead of only the kind of action and its raw payload, so you can see what a deletion would delete before approving it. * New: the approval queue no longer offers Approve for an action it cannot carry out - a file upload, a write caught inside another plugin, an MCP tool with no WordPress ability behind it - and says why instead. * Changed: the plugin's display name is now "Agentic Daisy AI Agent Firewall - Approvals, Audit & Undo for MCP", so the Plugins screen and the directory say what it is for. Only the name changed: the plugin folder, its settings, its data and the admin menu are exactly as before. * Fixed: an MCP client that authenticates with an application password no longer records one write per connection. Opening an MCP session is a handshake, sometimes a ping, and a listing of what the server offers - none of which change anything - but arriving with no agent identity, each of those was filed in the ledger as an unidentified write to an unknown thing. A monitor that reconnects every thirty seconds logged several thousand of them a day, which is the real cost: the entries the ledger exists to surface were buried under the plumbing of a client that had not done anything yet. Tool calls inside the same envelope are recorded exactly as before. The firewall has always ignored this plumbing for agents holding a token; it now treats an unidentified client's the same way. * Fixed: "Refuse writes from application passwords no agent claims" no longer stops such a client from connecting at all. It refused the handshake, so the client could never reach the point of asking to write - which looked like the endpoint being down rather than a write being refused, and prevented nothing, since a handshake writes nothing. The setting now refuses the tool call, which is the part that would have changed something. = 1.8.0 = * Fixed: WooCommerce actions are classified correctly. WooCommerce puts seven abilities in core and names them the other way round from WordPress core - `product-delete` rather than `delete-post` - which the firewall could make nothing of. A store *lookup* sat in the approval queue waiting for a human, rules written for products or for deletions never fired, and the audit ledger recorded every store action as "unknown". Classification now reads what an ability declares about itself through the Abilities API - whether it is read-only, and whether it is destructive - and reads the name only when an ability declares nothing. Queries are allowed; price changes and deletions are held. None of this is WooCommerce-specific: any plugin that describes its abilities properly is now governed properly. * New: policies can match on whether an ability declares its own action destructive. One rule - hold anything declared destructive - covers every plugin's dangerous actions, including ones that did not exist when you wrote the rule, instead of a list of entity types you have to keep up to date. The rule builder offers it as a three-way choice. "Declared not destructive" deliberately does not match an ability that declares nothing, because saying nothing is not a promise of safety. * Improved: the built-in defaults hold an action the ability itself marks destructive, even where that action would otherwise be allowed. WooCommerce marks updating a product destructive, and updating ordinary content is normally allowed - which is how an unattended price change would have gone straight through. * Fixed: the webhook test message sends `data` as an object, as every real event already did. An empty PHP array encodes as `[]`, so a test delivery carried `"data": []` where a rate-limit or ability-drift alert carried `"data": {...}` - two types for one field, on the one delivery a person is most likely to point a new parser at. * Fixed: an action held for approval through the Abilities API is reported as the error code `adaf_pending_approval`, carrying the id to poll and a 202 status. WordPress validates what an ability returns against the output that ability says it produces, and "held for review" matches no ability's declared output - so on WooCommerce, and on anything else declaring one, the agent was told its tool was broken rather than that a person had to decide, and never received the id it needs to check back. The action itself was always held correctly; only the answer was wrong. **If you have written an agent client that reads the held-action payload from the Abilities API, branch on the error code instead.** Denials already worked this way, and the REST API is unchanged. = 1.7.0 = * New: an Exposure Scan tab answers the question a client asks after something changes on their site: what can write here, and who is accountable for each of them. It runs on demand, reads only, and writes nothing. It reports application passwords no agent claims - anything holding one writes over the REST API with no identity, so no policy, scope or rate limit applies to it - how far the account behind each agent can reach, every registered ability and whether it matches software actually installed here, AI provider credentials WordPress stores in the options table without encrypting them, and code that runs on every request but never appears on the Plugins screen. Every finding links to the screen that resolves it, so the report is a way into the plugin rather than a list to act on somewhere else. There is a printable version with no admin menu, headed with your own name if you set one, for handing to a client; your browser's Print command turns it into a PDF. * New: you can be emailed when an action is held for your approval - a second checkbox on the Activity Ledger screen, deliberately **off by default** and unaffected by updating, so nobody is subscribed to anything by an upgrade. Switched on, each held action produces one message with a link straight to the approval queue. It is independent of the existing alert checkbox: turning either off leaves the other alone. * New: an action held for approval now announces itself. Until now a held action was visible only to whoever happened to open the dashboard, which is the one assumption an approval queue cannot afford to make - the agent has already been told "pending" and the work stays frozen until a person acts. Held actions are delivered to your webhook, if you have set one, with a link that opens the approval queue directly rather than the front page of the plugin. Each held action is announced once; a burst of them all get through, because each is a separate decision somebody has to make. * New: agents denied for reaching outside the scopes they were issued, or for trying while the kill switch is engaged, are announced the same way. Denials by policies you wrote are deliberately not - those are the firewall working exactly as designed, and an agent stuck in a loop generates them faster than anyone can read, which would bury the denials that mean something. Repeat denials from one agent are limited to one message an hour. Denials reach the webhook only and are never emailed. * New for developers: `adaf_action_pending` and `adaf_action_denied` now fire from the interception pipeline, after the audit ledger row exists, carrying the agent, the action and - for denials - the reason. The plugin previously exposed almost no extension surface at the point where decisions are made. * New: the anomaly alerts can be posted to a webhook, so they reach the channel you actually watch rather than only the site administrator's inbox. Save an endpoint under "Webhook URL" on the Settings page (Agent Firewall > Settings) and the same two events the alert email covers - an agent tripping its rate limit, and an ability changing definition - are delivered there as JSON. The message line is carried as both "text" and "content", which is all a Slack, Google Chat or Discord incoming webhook needs to render it, alongside structured fields for n8n, a log collector or an endpoint of your own; one setting covers all of them, with no per-service integration to configure. Deliveries are signed with a per-site secret as X-ADAF-Signature, so the receiving end can be sure a message came from your site, and a "Send a test message" button says what happened rather than leaving you to discover a broken endpoint during an incident. Email and webhook are independent switches, and delivery never delays the request that raised the alarm. * Improved: the agents table and the linked-user picker name each role the way WordPress names it everywhere else. The role was printed as its slug, which core roles survive by accident - "editor" is close enough to "Editor" - while a custom role read as a database key, showing an account as "(content_manager)" rather than "(Content Manager)". = 1.6.0 = * New: an application password can be bound to an agent, so AI clients that can only send a username and password are governed like any other agent. Most MCP clients authenticate that way and cannot send a custom `Authorization: Bearer` header, so until now they arrived with no identity at all - every policy, scope and rate limit was skipped, and their writes were only recorded as "untracked". A bound password identifies the agent exactly as its token does, and the whole pipeline applies: per-call classification of MCP requests, scopes, policy, rate limiting, the audit ledger and undo. Bind one on the Agents screen, choosing from the linked user's own passwords by name. One password identifies one agent, so a password another agent already holds is shown but not selectable, and deleting it on the user's profile ends the binding, which the Agents screen reports rather than hiding. * New: "Refuse writes from application passwords no agent claims" is now a checkbox on the Agents screen. The setting has existed since the first release, but the only way to reach it was to edit the database or read the source, so in practice nobody could refuse ungoverned automation who did not already know the option's name. Left off, such writes proceed and are ledgered as "untracked", as before. Turning it on will also stop any existing integration that uses an application password, so bind the ones you want to keep working first. * Security: an application password bound to an agent that is disabled or revoked is now refused with a 401, rather than falling back to being recorded as untracked automation. Disabling an agent has to stop it; letting it keep writing with only a ledger note is not disabling it. A binding that points at an agent linked to a different WordPress user is refused for the same reason - it would run the request under capabilities the authenticating user never had. * Improved: the documentation now says plainly that WP-CLI is not governed, and why. A `wp` command carries no request and no credential to identify, and it is not a boundary worth defending in any case: anyone who can run `wp` on the server can equally deactivate this plugin. This was previously an open question rather than a stated answer. = 1.5.0 = * Security: agents can no longer reach the firewall's own controls. An agent is linked to a WordPress user and inherits that user's capabilities - so one linked to an administrator held the very permission that governs this plugin, and could have issued itself tokens, rewritten the policies judging it, approved its own held actions, or read the audit ledger recording them. The firewall governed every door except its own. Agent tokens are now refused on the plugin's own screens and routes outright, for reads as well as writes, whatever user they are linked to. The one exception is an agent asking whether its own held action has been decided, which it needs in order to wait rather than retry. * Security: agents can no longer be linked to an administrator. Linking agents to a least-privilege user was always the documented advice; it is now enforced rather than suggested, and the check follows the capability rather than the role name, so a custom role carrying the same permission is refused too. If you have no suitable user yet, create one first - the error message says so. An agent linked to an administrator before this release keeps working and is flagged on the Agents screen so you can relink it; the change above already stops it reaching the controls in the meantime. * Improved: choosing the WordPress user an agent is linked to is now a list of names rather than a user ID. The field asked for a number WordPress shows almost nowhere - awkward before, and unanswerable alongside the change above, since you would have had to guess an ID, be told it was refused, and guess again. The list offers only users an agent may actually be linked to, each shown with its role, so the least-privilege choice is visible rather than something to go and look up. * Improved: the Agents screen names the WordPress user each agent is linked to, with that user's role, and links to their profile. It showed a bare user ID - "#2" - which identifies nobody, since WordPress surfaces user IDs almost nowhere; the name shown is now the same one you picked from the list when you created the agent. The role is shown alongside it because the linked user's capabilities are the ceiling on everything the agent can do. An agent whose linked user has since been deleted now says so instead of printing an ID that no longer resolves. * Improved: the agent form explains what the identifier field is and calls it a slug rather than a key, which oversold it - nothing unlocks, and an agent never sends it. It is filled in from the name as you type, lowercased and hyphenated the way WordPress derives a post slug from a title, and suffixed if that slug is already taken instead of failing when you save. Editing it yourself stops the name driving it. The REST API takes `agent_slug` now and still accepts `agent_key`, returning both, so anything already scripted against it keeps working. * Fixed: the screens no longer sit blank while they load. Every table now shows a spinner and the word "Loading" until its data arrives, and announces the result to screen readers the way WordPress announces its own. Two screens were worse than blank: Pending Approvals said "No actions are waiting for review." and Policies said "No policies yet" from the moment they opened, before either had asked the server anything - so on a slower site the firewall spent the first few seconds telling you nothing needed your attention when it had no idea yet. The Activity Ledger and Agents screens, which had no such message, now say when they are genuinely empty. * Fixed: "Compare" in the activity ledger took two clicks. The first only fetched the link and turned itself into a second link reading "View what changed", which had to be clicked in turn - nothing about the first click suggested it was only a preliminary. One click now opens the revision. The lookup is still made only when asked for, so a page of entries costs no extra queries for a link most people never follow. * Fixed: the Undo button and the Compare link in the ledger sat flush against each other with no space and no shared baseline. They are now spaced and aligned, and wrap rather than overflow in a narrow column. * Fixed: the three buttons in the first-run "Get started" panel did not line up, because each one sat directly below its own step and the steps run to different lengths. They now share a baseline. * Fixed: the admin interface stayed in English on translated sites. Its on-screen text was never connected to WordPress's translation system, so the language packs the directory builds for this plugin were downloaded and then ignored. Everything the plugin renders now uses them. = 1.4.0 = * New: the activity ledger says what an agent touched, not just its database id. A row that read "page #218" now names the page and links to it, and settings changes name the setting rather than reading only "option". The name is recorded at the moment the action is judged, so a page an agent deleted still shows what it was called - nothing can look up a deleted page afterward. If the target has been renamed since, both names are shown: somebody renamed it, and that is worth seeing. * New: themes, plugins, templates and widgets are named too. WordPress identifies these by name rather than by number, and that name was being discarded - so a row could say an agent changed a theme without saying which theme. Activating a plugin is among the most consequential things an agent can do to a site, and the record now says which one. * New: allowed post edits offer a Compare link to the WordPress revision screen, showing exactly what the agent changed. It loads only when you ask for it, and is quietly absent when no revision was recorded - revisions can be switched off or trimmed away. * Improved: the alert that warns you when an AI tool changes definition now shows the change. It named the ability and asked you to verify the change was expected, while storing only a fingerprint - so the previous wording was already gone and there was no way to check. Both versions of every changed field are now quoted, and a WordPress update that happened between checks is named, because it usually explains the change. That is offered as an explanation and not a clearance: any plugin can register a tool under a core-looking name. * Improved: tool-registry changes are recorded in the activity ledger as well as emailed, so a change survives the email being deleted. They carry a "drifted" status of their own and can be filtered like any other entry. * Fixed: activating the plugin on a fresh site could fail with "headers already sent", alongside warnings naming other plugins' translations as loading too early. The plugin scheduled its background sweeps while WordPress was still loading plugins, and asking WordPress for its list of schedules at that moment runs other plugins' code before they are ready. The sweeps are now scheduled a moment later, once WordPress has finished starting up. Only ever affected a first activation. = 1.3.0 = * New: the number of actions waiting for your approval now shows in the WordPress admin menu, the way comments awaiting moderation do, and beside the Pending Approvals tab. A held action blocks the agent that asked for it and expires after 72 hours, so noticing one should not depend on opening the plugin first; an empty queue shows nothing at all. * New: the activity ledger and the approval queue are paged, and both now say how many entries match what you are looking at. The ledger showed the twenty newest entries and the queue stopped at the first hundred held actions, in both cases without saying so - an audit log quietly showing you a fraction of itself. Every entry is now reachable, twenty, fifty or a hundred at a time. * Improved: the activity ledger stays fast as it grows. Listing it newest-first had no index to work from, so drawing a page of a large ledger meant reading the whole table and sorting it; this release adds the index, applied to your site automatically. * Fixed: database changes shipped in an update never reached sites that already had the plugin. WordPress reactivates a plugin silently after updating it, so the activation hook does not run - and that hook was the only thing that applied database changes. A site installed before an update kept the old structure indefinitely, and would have gone on doing so for every future release. The plugin now checks its own database version on load and migrates when it is behind, which is also how the index above reaches an existing site. = 1.2.0 = * New: WordPress 7.1 support, verified against the release candidate. Two changes matter for what the firewall can promise there. * New: a second interception door for abilities. WordPress 7.1 lets a plugin see every ability call before it runs, whoever registered the ability and whenever. Until now the firewall could only govern abilities registered after it loaded, because it works by wrapping their callbacks; one registered earlier - by a must-use plugin, say - was invisible to it. Such calls are now judged like any other, and a call already handled by the wrapper is still judged exactly once. * New: refused attempts are now in the audit ledger. An agent trying an ability its user may not run, or sending input that does not fit, was refused by WordPress before the firewall ever saw the call - and left no trace, even though repeated refusals are exactly what enumeration looks like. Each one is now recorded with a "refused" status that reads distinctly from a policy denial, names which check refused it, and joins the hash chain like every other entry. Permission failures are recorded on every supported version; malformed input needs WordPress 7.1. * Fixed: the Activity Ledger's status filter rejected "untracked" with an error instead of filtering by it. Every status the ledger can hold is now a valid filter. * Fixed: the ability-registry monitor could be blinded on WordPress 7.1. The alert that warns you when an AI tool appears or changes definition read the registry through a helper that 7.1 lets any plugin filter, so a plugin could have hidden its own ability from the very check meant to notice it. The monitor now reads the registry directly. = 1.1.0 = * New: MCP transport awareness. Agent tool calls that arrive as an MCP JSON-RPC envelope (its own POST, not a WordPress ability or a standard REST write) are now recognized on any route and evaluated per call, so `tools/list` and a `tools/call` that deletes an event are no longer indistinguishable. Protocol handshakes are treated as reads, tool names map to a policy action and entity, batches are judged call by call, and anything unparseable is held for approval rather than passing through. Blocked calls return a JSON-RPC error the agent can act on. Policy rules can target this door with `source: mcp`. * New: effect backstop. An agent-authenticated write that reaches WordPress by a transport the firewall did not recognize is now caught at the mutation itself (posts, deletions, options, users, role and capability grants) and evaluated before it takes effect, so an unanticipated binding cannot slip a write past policy. It stays dormant unless an authenticated agent is acting, never re-evaluates a call already judged at another door, and records each catch distinctly as an unclassified-transport write. Rules can target it with `source: unclassified`. * Improved: anomaly alert emails now name the site in the subject and body, so an administrator whose inbox serves several sites can tell at a glance which one alerted. = 1.0.1 = * Fixed: repeated "new ability" alerts for abilities that were already known. An ability briefly absent from the registry (a plugin mid-auto-update, or one that registers conditionally) was dropped from the fingerprint baseline, so its return alerted as new every time. Absent abilities now keep their stored fingerprint: an unchanged return is silent, and a changed one still alerts as a definition change. = 1.0.0 = * New: emergency kill switch. One control at the top of every screen denies all agent activity (reads included) until released; every denial is still ledgered, and agents receive a distinct non-retryable `lockdown` reason. * New: policy export and import as portable JSON. Import is append-only, re-validates every rule, attaches agent-scoped policies by agent name, and reports anything it skipped. * New: getting-started panel guides first-run setup until the first agent exists. * Improved: ability-registry alerts now arrive as one digest email per scan instead of one email per ability, so a plugin update registering many abilities no longer floods the inbox. * Improved: the Activity Ledger's "Decided by" column now names the kill switch and the token scope gate instead of attributing those denials to the built-in defaults. * Fixed: opt-in uninstall now removes every plugin option (alerting, fingerprints, retention, lockdown, strict app-password mode) and clears the retention cron job. * Fixed: application-password writes are now actually recorded. The readme promised they were "flagged", but in default mode they left no trace; each one now becomes an "untracked" entry in the audit ledger, naming the route, action and acting user, chained like every other entry. * Fixed: a POST to the singleton /wp/v2/settings route now classifies as an update, so policy rules matching update on options fire for the transport most clients use. Item routes addressed by slug (plugins, themes, sidebars, templates) also classify correctly. * Improved: site-editor and appearance routes (global styles, templates, navigation, menus, widgets, blocks, plugins, themes) now map to named entities policy rules can target, instead of falling into "unknown". Templates, global styles and widgets join the held-for-approval defaults; menus and navigation are treated as routine content. = 0.1.1 = * Security hardening: option snapshot capture and rollback restore are now strictly whitelist-bound. Input keys outside the settings map are never read into snapshots, and restore re-validates every option name before writing. = 0.1.0 = * Initial release: agent identities with scoped tokens, Abilities API and REST interception, deterministic policy engine with rate limiting, approval queue with a state-drift guard, hash-chained audit ledger with secret redaction, revision/trash/snapshot rollback, tool-registry drift alerts, application-password write detection, React admin dashboard.