personal memory agent
at main 169 lines 4.5 kB view raw
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4"""Support tool functions for agent workflows. 5 6Each function provides a discrete capability that both the talent agent 7(via ``sol call support``) and the convey routes can use. All outbound 8operations are **consent-gated** — they return a draft for review rather 9than submitting directly. 10""" 11 12from __future__ import annotations 13 14import logging 15from typing import Any 16 17logger = logging.getLogger(__name__) 18 19 20def support_diagnose() -> dict[str, Any]: 21 """Run local diagnostics — no network. 22 23 Returns a dict of system state suitable for the ``user_context`` field. 24 """ 25 from apps.support.diagnostics import collect_all 26 27 return collect_all() 28 29 30def support_search(query: str, portal_url: str | None = None) -> list[dict[str, Any]]: 31 """Search the knowledge base for articles matching *query*.""" 32 from apps.support.portal import get_client 33 34 client = get_client(portal_url=portal_url) 35 return client.search_articles(query=query) 36 37 38def support_article(slug: str, portal_url: str | None = None) -> dict[str, Any]: 39 """Read a single KB article by slug.""" 40 from apps.support.portal import get_client 41 42 client = get_client(portal_url=portal_url) 43 return client.get_article(slug) 44 45 46def support_create( 47 *, 48 subject: str, 49 description: str, 50 product: str = "solstone", 51 severity: str = "medium", 52 category: str | None = None, 53 user_email: str | None = None, 54 user_context: dict | None = None, 55 auto_context: bool = True, 56 portal_url: str | None = None, 57 anonymous: bool = False, 58) -> dict[str, Any]: 59 """Create a support ticket. 60 61 If *auto_context* is True (default), diagnostic data is collected 62 and merged into *user_context*. 63 """ 64 from apps.support.portal import get_client 65 66 if auto_context: 67 from apps.support.diagnostics import collect_all 68 69 diag = collect_all() 70 if user_context: 71 diag.update(user_context) 72 user_context = diag 73 74 client = get_client(portal_url=portal_url, anonymous=anonymous) 75 return client.create_ticket( 76 product=product, 77 subject=subject, 78 description=description, 79 severity=severity, 80 category=category, 81 user_email=user_email, 82 user_context=user_context, 83 ) 84 85 86def support_feedback( 87 *, 88 body: str, 89 product: str = "solstone", 90 portal_url: str | None = None, 91 anonymous: bool = False, 92) -> dict[str, Any]: 93 """Submit feedback (lower-friction path). 94 95 Feedback is a ticket with ``category="feedback"`` and low severity. 96 """ 97 return support_create( 98 subject="User feedback", 99 description=body, 100 product=product, 101 severity="low", 102 category="feedback", 103 portal_url=portal_url, 104 anonymous=anonymous, 105 ) 106 107 108def support_list( 109 *, 110 status: str | None = None, 111 portal_url: str | None = None, 112) -> list[dict[str, Any]]: 113 """List the user's tickets.""" 114 from apps.support.portal import get_client 115 116 client = get_client(portal_url=portal_url) 117 return client.list_tickets(status=status) 118 119 120def support_check( 121 ticket_id: int, 122 portal_url: str | None = None, 123) -> dict[str, Any]: 124 """Check status of a specific ticket (with message thread).""" 125 from apps.support.portal import get_client 126 127 client = get_client(portal_url=portal_url) 128 return client.get_ticket(ticket_id) 129 130 131def support_reply( 132 ticket_id: int, 133 content: str, 134 portal_url: str | None = None, 135) -> dict[str, Any]: 136 """Reply to a ticket.""" 137 from apps.support.portal import get_client 138 139 client = get_client(portal_url=portal_url) 140 return client.reply_to_ticket(ticket_id, content) 141 142 143def support_attach( 144 ticket_id: int, 145 file_path: str, 146 *, 147 filename: str | None = None, 148 portal_url: str | None = None, 149) -> dict[str, Any]: 150 """Attach a file to an existing ticket. 151 152 Returns the attachment metadata from the portal. 153 """ 154 from pathlib import Path 155 156 from apps.support.portal import get_client 157 158 client = get_client(portal_url=portal_url) 159 return client.attach_file(ticket_id, Path(file_path), filename=filename) 160 161 162def support_announcements( 163 portal_url: str | None = None, 164) -> list[dict[str, Any]]: 165 """List active announcements.""" 166 from apps.support.portal import get_client 167 168 client = get_client(portal_url=portal_url) 169 return client.list_announcements()