Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Agents API sits between tool/action discovery and product-specific automation. I
## What Agents API Owns

- Agent registration and lookup.
- Materialized agent identity lookup contracts.
- Runtime message and result value objects.
- Agent package and package-artifact contracts.
- Agent memory store contracts and value objects.
Expand Down Expand Up @@ -64,6 +65,9 @@ Register agent definitions from inside a `wp_agents_api_init` callback. Reads su
- `wp_register_agent()` / `wp_get_agent()` / `wp_get_agents()` / `wp_has_agent()` / `wp_unregister_agent()`
- `WP_Agent`
- `WP_Agents_Registry`
- `AgentsAPI\Identity\MaterializedAgentIdentity`
- `AgentsAPI\Identity\MaterializedAgentIdentityStoreInterface`
- `wp_register_materialized_agent_identity_store()` / `wp_get_materialized_agent_identity()`
- `WP_Agent_Package*` value objects and artifact registry helpers
- `AgentsAPI\AI\AgentMessageEnvelope`
- `AgentsAPI\AI\AgentConversationCompaction`
Expand Down
3 changes: 3 additions & 0 deletions agents-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
define( 'AGENTS_API_PLUGIN_FILE', __FILE__ );

require_once AGENTS_API_PATH . 'src/Registry/class-wp-agent.php';
require_once AGENTS_API_PATH . 'src/Identity/MaterializedAgentIdentity.php';
require_once AGENTS_API_PATH . 'src/Identity/MaterializedAgentIdentityStoreInterface.php';
require_once AGENTS_API_PATH . 'src/Packages/class-wp-agent-package-artifact.php';
require_once AGENTS_API_PATH . 'src/Packages/class-wp-agent-package-artifact-type.php';
require_once AGENTS_API_PATH . 'src/Packages/class-wp-agent-package-artifacts-registry.php';
Expand All @@ -34,6 +36,7 @@
require_once AGENTS_API_PATH . 'src/Packages/class-wp-agent-package-adopter-interface.php';
require_once AGENTS_API_PATH . 'src/Registry/class-wp-agents-registry.php';
require_once AGENTS_API_PATH . 'src/Registry/register-agents.php';
require_once AGENTS_API_PATH . 'src/Identity/register-materialized-agent-identities.php';
require_once AGENTS_API_PATH . 'src/Packages/register-agent-package-artifacts.php';
require_once AGENTS_API_PATH . 'src/Transcripts/ConversationTranscriptStoreInterface.php';
require_once AGENTS_API_PATH . 'src/Runtime/AgentMessageEnvelope.php';
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test": [
"php tests/bootstrap-smoke.php",
"php tests/conversation-compaction-smoke.php",
"php tests/materialized-agent-identities-smoke.php",
"php tests/no-product-imports-smoke.php"
]
}
Expand Down
201 changes: 201 additions & 0 deletions src/Identity/MaterializedAgentIdentity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php
/**
* Materialized agent identity value object.
*
* @package AgentsAPI
*/

namespace AgentsAPI\Identity;

defined( 'ABSPATH' ) || exit;

/**
* Durable agent instance identity.
*/
final class MaterializedAgentIdentity {

/**
* Durable identity ID.
*
* @var int
*/
private int $id;

/**
* Registered agent slug.
*
* @var string
*/
private string $slug;

/**
* Human-readable label.
*
* @var string
*/
private string $label;

/**
* Owner WordPress user ID.
*
* @var int
*/
private int $owner_user_id;

/**
* Persisted agent configuration.
*
* @var array<string, mixed>
*/
private array $config;

/**
* Created timestamp, when known.
*
* @var string|null
*/
private ?string $created_at;

/**
* Updated timestamp, when known.
*
* @var string|null
*/
private ?string $updated_at;

/**
* Constructor.
*
* @param int $id Durable identity ID.
* @param string $slug Registered agent slug.
* @param string $label Human-readable label.
* @param int $owner_user_id Owner WordPress user ID.
* @param array<string, mixed> $config Persisted agent configuration.
* @param string|null $created_at Created timestamp, when known.
* @param string|null $updated_at Updated timestamp, when known.
*/
public function __construct( int $id, string $slug, string $label, int $owner_user_id, array $config = array(), ?string $created_at = null, ?string $updated_at = null ) {
$slug = sanitize_title( $slug );
if ( $id <= 0 ) {
throw new \InvalidArgumentException( 'Materialized agent identity ID must be positive.' );
}
if ( '' === $slug ) {
throw new \InvalidArgumentException( 'Materialized agent identity slug cannot be empty.' );
}
if ( $owner_user_id <= 0 ) {
throw new \InvalidArgumentException( 'Materialized agent identity owner user ID must be positive.' );
}

$this->id = $id;
$this->slug = $slug;
$this->label = '' !== $label ? $label : $slug;
$this->owner_user_id = $owner_user_id;
$this->config = $config;
$this->created_at = $created_at;
$this->updated_at = $updated_at;
}

/**
* Create an identity from a storage row.
*
* @param array<string, mixed> $row Storage row.
* @return self
*/
public static function from_array( array $row ): self {
$config = $row['config'] ?? array();
if ( is_string( $config ) && '' !== $config ) {
$decoded = json_decode( $config, true );
$config = is_array( $decoded ) ? $decoded : array();
}

return new self(
(int) ( $row['id'] ?? 0 ),
(string) ( $row['slug'] ?? '' ),
(string) ( $row['label'] ?? '' ),
(int) ( $row['owner_user_id'] ?? 0 ),
is_array( $config ) ? $config : array(),
isset( $row['created_at'] ) ? (string) $row['created_at'] : null,
isset( $row['updated_at'] ) ? (string) $row['updated_at'] : null
);
}

/**
* Get the durable identity ID.
*
* @return int
*/
public function get_id(): int {
return $this->id;
}

/**
* Get the registered agent slug.
*
* @return string
*/
public function get_slug(): string {
return $this->slug;
}

/**
* Get the human-readable label.
*
* @return string
*/
public function get_label(): string {
return $this->label;
}

/**
* Get the owner WordPress user ID.
*
* @return int
*/
public function get_owner_user_id(): int {
return $this->owner_user_id;
}

/**
* Get persisted agent configuration.
*
* @return array<string, mixed>
*/
public function get_config(): array {
return $this->config;
}

/**
* Get created timestamp, when known.
*
* @return string|null
*/
public function get_created_at(): ?string {
return $this->created_at;
}

/**
* Get updated timestamp, when known.
*
* @return string|null
*/
public function get_updated_at(): ?string {
return $this->updated_at;
}

/**
* Convert the identity to generic array form.
*
* @return array{id:int,slug:string,label:string,owner_user_id:int,config:array<string,mixed>,created_at:string|null,updated_at:string|null}
*/
public function to_array(): array {
return array(
'id' => $this->id,
'slug' => $this->slug,
'label' => $this->label,
'owner_user_id' => $this->owner_user_id,
'config' => $this->config,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
);
}
}
40 changes: 40 additions & 0 deletions src/Identity/MaterializedAgentIdentityStoreInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/**
* Materialized agent identity store contract.
*
* @package AgentsAPI
*/

