sidebar = $sidebar; } /** * Handle get conversations request. * * Lists all conversations for the current user, optionally filtered by post. * * @since 0.1.4 * @param WP_REST_Request $request REST request. * @return WP_REST_Response|WP_Error */ public function handle_get_conversations( $request ) { $manager = WP_Agentic_Writer_Conversation_Manager::get_instance(); $status = sanitize_text_field( $request->get_param( 'status' ) ?: 'active' ); $limit = (int) $request->get_param( 'limit' ) ?: 20; $post_id = (int) $request->get_param( 'post_id' ) ?: 0; // If post_id is specified, check authorization before returning session. if ( $post_id > 0 ) { // Authorization: User must be able to edit this post. if ( ! current_user_can( 'edit_post', $post_id ) ) { return new WP_Error( 'forbidden', __( 'You do not have permission to access this post.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } $sessions = $manager->get_sessions_for_post( $post_id ); return new WP_REST_Response( [ 'sessions' => $sessions, 'count' => count( $sessions ), ], 200, ); } if ( $request->get_param( 'uncompleted' ) ) { $sessions = $manager->get_uncompleted_sessions( $limit ); } else { $sessions = $manager->get_user_sessions( $status, $limit ); } return new WP_REST_Response( [ 'sessions' => $sessions, 'count' => count( $sessions ), ], 200, ); } /** * Handle create conversation request. * * @since 0.1.4 * @param WP_REST_Request $request REST request. * @return WP_REST_Response|WP_Error */ public function handle_create_conversation( $request ) { $params = $request->get_json_params(); $manager = WP_Agentic_Writer_Conversation_Manager::get_instance(); $post_id = isset( $params['post_id'] ) ? (int) $params['post_id'] : 0; $focus_keyword = isset( $params['focus_keyword'] ) ? sanitize_text_field( $params['focus_keyword'] ) : ''; $title = isset( $params['title'] ) ? sanitize_text_field( $params['title'] ) : ''; // Authorization: If linking to a post, check edit permission. if ( $post_id > 0 && ! current_user_can( 'edit_post', $post_id ) ) { return new WP_Error( 'forbidden', __( 'You do not have permission to create a session for this post.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } if ( '' === $title && $post_id > 0 ) { $post = get_post( $post_id ); $base_title = $post ? sanitize_text_field( $post->post_title ) : ''; if ( '' === $base_title ) { $base_title = 'Conversation'; } $title = sprintf( '%s - %s', $base_title, current_time( 'Y-m-d H:i' ) ); } $session_id = $manager->create_session( [ 'post_id' => $post_id, 'focus_keyword' => $focus_keyword, 'title' => $title, ] ); if ( is_wp_error( $session_id ) ) { return $session_id; } $session = $manager->get_session( $session_id ); // MEMANTO: New session created. do_action( 'wpaw_memanto_session_start', $session_id, $post_id, get_current_user_id(), ); return new WP_REST_Response( $session, 201 ); } /** * Handle get single conversation request. * * @since 0.1.4 * @param WP_REST_Request $request REST request. * @return WP_REST_Response|WP_Error */ public function handle_get_conversation( $request ) { $session_id = sanitize_text_field( $request->get_param( 'session_id' ) ); $manager = WP_Agentic_Writer_Conversation_Manager::get_instance(); // Check authorization. if ( ! $manager->current_user_can_access( $session_id ) ) { return new WP_Error( 'forbidden', __( 'You do not have permission to access this conversation.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } $session = $manager->get_session( $session_id ); if ( ! $session ) { return new WP_Error( 'not_found', __( 'Conversation not found.', 'wp-agentic-writer' ), [ 'status' => 404 ], ); } $session = $this->hydrate_session_plan_messages( $session ); return new WP_REST_Response( $session, 200 ); } /** * Restore rich plan UI payloads for sessions that only stored a text summary. * * @since 0.2.2 * @param array $session Conversation session. * @return array */ private function hydrate_session_plan_messages( $session ) { if ( ! is_array( $session ) ) { return $session; } $post_id = isset( $session['post_id'] ) ? (int) $session['post_id'] : 0; if ( $post_id <= 0 || empty( $session['messages'] ) || ! is_array( $session['messages'] ) ) { return $session; } foreach ( $session['messages'] as $message ) { if ( isset( $message['type'] ) && 'plan' === $message['type'] && ! empty( $message['plan'] ) ) { return $session; } } $plan = get_post_meta( $post_id, '_wpaw_plan', true ); if ( ! is_array( $plan ) ) { return $session; } foreach ( $session['messages'] as $index => $message ) { $content = isset( $message['content'] ) ? (string) $message['content'] : ''; $role = isset( $message['role'] ) ? (string) $message['role'] : ''; if ( 'assistant' !== $role || false === strpos( $content, 'Outline ready.' ) ) { continue; } $session['messages'][ $index ]['type'] = 'plan'; $session['messages'][ $index ]['plan'] = $plan; break; } return $session; } /** * Handle update conversation request. * * @since 0.1.4 * @param WP_REST_Request $request REST request. * @return WP_REST_Response|WP_Error */ public function handle_update_conversation( $request ) { $params = $request->get_json_params(); $session_id = sanitize_text_field( $request->get_param( 'session_id' ) ); $manager = WP_Agentic_Writer_Conversation_Manager::get_instance(); // Check authorization. if ( ! $manager->current_user_can_access( $session_id ) ) { return new WP_Error( 'forbidden', __( 'You do not have permission to modify this conversation.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } $session = $manager->get_session( $session_id ); if ( ! $session ) { return new WP_Error( 'not_found', __( 'Conversation not found.', 'wp-agentic-writer' ), [ 'status' => 404 ], ); } // Update fields. if ( isset( $params['title'] ) ) { $manager->update_title( $session_id, $params['title'] ); } if ( isset( $params['focus_keyword'] ) ) { $manager->update_focus_keyword( $session_id, $params['focus_keyword'], ); } if ( isset( $params['status'] ) ) { if ( 'completed' === $params['status'] ) { $manager->mark_completed( $session_id ); // MEMANTO: Session completed. $post_id = $session['post_id'] ?? 0; do_action( 'wpaw_memanto_session_end', $session_id, (int) $post_id, ); } } $updated_session = $manager->get_session( $session_id ); return new WP_REST_Response( $updated_session, 200 ); } /** * Handle delete conversation request. * * @since 0.1.4 * @param WP_REST_Request $request REST request. * @return WP_REST_Response|WP_Error */ public function handle_delete_conversation( $request ) { $session_id = sanitize_text_field( $request->get_param( 'session_id' ) ); $manager = WP_Agentic_Writer_Conversation_Manager::get_instance(); // Check authorization. if ( ! $manager->current_user_can_access( $session_id ) ) { return new WP_Error( 'forbidden', __( 'You do not have permission to delete this conversation.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } $result = $manager->delete_session( $session_id ); if ( ! $result ) { return new WP_Error( 'delete_failed', __( 'Failed to delete conversation.', 'wp-agentic-writer' ), [ 'status' => 500 ], ); } // MEMANTO: Session deleted — treat as session end. $session = $manager->get_session_unchecked( $session_id ); $post_id = $session ? $session['post_id'] ?? 0 : 0; do_action( 'wpaw_memanto_session_end', $session_id, (int) $post_id ); return new WP_REST_Response( [ 'deleted' => true ], 200 ); } /** * Handle update conversation messages request. * * @since 0.1.4 * @param WP_REST_Request $request REST request. * @return WP_REST_Response|WP_Error */ public function handle_update_conversation_messages( $request ) { $params = $request->get_json_params(); $session_id = sanitize_text_field( $request->get_param( 'session_id' ) ); $manager = WP_Agentic_Writer_Conversation_Manager::get_instance(); // Check authorization. if ( ! $manager->current_user_can_access( $session_id ) ) { return new WP_Error( 'forbidden', __( 'You do not have permission to modify this conversation.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } $session = $manager->get_session( $session_id ); if ( ! $session ) { return new WP_Error( 'not_found', __( 'Conversation not found.', 'wp-agentic-writer' ), [ 'status' => 404 ], ); } $messages = isset( $params['messages'] ) ? $params['messages'] : []; if ( ! is_array( $messages ) ) { return new WP_Error( 'invalid_messages', __( 'Messages must be an array.', 'wp-agentic-writer' ), [ 'status' => 400 ], ); } // Safety: refuse to overwrite existing messages with an empty array. // This prevents race conditions during session switches from wiping // conversation history. Intentional clears go through clear-context. $existing_count = count( $session['messages'] ?? [] ); if ( empty( $messages ) && $existing_count > 0 ) { return new WP_REST_Response( [ 'updated' => false, 'message_count' => $existing_count, 'skipped' => 'empty_overwrite_blocked', ], 200, ); } $updated = $manager->update_messages( $session_id, $messages ); if ( ! $updated ) { return new WP_Error( 'message_update_failed', __( 'Failed to update conversation messages.', 'wp-agentic-writer', ), [ 'status' => 500 ], ); } return new WP_REST_Response( [ 'updated' => true, 'message_count' => count( $messages ) ], 200, ); } /** * Handle link conversation to post request. * * @since 0.1.4 * @param WP_REST_Request $request REST request. * @return WP_REST_Response|WP_Error */ public function handle_link_conversation_to_post( $request ) { $params = $request->get_json_params(); $session_id = sanitize_text_field( $request->get_param( 'session_id' ) ); $manager = WP_Agentic_Writer_Conversation_Manager::get_instance(); // First verify user has access to this session (before linking to post). if ( ! $manager->current_user_can_access( $session_id ) ) { return new WP_Error( 'forbidden', __( 'You do not have access to this conversation.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } $session = $manager->get_session( $session_id ); if ( ! $session ) { return new WP_Error( 'not_found', __( 'Conversation not found.', 'wp-agentic-writer' ), [ 'status' => 404 ], ); } $post_id = isset( $params['post_id'] ) ? (int) $params['post_id'] : 0; if ( $post_id <= 0 ) { return new WP_Error( 'invalid_post', __( 'Valid post ID is required.', 'wp-agentic-writer' ), [ 'status' => 400 ], ); } // Verify post exists and user can edit. if ( ! current_user_can( 'edit_post', $post_id ) ) { return new WP_Error( 'permission_denied', __( 'You do not have permission to edit this post.', 'wp-agentic-writer', ), [ 'status' => 403 ], ); } $manager->link_to_post( $session_id, $post_id ); $updated_session = $manager->get_session( $session_id ); return new WP_REST_Response( [ 'linked' => true, 'post_id' => $post_id, 'session' => $updated_session, ], 200, ); } }