Release notes · v4.2 → v9.2.6

Every release,
every story.

A single timeline. Every version Respira has shipped, with the stories behind the ones that mattered. Click into any release tagged story for the full write-up.

Connect your WordPress site
Looking for the plugin? The download and your license key are in your dashboard, one free sign-up away. No card.
766 Releases shipped
15 With a landing page
9.2.6 Latest stable
336 days Since v1.0
Product
Filtered GenerateBlocks · 3 releases See GenerateBlocks MCP → Clear
October 3, 2026
WordPress plugin v9.1.20 Patch October 3, 2026 JetEngine listings take element writes again
  • JetEngine listings take element writes again. update_element and batch_update on a JetEngine listing died with "array_map(): Argument #2 must be of type array, string given" before anything was written, and left an unapproved duplicate behind. JetEngine answers get_post_meta for its listing posts with a plain string where WordPress returns a list. The meta read now accepts both, and the write receipt can no longer be the reason a write fails. Reported by G. on two sites.

  • Elementor append works with Elementor's own format. mode append merged the page's existing (already simplified) elements with the new native ones, then decided how to read the whole list from the first node, which is always an existing one. The new section was never converted and the untyped-widget tripwire refused it. Each node is now read on its own; the tripwire still refuses a genuinely untyped widget, and its message names the first offending element's path, id and type. Reported by M.R.

  • Custom CSS on block pages is stored and renders. update_page, update_post and update_custom_post silently dropped custom_css on Gutenberg, Kadence, Spectra, GenerateBlocks and GreenShift pages while reporting success. It now goes to _respira_page_css, printed after the fidelity stylesheet, re-pointed to the duplicate's id for the preview and back to the original's on approval; the snapshot and write receipt cover it. A builder with nowhere to keep page CSS refuses with respira_custom_css_unsupported before any duplicate is made. Reported by J.G.

  • Divi Theme Builder page templates keep the global areas. A page-specific header or footer made through Respira went into a new template with its other areas empty, and Divi shows the theme's own header or footer in an empty area; a second layout for the same page went into a second template that lost the tie. New templates now carry the global layouts, a second layout for the same conditions goes into the existing template, updates clear the caches Divi's own save clears and list which templates render the layout, and list_theme_builder_templates warns about a dropped global area or a shared condition. Reported by C.P.

  • Divi 5 global modules use Divi's shape. save_divi_library_item now wraps the item in divi/placeholder, and the page reference carries blockName and localAttrs, as Divi's Save to Library does. A global text module no longer blanks every other text module on the page, a global code module renders, and apply_divi_library_item places the reference inside the page's wrapper. Unparsable block attributes are refused on save as they already were on update_custom_post.

  • A date set on a page duplicate survives approval. update_page wrote the explicit-date marker on a variable it never defines.

  • An ability refused by its own plugin says why. WordPress's ability_invalid_permissions arrived as a bare 500. Respira now runs the plugin's permission check again with the same input and user and answers 403 with the ability, the WordPress user and roles, the plugin's own reason and the fix when it is known (WPForms create-form: wp-admin, WPForms, Tools, AI MCP, Enable MCP Write Access). search_abilities shows the same answer as permission_check. Reported by S.G.