namespace AgentsAPI\Identity;

defined( 'ABSPATH' ) || exit;

/**
* Read-only lookup surface for durable agent instances.
*/
interface MaterializedAgentIdentityStoreInterface {

/**
* Get a materialized agent identity by durable ID.
*
* @param int $id Durable identity ID.
* @return MaterializedAgentIdentity|null Identity, or null when not found.
*/
public function get_by_id( int $id ): ?MaterializedAgentIdentity;

/**
* Get a materialized agent identity by registered slug.
*
* @param string $slug Registered agent slug.
* @return MaterializedAgentIdentity|null Identity, or null when not found.
*/
public function get_by_slug( string $slug ): ?MaterializedAgentIdentity;

/**
* Get materialized agent identities owned by a WordPress user.
*
* @param int $owner_user_id Owner WordPress user ID.
* @return MaterializedAgentIdentity[] Identities owned by the user.
*/
public function get_by_owner_user_id( int $owner_user_id ): array;
}
95 changes: 95 additions & 0 deletions src/Identity/register-materialized-agent-identities.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/**
* Materialized agent identity helpers.
*
* @package AgentsAPI
*/

use AgentsAPI\Identity\MaterializedAgentIdentity;
use AgentsAPI\Identity\MaterializedAgentIdentityStoreInterface;

defined( 'ABSPATH' ) || exit;

if ( ! function_exists( 'wp_register_materialized_agent_identity_store' ) ) {
/**
* Register the backing store for durable agent identities.
*
* @param MaterializedAgentIdentityStoreInterface $store Backing store.
* @return void
*/
function wp_register_materialized_agent_identity_store( MaterializedAgentIdentityStoreInterface $store ): void {
$GLOBALS['wp_materialized_agent_identity_store'] = $store;
}
}

if ( ! function_exists( 'wp_get_materialized_agent_identity_store' ) ) {
/**
* Get the registered backing store for durable agent identities.
*
* @return MaterializedAgentIdentityStoreInterface|null Backing store, or null when none is registered.
*/
function wp_get_materialized_agent_identity_store(): ?MaterializedAgentIdentityStoreInterface {
$store = $GLOBALS['wp_materialized_agent_identity_store'] ?? null;
if ( function_exists( 'apply_filters' ) ) {
$store = apply_filters( 'wp_materialized_agent_identity_store', $store );
}

return $store instanceof MaterializedAgentIdentityStoreInterface ? $store : null;
}
}

if ( ! function_exists( 'wp_get_materialized_agent_identity_by_id' ) ) {
/**
* Get a materialized agent identity by durable ID.
*
* @param int $id Durable identity ID.
* @return MaterializedAgentIdentity|null Identity, or null when not found.
*/
function wp_get_materialized_agent_identity_by_id( int $id ): ?MaterializedAgentIdentity {
$store = wp_get_materialized_agent_identity_store();
if ( null === $store ) {
return null;
}

return $store->get_by_id( $id );
}
}

if ( ! function_exists( 'wp_get_materialized_agent_identity' ) ) {
/**
* Resolve a registered agent definition to its durable identity.
*
* @param string|WP_Agent $agent Agent slug or registered definition object.
* @return MaterializedAgentIdentity|null Identity, or null when not registered/materialized.
*/
function wp_get_materialized_agent_identity( $agent ): ?MaterializedAgentIdentity {
$slug = $agent instanceof WP_Agent ? $agent->get_slug() : sanitize_title( (string) $agent );
if ( '' === $slug || ! wp_has_agent( $slug ) ) {
return null;
}

$store = wp_get_materialized_agent_identity_store();
if ( null === $store ) {
return null;
}

return $store->get_by_slug( $slug );
}
}

if ( ! function_exists( 'wp_get_materialized_agent_identities_by_owner_user_id' ) ) {
/**
* Get materialized agent identities owned by a WordPress user.
*
* @param int $owner_user_id Owner WordPress user ID.
* @return MaterializedAgentIdentity[] Identities owned by the user.
*/
function wp_get_materialized_agent_identities_by_owner_user_id( int $owner_user_id ): array {
$store = wp_get_materialized_agent_identity_store();
if ( null === $store ) {
return array();
}

return $store->get_by_owner_user_id( $owner_user_id );
}
}
Loading
Loading