Skip to content

fix(ai-plugin): writeMacro family no-ops on web Document Server — nes…#596

Open
zhaokaige wants to merge 1 commit into
ONLYOFFICE:masterfrom
zhaokaige:fix/ai-writemacro-nested-eval-shadowed
Open

fix(ai-plugin): writeMacro family no-ops on web Document Server — nes…#596
zhaokaige wants to merge 1 commit into
ONLYOFFICE:masterfrom
zhaokaige:fix/ai-writemacro-nested-eval-shadowed

Conversation

@zhaokaige

Copy link
Copy Markdown

On a web-deployed Document Server, writeMacro (and generateDocx / changeParagraphStyle, same pattern) silently fail: WRITE requests report success but never modify the document; READ requests return no data.

(1) WRITE: the macro code is passed to Asc.Editor.callCommand as a nested eval inside the function body. Asc.plugin.callCommand serializes the function via toString(), and the editor core executes it through _safe_eval_closure (sdk-all-min.js), which wraps the string with Function.apply(... return eval(…)) and shadows the window formal parameter — a deliberate plugin sandbox. A side effect is that a nested eval() inside the function body returns undefined and never runs, so the macro silently no-ops. Verified by four probes: new Function(code) and executeCommand("command", code) mutate; nested eval(code) and nested new Function(code)() do not. The macro code must be the function body, not runtime-eval'd data.

(2) READ: replacing eval with new Function fixes WRITE, but a new Function body discards the last expression's value. The prompts and tool descriptions were built on eval's auto-return semantics, so a READ macro returned nothing.

Fix:

  • helpers.js (3 sites): dual-form new Function wrapper. Expression form captures an IIFE / single expression; if new Function throws (multi-statement or has return), fall back to the statement form which captures a top-level return. try/catch + onlyoffice_id_result / onlyoffice_id_error_message contract preserved (safePluginEval swallows thrown errors).
  • helperFuncs.js + helpers.js: align prompts and tool descriptions to the new execution model — read examples use explicit return; "last expression" -> "explicit return"; add READ/WRITE/CHAT decision rule (mutate only on explicit modify; reading never mutates), GENERATIVE rule (ask first on ambiguous "write content" requests), and SELF-CHECK rule (verify top-level return on READ macros).

Sandbox untouched; plugin isolation unchanged.

…ted eval shadowed by sandbox

On a web-deployed Document Server, writeMacro (and generateDocx /
changeParagraphStyle, same pattern) silently fail: WRITE requests report
success but never modify the document; READ requests return no data.

(1) WRITE: the macro code is passed to Asc.Editor.callCommand as a nested
eval inside the function body. Asc.plugin.callCommand serializes the function
via toString(), and the editor core executes it through _safe_eval_closure
(sdk-all-min.js), which wraps the string with Function.apply(... return eval(…))
and shadows the window formal parameter — a deliberate plugin sandbox. A side
effect is that a nested eval() inside the function body returns undefined and
never runs, so the macro silently no-ops. Verified by four probes:
new Function(code) and executeCommand("command", code) mutate; nested
eval(code) and nested new Function(code)() do not. The macro code must be the
function body, not runtime-eval'd data.

(2) READ: replacing eval with new Function fixes WRITE, but a new Function
body discards the last expression's value. The prompts and tool descriptions
were built on eval's auto-return semantics, so a READ macro returned nothing.

Fix:
- helpers.js (3 sites): dual-form new Function wrapper. Expression form
  captures an IIFE / single expression; if new Function throws (multi-statement
  or has return), fall back to the statement form which captures a top-level
  return. try/catch + onlyoffice_id_result / onlyoffice_id_error_message
  contract preserved (safePluginEval swallows thrown errors).
- helperFuncs.js + helpers.js: align prompts and tool descriptions to the new
  execution model — read examples use explicit return; "last expression" ->
  "explicit return"; add READ/WRITE/CHAT decision rule (mutate only on explicit
  modify; reading never mutates), GENERATIVE rule (ask first on ambiguous
  "write content" requests), and SELF-CHECK rule (verify top-level return on
  READ macros).

Sandbox untouched; plugin isolation unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@drape3a drape3a added the AI Issues with plugin chatgpt label Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Issues with plugin chatgpt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants