# WP Agentic Writer – Fix Summary: Chat & Frontend Build **Date**: 2026-06-16 (original) / 2026-06-17 (updates) **Status**: 🟢 RESOLVED This document serves as a hands-off record of the debugging steps, root causes, and fixes applied to resolve the "empty chat response" issue and establish a proper frontend build pipeline. --- ## Updates (2026-06-17) ### Additional Backend Improvements 1. **Connection Test Caching** - Already existed in `class-provider-manager.php` (5-min TTL via transients) 2. **Cache Auto-Clear on Settings Save** - Added to `class-settings-v2.php` - Hooks to `updated_option` action - Clears connection test cache when local backend settings change 3. **Reasoning Content Parsing** - Added to `class-local-backend-provider.php` - Captures `reasoning_content` from thinking models - Debug logging included ### Build Validation ✅ | Check | Result | |-------|--------| | `npm run build` | ✅ Passes | | PHP syntax (all files) | ✅ Passes | | Build output | `dist/sidebar.js` (169 KB) | ### Important: Two sidebar.js Files | File | Loaded by PHP? | |------|----------------| | `assets/js/sidebar.js` (438 KB) | ❌ Legacy, NOT loaded | | `assets/js/dist/sidebar.js` (169 KB) | ✅ Current, loaded | --- ## Original Fixes (2026-06-16) --- ## 1. Frontend Build Pipeline Established **Problem**: The PHP plugin explicitly loads `assets/js/dist/sidebar.js`. However, ongoing refactoring work was occurring in `assets/js/src/index.jsx`. Because there was no active build process, changes made in `index.jsx` were not reflecting in the application. **Solution**: * Created `package.json` with `esbuild` to handle fast JSX compilation. * Added `scripts/build.js` configured specifically for WordPress Gutenberg (using `wp.element.createElement` and `wp.element.Fragment`). * **Critical Fix**: Ensured the esbuild configuration wraps the output in an anonymous IIFE (`format: "iife"`) *without* defining a `globalName: "wp"`. A named global would have caused the compiled script to overwrite WordPress's native `window.wp` object with `undefined`, breaking the editor. **Usage**: * Run `npm run build` to compile `src/index.jsx` -> `dist/sidebar.js`. * Run `npm run build:watch` during active development. --- ## 2. Backend Fix: PHP Fatal Error on Connection Test **Problem**: When the local backend provider received an HTTP 4xx/5xx error (e.g., when trying to test a connection to a rate-limited or failing model), the entire REST request crashed, resulting in a 500 server error and breaking the chat flow entirely. **Root Cause**: In `includes/class-local-backend-provider.php` (line 882), a double-quoted string was used for a `sprintf` format: `__("API Error (HTTP %1$d): %2$s", "wp-agentic-writer")` Because it was double-quoted, PHP attempted to interpolate `$d` and `$s` as variables, resolving them to empty strings. The resulting string `API Error (HTTP %1): %2` caused `sprintf` to throw a fatal `ValueError: Unknown format specifier ")"`. **Fix**: Changed the double quotes to single quotes to prevent PHP variable interpolation, preserving the intended positional specifiers: `__('API Error (HTTP %1$d): %2$s', "wp-agentic-writer")` *Scanned the rest of the codebase and confirmed this dangerous pattern does not exist anywhere else.* --- ## 3. Backend Fix: Agentic Models Returning "Empty Responses" **Problem**: The user experienced intermittent "empty chat response" errors even when the API connection was successful (HTTP 200). **Root Cause**: The configured models (e.g., `dough/kr/claude-sonnet-4.5-thinking` and `ag/gemini-3-flash-agent`) are **agentic/tool-calling** models. When fed a large WordPress system prompt asking for plain prose, these models burned all their tokens on internal "reasoning", attempted to emit a function/tool call, failed (`finish_reason: "malformed_function_call"`), and returned **zero text content**. Because the streaming buffer received no content, it fell back to non-streaming, which also yielded zero content, triggering a generic "empty response" error. **Fix**: 1. **Better Error Surfacing**: Updated the streaming and fallback logic in `class-local-backend-provider.php` to actively capture the `finish_reason` payload. 2. If the provider returns empty content but has a finish reason of `malformed_function_call`, `tool_calls`, or `function_call`, the backend now intercepts this and throws a highly specific, actionable error: > *"The selected model [Model Name] returned no text (finish reason: tool/function call). This usually means an agentic/coding model is being used for prose. Choose a standard chat model (without an -agent or -agentic suffix) in Settings."* 3. **Config Alignment**: Used WP-CLI to update the WordPress options table, switching the local backend models from the agentic variants to the standard `gemini/gemini-3-flash-preview` for all prose tasks (`chat`, `writing`, `planning`, etc.). --- ## Summary of Touched Files * `package.json` (New) * `scripts/build.js` (New) * `assets/js/dist/sidebar.js` (Rebuilt) * `includes/class-local-backend-provider.php` (Fixed string interpolation, added `finish_reason` edge-case handling, added error payload parsing). The chat functionality is now robust, gracefully handles agentic model failures, and the React frontend can be reliably compiled from `index.jsx`. --- ## 4. Next Recommendations With the frontend build pipeline established and the critical backend crashes resolved, the foundation is stable. Here are the recommended next steps: 1. **Complete the React File Splitting (Refactor Phase 2)** ⚠️ IN PROGRESS * Now that `index.jsx` compiles successfully to `dist/sidebar.js`, the massive 11,000+ line `index.jsx` should be split into modular React components. * **See `FRONTEND-REFACTOR-PHASE2.md` for the detailed modularization plan.** * The new `esbuild` setup natively supports resolving local imports, meaning you can safely extract components into a `src/components/` directory and import them into `index.jsx` without changing the PHP backend. * **Key clarification**: The source is `src/index.jsx`, which compiles to `dist/sidebar.js`. We are NOT creating a new `sidebar.jsx` source file - we are modularizing the existing `index.jsx`. 2. **Optimize `test_connection()` in the Provider Manager** ✅ COMPLETED * Implemented connection test caching using WordPress transients with 5-minute TTL in `class-provider-manager.php`. * Added automatic cache clearing in `class-settings-v2.php` when local backend settings are saved (URL, API key, model changes). * This eliminates redundant connection tests on every chat request, significantly reducing latency. 3. **Handle Reasoning Tokens for Thinking Models** ✅ COMPLETED * Added `reasoning_content` parsing to `chat_stream()` method in `class-local-backend-provider.php`. * The streaming parser now captures `chunk["choices"][0]["delta"]["reasoning_content"]` from thinking models like Claude extended thinking. * Reasoning content is passed through the callback so the frontend can optionally display it in a collapsible section. * Debug logging added to help identify when reasoning chunks are received.