@@ -191,12 +191,19 @@ const DOC_COMPILE_TIMEOUT_MS = 120_000
191191// them). Indented at column 0 so it concatenates cleanly after the user's script.
192192const XLSX_RECALC_SNIPPET = `
193193import subprocess as __sim_sp, shutil as __sim_sh, os as __sim_os
194- __sim_os.makedirs("/home/user/__recalc", exist_ok=True)
195- __sim_sp.run(
196- ["soffice", "--headless", "--convert-to", "xlsx", "--outdir", "/home/user/__recalc", "/home/user/output.xlsx"],
197- check=True, timeout=120, capture_output=True,
198- )
199- __sim_sh.move("/home/user/__recalc/output.xlsx", "/home/user/output.xlsx")
194+ # Best-effort: bake cached formula values via LibreOffice. If recalc fails
195+ # (soffice crash/timeout/unsupported), keep the openpyxl workbook as-is — it's
196+ # still a valid file (formulas just lack cached values). Never fail the user's
197+ # compile over an infra recalc failure.
198+ try:
199+ __sim_os.makedirs("/home/user/__recalc", exist_ok=True)
200+ __sim_sp.run(
201+ ["soffice", "--headless", "--convert-to", "xlsx", "--outdir", "/home/user/__recalc", "/home/user/output.xlsx"],
202+ check=True, timeout=120, capture_output=True,
203+ )
204+ __sim_sh.move("/home/user/__recalc/output.xlsx", "/home/user/output.xlsx")
205+ except Exception as __sim_recalc_err:
206+ print("xlsx recalc skipped:", __sim_recalc_err)
200207` . trim ( )
201208
202209interface CompileArgs {
@@ -324,19 +331,27 @@ ${finalize}
324331 outputSandboxPath,
325332 } )
326333
327- // Only treat the run as a success when the script reached the finalizer
328- // (__DOC_OK__) AND produced the output file. A script that writes the file then
329- // throws would otherwise persist a partial/corrupt artifact (mirrors the Python
330- // path, which checks result.error first).
334+ // Success requires the script to reach the finalizer (__DOC_OK__) AND produce
335+ // the output file — a script that writes then throws must not persist a
336+ // partial/corrupt artifact (mirrors the Python path).
331337 const out = `${ result . stdout || '' } \n${ result . error || '' } `
332- const failed = ! ! result . error || ! out . includes ( '__DOC_OK__' )
333- if ( ! failed && result . exportedFileContent ) {
338+ const errMatch = out . match ( / _ _ D O C _ E R R _ _ ( [ \s \S ] * ) / )
339+ if ( out . includes ( '__DOC_OK__' ) && result . exportedFileContent ) {
334340 return Buffer . from ( result . exportedFileContent , 'base64' )
335341 }
336- // Capture the full (possibly multi-line) error message after the marker.
337- const m = out . match ( / _ _ D O C _ E R R _ _ ( [ \s \S ] * ) / )
338- const msg = m ?. [ 1 ] ?. trim ( ) || result . error || 'the script produced no output'
339- throw new DocCompileUserError ( `${ ext . toUpperCase ( ) } generation failed: ${ msg } ` )
342+ if ( errMatch ) {
343+ // The script ran and threw — a user-code error the agent should fix.
344+ throw new DocCompileUserError (
345+ `${ ext . toUpperCase ( ) } generation failed: ${ errMatch [ 1 ] ?. trim ( ) || 'unknown error' } `
346+ )
347+ }
348+ // No __DOC_OK__ and no __DOC_ERR__ → node never completed (sandbox died, command
349+ // failure, or the output couldn't be read). That's a retriable system error, not
350+ // the agent's code — surface it as a plain Error so callers don't tell the agent
351+ // to "fix its code".
352+ throw new Error (
353+ `${ ext . toUpperCase ( ) } compile did not complete in the sandbox: ${ result . error || 'no output produced' } `
354+ )
340355}
341356
342357/**
0 commit comments