Skip to content
Merged
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
51 changes: 30 additions & 21 deletions homeboy.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,60 +9,69 @@
"metadata": {
"findings_count": 0
}
},
"lint:115f25907f28371418122a6879a5e3b487458182b6a658ba4e6fcf7f4c741bf5": {
"context_id": "data-machine",
"created_at": "2026-08-24T04:02:42Z",
"item_count": 0,
"known_fingerprints": [],
"metadata": {
"findings_count": 0
}
}
},
"changelog_target": "docs/CHANGELOG.md",
"remote_path": "wp-content/plugins/data-machine",
"extensions": {
"wordpress": {
"php": "8.2",
"node": "20",
"database_type": "sqlite",
"node": "20",
"php": "8.2",
"release_latest_branch": "release-latest",
"settings": {
"runtime_wordpress_version": "7.0",
"phpunit_no_tests": "fail",
"runtime_wordpress_version": "7.0",
"wp_codebox_phpunit_bootstrap_mode": "managed",
"wp_codebox_phpunit_config": "/wordpress/wp-content/plugins/data-machine/phpunit.sqlite.xml.dist"
}
}
},
"id": "data-machine",
"remote_path": "wp-content/plugins/data-machine",
"transforms": {
"drop_wp_ability_dead_guards": {
"description": "Drop dead class_exists('WP_Ability') and class_exists('WP_Abilities_Registry') guards. Plugin header requires WP 7.0 where both classes ship in core, so every guard on these symbols is unreachable. Ref: https://github.com/Extra-Chill/data-machine/issues/XXXX",
"rules": [
{
"id": "simple_guard",
"context": "file",
"description": "Remove standalone if ( ! class_exists( 'WP_Ability' ) ) { return; } blocks.",
"find": "(?s)\n\t\tif \\( ! class_exists\\( ?'WP_Ability' ?\\) \\) \\{\n\t\t\treturn;\n\t\t\\}\n",
"replace": "",
"files": "inc/Abilities/**/*.php",
"context": "file"
"find": "(?s)\n\t\tif \\( ! class_exists\\( ?'WP_Ability' ?\\) \\) \\{\n\t\t\treturn;\n\t\t\\}\n",
"id": "simple_guard",
"replace": ""
},
{
"id": "combined_guard_registered",
"context": "file",
"description": "Drop the WP_Ability half of 'if ( ! class_exists( WP_Ability ) || self::$registered ) { return; }' guards.",
"find": "(?s)if \\( ! class_exists\\( 'WP_Ability' \\) \\|\\| self::\\$registered \\) \\{\n\t+return;\n\t+\\}",
"replace": "if ( self::$$registered ) {\n\t\t\treturn;\n\t\t}",
"files": "inc/Abilities/**/*.php",
"context": "file"
"find": "(?s)if \\( ! class_exists\\( 'WP_Ability' \\) \\|\\| self::\\$registered \\) \\{\n\t+return;\n\t+\\}",
"id": "combined_guard_registered",
"replace": "if ( self::$$registered ) {\n\t\t\treturn;\n\t\t}"
},
{
"id": "ternary_registry_guard",
"context": "line",
"description": "Replace '(class_exists(WP_Abilities_Registry) ? WP_Abilities_Registry::get_instance() : null)' with a direct call — the ternary is unreachable.",
"find": "class_exists\\( 'WP_Abilities_Registry' \\) \\? \\\\WP_Abilities_Registry::get_instance\\(\\) : null",
"replace": "\\\\WP_Abilities_Registry::get_instance()",
"files": "inc/**/*.php",
"context": "line"
"find": "class_exists\\( 'WP_Abilities_Registry' \\) \\? \\\\WP_Abilities_Registry::get_instance\\(\\) : null",
"id": "ternary_registry_guard",
"replace": "\\\\WP_Abilities_Registry::get_instance()"
},
{
"id": "combined_guard_registered_engine",
"context": "file",
"description": "Same as combined_guard_registered but catches the one hit in inc/Engine/ (ResolvePendingActionAbility).",
"find": "(?s)if \\( ! class_exists\\( 'WP_Ability' \\) \\|\\| self::\\$registered \\) \\{\n\t+return;\n\t+\\}",
"replace": "if ( self::$$registered ) {\n\t\t\treturn;\n\t\t}",
"files": "inc/Engine/**/*.php",
"context": "file"
"find": "(?s)if \\( ! class_exists\\( 'WP_Ability' \\) \\|\\| self::\\$registered \\) \\{\n\t+return;\n\t+\\}",
"id": "combined_guard_registered_engine",
"replace": "if ( self::$$registered ) {\n\t\t\treturn;\n\t\t}"
}
]
}
Expand All @@ -85,4 +94,4 @@
"pattern": "\"version\":\\s*\"([0-9.]+)\""
}
]
}
}
10 changes: 5 additions & 5 deletions inc/Abilities/AgentMemoryAbilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ private function registerAbilities(): void {
* @return array Result.
*/
public static function getMemory( array $input ): array|\WP_Error {
$memory = self::resolveMemory( $input );
$memory = self::resolveMemory( $input );
if ( is_wp_error( $memory ) ) {
return $memory;
}
Expand Down Expand Up @@ -490,7 +490,7 @@ public static function writeSelfMemory( array $input ): array|\WP_Error {
* @return array Search results.
*/
public static function searchMemory( array $input ): array|\WP_Error {
$memory = self::resolveMemory( $input );
$memory = self::resolveMemory( $input );
if ( is_wp_error( $memory ) ) {
return $memory;
}
Expand Down Expand Up @@ -534,9 +534,9 @@ private static function memoryResult( array $result, string $code ): array|\WP_E
* @return AgentMemory|\WP_Error
*/
private static function resolveMemory( array $input ): AgentMemory|\WP_Error {
$user_id = (int) ( $input['user_id'] ?? 0 );
$agent_id = (int) ( $input['agent_id'] ?? 0 );
$filename = $input['file'] ?? 'MEMORY.md';
$user_id = (int) ( $input['user_id'] ?? 0 );
$agent_id = (int) ( $input['agent_id'] ?? 0 );
$filename = $input['file'] ?? 'MEMORY.md';
$principal = PermissionHelper::get_execution_principal();

if ( null !== $principal ) {
Expand Down
4 changes: 2 additions & 2 deletions inc/Abilities/Chat/AgentsChatHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public function execute( array $input ): array|WP_Error {
$agent_id = $identity ? $identity->agent_id : 0;

$calling_user_id = $this->resolveCallingUserId( $input );
$user_id = $this->resolveRuntimeUserId( $identity, (string) ( $input['agent'] ?? '' ), $input );
$user_id = $this->resolveRuntimeUserId( $identity, (string) ( $input['agent'] ?? '' ), $input );
if ( $user_id <= 0 ) {
return new WP_Error( 'no_user', __( 'No user context available.', 'data-machine' ), array( 'status' => 400 ) );
}
Expand Down Expand Up @@ -154,7 +154,7 @@ public function execute( array $input ): array|WP_Error {
'modes' => $modes,
'agent_id' => $agent_id,
'agent_slug' => $identity ? $identity->agent_slug : '',
'calling_user_id' => $calling_user_id,
'calling_user_id' => $calling_user_id,
'attachments' => $input['attachments'] ?? array(),
'client_context' => $client_context,
'tool_policy' => is_array( $input['tool_policy'] ?? null ) ? $input['tool_policy'] : null,
Expand Down
2 changes: 1 addition & 1 deletion inc/Abilities/Content/InsertContentAbility.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public static function execute( array $input ): array|\WP_Error {
/** Execute only from the pending-action dispatcher with a one-time receipt. */
public static function apply_pending_action( array $input, array $payload, array $receipt ): array|\WP_Error {
$input['preview'] = false;
$receipt_input = $input;
$receipt_input = $input;
unset( $receipt_input['preview'] );
$authorized = ContentActionHandlers::consume_receipt( 'insert_content', $receipt_input, $payload, $receipt );
if ( is_wp_error( $authorized ) ) {
Expand Down
44 changes: 22 additions & 22 deletions inc/Abilities/Email/EmailAbilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
class EmailAbilities {

private static bool $registered = false;
private ?array $activeMailbox = null;
private ?array $activeMailbox = null;

public function __construct() {
if ( self::$registered ) {
Expand Down Expand Up @@ -103,11 +103,11 @@ private function registerAbilities(): void {
'required' => array( 'uid' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'uid' => array(
'uid' => array(
'type' => 'integer',
'description' => __( 'Message UID to delete', 'data-machine' ),
),
'folder' => array(
'folder' => array(
'type' => 'string',
'default' => 'INBOX',
),
Expand Down Expand Up @@ -138,7 +138,7 @@ private function registerAbilities(): void {
'type' => 'object',
'required' => array( 'uid', 'destination' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'auth_ref' => self::authRefProperty(),
'uid' => array(
'type' => 'integer',
'description' => __( 'Message UID to move', 'data-machine' ),
Expand Down Expand Up @@ -179,20 +179,20 @@ private function registerAbilities(): void {
'required' => array( 'uid', 'flag' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'uid' => array(
'uid' => array(
'type' => 'integer',
'description' => __( 'Message UID', 'data-machine' ),
),
'flag' => array(
'flag' => array(
'type' => 'string',
'description' => __( 'IMAP flag: Seen, Flagged, Answered, Deleted, Draft', 'data-machine' ),
),
'action' => array(
'action' => array(
'type' => 'string',
'default' => 'set',
'description' => __( 'set or clear the flag', 'data-machine' ),
),
'folder' => array(
'folder' => array(
'type' => 'string',
'default' => 'INBOX',
),
Expand Down Expand Up @@ -223,7 +223,7 @@ private function registerAbilities(): void {
'type' => 'object',
'required' => array( 'search', 'destination' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'auth_ref' => self::authRefProperty(),
'search' => array(
'type' => 'string',
'description' => __( 'IMAP search criteria (e.g., FROM "github.com")', 'data-machine' ),
Expand Down Expand Up @@ -271,24 +271,24 @@ private function registerAbilities(): void {
'required' => array( 'search', 'flag' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'search' => array(
'search' => array(
'type' => 'string',
'description' => __( 'IMAP search criteria', 'data-machine' ),
),
'flag' => array(
'flag' => array(
'type' => 'string',
'description' => __( 'Flag: Seen, Flagged, Answered, Deleted, Draft', 'data-machine' ),
),
'action' => array(
'action' => array(
'type' => 'string',
'default' => 'set',
'description' => __( 'set or clear', 'data-machine' ),
),
'folder' => array(
'folder' => array(
'type' => 'string',
'default' => 'INBOX',
),
'max' => array(
'max' => array(
'type' => 'integer',
'default' => 500,
),
Expand Down Expand Up @@ -322,15 +322,15 @@ private function registerAbilities(): void {
'required' => array( 'search' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'search' => array(
'search' => array(
'type' => 'string',
'description' => __( 'IMAP search criteria', 'data-machine' ),
),
'folder' => array(
'folder' => array(
'type' => 'string',
'default' => 'INBOX',
),
'max' => array(
'max' => array(
'type' => 'integer',
'default' => 100,
'description' => __( 'Maximum messages to delete (safety limit, lower default)', 'data-machine' ),
Expand Down Expand Up @@ -365,11 +365,11 @@ private function registerAbilities(): void {
'required' => array( 'uid' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'uid' => array(
'uid' => array(
'type' => 'integer',
'description' => __( 'Message UID to unsubscribe from', 'data-machine' ),
),
'folder' => array(
'folder' => array(
'type' => 'string',
'default' => 'INBOX',
),
Expand Down Expand Up @@ -402,15 +402,15 @@ private function registerAbilities(): void {
'required' => array( 'search' ),
'properties' => array(
'auth_ref' => self::authRefProperty(),
'search' => array(
'search' => array(
'type' => 'string',
'description' => __( 'IMAP search criteria', 'data-machine' ),
),
'folder' => array(
'folder' => array(
'type' => 'string',
'default' => 'INBOX',
),
'max' => array(
'max' => array(
'type' => 'integer',
'default' => 20,
'description' => __( 'Max unique senders to unsubscribe from (deduped by sender)', 'data-machine' ),
Expand Down
4 changes: 2 additions & 2 deletions inc/Abilities/Engine/ScheduleNextStepAbility.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,13 @@ public function scheduleActionAtomically( int $job_id, string $flow_step_id ): i
}

$action_args = $this->actionArgs( $job_id, $flow_step_id );
$action = new \ActionScheduler_Action(
$action = new \ActionScheduler_Action(
'datamachine_execute_step',
$action_args,
new \ActionScheduler_SimpleSchedule( as_get_datetime_object( time() ) ),
'data-machine'
);
$action_id = $this->saveAtomicAction( $store, $action );
$action_id = $this->saveAtomicAction( $store, $action );
if ( $action_id <= 0
|| $action_id <= (int) $previous_max_id
|| \ActionScheduler_Store::STATUS_PENDING !== $store->get_status( $action_id )
Expand Down
2 changes: 1 addition & 1 deletion inc/Abilities/Fetch/FetchEmailAbility.php
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ private function fetchMessage( $connection, int $uid, array $config ): ?array {
*/
private function fetchBody( $connection, int $uid, bool $peek ): string {
$fetch_flags = FT_UID | ( $peek ? FT_PEEK : 0 );
$structure = imap_fetchstructure( $connection, $uid, FT_UID );
$structure = imap_fetchstructure( $connection, $uid, FT_UID );
if ( ! $structure ) {
return '';
}
Expand Down
15 changes: 8 additions & 7 deletions inc/Abilities/Publish/SendEmailAbility.php
Original file line number Diff line number Diff line change
Expand Up @@ -516,20 +516,20 @@ private function verifiedMailboxContext( array $config ): array|null|\WP_Error {
if ( ! is_array( $grant ) || empty( $grant['signature'] ) ) {
return new \WP_Error( 'email_queue_grant_invalid', 'Queued mailbox authorization is invalid.', array( 'status' => 403 ) );
}
$user_id = absint( $grant['user_id'] ?? 0 );
$agent_id = absint( $grant['agent_id'] ?? 0 );
$user_id = absint( $grant['user_id'] ?? 0 );
$agent_id = absint( $grant['agent_id'] ?? 0 );
$issued_at = absint( $grant['issued_at'] ?? 0 );
$nonce = (string) ( $grant['nonce'] ?? '' );
$payload = $this->mailboxGrantPayload( $config, $user_id, $agent_id, $issued_at, $nonce );
$expected = hash_hmac( 'sha256', $payload, wp_salt( 'auth' ) );
$expected = hash_hmac( 'sha256', $payload, wp_salt( 'auth' ) );
if ( ! hash_equals( $expected, (string) $grant['signature'] ) ) {
return new \WP_Error( 'email_queue_grant_invalid', 'Queued mailbox authorization is invalid.', array( 'status' => 403 ) );
}
return array(
'user_id' => $user_id,
'agent_id' => $agent_id,
'token_id' => absint( $grant['token_id'] ?? 0 ),
'issuer_type' => (string) ( $grant['issuer_type'] ?? '' ),
'user_id' => $user_id,
'agent_id' => $agent_id,
'token_id' => absint( $grant['token_id'] ?? 0 ),
'issuer_type' => (string) ( $grant['issuer_type'] ?? '' ),
'legacy_sender' => ! empty( $grant['legacy_sender'] ),
);
}
Expand All @@ -539,6 +539,7 @@ private function mailboxGrantPayload( array $config, int $user_id, int $agent_id
unset( $payload['_attempt'], $payload['_mailbox_grant'] );
$payload = $this->canonicalizeGrantValue( $payload );
return implode( '|', array(
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Canonical internal data is hashed for an HMAC and never unserialized.
hash( 'sha256', serialize( $payload ) ),
(string) $user_id,
(string) $agent_id,
Expand Down
Loading
Loading