"""Zendesk-like app — customer support ticket management.""" from typing import Dict, List, Optional from server.apps.base_app import BaseApp from server.schema_drift import SchemaDriftEngine class ZendeskApp(BaseApp): APP_NAME = "zendesk" OPERATIONS = [ "get_ticket", "acknowledge_ticket", "set_urgency", "assign_agent", "escalate_to_jira", "resolve_ticket", "add_note", "list_tickets", ] def __init__(self, drift: SchemaDriftEngine): super().__init__(drift) self._records: Dict[str, Dict] = {} # ------------------------------------------------------------------ # BaseApp interface # ------------------------------------------------------------------ def initialize(self, records: List[Dict]) -> None: self._records = {r["ticket_number"]: r for r in records} def execute(self, operation: str, args: Dict) -> Dict: method = getattr(self, f"_op_{operation}", None) if method is None: return { "success": False, "message": f"Unknown operation '{operation}'. Available: {', '.join(self.OPERATIONS)}", } try: return method(**args) except TypeError as exc: return {"success": False, "message": f"Bad args for '{operation}': {exc}"} def get_state_view(self, max_rows: int = 5) -> str: open_tickets = [r for r in self._records.values() if r.get("state") not in ("resolved", "closed")][:max_rows] if not open_tickets: return "No open tickets." lines = [] for rec in open_tickets: view = self._to_agent_view(rec) keep = ["ticket_number", "title", "urgency", "priority", "impact_level", "agent_email", "handler", "assigned_agent", "state", "ticket_state", "resolution_status", "customer_id"] compact = {k: v for k, v in view.items() if k in keep and v is not None} lines.append(str(compact)) return "\n".join(lines) def count_open_items(self) -> int: return sum(1 for r in self._records.values() if r.get("state") not in ("resolved", "closed")) # ------------------------------------------------------------------ # Workflow completion state checks # ------------------------------------------------------------------ def ticket_acknowledged(self) -> bool: """True once the primary p1/p0 ticket has been acknowledged (Workflow A step A1).""" return any( r.get("_is_primary_ticket") and r.get("_acknowledged") for r in self._records.values() ) def support_queried(self, account_id: str) -> bool: """True once tickets for account_id were listed (Workflow C step C2).""" return account_id in self._records.get("ZD-001", {}).get("_queried_accounts", []) or \ any(account_id in r.get("_queried_accounts", []) for r in self._records.values()) def profile_created(self) -> bool: """True once a new agent profile was created (Workflow B step B4).""" return any(r.get("_profile_created") for r in self._records.values()) # ------------------------------------------------------------------ # Operations # ------------------------------------------------------------------ def _op_get_ticket(self, ticket_number: str, customer_id: Optional[str] = None) -> Dict: # If customer_id provided, look up all tickets for that customer if customer_id: matching = [r for r in self._records.values() if r.get("customer_id") == customer_id] # Mark as queried for Workflow C for r in matching: r.setdefault("_queried_accounts", []) if customer_id not in r["_queried_accounts"]: r["_queried_accounts"].append(customer_id) if not matching: return {"success": True, "data": [], "message": f"No tickets found for customer {customer_id}"} return { "success": True, "data": [self._to_agent_view(r) for r in matching[:5]], "message": f"Found {len(matching)} tickets for {customer_id}", } rec = self._records.get(ticket_number) if not rec: return {"success": False, "message": f"Ticket {ticket_number} not found. Use list_tickets to browse."} rec.setdefault("_queried_accounts", []) cid = rec.get("customer_id") if cid and cid not in rec["_queried_accounts"]: rec["_queried_accounts"].append(cid) return {"success": True, "data": self._to_agent_view(rec), "ticket": rec, "message": f"Retrieved {ticket_number}"} def _op_acknowledge_ticket(self, ticket_number: str) -> Dict: rec = self._records.get(ticket_number) if not rec: return {"success": False, "message": f"Ticket {ticket_number} not found"} rec["_acknowledged"] = True if rec.get("state") == "new": rec["state"] = "open" return {"success": True, "ticket": rec, "message": f"Acknowledged {ticket_number} — status → open"} def _op_set_urgency(self, ticket_number: str, **kwargs) -> Dict: schema_error, schema_adapted = self._check_schema_drift(kwargs) if schema_error: hint = self._drift.translate_field("urgency", self.APP_NAME) return {"success": False, "schema_error": schema_error, "message": f"Schema error: use '{hint}' not '{schema_error}'"} rec = self._records.get(ticket_number) if not rec: return {"success": False, "message": f"Ticket {ticket_number} not found"} new_urgency = (kwargs.get("urgency") or kwargs.get("priority") or kwargs.get("impact_level")) if not new_urgency: return {"success": False, "message": "Provide urgency / priority / impact_level value"} rec["urgency"] = new_urgency return {"success": True, "schema_adapted": schema_adapted, "message": f"{ticket_number} urgency → '{new_urgency}'"} def _op_assign_agent(self, ticket_number: str, **kwargs) -> Dict: schema_error, schema_adapted = self._check_schema_drift(kwargs) if schema_error: hint = self._drift.translate_field("agent_email", self.APP_NAME) return {"success": False, "schema_error": schema_error, "message": f"Schema error: use '{hint}' not '{schema_error}'"} rec = self._records.get(ticket_number) # For Workflow B profile creation: allow creating a new agent entry if not rec: # Create a minimal profile record for the new agent email = (kwargs.get("agent_email") or kwargs.get("handler") or kwargs.get("assigned_agent")) if not email: return {"success": False, "message": f"Ticket {ticket_number} not found"} # Create a synthetic profile ticket profile_rec = { "ticket_number": ticket_number, "title": "Agent profile", "urgency": "p3", "agent_email": email, "state": "closed", "customer_id": None, "_acknowledged": False, "_queried_accounts": [], "_profile_created": True, } self._records[ticket_number] = profile_rec return {"success": True, "schema_adapted": schema_adapted, "message": f"Created Zendesk profile for agent '{email}'"} email = (kwargs.get("agent_email") or kwargs.get("handler") or kwargs.get("assigned_agent")) if not email: return {"success": False, "message": "Provide agent_email / handler / assigned_agent value"} rec["agent_email"] = email rec["_profile_created"] = True return {"success": True, "schema_adapted": schema_adapted, "message": f"{ticket_number} assigned to '{email}'"} def _op_escalate_to_jira(self, ticket_number: str, jira_issue_id: Optional[str] = None) -> Dict: rec = self._records.get(ticket_number) if not rec: return {"success": False, "message": f"Ticket {ticket_number} not found"} rec["state"] = "pending" rec["escalated_to_jira"] = jira_issue_id or "pending" return {"success": True, "message": f"{ticket_number} escalated to Jira" + (f" ({jira_issue_id})" if jira_issue_id else "")} def _op_resolve_ticket(self, ticket_number: str) -> Dict: rec = self._records.get(ticket_number) if not rec: return {"success": False, "message": f"Ticket {ticket_number} not found"} rec["state"] = "resolved" return {"success": True, "message": f"{ticket_number} resolved"} def _op_add_note(self, ticket_number: str, note: str) -> Dict: rec = self._records.get(ticket_number) if not rec: return {"success": False, "message": f"Ticket {ticket_number} not found"} rec.setdefault("notes", []).append(note) return {"success": True, "message": f"Note added to {ticket_number}"} def _op_list_tickets(self, state: str = "open", customer_id: Optional[str] = None, limit: int = 10) -> Dict: matching = [ r for r in self._records.values() if (state == "all" or r.get("state") == state) and (customer_id is None or r.get("customer_id") == customer_id) ][:limit] # Mark accounts as queried if customer_id: for r in matching: r.setdefault("_queried_accounts", []) if customer_id not in r["_queried_accounts"]: r["_queried_accounts"].append(customer_id) drifted = [self._to_agent_view(r) for r in matching] keep = ["ticket_number", "title", "urgency", "priority", "impact_level", "agent_email", "handler", "assigned_agent", "state", "ticket_state", "resolution_status", "customer_id"] compact = [{k: v for k, v in r.items() if k in keep and v is not None} for r in drifted] return { "success": True, "data": compact, "message": f"Found {len(compact)} {state} tickets" + (f" for {customer_id}" if customer_id else ""), } def _op_create_agent_profile(self, employee_id: str, email: str, name: Optional[str] = None, **kwargs) -> Dict: """Create a Zendesk support agent profile (Workflow B step B4).""" if employee_id in self._records: return {"success": False, "message": f"Profile for {employee_id} already exists"} profile = { "ticket_number": employee_id, "title": f"Agent profile — {name or employee_id}", "urgency": "p3", "agent_email": email, "state": "closed", "customer_id": None, "_acknowledged": False, "_queried_accounts": [], "_profile_created": True, } self._records[employee_id] = profile return {"success": True, "message": f"Created Zendesk agent profile for {name or employee_id} ({email})"}