@@ -4424,6 +4424,33 @@ def _product_safe(label: str, fn, fallback: Any) -> Any:
44244424 return {** fallback , "live" : False , "error" : str (exc )}
44254425 return fallback
44264426
4427+ def _learning_preview (value : Any , limit : int = 420 ) -> str :
4428+ text = str (value or "" )
4429+ text = re .sub (r"<think>[\s\S]*?</think>" , " " , text , flags = re .IGNORECASE )
4430+ text = re .sub (r"</?think>" , " " , text , flags = re .IGNORECASE )
4431+ lines : List [str ] = []
4432+ for line in text .splitlines ():
4433+ clean = " " .join (line .strip ().split ())
4434+ if not clean :
4435+ continue
4436+ lowered = clean .lower ()
4437+ if lowered .startswith (("model:" , "session:" , "source:" , "messages:" , "representative turns:" )):
4438+ continue
4439+ lines .append (clean )
4440+ preview = " " .join (lines )
4441+ preview = " " .join (preview .split ())
4442+ if len (preview ) <= limit :
4443+ return preview
4444+ clipped = preview [: max (0 , limit - 3 )].rstrip ()
4445+ boundary = max (clipped .rfind ("." ), clipped .rfind (";" ), clipped .rfind ("," ), clipped .rfind (" " ))
4446+ if boundary > limit * 0.65 :
4447+ clipped = clipped [:boundary ].rstrip ()
4448+ return f"{ clipped } ..."
4449+
4450+ def _learning_needs_distillation (value : Any ) -> bool :
4451+ text = str (value or "" ).lower ()
4452+ return "representative turns:" in text or bool (re .search (r"(^|\n)\s*(user|assistant|tool):" , text ))
4453+
44274454 def _compact_learning_row (row : Dict [str , Any ]) -> Dict [str , Any ]:
44284455 evidence = row .get ("evidence" ) or []
44294456 if not isinstance (evidence , list ):
@@ -4440,7 +4467,41 @@ def _compact_learning_row(row: Dict[str, Any]) -> Dict[str, Any]:
44404467 gate = str ((evidence [0 ] or {}).get ("kind" ) or "evidence backed" )
44414468 else :
44424469 gate = "needs approval"
4443- return {** row , "evidence_gate" : gate , "evidence_count" : len (evidence )}
4470+ metadata = row .get ("metadata" ) or {}
4471+ if not isinstance (metadata , dict ):
4472+ metadata = {}
4473+ body = str (row .get ("body" ) or "" )
4474+ needs_distillation = _learning_needs_distillation (body )
4475+ preview = _learning_preview (body )
4476+ if needs_distillation :
4477+ preview = "Raw session import compacted for review. Promote only after distilling a reusable, evidence-backed rule."
4478+ return {
4479+ "id" : row .get ("id" ),
4480+ "kind" : row .get ("kind" ),
4481+ "title" : _learning_preview (row .get ("title" ), limit = 140 ) or str (row .get ("id" ) or "Learning" ),
4482+ "body" : preview ,
4483+ "preview" : preview ,
4484+ "source_agent_id" : row .get ("source_agent_id" ),
4485+ "source_harness" : row .get ("source_harness" ),
4486+ "source_model" : metadata .get ("source_model" ) or metadata .get ("model" ),
4487+ "task_type" : row .get ("task_type" ),
4488+ "repo" : row .get ("repo" ),
4489+ "scope" : row .get ("scope" ),
4490+ "confidence" : row .get ("confidence" ),
4491+ "utility" : row .get ("utility" ),
4492+ "status" : row .get ("status" ),
4493+ "reuse_count" : row .get ("reuse_count" ),
4494+ "success_count" : success_count ,
4495+ "failure_count" : failure_count ,
4496+ "created_at" : row .get ("created_at" ),
4497+ "updated_at" : row .get ("updated_at" ),
4498+ "promoted_at" : row .get ("promoted_at" ),
4499+ "rejected_reason" : row .get ("rejected_reason" ),
4500+ "evidence_gate" : gate ,
4501+ "evidence_count" : len (evidence ),
4502+ "raw_body_chars" : len (body ),
4503+ "needs_distillation" : needs_distillation ,
4504+ }
44444505
44454506 def _learning_snapshot (limit : int = 80 ) -> Dict [str , Any ]:
44464507 from dhee .core .learnings import LearningExchange
@@ -4523,6 +4584,37 @@ def _ui_pack_counts() -> Dict[str, Any]:
45234584 pass
45244585 return counts
45254586
4587+ def _ui_workspace_summary () -> Dict [str , Any ]:
4588+ """Small command-center summary; avoid shipping the full workspace tree."""
4589+ db = _get_db ()
4590+ try :
4591+ workspaces = db .list_workspaces (user_id = _ui_user_id (), limit = 200 ) or []
4592+ except Exception : # noqa: BLE001
4593+ workspaces = []
4594+ project_count = 0
4595+ current_project_id = ""
4596+ for workspace in workspaces [:50 ]:
4597+ workspace_id = str (workspace .get ("id" ) or "" )
4598+ if not workspace_id :
4599+ continue
4600+ try :
4601+ projects = db .list_workspace_projects (
4602+ workspace_id = workspace_id ,
4603+ user_id = _ui_user_id (),
4604+ limit = 200 ,
4605+ ) or []
4606+ except Exception : # noqa: BLE001
4607+ projects = []
4608+ project_count += len (projects )
4609+ if not current_project_id and projects :
4610+ current_project_id = str (projects [0 ].get ("id" ) or "" )
4611+ return {
4612+ "count" : len (workspaces ),
4613+ "project_count" : project_count ,
4614+ "currentWorkspaceId" : str (workspaces [0 ].get ("id" ) or "" ) if workspaces else "" ,
4615+ "currentProjectId" : current_project_id ,
4616+ }
4617+
45264618 def _latest_dheemem_packs (limit : int = 8 ) -> List [Dict [str , Any ]]:
45274619 try :
45284620 from dhee .protocol import inspect_pack
@@ -4585,7 +4677,7 @@ def api_ui_command_center() -> Dict[str, Any]:
45854677 {"linked" : False , "repo_entries" : [], "totals" : {}},
45864678 )
45874679 learnings = _product_safe ("learnings" , lambda : _learning_snapshot (limit = 24 ), {"items" : [], "totals" : {}})
4588- workspaces = _product_safe ("workspaces" , lambda : list_workspaces_api (), {"workspaces " : [] })
4680+ workspaces = _product_safe ("workspaces" , lambda : _ui_workspace_summary (), {"count " : 0 , "project_count" : 0 })
45894681 sessions = router_sessions .get ("items" ) or []
45904682 active_task = next (
45914683 (row for row in (task_data .get ("tasks" ) or []) if str (row .get ("status" ) or "" ) == "active" ),
0 commit comments