Files
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

203 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 15 + 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` ~494501:
| 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`) — ~304313:
| 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** — ~316332:
| 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 anothers 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|PUT /api/admin/settings` |
| **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.ts` `SupportTicket` omits `csat`, `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` / `body` or category.
- No canned/FAQ store or docs-guide-like tree for tickets.
- No `author_role=system` first-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 optionally `TryAutoReplyLLM`).
### 4.3 AI fallback when auto fails
- Intended entry: implement `TryAutoReplyLLM` using `AIRoleSupport` completer; 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 (preagents 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/1115`, `plan-permissions/01-feature-keys.json`, `06-defaults-matrix`