Skip to content

fix(update): render raw HTML in release notes via rehype-raw#864

Closed
kaizhou-lab wants to merge 1 commit intomainfrom
fix/update-modal-raw-html-display
Closed

fix(update): render raw HTML in release notes via rehype-raw#864
kaizhou-lab wants to merge 1 commit intomainfrom
fix/update-modal-raw-html-display

Conversation

@kaizhou-lab
Copy link
Copy Markdown
Collaborator

Closes #655

Summary

  • Add an opt-in allowHtml prop to MarkdownView that enables the rehype-raw plugin, allowing raw HTML tags in markdown to be rendered as actual HTML elements
  • Apply allowHtml in UpdateModal where the content source is the project's own GitHub releases (trusted)

Root Cause

GitHub release notes often contain inline HTML (e.g. <img> tags for screenshots). react-markdown v10 does not render raw HTML by default — without the rehype-raw plugin, these tags are displayed as plain text, which is what users saw in the update dialog.

Test plan

  • Open "Check for Updates" when a newer release is available
  • Verify release notes render correctly, including embedded images and other HTML elements
  • Verify normal AI conversation markdown rendering is unaffected (no allowHtml = no change)

GitHub release notes often contain inline HTML tags (e.g. <img>).
react-markdown v10 does not render raw HTML by default, causing these
tags to appear as plain text in the update modal.

Add an opt-in `allowHtml` prop to MarkdownView that enables the
rehype-raw plugin for trusted content. Apply it in UpdateModal where
the source is the project's own GitHub releases.
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

CRITICAL Issues

1. Raw HTML rendering enables XSS in a security-sensitive surface (release notes)

File: src/renderer/components/Markdown.tsx:531-571

<ReactMarkdown
  remarkPlugins={[remarkGfm, remarkMath, remarkBreaks]}
  rehypePlugins={allowHtml ? [rehypeRaw, rehypeKatex] : [rehypeKatex]}
  components={{
    // ...
  }}
>
  {normalizedChildren}
</ReactMarkdown>

Problem: Enabling rehype-raw causes raw HTML in markdown to be parsed into real DOM nodes. Without an explicit sanitization step, this is a classic XSS vector. Even if the intended source is “trusted” (GitHub releases), the app is still rendering remote content inside an Electron renderer. A compromised GitHub account/repo, a malicious release author, or any upstream content injection would allow arbitrary HTML injection. At minimum this can lead to phishing UI inside the modal; depending on the app’s Electron hardening and what the renderer can access via IPC, it can become more serious.

react-markdown’s security model is: raw HTML is disabled by default; if you enable it, you should also sanitize (e.g., rehype-sanitize) with a strict allowlist.

Fix: Add sanitization when allowHtml is enabled, and restrict allowed tags/attributes to what you actually need (e.g., img, a, basic formatting). Example:

import rehypeRaw from 'rehype-raw';
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';

const releaseNotesSchema = {
  ...defaultSchema,
  tagNames: [
    ...(defaultSchema.tagNames ?? []),
    'img',
  ],
  attributes: {
    ...defaultSchema.attributes,
    img: ['src', 'alt', 'title', 'width', 'height'],
    a: ['href', 'title', 'target', 'rel'],
  },
};

<ReactMarkdown
  remarkPlugins={[remarkGfm, remarkMath, remarkBreaks]}
  rehypePlugins={
    allowHtml
      ? [rehypeRaw, [rehypeSanitize, releaseNotesSchema], rehypeKatex]
      : [rehypeKatex]
  }
/>

Also consider explicitly disallowing dangerous URL schemes in img/src and a/href (e.g., javascript:), even if sanitize should handle most of it.


HIGH Issues

2. allowHtml is enabled for remote content without any trust boundary enforcement

File: src/renderer/components/UpdateModal/index.tsx:214-224

<div className='text-13px text-t-secondary leading-relaxed'>
  <MarkdownView allowHtml>{updateInfo.body}</MarkdownView>
</div>

Problem: The PR description says the source is “the project's own GitHub releases (trusted)”, but the code does not enforce that trust boundary. updateInfo.body is still remote content. If update feeds ever change (mirror, self-hosted, prerelease channels, forks, etc.), this becomes an easy regression into rendering untrusted HTML.

Fix: Gate allowHtml behind an explicit “trusted source” check at the point of use (or in the update fetching layer), e.g. only enable when the release URL/owner/repo matches a hardcoded allowlist, or when the update metadata is signed/verified. If you can’t reliably verify trust, keep allowHtml off and instead support images via markdown (![]()) or a controlled subset via sanitization (preferred).


Summary

Level Count
CRITICAL 1
HIGH 1
MEDIUM 0

🤖 This review was generated by AI and may contain inaccuracies. Please focus on issues you agree with and feel free to disregard any that seem incorrect. Thank you for your contribution!

@kaizhou-lab
Copy link
Copy Markdown
Collaborator Author

Changes migrated to fix/auto-update-false-positive branch. Closing in favor of the consolidated fix.

