Skip to content

Commit c897b4d

Browse files
committed
AI Sidebar: use provider gate for editor surfaces
1 parent e367330 commit c897b4d

2 files changed

Lines changed: 83 additions & 90 deletions

File tree

projects/plugins/jetpack/extensions/plugins/ai-assistant-plugin/ai-sidebar/class-jetpack-ai-sidebar.php

Lines changed: 20 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -427,23 +427,13 @@ private static function is_jetpack_ai_sidebar_preview_enabled(): bool {
427427
return (bool) apply_filters( 'jetpack_ai_sidebar_enabled', $enabled );
428428
}
429429

430-
/**
431-
* Whether the sidebar surface should be exposed for this request.
432-
*
433-
* @return bool
434-
*/
435-
private static function should_expose_sidebar(): bool {
436-
return self::is_post_editor() && self::should_expose_provider();
437-
}
438-
439430
/**
440431
* Whether the Jetpack AI provider bundle should be exposed for this request.
441432
*
442-
* This is intentionally broader than the sidebar data gate: page/site editor
443-
* can consume Jetpack AI tools through another host/agent, without Jetpack AI
444-
* setting the active Agents Manager agent ID. It still stays scoped to the same
445-
* internal rollout gate as Generate Feedback: preview enabled, internal
446-
* testing environment, and AI features available.
433+
* This is scoped to the supported editor surfaces: post editor, page editor,
434+
* and site editor. It stays behind the same internal rollout gate as Generate
435+
* Feedback: preview enabled, internal testing environment, and AI features
436+
* available.
447437
*
448438
* @return bool
449439
*/
@@ -503,7 +493,7 @@ private static function get_jetpack_ai_sidebar_preview_config(): array {
503493
public static function is_toolbar_button_enabled(): bool {
504494
$preview_config = self::get_jetpack_ai_sidebar_preview_config();
505495

506-
return self::should_expose_sidebar()
496+
return self::should_expose_provider()
507497
&& true === ( $preview_config['features']['blockToolbarButton'] ?? false );
508498
}
509499

@@ -535,7 +525,7 @@ public static function add_agents_manager_data( $data ) {
535525
return $data;
536526
}
537527

538-
$fields = self::get_agents_manager_data_fields();
528+
$fields = self::get_agents_manager_data_fields( $data );
539529
if ( ! $fields ) {
540530
return $data;
541531
}
@@ -550,47 +540,24 @@ public static function add_agents_manager_data( $data ) {
550540
return $data;
551541
}
552542

553-
/**
554-
* Fields Jetpack contributes when Jetpack AI Sidebar owns the post editor.
555-
*
556-
* @return array
557-
*/
558-
private static function get_sidebar_am_fields(): array {
559-
$fields = self::get_provider_am_fields();
560-
$fields['agentId'] = AI_SIDEBAR_AGENT_ID;
561-
562-
return $fields;
563-
}
564-
565-
/**
566-
* Fields Jetpack contributes when another host owns Agents Manager.
567-
*
568-
* The page/site editor can consume Jetpack provider suggestions and tools, but
569-
* must keep the host-selected agent ID (for example Dolly).
570-
*
571-
* @return array
572-
*/
573-
private static function get_provider_am_fields(): array {
574-
return array(
575-
'jetpackAiSidebar' => self::get_jetpack_ai_sidebar_preview_config(),
576-
);
577-
}
578-
579543
/**
580544
* Fields Jetpack should add to `agentsManagerData` for the current screen.
581545
*
546+
* @param array $data Existing Agents Manager data.
582547
* @return array
583548
*/
584-
private static function get_agents_manager_data_fields(): array {
549+
private static function get_agents_manager_data_fields( array $data = array() ): array {
585550
if ( ! self::should_expose_provider() ) {
586551
return array();
587552
}
588553

589-
if ( self::should_expose_sidebar() ) {
590-
return self::get_sidebar_am_fields();
554+
$fields = array();
555+
if ( empty( $data['agentId'] ) ) {
556+
$fields['agentId'] = AI_SIDEBAR_AGENT_ID;
591557
}
558+
$fields['jetpackAiSidebar'] = self::get_jetpack_ai_sidebar_preview_config();
592559

593-
return self::get_provider_am_fields();
560+
return $fields;
594561
}
595562

596563
/**
@@ -639,7 +606,8 @@ public static function maybe_patch_jetpack_ai_sidebar_preview_data(): void {
639606
}
640607

641608
// Build the assignments from the same field source as the data filter so the
642-
// two emit paths cannot drift.
609+
// two emit paths cannot drift. agentId is guarded client-side because the
610+
// externally emitted payload may already define the active agent.
643611
$fields = self::get_agents_manager_data_fields();
644612
if ( ! $fields ) {
645613
return;
@@ -649,6 +617,11 @@ public static function maybe_patch_jetpack_ai_sidebar_preview_data(): void {
649617
foreach ( $fields as $key => $value ) {
650618
$assignment_value = wp_json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP );
651619

620+
if ( 'agentId' === $key ) {
621+
$assignments .= ' if ( ! agentsManagerData.agentId ) { agentsManagerData.agentId = ' . $assignment_value . '; }';
622+
continue;
623+
}
624+
652625
$assignments .= ' agentsManagerData.' . $key . ' = ' . $assignment_value . ';';
653626
}
654627

@@ -689,22 +662,6 @@ private static function is_block_editor(): bool {
689662
return $screen && $screen->is_block_editor();
690663
}
691664

692-
/**
693-
* Check if the current screen is the post block editor.
694-
*
695-
* @return bool
696-
*/
697-
private static function is_post_editor(): bool {
698-
if ( ! self::is_block_editor() ) {
699-
return false;
700-
}
701-
702-
$screen = get_current_screen();
703-
return $screen instanceof \WP_Screen
704-
&& 'post' === $screen->base
705-
&& 'post' === $screen->post_type;
706-
}
707-
708665
/**
709666
* Check if the current screen can consume the Jetpack AI provider bundle.
710667
*

projects/plugins/jetpack/tests/php/extensions/plugins/ai-sidebar/Jetpack_AI_Sidebar_Test.php

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -624,11 +624,13 @@ static function ( $features ) {
624624
}
625625

626626
/**
627-
* Test that the toolbar button feature stays unavailable outside the post editor.
627+
* Test that the toolbar button feature is available in the page editor.
628628
*/
629-
public function test_register_toolbar_button_extension_skips_page_editor() {
629+
public function test_register_toolbar_button_extension_marks_feature_available_in_page_editor() {
630630
$this->set_page_block_editor_screen();
631+
$_SERVER['A8C_PROXIED_REQUEST'] = '1';
631632
$this->make_legacy_block_toolbar_extensions_available();
633+
$this->enable_sidebar_extension_availability_checks();
632634
add_filter(
633635
'jetpack_ai_sidebar_preview_features',
634636
static function ( $features ) {
@@ -639,7 +641,30 @@ static function ( $features ) {
639641

640642
Jetpack_AI_Sidebar::register_toolbar_button_extension();
641643

642-
$this->assertFalse( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ) );
644+
$this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ) );
645+
$this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) );
646+
$this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) );
647+
}
648+
649+
/**
650+
* Test that the toolbar button feature is available in the site editor.
651+
*/
652+
public function test_register_toolbar_button_extension_marks_feature_available_in_site_editor() {
653+
$this->set_site_editor_screen();
654+
$_SERVER['A8C_PROXIED_REQUEST'] = '1';
655+
$this->make_legacy_block_toolbar_extensions_available();
656+
$this->enable_sidebar_extension_availability_checks();
657+
add_filter(
658+
'jetpack_ai_sidebar_preview_features',
659+
static function ( $features ) {
660+
$features['blockToolbarButton'] = true;
661+
return $features;
662+
}
663+
);
664+
665+
Jetpack_AI_Sidebar::register_toolbar_button_extension();
666+
667+
$this->assertTrue( \Jetpack_Gutenberg::is_available( AiAssistantPlugin\AI_SIDEBAR_TOOLBAR_BUTTON_EXTENSION ) );
643668
$this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-support' ) );
644669
$this->assertTrue( \Jetpack_Gutenberg::is_available( 'ai-assistant-image-extension' ) );
645670
}
@@ -949,9 +974,9 @@ public function test_add_agents_manager_data_allows_preview_without_ai_editorial
949974
}
950975

