janito 2.5.1__py3-none-any.whl → 2.6.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 (61) hide show
  1. janito/agent/setup_agent.py +231 -223
  2. janito/agent/templates/profiles/system_prompt_template_software_developer.txt.j2 +39 -0
  3. janito/cli/chat_mode/bindings.py +1 -26
  4. janito/cli/chat_mode/session.py +282 -294
  5. janito/cli/chat_mode/session_profile_select.py +125 -55
  6. janito/cli/chat_mode/shell/commands/tools.py +51 -48
  7. janito/cli/chat_mode/toolbar.py +42 -68
  8. janito/cli/cli_commands/list_tools.py +41 -56
  9. janito/cli/cli_commands/show_system_prompt.py +70 -49
  10. janito/cli/core/runner.py +6 -1
  11. janito/cli/core/setters.py +43 -34
  12. janito/cli/main_cli.py +25 -1
  13. janito/cli/prompt_core.py +76 -69
  14. janito/cli/rich_terminal_reporter.py +22 -1
  15. janito/cli/single_shot_mode/handler.py +95 -94
  16. janito/drivers/driver_registry.py +27 -29
  17. janito/drivers/openai/driver.py +436 -494
  18. janito/llm/agent.py +54 -68
  19. janito/provider_registry.py +178 -178
  20. janito/providers/anthropic/model_info.py +41 -22
  21. janito/providers/anthropic/provider.py +80 -67
  22. janito/providers/provider_static_info.py +18 -17
  23. janito/tools/adapters/local/__init__.py +66 -65
  24. janito/tools/adapters/local/adapter.py +79 -18
  25. janito/tools/adapters/local/create_directory.py +9 -9
  26. janito/tools/adapters/local/create_file.py +12 -12
  27. janito/tools/adapters/local/delete_text_in_file.py +16 -16
  28. janito/tools/adapters/local/find_files.py +2 -2
  29. janito/tools/adapters/local/get_file_outline/core.py +5 -5
  30. janito/tools/adapters/local/get_file_outline/search_outline.py +4 -4
  31. janito/tools/adapters/local/open_html_in_browser.py +15 -15
  32. janito/tools/adapters/local/python_file_run.py +4 -4
  33. janito/tools/adapters/local/read_files.py +40 -0
  34. janito/tools/adapters/local/remove_directory.py +5 -5
  35. janito/tools/adapters/local/remove_file.py +4 -4
  36. janito/tools/adapters/local/replace_text_in_file.py +21 -21
  37. janito/tools/adapters/local/run_bash_command.py +1 -1
  38. janito/tools/adapters/local/search_text/pattern_utils.py +2 -2
  39. janito/tools/adapters/local/search_text/traverse_directory.py +10 -10
  40. janito/tools/adapters/local/validate_file_syntax/core.py +7 -7
  41. janito/tools/adapters/local/validate_file_syntax/css_validator.py +2 -2
  42. janito/tools/adapters/local/validate_file_syntax/html_validator.py +7 -7
  43. janito/tools/adapters/local/validate_file_syntax/js_validator.py +2 -2
  44. janito/tools/adapters/local/validate_file_syntax/json_validator.py +2 -2
  45. janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +2 -2
  46. janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +2 -2
  47. janito/tools/adapters/local/validate_file_syntax/python_validator.py +2 -2
  48. janito/tools/adapters/local/validate_file_syntax/xml_validator.py +2 -2
  49. janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +2 -2
  50. janito/tools/adapters/local/view_file.py +12 -12
  51. janito/tools/path_security.py +204 -0
  52. janito/tools/tool_use_tracker.py +12 -12
  53. janito/tools/tools_adapter.py +66 -34
  54. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/METADATA +412 -412
  55. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/RECORD +59 -58
  56. janito/drivers/anthropic/driver.py +0 -113
  57. janito/tools/adapters/local/get_file_outline/python_outline_v2.py +0 -156
  58. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/WHEEL +0 -0
  59. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/entry_points.txt +0 -0
  60. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/licenses/LICENSE +0 -0
  61. {janito-2.5.1.dist-info → janito-2.6.0.dist-info}/top_level.txt +0 -0