kaizhou-lab pushed a commit that referenced this pull request Feb 27, 2026
…View

- Add rehype-raw support behind an opt-in allowHtml prop to MarkdownView
- Use allowHtml in UpdateModal so electron-updater's HTML release notes
  render correctly without needing a separate GitHub API call
- Migrated from PR #864

Closes #864
kaizhou-lab pushed a commit that referenced this pull request Feb 27, 2026
…View

- Add rehype-raw support behind an opt-in allowHtml prop to MarkdownView
- Use allowHtml in UpdateModal so electron-updater's HTML release notes
  render correctly without needing a separate GitHub API call
- Migrated from PR #864

Closes #864
minhlucvan added a commit to minhlucvan/AionUi that referenced this pull request Feb 27, 2026
* chore(ci): add manual build workflow for targeted platform builds

* chore(ci): add emoji to manual build workflow name

* chore(build-manual): add step summary with branch, commit, and platform info

* feat(gemini): lazy-load skills via index injection instead of full content

Previously, Gemini agent injected the full SKILL.md content (~3000+ chars)
of all builtin skills (e.g. cron) into system instructions on every
conversation, wasting tokens regardless of whether the skill was needed.

Now Gemini uses the same index-only injection strategy as ACP agents:
- First message injects only skill name + description (~302 chars)
- Full skill content is loaded on-demand via aioncli-core's activate_skill
- Worker config scans both top-level and _builtin/ skill directories
- Builtin skill names are merged into worker's enabledSkills list
- Added [LOAD_SKILL] detection in finish handler as a fallback mechanism

This reduces token usage by ~90% for conversations that don't use skills,
and aligns Gemini's skill loading strategy with ACP agents.

