Skip to content

refactor(api): tranche 4 — migrate Agents admin calls to /wp-abilities/v1 and delete wrapper routes - #3473

Merged
chubes4 merged 13 commits into
mainfrom
refactor/3456-tranche-4-agents
Sep 8, 2026
Merged

refactor(api): tranche 4 — migrate Agents admin calls to /wp-abilities/v1 and delete wrapper routes#3473
chubes4 merged 13 commits into
mainfrom
refactor/3456-tranche-4-agents

Conversation

@chubes4

@chubes4 chubes4 commented Sep 8, 2026

Copy link
Copy Markdown
Member

Part of #3456 (tranche 4: agents family)

Summary

inc/Api/Agents.php (981 lines, 0 ability calls) is deleted. The React admin now executes the registered REST-visible agent abilities through /wp-abilities/v1/abilities/datamachine/<slug>/run via the existing executeAbility() helper, exactly like tranches 1–3.

Net: −646 total lines; −629 production PHP (inc/Api/Agents.php 981 → 0; AgentAbilities.php +82; AgentAccessAbilities.php +268 new).

Route table

Retired route (datamachine/v1) Ability (datamachine/*) Consumer changed Deleted lines
GET /agents list-agents shared/queries/agents.js (useAgents → AgentSwitcher, LogsAgentTabs), Pages/Agent/.../api/agents.js::fetchAgents (useManageAgents → AgentListTab) route + handle_list
POST /agents create-agent api/agents.js::createAgent, shared/CreateAgentModal.jsx route + handle_create
GET /agents/me get-agent with me: true (new input option; no consumer existed in the admin — kept for external token integrations) none route + handle_me
GET /agents/{agent} + GET /agents/{agent_id} compat loop get-agent api/agents.js::fetchAgent (useAgent → AgentEditView) 2× route + handle_get + compat loop
PUT /agents/{agent} + compat loop update-agent api/agents.js::updateAgent (useUpdateAgent → AgentEditView) 2× route + handle_update
DELETE /agents/{agent} + compat loop delete-agent api/agents.js::deleteAgent (useDeleteAgent) 2× route + handle_delete
GET/POST /agents/{id}/access + compat loop manage-agent-access (action: list|grant) — new ability api/agents.js::fetchAgentAccess/grantAccess (useAgentAccess, useGrantAccess → AgentEditView) 2× route + handle_list_access/handle_grant_access
DELETE /agents/{id}/access/{user_id} + compat loop manage-agent-access (action: revoke) api/agents.js::revokeAccess (useRevokeAccess) 2× route + handle_revoke_access
GET/POST /agents/{id}/tokens + compat loop list-agent-tokens / create-agent-token none (no admin UI consumer; abilities already existed) 2× route + handle_list_tokens/handle_create_token
DELETE /agents/{id}/tokens/{key} + compat loop revoke-agent-token none 2× route + handle_revoke_token

/agent/authorize* and /agent/auth/* (browser authorization flow, inc/Core/Auth/) are not part of this tranche — they stay as transport routes. docs/api/endpoints/agents.md was rescoped rather than deleted to keep documenting those live routes (same treatment files.md got in tranche 3b).

Access routes decision

GET/POST /agents/{id}/access and DELETE .../access/{user_id} call the AgentAccess store directly. agents-api (Automattic/agents-api) only exposes the read side as abilities today: agents/can-access-agent and agents/list-accessible-agents (src/Auth/register-agent-access-abilities.php); there is no grant/revoke/list-for-agent ability. Per the tranche plan I added one DM ability, datamachine/manage-agent-access (action: list|grant|revoke, permission manage_agents — same as the retired routes, show_in_rest: true, inc/Abilities/AgentAccessAbilities.php, new class modeled on AgentTokenAbilities) rather than forking the access store. Grant/revoke belong upstream in agents-api: Automattic/agents-api#537 tracks that; the DM ability is the interim.

Defaults moved (parity ledger)

  • owner_id => get_current_user_id() (POST /agents): moved into the ability (AgentAbilities::createAgent defaults owner_id to PermissionHelper::acting_user_id() when absent). This is permission-relevant (it decides who owns the created agent), so it lives in the ability where non-admin self-service scoping already lives; CLI --owner behavior unchanged.
  • include_role => true (GET /agents): dropped, not ported. No admin JS reads user_role/is_owner/description (grepped inc/Core/Admin); the wrapper's "attaches timestamps" comment was stale — it never attached them, so AgentListTab's created_at column was already empty before and after this change.
  • role => viewer, label => '', config => [], delete_files => false: already defaulted in the abilities or the JS callers — no changes needed.
  • /agents/me payload (agent_*, owner_id, site_url, site_name): folded into get-agent me: true output (agent object + site key, both declared in the output schema). Permission note: get-agent requires manage_agents (agent tokens pass through the agent_can capability ceiling), slightly tighter than the old route's is_user_logged_in(); acceptable because no in-repo consumer relied on the looser gate.

Verification

  • php -l on all six changed PHP files: clean
  • npm ci && npm run build (wp-scripts/webpack): compiled successfully (build output not committed)
  • npx eslint on the three changed JS/JSX files: clean
  • New tests/Unit/Abilities/AgentsAbilityRestTest.php runs the full surface as admin through the core REST runner: CRUD round trip (create with owner default → get → update name+config → list w/ role → delete w/ files → 404 after), me resolution + site metadata, token create/list/revoke (+404 double-revoke), access grant/list/revoke (+owner-revoke 400, unknown-agent 404). Replaces the deleted tests/Unit/Api/Agents* route tests; AllAbilitiesRegisteredTest gains datamachine/manage-agent-access.

Manual admin smoke checklist (Agents page)

  • Agent page list loads (table renders, Created column unchanged — empty as before)
  • Create agent via "Add New" and via the header AgentSwitcher "+ Create Agent" → appears in list, auto-selected
  • Rename agent / update config in AgentEditView → "Agent updated." success, detail refetches
  • Delete agent (with and without files) → gone from list and switcher
  • Tokens tab: create token (raw token shown once), list, revoke
  • Access: grant user (role operator), list shows grant with display name, revoke; owner revoke blocked
  • Header AgentSwitcher on every DM page shows the agent list and selection persists
  • External smoke: POST /wp-abilities/v1/abilities/datamachine/get-agent/run with {"input":{"me":true}} + agent bearer token returns identity + site

AI-generated with Claude Code via Homeboy worktree

homeboy-ci Bot added 3 commits September 8, 2026 01:36
…gent, owner default contract test, PHPStan stub ignores
…interim manage-agent-access ability

The write side of agent access belongs in the Agents API substrate, not
patched over from Data Machine. Automattic/agents-api#537 adds
agents/grant-agent-access, revoke-agent-access, and list-agent-users; until
that lands the three datamachine/v1 access routes stay and the admin keeps
calling them. Every other Agents route is deleted as planned.
@chubes4

chubes4 commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

Reworked per maintainer: the interim datamachine/manage-agent-access ability is removed. Papering over a substrate gap from the consumer is the wrong layer — Agents API was extracted from this codebase; the fix goes there. Automattic/agents-api#537 is being implemented now (agents/grant-agent-access, revoke-agent-access, list-agent-users over the existing WP_Agent_Access_Store contract). Until it merges, the three /agents/{id}/access routes stay in a 308-line Agents.php and the admin keeps calling them; everything else in the family is deleted as planned. Follow-up after #537: switch the admin to the agents/* slugs and delete Agents.php entirely. Net this PR: −660.

…aseline smoke-stub shadowing of WP_Agent_Access_Grant
…ugh agents-api abilities

Automattic/agents-api#538 shipped agents/grant-agent-access, revoke-agent-access,
and list-agent-users over WP_Agent_Access_Store, which Data Machine already
implements via AgentAccessStoreAdapter. The admin Access panel now calls those
slugs (keyed by agent slug, per the registry) and enriches grants with WP user
display fields client-side via /wp/v2/users. executeAbility() accepts a
qualified slug so non-datamachine namespaces work without a second helper.

Reverts the out-of-scope AgentsCommand/AgentAccess touches from the previous
fixup; they dragged 28 pre-existing smoke-stub PHPStan findings (#3474) into
changed-file scope.
@chubes4

chubes4 commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

Superseded the retained-routes plan: Automattic/agents-api#538 merged (agents/grant-agent-access, revoke-agent-access, list-agent-users), so inc/Api/Agents.php is now deleted outright — no retained routes, no interim ability. The admin Access panel calls the agents/* slugs keyed by agent slug and enriches grants with /wp/v2/users client-side (presentation, not domain). executeAbility() accepts a qualified slug for non-datamachine/ namespaces.

Dependency note: DM pins wordpress/agents-api: dev-main, so CI and fresh installs pick up #538 immediately. The bundled release for production should pin an agents-api tag that includes 5a47121 — v0.8.0 predates it, so agents-api needs a patch release (v0.8.1) before this ships in a DM release. Net this PR: −905.

@chubes4
chubes4 merged commit 31befb6 into main Sep 8, 2026
30 checks passed
@chubes4
chubes4 deleted the refactor/3456-tranche-4-agents branch September 8, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant