"""Snapshot Builder — assembles VisualSnapshot and collab_sync data. All data formats match what the frontend expects: - VisualSnapshot for initial game state (types/visual.ts) - collab_sync ack for kanban/chat data (collabSync.ts maps snake_case → camelCase) Also provides reconcile_sessions() which backfills CLI-generated session history into the UI rendering cache (ChatStore), so that conversations started via CLI are visible when the user later opens the UI. """ from __future__ import annotations import inspect import json import re import time import uuid from typing import Any, TYPE_CHECKING from loguru import logger from opc.core.models import normalize_role_runtime_status from opc.core.transcript_visibility import ( FULL_DETAIL_ONLY_TRANSCRIPT_KINDS, TranscriptDetailLevel, normalize_transcript_detail_level, transcript_metadata_visible, ) from opc.layer2_organization.phase import ( DONE_PHASES, IN_PROGRESS_PHASES, IN_REVIEW_PHASES, Phase, TODO_PHASES, effective_owner, is_report_execution_work_item_metadata, is_review_execution_work_item_metadata, kanban_column, should_hide_work_item_from_company_kanban, verdict, ) from opc.layer2_organization.work_item_context_view import WorkItemContextView from opc.layer2_organization.work_item_identity import ( WORK_ITEM_PROJECTION_ID_KEY, WORK_ITEM_TURN_TYPE_KEY, projection_id_for_work_item, turn_type_for_work_item, work_item_identity_payload, work_item_identity_payload_from_metadata, work_item_projection_id_from_metadata, work_item_turn_type_from_metadata, ) from opc.layer2_organization.work_item_links import task_by_linked_work_item_id from opc.plugins.office_ui.execution_identity import ( canonicalize_execution_identity, execution_identity_from_task, normalize_company_profile, normalize_exec_mode, normalize_preferred_agent, ) # The frontend's KanbanColumn definitions use hyphenated ids # ("in-progress", "in-review"); kanban_column() returns underscore form # ("in_progress", "in_review") to match the underlying Phase values. # Convert on the way out. _COLUMN_ID_FOR_FRONTEND = { "todo": "todo", "in_progress": "in-progress", "in_review": "in-review", "done": "done", } def _frontend_column_id(phase: Phase) -> str: return _COLUMN_ID_FOR_FRONTEND[kanban_column(phase)] def _runtime_status_from_member_meta(member_session_meta: dict[str, Any]) -> str: return normalize_role_runtime_status( member_session_meta.get("status") or member_session_meta.get("resident_status"), member_session_meta.get("focused_work_item_id"), ) from opc.presentation.kanban import ( STATUS_TO_COLUMN, build_base_task_payload, build_board_columns, build_company_board_columns, datetime_to_timestamp, priority_to_label, ) from opc.layer3_agent.adapters.codex_adapter import CodexAdapter if TYPE_CHECKING: from opc.engine import OPCEngine from opc.plugins.office_ui.agent_store import AgentStore from opc.plugins.office_ui.chat_store import ChatStore from opc.plugins.office_ui.event_adapter import EventAdapter def _coerce_timestamp(value: Any) -> float | None: try: if value in (None, "", [], {}): return None return float(value) except (TypeError, ValueError): return None def _session_message_ui_identity(message: Any) -> tuple[str, float, dict[str, Any]]: metadata = dict(getattr(message, "metadata", {}) or {}) canonical_id = str(metadata.get("ui_message_id") or getattr(message, "message_id", "") or "").strip() timestamp = _coerce_timestamp(metadata.get("ui_created_at")) if timestamp is None: created_at = getattr(message, "created_at", None) timestamp = created_at.timestamp() if hasattr(created_at, "timestamp") else time.time() ui_meta: dict[str, Any] = {} if canonical_id and canonical_id != str(getattr(message, "message_id", "") or "").strip(): ui_meta["ui_message_id"] = canonical_id if "ui_created_at" in metadata and timestamp is not None: ui_meta["ui_created_at"] = timestamp return canonical_id or str(getattr(message, "message_id", "") or ""), timestamp, ui_meta _FULL_DETAIL_ONLY_TRANSCRIPT_KINDS = FULL_DETAIL_ONLY_TRANSCRIPT_KINDS _TRANSCRIPT_DUPLICATE_KIND_GROUPS: tuple[frozenset[str], ...] = ( frozenset({ "runtime_v2_assistant", "runtime_v2_company_assistant", "top_level_reply", "company_role_result", "company_role_result_retry", "child_task_result", "child_task_result_retry", "child_result", }), ) _RESULT_SURFACE_PRIORITY: dict[str, int] = { "child_task_result": 80, "child_task_result_retry": 79, "company_role_result": 75, "company_role_result_retry": 74, "child_result": 70, "runtime_v2_assistant": 60, "runtime_v2_company_assistant": 20, "top_level_reply": 40, } _MARKDOWN_SECTION_RE = re.compile(r"^##\s+(.+?)\s*$", re.MULTILINE) _VERIFICATION_FOOTER_RE = re.compile(r"^Verification:\s", re.IGNORECASE) def _metadata_is_task_mode_runtime(metadata: dict[str, Any]) -> bool: execution_mode = str(metadata.get("execution_mode", "") or "").strip().lower() if execution_mode == "company_mode": return False if execution_mode in {"task_mode", "task", "project_mode", "project"}: return True projection_id = str(metadata.get("work_item_projection_id", "") or "").strip() if projection_id and projection_id != "task_mode_execution": return False if str(metadata.get("company_profile", "") or "").strip(): return False return ( str(metadata.get("mode", "") or "").strip().lower() == "task" or str(metadata.get("runtime_kind", "") or "").strip() == "task_mode_agent_turn" or str(metadata.get("task_mode_contract", "") or "").strip() == "single_full_capability_main_agent" or projection_id == "task_mode_execution" ) def _normalize_duplicate_content(content: Any) -> str: normalized = CodexAdapter.normalize_transcript_text(str(content or "")) normalized = "\n".join(line.rstrip() for line in normalized.splitlines()).strip() normalized = re.sub(r"\n{3,}", "\n\n", normalized) normalized = _strip_narrative_title_prefix(normalized) paragraphs = [part.strip() for part in re.split(r"\n{2,}", normalized) if part.strip()] if len(paragraphs) > 1 and _VERIFICATION_FOOTER_RE.match(paragraphs[-1]): normalized = "\n\n".join(paragraphs[:-1]).strip() return normalized def _strip_narrative_title_prefix(content: str) -> str: trimmed = str(content or "").strip() markdown_title = re.match(r"^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$", trimmed) if markdown_title: body = markdown_title.group(2).strip() if len(body) >= 80: return body colon_index = trimmed.find(": ") if colon_index < 8 or colon_index > 160: return trimmed prefix = trimmed[:colon_index].replace("*", "").strip() body = trimmed[colon_index + 2 :].strip() if len(body) < 80: return trimmed if not re.search(r"[A-Za-z\u4e00-\u9fff]", prefix): return trimmed if re.match(r"^(https?|file)$", prefix, flags=re.IGNORECASE): return trimmed return body def _normalize_transcript_detail_level(value: Any) -> TranscriptDetailLevel: return normalize_transcript_detail_level(value) def _transcript_message_kind(message: Any) -> str: metadata = dict(getattr(message, "metadata", {}) or {}) return str(metadata.get("kind", "") or "").strip() def _transcript_message_visibility(kind: str) -> TranscriptDetailLevel: return "full" if kind in _FULL_DETAIL_ONLY_TRANSCRIPT_KINDS else "summary" def _transcript_message_hidden_from_ui( message: Any, *, detail_level: TranscriptDetailLevel = "summary", ) -> bool: metadata = dict(getattr(message, "metadata", {}) or {}) return not transcript_metadata_visible(metadata, detail_level=detail_level) def _render_text_parts(parts: list[Any]) -> str: lines: list[str] = [] for part in parts: payload = part.payload if isinstance(part.payload, dict) else {} if part.part_type != "text": continue text = payload.get("text", "") if text: lines.append(CodexAdapter.normalize_transcript_text(str(text))) return "\n\n".join(line for line in lines if line).strip() def _render_thinking_parts(parts: list[Any]) -> str: lines: list[str] = [] for part in parts: payload = part.payload if isinstance(part.payload, dict) else {} if part.part_type != "thinking": continue text = payload.get("text", "") if text: lines.append(CodexAdapter.normalize_transcript_text(str(text))) return "\n\n".join(line for line in lines if line).strip() def _split_markdown_sections(content: str) -> dict[str, str]: normalized = CodexAdapter.normalize_transcript_text(str(content or "")).strip() if not normalized: return {} sections: dict[str, str] = {} matches = list(_MARKDOWN_SECTION_RE.finditer(normalized)) if not matches: sections[""] = normalized return sections for index, match in enumerate(matches): title = match.group(1).strip() body_start = match.end() body_end = matches[index + 1].start() if index + 1 < len(matches) else len(normalized) body = normalized[body_start:body_end].strip() if body: sections[title] = body return sections def _summarize_runtime_user_turn(content: str) -> str: sections = _split_markdown_sections(content) if not sections: return "" responsibility = sections.get("Your Responsibility", "").strip() global_intent = sections.get("Global Intent Summary", "").strip() task_text = sections.get("Task", "").strip() work_item_projection = ( sections.get("Work Item Projection", "").strip() or sections.get("Work Item", "").strip() ) current_role = sections.get("Current Role", "").strip() lines: list[str] = ["### Execution Context"] primary = responsibility or global_intent or task_text if primary: lines.append(primary) if global_intent and global_intent != primary: lines.append(f"Mission: {global_intent}") if work_item_projection: lines.append(f"Work item: {work_item_projection}") if current_role: lines.append(f"Role: {_humanize_identifier(current_role)}") if len(lines) == 1: fallback = next((value.strip() for value in sections.values() if value.strip()), "") if fallback: lines.append(fallback) return "\n\n".join(line for line in lines if line).strip() def _strip_trailing_verification_footer(content: Any) -> tuple[str, str | None]: normalized = CodexAdapter.normalize_transcript_text(str(content or "")).strip() if not normalized: return "", None paragraphs = [part.strip() for part in re.split(r"\n{2,}", normalized) if part.strip()] if not paragraphs: return normalized, None footer = paragraphs[-1] if not _VERIFICATION_FOOTER_RE.match(footer): return normalized, None body = "\n\n".join(paragraphs[:-1]).strip() if not body: return normalized, None return body, footer def _sanitize_ui_message_dict(message: dict[str, Any]) -> dict[str, Any]: sanitized = dict(message) sender = str(sanitized.get("sender", "") or "").strip().lower() metadata = dict(sanitized.get("metadata", {}) or {}) role = str(metadata.get("role", "") or "").strip().lower() if sender == "user" or role == "user": sanitized["metadata"] = metadata return sanitized content, verification_footer = _strip_trailing_verification_footer(sanitized.get("content", "")) sanitized["content"] = content if verification_footer: metadata.setdefault("verification_verdict", verification_footer) sanitized["metadata"] = metadata return sanitized def _humanize_identifier(value: str) -> str: normalized = str(value or "").strip() if not normalized: return "" if "_" not in normalized and "-" not in normalized and normalized.isalpha() and len(normalized) <= 4: return normalized.upper() return normalized.replace("_", " ").replace("-", " ").title() def _work_item_role_payload(task: Any) -> dict[str, Any]: meta = task.metadata if hasattr(task, "metadata") and isinstance(task.metadata, dict) else {} role_id = str(getattr(task, "assigned_to", "") or meta.get("work_item_role_id", "") or "").strip() # work_item_role_name is no longer stamped on task metadata; humanize the # role_id directly for UI display. role_name = _humanize_identifier(role_id) if role_id else "" return { "work_item_role_id": role_id or None, "work_item_role_name": role_name or None, } _WORK_ITEM_PROGRESS_TYPES: frozenset[str] = frozenset({ "work_item_started", "gate_approved", "gate_rejected", "awaiting_manager_review", "awaiting_human", "awaiting_review", "awaiting_peer", "work_item_failed", "deadlock", "gate_result", }) def _execution_turn_alias_payload(runtime_task_id: str | None) -> dict[str, Any]: """Canonical identity for the runtime Task backing an execution turn.""" task_id = str(runtime_task_id or "").strip() return { "runtime_task_id": task_id or None, "execution_turn_id": task_id or None, } def _work_item_entry_from_progress( entry: dict[str, Any], *, runtime_task_id: str, projection_id_hint: str = "", projection_title_hint: str = "", role_name_hint: str = "", ) -> dict[str, Any] | None: entry_type = str(entry.get("type", "") or "").strip() projection_id = str( entry.get("work_item_projection_id") or entry.get("workItemProjectionId") or projection_id_hint or "" ).strip() turn_type = str( entry.get("work_item_turn_type") or entry.get("workItemTurnType") or entry.get("turn_type") or entry.get("turnType") or "" ).strip() if entry_type not in _WORK_ITEM_PROGRESS_TYPES and not projection_id: return None timestamp = _coerce_timestamp(entry.get("timestamp")) if timestamp is None: timestamp = time.time() projection_title = str( entry.get("work_item_projection_title") or entry.get("workItemProjectionTitle") or role_name_hint or projection_title_hint or (projection_id.replace("_", " ").replace("-", " ").title() if projection_id else "") or "" ).strip() role_name = str(entry.get("role_name") or entry.get("roleName") or role_name_hint or "").strip() detail = entry.get("detail") if detail is not None: detail = str(detail).strip() or None return { "timestamp": timestamp, "type": entry_type or "gate_result", **work_item_identity_payload(projection_id=projection_id, turn_type=turn_type), "work_item_projection_title": projection_title or None, "role_name": role_name or None, "detail": detail, **_execution_turn_alias_payload(runtime_task_id), } # ── Per-role work-item rollup for the chat "Execution Progress" panel ── # # UI design contract: 1 row = 1 DelegationWorkItem (NOT 1 Task / runtime turn). # Same work item walking through execute → review → rework stays a single row; # its phase changes drive the row colour. Reviewer sees their review work # items naturally because ``effective_owner(phase, item)`` swaps owner to the # manager during ``in_review`` phases — no need to surface review Tasks as # separate sessions. # # This rollup is consumed by ``WorkItemProgressCard`` / # ``AgentWorkPanel`` / ``ContextPanel`` and intentionally bypasses the # Task-keyed ``formatted_sessions`` source, which was contaminated by # channel-message timestamps and Task-lifecycle filtering. _PHASE_LIVE_RUNTIME_STATES = {"reflecting", "tool_active"} def _phase_value(phase: Any) -> str: return phase.value if hasattr(phase, "value") else str(phase or "") def _coerce_event_timestamp(value: Any) -> float | None: """Normalize a datetime / numeric / None field to ``float | None``. ``datetime_to_timestamp`` from ``opc.presentation.kanban`` returns ``time.time()`` for missing values, which is fine for kanban cards (they always have timestamps) but wrong for the role rollup, where a missing value must NOT silently become "now" — that would re-create the very "1s ago" bug this rewrite is supposed to fix. """ if value is None: return None if hasattr(value, "timestamp"): try: return float(value.timestamp()) except Exception: return None if isinstance(value, (int, float)): return float(value) try: return float(value) except (TypeError, ValueError): return None def _role_aggregated_status( *, runtime_status: str, work_item_phases: list[str], ) -> str: """Reduce one role's set of work-item phases + tracker state to a single aggregated status the UI can colour directly. Rules (matches plan/bug-breezy-dragonfly.md): * runtime tracker is reflecting/tool_active → ``active`` (orange + pulse) * any phase in IN_PROGRESS_PHASES → ``active`` (orange, no pulse) * any phase in IN_REVIEW_PHASES or TODO_PHASES → ``waiting`` (yellow) * all phases in DONE_PHASES, none failed/cancelled → ``done`` (green) * all phases terminal but some failed/cancelled → ``failed`` (red) * otherwise (no work items) → ``pending`` """ if not work_item_phases: return "pending" phase_set = {p for p in work_item_phases if p} todo_values = {p.value for p in TODO_PHASES} in_progress_values = {p.value for p in IN_PROGRESS_PHASES} in_review_values = {p.value for p in IN_REVIEW_PHASES} done_values = {p.value for p in DONE_PHASES} if runtime_status in _PHASE_LIVE_RUNTIME_STATES: return "active" all_terminal = phase_set.issubset(done_values) if all_terminal: if phase_set & {Phase.FAILED.value, Phase.CANCELLED.value}: return "failed" return "done" if phase_set & in_progress_values: return "active" if phase_set & (in_review_values | todo_values): return "waiting" return "pending" def _resolve_role_runtime_status( *, event_adapter: "EventAdapter | None", role_id: str, ) -> str: """Pull `idle | reflecting | tool_active` from the per-role agent tracker. Falls back to ``idle`` when the tracker is missing. """ if not event_adapter or not role_id: return "idle" try: agent_id = event_adapter._resolve_role_to_agent(role_id) except Exception: return "idle" if not agent_id: return "idle" try: tracker = event_adapter.get_tracker(agent_id) except Exception: return "idle" if not tracker or not hasattr(tracker, "state"): return "idle" state = tracker.state state_val = state.value if hasattr(state, "value") else str(state) if state_val in _PHASE_LIVE_RUNTIME_STATES or state_val == "idle": return state_val return "idle" def _format_role_display_name(role_id: str, role_name: str = "") -> str: if role_name and role_name.strip(): return role_name.strip() if not role_id: return "" return role_id.replace("_", " ").replace("-", " ").title() def _filter_progress_log_by_projection( progress_log: list[Any], projection_id: str, ) -> list[dict[str, Any]]: """Return only progress entries belonging to this work-item projection. Entries without a ``work_item_projection_id`` are kept for backwards compatibility (older entries didn't stamp projection id). Entries whose projection id doesn't match are dropped. Output is a fresh list of dicts (defensive copy so downstream mutations don't bleed back). """ out: list[dict[str, Any]] = [] for entry in progress_log or []: if not isinstance(entry, dict): continue entry_projection = str(entry.get("work_item_projection_id") or "").strip() if entry_projection and projection_id and entry_projection != projection_id: continue out.append(dict(entry)) out.sort(key=lambda e: float(e.get("timestamp") or 0.0)) return out _TERMINAL_PHASE_VALUES: frozenset[str] = frozenset({ "approved", "failed", "cancelled", }) # Tail-end gate / digest events (e.g. ``[Company:projection] gate approved``) # can be persisted seconds after the work item's ``updated_at`` was last # stamped, so a strict equality cap drops the closing entry. Sixty seconds # is well under the smallest expected gap between consecutive shared work # items on the same origin task and comfortably covers the observed lag. _TIME_WINDOW_UPPER_GRACE_SEC = 60.0 def _work_item_time_window(item: Any) -> tuple[float | None, float | None]: """Return ``(lower, upper)`` epoch-second bounds for the work item's runtime activity. Used to scope progress entries when they have to be fetched from a shared origin task that also carries other work items' entries. ``upper`` is ``None`` for non-terminal phases — the work item is still emitting events, so no cap is meaningful yet. """ lower = _coerce_event_timestamp(getattr(item, "created_at", None)) phase_value = _phase_value(getattr(item, "phase", None)) upper: float | None = None if phase_value in _TERMINAL_PHASE_VALUES: updated = _coerce_event_timestamp(getattr(item, "updated_at", None)) if updated is not None: upper = updated + _TIME_WINDOW_UPPER_GRACE_SEC return lower, upper def _progress_entries_for_work_item( item: Any, linked_task: Any | None, *, progress_by_task: dict[str, list[dict[str, Any]]] | None, ) -> list[dict[str, Any]]: """Return detailed runtime activity for one DelegationWorkItem. ``task_progress`` is the authoritative source for thinking/tool/gate records. Two source paths exist: 1. The work item's own runtime task — the default and uncontested case. 2. The runtime task's ``origin_task_id`` when ``shared_role_session=True`` — leader-side turns (intake, review) ride on the user-facing primary chat and ``ws_handler`` redirects ``on_progress`` writes to the origin task. Without this fallback the Execution Progress panel renders "No runtime activity yet" for the leader's intake even though the LLM produced a full timeline. Entries pulled from path 2 are time-windowed against the work item's ``created_at`` / ``updated_at`` so unstamped runtime entries from a sibling shared-role-session work item that also rides on the same origin task can't bleed into this row. The metadata ``progress_log`` fallback exists for older snapshots and for tests that only construct in-memory work items. """ projection_id = projection_id_for_work_item(item) or "" raw_entries: list[Any] = [] seen_keys: set[tuple[Any, ...]] = set() def _push(entry: Any, *, window: tuple[float | None, float | None] | None = None) -> None: if not isinstance(entry, dict): return if window is not None: ts = _coerce_event_timestamp(entry.get("timestamp")) lower, upper = window if ts is not None: if lower is not None and ts < lower: return if upper is not None and ts > upper: return key = ( entry.get("timestamp"), entry.get("type"), entry.get("summary"), entry.get("detail"), ) if key in seen_keys: return seen_keys.add(key) raw_entries.append(entry) if progress_by_task and linked_task is not None: runtime_task_id = str(getattr(linked_task, "id", "") or "").strip() if runtime_task_id: for entry in progress_by_task.get(runtime_task_id, []) or []: _push(entry) metadata = dict(getattr(linked_task, "metadata", {}) or {}) if bool(metadata.get("shared_role_session", False)): origin_task_id = str(metadata.get("origin_task_id", "") or "").strip() if origin_task_id and origin_task_id != runtime_task_id: window = _work_item_time_window(item) for entry in progress_by_task.get(origin_task_id, []) or []: _push(entry, window=window) if not raw_entries: view = WorkItemContextView(item, linked_task) raw_entries = view.get_list("progress_log") return _filter_progress_log_by_projection(raw_entries, projection_id) def _activity_section_for_work_item( item: Any, linked_task: Any | None, *, progress_by_task: dict[str, list[dict[str, Any]]] | None, kind: str, title: str, role_name: str = "", ) -> dict[str, Any] | None: entries = _progress_entries_for_work_item( item, linked_task, progress_by_task=progress_by_task, ) runtime_task_id = ( str(getattr(linked_task, "id", "") or "").strip() if linked_task is not None else "" ) if not entries and not runtime_task_id: return None role_id = str(getattr(item, "role_id", "") or "").strip() view = WorkItemContextView(item, linked_task) display_role_name = ( role_name or str(view.get("work_item_role_name", "") or "").strip() or _format_role_display_name(role_id) ) return { "kind": kind, "title": title or str(getattr(item, "title", "") or "").strip() or kind.title(), "role_name": display_role_name or None, "runtime_task_id": runtime_task_id or None, "entries": entries, "_sort_at": _coerce_event_timestamp(getattr(item, "created_at", None)) or _coerce_event_timestamp(getattr(item, "updated_at", None)) or (float(entries[0].get("timestamp") or 0.0) if entries else 0.0), } def _is_final_delivery_rollup_item(item: Any, meta: dict[str, Any], kind: str) -> bool: turn_type = turn_type_for_work_item(item, fallback=str(meta.get("work_item_turn_type", "") or "")) if kind != "delivery" and turn_type not in {"deliver", "delivery"}: return False return ( str(meta.get("feedback_scope", "") or "").strip() == "final" or bool(meta.get("authoritative_output", False)) or bool(meta.get("requires_user_feedback", False)) or bool(meta.get("user_visible", False)) ) def _public_activity_section(section: dict[str, Any]) -> dict[str, Any]: return { key: value for key, value in section.items() if not key.startswith("_") } def _build_role_work_items_for_session( *, work_items: list[Any], task_by_work_item_id: dict[str, Any], event_adapter: "EventAdapter | None", progress_by_task: dict[str, list[dict[str, Any]]] | None = None, group_by_executor: bool = False, ) -> dict[str, dict[str, Any]]: """Group DelegationWorkItems and emit per-role summaries. The output schema is documented in ``plan/bug-breezy-dragonfly.md`` and consumed by ``frontend_src/lib/collabSync.ts:mapBackendRoleWorkItems``. Hidden report/review execution work items are not rows. Their runtime activity is attached to the target visible work item so the UI keeps "1 row = 1 business work item" while preserving the audit trail. """ result: dict[str, dict[str, Any]] = {} auxiliary_sections_by_target: dict[str, list[dict[str, Any]]] = {} for item in work_items or []: meta = dict(getattr(item, "metadata", {}) or {}) if is_review_execution_work_item_metadata(meta): target_id = str(meta.get("review_target_work_item_id", "") or "").strip() section_kind = "review" title = str(getattr(item, "title", "") or "").strip() or "Review activity" elif is_report_execution_work_item_metadata(meta): target_id = str(meta.get("report_target_work_item_id", "") or "").strip() section_kind = "report" title = str(getattr(item, "title", "") or "").strip() or "Report activity" else: continue if not target_id: continue wi_id = str(getattr(item, "work_item_id", "") or "").strip() linked_task = task_by_work_item_id.get(wi_id) section = _activity_section_for_work_item( item, linked_task, progress_by_task=progress_by_task, kind=section_kind, title=title, ) if section is None: continue auxiliary_sections_by_target.setdefault(target_id, []).append(section) for item in work_items or []: meta = dict(getattr(item, "metadata", {}) or {}) if is_review_execution_work_item_metadata(meta): continue if is_report_execution_work_item_metadata(meta): continue if bool(meta.get("attention_work_item", False)): continue kind = str(getattr(item, "kind", "") or "").strip().lower() is_final_delivery = _is_final_delivery_rollup_item(item, meta, kind) phase = item.phase if hasattr(item, "phase") else None if phase is None: continue if group_by_executor and kind not in {"execute", "intake", "delivery"} and not is_final_delivery: continue if should_hide_work_item_from_company_kanban(meta): include_deleted_business_item = ( group_by_executor and kind in {"execute", "intake"} and ( bool(meta.get("deleted_by_manager_tool", False)) or phase == Phase.CANCELLED ) ) if not include_deleted_business_item: continue # Top-level orchestration cards (no parent_work_item_id) are normally # filtered to keep parity with the company kanban's `visible_work_items` # filter. Exception: the leader's own `intake` card — it is the only # work item the leader has during planning/dispatch, and hiding it # leaves the leader invisible on the Execution Progress panel until # children produce review activity. Show it here so the leader's # runtime turns are visible from t=0; the kanban side stays unchanged. if not str(getattr(item, "parent_work_item_id", "") or "").strip(): if kind != "intake" and not is_final_delivery: continue executor_role_id = str(getattr(item, "role_id", "") or "").strip() reviewer_role_id = str(getattr(item, "manager_role_id", "") or "").strip() try: if group_by_executor: role_id = executor_role_id else: role_id, _seat_id = effective_owner(phase, item) except Exception: role_id = executor_role_id role_id = (role_id or "").strip() if not role_id: continue wi_id = str(getattr(item, "work_item_id", "") or "").strip() linked_task = task_by_work_item_id.get(wi_id) view = WorkItemContextView(item, linked_task) projection_id = projection_id_for_work_item(item) or "" progress_log = _progress_entries_for_work_item( item, linked_task, progress_by_task=progress_by_task, ) is_review_target = phase in IN_REVIEW_PHASES execution_turn_id = ( str(getattr(linked_task, "id", "") or "").strip() if linked_task is not None else "" ) wf_role_name = str(view.get("work_item_role_name", "") or "").strip() row_role_name = _format_role_display_name(executor_role_id, wf_role_name) activity_sections: list[dict[str, Any]] = [] main_section = _activity_section_for_work_item( item, linked_task, progress_by_task=progress_by_task, kind=str(getattr(item, "kind", "") or "").strip() or "execute", title=str(getattr(item, "title", "") or "").strip() or "Work item activity", role_name=row_role_name, ) if main_section is not None: activity_sections.append(main_section) activity_sections.extend( sorted( auxiliary_sections_by_target.get(wi_id, []), key=lambda section: ( float(section.get("_sort_at") or 0.0), str(section.get("runtime_task_id") or ""), str(section.get("kind") or ""), ), ) ) row = { "work_item_id": wi_id, "work_item_projection_id": projection_id or None, "phase": _phase_value(phase), "kanban_column": _COLUMN_ID_FOR_FRONTEND[kanban_column(phase)], "title": str(getattr(item, "title", "") or "").strip(), "kind": str(getattr(item, "kind", "") or "").strip() or None, "is_review_target": is_review_target, "executor_role_id": executor_role_id or None, "executor_role_name": row_role_name or None, "reviewer_role_id": reviewer_role_id or None, "created_at": _coerce_event_timestamp(getattr(item, "created_at", None)), "updated_at": _coerce_event_timestamp(getattr(item, "updated_at", None)), "execution_turn_id": execution_turn_id or None, "progress_log": progress_log, "activity_sections": [ _public_activity_section(section) for section in activity_sections ], } # Group by effective owner role. ``role_id`` is stable per logical # role within a run; fancier composite keys are unnecessary here # because each session's work items belong to a single delegation # run. summary = result.setdefault(role_id, { "role_key": role_id, "role_id": role_id, "role_name": _format_role_display_name(role_id), "team_instance_id": None, "runtime_status": "idle", "aggregated_status": "pending", "work_items": [], }) summary["work_items"].append(row) # Promote a richer role_name as soon as we see one. Current-owner rows # in review carry the executor's role_name, so leave reviewer groups # named by reviewer role. Executor rollups intentionally keep that # executor name even while the row is waiting on review. if (group_by_executor or not is_review_target) and row_role_name and summary["role_name"] == _format_role_display_name(role_id): summary["role_name"] = row_role_name for summary in result.values(): summary["work_items"].sort( key=lambda r: ( float(r.get("created_at") or 0.0), str(r.get("work_item_id") or ""), ) ) for summary in result.values(): summary["runtime_status"] = _resolve_role_runtime_status( event_adapter=event_adapter, role_id=summary["role_id"], ) summary["aggregated_status"] = _role_aggregated_status( runtime_status=summary["runtime_status"], work_item_phases=[wi.get("phase") for wi in summary["work_items"]], ) return result def _build_executor_role_work_items_for_session( *, work_items: list[Any], task_by_work_item_id: dict[str, Any], event_adapter: "EventAdapter | None", progress_by_task: dict[str, list[dict[str, Any]]] | None = None, ) -> dict[str, dict[str, Any]]: """Display-only rollup for Execution Progress. Unlike ``_build_role_work_items_for_session`` this groups business rows by the original executor role even while they are awaiting manager review. Runtime ownership, review routing, and kanban board semantics continue to use the current-owner rollup. """ return _build_role_work_items_for_session( work_items=work_items, task_by_work_item_id=task_by_work_item_id, event_adapter=event_adapter, progress_by_task=progress_by_task, group_by_executor=True, ) def _build_session_work_item_log( task: Any, *, task_meta: dict[str, Any], child_tasks: list[Any], task_meta_map: dict[str, dict[str, Any]], progress_by_task: dict[str, list[dict[str, Any]]], ) -> list[dict[str, Any]]: work_item_entries: list[dict[str, Any]] = [] visited_task_ids: set[str] = set() def collect_entries(source_task: Any, source_meta: dict[str, Any]) -> None: task_id = str(getattr(source_task, "id", "") or "").strip() if not task_id or task_id in visited_task_ids: return visited_task_ids.add(task_id) work_item_role = _work_item_role_payload(source_task) projection_id_hint = work_item_projection_id_from_metadata(source_meta) role_name_hint = str(work_item_role.get("work_item_role_name") or "").strip() projection_title_hint = role_name_hint or ( projection_id_hint.replace("_", " ").replace("-", " ").title() if projection_id_hint else "" ) for progress_entry in progress_by_task.get(task_id, []): if not isinstance(progress_entry, dict): continue work_item_entry = _work_item_entry_from_progress( progress_entry, runtime_task_id=task_id, projection_id_hint=projection_id_hint, projection_title_hint=projection_title_hint, role_name_hint=role_name_hint, ) if work_item_entry is not None: work_item_entries.append(work_item_entry) if work_item_projection_id_from_metadata(task_meta): collect_entries(task, task_meta) for child in child_tasks: collect_entries(child, task_meta_map.get(getattr(child, "id", ""), {})) work_item_entries.sort( key=lambda item: ( float(item.get("timestamp") or 0.0), str(item.get("execution_turn_id") or item.get("runtime_task_id") or ""), str(item.get("type") or ""), ) ) deduped: list[dict[str, Any]] = [] seen: set[tuple[Any, ...]] = set() for item in work_item_entries: key = ( item.get("timestamp"), item.get("type"), item.get("work_item_projection_id"), item.get("execution_turn_id") or item.get("runtime_task_id"), item.get("detail"), ) if key in seen: continue seen.add(key) deduped.append(item) return deduped def _transcript_item_to_ui_message( item: dict[str, Any], *, channel_id: str, task_id: str, detail_level: TranscriptDetailLevel = "summary", ) -> dict[str, Any] | None: message = item.get("message") if ( not message or getattr(message, "summary_flag", False) or _transcript_message_hidden_from_ui(message, detail_level=detail_level) ): return None kind = _transcript_message_kind(message) if kind in _FULL_DETAIL_ONLY_TRANSCRIPT_KINDS: content = _render_text_parts(item.get("parts", [])) else: content = _render_parts(item.get("parts", [])) content = content.strip() if kind == "runtime_v2_user_turn": content = _summarize_runtime_user_turn(content) if not content: return None role = str(getattr(message, "role", "") or "").strip().lower() agent_id = str(getattr(message, "agent_id", "") or "").strip() if kind == "runtime_v2_user_turn": sender = "system" sender_name = "Execution" elif agent_id and role in ("assistant", "subagent"): sender = agent_id sender_name = str(dict(getattr(message, "metadata", {}) or {}).get("visible_speaker", "") or "").strip() if not sender_name: sender_name = agent_id.replace("-", " ").replace("_", " ").title() if ( sender_name.strip().lower().replace(" ", "_") == "task_generalist" and kind in { "runtime_v2_assistant", "runtime_v2_company_assistant", "runtime_v2_intermediate_assistant", "top_level_reply", } ): sender_name = "OPC" else: sender, sender_name = _ROLE_MAP.get(role, ("system", "OPC")) if role != "user": content, verification_footer = _strip_trailing_verification_footer(content) if not content: return None else: verification_footer = None message_id, timestamp, ui_meta = _session_message_ui_identity(message) message_metadata = dict(getattr(message, "metadata", {}) or {}) runtime_thinking = ( _render_thinking_parts(item.get("parts", []) if isinstance(item, dict) else []) or str(message_metadata.get("runtime_thinking", "") or "").strip() ) return { "message_id": message_id or str(getattr(message, "message_id", "") or str(uuid.uuid4())), "channel_id": channel_id, "sender": sender, "sender_name": sender_name, "content": content, "created_at": timestamp, "reply_to_id": None, "mentions": [], "metadata": { "source": "engine", "role": role, "task_id": task_id, "transcript_kind": kind, "detail_visibility": ( "summary" if message_metadata.get("company_final_turn") else _transcript_message_visibility(kind) ), **({"type": "system"} if kind == "runtime_v2_user_turn" else {}), **({"verification_verdict": verification_footer} if verification_footer else {}), **({"runtime_thinking": runtime_thinking} if runtime_thinking else {}), **({ key: message_metadata.get(key) for key in ("canonical_turn_id", "turn_id") if message_metadata.get(key) }), **ui_meta, }, } def _messages_should_collapse_duplicate(left: dict[str, Any], right: dict[str, Any]) -> bool: left_content = _normalize_duplicate_content(left.get("content", "")) right_content = _normalize_duplicate_content(right.get("content", "")) if not left_content or not right_content or left_content != right_content: return False left_meta = dict(left.get("metadata", {}) or {}) right_meta = dict(right.get("metadata", {}) or {}) left_kind = str(left_meta.get("transcript_kind", "") or "").strip() right_kind = str(right_meta.get("transcript_kind", "") or "").strip() if not left_kind or not right_kind or left_kind == right_kind: return False return any({left_kind, right_kind} <= group for group in _TRANSCRIPT_DUPLICATE_KIND_GROUPS) def _prefer_duplicate_message(left: dict[str, Any], right: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]: left_kind = str(dict(left.get("metadata", {}) or {}).get("transcript_kind", "") or "").strip() right_kind = str(dict(right.get("metadata", {}) or {}).get("transcript_kind", "") or "").strip() if _RESULT_SURFACE_PRIORITY.get(left_kind, 0) >= _RESULT_SURFACE_PRIORITY.get(right_kind, 0): return left, right return right, left def collapse_adjacent_transcript_duplicates(messages: list[dict[str, Any]]) -> list[dict[str, Any]]: """Collapse duplicate rendered result surfaces in chronological order. This is public within the Office UI package because transcript pagination formats raw database chunks incrementally. Reusing the renderer's exact collapse rule at chunk boundaries keeps pagination and full snapshots identical. """ collapsed: list[dict[str, Any]] = [] for message in messages: if not collapsed: collapsed.append(message) continue previous = collapsed[-1] if not _messages_should_collapse_duplicate(previous, message): collapsed.append(message) continue preferred, secondary = _prefer_duplicate_message(previous, message) merged_metadata = { **dict(secondary.get("metadata", {}) or {}), **dict(preferred.get("metadata", {}) or {}), } merged_visibility = "summary" if ( str(dict(previous.get("metadata", {}) or {}).get("detail_visibility", "") or "").strip() == "full" and str(dict(message.get("metadata", {}) or {}).get("detail_visibility", "") or "").strip() == "full" ): merged_visibility = "full" merged_metadata["detail_visibility"] = merged_visibility preferred_content = str(preferred.get("content", "") or "") secondary_content = str(secondary.get("content", "") or "") normalized_content = _normalize_duplicate_content(preferred_content) merged_content = ( normalized_content if normalized_content and normalized_content == _normalize_duplicate_content(secondary_content) else preferred_content ) collapsed[-1] = { **secondary, **preferred, "content": merged_content, "metadata": merged_metadata, } return collapsed def build_transcript_ui_messages( transcript: list[dict[str, Any]], *, channel_id: str, task_id: str, detail_level: TranscriptDetailLevel = "summary", ) -> list[dict[str, Any]]: formatted_messages: list[dict[str, Any]] = [] for item in transcript: formatted = _transcript_item_to_ui_message( item, channel_id=channel_id, task_id=task_id, detail_level="full", ) if not formatted: continue formatted_messages.append({ "message_id": formatted["message_id"], "sender": formatted["sender"], "sender_name": formatted["sender_name"], "content": formatted["content"], "timestamp": float(formatted.get("created_at") or time.time()), "reply_to_id": formatted.get("reply_to_id"), "mentions": list(formatted.get("mentions", [])), "metadata": dict(formatted.get("metadata", {}) or {}), }) collapsed_messages = collapse_adjacent_transcript_duplicates(formatted_messages) normalized_detail_level = _normalize_transcript_detail_level(detail_level) if normalized_detail_level == "full": return collapsed_messages return [ message for message in collapsed_messages if str(dict(message.get("metadata", {}) or {}).get("detail_visibility", "summary")).strip() != "full" ] def task_to_kanban( task: Any, display_num: int, event_adapter: EventAdapter | None = None, ) -> dict[str, Any]: """Convert OPC Task to frontend KanbanTask (snake_case for collabSync). Frontend's collabSync.ts mapBackendTask() will convert to camelCase. """ meta = task.metadata if hasattr(task, "metadata") and isinstance(task.metadata, dict) else {} runtime_meta = dict(meta.get("runtime_v2", {}) or {}) member_session_meta = dict(meta.get("member_session_state", {}) or {}) assignee_ids = ( [event_adapter._resolve_role_to_agent(task.assigned_to)] if event_adapter and task.assigned_to else ([task.assigned_to] if task.assigned_to else []) ) work_item_role = _work_item_role_payload(task) result = build_base_task_payload( task, display_num, assignee_ids=assignee_ids, extra={ **work_item_identity_payload_from_metadata(meta), "company_profile": meta.get("company_profile"), **work_item_role, "work_item_gate": meta.get("work_item_gate"), "employee_assignment": meta.get("employee_assignment"), "selected_execution_agent": _resolve_task_selected_execution_agent(task), "origin_channel": meta.get("origin_channel"), "progress_log": list(meta.get("progress_log", []))[-10:], "handoff_context": _extract_work_item_summary_for_downstream(meta), "runtime_session_id": runtime_meta.get("runtime_session_id"), "resume_cursor": runtime_meta.get("resume_cursor"), "worktree_path": runtime_meta.get("worktree_path"), "runtime_status": runtime_meta.get("status"), "verification": runtime_meta.get("verification"), "prompt_prefix_fingerprint": runtime_meta.get("prompt_prefix_fingerprint"), "resident_status": _runtime_status_from_member_meta(member_session_meta), "actionable_inbox_count": member_session_meta.get("actionable_inbox_count"), "protocol_backlog_count": member_session_meta.get("protocol_backlog_count"), "notification_backlog_count": member_session_meta.get("notification_backlog_count"), "latest_notification": member_session_meta.get("latest_notification"), # Fix 5 PR7: queue depth surfaces "CTO has 2 tasks queued" # behind the focused one when the serial-queue flag is on. "pending_queue_depth": int( member_session_meta.get("pending_queue_depth", 0) or 0 ), "pending_work_item_ids": list( member_session_meta.get("pending_work_item_ids", []) or [] ), }, ) # Agent runtime state from EventAdapter trackers (via public API) # task.assigned_to stores opc_role_id; get_tracker expects UI agent_id if event_adapter and task.assigned_to: agent_id = event_adapter._resolve_role_to_agent(task.assigned_to) tracker = event_adapter.get_tracker(agent_id) if tracker: result["agent_status"] = tracker.state.value result["current_tool"] = tracker.current_tool task_id = str(getattr(task, "id", "") or "").strip() result.update(_execution_turn_alias_payload(task_id)) return result def work_item_to_kanban( item: Any, display_num: int, *, board_id: str, event_adapter: EventAdapter | None = None, seat_by_id: dict[str, Any] | None = None, employee_name_by_id: dict[str, str] | None = None, task_by_work_item_id: dict[str, Any] | None = None, suspended_phase_by_work_item_id: dict[str, str] | None = None, ) -> dict[str, Any]: seat_by_id = seat_by_id or {} employee_name_by_id = employee_name_by_id or {} task_by_work_item_id = task_by_work_item_id or {} metadata = dict(getattr(item, "metadata", {}) or {}) phase: Phase = item.phase work_item_id = str(getattr(item, "work_item_id", "") or "").strip() suspended_phase_value = str((suspended_phase_by_work_item_id or {}).get(work_item_id, "") or "").strip() if not suspended_phase_value and str(metadata.get("dispatch_hold", "") or "").strip() == "company_runtime_suspended": suspended_phase_value = str(metadata.get("suspended_phase", "") or "").strip() if suspended_phase_value: try: phase = Phase(suspended_phase_value) except ValueError: pass column = _frontend_column_id(phase) worker_seat_id = str(getattr(item, "seat_id", "") or metadata.get("seat_id", "") or "").strip() worker_role_id = str(getattr(item, "role_id", "") or "").strip() # Kanban-push: when a card is in `in_review`, its effective owner swaps # to the manager so the UI naturally places it in the reviewer's # swimlane/avatar row. The immutable `work_item_role_id` / `role_id` # values stay as the executor identity for audit purposes. effective_role_id, effective_seat_id = effective_owner(phase, item) seat_id = effective_seat_id or worker_seat_id seat = seat_by_id.get(seat_id) employee_id = str(getattr(seat, "employee_id", "") or "").strip() if seat is not None else "" employee_name = employee_name_by_id.get(employee_id, "") linked_task = task_by_work_item_id.get(str(getattr(item, "work_item_id", "") or "").strip()) linked_meta = _task_metadata(linked_task) if linked_task is not None else {} # Work-item context sync: WorkItemContextView prefers work_item.metadata, falls # back to linked_task.metadata. The four UI-critical fields # (work_item_role_name / employee_prompt_context / employee_delta_context # / progress_log) are mirrored onto work_item by Step 9, so reads below # transparently see them from the work_item side once mirrored. view = WorkItemContextView(item, linked_task) role_id = effective_role_id or worker_role_id role_name = role_id.replace("_", " ").replace("-", " ").title() if role_id else "" wf_role_name = str(view.get("work_item_role_name", "") or "").strip() if wf_role_name: role_name = wf_role_name # employee_assignment is WorkItem-owned, with a permitted runtime Task # execution-copy fallback during migration. employee_assignment = view.get_dict("employee_assignment") if employee_id and not str(employee_assignment.get("employee_id", "") or "").strip(): employee_assignment["employee_id"] = employee_id if employee_name and not str(employee_assignment.get("name", "") or "").strip(): employee_assignment["name"] = employee_name prompt_ctx = str(view.get("employee_prompt_context", "") or "").strip() if prompt_ctx: employee_assignment.setdefault("prompt_context", prompt_ctx) delta_ctx = str(view.get("employee_delta_context", "") or "").strip() if delta_ctx: employee_assignment.setdefault("delta_context", delta_ctx) assignee_ids = ( [event_adapter._resolve_role_to_agent(role_id)] if event_adapter and role_id else ([role_id] if role_id else []) ) blocked_reason = str(getattr(item, "blocked_reason", "") or "").strip() rework_feedback = str(metadata.get("rework_feedback", "") or "").strip() work_item_projection_id = projection_id_for_work_item(item) work_item_turn_type = turn_type_for_work_item(item) # Card identity is the DelegationWorkItem.work_item_id. `session_id`, # `chat_channel_id`, and `runtime_task_id` are audit/chat-routing references # to the runtime Task, not the card's identity. linked_task_id = str(getattr(linked_task, "id", "") or "").strip() if linked_task is not None else "" payload = { "task_id": work_item_id, "work_item_id": work_item_id, "display_id": f"OPC-{display_num}", "board_id": board_id, "column_id": column, "title": str(getattr(item, "title", "") or "").strip(), "description": str(getattr(item, "summary", "") or "").strip(), # `phase` is the single source of truth (14-state machine value); # `kanban_column` is the pure-function projection consumed by the UI # to place the card in one of the four columns. "phase": phase.value, "kanban_column": column, "priority": None, "assignee_ids": assignee_ids, "tags": [str(getattr(item, "kind", "") or "").strip()] if str(getattr(item, "kind", "") or "").strip() else [], "sort_order": display_num, "session_id": getattr(linked_task, "session_id", None) if linked_task is not None else None, "chat_channel_id": f"session:{getattr(linked_task, 'id', '')}" if linked_task is not None else "", "created_at": datetime_to_timestamp(getattr(item, "created_at", None)), "updated_at": datetime_to_timestamp(getattr(item, "updated_at", None)), "dependencies": [ str(dep).strip() for dep in list(metadata.get("dependency_work_item_ids", []) or []) if str(dep).strip() ], **work_item_identity_payload(projection_id=work_item_projection_id, turn_type=work_item_turn_type), "company_profile": str(linked_meta.get("company_profile", "") or "").strip() if linked_task is not None else None, # work_item_role_id keeps the executor identity for audit/UI labels; # assignee_ids above already reflects the effective owner for lane # placement. "work_item_role_id": worker_role_id or None, "work_item_role_name": ( (worker_role_id.replace("_", " ").replace("-", " ").title() if worker_role_id else "") or role_name or None ), "employee_assignment": employee_assignment or None, # Work-item context sync: progress_log via view (work_item side preferred; # falls back to linked_task.metadata). view.get_list returns a copy # so the subsequent [-10:] slice is safe. "progress_log": view.get_list("progress_log")[-10:], "runtime_session_id": str(metadata.get("assigned_role_runtime_id", "") or getattr(item, "role_runtime_session_id", "") or "").strip() or None, "blocked_reason": blocked_reason or None, "review_verdict": verdict(phase), "review_summary": ( str((metadata.get("structured_review_verdict") or {}).get("summary", "") or "").strip() or None if isinstance(metadata.get("structured_review_verdict"), dict) else None ), "review_owner_role_id": str(metadata.get("review_owner_role_id", "") or "").strip() or None, "review_owner_seat_id": str(metadata.get("review_owner_seat_id", "") or "").strip() or None, "scope_key": str(metadata.get("scope_key", "") or "").strip() or None, "completion_report": metadata.get("completion_report"), "rework_feedback": rework_feedback or None, "manager_role_id": str(getattr(item, "manager_role_id", "") or "").strip() or None, "manager_seat_id": str(getattr(item, "manager_seat_id", "") or "").strip() or None, **_execution_turn_alias_payload(linked_task_id), "handoff_context": _extract_markdown_text(view.get("handoff_context"), max_chars=500), "original_message": str(linked_meta.get("original_message", "") or "").strip() or None, "planning_context": str(metadata.get("planning_context", "") or "").strip() or None, "deliverables": [ str(value).strip() for value in list(metadata.get("deliverables", []) or []) if str(value).strip() ], "acceptance_criteria": [ str(value).strip() for value in list(metadata.get("acceptance_criteria", []) or []) if str(value).strip() ], "delegation_rationale": str(metadata.get("delegation_rationale", "") or "").strip() or None, "non_overlap_guard": str(metadata.get("non_overlap_guard", "") or "").strip() or None, "coordination_notes": str(metadata.get("coordination_notes", "") or "").strip() or None, "resident_assignment": dict(linked_meta.get("resident_assignment", {}) or {}) or None, "member_session_state": dict(linked_meta.get("member_session_state", {}) or {}) or None, "ownership_contract": dict(linked_meta.get("ownership_contract", {}) or {}) or None, } return payload def _build_board_payload( board_id: str, name: str, *, description: str | None = None, now: float | None = None, ) -> dict[str, Any]: timestamp = float(now) if now is not None else time.time() return { "board_id": board_id, "name": name, "description": description or "", "prefix": "OPC", "color": "#4f46e5", "next_task_num": 1, "created_at": timestamp, "updated_at": timestamp, } def _board_label(value: str, *, fallback: str) -> str: label = str(value or "").strip() if not label: return fallback compact = " ".join(label.split()) return compact if len(compact) <= 64 else compact[:61].rstrip() + "..." def _primary_session_tasks_by_session_id( tasks: list[Any], *, task_meta_map: dict[str, dict[str, Any]] | None = None, ) -> tuple[dict[str, Any], list[str]]: primary_tasks_by_session_id: dict[str, Any] = {} ordered_session_ids: list[str] = [] for task in tasks: task_id = str(getattr(task, "id", "") or "").strip() task_meta = ( task_meta_map.get(task_id, {}) if task_meta_map is not None and task_id else _task_metadata(task) ) if bool(task_meta.get("review_task", False)): continue session_id = str(getattr(task, "session_id", "") or "").strip() if not session_id or _task_parent_session_link(task, task_meta): continue current = primary_tasks_by_session_id.get(session_id) if current is None: primary_tasks_by_session_id[session_id] = task ordered_session_ids.append(session_id) continue current_meta = ( task_meta_map.get(str(getattr(current, "id", "") or "").strip(), {}) if task_meta_map is not None else _task_metadata(current) ) if _session_container_rank(task, task_meta) > _session_container_rank(current, current_meta): primary_tasks_by_session_id[session_id] = task return primary_tasks_by_session_id, ordered_session_ids def _shared_role_identity_tasks_by_session_id( tasks: list[Any], *, task_meta_map: dict[str, dict[str, Any]] | None = None, ) -> dict[str, Any]: identity_tasks_by_session_id: dict[str, Any] = {} for task in tasks: task_id = str(getattr(task, "id", "") or "").strip() task_meta = ( task_meta_map.get(task_id, {}) if task_meta_map is not None and task_id else _task_metadata(task) ) session_id = _shared_role_session_key(task, task_meta) if not session_id: continue current = identity_tasks_by_session_id.get(session_id) if current is None: identity_tasks_by_session_id[session_id] = task continue current_id = str(getattr(current, "id", "") or "").strip() current_meta = ( task_meta_map.get(current_id, {}) if task_meta_map is not None and current_id else _task_metadata(current) ) if _session_representative_rank(task, task_meta) > _session_representative_rank(current, current_meta): identity_tasks_by_session_id[session_id] = task return identity_tasks_by_session_id async def build_company_kanban_projection( engine: "OPCEngine", *, project_id: str, tasks: list[Any], event_adapter: EventAdapter | None = None, ) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]], dict[str, Any] | None]: """Build a per-session kanban projection for company/custom mode. Each primary session (parent-less task) gets its own board, regardless of whether a delegation run has been started yet. Sessions with an open run get that run's work items; sessions without a run get an empty board so the leader can see their workspace from the moment they create the session. """ if not getattr(engine, "store", None): return [], [], [], None store = engine.store # Group primary tasks by session (order preserved by task creation time # when the caller passes an ordered list). Review Tasks are runtime # scheduling artifacts, never a session's primary task. primary_tasks_by_session_id, ordered_session_ids = _primary_session_tasks_by_session_id(tasks) if not primary_tasks_by_session_id: return [], [], [], None runs: list[Any] = [] list_runs = getattr(store, "list_delegation_runs", None) if callable(list_runs): raw_runs = list_runs(project_id=project_id) raw_runs = await raw_runs if inspect.isawaitable(raw_runs) else raw_runs if isinstance(raw_runs, (list, tuple)): runs = list(raw_runs) if not runs: list_open_runs = getattr(store, "list_open_delegation_runs", None) if callable(list_open_runs): raw_runs = list_open_runs(project_id=project_id) raw_runs = await raw_runs if inspect.isawaitable(raw_runs) else raw_runs if isinstance(raw_runs, (list, tuple)): runs = list(raw_runs) runs_by_session_id: dict[str, list[Any]] = {} for run in runs: sid = str(getattr(run, "session_id", "") or "").strip() if sid: runs_by_session_id.setdefault(sid, []).append(run) employee_name_by_id: dict[str, str] = {} org_engine = getattr(engine, "org_engine", None) hydrate_links = getattr(store, "hydrate_task_work_item_links", None) if callable(hydrate_links): try: await hydrate_links(tasks) except Exception: logger.opt(exception=True).debug("build_company_kanban_projection: link hydration failed") task_by_work_item_id = task_by_linked_work_item_id(tasks) suspended_phase_by_session: dict[str, dict[str, str]] = {} get_checkpoints = getattr(store, "get_pending_checkpoints", None) if callable(get_checkpoints): try: checkpoints = await get_checkpoints( project_id=project_id, checkpoint_types=["company_runtime_suspended", "company_runtime_interrupted"], ) except Exception: checkpoints = [] for checkpoint in checkpoints: sid = str(getattr(checkpoint, "session_id", "") or "").strip() if not sid: continue phase_by_work_item = suspended_phase_by_session.setdefault(sid, {}) for snapshot in list((getattr(checkpoint, "payload", {}) or {}).get("active_work_items", []) or []): if not isinstance(snapshot, dict): continue wid = str(snapshot.get("work_item_id", "") or "").strip() phase = str(snapshot.get("phase", "") or "").strip() if wid and phase: phase_by_work_item[wid] = phase formatted_tasks: list[dict[str, Any]] = [] formatted_columns: list[dict[str, Any]] = [] formatted_boards: list[dict[str, Any]] = [] work_items_by_session_id: dict[str, list[Any]] = {} projection_meta: dict[str, Any] = { "run_ids": [], "board_ids": [], "session_ids": [], # Surfaces the run's DelegationWorkItems back to ``build_snapshot`` so # ``_build_role_work_items_for_session`` can render the work-item # driven Execution Progress panel without re-fetching from the store. "work_items_by_session_id": work_items_by_session_id, "task_by_work_item_id": task_by_work_item_id, } for session_id in ordered_session_ids: primary_task = primary_tasks_by_session_id[session_id] board_id = str(getattr(primary_task, "id", "") or "").strip() or f"session:{session_id}" board_name = _board_label( str(getattr(primary_task, "title", "") or "").strip(), fallback=f"Session {session_id[:8]}", ) session_runs = sorted( runs_by_session_id.get(session_id, []), key=lambda item: ( str(getattr(item, "created_at", "") or ""), str(getattr(item, "run_id", "") or ""), ), ) description = ( "Session board for delegation runs " + ", ".join( str(getattr(run, "run_id", "") or "").strip()[:8] for run in session_runs[:3] ) if session_runs else f"Session board for {session_id[:8]}" ) formatted_boards.append(_build_board_payload(board_id, board_name, description=description)) formatted_columns.extend(build_company_board_columns(board_id)) if not session_runs: projection_meta["run_ids"].append("") projection_meta["board_ids"].append(board_id) projection_meta["session_ids"].append(session_id) continue work_items: list[Any] = [] seat_by_id: dict[str, Any] = {} for run in session_runs: run_id = str(getattr(run, "run_id", "") or "").strip() if not run_id: continue work_items.extend(await store.list_delegation_work_items(run_id)) runtime_seats = await store.list_seat_states(run_id=run_id) if hasattr(store, "list_seat_states") else [] for seat in runtime_seats: seat_id = str(getattr(seat, "seat_id", "") or "").strip() if seat_id: seat_by_id[seat_id] = seat work_items = sorted( work_items, key=lambda item: ( str(getattr(item, "created_at", "") or ""), str(getattr(item, "work_item_id", "") or ""), ), ) # Stash the unfiltered work-item list so ``build_snapshot`` can hand # it to ``_build_role_work_items_for_session`` for the per-role # rollup. Do NOT use ``visible_work_items`` here — the role panel's # own filter already excludes hidden / auxiliary items, and using # the raw list keeps a single source of truth for that decision. work_items_by_session_id[session_id] = list(work_items) if org_engine is not None and hasattr(org_engine, "get_employee"): for seat in seat_by_id.values(): employee_id = str(getattr(seat, "employee_id", "") or "").strip() if not employee_id or employee_id in employee_name_by_id: continue employee = org_engine.get_employee(employee_id) employee_name_by_id[employee_id] = str(getattr(employee, "name", "") or employee_id).strip() visible_work_items = [ item for item in work_items if str(getattr(item, "parent_work_item_id", "") or "").strip() and not bool(dict(getattr(item, "metadata", {}) or {}).get("attention_work_item", False)) and not should_hide_work_item_from_company_kanban(dict(getattr(item, "metadata", {}) or {})) ] formatted_tasks.extend( work_item_to_kanban( item, index + 1, board_id=board_id, event_adapter=event_adapter, seat_by_id=seat_by_id, employee_name_by_id=employee_name_by_id, task_by_work_item_id=task_by_work_item_id, suspended_phase_by_work_item_id=suspended_phase_by_session.get(session_id, {}), ) for index, item in enumerate(visible_work_items) ) projection_meta["run_ids"].extend( str(getattr(run, "run_id", "") or "").strip() for run in session_runs ) projection_meta["board_ids"].append(board_id) projection_meta["session_ids"].append(session_id) return formatted_tasks, formatted_columns, formatted_boards, projection_meta def _agent_with_runtime( agent: dict[str, Any], event_adapter: EventAdapter | None, ) -> dict[str, Any]: result = dict(agent) agent_id = str(result.get("agent_id", "") or "").strip() tracker = event_adapter.get_tracker(agent_id) if event_adapter and agent_id else None runtime_status = tracker.state.value if tracker else str(result.get("status", "idle") or "idle") result["status"] = runtime_status result["runtime_status"] = runtime_status result["current_tool"] = tracker.current_tool if tracker else result.get("current_tool") result["current_task_id"] = tracker.task_id if tracker else result.get("current_task_id") return result def _extract_markdown_text(value: Any, *, max_chars: int | None = None) -> str | None: if not value: return None if isinstance(value, str): text = value.strip() elif isinstance(value, dict): preferred = ( value.get("markdown") or value.get("full_text") or value.get("text") or value.get("summary") ) text = str(preferred).strip() if preferred else json.dumps(value, ensure_ascii=False, indent=2) else: text = str(value).strip() if not text: return None if max_chars is None or len(text) <= max_chars: return text return text[: max_chars - 1].rstrip() + "…" def _extract_work_item_summary_for_downstream(meta: dict[str, Any]) -> str | None: """Extract a short handoff summary from task metadata.""" return _extract_markdown_text(meta.get("handoff_context"), max_chars=500) def _task_metadata(task: Any) -> dict[str, Any]: if hasattr(task, "metadata") and isinstance(task.metadata, dict): return task.metadata return {} def _normalize_session_exec_mode(value: Any) -> str: return normalize_exec_mode(value) def _normalize_session_company_profile( value: Any, *, default: str = "corporate", ) -> str: return normalize_company_profile(value, default=default) def _canonical_session_identity( exec_mode: Any, company_profile: Any, org_id: Any = "", ) -> tuple[str, str, str]: identity = canonicalize_execution_identity( exec_mode=exec_mode, company_profile=company_profile, org_id=org_id, explicit_exec_mode=bool(str(exec_mode or "").strip()), ) return identity.exec_mode, identity.company_profile, identity.org_id def _normalize_session_preferred_agent( value: Any, *, default: str = "native", ) -> str: return normalize_preferred_agent(value, default=default) def _resolve_task_session_config( task: Any, *, default_exec_mode: str = "task", default_company_profile: str = "corporate", ) -> tuple[str, str]: identity = execution_identity_from_task( task, default_exec_mode=default_exec_mode, default_company_profile=default_company_profile, ) return identity.exec_mode, identity.company_profile def _resolve_task_preferred_agent( task: Any, *, default_preferred_agent: str = "native", ) -> str: identity = execution_identity_from_task( task, default_preferred_agent=default_preferred_agent, ) return _normalize_session_preferred_agent( identity.preferred_agent, default=default_preferred_agent, ) def _resolve_task_selected_execution_agent( task: Any, *, default_agent: str = "native", ) -> str: metadata = _task_metadata(task) mode = str(metadata.get("mode", "") or "").strip().lower() exec_mode = str(metadata.get("exec_mode", "") or "").strip().lower() execution_mode = str(metadata.get("execution_mode", "") or "").strip().lower() task_mode_contract = str(metadata.get("task_mode_contract", "") or "").strip() is_task_mode = ( mode == "task" or exec_mode in {"task", "project", "single"} or execution_mode in {"task", "task_mode", "project"} or task_mode_contract == "single_full_capability_main_agent" ) explicit_selected = metadata.get("selected_execution_agent") if is_task_mode: if explicit_selected not in (None, "", [], {}): return _normalize_session_preferred_agent(explicit_selected, default=default_agent) preferred = metadata.get("preferred_agent") if preferred not in (None, "", [], {}): return _normalize_session_preferred_agent(preferred, default=default_agent) agent_selection = dict(metadata.get("agent_selection", {}) or {}) selected = agent_selection.get("selected") if selected not in (None, "", [], {}): return _normalize_session_preferred_agent(selected, default=default_agent) if explicit_selected not in (None, "", [], {}): return _normalize_session_preferred_agent(explicit_selected, default=default_agent) assigned = getattr(task, "assigned_external_agent", None) if assigned not in (None, "", [], {}): return _normalize_session_preferred_agent(assigned, default=default_agent) return _normalize_session_preferred_agent("native", default=default_agent) def _task_mode_origin_ui_task_id(task: Any, task_meta: dict[str, Any] | None = None) -> str: meta = task_meta if task_meta is not None else _task_metadata(task) task_id = str(getattr(task, "id", "") or "").strip() origin_task_id = str(meta.get("origin_task_id", "") or "").strip() if not origin_task_id or origin_task_id == task_id: return "" mode = str(meta.get("mode", "") or "").strip().lower() exec_mode = str(meta.get("exec_mode", "") or "").strip().lower() execution_mode = str(meta.get("execution_mode", "") or "").strip().lower() task_mode_contract = str(meta.get("task_mode_contract", "") or "").strip() if ( mode == "task" or exec_mode in {"task", "project", "single"} or execution_mode in {"task", "task_mode", "project"} or task_mode_contract == "single_full_capability_main_agent" ): return origin_task_id return "" def _task_parent_session_link(task: Any, meta: dict[str, Any]) -> str: """Resolve the parent session link, including older metadata-only records.""" if bool(meta.get("shared_role_session", False)): return "" direct_parent = str(getattr(task, "parent_session_id", "") or "").strip() if direct_parent: return direct_parent legacy_parent = str(meta.get("parent_session_id", "") or "").strip() if legacy_parent and work_item_projection_id_from_metadata(meta): return legacy_parent return "" def _shared_role_session_key(task: Any, meta: dict[str, Any]) -> str: if not bool(meta.get("shared_role_session", False)): return "" return str(getattr(task, "session_id", "") or "").strip() def _session_container_rank(task: Any, meta: dict[str, Any]) -> tuple[int, tuple[int, int, int, float]]: role_id = str(getattr(task, "assigned_to", "") or meta.get("work_item_role_id", "") or "").strip() work_item_projection_id = work_item_projection_id_from_metadata(meta) shared_role_session = bool(meta.get("shared_role_session", False)) # Prefer the user-facing root session row when it shares a session_id # with the final-decider role session. The board / sidebar container must # stay keyed to the origin task so frontend board selection remains stable. if not shared_role_session and not work_item_projection_id and not role_id: bucket = 3 elif not shared_role_session: bucket = 2 else: bucket = 1 return bucket, _session_representative_rank(task, meta) def _session_representative_rank(task: Any, meta: dict[str, Any]) -> tuple[int, int, int, float]: status = str( getattr(getattr(task, "status", None), "value", getattr(task, "status", "pending")) ).strip().lower() status_rank = { "running": 6, "pending": 5, "blocked": 4, "awaiting_peer": 4, "awaiting_manager_review": 4, "awaiting_human": 4, "awaiting_review": 4, "done": 3, "failed": 2, "idle": 1, "cancelled": 0, }.get(status, 1) role_id = str(getattr(task, "assigned_to", "") or meta.get("work_item_role_id", "") or "").strip() created_at = getattr(task, "created_at", None) created_ts = created_at.timestamp() if hasattr(created_at, "timestamp") else 0.0 return ( 1 if role_id else 0, status_rank, 1 if bool(meta.get("authoritative_output", False)) else 0, created_ts, ) def _slice_transcript_from_boundary( transcript: list[dict[str, Any]], boundary_message_id: str, ) -> list[dict[str, Any]]: if not boundary_message_id: return transcript for idx, item in enumerate(transcript): message = item.get("message") if getattr(message, "message_id", "") == boundary_message_id: return transcript[idx + 1:] return transcript async def _build_session_context_preview( engine: "OPCEngine", session_id: str | None, *, max_chars: int | None = 4000, ) -> str | None: """Build a UI-visible context preview from persisted transcript data. Child sessions often have rich persisted assignment/context in their transcript even when task metadata lacks a dedicated handoff summary. """ if not getattr(engine, "store", None) or not session_id: return None try: transcript = await engine.store.get_session_transcript(session_id) except Exception: return None if not transcript: return None try: compaction = await engine.store.get_latest_session_compaction(session_id) except Exception: compaction = None boundary_message_id = getattr(compaction, "source_boundary_message_id", "") if compaction else "" visible = _slice_transcript_from_boundary(transcript, boundary_message_id) blocks: list[str] = [] for item in visible: message = item.get("message") if not message or getattr(message, "summary_flag", False): continue content = _render_parts(item.get("parts", [])).strip() if not content: continue role = str(getattr(message, "role", "") or "").strip().lower() if role == "user": label = "User" elif role == "system": label = "System" else: agent_id = str(getattr(message, "agent_id", "") or "").strip() label = agent_id or "Assistant" blocks.append(f"[{label}]\n{content}") if not blocks: return None rendered = "\n\n".join(blocks).strip() if max_chars is None or len(rendered) <= max_chars: return rendered return rendered[: max_chars - 1].rstrip() + "…" # ── CLI → UI session reconciliation ─────────────────────────────────────── # Role mapping: OPCStore role → ChatStore (sender, sender_name) _ROLE_MAP: dict[str, tuple[str, str]] = { "user": ("user", "You"), "assistant": ("assistant", "OPC"), "system": ("system", "OPC"), } def _render_parts(parts: list[Any]) -> str: """Convert session_parts into a single content string for UI display.""" lines: list[str] = [] for part in parts: pt = part.part_type payload = part.payload if isinstance(part.payload, dict) else {} if pt == "text": text = payload.get("text", "") if text: lines.append(CodexAdapter.normalize_transcript_text(str(text))) elif pt == "subtask_result": title = payload.get("task_title", "Sub-task") summary = payload.get("summary", "") lines.append(f"**{title}**: {summary}" if summary else f"**{title}** completed") elif pt == "task_result": title = payload.get("task_title", "Task") summary = payload.get("summary", "") lines.append(f"**{title}**: {summary}" if summary else f"**{title}** completed") elif pt == "tool_result": tool_name = str(payload.get("tool_name", "tool") or "tool") decision = dict(payload.get("permission_decision", {}) or {}) summary = f"Tool result [{tool_name}]" rationale = str(decision.get("rationale", "") or "").strip() if rationale: summary = f"{summary}: {rationale}" lines.append(summary) # tool_output is typically noise (already shown via progress); skip return "\n".join(lines) if lines else "" def _snapshot_runtime_checkpoint_payload( payload: dict[str, Any], runtime_meta: dict[str, Any], ) -> dict[str, Any]: return { "runtime_session_id": str(payload.get("runtime_session_id", "") or runtime_meta.get("runtime_session_id", "") or "").strip(), "resume_cursor": payload.get("resume_cursor", runtime_meta.get("resume_cursor")), "active_subagents": list(payload.get("active_subagents", runtime_meta.get("active_subagents", [])) or []), "permission_requests": list(payload.get("permission_requests", runtime_meta.get("permission_requests", [])) or []), "worktree_path": str(payload.get("worktree_path", "") or runtime_meta.get("worktree_path", "") or "").strip(), } async def _build_snapshot_checkpoint_meta(engine: "OPCEngine", task: Any) -> dict[str, Any] | None: session_id = str(getattr(task, "session_id", "") or "").strip() if not session_id: return None getter = getattr(engine, "get_latest_pending_checkpoint_for_session", None) if not callable(getter): return None maybe_checkpoint = getter(session_id) checkpoint = await maybe_checkpoint if inspect.isawaitable(maybe_checkpoint) else maybe_checkpoint if not checkpoint: return None payload = dict(getattr(checkpoint, "payload", {}) or {}) checkpoint_type = str(getattr(checkpoint, "checkpoint_type", "") or "").strip() metadata = dict(getattr(task, "metadata", {}) or {}) runtime_meta = dict(metadata.get("runtime_v2", {}) or {}) if checkpoint_type == "task_user_input": pause_request = dict(payload.get("pause_request", {}) or {}) is_task_mode = _metadata_is_task_mode_runtime({**metadata, **payload}) work_item_projection_id = "" if is_task_mode else work_item_projection_id_from_metadata(metadata) work_item_turn_type = "" if is_task_mode else work_item_turn_type_from_metadata(metadata, fallback="") work_item_projection_title = str(getattr(task, "title", "") or "").strip() questions = [ str(item).strip() for item in list(pause_request.get("questions", []) or []) if str(item).strip() ] input_questions = [ dict(item) for item in list(pause_request.get("input_questions", []) or []) if isinstance(item, dict) and str(item.get("question", "") or item.get("header", "") or "").strip() ] if not input_questions: input_questions = [ { "id": f"question_{index + 1}", "header": "", "question": question, "options": [], "allow_freeform": True, "required": True, } for index, question in enumerate(questions) ] return { "checkpoint_type": "task_user_input", "checkpoint_id": checkpoint.checkpoint_id, "task_id": str(payload.get("task_id", "") or getattr(task, "id", "") or "").strip(), **work_item_identity_payload(projection_id=work_item_projection_id, turn_type=work_item_turn_type), **({} if is_task_mode else {"work_item_projection_title": work_item_projection_title}), "execution_mode": payload.get("execution_mode") or metadata.get("execution_mode") or ("task_mode" if is_task_mode else ""), "summary": str(pause_request.get("reason", "") or payload.get("prompt", "") or "").strip(), "prompt": str(payload.get("prompt", "") or "").strip(), "questions": questions, "input_questions": input_questions, "required_fields": [ str(item).strip() for item in list(pause_request.get("required_fields", []) or []) if str(item).strip() ], "context_note": str(pause_request.get("context_note", "") or "").strip(), "resume_hint": str(pause_request.get("resume_hint", "") or "").strip(), "requesting_role_id": str( payload.get("requesting_role_id") or pause_request.get("requesting_role_id") or "" ).strip(), "requesting_task_id": str( payload.get("requesting_task_id") or pause_request.get("requesting_task_id") or "" ).strip(), "requesting_work_item_id": str( payload.get("requesting_work_item_id") or pause_request.get("requesting_work_item_id") or "" ).strip(), "seat_id": str(payload.get("seat_id") or pause_request.get("seat_id") or "").strip(), **_snapshot_runtime_checkpoint_payload(payload, runtime_meta), } if checkpoint_type == "company_work_item_gate": gate = dict(payload.get("gate", {}) or {}) projection_id = str(payload.get("work_item_projection_id") or "").strip() turn_type = str(payload.get("work_item_turn_type") or "").strip() projection_title = str( payload.get("work_item_projection_title") or projection_id or "Work item gate" ).strip() gate_type = str(gate.get("type", "") or "review").strip() prompt_lines = [ f"{projection_title} requires confirmation.", f"Gate type: {gate_type}", ] instructions = str(gate.get("instructions", "") or "").strip() if instructions: prompt_lines.append(f"Instructions: {instructions}") return { "checkpoint_type": "company_work_item_gate", "checkpoint_id": checkpoint.checkpoint_id, **work_item_identity_payload(projection_id=projection_id, turn_type=turn_type), "work_item_projection_title": projection_title, "company_profile": payload.get("company_profile", ""), "summary": instructions or f"Pending {gate_type} confirmation", "prompt": "\n".join(prompt_lines), "options": [ {"id": "approve", "label": "Approve"}, {"id": "deny", "label": "Deny"}, ], "default_action": "deny", **_snapshot_runtime_checkpoint_payload(payload, runtime_meta), } if checkpoint_type == "company_recruitment_confirmation": recruitment_plan = dict(payload.get("recruitment_plan", {}) or {}) plan_metadata = dict(recruitment_plan.get("metadata", {}) or {}) recruitment_agent = _normalize_session_preferred_agent( payload.get("recruitment_agent") or plan_metadata.get("recruitment_agent") or "native", default="native", ) proposals_raw = list(recruitment_plan.get("proposals", []) or []) employee_payloads: list[dict[str, Any]] = [] template_payloads: list[dict[str, Any]] = [] org_engine = getattr(engine, "org_engine", None) talent_market = getattr(engine, "talent_market", None) is_placeholder = getattr(engine, "_is_placeholder_staffing_employee", lambda _employee: False) employee_payload = getattr(engine, "_staffing_employee_payload", None) template_payload = getattr(engine, "_staffing_template_payload", None) if org_engine and callable(employee_payload): try: employee_payloads = [ employee_payload(employee) for employee in org_engine.list_employees() if not is_placeholder(employee) ] except Exception: employee_payloads = [] if talent_market and callable(template_payload): try: template_payloads = [ template_payload(template) for template in talent_market.list_available_templates() if str(getattr(template, "id", "") or "").strip() ] except Exception: template_payloads = [] employees_by_id = { str(item.get("employee_id", "") or "").strip(): item for item in employee_payloads if str(item.get("employee_id", "") or "").strip() } templates_by_id = { str(item.get("template_id", "") or "").strip(): item for item in template_payloads if str(item.get("template_id", "") or "").strip() } employees_by_role: dict[str, list[dict[str, Any]]] = {} for item in employee_payloads: role_ids = [ str(role_id or "").strip() for role_id in list(item.get("role_ids", []) or []) if str(role_id or "").strip() ] or [str(item.get("role_id", "") or "").strip()] for role_id in role_ids: if role_id: employees_by_role.setdefault(role_id, []).append(item) proposals: list[dict[str, Any]] = [] staffing_roles: list[dict[str, Any]] = [] staffing_selections: dict[str, dict[str, str]] = {} recruitment_rationales: list[dict[str, Any]] = [] payload_role_agents: dict[str, str] = {} raw_payload_role_agents = payload.get("recruitment_role_agents") if isinstance(raw_payload_role_agents, dict): payload_role_agents = { str(raw_role_id or "").strip(): _normalize_session_preferred_agent(raw_agent, default="codex") for raw_role_id, raw_agent in raw_payload_role_agents.items() if str(raw_role_id or "").strip() } recruitment_role_agents: dict[str, str] = {} for proposal in proposals_raw: if not isinstance(proposal, dict): continue role_id = str(proposal.get("role_id", "") or "").strip() proposal_metadata = dict(proposal.get("metadata", {}) or {}) default_agent = "codex" selected_agent = _normalize_session_preferred_agent( payload_role_agents.get(role_id) or proposal_metadata.get("selected_execution_agent"), default=default_agent, ) if role_id: recruitment_role_agents[role_id] = selected_agent entry: dict[str, Any] = { "role_id": role_id, "status": proposal.get("status", ""), "rationale": proposal.get("rationale", ""), "role_labels": list(proposal.get("role_labels", []) or []), "default_agent": default_agent, "selected_agent": selected_agent, } candidate = proposal.get("candidate") if isinstance(candidate, dict): entry["candidate"] = { "template_id": candidate.get("template_id", ""), "template_name": candidate.get("template_name", ""), "category": candidate.get("category", ""), "domains": list(candidate.get("domains", []) or []), "proposed_name": candidate.get("proposed_employee_name", ""), "rationale": candidate.get("rationale", ""), } template_id = str(candidate.get("template_id", "") or "").strip() if template_id and template_id not in templates_by_id: templates_by_id[template_id] = { "kind": "template", "template_id": template_id, "template_name": candidate.get("template_name", "") or template_id, "category": candidate.get("category", ""), "domains": list(candidate.get("domains", []) or []), "tags": [], "description": "", "preferred_external_agent": candidate.get("preferred_external_agent"), "source_path": candidate.get("source_path", ""), } existing_employee = proposal.get("existing_employee") if isinstance(existing_employee, dict): entry["existing_employee"] = { "employee_id": existing_employee.get("employee_id", ""), "employee_name": existing_employee.get("employee_name", ""), "role_id": existing_employee.get("role_id", ""), "domains": list(existing_employee.get("domains", []) or []), "experience_score": existing_employee.get("experience_score", 0), "rationale": existing_employee.get("rationale", ""), } employee_id = str(existing_employee.get("employee_id", "") or "").strip() if employee_id and employee_id not in employees_by_id: item = { "kind": "employee", "employee_id": employee_id, "employee_name": existing_employee.get("employee_name", "") or employee_id, "role_id": existing_employee.get("role_id", "") or role_id, "category": existing_employee.get("category", ""), "domains": list(existing_employee.get("domains", []) or []), "tags": [], "description": "", "preferred_external_agent": None, "experience_score": existing_employee.get("experience_score", 0), } employees_by_id[employee_id] = item if role_id: employees_by_role.setdefault(role_id, []).append(item) proposals.append(entry) role_label = str((proposal.get("role_labels", []) or [role_id])[0] or role_id) default_selection: dict[str, str] = {"kind": "fallback", "id": ""} selection_label = "Fallback role-only" if isinstance(existing_employee, dict) and str(existing_employee.get("employee_id", "") or "").strip(): employee_id = str(existing_employee.get("employee_id", "") or "").strip() default_selection = {"kind": "employee", "id": employee_id, "employee_id": employee_id} selection_label = str(existing_employee.get("employee_name", "") or employee_id) elif isinstance(candidate, dict) and str(candidate.get("template_id", "") or "").strip(): template_id = str(candidate.get("template_id", "") or "").strip() default_selection = {"kind": "template", "id": template_id, "template_id": template_id} selection_label = str(candidate.get("proposed_employee_name") or candidate.get("template_name") or template_id) if role_id: staffing_selections[role_id] = dict(default_selection) same_role_ids = { str(item.get("employee_id", "") or "").strip() for item in employees_by_role.get(role_id, []) if str(item.get("employee_id", "") or "").strip() } same_role_ids.update( str(item or "").strip() for item in list(proposal.get("existing_employee_ids", []) or []) if str(item or "").strip() ) staffing_roles.append( { "role_id": role_id, "role_label": role_label, "role_responsibility": "", "default_selection": default_selection, "same_role_employee_ids": sorted(same_role_ids), "fallback_available": True, "default_agent": default_agent, "selected_agent": selected_agent, "default_source": "recruitment", } ) reason_parts = [ str((candidate or {}).get("rationale", "") or "").strip() if isinstance(candidate, dict) else "", str((existing_employee or {}).get("rationale", "") or "").strip() if isinstance(existing_employee, dict) else "", str(proposal.get("rationale", "") or "").strip(), ] recruitment_rationales.append( { "role_id": role_id, "role_label": role_label, "status": proposal.get("status", ""), "selection_label": selection_label, "rationale": next((item for item in reason_parts if item), ""), } ) return { "checkpoint_type": "company_recruitment_confirmation", "checkpoint_id": checkpoint.checkpoint_id, "company_profile": recruitment_plan.get("company_profile", "corporate"), "proposals": proposals, "summary": recruitment_plan.get("summary", ""), "recruitment_agent": recruitment_agent, "recruitment_role_agents": recruitment_role_agents, "recruitment_rationales": recruitment_rationales, "staffing_roles": staffing_roles, "staffing_pool": { "employees": list(employees_by_id.values()), "templates": list(templates_by_id.values()), }, "staffing_selections": staffing_selections, } if checkpoint_type == "company_staffing_selection": raw_role_agents = payload.get("recruitment_role_agents") payload_role_agents = raw_role_agents if isinstance(raw_role_agents, dict) else {} recruitment_agent = _normalize_session_preferred_agent( payload.get("recruitment_agent") or "native", default="native", ) staffing_roles: list[dict[str, Any]] = [] recruitment_role_agents: dict[str, str] = {} for raw_role in list(payload.get("staffing_roles", []) or []): if not isinstance(raw_role, dict): continue role = dict(raw_role) role_id = str(role.get("role_id", "") or "").strip() selected_agent = _normalize_session_preferred_agent( payload_role_agents.get(role_id) or role.get("selected_agent") or role.get("default_agent") or "codex", default="codex", ) if role_id: role["selected_agent"] = selected_agent recruitment_role_agents[role_id] = selected_agent staffing_roles.append(role) return { "checkpoint_type": "company_staffing_selection", "checkpoint_id": checkpoint.checkpoint_id, "company_profile": payload.get("company_profile", "corporate"), "summary": payload.get("summary") or "Select staff manually, or run automatic recruitment.", "staffing_strategy": payload.get("staffing_strategy", ""), "recommended_action": payload.get("recommended_action", ""), "staffing_defaults": dict(payload.get("staffing_defaults", {}) or {}), "staffing_roles": staffing_roles, "recruitment_agent": recruitment_agent, "recruitment_role_agents": recruitment_role_agents, "staffing_pool": dict(payload.get("staffing_pool", {}) or {}), } if checkpoint_type == "company_delivery_feedback": delivery_package = payload.get("delivery_package") if not isinstance(delivery_package, dict): delivery_package = {} result_content = str(payload.get("result_content", "") or "").strip() summary = str( delivery_package.get("executive_summary") or delivery_package.get("summary") or payload.get("work_item_summary") or payload.get("work_item_summary_for_downstream") or result_content or "" ).strip() projection_id = str(payload.get("work_item_projection_id") or "").strip() turn_type = str(payload.get("work_item_turn_type") or "").strip() waiting_task_id = str( payload.get("waiting_task_id") or payload.get("task_id") or getattr(checkpoint, "task_id", "") or "" ).strip() return { "checkpoint_type": "company_delivery_feedback", "checkpoint_id": checkpoint.checkpoint_id, "waiting_task_id": waiting_task_id, "task_id": waiting_task_id or str(getattr(task, "id", "") or "").strip(), **work_item_identity_payload(projection_id=projection_id, turn_type=turn_type), "work_item_projection_title": payload.get("work_item_projection_title", ""), "company_profile": payload.get("company_profile", ""), "feedback_scope": payload.get("feedback_scope", "work_item"), "summary": summary, "prompt": payload.get("prompt", ""), "options": [ {"id": "approve", "label": "Fully Agree / 完全同意"}, {"id": "ignore", "label": "Ignore / 忽略"}, {"id": "feedback", "label": "Feedback / 反馈"}, ], "delivery_package": delivery_package, "result_content": result_content, "delivery_revision": payload.get("delivery_revision", ""), "owner_directive_revision": payload.get("owner_directive_revision", ""), "latest_user_directive": str(payload.get("latest_user_directive", "") or "").strip(), "waiting_work_item_id": str(payload.get("waiting_work_item_id", "") or "").strip(), **_snapshot_runtime_checkpoint_payload(payload, runtime_meta), } return None def _checkpoint_meta_targets_task(checkpoint_meta: dict[str, Any], task: Any) -> bool: if str(checkpoint_meta.get("checkpoint_type", "") or "").strip() != "company_delivery_feedback": return True target_task_id = str( checkpoint_meta.get("waiting_task_id") or checkpoint_meta.get("task_id") or "" ).strip() task_id = str(getattr(task, "id", "") or "").strip() return not target_task_id or not task_id or target_task_id == task_id def _message_can_host_checkpoint_meta(message: dict[str, Any], checkpoint_meta: dict[str, Any]) -> bool: if str(checkpoint_meta.get("checkpoint_type", "") or "").strip() != "company_delivery_feedback": return True metadata = dict(message.get("metadata", {}) or {}) if str(message.get("sender", "") or "").strip().lower() == "system": return False if str(metadata.get("kind", "") or "").strip() == "worker_notification": return False # Parent-session child_result entries mirror child outputs. The review card # belongs to the actual waiting delivery task, not to every mirror. if str(metadata.get("transcript_kind", "") or "").strip() == "child_result": return False return True def _synthetic_checkpoint_card_message( *, channel_id: str, checkpoint_meta: dict[str, Any], ) -> dict[str, Any]: checkpoint_id = str(checkpoint_meta.get("checkpoint_id", "") or "").strip() created_at = time.time() return { "message_id": f"checkpoint::{checkpoint_id}" if checkpoint_id else str(uuid.uuid4()), "channel_id": channel_id, "sender": "assistant", "sender_name": str( checkpoint_meta.get("work_item_projection_title") or checkpoint_meta.get("requesting_role_id") or "Company Member" ), "content": str( checkpoint_meta.get("prompt") or checkpoint_meta.get("summary") or "Human review requested." ), "timestamp": created_at, "created_at": created_at, "reply_to_id": None, "mentions": [], "metadata": dict(checkpoint_meta), } def _attach_or_create_checkpoint_card( messages: list[dict[str, Any]], *, channel_id: str, checkpoint_meta: dict[str, Any], ) -> None: checkpoint_id = str(checkpoint_meta.get("checkpoint_id", "") or "").strip() synthetic_message_id = f"checkpoint::{checkpoint_id}" if checkpoint_id else "" for message in reversed(messages): if message.get("channel_id") != channel_id: continue if synthetic_message_id and str(message.get("message_id", "") or "") == synthetic_message_id: metadata = dict(message.get("metadata", {}) or {}) metadata.update(checkpoint_meta) message["metadata"] = metadata return if str(message.get("sender", "") or "").strip().lower() == "user": continue if not _message_can_host_checkpoint_meta(message, checkpoint_meta): continue metadata = dict(message.get("metadata", {}) or {}) metadata.update(checkpoint_meta) message["metadata"] = metadata return messages.append(_synthetic_checkpoint_card_message( channel_id=channel_id, checkpoint_meta=checkpoint_meta, )) async def _build_company_runtime_control_by_task( engine: "OPCEngine", tasks: list[Any], project_id: str, ) -> dict[str, dict[str, Any]]: store = getattr(engine, "store", None) if not store: return {} parent_task_by_session: dict[str, str] = {} tasks_by_parent_session: dict[str, list[Any]] = {} for task in tasks: metadata = dict(getattr(task, "metadata", {}) or {}) mode = str(metadata.get("mode", "") or metadata.get("exec_mode", "") or "").strip().lower() is_company_runtime_task = bool( mode in {"company", "org", "custom"} or str(getattr(task, "parent_session_id", "") or "").strip() or metadata.get("company_profile") or metadata.get("company_work_item_plan") or metadata.get("work_item_runtime") or metadata.get("work_item_projection_id") ) if not is_company_runtime_task: continue session_id = str(getattr(task, "session_id", "") or "").strip() parent_session_id = str(getattr(task, "parent_session_id", "") or "").strip() task_id = str(getattr(task, "id", "") or "").strip() if session_id and not parent_session_id: parent_task_by_session[session_id] = task_id runtime_parent_session_id = parent_session_id or session_id if runtime_parent_session_id: tasks_by_parent_session.setdefault(runtime_parent_session_id, []).append(task) checkpoints_by_session: dict[str, Any] = {} getter = getattr(store, "get_execution_checkpoints", None) if not callable(getter): getter = getattr(store, "get_pending_checkpoints", None) if callable(getter): try: kwargs = { "project_id": project_id, "checkpoint_types": ["company_runtime_suspended", "company_runtime_interrupted"], } if getattr(getter, "__name__", "") == "get_execution_checkpoints": kwargs["statuses"] = ["pending", "resuming"] checkpoints = await getter(**kwargs) for checkpoint in checkpoints: sid = str(getattr(checkpoint, "session_id", "") or "").strip() if sid and sid not in checkpoints_by_session: checkpoints_by_session[sid] = checkpoint except Exception: logger.opt(exception=True).debug("snapshot: failed to load company runtime checkpoints") result: dict[str, dict[str, Any]] = {} for parent_session_id, group in tasks_by_parent_session.items(): checkpoint = checkpoints_by_session.get(parent_session_id) parent_task_id = parent_task_by_session.get(parent_session_id, "") if not parent_task_id: for task in group: if not str(getattr(task, "parent_session_id", "") or "").strip(): parent_task_id = str(getattr(task, "id", "") or "").strip() break if not parent_task_id and group: parent_task_id = str(getattr(group[0], "id", "") or "").strip() def _task_status_value(task: Any) -> str: status = getattr(task, "status", "") if hasattr(status, "value"): return str(status.value or "").strip().lower() return str(status or "").strip().lower().removeprefix("taskstatus.") non_terminal_group = [ task for task in group if _task_status_value(task) not in {"done", "failed", "cancelled"} ] has_running_task = any( _task_status_value(task) == "running" for task in non_terminal_group ) any_stop_in_progress = any( str((getattr(task, "metadata", {}) or {}).get("company_runtime_stop_state", "") or "").strip() in {"suspending", "suspended", "resuming_after_suspending"} and bool(str((getattr(task, "metadata", {}) or {}).get("company_runtime_stop_marked_at", "") or "").strip()) for task in non_terminal_group ) any_held_suspended = any( str((getattr(task, "metadata", {}) or {}).get("dispatch_hold", "") or "").strip() == "company_runtime_suspended" for task in non_terminal_group ) any_resuming = any( str((getattr(task, "metadata", {}) or {}).get("company_runtime_stop_state", "") or "").strip() == "resuming" for task in non_terminal_group ) checkpoint_status = str(getattr(checkpoint, "status", "") or "").strip().lower() if checkpoint is not None else "" if any_resuming or checkpoint_status == "resuming": state = "resuming" elif checkpoint is not None: state = "suspended" elif any_held_suspended or ( any_stop_in_progress and any( str((getattr(task, "metadata", {}) or {}).get("company_runtime_stop_state", "") or "").strip() in {"suspending", "resuming_after_suspending"} for task in non_terminal_group ) ): state = "suspending" elif has_running_task: state = "running" else: state = "idle" checkpoint_payload = dict(getattr(checkpoint, "payload", {}) or {}) if checkpoint is not None else {} pending_checkpoint_id = str(getattr(checkpoint, "checkpoint_id", "") or "").strip() if checkpoint is not None else "" for task in group: task_id = str(getattr(task, "id", "") or "").strip() if not task_id: continue result[task_id] = { "runtime_control_state": state, "can_stop": state == "running", "can_resume": state == "suspended", "resume_parent_task_id": parent_task_id, "resume_parent_session_id": parent_session_id, "pending_runtime_checkpoint_id": pending_checkpoint_id, "stop_intent_id": str(checkpoint_payload.get("stop_intent_id", "") or ""), } return result async def reconcile_sessions( engine: "OPCEngine", chat_store: "ChatStore", tasks: list[Any], project_id: str = "default", *, max_hydrate: int = 10, ) -> int: """Backfill CLI-generated session history into ChatStore. For each task that has a session_id, this reads the authoritative transcript from engine.store (tasks.db) and inserts any messages that are missing from the ChatStore rendering cache (ui_state.db). ``max_hydrate`` caps the number of empty sessions that get their full transcript loaded in a single call. This keeps collab_sync fast for large projects — remaining sessions are hydrated lazily when opened. Returns total number of messages backfilled. """ if not engine.store: return 0 total_backfilled = 0 hydrated = 0 task_ids = { str(getattr(task, "id", "") or "").strip() for task in tasks if str(getattr(task, "id", "") or "").strip() } session_channel_stats = await chat_store.get_channel_stats( [ f"session:{getattr(task, 'id', '')}" for task in tasks if str(getattr(task, "id", "") or "").strip() ], project_id=project_id, ) for task in tasks: session_id = getattr(task, "session_id", None) if not session_id: continue task_meta = _task_metadata(task) canonical_task_id = _task_mode_origin_ui_task_id(task, task_meta) if canonical_task_id and canonical_task_id in task_ids: continue channel_id = f"session:{task.id}" # Ensure the UI session channel exists for every persisted task session. # This repairs cases where messages/progress were written before the # channel row was created, which would otherwise make the task look # orphaned and disappear from kanban. await chat_store.create_session_channel( task_id=task.id, title=task.title, project_id=project_id, ) # Defer expensive transcript hydration until the session is actually opened. # This keeps collab_sync lightweight for large projects while still repairing # empty/missing channels created outside the UI. existing_count = int(session_channel_stats.get(channel_id, {}).get("message_count", 0) or 0) if existing_count > 0: continue # Cap expensive transcript loads to keep collab_sync fast for large projects. if hydrated >= max_hydrate: continue hydrated += 1 # Get authoritative transcript from engine store try: transcript = await engine.store.get_session_transcript(session_id) except Exception: continue ui_messages = build_transcript_ui_messages( transcript, channel_id=channel_id, task_id=str(getattr(task, "id", "") or ""), detail_level="summary", ) if len(ui_messages) <= existing_count: # ChatStore already has at least as many visible messages — skip continue inserted_messages = await chat_store.backfill_messages(channel_id, ui_messages, project_id) if inserted_messages: logger.info(f"Reconciled {len(inserted_messages)} messages for session:{task.id}") total_backfilled += len(inserted_messages) return total_backfilled # ── Snapshot builder ─────────────────────────────────────────────────────── async def build_snapshot( engine: OPCEngine, agent_store: AgentStore, chat_store: ChatStore, event_adapter: EventAdapter, ) -> dict[str, Any]: """Build VisualSnapshot matching frontend types/visual.ts.""" project_id = engine.project_id or "default" agents = await agent_store.get_all() snapshot_agents = { str(agent.get("agent_id", "") or ""): _agent_with_runtime(agent, event_adapter) for agent in agents } # Skills skills_data: list[Any] = [] if engine.skills: try: all_skills = engine.skills.list_skills() if all_skills: skills_data = all_skills except Exception: logger.debug("Failed to load skills for snapshot") # Timeline from event history timeline: list[dict[str, Any]] = [] try: history = engine.event_bus.get_history(limit=50) for evt in history: visual_events = event_adapter.translate(evt) timeline.extend(visual_events) except Exception: logger.debug("Failed to build timeline for snapshot") # Agent templates templates = await agent_store.get_templates(engine.org_engine) return { "project_id": project_id, "agents": snapshot_agents, "channels": {}, # Channels loaded via collab_sync, not snapshot "skills": { "recent": [ { "skill_name": s.name if hasattr(s, "name") else (s.get("name", "") if isinstance(s, dict) else str(s)), "version": 1, "timestamp": 0, } for s in (skills_data or [])[-5:] ], "total": len(skills_data or []), }, "practice": {"count": 0, "last": None}, "milestones": [], "timeline": timeline, # Extra field (accessed via snapshot?.agent_templates in App.tsx) "agent_templates": templates, } # ── project index / collab_sync builders ─────────────────────────────────── async def build_project_index_sync( engine: OPCEngine, agent_store: AgentStore, chat_store: ChatStore, event_adapter: EventAdapter | None = None, *, exec_mode: str | None = None, ) -> dict[str, Any]: """Build a lightweight project index for fast project switching. The index deliberately avoids project-wide message/progress hydration and the full company work-item projection. Complete context stays available through ``session_detail``. """ project_id = engine.project_id or "default" tasks: list[Any] = [] if engine.store: try: tasks = await engine.store.get_tasks(project_id=project_id) except Exception: logger.opt(exception=True).warning("Failed to load tasks for project index") existing_task_ids = { str(getattr(task, "id", "") or "").strip() for task in tasks if str(getattr(task, "id", "") or "").strip() } get_channels = getattr(chat_store, "get_channels", None) if callable(get_channels): raw_channels = get_channels(project_id) resolved_channels = await raw_channels if inspect.isawaitable(raw_channels) else raw_channels channels = list(resolved_channels) if isinstance(resolved_channels, list) else [] else: channels = [] get_session_channels = getattr(chat_store, "get_session_channels", None) if callable(get_session_channels): raw_session_channels = get_session_channels(project_id) resolved_session_channels = ( await raw_session_channels if inspect.isawaitable(raw_session_channels) else raw_session_channels ) session_channels = list(resolved_session_channels) if isinstance(resolved_session_channels, list) else [] else: session_channels = [] session_channel_map = {ch["channel_id"]: ch for ch in session_channels} known_channel_ids = { str(ch.get("channel_id", "") or "").strip() for ch in channels if str(ch.get("channel_id", "") or "").strip() } for task in tasks: task_id = str(getattr(task, "id", "") or "").strip() session_id = str(getattr(task, "session_id", "") or "").strip() if not task_id or not session_id: continue canonical_task_id = _task_mode_origin_ui_task_id(task, _task_metadata(task)) if canonical_task_id and canonical_task_id in existing_task_ids: continue channel_id = f"session:{task_id}" if channel_id in session_channel_map: continue title = str(getattr(task, "title", "") or "Session").strip() or "Session" try: create_session_channel = getattr(chat_store, "create_session_channel", None) if not callable(create_session_channel): raise AttributeError("create_session_channel unavailable") repaired_channel = await create_session_channel(task_id, title, project_id=project_id) except Exception: created_at = getattr(task, "created_at", None) repaired_channel = { "channel_id": channel_id, "type": "session", "name": title, "office_id": None, "participants": ["user"], "created_at": ( created_at.timestamp() if hasattr(created_at, "timestamp") else time.time() ), } session_channel_map[channel_id] = repaired_channel if channel_id not in known_channel_ids: channels.append(repaired_channel) known_channel_ids.add(channel_id) channel_ids = list(session_channel_map.keys()) session_channel_stats: dict[str, Any] = {} index_stats_getter = getattr(chat_store, "get_channel_index_stats", None) stats_getter = getattr(chat_store, "get_channel_stats", None) if callable(index_stats_getter): raw_stats = index_stats_getter(channel_ids, project_id=project_id) resolved_stats = await raw_stats if inspect.isawaitable(raw_stats) else raw_stats session_channel_stats = resolved_stats if isinstance(resolved_stats, dict) else {} elif callable(stats_getter): raw_stats = stats_getter(channel_ids, project_id=project_id) resolved_stats = await raw_stats if inspect.isawaitable(raw_stats) else raw_stats session_channel_stats = resolved_stats if isinstance(resolved_stats, dict) else {} formatted_channels = [ { "channel_id": ch["channel_id"], "channel_type": ch["type"], "name": ch["name"], "office_id": ch.get("office_id"), "participants": ch["participants"], "created_at": ch["created_at"], } for ch in channels ] now = time.time() all_task_meta_map = {str(getattr(t, "id", "") or ""): _task_metadata(t) for t in tasks} session_tasks = [ t for t in tasks if not bool(all_task_meta_map.get(str(getattr(t, "id", "") or ""), {}).get("review_task", False)) and not ( (canonical_id := _task_mode_origin_ui_task_id( t, all_task_meta_map.get(str(getattr(t, "id", "") or ""), {}), )) and canonical_id in existing_task_ids ) ] active_tasks = [ t for t in session_tasks if (t.status.value if hasattr(t.status, "value") else str(t.status)) != "cancelled" ] task_meta_map = {str(getattr(t, "id", "") or ""): _task_metadata(t) for t in session_tasks} primary_tasks_by_session_id, ordered_session_ids = _primary_session_tasks_by_session_id( session_tasks, task_meta_map=task_meta_map, ) shared_identity_tasks_by_session_id = _shared_role_identity_tasks_by_session_id( session_tasks, task_meta_map=task_meta_map, ) child_tasks_by_parent: dict[str, list[Any]] = {} for task in session_tasks: task_meta = task_meta_map.get(str(getattr(task, "id", "") or ""), {}) parent_link = _task_parent_session_link(task, task_meta) if parent_link: child_tasks_by_parent.setdefault(parent_link, []).append(task) normalized_exec_mode = _normalize_session_exec_mode(exec_mode) has_company_sessions = any( _resolve_task_session_config(task)[0] in {"company", "org"} for task in session_tasks ) if normalized_exec_mode in {"company", "org"} or has_company_sessions: formatted_boards: list[dict[str, Any]] = [] formatted_columns: list[dict[str, Any]] = [] for session_id in ordered_session_ids: primary_task = primary_tasks_by_session_id[session_id] board_id = str(getattr(primary_task, "id", "") or "").strip() or f"session:{session_id}" board_name = _board_label( str(getattr(primary_task, "title", "") or "").strip(), fallback=f"Session {session_id[:8]}", ) formatted_boards.append(_build_board_payload( board_id, board_name, description=f"Session board for {session_id[:8]}", now=now, )) formatted_columns.extend(build_company_board_columns(board_id, now=now)) formatted_tasks: list[dict[str, Any]] = [] else: formatted_boards = [ _build_board_payload( project_id, project_id if project_id != "default" else "Main Board", now=now, ) ] formatted_columns = build_board_columns(project_id, now=now) formatted_tasks = [ task_to_kanban(t, i + 1, event_adapter) for i, t in enumerate(active_tasks) ] default_context_window = None llm_provider = getattr(engine, "llm", None) if llm_provider is not None: try: default_context_window = llm_provider.get_context_window() except Exception: default_context_window = None runtime_control_by_task = await _build_company_runtime_control_by_task( engine, session_tasks, project_id, ) formatted_sessions: list[dict[str, Any]] = [] for t in session_tasks: task_id = str(getattr(t, "id", "") or "").strip() channel_id = f"session:{task_id}" ch = session_channel_map.get(channel_id) if not task_id or not ch: continue t_meta = task_meta_map.get(task_id, _task_metadata(t)) session_id = str(getattr(t, "session_id", "") or "").strip() representative_task = primary_tasks_by_session_id.get(session_id) representative_task_id = str(getattr(representative_task, "id", "") or "").strip() shared_session_id = _shared_role_session_key(t, t_meta) if shared_session_id and representative_task_id and representative_task_id != task_id: continue identity_task = t identity_meta = t_meta shared_identity_task = shared_identity_tasks_by_session_id.get(session_id) shared_identity_task_id = str(getattr(shared_identity_task, "id", "") or "").strip() if shared_identity_task_id and shared_identity_task_id != task_id: identity_task = shared_identity_task identity_meta = task_meta_map.get(shared_identity_task_id, _task_metadata(shared_identity_task)) status_val = t.status.value if hasattr(t.status, "value") else str(t.status) channel_stats = session_channel_stats.get(channel_id, {}) msg_count = int(channel_stats.get("message_count", 0) or 0) created_ts = t.created_at.timestamp() if hasattr(t.created_at, "timestamp") else time.time() updated_ts = created_ts latest_ts = channel_stats.get("latest_timestamp") if latest_ts: updated_ts = float(latest_ts) parent_sid = _task_parent_session_link(t, t_meta) or None session_mode = "child" if parent_sid else "primary" exec_mode_val, company_profile_val = _resolve_task_session_config(t) preferred_agent_val = _resolve_task_preferred_agent(t) selected_execution_agent_val = _resolve_task_selected_execution_agent(identity_task) child_keys = {task_id} if session_id: child_keys.add(session_id) child_tasks: list[Any] = [] seen_child_ids: set[str] = set() for key in child_keys: for child in child_tasks_by_parent.get(key, []): child_id = str(getattr(child, "id", "") or "") if not child_id or child_id == task_id or child_id in seen_child_ids: continue seen_child_ids.add(child_id) child_tasks.append(child) identity_task_id = str(getattr(identity_task, "id", "") or "").strip() company_profile = t_meta.get("company_profile") or identity_meta.get("company_profile") org_id = ( t_meta.get("org_id") or t_meta.get("organization_id") or identity_meta.get("org_id") or identity_meta.get("organization_id") ) if not company_profile and session_mode == "primary": for child in child_tasks: child_meta = task_meta_map.get(str(getattr(child, "id", "") or ""), {}) inherited_profile = child_meta.get("company_profile") if inherited_profile: company_profile = inherited_profile break is_task_mode_runtime = _metadata_is_task_mode_runtime({**t_meta, **identity_meta}) runtime_meta = { **dict(t_meta.get("runtime_v2", {}) or {}), **dict(identity_meta.get("runtime_v2", {}) or {}), } member_session_meta = { **dict(t_meta.get("member_session_state", {}) or {}), **dict(identity_meta.get("member_session_state", {}) or {}), } employee_assignment = { **dict(t_meta.get("employee_assignment", {}) or {}), **dict(identity_meta.get("employee_assignment", {}) or {}), } identity_payload = {} if is_task_mode_runtime else work_item_identity_payload_from_metadata(identity_meta) fallback_identity_payload = {} if is_task_mode_runtime else work_item_identity_payload_from_metadata(t_meta) if not is_task_mode_runtime: if WORK_ITEM_PROJECTION_ID_KEY not in identity_payload: projection_value = fallback_identity_payload.get(WORK_ITEM_PROJECTION_ID_KEY) if projection_value: identity_payload[WORK_ITEM_PROJECTION_ID_KEY] = projection_value if WORK_ITEM_TURN_TYPE_KEY not in identity_payload: turn_value = fallback_identity_payload.get(WORK_ITEM_TURN_TYPE_KEY) if turn_value: identity_payload[WORK_ITEM_TURN_TYPE_KEY] = turn_value tracker_data: dict[str, Any] = {} if event_adapter and getattr(identity_task, "assigned_to", ""): try: agent_id = event_adapter._resolve_role_to_agent(identity_task.assigned_to) get_tracker = getattr(event_adapter, "get_tracker", None) tracker = get_tracker(agent_id) if callable(get_tracker) else None if tracker: tracker_data = { "agent_status": tracker.state.value, "current_tool": tracker.current_tool, } except Exception: tracker_data = {} exec_mode_val, resolved_company_profile, org_id = _canonical_session_identity( exec_mode_val, company_profile or company_profile_val, org_id, ) formatted_sessions.append({ "project_id": project_id, "task_id": task_id, **_execution_turn_alias_payload(task_id), "session_id": getattr(t, "session_id", None), "parent_session_id": parent_sid, "mode": session_mode, "exec_mode": exec_mode_val, "company_profile": resolved_company_profile, "org_id": org_id, "preferred_agent": preferred_agent_val, "selected_execution_agent": selected_execution_agent_val, "channel_id": channel_id, "title": getattr(t, "title", "") or "Session", "status": status_val, "column_id": STATUS_TO_COLUMN.get(status_val, "todo"), "assignee_ids": [event_adapter._resolve_role_to_agent(identity_task.assigned_to)] if event_adapter and getattr(identity_task, "assigned_to", "") else ([identity_task.assigned_to] if getattr(identity_task, "assigned_to", "") else []), "priority": priority_to_label(t.priority) if isinstance(t.priority, int) else None, "tags": t.tags or [], "created_at": created_ts, "updated_at": updated_ts, "message_count": msg_count, "latest_preview": channel_stats.get("latest_preview") or "", "latest_sender": channel_stats.get("latest_sender") or "", "latest_message_id": channel_stats.get("latest_message_id") or "", **identity_payload, **({} if is_task_mode_runtime else _work_item_role_payload(identity_task)), "work_item_gate": None if is_task_mode_runtime else (identity_meta.get("work_item_gate") or t_meta.get("work_item_gate")), "employee_assignment": employee_assignment or None, "origin_channel": identity_meta.get("origin_channel") or t_meta.get("origin_channel"), "origin_task_id": t_meta.get("origin_task_id") or task_id, "is_company_runtime": bool( not is_task_mode_runtime and session_mode == "primary" and ( company_profile or child_tasks or (identity_task_id and identity_task_id != task_id) ) ), **runtime_control_by_task.get(task_id, {}), "artifacts": identity_meta.get("artifacts") or t_meta.get("artifacts"), "runtime_session_id": runtime_meta.get("runtime_session_id"), "resume_cursor": runtime_meta.get("resume_cursor"), "active_subagents": list(runtime_meta.get("active_subagents", []) or []), "permission_requests": list(runtime_meta.get("permission_requests", []) or []), "worktree_path": runtime_meta.get("worktree_path"), "context_tokens": runtime_meta.get("context_tokens"), "context_window": runtime_meta.get("context_window") or default_context_window, "context_remaining_pct": runtime_meta.get("context_remaining_pct"), "pending_permission_count": runtime_meta.get("pending_permission_count"), "drain_mode": runtime_meta.get("drain_mode"), "resident_status": _runtime_status_from_member_meta(member_session_meta), "actionable_inbox_count": member_session_meta.get("actionable_inbox_count"), "protocol_backlog_count": member_session_meta.get("protocol_backlog_count"), "notification_backlog_count": member_session_meta.get("notification_backlog_count"), "latest_notification": member_session_meta.get("latest_notification"), "index_loaded": True, **tracker_data, }) return { "ok": True, "project_id": project_id, "sync_scope": "index", "channels": formatted_channels, "messages": [], "boards": formatted_boards, "columns": formatted_columns, "tasks": formatted_tasks, "sessions": formatted_sessions, } async def build_collab_sync( engine: OPCEngine, agent_store: AgentStore, chat_store: ChatStore, event_adapter: EventAdapter | None = None, *, exec_mode: str | None = None, ) -> dict[str, Any]: """Build full collab_sync response. Frontend collabSync.ts maps all snake_case → camelCase automatically. """ project_id = engine.project_id or "default" # Get OPC tasks tasks: list[Any] = [] if engine.store: try: tasks = await engine.store.get_tasks(project_id=project_id) except Exception: logger.warning("Failed to load tasks for collab_sync") company_board_tasks: list[dict[str, Any]] = [] company_board_columns: list[dict[str, Any]] = [] company_boards: list[dict[str, Any]] = [] company_projection_meta: dict[str, Any] | None = None if engine.store: try: company_board_tasks, company_board_columns, company_boards, company_projection_meta = await build_company_kanban_projection( engine, project_id=project_id, tasks=tasks, event_adapter=event_adapter, ) except Exception: logger.opt(exception=True).warning("Failed to build company-mode kanban projection") # Per-session DelegationWorkItem rollups produced alongside the company # kanban projection. Used by the work-item-driven Execution Progress # panel — keyed by ``task.session_id`` (matches the formatted_sessions # loop's ``session_id`` lookup). work_items_by_session_id: dict[str, list[Any]] = ( (company_projection_meta or {}).get("work_items_by_session_id", {}) or {} ) company_task_by_work_item_id: dict[str, Any] = ( (company_projection_meta or {}).get("task_by_work_item_id", {}) or {} ) # In company/custom mode the projection already creates one board per # primary session. If the projection returned nothing (no primary sessions # exist yet) we intentionally leave company_boards empty so the frontend # shows an empty-state prompt instead of a misleading project-wide board. # Reconcile CLI-generated session history into ChatStore before reading try: backfilled = await reconcile_sessions(engine, chat_store, tasks, project_id) if backfilled: logger.info(f"Reconciled {backfilled} total messages from engine → ChatStore") except Exception: logger.opt(exception=True).warning("Session reconciliation failed (non-fatal)") existing_task_ids = { str(getattr(task, "id", "") or "").strip() for task in tasks if str(getattr(task, "id", "") or "").strip() } # Get chat data (project-scoped) channels = await chat_store.get_channels(project_id) messages = await chat_store.get_messages(project_id) # Only check for pending checkpoints on tasks that are actively waiting # for user input (idle/running). Skipping done/failed/cancelled tasks # avoids O(n) engine calls that block collab_sync on large projects. _checkpoint_eligible_statuses = { "idle", "running", "blocked", "awaiting_manager_review", "awaiting_human", "awaiting_review", } for task in tasks: t_status = task.status.value if hasattr(task.status, "value") else str(task.status) if t_status not in _checkpoint_eligible_statuses: continue checkpoint_meta = await _build_snapshot_checkpoint_meta(engine, task) if not checkpoint_meta: continue if not _checkpoint_meta_targets_task(checkpoint_meta, task): continue channel_id = f"session:{getattr(task, 'id', '')}" _attach_or_create_checkpoint_card( messages, channel_id=channel_id, checkpoint_meta=checkpoint_meta, ) # Prune orphan/stale session channels and their messages. # A session channel is stale only when its task_id no longer exists in the DB. stale_session_channels: set[str] = set() for ch in channels: cid = ch["channel_id"] if not cid.startswith("session:"): continue tid = cid[len("session:"):] if tid not in existing_task_ids: stale_session_channels.add(cid) if stale_session_channels: channels = [ch for ch in channels if ch["channel_id"] not in stale_session_channels] messages = [msg for msg in messages if msg["channel_id"] not in stale_session_channels] # Persist: delete orphan channels and their messages from ui_state.db for stale_ch in stale_session_channels: try: await chat_store.delete_channel(stale_ch, project_id=project_id) except Exception: pass # Also prune activity messages whose metadata.task_id points to a dead task activity_channel = f"activity:{project_id}" dead_task_ids = { tid for ch_id in stale_session_channels if (tid := ch_id[len("session:"):]) } if dead_task_ids: messages = [ msg for msg in messages if not ( msg["channel_id"] == activity_channel and isinstance(msg.get("metadata"), dict) and msg["metadata"].get("task_id") in dead_task_ids ) ] # Persist: delete matching activity messages + progress from ui_state.db for tid in dead_task_ids: try: await chat_store.delete_activity_messages_for_task(project_id, tid) except Exception: pass try: await chat_store.delete_progress(tid, project_id=project_id) except Exception: pass # Format channels for collabSync.ts mapBackendChannel() formatted_channels = [ { "channel_id": ch["channel_id"], "channel_type": ch["type"], "name": ch["name"], "office_id": ch.get("office_id"), "participants": ch["participants"], "created_at": ch["created_at"], } for ch in channels ] # Format messages for collabSync.ts mapBackendMessage() formatted_messages = [] for msg in messages: sanitized = _sanitize_ui_message_dict(msg) formatted_messages.append({ "message_id": sanitized["message_id"], "channel_id": sanitized["channel_id"], "sender": sanitized["sender"], "sender_name": sanitized["sender_name"], "content": sanitized["content"], "created_at": sanitized["created_at"], "reply_to_id": sanitized.get("reply_to_id"), "mentions": sanitized.get("mentions", []), "metadata": sanitized.get("metadata", {}), }) # Board (one per project in task mode; one per session in company/org # mode). In company/org mode with no primary sessions # yet, leave the boards/columns empty so the frontend shows an # empty-state prompt instead of a stale project-wide board that would # fight the session-driven selection logic and cause a render loop. now = time.time() normalized_exec_mode = _normalize_session_exec_mode(exec_mode) has_company_sessions = any( _resolve_task_session_config(task)[0] in {"company", "org"} for task in tasks ) using_company_board = ( normalized_exec_mode in {"company", "org"} or has_company_sessions ) if using_company_board: formatted_boards = list(company_boards) formatted_columns = list(company_board_columns) if company_boards: task_counts_by_board: dict[str, int] = {} for task in company_board_tasks: board_id = str(task.get("board_id", "") or "").strip() if not board_id: continue task_counts_by_board[board_id] = task_counts_by_board.get(board_id, 0) + 1 for board in formatted_boards: board_id = str(board.get("board_id", "") or "").strip() board["next_task_num"] = int(task_counts_by_board.get(board_id, 0) or 0) + 1 else: formatted_boards = [ _build_board_payload( project_id, project_id if project_id != "default" else "Main Board", now=now, ) ] formatted_columns = build_board_columns(project_id, now=now) # Sessions — merged task+channel data for the sidebar session_channels = await chat_store.get_session_channels(project_id) session_channel_map = {ch["channel_id"]: ch for ch in session_channels} known_channel_ids = { str(ch.get("channel_id", "") or "").strip() for ch in channels if str(ch.get("channel_id", "") or "").strip() } persisted_task_ids = { str(getattr(task, "id", "") or "").strip() for task in tasks if str(getattr(task, "id", "") or "").strip() } repaired_session_channel_ids: list[str] = [] for task in tasks: task_id = str(getattr(task, "id", "") or "").strip() session_id = str(getattr(task, "session_id", "") or "").strip() if not task_id or not session_id: continue canonical_task_id = _task_mode_origin_ui_task_id(task, _task_metadata(task)) if canonical_task_id and canonical_task_id in persisted_task_ids: continue channel_id = f"session:{task_id}" if channel_id in session_channel_map: continue title = str(getattr(task, "title", "") or "Session").strip() or "Session" try: repaired_channel = await chat_store.create_session_channel( task_id, title, project_id=project_id, ) except Exception: created_at = getattr(task, "created_at", None) repaired_channel = { "channel_id": channel_id, "type": "session", "name": title, "office_id": None, "participants": ["user"], "created_at": ( created_at.timestamp() if hasattr(created_at, "timestamp") else time.time() ), } session_channel_map[channel_id] = repaired_channel repaired_session_channel_ids.append(channel_id) if channel_id not in known_channel_ids: channels.append(repaired_channel) known_channel_ids.add(channel_id) session_channel_stats = await chat_store.get_channel_stats( list(session_channel_map.keys()), project_id=project_id, ) if repaired_session_channel_ids: logger.info( "Recovered %d missing session channel(s) for project %s", len(repaired_session_channel_ids), project_id, ) progress_by_task = await chat_store.get_progress_many( [ str(getattr(task, "id", "") or "").strip() for task in tasks if str(getattr(task, "id", "") or "").strip() ], project_id=project_id, ) # Tasks — keep cancelled rows in session state, but exclude them from # task-mode kanban cards. Missing session channels are repaired above so # valid work-item projections remain visible in historical and live UI. # Kanban-push review Tasks are runtime-internal scheduling units (they # exist only to drive manager review turns) and must never surface on # the kanban board or the sessions sidebar — the user already sees the # child work item's card move into the "In Review" column. all_task_meta_map = { str(getattr(t, "id", "") or ""): _task_metadata(t) for t in tasks } session_tasks = [ t for t in tasks if not bool(all_task_meta_map.get(str(getattr(t, "id", "") or ""), {}).get("review_task", False)) and not ( (canonical_id := _task_mode_origin_ui_task_id( t, all_task_meta_map.get(str(getattr(t, "id", "") or ""), {}), )) and canonical_id in persisted_task_ids ) ] active_tasks = [ t for t in session_tasks if (t.status.value if hasattr(t.status, "value") else str(t.status)) != "cancelled" ] task_meta_map = {t.id: _task_metadata(t) for t in session_tasks} primary_tasks_by_session_id, _ = _primary_session_tasks_by_session_id( session_tasks, task_meta_map=task_meta_map, ) shared_identity_tasks_by_session_id = _shared_role_identity_tasks_by_session_id( session_tasks, task_meta_map=task_meta_map, ) child_tasks_by_parent: dict[str, list[Any]] = {} for task in session_tasks: task_meta = task_meta_map.get(task.id, {}) parent_link = _task_parent_session_link(task, task_meta) if parent_link: child_tasks_by_parent.setdefault(parent_link, []).append(task) formatted_tasks = ( company_board_tasks if using_company_board else [task_to_kanban(t, i + 1, event_adapter) for i, t in enumerate(active_tasks)] ) formatted_sessions = [] default_context_window = None llm_provider = getattr(engine, "llm", None) if llm_provider is not None: try: default_context_window = llm_provider.get_context_window() except Exception: default_context_window = None runtime_control_by_task = await _build_company_runtime_control_by_task( engine, session_tasks, project_id, ) # Walk `session_tasks` (review_task already filtered out above) instead # of the raw `tasks` list, so stopped/cancelled child sessions remain # visible after refresh while review tasks stay hidden. Review tasks are runtime # scheduling artefacts for manager review turns — a single executed # work item can spawn N of them, each with its own Task row and session # channel. Showing them as separate "turns" in the execution panel # makes every role look like it did 3-5x the work it actually did. for t in session_tasks: channel_id = f"session:{t.id}" ch = session_channel_map.get(channel_id) # Only include tasks that have an actual session channel (created via chat) if not ch: continue t_meta = task_meta_map.get(t.id, _task_metadata(t)) session_id = str(getattr(t, "session_id", "") or "").strip() representative_task = primary_tasks_by_session_id.get(session_id) representative_task_id = str(getattr(representative_task, "id", "") or "").strip() shared_session_id = _shared_role_session_key(t, t_meta) if shared_session_id and representative_task_id and representative_task_id != str(getattr(t, "id", "") or "").strip(): continue identity_task = t identity_meta = t_meta shared_identity_task = shared_identity_tasks_by_session_id.get(session_id) shared_identity_task_id = str(getattr(shared_identity_task, "id", "") or "").strip() if shared_identity_task_id and shared_identity_task_id != str(getattr(t, "id", "") or "").strip(): identity_task = shared_identity_task identity_meta = task_meta_map.get(shared_identity_task_id, _task_metadata(shared_identity_task)) status_val = t.status.value if hasattr(t.status, "value") else str(t.status) channel_stats = session_channel_stats.get(channel_id, {}) msg_count = int(channel_stats.get("message_count", 0) or 0) tracker_data: dict[str, Any] = {} if event_adapter and getattr(identity_task, "assigned_to", ""): agent_id = event_adapter._resolve_role_to_agent(identity_task.assigned_to) tracker = event_adapter.get_tracker(agent_id) if tracker: tracker_data = { "agent_status": tracker.state.value, "current_tool": tracker.current_tool, } # Determine parent linkage and mode parent_sid = _task_parent_session_link(t, t_meta) or None session_mode = "child" if parent_sid else "primary" exec_mode_val, company_profile_val = _resolve_task_session_config(t) preferred_agent_val = _resolve_task_preferred_agent(t) selected_execution_agent_val = _resolve_task_selected_execution_agent(identity_task) _created_ts = t.created_at.timestamp() if hasattr(t.created_at, "timestamp") else time.time() _updated_ts = _created_ts if ch and msg_count > 0: latest_ts = channel_stats.get("latest_timestamp") if latest_ts: _updated_ts = float(latest_ts) session_id = str(getattr(t, "session_id", "") or "").strip() child_keys = {t.id} if session_id: child_keys.add(session_id) child_tasks: list[Any] = [] seen_child_ids: set[str] = set() for key in child_keys: for child in child_tasks_by_parent.get(key, []): child_id = str(getattr(child, "id", "") or "") if not child_id or child_id == t.id or child_id in seen_child_ids: continue seen_child_ids.add(child_id) child_tasks.append(child) identity_task_id = str(getattr(identity_task, "id", "") or "").strip() company_profile = t_meta.get("company_profile") or identity_meta.get("company_profile") if not company_profile and session_mode == "primary": for child in child_tasks: child_meta = task_meta_map.get(child.id, {}) inherited_profile = child_meta.get("company_profile") if inherited_profile: company_profile = inherited_profile break is_task_mode_runtime = _metadata_is_task_mode_runtime({**t_meta, **identity_meta}) progress_log = list(progress_by_task.get(t.id, [])) if identity_task_id and identity_task_id != str(getattr(t, "id", "") or "").strip(): for entry in progress_by_task.get(identity_task_id, []): if entry not in progress_log: progress_log.append(entry) progress_log.sort(key=lambda item: float(item.get("timestamp") or 0.0)) if is_task_mode_runtime: filtered_progress: list[dict[str, Any]] = [] visible_task_mode_progress = {"thinking", "tool_call", "autonomy", "needs_input", "work_item_failed"} for raw_entry in progress_log: entry = dict(raw_entry or {}) entry_type = str(entry.get("type", "") or "").strip() if entry_type not in visible_task_mode_progress: continue if str(entry.get("work_item_projection_id", "") or "").strip() == "task_mode_execution": entry.pop("work_item_projection_id", None) entry.pop("work_item_turn_type", None) entry.pop("work_item_projection_title", None) entry.pop("is_company_runtime", None) if entry_type == "thinking": entry["summary"] = "Thinking" filtered_progress.append(entry) progress_log = filtered_progress work_item_child_tasks = list(child_tasks) if identity_task_id and identity_task_id != str(getattr(t, "id", "") or "").strip(): if all(str(getattr(child, "id", "") or "").strip() != identity_task_id for child in work_item_child_tasks): work_item_child_tasks.insert(0, identity_task) work_item_log = _build_session_work_item_log( t, task_meta=t_meta, child_tasks=work_item_child_tasks, task_meta_map=task_meta_map, progress_by_task=progress_by_task, ) if is_task_mode_runtime: work_item_log = [] # Work-item-driven rollup for the chat "Execution Progress" panel. # Only meaningful for primary company-mode sessions (children share # the parent's work-item set through the parent's payload). role_work_items_payload: dict[str, Any] | None = None executor_role_work_items_payload: dict[str, Any] | None = None if session_mode == "primary" and not is_task_mode_runtime: session_work_items = work_items_by_session_id.get(session_id, []) if session_work_items: role_work_items_payload = _build_role_work_items_for_session( work_items=session_work_items, task_by_work_item_id=company_task_by_work_item_id, event_adapter=event_adapter, progress_by_task=progress_by_task, ) if not role_work_items_payload: role_work_items_payload = None executor_role_work_items_payload = _build_executor_role_work_items_for_session( work_items=session_work_items, task_by_work_item_id=company_task_by_work_item_id, event_adapter=event_adapter, progress_by_task=progress_by_task, ) if not executor_role_work_items_payload: executor_role_work_items_payload = None handoff_context = ( _extract_work_item_summary_for_downstream(t_meta) or _extract_work_item_summary_for_downstream(identity_meta) ) if is_task_mode_runtime: handoff_context = "" runtime_meta = { **dict(t_meta.get("runtime_v2", {}) or {}), **dict(identity_meta.get("runtime_v2", {}) or {}), } member_session_meta = { **dict(t_meta.get("member_session_state", {}) or {}), **dict(identity_meta.get("member_session_state", {}) or {}), } runtime_control_meta = runtime_control_by_task.get(str(getattr(t, "id", "") or "").strip(), {}) employee_assignment = { **dict(t_meta.get("employee_assignment", {}) or {}), **dict(identity_meta.get("employee_assignment", {}) or {}), } identity_payload = {} if is_task_mode_runtime else work_item_identity_payload_from_metadata(identity_meta) fallback_identity_payload = {} if is_task_mode_runtime else work_item_identity_payload_from_metadata(t_meta) if not is_task_mode_runtime: if WORK_ITEM_PROJECTION_ID_KEY not in identity_payload: projection_value = fallback_identity_payload.get(WORK_ITEM_PROJECTION_ID_KEY) if projection_value: identity_payload[WORK_ITEM_PROJECTION_ID_KEY] = projection_value if WORK_ITEM_TURN_TYPE_KEY not in identity_payload: turn_value = fallback_identity_payload.get(WORK_ITEM_TURN_TYPE_KEY) if turn_value: identity_payload[WORK_ITEM_TURN_TYPE_KEY] = turn_value # Defer expensive transcript-based context preview to session_detail. # Loading full transcripts for every child task blocks collab_sync # and makes large projects unresponsive on click. runtime_task_id = str(getattr(t, "id", "") or "").strip() org_id = ( t_meta.get("org_id") or t_meta.get("organization_id") or identity_meta.get("org_id") or identity_meta.get("organization_id") ) exec_mode_val, resolved_company_profile, org_id = _canonical_session_identity( exec_mode_val, company_profile or company_profile_val, org_id, ) formatted_sessions.append({ "project_id": project_id, "task_id": t.id, **_execution_turn_alias_payload(runtime_task_id), "session_id": t.session_id, "parent_session_id": parent_sid, "mode": session_mode, "exec_mode": exec_mode_val, "company_profile": resolved_company_profile, "org_id": org_id, "preferred_agent": preferred_agent_val, "selected_execution_agent": selected_execution_agent_val, "channel_id": channel_id, "title": t.title, "status": status_val, "column_id": STATUS_TO_COLUMN.get(status_val, "todo"), # Convert opc_role_id → UI agent_id for frontend matching "assignee_ids": [event_adapter._resolve_role_to_agent(identity_task.assigned_to)] if event_adapter and getattr(identity_task, "assigned_to", "") else ([identity_task.assigned_to] if getattr(identity_task, "assigned_to", "") else []), "priority": priority_to_label(t.priority) if isinstance(t.priority, int) else None, "tags": t.tags or [], "created_at": _created_ts, "updated_at": _updated_ts, "message_count": msg_count, # Company Mode metadata **identity_payload, "company_profile": resolved_company_profile, **({} if is_task_mode_runtime else _work_item_role_payload(identity_task)), "work_item_gate": None if is_task_mode_runtime else (identity_meta.get("work_item_gate") or t_meta.get("work_item_gate")), "employee_assignment": employee_assignment or None, "origin_channel": identity_meta.get("origin_channel") or t_meta.get("origin_channel"), "origin_task_id": t_meta.get("origin_task_id") or t.id, # Progress + handoff (restored on page refresh) "progress_log": progress_log, "work_item_log": work_item_log, # Per-role DelegationWorkItem rollup. The frontend uses this to # drive the Execution Progress panel (1 row = 1 work item), # superseding the older Task-keyed grouping that mistook # runtime turns for work-item rows. "role_work_items": role_work_items_payload, # Display-only executor rollup. Execution Progress uses this so # original worker chips stay visible while work waits on review. "executor_role_work_items": executor_role_work_items_payload, "handoff_context": handoff_context, "is_company_runtime": bool( not is_task_mode_runtime and session_mode == "primary" and ( company_profile or child_tasks or (identity_task_id and identity_task_id != str(getattr(t, "id", "") or "").strip()) or work_item_log ) ), **runtime_control_meta, "artifacts": identity_meta.get("artifacts") or t_meta.get("artifacts"), "runtime_session_id": runtime_meta.get("runtime_session_id"), "resume_cursor": runtime_meta.get("resume_cursor"), "active_subagents": list(runtime_meta.get("active_subagents", []) or []), "permission_requests": list(runtime_meta.get("permission_requests", []) or []), "worktree_path": runtime_meta.get("worktree_path"), "context_tokens": runtime_meta.get("context_tokens"), "context_window": runtime_meta.get("context_window") or default_context_window, "context_remaining_pct": runtime_meta.get("context_remaining_pct"), "input_tokens": runtime_meta.get("input_tokens"), "output_tokens": runtime_meta.get("output_tokens"), "total_tokens": runtime_meta.get("total_tokens"), "turn_cost_usd": runtime_meta.get("turn_cost_usd"), "session_cost_usd": runtime_meta.get("session_cost_usd"), "pending_permission_count": runtime_meta.get("pending_permission_count"), "drain_mode": runtime_meta.get("drain_mode"), "resident_status": _runtime_status_from_member_meta(member_session_meta), "actionable_inbox_count": member_session_meta.get("actionable_inbox_count"), "protocol_backlog_count": member_session_meta.get("protocol_backlog_count"), "notification_backlog_count": member_session_meta.get("notification_backlog_count"), "latest_notification": member_session_meta.get("latest_notification"), # Fix 5 PR7: queue depth surfaces "CTO has 2 tasks queued" # behind the focused one when the serial-queue flag is on. "pending_queue_depth": int( member_session_meta.get("pending_queue_depth", 0) or 0 ), "pending_work_item_ids": list( member_session_meta.get("pending_work_item_ids", []) or [] ), **tracker_data, }) return { "ok": True, "project_id": project_id, "sync_scope": "full", "channels": formatted_channels, "messages": formatted_messages, "boards": formatted_boards, "columns": formatted_columns, "tasks": formatted_tasks, "sessions": formatted_sessions, }