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.
Files changed (319) hide show
  1. shikiru/__init__.py +1 -0
  2. shikiru/__main__.py +8 -0
  3. shikiru/agents/__init__.py +3 -0
  4. shikiru/agents/contracts.py +590 -0
  5. shikiru/agents/registry.py +87 -0
  6. shikiru/agents/sdk/__init__.py +17 -0
  7. shikiru/agents/sdk/reset_time.py +137 -0
  8. shikiru/agents/sdk/turn.py +335 -0
  9. shikiru/agents/settings.py +151 -0
  10. shikiru/agents/testing/__init__.py +26 -0
  11. shikiru/agents/testing/contract_suite.py +653 -0
  12. shikiru/agents/testing/resume.py +15 -0
  13. shikiru/bootstrap.py +1277 -0
  14. shikiru/cli/__init__.py +1 -0
  15. shikiru/cli/app.py +770 -0
  16. shikiru/cli/context.py +63 -0
  17. shikiru/cli/render.py +1475 -0
  18. shikiru/cli/report.py +156 -0
  19. shikiru/cli/session_prompt.py +63 -0
  20. shikiru/cli/view.py +131 -0
  21. shikiru/clock.py +41 -0
  22. shikiru/config/__init__.py +8 -0
  23. shikiru/config/defaults.toml +131 -0
  24. shikiru/config/loader.py +178 -0
  25. shikiru/config/settings.py +392 -0
  26. shikiru/dashboard/__init__.py +3 -0
  27. shikiru/dashboard/actions_list.py +799 -0
  28. shikiru/dashboard/activity.py +150 -0
  29. shikiru/dashboard/app.py +1765 -0
  30. shikiru/dashboard/blocker_evidence.py +122 -0
  31. shikiru/dashboard/blocker_rows.py +84 -0
  32. shikiru/dashboard/briefing_view.py +871 -0
  33. shikiru/dashboard/contract.py +1086 -0
  34. shikiru/dashboard/covered.py +32 -0
  35. shikiru/dashboard/detail.py +1209 -0
  36. shikiru/dashboard/detail_fold.py +244 -0
  37. shikiru/dashboard/detail_hint.py +68 -0
  38. shikiru/dashboard/details_list_view.py +150 -0
  39. shikiru/dashboard/diagnostics.py +80 -0
  40. shikiru/dashboard/diagnostics_sections.py +186 -0
  41. shikiru/dashboard/evidence_table.py +123 -0
  42. shikiru/dashboard/extra_details.py +214 -0
  43. shikiru/dashboard/fault_guidance.py +216 -0
  44. shikiru/dashboard/fold_bodies.py +276 -0
  45. shikiru/dashboard/help_keys.py +166 -0
  46. shikiru/dashboard/help_screen.py +204 -0
  47. shikiru/dashboard/interaction.py +17 -0
  48. shikiru/dashboard/ladder_editor.py +544 -0
  49. shikiru/dashboard/live_settings.py +62 -0
  50. shikiru/dashboard/motion.py +368 -0
  51. shikiru/dashboard/orchestration_fields.py +201 -0
  52. shikiru/dashboard/palette.py +1606 -0
  53. shikiru/dashboard/palette_app.py +89 -0
  54. shikiru/dashboard/palette_drafts.py +33 -0
  55. shikiru/dashboard/resolution.py +573 -0
  56. shikiru/dashboard/resolution_view.py +302 -0
  57. shikiru/dashboard/row_list.py +256 -0
  58. shikiru/dashboard/section_rows.py +173 -0
  59. shikiru/dashboard/services.py +665 -0
  60. shikiru/dashboard/session_history.py +402 -0
  61. shikiru/dashboard/shikiru_init_ascii_art.txt +40 -0
  62. shikiru/dashboard/task_picker.py +358 -0
  63. shikiru/dashboard/tick.py +59 -0
  64. shikiru/dashboard/timeline_filter.py +129 -0
  65. shikiru/dashboard/timeline_section.py +385 -0
  66. shikiru/dashboard/usage_body.py +269 -0
  67. shikiru/dashboard/views.py +2314 -0
  68. shikiru/dashboard/wizard.py +1389 -0
  69. shikiru/domain/__init__.py +1 -0
  70. shikiru/domain/acceptance.py +130 -0
  71. shikiru/domain/activity.py +28 -0
  72. shikiru/domain/attempts.py +333 -0
  73. shikiru/domain/briefing.py +257 -0
  74. shikiru/domain/commands.py +219 -0
  75. shikiru/domain/decisions.py +231 -0
  76. shikiru/domain/docker.py +262 -0
  77. shikiru/domain/engine_uptime.py +87 -0
  78. shikiru/domain/escalation.py +194 -0
  79. shikiru/domain/escalation_evidence.py +186 -0
  80. shikiru/domain/escalation_panel.py +144 -0
  81. shikiru/domain/events.py +71 -0
  82. shikiru/domain/evidence_export.py +89 -0
  83. shikiru/domain/faults.py +86 -0
  84. shikiru/domain/feedback.py +180 -0
  85. shikiru/domain/feedback_rounds.py +172 -0
  86. shikiru/domain/fencing.py +31 -0
  87. shikiru/domain/ladder.py +102 -0
  88. shikiru/domain/ladder_text.py +44 -0
  89. shikiru/domain/levels.py +44 -0
  90. shikiru/domain/motion.py +199 -0
  91. shikiru/domain/packet_evidence.py +377 -0
  92. shikiru/domain/pushes.py +47 -0
  93. shikiru/domain/quota_blocks.py +44 -0
  94. shikiru/domain/quota_waits.py +219 -0
  95. shikiru/domain/recovery.py +194 -0
  96. shikiru/domain/reports.py +498 -0
  97. shikiru/domain/results.py +217 -0
  98. shikiru/domain/runs.py +85 -0
  99. shikiru/domain/scheduling.py +231 -0
  100. shikiru/domain/status_tasks.py +40 -0
  101. shikiru/domain/steps.py +325 -0
  102. shikiru/domain/tasks.py +174 -0
  103. shikiru/domain/timeline.py +208 -0
  104. shikiru/domain/timeline_phrases.py +271 -0
  105. shikiru/domain/versions.py +18 -0
  106. shikiru/domain/workflow.py +372 -0
  107. shikiru/engine/__init__.py +0 -0
  108. shikiru/engine/acceptance.py +90 -0
  109. shikiru/engine/agents.py +61 -0
  110. shikiru/engine/cosmetic_decorator.py +99 -0
  111. shikiru/engine/daemon.py +409 -0
  112. shikiru/engine/dependency.py +37 -0
  113. shikiru/engine/escalation_filing.py +110 -0
  114. shikiru/engine/escalations.py +401 -0
  115. shikiru/engine/events.py +285 -0
  116. shikiru/engine/evidence.py +81 -0
  117. shikiru/engine/fault_reporter.py +144 -0
  118. shikiru/engine/feedback.py +577 -0
  119. shikiru/engine/feedback_audit.py +80 -0
  120. shikiru/engine/feedback_intake.py +95 -0
  121. shikiru/engine/feedback_snapshot.py +148 -0
  122. shikiru/engine/flow.py +87 -0
  123. shikiru/engine/handlers/__init__.py +33 -0
  124. shikiru/engine/handlers/base.py +148 -0
  125. shikiru/engine/handlers/engine.py +24 -0
  126. shikiru/engine/handlers/escalations.py +174 -0
  127. shikiru/engine/handlers/operator.py +81 -0
  128. shikiru/engine/handlers/tasks.py +237 -0
  129. shikiru/engine/history.py +128 -0
  130. shikiru/engine/integration.py +1234 -0
  131. shikiru/engine/integration_turns.py +337 -0
  132. shikiru/engine/layout_guard.py +362 -0
  133. shikiru/engine/lifecycle.py +60 -0
  134. shikiru/engine/monitor.py +317 -0
  135. shikiru/engine/notification_delivery.py +124 -0
  136. shikiru/engine/notification_feed.py +57 -0
  137. shikiru/engine/notifier.py +91 -0
  138. shikiru/engine/pull_request_closing.py +117 -0
  139. shikiru/engine/push_service.py +177 -0
  140. shikiru/engine/quota_probe_threads.py +79 -0
  141. shikiru/engine/quota_reconciler.py +210 -0
  142. shikiru/engine/quota_schedule.py +24 -0
  143. shikiru/engine/quota_waits.py +102 -0
  144. shikiru/engine/reconcile.py +91 -0
  145. shikiru/engine/recovery.py +284 -0
  146. shikiru/engine/retention.py +102 -0
  147. shikiru/engine/root_preparation.py +86 -0
  148. shikiru/engine/scheduler.py +320 -0
  149. shikiru/engine/session_guard.py +46 -0
  150. shikiru/engine/sessions.py +95 -0
  151. shikiru/engine/signalled.py +75 -0
  152. shikiru/engine/step_runner.py +140 -0
  153. shikiru/engine/supervisor.py +101 -0
  154. shikiru/engine/takeover.py +223 -0
  155. shikiru/engine/task_facts.py +46 -0
  156. shikiru/engine/tick_guard.py +95 -0
  157. shikiru/engine/titles.py +297 -0
  158. shikiru/engine/transitions.py +79 -0
  159. shikiru/engine/turn_context.py +206 -0
  160. shikiru/engine/turn_execution.py +30 -0
  161. shikiru/engine/turn_loop.py +230 -0
  162. shikiru/engine/turn_processes.py +79 -0
  163. shikiru/engine/turns.py +457 -0
  164. shikiru/errors.py +144 -0
  165. shikiru/helper/__init__.py +3 -0
  166. shikiru/helper/helper.py +182 -0
  167. shikiru/helper/models.py +11 -0
  168. shikiru/mcp/__init__.py +1 -0
  169. shikiru/mcp/server.py +196 -0
  170. shikiru/operator/__init__.py +33 -0
  171. shikiru/operator/attachment.py +24 -0
  172. shikiru/operator/commands.py +319 -0
  173. shikiru/operator/detectors.py +374 -0
  174. shikiru/operator/doctor.py +351 -0
  175. shikiru/operator/events.py +57 -0
  176. shikiru/operator/evidence_export.py +498 -0
  177. shikiru/operator/evidence_writer.py +78 -0
  178. shikiru/operator/lifecycle.py +294 -0
  179. shikiru/operator/plugin_checks.py +194 -0
  180. shikiru/operator/probe.py +287 -0
  181. shikiru/operator/queries.py +1601 -0
  182. shikiru/operator/services.py +36 -0
  183. shikiru/operator/sessions.py +378 -0
  184. shikiru/operator/strip_views.py +162 -0
  185. shikiru/operator/timeline.py +416 -0
  186. shikiru/operator/toml_text.py +62 -0
  187. shikiru/palette.py +18 -0
  188. shikiru/paths.py +91 -0
  189. shikiru/plugins/__init__.py +0 -0
  190. shikiru/plugins/agents/__init__.py +1 -0
  191. shikiru/plugins/agents/claude/__init__.py +5 -0
  192. shikiru/plugins/agents/claude/agent.py +251 -0
  193. shikiru/plugins/agents/claude/context.py +116 -0
  194. shikiru/plugins/agents/claude/headless.py +92 -0
  195. shikiru/plugins/agents/claude/hook.py +57 -0
  196. shikiru/plugins/agents/claude/model_catalog.py +105 -0
  197. shikiru/plugins/agents/claude/options.py +62 -0
  198. shikiru/plugins/agents/claude/turn.py +1018 -0
  199. shikiru/plugins/agents/codex/__init__.py +5 -0
  200. shikiru/plugins/agents/codex/agent.py +486 -0
  201. shikiru/plugins/agents/codex/headless.py +166 -0
  202. shikiru/plugins/agents/codex/home.py +14 -0
  203. shikiru/plugins/agents/codex/model_catalog.py +128 -0
  204. shikiru/plugins/agents/codex/options.py +94 -0
  205. shikiru/plugins/agents/codex/quota.py +195 -0
  206. shikiru/plugins/agents/codex/sandbox.py +31 -0
  207. shikiru/plugins/agents/codex/sandbox_probe.py +97 -0
  208. shikiru/plugins/agents/codex/turn.py +767 -0
  209. shikiru/plugins/agents/codex/usage.py +106 -0
  210. shikiru/plugins/agents/shared_settings.py +62 -0
  211. shikiru/plugins/widgets/__init__.py +3 -0
  212. shikiru/plugins/widgets/dialogue.py +144 -0
  213. shikiru/plugins/widgets/disk.py +123 -0
  214. shikiru/plugins/widgets/docker.py +225 -0
  215. shikiru/plugins/widgets/escalations.py +402 -0
  216. shikiru/plugins/widgets/faults.py +204 -0
  217. shikiru/plugins/widgets/header.py +321 -0
  218. shikiru/plugins/widgets/queue.py +1140 -0
  219. shikiru/plugins/widgets/queue_hint.py +93 -0
  220. shikiru/plugins/widgets/resources.py +105 -0
  221. shikiru/plugins/widgets/strips.py +82 -0
  222. shikiru/plugins/widgets/usage.py +176 -0
  223. shikiru/private.py +94 -0
  224. shikiru/process.py +535 -0
  225. shikiru/prompts/__init__.py +3 -0
  226. shikiru/prompts/renderer.py +85 -0
  227. shikiru/prompts/templates/_base.md.j2 +17 -0
  228. shikiru/prompts/templates/_macros.md.j2 +61 -0
  229. shikiru/prompts/templates/conflicts.md.j2 +22 -0
  230. shikiru/prompts/templates/consult.md.j2 +5 -0
  231. shikiru/prompts/templates/continue.md.j2 +4 -0
  232. shikiru/prompts/templates/feedback_analysis.md.j2 +29 -0
  233. shikiru/prompts/templates/feedback_rework.md.j2 +33 -0
  234. shikiru/prompts/templates/feedback_verify.md.j2 +33 -0
  235. shikiru/prompts/templates/handoff.md.j2 +23 -0
  236. shikiru/prompts/templates/implement.md.j2 +13 -0
  237. shikiru/prompts/templates/plan.md.j2 +30 -0
  238. shikiru/prompts/templates/rework.md.j2 +13 -0
  239. shikiru/prompts/templates/triage.md.j2 +20 -0
  240. shikiru/prompts/templates/verify.md.j2 +29 -0
  241. shikiru/store/__init__.py +1 -0
  242. shikiru/store/commands.py +242 -0
  243. shikiru/store/database.py +297 -0
  244. shikiru/store/escalations.py +240 -0
  245. shikiru/store/events.py +213 -0
  246. shikiru/store/feedback.py +266 -0
  247. shikiru/store/migrations/001_initial.sql +63 -0
  248. shikiru/store/migrations/002_headless_turns.sql +31 -0
  249. shikiru/store/migrations/003_drop_command_handled_at.sql +1 -0
  250. shikiru/store/migrations/004_repo_base_ref.sql +3 -0
  251. shikiru/store/migrations/005_complexity_explicit.sql +3 -0
  252. shikiru/store/migrations/006_events_kind_index.sql +1 -0
  253. shikiru/store/migrations/007_notification_cursor.sql +1 -0
  254. shikiru/store/migrations/008_reported_usage.sql +1 -0
  255. shikiru/store/migrations/009_billing_declarations.sql +2 -0
  256. shikiru/store/migrations/010_canonical_quota_windows.sql +3 -0
  257. shikiru/store/migrations/012_drop_task_quota_evidence.sql +2 -0
  258. shikiru/store/migrations/013_push_targets.sql +7 -0
  259. shikiru/store/migrations/014_agent_block_time.sql +6 -0
  260. shikiru/store/migrations/015_repair_orphan_agent_blocks.sql +3 -0
  261. shikiru/store/migrations/016_feedback_rounds.sql +29 -0
  262. shikiru/store/migrations/017_feedback_round_comments.sql +5 -0
  263. shikiru/store/migrations/__init__.py +1 -0
  264. shikiru/store/notification_cursor.py +29 -0
  265. shikiru/store/pushes.py +117 -0
  266. shikiru/store/quotas.py +153 -0
  267. shikiru/store/reader.py +310 -0
  268. shikiru/store/retention.py +92 -0
  269. shikiru/store/runs.py +227 -0
  270. shikiru/store/tasks.py +949 -0
  271. shikiru/telemetry/__init__.py +1 -0
  272. shikiru/telemetry/disk.py +167 -0
  273. shikiru/telemetry/docker.py +164 -0
  274. shikiru/telemetry/resources.py +296 -0
  275. shikiru/tmux/__init__.py +3 -0
  276. shikiru/tmux/client.py +637 -0
  277. shikiru/tmux/decorator.py +225 -0
  278. shikiru/tmux/layout.py +200 -0
  279. shikiru/tmux/session.py +381 -0
  280. shikiru/ui/__init__.py +9 -0
  281. shikiru/ui/age.py +45 -0
  282. shikiru/ui/bubbles.py +41 -0
  283. shikiru/ui/compact.py +1209 -0
  284. shikiru/ui/console.py +49 -0
  285. shikiru/ui/fold.py +50 -0
  286. shikiru/ui/gated_static.py +117 -0
  287. shikiru/ui/gauge.py +40 -0
  288. shikiru/ui/glyphs.py +109 -0
  289. shikiru/ui/keys.py +57 -0
  290. shikiru/ui/live_interval.py +42 -0
  291. shikiru/ui/popup.py +311 -0
  292. shikiru/ui/quota_status.py +26 -0
  293. shikiru/ui/recent_window.py +78 -0
  294. shikiru/ui/screen.py +57 -0
  295. shikiru/ui/section.py +193 -0
  296. shikiru/ui/session.py +65 -0
  297. shikiru/ui/size.py +17 -0
  298. shikiru/ui/splash.py +42 -0
  299. shikiru/ui/status.py +47 -0
  300. shikiru/ui/status_strip.py +149 -0
  301. shikiru/ui/stream.py +152 -0
  302. shikiru/ui/strip.py +332 -0
  303. shikiru/ui/theme.py +83 -0
  304. shikiru/ui/tokens.py +51 -0
  305. shikiru/view_log.py +126 -0
  306. shikiru/workspace/__init__.py +3 -0
  307. shikiru/workspace/base_refs.py +160 -0
  308. shikiru/workspace/caches.py +136 -0
  309. shikiru/workspace/facts.py +118 -0
  310. shikiru/workspace/git.py +601 -0
  311. shikiru/workspace/github.py +744 -0
  312. shikiru/workspace/messages.py +107 -0
  313. shikiru/workspace/reports.py +145 -0
  314. shikiru/workspace/roots.py +180 -0
  315. shikiru_cli-0.2.0.dist-info/METADATA +428 -0
  316. shikiru_cli-0.2.0.dist-info/RECORD +319 -0
  317. shikiru_cli-0.2.0.dist-info/WHEEL +4 -0
  318. shikiru_cli-0.2.0.dist-info/entry_points.txt +18 -0
  319. 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