* 构建系统迁移 (Webpack → electron-vite) (iOfficeAI#947)

* feat: migrate build system from webpack/forge to electron-vite + bun

- Replace electron-forge + webpack with electron-vite for faster builds
- Add electron.vite.config.ts with UnoCSS, icon-park, and static copy plugins
- Remove forge.config.ts and config/webpack/ directory
- Update electron-builder.yml for electron-vite output structure
- Switch test runner from jest to vitest for better ESM support
- Add vitest.config.ts and tests/vitest.setup.ts
- Update package.json scripts to use bun/electron-vite commands
- Remove webpack-specific devDependencies, add electron-vite/vite/vitest
- Add justfile for task automation
- Add vx.toml and vx.lock for tool version management
- Update CI workflows for new build system
- Add configureConsole.ts for structured logging
- Update configureChromium.ts with CDP port management
- Add src/renderer/index.html as vite entry point

Signed-off-by: longhao <hal.long@outlook.com>

* fix: resolve build issues and migrate tests to Vitest

* docs: add development guide with vx to all language READMEs

* fix: update entry point loading for electron-vite

* chore: add pre-commit config for local/CI consistency

* chore: migrate CI package manager from npm to bun

* fix: add base path for renderer and update pre-commit to use bun

* fix: update pre-commit config for prek compatibility

* fix: add rollup native modules for all platforms

* fix: update CI cache strategy to avoid stale node_modules

* fix: add defer-to-connect as explicit dependency

* fix: add missing got nested dependencies for bun compatibility

* fix: use --ignore-scripts for bun install in CI

* refactor: migrate CI to use vx GitHub Action

* fix: explicitly specify node@22 in vx action

* fix: pin vx action to v0.8.4

* fix: configure bun to use default npm registry

* fix: use vx bunx directly instead of vx run

* fix: remove node_modules before install to avoid stale deps

* fix: use --ignore-scripts for bun install

* fix: regenerate bun.lock with default npm registry

* fix: update tsconfig.json with bundler moduleResolution

* fix: remove rm -rf node_modules from CI (incompatible with PowerShell)

* fix: change module to esnext for bundler moduleResolution

* feat: add build-test job for all platforms in PR checks

* fix: add type annotation for transform return value, add build-test job in PR checks

* chore(ci): use vx prek for code quality checks

* style: fix formatting issues by prek

* chore(ci): optimize prek to check only changed files in PR

* fix(ci): fetch full history for prek and parallelize build-test

* fix(build): use vx bunx instead of bunx for CI compatibility

* fix(ci): add --quiet to eslint and fix platform-specific build targets

* fix(ci): separate build args per platform and add diff dependency

* fix(lint): resolve prettier and eslint errors

* fix(ci): use vx --with msvc for Windows native module rebuild

* fix: resolve prettier error and downgrade diff to ^7.0.0 for electron-builder compatibility

* fix: remove duplicate diff key in package.json

* fix(ci): use vx just rebuild-native for Windows MSVC toolchain

* fix: regenerate bun.lock with diff@7.0.0

* fix: Windows build with npm dependencies

* fix: use npm for builds due to bun nested overrides limitation

* fix: add nested npm override for diff2html/diff

* ci: move ilammy/msvc-dev-cmd after vx setup to fix MSVC linker PATH on Windows runners

Signed-off-by: longhao <hal.long@outlook.com>

* ci: move ilammy/msvc-dev-cmd after vx setup to fix MSVC linker PATH on Windows runners

Signed-off-by: longhao <hal.long@outlook.com>

* ci: use vx bun install instead of vx npm install to fix dependency resolution

* ci: add --no-frozen-lockfile to bun install to fix diff dependency resolution

* ci: move ilammy/msvc-dev-cmd after vx setup to fix MSVC linker PATH on Windows runners

Signed-off-by: longhao <hal.long@outlook.com>

* fix(ci): restore UTF-8 encoding, add --no-frozen-lockfile, remove choco install step

* fix(deps): remove diff override to allow @office-ai/aioncli-core to use diff@^7.0.0

* ci: remove redundant Windows setup steps and fix macOS zip build error

- pr-checks.yml: remove choco/MSBuild/node-gyp/Windows SDK setup steps
  as they are already handled internally by �x just build-win-x64/arm64
  via �x --with msvc toolchain
- electron-builder.yml: remove macOS zip target to fix 7zip 16.02
  E_INVALIDARG error when compressing .app bundles with symlinks on CI
- .pre-commit-config.yaml: align local pre-commit hooks with CI prek config

* fix: remove nested vx call in getBunxCommand to fix MSVC env injection on Windows

Previously getBunxCommand() returned 'vx bunx.cmd', and getCommandPrefix()
returned 'vx --with msvc', resulting in the combined command:

  vx --with msvc vx bunx.cmd --yes electron-rebuild ...

The nested 'vx' call meant MSVC environment variables (VCINSTALLDIR, etc.)
were injected into a child vx process, not into the final node-gyp process,
so node-gyp could not find Visual Studio.

Fix: getBunxCommand() now returns the bare 'bunx.cmd' / 'bunx' command.
The caller's cmdPrefix ('vx --with msvc') already provides the vx entry
point and injects MSVC env vars directly into the subprocess that runs
node-gyp, so no second 'vx' wrapper is needed.

* fix: remove accidental $null file and add to .gitignore

PowerShell treats >  as a file redirect (creating a literal file
named $null) instead of discarding output like bash's > /dev/null.
This file was accidentally committed in b7c68bf.

- Remove the $null file from git tracking (git rm)
- Add $null to .gitignore to prevent future accidental commits

* ci: remove redundant 'Get Electron version' step and npm_config_target

The 'Get Electron version' step parsed the electron version from package.json
and passed it as npm_config_target to rebuild/build steps. This is unnecessary
because:

1. electron-builder reads the electron version directly from package.json
2. afterPack.js / rebuildNativeModules.js reads the version internally
3. electron-rebuild auto-detects the version from the installed electron package

Removing the step and all references to steps.electron-version.outputs.version
simplifies the workflow and eliminates the 'The operation was canceled' error
that occurred when the node -p command failed in certain runner environments.

* fix: resolve 7zip E_INVALIDARG on macOS/Windows via bun patch

- Add patches/7zip-bin@5.2.0.patch: replace bundled 7za with native wrappers
  - macOS: bundled 7za 16.02 fails on .app bundles with symlinks -> use system \zip -r -y\
  - Windows: bundled 7za 21.07 fails on large directories -> use PowerShell Compress-Archive
  - Linux: unchanged, still uses bundled 7za
  - Both wrappers fall back to bundled binary if wrapper creation fails
- package.json: add patchedDependencies for 7zip-bin@5.2.0
- electron-builder.yml: restore zip targets for mac and win (now fixed via patch)

* fix: correct 7zip-bin patch format (remove fake index hash)

bun patch parser performs hunk_header_integrity_check on the index line.
The previous patch had a fake hash index 1234567..abcdefg which caused:
  error: failed to parse patchfile: hunk_header_integrity_check_failed

Fix: remove the index line entirely, bun accepts standard diff format
without the index hash line.

* fix: regenerate 7zip-bin patch with correct git hunk headers

Previous patch had incorrect hunk header line counts causing:
  error: failed to parse patchfile: hunk_header_integrity_check_failed

Fix: regenerated patch using git diff --no-index with real file hashes
and correct hunk headers (@@ -1,6 +1,8 @@ and @@ -8,15 +10,93 @@).

Patch changes:
- macOS: use system zip instead of bundled 7za 16.02 (fixes symlink issue)
- Windows: use PowerShell Compress-Archive instead of bundled 7za 21.07 (fixes E_INVALIDARG)
- Linux: unchanged, still uses bundled 7za

* fix: rewrite 7zip-bin Windows patch using spawn monkey-patch

Previous approach used a .cmd wrapper file which failed with:
  spawn EINVAL - Windows batch goto/label not supported inside if blocks

New approach:
- Windows: monkey-patch child_process.spawn to intercept 7za.exe calls
  with 'a' command and replace with PowerShell .NET ZipFile API
  (System.IO.Compression.ZipFile::CreateFromDirectory)
- macOS: improved shell wrapper with proper flag-skipping logic
- path7za returns real 7za.exe on Windows (no wrapper file needed)

Also regenerated patch with correct git hunk headers and relative paths.

* fix: patch execFile in addition to spawn to intercept 7za zip calls

* fix: add *.patch to .gitattributes to enforce LF line endings

* fix: regenerate 7zip-bin patch with correct hunk headers for bun compatibility

* fix: use prebuild-install first for Windows same-arch builds

- electron-rebuild hangs on 'Searching dependency tree' in CI
- prebuild-install is faster and more reliable
- will fallback to electron-rebuild if no prebuilt binary available

* fix(ci): use prebuild-install first for Windows native modules in release build

- Consistent with afterPack.js fix (commit 2df0630)
- Try prebuild-install first, fallback to electron-rebuild
- Remove npm_config_build_from_source to allow prebuilt binaries
- Avoids electron-rebuild hanging on 'Searching dependency tree'

* fix(lint): remove trailing whitespace in multiple files

Fixed files:
- .github/workflows/build-and-release.yml
- skills/skill-creator/scripts/quick_validate.py
- .prettierignore
- skills/xlsx/recalc.py

* fix: address PR iOfficeAI#947 review feedback

- fix(toolTypes): change import type to value import for CodexAgentEventType to avoid runtime undefined
- fix(toolTypes): replace any with unknown in ToolRenderer.config and ToolDefinition.schema
- fix(eventData): change interface CodexJsonRpcEvent to type per project prefer-type convention
- fix(runtimePatches): only patch webpack dev server overlay when it already exists, avoid creating stub
- fix(toolUtils): decouple common layer from renderer i18n by injecting TranslateFn instead of importing i18n directly

* chore: remove vx dependency, use standard bun/node/choco toolchain

- justfile: replace all vx <cmd> with direct bun/node/bunx/npm/git calls
  - remove vx --with msvc wrapper (MSVC assumed available via VS2022 install)
  - update comments to reference choco/brew for tool installation
  - fix all internal vx just references to plain just
- build-and-release.yml: replace loonghao/vx action with setup-node + setup-bun + setup-python
  - remove all vx prefixes from run commands
  - fix linux matrix command: vx bun run dist:linux -> bun run dist:linux
  - add setup-node to create-tag job
- pr-checks.yml: replace loonghao/vx action with setup-node + setup-bun
  - remove all vx prefixes from run commands
  - add explicit just installation via choco (Windows) / install.sh (macOS/Linux)
  - add setup-msbuild step for Windows build jobs
  - replace vx just build-win-* with plain just build-win-*

* chore: remove vx.lock/vx.toml from tracking, fix CI prek 404 error

- .gitignore: add vx.lock and vx.toml (local dev only, not needed in CI)
- git rm --cached vx.lock vx.toml: untrack these files from repo
- pr-checks.yml: replace prek (404 on npm) with standard bun run lint + format:check + tsc --noEmit
- build-and-release.yml: same prek -> lint/format:check/tsc fix

* fix: remove vx commands, fix linux matrix YAML indent in build-and-release.yml

- scripts/build-with-builder.js: replace 'vx bunx electron-vite build' and 'vx bunx electron-builder' with 'bunx ...'
- build-and-release.yml: fix linux matrix command field indentation (YAML parse error)

* chore: remove vx-usage skills from all AI tool directories

* ci: use extractions/setup-just action instead of curl/choco install

* ci: add prek for incremental code quality checks on PR diff files

* fix: use correct .pre-commit-config.yaml format for prek, remove wrong prek.config.ts

* docs: remove vx references from all readme development sections, use prek/just/bun

---------

Signed-off-by: longhao <hal.long@outlook.com>

* fix: add Node.js engine version constraint to package.json

After the build system migration (iOfficeAI#947), using Node 25 causes
`crypto.getRandomValues is not a function` error at startup.
Add engines field to enforce Node >=22 <25.

Closes iOfficeAI#955

* ci: extract reusable build workflow to eliminate duplication

Move shared code-quality and build jobs from build-and-release.yml and
build-manual.yml into a new _build-reusable.yml reusable workflow.

- Add .github/workflows/_build-reusable.yml with workflow_call trigger
  supporting matrix, ref, skip_code_quality, and append_commit_hash inputs
- Reduce build-and-release.yml from ~530 to 253 lines
- Reduce build-manual.yml from ~540 to 82 lines
- Fix: add missing MSVC ARM64 toolchain step to build-and-release path
- Fix: add Write build summary step to build-and-release path
- Unify artifact retention-days to 7 across both workflows

* chore: remove package-lock.json (project uses bun.lock)

* chore: ignore package-lock.json and yarn.lock

* ci: sync bug fixes from fix/ci-vitest-test-path to reusable workflow

- Remove incorrect vitest test path restriction (run all tests, not just tests/extensions/)
- Add exit 0 after Windows native module verification to reset exit code on success

* ci: make MSVC ARM64 toolchain install non-blocking

Chocolatey community feed is unreliable in CI (HTTP 499 errors).
Setting continue-on-error: true so Chocolatey failures don't abort
the windows-arm64 build — prebuild-install will download prebuilt
binaries instead, and electron-rebuild with MSVC only runs as a
last resort.

* feat(markdown): support \[...\] and \(...\) LaTeX math delimiters

Add preprocessing to convert LaTeX-style math delimiters to dollar-sign
delimiters before remark-math processes them:
- \[...\] → $$...$$ (block display math)
- \(...\) → $...$ (inline math)

Content inside fenced code blocks and inline code spans is preserved.
Applied to both chat messages (Markdown.tsx) and preview panel
(MarkdownViewer.tsx).

Closes iOfficeAI#936

* ci: consolidate per-platform build summaries into single job

Replace 5 duplicate per-platform "Write build summary" steps in the
matrix build job with a single `build-summary` job that runs after all
builds complete.

- Add `outputs.commit-short` to `build` job (last-wins, same value
  across all matrix instances)
- Remove per-platform "Write build summary" step (lines 162-170)
- Add new `build-summary` job with `needs: [code-quality, build]` and
  `if: always()` to ensure it runs even when code-quality is skipped or
  build fails
- Use `jq` to extract all platform names from the matrix input and
  display them in a single consolidated table

* fix(gemini): warn on unexpected errors when loading _builtin skills

Only ignore ENOENT errors (directory not found) when scanning the
_builtin skills subdirectory. Log a warning for other unexpected
failures to aid debugging.

* fix(gemini): compact large tool responses in history to prevent context overflow

When files are uploaded and read via read_file tool calls, the file
content (especially base64-encoded images/PDFs) accumulates in the
conversation history as functionResponse parts. Since the entire history
is resent on every subsequent turn, this causes rapid context window
exhaustion — making it impossible to continue the conversation.

After each agentic loop finishes (no pending tool calls), walk through
the history and:
- Replace inlineData (base64 images/audio/pdf) with text placeholders
- Truncate text functionResponse parts exceeding 10K characters
- Preserve functionCall ↔ functionResponse pairing for API compatibility

The truncation hints guide the model to re-read files via read_file
tool if needed in later turns.

Closes iOfficeAI#870

* fix(ui): detect orphaned </think> closing tags from MiniMax M2.5

The `hasThinkTags` regex only matched opening `<think>` tags, missing
orphaned closing `</think>` tags. Models like MiniMax M2.5 omit the
opening tag, causing thinking content to leak into the UI.

- Update `hasThinkTags` to match both opening and closing tags
- Update `stripThinkTags` to remove content before orphaned `</think>`
- Fix detection in Gemini stream processing, GeminiAgentManager, and
  frontend thinkTagFilter
- Add MiniMax M2.5 test cases

Closes iOfficeAI#876

* fix(gemini): disable native SkillManager to prevent XML injection into system prompt

Set skillsSupport: false to prevent aioncli-core's native SkillManager
from injecting XML <available_skills> blocks into the system prompt.

AionUi handles skills through its own AcpSkillManager mechanism with
plain-text index injection (iOfficeAI#952). The native SkillManager is redundant
and causes duplicate activate_skill tool registration and skill content
leaking into all conversations.

Closes iOfficeAI#965

* fix(ui): preserve orphaned </think> in stream for frontend filtering

The previous fix stripped orphaned </think> tags at the per-chunk level
in processGeminiStreamEvents and GeminiAgentManager, which prevented
the frontend from detecting thinking content in accumulated messages.

In streaming mode, thinking content arrives in earlier chunks without
any tags. Only the chunk containing </think> has the tag. By stripping
it at the backend, the frontend's accumulated content had no tags left,
so MessagetText's stripThinkTags never triggered — leaving thinking
content permanently visible.

- Preserve orphaned </think> in content stream (utils.ts)
- Only strip complete <think>...</think> blocks in filterThinkTagsFromMessage
- Filter think tags in useAutoTitle before extracting conversation name
- Add streaming accumulated content test case

* fix(ui): prevent horizontal overflow on summary message items

Replace w-full with min-w-0 on file_summary/tool_summary container
to match MessageItem behavior, preventing unwanted horizontal scrollbar
in the message list.

Closes iOfficeAI#967

* feat: update version to 1.8.17

* fix(lint): fix prettier formatting and replace no-explicit-any in tests

- Fix 4 prettier formatting errors in src/agent/gemini/utils.ts
- Replace `as any` with `as unknown as string` in ThinkTagDetector tests

* fix(build): skip 7zip monkey-patch for 7z format to fix Windows NSIS installer

The 7zip-bin patch introduced in iOfficeAI#947 replaces all 7za.exe archive
creation calls with PowerShell ZipFile API on Windows. This breaks
NSIS installer packaging because electron-builder uses 7za to create
.7z archives that the NSIS stub extracts on install. When the archive
is silently created in ZIP format instead of 7z, the installer stub
fails to extract app files, leaving only the uninstaller and shortcuts.

Add two guards to buildPsZipArgs():
- Return null when args contain -t7z (explicit 7z format flag)
- Return null when output file ends with .7z

This ensures the real 7za.exe handles NSIS packaging while the
PowerShell fallback still applies for ZIP archive creation.

Closes iOfficeAI#971

* fix(build): remove Windows 7zip monkey-patch to fix NSIS installer (iOfficeAI#971)

The monkey-patch replaced all 7za.exe calls with PowerShell ZipFile API
on Windows, creating ZIP format instead of 7z. This broke NSIS installers
since nsis7z.dll can only extract real 7z archives, leaving only the
uninstaller and desktop shortcut after installation.

Remove the Windows monkey-patch entirely and let the bundled 7za.exe
handle all archive operations directly. Keep macOS wrapper intact for
symlink compatibility.

* fix(build): remove Windows 7zip monkey-patch and fix compression level (iOfficeAI#971)

The 7zip-bin patch monkey-patched child_process.spawn/execFile on Windows
to replace all 7za.exe calls with PowerShell ZipFile API. This created
ZIP format instead of 7z, breaking NSIS installers (nsis7z.dll cannot
extract ZIP archives), leaving only the uninstaller after installation.

The monkey-patch was originally added to work around 7za E_INVALIDARG
errors, but the actual root cause was ELECTRON_BUILDER_COMPRESSION_LEVEL
being set to 'maximum' (a string) instead of '9' (numeric). 7za's -mx
flag only accepts numeric values 0-9.

Changes:
- Remove Windows monkey-patch from 7zip-bin patch (keep macOS wrapper)
- Fix compression level: 'maximum'/'normal' -> '9'/'7'

* update wx group

* fix(confirm): prevent buttons from being hidden when title/description is too long (iOfficeAI#979)

Move title and divider into the scrollable area so that action buttons
always remain visible at the bottom of the confirmation dialog,
regardless of content length.

* fix(confirm): add ellipsis truncation for long titles in confirmation dialog

* feat: implement electron-builder auto-update functionality

* fix: add electron-log dependency for auto-updater service

* feat: add tests and optimize CI caching

* refactor(auto-update): remove mainWindow param, add triggerEventForTest, fix tests

- updateBridge: remove mainWindow param from createAutoUpdateStatusBroadcast,
  making it a pure ipcBridge emitter with no window binding
- index.ts: update call site to match new signature
- autoUpdaterService: store handlers in _autoUpdaterHandlers Map for precise
  cleanup; resetForTest now calls removeListener per event instead of
  removeAllListeners; expose triggerEventForTest to invoke handlers directly
- autoUpdaterService.test.ts: replace all mock.calls+if guard patterns with
  triggerEventForTest; add resetForTest/reset lifecycle tests; fix weak assertions
- autoUpdate.integration.test.ts: remove window arg from broadcast factory;
  harden weak assertions; add full chain test wiring initUpdateBridge +
  initialize(createAutoUpdateStatusBroadcast())

* chore(auto-update): update ipcBridge channels, vitest coverage config and deps

- ipcBridge.ts: add/adjust auto-update IPC channel definitions
- vitest.config.ts: add coverage include paths and thresholds for auto-update modules
- package.json / bun.lock: sync dependency updates

* revert(ci): restore non-auto-update CI changes in _build-reusable.yml

Revert changes unrelated to auto-update:
- Restore workflow name to 'Build Pipeline (Reusable)'
- Restore input descriptions, defaults and append_commit_hash param
- Remove bun/Electron cache steps added in code-quality and build jobs
- Restore build-summary job
- Restore artifact name with append_commit_hash logic
- Restore Windows build command via env var

Keep only auto-update related change:
- unit test command scoped to tests/unit/ directory

* fix: resolve CI failures in auto-update related files

- fix(test): increase waitForExit timeout to 8000ms in test_acp_connection_disconnect.ts
  to cover two sequential taskkill calls (each up to 2000ms) on Windows
- fix(UpdateModal): pass {} to autoUpdate.check.invoke() to satisfy TS2554
  (Expected 1 argument, but got 0)
- fix(format): run prettier on UpdateModal/index.tsx, autoUpdaterService.test.ts,
  vitest.config.ts to fix formatting errors reported by CI code quality check

* fix: resolve pre-commit hook failures

- fix(autoUpdaterService): cast event to keyof AppUpdaterEvents in removeListener
  to fix TS2345 type error in resetForTest()
- fix(ci): add missing newline at end of _build-reusable.yml

* fix: increase waitForPidFile timeout and complete i18n update translations

- fix(test): increase waitForPidFile timeout from 5000ms to 10000ms in
  test_acp_connection_disconnect.ts to avoid race condition when running
  all tests in parallel (child process startup slower under load)
- feat(i18n): add missing update keys to ja-JP, ko-KR, tr-TR, zh-TW
  - add downloadAndInstall, readyToInstall, readyToInstallDesc,
    installNow, autoUpdateMode to all four locales
  - translate ko-KR update block from English to Korean

* fix: prettier format and add electron-builder binaries cache

- fix(lint): reformat autoUpdater.removeListener call to single line
  to satisfy prettier/prettier rule in autoUpdaterService.ts
- ci: add actions/cache step for electron-builder binaries
  (winCodeSign, nsis, etc.) to avoid repeated downloads on each build
  cache key scoped by platform, arch and package.json hash

* fix(auto-update): fix Switch toggle bug, null crash, missing release assets

- Fix UpdateModal Switch disappearing when toggled off
- Add null check for autoUpdater.checkForUpdates() in dev mode
- Add try-catch to quitAndInstall IPC handler
- Include *.yml manifests and *.blockmap in release uploads

* chore(build): set releaseType to prerelease for auto-update testing

* fix(auto-update): fix quitAndInstall return type to match IPC definition

* chore: downgrade version to 1.8.10 for auto-update testing

* Revert "chore: downgrade version to 1.8.10 for auto-update testing"

This reverts commit 7dcf458.

* fix(ci): remove arch from cache key to avoid comma in Linux multi-arch builds

* fix(ci): add exit 0 to Windows native module rebuild to reset exit code

* fix(auto-update): fix 404 on update check, missing yml artifacts, and prerelease detection

- Hardcode publish owner/repo in electron-builder.yml to fix unresolved
  env var template causing 404 on update check
- Add *.yml and *.blockmap to artifact upload in reusable build workflow
  so electron-updater manifest files are included in releases
- Pass includePrerelease to autoUpdate.check so prerelease versions
  can be detected when the setting is enabled

* fix(auto-update): use markdown release notes instead of raw HTML

electron-updater returns releaseNotes as HTML, but MarkdownView expects
markdown. Now also fetches release info from manual check to get the
markdown body for proper rendering.

* chore(build): revert releaseType to release after auto-update testing

* fix(ci): remove explicit tests folder from vitest run command

vitest.config.ts already configures include patterns, no need to
pass the folder explicitly.

fix(test): fix flaky Windows process exit timeout in disconnect test

- AcpConnection.terminateChild: use taskkill /T /F directly on Windows
  instead of first trying without /F, avoiding delayed process exit
- waitForExit: after timeout, check if process already exited before
  throwing (taskkill may succeed but exit event can be delayed on Windows)

* ci: remove hardcoded test path from vitest command

* fix(webui): resolve packaged renderer path lookup (iOfficeAI#980)

* fix(webui): resolve packaged renderer path lookup

* fix(webui): differentiate guide link style and enable tertiary text token

* fix(ci): resolve prettier and ts build issues in webui startup changes

* feat: preserve user mode/model preferences, IME fix, cron badge, and tool call details

1. fix(guid): persist preferred mode and model per agent so new
   conversations remember the user's last selection instead of
   resetting to default
2. fix(guid): prevent IME composition enter from truncating and
   sending incomplete Chinese input
3. feat(messages): wrap cron-triggered messages in a dedicated UI
   bubble with schedule badge and trigger time
4. feat(messages): allow expanding tool call groups to view
   invocation details

Also: deprecate CodexAgentManager (now handled via ACP), fix
ThinkTagDetector edge cases, and prevent ACP model cache from
leaking session-level model switches to the Guid page.

* refactor(history): split WorkspaceGroupedHistory into sub-modules

Extract the 1308-line monolithic component into grouped-history/:
- types.ts: shared type definitions
- utils/groupingHelpers.ts: timeline grouping and sorting logic
- utils/exportHelpers.ts: file/path/markdown/json export builders
- ConversationRow.tsx: conversation row sub-component
- hooks/useConversations.ts: data fetching and workspace expansion
- hooks/useBatchSelection.ts: batch mode selection state
- hooks/useConversationActions.ts: delete/rename/pin/navigate actions
- hooks/useExport.ts: full export workflow
- index.tsx: main component assembling all hooks

Original file replaced with a single re-export for backward compat.

Also fix conversation modifyTime not updating on user message send
in AcpAgentManager and GeminiAgentManager, causing stale history sort.

* fix(mcp): improve error messages, stale config detection, and TTY crash prevention (iOfficeAI#972)

* fix(acp): graceful fallback when session resume fails after bridge package rename

* fix(mcp): improve error messages, stale config detection, and TTY crash prevention

- Add user-friendly error messages for missing node/npx (ENOENT), permission
  errors (EACCES), and timeouts in testStdioConnection
- Detect MCP config changes in GeminiAgentManager.sendMessage and re-bootstrap
  worker when servers are added/removed/toggled, preventing stale tool calls
- Show error details in tool call failure UI (description + resultDisplay fallback)
- Introduce safeExec/safeExecFile utility using spawn with detached:true to
  isolate child process groups, preventing CLI tools from triggering SIGTTOU
  (zsh: suspended tty output) on the parent Electron process
- Serialize MCP service operations with withServiceLock to prevent process storms

* fix(mcp): use comprehensive fingerprint to detect delete-then-re-add scenarios

The previous fingerprint only included names of enabled+connected servers,
so deleting and re-adding a server with the same name produced an identical
fingerprint and skipped worker re-bootstrap. The new fingerprint captures
name, enabled, status, and transport key for every server entry.

* fix(ui): improve cron badge and message bubble styling

- CronBadge: add rounded-full, bg-fill-2 background, i18n date format
- CronBadge: remove cronJobName display, show only trigger time
- MessageText: apply bubble style (bg, padding, border-radius) to cron messages
- MessageText: remove w-full from bubble to fix left border-radius clipping

* style(ui): adjust cron badge font size class

* feat(settings): polish remote webui and channels UX (iOfficeAI#974)

* fix(workspace): prevent data flicker during AI function calls

Remove refreshWorkspace from the reset effect's dependency array in
useWorkspaceEvents to prevent unnecessary state clearing when AI tools
are invoked. Use a ref to always call the latest refreshWorkspace
without triggering the effect.

Also preserve user's expanded tree keys during workspace refresh by
using functional setExpandedKeys update, only resetting on initial
load or search.

* fix(messages): resolve streaming auto-scroll jitter and follow issues (iOfficeAI#976)

- Replace manual scrollToIndex during streaming with Virtuoso's native
  followOutput prop to avoid layout recalculation conflicts
- Add atBottomStateChange callback for reliable bottom detection
- Use timestamp-based guard (150ms) instead of flag+setTimeout to filter
  programmatic scroll events, preventing false user-scroll detection
- Simplify useEffect to only handle user-sent message force-scroll

* chore: bump version to 1.8.18 (iOfficeAI#998)

Co-authored-by: zk <zk@users.noreply.local>

* style: remove extra blank line in MessagetText

* fix: add return-type annotation to suppress TS7011

* fix(update): correct false-positive update detection and auto-show modal

- Add semver comparison in autoUpdate.check handler to prevent reporting
  updates when the remote version is not newer than the current version
- Reset allowDowngrade before startup check to prevent stale state from
  prior setAllowPrerelease(true) calls causing downgrade false positives
- Auto-show UpdateModal when update-available status event is received
  so startup update checks are visible to users

* fix(gemini): prevent message leak across conversations when switching tabs

GeminiConversationPanel was missing a `key` prop, causing React to reuse
the component (and its MessageListProvider state) when switching between
Gemini conversations. Combined with the merge logic introduced in 1.8.18
(commit 48868ce), messages from previous conversations were preserved as
"streaming only" entries instead of being discarded.

Two fixes applied:
- Add `key={conversation.id}` to GeminiConversationPanel so the component
  remounts on conversation switch (consistent with ACP/Codex/OpenClaw).
- Filter by conversation_id in useMessageLstCache merge logic to prevent
  stale messages from other conversations from leaking through.

* chore: set version to 1.8.9 for auto-update testing

* feat(markdown): add allowHtml prop for raw HTML rendering in MarkdownView

- Add rehype-raw support behind an opt-in allowHtml prop to MarkdownView
- Use allowHtml in UpdateModal so electron-updater's HTML release notes
  render correctly without needing a separate GitHub API call
- Migrated from PR iOfficeAI#864

Closes iOfficeAI#864

* Revert "chore: set version to 1.8.9 for auto-update testing"

This reverts commit fe39479.

* fix(workspace): ignore stale aborted getWorkspace responses

The backend uses buildLastAbortController() to cancel previous
getWorkspace IPC calls when a new one starts. Aborted requests
return [] which was being applied to state, causing the workspace
tree to flash empty during AI function calls.

Add a sequence counter (loadSeqRef) so only the latest request's
response updates the UI. Stale/aborted responses are silently
discarded.

* Revert "fix(workspace): prevent data flicker during AI function calls"

This reverts commit dac5e7f.

* docs(readme): remove outdated justfile commands section

---------

Signed-off-by: longhao <hal.long@outlook.com>
Co-authored-by: zynx <>
Co-authored-by: 瓦砾 <icey.liu1994@gmail.com>
Co-authored-by: zk <zk@users.noreply.local>
Co-authored-by: Hal <13111745+loonghao@users.noreply.github.com>
Co-authored-by: goworm <zmworm@gmail.com>
Co-authored-by: zynx <3362922+piorpua@users.noreply.github.com>
Co-authored-by: longhao <hal.long@outlook.com>
Co-authored-by: Cocoon-Break <54054995+kuishou68@users.noreply.github.com>
Co-authored-by: vishergreat <visherlin@office-ai.cn>
Co-authored-by: kaizhou-lab <1558390418@qq.com>
Co-authored-by: kuishou68 <lijianlin6868@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 检查更新,显示html代码

1 participant