import { SmrtObjectOptions } from '@happyvertical/smrt-core'; import { SupportAiRun, SupportAiRunCollection } from '../models/support-ai-run.js'; import { SupportCase } from '../models/support-case.js'; import { SupportInteractionCollection } from '../models/support-interaction.js'; import { SupportPolicyCollection } from '../models/support-policy.js'; import { HumanHandoffTrigger } from '../types.js'; import { HumanHandoffService } from './human-handoff-service.js'; import { SupportCaseService } from './support-case-service.js'; import { IntakeResult } from './support-intake-service.js'; /** One knowledge source consulted while drafting an automated answer. */ export interface KnowledgeSnippet { /** Source kind, e.g. `fact`, `article`, `document`. */ kind: string; /** Source reference (id, slug, or URL) for the audit trail. */ ref: string; label?: string; content: string; } /** Classification returned by {@link SupportAiBoundary.classify}. */ export interface SupportAiClassifyResult { /** One of the offered severity keys, or `''` when undetermined. */ severity: string; /** Short free-vocabulary category, or `''` when undetermined. */ category: string; /** Whether the matter is sensitive (always triggers a Human Handoff). */ sensitive: boolean; /** Confidence in `[0, 1]`. */ confidence: number; /** Model identifier, when the boundary knows it. */ model?: string; } /** Draft answer returned by {@link SupportAiBoundary.answer}. */ export interface SupportAiAnswerResult { reply: string; /** Confidence in `[0, 1]`; below the policy threshold → Human Handoff. */ confidence: number; /** Whether the reply would fully resolve the request (FR-28a). */ proposedResolution: boolean; /** Model identifier, when the boundary knows it. */ model?: string; } /** * THE AI seam: the only thing tests mock. The default implementation * ({@link createDefaultAiBoundary}) delegates to the case's own `do()` AI * operation from `smrt-core`. */ export interface SupportAiBoundary { classify(input: { subject: string; body: string; severityKeys: string[]; sensitiveCategories: string[]; }): Promise; answer(input: { subject: string; body: string; knowledge: KnowledgeSnippet[]; caseSummary: string; }): Promise; } /** * The knowledge seam: retrieve the snippets an automated answer may cite. * The default provider returns nothing, keeping this package dependency-free * — apps plug `smrt-facts` / `smrt-content`-backed providers. */ export interface SupportKnowledgeProvider { retrieve(input: { subject: string; body: string; projectId: string | null; tenantId: string | null; }): Promise; } /** Notification passed to `onHandoff` after each handoff attempt. */ export interface SupportHandoffNotice { supportCase: SupportCase; trigger: HumanHandoffTrigger; /** True when the no-repeat guarantee deduped this trigger. */ alreadyActive: boolean; } /** Options for {@link SupportAiWorkflow.create}. */ export interface SupportAiWorkflowOptions extends SmrtObjectOptions { /** The AI seam; omitted → {@link createDefaultAiBoundary} per case. */ boundary?: SupportAiBoundary; /** The knowledge seam; omitted → a no-op provider. */ knowledge?: SupportKnowledgeProvider; /** Handoff engine to share; omitted → one is created lazily. */ handoffService?: HumanHandoffService; /** Called after every handoff attempt (app notification seam). */ onHandoff?: (notice: SupportHandoffNotice) => Promise; } /** * Numeric rank of a severity key by its numeric suffix (`sev1` → 1; lower is * more severe). Returns `NaN` for keys without a numeric suffix, which makes * every comparison false — auto-resolution fails closed and the * high-severity trigger stays quiet for unknown vocabularies. */ export declare function severityRank(severityKey: string | null | undefined): number; /** * The Automated Support Response pipeline. Construct with * {@link SupportAiWorkflow.create}; wire {@link processIntake} into * `SupportIntakeService`'s `onCaseIntake` hook. */ export declare class SupportAiWorkflow { readonly caseService: SupportCaseService; readonly policies: SupportPolicyCollection; readonly aiRuns: SupportAiRunCollection; readonly interactions: SupportInteractionCollection; readonly handoffs: HumanHandoffService; private readonly boundary; private readonly knowledge; private readonly onHandoff?; protected constructor(deps: { caseService: SupportCaseService; policies: SupportPolicyCollection; aiRuns: SupportAiRunCollection; interactions: SupportInteractionCollection; handoffs: HumanHandoffService; boundary: SupportAiBoundary | null; knowledge: SupportKnowledgeProvider; onHandoff?: (notice: SupportHandoffNotice) => Promise; }); static create(options: SupportAiWorkflowOptions): Promise; /** * Entry point for `SupportIntakeService.onCaseIntake`: run one automated * pass over the case an inbound interaction created or joined. */ processIntake(result: IntakeResult): Promise; /** * Run one Automated Support Response pass over a case: acknowledge → * classify → answer → troubleshoot → resolve, each gated by the resolved * policy and audited as a {@link SupportAiRun}. Cases with `aiEnabled` * off, or no longer open, are left untouched. */ processCase(caseId: string, opts?: { interactionId?: string | null; }): Promise; /** Acknowledge phase: post the templated receipt once per case. */ private runAcknowledge; /** Why autonomous resolution must not run, or `null` when it may. */ private resolveSkipReason; /** Resolve the governing policy: matching row, else built-in defaults. */ private resolveEffectivePolicy; /** * Prior boundary-consuming answer attempts. Low-confidence answers consume * the boundary too (`handed_off`), so they count toward the attempt budget * — only `skipped` runs (which never called the boundary) are free. */ private countAnswerAttempts; /** The request text a pass reasons over: triggering interaction, else the * case description. */ private requestBodyOf; /** Severity vocabulary: the case's plan snapshot, else the defaults. */ private severityKeysOf; /** One-line case summary handed to the answer boundary. */ private caseSummaryOf; /** Route a trigger through the handoff engine and notify the app seam. */ private triggerHandoff; /** Append one audit run plus its `ai_run` case event. */ private writeRun; /** The persisted id of a saved case. */ private caseIdOf; } /** * The default knowledge provider: retrieves nothing. Keeps the package * dependency-free; apps supply providers backed by `smrt-facts`, * `smrt-content`, or their own corpus. */ export declare function createNoopKnowledgeProvider(): SupportKnowledgeProvider; /** * The default AI boundary: delegates to the case's own `do()` AI operation * (smrt-core) asking for strict JSON, parsed defensively — a malformed * classification comes back sensitive with zero confidence, failing toward * the human. */ export declare function createDefaultAiBoundary(getCase: () => SupportCase): SupportAiBoundary; export default SupportAiWorkflow; //# sourceMappingURL=support-ai-workflow.d.ts.map