Changelog
All notable changes to Infinia (蜂语 / FengYu). The canonical, always-up-to-date source is the repository's CHANGELOG.md — this page is generated from it on every docs build (see docs/scripts/sync-changelog.mjs). Do not edit this file directly; edit the root CHANGELOG.md instead.
Latest release
v4.0.0-beta.4 — 2026-08-18 · GitHub release
[Unreleased]
♻️ Changed
- Notification entry moved into the account menu. The sidebar's standalone bell button is gone; the notification center now opens from a "Notifications" item in the menu behind the username (with a live unread count chip), and the panel drops in exactly where the account menu was — including the rail-sidebar anchor. Discoverability of new notifications is kept by an unread beacon dot on the user avatar while any notification is unread. The panel itself is unchanged: same live SSE feed, mark-read, and navigation behavior.
🐛 Fixed
- Update checks never ran in the current shell — and now repeat periodically. The startup update probe lived in
StatusBar, a component the current shell layout no longer renders, so no update was ever detected outside the About page's manual button. The probe (plus a new idempotent 6-hour re-check for long-running sessions) now lives inAppShell, the one component mounted on every route. When a newer release is found, the sidebar's About (ⓘ) entry carries a red beacon dot (with the available version as its tooltip) until the update is installed — mirroring the avatar's unread-notification dot.
✨ Added
- Update plugins from a local package, with a version-aware confirmation. Installing a locally uploaded
.fypover an already-installed plugin id now behaves as a first-class update flow instead of a silent replace. New pre-install inspection endpoints (POST /api/plugin-market/inspectand its desktop path twin/inspect-native) read the incoming package's manifest without installing and return whether the id is installed plus the version step (upgrade/same/downgrade, ordered by the same semver comparator behind the catalog's update badge). The marketplace UI routes every local.fyppick through a confirmation dialog showinginstalled → incomingversions — warning on a downgrade or same-version reinstall — and the detail drawer of an installed plugin gains an "Update from local package" entry; the upload itself keeps the existing update gate (stop the running worker, atomic directory swap, enabled state preserved). This also gives non-catalog third-party plugins a working update path: catalog-based/{id}/updatecan only resolve plugins listed in a configured marketplace. - Host-side unified notifications. One pipeline now reaches the user everywhere: a persisted notification center (
/api/notifications— create/list/read/delete with a 200-row retention window), a live ticket-authenticated SSE stream (GET /api/notifications/stream) that every connected shell subscribes to, in-app toasts while the app is visible, and native OS notifications through the Electron shell when it is not (clicking one focuses the window). Producers: the pluginnotifyhost bridge — now gated on the declarednotificationspermission, which was previously advisory-only and always fell back to the iframe-internal center — and agent run termination (completed/failed; user cancels stay silent). The sidebar gains a bell with an unread badge and a notification-center panel. The official plugins whose UIs callnotify(email, excel, offlinepython) now declare thenotificationspermission, so their notifications ride the real host surface instead of the iframe-local fallback.
🐛 Fixed
- About page update row: the unsigned-install confirm showed alongside the "Update now" button and "Cancel" did nothing. The inline confirm popover's
v-elsewas chained to the download-progress bar'sv-ifinstead of the!confirmingbutton, so the popover rendered whenever a download wasn't in flight — overlapping the "Update now" button, ignoring Cancel (its visibility never depended onconfirming), and after clicking "Update now" only "Continue/Cancel" remained. The popover now pairs with the!confirmingbutton and the progress bar is an independent element, restoring the intended gate: "Update now" → warning + Continue/Cancel → download progress. - Plugin/skill detail drawer stayed open on a stale snapshot after marketplace operations. The drawer held the row object captured when it was opened, so after an operation refreshed the list the drawer kept rendering the old version/enabled label — and after an uninstall it stayed open with dead action buttons (a catalog plugin's row even survives uninstalling). Uninstalling a plugin or skill from the drawer now closes it, and every other operation (update, toggle, local-package update) re-points the drawer at the refreshed row — the skill preview also reloads after a skill update.
- Email Center contacts page: the New-contact form and the tag manager were unreachable. In the real host iframe the page usually renders in the narrow single-column layout (the
.fy-plugin-pagecontainer sits below the 1000px breakpoint once the plugin's own drawer is subtracted), which stacks those cards below the tall contact list — and wheel scrolling over the list did nothing because.contact-list-scroll'soverscroll-behavior: containblocked scroll chaining to the page even with an empty list. The containment is removed (the wide two-column layout never scrolls the document, so it gains nothing from it); wheel/touch scrolling now chains and the below-fold cards are reachable again.
[4.0.0-beta.4] — 2026-08-18
✨ Added
- Unified option-source standard for flow-node inputs. Three declarative candidate kinds replace the excel-only vocabulary: static
options; CATALOGsource(a plugin list method the host fetches and maps through value/label/labelSecondary, single- or multi-select); and CONTEXTcontext(datasets derived at edit time from another input's value — e.g. the workbook path → sheets/columns via the analyze RPC — consumed throughoptionsFromContextreferences, including row-field keying). The node inspector loads catalogs on focus with a manual fallback, renders context triggers beside their input, and feeds datalists; the run form's x-fengyu-enum annotations now share the same fetch path. Excel migrated to context/optionsFromContext (the analyze widget and workbook-sheets/workbook-columns annotations retire); Email declares catalog sources for 发件账号 and 收件/抄送分组.
♻️ Changed
- Explicit flow-node declarations (canvas no longer derives node forms from AI-tool schemas). Plugins declare nodes in
manifest.flowNodes[](tool binding, label, color, icon, typedinputswith widget configs — text/number/switch/select/textarea/json/analyze/rows — and namedoutputs); the host ships built-in declarations inflow-nodes/builtin.json(currentlyjson_format). The tool descriptor API carries the declaration through, the canvas palette lists ONLY declared nodes, node cards render declared labels/icons/colors and NAMED OUTPUT PORTS (Flowise outputAnchors), and the node inspector renders the declared widgets (descriptor defaults apply on drop, e.g.action=add). Non-declared tools remain available to the model but never appear as canvas nodes. Excel (复杂拆分/执行拆分) and Email (批量发送/放行发送) ship the first declarations; legacy saved graphs keep the schema-derived fallback rendering.
♻️ Changed
- 1:1 Vue replica of Flowise's AgentFlow v2 canvas (React removed). The flow builder's canvas now mirrors the dark AgentFlow canvas from the screenshot, rebuilt in pure Vue on vue-flow after a full read of Flowise's source: node-type colors from
tokens.tstint each card via MUI'sdarken(color, 0.8)formula (hover 0.7, border alpha .5/.8/selected), the 40px radius-15 icon badge, NodeInputHandle's 5×20 color bar, the hover-revealed chevron output handle, AgentFlowEdge's gradient bezier strokes with hover delete buttons, the #1a1a1a dot grid, bottom-center Controls with snap/background toggles, and the dark MiniMap. The Delete key is now history-aware (vue-flow's built-in deleteKeyCode bypassed the undo stack). react/react-dom/reactflow and the React island are gone. - One-click workflow templates & ordinary-user run form. The canvas ships a built-in template gallery (empty-canvas state and the workflow library); the first template, Excel split → batch email, pre-wires
excel_complex_config → excel_execute → email_send_batch → confirm_send, pre-maps nested output references (confirmation.confirmationId), and ships a run form a non-technical user fills in: file inputs render upload pickers, shared output folders are minted automatically, and account/recipient-group inputs render live dropdowns fed by plugin list tools (email_accounts_list,email_tags_list) instead of numeric ids. - Multi-rule complex split configuration. The template's split rules input is a dynamic row editor — one row per worksheet (
工作表+拆分列), bound whole asentries: \{\{inputs.rules\}\}— and an omittedheaderIndexnow defaults to the first header row in the Excel plugin, so neither the canvas nor the model needs row numbers. Uploading a workbook analyzes it through the plugin (excel_analyze) and turns the sheet/column fields into datalist candidates, so users pick their real sheet and column names instead of typing them blind; the union fallback keeps fields free-text. - Run-scoped file grants for workflows.
POST /api/agent/runandPOST /api/workflows/{id}/runaccept afilesarray (pass-through picker grants, a native path, orcreateSharedDirectory). File-class inputs travel as@file:<name>placeholders that the host swaps for the current plugin's FileRef at dispatch (RunFileContext+AiToolFileInjector.bindRunFilePlaceholders), and a host-minted shared scratch directory is granted live to every eligible plugin — so an Excel split step's outputs are readable by a later Email step on every sandbox backend. confirm_sendas an approval-gated AI tool. The email plugin now exposesconfirm_sendwith effectexternal: chat keeps its confirmation card, while visual workflows mark the send step requires approval, so every permission mode except full-access pauses for a one-click human go-ahead before dispatching the batch.- Nested node-output mapping. The inspector's output picker flattens one nesting level into dotted paths (
confirmation.confirmationId), so a follow-up step can map a specific nested field instead of the whole result object. - e2e smoke: full template chain.
scripts/e2e-smoke.shnow boots a local SMTP sink and drives the whole scenario — uploaded workbook + shared dir + run file grants + approval-gatedconfirm_send— asserting four completed steps and a delivered message. - Permission rules & lifecycle hooks (grok-build-informed agent runtime hardening). A user-configurable guard now sits between the permission modes and every AI tool call, evaluated as
PreToolUse hooks → deny rules → ask rules → allow rules → mode default. Rules follow theCommand(git status)/Tool(excel_*)/Effect(read)/Mcp(server__*)/WebFetch(domain:…)grammar with order-independentdeny > ask > allowprecedence, per-segment shell-chain checks (an allow must cover every segment ofa && b | c; deny/ask match any), and a dangerous-command floor (rm,sudo,git push, …) that allow rules cannot bypass. Lifecycle hooks extend the same pipeline: command hooks (event JSON on stdin; exit 2 denies with stderr as the reason; a stdout{"decision":"deny"}gate document denies on any exit code) and HTTP callbacks forpre_tool_use,post_tool_use,post_tool_use_failure,run_complete, andrun_error, failing open when a hook crashes or times out. Configured in Settings (runtime & security), shared by chat and agent runs; a denied call fails its step with the rule's reason so the model can replan around it. - Plugin-contributed hooks with an enable≠trust gate. A
.fyppackage may shiphooks/hooks.json(grok-shaped or FengYu's flat list); installing or enabling the plugin never activates them — the user trusts the plugin explicitly viaPOST /api/plugin-hooks/{id}/trust, and untrusting takes effect on the next call. Trusted hooks run with the plugin install dir as working directory plusFENGYU_PLUGIN_ROOT/FENGYU_PLUGIN_DATAenv andplugin/<id>/<name>namespacing. - Workflow schedules. Published workflows run on schedules (
task_scheduletools +/api/agent/schedulesREST): 60-second minimum interval, 50-schedule cap, 7-day expiry, optional immediate first fire, delayed one-shots viarecurring:false. Scheduled runs are ordinary background tasks (task_output/wait/kill apply); schedules are in-memory by default. - Host-level background tasks. Long workflows no longer block the synchronous tool slot:
task_submit_workflowreturns ataskIdimmediately,task_outputpolls or blocks,task_waitwaits on up to 20 tasks (any/all), andtask_killstops a runaway task with cooperative cancellation first and SIGTERM → SIGKILL escalation for process-backed ones. The same registry backsGET /api/agent/tasksfor the UI. - Run-history search, fork, and rewind.
GET /api/agent/runs?q=…searches goal/summary/error text;POST /runs/{id}/forkre-runs a finished run's plan as a fresh peer;POST /runs/{id}/rewind {keepSteps}truncates the plan to its first N steps (inheriting only earlier completed executions) and resumes under plan review — dropped steps' side effects are documented as not rolled back. Runs also record the plugin-sandbox posture they were created under and refuse replay while the host runs unsandboxed, so isolation can never silently weaken. - Read-only batch capability.
POST /api/agent/batchacceptscapabilityMode: "read-only", restricting every child run toread-effect tools — parallel research/review tasks can be declared mutation-free up front. - Cross-session memory (experimental, off by default).
memory_remember,memory_search,memory_list, andmemory_forgettools over a per-user store with keyword × 7-day-half-life recency ranking; relevant memories are injected into agent planning context when the Settings toggle is on. - Usage metrics with OTLP export. Agent runs and per-tool steps are counted and timed through Micrometer (
fengyu.agent.runs/fengyu.agent.steps/fengyu.agent.run.duration), readable at/actuator/metrics; settingmanagement.otlp.metrics.export.urlstreams the same metrics to an OpenTelemetry collector for production observability (the registry stays dormant with no URL). - Marketplace checksum pinning. With "require plugin checksums" enabled, installing a plugin demands its
.fyp.sha256sidecar (fengyu CLI packager output) and rejects mismatches — supply-chain hardening for third-party packages. - Computer use (ChatGPT-desktop-style screen control). Desktop builds now expose a
computer_*AI tool family driven byjava.awt.Robotinside the backend JVM, with the same behavior on Windows, macOS, and Linux: screen capture with Hi-DPI scale reporting (the PNG reaches vision models through the same media bridge asbrowser_screenshot), display/app enumeration, application launch and activation (PowerShell on Windows with.exe-stripped process-name and window-title fallback matching;open/osascript on macOS;gtk-launch/wmctrlon Linux), mouse move/click/double-click/drag/scroll, keyboard typing with clipboard-paste fallback for non-ASCII text, key combos, and a settle wait. Input-injecting calls areexternaleffects that pass the per-turn approval gate; observing calls areread. A Settings card (Runtime & security → Computer use,computerUseEnabled, default on) shows the capability probe and can hide the whole family per registry snapshot. Windows needs no extra permissions (UAC/elevated windows stay OS-protected); macOS needs Screen Recording and Accessibility. Display-less hosts degrade to"computer use unavailable"envelopes. A dedicated cross-OS workflow (.github/workflows/computer-use-ci.yml) runs the family's unit tests on Windows/Linux/macOS and the real-display Robot integration test on a Windows runner. - Dynamic MCP server runtime. MCP servers can now be added, edited, removed, tested, and called from Settings or the REST API. STDIO, SSE, and Streamable HTTP connections are persisted, initialized immediately, discovered with
tools/list, and injected into the live AI tool catalog without restarting FengYu; credentials are kept in a protected sidecar file and are never returned. - Reusable workflows with one manual/AI execution path. Visual DAGs can now be persisted with JSON Schema inputs, loaded and revised in the canvas, run manually with typed
\{\{inputs.*\}\}bindings, and published as dynamically discovered Spring AI tools. Manual and model-triggered calls share the existing dependency-aware runner, tool registry, durable run history, SSE events, and audit trail; saved definitions reject nested workflow tools to prevent recursion. Definitions persist the canvas node layout, so a saved graph reopens exactly as arranged; the workflow library can save a copy of any definition as a starting point, and the run panel shows each step's actual output. - One-call complex Excel split configuration.
excel_complex_configaccepts anentriesarray declaring the complete rule set in one call (one rule per sheet,columnNameresolved to a column index against the analysis) plus an optionalfilePaththat analyzes the workbook in the same call and leaves the session inCOMPLEXmode — a FengyuFlow canvas needs justexcel_complex_config → excel_execute. Re-running the same call is idempotent (entries replace, not append), duplicate rules for one sheet are rejected up front, and the shared AI session is now pinned and synchronized so concurrent workflow steps cannot corrupt it.excel_executewith a blankoutputDirwrites into the plugin's default output folder — injected by the host as a plain sandbox-writable path, deliberately not registered as a file grant because grant registration restarts stateful plugin workers and would destroy their in-memory sessions mid-flow.scripts/e2e-smoke.shcovers the single-node complex-split workflow end to end.
♻️ Changed
- The canvas is now a literal port of Flowise's canvas (vue-flow removed). The flow builder's stage runs Flowise's own React + React Flow stack as an island inside the Vue shell: ported
CanvasNode/ButtonEdge/StickyNotecomponents and the chatflow canvas wiring live insrc/flowise/, mounted throughFlowiseCanvasHost.vue. The Vue side stays the single source of truth — nodes/edges flow in as props and every renderer mutation (select/position/dimensions/ remove, connects, note edits, edge deletes) travels back through controlled-mode change channels (applyCanvasNodeChanges/applyCanvasEdgeChanges). reactflow v11's controlled mode requires the parent to store measured dimensions back onto nodes — miss it and nodes stay invisible with edgeless rendering; that round-trip is now covered by unit tests. All@vue-flow/*dependencies are gone. - Flowise canvas replica. The flow builder's canvas now mirrors Flowise's CanvasNode design (source-verified against the archived FlowiseAI/Flowise repo): 300px node cards with a circular white icon badge and per-category accent colors, grey "Inputs"/"Output" section bands, and Flowise's handle states (red on invalid connection, green on a valid target). Edges are Flowise ButtonEdge replicas — smooth bezier curves with a midpoint delete button — and structural edits (node/edge/note add, remove, moves) gained undo/redo (toolbar buttons, ⌘/Ctrl+Z, ⇧⌘Z / Ctrl+Y, 50-step cap).
- Fixed silently dropped canvas edges. vue-flow re-validates programmatic edge assignments through
isValidConnectionAFTER the parent state already contains the new edge — the builder's duplicate check therefore rejected every auto-connected, restored, or linked edge. Validation now runs against the store-supplied pre-update edge list. (The same latent bug existed in the old AiAgent canvas.) The Flowise edge-delete button also needed an explicitpointer-events: all— vue-flow's edge-label layer disables pointer events for its whole subtree. - One tool-call mode for AI Chat and flows (Flowise chat-with-your-flow). The flow builder ships a Flowise-style docked chat panel: sending a message binds the turn to the flow being edited (
POST /api/ai/chatnow acceptsworkflowId), and the backend exposes that flow — draft or published — to the model asrun_current_flow, an ordinary tool call in the same chat tool-call loop that powers AI Chat (identical permission modes, approval gates, and SSEtoolevents; the panel auto-saves pending edits first). AI Chat reaches published flows through the same loop viarun_workflow_<id>, making chat and the canvas peers over one runtime. Internals: request-scopedBoundToolsContextmerges bound tools into each turn's registry snapshot (cloud + Ollama backends), andWorkflowExecutionService.executeForAi(…, requirePublished)admits drafts on the bound path only. - Flow builder rebuilt Flowise-style (flows refactor). The visual canvas moved out of the AI Agent page into a dedicated Flows section (
/flows): a library page listing saved flows as cards (open / duplicate / delete) plus templates, and a full-workspace builder with a categorized, collapsible node palette (search, drag-to-add), a right-hand node configuration panel, Flowise-style sticky notes (annotation-only nodes), per-category node accent colors, and the same run dialog / execution panel / settings drawer as before. The AI Agent page (/agent) is now the pure AI-planning view;AiAgent.vueshrank from ~3300 lines to ~350 by extracting the shared SSE run engine (useAgentRunStream) and the canvas intoFlowBuilder.vue+ focused child components. - Workflows persist the authored canvas graph. Definitions gained an optional
graphfield (ai_workflow.graph_json) storing the exact nodes/edges/sticky-notes as arranged — node ids are stable across save/reload, so\{\{node.<id>.result\}\}references survive.plan+layoutremain the compiled execution contract; older definitions without a graph still reconstruct the canvas from the plan. The backend validates graph shape (nodes/edges lists, size caps) without interpreting node internals. - FengyuFlow fails fast instead of failing mid-run. Saving a workflow now rejects
\{\{inputs.*\}\}references that the input schema never declares (previously such a graph saved fine and failed at every run) and caps definitions at 64 steps. The manual-run dialog blocks the start until required workflow inputs are filled, host validation messages surface localized in the UI, and unsaved canvas work is protected by confirm dialogs on switch/new/delete plus a browser close guard.scripts/e2e-smoke.shnow probes workflow create/layout/manual-run/ publish-as-AI-tool/delete end to end against a real backend. - Plugin tool failures now carry their reason. Most official plugin methods report failure as
success:falseplus a localizedsummarywith noerrorfield; the host's result normalizer only readerror, so agent runs and workflows showed a useless "Tool reported failure". The normalizer now falls back tosummary, surfacing the actual localized diagnostic. - All JavaScript areas now install through Yarn 4 (corepack).
frontend/,desktop/electron/,docs/, and the four@infinia/*toolchain packages pinyarn@4.18.0viapackageManager+.yarnrc.ymland install from committedyarn.lockfiles (--immutablein CI); thepackage-lock.jsonfiles are gone and npm lockfiles are deprecated for repository areas. Onlyfengyu initstill scaffolds npm-based projects for third-party plugin authors, and the release workflows cache and install with yarn accordingly.
✨ Added
- Three-control complex-split rows + in-node workbook analysis.
excel_complex_configentry rows in the flow builder now show only Sheet 名称 / 拆分列 / 整表拷贝 (headerIndex and columnIndex fold away asx-fengyu-advanced; the new per-entrycopyEntireSheetboolean is the canvas-friendly spelling of the (-1, -1) whole-sheet convention, honored by the Excel worker). The node's File Path input gains an analyze button (pluginanalyzeRPC, isolatedcanvas-<nodeId>session) whose sheet/column results feed datalist pickers — the 拆分列 candidates narrow to the chosen sheet.
🐛 Fixed
- Settings page was a blank screen in production builds (dev worked). Four i18n messages (
settings.mcp.placeholderEnvironment/placeholderHeaders/placeholderArgumentsObjectandsettings.guardHooksHint) embed literal JSON examples, and their{/}was parsed by vue-i18n's message compiler as placeholder syntax — INVALID_TOKEN_IN_PLACEHOLDER. The dev build tolerates the compile error (warn + raw-message fallback), but the production build throwsSyntaxError: 2from the vendored Vue bundle, killing the whole Settings view (the shell survives, the page body dies). The literal braces are now escaped with vue-i18n's{'{'}/{'}'}literal syntax in both locales. - A "fresh" directory silently adopted the config from
~/.fengyu— the setup wizard never appeared.DataSourceConfigServiceprobed legacy locations (<cwd>,~/.fengyu) and migrated theirdatasource.propertiesinto the runtime root on load, so once any run had saved a config under~/.fengyu, every new portable extraction (or any directory the desktop shell pins via-Dfengyu.runtime.dir) booted straight into APP mode and the main window. An explicitly pinned runtime root now stays isolated — fresh directory, fresh setup wizard; unpinnedjava -jarruns keep the legacy migration for upgrades. - Setup wizard's restart detection raced the dying SETUP backend (main shell dead on arrival after setup). The wizard polled
/api/setup/statusforinitialized:true, but the still-exiting SETUP backend answers exactly that for its ~1s grace period (the config was just persisted), so the SPA navigated to the main shell while every app API still 404'd — and once APP mode came up the endpoint 404s by design, so the poll could also never succeed and timed out. The wizard now treats that 404 as the "restarted into APP mode" signal (same contract as the SPA router guard) and ignores the ambiguous 200. - Desktop: launching on a configured machine (or right after finishing the setup wizard) killed the healthy backend with "setup status request failed: HTTP 404".
/api/setup/**is token-bypassed and therefore only mapped in SETUP mode, so an APP-mode backend answers 404 there by design — the desktop shell's startup probe treated that as fatal (and the SETUP→APP restart after the wizard hit the same probe), while the SPA router guard already read 404 as "already configured". The desktop probe now does the same: 404 → APP mode, other statuses stay fatal. - Drag-connect on the canvas. Flowise's NodeInputHandle color bar (the 5×20 vertical strip) broke vue-flow's drop hit-test —
elementFromPointlanded on the bar's inner div instead of the handle, so no connection ever fired. The input handle is now a small node-colored dot with no inner content, which also removes the vertical line from every node. - Canvas follows the app theme. The hardcoded #1a1a1a surface, white text, and dark controls/minimap are gone — nodes tint per Flowise's useNodeColors formulas on BOTH themes (dark:
darken(color, 0.8), light:lighten(color, 0.9)) and the surface, controls, minimap, and text use the Vuetify theme tokens, switching with the app's light/dark setting. - Empty-canvas "添加节点" button and template cards were unclickable. The empty-state overlay rendered inside VueFlow's default slot, where the interactive pane stacks above slot content and swallowed every click. The overlay now lives outside the VueFlow root (pointer-events only on its buttons), so both the centered add-node button and the built-in template cards respond.
- Canvas test runs now bind
\{\{inputs.*\}\}. An unsaved canvas run used to post the compiled plan with literal placeholders, so tools received\{\{inputs.x\}\}strings; the client-side compiler now binds run-form inputs with the same semantics as the backend (exact reference → typed value, embedded → rendered text) while saves keep placeholders for later re-binding. The Excel→Email template's required-but-emptyccGroupTagIdslikewise no longer blocks the run: it maps to an optional input seeded with[]. - Text-only fallback for strict OpenAI-compatible gateways. Some gateways only accept string
contentin chat messages and reject the array-form (multimodal) content that screenshot tools produce — e.g. Go-based relays answeringjson: cannot unmarshal array into Go struct field ChatMessage.messages.content of type string. The chat round now detects that 400, retries once without image attachments, and stays text-only for that endpoint from then on; screenshots remain in the conversation history and UI, and vision-capable endpoints are unaffected.
🔒 Security
- Plugin SDK no longer bridges to a wildcard origin (BREAKING for plugin toolchain 2.0.0).
@infinia/plugin-sdkremoved the historical'*'default for the postMessage bridge: the embedding shell must append?shellOrigin=<its origin>to the plugin URL (or the caller passesallowedOrigin), otherwise the client refuses every request — previously ANY website that iframed the loopback-served plugin page could silently receive every invoke response. Plugin UIs built with this SDK therefore refuse to bridge on hosts that do not pass?shellOrigin=, which is why the unreleased toolchain line moves to 2.0.0. The parameter pins the bridge for well-behaved hosts but is not an authenticity boundary (an embedder controlling the iframe URL can forge it); origin assurance belongs server-side. - Dev-server AUTH token handshake. The
fengyu-plugin-devkitloopback dev server requires every connection to lead withAUTH <token>; the token is random per start and shared with the dev Vite client through a user-only file under~/.fengyu/that is created0600owner-only from the first byte. Loopback binding alone had left the full worker RPC surface open to any local process. - Optional Ed25519 checksum signing for releases.
fengyu-release.ymlsignschecksums.txtintochecksums.txt.sig(base64 Ed25519) whenever theFENGYU_SIGNING_KEYsecret is configured; without the secret the release stays checksum-only, so unsigned builds keep working.
⬆️ Dependencies
vue-i18n10.0.8 → 11.4.8 (frontend + email plugin UI). v9/v10 are EOL upstream — the project now forbids EOL dependencies on every surface. v11's stricter message compiler surfaced one unescaped literal@insettings.mcp.placeholderArguments, now escaped with{'@'}in both locales.vite6 → 7.3.6 (frontend, with@vitejs/plugin-vue6 and a resolution unifying every vite resolve on 7.x). Vite 6 left the upstream support window once Node 20 reached EOL; 7.x is the maintained line the plugin toolchain already uses.vitest3 → 4.1 (frontend + desktop) andpinia2.3 → 4.0.3 (frontend). Vitest 4's new spy implementation is not constructible from arrow-function implementations, so the two desktop suites that stubnew BrowserWindow(...)were switched to function-expression implementations.typescript5.7 → 5.9.3 andplaywright1.50 → 1.62.1 (desktop +@playwright/test), aligning the desktop toolchain with the repo's 5.9 TypeScript line and a current Playwright.- Removed dead dependencies:
miglayout-swing(a JavaFX-migration-era Swing layout manager with zero source references) and the never-referencedcom.microsoft.playwrightmanaged entry + property in the root POM. fengyu-release.ymlnow runsyarn npm audit --environment productionfor the frontend and desktop areas — any advisory, including EOL/maintenance deprecations without an exploitable CVE, fails the release (the recorded no-EOL-dependencies policy).
✨ Added
- Constrained intranet application updates through FY-Proxy. The desktop loads its persisted update channel before the first automatic probe, supports the same channel for manual checks, verifies FY-Proxy SHA-256 metadata for Windows portable ZIP updates, and uses a dedicated electron-updater feed for lite x64 deb packages. The proxy accepts and serves only those two package classes and rejects NSIS, AppImage, macOS, JRE, and portable Web/JAR assets.
- Automatic conversation compaction for long AI chats. The cloud and Ollama chat paths now estimate provider input from UTF-8 bytes and, at 60% of the configured context window, summarize the oldest complete rounds into a marked assistant context note while preserving system messages and the latest eight rounds verbatim. The full transcript remains unchanged for the UI and persistence, and a failed summary falls back safely to the original history. Settings exposes the context-window size (32,768 tokens by default;
0disables compaction). - Stateful, multimodal browser sessions. A Java-side
BrowserSessionnow routes operations to the same isolated Electron context/tab, caches URL/title and snapshot refs per tab, and rejects unknown or stale refs before an action. Four tab-management tools plusbrowser_batchextend the browser surface to 21 tools; the batch path performs one snapshot plus click/type/press in a single serialized bridge request.browser_screenshotnow carries the actual PNG into the next Spring AI model round asMedia(image/png)while retaining its DOM snapshot and accessibility text fallback. - First-class read-only web retrieval. New host-embedded
web_searchandweb_fetchtools separate lightweight discovery/content reading from interactive browser work. Fetches are bounded, follow a limited redirect chain, reject local/private-network targets, and enter the shared approval policy asreadoperations.
♻️ Changed
- Tool-loop context is now bounded. Tool responses larger than 64 KiB retain their first 75% and final 25% with an explicit omitted-character marker before the next model round, preventing a single result from consuming the conversation window while the SSE activity still receives the complete result.
- Chat and Plan-and-Execute share one approval policy. Both paths now use the same decision for
read,write,command, andexternaleffects, eliminating future permission drift. MCP tools remain conservatively wrapped asexternal, isolated plugin tools keep their manifest effect, and the 21 built-in browser tools now declare per-operation effects: inspection isread, while navigation, page interaction, JavaScript evaluation, and window close areexternal. - Browser bridge I/O is asynchronous. Java uses
HttpClient.sendAsyncwith virtual-thread response processing and waits only at Spring AI's synchronous tool boundary. Electron preserves serialized input semantics while routing independent logical sessions, contexts, and tabs.
🐛 Fixed
- Command failures no longer lose their diagnostic tail.
execute_commanddrains stdout and stderr concurrently and returns them separately (stdout/stderrplus per-stream truncation flags), while keeping the combinedoutput/truncatedfields for compatibility. Oversized streams preserve both the head and tail instead of discarding the end where compilers and shells usually report the actionable error.
[4.0.0-beta.3] — 2026-08-13
✨ Added
- One responsive UI system for every official plugin.
@infinia/plugin-uinow providesFyPluginPagefor shared responsive content gutters andFyProgressfor determinate or indeterminate long-running state, plus reusable surface/action/status/log/table CSS hooks.FyPluginShellowns the notification center and supports navigation-free single-workspace apps; notification composables share one client-bound fallback queue with consistent tones and timing. - Plugin manifest schema v2 — the single hand-written contract.
manifest.jsonis nowschemaVersion: 2withadditionalProperties: falsethroughout. RPC methods live once in anrpc.methodstable whoseinputSchema/outputSchemaare JSON-Schema objects (not escaped strings);aiToolsreference those methods bymethodwith a mandatoryeffect, duplicating no schema.backendkeeps onlycallTimeoutSeconds— the worker is implicitlyjava -jar backend/worker.jarover JSON-RPC 2.0 (backend.command/backend.protocolremoved). The CLI validator enforces every rule, including a raw-text duplicate-rpc.methods-key scan (JSON.parse would otherwise silently merge duplicates). - Deterministic contract generator.
fengyu init|dev|buildgenerate a typed TS RPC client (ui-src/src/generated/fengyu-rpc.ts—createPluginRpc(client)+ per-methodInput/Outputtypes) and Java records + a centralizedPluginMethodsname class (src/main/java/<id>/generated/) straight fromrpc.methods. Output is byte-for-byte stable (sorted), handles identifier escaping / reserved words / nullable / required, nests records for object/array fields, and rejects unsupported JSON-Schema constructs instead of coercing toObject.fengyu checkdetects generated-file drift without writing. - Typed Java Worker SDK + standard cancellation.
JsonRpcWorker.method(NAME, Input, Output, handler)registers typed handlers;RpcContextexposescallId/pluginId/pluginRoot/locale/cancellation()/logger(). The dispatch loop is split into a reader + handler pool so a$/cancelRequestnotification cancels an in-flight call (returnsCANCELLED, worker survives); EOF drains gracefully. StableRpcError.Codeset (INVALID_ARGUMENT/PERMISSION_DENIED/NOT_FOUND/CONFLICT/CANCELLED/INTERNAL) maps to JSON-RPC codes and anerror.data.codelabel. The oldJsonRpcWorker.string/integerMap-parsing helpers were removed. - Reserved
_fengyumetadata envelope on the worker RPC frame. The request locale now rides in a host-owned top-level_fengyuobject (_fengyu.locale) instead of being injected intoparams, so a plugin method may freely declare its ownlocaleinput field without it being overwritten by the request locale. The Worker SDK reads_fengyu.locale(binding bothRpcContext.locale()andWorkerLocale, so synchronous handlers now resolve messages in the request language — previously onlyJobspropagated locale) and falls back to the legacyparams.localekey for hosts that have not yet adopted the envelope. Any frame-root key beginning with_fengyuis reserved. - Shared plugin bridge protocol
3.0.0and capability pre-check.@infinia/plugin-sdk/protocolis the side-effect-free source for iframe/host message types, method constants, capabilities, and structured errors.HostEnvironmentnow carriespluginId/pluginVersion/permissions;HostError.codeaddsTIMEOUT/CANCELLED. The SDK validates the capability before each request, posts the cancel notification on both timeout and abort, and silently drops responses for unknown ids / wrong origin / wrong protocol version. - Host v2 install + dispatch. Install accepts schema v2 only and validates
rpc.methods(unknown method rejected before worker start; AI tool schemas read from the referenced method's object schema — no string re-parsing). The iframe/HTTPcallIdis passed through as the JSON-RPCid. Cancel sends$/cancelRequestfirst and only force-restarts on timeout. Worker errors map to typed exceptions:PERMISSION_DENIED→ HTTP 403 (no longer a generic 500),CANCELLED→ 499. - All four official plugins migrated to the typed model.
markdown(canary),excel,offlinepython, andemailnow ship schema-v2 manifests, generated TS/Java contracts, typedmethod(...)workers, and transport-cancellation tests.offlinepython's domainbuild.cancel/deploy.cancelnow reaps the whole Python/pip subprocess tree. - Plugin cards re-fetch their server-localized name/description on locale change. Names and descriptions are resolved server-side per request locale (each manifest's i18n block), but the tools page, market "plugins" tab, and store tab fetched once and cached — so switching the UI language flipped the
$t()strings but left the cards' server-supplied text in the old language until a manual reload. A reactivelocaleRefnow drives re-fetches in the plugins/plugin stores andPluginMarket.vue; the axios interceptor already sends the currentAccept-Languageheader on every request.
♻️ Changed
- Markdown, Excel, Email Center, and Offline Python now use the same official UI foundation. Email's private green palette and custom task rail were removed; all four plugins now use the shared responsive shell/page, notification path, progress treatment, theme tokens, and container-aware narrow layouts. The host sidebar automatically enters rail mode when space is constrained, and both CLI Vue templates emit the same composition for new plugins.
- Convention-based Toolchain 2 CLI. The public command surface is
fengyu init,dev,check, andbuild.fengyu.plugin.jsonand arbitrary command arrays were removed; projects use standard npm scripts and Maven lifecycles, the Worker is discovered as the uniquetarget/*-worker.jar, and packages are written underdist/. The unused direct-ESMdefault.mount(el, ctx)loader was removed; sandboxed iframe + shared protocol is the only UI runtime. offlinepythonRPC method names are lowerCamelCase. Schema v2 forbids dots in method keys, soconfig.get→configGet,requirements.save→requirementsSave,build.start→buildStart, etc. The smoke scripts were updated; external callers of the old dotted names must switch.
🐛 Fixed
- Official plugin layouts now update correctly in real host iframes. Step changes remount their keyed content so restored Excel sessions cannot retain a previous step's layout class; host notification rejection reaches the shared local snackbar; and bundled official plugins are refreshed when same-version
.fypbytes change instead of leaving stale installed UI behind. - macOS no longer shows "compatibility mode" for process isolation. The Settings "Process isolation" badge was a strict
sandboxed ? active : compatibilitybinary, so macOS'ssandbox-execposture (correctly classified by the backend asreduced— a deny-sensitive boundary, not full isolation) collapsed into the no-isolation warning "兼容模式 · 必须明确审批", contradicting the card below it (which keys the compatibility toggle oncompatibilityMode). The badge is now three-state —sandboxed(Linux bwrap) andreduced(macOS) both render green; the yellow "compatibility mode" chip is reserved for Windows Job Object / no-backend. A muted hint explains the macOS deny-sensitive model. Backend behavior was already correct; this is a frontend display fix. - offlinepython no longer crashes on omitted nullable fields, and its logging is no longer silently dropped.
verify'sscopeanddeploy'starget.kindare nullable enums in the manifest (defaulting to ALL / global); omitting them threwNullPointerExceptionbefore the existing null-handling ran. The customOpbLoggerwrapper — wired with a null instance in production — was removed in favour of direct SLF4J, so the package-done milestone now emits and previously silentcatchblocks log. (Excel's analyze/executecatchblocks also warn-log now; cancellation still propagates asCANCELLEDfirst.)
[4.0.0-beta.2] — 2026-08-11
✨ Added
- Settings-driven update channel. The Settings page can now point the app's update check at an intranet/offline FY-Proxy base URL instead of the default GitHub feed. The value is persisted in the
app_settingstore and honored on both update paths: the backendUpdateCheckServicereads it per check (live without a JVM restart), and the Electron shell bootstrapsFENGYU_UPDATE_API_BASEfrom the backend before the first update check and updates it live via anupdate:set-api-baseIPC from the renderer.PUT /api/settingsvalidates the value as an absolute HTTP(S) URL with no credentials, query, or fragment, mirroring the desktopupdate-feed.tsvalidation so both channels accept the same value; invalid values map to HTTP 400. - Full i18n for all official plugins (front + back end). Plugin worker backends now render localized
summary/errormessages, and the Markdown and Excel plugin UIs are fully localized to match the Email and Offline Python Builder plugins. Locale flows per-request from the hostAccept-Languageheader (and the AI chat turn) into the worker, with no change to the JSON-RPC envelope or thePluginHandlersignature:- SDK
1.3.0addsWorkerLocale(per-request locale ThreadLocal bound byJsonRpcWorker.servefrom alocaleparams key),PluginMessages(a classpathi18n/messages[_zh].propertiesbundle resolver), and keyedokKey/failKey/thelpers onPluginHandlerSupport. Workers without bundles keep their prior English behaviour (default localeen). - Host injects the resolved locale on both call paths:
PluginRuntimeController.invokereadsAccept-Language, and the AI path carries the locale through a newAiToolLocaleContextThreadLocal (mirroringAiPermissionContext) bound for the chat turn. - Plugin backends ship
i18n/messages[_zh].propertiesand key their user-facing strings: Markdown, Excel (worker + progress logs), Email (handlers + services), and Offline Python Builder (activating the previously-orphaned bundles). The Offline Python doctor checkid/valueprotocol tokens stay locale-neutral (the frontend translates them, as before). - Markdown + Excel frontends gain a lightweight
i18n.ts+useFengYuEnvironment()composable (mirroring the Offline Python pattern), keying every visible UI string. - Unified production lifecycle. All six plugin-tooling artifacts now agree on
1.3.0.@infinia/plugin-uiprovidesmountFengYuAppandcreateFengYuI18n, so the four official plugin UIs share one ready/theme/locale/mount/pagehide-dispose path. The TS SDK deduplicatesready(), caches and merges environment updates, and exposescurrentEnvironment(). WorkerJobsinherit the initiating request locale, support race-safe cancellation and reject starts after close;JsonRpcWorker.onClosecloses registered resources in reverse order before exit, and the official async workers register their job registries for teardown. Toolchain and official-plugin lockfiles also pick up patchedfast-uri,nanoid,dompurify, andbrace-expansionreleases; their npm audits now report zero known vulnerabilities.
- SDK
[4.0.0-beta.1] — 2026-08-09
✨ Added
- Official Browser Agent. The fifth bundled plugin drives a real Chromium through nine confirmation-aware AI tools for navigation, clicking, typing, DOM inspection, screenshots, waiting, JavaScript evaluation, and session shutdown. Chromium resolution supports a configured system browser, a plugin-managed download, and Playwright fallback.
- Isolated plugin database lifecycle and localized manifests. Database-capable workers now use user-authorized per-plugin credentials with recoverable provisioning/deprovisioning state, while manifest display strings and AI-tool descriptions support locale-family fallback. Plugin logs are persisted per plugin and exposed through ordered REST/SSE history.
- Windows Job Object process isolation backend (
ProcessSandboxWINDOWS_JOB). Plugin workers and AI-authored commands on Windows now run inside a Win32 Job Object configured withKILL_ON_JOB_CLOSE, giving reliable process-tree termination: closing the job handle (orTerminateJobObject) kills the worker and any descendants (e.g. apipsubprocess) without relying onProcessHandle.descendants(), which was unreliable on Windows. The Job Object is a process-layer isolation only — filesystem and network confinement remain a known gap on Windows (the explicit-approval gate still guards every effect there).GET /api/security/process-isolationreportsbackend: "windows-job". JNA 5.19.1 was added for the Win32 binding.
♻️ Changed
- Browser automation moved from
plugin-browser(Playwright) to a host-embedded capability. Browser automation is now built into the desktop application and exposed by the backendBrowserTool, not a.fypplugin. It drives a real browser window through Electron's nativewebContentsand the Chrome DevTools Protocol (CDP) over a loopback HTTP bridge — no Playwright dependency and no separate Chromium download. The nine AI tools (browser_navigate,browser_click,browser_type,browser_get_text,browser_query,browser_screenshot,browser_wait_for,browser_eval_js,browser_close) remain, each approval-gated. The capability is desktop-only: it requires the Electron shell and is unavailable in pure-web / headless mode.
🗑️ Removed
plugin-browser(fan.summer.browser) official plugin. The Playwright-based browser plugin has been removed; its function is now provided by the host-embeddedBrowserTool(see Changed above).OfficialPluginsnow ships four plugins:plugin-markdown,plugin-excel,plugin-email, andplugin-offlinepython.
🐛 Fixed
- Official plugins no longer reappear after the user uninstalls them. Uninstall previously deleted both the package directory and the integrity record, leaving no trace; on the next restart the official-plugin seeder could not distinguish a user uninstall from a never-installed plugin and reinstalled the bundled archive. A persistent uninstall tombstone now marks uninstalled plugins; the seeder checks it before seeding and skips them. A later reinstall (local upload, online upgrade, or a bundled upgrade) clears the tombstone so the cycle is repeatable.
- Local
.fypinstall with a matching.sha256sidecar may now claim official identity. The native install path now verifies a sibling<archive>.sha256sidecar (GNU coreutilssha256sum -cformat — the same credential the official seeder verifies), letting a user install a rebuilt official plugin locally at the same trust level. Without a sidecar (or with a mismatched one) the install stays untrusted, so the existing reservation still blocks official / namespace-squatting. (Asymmetric signature verification remains a tracked follow-up; a sidecar is a tamper/corruption check, not an independent authenticity anchor.) - Excel split no longer falls back to copying the whole sheet after a worker restart. The host tears down and relaunches a plugin worker whenever its file-grant version changes — in the Excel wizard, picking the output folder (Output step) grants the output dir after
configure(Mode step), so the worker servingsplitis a fresh process whose in-memory session store is empty.splitnow re-applies the full split config from its own arguments (fields absent are left untouched, preserving partial-update callers), making the session store a cache rather than a correctness dependency. - Web and desktop dependency advisories. Updated DOMPurify, nanoid, js-yaml, PostCSS, fast-uri, and brace-expansion to patched compatible versions; both npm dependency audits now report zero known vulnerabilities.
- Beta plugin runtime hardening. Official package checksum sidecars now travel with
.fypartifacts through Web/desktop release assembly; plugin DB provisioning/deprovisioning keeps recoverable lifecycle state; JSON-RPC frames, async job logs, and plugin-log SSE replay are bounded and ordered; Windows command Job Object handles are reclaimed on every path. Plugin uninstall now asks whether to retain or permanently delete runtime data and the provisioned DB namespace, and reports deletion failures instead of silently leaving data behind. - Plugin worker processes no longer leak after the host exits. A plugin worker is an independent JVM spawned by the host backend; previously the worker was often not reaped when the host exited (macOS/Windows have no equivalent of Linux's
bwrap --die-with-parent, and Electron only signalled the backend's direct PID with no tree-kill), so leaked workers kept holding the exclusive file locks of embedded databases (H2/SQLite), leaving the database files undeletable. Fixed with four complementary layers of defense:- Worker SDK watchdog (
fengyu-plugin-sdk1.1.0 → 1.2.0): the productionrun()entry point gained dual watchdogs — stdin-EOF (primary) and a parent-liveness poll (auxiliary). On host shutdown/crash the worker auto-System.exits, ensuring it exits and releases file locks even when it holds non-daemon thread pools (HikariCP, etc.). - Explicit host shutdown hook:
HeadlessLauncherregisters a JVM shutdown hook in APP mode that is independent of Spring and callsPluginProcessManager.close()directly, instead of relying solely on the timing of Spring's default hook. - Grandchild-process fallback: after destroying a worker,
PluginProcessManager.Worker.close()recursivelydestroyForciblys its descendant processes (e.g. offlinepython'spipsubprocess) to avoid orphaning them. - Electron tree-kill: the desktop shell adds a
tree-killdependency so that on exit it sends SIGTERM→SIGKILL to the entire backend process tree (including worker grandchildren), as a fallback when the host crashes. - The exit traps in
scripts/e2e-smoke.shandscripts/offlinepython-e2e-smoke.shnow also runpkill -Pto clean up worker subprocess trees.
- Worker SDK watchdog (
- Offline Python Builder deploy now uses the deployment machine's Python. The deploy step previously re-ran Python detection with a null hint, discarding the interpreter resolved from the target — so on machines whose Python (conda/pyenv/venv) was not on
PATH, version detection returnednull, every C-extension wheel (numpy/pandas/…) was judged incompatible, and the deploy silently installed zero packages (or reported success with nothing actually installed).DeployServicenow resolves the version from the target's interpreter and fails loudly if it cannot, instead of masking the failure as an empty match. The Deploy panel also auto-detects this machine's interpreter on load and falls back to a manual path input when detection fails, so offline machines with non-PATHinterpreters can still deploy.
[4.0.0-alpha.8] — 2026-08-04
🐛 Fixed
- Email plugin now works with the default embedded-H2 database. The host holds an exclusive file lock on its own H2 file, so a sandboxed plugin worker could not attach to the same file — H2 rejected the
AUTO_SERVER+ sandbox combination and every email RPC failed at worker boot. Database-permission workers now get their own DB file under their plugin data directory for embedded databases (H2/SQLite), while remote databases (MySQL/PostgreSQL) still share the host URL (real servers handle concurrent connections). The uselessAUTO_SERVER=TRUEoption was also dropped from the host H2 URL template. The e2e smoke now exercises an email RPC end-to-end.
[4.0.0-alpha.7] — 2026-08-04
✨ Added
- Unified plugin store (Claude / Codex / FengYu). A new
/api/plugin-store/*REST surface lets you subscribe to third-party Claude Code and OpenAI Codex marketplaces alongside the FengYu catalog, browse a merged, filtered, source-badged grid, and install Claude/Codex plugins by cloning their git source (JGit) with pinned-sha verification. The frontend ships a unified "Stores" tab with a source manager, install/update/uninstall actions, declared-skills and MCP rendering, and an in-app detail drawer. JGit 7.7.0 was added for clone support. - Windows unsandboxed-plugins toggle. On platforms without a native process sandbox (Windows, or any host where
ProcessSandbox.detect()isNONE), a new Settings row — gated behind a confirmation dialog and defaulting fail-closed — lets a user opt into running plugin workers via theunrestricted()channel (effectiveUnrestricted = fullAccess || unsandboxedPluginsEnabled). AI command-approval and the sandbox fail-closed primitive are untouched; this only unlocks plugin workers.
🐛 Fixed
- Store installer path-traversal (security). A malicious third-party marketplace entry with a
namecontaining path-traversal sequences (../…) could delete or overwrite arbitrary user-writable files, because the raw name flowed unchecked intoskills/<uid>andmcp-servers/<uid>.jsonpaths that are both deleted and written on install. Catalog adapters now slugify the name to a single safe path segment, and the installer asserts every uid-derived path stays inside the runtime root before any delete/write (defense in depth). - Store clone URL scheme validation (security). Clone URLs from third-party marketplace JSON are now restricted to
https/http/file;ftp:,jar:, and bare local paths are rejected before JGit sees them. - Store clone cleanup and timeouts. A failed clone no longer leaves a
.clone-/agent-*temp dir (with.git) behind, and the configuredfengyu.store.git-clone-timeout-secondsis now actually applied to the clone. - Symlink defense in skill extraction (security). A malicious repo containing a symlink whose target escapes the plugin root can no longer leak host-readable files into the runtime tree; the skill walker now skips symlinks and copies with
NOFOLLOW_LINKS. - Codex install integrity. Codex sources (which declare no pinned sha) now record the resolved HEAD commit sha in the install record, so every install carries an auditable content fingerprint instead of
null. - Catalog fetch size cap. Catalog responses are now bounded to 16 MiB, so a malicious or broken catalog URL cannot OOM the backend by streaming an unbounded body into memory.
- Homepage XSS (security). The store detail drawer's Homepage button now allows only
http(s):/mailto:URLs, blockingjavascript:URIs from third-party catalog fields. - Frontend store error surfacing. Install/update/uninstall failures now surface to the user instead of being silently swallowed;
busyis always reset. Malformed catalog array fields are coerced to[]so the template never throws. - Plugin enable/disable marker. Toggling a Claude/Codex plugin's enable state now writes the
.disabledmarker the skill loader reads, so disabling actually stops the skill from loading.
♻️ Changed
- Hardened the unsandboxed-plugins platform gate to rely on
compatibilityMode(isNativeSandboxAvailable()), per the design — the toggle appears on any platform lacking a native sandbox, not strictly Windows.
[4.0.0-alpha.6] — 2026-08-02
✨ Added
- AI chat now has Codex-style action approval profiles. The composer offers Ask for approval, Approve for me, and Full access; command execution and plugin-declared read/write/external effects share one host approval gate. Approval cards stay inside the composer, while calls render as compact progress rows such as
Read FengYu Plugin Dev skill. Plugin manifests can declare an optionalaiTools[].effect, with undeclared third-party effects treated conservatively.
🐛 Fixed
- AI chat now handles Excel file workflows and approval-heavy replies reliably. Attached files/directories are granted to every compatible backend plugin, and existing absolute paths typed in a user message are converted into read-only plugin-scoped FileRefs. Selected directories retain writable access where declared and are injected into single write-directory plugin tool parameters; Excel analysis is preferred before split operations, and unresolved FileRef objects can no longer become map-shaped output folders. IME composition no longer drops a trailing English segment on click-to-send, and final answers render below command approval cards.
✨ Added
- Live visual-workflow tool contracts. Plugin AI tools may declare an optional serialized
outputSchema; the official Excel, Email, and Offline Python tools now publish user-facing input metadata and result-envelope schemas for canvas configuration.
♻️ Changed
- Agent tool discovery now follows the installed-plugin lifecycle. New runs read a live tool registry, while canvas nodes survive tool disable/uninstall, block unsafe execution, and reconcile newly required inputs when the same tool is enabled again.
- A directory the user names as an output target never becomes worker-writable. Instead a plugin-owned staging directory is created per turn and handed to the worker as a writable sandbox root; the host copies its contents to the real target after the turn completes and deletes the staging tree. The real directory stays read-only, so the OS sandbox writable-roots remain stable (one staging grant per turn) and a worker can never overwrite files in a user-named folder. Plugin workers also now receive a per-plugin writable temp directory (
-Djava.io.tmpdir+TMPDIR/TMP/TEMP), andPLUGIN_DATA_DIR_ENVis set for every plugin rather than only database-capable ones. macOS sandbox writable-roots are canonicalized viatoRealPath()before the profile is built (resolves/var→/private/var). - The chat tool-loop cap is now configurable. A new
ai.max_tool_roundssetting (default 50,0= unlimited) replaces the previously hard-coded per-backend limits and bounds the number of tool-call rounds a turn may take, stopping a model that re-requests the same tool from wedging the virtual thread and locking the backend. It is editable in Settings → AI. - Bumped Apache POI to 5.5.1.
🐛 Fixed
- The permission-mode menu is now disabled while a generation is in flight, so an approval profile can no longer be switched mid-turn.
[4.0.0] — 2026-07-29
✨ Added
- Built-in
fengyu-plugin-devskill. A second built-in skill (alongsidefengyu-features) that teaches the in-app assistant the 4.0.0 plugin model:.fyppackages (sandboxed iframe UI + out-of-process JSON-RPC worker), themanifest.jsonfields, the permission enum,aiTools, and thefengyuCLI build/install flow. It is authored for the app's runtime context, distinct from the repo's agent-workflow skill of the same id.
♻️ Changed
- Removed the legacy
FengYu-Apimodule. Host-only AI contracts, tool categories, and theme state now live in the headlessFengYuapplication module. The obsolete JavaFX preview assets and in-process plugin logging bridge were deleted, and Maven no longer manages JavaFX artifacts. - Unified the application version at 4.0.0. Maven, the frontend, Electron shell, built-in skills, and official plugin packages now use the stable version.
- Scoped the VitePress toolchain to
docs/. The documentation package manifest, lockfile, installed dependencies, local commands, and CI cache now live with the documentation sources instead of occupying the repository root.
[4.0.0-alpha.5] — 2026-07-29
✨ Added
- Agent runs are now durable. Each plan-and-execute run is snapshotted to the database (
ai_agent_run) with a sequenced lifecycle-event append log (ai_agent_run_event) coveringplan_ready,plan_approval_requested,step_start/step_complete,step_approval_requested,complete, anderror/cancelled. History is listable/detailable per user, a failed or cancelled run can be resumed from its last completed step, and on restart any non-terminal in-flight run (PLANNING / AWAITING_*_APPROVAL / EXECUTING) is reclassified as FAILED. Persistence failures are logged but never kill a healthy run. - Sensitive tool calls require explicit user approval. A new
ApprovalRequiredToolcontract marks host tools that must never run without confirmation. In ordinary chat,ChatToolApprovalGateblocks each model response containing such calls on a confirmation card (5-minute expiry), and in the Plan-and-Execute Agent each step pauses for the same gate. Cancelling a generation or swapping the backend rejects all pending approvals. execute_commandtool with OS-level process sandboxing. AI-authored shell commands run inside a native isolator when one is available —bwrap(bubblewrap) on Linux andsandbox-exec(Seatbelt) on macOS — with read-only system files, writes confined to the working directory, and the network isolated unless explicitly opted in. Inherited environment variables holdingTOKEN/SECRET/PASSWORD/API_KEY/CREDENTIAL/COOKIE/AUTHORIZATIONare stripped before launch, output is bounded (default 64 KiB, max 256 KiB) with truncation flagged, and a bounded timeout (default 30s, max 600s) forcibly terminates descendants. Where no isolator exists the tool falls back to direct execution and disclosescompatibilityModein the result; approval stays mandatory regardless.GET /api/security/process-isolationreports the active backend.- MCP (Model Context Protocol) client. MCP servers configured via
spring.ai.mcp.client.*are connected at startup and surface their tools to the Agent.GET /api/mcp/statusreports the enabled flag, connection/tool counts, and per-connection detail (name, version, protocol version, initialized). - The host and Java plugin Workers now share one live log level. The Settings page persists
TRACE,DEBUG,INFO,WARN,ERROR, orOFF, applies it to the host's Logback namespaces, and pushes it to running Workers without a restart. The Java Worker SDK replacesslf4j-simplewith a structured stderr provider, preserving logger name, thread, level, message, and exception stack while keeping stdout reserved for JSON-RPC; legacy free-form stderr remains supported.
🔒 Security
- Runtime secret files are owner-only on POSIX.
SensitiveFilePermissionsappliesrwx------to secret/key-material directories andrw-------to their files on macOS/Linux (no-op on Windows, where user-profile ACLs apply).
♻️ Changed
- Default runtime state is self-contained under the launch directory. Without an explicit
fengyu.runtime.dir, the app now stores embedded databases in<program-working-directory>/.fengyu/database/and configuration, logs, plugins, skills, and other writable state under<program-working-directory>/.fengyu/. - Plugin Workers are sandboxed per manifest permissions.
ProcessSandbox.plugin(...)confines each Worker's writes to its plugin-owned roots (broadened whenfiles.writeis declared) and isolates the network according to the manifest, on supported isolators.
[4.0.0-alpha.4] — 2026-07-28
🐛 Fixed
- Plugin UI icons now render reliably in sandboxed third-party plugins. The host CSP explicitly permits same-origin and bundled
data:fonts for compatibility with existing.fyppackages, while@infinia/plugin-uileaves its MDI stylesheet to the consuming Vite app so new builds emit ordinary hashed font assets instead of embedding multi-megabyte fonts in the library CSS. Official plugin UIs now declare@mdi/fontdirectly so the Vite build can resolve the externalized@importunder strictnpm ci. window.fengyuaccess is SSR-safe. The desktop-bridge calls added tosettings.ts,main.ts, androuter/index.tsare now guarded withtypeof window !== 'undefined', so they no longer throwReferenceErrorin vite SSR / thenode --testsuite.
♻️ Changed
- Windows desktop releases ship an extract-and-run ZIP instead of a self-extracting portable
.exe. Both the lite and JRE variants keep the NSIS installer (*-win-x64-setup.exe) and now publish*-win-x64-portable.zip(extract once, then runInfinia.exe); the startup-time self-extraction of the old portable executable is gone. Artifact names were unified to<product>-<version>-<platform>-<arch>[<form>].<ext>across macOS / Windows / Linux (Infinia-4.0.0-mac-arm64.dmg,Infinia-4.0.0-win-x64-setup.exe, …), and the release workflow + its contract test were updated to match. - Desktop startup no longer flashes and no longer requires a live backend to pick a route. The shell shows a splash window while it probes the backend, exposes the pre-probed setup state and the chosen theme to the renderer via
window.fengyu.setupMode()/initialTheme()/setTheme(), and the router consumes that snapshot on first navigation before falling back to live checks.
🔧 Internal
- Backend runtime directories are centralized. Plugin, skill, plugin-data, and transient-file directories are now derived from one stable root via the new
RuntimePathsand overridable throughfengyu.runtime.dir(default~/.fengyu), replacing scatteredSystem.getProperty("user.dir")paths.CryptoUtilderives the.machineidfrom the same root. This makes the packaged Electron shell and the portable Web distribution agree on where state lives. - Portable Web distribution keeps its state self-contained.
run.sh/run.batnow pass-Dfengyu.runtime.dir=<dist>/data, so the database, config, logs, and plugin data land next to the launcher inside the extracted folder rather than in the user's home directory — preserving the "unzip and run, move/delete leaves nothing behind" portability contract.scripts/e2e-smoke.shpins the same property to its temp dir so its run is repeatable.
[4.0.0-alpha.3] — 2026-07-25
♻️ Changed — Desktop shell: Tauri → Electron
- The desktop shell was rewritten from Tauri 2.0 (Rust) to Electron 43 (TypeScript). The backend lifecycle is unchanged — Electron spawns the backend JAR over loopback, waits for
/api/health, and hands the token + api-base to the renderer via acontextBridgepreload (window.fengyu). This replaces the old Tauriwindow.__FENGYU_*globals. - Two release variants per platform. Each platform ships a lite build (user supplies Java 21+ on
PATH) and a JRE build that bundles ajlink-minimized JRE under<resources>/jre/:- macOS (arm64 + x64) —
Infinia-<ver>-mac.dmgandInfinia-<ver>-mac-jre.dmg. - Windows (x64) —
Infinia-<ver>-win.exe(NSIS installer) and a portable exe; plus the-jre.exeNSIS variant. - Linux (x64) —
Infinia-<ver>.AppImage/.deband the-jre.AppImage.
- macOS (arm64 + x64) —
- Auto-updater (
electron-updateragainst GitHub Releases), system tray (hide-to-tray on window close, backend stays alive until app quit), single-instance lock, and file logging (electron-log→~/.fengyu/logs/desktop.log). - Dev mode defaults to connecting an IDE-started backend at
http://127.0.0.1:24056(no spawn, no token, no supervisor); setFENGYU_JAR=<jar>(orFENGYU_DEV_BACKEND=disabled) to make the shell spawn its own backend with the full release lifecycle. - Security posture:
contextIsolation: true,nodeIntegration: false,sandbox: true. Navigation guards —setWindowOpenHandlerdelegateshttp(s)targets to the system browser and denieswindow.open('file://...');will-navigateblocks cross-origin in-page navigation. See docs/en/architecture/desktop.md.
♻️ Toolchain directory consolidation
- Consolidated 7 plugin toolchain directories (2 Maven + 4 npm + schema) into
toolchain/, flattening intermediate layers and unifying short semantic names (sdk-java/devkit-java/sdk-ts/ui/dev/cli/spec):FengYu-Plugin-Sdk→toolchain/sdk-javaFengYu-Plugin-DevKit→toolchain/devkit-javaplugin-sdk/typescript→toolchain/sdk-tsplugin-ui/vue→toolchain/uiplugin-dev→toolchain/devplugin-cli→toolchain/cliplugin-spec→toolchain/spec
- CI/release workflows and skills were renamed to
toolchain-*(plugin-tooling.yml→toolchain-ci.yml,plugin-tooling-release.yml→toolchain-release.yml). The tag prefixplugin-tooling-v*is unchanged. - Maven artifactIds and npm package names are unchanged:
fan.summer.fengyu.sdk:fengyu-plugin-sdk,fan.summer.fengyu.sdk:fengyu-plugin-devkit, and@infinia/plugin-sdk/@infinia/plugin-ui/@infinia/plugin-cli/@infinia/plugin-devare still published under their original names. The repo directories changed; the coordinates did not.
✨ Added
- Skills — a third extension surface (peer to plugins and AI tools) using Codex-style progressive disclosure. Enabled skills appear as a compact catalog in the system prompt, and the assistant loads a skill's full body on demand via the built-in
skilltool — so large guidance documents never bloat the per-request token budget.- Managed like plugins: skills are packaged as
.fysarchives (zip:manifest.json+SKILL.md) and installed under~/.fengyu/skills/<id>/— a filesystem peer of~/.fengyu/plugins/<id>/. The full install/uninstall/enable/disable lifecycle mirrors the plugin system (atomic publish + backup rollback inSkillPackageService). - Marketplace:
fengyu.skills.catalog-urlpoints at a remote catalog JSON;SkillMarketplaceServicemerges remote entries with local install state (the lifecycle twin ofPluginMarketplaceService). Install/update by id from the catalog. - Enable state is a
.disabledfilesystem marker (not a DB row), exactly like plugins, so it survives reinstall. - OfficialSkillSeeder (
ApplicationRunner) idempotently seeds bundled.fysartifacts on boot, mirroringOfficialPluginSeeder. - Two discovery sources: built-in (
classpath:/skills/<id>/SKILL.md, packaged in the JAR, cannot be uninstalled/disabled) and installed (.fyspackages under~/.fengyu/skills/). Installed skills override built-ins on id clash. - New REST surface under
/api/skills: list, detail, market, upload, upload-native, install, update, PATCH enabled, DELETE (409 for built-in skills). All requireX-FengYu-Token. - Frontend: skill management is integrated into the Plugins page (
/plugins) — a Codex-stylePlugins | Skillstab pair at the top switches the view, with an installed fast-row and a card grid. A single Upload button accepts both.fypand.fysarchives and routes each by extension. No separate/skillsroute. - Built-in example skill
fengyu-features(answers "what can FengYu do"), now with amanifest.jsonalongside itsSKILL.md. - Skills are decoupled from plugins — they never touch
plugin-spec/or a plugin manifest. - See docs/en/skills/ and docs/zh/skills/.
- Managed like plugins: skills are packaged as
- IDE plugin debugging (plugin toolchain 1.1.0) —
fengyu plugin devis replaced by an IDE-native flow so third-party authors debug UI and worker with real breakpoints, no JDWP remote attach.@infinia/plugin-dev(new npm package,plugin-dev/) — a Vite plugin that turns the dev server into a FengYu host simulator: serves the iframe shell at/__fengyu, bridges@infinia/plugin-sdk'spostMessagecalls, and forwardsrpc.invoketo the dev worker over loopback TCP.fengyu-plugin-devkit(new Maven artifact,fan.summer.fengyu.sdk:fengyu-plugin-devkit,FengYu-Plugin-DevKit/) — a loopback-only TCP JSON-RPC server (PluginDevServer) that drives the worker'sserve(RpcTransport)loop. Scaffolded asPluginDevMainunderworker/src/test/java; declared<scope>test</scope>so it never ships in the shaded JAR.RpcTransportabstraction in the Java Worker SDK —JsonRpcWorker.serve(RpcTransport)shares the dispatch loop between production stdio (StdioTransport) and the devkit's loopback socket.run()/run(InputStream, OutputStream)are unchanged in behaviour.- The scaffolder now generates a shared
<Prefix>Worker.create()handler factory, the production<Prefix>WorkerMain, and the IDE-debugPluginDevMain. UI-only scaffolds setmockWorker: true.
- Real LLM planner + visual canvas workflow builder (AiAgent). The empty
StubPlanGeneratoris replaced byChatBackendPlanGenerator, which asks the active AI backend for a validated structured workflow while keeping tools disabled during planning (newChatBackend#chatWithoutToolsdefault — bothOllamaLocalBackendandSpringAiCloudBackendhonor the toggle).AgentRunnervalidates every workflow (model- or user-supplied) before any tool runs, resolves step-result references (\{\{steps.N.result\}\},\{\{last.result\}\}), and accepts a caller-supplied workflow viaPOST /api/agent/runso the HTTP API can drive deterministic execution. The frontend gains a Vue Flow canvas (AiAgent.vue) with a tool palette,WorkflowToolNode, andworkflow.tsthat compiles the graph into theAgentPlansent to the backend — a no-code peer to the AI plan path. EN/ZH strings updated. Also: the Electron main window starts hidden on a dark surface and is revealed only on first paint (or load failure), removing the white flash on cold start.
🐛 Fixed
- IDE Worker failures no longer look successful. When
workerEndpointis configured,@infinia/plugin-devreturns connection failures as RPC errors instead of silently substitutingdevMockdata. All official plugin UIs now expose the documentednpm run deventry point. - Plugin-tooling release gates now ship the canonical manifest schema, exempt the independently versioned Worker SDK from the application-parent check, and resolve the patched
fast-uri3.1.4. - First-launch (SETUP mode) no longer crashes.
SkillControllerwas component-scanned into the DB-less SETUP context but depends onSkillRegistry/SkillPackageService/SkillMarketplaceService(in theai.skillpackage, which SETUP mode does not scan), causing anUnsatisfiedDependencyExceptionthat aborted startup before the database wizard could run. It is now excluded fromSetupApplication's scan alongside the other APP-only controllers. - B1 — actuator
restartendpoint removed from the default exposure.application.ymlnow setsmanagement.endpoints.web.exposure.include: healthonly. The/actuator/restartendpoint was reachable, and in the Web bundle's default no-token posture any loopback process could force a context restart (DoS). The SETUP→APP restart already goes throughSystem.exit(SETUP_DONE)+ the desktop supervisor, so no functionality is lost. - B2 — Web bundle generates a per-launch token by default.
distribution/web/run.shandrun.batnow generate a random--token=when the user passes none (previously auth was disabled by default). Explicit--token=<t>still overrides. - D1 — desktop navigation guards.
setWindowOpenHandlerdenieswindow.openand delegateshttp(s)targets to the system browser;will-navigateblocks cross-origin in-page navigation. Prevents a compromised page fromwindow.open('file://...'). - D2 — auto-updater skips the JRE variant. JRE-bundled builds detect
resourcesPath/jreand skip the update check — the updater feed only references the lite variant, so auto-update would silently downgrade JRE users to the Java-dependent lite build. Full per-variant feeds are deferred. - D3 — supervisor
stop()is now saved and called.main.tsstores thesuperviseSetupRestartreturn value and calls it inkillBackend()(defensive; prevents a future leak if a persistent listener is ever added). - D4 — APP-mode backend crash shows a dialog. A lightweight exit listener in APP mode shows
dialog.showErrorBoxand quits on an unexpected backend crash (previously silent — the user saw only connection errors). The alpha does not auto-restart. - Desktop — right-edge dark strip on the window. The shell allowed a document-level scrollbar whose transparent track exposed Electron's native window backing as a thin dark line on the right.
html/body/#appnow setoverflow: hidden— the shell owns scrolling inside its panes (sidebar history, chat column) and no document scrollbar is ever created. The window'sbackgroundColoris also aligned to the dark theme (#0d0d0d) so the native backing never contrasts with the renderer. - Electron migration & tooling gates hardened. New
desktop/electron/scripts/verify-frontend-dist.mjsblocks a desktop build iffrontend-dist/is missing or stale;backend/spawn.ts,supervisor.ts,util/health.tsandmain.tsreceived additional lifecycle hardening with new unit tests (health.test.ts,spawn.test.ts, expandedsupervisor.test.ts). - Plugin toolchain —
sdk-tslockfile desynced from 1.1.0 (root version stayed at 1.0.0); regenerated so the root andpackages[""]agree. Also forcedbrace-expansion=5.0.8vianpm overridesintoolchain/uito clear 6 high-severity audit findings on@vue/test-utils → js-beautify → … → brace-expansion@2.1.2(no upstream fix exists on the 2.x line). - Splash screen — shipped in the JRE build variant.
electron-builder.jre.ymlhad not been synced with the lite config whenresources/splash.htmlwas added to the desktop asar, so the self-contained JRE build (the flagship download) silently never showed a splash. The file list now matcheselectron-builder.yml, and both configs includeresources/splash.html. - AI planner timeout no longer wedges the backend. When a planning call exceeded its 180s budget (e.g. a hung Ollama process or a stalled provider connection),
ChatBackendPlanGeneratorgave up but the underlying stream kept blocking onblockLast()with no way to interrupt it. BothOllamaLocalBackendandSpringAiCloudBackendnow hold the ReactorDisposableand await aCountDownLatchinstead ofblockLast(), socancelGeneration()candispose()the stream mid-flight and release the worker. The planner callscancelGeneration()on any timeout/failure path, guaranteeing thegeneratingflag is cleared and every subsequentchat/ planning request no longer fails with "Generation already in progress". Covered by a new regression test (ChatBackendPlanGeneratorTest).
♻️ Changed
- CLI scope narrowed to
create+build.fengyu plugin devmoved to the IDE (@infinia/plugin-dev+fengyu-plugin-devkit);fengyu plugin validateis now a built-in step ofbuild(the staging tree is always validated before packaging);fengyu plugin installis done through the host's plugin marketplace UI (POST /api/plugin-market/upload).--port,--host,--token, and--ui-portCLI flags were removed with their commands. - Plugin toolchain locked at six artifacts, all released together as
plugin-tooling-vX.Y.Z: the Worker SDK, the devkit,@infinia/plugin-sdk,@infinia/plugin-ui,@infinia/plugin-cli, and@infinia/plugin-dev.plugin-cli/scripts/resolve-tooling-version.mjsverifies all six.
🗑️ Removed
fengyu plugin dev,fengyu plugin validate, andfengyu plugin installCLI subcommands and their source (plugin-cli/src/dev.mjs,worker.mjs,install.mjs). Development now happens in the IDE via@infinia/plugin-dev; theFENGYU_DEBUGJDWP remote-attach workaround is no longer needed (runPluginDevMainand set breakpoints directly).
[4.0.0-alpha.1] — 2026-07-19
First public alpha of the 4.0 line. Infinia (FengYu) is re-architected from a JavaFX desktop app into a headless web + desktop application: a loopback-only Spring Boot backend, a Vue 3.5 + TypeScript SPA (identical for browser and desktop), and a Tauri 2.0 desktop shell that sidecar-launches the backend. Built-in tools become isolated .fyp plugins — a sandboxed iframe UI talking to an out-of-process JSON-RPC 2.0 worker. This alpha publishes unsigned Windows/macOS/Linux Tauri packages and a portable, loopback-only Web distribution.
⚠️ Breaking Changes
- JavaFX is gone. All JavaFX code and dependencies are deleted —
FengYuApp, theui/shell, all built-in tool UI classes, the v1PluginRegistry/PluginLoader, and everyorg.openjfx:*dependency. The running backend is headless (no window). - New entry point:
fan.summer.fengyu.HeadlessLauncher(wasfan.summer.Launcher). It boots a loopback Spring Boot web server:java -jar FengYu-4.0.0-alpha.1.jar --port=<n> --token=<t>. - Plugin contract v2 (
FengYuPluginV2):descriptor()+invoke(action, args)(JSON-in / JSON-out) +aiTools(). The oldcreateView()→ JavaFXNodecontract is removed; UI is now a separately-served micro-frontend ESM bundle (PluginDescriptor.uiEntry). IconStyledecoupled from JavaFX — colours are RGB ints +getColorHex()(nojavafx.scene.paint.Color).- Database layer migrated from MyBatis to Spring Data JPA + Hibernate 7 — see Removed.
✨ Added
- Headless backend (
fan.summer.fengyu.web.*):GET /api/health,GET /api/plugins,POST /api/plugins/{id}/invoke,GET /plugin-ui/{id}/**(serves MF bundles),GET/PUT /api/settings,POST /api/ai/chat+GET /api/ai/stream(SSE: token / thinking / tool / done / error). Loopback-only bind + per-launchX-FengYu-Tokenauth (?token=for the SSE stream). - Alpha desktop + web release pipeline —
v4.0.0-alpha.1publishes unsigned Windows/macOS/Linux Tauri packages and a portable, loopback-only Web distribution. The Vue SPA is baked into the shaded backend JAR (static/) and served by a newSpaForwardController; a release-tag resolver (scripts/resolve-release-version.mjs) drives the version strings, andscripts/package-web-release.shtest-web-release.shassemble and smoke-test the archive. Code-signing, a bundled JRE, and the auto-updater remain deferred to a later release.
- Multi-datasource setup wizard: first launch guides users through database selection (H2 / SQLite / MySQL / PostgreSQL) with connection testing and automatic schema initialization. The backend boots in SETUP mode (minimal Spring context, no JPA) when
~/.fengyu/config/datasource.propertiesis absent, and APP mode (full context) once it exists. The Tauri/desktop supervisor restarts the sidecar after the wizard completes. - JPA migration: the database layer migrated from MyBatis to Spring Data JPA + Hibernate 7 (
ddl-auto=update). All 14 entities ported with@Entityannotations; 14 Spring Data repositories replace the MyBatis mappers. - User-system groundwork:
sys_user/sys_sessiontables,user_idrow-level isolation on all user-scoped tables, and pluggableAuthProvider/SecurityContextinterfaces with a Noop implementation (login UI deferred to a later phase). Local offline mode attributes all data to a single virtual user (id=1, "ZFlow-Summer"), created on APP-mode startup. - AES-GCM encryption (
CryptoUtil) for the datasource password field indatasource.properties— keys are machine-bound via a per-machine UUID. - Official plugin UI kit
@infinia/plugin-ui— a Vuetify 3 (Material Design 3) component library for generated plugins. ShipsFyPluginShell,FyPageHeader,FyToolbar, SDK-backedFyFilePicker/FyDirectoryPicker,FyStepWizard,FyTaskTable,FyNotificationCenter, theFyEmptyState/FyLoadingState/FyErrorState/FyPermissionNoticestate panels, andFyConfirmDialog, pluscreateFengYuVuetify,bindFengYuEnvironment, andprovideFengYuClientso the scaffoldedmain.tsbinds the host theme/locale automatically. - Email Center
.fyp(fan.summer.email) with a sandboxed five-tab Vue/Vuetify/TipTap UI, an isolated official-SDK Java Worker, and package permissions limited to database, email network, and authorized file read/write capabilities. Multi-account SMTP/IMAP configuration, AES-GCM credential storage, address-book/tag CRUD, confirmed single and batch sending, manual IMAP.emlcollection, archive search/detail, and seven manifest-declared AI tools. Plugin-owned tables use theFengTu_PL_Email_namespace across H2, SQLite, MySQL, and PostgreSQL. - Excel Splitter
.fyp(fan.summer.excel) — BY_SHEET / BY_COLUMN / COMPLEX split modes with a stateful four-step wizard, six manifest-declared AI tools, and authorized file read/write. - Markdown Editor
.fyp(fan.summer.markdown) — first official v2 plugin: server-side commonmark render viainvoke("render", {markdown})plus a Vue split-editor + live preview. frontend/— Vue 3.5.39 + TS shell: sidebar (collapsible, categories), theme (dark/light), settings, AI chat (SSE + markdown + collapsible thinking), ToolGrid, and a micro-frontend host that dynamically imports each plugin'suiEntry.desktop/— Tauri 2.0 shell: spawns the Java sidecar (--port=24056), readsFENGYU_PORTfrom stdout, polls/api/health, injects the backend URL + token into the webview, kills the sidecar on close.- Publishable plugin toolchain — the Java Worker SDK (
fan.summer.fengyu.sdk:fengyu-plugin-sdk, independently versioned, GitHub Packages) and the npm packages@infinia/plugin-sdk,@infinia/plugin-ui,@infinia/plugin-cli, plus a release workflow with a clean-consumer smoke job. - Default Vue + Java scaffold —
fengyu plugin createproduces a complete plugin by default: a Vue/Vuetify UI (ui-src/) backed by a Java JSON-RPC worker (worker/), with the Maven Wrapper, a build declaration, tests, and a GitHub Packagessettings.xml.--ui-onlyretains the lightweight UI-only template. - Real-worker dev simulator —
fengyu plugin devbuilds the worker JAR (if missing), starts the real Java JSON-RPC worker, and forwards the UI'srpc.invokecalls overPOST /__rpc. Java source edits trigger a debounced rebuild + worker restart. - Declared build lifecycle —
fengyu.plugin.jsondrives an ordered, atomic pipeline (prepare → install → test → build → validate staging → package) with the Maven Wrapper (no system Maven fallback).--skip-testsskips tests only, never type checking or packaging. - Shared manifest contract — a canonical
plugin-spec/manifest.schema.json+ fixtures shared by the CLI and the host, includingdatabaseandnetwork.emailpermissions and AI-toolmethod/ object-schema validation. - Offline Python Builder
.fyp(fan.summer.offlinepython) — doctor, dependency search, project init, async wheelhouse builds with streamed logs, and output verification.
♻️ Changed
- Vuetify 3 (Material Design 3) adoption — full visual-language switch for the web shell and plugin micro-frontends, from the legacy
--sk-*IntelliJ-token system to MD3. Theme driven by Vuetify's global singleton fromuseThemeStore; plugins share the host's Vuetify instance viaPluginContext.vuetify. - Stateful plugin workflows —
@infinia/plugin-uiprovides a controlled, persistent-ready step wizard with explicit states, async validation, branching, invalidation, and snapshots; the official Excel plugin adopts it with reload re-analysis and configuration replay, worker-faithful mode validation, safe output reselection, and explicit completion/download. - Refined plugin-ui surface —
@infinia/plugin-uigains a theme-driven polish layer so plugins usingFyPluginShellshare one calm, low-elevation design language (hairline borders, soft primary active chips, a brand marker, de-uppercased buttons, denser fields/tables). Every color resolves through a Vuetify theme variable; the email green palette is explicitly excluded. - Node.js 24.18.0 baseline — documentation,
plugin-cliengine metadata, and every GitHub Actions workflow now use the same exact Node.js version, protected by a repository contract test. - Official plugins built by the CLI — Markdown, Excel, and Email are packaged by
fengyu plugin build(a CI matrix); the legacy shell packager and centralized source manifests are removed. - Offline-first install —
fengyu plugin installvalidates the archive (limits, paths, manifest) before any network access; unsafe or invalid packages are rejected with zero fetch calls. - Strict SDK RPC contracts — the worker surfaces canonical JSON-RPC errors (
-32700parse,-32600invalid request,-32601unknown method,-32000handler failure); the TypeScript client removes abort listeners on every settled path. - HeadlessLauncher now selects
SetupApplication(SETUP) vsAiApplication(APP, withfengyu.mode=app) based ondatasource.propertiespresence; the desktop host restarts the sidecar onSETUP_DONE(exit 0) to enter APP mode. AiConfigService/AiConfigServiceHeadless/EmailUtilconverted from static utilities to Spring beans scoped bySecurityContext.currentUserId(). Setup-wizard endpoints (/api/setup/*) bypass token auth (TokenAuthFilter).- Email batch sending creates one message per parsed attachment tag; all matching contacts share the To/CC fields, To takes precedence over CC, and failed-item retry was removed.
🗑️ Removed
DatabaseInit, all MyBatis mapper interfaces (12),mybatis-config.xml, all mapper XML (12), and the MyBatis dependency.- All JavaFX code and dependencies (see Breaking Changes above).
🐛 Fixed
- Headless fat-jar boot: aligned
logback-classic/logback-coreversions (a split pair crashed onJaninoEventEvaluatorBaseat first logger init); added shadeAppendingTransformerforAutoConfiguration.imports(Spring Boot 4 splits web/Tomcat autoconfig across module jars — without merging, embedded Tomcat silently never started); emit-parametersso Spring MVC resolves@PathVariable/@RequestParamnames. VirtualUserInitializernative INSERT now runs inside@Transactional(was throwingTransactionRequiredException).- Atomic
.fyppackaging: a failure at any stage leaves no.fyp, no.tmp-*, and no staging dir. - Offline Python Builder now opens a writable project workspace, passes complete
FileRefobjects through the host bridge, reports translated job states, stops failed polling, and performs real build/deploy cancellation instead of changing UI state only. - Email archive timestamps on SQLite (including upgrade migration), literal wildcard search, account/folder path isolation, UTF-8 filename limits, and temporary-file cleanup.
[3.2.0] — IDEA 2025 New UI Redesign
v3.2.0 — 2026-06-30
This release re-skins the app from glassmorphism-dark to the JetBrains IDEA 2025 New UI look: a flat, token-based theme with switchable dark / light themes, a collapsible sidebar, and native OS window chrome. Theming is driven by JavaFX looked-up color tokens (-sk-*) declared per theme on the scene root, so a theme switch is just a root class swap — no stylesheet reload.
⚠️ Breaking Changes
.glass-*CSS utility classes renamed to.sk-*(infengyu-common.css). External plugins that callgetStyleClass().add("glass-...")or reference.glass-*selectors must update. The full mapping:old new glass-dialogsk-dialogglass-field/glass-field-labelsk-field/sk-field-labelglass-tab-panesk-tab-paneglass-combosk-comboglass-tablesk-tableglass-checkboxsk-checkboxglass-btn-primary/glass-btn-secondarysk-btn-primary/sk-btn-secondaryglass-notif-*sk-notif-*The external plugin repo (
MuskStark/FengYu-Plugin) is updated separately; flag this rename when migrating third-party plugins.
🎨 Theme (strict tokenization)
- Token set expanded 14 → 19 — added
-sk-shadow,-sk-scrim,-sk-success-soft,-sk-warning-soft,-sk-danger-soft(each under both.theme-darkand.theme-light). Custom themes/stylesheets that hardcoded the old 14 must add these 5 or popups/dialogs/cards will have undefined shadows and status soft-fills. - Fixed popups rendering as un-themed white —
GlassNotification(toast/notify/confirm) loaded the stylesheet but never stamped the theme class on its scene, so every-sk-*token was undefined and all popups fell back to JavaFX default white in both themes. Root-cause fix viaThemes.applyTo(scene). - Removed all hardcoded colors from popups, dialogs,
StepWizard,ToggleSwitch, status labels, and CSS drop-shadows. Everything now resolves through-sk-*tokens and adapts correctly to dark and light themes. NotablyStepWizardidle dots andToggleSwitchoff-track were invisible on the light theme.
✨ New
- Dark / light theme system —
fan.summer.api.theme.ThemeService(API module, no DB dependency) holds the activeTheme.DARK/Theme.LIGHT, stamps atheme-dark/theme-lightclass on every registered scene root, and firesonChangelisteners. Switchable from the sidebar footer (☀/☾) and the Settings page; persisted in thethemesetting (darkdefault). - Looked-up color tokens (
-sk-bg,-sk-bg-elevated,-sk-text,-sk-accent,-sk-border, …) declared per theme infengyu-common.css; swapping the root class re-resolves every token with no stylesheet reload. - Collapsible sidebar —
«/»toggle between the label view and a 48px icon-strip; collapse state persisted via thesidebar.collapsedsetting. - Native window chrome —
StageStyle.DECORATEDgives the real OS title bar + close/min/max (macOS traffic lights), replacing the custom transparent window. MarkdownRenderer.render(md, Theme)/renderPlain(md, Theme)overloads (theme-aware dark/light CSS palettes); no-arg forms delegate viaThemeService.current().
♻️ Changed
fengyu-common.cssrewritten: token definitions under.theme-dark/.theme-light, every component flattened to IDEA New UI style (neutral-gray selection with a left accent bar, slim 4–8px scrollbars, flat fields/buttons/tables/tabs/dialogs/notifications), all.glass-*→.sk-*.shell.cssrewritten token-based for the New UI shell (.app-root,.sidebar+.collapsed, capsule.search-bar, flat.tool-card,.detail-panel,.statusbar,.store-*).Themes.applyTo(scene)now delegates toThemeService.registerScene(scene)(loads the common stylesheet + stamps the theme class); the shared stylesheet load is factored intoThemes.loadCommonStylesheet(scene)to keep the delegation non-recursive.FengYuAppreads the persisted theme on startup and registers the main scene withThemeService.AiChatPluginderives its WebView background from the active theme and re-renders the conversation live on theme change.- Inline
#5b8cf7accent literals replaced with#3574F0/ the dark palette across the sidebar and Markdown link CSS.
🔥 Removed
fan.summer.ui.titlebar.TitleBar— replaced by native OS window chrome.fan.summer.ui.util.WindowResizeHelper— nativeDECORATEDresize/drag/maximize replaces it; the macOSisMaximized()-on-TRANSPARENTbug is gone with it.
[3.1.0] — LangChain4j ChatBackend + Plugin-Owned AI Tools
v3.1.0 — 2026-06-25
This release rebuilds the AI subsystem on LangChain4j and unifies the two cloud providers (OpenAI + Anthropic) into a single CloudChatBackend class behind a new ChatBackend interface. Plugins can now self-declare their own AI tools. The local tool-calling model is Qwen3-4B (Hermes <tool_call> + streamed <think> reasoning), running in a hardened out-of-process worker.
⚠️ Breaking Changes
AiServiceinterface removed — replaced byChatBackend. External plugins callingAiServiceProvider.getService()must change the return type fromAiServicetoChatBackend. Seedocs/migration-3.1.mdfor the migration guide.OpenAiServiceandAnthropicServiceconcrete classes removed — replaced by a singleCloudChatBackendclass withopenAi(...)/anthropic(...)static factories. One unified class serves both providers.CloudAiConfigProviderand standaloneStreamingResponseHandlerBridgeremoved — their logic moved intoCloudChatBackend(config accessors are public methods on the class; the stream bridge is a private inner class).AiServiceImplrenamed toLocalChatBackend— pure rename, no behavior change.BuiltinAiToolRegistrarremoved — plugins now self-register AI tools viaFengYuPlugin.aiTools(); the central registrar and its startup call are gone.
✨ New
- Plugins self-declare AI tools via
FengYuPlugin.aiTools()— the registry auto-registers/unregisters them on add/remove (including JAR hot-reload). No central registrar. AiToolinterface declares per-mode visibility (supportsLocal/supportsCloud) and dual descriptions (getDescription/getLocalDescription);AiServiceProvider.getTools()filters by the active backend mode.AiToolDescriptionshelper centralises cloud-rich / local-concise description templates.- Qwen3-4B local tool-calling — Hermes
<tool_call>parsing (ToolCallParser),ThinkingStreamSegmenter(THINK / CONTENT / tool-call stream splitting),Qwen3Adapter(Hermes system prompt +/no_thinktoggle), and a collapsible thinking card in the chat UI. - New
ChatBackendinterface (fan.summer.api.ai.ChatBackend). - New
CloudChatBackendclass withopenAi(...)/anthropic(...)factories. LocalChatBackend(renamed fromAiServiceImpl).AiToolCall.of(id, name, arguments)overload to preserve server-issued tool-call IDs when bridging from LangChain4j.- Tests:
CloudChatBackendTest(11) + adapter tests forChatMessageMapper/AiToolToToolSpecification;ThinkingStreamSegmenterTest(11) +LocalChatBackendMaxTokensTest(3). - Migration guide at
docs/migration-3.1.md(EN + ZH).
♻️ Changed
- All 16 builtin AI tools return standardized JSON
{success, summary, ...payload}; tool descriptions follow a cloud-rich / local-concise dual template. BuiltinToolRegistrar.register()routes throughPluginRegistry.addPluginsto auto-register plugin AI tools in one pass.- Unified
ChatBackendinterface inFengYu-Api— non-sealed (Java forbids cross-module sealed permits). Two known implementors:CloudChatBackend,LocalChatBackend. UI consumers useinstanceofchecks; the interface itself is treated as opaque. CloudChatBackendunifies OpenAI + Anthropic in one class (~450 LOC). HTTP/SSE, tool-loop plumbing, and stream bridging are delegated to LangChain4j's streaming models; provider differences isolated to abuildStreamingModel(...)switch on an internalProviderenum.SynchronousChatHelper(browser planner) rewritten to use LC4j's synchronousOpenAiChatModeldirectly viaCloudChatBackendconfig accessors.AiServiceProviderexposesChatBackendeverywhere (method names unchanged).- Sampling parameters (temperature / topP / maxTokens) are honoured per-call — settings changes take effect on the next message without restarting the chat.
- Default
maxTokensraised 512 → 2048 (the Qwen3 thinking-model floor), enforced once at thechat()entry so both the native and Java backends benefit.
🐛 Fixes
- Qwen3 silent empty answer — a thinking model truncated mid-
<think>produced an empty answer becausestripThinkwiped the unclosed block. ThemaxTokensbudget is now floored toQWEN3_MIN_MAX_TOKENS(2048) at the unifiedchat()entry, with a diagnostic warning when output survives only as a think block. - Qwen3 on the Java backend leaked raw
<think>tags into the answer — now routed throughThinkingStreamSegmenter(thinking → collapsible card) and stripped from the final answer/history, matching the native path. AiConfigService.getAiMaxTokens()default synced to 2048 (was a stale 512 that disagreed with the settings UI).- AI worker IPC — the child process pins a dedicated
logback-worker.xml(noConsoleAppender) so worker logs no longer corrupt the line-delimited JSON pipe on stdout; stderr is drained on its own thread into the shared log. - AI worker native load — the child JVM loads the llama.cpp library at startup (
NativeLoader.load()) soLlamaContextconstruction no longer throws "Native library not loaded". - AI worker crash recovery —
handleChildExitwaits for a real exit code instead of throwingIllegalThreadStateExceptionon stdout EOF, so pending callbacks are released and auto-restart runs reliably. - Qwen3.5 hybrid-model warning — filenames matching
qwen3.5/qwen35now warn that the native worker is known to SIGABRT on multi-turn (use Qwen3-4B). - Cloud
testConnection()null-message bug on macOS —ConnectExceptionwith anullmessage now falls back toe.getClass().getSimpleName() + ": " + e. - Anthropic multi-round tool calling — server-issued
tool_use_idpreserved through theAiToolCall → LangChain4j → AiToolCallround-trip (previously caused HTTP 400 on round 2). - Multi-turn conversation continuity — the assistant's final reply is appended to
historybefore the service returns. - OpenAI tool-round message ordering — the assistant-with-tools message is appended before
ToolExecutor.executeAndFeed. pdf_merge.filePathsparameter type fixed ("array"→"string[]"); enums declared forbase64.mode,hash_calculate.algorithm,color_convert.from/to.ToolExecutorerror output is always JSON{success:false,error:...};ExcelConfigureToolsuccess returnssuccess:true.testConnection()HttpClientwrapped in try-with-resources; thread-safety hardening on the cloud stream handler.
🔥 Removed
BuiltinAiToolRegistrar— superseded by plugin-ownedaiTools().- FunctionGemma adapter and
OfflineNlNormalizer— replaced by the Qwen3 path.
⬆️ Dependencies
dev.langchain4j:langchain4j-open-ai:1.2.0dev.langchain4j:langchain4j-anthropic:1.2.0- (1.0.1 was originally pinned but
langchain4j-anthropicwas never published at that version; bumped to the lowest GA where both modules co-exist)
⚠️ Known Behavior Changes
cancelGeneration()on cloud backends is best-effort (LangChain4j 1.x does not expose mid-stream cancellation on streaming models); the in-progress flag is still cleared. Local mode is unaffected.- Mid-stream SSE errors now surface via
callback.onErroron the JavaFX Application Thread. - The local tool-calling model is Qwen3-4B; the native worker requests full GPU offload automatically on builds that ship a GPU backend.
📉 Net Code Change
- Deleted:
AiService(117 LOC),OpenAiService(244 LOC),AnthropicService(283 LOC),CloudAiConfigProvider(22 LOC),StreamingResponseHandlerBridge(120 LOC),StreamingResponseHandlerBridgeTest(214 LOC),BuiltinAiToolRegistrar, FunctionGemma adapter +OfflineNlNormalizer≈ 1000+ LOC removed. - Added:
ChatBackend(86 LOC),CloudChatBackend(450 LOC), the Qwen3 toolchain (ThinkingStreamSegmenter,Qwen3Adapter,ToolCallParser), worker hardening, tests, migration guides ≈ 1100+ LOC added. - Net: roughly even on LOC, but cloud code is one unified class and local AI has a dedicated tool-calling model + isolated worker.
[3.0.1] — FunctionGemma Offline Adaptation
v3.0.1 — 2026-06-21
✨ New Features
- FunctionGemma Multi-Round Tool Loop: Host-driven
analyze → configure → executeloop for the FunctionGemma-270m-it local model; tool-call tokens are suppressed during call rounds and only the final response is forwarded to the UI - Offline CN→EN Keyword Normalizer:
OfflineNlNormalizerrewrites Chinese tool-name keywords to English before local-model parsing, no network required (resource-backednl-normalizer.properties) - Enum-Schema Tool Parameters:
AiToolParamgains anenumValuesfield; tool declarations now emitenum:[...]constraints to FunctionGemma, OpenAI, and Anthropic backends — materially improves small-model parameter reliability - Enriched Excel AI tool descriptions and added enum constraints on
mode/actionparameters
🐛 Fixes
- Harden
FunctionGemmaAdapterparser: 🪙 (U+1FA99) string delimiter correctly handles values containing commas, braces, and multiple tool calls in a single response - Release
GGUFModelmmap on unload via best-effortunmap - Harden
GGUFReaderagainst malformed or truncated model files - Serialise
PluginLoaderJAR load/unload on a single-thread scheduler - Complete
LlamaRunnergeneration cleanly when cancelled during prefill - Drive
TokenBatcherflushes off the FX thread - Let the native AI worker exit gracefully before force-killing it
- Close target POI
WorkbookinExcelUtileven when copy/write throws - Low-priority stability cleanup (MDI font log, daemon UI threads)
[3.0.0] — JavaFX Migration
v3.0.0 — 2026-06-12
- Update app icons for v3.0.0 release
- Resolve static analysis warnings across codebase (Qodana)
v3.0.0-rc.3 — 2026-06-10
- Slash Commands: Type
/in AI chat to list available tools, get help on a specific tool, or invoke a tool directly without model inference — supports both direct execution and guided model parameter extraction - Plugin Resource Isolation: Child-first
ClassLoaderfor external plugins ensures plugin resources are resolved from the plugin JAR before the host;PluginContextprovides TCCL switching on every plugin lifecycle call and event dispatch - Plugin Store Redesign: Searchable, filterable card grid for the online plugin store with install state indicators and version comparison
- AI Configuration Service: Extracted
AiConfigServicecentralizes AI configuration access, decoupling it from UI settings code - Email Archive: New
email_archivetable, entity, and mapper for email archive storage - Fix sidebar icons not displaying on Windows — switched from JavaFX
Fonticons to MDI webfont - Fix email settings save always failing; now shows missing required field names
- Fix Excel complex split Phase 3 corrupting pre-existing output files — only merge into files created during the split operation
- Fix POI
NullPointerExceptionduring cross-workbook cell style cloning when data format string is null - Harden Excel Splitter progress callback with null guard
- Extract
StorePluginandStorePluginLogicfromOnlineStorePanewith unit tests - Add GPLv3 license file to the repository
- Add JUnit 5 test dependency to
FengYumodule
v3.0.0-rc.2 — 2026-06-05
- Tool Favorites: Bookmark tools with a star toggle on tool cards and the detail panel; favorites persist across restarts via H2 database and are filterable from the sidebar "Favorites" category
- Lazy AI Backend: Local AI backend (native/Java) initialization is deferred until the AI tool is first opened, improving startup performance; Java/Native inference engine toggle in AI settings
- Plugin Uninstall: Uninstall external plugins from the detail panel with confirmation dialog; closes ClassLoader, removes JAR file, and cleans up from registry
- Install Toast Notifications: Success toast notification when a plugin is installed from the online store or local JAR
- Token Batching: AI token output is batched at 50ms intervals to reduce FX thread flooding during high-speed generation
- Crash Rate Limiting: Native worker auto-restart respects a time window (3 crashes within 5 min) to prevent restart storms
- Settings Cache: App settings are cached in memory with debounced DB writes (300ms) to reduce database load during rapid UI interaction
- Fix native library loading on hardened Linux distros (UOS/Deepin/Kylin) where
SecurityExceptionis thrown for unsigned.sofiles - Fix email batch sending mutating shared recipient lists across iterations
- Fix online store plugin catalog parsing — replaced hand-rolled string slicing with Gson-based
JsonHelper - Fix
WindowResizeHelperdouble-attachment causing duplicate event filters - Thread-safety hardening across
PluginLoader,PluginRegistry, andMainWindow(ConcurrentHashMap,volatile,synchronizedSet) - Stagger limit for tool card entry animations (max 30) to avoid creating hundreds of
PauseTransitioninstances - Fix plugin JAR deletion on Windows — retry with
System.gc()hint, fall back todeleteOnExit()if file is still locked - Fix
onUnload()lifecycle callback not fired when unloading plugin JARs - Fix cached plugin view not cleared when uninstalling an inactive plugin, preventing GC of plugin classes
- Fix English locale (
Locale.ENGLISH) returning Chinese strings on Chinese-locale systems —ResourceBundleno longer falls back to JVM default locale - Fix Windows no-JRE release zip redundantly including the fat JAR alongside the Launch4j exe (which already embeds it)
v3.0.0-rc.1 — 2026-06-04
- Browser Automation: AI-callable
browser_automatetool that automates web browsers via natural language; uses Playwright with the system's installed Chrome/Edge/Chromium (no separate browser download); observe-think-act loop with page DOM snapshots, CSS selector targeting, and a planner LLM - Resizable Window: Edge and corner drag resize for the undecorated
StageStyle.TRANSPARENTwindow viaWindowResizeHelper; uses screen coordinates for macOS compatibility - Responsive Layout: Dynamic
FlowPanewrap length bound to viewport width;windowPaneandContentAreaproperly fill parent withsetMaxWidth/Height(Double.MAX_VALUE) - Pure Java PDF-to-DOCX:
PdfBoxToDocxConverterusing PDFBox for extraction and Apache POI for DOCX generation — no external Office installation required; three-tier page strategy (text → extracted images → full-page render fallback) - Native Backend Health Tracking:
NativeLoader.FailureReasonenum for structured failure diagnostics; degraded-mode banner in AI chat when native acceleration is unavailable - Fix macOS window resize not working due to unreliable
stage.isMaximized()withStageStyle.TRANSPARENT - Fix tool grid layout not responsive to window width changes
- Fix Playwright runtime attempting to download browser driver unnecessarily
- Fix AI browser planner recursively invoking
browser_automatetool via tool injection loop
v3.0.0-beta.2 — 2026-05-26