4949
5050# Internal wrapper function names to replace with <module>
5151_WRAPPER_NAMES = ("__repl_wrapper__" , "__wrapper__" )
52- _TRUNCATED_DIAGNOSTIC_PATTERN = re .compile (
53- r"\A<truncated-output>\n"
54- r"Output too large \(([\d,]+) chars\)\. "
55- r"Showing first ([\d,]+) and last ([\d,]+) chars\.\n"
56- r"The ([\d,]+) chars in the middle are not recoverable\.\n\n"
57- r"(.*?)\n\n\.\.\. ([\d,]+) chars not shown \.\.\.\n\n"
58- r"(.*)\n</truncated-output>\Z" ,
59- re .DOTALL ,
60- )
52+
6153
6254# ---------------------------------------------------------------------------
6355# Targeted model-recovery hints
7870_HEREDOC_RE = re .compile (r"<<-?\s*['\"]?\w+['\"]?" )
7971
8072# Appended to SyntaxErrors that look like heredoc-in-quoted-string failures.
81- _HEREDOC_HINT = """ \
82- Hint: this looks like a bash heredoc (`<<...`) embedded in a single- or
83- double-quoted Python string. Python rejects multi-line single/double -quoted
84- strings, which is why the parser errored before reaching the heredoc body.
73+ _HEREDOC_HINT = ''' \
74+ Hint: this looks like a shell heredoc (`<<...`) embedded in a single- or
75+ multiple-line Python string. Put the complete command in a triple -quoted
76+ string, then pass that value using the callable's documented API:
8577
86- Fix 1 (preferred) — use a triple-quoted Python string so newlines are legal:
87- await shell.run(\" \" \" cat <<EOF
88- content
89- EOF\" \" \" )
78+ command = """cat <<'EOF'
79+ content
80+ EOF"""
9081
91- Fix 2 — write the script to a file first, then run it:
92- await shell.write("/tmp/script.sh", script_text)
93- await shell.run("bash /tmp/script.sh")"""
82+ Use `doc(...)` to inspect the available command runner and how it accepts
83+ commands or standard input.'''
9484
9585
9686# Call-shape TypeError messages — a method/function called with the wrong
@@ -498,77 +488,11 @@ def _hard_bound_text(text: str, limit: int, *, closing: str = "") -> str:
498488 return marker [:limit ]
499489
500490
501- def _bound_preformatted_diagnostic (
502- text : str ,
503- max_error : int | None ,
504- tail_chars : int | None ,
505- ) -> str :
506- """Bound trusted backend text without nesting a valid truncation envelope."""
507- text = text .rstrip ()
508- limit , tail = _diagnostic_budget (max_error , tail_chars )
509- if len (text ) <= limit :
510- return text
511-
512- match = _TRUNCATED_DIAGNOSTIC_PATTERN .fullmatch (text )
513- if match is None :
514- return _bound_diagnostic (text , max_error , tail_chars )
515-
516- total_text , old_head_text , old_tail_text , dropped_text , head , repeated_text , tail_text = (
517- match .groups ()
518- )
519- try :
520- total = int (total_text .replace ("," , "" ))
521- old_head_chars = int (old_head_text .replace ("," , "" ))
522- old_tail_chars = int (old_tail_text .replace ("," , "" ))
523- dropped = int (dropped_text .replace ("," , "" ))
524- repeated_dropped = int (repeated_text .replace ("," , "" ))
525- except ValueError :
526- return _bound_diagnostic (text , max_error , tail_chars )
527- if (
528- old_head_chars != len (head )
529- or old_tail_chars != len (tail_text )
530- or dropped != repeated_dropped
531- or total != old_head_chars + old_tail_chars + dropped
532- ):
533- return _bound_diagnostic (text , max_error , tail_chars )
534-
535- desired_tail = limit // 2 if tail is None else tail
536- desired_head = limit - desired_tail
537- if old_head_chars <= desired_head and old_tail_chars <= desired_tail :
538- return _hard_bound_text (
539- text ,
540- limit + 1_024 ,
541- closing = "\n </truncated-output>" ,
542- )
543-
544- # The original middle is already gone, so retain as much of each requested
545- # window as remains available and accurately describe the larger omission.
546- bounded_head = head [:desired_head ]
547- bounded_tail = tail_text [- desired_tail :] if desired_tail else ""
548- new_dropped = total - len (bounded_head ) - len (bounded_tail )
549- rebuilt = (
550- "<truncated-output>\n "
551- f"Output too large ({ total :,} chars). "
552- f"Showing first { len (bounded_head ):,} and last { len (bounded_tail ):,} chars.\n "
553- f"The { new_dropped :,} chars in the middle are not recoverable.\n \n "
554- f"{ bounded_head } \n \n "
555- f"... { new_dropped :,} chars not shown ...\n \n "
556- f"{ bounded_tail } \n "
557- "</truncated-output>"
558- )
559- return _hard_bound_text (
560- rebuilt ,
561- limit + 1_024 ,
562- closing = "\n </truncated-output>" ,
563- )
564-
565-
566491class ErrorFormatter (Protocol ):
567492 """Preferred strategy formatter contract.
568493
569- Implementations receive trusted backend-rendered diagnostics and the resolved
570- per-call error budget. Custom strategy formatters must implement this complete
571- contract.
494+ Implementations receive the exception, source context, and resolved per-call
495+ error budget. Custom strategy formatters must implement this complete contract.
572496 """
573497
574498 def format (
@@ -577,7 +501,6 @@ def format(
577501 code : str | None = None ,
578502 * ,
579503 line_offset : int = 0 ,
580- formatted_error : str = "" ,
581504 max_error : int | None = None ,
582505 tail_chars : int | None = None ,
583506 ) -> str : ...
@@ -601,7 +524,6 @@ def format(
601524 code : str | None = None ,
602525 * ,
603526 line_offset : int = 0 ,
604- formatted_error : str = "" ,
605527 max_error : int | None = None ,
606528 tail_chars : int | None = None ,
607529 ) -> str :
@@ -611,9 +533,6 @@ def format(
611533 error: The exception to format.
612534 code: Optional source code (used for syntax errors if text is missing).
613535 line_offset: Number of wrapper lines to subtract from line numbers.
614- formatted_error: Trusted diagnostic already rendered by an execution
615- backend. It bypasses local rendering so worker-local traceback
616- and source context are preserved.
617536 max_error: Maximum retained diagnostic characters. ``None`` uses the
618537 framework default.
619538 tail_chars: Characters reserved for the retained tail. ``None`` uses
@@ -622,10 +541,15 @@ def format(
622541 Returns:
623542 Formatted error string with adjusted line numbers.
624543 """
625- if formatted_error :
626- # Worker-generated truncation envelopes have already applied the
627- # resolved policy; preserve one envelope while retaining a hard cap.
628- return _bound_preformatted_diagnostic (formatted_error , max_error , tail_chars )
544+ from nooa .runtime .sandbox .errors import SandboxExecutionError
545+
546+ if isinstance (error , SandboxExecutionError ):
547+ limit , _ = _diagnostic_budget (max_error , tail_chars )
548+ return _hard_bound_text (
549+ error .diagnostic .rstrip () or str (error ),
550+ limit + 1_024 ,
551+ closing = "\n </truncated-output>" ,
552+ )
629553
630554 if isinstance (error , SyntaxError ):
631555 formatted = self ._format_syntax_error (error , code , line_offset )
@@ -699,7 +623,6 @@ def format_error_for_llm(
699623 code : str | None = None ,
700624 * ,
701625 line_offset : int = 0 ,
702- formatted_error : str = "" ,
703626 max_error : int | None = None ,
704627 tail_chars : int | None = None ,
705628) -> str :
@@ -719,11 +642,6 @@ def format_error_for_llm(
719642 line_offset: Number of wrapper lines to subtract from line numbers.
720643 This compensates for lines added by the async wrapper (e.g.,
721644 "async def __repl_wrapper__():", "try:", etc.).
722- formatted_error: Optional preformatted diagnostic produced by a trusted
723- execution backend such as the sandbox worker. When non-empty, it
724- bypasses local formatting (including ``code``, ``line_offset``, and
725- bad-call hint handling). The producer is responsible for applying any
726- required line adjustment.
727645 max_error: Maximum retained diagnostic characters. ``None`` uses the
728646 framework default.
729647 tail_chars: Characters reserved for the retained tail. ``None`` uses
@@ -737,7 +655,6 @@ def format_error_for_llm(
737655 error ,
738656 code ,
739657 line_offset = line_offset ,
740- formatted_error = formatted_error ,
741658 max_error = max_error ,
742659 tail_chars = tail_chars ,
743660 )
0 commit comments