951976
/**
952-
* Jetpack AI Sidebar owns the post editor agent.
977+
* Existing host agent IDs are preserved.
953978
*/
954-
public function test_add_agents_manager_data_overrides_existing_post_editor_agent_id() {
979+
public function test_add_agents_manager_data_preserves_existing_agent_id() {
955980
$this->set_block_editor_screen();
956981
$_SERVER['A8C_PROXIED_REQUEST'] = '1';
957982

@@ -962,39 +987,58 @@ public function test_add_agents_manager_data_overrides_existing_post_editor_agen
962987
)
963988
);
964989

965-
$this->assertSame( 'wp-orchestrator', $data['agentId'] );
990+
$this->assertSame( 'dolly', $data['agentId'] );
966991
$this->assertSame( true, $data['jetpackAiSidebar']['enabled'] );
967992
}
968993

969994
/**
970995
* Platform-emitted provider data is exposed in the Simple internal-testing
971-
* page editor without changing the agent.
996+
* page editor and sets the default agent when upstream has not selected one.
972997
*/
973-
public function test_add_agents_manager_data_adds_simple_internal_testing_provider_config_in_page_editor_without_overriding_agent() {
998+
public function test_add_agents_manager_data_adds_simple_internal_testing_provider_config_in_page_editor() {
974999
$this->set_page_block_editor_screen();
9751000
$this->simulate_wpcom_simple();
9761001
$_SERVER['A8C_PROXIED_REQUEST'] = '1';
9771002

9781003
$data = Jetpack_AI_Sidebar::add_agents_manager_data( array( 'sectionName' => 'gutenberg' ) );
9791004

980-
$this->assertArrayNotHasKey( 'agentId', $data );
1005+
$this->assertSame( 'wp-orchestrator', $data['agentId'] );
9811006
$this->assertSame( true, $data['jetpackAiSidebar']['enabled'] );
9821007
$this->assertSame( true, $data['jetpackAiSidebar']['features']['generateFeedback'] );
9831008
$this->assertSame( true, $data['jetpackAiSidebar']['features']['optimizeTitleSuggestion'] );
9841009
}
9851010

1011+
/**
1012+
* Platform-emitted page editor data keeps an existing host-selected agent.
1013+
*/
1014+
public function test_add_agents_manager_data_preserves_existing_agent_id_in_page_editor() {
1015+
$this->set_page_block_editor_screen();
1016+
$this->simulate_wpcom_simple();
1017+
$_SERVER['A8C_PROXIED_REQUEST'] = '1';
1018+
1019+
$data = Jetpack_AI_Sidebar::add_agents_manager_data(
1020+
array(
1021+
'sectionName' => 'gutenberg',
1022+
'agentId' => 'dolly',
1023+
)
1024+
);
1025+
1026+
$this->assertSame( 'dolly', $data['agentId'] );
1027+
$this->assertSame( true, $data['jetpackAiSidebar']['enabled'] );
1028+
}
1029+
9861030
/**
9871031
* Platform-emitted provider data is exposed in the Simple internal-testing
988-
* site editor without changing the agent.
1032+
* site editor and sets the default agent when upstream has not selected one.
9891033
*/
990-
public function test_add_agents_manager_data_adds_simple_internal_testing_provider_config_in_site_editor_without_overriding_agent() {
1034+
public function test_add_agents_manager_data_adds_simple_internal_testing_provider_config_in_site_editor() {
9911035
$this->set_site_editor_screen();
9921036
$this->simulate_wpcom_simple();
9931037
$_SERVER['A8C_PROXIED_REQUEST'] = '1';
9941038

9951039
$data = Jetpack_AI_Sidebar::add_agents_manager_data( array( 'sectionName' => 'site-editor' ) );
9961040

997-
$this->assertArrayNotHasKey( 'agentId', $data );
1041+
$this->assertSame( 'wp-orchestrator', $data['agentId'] );
9981042
$this->assertSame( true, $data['jetpackAiSidebar']['enabled'] );
9991043
$this->assertSame( true, $data['jetpackAiSidebar']['features']['generateFeedback'] );
10001044
$this->assertSame( true, $data['jetpackAiSidebar']['features']['optimizeTitleSuggestion'] );
@@ -1051,12 +1095,8 @@ public function test_patch_jetpack_ai_sidebar_preview_data_sets_fields_when_am_e
10511095

10521096
$inline_script = $this->get_agents_manager_inline_script();
10531097

1054-
$this->assertStringNotContainsString(
1055-
'if ( ! agentsManagerData.agentId )',
1056-
$inline_script
1057-
);
10581098
$this->assertStringContainsString(
1059-
'agentsManagerData.agentId = "wp-orchestrator"',
1099+
'if ( ! agentsManagerData.agentId ) { agentsManagerData.agentId = "wp-orchestrator"; }',
10601100
$inline_script
10611101
);
10621102
$this->assertStringContainsString(
@@ -1086,22 +1126,22 @@ public function test_patch_jetpack_ai_sidebar_preview_data_noop_when_am_not_enqu
10861126
}
10871127

10881128
/**
1089-
* The external AM payload patch adds internal-testing provider config in the page editor without changing the agent.
1129+
* The external AM payload patch adds provider config in the page editor and guards the default agent assignment.
10901130
*/
1091-
public function test_patch_jetpack_ai_sidebar_preview_data_adds_internal_testing_provider_config_in_page_editor_without_overriding_agent() {
1131+
public function test_patch_jetpack_ai_sidebar_preview_data_adds_internal_testing_provider_config_in_page_editor() {
10921132
$this->set_page_block_editor_screen();
10931133
$this->simulate_wpcom_platform();
10941134
$this->cache_sidebar_asset_data();
10951135
$_SERVER['A8C_PROXIED_REQUEST'] = '1';
10961136
wp_enqueue_script( 'agents-manager', 'https://example.com/am.js', array(), '1.0', true );
1097-
wp_add_inline_script( 'agents-manager', 'const agentsManagerData = { sectionName: "gutenberg" };', 'before' );
1137+
wp_add_inline_script( 'agents-manager', 'const agentsManagerData = { sectionName: "gutenberg", agentId: "dolly" };', 'before' );
10981138

10991139
Jetpack_AI_Sidebar::maybe_patch_jetpack_ai_sidebar_preview_data();
11001140

11011141
$inline_script = $this->get_agents_manager_inline_script();
11021142

1103-
$this->assertStringNotContainsString(
1104-
'agentsManagerData.agentId',
1143+
$this->assertStringContainsString(
1144+
'if ( ! agentsManagerData.agentId ) { agentsManagerData.agentId = "wp-orchestrator"; }',
11051145
$inline_script
11061146
);
11071147
$this->assertStringContainsString(
@@ -1142,12 +1182,8 @@ function () {
11421182

11431183
$inline_script = $this->get_agents_manager_inline_script();
11441184

1145-
$this->assertStringNotContainsString(
1146-
'if ( ! agentsManagerData.agentId )',
1147-
$inline_script
1148-
);
11491185
$this->assertStringContainsString(
1150-
'agentsManagerData.agentId = "wp-orchestrator"',
1186+
'if ( ! agentsManagerData.agentId ) { agentsManagerData.agentId = "wp-orchestrator"; }',
11511187
$inline_script
11521188
);
11531189
$this->assertStringContainsString(

0 commit comments

Comments
 (0)