OS Settings tabs, AI Copilot extensibility, UI component kit expansion#15
Merged
AllTerrainDeveloper merged 4 commits intotrunkfrom Apr 24, 2026
Merged
Conversation
- Implemented a new registry for third-party OS Settings tabs in `src/settings/registry.ts`, allowing plugins to register additional settings tabs. - Created a server sync module in `src/settings/server-sync.ts` to handle live updates of settings tabs from the server. - Introduced a new inline code component `<wpd-code>` with styles and tests for rendering code snippets safely without intercepting keypresses. - Developed an ordered steps component `<wpd-steps>` and its child `<wpd-step>` for structured onboarding flows, complete with styles and tests. - Added PHPUnit tests for the new settings tab registration functions to ensure proper functionality and error handling. Co-authored-by: Copilot <copilot@github.com>
…ted tests - Add `wp.desktop.ai.ask()` function for programmatic access to AI Copilot. - Define interfaces for AskOptions, AskResult, and AskToolCall to structure API responses. - Implement command invocation logic for tool calls, including error handling for unregistered commands. - Create unit tests for `wp.desktop.ai.ask()` covering various scenarios including normal responses, tool calls, and error handling. - Introduce PHP unit tests for `wp_register_desktop_ai_tool()` to validate tool registration and invocation. - Ensure proper handling of system prompts and follow-up responses in the AI interaction flow.
- Made fallbackContext a required property in AskDeps interface to ensure proper command context handling. - Introduced liftMessage and serialiseOutcome utility functions for better message handling and serialization of command results. - Refactored ask function to utilize postToSearch for network requests, improving error handling and reducing code duplication. - Enhanced dispatchToolCall to handle command execution and error structuring uniformly. - Added tests to cover new functionality and ensure robust error handling for empty queries with non-default options. feat(commands): update notify method documentation - Updated documentation for the notify method in CommandContext to clarify its current no-op status and future plans for implementation. fix(ui): mark WpdCode and WpdSteps components as experimental - Changed status of WpdCode and WpdSteps components from stable to experimental to reflect their current development stage. test(ai): add unit tests for aiSearch extensibility - Created a new test suite for verifying the extensibility of the /ai/search endpoint, ensuring filters and actions function as expected.
epeicher
approved these changes
Apr 24, 2026
Collaborator
epeicher
left a comment
There was a problem hiding this comment.
Thanks @AllTerrainDeveloper! I have done the following tests:
- Register a slash-command with aiCallable: true; call wp.desktop.ai.ask( '…', { tools: 'aiCallable' } ); verify the command fires. ✅
- Same, with followUp: true; verify res.message is conversational (not the raw run() return). ✅
| First | Follow up |
|---|---|
![]() |
![]() |
- Register a PHP tool with a capability; verify it's invisible to non-admins
| Admin | Non-admins |
|---|---|
![]() |
![]() |
- Intentionally throw inside a PHP tool handler; verify wp_desktop_ai_search_error fires and the agent can still answer.
- Verify adds a gap in a plugin settings tab; verify existing built-in sections render unchanged. ✅
<wpd-code>c</wpd-code>inside a page doesn't steal c keystrokes. ✅
<wpd-steps>with steps added/removed renumbers automatically. ✅
wp.desktop.ready(fn)called from a server-sync-injected script (i.e. after wp-desktop.init has fired) invokes fn synchronously via microtask. ✅
And they all have passed as expected. LGTM!
Collaborator
Author
|
Wow, great testing! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




