feat(ai): enhance session management, inquirer theme, and gRPC MCP support

- AI & Session Management:
  - Add random suffix to session IDs to ensure uniqueness.
  - Implement optional pagination/limit in session listing (default 20).
  - Add `--all` flag to `ai` CLI commands to view all sessions.
  - Keep active session ID and path synced correctly during clean session startups.

- CLI UI/UX:
  - Add a custom `ConnpyTheme` for inquirer prompts that dynamically translates
    active hex style colors to terminal ANSI/blessed escapes.

- gRPC & Services:
  - Implement remote MCP server listing (`list_mcp_servers` RPC and services).
  - Stream responder updates (`__RESPONDER__`) to toggle headers dynamically between
    "Network Engineer" and "Network Architect" on remote/web clients.
  - Fix remote client deadlock risk by ensuring `final_mark` is sent on exceptions.
  - Hydrate client-side chat history correctly on initial streaming request.

- Testing:
  - Add integration tests for AI gRPC services and MCP server listing.
This commit is contained in:
2026-05-20 12:27:02 -03:00
parent 468868ac18
commit dce9982454
11 changed files with 515 additions and 319 deletions
+20 -3
View File
@@ -758,6 +758,7 @@ class AIStub:
full_content = ""
header_printed = False
current_responder = "engineer"
final_result = {"response": "", "chat_history": []}
# Background thread to pull responses from gRPC into a local queue
@@ -802,6 +803,10 @@ class AIStub:
break
if response.status_update:
if response.status_update.startswith("__RESPONDER__:"):
current_responder = response.status_update.split(":")[1].lower()
continue
if response.requires_confirmation:
if status: status.stop()
@@ -854,7 +859,9 @@ class AIStub:
stable_console = RichConsole(theme=connpy_theme, file=get_original_stdout())
# Print header on first chunk
stable_console.print(Rule("[bold engineer]Network Engineer[/bold engineer]", style="engineer"))
alias = "architect" if current_responder == "architect" else "engineer"
role_label = "Network Architect" if current_responder == "architect" else "Network Engineer"
stable_console.print(Rule(f"[bold {alias}]{role_label}[/bold {alias}]", style=alias))
header_printed = True
# Initialize parser
@@ -906,8 +913,13 @@ class AIStub:
return self.stub.confirm(connpy_pb2.StringRequest(value=input_text)).value
@handle_errors
def list_sessions(self):
return from_value(self.stub.list_sessions(Empty()).data)
def list_sessions(self, limit=None):
from .utils import from_value
res = self.stub.list_sessions(Empty())
sessions = from_value(res.data) or []
if limit and len(sessions) > limit:
return sessions[:limit], len(sessions)
return sessions, len(sessions)
@handle_errors
def delete_session(self, session_id):
@@ -929,6 +941,11 @@ class AIStub:
)
self.stub.configure_mcp(req)
@handle_errors
def list_mcp_servers(self):
res = self.stub.list_mcp_servers(Empty())
return from_value(res.data) or {}
@handle_errors
def load_session_data(self, session_id):
return from_struct(self.stub.load_session_data(connpy_pb2.StringRequest(value=session_id)).data)