From 25c3eccca82e211adeec5412c069f2e5017f4228 Mon Sep 17 00:00:00 2001 From: banteg <4562643+banteg@users.noreply.github.com> Date: Tue, 30 Dec 2025 13:52:19 +0400 Subject: [PATCH] fix: resume tag in progress messages, fixes sequencing (#3) --- src/takopi/exec_bridge.py | 6 ++---- src/takopi/exec_render.py | 13 +++++++++++-- tests/test_exec_render.py | 4 +++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/takopi/exec_bridge.py b/src/takopi/exec_bridge.py index 209d296..01dfd39 100644 --- a/src/takopi/exec_bridge.py +++ b/src/takopi/exec_bridge.py @@ -557,10 +557,8 @@ async def _handle_message( elapsed = clock() - started_at status = "done" if saw_agent_message else "error" - final_md = ( - progress_renderer.render_final(elapsed, answer, status=status) - + f"\n\nresume: `{session_id}`" - ) + progress_renderer.resume_session = session_id + final_md = progress_renderer.render_final(elapsed, answer, status=status) logger.debug("[final] markdown: %s", final_md) final_rendered, final_entities = render_markdown(final_md) can_edit_final = ( diff --git a/src/takopi/exec_render.py b/src/takopi/exec_render.py index a287dff..6164915 100644 --- a/src/takopi/exec_render.py +++ b/src/takopi/exec_render.py @@ -211,9 +211,11 @@ class ExecProgressRenderer: self.command_width = command_width self.recent_actions: deque[str] = deque(maxlen=max_actions) self.last_item: int | None = None + self.resume_session: str | None = None def note_event(self, event: dict[str, Any]) -> bool: if event["type"] == "thread.started": + self.resume_session = event["thread_id"] return True self.last_item, _, progress_line, progress_prefix = format_event( @@ -240,12 +242,19 @@ class ExecProgressRenderer: def render_progress(self, elapsed_s: float) -> str: header = format_header(elapsed_s, self.last_item, label="working") - return self._assemble(header, list(self.recent_actions)) + message = self._assemble(header, list(self.recent_actions)) + return self._append_resume(message) def render_final(self, elapsed_s: float, answer: str, status: str = "done") -> str: header = format_header(elapsed_s, self.last_item, label=status) answer = (answer or "").strip() - return header + ("\n\n" + answer if answer else "") + message = header + ("\n\n" + answer if answer else "") + return self._append_resume(message) + + def _append_resume(self, message: str) -> str: + if not self.resume_session: + return message + return message + f"\n\nresume: `{self.resume_session}`" @staticmethod def _assemble(header: str, lines: list[str]) -> str: diff --git a/tests/test_exec_render.py b/tests/test_exec_render.py index fa5a2a9..9a82fe2 100644 --- a/tests/test_exec_render.py +++ b/tests/test_exec_render.py @@ -118,12 +118,14 @@ def test_progress_renderer_renders_progress_and_final() -> None: progress = r.render_progress(3.0) assert progress.startswith("working · 3s · step 3") assert "1\\. ✓ `bash -lc ls`" in progress + assert "resume: `0199a213-81c0-7800-8aa1-bbab2a035a53`" in progress final = r.render_final(3.0, "answer", status="done") assert final.startswith("done · 3s · step 3") assert "running:" not in final assert "ran:" not in final - assert final.endswith("answer") + assert "answer" in final + assert final.rstrip().endswith("resume: `0199a213-81c0-7800-8aa1-bbab2a035a53`") def test_progress_renderer_clamps_actions_and_ignores_unknown() -> None: