feat(psa): add PSA post preview, post-to-ticket, and post history endpoints

Three new session-scoped endpoints for posting session documentation
to linked PSA tickets:

- GET /sessions/{id}/psa-post/preview — generates PSA-formatted content,
  fetches ticket details and available statuses, counts previous posts
- POST /sessions/{id}/psa-post — posts note to CW ticket with optional
  status update, logs all actions in psa_post_log audit trail
- GET /sessions/{id}/psa-posts — returns post history for the session

New schemas: PsaPostRequest, PsaPostResponse, PsaPreviewResponse,
PsaPostLogResponse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-14 23:31:11 -04:00
parent 74ee5009c2
commit 5ade3be44e
3 changed files with 310 additions and 0 deletions

View File

@@ -18,6 +18,7 @@ from .script_template import (
from .psa_connection import (
PsaConnectionCreate, PsaConnectionUpdate, PsaConnectionResponse, PsaConnectionTestResponse,
PSATicketSearchResult, PSATicketStatusItem,
PsaPostRequest, PsaPostResponse, PsaPreviewResponse, PsaPostLogResponse,
)
__all__ = [
@@ -46,4 +47,5 @@ __all__ = [
# PSA Connection
"PsaConnectionCreate", "PsaConnectionUpdate", "PsaConnectionResponse", "PsaConnectionTestResponse",
"PSATicketSearchResult", "PSATicketStatusItem",
"PsaPostRequest", "PsaPostResponse", "PsaPreviewResponse", "PsaPostLogResponse",
]

View File

@@ -64,3 +64,45 @@ class PSATicketStatusItem(BaseModel):
id: int
name: str
is_closed: bool = False
# ── PSA post (note posting) schemas ──────────────────────────────
class PsaPostRequest(BaseModel):
note_type: str = Field(pattern="^(internal_analysis|resolution|description)$")
content: str = Field(min_length=1)
update_status_id: int | None = None
class PsaPostResponse(BaseModel):
id: str
session_id: str
ticket_id: str
note_type: str
status: str
external_note_id: str | None = None
error_message: str | None = None
status_changed_from: str | None = None
status_changed_to: str | None = None
posted_at: str
class PsaPreviewResponse(BaseModel):
content: str
ticket: PSATicketSearchResult
available_statuses: list[PSATicketStatusItem]
character_count: int
previous_posts: int
class PsaPostLogResponse(BaseModel):
id: str
ticket_id: str
note_type: str
status: str
error_message: str | None = None
status_changed_from: str | None = None
status_changed_to: str | None = None
posted_at: str
content_preview: str # first 200 chars