PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.12
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.12
1.1.12 1.1.11 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 All 36 releases
← All changes | includes/agents/rest.php +119 -8 1.1.0 → 1.1.12 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',
@@ -290,8 +343,13 @@
290 343 * @param WP_REST_Request $request REST request.
291 344 * @return WP_REST_Response|WP_Error
292 345 */
293 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.
294 352 $user = openstation_agent_create(
295 353 array(
296 354 'name' => (string) $request['name'],
297 355 'role' => (string) $request['role'],
@@ -297,8 +355,12 @@
297 355 'role' => (string) $request['role'],
298 356 'description' => (string) $request['description'],
299 357 'instructions' => (string) $request['instructions'],
300 358 'abilities' => (array) $request['abilities'],
359 + 'triggers' => (array) $request['triggers'],
360 + 'vibes' => (string) $request['vibes'],
361 + 'face' => $request['face'],
362 + 'faceSeed' => (int) $request['faceSeed'],
301 363 )
302 364 );
303 365 if ( is_wp_error( $user ) ) {
304 366 $data = $user->get_error_data();
@@ -337,9 +399,21 @@
337 399 $body = array();
338 400 }
339 401
340 402 $fields = array();
341 - $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 + );
342 416 foreach ( $allowed as $field ) {
343 417 if ( array_key_exists( $field, $body ) ) {
344 418 $fields[ $field ] = $body[ $field ];
345 419 }
@@ -405,10 +479,11 @@
405 479
406 480 $source = (string) $request['source'];
407 481
408 482 // Per-agent gate. The route's `permission_callback` cannot run this
409 - // one: it has no access to the resolved agent, and the capability an
410 - // agent requires is a property of that agent's trigger config.
483 + // one: it has no access to the resolved agent, and the capabilities an
484 + // agent requires are a property of that agent's trigger config. The
485 + // gate applies all of them whatever `source` the request names.
411 486 if ( ! openstation_agent_user_can_invoke_agent( (int) $user->ID, $source ) ) {
412 487 return new WP_Error(
413 488 'openstation_agents_forbidden',
414 489 __( 'You do not have permission to invoke this agent.', 'desktop-mode' ),
@@ -415,8 +490,12 @@
415 490 array( 'status' => rest_authorization_required_code() )
416 491 );
417 492 }
418 493
494 + if ( $request['async'] ) {
495 + return openstation_agents_rest_enqueue_job( $request );
496 + }
497 +
419 498 $result = openstation_agent_invoke(
420 499 (int) $user->ID,
421 500 (string) $request['message'],
422 501 array(
@@ -444,8 +523,37 @@
444 523 return rest_ensure_response( openstation_agents_abilities_catalogue() );
445 524 }
446 525
447 526 /**
527 + * `brief` must carry words and fit the drafting cap.
528 + *
529 + * @param mixed $value Raw param.
530 + * @return bool
531 + */
532 +function openstation_agents_rest_validate_brief( $value ) {
533 + return is_string( $value )
534 + && '' !== trim( $value )
535 + && mb_strlen( $value ) <= OPENSTATION_AGENT_DRAFT_BRIEF_MAX;
536 +}
537 +
538 +/**
539 + * POST /agents/draft — draft a definition from a brief.
540 + *
541 + * Nothing is created: the wizard shows the draft for review and the
542 + * create route is still the only way an agent comes to exist.
543 + *
544 + * @param WP_REST_Request $request Request.
545 + * @return WP_REST_Response|WP_Error
546 + */
547 +function openstation_agents_rest_draft( WP_REST_Request $request ) {
548 + $draft = openstation_agent_draft( (string) $request['brief'], get_current_user_id() );
549 + if ( is_wp_error( $draft ) ) {
550 + return $draft;
551 + }
552 + return rest_ensure_response( $draft );
553 +}
554 +
555 +/**
448 556 * GET /agents/trigger-kinds — the trigger-kinds catalogue.
449 557 *
450 558 * @return WP_REST_Response
451 559 */
@@ -501,15 +609,15 @@
501 609 }
502 610
503 611 $avatar = get_avatar_url( $user->ID, array( 'size' => 96 ) );
504 612 if ( ! is_string( $avatar ) || '' === $avatar ) {
505 - $avatar = openstation_agent_avatar_url();
613 + $avatar = openstation_agent_avatar_url( (int) $user->ID );
506 614 }
507 615
508 616 return array(
509 617 'id' => (int) $user->ID,
510 618 'slug' => $slug,
511 - 'name' => (string) $user->display_name,
619 + 'name' => openstation_plain_text_title( $user->display_name ),
512 620 'description' => openstation_agent_get_description( (int) $user->ID ),
513 621 'instructions' => openstation_agent_get_instructions( (int) $user->ID ),
514 622 'role' => $role,
515 623 'abilities' => openstation_agent_get_abilities( (int) $user->ID ),
@@ -515,7 +623,10 @@
515 623 'abilities' => openstation_agent_get_abilities( (int) $user->ID ),
516 624 'triggers' => openstation_agent_get_triggers( (int) $user->ID ),
517 625 'model' => openstation_agent_get_model( (int) $user->ID ),
518 626 'rateLimit' => openstation_agent_get_rate_limit( (int) $user->ID ),
627 + 'vibes' => openstation_agent_get_vibes( (int) $user->ID ),
628 + 'face' => openstation_agent_get_face( (int) $user->ID ),
629 + 'faceSeed' => openstation_agent_get_face_seed( (int) $user->ID ),
519 630 'avatarUrl' => $avatar,
520 631 );
521 632 }