PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
← All changes | includes/agents/rest.php +120 -47 1.0.01.1.10 View file →
@@ -73,8 +73,35 @@
73 73 'type' => 'array',
74 74 'default' => array(),
75 75 'items' => array( 'type' => 'string' ),
76 76 ),
77 + // Like `face`, deliberately schema-light. Each row
78 + // is validated against the live trigger-kind
79 + // catalogue by openstation_agent_sanitize_triggers(),
80 + // which drops rows it does not recognise rather
81 + // than rejecting the whole create.
82 + 'triggers' => array(
83 + 'type' => 'array',
84 + 'default' => array(),
85 + ),
86 + 'vibes' => array(
87 + 'type' => 'string',
88 + 'default' => '',
89 + ),
90 + // `face` carries no schema beyond "object" and no
91 + // sanitize_callback on purpose. The real validator is
92 + // openstation_agent_sanitize_face_json(), which clamps
93 + // every number; a partial JSON Schema here would only
94 + // suggest the route had checked it.
95 + 'face' => array(
96 + 'type' => 'object',
97 + 'default' => null,
98 + ),
99 + 'faceSeed' => array(
100 + 'type' => 'integer',
101 + 'default' => 0,
102 + 'sanitize_callback' => 'absint',
103 + ),
77 104 ),
78 105 ),
79 106 )
80 107 );
@@ -90,8 +117,26 @@
90 117 );
91 118
92 119 register_rest_route(
93 120 $namespace,
121 + '/agents/draft',
122 + array(
123 + 'methods' => WP_REST_Server::CREATABLE,
124 + 'permission_callback' => 'openstation_agents_rest_write_permission',
125 + 'callback' => 'openstation_agents_rest_draft',
126 + 'args' => array(
127 + 'brief' => array(
128 + 'type' => 'string',
129 + 'required' => true,
130 + 'sanitize_callback' => 'sanitize_textarea_field',
131 + 'validate_callback' => 'openstation_agents_rest_validate_brief',
132 + ),
133 + ),
134 + )
135 + );
136 +
137 + register_rest_route(
138 + $namespace,
94 139 '/agents/trigger-kinds',
95 140 array(
96 141 'methods' => WP_REST_Server::READABLE,
97 142 'permission_callback' => 'openstation_agents_rest_read_permission',
@@ -148,14 +193,22 @@
148 193 'methods' => WP_REST_Server::CREATABLE,
149 194 'permission_callback' => 'openstation_agents_rest_invoke_permission',
150 195 'callback' => 'openstation_agents_rest_invoke',
151 196 'args' => array(
152 - 'message' => array(
197 + 'async' => array(
198 + 'type' => 'boolean',
199 + 'default' => false,
200 + ),
201 + 'requestId' => array(
202 + 'type' => 'string',
203 + 'format' => 'uuid',
204 + ),
205 + 'message' => array(
153 206 'type' => 'string',
154 207 'required' => true,
155 208 'sanitize_callback' => 'sanitize_textarea_field',
156 209 ),
157 - 'source' => array(
210 + 'source' => array(
158 211 'type' => 'string',
159 212 'default' => 'chat',
160 213 'enum' => array( 'chat', 'drag', 'send-to' ),
161 214 'sanitize_callback' => 'sanitize_key',
@@ -163,9 +216,9 @@
163 216 // Prior conversation turns, oldest first. Without these
164 217 // every message is a contextless run — a follow-up like
165 218 // "yes, do it" would be resolved against nothing and the
166 219 // agent could act on the wrong entity entirely.
167 - 'history' => array(
220 + 'history' => array(
168 221 'type' => 'array',
169 222 'default' => array(),
170 223 'items' => array(
171 224 'type' => 'object',
@@ -185,53 +238,16 @@
185 238 add_action( 'rest_api_init', 'openstation_agents_register_rest_routes' );
186 239
187 240 // ---------------------------------------------------------------------------
188 241 // Permissions
242 +//
243 +// The three capability gates themselves (`openstation_agents_user_can_read`
244 +// / `_manage` / `_invoke`) live in bootstrap.php: the WP Explorer
245 +// integration loads while the feature flag is off, and this file does
246 +// not.
189 247 // ---------------------------------------------------------------------------
190 248
191 249 /**
192 - * Whether the current user can see agents.
193 - *
194 - * @return bool
195 - */
196 -function openstation_agents_user_can_read() {
197 - /**
198 - * Filter whether the current user can read OpenStation agents.
199 - *
200 - * @param bool $can Default: `edit_posts` capability.
201 - */
202 - return (bool) apply_filters( 'openstation_agents_user_can_read', current_user_can( 'edit_posts' ) );
203 -}
204 -
205 -/**
206 - * Whether the current user can create / edit / delete agents.
207 - *
208 - * @return bool
209 - */
210 -function openstation_agents_user_can_manage() {
211 - /**
212 - * Filter whether the current user can manage OpenStation agents.
213 - *
214 - * @param bool $can Default: `edit_users` capability.
215 - */
216 - return (bool) apply_filters( 'openstation_agents_user_can_manage', current_user_can( 'edit_users' ) );
217 -}
218 -
219 -/**
220 - * Whether the current user can invoke agents.
221 - *
222 - * @return bool
223 - */
224 -function openstation_agents_user_can_invoke() {
225 - /**
226 - * Filter whether the current user can invoke OpenStation agents.
227 - *
228 - * @param bool $can Default: `edit_posts` capability.
229 - */
230 - return (bool) apply_filters( 'openstation_agents_user_can_invoke', current_user_can( 'edit_posts' ) );
231 -}
232 -
233 -/**
234 250 * Read-route permission callback.
235 251 *
236 252 * @return bool|WP_Error
237 253 */
@@ -327,8 +343,13 @@
327 343 * @param WP_REST_Request $request REST request.
328 344 * @return WP_REST_Response|WP_Error
329 345 */
330 346 function openstation_agents_rest_create( WP_REST_Request $request ) {
347 + // Every field the route declares is forwarded. `vibes`, `face` and
348 + // `faceSeed` are the character half of an agent, and a create that
349 + // took the name and dropped the portrait is how an agent ends up
350 + // wearing the fallback glyph seconds after someone picked a face
351 + // for it. `openstation_agent_create()` sanitizes each one.
331 352 $user = openstation_agent_create(
332 353 array(
333 354 'name' => (string) $request['name'],
334 355 'role' => (string) $request['role'],
@@ -334,8 +355,12 @@
334 355 'role' => (string) $request['role'],
335 356 'description' => (string) $request['description'],
336 357 'instructions' => (string) $request['instructions'],
337 358 'abilities' => (array) $request['abilities'],
359 + 'triggers' => (array) $request['triggers'],
360 + 'vibes' => (string) $request['vibes'],
361 + 'face' => $request['face'],
362 + 'faceSeed' => (int) $request['faceSeed'],
338 363 )
339 364 );
340 365 if ( is_wp_error( $user ) ) {
341 366 $data = $user->get_error_data();
@@ -374,9 +399,21 @@
374 399 $body = array();
375 400 }
376 401
377 402 $fields = array();
378 - $allowed = array( 'name', 'role', 'description', 'instructions', 'abilities', 'triggers', 'model', 'rateLimit' );
403 + $allowed = array(
404 + 'name',
405 + 'role',
406 + 'description',
407 + 'instructions',
408 + 'abilities',
409 + 'triggers',
410 + 'model',
411 + 'rateLimit',
412 + 'vibes',
413 + 'face',
414 + 'faceSeed',
415 + );
379 416 foreach ( $allowed as $field ) {
380 417 if ( array_key_exists( $field, $body ) ) {
381 418 $fields[ $field ] = $body[ $field ];
382 419 }
@@ -452,8 +489,12 @@
452 489 array( 'status' => rest_authorization_required_code() )
453 490 );
454 491 }
455 492
493 + if ( $request['async'] ) {
494 + return openstation_agents_rest_enqueue_job( $request );
495 + }
496 +
456 497 $result = openstation_agent_invoke(
457 498 (int) $user->ID,
458 499 (string) $request['message'],
459 500 array(
@@ -481,8 +522,37 @@
481 522 return rest_ensure_response( openstation_agents_abilities_catalogue() );
482 523 }
483 524
484 525 /**
526 + * `brief` must carry words and fit the drafting cap.
527 + *
528 + * @param mixed $value Raw param.
529 + * @return bool
530 + */
531 +function openstation_agents_rest_validate_brief( $value ) {
532 + return is_string( $value )
533 + && '' !== trim( $value )
534 + && mb_strlen( $value ) <= OPENSTATION_AGENT_DRAFT_BRIEF_MAX;
535 +}
536 +
537 +/**
538 + * POST /agents/draft — draft a definition from a brief.
539 + *
540 + * Nothing is created: the wizard shows the draft for review and the
541 + * create route is still the only way an agent comes to exist.
542 + *
543 + * @param WP_REST_Request $request Request.
544 + * @return WP_REST_Response|WP_Error
545 + */
546 +function openstation_agents_rest_draft( WP_REST_Request $request ) {
547 + $draft = openstation_agent_draft( (string) $request['brief'], get_current_user_id() );
548 + if ( is_wp_error( $draft ) ) {
549 + return $draft;
550 + }
551 + return rest_ensure_response( $draft );
552 +}
553 +
554 +/**
485 555 * GET /agents/trigger-kinds — the trigger-kinds catalogue.
486 556 *
487 557 * @return WP_REST_Response
488 558 */
@@ -538,9 +608,9 @@
538 608 }
539 609
540 610 $avatar = get_avatar_url( $user->ID, array( 'size' => 96 ) );
541 611 if ( ! is_string( $avatar ) || '' === $avatar ) {
542 - $avatar = openstation_agent_avatar_url();
612 + $avatar = openstation_agent_avatar_url( (int) $user->ID );
543 613 }
544 614
545 615 return array(
546 616 'id' => (int) $user->ID,
@@ -552,7 +622,10 @@
552 622 'abilities' => openstation_agent_get_abilities( (int) $user->ID ),
553 623 'triggers' => openstation_agent_get_triggers( (int) $user->ID ),
554 624 'model' => openstation_agent_get_model( (int) $user->ID ),
555 625 'rateLimit' => openstation_agent_get_rate_limit( (int) $user->ID ),
626 + 'vibes' => openstation_agent_get_vibes( (int) $user->ID ),
627 + 'face' => openstation_agent_get_face( (int) $user->ID ),
628 + 'faceSeed' => openstation_agent_get_face_seed( (int) $user->ID ),
556 629 'avatarUrl' => $avatar,
557 630 );
558 631 }