September 27, 2026
WordPress plugin v9.1.0 Minor September 27, 2026 Plan mode for every MCP client
  • Plan mode for every MCP client. begin_session takes mode: "plan", on the site's own MCP endpoint and on the REST routes the npm server calls. From then on every write answers respira_plan_step instead of changing the site: a step id, the tool, its arguments with secrets hidden, the predicted effect, and whether it would land on a draft copy or the live item. Reads run as usual, and a write the connection could never make is still refused rather than planned. apply_plan (POST /respira/v2/plan/apply) runs the steps in order, each through the same checks as a direct call, stops at the first failure unless continue_on_error is set, and returns one receipt with each step's outcome, snapshot ids and preview links. The apply call is the person's approval for a step whose tool asks for one, and the receipt names those steps. get_plan shows the plan, discard_plan empties it, and end_session turns plan mode off and says how many steps never ran. One write can be planned without a session by sending _respira_plan: true.

  • Read-only abilities from other plugins, one switch. Respira > Abilities has a new setting, off unless the site owner turns it on: "Let Respira run read-only abilities". With it on, an agent can call any ability that another plugin declares read-only and not destructive, without the owner enabling each one. On a site with Elementor 4.3, WooCommerce, WPForms and WP Mail SMTP that is 19 abilities, among them Elementor's page structure and widget schemas. Every call still runs through Respira's safety wrap and the activity log, which records that the setting let it through, and through the plugin's own permission check. Anything that can change the site, or that does not say it is read-only, still has to be enabled by name. Search and the refusal message say which applies to each ability.

  • A site keeps its own instructions for AI agents. Respira > AI Skills holds skills: short Markdown pages that tell every agent working on this site how a thing is done here, with a title, a one-line description, an on/off switch and an "agents may edit" switch. Each save is a revision, ten are kept, and any of them can be restored from the standard revisions screen. Site Memory and Playbooks are unchanged; a skill is the longer, human-readable third store.

  • Agents can read and, when asked, write them. respira_list_skills, respira_get_skill, respira_write_skill and respira_delete_skill are on the npm server and on the site's own MCP endpoint, under /respira/v1/skills. A write is refused on a skill marked as not editable by agents. A delete is approval-gated by default, like deleting a page, and goes to the trash. A skill's body is treated as information about the site, never as an instruction to Respira's tools.

  • Every session knows the skills exist. respira_get_site_context carries a site_skills block next to site_memory: the enabled skills' names and descriptions and one paragraph telling the agent to read a matching skill before the task. Bodies never ride along; they come from respira_get_skill.

  • What your AI is told. A new screen under Respira shows, in order, what an agent receives from this site: the connection instructions the site's MCP endpoint sends (read from the same method the endpoint calls, with a note about what the npm server adds), Site Memory with each note's source, the skills with a link to edit each one, the playbooks, and the active design direction. Read-only; every section says where the change is made.

  • Skills are edited where posts are edited. Respira > AI Skills opens the standard post editor: the body is a plain Markdown box, the Excerpt box is named Description, and a sidebar box holds the two switches, the skill's name and who last wrote it. Revisions show in the standard revisions screen, with a link from the sidebar, and a restore says so. A skill gets its name from the title on the first save, draft included; a draft is listed but stays out of the site context until it is published.

  • Divi 5: native attributes by default. From Bronko's field test: the build was fast and faithful, but the handover lost, because an agent could fall back to inline style="" HTML and to code modules, and the client then found styling the Visual Builder cannot show or edit. Every agent write to a Divi 5 page (build_page, inject_builder_content, update_element, update_module, batch_update, apply_builder_patch and the add_* shortcuts) now takes two options, allow_inline_styles and allow_code_modules, both false by default.

    • A style Divi 5 has a design setting for is written as that attribute, whether it arrives as a settings key, as a CSS name (text-transform, border) or as a style="" on the element that wraps the module's whole content. New native routes: text transform (Divi's capitalization from 5.8.1, its style list before), italic, underline and strike-through, border width, style and colour (all sides or one), opacity (Divi's Filters), height, min and max height, z-index, overflow, and the alignment add_heading, add_text and add_button already offered.
    • A style with no native place is not written and comes back in the write result as unmapped_styles, one entry per style with the element (type, path, node id, text), the property, the value and the reason: a colour on a <span> inside a sentence, a <style> block in a text module, a CSS variable other than a Divi global colour, a property Divi has no setting for.
    • A write that adds a divi/code module is refused (respira_divi5_code_module_refused, 422, nothing written) and names the native module to use: divi/video for a video embed, divi/map, divi/contact-form, divi/heading, divi/image, divi/button or divi/text. A script or an embed the user asked for goes through with allow_code_modules: true.
    • What a page already stores is left as it is: an existing code module can be edited, and inline styles already on the page (the Visual Builder's own rich-text colouring) stay.
    • Both options true is the previous behaviour, byte for byte (measured on Divi 5.13). Divi 4 does not change (measured on 4.27.8). The HTML converter keeps its code modules and its carried stylesheet in fidelity mode.
    • Measured on Divi 5.13 with the styling a real prompt carries, across the four write paths: 63 of 116 requested styles landed as native attributes before, 110 after, and every one of the 110 renders. The other six are four in the module's own Custom CSS field (left as sent) and two colours on part of a sentence, reported.
  • A host that strips the Authorization header no longer ends the connection. Respira's clients now send the credential a second time, in X-Respira-Authorization, and the plugin uses that copy when a REST request arrives with no credential at all. It never overwrites a real Authorization header (a staging site's HTTP password keeps working), it stands aside when X-Respira-API-Key is present, and it accepts only Bearer followed by a Respira key or access token, so it cannot log anyone in through application passwords or another plugin. Turn it off with define( 'RESPIRA_DISABLE_AUTH_FALLBACK', true ); in wp-config.php, or the respira_allow_auth_fallback filter from a must-use plugin. The Authorization self-test now reports whether the copy arrives, with a new status, stripped_fallback_ok, for a server that eats Authorization but passes Respira's header, and the /admin/keys route that respira.press uses to deliver site tokens reads the header wherever the server put it.

  • Respira > Troubleshoot. A new screen runs the connection checks from the site's own server and shows each as pass or fail, with the cause and one next step: the REST API, the ?rest_route= address, a web page where JSON belongs (a Cloudflare challenge, a firewall's block page, a maintenance plugin or the site's own front page, each named), the Authorization header, Respira's backup header, the MCP address, sign-in discovery at /.well-known/oauth-protected-resource, a bot filter that refuses the user agents claude.ai's and ChatGPT's connectors send (asked twice before it is called a rule), the security plugins and CDNs in front of the site, and whether the site can reach respira.press. "Copy report for your host" produces a plain-text report for a hosting support team: what failed, what to allow, and the id of every test request (each carries an X-Respira-Probe-Id header), with no passwords, cookies, keys or page contents. Linked from Setup when linking fails or an agent will not connect.

  • Let your AI connect itself. Setup's "connect your agent" step now has a prompt to paste into Claude Code, Cursor, Codex or any agent that can change its own settings: it adds the site's MCP address (with the ?rest_route= form as the fallback), reads the site back through the tool ending in get_site_context, and reports the site name and address it got, which the prompt names in advance. It says which steps stay with the owner: approving the Respira sign-in in the browser, restarting the app, and, for claude.ai and ChatGPT, adding the custom connector. A second prompt only checks the connection and changes nothing. Neither carries a key.

  • Errors that say what to do. Respira errors on the connection, sign-in and approval paths now carry two more fields in their data: cause, one of credential, capability, licence, security_gate, approval, host_environment, invalid_input, not_found, conflict, rate_limited or server_error, and retry_ok, whether sending the same call again can work. A revoked key says credential and no retry; a rate limit, or a site token that could not be checked because respira.press was out of reach, says retry. Errors from the shared helper and the wp-admin approval screens carry them too, from their HTTP status when the code is not one of the known ones. On the site's own MCP address a failed tool call adds one sentence to the message ("The key or token is the problem, not the request. Sending the same call again will not help until that changes.") and the pair in _meta["respira/error"]. A value an error sets itself is never replaced.

  • Block editor writes are render-checked, like every other builder's. After a write to a block editor page (update_page, update_post, inject_builder_content, the add_* tools, convert_html_to_builder, and Kadence, Spectra, GenerateBlocks and GreenShift pages), Respira reads the saved markup the way WordPress's block parser does and renders every block in process, so a draft or a duplicate is checked too; a published page is also fetched. The response's trace carries render_validator_pass, validator_warnings naming each problem with its block and path, and the snapshot_uuid that undoes the write in one respira_restore_snapshot call. It catches a closing comment with no opener (WordPress stops reading blocks there), a closer under the wrong name, a block never closed, attribute JSON that does not parse, a comment the parser does not read as a block, a heading or paragraph whose text sits only in its attributes and renders as nothing, a block whose render throws, and it warns on an unregistered block type, an attribute WordPress will drop, and PHP warnings raised while rendering. On a Studio site, 45 of 46 broken writes answered success with nothing said; now all 46 come back with the problem named (40 as a failed check or a refusal, 6 as a warning), and the 25 correct writes pass clean.

  • The active design direction is in the site context. respira_get_site_context carries an active_design_direction block next to site_skills: the direction's name, its colour roles, type families, scale and weights, spacing and radius under the direction's own keys, one line on how page writes use it, and respira_get_design_direction for the rest. Guidance and the brand scale stay out, so the block is a few hundred bytes. It is null when no direction is active.

  • Background writes start right away, not on the next visit. A write sent with _respira_async used to wait for WordPress's scheduled tasks, which run when someone visits the site, so on a quiet site a queued job could sit for hours. It now starts as soon as it is queued: on hosts that allow it, Respira answers first and keeps working in the same request; elsewhere it starts the job with a signed, single-use request to the site itself. The scheduled task stays as a backstop, so a site that blocks both still runs the job exactly as before. On a test site with scheduled tasks switched off, a queued write finished within a second. respira_get_job_status now says how each job was started (dispatch) and what ran it (ran_via).

  • A queued job only runs if Respira queued it. Every job record is signed with a key derived from the site's own salts. A record changed in the database afterwards, by hand or by a search-and-replace during a migration, is refused and reported, never run. When a job starts, the key that queued it must still be active, unrevoked and unexpired, and the job runs with that key's current permissions. The host's time and memory limits apply as before; a job that dies mid-run is reported as failed instead of staying "running"; and at most two background jobs run at once while the rest wait their turn.

  • The guarded plugin update queues through REST too. Sent to POST /security/guarded-update without _respira_async, the whole run used to happen inside the request: on a test host the proxy answered 503 after about five minutes and PHP was stopped in the middle of the backup. The route now takes _respira_async the way the site's MCP endpoint does, queues the run as the same signed job, and answers at once with status: queued, a job_id, the run_id and respira_get_job_status to poll. A call that carries neither _respira_async nor run_inline: true is refused with a sentence that names both, on the REST route and the MCP endpoint alike.

  • Waiting for the backup no longer holds a PHP worker. The queued run used to sleep for up to ten minutes while Duplicator or BackWPup worked, on a host with one or two workers taking one of the few the backup plugin needs for its own requests. It now reads the backup's state once, hands the worker back, and starts again five seconds later, as the same signed job: from a read of its progress, or from WordPress's scheduled tasks when nobody is reading. The receipt, the progress steps, the ten minute limit and the refusal when the limit passes are the same as before, and a backup that is only seen finished long after the limit does not lead to an update nobody is watching.

  • Divi 5 update_element writes what build_page writes. Letter spacing, line height, border radius and box shadow were dropped on update although a build wrote them, and padding shorthand ("17px 33px") was stored as a string Divi never compiles. A single padding value ("24px") had the same problem on every path. With the new default both are native spacing and radius attributes.

  • Divi 5 drop warnings no longer tell the agent to fall back to a divi/code block.

  • Comments can be marked as spam or moved to the trash. update_comment takes status as approve, hold, spam or trash (and still accepts 1 and 0), and makes the change the way wp-admin does, so a spam comment teaches Akismet and a comment in the trash or in spam comes back out cleanly. Status 0 used to be ignored without a word; it now holds the comment. On a site with the trash turned off, trash is refused rather than deleting the comment for good; delete_comment is the tool for that. The answer carries status_label in words.

  • The audit log no longer grows forever. Entries older than 180 days are removed once a day. The window is set in Respira, Settings, under Audit & approvals (0 keeps everything, otherwise 7 to 3650 days), and a respira_audit_retention_days filter can decide for the site instead, which the screen then says. Only the oldest entries go, as one unbroken run, so the tamper seal on the log keeps verifying; they go 1,000 at a time by id, so a site with millions of rows never has its audit table locked for long, and a large backlog finishes in a follow-up run a few minutes later. An agent cannot change the window. On an existing site the first daily run removes everything already older than 180 days.

  • Every SVG Respira uploads is cleaned first. On a site where another plugin allows SVG uploads, an agent's SVG was stored exactly as sent, and an SVG is a document a browser runs. upload_media, sideload_image (and every tool that sideloads a remote image), stock images and the workspace logo now clean an SVG before storing it, whether it is recognised by its name or by its bytes (.svgz too), with the enshrined/svg-sanitize library: scripts, event handlers, foreignObject, javascript: and data: links and every reference to outside content are removed, and the result is read back and checked. An SVG that cannot be parsed, or still carries anything dangerous, is refused with respira_svg_unsafe. Respira still never changes which file types a site accepts, and uploads made in wp-admin or by other plugins are untouched. If the cleaner is missing, SVG uploads through Respira are refused rather than stored unchecked.

  • A theme file write can be undone. write_theme_file and append_theme_file overwrote the only copy on disk. Each now stores the file's current bytes in a snapshot first and answers with snapshot_uuid; respira_restore_snapshot puts the previous content back, or removes the file if the write created it, and takes a copy of the file as it is before doing so. The stored path goes through the same checks as a live request (CSS, SCSS, LESS or JSON, inside the themes folder, no traversal). If the copy cannot be saved, nothing is written.

  • A delete through Respira can be undone. delete_page, delete_post, delete_custom_post and delete_media used to remove the item for good with no snapshot. They now move it to the trash where WordPress keeps one for that type, take a snapshot first, and answer with snapshot_uuid and an undo block: respira_restore_snapshot takes it back out of the trash with its old status and slug. permanent: true still deletes for good where the existing live-edit guards allow it, and so does a site with the trash turned off, but only after a snapshot that can recreate the post under the same ID with its row, every field, its terms, its comments and its child pages; if that snapshot cannot be taken, nothing is deleted. Media has no trash unless MEDIA_TRASH is on, so a media delete moves the files into a protected folder under uploads and restore puts them back byte for byte; the folder is removed when its snapshot is pruned. Media whose file is not on the server (offloaded to cloud storage) is refused rather than deleted beyond recovery. delete_media now honours force: true as "skip the trash", which its tool description always said. Restoring any older snapshot of a permanently deleted post, including through a session rollback, recreates it first.

  • An agent can no longer change the options that lock people out of a site. update_option and delete_option refuse siteurl, home, admin_email, users_can_register, default_role, active_plugins, uninstall_plugins, template, stylesheet, db_version, cron, the roles option under any table prefix, every WordPress key and salt, and Respira's own safety, governance, connection and licence settings, with respira_option_protected and a sentence on where to make the change instead. Names are matched the way the database matches them, so SiteURL or siteurl is refused too. Refused before the snapshot, so nothing is left behind. The respira_protected_options filter adds names to the list; it cannot take any away. list_options now shows values that look like keys, salts, tokens or passwords as [redacted], inside settings arrays too, and get_option never returns WordPress's salts or Respira's own licence key and tokens.

  • Ability search reads the site, not a list. search_abilities now searches every ability registered on the site at the moment of the call, from every active plugin, with the plugin's own label, description, category and read-only and destructive annotations. It used to read a list bundled with Respira that named 12 abilities from other plugins, so on a site running Elementor 4.3, Ninja Forms, WPForms and WP Mail SMTP an agent found none of the 80 those plugins registered. Results are ranked (the ability's name first, then its label, plugin, category and description), 20 by default and never more than 100. Each one says whether Respira can invoke it right now and, when it cannot, why not and what the site owner would change. The bundled list is still used where WordPress has no Abilities API.

  • Tool switches and profiles now bind on the site's own MCP endpoint. A tool switched off in Respira > Tools, and the Observe and Content profiles, were enforced over REST and the npm server but not over the site's own MCP endpoint (the one Claude Desktop, ChatGPT and the AER chat use): its ability ids carry two prefixes and only one was stripped before the lookup, so nothing matched the blocked list. Both id shapes now land on the same name. Found on 2026-09-27; the fix is one loop and a test that drives the real class with both shapes.

  • One plugin, one advisory, with a way back. guarded_plugin_update updates a wordpress.org plugin to the exact version offered, with a restore point before and a health check after, and puts the previous version back from wordpress.org when the check fails. Before anything is written it checks the offer, that the previous version is archived on wordpress.org (a premium plugin is refused: no archive, no rollback), the new version's PHP and WordPress requirements, permission and governance, the backup plugin the site runs, and that the site answers from the server. Duplicator and BackWPup take a fresh backup through their abilities and the run waits for it; UpdraftPlus starts one the way its own command line does and the run reads the record it writes; any other provider, or none, closes the one-click path unless the caller says, in its own argument, that no restore point is accepted. After the update: the site answers, the front page and the most recently edited page answer, no new fatal in debug.log, and the builder still renders. The receipt (versions, advisory, every preflight answer, the four checks, the rollback if any, duration) goes to the activity log and back to the caller. Approval-gated; queue it with _respira_async and read progress with get_guarded_update. check_guarded_update is the same preflight as a read, so a button can say why it is disabled before anyone presses it. Readiness manifest key guarded_update.

  • A backup on its own. take_backup takes a backup of the site through its own backup plugin with no update afterwards: the guarded update's restore point step as a standalone call, for the backups card in Respira AER. The same tiers (Duplicator and BackWPup through their abilities, polled until finished; UpdraftPlus through the action its own command line fires), the same ten-minute limit, the same run record (get_guarded_update reads it while it is queued) and the same activity row. With any other backup plugin, or none, it refuses and says what to do instead. The receipt names the provider, the id or file the provider gave back, and how long it took. Approval-gated; queue it with _respira_async. Readiness manifest key take_backup.

