307 lines
11 KiB
JSON
307 lines
11 KiB
JSON
{
|
|||
|
|
"doc": "01-extension-points",
|
||
|
|
"agent": "1/10",
|
||
|
|
"generated_for": "support-auto",
|
||
|
|
"related_docs": [
|
||
|
|
"docs/support-auto/01-inventory.md",
|
||
|
|
"docs/admin-roles-support/11-support-api-contract.json",
|
||
|
|
"docs/admin-roles-support/12-support-backend.md",
|
||
|
|
"docs/admin-roles-support/13-support-ratings.md",
|
||
|
|
"docs/admin-roles-support/14-support-staff-ui.md",
|
||
|
|
"docs/plan-permissions/01-feature-keys.json"
|
||
|
|
],
|
||
|
|
"ai_admin_settings_paths": {
|
||
|
|
"web_ui": {
|
||
|
|
"route": "/admin/settings",
|
||
|
|
"file": "apps/web/src/routes/admin/settings/+page.svelte",
|
||
|
|
"tab": "ai",
|
||
|
|
"label": "AI roles",
|
||
|
|
"roles_shown": ["processing", "vectorization", "docs_api", "support"]
|
||
|
|
},
|
||
|
|
"web_client": {
|
||
|
|
"platform_settings": "apps/web/src/lib/admin-platform-settings.ts",
|
||
|
|
"ai_roles": "apps/web/src/lib/admin-ai-roles.ts",
|
||
|
|
"symbols": [
|
||
|
|
"loadPlatformAdminSettings",
|
||
|
|
"savePlatformAdminSettings",
|
||
|
|
"saveAIRoles",
|
||
|
|
"extractAIRoles",
|
||
|
|
"testAIRole",
|
||
|
|
"AI_ROLES",
|
||
|
|
"AI_ROLE_META.support"
|
||
|
|
],
|
||
|
|
"api": {
|
||
|
|
"get_put": "GET|PUT /api/admin/settings",
|
||
|
|
"payload_key": "ai_roles",
|
||
|
|
"role_probe_expected": "POST /api/admin/settings/ai-roles/{role}/test",
|
||
|
|
"role_probe_status": "client_optional_404_skipped_not_mounted"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"api": {
|
||
|
|
"handlers": "apps/api/internal/httpapi/admin_settings_handlers.go",
|
||
|
|
"symbols": ["handleGetAdminSettings", "handlePutAdminSettings"],
|
||
|
|
"auth": ["RequireSession", "RequirePlatformAdmin"],
|
||
|
|
"mount": "apps/api/internal/httpapi/server.go"
|
||
|
|
},
|
||
|
|
"platformsettings": {
|
||
|
|
"package": "apps/api/internal/platformsettings",
|
||
|
|
"role_constants": {
|
||
|
|
"file": "apps/api/internal/platformsettings/types.go",
|
||
|
|
"AIRoleProcessing": "processing",
|
||
|
|
"AIRoleVectorization": "vectorization",
|
||
|
|
"AIRoleDocsAPI": "docs_api",
|
||
|
|
"AIRoleSupport": "support",
|
||
|
|
"AIRoles": ["processing", "vectorization", "docs_api", "support"]
|
||
|
|
},
|
||
|
|
"symbols": [
|
||
|
|
{
|
||
|
|
"name": "ValidAIRole",
|
||
|
|
"path": "apps/api/internal/platformsettings/ai_configs.go",
|
||
|
|
"line": 30
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "publicAIConfigs",
|
||
|
|
"path": "apps/api/internal/platformsettings/ai_configs.go",
|
||
|
|
"line": 39,
|
||
|
|
"sym": "sym:descrybe-v2:apps/api/internal/platformsettings/ai_configs.go:39:publicAIConfigs"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "patchAIConfigs",
|
||
|
|
"path": "apps/api/internal/platformsettings/ai_configs.go",
|
||
|
|
"line": 116
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "ResolveAIConfig",
|
||
|
|
"path": "apps/api/internal/platformsettings/ai_configs.go",
|
||
|
|
"line": 270
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"storage_note": "JSON in platform company_settings (SystemCompanyID); no dedicated AI roles table"
|
||
|
|
},
|
||
|
|
"aiprovider": {
|
||
|
|
"file": "apps/api/internal/aiprovider/roles.go",
|
||
|
|
"RoleSupport": "support",
|
||
|
|
"symbols": [
|
||
|
|
{
|
||
|
|
"name": "ResolveCompleterForRole",
|
||
|
|
"path": "apps/api/internal/aiprovider/roles.go",
|
||
|
|
"line": 44,
|
||
|
|
"sym": "sym:descrybe-v2:apps/api/internal/aiprovider/roles.go:44:ResolveCompleterForRole",
|
||
|
|
"note": "May resolve RoleSupport when slot configured; ticket auto-reply must still gate via TryAutoReplyLLM"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"not_for_tickets": {
|
||
|
|
"docs_ask": {
|
||
|
|
"component": "apps/web/src/lib/components/docs/DocsAskGuide.svelte",
|
||
|
|
"guide": "apps/web/src/lib/docs-guide",
|
||
|
|
"rule": "rule-based only; never AIRoleDocsAPI or AIRoleSupport"
|
||
|
|
},
|
||
|
|
"tenant_ai_test": {
|
||
|
|
"path": "POST /integrations/ai/test",
|
||
|
|
"handler": "handleTestAIIntegration",
|
||
|
|
"note": "company BYOK probe, not platform ai_roles"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"support_desk_inventory": {
|
||
|
|
"schema": [
|
||
|
|
"apps/api/sql/schema/025_support_center.sql",
|
||
|
|
"apps/api/sql/schema/027_capabilities_support_perf.sql",
|
||
|
|
"apps/api/sql/schema/029_staff_roles.sql",
|
||
|
|
"apps/api/sql/schema/030_support_desk.sql"
|
||
|
|
],
|
||
|
|
"service_package": "apps/api/internal/support",
|
||
|
|
"key_symbols": [
|
||
|
|
{
|
||
|
|
"name": "Create",
|
||
|
|
"path": "apps/api/internal/support/tickets.go",
|
||
|
|
"line": 252,
|
||
|
|
"hook_note": "Post-create extension for content auto-reply; must not call Completer directly"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "ReplyAsUser",
|
||
|
|
"path": "apps/api/internal/support/tickets.go",
|
||
|
|
"line": 306
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "ReplyAsAgent",
|
||
|
|
"path": "apps/api/internal/support/tickets.go",
|
||
|
|
"line": 369,
|
||
|
|
"hook_note": "Human-only; draft-assist UI should call TryAutoReplyLLM separately"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "GetAdmin",
|
||
|
|
"path": "apps/api/internal/support/tickets.go",
|
||
|
|
"line": 198,
|
||
|
|
"sym": "sym:descrybe-v2:apps/api/internal/support/tickets.go:198:GetAdmin",
|
||
|
|
"hook_note": "Richer staff detail / CSAT attach point"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "TryAutoReplyLLM",
|
||
|
|
"path": "apps/api/internal/support/ai_auto_reply.go",
|
||
|
|
"line": 18,
|
||
|
|
"sym": "sym:descrybe-v2:apps/api/internal/support/ai_auto_reply.go:18:TryAutoReplyLLM",
|
||
|
|
"status": "stub_refuses",
|
||
|
|
"error": "ErrAIAutoReplyDisabled"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "SubmitCSAT",
|
||
|
|
"path": "apps/api/internal/support/ratings.go",
|
||
|
|
"line": 18
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "AggregateCSAT",
|
||
|
|
"path": "apps/api/internal/support/ratings.go",
|
||
|
|
"line": 78
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "Claim",
|
||
|
|
"path": "apps/api/internal/support/desk.go",
|
||
|
|
"line": 108
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "ResolveStaffAccess",
|
||
|
|
"path": "apps/api/internal/auth/staff.go",
|
||
|
|
"line": 64
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"http_routes": {
|
||
|
|
"customer_prefix": "/api/support",
|
||
|
|
"staff_prefix": "/api/admin/support",
|
||
|
|
"staff_middleware": "RequireSupportDesk",
|
||
|
|
"admin_only_examples": [
|
||
|
|
"GET /api/admin/support/csat",
|
||
|
|
"PUT /api/admin/support/agents/{id}",
|
||
|
|
"GET|PUT /api/admin/settings"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"web": {
|
||
|
|
"customer": [
|
||
|
|
"apps/web/src/routes/support/+page.svelte",
|
||
|
|
"apps/web/src/routes/support/new/+page.svelte",
|
||
|
|
"apps/web/src/routes/support/[ticketId]/+page.svelte"
|
||
|
|
],
|
||
|
|
"staff": [
|
||
|
|
"apps/web/src/routes/admin/support/+page.svelte",
|
||
|
|
"apps/web/src/routes/admin/support/[id]/+page.svelte"
|
||
|
|
],
|
||
|
|
"clients": [
|
||
|
|
"apps/web/src/lib/support/api.ts",
|
||
|
|
"apps/web/src/lib/support/admin-api.ts",
|
||
|
|
"apps/web/src/lib/support/types.ts"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"plan_permission_keys": {
|
||
|
|
"section": "support",
|
||
|
|
"keys": [
|
||
|
|
"support.center",
|
||
|
|
"support.ticket_create",
|
||
|
|
"support.ticket_thread"
|
||
|
|
],
|
||
|
|
"shell": ["shell.support_notifications"],
|
||
|
|
"defaults": "ON for all public plans per docs/plan-permissions/06-defaults-matrix",
|
||
|
|
"missing_for_auto_reply": [
|
||
|
|
"no support.auto_reply key",
|
||
|
|
"no support.ai_assist key",
|
||
|
|
"CSAT not a separate feature key"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"extension_points": {
|
||
|
|
"richer_ticket_detail": [
|
||
|
|
{
|
||
|
|
"id": "staff_get_payload",
|
||
|
|
"target": "GetAdmin / handleAdminGetSupportTicket",
|
||
|
|
"path": "apps/api/internal/support/tickets.go",
|
||
|
|
"opportunity": "Already joins company/requester/assignee + messages + attachCSAT; expose resolved_by, richer org context"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "admin_client_types",
|
||
|
|
"target": "SupportTicket in admin-api.ts",
|
||
|
|
"path": "apps/web/src/lib/support/admin-api.ts",
|
||
|
|
"gap": "Omits csat / csat_eligible / resolved_by_user_id despite API capability"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "staff_detail_ui",
|
||
|
|
"target": "admin/support/[id]/+page.svelte",
|
||
|
|
"gap": "Shows subject, category, priority, company, thread, claim/assign — no CSAT panel, no linked resources, no attachments"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "csat_aggregate_ui",
|
||
|
|
"target": "GET /api/admin/support/csat",
|
||
|
|
"gap": "API exists; no admin UI page/widget"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "schema_extensions",
|
||
|
|
"note": "No attachments/metadata/product_id columns; author_role=system available for bot messages; csat_token_* unused"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"auto_response_from_question_content": [
|
||
|
|
{
|
||
|
|
"id": "post_create_hook",
|
||
|
|
"preferred_insert": "after successful Create commit (or async job enqueued from handleCreateSupportTicket)",
|
||
|
|
"inputs": ["CreateInput.subject", "CreateInput.body", "CreateInput.category"],
|
||
|
|
"pattern_reuse": "DocsAskGuide / docs-guide decision tree (rule-based, no LLM)",
|
||
|
|
"output": "insert support_messages author_role=system OR ReplyAsAgent-equivalent with system author"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "post_user_reply_hook",
|
||
|
|
"target": "ReplyAsUser",
|
||
|
|
"note": "Same matcher for follow-ups; reopen already sets status=open"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "missing_today",
|
||
|
|
"items": [
|
||
|
|
"no FAQ/canned reply store",
|
||
|
|
"no keyword classifier",
|
||
|
|
"no system first-response writer"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"ai_fallback_when_auto_fails": [
|
||
|
|
{
|
||
|
|
"id": "sole_llm_gate",
|
||
|
|
"target": "TryAutoReplyLLM",
|
||
|
|
"path": "apps/api/internal/support/ai_auto_reply.go",
|
||
|
|
"line": 18,
|
||
|
|
"current": "returns ErrAIAutoReplyDisabled",
|
||
|
|
"intended_deps": [
|
||
|
|
"platformsettings.AIRoleSupport / ResolveAIConfig",
|
||
|
|
"aiprovider.ResolveCompleterForRole(..., RoleSupport)"
|
||
|
|
],
|
||
|
|
"contract": "Create/ReplyAsUser/ReplyAsAgent must not call Completer; route AI through this function"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "config_prerequisite",
|
||
|
|
"path": "/admin/settings ai_roles.support",
|
||
|
|
"fields": ["provider", "base_url", "model", "api_key", "enabled"],
|
||
|
|
"note": "Slot configurable today; product still needs explicit enable beyond stub"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "suggested_flow",
|
||
|
|
"steps": [
|
||
|
|
"rule/FAQ matcher on question content",
|
||
|
|
"on miss or low confidence → TryAutoReplyLLM",
|
||
|
|
"on LLM refuse/error → leave open for staff claim (no silent drop)",
|
||
|
|
"optional: draft-only for agent UI vs auto-post system message"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "do_not",
|
||
|
|
"items": [
|
||
|
|
"wire DocsAskGuide to AIRoleSupport",
|
||
|
|
"use AIRoleDocsAPI for tickets",
|
||
|
|
"call ResolveCompleterForRole from ReplyAsAgent"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"gaps_summary": {
|
||
|
|
"ai_admin": "paths exist and support role is first-class",
|
||
|
|
"auto_reply": "stub only; no content matcher; no enable flag/job",
|
||
|
|
"staff_detail": "basic thread UI; CSAT/org depth/attachments missing in staff client",
|
||
|
|
"plan_keys": "tenant support.* only; no auto-reply entitlements"
|
||
|
|
}
|
||
|
|
}
|