From b4aebbb21311a416413757a42c28a444e9e80d7a Mon Sep 17 00:00:00 2001 From: Miguel Lezama Date: Tue, 19 May 2026 14:21:49 -0300 Subject: [PATCH] Agent files: wire CPT contents into runtime system prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `openclawp_agent_file` CPT shipped in #71 was authored but never read by the runner. Append the per-agent file set (globals + agent-targeted) to the agent's static description before passing it to `using_system_instruction()`. - New `OpenclaWP_Agent_Files_Store::for_agent_as_text( $agent_slug )` concatenates the matching files as `=== title ===\nbody\n…` plain text. - New `openclawp_runner_system_instruction` filter lets other plugins layer in further per-turn customisation after agent files are merged. šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) --- .../class-openclawp-agent-files-store.php | 33 +++++++++++++++++-- includes/class-openclawp-runner.php | 24 ++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/includes/class-openclawp-agent-files-store.php b/includes/class-openclawp-agent-files-store.php index a132ae9..c165475 100644 --- a/includes/class-openclawp-agent-files-store.php +++ b/includes/class-openclawp-agent-files-store.php @@ -14,9 +14,9 @@ * author these documents exclusively through openclaWP's own surface * (see {@see OpenclaWP_Agent_Files_Admin}). * - * TODO (follow-up PR): wire the file contents into the runtime system prompt - * via `OpenclaWP_Canonical_Chat_Handler`. This PR ships the CPT + admin UI - * only so admins can start authoring files while the runtime side bakes. + * Runtime consumer: {@see OpenclaWP_Runner} (see `build_turn_runner()` around + * the `using_system_instruction()` call) appends `for_agent_as_text()` to the + * agent's static description on every turn. * * @package OpenclaWP * @since 0.11.0 @@ -106,6 +106,33 @@ public static function for_agent( string $agent_slug ): array { return $out; } + /** + * Return the files targeted at a given agent concatenated as plain text, + * ready to append to a system prompt. + * + * Each file is emitted as: + * + * === {post_title} === + * {post_content} + * + * Sections are joined with a blank line. Files with empty bodies are + * skipped. Returns an empty string when no files apply — callers can + * treat the result as a safe no-op append. + * + * @param string $agent_slug Agent slug, e.g. `openclawp-example`. + */ + public static function for_agent_as_text( string $agent_slug ): string { + $sections = array(); + foreach ( self::for_agent( $agent_slug ) as $post ) { + $body = (string) $post->post_content; + if ( '' === trim( $body ) ) { + continue; + } + $sections[] = '=== ' . $post->post_title . " ===\n" . $body; + } + return implode( "\n\n", $sections ); + } + /** * Fetch a file by its title (file name slot, e.g. "AGENTS.md"). * diff --git a/includes/class-openclawp-runner.php b/includes/class-openclawp-runner.php index e9d20b2..e096f87 100644 --- a/includes/class-openclawp-runner.php +++ b/includes/class-openclawp-runner.php @@ -298,6 +298,30 @@ private static function build_turn_runner( WP_Agent $agent, array $session, stri // WP_AI_Client_Prompt_Builder forwards these via __call, so // method_exists() returns false for them — call directly. $description = $agent_obj->get_description(); + + // Append admin-authored Agent files (CPT shipped in PR #71). + // When the CPT is empty for this agent, this is a no-op. + if ( class_exists( 'OpenclaWP_Agent_Files_Store' ) ) { + $agent_files_text = OpenclaWP_Agent_Files_Store::for_agent_as_text( $agent_obj->get_slug() ); + if ( '' !== $agent_files_text ) { + $description = ( '' !== $description ) + ? $description . "\n\n" . $agent_files_text + : $agent_files_text; + } + } + + /** + * Filter the system instruction the runner passes to the AI + * client for this turn. Receives the post-agent-files + * description + the agent slug. + * + * @since 0.11.0 + * + * @param string $description Fully assembled system instruction. + * @param string $agent_slug Agent that's about to run. + */ + $description = (string) apply_filters( 'openclawp_runner_system_instruction', $description, $agent_obj->get_slug() ); + if ( '' !== $description ) { $builder = $builder->using_system_instruction( $description ); }