"""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, Literal, TYPE_CHECKING from loguru import logger from opc.core.models import normalize_role_runtime_status 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 TranscriptDetailLevel = Literal["summary", "full"] _FULL_DETAIL_ONLY_TRANSCRIPT_KINDS: frozenset[str] = frozenset({ "runtime_v2_user_turn", "runtime_v2_intermediate_assistant", "runtime_v2_company_assistant", "runtime_v2_tool_output", }) _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: normalized = str(value or "").strip().lower() if normalized == "full": return "full" return "summary" 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 {}) kind = str(metadata.get("kind", "") or "").strip() if metadata.get("company_final_turn"): # The role's final reply of a company turn is the user-visible result # (intake/aggregate turns have no engine-recorded result surface, so # hiding this would drop the reply from the chat entirely). return False return detail_level != "full" and kind in _FULL_DETAIL_ONLY_TRANSCRIPT_KINDS 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]]: 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, }