v0.4.3 — Foundation: PHPStan, security hardening, SSE parser + modal carve
No-behaviour-change maintenance release. Four small waves of cleanup that make the next round of feature work safer to touch.
Foundation (CI)
- PHPStan level 5 added to the lint job, 52 pre-existing errors fixed (return-type narrowing on
ToolProviderInterface::getTools, deadis_wp_errorchecks,?? new \stdClassprecedence). Builds now fail on new static-analysis regressions. - Jest wired up for client-side helpers; first suite covers
editor/diff.js(17 tests).
Security hardening
- Prompt-injection defence on selection context.
ChatEndpoint::buildSelectionPreamblenow strips control characters and wraps user-selected text in a labelled tilde fence so block content can't smuggle instructions into the system prompt path. - Try/finally on temp files in
ImageGenerationToolProvider::sideloadBinary— orphan files no longer accumulate when the sideload fails mid-flight.
SSE parser consolidation
- Extracted
SseStreamReader(131 LOC) — every streaming provider was hand-rolling the same curl + line-buffereddata:parser. Anthropic/OpenAI-compatible/Gemini now delegate, shedding ~150 LOC of duplication. - Fixed a real precedence bug in Gemini's mime-type guess (
'image/' . pathinfo(...) ?: 'jpeg'defaulted to the whole string when no extension was present).
assistant-modal.jsx carve-up
- 2577 LOC → 778 LOC (−70%) split across 8 focused modules:
MicButton,ReadAloudController,UndoContext,ToolCallFallback,Messages,Composer,SidePanels,skills-cache. Render order, hook order, and ref identities preserved end-to-end; verified by manual smoke (list → edit → undo) on generogrowth.
Misc
Plugin::polylangVoiceLanguagesbarecatchreplaced with logged\Throwablecatch (was hiding silent failures).MessageLooptoken accumulators now cast through(int)so providers returning numeric strings can't poison the running total.new static()→new self()inPlugin::getInstancefor tighter typing.
Full changelog: v0.4.2...v0.4.3