minion-code 0.1.0__py3-none-any.whl → 0.1.2__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 (115) hide show
  1. examples/cli_entrypoint.py +60 -0
  2. examples/{agent_with_todos.py → components/agent_with_todos.py} +58 -47
  3. examples/{message_response_children_demo.py → components/message_response_children_demo.py} +61 -55
  4. examples/components/messages_component.py +199 -0
  5. examples/file_freshness_example.py +22 -22
  6. examples/file_watching_example.py +32 -26
  7. examples/interruptible_tui.py +921 -3
  8. examples/repl_tui.py +129 -0
  9. examples/skills/example_usage.py +57 -0
  10. examples/start.py +173 -0
  11. minion_code/__init__.py +1 -1
  12. minion_code/acp_server/__init__.py +34 -0
  13. minion_code/acp_server/agent.py +539 -0
  14. minion_code/acp_server/hooks.py +354 -0
  15. minion_code/acp_server/main.py +194 -0
  16. minion_code/acp_server/permissions.py +142 -0
  17. minion_code/acp_server/test_client.py +104 -0
  18. minion_code/adapters/__init__.py +22 -0
  19. minion_code/adapters/output_adapter.py +207 -0
  20. minion_code/adapters/rich_adapter.py +169 -0
  21. minion_code/adapters/textual_adapter.py +254 -0
  22. minion_code/agents/__init__.py +2 -2
  23. minion_code/agents/code_agent.py +517 -104
  24. minion_code/agents/hooks.py +378 -0
  25. minion_code/cli.py +538 -429
  26. minion_code/cli_simple.py +665 -0
  27. minion_code/commands/__init__.py +136 -29
  28. minion_code/commands/clear_command.py +19 -46
  29. minion_code/commands/help_command.py +33 -49
  30. minion_code/commands/history_command.py +37 -55
  31. minion_code/commands/model_command.py +194 -0
  32. minion_code/commands/quit_command.py +9 -12
  33. minion_code/commands/resume_command.py +181 -0
  34. minion_code/commands/skill_command.py +89 -0
  35. minion_code/commands/status_command.py +48 -73
  36. minion_code/commands/tools_command.py +54 -52
  37. minion_code/commands/version_command.py +34 -69
  38. minion_code/components/ConfirmDialog.py +430 -0
  39. minion_code/components/Message.py +318 -97
  40. minion_code/components/MessageResponse.py +30 -29
  41. minion_code/components/Messages.py +351 -0
  42. minion_code/components/PromptInput.py +499 -245
  43. minion_code/components/__init__.py +24 -17
  44. minion_code/const.py +7 -0
  45. minion_code/screens/REPL.py +1453 -469
  46. minion_code/screens/__init__.py +1 -1
  47. minion_code/services/__init__.py +20 -20
  48. minion_code/services/event_system.py +19 -14
  49. minion_code/services/file_freshness_service.py +223 -170
  50. minion_code/skills/__init__.py +25 -0
  51. minion_code/skills/skill.py +128 -0
  52. minion_code/skills/skill_loader.py +198 -0
  53. minion_code/skills/skill_registry.py +177 -0
  54. minion_code/subagents/__init__.py +31 -0
  55. minion_code/subagents/builtin/__init__.py +30 -0
  56. minion_code/subagents/builtin/claude_code_guide.py +32 -0
  57. minion_code/subagents/builtin/explore.py +36 -0
  58. minion_code/subagents/builtin/general_purpose.py +19 -0
  59. minion_code/subagents/builtin/plan.py +61 -0
  60. minion_code/subagents/subagent.py +116 -0
  61. minion_code/subagents/subagent_loader.py +147 -0
  62. minion_code/subagents/subagent_registry.py +151 -0
  63. minion_code/tools/__init__.py +8 -2
  64. minion_code/tools/bash_tool.py +16 -3
  65. minion_code/tools/file_edit_tool.py +201 -104
  66. minion_code/tools/file_read_tool.py +183 -26
  67. minion_code/tools/file_write_tool.py +17 -3
  68. minion_code/tools/glob_tool.py +23 -2
  69. minion_code/tools/grep_tool.py +229 -21
  70. minion_code/tools/ls_tool.py +28 -3
  71. minion_code/tools/multi_edit_tool.py +89 -84
  72. minion_code/tools/python_interpreter_tool.py +9 -1
  73. minion_code/tools/skill_tool.py +210 -0
  74. minion_code/tools/task_tool.py +287 -0
  75. minion_code/tools/todo_read_tool.py +28 -24
  76. minion_code/tools/todo_write_tool.py +82 -65
  77. minion_code/{types.py → type_defs.py} +15 -2
  78. minion_code/utils/__init__.py +45 -17
  79. minion_code/utils/config.py +610 -0
  80. minion_code/utils/history.py +114 -0
  81. minion_code/utils/logs.py +53 -0
  82. minion_code/utils/mcp_loader.py +153 -55
  83. minion_code/utils/output_truncator.py +233 -0
  84. minion_code/utils/session_storage.py +369 -0
  85. minion_code/utils/todo_file_utils.py +26 -22
  86. minion_code/utils/todo_storage.py +43 -33
  87. minion_code/web/__init__.py +9 -0
  88. minion_code/web/adapters/__init__.py +5 -0
  89. minion_code/web/adapters/web_adapter.py +524 -0
  90. minion_code/web/api/__init__.py +7 -0
  91. minion_code/web/api/chat.py +277 -0
  92. minion_code/web/api/interactions.py +136 -0
  93. minion_code/web/api/sessions.py +135 -0
  94. minion_code/web/server.py +149 -0
  95. minion_code/web/services/__init__.py +5 -0
  96. minion_code/web/services/session_manager.py +420 -0
  97. minion_code-0.1.2.dist-info/METADATA +476 -0
  98. minion_code-0.1.2.dist-info/RECORD +111 -0
  99. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/WHEEL +1 -1
  100. minion_code-0.1.2.dist-info/entry_points.txt +6 -0
  101. tests/test_adapter.py +67 -0
  102. tests/test_adapter_simple.py +79 -0
  103. tests/test_file_read_tool.py +144 -0
  104. tests/test_readonly_tools.py +0 -2
  105. tests/test_skills.py +441 -0
  106. examples/advance_tui.py +0 -508
  107. examples/rich_example.py +0 -4
  108. examples/simple_file_watching.py +0 -57
  109. examples/simple_tui.py +0 -267
  110. examples/simple_usage.py +0 -69
  111. minion_code-0.1.0.dist-info/METADATA +0 -350
  112. minion_code-0.1.0.dist-info/RECORD +0 -59
  113. minion_code-0.1.0.dist-info/entry_points.txt +0 -4
  114. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/licenses/LICENSE +0 -0
  115. {minion_code-0.1.0.dist-info → minion_code-0.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ CLI Entry Point
5
+
6
+ This is the main entry point that provides access to all CLI interfaces:
7
+ - Modern TUI REPL (default)
8
+ - Traditional console CLI
9
+ - Direct command execution
10
+ """
11
+
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ # Add project root to path
16
+ sys.path.insert(0, str(Path(__file__).parent.parent))
17
+
18
+
19
+ def run():
20
+ """Main CLI entry point"""
21
+ try:
22
+ from minion_code.cli import app
23
+
24
+ app()
25
+ except Exception as e:
26
+ print(f"❌ Error starting CLI: {e}")
27
+ sys.exit(1)
28
+
29
+
30
+ def run_repl():
31
+ """Direct REPL entry point"""
32
+ try:
33
+ from minion_code.screens.REPL import run as run_repl_func
34
+
35
+ run_repl_func()
36
+ except ImportError as e:
37
+ print(f"❌ TUI dependencies not available: {e}")
38
+ print("💡 Install with: pip install textual rich")
39
+ # Fallback to console
40
+ from minion_code.cli_simple import app
41
+
42
+ app()
43
+ except Exception as e:
44
+ print(f"❌ Error starting REPL: {e}")
45
+ sys.exit(1)
46
+
47
+
48
+ def run_console():
49
+ """Direct console CLI entry point"""
50
+ try:
51
+ from minion_code.cli_simple import app
52
+
53
+ app()
54
+ except Exception as e:
55
+ print(f"❌ Error starting console CLI: {e}")
56
+ sys.exit(1)
57
+
58
+
59
+ if __name__ == "__main__":
60
+ run()
@@ -8,7 +8,7 @@ import uuid
8
8
  import json
9
9
 
10
10
  # Add the parent directory to the path so we can import minion_code
11
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
11
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
12
12
 
13
13
  from minion_code.agents.code_agent import MinionCodeAgent
14
14
 
@@ -16,84 +16,91 @@ from minion_code.agents.code_agent import MinionCodeAgent
16
16
  async def demonstrate_todo_workflow():
17
17
  """Demonstrate using Todo tools with MinionCodeAgent."""
18
18
  print("=== MinionCodeAgent Todo Workflow Demo ===\n")
19
-
19
+
20
20
  # Create agent
21
21
  print("1. Creating MinionCodeAgent...")
22
- agent = await MinionCodeAgent.create(
23
- name="Todo Demo Agent",
24
- llm="gpt-4o-mini"
25
- )
22
+ agent = await MinionCodeAgent.create(name="Todo Demo Agent", llm="gpt-4o-mini")
26
23
  print("✓ Agent created successfully\n")
27
-
24
+
28
25
  # Show available tools
29
26
  print("2. Available tools:")
30
27
  tools_info = agent.get_tools_info()
31
- todo_tools = [tool for tool in tools_info if 'todo' in tool['name'].lower()]
28
+ todo_tools = [tool for tool in tools_info if "todo" in tool["name"].lower()]
32
29
  for tool in todo_tools:
33
- readonly_icon = "🔒" if tool['readonly'] else "✏️"
30
+ readonly_icon = "🔒" if tool["readonly"] else "✏️"
34
31
  print(f" {readonly_icon} {tool['name']}: {tool['description']}")
35
32
  print()
36
-
33
+
37
34
  # Define project tasks
38
35
  tasks = [
39
36
  "Analyze project requirements",
40
- "Design system architecture",
37
+ "Design system architecture",
41
38
  "Implement core functionality",
42
39
  "Write unit tests",
43
40
  "Create documentation",
44
- "Deploy to production"
41
+ "Deploy to production",
45
42
  ]
46
-
43
+
47
44
  # Create initial todos using agent
48
45
  print("3. Creating initial todos...")
49
46
  todos_data = []
50
47
  for i, task in enumerate(tasks):
51
- todos_data.append({
52
- "id": str(uuid.uuid4()),
53
- "content": task,
54
- "status": "pending" if i > 0 else "in_progress", # First task in progress
55
- "priority": "high" if i < 2 else "medium"
56
- })
57
-
48
+ todos_data.append(
49
+ {
50
+ "id": str(uuid.uuid4()),
51
+ "content": task,
52
+ "status": (
53
+ "pending" if i > 0 else "in_progress"
54
+ ), # First task in progress
55
+ "priority": "high" if i < 2 else "medium",
56
+ }
57
+ )
58
+
58
59
  todos_json = json.dumps(todos_data)
59
-
60
+
60
61
  # Use agent to create todos
61
62
  create_message = f"""Please use the todo_write tool to create these todos:
62
63
  {todos_json}"""
63
-
64
+
64
65
  response = await agent.run_async(create_message)
65
- print(f"✓ Agent response: {response.answer if hasattr(response, 'answer') else str(response)}\n")
66
-
66
+ print(
67
+ f"✓ Agent response: {response.answer if hasattr(response, 'answer') else str(response)}\n"
68
+ )
69
+
67
70
  # Read current todos
68
71
  print("4. Reading current todos...")
69
72
  read_message = """Please use the todo_read tool to show current todos."""
70
-
73
+
71
74
  response = await agent.run_async(read_message)
72
- print(f"✓ Current todos:\n{response.answer if hasattr(response, 'answer') else str(response)}\n")
73
-
75
+ print(
76
+ f"✓ Current todos:\n{response.answer if hasattr(response, 'answer') else str(response)}\n"
77
+ )
78
+
74
79
  # Simulate completing a few tasks
75
80
  print("5. Completing tasks...")
76
-
81
+
77
82
  # Complete first task and start next
78
83
  for i in range(3): # Complete 3 tasks
79
84
  print(f" Step {i+1}: Completing current task and starting next...")
80
-
85
+
81
86
  # Get current todos from storage to update them
82
87
  from minion_code.utils.todo_storage import get_todos, TodoStatus
88
+
83
89
  # Use the same agent ID logic as the tool
84
90
  import hashlib
91
+
85
92
  agent_id = "demo_agent" # We'll use a consistent ID for this demo
86
93
  current_todos = get_todos(agent_id)
87
-
94
+
88
95
  if not current_todos:
89
96
  print(" No todos found")
90
97
  break
91
-
98
+
92
99
  # Update todos: complete in_progress, start next pending
93
100
  updated_todos = []
94
101
  found_in_progress = False
95
102
  started_next = False
96
-
103
+
97
104
  for todo in current_todos:
98
105
  if todo.status == TodoStatus.IN_PROGRESS:
99
106
  # Mark as completed
@@ -101,7 +108,7 @@ async def demonstrate_todo_workflow():
101
108
  "id": todo.id,
102
109
  "content": todo.content,
103
110
  "status": "completed",
104
- "priority": todo.priority.value
111
+ "priority": todo.priority.value,
105
112
  }
106
113
  found_in_progress = True
107
114
  elif todo.status == TodoStatus.PENDING and not started_next:
@@ -110,7 +117,7 @@ async def demonstrate_todo_workflow():
110
117
  "id": todo.id,
111
118
  "content": todo.content,
112
119
  "status": "in_progress",
113
- "priority": todo.priority.value
120
+ "priority": todo.priority.value,
114
121
  }
115
122
  started_next = True
116
123
  else:
@@ -119,32 +126,36 @@ async def demonstrate_todo_workflow():
119
126
  "id": todo.id,
120
127
  "content": todo.content,
121
128
  "status": todo.status.value,
122
- "priority": todo.priority.value
129
+ "priority": todo.priority.value,
123
130
  }
124
-
131
+
125
132
  updated_todos.append(todo_data)
126
-
133
+
127
134
  if not found_in_progress:
128
135
  print(" No in_progress task found")
129
136
  break
130
-
137
+
131
138
  # Update todos using agent
132
139
  updated_json = json.dumps(updated_todos)
133
140
  update_message = f"""Please use the todo_write tool to update todos:
134
141
  {updated_json}"""
135
-
142
+
136
143
  response = await agent.run_async(update_message)
137
- print(f" ✓ Updated: {response.answer if hasattr(response, 'answer') else str(response)}")
138
-
144
+ print(
145
+ f" ✓ Updated: {response.answer if hasattr(response, 'answer') else str(response)}"
146
+ )
147
+
139
148
  print()
140
-
149
+
141
150
  # Final status
142
151
  print("6. Final todo status...")
143
152
  final_message = """Please use the todo_read tool to show final todos."""
144
-
153
+
145
154
  response = await agent.run_async(final_message)
146
- print(f"✓ Final todos:\n{response.answer if hasattr(response, 'answer') else str(response)}\n")
147
-
155
+ print(
156
+ f"✓ Final todos:\n{response.answer if hasattr(response, 'answer') else str(response)}\n"
157
+ )
158
+
148
159
  # Show conversation history
149
160
  print("7. Conversation summary:")
150
161
  history = agent.get_conversation_history()
@@ -152,7 +163,7 @@ async def demonstrate_todo_workflow():
152
163
  for i, interaction in enumerate(history, 1):
153
164
  print(f" {i}. User: {interaction['user_message'][:50]}...")
154
165
  print(f" Agent: {str(interaction['agent_response'])[:50]}...")
155
-
166
+
156
167
  print("\n=== Demo Complete ===")
157
168
 
158
169
 
@@ -162,4 +173,4 @@ def main():
162
173
 
163
174
 
164
175
  if __name__ == "__main__":
165
- main()
176
+ main()
@@ -13,15 +13,20 @@ from textual.widgets import Header, Footer, Button, Static
13
13
  # Import the components
14
14
  import sys
15
15
  import os
16
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
16
+
17
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
17
18
 
18
19
  from minion_code.components import Message, MessageResponse
19
- from minion_code.types import Message as MessageType, MessageContent, MessageType as MsgType
20
+ from minion_code.type_defs import (
21
+ Message as MessageType,
22
+ MessageContent,
23
+ MessageType as MsgType,
24
+ )
20
25
 
21
26
 
22
27
  class MessageResponseChildrenDemo(App):
23
28
  """Demo showing MessageResponse children usage like React"""
24
-
29
+
25
30
  CSS = """
26
31
  Screen {
27
32
  background: $surface;
@@ -46,79 +51,82 @@ class MessageResponseChildrenDemo(App):
46
51
  background: $surface-lighten-1;
47
52
  }
48
53
  """
49
-
54
+
50
55
  def compose(self) -> ComposeResult:
51
56
  """Compose the demo interface"""
52
57
  yield Header()
53
-
58
+
54
59
  with Container(classes="demo-container"):
55
60
  with ScrollableContainer(classes="messages-container", id="messages"):
56
61
  # Show examples of different usage patterns
57
62
  yield from self._create_usage_examples()
58
-
63
+
59
64
  # with Container(classes="controls"):
60
65
  # yield Button("Add Tool Progress", id="add_tool_progress", variant="primary")
61
66
  # yield Button("Add Nested Response", id="add_nested", variant="success")
62
67
  # yield Button("Add Multiple Children", id="add_multiple", variant="warning")
63
68
  # yield Button("Add Dynamic Mount", id="add_dynamic", variant="default")
64
69
  # yield Button("Clear", id="clear", variant="error")
65
-
70
+
66
71
  yield Footer()
67
72
  self.log(self.tree)
68
-
73
+
69
74
  def _create_usage_examples(self):
70
75
  """Create examples showing different MessageResponse usage patterns"""
71
76
  child_msg = MessageType(
72
77
  type=MsgType.ASSISTANT,
73
78
  message=MessageContent("This message is wrapped in MessageResponse"),
74
- timestamp=time.time()
79
+ timestamp=time.time(),
75
80
  )
76
81
  yield Message(child_msg, classes="example-header")
77
- yield Static("\n2. Single child message (React-like):", classes="example-header")
82
+ yield Static(
83
+ "\n2. Single child message (React-like):", classes="example-header"
84
+ )
78
85
 
79
86
  # This is equivalent to: <MessageResponse><Message /></MessageResponse>
80
87
 
81
88
  yield MessageResponse(children=[Message(child_msg, classes="example-header")])
82
-
89
+
83
90
  # Example 3: Tool execution progress
84
91
  yield Static("\n3. Tool execution progress:", classes="example-header")
85
92
  tool_msg = MessageType(
86
93
  type=MsgType.TOOL_USE,
87
- message=MessageContent([{
88
- "type": "tool_use",
89
- "id": "tool_demo",
90
- "name": "file_editor",
91
- "input": {"path": "demo.py", "content": "print('Hello')"}
92
- }]),
93
- timestamp=time.time()
94
+ message=MessageContent(
95
+ [
96
+ {
97
+ "type": "tool_use",
98
+ "id": "tool_demo",
99
+ "name": "file_editor",
100
+ "input": {"path": "demo.py", "content": "print('Hello')"},
101
+ }
102
+ ]
103
+ ),
104
+ timestamp=time.time(),
94
105
  )
95
106
  yield MessageResponse(children=[Message(tool_msg, verbose=True)])
96
-
107
+
97
108
  # Example 4: Multiple children (using initialization)
98
109
  yield Static("\n4. Multiple children:", classes="example-header")
99
-
110
+
100
111
  # Add multiple children to demonstrate flexibility
101
112
  status_msg = MessageType(
102
113
  type=MsgType.ASSISTANT,
103
114
  message=MessageContent("Step 1: Analyzing code..."),
104
- timestamp=time.time()
115
+ timestamp=time.time(),
105
116
  )
106
117
  progress_msg = MessageType(
107
118
  type=MsgType.ASSISTANT,
108
119
  message=MessageContent("Step 2: Applying changes..."),
109
- timestamp=time.time()
120
+ timestamp=time.time(),
110
121
  )
111
-
122
+
112
123
  # Create with children during initialization
113
- yield MessageResponse(children=[
114
- Message(status_msg),
115
- Message(progress_msg)
116
- ])
117
-
124
+ yield MessageResponse(children=[Message(status_msg), Message(progress_msg)])
125
+
118
126
  def on_button_pressed(self, event: Button.Pressed) -> None:
119
127
  """Handle button presses"""
120
128
  messages_container = self.query_one("#messages", ScrollableContainer)
121
-
129
+
122
130
  if event.button.id == "add_tool_progress":
123
131
  self._add_tool_progress(messages_container)
124
132
  elif event.button.id == "add_nested":
@@ -129,48 +137,45 @@ class MessageResponseChildrenDemo(App):
129
137
  self._add_dynamic_mount(messages_container)
130
138
  elif event.button.id == "clear":
131
139
  self._clear_messages(messages_container)
132
-
140
+
133
141
  def _add_tool_progress(self, container):
134
142
  """Add tool execution progress using MessageResponse + Message"""
135
143
  # Simulate tool execution progress
136
144
  tool_msg = MessageType(
137
145
  type=MsgType.ASSISTANT,
138
146
  message=MessageContent("🔧 Executing file operation..."),
139
- timestamp=time.time()
147
+ timestamp=time.time(),
140
148
  )
141
-
149
+
142
150
  # Create MessageResponse with child during initialization
143
151
  response = MessageResponse(children=[Message(tool_msg)])
144
-
152
+
145
153
  container.mount(response)
146
154
  container.scroll_end()
147
-
155
+
148
156
  def _add_nested_response(self, container):
149
157
  """Add nested MessageResponse (response within response)"""
150
158
  # Create outer message
151
159
  outer_msg = MessageType(
152
160
  type=MsgType.ASSISTANT,
153
161
  message=MessageContent("Starting complex operation..."),
154
- timestamp=time.time()
162
+ timestamp=time.time(),
155
163
  )
156
-
164
+
157
165
  # Create inner message for sub-operation
158
166
  inner_msg = MessageType(
159
167
  type=MsgType.ASSISTANT,
160
168
  message=MessageContent(" └─ Sub-operation in progress..."),
161
- timestamp=time.time()
169
+ timestamp=time.time(),
162
170
  )
163
-
171
+
164
172
  # Create nested structure with initialization
165
173
  inner_response = MessageResponse(children=[Message(inner_msg)])
166
- outer_response = MessageResponse(children=[
167
- Message(outer_msg),
168
- inner_response
169
- ])
170
-
174
+ outer_response = MessageResponse(children=[Message(outer_msg), inner_response])
175
+
171
176
  container.mount(outer_response)
172
177
  container.scroll_end()
173
-
178
+
174
179
  def _add_multiple_children(self, container):
175
180
  """Add MessageResponse with multiple child messages"""
176
181
  # Create multiple related messages
@@ -178,38 +183,39 @@ class MessageResponseChildrenDemo(App):
178
183
  MessageType(
179
184
  type=MsgType.ASSISTANT,
180
185
  message=MessageContent(f"Processing step {i+1}/3..."),
181
- timestamp=time.time()
182
- ) for i in range(3)
186
+ timestamp=time.time(),
187
+ )
188
+ for i in range(3)
183
189
  ]
184
-
190
+
185
191
  # Create MessageResponse with multiple children during initialization
186
192
  message_widgets = [Message(msg) for msg in messages]
187
193
  response = MessageResponse(children=message_widgets)
188
-
194
+
189
195
  container.mount(response)
190
196
  container.scroll_end()
191
-
197
+
192
198
  def _add_dynamic_mount(self, container):
193
199
  """Add MessageResponse and then dynamically mount children after it's mounted"""
194
200
  # Create empty MessageResponse first
195
201
  response = MessageResponse(content="Dynamic mounting example:")
196
-
202
+
197
203
  # Mount it to the container
198
204
  container.mount(response)
199
-
205
+
200
206
  # Use call_after_refresh to mount children after the component is fully mounted
201
207
  def mount_children_after():
202
208
  msg = MessageType(
203
209
  type=MsgType.ASSISTANT,
204
210
  message=MessageContent("This was added dynamically after mounting!"),
205
- timestamp=time.time()
211
+ timestamp=time.time(),
206
212
  )
207
213
  response.mount_child(Message(msg))
208
-
214
+
209
215
  # Schedule the dynamic mounting
210
216
  self.call_after_refresh(mount_children_after)
211
217
  container.scroll_end()
212
-
218
+
213
219
  def _clear_messages(self, container):
214
220
  """Clear all messages except headers"""
215
221
  for child in list(container.children)[2:]: # Keep title and subtitle
@@ -223,4 +229,4 @@ def main():
223
229
 
224
230
 
225
231
  if __name__ == "__main__":
226
- main()
232
+ main()