Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
10 KiB
01 — Support desk + admin AI inventory (auto-reply / AI assist)
Agent: 1/10 · Mode: read-only inventory
Tools: codehelper project_context → kickoff → query/investigate; Read/Grep/Shell fallback (read_workspace_file / write_workspace_file unavailable in focused MCP profile)
Related: docs/admin-roles-support/*, docs/plan-permissions/01-feature-keys.json
Executive summary
Support desk is shipped: customer tickets + CSAT, staff queue/claim with support_staff, platform admin aggregate CSAT, agents directory. Platform AI already has a dedicated support role slot (provider / base_url / model / encrypted key) editable at /admin/settings → AI roles. LLM ticket auto-reply is an intentional safe stub (TryAutoReplyLLM → always ErrAIAutoReplyDisabled); Create / ReplyAsUser / ReplyAsAgent must not call Completers. No FAQ/canned/rule-based ticket auto-response exists yet (docs Ask is a separate rule tree and must not use AIRoleSupport).
1. Support desk — current surface
1.1 Schema
| Migration | Purpose |
|---|---|
025_support_center.sql |
support_tickets, support_messages, support_notifications |
027_capabilities_support_perf.sql |
assignee / activity indexes |
029_staff_roles.sql |
users.staff_role ∈ admin|developer|support_staff |
030_support_desk.sql |
resolved_by_user_id, CSAT table + token cols, unassigned index, ticket_claimed / csat_requested kinds |
Ticket enums: status open|pending|resolved|closed; category billing|bug|account|other; priority low|normal|high.
Message author_role: user|agent|system (authorship, not staff RBAC).
CSAT: support_csat_ratings — one rating per ticket (score 1–5 + comment). Public token columns exist but are not wired.
1.2 Domain packages
| Path | Role |
|---|---|
apps/api/internal/support/types.go |
Ticket, Message, CSAT*, CreateInput, ReplyInput, list scopes |
apps/api/internal/support/tickets.go |
Create, Get/GetAdmin, ReplyAsUser, ReplyAsAgent, UpdateAdmin, listMessages |
apps/api/internal/support/desk.go |
scopes, Claim/Release, GetAdminForActor |
apps/api/internal/support/agents.go |
agents directory via staff_role |
apps/api/internal/support/ratings.go |
SubmitCSAT, AggregateCSAT, attachCSAT |
apps/api/internal/support/ai_auto_reply.go |
TryAutoReplyLLM stub (sole LLM gate) |
apps/api/internal/support/errors.go |
ErrAIAutoReplyDisabled |
1.3 HTTP API
Customer (RequireSession + RequireCompany) — server.go ~494–501:
| Method | Path |
|---|---|
| GET/POST | /api/support/tickets |
| GET | /api/support/tickets/{id} |
| POST | /api/support/tickets/{id}/messages |
| POST | /api/support/tickets/{id}/csat |
| GET/POST | /api/support/notifications (+ read / read-all) |
Staff desk (RequireSupportDesk) — ~304–313:
| Method | Path |
|---|---|
| GET | /api/admin/support/tickets (scope=inbox|mine|unassigned|all) |
| GET/PATCH | /api/admin/support/tickets/{id} |
| POST | …/messages, …/claim, …/release |
| GET | /api/admin/support/agents |
Platform admin only — ~316–332:
| Method | Path |
|---|---|
| GET | /api/admin/support/csat?from=&to= |
| PUT | /api/admin/support/agents/{id} |
| GET/PUT | /api/admin/settings (includes ai_roles) |
| PATCH | /api/admin/users/{id}/staff-role |
Contract design: docs/admin-roles-support/11-support-api-contract.json · backend notes: 12-support-backend.md · CSAT: 13-support-ratings.md.
1.4 Staff access
| Symbol | Path | Behavior |
|---|---|---|
ResolveStaffAccess |
apps/api/internal/auth/staff.go |
support_staff → desk only; admin/developer/legacy platform admin → full |
RequireSupportDesk |
httpapi/middleware.go |
desk routes |
RequirePlatformAdmin |
same | excludes support_staff from billing/settings/users |
requireSupportDesk / requirePlatformAdmin |
apps/web/src/lib/admin-gate.ts |
UI gates |
Visibility: queue + claim (inbox/mine/unassigned; all = full admin). Cross-assignee GET → 404; reply on another’s claim → 409.
1.5 Web UI
| Persona | Routes | Client |
|---|---|---|
| Customer | /support, /support/new, /support/[ticketId] |
$lib/support/api.ts, CSAT via SupportTicketRating.svelte |
| Staff | /admin/support, /admin/support/[id] |
$lib/support/admin-api.ts |
| Role grant | /admin/users |
PATCH …/staff-role |
| AI keys | /admin/settings tab AI roles |
$lib/admin-ai-roles.ts |
Docs: 14-support-staff-ui.md, 15-user-support-ui.md.
1.6 Plan-permission keys (tenant Support Center)
From docs/plan-permissions/01-feature-keys.json / plan-feature-catalog.ts — section support, defaults ON all public plans:
| Key | Route |
|---|---|
support.center |
/support |
support.ticket_create |
/support/new |
support.ticket_thread |
/support/[ticketId] |
shell.support_notifications |
notification bell |
No plan keys for staff desk, AI auto-reply, or CSAT (CSAT rides on thread eligibility).
2. Admin AI configuration (platform)
2.1 Role catalog
Stored in platform settings JSON (company_settings / SystemCompanyID) — not a dedicated SQL table.
| Role key | Constant | Purpose today |
|---|---|---|
processing |
AIRoleProcessing / RoleProcessing |
Product pipeline chat; falls back to legacy openai + env |
vectorization |
AIRoleVectorization |
Embeddings; env fallback |
docs_api |
AIRoleDocsAPI |
Config slot only — /docs Ask must not use it |
support |
AIRoleSupport / RoleSupport |
Ticket-assist slot — configure only; auto-reply gated by stub |
Fields per role: provider, base_url, model, encrypted api_key, enabled, optional extras. Secrets masked on GET (api_key_last4 / masked).
2.2 Existing AI admin settings paths
| Layer | Path / symbol |
|---|---|
| UI | apps/web/src/routes/admin/settings/+page.svelte — tabs Overview / AI roles / Mail / Other / Admin Users |
| Client load/save | apps/web/src/lib/admin-platform-settings.ts — `GET |
| Client AI roles | apps/web/src/lib/admin-ai-roles.ts — AI_ROLES, saveAIRoles, extractAIRoles, testAIRole |
| API handlers | apps/api/internal/httpapi/admin_settings_handlers.go — handleGetAdminSettings / handlePutAdminSettings |
| Storage / resolve | apps/api/internal/platformsettings/ — ai_configs.go (publicAIConfigs, patchAIConfigs, ResolveAIConfig), types.go (AIRoles, AIRoleSupport) |
| Runtime completer | apps/api/internal/aiprovider/roles.go — ResolveCompleterForRole (supports RoleSupport when configured) |
| Tenant BYOK test (not platform roles) | POST /api/…/integrations/ai/test — handleTestAIIntegration |
| Optional role probe | Client expects POST /api/admin/settings/ai-roles/{role}/test — not mounted; testAIRole treats 404/501 as skipped |
ASSUMPTION: Enabling LLM ticket assist should use ResolveCompleterForRole(..., RoleSupport) (or ResolveAIConfig(AIRoleSupport)) only inside/behind TryAutoReplyLLM, never from Create/Reply handlers directly.
2.3 Docs Ask (non-ticket, rule-based)
DocsAskGuide.svelte + $lib/docs-guide — decision tree only. Explicitly must never call AIRoleDocsAPI or AIRoleSupport. Useful pattern for content-based auto-response before LLM fallback — not wired to tickets.
3. Auto-reply / AI assist — current state
| Capability | Status |
|---|---|
Admin config slot ai_roles.support |
Exists (UI + API + tests) |
aiprovider.ResolveCompleterForRole(RoleSupport) |
Resolves when slot configured |
support.TryAutoReplyLLM |
Stub refuses (ErrAIAutoReplyDisabled) |
| Call sites from Create / Reply* | Forbidden by comments; Create documents “do not call” |
| FAQ / keyword / canned auto-reply | Absent |
| Draft-assist UI for agents | Absent |
| Job/queue for async auto-reply | Absent |
| Plan/feature flag for auto-reply | Absent |
author_role=system messages |
Schema allows; no auto-writer |
Test lock: ai_auto_reply_test.go — TestTryAutoReplyLLM_refuses.
4. Gaps (richer detail / auto-response / AI fallback)
4.1 Richer ticket detail
- Staff
admin-api.tsSupportTicketomitscsat,csat_eligible,resolved_by_user_id— API can attach CSAT on GetAdmin but admin UI/types ignore it. - No admin CSAT aggregate UI for
GET /api/admin/support/csat. - No attachments, linked product/feed IDs, or structured metadata on tickets/messages.
- No SLA timers, tags, macros, or customer org context beyond company name + requester email.
- Public CSAT token invite flow (
csat_token_hash) unused. - Message list is plain body text only (no markdown/HTML sanitization layer called out for AI drafts).
4.2 Auto-response from question content
- No classifier on
CreateInput.subject/bodyor category. - No canned/FAQ store or docs-guide-like tree for tickets.
- No
author_role=systemfirst-response after create. - Create commits then returns — no post-commit hook for auto-reply (natural extension: after successful
Create/ReplyAsUser, call a non-LLM matcher then optionallyTryAutoReplyLLM).
4.3 AI fallback when auto fails
- Intended entry: implement
TryAutoReplyLLMusingAIRoleSupportcompleter; today always fails closed. - No confidence threshold, human-handoff status (
pending), or “AI attempted” audit fields. - No product enable flag separate from
ai_roles.support.enabled(comments require an explicit gate). - Docs Ask must remain separate — do not reuse docs_api for ticket LLM.
5. Prior inventory note
docs/admin-roles-support/02-current-inventory.md is stale relative to shipped staff roles + CSAT (pre–agents 6/12/13/14). Prefer 12–15 + this doc for current support/AI state.
6. Sources (codehelper + docs)
- Investigate:
TryAutoReplyLLM,publicAIConfigs,GetAdmin - Query: support tickets/CSAT, admin AI settings, plan support keys
- Docs:
admin-roles-support/11–15,plan-permissions/01-feature-keys.json,06-defaults-matrix