WooCommerce add-on v4.0.1 Patch An agent orders summary could 500 the whole site on hosts that answer plugin bootstrap before WooCommerce finishes waking up
  • An agent orders summary could 500 the whole site on hosts that answer plugin bootstrap before WooCommerce finishes waking up. It now waits for WooCommerce's own readiness signal before touching order data, and falls back to an honest empty summary instead of a fatal if that signal never comes.

  • The Observe governance profile closed only 42 of 277 tools despite its own description promising every write closed. It computed its blocked set from an eleven-family category registry, and a tool in no family was never considered; it now derives the blocked set from each tool's own declared read/write flag instead, closing 162 tools including every WooCommerce write. Reported by D.L.

  • add_sale_badge and add_low_stock_badge were flagged read-only while actually writing, which meant a governance profile closing writes left them open and the transport withheld the retry fields that make an interrupted write safe to repeat. Both now correctly declare themselves as writes.

  • convert_html_to_builder with no builder specified threw a type error on any block-library site (GreenShift, Spectra, Kadence Blocks, GenerateBlocks) instead of converting, because auto-detection asked "is this Gutenberg" by name instead of by relationship; naming the builder explicitly masked the bug by skipping detection. Also fixed: a successful token mapping was reported as both mapped and unmapped in the same response, a comparison bug not a data bug.

  • All five WooCommerce card/checkout tools now declare edit_target and verification_marker in their schemas; the backend has always forwarded both, but no schema advertised them, so they were unreachable from any client.

Showing the most recent 146 releases. Older entries are in the release feed. Every release follows semver.org.