kweaver-dolphin 0.1.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.
- DolphinLanguageSDK/__init__.py +58 -0
- dolphin/__init__.py +62 -0
- dolphin/cli/__init__.py +20 -0
- dolphin/cli/args/__init__.py +9 -0
- dolphin/cli/args/parser.py +567 -0
- dolphin/cli/builtin_agents/__init__.py +22 -0
- dolphin/cli/commands/__init__.py +4 -0
- dolphin/cli/interrupt/__init__.py +8 -0
- dolphin/cli/interrupt/handler.py +205 -0
- dolphin/cli/interrupt/keyboard.py +82 -0
- dolphin/cli/main.py +49 -0
- dolphin/cli/multimodal/__init__.py +34 -0
- dolphin/cli/multimodal/clipboard.py +327 -0
- dolphin/cli/multimodal/handler.py +249 -0
- dolphin/cli/multimodal/image_processor.py +214 -0
- dolphin/cli/multimodal/input_parser.py +149 -0
- dolphin/cli/runner/__init__.py +8 -0
- dolphin/cli/runner/runner.py +989 -0
- dolphin/cli/ui/__init__.py +10 -0
- dolphin/cli/ui/console.py +2795 -0
- dolphin/cli/ui/input.py +340 -0
- dolphin/cli/ui/layout.py +425 -0
- dolphin/cli/ui/stream_renderer.py +302 -0
- dolphin/cli/utils/__init__.py +8 -0
- dolphin/cli/utils/helpers.py +135 -0
- dolphin/cli/utils/version.py +49 -0
- dolphin/core/__init__.py +107 -0
- dolphin/core/agent/__init__.py +10 -0
- dolphin/core/agent/agent_state.py +69 -0
- dolphin/core/agent/base_agent.py +970 -0
- dolphin/core/code_block/__init__.py +0 -0
- dolphin/core/code_block/agent_init_block.py +0 -0
- dolphin/core/code_block/assign_block.py +98 -0
- dolphin/core/code_block/basic_code_block.py +1865 -0
- dolphin/core/code_block/explore_block.py +1327 -0
- dolphin/core/code_block/explore_block_v2.py +712 -0
- dolphin/core/code_block/explore_strategy.py +672 -0
- dolphin/core/code_block/judge_block.py +220 -0
- dolphin/core/code_block/prompt_block.py +32 -0
- dolphin/core/code_block/skill_call_deduplicator.py +291 -0
- dolphin/core/code_block/tool_block.py +129 -0
- dolphin/core/common/__init__.py +17 -0
- dolphin/core/common/constants.py +176 -0
- dolphin/core/common/enums.py +1173 -0
- dolphin/core/common/exceptions.py +133 -0
- dolphin/core/common/multimodal.py +539 -0
- dolphin/core/common/object_type.py +165 -0
- dolphin/core/common/output_format.py +432 -0
- dolphin/core/common/types.py +36 -0
- dolphin/core/config/__init__.py +16 -0
- dolphin/core/config/global_config.py +1289 -0
- dolphin/core/config/ontology_config.py +133 -0
- dolphin/core/context/__init__.py +12 -0
- dolphin/core/context/context.py +1580 -0
- dolphin/core/context/context_manager.py +161 -0
- dolphin/core/context/var_output.py +82 -0
- dolphin/core/context/variable_pool.py +356 -0
- dolphin/core/context_engineer/__init__.py +41 -0
- dolphin/core/context_engineer/config/__init__.py +5 -0
- dolphin/core/context_engineer/config/settings.py +402 -0
- dolphin/core/context_engineer/core/__init__.py +7 -0
- dolphin/core/context_engineer/core/budget_manager.py +327 -0
- dolphin/core/context_engineer/core/context_assembler.py +583 -0
- dolphin/core/context_engineer/core/context_manager.py +637 -0
- dolphin/core/context_engineer/core/tokenizer_service.py +260 -0
- dolphin/core/context_engineer/example/incremental_example.py +267 -0
- dolphin/core/context_engineer/example/traditional_example.py +334 -0
- dolphin/core/context_engineer/services/__init__.py +5 -0
- dolphin/core/context_engineer/services/compressor.py +399 -0
- dolphin/core/context_engineer/utils/__init__.py +6 -0
- dolphin/core/context_engineer/utils/context_utils.py +441 -0
- dolphin/core/context_engineer/utils/message_formatter.py +270 -0
- dolphin/core/context_engineer/utils/token_utils.py +139 -0
- dolphin/core/coroutine/__init__.py +15 -0
- dolphin/core/coroutine/context_snapshot.py +154 -0
- dolphin/core/coroutine/context_snapshot_profile.py +922 -0
- dolphin/core/coroutine/context_snapshot_store.py +268 -0
- dolphin/core/coroutine/execution_frame.py +145 -0
- dolphin/core/coroutine/execution_state_registry.py +161 -0
- dolphin/core/coroutine/resume_handle.py +101 -0
- dolphin/core/coroutine/step_result.py +101 -0
- dolphin/core/executor/__init__.py +18 -0
- dolphin/core/executor/debug_controller.py +630 -0
- dolphin/core/executor/dolphin_executor.py +1063 -0
- dolphin/core/executor/executor.py +624 -0
- dolphin/core/flags/__init__.py +27 -0
- dolphin/core/flags/definitions.py +49 -0
- dolphin/core/flags/manager.py +113 -0
- dolphin/core/hook/__init__.py +95 -0
- dolphin/core/hook/expression_evaluator.py +499 -0
- dolphin/core/hook/hook_dispatcher.py +380 -0
- dolphin/core/hook/hook_types.py +248 -0
- dolphin/core/hook/isolated_variable_pool.py +284 -0
- dolphin/core/interfaces.py +53 -0
- dolphin/core/llm/__init__.py +0 -0
- dolphin/core/llm/llm.py +495 -0
- dolphin/core/llm/llm_call.py +100 -0
- dolphin/core/llm/llm_client.py +1285 -0
- dolphin/core/llm/message_sanitizer.py +120 -0
- dolphin/core/logging/__init__.py +20 -0
- dolphin/core/logging/logger.py +526 -0
- dolphin/core/message/__init__.py +8 -0
- dolphin/core/message/compressor.py +749 -0
- dolphin/core/parser/__init__.py +8 -0
- dolphin/core/parser/parser.py +405 -0
- dolphin/core/runtime/__init__.py +10 -0
- dolphin/core/runtime/runtime_graph.py +926 -0
- dolphin/core/runtime/runtime_instance.py +446 -0
- dolphin/core/skill/__init__.py +14 -0
- dolphin/core/skill/context_retention.py +157 -0
- dolphin/core/skill/skill_function.py +686 -0
- dolphin/core/skill/skill_matcher.py +282 -0
- dolphin/core/skill/skillkit.py +700 -0
- dolphin/core/skill/skillset.py +72 -0
- dolphin/core/trajectory/__init__.py +10 -0
- dolphin/core/trajectory/recorder.py +189 -0
- dolphin/core/trajectory/trajectory.py +522 -0
- dolphin/core/utils/__init__.py +9 -0
- dolphin/core/utils/cache_kv.py +212 -0
- dolphin/core/utils/tools.py +340 -0
- dolphin/lib/__init__.py +93 -0
- dolphin/lib/debug/__init__.py +8 -0
- dolphin/lib/debug/visualizer.py +409 -0
- dolphin/lib/memory/__init__.py +28 -0
- dolphin/lib/memory/async_processor.py +220 -0
- dolphin/lib/memory/llm_calls.py +195 -0
- dolphin/lib/memory/manager.py +78 -0
- dolphin/lib/memory/sandbox.py +46 -0
- dolphin/lib/memory/storage.py +245 -0
- dolphin/lib/memory/utils.py +51 -0
- dolphin/lib/ontology/__init__.py +12 -0
- dolphin/lib/ontology/basic/__init__.py +0 -0
- dolphin/lib/ontology/basic/base.py +102 -0
- dolphin/lib/ontology/basic/concept.py +130 -0
- dolphin/lib/ontology/basic/object.py +11 -0
- dolphin/lib/ontology/basic/relation.py +63 -0
- dolphin/lib/ontology/datasource/__init__.py +27 -0
- dolphin/lib/ontology/datasource/datasource.py +66 -0
- dolphin/lib/ontology/datasource/oracle_datasource.py +338 -0
- dolphin/lib/ontology/datasource/sql.py +845 -0
- dolphin/lib/ontology/mapping.py +177 -0
- dolphin/lib/ontology/ontology.py +733 -0
- dolphin/lib/ontology/ontology_context.py +16 -0
- dolphin/lib/ontology/ontology_manager.py +107 -0
- dolphin/lib/skill_results/__init__.py +31 -0
- dolphin/lib/skill_results/cache_backend.py +559 -0
- dolphin/lib/skill_results/result_processor.py +181 -0
- dolphin/lib/skill_results/result_reference.py +179 -0
- dolphin/lib/skill_results/skillkit_hook.py +324 -0
- dolphin/lib/skill_results/strategies.py +328 -0
- dolphin/lib/skill_results/strategy_registry.py +150 -0
- dolphin/lib/skillkits/__init__.py +44 -0
- dolphin/lib/skillkits/agent_skillkit.py +155 -0
- dolphin/lib/skillkits/cognitive_skillkit.py +82 -0
- dolphin/lib/skillkits/env_skillkit.py +250 -0
- dolphin/lib/skillkits/mcp_adapter.py +616 -0
- dolphin/lib/skillkits/mcp_skillkit.py +771 -0
- dolphin/lib/skillkits/memory_skillkit.py +650 -0
- dolphin/lib/skillkits/noop_skillkit.py +31 -0
- dolphin/lib/skillkits/ontology_skillkit.py +89 -0
- dolphin/lib/skillkits/plan_act_skillkit.py +452 -0
- dolphin/lib/skillkits/resource/__init__.py +52 -0
- dolphin/lib/skillkits/resource/models/__init__.py +6 -0
- dolphin/lib/skillkits/resource/models/skill_config.py +109 -0
- dolphin/lib/skillkits/resource/models/skill_meta.py +127 -0
- dolphin/lib/skillkits/resource/resource_skillkit.py +393 -0
- dolphin/lib/skillkits/resource/skill_cache.py +215 -0
- dolphin/lib/skillkits/resource/skill_loader.py +395 -0
- dolphin/lib/skillkits/resource/skill_validator.py +406 -0
- dolphin/lib/skillkits/resource_skillkit.py +11 -0
- dolphin/lib/skillkits/search_skillkit.py +163 -0
- dolphin/lib/skillkits/sql_skillkit.py +274 -0
- dolphin/lib/skillkits/system_skillkit.py +509 -0
- dolphin/lib/skillkits/vm_skillkit.py +65 -0
- dolphin/lib/utils/__init__.py +9 -0
- dolphin/lib/utils/data_process.py +207 -0
- dolphin/lib/utils/handle_progress.py +178 -0
- dolphin/lib/utils/security.py +139 -0
- dolphin/lib/utils/text_retrieval.py +462 -0
- dolphin/lib/vm/__init__.py +11 -0
- dolphin/lib/vm/env_executor.py +895 -0
- dolphin/lib/vm/python_session_manager.py +453 -0
- dolphin/lib/vm/vm.py +610 -0
- dolphin/sdk/__init__.py +60 -0
- dolphin/sdk/agent/__init__.py +12 -0
- dolphin/sdk/agent/agent_factory.py +236 -0
- dolphin/sdk/agent/dolphin_agent.py +1106 -0
- dolphin/sdk/api/__init__.py +4 -0
- dolphin/sdk/runtime/__init__.py +8 -0
- dolphin/sdk/runtime/env.py +363 -0
- dolphin/sdk/skill/__init__.py +10 -0
- dolphin/sdk/skill/global_skills.py +706 -0
- dolphin/sdk/skill/traditional_toolkit.py +260 -0
- kweaver_dolphin-0.1.0.dist-info/METADATA +521 -0
- kweaver_dolphin-0.1.0.dist-info/RECORD +199 -0
- kweaver_dolphin-0.1.0.dist-info/WHEEL +5 -0
- kweaver_dolphin-0.1.0.dist-info/entry_points.txt +27 -0
- kweaver_dolphin-0.1.0.dist-info/licenses/LICENSE.txt +201 -0
- kweaver_dolphin-0.1.0.dist-info/top_level.txt +2 -0
dolphin/cli/ui/layout.py
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Layout Manager - Terminal layout management with fixed bottom status area.
|
|
3
|
+
|
|
4
|
+
This module provides the LayoutManager class which manages terminal layouts
|
|
5
|
+
with a fixed bottom status area and scrollable content region.
|
|
6
|
+
|
|
7
|
+
Architecture:
|
|
8
|
+
┌──────────────────────────────────────────────────────┐
|
|
9
|
+
│ [Scrollable Content Region] │ ← Lines 1 to (height - 3)
|
|
10
|
+
│ ✓ Tool output, logs, responses, user input │
|
|
11
|
+
│ > User input appears here in scroll region │
|
|
12
|
+
│ ... │
|
|
13
|
+
└──────────────────────────────────────────────────────┘
|
|
14
|
+
──────────────────────────────────────────────────────── ← Fixed separator at height-2
|
|
15
|
+
⠋ Status Bar (animated spinner + timer) ← Fixed at height-1
|
|
16
|
+
|
|
17
|
+
Features:
|
|
18
|
+
- ANSI scroll region for content (lines 1 to N-3)
|
|
19
|
+
- Fixed separator line between content and status
|
|
20
|
+
- Fixed status bar at bottom with spinner animation
|
|
21
|
+
- Input prompt appears INSIDE the scroll region (not fixed)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import signal
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
from typing import Optional
|
|
28
|
+
|
|
29
|
+
from dolphin.cli.ui.console import StatusBar, Theme, set_active_status_bar
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _should_enable_layout() -> bool:
|
|
33
|
+
"""Multi-check to ensure layout is only enabled in correct environments.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
True if the terminal supports advanced layout features
|
|
37
|
+
"""
|
|
38
|
+
# Not a TTY
|
|
39
|
+
if not sys.stdout.isatty():
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
# No TERM environment variable
|
|
43
|
+
if not os.environ.get('TERM'):
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
# Running in Jupyter
|
|
47
|
+
if 'ipykernel' in sys.modules:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
# Running in pytest
|
|
51
|
+
if 'pytest' in sys.modules:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
# Dumb terminal
|
|
55
|
+
if os.environ.get('TERM') == 'dumb':
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class LayoutManager:
|
|
62
|
+
"""Manages terminal layout with fixed bottom status area.
|
|
63
|
+
|
|
64
|
+
Provides a modern CLI experience with:
|
|
65
|
+
- Scrollable content region at top (including user input)
|
|
66
|
+
- Fixed separator line
|
|
67
|
+
- Fixed status bar at bottom
|
|
68
|
+
|
|
69
|
+
The layout uses ANSI escape sequences to create scroll regions.
|
|
70
|
+
User input happens INSIDE the scroll region, not in a fixed area.
|
|
71
|
+
|
|
72
|
+
Usage:
|
|
73
|
+
layout = LayoutManager(enabled=True)
|
|
74
|
+
layout.start_session("Interactive", "my_agent")
|
|
75
|
+
|
|
76
|
+
# During execution
|
|
77
|
+
layout.show_status("Processing your request...")
|
|
78
|
+
|
|
79
|
+
# Content prints normally and scrolls
|
|
80
|
+
print("Some output...")
|
|
81
|
+
|
|
82
|
+
# End session
|
|
83
|
+
layout.end_session()
|
|
84
|
+
|
|
85
|
+
Attributes:
|
|
86
|
+
enabled: Whether layout features are active
|
|
87
|
+
_status_bar: Active StatusBar instance
|
|
88
|
+
_terminal_height: Cached terminal height
|
|
89
|
+
_terminal_width: Cached terminal width
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
# Reserve lines at bottom: separator (1) + status bar (1) + buffer (1)
|
|
93
|
+
BOTTOM_RESERVE = 3
|
|
94
|
+
|
|
95
|
+
def __init__(self, enabled: bool = True):
|
|
96
|
+
"""Initialize layout manager.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
enabled: Enable layout features (auto-disabled if terminal unsupported)
|
|
100
|
+
"""
|
|
101
|
+
self.enabled = enabled and _should_enable_layout()
|
|
102
|
+
self._status_bar: Optional[StatusBar] = None
|
|
103
|
+
self._terminal_height = 24
|
|
104
|
+
self._terminal_width = 80
|
|
105
|
+
self._scroll_region_active = False
|
|
106
|
+
self._session_active = False
|
|
107
|
+
self._original_sigwinch_handler = None
|
|
108
|
+
|
|
109
|
+
def _get_terminal_size(self) -> tuple:
|
|
110
|
+
"""Get terminal dimensions.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Tuple of (height, width)
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
import shutil
|
|
117
|
+
size = shutil.get_terminal_size()
|
|
118
|
+
return size.lines, size.columns
|
|
119
|
+
except Exception:
|
|
120
|
+
return 24, 80
|
|
121
|
+
|
|
122
|
+
def _handle_resize(self, signum, frame):
|
|
123
|
+
"""Handle terminal resize event."""
|
|
124
|
+
if not self.enabled:
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
# Get new size
|
|
128
|
+
try:
|
|
129
|
+
height, width = self._get_terminal_size()
|
|
130
|
+
except:
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
if height == self._terminal_height and width == self._terminal_width:
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
old_height = self._terminal_height
|
|
137
|
+
|
|
138
|
+
# Update dimensions
|
|
139
|
+
self._terminal_height = height
|
|
140
|
+
self._terminal_width = width
|
|
141
|
+
|
|
142
|
+
# Re-setup scroll region
|
|
143
|
+
try:
|
|
144
|
+
scroll_bottom = height - self.BOTTOM_RESERVE
|
|
145
|
+
if scroll_bottom >= 5:
|
|
146
|
+
sys.stdout.write(f"\033[1;{scroll_bottom}r")
|
|
147
|
+
self._scroll_region_active = True
|
|
148
|
+
else:
|
|
149
|
+
sys.stdout.write("\033[r")
|
|
150
|
+
self._scroll_region_active = False
|
|
151
|
+
except:
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
# Update Status Bar if active
|
|
155
|
+
if self._status_bar:
|
|
156
|
+
self._status_bar.fixed_row = height - 1
|
|
157
|
+
|
|
158
|
+
# Redraw Separator
|
|
159
|
+
if self._scroll_region_active:
|
|
160
|
+
self._draw_separator()
|
|
161
|
+
|
|
162
|
+
sys.stdout.flush()
|
|
163
|
+
|
|
164
|
+
def _setup_scroll_region(self) -> None:
|
|
165
|
+
"""Setup ANSI scroll region (top portion of screen)."""
|
|
166
|
+
if not self.enabled:
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
self._terminal_height, self._terminal_width = self._get_terminal_size()
|
|
170
|
+
scroll_bottom = self._terminal_height - self.BOTTOM_RESERVE
|
|
171
|
+
|
|
172
|
+
if scroll_bottom < 5:
|
|
173
|
+
# Terminal too small, disable layout
|
|
174
|
+
self.enabled = False
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
# Push existing content up to prevent overlap with fixed region
|
|
178
|
+
sys.stdout.write("\n" * self.BOTTOM_RESERVE)
|
|
179
|
+
|
|
180
|
+
# Set scroll region: ESC[<top>;<bottom>r
|
|
181
|
+
sys.stdout.write(f"\033[1;{scroll_bottom}r")
|
|
182
|
+
# Move cursor to top of scroll region
|
|
183
|
+
sys.stdout.write("\033[1;1H")
|
|
184
|
+
sys.stdout.flush()
|
|
185
|
+
self._scroll_region_active = True
|
|
186
|
+
|
|
187
|
+
def _reset_scroll_region(self) -> None:
|
|
188
|
+
"""Reset scroll region to full screen.
|
|
189
|
+
|
|
190
|
+
This method:
|
|
191
|
+
1. Resets the scroll region to cover the entire terminal
|
|
192
|
+
2. Clears the fixed separator and status bar lines (they are now stale)
|
|
193
|
+
3. Positions cursor at a reasonable location for subsequent output
|
|
194
|
+
"""
|
|
195
|
+
if self._scroll_region_active:
|
|
196
|
+
height = self._terminal_height
|
|
197
|
+
|
|
198
|
+
# Build a single atomic output sequence
|
|
199
|
+
output_parts = [
|
|
200
|
+
"\033[r", # Reset scroll region to full screen
|
|
201
|
+
"\033[?25h", # Show cursor
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
# Clear the fixed separator line (was at height-2)
|
|
205
|
+
sep_line = height - 2
|
|
206
|
+
if sep_line > 0:
|
|
207
|
+
output_parts.append(f"\033[{sep_line};1H\033[K")
|
|
208
|
+
|
|
209
|
+
# Clear the fixed status bar line (was at height-1)
|
|
210
|
+
status_line = height - 1
|
|
211
|
+
if status_line > 0:
|
|
212
|
+
output_parts.append(f"\033[{status_line};1H\033[K")
|
|
213
|
+
|
|
214
|
+
# Clear the bottom line too (height)
|
|
215
|
+
output_parts.append(f"\033[{height};1H\033[K")
|
|
216
|
+
|
|
217
|
+
# Position cursor at what was the scroll region bottom + 1
|
|
218
|
+
# This is where new output should appear after session ends
|
|
219
|
+
scroll_bottom = height - self.BOTTOM_RESERVE
|
|
220
|
+
cursor_row = scroll_bottom + 1
|
|
221
|
+
if cursor_row > height:
|
|
222
|
+
cursor_row = height
|
|
223
|
+
output_parts.append(f"\033[{cursor_row};1H")
|
|
224
|
+
|
|
225
|
+
sys.stdout.write("".join(output_parts))
|
|
226
|
+
sys.stdout.flush()
|
|
227
|
+
self._scroll_region_active = False
|
|
228
|
+
|
|
229
|
+
def _draw_separator(self) -> None:
|
|
230
|
+
"""Draw separator line between content and status area."""
|
|
231
|
+
if not self.enabled:
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
height, width = self._get_terminal_size()
|
|
235
|
+
sep_line = height - 2 # Separator at height-2
|
|
236
|
+
|
|
237
|
+
# Draw separator with atomic ANSI sequence
|
|
238
|
+
output = (
|
|
239
|
+
f"\0337" # Save cursor (DEC)
|
|
240
|
+
f"\033[{sep_line};1H" # Move to separator line
|
|
241
|
+
f"\033[K{Theme.BORDER}{'─' * (width)}{Theme.RESET}" # Clear & Draw
|
|
242
|
+
f"\0338" # Restore cursor (DEC)
|
|
243
|
+
)
|
|
244
|
+
sys.stdout.write(output)
|
|
245
|
+
sys.stdout.flush()
|
|
246
|
+
|
|
247
|
+
def start_session(self, mode: str, agent_name: str) -> None:
|
|
248
|
+
"""Start a new session with the layout.
|
|
249
|
+
|
|
250
|
+
This displays the session banner and initializes the layout.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
mode: Session mode (e.g., "Interactive", "Execution")
|
|
254
|
+
agent_name: Name of the agent being run
|
|
255
|
+
"""
|
|
256
|
+
self._session_active = True
|
|
257
|
+
|
|
258
|
+
if self.enabled:
|
|
259
|
+
# Register resize handler
|
|
260
|
+
if hasattr(signal, 'SIGWINCH'):
|
|
261
|
+
self._original_sigwinch_handler = signal.signal(signal.SIGWINCH, self._handle_resize)
|
|
262
|
+
|
|
263
|
+
self._setup_scroll_region()
|
|
264
|
+
# Initial separator
|
|
265
|
+
self._draw_separator()
|
|
266
|
+
|
|
267
|
+
def end_session(self) -> None:
|
|
268
|
+
"""End the current session and cleanup."""
|
|
269
|
+
self._session_active = False
|
|
270
|
+
|
|
271
|
+
# Restore original signal handler
|
|
272
|
+
if hasattr(signal, 'SIGWINCH') and self._original_sigwinch_handler:
|
|
273
|
+
signal.signal(signal.SIGWINCH, self._original_sigwinch_handler)
|
|
274
|
+
self._original_sigwinch_handler = None
|
|
275
|
+
|
|
276
|
+
# Stop status bar if running
|
|
277
|
+
self.hide_status()
|
|
278
|
+
|
|
279
|
+
# Reset terminal state
|
|
280
|
+
self._reset_scroll_region()
|
|
281
|
+
|
|
282
|
+
# Note: Removed the final print() here - it caused extra blank lines
|
|
283
|
+
# The _reset_scroll_region already positions cursor properly
|
|
284
|
+
|
|
285
|
+
def show_status(
|
|
286
|
+
self,
|
|
287
|
+
message: str = "Processing",
|
|
288
|
+
hint: str = "esc to interrupt"
|
|
289
|
+
) -> StatusBar:
|
|
290
|
+
"""Show animated status bar at fixed bottom position.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
message: Status message to display
|
|
294
|
+
hint: Hint text (shown in parentheses)
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
The StatusBar instance
|
|
298
|
+
"""
|
|
299
|
+
# Stop existing status bar
|
|
300
|
+
if self._status_bar:
|
|
301
|
+
self._status_bar.stop(clear=True)
|
|
302
|
+
|
|
303
|
+
# Calculate fixed row for status bar
|
|
304
|
+
fixed_row = None
|
|
305
|
+
if self.enabled and self._scroll_region_active:
|
|
306
|
+
height, _ = self._get_terminal_size()
|
|
307
|
+
fixed_row = height - 1 # Status bar at height-1 (bottom)
|
|
308
|
+
|
|
309
|
+
StatusBar._debug_log(f"LayoutManager.show_status: enabled={self.enabled}, scroll_active={self._scroll_region_active}, fixed_row={fixed_row}")
|
|
310
|
+
|
|
311
|
+
self._status_bar = StatusBar(message=message, hint=hint, fixed_row=fixed_row)
|
|
312
|
+
self._status_bar.start()
|
|
313
|
+
|
|
314
|
+
# Register with global coordinator for LivePlanCard coordination
|
|
315
|
+
set_active_status_bar(self._status_bar)
|
|
316
|
+
|
|
317
|
+
return self._status_bar
|
|
318
|
+
|
|
319
|
+
def hide_status(self, clear: bool = True) -> None:
|
|
320
|
+
"""Hide the status bar.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
clear: Whether to clear the status bar line
|
|
324
|
+
"""
|
|
325
|
+
if self._status_bar:
|
|
326
|
+
# Check if we were using fixed row
|
|
327
|
+
fixed_row = self._status_bar.fixed_row
|
|
328
|
+
|
|
329
|
+
self._status_bar.stop(clear=False)
|
|
330
|
+
self._status_bar = None
|
|
331
|
+
|
|
332
|
+
# Unregister from global coordinator
|
|
333
|
+
set_active_status_bar(None)
|
|
334
|
+
|
|
335
|
+
if clear and fixed_row is not None:
|
|
336
|
+
# Clear the status bar row
|
|
337
|
+
sys.stdout.write("\0337") # Save cursor
|
|
338
|
+
sys.stdout.write(f"\033[{fixed_row};1H\033[K")
|
|
339
|
+
sys.stdout.write("\0338") # Restore cursor
|
|
340
|
+
sys.stdout.flush()
|
|
341
|
+
elif clear:
|
|
342
|
+
# Fallback for inline status bar
|
|
343
|
+
sys.stdout.write("\r\033[K")
|
|
344
|
+
sys.stdout.flush()
|
|
345
|
+
|
|
346
|
+
def update_status(self, message: str) -> None:
|
|
347
|
+
"""Update status bar message.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
message: New status message
|
|
351
|
+
"""
|
|
352
|
+
if self._status_bar:
|
|
353
|
+
self._status_bar.update_message(message)
|
|
354
|
+
|
|
355
|
+
def display_interrupt_prompt(self) -> None:
|
|
356
|
+
"""Display the interrupt prompt UI.
|
|
357
|
+
|
|
358
|
+
Shows a formatted prompt indicating execution was interrupted
|
|
359
|
+
and user can provide new input.
|
|
360
|
+
"""
|
|
361
|
+
print()
|
|
362
|
+
print(f"{Theme.WARNING}{'━' * 40}{Theme.RESET}")
|
|
363
|
+
print(f"{Theme.WARNING}🛑 Execution interrupted{Theme.RESET}")
|
|
364
|
+
print(f"{Theme.MUTED}Enter new instructions, or press Enter to continue{Theme.RESET}")
|
|
365
|
+
print(f"{Theme.WARNING}{'━' * 40}{Theme.RESET}")
|
|
366
|
+
|
|
367
|
+
def display_completion(self, message: str = "Completed") -> None:
|
|
368
|
+
"""Display completion message.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
message: Completion message to display
|
|
372
|
+
"""
|
|
373
|
+
print(f"\n{Theme.SUCCESS}✓ {message}{Theme.RESET}")
|
|
374
|
+
|
|
375
|
+
def display_error(self, message: str) -> None:
|
|
376
|
+
"""Display error message.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
message: Error message to display
|
|
380
|
+
"""
|
|
381
|
+
print(f"\n{Theme.ERROR}✗ {message}{Theme.RESET}")
|
|
382
|
+
|
|
383
|
+
def display_info(self, message: str) -> None:
|
|
384
|
+
"""Display info message.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
message: Info message to display
|
|
388
|
+
"""
|
|
389
|
+
print(f"{Theme.PRIMARY}ℹ {message}{Theme.RESET}")
|
|
390
|
+
|
|
391
|
+
def display_warning(self, message: str) -> None:
|
|
392
|
+
"""Display warning message.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
message: Warning message to display
|
|
396
|
+
"""
|
|
397
|
+
print(f"{Theme.WARNING}⚠ {message}{Theme.RESET}")
|
|
398
|
+
|
|
399
|
+
async def get_user_input(self, prompt: str = "> ") -> str:
|
|
400
|
+
"""Get user input asynchronously.
|
|
401
|
+
|
|
402
|
+
This uses prompt_toolkit for async input with proper terminal handling.
|
|
403
|
+
Input happens INSIDE the scroll region, not in a fixed area.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
prompt: Input prompt string
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
User input string
|
|
410
|
+
"""
|
|
411
|
+
from dolphin.cli.ui.input import prompt_conversation
|
|
412
|
+
|
|
413
|
+
# Hide status bar during input
|
|
414
|
+
if self._status_bar:
|
|
415
|
+
self._status_bar.stop(clear=True)
|
|
416
|
+
|
|
417
|
+
# Ensure cursor is visible for input
|
|
418
|
+
sys.stdout.write("\033[?25h")
|
|
419
|
+
sys.stdout.flush()
|
|
420
|
+
|
|
421
|
+
try:
|
|
422
|
+
return await prompt_conversation(prompt)
|
|
423
|
+
finally:
|
|
424
|
+
# If we are returning to a session, the caller will re-show/hide as needed
|
|
425
|
+
pass
|