Files
OpenOPC/opc/plugins/cli_board/state/models.py
T
2026-07-01 17:56:31 +08:00

260 lines
7.5 KiB
Python

"""State models used by the OpenOPC CLI board."""
from __future__ import annotations
import time
from dataclasses import dataclass, field
from typing import Any, Literal
PaneFocus = Literal["session-rail", "main", "context"]
ViewMode = Literal["kanban", "list", "focus", "pipeline", "org"]
ContextTab = Literal["detail", "session", "activity"]
DensityMode = Literal["compact", "comfortable"]
@dataclass
class PendingCheckpointView:
checkpoint_id: str
checkpoint_type: str
status: str
session_id: str | None
task_id: str | None
summary: str
prompt: str
payload: dict[str, Any] = field(default_factory=dict)
@property
def short_label(self) -> str:
return f"{self.checkpoint_type} ({self.status})"
@dataclass
class LinkedExecutionView:
task_id: str
title: str
status: str
assigned_to: str
session_id: str | None
created_at: float
updated_at: float
runtime_task_id: str | None = None
execution_turn_id: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass
class SessionMessageView:
message_id: str
role: str
sender_name: str
content: str
created_at: float
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass
class RuntimeTaskState:
status: str = "idle"
current_tool: str | None = None
iteration: int = 0
tool_elapsed_ms: int = 0
last_tool_summary: str = ""
context_tokens: int = 0
context_window: int = 0
context_remaining_pct: int = 0
turn_cost_usd: float = 0.0
session_cost_usd: float = 0.0
pending_permission_count: int = 0
drain_mode: str = "idle"
progress_entries: list[str] = field(default_factory=list)
updated_at: float = field(default_factory=time.time)
def push_progress(self, text: str, *, max_entries: int = 50) -> None:
entry = str(text or "").strip()
if not entry:
return
self.progress_entries.append(entry)
if len(self.progress_entries) > max_entries:
self.progress_entries = self.progress_entries[-max_entries:]
self.updated_at = time.time()
@dataclass
class BoardAlert:
alert_id: str
level: str
title: str
message: str
task_id: str | None = None
created_at: float = field(default_factory=time.time)
@dataclass
class SessionSummaryView:
task_id: str
title: str
status: str
column_id: str
session_id: str | None
updated_at: float
created_at: float
assigned_to: str = ""
priority: str | None = None
pending_checkpoint: bool = False
linked_task_count: int = 0
tags: list[str] = field(default_factory=list)
runtime_task_id: str | None = None
execution_turn_id: str | None = None
@dataclass
class BoardMetrics:
total_tasks: int = 0
visible_tasks: int = 0
filtered_tasks: int = 0
hidden_task_count: int = 0
todo_count: int = 0
in_progress_count: int = 0
in_review_count: int = 0
done_count: int = 0
running_count: int = 0
blocked_count: int = 0
failed_count: int = 0
pending_checkpoint_count: int = 0
active_session_count: int = 0
stale_task_count: int = 0
alert_count: int = 0
last_refreshed_at: float = field(default_factory=time.time)
last_runtime_update: float | None = None
@dataclass
class BoardTaskView:
task_id: str
title: str
description: str
status: str
column_id: str
priority: str | None
assignee_ids: list[str] = field(default_factory=list)
assigned_to: str = ""
tags: list[str] = field(default_factory=list)
session_id: str | None = None
created_at: float = 0.0
updated_at: float = 0.0
metadata: dict[str, Any] = field(default_factory=dict)
pending_checkpoint: PendingCheckpointView | None = None
linked_task_count: int = 0
result_content: str | None = None
artifacts: list[Any] = field(default_factory=list)
origin_task_id: str | None = None
display_id: str = ""
dependencies: list[str] = field(default_factory=list)
# Company-mode fields: card identity is the DelegationWorkItem.
# `task_id` carries `work_item_id` in company mode; the runtime Task and its
# session are referenced via runtime_task_id / execution_turn_id aliases.
work_item_id: str | None = None
runtime_task_id: str | None = None
execution_turn_id: str | None = None
@property
def is_terminal(self) -> bool:
return self.status in {"done", "failed", "cancelled"}
@dataclass
class TaskDetailView:
task: BoardTaskView
transcript: list[SessionMessageView] = field(default_factory=list)
linked_executions: list[LinkedExecutionView] = field(default_factory=list)
progress_entries: list[str] = field(default_factory=list)
pending_checkpoint: PendingCheckpointView | None = None
result_content: str | None = None
artifacts: list[Any] = field(default_factory=list)
context_preview: str | None = None
@dataclass
class OrgRoleView:
"""One role in the org tree."""
role_id: str
name: str
responsibility: str
reports_to: str = "owner"
employee_count: int = 0
children: list[OrgRoleView] = field(default_factory=list)
@dataclass
class OrgEmployeeView:
"""One employee summary."""
employee_id: str
name: str
role_id: str
category: str = ""
domains: list[str] = field(default_factory=list)
seniority: str = "junior"
@dataclass
class OrgSnapshotView:
"""Read-only snapshot of org structure."""
role_tree: list[OrgRoleView] = field(default_factory=list)
employees: list[OrgEmployeeView] = field(default_factory=list)
company_profile: str = ""
role_count: int = 0
employee_count: int = 0
@dataclass
class PipelineWorkItemView:
"""One work-item projection in a company-mode runtime pipeline."""
projection_id: str
title: str
role_id: str
status: str = "pending" # pending | running | done | failed | cancelled | blocked
assigned_to: str = ""
task_id: str | None = None
runtime_task_id: str | None = None
execution_turn_id: str | None = None
session_id: str | None = None
elapsed_sec: float = 0.0
current_tool: str | None = None
tool_elapsed_ms: int = 0
last_tool_summary: str = ""
context_remaining_pct: int = 0
turn_cost_usd: float = 0.0
has_gate: bool = False
gate_type: str | None = None # review | approval | human_confirmation
dependencies: list[str] = field(default_factory=list)
parallel_group: str | None = None
@dataclass
class PipelineSnapshot:
"""Full pipeline state for the selected company-mode runtime."""
parent_task_id: str = ""
parent_title: str = ""
profile: str = ""
work_items: list[PipelineWorkItemView] = field(default_factory=list)
done_count: int = 0
total_count: int = 0
elapsed_sec: float = 0.0
@dataclass
class BoardSnapshot:
project_id: str
tasks: list[BoardTaskView] = field(default_factory=list)
hidden_task_count: int = 0
pending_checkpoint_count: int = 0
last_refreshed_at: float = field(default_factory=time.time)
session_summaries: list[SessionSummaryView] = field(default_factory=list)
alerts: list[BoardAlert] = field(default_factory=list)
metrics: BoardMetrics = field(default_factory=BoardMetrics)
# "standard" → cards are runtime Tasks; "company" → cards are DelegationWorkItems.
mode: str = "standard"
# When set, BoardStateStore adopts this column order (lets company mode
# surface the in-review column without hardcoding it client-side).
column_order: list[str] | None = None