// A refusal of the platform's knowledge service as this module's one closed set of codes // (`KNOWLEDGE_REFUSALS`), which the pages and forms show in the reader's language. A refusal that is the // deployment's or the platform's says so; the service's own words stay in `detail`, for the log and the API. import { AppError, notFound } from "@conusai-kit/core" import { KnowledgeError, type KnowledgeReasonCode } from "@conusai-kit/platform" import type { KnowledgeRefusal, ServiceState } from "../contract/index.js" const REFUSAL: Readonly, KnowledgeRefusal>> = { plan_required: "plan_required", feature_disabled: "feature_disabled", quota_exhausted: "quota_exhausted", budget_exhausted: "budget_exhausted", // The service refused the token itself: `knowledge.collections:use` is missing, `*` never carries it. forbidden: "scope_missing", indexing: "indexing", approval_required: "approval_required", runtime_unavailable: "runtime_unavailable", configuration_missing: "configuration_missing", // The platform refused the token's identity: nothing the person can act on, the app is not served. unauthenticated: "runtime_unavailable", } const STATUS: Readonly> = { plan_required: 403, feature_disabled: 403, approval_required: 403, quota_exhausted: 429, budget_exhausted: 429, scope_missing: 503, indexing: 503, runtime_unavailable: 503, configuration_missing: 503, } const TITLE: Readonly> = { plan_required: "The organization's plan does not include document search", feature_disabled: "Document search is disabled by policy", approval_required: "The platform asks for an approval first", quota_exhausted: "The indexing quota is exhausted", budget_exhausted: "The AI budget is exhausted", scope_missing: "This deployment's token does not hold knowledge.collections:use", indexing: "The knowledge service is building its index", runtime_unavailable: "The knowledge service is unavailable", configuration_missing: "The knowledge deployment configuration is incomplete", } /** The typed refusal of a service error; a resource it does not hold has none, it is simply not there. */ export function refusalOfReason(reason: KnowledgeReasonCode): KnowledgeRefusal | undefined { return reason === "unavailable_resource" ? undefined : REFUSAL[reason] } /** The readiness state a refusal puts the service in: a plan or a policy is "disabled", not "down". */ export function stateOfRefusal(refusal: KnowledgeRefusal | undefined): ServiceState { if (refusal === "plan_required" || refusal === "feature_disabled") return "disabled" return refusal === "indexing" ? "indexing" : "unavailable" } /** The app error for anything the service threw; a programming error passes through as it is. */ export function knowledgeRefusal(error: unknown): unknown { if (!(error instanceof KnowledgeError)) return error const refusal = refusalOfReason(error.reason) if (refusal === undefined) return notFound(error.message) return new AppError({ type: `/problems/${refusal}`, title: TITLE[refusal], status: STATUS[refusal], code: refusal, detail: error.message, }) } /** A refusal waiting cannot fix: the index job records it on the document instead of retrying it. */ export function permanentRefusal(error: KnowledgeError): KnowledgeRefusal | undefined { const refusal = refusalOfReason(error.reason) return refusal === "indexing" || refusal === "runtime_unavailable" ? undefined : refusal }