feat(mma): Implement track-scoped history and optimized sub-agent toolsets

This commit is contained in:
2026-02-27 19:51:13 -05:00
parent 134a11cdc2
commit b845b89543
2 changed files with 58 additions and 5 deletions

View File

@@ -754,6 +754,12 @@ class App:
self.active_track = None
self.active_tickets = []
# Load track-scoped history if track is active
if self.active_track:
track_history = project_manager.load_track_history(self.active_track.id, self.ui_files_base_dir)
if track_history:
self.disc_entries = _parse_history_entries(track_history, self.disc_roles)
def _save_active_project(self):
if self.active_project_path:
try:
@@ -790,6 +796,10 @@ class App:
def _flush_disc_entries_to_project(self):
history_strings = [project_manager.entry_to_str(e) for e in self.disc_entries]
if self.active_track:
project_manager.save_track_history(self.active_track.id, history_strings, self.ui_files_base_dir)
return
disc_sec = self.project.setdefault("discussion", {})
discussions = disc_sec.setdefault("discussions", {})
disc_data = discussions.setdefault(self.active_discussion, project_manager.default_discussion())
@@ -1398,7 +1408,8 @@ class App:
self._save_active_project()
self._flush_to_config()
save_config(self.config)
flat = project_manager.flat_config(self.project, self.active_discussion)
track_id = self.active_track.id if self.active_track else None
flat = project_manager.flat_config(self.project, self.active_discussion, track_id=track_id)
full_md, path, file_items = aggregate.run(flat)
# Build stable markdown (no history) for Gemini caching
screenshot_base_dir = Path(flat.get("screenshots", {}).get("base_dir", "."))

View File

@@ -225,11 +225,17 @@ def migrate_from_legacy_config(cfg: dict) -> dict:
# ── flat config for aggregate.run() ─────────────────────────────────────────
def flat_config(proj: dict, disc_name: str | None = None) -> dict:
def flat_config(proj: dict, disc_name: str | None = None, track_id: str | None = None) -> dict:
"""Return a flat config dict compatible with aggregate.run()."""
disc_sec = proj.get("discussion", {})
name = disc_name or disc_sec.get("active", "main")
disc_data = disc_sec.get("discussions", {}).get(name, {})
if track_id:
history = load_track_history(track_id, proj.get("files", {}).get("base_dir", "."))
else:
name = disc_name or disc_sec.get("active", "main")
disc_data = disc_sec.get("discussions", {}).get(name, {})
history = disc_data.get("history", [])
return {
"project": proj.get("project", {}),
"output": proj.get("output", {}),
@@ -237,7 +243,7 @@ def flat_config(proj: dict, disc_name: str | None = None) -> dict:
"screenshots": proj.get("screenshots", {}),
"discussion": {
"roles": disc_sec.get("roles", []),
"history": disc_data.get("history", []),
"history": history,
},
}
@@ -267,3 +273,39 @@ def load_track_state(track_id: str, base_dir: str | Path = ".") -> 'TrackState':
with open(state_file, "rb") as f:
data = tomllib.load(f)
return TrackState.from_dict(data)
def load_track_history(track_id: str, base_dir: str | Path = ".") -> list:
"""
Loads the discussion history for a specific track from its state.toml.
Returns a list of entry strings formatted with @timestamp.
"""
from models import TrackState
state = load_track_state(track_id, base_dir)
if not state:
return []
history = []
for entry in state.discussion:
e = dict(entry)
ts = e.get("ts")
if isinstance(ts, datetime.datetime):
e["ts"] = ts.strftime(TS_FMT)
history.append(entry_to_str(e))
return history
def save_track_history(track_id: str, history: list, base_dir: str | Path = "."):
"""
Saves the discussion history for a specific track to its state.toml.
'history' is expected to be a list of formatted strings.
"""
from models import TrackState
state = load_track_state(track_id, base_dir)
if not state:
return
roles = ["User", "AI", "Vendor API", "System", "Reasoning"]
entries = [str_to_entry(h, roles) for h in history]
state.discussion = entries
save_track_state(track_id, state, base_dir)