260 lines
7.5 KiB
Python
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
|