CHANGELOG-PROPOSAL.txt
2 days ago
QA-LOOP.md
2 days ago
STUDY-CONSOLIDATION.md
2 days ago
STUDY-MCP.md
2 days ago
STUDY-WORKSPACE.md
2 days ago
STUDY-WPAI-LEARNINGS.md
2 days ago
STUDY-WORKSPACE.md
328 lines
| 1 | # STUDY-WORKSPACE.md — Chatbot polish → Local Knowledge → Workspace |
| 2 | |
| 3 | > **Living task doc.** Born 2026-07-24 from the hands-on chatbot review + Jordy's Workspace vision |
| 4 | > ("a real BIG chatbot": full-screen, history on the left, model picker; our own cheap Mammouth AI, |
| 5 | > Pro only). Guiding principle from Jordy: **don't make everything overly complicated. Build smart |
| 6 | > reusable components, keep the current chatbots light, build Workspace separately on the same |
| 7 | > components.** So: polish first, then a refactor/extraction step with heavy testing, then Workspace. |
| 8 | > Demand rule applies ([[feedback_demand_driven_features]]): each phase ships user-visible value. |
| 9 | |
| 10 | ## Phase 1 — Chatbot polish (from the 2026-07-24 hands-on review) |
| 11 | |
| 12 | - [x] **Over-limit lockout.** Limits refuse via WP_Error → `Meow_MWAI_RefusedException` → REST returns |
| 13 | `overLimit: true` (HTTP 429) → frontend locks the input (same state GDPR uses). Streaming errors now |
| 14 | pass refusal messages through instead of the generic "Oops". Demand: wp.org thread "Chat remains |
| 15 | active after usage limit is reached". Validated live as guest + smoke run green. |
| 16 | - [x] **Config errors gated to editors.** "Chatbot 'x' not found" only renders for `edit_posts`; |
| 17 | visitors get '' + error_log. Both shortcode call sites. |
| 18 | - [x] **Transient errors not persisted.** `saveMessages` filters `isError`/role error, matching |
| 19 | `onCommitDiscussions`. |
| 20 | - [x] **Copy button on code blocks.** `pre` override in ChatbotContent.js; hljs-compatible. |
| 21 | - [x] **Visitor-friendly default over-limit messages** (constants/init.php; existing sites keep |
| 22 | their saved text). |
| 23 | - [x] **Image size cap in messages.** Markdown images rendered unconstrained (a logo filled the |
| 24 | whole chat viewport). Cap height, click opens full image. (2026-07-24) |
| 25 | - [x] **Empty-state hint.** A bot with no startSentence opens as a dead gray box; show a subtle |
| 26 | hint in the empty messages area. (2026-07-24) |
| 27 | - [ ] **Chatbot Block page silent mount failure.** On ai.nekod.net/sample-page/ the container divs |
| 28 | render but no chatbot mounts, no console error. Reproduce with console open, fix. (Suspect: |
| 29 | inline-params block + site-wide popup of the same botId on one page.) |
| 30 | - [ ] **Emoji download icon + translation error notice**: shipped 08d04199 / bfb7fce5 (2026-07-24). |
| 31 | Listed for the record; done. |
| 32 | |
| 33 | ## Phase 2 — Local Knowledge DB (default embeddings env, "zero external services") |
| 34 | |
| 35 | Goal: a new **"Internal (WordPress DB)"** embeddings environment; no Pinecone/Qdrant account needed. |
| 36 | Removes the #1 friction on the top Pro conversion driver. Target: default env for new installs once |
| 37 | proven. |
| 38 | |
| 39 | - [x] **Tier 1: PHP brute-force cosine.** DONE 2026-07-24: `premium/addons/internal.php`, packed |
| 40 | float32 BLOBs in `wp_mwai_local_vectors`, batched scan + wpdb flush. Measured on ai.nekod.net: |
| 41 | 1.4ms @ 3 vectors, 210ms @ 5,000 (1536 dims). End-to-end proven: smoke-rag chatbot answered a |
| 42 | fact that exists only in the local store. Default env for FRESH installs (first in |
| 43 | `embeddings_envs` defaults); existing sites unaffected. |
| 44 | - [ ] **Dimension control.** Matryoshka truncation option (512/768) to cut storage + CPU 2-3x. |
| 45 | - [ ] **Tier 2: MariaDB native VECTOR (11.7+).** Auto-detect; HNSW index, same env type. Optional, |
| 46 | later. |
| 47 | - [ ] **Hybrid bonus.** Local content column makes keyword (FULLTEXT) + vector RRF fusion trivial; |
| 48 | quietly answers the Qdrant hybrid ask (swimgeek) provider-neutrally. |
| 49 | - [x] **Smoke coverage.** DONE 2026-07-24: RAGCHAT check in `test-smoke.js` (site-level + `rag` |
| 50 | arg) on the smoke-rag fixture bot wired to Internal env intern01. Validated against a simulated |
| 51 | dropped-context regression (broken query_vectors → FAIL, restored → PASS). |
| 52 | - [x] **UX.** DONE 2026-07-24: "Internal (WordPress DB)" card first in the env chooser (badge |
| 53 | "Simplest"), no API key/server fields, info notice instead. Migration: none needed. |
| 54 | |
| 55 | ## Phase 3 — Component refactor + heavy testing (the step BEFORE Workspace) |
| 56 | |
| 57 | Keep current chatbots light; extract reusable pieces Workspace will share. Overlaps consolidation |
| 58 | item #7 (ChatbotContext decomposition): this phase funds it. |
| 59 | |
| 60 | - [x] **Inventory pass.** DONE 2026-07-24 (findings below). |
| 61 | |
| 62 | ### Inventory findings (2026-07-24) |
| 63 | |
| 64 | **The coupling is concentrated in ONE file.** `ChatbotContext.js` (1564 lines) is the only heavy |
| 65 | provider. Everything else is either already decoupled or a thin display consumer. This is the good case: |
| 66 | we extract logic from one file, not untangle a web. |
| 67 | |
| 68 | **Already shared / reusable as-is (no work):** |
| 69 | - `helpers.js` — the real network layer (`mwaiFetch`, `mwaiFetchUpload`, `mwaiHandleRes`), zero context, |
| 70 | already imported by both contexts. This IS the streaming glue; nothing to extract. |
| 71 | - `helpers/tokenManager.js` — nonce singleton, already shared. |
| 72 | - `MwaiAPI.js` — the `window.MwaiAPI` singleton + filter/action bus. The bridge the two contexts talk |
| 73 | through. Already global. |
| 74 | - `ChatbotContent.js` (236), `ChatbotEvents.js` (291), `ChatbotSpinners.js`, `AudioVisualizer.js` — |
| 75 | pure props-driven, no context. Drop into Workspace directly. |
| 76 | |
| 77 | **The whole history sidebar is already independent.** `DiscussionsContext.js` (415) makes ZERO |
| 78 | `useChatbotContext()` calls — it takes a `system` prop (botId, restNonce), hits |
| 79 | `mwai-ui/v1/discussions/{list,edit,delete}` itself, and reaches the chatbot only via |
| 80 | `MwaiAPI.getChatbot()`. `DiscussionsUI.js` + `DiscussionsSystem.js` ride on it. **Workspace's left |
| 81 | column = mount `DiscussionsSystem` with a `system` prop. Reuse, don't rebuild.** |
| 82 | |
| 83 | **The one real extraction — the submit/stream state machine.** Inside `ChatbotContext` these members |
| 84 | are the reusable "chat session" logic, tangled together but separable from all the display params: |
| 85 | `onSubmit` (L867, the streaming submit), `addErrorMessage`/`retryLastQuery`/`lastFailedQuery`, `locked`, |
| 86 | `saveMessages` (localStorage), `refreshRestNonce`, the file-upload callbacks |
| 87 | (`onFileUpload`/`onUploadFile`/`onMultiFileUpload`), `messages`/`chatId`/`sessionId`/`previousResponseId`. |
| 88 | It needs only: botId, customId, sessionId, chatId, restNonce, and a **body-params bag** (envId, model, |
| 89 | temperature, mcpServers…) — NOT the ~70 display fields (aiName, avatars, theme, icon…) that make up the |
| 90 | rest of `state`. Backend already accepts those body params on `chats/submit` (MWAI_CHATBOT_SERVER_PARAMS), |
| 91 | so per-conversation model switching needs no server change. |
| 92 | |
| 93 | **Thin display consumers (read state + call actions; ~11 files):** ChatbotUI, ChatbotBody, ChatbotReply, |
| 94 | ChatbotName, ChatbotHeader, ChatbotTrigger, ChatbotInput, ChatbotSubmit, ChatUploadIcon, ChatClearIcon, |
| 95 | MwaiFiles, plus TerminalMessages. These read a context shape; they don't care WHO provides it. |
| 96 | |
| 97 | **Cross-surface consumers to not break:** `screens/chatbots/Chatbots.js` mounts `ChatbotSystem` (admin |
| 98 | preview); `premium/js/forms/FormSubmit.js` imports the `MwaiAPI` singleton. Both stay valid under the plan. |
| 99 | |
| 100 | ### Extraction strategy (chosen: hook, not rewrite) |
| 101 | |
| 102 | Per Jordy's "keep chatbots light, don't overcomplicate, no behavior change": extract the LOGIC into a |
| 103 | shared hook, keep the DISPLAY components as context-consumers, and give Workspace its own thin context of |
| 104 | the same shape. The chatbot keeps using `ChatbotContext` (now internally delegating), so its behavior is |
| 105 | byte-identical; Workspace supplies a compatible context that reuses the hook + the discussions sidebar. |
| 106 | |
| 107 | - [x] **Extract `useChatSession()`** into `app/js/components/chat/` — the submit/stream/error/lock/upload/ |
| 108 | localStorage machine listed above, parameterized by `{ botId, customId, session, restNonce, bodyParams }`. |
| 109 | `ChatbotContext` refactors to consume it and spread the result into its existing `state`/`actions` (no |
| 110 | API change to any consumer). This is the only behavior-sensitive change; it lands behind the smoke gate. |
| 111 | **Slice one DONE 2026-07-24: `useRestNonce`** (`app/js/components/chat/useRestNonce.js`): nonce state + |
| 112 | ref + tokenManager subscription + `updateToken` + `refreshRestNonce`(start_session), previously |
| 113 | duplicated VERBATIM in ChatbotContext and DiscussionsContext (3 more inline `handleTokenUpdate` copies |
| 114 | removed there). Both contexts consume it now. Verified: full smoke gate 35/0, live popup chat green, |
| 115 | chatbot.js size unchanged (303K). **Slice two DONE 2026-07-24: `useChatUploads`** (single + multi upload state, progress, the 6 |
| 116 | callbacks against mwai-ui/v1/files/upload), moved verbatim; ChatbotContext passes addErrorMessage |
| 117 | as onError and its clear-errors behavior as onBeforeUpload. Gate 35/0, build clean, bundle 303K |
| 118 | unchanged, eslint errors down 54 → 52 (both remaining top offenders pre-exist, incl. the dormant |
| 119 | `setContentType` no-undef in updateComponentConfig, ChatbotContext ~L1284, worth a fix someday). |
| 120 | Uploads hand-tested by Jordy in the browser (2026-07-24): working. |
| 121 | **Slice three DONE 2026-07-24: the full `useChatSession`** (`app/js/components/chat/useChatSession.js`, |
| 122 | 616 lines) composing useRestNonce + useChatUploads and owning messages/chatId/busy/locked/error state, |
| 123 | localStorage persistence, the streaming onSubmit, and the serverReply effect. Surface behaviors are |
| 124 | injected (makeInitialMessages, onQueryStart, onCleared, onActions/onShortcuts/onBlocks), so the |
| 125 | chatbot's eval-actions and shortcut/block handling stay chatbot-only while Workspace passes its own. |
| 126 | ChatbotContext went 1564 → 881 lines and exposes an IDENTICAL state/actions API. Verified: gate 35/0, |
| 127 | live browser chat (start sentence + streamed reply), bundle 303K unchanged. Phase 3 extraction COMPLETE. |
| 128 | - [~] **Verify no chatbot behavior change.** (Gate + live chat green after each slice; the broader |
| 129 | hands-on pass across popup/inline/discussions and 2-3 themes remains before calling the phase closed.) |
| 130 | Original item: Full smoke gate + hands-on popup/inline/discussions across |
| 131 | 2-3 themes. `git diff` on `chatbot.js` bundle behavior must be nil. This is the gate for the whole phase. |
| 132 | - [ ] **Confirm reusables need no change:** ChatbotContent, ChatbotEvents, DiscussionsSystem/UI/Context, |
| 133 | helpers, tokenManager, MwaiAPI — used by Workspace as-is. (Inventory says yes; confirm at build time.) |
| 134 | - [ ] **Bundle discipline.** Workspace gets its own webpack entry (`workspace: './app/js/workspace.js'`, |
| 135 | own `chunkLoadingGlobal`) — the config already has 4 parallel entries, so this is ~5 lines. Verify |
| 136 | `chatbot.js` size does not grow from the `useChatSession` extraction. |
| 137 | |
| 138 | ## Phase 4 — Workspace v1 (Pro only, admins only) |
| 139 | |
| 140 | The pitch: "your own ChatGPT: every model, your API keys, no per-seat subscription." Full-screen, |
| 141 | history left, generous spacing. TypingMind/LibreChat/Mammouth class, inside WordPress. |
| 142 | |
| 143 | **Design direction (Jordy, 2026-07-24):** |
| 144 | - **Structure like TypingMind** (left sidebar with chats, clean center pane, model picker up top), |
| 145 | but "extremely pretty": TypingMind's layout is the reference, NOT its level of refinement. We do |
| 146 | better. Bespoke design system for this surface (CSS variables, not NekoUI widgets: this is a |
| 147 | product surface, not an admin form). |
| 148 | - **Two themes only for v1: dark and light.** A theme toggle, both first-class. No full theming |
| 149 | engine for now. |
| 150 | - **Configurable accent color, Meow blue by default** (`--neko-blue`, hsl 217 80% 42%; brightened to |
| 151 | 62% lightness on dark). A per-user preference like the theme; the mock ships 5 swatches (blue, |
| 152 | brass, teal, rose, violet). Approved by Jordy on the mock, 2026-07-24. |
| 153 | - **Mock approved 2026-07-24** (`labs/workspace-mock.html`, view at |
| 154 | ai.nekod.net/wp-content/plugins/ai-engine-pro/labs/workspace-mock.html). It is the design |
| 155 | reference for the real build. |
| 156 | - **Settings are PER WORDPRESS USER** (user meta, e.g. `mwai_workspace_prefs`: theme, default |
| 157 | env/model, sidebar state…). AI Engine Settings barely configures Workspace itself; later it becomes |
| 158 | the place where admins LIMIT what users can set (allowed envs/models, spend caps per user). For v1: |
| 159 | admins only, so limits can wait. |
| 160 | |
| 161 | - [x] **Skeleton.** SHIPPED 2026-07-24: `module_workspace` toggle (Modules > Chatbots & Knowledge, |
| 162 | Pro), full-screen admin page `admin.php?page=mwai_workspace` under Meow Apps (chrome hidden), |
| 163 | own webpack entry (`app/workspace.js`, 104K, chunk global wpJsonMwaiWorkspace), dark/light + |
| 164 | accent via per-user prefs (user meta `mwai_workspace_prefs`, REST `mwai/v1/workspace/prefs`), |
| 165 | `premium/workspace.css` from the approved mock. Runs on the internal bot `mwai_workspace` |
| 166 | (mwai_internal_chatbot filter, based on the default bot, admin-gated). |
| 167 | - [x] **Chat pane** SHIPPED 2026-07-24 on `useChatSession` (streaming, markdown via the shared |
| 168 | ChatbotContent incl. code copy, error rendering, thinking dots, autoscroll). |
| 169 | - [x] **History sidebar** SHIPPED 2026-07-24 on the discussions REST (list by botId, date groups, |
| 170 | client search, AI titles, inline rename, two-step delete, new chat, auto-refresh after replies). |
| 171 | - [x] **Model picker** SHIPPED 2026-07-24: envs + chat models grouped with provider dots |
| 172 | (catalog built server-side with the smoke-gate filtering); selection is sent per-request as |
| 173 | envId/model body params and remembered in prefs. Advanced params (temperature…) still open. |
| 174 | - [x] **Icon rail + collapsible panel** SHIPPED 2026-07-24 (Jordy's TypingMind-column ask): far-left |
| 175 | rail always visible (Nyao logo, accent + New chat, Chats/Prompts/Agents-soon, Settings + Admin at |
| 176 | bottom, 10px labels); the old sidebar is now a sliding panel (collapse chevron in header; clicking |
| 177 | the active rail item toggles; collapsed state persists in prefs). Theme + accent moved from the |
| 178 | user card into the Settings panel (segmented Dark/Light + swatch row). Agents = coming-soon panel |
| 179 | (chat-nyao-2 robot cat). Access points shipped same day: Admin Bar entry (on by default, toggle in |
| 180 | Settings > Others > Admin Bar), NekoHeader button, Meow Apps menu entry removed (hidden page + |
| 181 | admin_title filter). |
| 182 | - [x] **Prompt library v1** SHIPPED 2026-07-24: per-user saved prompts (title + text, in |
| 183 | `mwai_workspace_prefs`, sanitized server-side, capped 200×20k chars); one click inserts into the |
| 184 | composer (appends if text present) and focuses it; inline editor, two-step delete. |
| 185 | - [x] **Composer toolbar (TypingMind-style features + pins)** SHIPPED 2026-07-24: [+] menu listing |
| 186 | Attach files / Knowledge / MCP Servers / Functions with per-feature pin toggles (pins persist in |
| 187 | prefs; default pinned: uploads + knowledge); an ACTIVE feature's icon always shows even unpinned, |
| 188 | with a count badge. Popovers anchored above the composer, click-outside + focus-composer close. |
| 189 | - [x] **Uploads** SHIPPED 2026-07-24: multi-upload (8 max) via paperclip, drag/drop over the whole |
| 190 | pane (dashed overlay), paste; chips with image thumbnails/doc icons, per-file progress, remove; |
| 191 | image previews inside the sent bubble; over-limit and invalid-file handled with friendly notices; |
| 192 | soft amber warning when the model lacks the vision tag (never blocks). Verified: 6 files at once |
| 193 | (3 PNG read correctly by GPT-5.6 Sol), txt content extracted server-side and answered from. |
| 194 | - [x] **Knowledge** SHIPPED 2026-07-24: brain popover picks an embeddings env per conversation |
| 195 | (None + list, persists as default in prefs), sent as embeddingsEnvId body param (same channel as |
| 196 | chatbots). Verified both ways with the Internal DB: connected → answers the secret passphrase; |
| 197 | None → does not know it. |
| 198 | - [x] **MCP Servers + Functions** SHIPPED 2026-07-24: plug/braces popovers (multi-select from |
| 199 | mcp_envs and mwai_functions_list minus editor-assistant), sent per-request as mcpServers/functions |
| 200 | (function-aware chatbot_query consumes them). Verified: Offbeat Japan MCP listed real latest |
| 201 | posts via GPT-5.6 Sol; Code Engine functions returned the magic word + relayed a failing sensor |
| 202 | gracefully; Gemini mid-thread switch degraded gracefully (functions yes, external MCP no: the |
| 203 | known Gemini gap). Empty/module-off states link to settings. |
| 204 | - [x] **Create Image** BUILT 2026-07-24 (uncommitted, per Jordy's ask; commit with his review): a toggle |
| 205 | in the composer [+] menu (pinnable, photo icon, status "On"). When on, the request carries |
| 206 | tools:['image_generation']: the OpenAI engine maps it to the native Responses image tool, Gemini |
| 207 | Flash Image models output images natively (toggle harmless there), other models get a soft amber |
| 208 | warning naming capable models. Model catalog gained an `image` capability flag (OpenAI model |
| 209 | tools + Google image-generation feature). Images come back as markdown (Reply::set_choices saves |
| 210 | b64 per the image settings), so rendering AND discussion persistence are free; Workspace CSS |
| 211 | upsizes .mwai-image to 480px with radius+shadow (the shared renderer inline-caps at 220px, hence |
| 212 | !important). Live QA all green: GPT-5.6 Sol generation; GPT multi-turn edit (snow + lantern added |
| 213 | to the same scene); upload-an-image + edit (bicycle→red scooter, scene preserved); Gemini 3.1 |
| 214 | Flash Image generation + in-context edit (watercolor of the same kitchen); Claude soft warning; |
| 215 | trail shows "Generating image..."; images + per-message model chips survive reload. |
| 216 | - [x] **Stop button** SHIPPED 2026-07-24: AbortController in useChatSession (stopGeneration); |
| 217 | partial reply kept with a STOPPED chip; the turn persists through discussions/truncate, which now |
| 218 | also creates the row for first-turn stops and keeps stopped/extra.model through sanitization. |
| 219 | - [x] **Per-message model attribution** FIXED 2026-07-24: assistant messages store extra.model |
| 220 | server-side (discussions), live messages stamped at submit time; chips now show the model that |
| 221 | actually answered (verified o3 vs GPT-5.6 Sol vs Gemini 3.1 Pro in one thread). |
| 222 | - [x] **Real errors for admins** 2026-07-24: streaming errors pass the actual provider message to |
| 223 | manage_options users (visitors keep the generic one). Found + fixed en route: error messages were |
| 224 | invisible in the Workspace (its own admin-notice-hiding CSS matched the .error class; renamed to |
| 225 | .is-error), and the OpenAI Responses API sent temperature to o-series models (400) — now gated |
| 226 | like chatml via o1-model/no-temperature tags. |
| 227 | - [x] **QoL batch 2026-07-25:** generated-image lifetime now honors the Files > Expiration setting |
| 228 | everywhere (the "Never" default silently fell through to the 1h uploaded-files TTL; fixed in |
| 229 | Reply::save_temp_image_from_b64 for chatbots AND Workspace, replacing an earlier |
| 230 | workspace-only 10y override); pinned conversations (prefs.pinnedChats, PINNED sidebar group, pin icon in row |
| 231 | actions); export-as-Markdown icon in row actions; ChatGPT-style time chips between messages |
| 232 | ("Today 6:36 PM", stored timestamps now saved on discussion messages + kept by truncate); |
| 233 | a "..." menu on AI messages with the message time and **Branch in new chat** (copies the thread |
| 234 | up to that message into a new server-backed discussion via the truncate endpoint); keyboard |
| 235 | shortcuts (Cmd/Ctrl+K new chat in capture phase so WP's own palette stays closed, Esc stops |
| 236 | generation); hint line documents them. All verified live; smoke gate 35/0. |
| 237 | - [x] **This WordPress (MCP tools as functions)** SHIPPED 2026-07-25 (Jordy's ask; bridge over |
| 238 | self-MCP for reachability + all-provider coverage): globe entry in the [+] menu with a master |
| 239 | toggle ("Work on {site}") and per-category checkboxes (Core preselected; counts + tool-name |
| 240 | tooltips), pinnable, persisted (prefs wpMode/wpCategories). Client sends `wpTools:[categories]`; |
| 241 | workspace.php stashes it in mwai_internal_chatbot (admin + workspace-bot only), converts the |
| 242 | matching `mwai_mcp_tools` schemas into functions on mwai_chatbot_query (new |
| 243 | `Meow_MWAI_Query_Function::from_raw_schema` + rawSchema passthrough in all four serializers), |
| 244 | and executes via the `mwai_mcp_callback` filter in an mwai_ai_feedback handler (JSON-RPC/MCP |
| 245 | envelopes unwrapped, Throwable-contained, int call id since handlers type `?int $id`). Tool |
| 246 | catalog is served by REST (`workspace/wp-tools`) because core registers its tools filter on |
| 247 | rest_api_init only. maxDepth raised to 20 for workspace scope (find→read→edit→verify burns 5+). |
| 248 | Verified live: GPT-5.6 Luna counts+lists posts; Claude Opus 5 full write flow (find draft, |
| 249 | append haiku, read back, ID+permalink); Gemini 3.5 Flash Interactions protocol correct |
| 250 | (function_result+call_id+previous_interaction_id; the model itself over-calls tools — its |
| 251 | quirk, stray write failed safely on validation). |
| 252 | - [x] **Anthropic multi-round function calling FIXED plugin-wide 2026-07-25** (found via the |
| 253 | bridge, affects chatbots too, likely the real cause of Jordy's "Claude re-calls tools" report): |
| 254 | engines/core.php cleared the feedback blocks on every recursion, so stateless providers |
| 255 | (Anthropic, Chat Completions, classic Gemini) lost every earlier tool exchange of the turn — |
| 256 | round 3 sent only round 2's pair, the model re-issued identical calls and tripped the loop |
| 257 | detector. Blocks now ACCUMULATE for stateless replies (engines replay assistant tool_use + |
| 258 | tool_result per block, rebuilding the chain) and still reset for stateful ones (OpenAI |
| 259 | Responses, Google Interactions, Assistants) which hold history server-side. |
| 260 | - [x] **Claude Opus 5 added 2026-07-25** (claude-opus-5, family claude-5, no-temperature, |
| 261 | 1M ctx / 128k out, $5/$25; 'latest' moved off Opus 4.8). Verified live in the Workspace. |
| 262 | - [x] **Image lifetime in the lightbox** SHIPPED 2026-07-25 (Jordy's ask): the lightbox bar shows a |
| 263 | live 1s-ticking countdown chip driven by the temp-file record ("Expires in 57m 41s", amber under |
| 264 | 10 min, "Kept forever" when the setting is Never, green "In Media Library" for attachments) via |
| 265 | REST `workspace/image-info` (URL → expires, server-clock offset applied client-side), plus a |
| 266 | **Save to Media Library** action via `workspace/image-persist`: copies the file into a real |
| 267 | attachment (wp_upload_bits + wp_insert_attachment + metadata), rewrites the URL in the stored |
| 268 | discussion messages AND the live thread, then deletes the temp file record. Verified live: |
| 269 | generated with a 1h TTL → countdown ticks → saved → chip flips to In Media Library → reload |
| 270 | serves the attachment URL from the rewritten history. |
| 271 | - [x] **Batch 2026-07-25 (post-review):** WordPress Tools rename + official WP mark icon (the |
| 272 | globe didn't read as WordPress; filled path overrides the stroke-based icon set); advanced |
| 273 | model params in the topbar (temperature slider, hidden-locked on no-temperature models; |
| 274 | reasoning-effort segmented control on reasoning models; per-user prefs.advanced; build_envs now |
| 275 | exposes the reasoning/no-temperature tags); server-side conversation search (the shared |
| 276 | discussions chats_query 'preview' filter now matches title too, and the UI list endpoint takes |
| 277 | a `search` param — usable by the chatbot discussions UI as well; Workspace sidebar debounces |
| 278 | into it past the loaded 50); Settings > Workspace section (workspace_image/_wp_tools/_mcp/ |
| 279 | _functions/_knowledge availability toggles, defaults on, enforced server-side in chatbot_query + |
| 280 | wpTools stash + hidden in the composer). Verified live incl. toggling Create Image off. |
| 281 | Full gaps list mirrored in ~/Desktop/WORKSPACE-MISSING.md (permission dialogs = top item). |
| 282 | - [x] **Freemium split** SHIPPED 2026-07-25 (Jordy's call, matching the strategy discussion): |
| 283 | the Workspace moved to the free core (classes/modules/workspace.php, app/workspace.css, |
| 284 | Meow_MWAI_Modules_Workspace, instantiated from classes/core.php on module_workspace, no more |
| 285 | requirePro on the module toggle). Knowledge, MCP Servers and Functions stay Pro: their Settings |
| 286 | checkboxes are requirePro, the server computes effective flags (option AND is_registered) and |
| 287 | strips them from the query, and unregistered installs see them as tasteful locked "Pro" rows in |
| 288 | the [+] menu linking to meowapps.com. WordPress Tools (core, free MCP) and Create Image stay |
| 289 | free. Settings > Workspace gained a Roles block stating admins-only for now. |
| 290 | - [x] **Tool approval dialogs** SHIPPED 2026-07-25 (the top item of the gaps list): any |
| 291 | WordPress Tool that can change the site (MCP readOnlyHint annotations; name-heuristic fallback, |
| 292 | unknown verbs count as writes) pauses the turn via Meow_MWAI_ApprovalRequiredException. The |
| 293 | pipeline turns it into an in-thread approval card (custom UI: tool name, args JSON, Deny / |
| 294 | Allow Once / Always Allow in This Chat), stores nothing while pending, and the decision resumes |
| 295 | the turn through the edit-message truncate+resubmit primitive with the decision riding along |
| 296 | (wpToolsAllowed/wpToolsDenied params). A per-turn transient cache returns already-executed |
| 297 | write results on re-runs so approving call 2 never re-executes call 1 (no duplicate posts). |
| 298 | Verified live: create paused, Allow Once created exactly one post; Deny handed the refusal to |
| 299 | the AI ("no changes were made"); Always Allow let a two-post turn complete with one card. |
| 300 | Stale-closure gotcha: the resume MUST go through a latest-ref or it submits pre-decision atts. |
| 301 | - [x] **Conversation folders** SHIPPED 2026-07-25: per-user named folders in the sidebar |
| 302 | (prefs.folders, max 30, sanitized server-side), one folder per conversation, assigned via a |
| 303 | folder menu on each row (with inline "New folder..."), collapsible groups with count chips, |
| 304 | hover rename + two-step delete (deleting releases the chats back to the date groups), folder |
| 305 | membership wins over date groups and Pinned, folders with no matches hide during search. |
| 306 | Verified live incl. collapse-state persistence across reloads. |
| 307 | - [ ] **Pinned conversations vs retention:** the discussions cleanup deletes old rows regardless |
| 308 | of Workspace pins (a pinned chat vanished after retention ran). Either exclude pinned chatIds |
| 309 | from cleanup or prune dead pins from prefs. Small, worth doing with folders. |
| 310 | - [ ] **Roles beyond admin** parked (Jordy 2026-07-25): needs a real permissions design first, |
| 311 | because by default a user could pick any environment, model, and feature. The settings surface |
| 312 | for restricting envs/models/features per role is the prerequisite, not the role toggle itself. |
| 313 | - [~] **v1 polish:** per-conversation cost badge wired. Still open: folders (pins shipped), |
| 314 | self-hosted fonts (Google Fonts CDN for now, admin page only), image previews for reloaded |
| 315 | conversations (server stores text only). |
| 316 | **QA 2026-07-24 (all green):** two full passes incl. adversarial (corrupt PNG rejected with a |
| 317 | friendly error, 8-file limit notice, stop before first token, [ERROR] hook, light+dark for all |
| 318 | new UI, pin/unpin round-trips); smoke gate 35/0 after each batch. |
| 319 | - [ ] **Later (v2+):** roles beyond admin, front-end variant, agents, shared/team conversations. |
| 320 | |
| 321 | ## Standing rules for this program |
| 322 | |
| 323 | - One commit per finished, tested item; smoke gate (`node labs/tests/test-smoke.js`) before each |
| 324 | chatbot/engine-path commit. |
| 325 | - Bundles stay out of fix commits (Nekofy sweeps them at release). |
| 326 | - Anything that grows `chatbot.js` for Workspace's benefit is wrong; separate entry. |
| 327 | - Tick items here as they land; /pulse may surface the top unchecked item. |
| 328 |