shikiru-cli 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shikiru/__init__.py +1 -0
- shikiru/__main__.py +8 -0
- shikiru/agents/__init__.py +3 -0
- shikiru/agents/contracts.py +590 -0
- shikiru/agents/registry.py +87 -0
- shikiru/agents/sdk/__init__.py +17 -0
- shikiru/agents/sdk/reset_time.py +137 -0
- shikiru/agents/sdk/turn.py +335 -0
- shikiru/agents/settings.py +151 -0
- shikiru/agents/testing/__init__.py +26 -0
- shikiru/agents/testing/contract_suite.py +653 -0
- shikiru/agents/testing/resume.py +15 -0
- shikiru/bootstrap.py +1277 -0
- shikiru/cli/__init__.py +1 -0
- shikiru/cli/app.py +770 -0
- shikiru/cli/context.py +63 -0
- shikiru/cli/render.py +1475 -0
- shikiru/cli/report.py +156 -0
- shikiru/cli/session_prompt.py +63 -0
- shikiru/cli/view.py +131 -0
- shikiru/clock.py +41 -0
- shikiru/config/__init__.py +8 -0
- shikiru/config/defaults.toml +131 -0
- shikiru/config/loader.py +178 -0
- shikiru/config/settings.py +392 -0
- shikiru/dashboard/__init__.py +3 -0
- shikiru/dashboard/actions_list.py +799 -0
- shikiru/dashboard/activity.py +150 -0
- shikiru/dashboard/app.py +1765 -0
- shikiru/dashboard/blocker_evidence.py +122 -0
- shikiru/dashboard/blocker_rows.py +84 -0
- shikiru/dashboard/briefing_view.py +871 -0
- shikiru/dashboard/contract.py +1086 -0
- shikiru/dashboard/covered.py +32 -0
- shikiru/dashboard/detail.py +1209 -0
- shikiru/dashboard/detail_fold.py +244 -0
- shikiru/dashboard/detail_hint.py +68 -0
- shikiru/dashboard/details_list_view.py +150 -0
- shikiru/dashboard/diagnostics.py +80 -0
- shikiru/dashboard/diagnostics_sections.py +186 -0
- shikiru/dashboard/evidence_table.py +123 -0
- shikiru/dashboard/extra_details.py +214 -0
- shikiru/dashboard/fault_guidance.py +216 -0
- shikiru/dashboard/fold_bodies.py +276 -0
- shikiru/dashboard/help_keys.py +166 -0
- shikiru/dashboard/help_screen.py +204 -0
- shikiru/dashboard/interaction.py +17 -0
- shikiru/dashboard/ladder_editor.py +544 -0
- shikiru/dashboard/live_settings.py +62 -0
- shikiru/dashboard/motion.py +368 -0
- shikiru/dashboard/orchestration_fields.py +201 -0
- shikiru/dashboard/palette.py +1606 -0
- shikiru/dashboard/palette_app.py +89 -0
- shikiru/dashboard/palette_drafts.py +33 -0
- shikiru/dashboard/resolution.py +573 -0
- shikiru/dashboard/resolution_view.py +302 -0
- shikiru/dashboard/row_list.py +256 -0
- shikiru/dashboard/section_rows.py +173 -0
- shikiru/dashboard/services.py +665 -0
- shikiru/dashboard/session_history.py +402 -0
- shikiru/dashboard/shikiru_init_ascii_art.txt +40 -0
- shikiru/dashboard/task_picker.py +358 -0
- shikiru/dashboard/tick.py +59 -0
- shikiru/dashboard/timeline_filter.py +129 -0
- shikiru/dashboard/timeline_section.py +385 -0
- shikiru/dashboard/usage_body.py +269 -0
- shikiru/dashboard/views.py +2314 -0
- shikiru/dashboard/wizard.py +1389 -0
- shikiru/domain/__init__.py +1 -0
- shikiru/domain/acceptance.py +130 -0
- shikiru/domain/activity.py +28 -0
- shikiru/domain/attempts.py +333 -0
- shikiru/domain/briefing.py +257 -0
- shikiru/domain/commands.py +219 -0
- shikiru/domain/decisions.py +231 -0
- shikiru/domain/docker.py +262 -0
- shikiru/domain/engine_uptime.py +87 -0
- shikiru/domain/escalation.py +194 -0
- shikiru/domain/escalation_evidence.py +186 -0
- shikiru/domain/escalation_panel.py +144 -0
- shikiru/domain/events.py +71 -0
- shikiru/domain/evidence_export.py +89 -0
- shikiru/domain/faults.py +86 -0
- shikiru/domain/feedback.py +180 -0
- shikiru/domain/feedback_rounds.py +172 -0
- shikiru/domain/fencing.py +31 -0
- shikiru/domain/ladder.py +102 -0
- shikiru/domain/ladder_text.py +44 -0
- shikiru/domain/levels.py +44 -0
- shikiru/domain/motion.py +199 -0
- shikiru/domain/packet_evidence.py +377 -0
- shikiru/domain/pushes.py +47 -0
- shikiru/domain/quota_blocks.py +44 -0
- shikiru/domain/quota_waits.py +219 -0
- shikiru/domain/recovery.py +194 -0
- shikiru/domain/reports.py +498 -0
- shikiru/domain/results.py +217 -0
- shikiru/domain/runs.py +85 -0
- shikiru/domain/scheduling.py +231 -0
- shikiru/domain/status_tasks.py +40 -0
- shikiru/domain/steps.py +325 -0
- shikiru/domain/tasks.py +174 -0
- shikiru/domain/timeline.py +208 -0
- shikiru/domain/timeline_phrases.py +271 -0
- shikiru/domain/versions.py +18 -0
- shikiru/domain/workflow.py +372 -0
- shikiru/engine/__init__.py +0 -0
- shikiru/engine/acceptance.py +90 -0
- shikiru/engine/agents.py +61 -0
- shikiru/engine/cosmetic_decorator.py +99 -0
- shikiru/engine/daemon.py +409 -0
- shikiru/engine/dependency.py +37 -0
- shikiru/engine/escalation_filing.py +110 -0
- shikiru/engine/escalations.py +401 -0
- shikiru/engine/events.py +285 -0
- shikiru/engine/evidence.py +81 -0
- shikiru/engine/fault_reporter.py +144 -0
- shikiru/engine/feedback.py +577 -0
- shikiru/engine/feedback_audit.py +80 -0
- shikiru/engine/feedback_intake.py +95 -0
- shikiru/engine/feedback_snapshot.py +148 -0
- shikiru/engine/flow.py +87 -0
- shikiru/engine/handlers/__init__.py +33 -0
- shikiru/engine/handlers/base.py +148 -0
- shikiru/engine/handlers/engine.py +24 -0
- shikiru/engine/handlers/escalations.py +174 -0
- shikiru/engine/handlers/operator.py +81 -0
- shikiru/engine/handlers/tasks.py +237 -0
- shikiru/engine/history.py +128 -0
- shikiru/engine/integration.py +1234 -0
- shikiru/engine/integration_turns.py +337 -0
- shikiru/engine/layout_guard.py +362 -0
- shikiru/engine/lifecycle.py +60 -0
- shikiru/engine/monitor.py +317 -0
- shikiru/engine/notification_delivery.py +124 -0
- shikiru/engine/notification_feed.py +57 -0
- shikiru/engine/notifier.py +91 -0
- shikiru/engine/pull_request_closing.py +117 -0
- shikiru/engine/push_service.py +177 -0
- shikiru/engine/quota_probe_threads.py +79 -0
- shikiru/engine/quota_reconciler.py +210 -0
- shikiru/engine/quota_schedule.py +24 -0
- shikiru/engine/quota_waits.py +102 -0
- shikiru/engine/reconcile.py +91 -0
- shikiru/engine/recovery.py +284 -0
- shikiru/engine/retention.py +102 -0
- shikiru/engine/root_preparation.py +86 -0
- shikiru/engine/scheduler.py +320 -0
- shikiru/engine/session_guard.py +46 -0
- shikiru/engine/sessions.py +95 -0
- shikiru/engine/signalled.py +75 -0
- shikiru/engine/step_runner.py +140 -0
- shikiru/engine/supervisor.py +101 -0
- shikiru/engine/takeover.py +223 -0
- shikiru/engine/task_facts.py +46 -0
- shikiru/engine/tick_guard.py +95 -0
- shikiru/engine/titles.py +297 -0
- shikiru/engine/transitions.py +79 -0
- shikiru/engine/turn_context.py +206 -0
- shikiru/engine/turn_execution.py +30 -0
- shikiru/engine/turn_loop.py +230 -0
- shikiru/engine/turn_processes.py +79 -0
- shikiru/engine/turns.py +457 -0
- shikiru/errors.py +144 -0
- shikiru/helper/__init__.py +3 -0
- shikiru/helper/helper.py +182 -0
- shikiru/helper/models.py +11 -0
- shikiru/mcp/__init__.py +1 -0
- shikiru/mcp/server.py +196 -0
- shikiru/operator/__init__.py +33 -0
- shikiru/operator/attachment.py +24 -0
- shikiru/operator/commands.py +319 -0
- shikiru/operator/detectors.py +374 -0
- shikiru/operator/doctor.py +351 -0
- shikiru/operator/events.py +57 -0
- shikiru/operator/evidence_export.py +498 -0
- shikiru/operator/evidence_writer.py +78 -0
- shikiru/operator/lifecycle.py +294 -0
- shikiru/operator/plugin_checks.py +194 -0
- shikiru/operator/probe.py +287 -0
- shikiru/operator/queries.py +1601 -0
- shikiru/operator/services.py +36 -0
- shikiru/operator/sessions.py +378 -0
- shikiru/operator/strip_views.py +162 -0
- shikiru/operator/timeline.py +416 -0
- shikiru/operator/toml_text.py +62 -0
- shikiru/palette.py +18 -0
- shikiru/paths.py +91 -0
- shikiru/plugins/__init__.py +0 -0
- shikiru/plugins/agents/__init__.py +1 -0
- shikiru/plugins/agents/claude/__init__.py +5 -0
- shikiru/plugins/agents/claude/agent.py +251 -0
- shikiru/plugins/agents/claude/context.py +116 -0
- shikiru/plugins/agents/claude/headless.py +92 -0
- shikiru/plugins/agents/claude/hook.py +57 -0
- shikiru/plugins/agents/claude/model_catalog.py +105 -0
- shikiru/plugins/agents/claude/options.py +62 -0
- shikiru/plugins/agents/claude/turn.py +1018 -0
- shikiru/plugins/agents/codex/__init__.py +5 -0
- shikiru/plugins/agents/codex/agent.py +486 -0
- shikiru/plugins/agents/codex/headless.py +166 -0
- shikiru/plugins/agents/codex/home.py +14 -0
- shikiru/plugins/agents/codex/model_catalog.py +128 -0
- shikiru/plugins/agents/codex/options.py +94 -0
- shikiru/plugins/agents/codex/quota.py +195 -0
- shikiru/plugins/agents/codex/sandbox.py +31 -0
- shikiru/plugins/agents/codex/sandbox_probe.py +97 -0
- shikiru/plugins/agents/codex/turn.py +767 -0
- shikiru/plugins/agents/codex/usage.py +106 -0
- shikiru/plugins/agents/shared_settings.py +62 -0
- shikiru/plugins/widgets/__init__.py +3 -0
- shikiru/plugins/widgets/dialogue.py +144 -0
- shikiru/plugins/widgets/disk.py +123 -0
- shikiru/plugins/widgets/docker.py +225 -0
- shikiru/plugins/widgets/escalations.py +402 -0
- shikiru/plugins/widgets/faults.py +204 -0
- shikiru/plugins/widgets/header.py +321 -0
- shikiru/plugins/widgets/queue.py +1140 -0
- shikiru/plugins/widgets/queue_hint.py +93 -0
- shikiru/plugins/widgets/resources.py +105 -0
- shikiru/plugins/widgets/strips.py +82 -0
- shikiru/plugins/widgets/usage.py +176 -0
- shikiru/private.py +94 -0
- shikiru/process.py +535 -0
- shikiru/prompts/__init__.py +3 -0
- shikiru/prompts/renderer.py +85 -0
- shikiru/prompts/templates/_base.md.j2 +17 -0
- shikiru/prompts/templates/_macros.md.j2 +61 -0
- shikiru/prompts/templates/conflicts.md.j2 +22 -0
- shikiru/prompts/templates/consult.md.j2 +5 -0
- shikiru/prompts/templates/continue.md.j2 +4 -0
- shikiru/prompts/templates/feedback_analysis.md.j2 +29 -0
- shikiru/prompts/templates/feedback_rework.md.j2 +33 -0
- shikiru/prompts/templates/feedback_verify.md.j2 +33 -0
- shikiru/prompts/templates/handoff.md.j2 +23 -0
- shikiru/prompts/templates/implement.md.j2 +13 -0
- shikiru/prompts/templates/plan.md.j2 +30 -0
- shikiru/prompts/templates/rework.md.j2 +13 -0
- shikiru/prompts/templates/triage.md.j2 +20 -0
- shikiru/prompts/templates/verify.md.j2 +29 -0
- shikiru/store/__init__.py +1 -0
- shikiru/store/commands.py +242 -0
- shikiru/store/database.py +297 -0
- shikiru/store/escalations.py +240 -0
- shikiru/store/events.py +213 -0
- shikiru/store/feedback.py +266 -0
- shikiru/store/migrations/001_initial.sql +63 -0
- shikiru/store/migrations/002_headless_turns.sql +31 -0
- shikiru/store/migrations/003_drop_command_handled_at.sql +1 -0
- shikiru/store/migrations/004_repo_base_ref.sql +3 -0
- shikiru/store/migrations/005_complexity_explicit.sql +3 -0
- shikiru/store/migrations/006_events_kind_index.sql +1 -0
- shikiru/store/migrations/007_notification_cursor.sql +1 -0
- shikiru/store/migrations/008_reported_usage.sql +1 -0
- shikiru/store/migrations/009_billing_declarations.sql +2 -0
- shikiru/store/migrations/010_canonical_quota_windows.sql +3 -0
- shikiru/store/migrations/012_drop_task_quota_evidence.sql +2 -0
- shikiru/store/migrations/013_push_targets.sql +7 -0
- shikiru/store/migrations/014_agent_block_time.sql +6 -0
- shikiru/store/migrations/015_repair_orphan_agent_blocks.sql +3 -0
- shikiru/store/migrations/016_feedback_rounds.sql +29 -0
- shikiru/store/migrations/017_feedback_round_comments.sql +5 -0
- shikiru/store/migrations/__init__.py +1 -0
- shikiru/store/notification_cursor.py +29 -0
- shikiru/store/pushes.py +117 -0
- shikiru/store/quotas.py +153 -0
- shikiru/store/reader.py +310 -0
- shikiru/store/retention.py +92 -0
- shikiru/store/runs.py +227 -0
- shikiru/store/tasks.py +949 -0
- shikiru/telemetry/__init__.py +1 -0
- shikiru/telemetry/disk.py +167 -0
- shikiru/telemetry/docker.py +164 -0
- shikiru/telemetry/resources.py +296 -0
- shikiru/tmux/__init__.py +3 -0
- shikiru/tmux/client.py +637 -0
- shikiru/tmux/decorator.py +225 -0
- shikiru/tmux/layout.py +200 -0
- shikiru/tmux/session.py +381 -0
- shikiru/ui/__init__.py +9 -0
- shikiru/ui/age.py +45 -0
- shikiru/ui/bubbles.py +41 -0
- shikiru/ui/compact.py +1209 -0
- shikiru/ui/console.py +49 -0
- shikiru/ui/fold.py +50 -0
- shikiru/ui/gated_static.py +117 -0
- shikiru/ui/gauge.py +40 -0
- shikiru/ui/glyphs.py +109 -0
- shikiru/ui/keys.py +57 -0
- shikiru/ui/live_interval.py +42 -0
- shikiru/ui/popup.py +311 -0
- shikiru/ui/quota_status.py +26 -0
- shikiru/ui/recent_window.py +78 -0
- shikiru/ui/screen.py +57 -0
- shikiru/ui/section.py +193 -0
- shikiru/ui/session.py +65 -0
- shikiru/ui/size.py +17 -0
- shikiru/ui/splash.py +42 -0
- shikiru/ui/status.py +47 -0
- shikiru/ui/status_strip.py +149 -0
- shikiru/ui/stream.py +152 -0
- shikiru/ui/strip.py +332 -0
- shikiru/ui/theme.py +83 -0
- shikiru/ui/tokens.py +51 -0
- shikiru/view_log.py +126 -0
- shikiru/workspace/__init__.py +3 -0
- shikiru/workspace/base_refs.py +160 -0
- shikiru/workspace/caches.py +136 -0
- shikiru/workspace/facts.py +118 -0
- shikiru/workspace/git.py +601 -0
- shikiru/workspace/github.py +744 -0
- shikiru/workspace/messages.py +107 -0
- shikiru/workspace/reports.py +145 -0
- shikiru/workspace/roots.py +180 -0
- shikiru_cli-0.2.0.dist-info/METADATA +428 -0
- shikiru_cli-0.2.0.dist-info/RECORD +319 -0
- shikiru_cli-0.2.0.dist-info/WHEEL +4 -0
- shikiru_cli-0.2.0.dist-info/entry_points.txt +18 -0
- shikiru_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1086 @@
|
|
|
1
|
+
"""Stable view models and plugin protocols for dashboard widgets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import TYPE_CHECKING, Literal, Protocol
|
|
9
|
+
|
|
10
|
+
from shikiru.agents.contracts import (
|
|
11
|
+
BILLING_MODE_UNKNOWN,
|
|
12
|
+
BILLING_SOURCE_UNKNOWN,
|
|
13
|
+
COST_BASIS_UNKNOWN,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
|
|
19
|
+
from textual.screen import ModalScreen
|
|
20
|
+
from textual.widget import Widget
|
|
21
|
+
|
|
22
|
+
from shikiru.agents.contracts import BillingMode, BillingSource, CostBasis
|
|
23
|
+
from shikiru.clock import Clock
|
|
24
|
+
from shikiru.config.settings import ComplexityName, Settings
|
|
25
|
+
from shikiru.dashboard.services import TaskWorkSample
|
|
26
|
+
from shikiru.domain.briefing import (
|
|
27
|
+
VerdictCounts,
|
|
28
|
+
)
|
|
29
|
+
from shikiru.domain.decisions import TaskDecisions
|
|
30
|
+
from shikiru.domain.levels import UsageLevel
|
|
31
|
+
from shikiru.domain.results import TaskResults
|
|
32
|
+
from shikiru.domain.tasks import EscalationReason, Role
|
|
33
|
+
from shikiru.ui.strip import TaskStripData
|
|
34
|
+
|
|
35
|
+
from shikiru.domain.timeline import (
|
|
36
|
+
TIMELINE_ACTIVITY_CATEGORY,
|
|
37
|
+
TIMELINE_AGENT_CATEGORY,
|
|
38
|
+
TIMELINE_ALL_CATEGORY,
|
|
39
|
+
TIMELINE_DECISION_CATEGORY,
|
|
40
|
+
TIMELINE_ENGINE_CATEGORY,
|
|
41
|
+
TIMELINE_MESSAGE_CATEGORY,
|
|
42
|
+
TIMELINE_SIGNAL_CATEGORY,
|
|
43
|
+
TIMELINE_SOURCE_AGENT,
|
|
44
|
+
TIMELINE_SOURCE_ENGINE,
|
|
45
|
+
TIMELINE_TOOL_CATEGORY,
|
|
46
|
+
TIMELINE_TRANSITION_CATEGORY,
|
|
47
|
+
TIMELINE_USAGE_CATEGORY,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
WIDGET_ENTRY_POINT_GROUP = "shikiru.widgets"
|
|
51
|
+
FOCUS_FAULTS_VIEW = "widget-faults"
|
|
52
|
+
FOCUS_PROJECT_DIAGNOSTICS_VIEW = "project-diagnostics"
|
|
53
|
+
TASK_STATUS_QUEUED = "queued"
|
|
54
|
+
TASK_TERMINAL_STATUSES = frozenset({"done", "cancelled"})
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TaskTimelineCategory(StrEnum):
|
|
58
|
+
"""A timeline category exposed by the dashboard contract."""
|
|
59
|
+
|
|
60
|
+
ACTIVITY = TIMELINE_ACTIVITY_CATEGORY
|
|
61
|
+
AGENT = TIMELINE_AGENT_CATEGORY
|
|
62
|
+
ALL = TIMELINE_ALL_CATEGORY
|
|
63
|
+
DECISION = TIMELINE_DECISION_CATEGORY
|
|
64
|
+
ENGINE = TIMELINE_ENGINE_CATEGORY
|
|
65
|
+
MESSAGE = TIMELINE_MESSAGE_CATEGORY
|
|
66
|
+
SIGNAL = TIMELINE_SIGNAL_CATEGORY
|
|
67
|
+
TOOL = TIMELINE_TOOL_CATEGORY
|
|
68
|
+
TRANSITION = TIMELINE_TRANSITION_CATEGORY
|
|
69
|
+
USAGE = TIMELINE_USAGE_CATEGORY
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TaskTimelineSource(StrEnum):
|
|
73
|
+
"""A persisted evidence source exposed by the dashboard contract."""
|
|
74
|
+
|
|
75
|
+
AGENT = TIMELINE_SOURCE_AGENT
|
|
76
|
+
ENGINE = TIMELINE_SOURCE_ENGINE
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
OnChange = Callable[[], Awaitable[None]]
|
|
80
|
+
OnChangeRegistration = Callable[[OnChange], Awaitable[None]]
|
|
81
|
+
MotionRegistration = Callable[[OnChange], Callable[[], None]]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True)
|
|
85
|
+
class PaletteDraft:
|
|
86
|
+
"""One transient contextual form draft retained while its caller is visible."""
|
|
87
|
+
|
|
88
|
+
task_id: str
|
|
89
|
+
action: str
|
|
90
|
+
guidance: str
|
|
91
|
+
role: str | None = None
|
|
92
|
+
role_explicit: bool = False
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TaskPaletteRoute(StrEnum):
|
|
96
|
+
"""A contextual palette destination opened in the task or project workspace."""
|
|
97
|
+
|
|
98
|
+
INSPECT = "inspect"
|
|
99
|
+
EVIDENCE = "evidence"
|
|
100
|
+
USAGE = "usage"
|
|
101
|
+
COMMANDS = "commands"
|
|
102
|
+
DIAGNOSTICS = "diagnostics"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class ExtensionEntry:
|
|
107
|
+
"""An extension widget listed in the palette, opened through its contextual view."""
|
|
108
|
+
|
|
109
|
+
choice_id: str
|
|
110
|
+
label: str
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass(frozen=True)
|
|
114
|
+
class TaskRouteChoice:
|
|
115
|
+
"""A task destination chosen from the Actions list, with the task it was chosen for."""
|
|
116
|
+
|
|
117
|
+
task_id: str
|
|
118
|
+
route: TaskPaletteRoute
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True)
|
|
122
|
+
class ProjectRouteChoice:
|
|
123
|
+
"""A project destination chosen from the Actions list."""
|
|
124
|
+
|
|
125
|
+
route: TaskPaletteRoute
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
PaletteReturn = TaskRouteChoice | ProjectRouteChoice | ExtensionEntry
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class PaletteAction(StrEnum):
|
|
132
|
+
"""An operator action the palette popups can open."""
|
|
133
|
+
|
|
134
|
+
SUBMIT = "submit"
|
|
135
|
+
PRIORITIZE = "prioritize"
|
|
136
|
+
PAUSE = "pause"
|
|
137
|
+
PAUSE_NOW = "pause-now"
|
|
138
|
+
RESUME = "resume"
|
|
139
|
+
TAKE_OVER = "take-over"
|
|
140
|
+
HAND_BACK = "hand-back"
|
|
141
|
+
CANCEL = "cancel"
|
|
142
|
+
RETRY = "retry"
|
|
143
|
+
RUNG = "rung"
|
|
144
|
+
MESSAGE = "message"
|
|
145
|
+
PEEK = "peek"
|
|
146
|
+
STOP = "stop"
|
|
147
|
+
START = "start"
|
|
148
|
+
EXPORT = "export"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass(frozen=True)
|
|
152
|
+
class PaletteRequest:
|
|
153
|
+
"""Optional task, role and action context for the shared operator palette."""
|
|
154
|
+
|
|
155
|
+
action: str | None = None
|
|
156
|
+
task_id: str | None = None
|
|
157
|
+
role: str | None = None
|
|
158
|
+
drafts: tuple[PaletteDraft, ...] = ()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
OpenPalette = Callable[[str], Awaitable[None]]
|
|
162
|
+
OpenPaletteContext = Callable[[PaletteRequest], Awaitable[None]]
|
|
163
|
+
OpenResolution = Callable[[str], Awaitable[None]]
|
|
164
|
+
ResolutionScreenFactory = Callable[[str], "ModalScreen[bool]"]
|
|
165
|
+
SelectTaskContext = Callable[[str | None], None]
|
|
166
|
+
OpenTaskDetail = Callable[[str], Awaitable[None]]
|
|
167
|
+
OpenTaskDetailView = Callable[[str, str], Awaitable[None]]
|
|
168
|
+
NavigateTaskWindow = Callable[[str], Awaitable[str]]
|
|
169
|
+
NavigateHome = Callable[[], Awaitable[str]]
|
|
170
|
+
OpenContextualView = Callable[[str], Awaitable[None]]
|
|
171
|
+
ActivateAttentionDecision = Callable[[], Awaitable[bool]]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class TaskBrowserFilter(StrEnum):
|
|
175
|
+
"""A task browser category or one exact task status."""
|
|
176
|
+
|
|
177
|
+
ACTIVE = "active"
|
|
178
|
+
WAITING = "waiting"
|
|
179
|
+
HISTORY = "history"
|
|
180
|
+
ALL = "all"
|
|
181
|
+
RUNNING = "running"
|
|
182
|
+
INTEGRATING = "integrating"
|
|
183
|
+
ESCALATED = "escalated"
|
|
184
|
+
QUEUED = "queued"
|
|
185
|
+
DONE = "done"
|
|
186
|
+
CANCELLED = "cancelled"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class WidgetPlacement(StrEnum):
|
|
190
|
+
"""A compatible optional placement for a configured dashboard widget."""
|
|
191
|
+
|
|
192
|
+
IDENTITY = "identity"
|
|
193
|
+
TASK_LIST = "task_list"
|
|
194
|
+
ATTENTION = "attention"
|
|
195
|
+
PROJECT_USAGE = "project_usage"
|
|
196
|
+
RESOURCES = "resources"
|
|
197
|
+
ACTIVITY = "activity"
|
|
198
|
+
DETAILED_USAGE = "detailed_usage"
|
|
199
|
+
DIAGNOSTICS = "diagnostics"
|
|
200
|
+
WORKFLOW = "workflow"
|
|
201
|
+
EXTENSIONS = "extensions"
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class CommandFeedbackStatus(StrEnum):
|
|
205
|
+
"""Operator-facing names for stored command lifecycle states."""
|
|
206
|
+
|
|
207
|
+
PENDING = "Pending"
|
|
208
|
+
APPLIED = "Applied"
|
|
209
|
+
REJECTED = "Rejected"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class DiagnosticSampleState(StrEnum):
|
|
213
|
+
"""An explicit state for a secondary diagnostic observation."""
|
|
214
|
+
|
|
215
|
+
AVAILABLE = "available"
|
|
216
|
+
EMPTY = "empty"
|
|
217
|
+
UNAVAILABLE = "unavailable"
|
|
218
|
+
STALE = "stale"
|
|
219
|
+
FAILED = "failed"
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class TaskHistoryGap(StrEnum):
|
|
223
|
+
"""A semantic reason that retained task turn history is incomplete."""
|
|
224
|
+
|
|
225
|
+
TRUNCATED = "history_truncated"
|
|
226
|
+
UNLINKED_TURN = "unlinked_turn"
|
|
227
|
+
MISSING_HISTORY = "missing_history"
|
|
228
|
+
UNENDED_TURN = "unended_turn"
|
|
229
|
+
MISSING_TURN_START = "missing_turn_start"
|
|
230
|
+
MISSING_RECORDED_EFFORT = "missing_recorded_effort"
|
|
231
|
+
MISSING_USAGE_DELTA = "missing_usage_delta"
|
|
232
|
+
INCOMPLETE_IDENTITY = "incomplete_turn_identity"
|
|
233
|
+
INCONSISTENT_IDENTITY = "inconsistent_turn_identity"
|
|
234
|
+
READ_FAILED = "history_read_failed"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@dataclass(frozen=True)
|
|
238
|
+
class RepoStatus:
|
|
239
|
+
"""The project checkout's state, as shown by the `header` widget."""
|
|
240
|
+
|
|
241
|
+
branch: str | None
|
|
242
|
+
ahead: int | None
|
|
243
|
+
behind: int | None
|
|
244
|
+
dirty: int | None
|
|
245
|
+
sampled_ago: str | None = None
|
|
246
|
+
root: str | None = None
|
|
247
|
+
added_lines: int | None = None
|
|
248
|
+
deleted_lines: int | None = None
|
|
249
|
+
binary_entries: int | None = None
|
|
250
|
+
untracked_paths: int | None = None
|
|
251
|
+
state: DiagnosticSampleState = DiagnosticSampleState.AVAILABLE
|
|
252
|
+
error: str | None = None
|
|
253
|
+
base: str | None = None
|
|
254
|
+
unpushed: int | None = None
|
|
255
|
+
unpulled: int | None = None
|
|
256
|
+
remote_configured: bool = True
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class PublicationState(StrEnum):
|
|
260
|
+
"""How a base branch compares with its remote counterpart."""
|
|
261
|
+
|
|
262
|
+
IN_SYNC = "in_sync"
|
|
263
|
+
AWAITING_PUSH = "awaiting_push"
|
|
264
|
+
NOT_IN_SYNC = "not_in_sync"
|
|
265
|
+
UNAVAILABLE = "unavailable"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@dataclass(frozen=True)
|
|
269
|
+
class PublicationView:
|
|
270
|
+
"""Whether the base's commits reached its remote, and when its pending push is scheduled."""
|
|
271
|
+
|
|
272
|
+
state: PublicationState
|
|
273
|
+
remote: str
|
|
274
|
+
unpushed: int | None = None
|
|
275
|
+
push_time: str | None = None
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@dataclass(frozen=True)
|
|
279
|
+
class EngineStatusView:
|
|
280
|
+
"""Whether the engine runs, and how long the live or previous run lasted."""
|
|
281
|
+
|
|
282
|
+
up: bool
|
|
283
|
+
uptime_seconds: float | None = None
|
|
284
|
+
previous_run_seconds: float | None = None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@dataclass(frozen=True)
|
|
288
|
+
class HeaderView:
|
|
289
|
+
"""Project and engine summary rendered by the header widget."""
|
|
290
|
+
|
|
291
|
+
project: str
|
|
292
|
+
engine_up: bool
|
|
293
|
+
occupied_slots: int
|
|
294
|
+
maximum_slots: int
|
|
295
|
+
repo: RepoStatus
|
|
296
|
+
alerts: tuple[str, ...]
|
|
297
|
+
publication: PublicationView
|
|
298
|
+
integration_mode: str = ""
|
|
299
|
+
engine_uptime_seconds: float | None = None
|
|
300
|
+
previous_engine_run_seconds: float | None = None
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
@dataclass(frozen=True)
|
|
304
|
+
class RecordedUsageView:
|
|
305
|
+
"""Recorded project or task usage with token, billing and reporting coverage."""
|
|
306
|
+
|
|
307
|
+
session_count: int
|
|
308
|
+
input_tokens: int
|
|
309
|
+
output_tokens: int
|
|
310
|
+
cache_read_tokens: int
|
|
311
|
+
cache_write_tokens: int
|
|
312
|
+
reported_sessions: int
|
|
313
|
+
legacy_token_sessions: int
|
|
314
|
+
unknown_token_sessions: int
|
|
315
|
+
billed_cost_usd: float | None
|
|
316
|
+
billed_cost_sessions: int
|
|
317
|
+
api_billed_cost_usd: float | None
|
|
318
|
+
api_billed_cost_sessions: int
|
|
319
|
+
api_cost_unreported_sessions: int
|
|
320
|
+
api_equivalent_cost_usd: float | None
|
|
321
|
+
api_equivalent_cost_sessions: int
|
|
322
|
+
unknown_billing_sessions: int
|
|
323
|
+
subscription_sessions: int
|
|
324
|
+
subscription_agents: tuple[str, ...]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@dataclass(frozen=True)
|
|
328
|
+
class WorkspaceSummaryView:
|
|
329
|
+
"""Task and attention counts for the home summary controls."""
|
|
330
|
+
|
|
331
|
+
attention_groups: int
|
|
332
|
+
decisions: int
|
|
333
|
+
stalls: int
|
|
334
|
+
faults: int
|
|
335
|
+
running: int
|
|
336
|
+
waiting: int
|
|
337
|
+
engine_up: bool | None = None
|
|
338
|
+
held_tasks: int | None = None
|
|
339
|
+
holds: tuple[str, ...] | None = None
|
|
340
|
+
operator_owned: tuple[str, ...] | None = None
|
|
341
|
+
held_task_states: tuple[HeldTaskStateView, ...] | None = None
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def attention(self) -> int:
|
|
345
|
+
"""Count grouped attention rows, which may overlap waiting tasks."""
|
|
346
|
+
return self.attention_groups
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@dataclass(frozen=True)
|
|
350
|
+
class HeldTaskStateView:
|
|
351
|
+
"""The observed holds and operator-owned roles for one task."""
|
|
352
|
+
|
|
353
|
+
task_id: str
|
|
354
|
+
holds: tuple[str, ...]
|
|
355
|
+
operator_owned_roles: tuple[str, ...]
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
@dataclass(frozen=True)
|
|
359
|
+
class QueueRow:
|
|
360
|
+
"""One queued task row, already ordered by the scheduler's dispatch rules."""
|
|
361
|
+
|
|
362
|
+
task_id: str
|
|
363
|
+
title: str
|
|
364
|
+
priority: int
|
|
365
|
+
dependencies: tuple[str, ...]
|
|
366
|
+
wait_reason: str | None
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
@dataclass(frozen=True)
|
|
370
|
+
class TaskBrowserRow:
|
|
371
|
+
"""One task row with its identity and state prepared for the selectable browser."""
|
|
372
|
+
|
|
373
|
+
task_id: str
|
|
374
|
+
title: str
|
|
375
|
+
status: str
|
|
376
|
+
step: str | None
|
|
377
|
+
iteration: int
|
|
378
|
+
acting_role: str | None
|
|
379
|
+
priority: int
|
|
380
|
+
dependencies: tuple[str, ...]
|
|
381
|
+
wait_reason: str | None
|
|
382
|
+
holds: tuple[str, ...]
|
|
383
|
+
age: str
|
|
384
|
+
group_label: str | None
|
|
385
|
+
open_decision: bool
|
|
386
|
+
diagnosed_stall: bool
|
|
387
|
+
requeued_age: str | None = None
|
|
388
|
+
agent_processing: bool = False
|
|
389
|
+
quota_wait: bool = False
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
@dataclass(frozen=True)
|
|
393
|
+
class TaskBrowserView:
|
|
394
|
+
"""A bounded page of matching tasks and the selected task's retained context."""
|
|
395
|
+
|
|
396
|
+
filter: TaskBrowserFilter
|
|
397
|
+
search: str
|
|
398
|
+
rows: tuple[TaskBrowserRow, ...]
|
|
399
|
+
selected_task: TaskBrowserRow | None
|
|
400
|
+
matching_count: int
|
|
401
|
+
filtered_count: int
|
|
402
|
+
project_count: int
|
|
403
|
+
has_more: bool
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@dataclass(frozen=True)
|
|
407
|
+
class TaskReferenceView:
|
|
408
|
+
"""A related task's identity and recorded workflow state."""
|
|
409
|
+
|
|
410
|
+
task_id: str
|
|
411
|
+
title: str
|
|
412
|
+
status: str
|
|
413
|
+
status_reason: str | None
|
|
414
|
+
mode: str | None
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@dataclass(frozen=True)
|
|
418
|
+
class TaskTestView:
|
|
419
|
+
"""An agent-reported test entry with its source made explicit."""
|
|
420
|
+
|
|
421
|
+
name: str
|
|
422
|
+
result: str
|
|
423
|
+
details: str
|
|
424
|
+
provenance: str
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
@dataclass(frozen=True)
|
|
428
|
+
class TaskReportView:
|
|
429
|
+
"""One validated agent report or engine verdict for task detail."""
|
|
430
|
+
|
|
431
|
+
turn: str
|
|
432
|
+
kind: str
|
|
433
|
+
role: str
|
|
434
|
+
provenance: str
|
|
435
|
+
summary: str
|
|
436
|
+
decision: str | None
|
|
437
|
+
question: str
|
|
438
|
+
issues: tuple[str, ...]
|
|
439
|
+
risks: tuple[str, ...]
|
|
440
|
+
tests: tuple[TaskTestView, ...]
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
@dataclass(frozen=True)
|
|
444
|
+
class TaskSessionView:
|
|
445
|
+
"""One task session's recorded identity, status and usage."""
|
|
446
|
+
|
|
447
|
+
role: str
|
|
448
|
+
agent: str
|
|
449
|
+
model: str
|
|
450
|
+
effort: str | None
|
|
451
|
+
session_id: str | None
|
|
452
|
+
status: str
|
|
453
|
+
input_tokens: int
|
|
454
|
+
output_tokens: int
|
|
455
|
+
cache_read_tokens: int
|
|
456
|
+
cache_write_tokens: int
|
|
457
|
+
cost_usd: float | None
|
|
458
|
+
cost_basis: CostBasis
|
|
459
|
+
context_used_pct: float | None
|
|
460
|
+
usage_reported: bool | None = None
|
|
461
|
+
legacy_token_totals: bool = False
|
|
462
|
+
legacy_cost_recorded: bool = False
|
|
463
|
+
billing_mode: BillingMode = BILLING_MODE_UNKNOWN
|
|
464
|
+
billing_source: BillingSource = BILLING_SOURCE_UNKNOWN
|
|
465
|
+
run_id: int | None = None
|
|
466
|
+
current_lineage: bool = True
|
|
467
|
+
model_history: tuple[TaskModelHistoryView, ...] = ()
|
|
468
|
+
history_gaps: tuple[TaskHistoryGap, ...] = ()
|
|
469
|
+
context_level: UsageLevel | None = None
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
@dataclass(frozen=True)
|
|
473
|
+
class TaskTurnUsageDeltaView:
|
|
474
|
+
"""Usage delta recorded for one completed agent turn."""
|
|
475
|
+
|
|
476
|
+
input_tokens: int
|
|
477
|
+
output_tokens: int
|
|
478
|
+
cache_read_tokens: int
|
|
479
|
+
cache_write_tokens: int
|
|
480
|
+
cost_usd: float | None
|
|
481
|
+
cost_basis: CostBasis = COST_BASIS_UNKNOWN
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
@dataclass(frozen=True)
|
|
485
|
+
class TaskModelHistoryView:
|
|
486
|
+
"""One bounded turn observation with model identity and per-turn usage, when recorded."""
|
|
487
|
+
|
|
488
|
+
agent: str
|
|
489
|
+
model: str
|
|
490
|
+
effort: str | None
|
|
491
|
+
turn: str | None
|
|
492
|
+
status: str
|
|
493
|
+
usage_delta: TaskTurnUsageDeltaView | None = None
|
|
494
|
+
identity_complete: bool = False
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
@dataclass(frozen=True)
|
|
498
|
+
class TaskMilestoneView:
|
|
499
|
+
"""One workflow stage and the recorded outcome shown in the compact inspector."""
|
|
500
|
+
|
|
501
|
+
stage: str
|
|
502
|
+
state: str
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
@dataclass(frozen=True)
|
|
506
|
+
class TaskTimelineEntryView:
|
|
507
|
+
"""One recorded engine event or normalized agent signal with stable identity."""
|
|
508
|
+
|
|
509
|
+
identity: str
|
|
510
|
+
timestamp: str | None
|
|
511
|
+
source: str
|
|
512
|
+
category: str
|
|
513
|
+
summary: str
|
|
514
|
+
phrase: str
|
|
515
|
+
glyph: str
|
|
516
|
+
tone: str
|
|
517
|
+
detail: str
|
|
518
|
+
role: str | None
|
|
519
|
+
turn: str | None
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
@dataclass(frozen=True)
|
|
523
|
+
class TaskTimelinePageView:
|
|
524
|
+
"""A bounded newest-first task evidence page with an older-page cursor."""
|
|
525
|
+
|
|
526
|
+
entries: tuple[TaskTimelineEntryView, ...]
|
|
527
|
+
older_cursor: str | None
|
|
528
|
+
has_older: bool
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@dataclass(frozen=True)
|
|
532
|
+
class CommandFeedbackEntryView:
|
|
533
|
+
"""One stored command and its current result, ready for compact display."""
|
|
534
|
+
|
|
535
|
+
command_id: int
|
|
536
|
+
action: str
|
|
537
|
+
target: str
|
|
538
|
+
status: CommandFeedbackStatus
|
|
539
|
+
result: str | None
|
|
540
|
+
queued_at: str | None = None
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
@dataclass(frozen=True)
|
|
544
|
+
class CommandHistoryView:
|
|
545
|
+
"""A bounded command-history page with current engine liveness for pending rows."""
|
|
546
|
+
|
|
547
|
+
engine_up: bool
|
|
548
|
+
entries: tuple[CommandFeedbackEntryView, ...]
|
|
549
|
+
offset: int
|
|
550
|
+
limit: int
|
|
551
|
+
total_count: int
|
|
552
|
+
has_more: bool
|
|
553
|
+
status: CommandFeedbackStatus | None
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
@dataclass(frozen=True)
|
|
557
|
+
class TaskDetailView:
|
|
558
|
+
"""Ready-to-render task state, evidence and existing target metadata."""
|
|
559
|
+
|
|
560
|
+
task_id: str
|
|
561
|
+
title: str
|
|
562
|
+
status: str
|
|
563
|
+
status_reason: str | None
|
|
564
|
+
mode: str | None
|
|
565
|
+
step: str
|
|
566
|
+
acting_role: str
|
|
567
|
+
iteration: int
|
|
568
|
+
holds: tuple[str, ...]
|
|
569
|
+
wait_reason: str | None
|
|
570
|
+
ownership: tuple[str, ...]
|
|
571
|
+
request: str
|
|
572
|
+
acceptance_criteria: tuple[str, ...]
|
|
573
|
+
progress: TaskStripData
|
|
574
|
+
latest_report: TaskReportView | None
|
|
575
|
+
latest_verdict: TaskReportView | None
|
|
576
|
+
branch: str | None
|
|
577
|
+
worktree_path: str
|
|
578
|
+
window_id: str | None
|
|
579
|
+
sessions: tuple[TaskSessionView, ...]
|
|
580
|
+
complexity: ComplexityName | None = None
|
|
581
|
+
relationships: TaskRelationshipsView | None = None
|
|
582
|
+
blockers: tuple[TaskBlockerView, ...] = ()
|
|
583
|
+
blocker_evidence: BlockerEvidenceView | None = None
|
|
584
|
+
submitted_age: str = ""
|
|
585
|
+
requeued_age: str | None = None
|
|
586
|
+
usage: RecordedUsageView | None = None
|
|
587
|
+
signal_observed: bool = False
|
|
588
|
+
agent_usage: tuple[AgentUsageView, ...] = ()
|
|
589
|
+
priority: int | None = None
|
|
590
|
+
actual_stage: str | None = None
|
|
591
|
+
actual_stage_started_at: datetime | None = None
|
|
592
|
+
stage_ended_at: datetime | None = None
|
|
593
|
+
milestones: tuple[TaskMilestoneView, ...] = ()
|
|
594
|
+
latest_signal: str | None = None
|
|
595
|
+
latest_signal_at: datetime | None = None
|
|
596
|
+
max_iterations: int = 0
|
|
597
|
+
started_at: datetime | None = None
|
|
598
|
+
escalation_remaining: int | None = None
|
|
599
|
+
verdict_counts: VerdictCounts | None = None
|
|
600
|
+
last_words: str | None = None
|
|
601
|
+
results: TaskResults | None = None
|
|
602
|
+
decisions: TaskDecisions | None = None
|
|
603
|
+
can_take_over: bool = False
|
|
604
|
+
|
|
605
|
+
@property
|
|
606
|
+
def quota_wait(self) -> QuotaWaitView | None:
|
|
607
|
+
"""The quota wait among the blockers, when the task waits behind a quota block."""
|
|
608
|
+
return next((item.quota_wait for item in self.blockers if item.quota_wait), None)
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
@dataclass(frozen=True)
|
|
612
|
+
class TaskFamilyOutcomeView:
|
|
613
|
+
"""The exact recorded count of family tasks in one lifecycle outcome."""
|
|
614
|
+
|
|
615
|
+
outcome: str
|
|
616
|
+
count: int
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
@dataclass(frozen=True)
|
|
620
|
+
class TaskRelationshipsView:
|
|
621
|
+
"""Parentage, dependencies and recorded replacements kept as separate relationships."""
|
|
622
|
+
|
|
623
|
+
family_parent: TaskReferenceView
|
|
624
|
+
family_root: TaskReferenceView
|
|
625
|
+
family_children: tuple[TaskReferenceView, ...]
|
|
626
|
+
family_child_count: int
|
|
627
|
+
dependencies: tuple[TaskReferenceView, ...]
|
|
628
|
+
dependency_count: int
|
|
629
|
+
blocking_dependencies: tuple[TaskReferenceView, ...]
|
|
630
|
+
blocking_dependency_count: int
|
|
631
|
+
dependents: tuple[TaskReferenceView, ...]
|
|
632
|
+
dependent_count: int
|
|
633
|
+
replacements: tuple[TaskReferenceView, ...]
|
|
634
|
+
replacement_count: int
|
|
635
|
+
family_outcomes: tuple[TaskFamilyOutcomeView, ...]
|
|
636
|
+
page_offset: int
|
|
637
|
+
page_size: int
|
|
638
|
+
has_more: bool
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
@dataclass(frozen=True)
|
|
642
|
+
class QuotaWaitView:
|
|
643
|
+
"""The quota block a task waits behind and when its stored data next changes.
|
|
644
|
+
|
|
645
|
+
Its wording is built for the tick it is shown at, so the blocker and briefing count down and
|
|
646
|
+
take a new date with the clock; `refresh_at` is when the stored data must be read again.
|
|
647
|
+
"""
|
|
648
|
+
|
|
649
|
+
agent: str
|
|
650
|
+
until: datetime
|
|
651
|
+
refresh_at: datetime
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
@dataclass(frozen=True)
|
|
655
|
+
class QuotaReadingView:
|
|
656
|
+
"""One quota window of a provider as the scheduler last observed it."""
|
|
657
|
+
|
|
658
|
+
window: str
|
|
659
|
+
used_pct: float
|
|
660
|
+
resets_at: datetime | None
|
|
661
|
+
observed_at: datetime
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
@dataclass(frozen=True)
|
|
665
|
+
class QuotaEvidenceView:
|
|
666
|
+
"""The quota facts behind one provider's active blocker.
|
|
667
|
+
|
|
668
|
+
`readings` excludes expired windows. `has_recorded_readings` distinguishes a provider with
|
|
669
|
+
only expired windows from one with no stored reading. `retry_note` names a difference between
|
|
670
|
+
the engine's retry and the provider reset, when the readings show one.
|
|
671
|
+
"""
|
|
672
|
+
|
|
673
|
+
agent: str
|
|
674
|
+
readings: tuple[QuotaReadingView, ...] = ()
|
|
675
|
+
retry_note: str | None = None
|
|
676
|
+
has_recorded_readings: bool = False
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
@dataclass(frozen=True)
|
|
680
|
+
class BlockerEvidenceView:
|
|
681
|
+
"""The facts behind a task's Blockers decision: quota groups, observation times, notes, raw."""
|
|
682
|
+
|
|
683
|
+
quotas: tuple[QuotaEvidenceView, ...] = ()
|
|
684
|
+
observed_at: tuple[datetime, ...] = ()
|
|
685
|
+
notes: tuple[str, ...] = ()
|
|
686
|
+
raw: tuple[str, ...] = ()
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
@dataclass(frozen=True)
|
|
690
|
+
class TaskBlockerView:
|
|
691
|
+
"""One active blocker of a task: its cause, the detail lines under it and the tasks it lists."""
|
|
692
|
+
|
|
693
|
+
kind: str
|
|
694
|
+
headline: str
|
|
695
|
+
details: tuple[str, ...] = ()
|
|
696
|
+
related_tasks: tuple[TaskReferenceView, ...] = ()
|
|
697
|
+
quota_wait: QuotaWaitView | None = None
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
@dataclass(frozen=True)
|
|
701
|
+
class EscalationPanelView:
|
|
702
|
+
"""One open escalation, including the exact packet rendered by its splash panel."""
|
|
703
|
+
|
|
704
|
+
escalation_id: str
|
|
705
|
+
task_id: str
|
|
706
|
+
headline: str
|
|
707
|
+
question: str
|
|
708
|
+
options: tuple[str, ...]
|
|
709
|
+
packet: str
|
|
710
|
+
pulse_red: bool
|
|
711
|
+
task_title: str = ""
|
|
712
|
+
age: str = ""
|
|
713
|
+
resolution_actions: tuple[str, ...] = ()
|
|
714
|
+
answer_available: bool = False
|
|
715
|
+
evidence_error: str | None = None
|
|
716
|
+
reason: EscalationReason | None = None
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
@dataclass(frozen=True)
|
|
720
|
+
class AttentionGroupView:
|
|
721
|
+
"""One task's open decisions, diagnosed stall and recorded engine faults."""
|
|
722
|
+
|
|
723
|
+
task_id: str | None
|
|
724
|
+
title: str
|
|
725
|
+
reasons: tuple[str, ...]
|
|
726
|
+
escalations: tuple[EscalationPanelView, ...]
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
@dataclass(frozen=True)
|
|
730
|
+
class AttentionView:
|
|
731
|
+
"""Grouped inspection items and their decision, stall and fault counts."""
|
|
732
|
+
|
|
733
|
+
groups: tuple[AttentionGroupView, ...]
|
|
734
|
+
decisions: int
|
|
735
|
+
stalls: int
|
|
736
|
+
faults: int
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
@dataclass(frozen=True)
|
|
740
|
+
class DialogueBubbleView:
|
|
741
|
+
"""One event summary prepared for the dialogue widget."""
|
|
742
|
+
|
|
743
|
+
role: Role | None
|
|
744
|
+
text: str
|
|
745
|
+
task_id: str
|
|
746
|
+
age: str
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
@dataclass(frozen=True)
|
|
750
|
+
class QuotaGaugeView:
|
|
751
|
+
"""One provider-reported quota window with human-readable time context."""
|
|
752
|
+
|
|
753
|
+
name: str
|
|
754
|
+
used_pct: float
|
|
755
|
+
resets_in: str
|
|
756
|
+
observed_ago: str
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
@dataclass(frozen=True)
|
|
760
|
+
class AgentUsageView:
|
|
761
|
+
"""Token and priced-cost totals plus every quota window for one agent."""
|
|
762
|
+
|
|
763
|
+
agent: str
|
|
764
|
+
input_tokens: int
|
|
765
|
+
output_tokens: int
|
|
766
|
+
cache_read_tokens: int
|
|
767
|
+
cache_write_tokens: int
|
|
768
|
+
cost_usd: float | None
|
|
769
|
+
cost_basis: CostBasis | Literal["mixed"]
|
|
770
|
+
quotas: tuple[QuotaGaugeView, ...]
|
|
771
|
+
run_count: int = 0
|
|
772
|
+
usage_reported_runs: int = 0
|
|
773
|
+
legacy_token_runs: int = 0
|
|
774
|
+
billed_cost_usd: float | None = None
|
|
775
|
+
billed_cost_runs: int = 0
|
|
776
|
+
api_equivalent_cost_usd: float | None = None
|
|
777
|
+
api_equivalent_cost_runs: int = 0
|
|
778
|
+
unknown_cost_runs: int = 0
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
@dataclass(frozen=True)
|
|
782
|
+
class QuotaBlockView:
|
|
783
|
+
"""One agent blocked by a provider quota, announced by its banner."""
|
|
784
|
+
|
|
785
|
+
agent: str
|
|
786
|
+
banner: str
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
@dataclass(frozen=True)
|
|
790
|
+
class ResourceView:
|
|
791
|
+
"""Host, Shikiru and agent resource measurements from one sample."""
|
|
792
|
+
|
|
793
|
+
host_cpu_pct: float
|
|
794
|
+
host_ram_used_pct: float
|
|
795
|
+
free_ram_gb: float
|
|
796
|
+
instance_cpu_pct: float | None
|
|
797
|
+
instance_rss_gb: float | None
|
|
798
|
+
sampled_at: datetime | None = None
|
|
799
|
+
state: DiagnosticSampleState | None = None
|
|
800
|
+
sampled_ago: str | None = None
|
|
801
|
+
error: str | None = None
|
|
802
|
+
host_ram_used_bytes: int | None = None
|
|
803
|
+
host_ram_total_bytes: int | None = None
|
|
804
|
+
shikiru_cpu_pct: float | None = None
|
|
805
|
+
shikiru_rss_gb: float | None = None
|
|
806
|
+
agents_cpu_pct: float | None = None
|
|
807
|
+
agents_rss_gb: float | None = None
|
|
808
|
+
shikiru_process_tree_empty: bool | None = None
|
|
809
|
+
agents_process_tree_empty: bool | None = None
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
@dataclass(frozen=True)
|
|
813
|
+
class ContainerView:
|
|
814
|
+
"""One running container's CPU and memory as numbers."""
|
|
815
|
+
|
|
816
|
+
name: str
|
|
817
|
+
cpu_pct: float
|
|
818
|
+
memory_bytes: int
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
@dataclass(frozen=True)
|
|
822
|
+
class ContainerProblemView:
|
|
823
|
+
"""One unhealthy, restarting or non-zero-exited container."""
|
|
824
|
+
|
|
825
|
+
name: str
|
|
826
|
+
reason: str
|
|
827
|
+
exit_code: int | None = None
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
@dataclass(frozen=True)
|
|
831
|
+
class ElsewhereView:
|
|
832
|
+
"""Running containers outside the project; usage is `None` when unavailable."""
|
|
833
|
+
|
|
834
|
+
running: int
|
|
835
|
+
cpu_pct: float | None
|
|
836
|
+
memory_bytes: int | None
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
@dataclass(frozen=True)
|
|
840
|
+
class DockerSummaryView:
|
|
841
|
+
"""Scoped container counts, usage totals, problems, top five by CPU and the rest."""
|
|
842
|
+
|
|
843
|
+
running: int
|
|
844
|
+
total: int
|
|
845
|
+
cpu_pct: float | None
|
|
846
|
+
memory_bytes: int | None
|
|
847
|
+
problems: tuple[ContainerProblemView, ...]
|
|
848
|
+
top: tuple[ContainerView, ...]
|
|
849
|
+
elsewhere: ElsewhereView
|
|
850
|
+
usage_available: bool
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
@dataclass(frozen=True)
|
|
854
|
+
class DockerView:
|
|
855
|
+
"""Docker sampling state, including stale or failed observations."""
|
|
856
|
+
|
|
857
|
+
available: bool
|
|
858
|
+
summary: DockerSummaryView | None = None
|
|
859
|
+
state: DiagnosticSampleState | None = None
|
|
860
|
+
sampled_ago: str | None = None
|
|
861
|
+
error: str | None = None
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
@dataclass(frozen=True)
|
|
865
|
+
class DiskView:
|
|
866
|
+
"""Free and total bytes of the project filesystem, the `.shikiru` size and sampling state."""
|
|
867
|
+
|
|
868
|
+
state: DiagnosticSampleState
|
|
869
|
+
free_bytes: int | None = None
|
|
870
|
+
total_bytes: int | None = None
|
|
871
|
+
shikiru_bytes: int | None = None
|
|
872
|
+
free_level: UsageLevel | None = None
|
|
873
|
+
sampled_ago: str | None = None
|
|
874
|
+
error: str | None = None
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
@dataclass(frozen=True)
|
|
878
|
+
class FaultDetailRow:
|
|
879
|
+
"""One labelled piece of recorded evidence, or the plain statement that it is unavailable."""
|
|
880
|
+
|
|
881
|
+
label: str
|
|
882
|
+
value: str
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
@dataclass(frozen=True)
|
|
886
|
+
class EngineFaultView:
|
|
887
|
+
"""One open engine fault streak as a compact card with its evidence behind a disclosure."""
|
|
888
|
+
|
|
889
|
+
key: str
|
|
890
|
+
task_id: str | None
|
|
891
|
+
scope: str
|
|
892
|
+
status: str
|
|
893
|
+
impact: str
|
|
894
|
+
cause: str
|
|
895
|
+
next_step: str
|
|
896
|
+
needs_action: bool
|
|
897
|
+
details: tuple[FaultDetailRow, ...]
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
class DashboardViews(Protocol):
|
|
901
|
+
"""Ready-to-render data; widget plugins never receive state or configuration objects."""
|
|
902
|
+
|
|
903
|
+
async def header(self) -> HeaderView:
|
|
904
|
+
"""Return the project, engine, slots and repository view."""
|
|
905
|
+
...
|
|
906
|
+
|
|
907
|
+
def engine_up(self) -> bool:
|
|
908
|
+
"""Return current engine lock liveness for active-turn presentation."""
|
|
909
|
+
...
|
|
910
|
+
|
|
911
|
+
def engine_status(self) -> EngineStatusView:
|
|
912
|
+
"""Return engine liveness with its recorded run duration."""
|
|
913
|
+
...
|
|
914
|
+
|
|
915
|
+
def project_usage(self) -> RecordedUsageView:
|
|
916
|
+
"""Return token and monetary coverage for all retained session lineages."""
|
|
917
|
+
...
|
|
918
|
+
|
|
919
|
+
def workspace_summary(self) -> WorkspaceSummaryView:
|
|
920
|
+
"""Return attention, running and waiting counts for home navigation."""
|
|
921
|
+
...
|
|
922
|
+
|
|
923
|
+
def attention(self) -> AttentionView:
|
|
924
|
+
"""Return open decisions, diagnosed stalls and recorded engine faults."""
|
|
925
|
+
...
|
|
926
|
+
|
|
927
|
+
def engine_faults(self) -> tuple[EngineFaultView, ...]:
|
|
928
|
+
"""Return recorded engine fault streaks that have not recovered."""
|
|
929
|
+
...
|
|
930
|
+
|
|
931
|
+
def strips(self) -> list[TaskStripData]:
|
|
932
|
+
"""Return active task strips, with the focused task first."""
|
|
933
|
+
...
|
|
934
|
+
|
|
935
|
+
def queue(self) -> list[QueueRow]:
|
|
936
|
+
"""Return queued tasks in dispatch order."""
|
|
937
|
+
...
|
|
938
|
+
|
|
939
|
+
def task_browser(
|
|
940
|
+
self,
|
|
941
|
+
*,
|
|
942
|
+
task_filter: TaskBrowserFilter,
|
|
943
|
+
search: str,
|
|
944
|
+
offset: int,
|
|
945
|
+
limit: int,
|
|
946
|
+
selected_task_id: str | None,
|
|
947
|
+
) -> TaskBrowserView:
|
|
948
|
+
"""Return one bounded browser page and preserve context for a selected task."""
|
|
949
|
+
...
|
|
950
|
+
|
|
951
|
+
@property
|
|
952
|
+
def recent_task_limit(self) -> int:
|
|
953
|
+
"""Return how many most recent terminal tasks Home Recent can page through."""
|
|
954
|
+
...
|
|
955
|
+
|
|
956
|
+
@property
|
|
957
|
+
def recent_task_window(self) -> int:
|
|
958
|
+
"""Return how many Recent tasks Home shows at once."""
|
|
959
|
+
...
|
|
960
|
+
|
|
961
|
+
def task_browser_pages(
|
|
962
|
+
self,
|
|
963
|
+
*,
|
|
964
|
+
task_filter: TaskBrowserFilter,
|
|
965
|
+
search: str,
|
|
966
|
+
limit: int,
|
|
967
|
+
page_count: int,
|
|
968
|
+
selected_task_id: str | None,
|
|
969
|
+
) -> tuple[TaskBrowserView, ...]:
|
|
970
|
+
"""Return contiguous bounded browser pages from one consistent read snapshot."""
|
|
971
|
+
...
|
|
972
|
+
|
|
973
|
+
async def task_detail(
|
|
974
|
+
self,
|
|
975
|
+
task_id: str,
|
|
976
|
+
*,
|
|
977
|
+
relationship_offset: int = 0,
|
|
978
|
+
) -> TaskDetailView | None:
|
|
979
|
+
"""Return task state, recorded workspace evidence and and relationships."""
|
|
980
|
+
...
|
|
981
|
+
|
|
982
|
+
async def live_work(self, task_id: str) -> TaskWorkSample | None:
|
|
983
|
+
"""Return the live work of a task whose Results still change, or None."""
|
|
984
|
+
...
|
|
985
|
+
|
|
986
|
+
async def task_timeline(
|
|
987
|
+
self,
|
|
988
|
+
task_id: str,
|
|
989
|
+
*,
|
|
990
|
+
cursor: str | None,
|
|
991
|
+
role: str | None,
|
|
992
|
+
turn: str | None,
|
|
993
|
+
category: str,
|
|
994
|
+
) -> TaskTimelinePageView | None:
|
|
995
|
+
"""Return only a bounded timeline page for state and signal refreshes."""
|
|
996
|
+
...
|
|
997
|
+
|
|
998
|
+
def escalations(self) -> list[EscalationPanelView]:
|
|
999
|
+
"""Return every open escalation and its question and options from the packet."""
|
|
1000
|
+
...
|
|
1001
|
+
|
|
1002
|
+
def dialogue(self, task_id: str | None = None) -> list[DialogueBubbleView]:
|
|
1003
|
+
"""Return speech-worthy events for the project or one task, oldest first."""
|
|
1004
|
+
...
|
|
1005
|
+
|
|
1006
|
+
def usage(self, task_id: str | None = None) -> list[AgentUsageView]:
|
|
1007
|
+
"""Return project usage or usage recorded for one task."""
|
|
1008
|
+
...
|
|
1009
|
+
|
|
1010
|
+
def quota_blocks(self) -> list[QuotaBlockView]:
|
|
1011
|
+
"""Return one banner per agent whose quota block is still in effect."""
|
|
1012
|
+
...
|
|
1013
|
+
|
|
1014
|
+
def command_history(
|
|
1015
|
+
self,
|
|
1016
|
+
*,
|
|
1017
|
+
expanded: bool = False,
|
|
1018
|
+
limit: int | None = None,
|
|
1019
|
+
offset: int = 0,
|
|
1020
|
+
status: CommandFeedbackStatus | None = None,
|
|
1021
|
+
) -> CommandHistoryView:
|
|
1022
|
+
"""Return a bounded recent or paginated project command history."""
|
|
1023
|
+
...
|
|
1024
|
+
|
|
1025
|
+
async def resources(self) -> ResourceView:
|
|
1026
|
+
"""Return host usage and the engine/task-pane process-tree usage."""
|
|
1027
|
+
...
|
|
1028
|
+
|
|
1029
|
+
def resource_sample(self) -> ResourceView | None:
|
|
1030
|
+
"""Return the latest timestamped resource observation without sampling again."""
|
|
1031
|
+
...
|
|
1032
|
+
|
|
1033
|
+
def on_resources_refreshed(self, callback: Callable[[], None]) -> Callable[[], None]:
|
|
1034
|
+
"""Call `callback` after every `resources()` call; return the unsubscribe function."""
|
|
1035
|
+
...
|
|
1036
|
+
|
|
1037
|
+
async def containers(self) -> DockerView:
|
|
1038
|
+
"""Return the current Docker sample and whether Docker is available."""
|
|
1039
|
+
...
|
|
1040
|
+
|
|
1041
|
+
async def disk(self) -> DiskView:
|
|
1042
|
+
"""Return the cached disk sample with its free-space level."""
|
|
1043
|
+
...
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
class DashboardWidget(Protocol):
|
|
1047
|
+
"""A system-pane panel plugin, registered under `WIDGET_ENTRY_POINT_GROUP`."""
|
|
1048
|
+
|
|
1049
|
+
name: str
|
|
1050
|
+
|
|
1051
|
+
def create(self, context: WidgetContext) -> Widget:
|
|
1052
|
+
"""Build this widget from its views and update subscription."""
|
|
1053
|
+
...
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
class SettingsSource(Protocol):
|
|
1057
|
+
"""The project settings a dashboard applies, read where each consumer uses them."""
|
|
1058
|
+
|
|
1059
|
+
def current(self) -> Settings:
|
|
1060
|
+
"""Return the settings applied now."""
|
|
1061
|
+
...
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
@dataclass(frozen=True)
|
|
1065
|
+
class WidgetContext:
|
|
1066
|
+
"""Data and update subscription supplied to a `DashboardWidget`."""
|
|
1067
|
+
|
|
1068
|
+
views: DashboardViews
|
|
1069
|
+
focus_task: str | None
|
|
1070
|
+
settings: SettingsSource
|
|
1071
|
+
clock: Clock
|
|
1072
|
+
on_change: OnChangeRegistration
|
|
1073
|
+
on_motion: MotionRegistration
|
|
1074
|
+
open_palette: OpenPalette
|
|
1075
|
+
open_palette_context: OpenPaletteContext | None = None
|
|
1076
|
+
open_resolution: OpenResolution | None = None
|
|
1077
|
+
open_task_detail: OpenTaskDetail | None = None
|
|
1078
|
+
open_task_detail_view: OpenTaskDetailView | None = None
|
|
1079
|
+
navigate_task_window: NavigateTaskWindow | None = None
|
|
1080
|
+
navigate_home: NavigateHome | None = None
|
|
1081
|
+
on_task_selected: SelectTaskContext | None = None
|
|
1082
|
+
activate_home_summary: Callable[[str], Awaitable[None]] | None = None
|
|
1083
|
+
placement: WidgetPlacement = WidgetPlacement.EXTENSIONS
|
|
1084
|
+
home_attention_present: bool = False
|
|
1085
|
+
open_contextual_view: OpenContextualView | None = None
|
|
1086
|
+
activate_attention_decision: ActivateAttentionDecision | None = None
|