merge: merge origin/gamma/refactor and origin/feature/teamhoa/r05-r06 into feature/delta-team/epic-R04

This commit is contained in:
2026-08-27 12:23:43 +09:00
57 changed files with 3003 additions and 512 deletions
+23 -4
View File
@@ -1136,6 +1136,15 @@ class ChatPanel(QWidget):
"snapshot_len": len(snapshot), "out_dir": out_dir,
"home_id": self.session_id, "home_messages": self.messages,
"home_title": self.title, "home_out_root": self.workspace_dir(),
# R06-T04: captured NOW, at submit time — see _persist_session's
# use of this. Without it, a background turn (this session isn't
# the one currently displayed) saves into whatever
# ctx.config.history_dir() resolves to AT THE TIME IT FINISHES,
# which is the *currently viewed* project's history folder if the
# user switched projects (ui/workspace_tab.py::_load_current)
# while this turn was still running — silently saving one
# project's conversation into another project's history folder.
"home_history_dir": self.ctx.config.history_dir(),
"detached": False,
# For re-rendering the in-progress turn if the user reopens this chat:
"display_text": text, "partial": "", "plan_steps": [],
@@ -1366,10 +1375,18 @@ class ChatPanel(QWidget):
return ctx.get("home_id") == self.session_id and not ctx.get("detached")
def _save_snapshot(self, session_id: str, messages: List[Dict[str, Any]],
title: str, inputs: Optional[List[str]] = None) -> None:
title: str, inputs: Optional[List[str]] = None,
history_dir: Optional[Path] = None) -> None:
"""Persist a conversation by id (used both to register it in History the
moment it starts and to save a finished background turn). No-op until it has
a user message. Never raises into the UI."""
a user message. Never raises into the UI.
``history_dir``, when given, is used INSTEAD of
``self.ctx.config.history_dir()`` — see ``_persist_session`` (R06-T04):
a background turn must save into the project it started in, not
whichever project happens to be selected in the Workspace screen by
the time the turn finishes.
"""
if not self.ctx.config.history.get("autosave", True):
return
if not any(m.get("role") == "user" for m in messages):
@@ -1377,7 +1394,8 @@ class ChatPanel(QWidget):
try:
from ..core.history import save_conversation
save_conversation(
self.ctx.config.history_dir(), self.kind, session_id,
history_dir if history_dir is not None else self.ctx.config.history_dir(),
self.kind, session_id,
messages, title, inputs=list(inputs or []), outputs=[],
# Only the CURRENT view knows its project for sure; a background
# turn's save must not overwrite another conversation's project
@@ -1393,7 +1411,8 @@ class ChatPanel(QWidget):
view-based _autosave can't). Outputs are rebuilt from disk on reopen."""
self._save_snapshot(ctx["home_id"], ctx["home_messages"],
ctx.get("home_title", ""),
inputs=ctx.get("record", {}).get("inputs", []))
inputs=ctx.get("record", {}).get("inputs", []),
history_dir=ctx.get("home_history_dir"))
self.history_changed.emit()
def running_session_ids(self):