Summary
Opens three new extension surfaces for third-party plugins:
wp.desktop.registerSettingsTab()(+ PHP registration APIs for live activation / deactivation).wp.desktop.ai.ask()posts to the same/ai/searchendpoint the built-in overlay uses; opt-in flags let registered slash-commands become AI-invokable tools; server-side plugins can register their own PHP-dispatched AI tools.<wpd-code>,<wpd-steps>/<wpd-step>, plus an opt-instackattribute on<wpd-section>.Plus a load of PHP filters / actions around the AI endpoint, a
wp.desktop.ready()bootstrap alias to close a long-standing late-load race, and a bundle of documentation + tests.Nothing in this PR is a breaking change. Every new API is additive. Every new hook is marked Experimental, since 0.17.0 — we reserve the right to adjust signatures before 0.17.0 cuts.
What's new and what can be achieved with the new hooks
Demo.24.April.mov
1. OS Settings tabs (Experimental)
Third-party plugins can now own a tab in the OS Settings window.
JS entry point:
Built-in tab orders:
appearance=10, ai=20, extended=30, help=40. Third-party default100.PHP registration (for live refresh):
Third-party tabs now see the same OS Settings state the built-in AI tab sees via
ctx.getOsSettings()({ wallpaper, accent, dockSize, ai: { enabled, provider, apiKey } }) +ctx.subscribeOsSettings(cb)for live updates — removes thelocalStorage-poke workaround from plugin code.2. AI Copilot programmatic access (Experimental)
wp.desktop.ai.ask( query, opts? )— the new client-side entry pointAskOptions:
signalresumeTool/startOffsettoolsfalse | 'aiCallable' | string[] | (slug) => boolean— opt in to command tool-calling (see below).systemPromptstring | { mode: 'append' | 'replace', text: string }.followUpboolean— whentrue, ask the AI to compose a natural-language reply after a command runs (see below).commandContextCommandContexthanded to invoked commands.Commands as AI tools
DesktopCommandnow has an opt-inaiCallable: trueflag. When set,ask()callers withtools: 'aiCallable'harvest the command into the OpenAI tool list. If the model picks it, the server returns{ answer_type: 'tool_call', tool: { slug, args } }and the shell invokesrun()locally.Opt-in is deliberate. AI tool-calling is a paraphrasing channel — auto-exposing every registered command (including destructive ones) would turn a typo into a catastrophe.
followUp: true— agentic mode (natural-language replies)Default (one-shot) mode sets
res.messageto whatever the plugin'srun()returned — typically a short status string. Opt into agentic mode to get an AI-composed reply in the voice of the system prompt:ask()does not throw —res.messagefalls back to the one-shot string.Use it for voice / chat / assistant surfaces; skip for one-tap "execute" UI buttons.
wp_register_desktop_ai_tool()— PHP-dispatched AI toolsFor integrations whose logic is inherently server-side (WooCommerce lookups, site-health checks, WP-CLI wrappers):
capabilityis enforced before the tool is visible to the model — unauthorised users never see it exists. Thrown exceptions andWP_Errorreturns are caught into a structured{ error, tool, message }payload so the agent can continue with other tools rather than hard-failing.New PHP filters / actions
All Experimental, since 0.17.0. Full documentation in
docs/hooks-reference.md.Filters (transform the request / response):
wp_desktop_ai_request— rewrite the whole request bundle before the agent loop starts.wp_desktop_ai_system_prompt_appendix— stacking append to the built-in instructions (the 95% extension point).wp_desktop_ai_system_prompt— final transform pass.wp_desktop_ai_system_prompt_replace_capability— cap required formode: 'replace'. Defaultmanage_options.wp_desktop_ai_tools— transform the whole tool list.wp_desktop_ai_command_tools— transform only the command-derived subset.wp_desktop_ai_command_allowed— per-slug gate, returnfalseto drop.wp_desktop_ai_tool_result— mutate a tool's result before it goes back to the model.wp_desktop_ai_answer— final transform hook on every success path.wp_desktop_ai_followup_outcome_max_chars— bound the outcome payload size (default 4000).Actions (observability):
wp_desktop_ai_search_started— first anchor of the trio.wp_desktop_ai_tool_called— fires for every tool invocation.wp_desktop_ai_search_completed— fires on every success path.wp_desktop_ai_search_error— catchesWP_Errorpaths + tool-handler throws.wp_desktop_ai_tool_registered— fires afterwp_register_desktop_ai_tool()succeeds.Every hook carries a shared
request_idUUID for trace correlation.3. UI component kit additions (Experimental)
<wpd-section stack>— opt-in gap between slotted childrenGap is
--wpd-section-gap(default12px). Backwards compatible — existing sections (no attribute) render exactly as before.<wpd-code>— inline / block code badgeRenders via a real
<code>element with no global keypress listeners — safe for URLs, flag names, or anything else that would steal keystrokes if stamped out as<wpd-key>.<wpd-steps>+<wpd-step>— auto-numbered onboardingNumbers come from a CSS counter — inserting or removing a step renumbers the rest automatically.
doneon a step renders ✓ instead of the number.4.
wp.desktop.ready( fn )— bootstrap aliasShort alias of
whenReadymirroringjQuery( fn ). Runs the callback synchronously (microtask) ifwp-desktop.inithas already fired, otherwise queues viaaddAction. Safe for scripts injected mid-session by any server-sync module (widgets, wallpapers, commands, settings tabs) — closes a class of late-load races whereaddAction('wp-desktop.init', …)silently misses the already-fired event.Every doc and recipe example that previously used
wp.hooks.addAction('wp-desktop.init', …)has been migrated towp.desktop.ready()with an explicit "Why notaddActiondirectly?" callout in the JS reference.Implementation notes
Security posture
aiCallableis opt-in per command — destructive commands don't auto-expose to natural-language invocation.systemPrompt: { mode: 'replace' }requiresmanage_optionsby default (filterable); non-admin requests silently downgrade toappend.mb_substr(with byte-level fallback) so multibyte payloads don't produce invalid JSON.Live refresh
OS Settings tabs follow the same server-sync pattern as commands / widgets / wallpapers:
serverSettingsTabScripts+serverSettingsTabsin the shell payload,src/settings/server-sync.tsinjects scripts on activation + unregisters tabs on deactivation via either the JSownertag or the PHP-declared id↔handle snapshot.Extracted helpers
wpdm_ai_compose_instructions()— unifies the three-layer system-prompt composition across primary + follow-up legs.src/ai/ask.tssplit intopostToSearch,dispatchToolCall,composeFollowUp— top-levelask()is now ~70 lines.Files changed
PHP:
includes/ai-copilot/tools-registry.php(new) —wp_register_desktop_ai_tool()+ registry.includes/ai-copilot/search.php— extensibility surface, command tools, follow-up leg, prompt composition helper.includes/ai-copilot/bootstrap.php— load the new registry file.includes/settings-tabs.php(new) —wp_desktop_register_settings_tab_script()+wp_register_desktop_settings_tab().wp-desktop-mode.php— load settings-tabs include.includes/render.php,includes/helpers.php— payload wiring.TypeScript:
src/ai/ask.ts(new)src/settings/registry.ts(new),src/settings/server-sync.ts(new)src/settings/index.ts— tabs interleaving +ctx.getOsSettings/subscribeOsSettings.src/ai-assistant.ts—asklate-binding viaattachAsk.src/commands.ts—aiCallableflag,listAiCallableCommands(),notifyJSDoc rewrite.src/types.ts— new server-entry types.src/ui/components/wpd-section/*—stackattribute.src/ui/components/wpd-code/*(new)src/ui/components/wpd-steps/*(new)src/desktop.ts— public API surface + ready alias + sync wiring.src/public-api.ts,src/ui/components/index.ts— exports.Tests (402 passing, +18 new):
tests/phpunit/tests/aiToolsRegistry.php— 9 cases (registry validation, capability gating, WP_Error / exception handling).tests/phpunit/tests/aiSearchExtensibility.php— 10 cases (every layer ofwpdm_ai_compose_instructions).tests/phpunit/tests/settingsTabs.php— 10 cases (registration, payload, action fires).tests/vitest/ai-ask.test.ts— 18 cases (ask wrapper, command harvest, tool_call dispatch, follow-up leg happy / degrade / abort, system-prompt plumbing, empty-query guard).src/ui/components/wpd-code/wpd-code.test.ts,src/ui/components/wpd-steps/wpd-steps.test.ts— new component smoke tests.Docs:
docs/hooks-reference.md— AI extensibility section,wp_register_desktop_ai_tool(), settings-tab registrations.docs/javascript-reference.md—wp.desktop.ai.ask,registerSettingsTab,ready()alias,<wpd-code>+<wpd-steps>usage, bootstrap "why not addAction" callout.docs/examples/ai-ask.md(new) — 6-section recipe (simple ask, commands-as-tools, allowlists, system prompt, PHP tool registration, observability, cancellation).docs/examples/README.md— index.register-command.md,register-wallpaper.mdto usewp.desktop.ready().CLAUDE.md— live-refresh payload shape updated; doc tree annotated.Bundle impact
desktop.min.js: 285 KB → 297 KB (gzip 78 KB → 81 KB). ~+3 KB gz for the AI extensibility + settings-tab registry + two new components.Status labels
Everything new is marked Experimental, since 0.17.0. We'll flip the labels to Stable in the release PR once we're confident the shapes stand. No plugin should depend on an
Experimentalsignature across a major version.Test plan
OPTIONALLY DOWNLOAD THE PLUGIN SHOWCASED IN THE DEMO:
alcazaba-voice.zip
wp_register_desktop_settings_tab(); verify it appears in the OS Settings window without F5.aiCallable: true; callwp.desktop.ai.ask( '…', { tools: 'aiCallable' } ); verify the command fires.followUp: true; verifyres.messageis conversational (not the rawrun()return).capability; verify it's invisible to non-admins.wp_desktop_ai_search_errorfires and the agent can still answer.<wpd-section stack>adds a gap in a plugin settings tab; verify existing built-in sections render unchanged.<wpd-code>c</wpd-code>inside a page doesn't stealckeystrokes.<wpd-steps>with steps added/removed renumbers automatically.wp.desktop.ready(fn)called from a server-sync-injected script (i.e. afterwp-desktop.inithas fired) invokesfnsynchronously via microtask.ask()viaAbortController.abort(); verifyAbortErrorrejects and no ghost state lingers.🤖 Generated with Claude Code