@@ -1,294 +1,282 @@
1
- """
2
- Session management for Janito Chat CLI.
3
- Defines ChatSession and ChatShellState classes.
4
- """
5
-
6
- from __future__ import annotations
7
-
8
- import types
9
- from rich.console import Console
10
- from rich.rule import Rule
11
- from prompt_toolkit.history import InMemoryHistory
12
- from janito.cli.chat_mode.shell.input_history import UserInputHistory
13
- from prompt_toolkit.formatted_text import HTML
14
- from prompt_toolkit import PromptSession
15
- from janito.cli.chat_mode.toolbar import get_toolbar_func
16
- from prompt_toolkit.enums import EditingMode
17
- from janito.cli.chat_mode.prompt_style import chat_shell_style
18
- from janito.cli.chat_mode.bindings import KeyBindingsFactory
19
- from janito.cli.chat_mode.shell.commands import handle_command
20
- from janito.cli.chat_mode.shell.autocomplete import ShellCommandCompleter
21
-
22
- # Shared prompt/agent factory
23
- from janito.cli.prompt_setup import setup_agent_and_prompt_handler
24
-
25
-
26
- class ChatShellState:
27
- def __init__(self, mem_history, conversation_history):
28
- self.mem_history = mem_history
29
- self.conversation_history = conversation_history
30
- self.paste_mode = False
31
- self._port = None
32
- self._pid = None
33
- self._stdout_path = None
34
- self._stderr_path = None
35
- self.livereload_stderr_path = None
36
- self._status = "starting" # Tracks the current status (updated by background thread/UI)
37
-
38
- self.last_usage_info = {}
39
- self.last_elapsed = None
40
- self.main_agent = {}
41
- self.mode = None
42
- self.agent = None
43
- self.main_agent = None
44
- self.main_enabled = False
45
-
46
-
47
- class ChatSession:
48
- def __init__(
49
- self,
50
- console,
51
- provider_instance=None,
52
- llm_driver_config=None,
53
- role=None,
54
- args=None,
55
- verbose_tools=False,
56
- verbose_agent=False,
57
-
58
- allowed_permissions=None,
59
- ):
60
-
61
- self.console = console
62
- self.user_input_history = UserInputHistory()
63
- self.input_dicts = self.user_input_history.load()
64
- self.mem_history = InMemoryHistory()
65
- for item in self.input_dicts:
66
- if isinstance(item, dict) and "input" in item:
67
- self.mem_history.append_string(item["input"])
68
- self.provider_instance = provider_instance
69
- self.llm_driver_config = llm_driver_config
70
-
71
- # --- Profile selection (interactive) ---------------------------------
72
- profile = getattr(args, "profile", None) if args is not None else None
73
- profile_system_prompt = None
74
- if profile is None:
75
- try:
76
- from janito.cli.chat_mode.session_profile_select import select_profile
77
-
78
- result = select_profile()
79
- if isinstance(result, dict):
80
- profile = result.get("profile")
81
- profile_system_prompt = result.get("profile_system_prompt")
82
- elif isinstance(result, str) and result.startswith("role:"):
83
- role = result[len("role:") :].strip()
84
- profile = "developer"
85
- else:
86
- profile = (
87
- "developer" if result == "software developer" else result
88
- )
89
- except ImportError:
90
- profile = "helpful assistant"
91
-
92
- # ---------------------------------------------------------------------
93
- from janito.conversation_history import LLMConversationHistory
94
-
95
- conversation_history = LLMConversationHistory()
96
-
97
- # Build agent and core prompt handler via the shared helper
98
- self.agent, self._prompt_handler = setup_agent_and_prompt_handler(
99
- args=args,
100
- provider_instance=provider_instance,
101
- llm_driver_config=llm_driver_config,
102
- role=role,
103
- verbose_tools=verbose_tools,
104
- verbose_agent=verbose_agent,
105
-
106
- allowed_permissions=allowed_permissions,
107
- profile=profile,
108
- profile_system_prompt=profile_system_prompt,
109
- conversation_history=conversation_history,
110
- )
111
-
112
- self.shell_state = ChatShellState(self.mem_history, conversation_history)
113
- self.shell_state.agent = self.agent
114
- # Filter execution tools at startup
115
- try:
116
- # Permissions are now managed globally; registry filtering is automatic
117
- getattr(__import__('janito.tools', fromlist=['get_local_tools_adapter']), 'get_local_tools_adapter')()
118
- except Exception as e:
119
- self.console.print(f"[yellow]Warning: Could not filter execution tools at startup: {e}[/yellow]")
120
- from janito.perf_singleton import performance_collector
121
-
122
- self.performance_collector = performance_collector
123
- self.key_bindings = KeyBindingsFactory.create()
124
- # Attach agent to prompt handler now that agent is initialized
125
- self._prompt_handler.agent = self.agent
126
- self._prompt_handler.conversation_history = (
127
- self.shell_state.conversation_history
128
- )
129
-
130
- self._support = False
131
- if args and getattr(args, "web", False):
132
- self._support = True
133
- self.shell_state._support = self._support
134
- from janito.cli._starter import _start_and_watch
135
- from janito.cli.config import get__port
136
- import threading
137
- from rich.console import Console
138
-
139
- Console().print("[yellow]Starting in background...[/yellow]")
140
- self._lock = threading.Lock()
141
- _thread = _start_and_watch(
142
- self.shell_state, self._lock, get__port()
143
- )
144
- # Initial status is set to 'starting' by constructor; the watcher will update
145
- self._thread = _thread
146
-
147
- # Start a background timer to update live status (for UI responsiveness)
148
- import threading, datetime
149
-
150
- # Health check and liveness thread removed as per refactor to eliminate localhost references.
151
-
152
- else:
153
- self.shell_state._support = False
154
- self.shell_state._status = "offline"
155
-
156
- def run(self):
157
- self.console.clear()
158
- from janito import __version__
159
- self.console.print(
160
- f"[bold green]Janito Chat Mode v{__version__}[/bold green]"
161
- )
162
- self.console.print("[green]/help for commands /exit or Ctrl+C to quit[/green]")
163
- import os
164
- cwd = os.getcwd()
165
- home = os.path.expanduser('~')
166
- if cwd.startswith(home):
167
- cwd_display = '~' + cwd[len(home):]
168
- else:
169
- cwd_display = cwd
170
- self.console.print(f"[green]Working Dir:[/green] {cwd_display}")
171
-
172
- # Inform user if no privileges are enabled
173
- from janito.cli.chat_mode.shell.commands._priv_check import user_has_any_privileges
174
- if not user_has_any_privileges():
175
- self.console.print("[yellow]Note: You currently have no privileges enabled. If you need to interact with files or the system, enable permissions using /read on, /write on, or /execute on.[/yellow]")
176
-
177
- session = self._create_prompt_session()
178
- self._chat_loop(session)
179
-
180
- def _chat_loop(self, session):
181
- self.msg_count = 0
182
- timer_started = False
183
- while True:
184
- if not timer_started:
185
- timer_started = True
186
- cmd_input = self._handle_input(session)
187
- if cmd_input is None:
188
- break
189
- if not cmd_input:
190
- continue
191
- if self._handle_exit_conditions(cmd_input):
192
- break
193
- if self._handle_command_input(cmd_input):
194
- continue
195
- self.user_input_history.append(cmd_input)
196
- self._process_prompt(cmd_input)
197
-
198
- def _handle_command_input(self, cmd_input):
199
- if cmd_input.startswith("/"):
200
- handle_command(cmd_input, shell_state=self.shell_state)
201
- return True
202
- if cmd_input.startswith("!"):
203
- # Pass everything after ! to the bang handler
204
- handle_command(f"! {cmd_input[1:]}", shell_state=self.shell_state)
205
- return True
206
- return False
207
-
208
- def _process_prompt(self, cmd_input):
209
- try:
210
- import time
211
- final_event = (
212
- self._prompt_handler.agent.last_event
213
- if hasattr(self._prompt_handler.agent, "last_event")
214
- else None
215
- )
216
- start_time = time.time()
217
- self._prompt_handler.run_prompt(cmd_input)
218
- end_time = time.time()
219
- elapsed = end_time - start_time
220
- self.msg_count += 1
221
- # After prompt, print the stat line using the shared core function
222
- from janito.formatting_token import print_token_message_summary
223
-
224
- usage = self.performance_collector.get_last_request_usage()
225
- print_token_message_summary(self.console, self.msg_count, usage, elapsed=elapsed)
226
- # Print exit reason if present in the final event
227
- if final_event and hasattr(final_event, "metadata"):
228
- exit_reason = (
229
- final_event.metadata.get("exit_reason")
230
- if hasattr(final_event, "metadata")
231
- else None
232
- )
233
- if exit_reason:
234
- self.console.print(
235
- f"[bold yellow]Exit reason: {exit_reason}[/bold yellow]"
236
- )
237
-
238
- except Exception as exc:
239
- self.console.print(f"[red]Exception in agent: {exc}[/red]")
240
- import traceback
241
- self.console.print(traceback.format_exc())
242
-
243
- def _create_prompt_session(self):
244
- return PromptSession(
245
- style=chat_shell_style,
246
- completer=ShellCommandCompleter(),
247
- history=self.mem_history,
248
- editing_mode=EditingMode.EMACS,
249
- key_bindings=self.key_bindings,
250
- bottom_toolbar=lambda: get_toolbar_func(
251
- self.performance_collector, 0, self.shell_state
252
- )(),
253
- )
254
-
255
- def _handle_input(self, session):
256
- injected = getattr(self.shell_state, "injected_input", None)
257
- if injected is not None:
258
- cmd_input = injected
259
- self.shell_state.injected_input = None
260
- else:
261
- try:
262
- cmd_input = session.prompt(HTML("<inputline>💬 </inputline>"))
263
- except (KeyboardInterrupt, EOFError):
264
- self._handle_exit()
265
- return None
266
- sanitized = cmd_input.strip()
267
- # Ensure UTF-8 validity and sanitize if needed
268
- try:
269
- # This will raise UnicodeEncodeError if not encodable
270
- sanitized.encode("utf-8")
271
- except UnicodeEncodeError:
272
- # Replace invalid characters
273
- sanitized = sanitized.encode("utf-8", errors="replace").decode("utf-8")
274
- self.console.print(
275
- "[yellow]Warning: Some characters in your input were not valid UTF-8 and have been replaced.[/yellow]"
276
- )
277
- return sanitized
278
-
279
- def _handle_exit(self):
280
- self.console.print("[bold yellow]Exiting chat. Goodbye![/bold yellow]")
281
- # Ensure driver thread is joined before exit
282
- if hasattr(self, "agent") and hasattr(self.agent, "join_driver"):
283
- if (
284
- hasattr(self.agent, "input_queue")
285
- and self.agent.input_queue is not None
286
- ):
287
- self.agent.input_queue.put(None)
288
- self.agent.join_driver()
289
-
290
- def _handle_exit_conditions(self, cmd_input):
291
- if cmd_input.lower() in ("/exit", ":q", ":quit"):
292
- self._handle_exit()
293
- return True
294
- return False
1
+ """
2
+ Session management for Janito Chat CLI.
3
+ Defines ChatSession and ChatShellState classes.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import types
9
+ from rich.console import Console
10
+ from rich.rule import Rule
11
+ from prompt_toolkit.history import InMemoryHistory
12
+ from janito.cli.chat_mode.shell.input_history import UserInputHistory
13
+ from prompt_toolkit.formatted_text import HTML
14
+ from prompt_toolkit import PromptSession
15
+ from janito.cli.chat_mode.toolbar import get_toolbar_func
16
+ from prompt_toolkit.enums import EditingMode
17
+ from janito.cli.chat_mode.prompt_style import chat_shell_style
18
+ from janito.cli.chat_mode.bindings import KeyBindingsFactory
19
+ from janito.cli.chat_mode.shell.commands import handle_command
20
+ from janito.cli.chat_mode.shell.autocomplete import ShellCommandCompleter
21
+ import time
22
+
23
+ # Shared prompt/agent factory
24
+ from janito.cli.prompt_setup import setup_agent_and_prompt_handler
25
+
26
+ import time
27
+
28
+ class ChatShellState:
29
+ def __init__(self, mem_history, conversation_history):
30
+ self.mem_history = mem_history
31
+ self.conversation_history = conversation_history
32
+ self.paste_mode = False
33
+ self._port = None
34
+ self._pid = None
35
+ self._stdout_path = None
36
+ self._stderr_path = None
37
+ self.livereload_stderr_path = None
38
+ self._status = "starting" # Tracks the current status (updated by background thread/UI)
39
+
40
+ self.last_usage_info = {}
41
+ self.last_elapsed = None
42
+ self.main_agent = {}
43
+ self.mode = None
44
+ self.agent = None
45
+ self.main_agent = None
46
+ self.main_enabled = False
47
+
48
+ class ChatSession:
49
+ def __init__(
50
+ self,
51
+ console,
52
+ provider_instance=None,
53
+ llm_driver_config=None,
54
+ role=None,
55
+ args=None,
56
+ verbose_tools=False,
57
+ verbose_agent=False,
58
+ allowed_permissions=None,
59
+ ):
60
+ self.console = console
61
+ self.user_input_history = UserInputHistory()
62
+ self.input_dicts = self.user_input_history.load()
63
+ self.mem_history = InMemoryHistory()
64
+ for item in self.input_dicts:
65
+ if isinstance(item, dict) and "input" in item:
66
+ self.mem_history.append_string(item["input"])
67
+ self.provider_instance = provider_instance
68
+ self.llm_driver_config = llm_driver_config
69
+
70
+ profile, role, profile_system_prompt = self._select_profile_and_role(args, role)
71
+ conversation_history = self._create_conversation_history()
72
+ self.agent, self._prompt_handler = self._setup_agent_and_prompt_handler(
73
+ args, provider_instance, llm_driver_config, role, verbose_tools, verbose_agent, allowed_permissions, profile, profile_system_prompt, conversation_history
74
+ )
75
+ self.shell_state = ChatShellState(self.mem_history, conversation_history)
76
+ self.shell_state.agent = self.agent
77
+ self._filter_execution_tools()
78
+ from janito.perf_singleton import performance_collector
79
+ self.performance_collector = performance_collector
80
+ self.key_bindings = KeyBindingsFactory.create()
81
+ self._prompt_handler.agent = self.agent
82
+ self._prompt_handler.conversation_history = self.shell_state.conversation_history
83
+ self._support = False
84
+ self._maybe_enable_web_support(args)
85
+
86
+ def _select_profile_and_role(self, args, role):
87
+ profile = getattr(args, "profile", None) if args is not None else None
88
+ role_arg = getattr(args, "role", None) if args is not None else None
89
+ profile_system_prompt = None
90
+ if profile is None and role_arg is None:
91
+ try:
92
+ from janito.cli.chat_mode.session_profile_select import select_profile
93
+ result = select_profile()
94
+ if isinstance(result, dict):
95
+ profile = result.get("profile")
96
+ profile_system_prompt = result.get("profile_system_prompt")
97
+ elif isinstance(result, str) and result.startswith("role:"):
98
+ role = result[len("role:") :].strip()
99
+ profile = "developer"
100
+ else:
101
+ profile = (
102
+ "developer" if result == "software developer" else result
103
+ )
104
+ except ImportError:
105
+ profile = "helpful assistant"
106
+ if role_arg is not None:
107
+ role = role_arg
108
+ if profile is None:
109
+ profile = "developer"
110
+ return profile, role, profile_system_prompt
111
+
112
+ def _create_conversation_history(self):
113
+ from janito.conversation_history import LLMConversationHistory
114
+ return LLMConversationHistory()
115
+
116
+ def _setup_agent_and_prompt_handler(self, args, provider_instance, llm_driver_config, role, verbose_tools, verbose_agent, allowed_permissions, profile, profile_system_prompt, conversation_history):
117
+ return setup_agent_and_prompt_handler(
118
+ args=args,
119
+ provider_instance=provider_instance,
120
+ llm_driver_config=llm_driver_config,
121
+ role=role,
122
+ verbose_tools=verbose_tools,
123
+ verbose_agent=verbose_agent,
124
+ allowed_permissions=allowed_permissions,
125
+ profile=profile,
126
+ profile_system_prompt=profile_system_prompt,
127
+ conversation_history=conversation_history,
128
+ )
129
+
130
+ def _filter_execution_tools(self):
131
+ try:
132
+ getattr(__import__('janito.tools', fromlist=['get_local_tools_adapter']), 'get_local_tools_adapter')()
133
+ except Exception as e:
134
+ self.console.print(f"[yellow]Warning: Could not filter execution tools at startup: {e}[/yellow]")
135
+
136
+ def _maybe_enable_web_support(self, args):
137
+ if args and getattr(args, "web", False):
138
+ self._support = True
139
+ self.shell_state._support = self._support
140
+ from janito.cli._starter import _start_and_watch
141
+ from janito.cli.config import get__port
142
+ import threading
143
+ from rich.console import Console
144
+ Console().print("[yellow]Starting in background...[/yellow]")
145
+ self._lock = threading.Lock()
146
+ _thread = _start_and_watch(
147
+ self.shell_state, self._lock, get__port()
148
+ )
149
+ self._thread = _thread
150
+ else:
151
+ self.shell_state._support = False
152
+ self.shell_state._status = "offline"
153
+
154
+ def run(self):
155
+ self.console.clear()
156
+ from janito import __version__
157
+ self.console.print(
158
+ f"[bold green]Janito Chat Mode v{__version__}[/bold green]"
159
+ )
160
+ self.console.print("[green]/help for commands /exit or Ctrl+C to quit[/green]")
161
+ import os
162
+ cwd = os.getcwd()
163
+ home = os.path.expanduser('~')
164
+ if cwd.startswith(home):
165
+ cwd_display = '~' + cwd[len(home):]
166
+ else:
167
+ cwd_display = cwd
168
+ self.console.print(f"[green]Working Dir:[/green] {cwd_display}")
169
+
170
+ from janito.cli.chat_mode.shell.commands._priv_check import user_has_any_privileges
171
+ if not user_has_any_privileges():
172
+ self.console.print("[yellow]Note: You currently have no privileges enabled. If you need to interact with files or the system, enable permissions using /read on, /write on, or /execute on.[/yellow]")
173
+
174
+ session = self._create_prompt_session()
175
+ self._chat_loop(session)
176
+
177
+ def _chat_loop(self, session):
178
+ self.msg_count = 0
179
+ timer_started = False
180
+ while True:
181
+ if not timer_started:
182
+ timer_started = True
183
+ cmd_input = self._handle_input(session)
184
+ if cmd_input is None:
185
+ break
186
+ if not cmd_input:
187
+ continue
188
+ if self._handle_exit_conditions(cmd_input):
189
+ break
190
+ if self._handle_command_input(cmd_input):
191
+ continue
192
+ self.user_input_history.append(cmd_input)
193
+ self._process_prompt(cmd_input)
194
+
195
+ def _handle_command_input(self, cmd_input):
196
+ if cmd_input.startswith("/"):
197
+ handle_command(cmd_input, shell_state=self.shell_state)
198
+ return True
199
+ if cmd_input.startswith("!"):
200
+ handle_command(f"! {cmd_input[1:]}", shell_state=self.shell_state)
201
+ return True
202
+ return False
203
+
204
+ def _process_prompt(self, cmd_input):
205
+ try:
206
+ import time
207
+ final_event = (
208
+ self._prompt_handler.agent.last_event
209
+ if hasattr(self._prompt_handler.agent, "last_event")
210
+ else None
211
+ )
212
+ start_time = time.time()
213
+ self._prompt_handler.run_prompt(cmd_input)
214
+ end_time = time.time()
215
+ elapsed = end_time - start_time
216
+ self.msg_count += 1
217
+ from janito.formatting_token import print_token_message_summary
218
+ usage = self.performance_collector.get_last_request_usage()
219
+ print_token_message_summary(self.console, self.msg_count, usage, elapsed=elapsed)
220
+ if final_event and hasattr(final_event, "metadata"):
221
+ exit_reason = (
222
+ final_event.metadata.get("exit_reason")
223
+ if hasattr(final_event, "metadata")
224
+ else None
225
+ )
226
+ if exit_reason:
227
+ self.console.print(
228
+ f"[bold yellow]Exit reason: {exit_reason}[/bold yellow]"
229
+ )
230
+ except Exception as exc:
231
+ self.console.print(f"[red]Exception in agent: {exc}[/red]")
232
+ import traceback
233
+ self.console.print(traceback.format_exc())
234
+
235
+ def _create_prompt_session(self):
236
+ return PromptSession(
237
+ style=chat_shell_style,
238
+ completer=ShellCommandCompleter(),
239
+ history=self.mem_history,
240
+ editing_mode=EditingMode.EMACS,
241
+ key_bindings=self.key_bindings,
242
+ bottom_toolbar=lambda: get_toolbar_func(
243
+ self.performance_collector, 0, self.shell_state
244
+ )(),
245
+ )
246
+
247
+ def _handle_input(self, session):
248
+ injected = getattr(self.shell_state, "injected_input", None)
249
+ if injected is not None:
250
+ cmd_input = injected
251
+ self.shell_state.injected_input = None
252
+ else:
253
+ try:
254
+ cmd_input = session.prompt(HTML("<inputline>💬 </inputline>"))
255
+ except (KeyboardInterrupt, EOFError):
256
+ self._handle_exit()
257
+ return None
258
+ sanitized = cmd_input.strip()
259
+ try:
260
+ sanitized.encode("utf-8")
261
+ except UnicodeEncodeError:
262
+ sanitized = sanitized.encode("utf-8", errors="replace").decode("utf-8")
263
+ self.console.print(
264
+ "[yellow]Warning: Some characters in your input were not valid UTF-8 and have been replaced.[/yellow]"
265
+ )
266
+ return sanitized
267
+
268
+ def _handle_exit(self):
269
+ self.console.print("[bold yellow]Exiting chat. Goodbye![/bold yellow]")
270
+ if hasattr(self, "agent") and hasattr(self.agent, "join_driver"):
271
+ if (
272
+ hasattr(self.agent, "input_queue")
273
+ and self.agent.input_queue is not None
274
+ ):
275
+ self.agent.input_queue.put(None)
276
+ self.agent.join_driver()
277
+
278
+ def _handle_exit_conditions(self, cmd_input):
279
+ if cmd_input.lower() in ("/exit", ":q", ":quit"):
280
+ self._handle_exit()
281
+ return True
282
+ return False