@@ -99,9 +99,34 @@ def _tool_result_preview(content: str) -> str:
9999 return _head_chars (preview , config .TOOL_RESULT_PREVIEW_CHARS )
100100
101101
102- # the completion-check filter: a [FINAL CHECK] header followed by the
103- # Goal:/Status:/Evidence: labels (anywhere in the block, any lines)
104- _FINAL_CHECK_RE = re .compile (r"\[FINAL CHECK\].*Goal:.*Status:.*Evidence:" , re .DOTALL )
102+ # the completion-check filter: a FINAL CHECK header followed by the
103+ # Goal/Status/Evidence labels (anywhere in the block, any lines).
104+ #
105+ # Models reformat the block from task-completion-rules.md freely, so the
106+ # pattern must tolerate markdown decoration. Seen in the wild:
107+ # "[FINAL CHECK]", "**[FINAL CHECK]**", "## Final Check", and labels as
108+ # "Goal:", "**Goal:**" or "**Goal**:" (colon outside the emphasis) —
109+ # the last variant has no literal "Goal:" in it, which is what made the
110+ # old literal pattern miss and leak the block into the panel.
111+ #
112+ # The header must be bracketed or start its own line: that keeps prose
113+ # like "let me do the final check" from truncating a real reply.
114+ _FC_LABEL = r"[*_`]*[ \t]*:" # "Goal:", "**Goal:**", "**Goal**:", "`Goal` :"
115+ _FC_HEADER = (
116+ r"(?:"
117+ r"(?:\*\*|__|#{1,6}[ \t]*)?" # decoration before a bracketed header
118+ r"\[[ \t]*final[ \t_]*check[ \t]*\]" # [FINAL CHECK], bracketed anywhere
119+ r"|(?:^|\n)[ \t]*(?:#{1,6}[ \t]*)?(?:\*\*|__)?[ \t]*"
120+ r"final[ \t_]+check\b" # ## Final Check / **FINAL CHECK**, line-anchored
121+ r")"
122+ )
123+ _FINAL_CHECK_RE = re .compile (
124+ _FC_HEADER + rf".*?Goal{ _FC_LABEL } .*?Status{ _FC_LABEL } .*?Evidence{ _FC_LABEL } " ,
125+ re .DOTALL | re .IGNORECASE ,
126+ )
127+ # a line left holding nothing but markdown decoration once the block is
128+ # cut away (e.g. the "> " or "**" in front of a decorated header)
129+ _FC_DANGLING_RE = re .compile (r"(?:^|\n)[ \t]*[*_#>`\-]+[ \t]*$" )
105130
106131
107132def _is_injected_user_text (text : str ) -> bool :
@@ -123,9 +148,9 @@ def _strip_final_check(text: str) -> str:
123148
124149 The task-completion rules make the model end with a [FINAL CHECK]
125150 block (Goal / Status / Evidence) — verification bookkeeping, not
126- content the user wants to read. The filter is the
127- "[FINAL CHECK].*Goal:.*Status:.*Evidence:" pattern: everything
128- from the header onward is dropped.
151+ content the user wants to read. The filter is ``_FINAL_CHECK_RE``
152+ (header + the three labels, markdown decoration tolerated):
153+ everything from the header onward is dropped.
129154
130155 The block is hidden even when it is the reply's ONLY content —
131156 check-only replies never render. Replies without the header are
@@ -134,7 +159,8 @@ def _strip_final_check(text: str) -> str:
134159 """
135160 m = _FINAL_CHECK_RE .search (text )
136161 if m is not None :
137- return text [: m .start ()].rstrip ()
162+ head = text [: m .start ()].rstrip ()
163+ return _FC_DANGLING_RE .sub ("" , head ).rstrip ()
138164 return text
139165
140166
0 commit comments