Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
3.9 KiB
3.9 KiB
08 — Staff auto / AI assist UI
Agent: 8/10 · Status: Implemented
Coordinates with: 02-contract (fields), 05-ticket-detail (schema + PATCH), 03/04 (match + AI drafts), 14-support-staff-ui (desk shell)
PROBLEM
Staff need to see auto-match / AI assist outcomes on a ticket, approve or edit draft_only AI drafts, disable further auto per ticket, and filter the queue for needs_human / ai_draft — without elevating support_staff beyond the existing desk gate.
WHAT SHIPPED
Access (least privilege preserved)
| Actor | Surfaces |
|---|---|
support_staff |
/admin/support/** only via requireSupportDesk / RequireSupportDesk |
| Full admin | Same desk + assign picker (unchanged) |
- New endpoints mount under the same
RequireSupportDeskgroup as claim/reply. - Approve/discard/get still call
staffMayAccessTicket(support_staff: own claim or unassigned open/pending; cross-assignee → 404 / 409). - No KB/settings/billing routes exposed to support_staff.
Queue (/admin/support)
- Filter chips: Any auto state · Needs human · AI draft (
?flag=). - Persisted in URL with existing
scope/status. - Auto column: status badge or “Auto off”.
- API:
GET /api/admin/support/tickets?flag=needs_human|ai_draftai_draft→auto_reply_status = ai_draftneeds_human→handed_off|failed|skipped
Ticket detail (/admin/support/[id])
- Auto-match / AI assist panel: status, last attempt, disable / re-enable auto (
PATCH { auto_reply_disabled }). - When
auto_reply_status=ai_draft: edit draft body → Approve & send or Discard draft. - Thread bubbles label auto/KB/AI messages; drafts stay internal-note style.
- Client falls back to public reply + disable if approve/discard routes are not yet mounted (404/501).
API client
$lib/support/admin-api.ts + $lib/support/auto-assist.ts
| Method | Path |
|---|---|
list (+ flag) |
GET …/tickets |
update (incl. auto_reply_disabled) |
PATCH …/tickets/{id} |
| approve draft | POST …/tickets/{id}/ai-draft/approve |
| discard draft | POST …/tickets/{id}/ai-draft/discard |
Domain: apps/api/internal/support/staff_auto.go (ApproveAIDraft, DiscardAIDraft, ApplyQueueFlag).
FILES
| Area | Path |
|---|---|
| Helpers | apps/web/src/lib/support/auto-assist.ts |
| Client | apps/web/src/lib/support/admin-api.ts |
| Queue UI | apps/web/src/routes/admin/support/+page.svelte |
| Detail UI | apps/web/src/routes/admin/support/[id]/+page.svelte |
| Domain | apps/api/internal/support/staff_auto.go, types.go (ListFilter.Flag), errors.go |
| List filter | apps/api/internal/support/tickets.go (ApplyQueueFlag) |
| HTTP | support_handlers.go, server.go (approve/discard mounts) |
VERIFICATION
cd apps/web && npm run check
cd apps/api && go test ./internal/support/ ./internal/httpapi/ -count=1
Manual:
- As
support_staff: open/admin/support?flag=needs_humanand?flag=ai_draft— no billing/settings nav. - Ticket with FAQ/AI outcome: panel shows status; Disable auto sticks across refresh.
ai_draftticket: edit draft → Approve & send → customer-visible agent reply; draft queue clears.- Discard →
handed_off/ auto disabled; ticket remains claimable. - Ticket assigned to another agent → get/approve → 404/409 (not full admin leak).
ASSUMPTIONS
- ASSUMPTION: Ticket auto columns come from agent 5 migration
031_support_ticket_detail(+ agent 3032KB). UI tolerates missing fields as empty/none. - ASSUMPTION:
draft_onlyAI posts internal notes withis_auto_reply+auto_source=aiand setsauto_reply_status=ai_draft(agent 4). - ASSUMPTION: Public staff reply already sets
auto_reply_disabled(existingReplyAsAgent).
ROLLBACK
Revert staff UI/client + staff_auto.go + approve/discard routes + ListFilter.Flag. Leave schema migrations in place.