@happyvertical/smrt-support 0.39.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/AGENTS.md +270 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/dist/__smrt-register__.d.ts +2 -0
  5. package/dist/__smrt-register__.d.ts.map +1 -0
  6. package/dist/index.d.ts +5 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +4358 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/manifest.json +6770 -0
  11. package/dist/models/index.d.ts +17 -0
  12. package/dist/models/index.d.ts.map +1 -0
  13. package/dist/models/service-time-entry.d.ts +75 -0
  14. package/dist/models/service-time-entry.d.ts.map +1 -0
  15. package/dist/models/support-ai-run.d.ts +45 -0
  16. package/dist/models/support-ai-run.d.ts.map +1 -0
  17. package/dist/models/support-case-event.d.ts +25 -0
  18. package/dist/models/support-case-event.d.ts.map +1 -0
  19. package/dist/models/support-case.d.ts +125 -0
  20. package/dist/models/support-case.d.ts.map +1 -0
  21. package/dist/models/support-channel-binding.d.ts +47 -0
  22. package/dist/models/support-channel-binding.d.ts.map +1 -0
  23. package/dist/models/support-compensation-plan.d.ts +42 -0
  24. package/dist/models/support-compensation-plan.d.ts.map +1 -0
  25. package/dist/models/support-interaction.d.ts +49 -0
  26. package/dist/models/support-interaction.d.ts.map +1 -0
  27. package/dist/models/support-plan.d.ts +92 -0
  28. package/dist/models/support-plan.d.ts.map +1 -0
  29. package/dist/models/support-policy.d.ts +84 -0
  30. package/dist/models/support-policy.d.ts.map +1 -0
  31. package/dist/models/support-service-target.d.ts +90 -0
  32. package/dist/models/support-service-target.d.ts.map +1 -0
  33. package/dist/models/support-settlement.d.ts +58 -0
  34. package/dist/models/support-settlement.d.ts.map +1 -0
  35. package/dist/models/support-specialist.d.ts +71 -0
  36. package/dist/models/support-specialist.d.ts.map +1 -0
  37. package/dist/models/support-work-link.d.ts +31 -0
  38. package/dist/models/support-work-link.d.ts.map +1 -0
  39. package/dist/permissions.d.ts +32 -0
  40. package/dist/permissions.d.ts.map +1 -0
  41. package/dist/services/coverage-calendar.d.ts +53 -0
  42. package/dist/services/coverage-calendar.d.ts.map +1 -0
  43. package/dist/services/human-handoff-service.d.ts +113 -0
  44. package/dist/services/human-handoff-service.d.ts.map +1 -0
  45. package/dist/services/index.d.ts +15 -0
  46. package/dist/services/index.d.ts.map +1 -0
  47. package/dist/services/keyed-mutex.d.ts +22 -0
  48. package/dist/services/keyed-mutex.d.ts.map +1 -0
  49. package/dist/services/service-target-engine.d.ts +133 -0
  50. package/dist/services/service-target-engine.d.ts.map +1 -0
  51. package/dist/services/service-time-entry-service.d.ts +73 -0
  52. package/dist/services/service-time-entry-service.d.ts.map +1 -0
  53. package/dist/services/support-ai-workflow.d.ts +178 -0
  54. package/dist/services/support-ai-workflow.d.ts.map +1 -0
  55. package/dist/services/support-case-service.d.ts +162 -0
  56. package/dist/services/support-case-service.d.ts.map +1 -0
  57. package/dist/services/support-intake-service.d.ts +106 -0
  58. package/dist/services/support-intake-service.d.ts.map +1 -0
  59. package/dist/services/support-plan-admin-service.d.ts +45 -0
  60. package/dist/services/support-plan-admin-service.d.ts.map +1 -0
  61. package/dist/services/support-routing-service.d.ts +117 -0
  62. package/dist/services/support-routing-service.d.ts.map +1 -0
  63. package/dist/services/time-entry-approval-service.d.ts +162 -0
  64. package/dist/services/time-entry-approval-service.d.ts.map +1 -0
  65. package/dist/smrt-knowledge.json +4242 -0
  66. package/dist/svelte/components/CaseDetail.svelte +277 -0
  67. package/dist/svelte/components/CaseDetail.svelte.d.ts +10 -0
  68. package/dist/svelte/components/CaseDetail.svelte.d.ts.map +1 -0
  69. package/dist/svelte/components/CaseQueue.svelte +148 -0
  70. package/dist/svelte/components/CaseQueue.svelte.d.ts +12 -0
  71. package/dist/svelte/components/CaseQueue.svelte.d.ts.map +1 -0
  72. package/dist/svelte/components/RoutingRationale.svelte +131 -0
  73. package/dist/svelte/components/RoutingRationale.svelte.d.ts +11 -0
  74. package/dist/svelte/components/RoutingRationale.svelte.d.ts.map +1 -0
  75. package/dist/svelte/components/TargetList.svelte +127 -0
  76. package/dist/svelte/components/TargetList.svelte.d.ts +9 -0
  77. package/dist/svelte/components/TargetList.svelte.d.ts.map +1 -0
  78. package/dist/svelte/components/TimeEntryApprovalQueue.svelte +226 -0
  79. package/dist/svelte/components/TimeEntryApprovalQueue.svelte.d.ts +11 -0
  80. package/dist/svelte/components/TimeEntryApprovalQueue.svelte.d.ts.map +1 -0
  81. package/dist/svelte/components/__tests__/CaseDetail.test.js +86 -0
  82. package/dist/svelte/components/__tests__/CaseQueue.test.js +67 -0
  83. package/dist/svelte/components/__tests__/RoutingRationale.test.js +69 -0
  84. package/dist/svelte/components/__tests__/TargetList.test.js +84 -0
  85. package/dist/svelte/components/__tests__/TimeEntryApprovalQueue.test.js +105 -0
  86. package/dist/svelte/index.d.ts +25 -0
  87. package/dist/svelte/index.d.ts.map +1 -0
  88. package/dist/svelte/index.js +18 -0
  89. package/dist/svelte/types.d.ts +129 -0
  90. package/dist/svelte/types.d.ts.map +1 -0
  91. package/dist/svelte/types.js +182 -0
  92. package/dist/types.d.ts +130 -0
  93. package/dist/types.d.ts.map +1 -0
  94. package/dist/types.js +109 -0
  95. package/dist/types.js.map +1 -0
  96. package/package.json +86 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["status","tenantId","supportCase"],"sources":["../src/__smrt-register__.ts","../src/permissions.ts","../src/models/service-time-entry.ts","../src/models/support-ai-run.ts","../src/models/support-case.ts","../src/models/support-case-event.ts","../src/models/support-channel-binding.ts","../src/models/support-compensation-plan.ts","../src/models/support-interaction.ts","../src/models/support-plan.ts","../src/models/support-policy.ts","../src/models/support-service-target.ts","../src/models/support-settlement.ts","../src/models/support-specialist.ts","../src/models/support-work-link.ts","../src/services/coverage-calendar.ts","../src/services/support-case-service.ts","../src/services/human-handoff-service.ts","../src/services/keyed-mutex.ts","../src/services/support-routing-service.ts","../src/services/service-target-engine.ts","../src/services/service-time-entry-service.ts","../src/services/support-ai-workflow.ts","../src/services/support-intake-service.ts","../src/services/support-plan-admin-service.ts","../src/services/time-entry-approval-service.ts","../src/index.ts"],"sourcesContent":["import { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Custom permission slugs contributed by `@happyvertical/smrt-support` to the\n * runtime permission catalog (the `personas.activate-directive` pattern).\n *\n * The privileged operations are permission splits, not bespoke checks:\n * - `support.reassign-case` — manual reassignment despite routing (FR-30).\n * - `support.approve-time-entry` — accept a Service Time Entry and derive its\n * commercial snapshots (FR-36) via the operator path.\n * - `support.manage-plans` — administer Managed Support Plans and Support\n * Compensation Plans.\n */\n\nimport {\n type PermissionDefinition,\n registerPermissionDefinitions,\n} from '@happyvertical/smrt-users';\n\n/** Authorizes manual case reassignment overriding automatic routing. */\nexport const REASSIGN_CASE_PERMISSION = 'support.reassign-case';\n\n/** Authorizes operator approval/rejection of Service Time Entries. */\nexport const APPROVE_TIME_ENTRY_PERMISSION = 'support.approve-time-entry';\n\n/** Authorizes plan administration (support + compensation plans). */\nexport const MANAGE_PLANS_PERMISSION = 'support.manage-plans';\n\nexport const SUPPORT_PERMISSION_DEFS: PermissionDefinition[] = [\n {\n slug: REASSIGN_CASE_PERMISSION,\n category: 'support',\n name: 'Reassign Support Case',\n description:\n 'Manually reassign a Support Case to a specialist, overriding automatic routing',\n },\n {\n slug: APPROVE_TIME_ENTRY_PERMISSION,\n category: 'support',\n name: 'Approve Service Time Entry',\n description:\n 'Approve or reject submitted Service Time Entries and finalize their charge/compensation snapshots',\n },\n {\n slug: MANAGE_PLANS_PERMISSION,\n category: 'support',\n name: 'Manage Support Plans',\n description:\n 'Create and edit Managed Support Plans and Support Compensation Plans',\n },\n];\n\n/**\n * An actor whose authority is expressed as held permission slugs. Minimal on\n * purpose — the gate depends on the permission model only (personas idiom).\n */\nexport interface SupportPrincipal {\n /** Stable id recorded as the acting reviewer/assigner. */\n readonly id?: string;\n /** Tenant the authority was resolved for (cross-tenant acts are refused). */\n readonly tenantId?: string;\n can(slug: string): boolean;\n}\n\n/** Build a {@link SupportPrincipal} from explicit granted slugs. */\nexport function supportPrincipalFromPermissions(\n permissions: Iterable<string>,\n options: { id?: string; tenantId?: string } = {},\n): SupportPrincipal {\n const granted = new Set(permissions);\n return {\n id: options.id,\n tenantId: options.tenantId,\n can: (slug: string) => granted.has(slug),\n };\n}\n\nlet supportPermissionsRegistered = false;\n\n/** Register the support permissions (returns an unregister fn for tests). */\nexport function registerSupportPermissions(): () => void {\n return registerPermissionDefinitions(SUPPORT_PERMISSION_DEFS);\n}\n\n/**\n * Register the support permissions once per process. Called as a package\n * entry side effect so any consumer sees the slugs in the catalog.\n */\nexport function ensureSupportPermissionsRegistered(): void {\n if (supportPermissionsRegistered) {\n return;\n }\n supportPermissionsRegistered = true;\n registerSupportPermissions();\n}\n","/**\n * ServiceTimeEntry — an auditable duration of Professional Service attributed\n * to its work context and delivering Participant (FR-34/FR-40, issue #1930).\n *\n * Work-context-generic by design: a Support Case is one context\n * (`caseId`), but planning and development work attach through the\n * polymorphic `workRefType`/`workRefId` pair, so this model can graduate to\n * the shared Professional Services boundary without a migration.\n *\n * Timer, manual, imported, and automatic agent entries share this one\n * validation and audit contract. Duration and work evidence stay separate\n * from money: client pricing derives from the Managed Support Plan and\n * provider earnings from the Support Compensation Plan at approval time (see\n * `TimeEntryApprovalService`), each into its own immutable snapshot row.\n *\n * **Immutability**: once a row is `approved`, its work-defining fields\n * (duration, period, description, evidence, context, participant, source)\n * are frozen — enforced at save time via the WeakMap-snapshot idiom\n * (`commerce LicenseSale` precedent). Disputes create explicit corrections\n * (`correctionOfId` chain); the original flips to `corrected` but its\n * snapshot never changes.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n parseJsonField,\n SERVICE_TIME_ENTRY_STATUS_TRANSITIONS,\n type ServiceTimeEntrySource,\n type ServiceTimeEntryStatus,\n type SupportParticipantKind,\n type TimeEntryEvidence,\n} from '../types.js';\n\n/** Fields frozen once an entry reaches `approved`. */\nconst FROZEN_FIELDS = [\n 'caseId',\n 'workRefType',\n 'workRefId',\n 'specialistId',\n 'participantKind',\n 'participantProfileId',\n 'agentRef',\n 'source',\n 'description',\n 'startedAt',\n 'endedAt',\n 'durationSeconds',\n 'evidence',\n] as const;\n\n/** Serialized frozen-field snapshot captured at/after approval. */\nconst approvedSnapshot = new WeakMap<ServiceTimeEntry, string>();\n\n/** Status each persisted row was loaded with (transition guard). */\nconst loadedEntryStatus = new WeakMap<\n ServiceTimeEntry,\n ServiceTimeEntryStatus\n>();\n\n/**\n * Normalize one frozen-field value so in-memory and raw-DB-row views compare\n * equal across adapters (SQLite returns dates as strings, numbers sometimes\n * as strings; empty string and null both mean \"unset\" for nullable refs).\n */\nfunction normalizeFrozenValue(key: string, value: unknown): unknown {\n if (value === undefined || value === null || value === '') {\n return key === 'durationSeconds' ? 0 : null;\n }\n if (key === 'startedAt' || key === 'endedAt') {\n const date = value instanceof Date ? value : new Date(String(value));\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString();\n }\n if (key === 'durationSeconds') {\n return Number(value);\n }\n return value;\n}\n\nfunction serializeFrozenView(view: Record<string, unknown>): string {\n const normalized: Record<string, unknown> = {};\n for (const key of FROZEN_FIELDS) {\n normalized[key] = normalizeFrozenValue(key, view[key]);\n }\n return JSON.stringify(normalized);\n}\n\nfunction serializeFrozenFields(entry: ServiceTimeEntry): string {\n const view: Record<string, unknown> = {};\n for (const key of FROZEN_FIELDS) {\n view[key] = (entry as unknown as Record<string, unknown>)[key];\n }\n return serializeFrozenView(view);\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'service_time_entries',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class ServiceTimeEntry extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Support Case context, when the work was support (Support Time Entry). */\n @foreignKey('SupportCase')\n caseId: string | null = null;\n\n /**\n * Polymorphic work context for non-case work (planning sessions,\n * development work items): a qualified class name + id. Either `caseId` or\n * a work ref (or both) should identify the context.\n */\n @field({ type: 'text', nullable: true })\n workRefType: string | null = null;\n\n @field({ type: 'text', nullable: true })\n workRefId: string | null = null;\n\n /** Specialist role record, when the deliverer is a Support Specialist. */\n @foreignKey('SupportSpecialist')\n specialistId: string | null = null;\n\n /** Whether a human or a software agent delivered the work. */\n @field({ type: 'text' })\n participantKind: SupportParticipantKind = 'human';\n\n /** Delivering human Participant (Person profile), when human. */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n participantProfileId: string | null = null;\n\n /** Delivering agent identity (persona/agent ref), when agent. */\n @field({ type: 'text' })\n agentRef: string = '';\n\n @field({ type: 'text' })\n source: ServiceTimeEntrySource = 'manual';\n\n @field({ type: 'text' })\n description: string = '';\n\n /** Work period bounds (informational; `durationSeconds` is authoritative). */\n startedAt: Date | null = null;\n\n endedAt: Date | null = null;\n\n /** Authoritative worked duration in seconds. */\n durationSeconds: number = 0;\n\n /** Work evidence: JSON array of {@link TimeEntryEvidence}. */\n @field({ type: 'text' })\n evidence: string = '[]';\n\n @field({ type: 'text' })\n status: ServiceTimeEntryStatus = 'draft';\n\n submittedAt: Date | null = null;\n\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n submittedByProfileId: string | null = null;\n\n approvedAt: Date | null = null;\n\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n approvedByProfileId: string | null = null;\n\n /** Which approval path accepted the entry (FR-36). */\n @field({ type: 'text' })\n approvalPath: string = '';\n\n rejectedAt: Date | null = null;\n\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n rejectedByProfileId: string | null = null;\n\n @field({ type: 'text' })\n rejectionReason: string = '';\n\n /** The approved entry this one explicitly corrects, when a correction. */\n @foreignKey('ServiceTimeEntry')\n correctionOfId: string | null = null;\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getEvidence(): TimeEntryEvidence[] {\n const parsed = parseJsonField<TimeEntryEvidence[]>(this.evidence, []);\n return Array.isArray(parsed) ? parsed : [];\n }\n\n setEvidence(items: TimeEntryEvidence[]): void {\n this.evidence = JSON.stringify(items ?? []);\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n\n /** Worked duration in decimal hours (UI convenience). */\n durationHours(): number {\n return this.durationSeconds / 3600;\n }\n\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedEntryStatus.set(this, this.status);\n if (this.status === 'approved' || this.status === 'corrected') {\n approvedSnapshot.set(this, serializeFrozenFields(this));\n }\n }\n return this;\n }\n\n override async save(): Promise<this> {\n const prior = await this.resolvePriorState();\n this.assertStatusTransition(prior?.status);\n this.assertApprovedImmutable(prior);\n const result = (await super.save()) as this;\n loadedEntryStatus.set(this, this.status);\n if (this.status === 'approved' || this.status === 'corrected') {\n approvedSnapshot.set(this, serializeFrozenFields(this));\n }\n return result;\n }\n\n protected assertStatusTransition(\n prior: ServiceTimeEntryStatus | undefined,\n ): void {\n if (prior === undefined) return;\n if (prior === this.status) return;\n const allowed = SERVICE_TIME_ENTRY_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `ServiceTimeEntry ${this.id}: illegal status transition ` +\n `'${prior}' → '${this.status}'.`,\n );\n }\n }\n\n /**\n * Once approved, the work-defining fields are immutable — compare against\n * the authoritative persisted row (not just the WeakMap), so an upsert via\n * `create({ id, _skipLoad: true })` cannot bypass the freeze.\n */\n protected assertApprovedImmutable(\n prior:\n | { status: ServiceTimeEntryStatus; frozen: string | null }\n | undefined,\n ): void {\n if (!prior) return;\n if (prior.status !== 'approved' && prior.status !== 'corrected') return;\n const persisted = prior.frozen ?? approvedSnapshot.get(this) ?? null;\n if (persisted === null) return;\n const current = serializeFrozenFields(this);\n if (current !== persisted) {\n throw new Error(\n `ServiceTimeEntry ${this.id}: approved entries are immutable — ` +\n 'create an explicit correction instead of editing the approved record.',\n );\n }\n }\n\n /** Authoritative prior state read from the DB row (S5 #1390 idiom). */\n protected async resolvePriorState(): Promise<\n { status: ServiceTimeEntryStatus; frozen: string | null } | undefined\n > {\n if (this.id) {\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row && row.status != null) {\n const status = row.status as ServiceTimeEntryStatus;\n let frozen: string | null = null;\n if (status === 'approved' || status === 'corrected') {\n const view: Record<string, unknown> = {};\n for (const key of FROZEN_FIELDS) {\n const column = key.replace(\n /[A-Z]/g,\n (c) => `_${c.toLowerCase()}`,\n );\n view[key] = row[column];\n }\n frozen = serializeFrozenView(view);\n }\n return { status, frozen };\n }\n } catch {\n // DB not ready / table absent — fall through to in-memory record.\n }\n }\n const status = loadedEntryStatus.get(this);\n if (status === undefined) return undefined;\n return { status, frozen: approvedSnapshot.get(this) ?? null };\n }\n}\n\nexport class ServiceTimeEntryCollection extends SmrtCollection<ServiceTimeEntry> {\n static readonly _itemClass = ServiceTimeEntry;\n\n async forCase(caseId: string): Promise<ServiceTimeEntry[]> {\n return this.list({ where: { caseId }, orderBy: 'created_at ASC' });\n }\n\n async forSpecialist(specialistId: string): Promise<ServiceTimeEntry[]> {\n return this.list({ where: { specialistId }, orderBy: 'created_at ASC' });\n }\n\n async pendingApproval(): Promise<ServiceTimeEntry[]> {\n return this.list({\n where: { status: 'submitted' },\n orderBy: 'submitted_at ASC',\n });\n }\n}\n\nexport default ServiceTimeEntry;\n","/**\n * SupportAiRun — the auditable record of one Automated Support Response phase\n * (FR-28a): what the AI attempted (acknowledge / classify / answer /\n * troubleshoot / resolve), with confidence, classification output, knowledge\n * references, attempted tool calls, and outcome. This is the \"prior automated\n * work\" a Human Handoff transfers losslessly (FR-28b).\n *\n * Append-only: generated surface is `list`/`get`; the workflow writes each\n * run once and never rewrites history.\n */\n\nimport {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n parseJsonField,\n type SupportAiRunOutcome,\n type SupportAiRunPhase,\n} from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_ai_runs',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class SupportAiRun extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportCase', { required: true })\n caseId: string = '';\n\n /** Inbound interaction that triggered this run, when applicable. */\n @foreignKey('SupportInteraction')\n interactionId: string | null = null;\n\n @field({ type: 'text' })\n phase: SupportAiRunPhase = 'acknowledge';\n\n @field({ type: 'text' })\n outcome: SupportAiRunOutcome = 'completed';\n\n /**\n * Model-reported confidence in [0, 1] for answer/classify/resolve phases;\n * null when the phase has no confidence semantics (e.g. acknowledge).\n */\n @field({ type: 'decimal', nullable: true })\n confidence: number | null = null;\n\n /** Classification output JSON: severity, category, sentiment, sensitive. */\n @field({ type: 'text' })\n classification: string = '{}';\n\n /** Outbound interaction holding the posted/drafted response, if any. */\n @foreignKey('SupportInteraction')\n responseInteractionId: string | null = null;\n\n /** Knowledge sources consulted (JSON array of `{kind, ref, label?}`). */\n @field({ type: 'text' })\n knowledgeRefs: string = '[]';\n\n /** Tool calls attempted during troubleshooting (JSON array). */\n @field({ type: 'text' })\n toolCalls: string = '[]';\n\n /** Error detail when `outcome === 'failed'`. */\n @field({ type: 'text' })\n error: string = '';\n\n /** Correlation id tying this run to AI-call telemetry / feedback signals. */\n @field({ type: 'text' })\n correlationId: string = '';\n\n /** Model identifier used, when known. */\n @field({ type: 'text' })\n model: string = '';\n\n startedAt: Date = new Date();\n\n completedAt: Date | null = null;\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getClassification(): Record<string, unknown> {\n return parseJsonField(this.classification, {});\n }\n\n setClassification(value: Record<string, unknown>): void {\n this.classification = JSON.stringify(value ?? {});\n }\n\n getKnowledgeRefs(): Array<Record<string, unknown>> {\n return parseJsonField(this.knowledgeRefs, []);\n }\n\n setKnowledgeRefs(refs: Array<Record<string, unknown>>): void {\n this.knowledgeRefs = JSON.stringify(refs ?? []);\n }\n\n getToolCalls(): Array<Record<string, unknown>> {\n return parseJsonField(this.toolCalls, []);\n }\n\n setToolCalls(calls: Array<Record<string, unknown>>): void {\n this.toolCalls = JSON.stringify(calls ?? []);\n }\n}\n\nexport class SupportAiRunCollection extends SmrtCollection<SupportAiRun> {\n static readonly _itemClass = SupportAiRun;\n\n /** All AI runs for a case, oldest first. */\n async forCase(caseId: string): Promise<SupportAiRun[]> {\n return this.list({ where: { caseId }, orderBy: 'started_at ASC' });\n }\n}\n\nexport default SupportAiRun;\n","/**\n * SupportCase — the canonical, tenant-scoped record of a client support\n * request (FR-28/FR-29): one Case per request across every channel, carrying\n * priority, severity, plan snapshot, lifecycle state, assignment, resolution,\n * and reopen history.\n *\n * Internal model — writes go through {@link SupportCaseService} (closed-facade\n * idiom, mirroring `smrt-chat`'s `ChatService`). The generated CRUD surface is\n * read-only (`list`/`get`); the save-time transition guard below additionally\n * rejects illegal status flips done via raw field assignment.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n OPEN_SUPPORT_CASE_STATUSES,\n parseJsonField,\n SUPPORT_CASE_STATUS_TRANSITIONS,\n type SupportCaseStatus,\n} from '../types.js';\n\n/**\n * Status each persisted row was loaded with, for the save-time transition\n * guard. WeakMap (not a column) so it never round-trips through the DB.\n */\nconst loadedCaseStatus = new WeakMap<SupportCase, SupportCaseStatus>();\n\n/** Generate a short human-facing case number (unique enough per tenant). */\nexport function generateCaseNumber(now: Date = new Date()): string {\n const stamp = now.getTime().toString(36).toUpperCase();\n const rand = Math.random().toString(36).slice(2, 6).toUpperCase();\n return `SUP-${stamp}-${rand}`;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_cases',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class SupportCase extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Human-facing case reference (e.g. `SUP-…`), generated at open time. */\n @field({ type: 'text' })\n caseNumber: string = '';\n\n /** Short client-facing summary of the request. */\n @field({ type: 'text' })\n subject: string = '';\n\n /** Initial request text (the first inbound interaction's body). */\n @field({ type: 'text' })\n description: string = '';\n\n @field({ type: 'text' })\n status: SupportCaseStatus = 'new';\n\n /** Queue ordering priority: `low` | `normal` | `high` | `urgent`. */\n @field({ type: 'text' })\n priority: string = 'normal';\n\n /**\n * Severity key resolved against the plan's severity definitions (e.g.\n * `sev1`…`sev4`). Empty until triage/classification assigns one.\n */\n @field({ type: 'text' })\n severity: string = '';\n\n /** Classification category (AI- or human-assigned). Free vocabulary. */\n @field({ type: 'text' })\n category: string = '';\n\n /** Whether the subject matter was flagged sensitive (handoff trigger). */\n @field({ type: 'boolean' })\n sensitive: boolean = false;\n\n /** Origin transport kind (`chat` | `email` | …). */\n @field({ type: 'text' })\n channelKind: string = '';\n\n /**\n * The Client this Case belongs to (FR-29) — a `Person` or `Organization`\n * Profile. Nullable so unmatched inbound email can park a Case for triage;\n * intake marks those with `metadata.unresolvedClient`.\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n clientProfileId: string | null = null;\n\n /** The person who opened the request, when distinct from the Client. */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n openedByProfileId: string | null = null;\n\n /**\n * App-defined Project reference (a client initiative, not a repo board).\n * Deliberately a plain string — the consuming app owns the Project concept.\n */\n @field({ type: 'text', nullable: true })\n projectId: string | null = null;\n\n /** Channel binding that created this case, when intake-created. */\n @foreignKey('SupportChannelBinding')\n bindingId: string | null = null;\n\n /**\n * Deterministic conversation key used by create-or-join intake dedup\n * (e.g. `chat:<roomId>[:<threadId>]` or `email:<rfc-message-id>`).\n */\n @field({ type: 'text' })\n threadKey: string = '';\n\n /** Managed Support Plan governing this case, when one applies. */\n @foreignKey('SupportPlan')\n planId: string | null = null;\n\n /**\n * JSON snapshot of the plan terms captured when the plan was applied, so\n * later plan edits never rewrite the terms a case was handled under.\n * Read through {@link getPlanSnapshot}.\n */\n @field({ type: 'text' })\n planSnapshot: string = '{}';\n\n @foreignKey('SupportSpecialist')\n assignedSpecialistId: string | null = null;\n\n assignedAt: Date | null = null;\n\n /** Client/plan preferred specialist considered first by routing (FR-30). */\n @foreignKey('SupportSpecialist')\n preferredSpecialistId: string | null = null;\n\n /** Current escalation level (0 = never escalated). */\n escalationLevel: number = 0;\n\n escalatedAt: Date | null = null;\n\n /** First outbound acknowledgement of any kind (starts as `null`). */\n acknowledgedAt: Date | null = null;\n\n /** First substantive outbound response (specialist or agent). */\n firstRespondedAt: Date | null = null;\n\n /** Set when the client explicitly requested a human (FR-28b). */\n humanRequestedAt: Date | null = null;\n\n resolvedAt: Date | null = null;\n\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n resolvedByProfileId: string | null = null;\n\n /** How the case was resolved: `automated` | `human` | `delivery`. */\n @field({ type: 'text' })\n resolutionKind: string = '';\n\n @field({ type: 'text' })\n resolutionSummary: string = '';\n\n closedAt: Date | null = null;\n\n /** Times this case was reopened; prior resolutions live in case events. */\n reopenCount: number = 0;\n\n lastReopenedAt: Date | null = null;\n\n /** Per-case escape hatch: disable the AI workflow for this case only. */\n @field({ type: 'boolean' })\n aiEnabled: boolean = true;\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n /** Whether the case is in an open (not resolved/closed) state. */\n isOpen(): boolean {\n return OPEN_SUPPORT_CASE_STATUSES.includes(this.status);\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n\n updateMetadata(patch: Record<string, unknown>): void {\n this.setMetadata({ ...this.getMetadata(), ...patch });\n }\n\n getPlanSnapshot(): Record<string, unknown> {\n return parseJsonField(this.planSnapshot, {});\n }\n\n setPlanSnapshot(value: Record<string, unknown>): void {\n this.planSnapshot = JSON.stringify(value ?? {});\n }\n\n /**\n * Capture the status the row was loaded with so the save-time guard can\n * reject illegal flips made via raw field assignment.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedCaseStatus.set(this, this.status);\n }\n return this;\n }\n\n /** Validate the status transition before persisting, then save. */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n const result = (await super.save()) as this;\n loadedCaseStatus.set(this, this.status);\n return result;\n }\n\n protected assertStatusTransition(prior: SupportCaseStatus | undefined): void {\n if (prior === undefined) return; // new row — any starting status\n if (prior === this.status) return; // no-op re-save\n const allowed = SUPPORT_CASE_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `SupportCase ${this.caseNumber || this.id}: illegal status transition ` +\n `'${prior}' → '${this.status}'.`,\n );\n }\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status. The WeakMap only covers rows\n * hydrated through {@link initialize}; a `create({ id, _skipLoad: true })`\n * upsert would otherwise look brand-new and skip the guard, so when the\n * instance carries an id, read the persisted row directly (S5 #1390 idiom).\n */\n protected async resolvePriorStatus(): Promise<SupportCaseStatus | undefined> {\n if (this.id) {\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row && row.status != null) {\n return row.status as SupportCaseStatus;\n }\n } catch {\n // DB not ready / table absent — fall through to the in-memory record.\n }\n }\n return loadedCaseStatus.get(this);\n }\n}\n\nexport class SupportCaseCollection extends SmrtCollection<SupportCase> {\n static readonly _itemClass = SupportCase;\n\n /**\n * Find the open case an inbound interaction should join, by conversation\n * key (create-or-join dedup, FR-28). Returns the most recently updated open\n * case for the key, or `null` when a new case should be created.\n */\n async findOpenByThreadKey(\n threadKey: string,\n options: { bindingId?: string | null } = {},\n ): Promise<SupportCase | null> {\n const where: Record<string, unknown> = {\n threadKey,\n 'status in': OPEN_SUPPORT_CASE_STATUSES,\n };\n if (options.bindingId !== undefined && options.bindingId !== null) {\n where.bindingId = options.bindingId;\n }\n const matches = await this.list({\n where,\n orderBy: 'updated_at DESC',\n limit: 1,\n });\n return matches[0] ?? null;\n }\n\n /** Queue listing with common filters, newest first. */\n async findQueue(\n options: {\n status?: SupportCaseStatus | SupportCaseStatus[];\n assignedSpecialistId?: string | null;\n clientProfileId?: string;\n projectId?: string;\n openOnly?: boolean;\n limit?: number;\n offset?: number;\n } = {},\n ): Promise<SupportCase[]> {\n const where: Record<string, unknown> = {};\n if (options.status !== undefined) {\n if (Array.isArray(options.status)) {\n where['status in'] = options.status;\n } else {\n where.status = options.status;\n }\n } else if (options.openOnly) {\n where['status in'] = OPEN_SUPPORT_CASE_STATUSES;\n }\n if (options.assignedSpecialistId !== undefined) {\n where.assignedSpecialistId = options.assignedSpecialistId;\n }\n if (options.clientProfileId !== undefined) {\n where.clientProfileId = options.clientProfileId;\n }\n if (options.projectId !== undefined) {\n where.projectId = options.projectId;\n }\n return this.list({\n where,\n orderBy: 'updated_at DESC',\n limit: options.limit ?? 50,\n offset: options.offset ?? 0,\n });\n }\n}\n\nexport default SupportCase;\n","/**\n * SupportCaseEvent — append-only audit trail for a Support Case: creations,\n * transitions, assignments, AI runs, handoffs, escalations, target state\n * changes, work links, and reopens. Together with interactions this is the\n * \"complete Case context\" a Human Handoff transfers (FR-28b) and the reopen\n * history FR-29b preserves.\n *\n * Events are immutable: the generated surface is `list`/`get` only and the\n * service never updates an event after writing it.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n parseJsonField,\n type SupportActorKind,\n type SupportCaseEventType,\n} from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_case_events',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class SupportCaseEvent extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportCase', { required: true })\n caseId: string = '';\n\n @field({ type: 'text' })\n eventType: SupportCaseEventType = 'note';\n\n @field({ type: 'text' })\n actorKind: SupportActorKind = 'system';\n\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n actorProfileId: string | null = null;\n\n occurredAt: Date = new Date();\n\n /** One-line human-readable summary for the case timeline. */\n @field({ type: 'text' })\n summary: string = '';\n\n /** Structured event payload (transition from/to, rationale, evidence, …). */\n @field({ type: 'text' })\n payload: string = '{}';\n\n getPayload(): Record<string, unknown> {\n return parseJsonField(this.payload, {});\n }\n\n setPayload(value: Record<string, unknown>): void {\n this.payload = JSON.stringify(value ?? {});\n }\n}\n\nexport class SupportCaseEventCollection extends SmrtCollection<SupportCaseEvent> {\n static readonly _itemClass = SupportCaseEvent;\n\n /** All events for a case, oldest first. */\n async forCase(\n caseId: string,\n options: { eventType?: SupportCaseEventType } = {},\n ): Promise<SupportCaseEvent[]> {\n const where: Record<string, unknown> = { caseId };\n if (options.eventType !== undefined) {\n where.eventType = options.eventType;\n }\n return this.list({ where, orderBy: 'occurred_at ASC' });\n }\n}\n\nexport default SupportCaseEvent;\n","/**\n * SupportChannelBinding — marks a transport container (a chat room, an email\n * account) as a Managed Support intake channel. Inbound activity on a bound\n * container creates-or-joins Support Cases; unbound containers are never\n * touched, so ordinary chat rooms and mailboxes stay out of the support\n * system.\n *\n * Bindings are configuration (operator-managed), so unlike the case-side\n * models they expose full generated CRUD.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n parseJsonField,\n parseStringArrayField,\n type SupportBindingKind,\n type SupportChannelKind,\n} from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_channel_bindings',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['target_type', 'target_id'],\n})\nexport class SupportChannelBinding extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Operator-facing binding name (inherited `name` is the display label). */\n name: string = '';\n\n @field({ type: 'text' })\n bindingKind: SupportBindingKind = 'chat_room';\n\n /** Channel kind interactions from this binding are recorded as. */\n @field({ type: 'text' })\n channelKind: SupportChannelKind = 'chat';\n\n /**\n * Qualified class of the bound container, e.g.\n * `@happyvertical/smrt-chat:ChatRoom` or\n * `@happyvertical/smrt-messages:EmailAccount`.\n */\n @field({ type: 'text' })\n targetType: string = '';\n\n /** Id of the bound container row. */\n @field({ type: 'text' })\n targetId: string = '';\n\n /**\n * Default Client for cases created from this binding (e.g. a dedicated\n * client support room). When null, intake resolves the client from the\n * interaction author (chat) or sender lookup (email).\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n clientProfileId: string | null = null;\n\n /** Default app-defined Project reference for cases from this binding. */\n @field({ type: 'text', nullable: true })\n projectId: string | null = null;\n\n /** Managed Support Plan applied to cases created from this binding. */\n @foreignKey('SupportPlan')\n planId: string | null = null;\n\n @field({ type: 'boolean' })\n enabled: boolean = true;\n\n /**\n * Addresses belonging to the provider side of an email binding — inbound\n * sync skips messages sent FROM these (they are our own outbound mail).\n * JSON array of e-mail addresses, matched case-insensitively.\n */\n @field({ type: 'text' })\n selfAddresses: string = '[]';\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getSelfAddresses(): string[] {\n return parseStringArrayField(this.selfAddresses).map((address) =>\n address.toLowerCase(),\n );\n }\n\n setSelfAddresses(addresses: string[]): void {\n this.selfAddresses = JSON.stringify(addresses ?? []);\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n}\n\nexport class SupportChannelBindingCollection extends SmrtCollection<SupportChannelBinding> {\n static readonly _itemClass = SupportChannelBinding;\n\n /** Find the enabled binding for a transport container, if any. */\n async findForTarget(\n targetType: string,\n targetId: string,\n ): Promise<SupportChannelBinding | null> {\n const matches = await this.list({\n where: { targetType, targetId, enabled: true },\n limit: 1,\n });\n return matches[0] ?? null;\n }\n}\n\nexport default SupportChannelBinding;\n","/**\n * SupportCompensationPlan — the provider-facing **Support Compensation Plan**\n * (FR-32/FR-35): the effective-dated terms under which a Support Specialist\n * earns for delivered support time. Deliberately separate from\n * {@link SupportPlan} (client pricing) so margin stays measurable and client\n * terms never leak provider terms.\n *\n * Resolution: the specialist-specific plan effective at the work instant\n * wins; otherwise the tenant default (`specialistId` null) effective at that\n * instant applies.\n */\n\nimport {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { parseJsonField } from '../types.js';\n\n// Provider earning terms are written only through the\n// `support.manage-plans`-gated `SupportPlanAdminService`; the generated\n// surface stays read-only (see SupportPlan for the rationale).\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_compensation_plans',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['tenant_id', 'specialist_id', 'effective_from'],\n})\nexport class SupportCompensationPlan extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Specialist this plan compensates; null = tenant default plan. */\n @foreignKey('SupportSpecialist')\n specialistId: string | null = null;\n\n /** Display name (inherited `name`; uniqueness comes from conflictColumns). */\n name: string = '';\n\n /** Hourly earning rate for delivered support time. */\n hourlyRate: number = 0.0;\n\n @field({ type: 'text' })\n currency: string = 'USD';\n\n /** Effective-dating: null bounds mean open-ended. */\n effectiveFrom: Date | null = null;\n\n effectiveTo: Date | null = null;\n\n /** `active` | `archived`. */\n @field({ type: 'text' })\n status: string = 'active';\n\n /** Additional agreement terms (JSON object; free-form, snapshotted). */\n @field({ type: 'text' })\n terms: string = '{}';\n\n getTerms(): Record<string, unknown> {\n return parseJsonField(this.terms, {});\n }\n\n setTerms(value: Record<string, unknown>): void {\n this.terms = JSON.stringify(value ?? {});\n }\n\n /** Whether the plan is effective at the given instant. */\n isEffectiveAt(at: Date = new Date()): boolean {\n if (this.status !== 'active') return false;\n if (this.effectiveFrom && at < this.effectiveFrom) return false;\n if (this.effectiveTo && at >= this.effectiveTo) return false;\n return true;\n }\n}\n\nexport class SupportCompensationPlanCollection extends SmrtCollection<SupportCompensationPlan> {\n static readonly _itemClass = SupportCompensationPlan;\n\n /**\n * Resolve the plan compensating a specialist at a work instant:\n * specialist-specific first, then the tenant default. Ties (overlapping\n * effective ranges) prefer an exact tenant match over a global\n * (NULL-tenant) plan, then the latest `effectiveFrom`.\n *\n * Pass `options.tenantId` (the work's tenant) whenever resolution may run\n * without an ambient tenant context (system/job paths list across\n * tenants): candidates are then limited to that tenant's plans plus\n * global NULL-tenant plans, so another tenant's default can never price\n * this tenant's work.\n */\n async resolveForSpecialist(\n specialistId: string,\n at: Date = new Date(),\n options: { tenantId?: string | null } = {},\n ): Promise<SupportCompensationPlan | null> {\n const candidates = await this.list({\n where: { status: 'active' },\n });\n const tenantId = options.tenantId;\n const effective = candidates.filter(\n (plan) =>\n plan.isEffectiveAt(at) &&\n (tenantId === undefined ||\n plan.tenantId === null ||\n plan.tenantId === tenantId),\n );\n const pick = (plans: SupportCompensationPlan[]) =>\n plans.sort((a, b) => {\n if (tenantId !== undefined) {\n const aExact = a.tenantId === tenantId ? 1 : 0;\n const bExact = b.tenantId === tenantId ? 1 : 0;\n if (aExact !== bExact) return bExact - aExact;\n }\n return (\n (b.effectiveFrom?.getTime() ?? 0) - (a.effectiveFrom?.getTime() ?? 0)\n );\n })[0] ?? null;\n const specific = pick(\n effective.filter((plan) => plan.specialistId === specialistId),\n );\n if (specific) return specific;\n return pick(effective.filter((plan) => plan.specialistId === null));\n }\n}\n\nexport default SupportCompensationPlan;\n","/**\n * SupportInteraction — a channel-neutral transport record attached to a\n * Support Case (FR-28/FR-29): one row per message/chat/email/voice exchange,\n * preserving chronology and transport metadata. The channel row (a\n * `ChatMessage`, an `Email`, …) stays in its own package; this row links it\n * via a qualified `sourceType`/`sourceId` pair and a globally unique\n * `sourceKey` so re-ingesting the same transport record is idempotent.\n *\n * Internal model — writes go through {@link SupportCaseService}. Generated\n * surface is read-only.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n parseJsonField,\n type SupportActorKind,\n type SupportChannelKind,\n type SupportInteractionDirection,\n} from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_interactions',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['source_key'],\n})\nexport class SupportInteraction extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportCase', { required: true })\n caseId: string = '';\n\n @field({ type: 'text' })\n direction: SupportInteractionDirection = 'inbound';\n\n @field({ type: 'text' })\n channelKind: SupportChannelKind = 'chat';\n\n @field({ type: 'text' })\n actorKind: SupportActorKind = 'client';\n\n /** Author identity when known (client contact, specialist, bot profile). */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n authorProfileId: string | null = null;\n\n /** When the exchange happened on its channel (not when it was ingested). */\n occurredAt: Date = new Date();\n\n /** Denormalized text body for the case timeline (source stays canonical). */\n @field({ type: 'text' })\n body: string = '';\n\n /**\n * Qualified class of the source transport record, e.g.\n * `@happyvertical/smrt-chat:ChatMessage` or\n * `@happyvertical/smrt-messages:Email`. Null for manual notes.\n */\n @field({ type: 'text', nullable: true })\n sourceType: string | null = null;\n\n /** Id of the source transport record. Null for manual notes. */\n @field({ type: 'text', nullable: true })\n sourceId: string | null = null;\n\n /**\n * Globally unique idempotency key for intake dedup (`conflictColumns`):\n * `chat:<messageId>`, `email:<emailId>`, or `manual:<uuid>` for\n * service-authored notes.\n */\n @field({ type: 'text' })\n sourceKey: string = '';\n\n /**\n * RFC 822 Message-ID for email interactions — a first-class column so\n * reply threading (`In-Reply-To` joins) is a keyed lookup instead of a\n * bounded metadata scan. Empty for non-email interactions.\n */\n @field({ type: 'text' })\n rfcMessageId: string = '';\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n}\n\nexport class SupportInteractionCollection extends SmrtCollection<SupportInteraction> {\n static readonly _itemClass = SupportInteraction;\n\n /** All interactions for a case in channel chronology (oldest first). */\n async forCase(caseId: string): Promise<SupportInteraction[]> {\n return this.list({\n where: { caseId },\n orderBy: 'occurred_at ASC',\n });\n }\n\n /** Look up an interaction by its idempotency key. */\n async bySourceKey(sourceKey: string): Promise<SupportInteraction | null> {\n const matches = await this.list({ where: { sourceKey }, limit: 1 });\n return matches[0] ?? null;\n }\n\n /** Keyed lookup of an email interaction by its RFC 822 Message-ID. */\n async byRfcMessageId(\n rfcMessageId: string,\n ): Promise<SupportInteraction | null> {\n if (!rfcMessageId) {\n return null;\n }\n const matches = await this.list({ where: { rfcMessageId }, limit: 1 });\n return matches[0] ?? null;\n }\n}\n\nexport default SupportInteraction;\n","/**\n * SupportPlan — the client-facing **Managed Support Plan** (FR-31): coverage\n * hours and channels, severity definitions, acknowledgement / response /\n * update / resolution Service Targets, pause rules, escalation policy, an\n * availability fee, included support time, and metered overage pricing.\n *\n * Pure hourly support is a plan with zero availability fee and zero included\n * minutes. A Service Target here is an operational objective — it becomes a\n * contractual Service Level Agreement only in the governing agreement, which\n * is out of this package's scope.\n *\n * Client pricing lives here; Support Specialist earnings live in the separate\n * {@link SupportCompensationPlan} — the two are never merged (FR-32/FR-35).\n *\n * `subscriptionPlanKey` optionally binds the plan to a\n * `@happyvertical/smrt-subscriptions` plan by its natural key for entitlement\n * checks — a string reference, deliberately not a package dependency.\n */\n\nimport {\n field,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type CoverageWindow,\n DEFAULT_SEVERITY_DEFINITIONS,\n type EscalationStep,\n parseJsonField,\n parseStringArrayField,\n type ServiceTargetMinutes,\n type SeverityDefinition,\n type TimeApprovalPolicy,\n} from '../types.js';\n\n/** Default target minutes applied when a plan omits a severity's targets. */\nexport const DEFAULT_TARGET_MINUTES: ServiceTargetMinutes = {\n acknowledgementMinutes: 60,\n responseMinutes: 240,\n updateMinutes: null,\n resolutionMinutes: null,\n};\n\n// Commercial terms are written only through the `support.manage-plans`-gated\n// `SupportPlanAdminService` — the generated surface stays read-only so an\n// authenticated caller cannot rewrite pricing/service terms via CRUD routes\n// (`create` would even upsert over `conflictColumns`).\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_plans',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['tenant_id', 'plan_key'],\n})\nexport class SupportPlan extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Natural key, unique per tenant (`conflictColumns`). */\n @field({ type: 'text' })\n planKey: string = '';\n\n /** Display name (inherited `name` field). */\n name: string = '';\n\n @field({ type: 'text' })\n description: string = '';\n\n /** `draft` | `active` | `archived`. */\n @field({ type: 'text' })\n status: string = 'active';\n\n /** Channels this plan covers (JSON array of channel kinds). */\n @field({ type: 'text' })\n channels: string = '[\"chat\",\"email\"]';\n\n /** IANA timezone the coverage calendar is expressed in. */\n @field({ type: 'text' })\n timezone: string = 'UTC';\n\n /**\n * Coverage calendar: JSON array of {@link CoverageWindow}. Empty array\n * means 24×7 coverage (clocks always run).\n */\n @field({ type: 'text' })\n coverage: string = '[]';\n\n /** Holiday dates excluded from coverage (JSON array of `YYYY-MM-DD`). */\n @field({ type: 'text' })\n holidays: string = '[]';\n\n /**\n * Severity vocabulary for this plan: JSON map of severity key →\n * {@link SeverityDefinition}. Empty object falls back to\n * `DEFAULT_SEVERITY_DEFINITIONS`.\n */\n @field({ type: 'text' })\n severityDefinitions: string = '{}';\n\n /**\n * Service Targets per severity: JSON map of severity key →\n * {@link ServiceTargetMinutes}. Missing severities fall back to\n * `DEFAULT_TARGET_MINUTES`; explicit `null` minutes disable that clock.\n */\n @field({ type: 'text' })\n targets: string = '{}';\n\n /**\n * Case statuses that pause Service Target clocks (FR-29b: waiting periods\n * pause clocks only when the plan says so). JSON array of statuses.\n */\n @field({ type: 'text' })\n pauseStatuses: string = '[\"waiting_on_client\"]';\n\n /** Escalation policy: JSON array of {@link EscalationStep}, level order. */\n @field({ type: 'text' })\n escalationPolicy: string = '[]';\n\n /** Recurring availability/retainer fee (0.0 = pure hourly support). */\n availabilityFeeAmount: number = 0.0;\n\n @field({ type: 'text' })\n currency: string = 'USD';\n\n /** Included support minutes per period (0 = none included). */\n includedMinutes: number = 0;\n\n /** Metered hourly rate for time beyond the included minutes. */\n overageHourlyRate: number = 0.0;\n\n /** Premium hourly rate for on-call work (0.0 = use overage rate). */\n onCallHourlyRate: number = 0.0;\n\n /**\n * Time-entry approval policy for work under this plan (FR-36): JSON\n * {@link TimeApprovalPolicy}. Empty object = operator approval.\n */\n @field({ type: 'text' })\n timeApprovalPolicy: string = '{}';\n\n /** Optional `@happyvertical/smrt-subscriptions` plan natural key binding. */\n @field({ type: 'text' })\n subscriptionPlanKey: string = '';\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getChannels(): string[] {\n return parseStringArrayField(this.channels);\n }\n\n setChannels(channels: string[]): void {\n this.channels = JSON.stringify(channels ?? []);\n }\n\n getCoverage(): CoverageWindow[] {\n const parsed = parseJsonField<CoverageWindow[]>(this.coverage, []);\n return Array.isArray(parsed) ? parsed : [];\n }\n\n setCoverage(windows: CoverageWindow[]): void {\n this.coverage = JSON.stringify(windows ?? []);\n }\n\n getHolidays(): string[] {\n return parseStringArrayField(this.holidays);\n }\n\n setHolidays(dates: string[]): void {\n this.holidays = JSON.stringify(dates ?? []);\n }\n\n getSeverityDefinitions(): Record<string, SeverityDefinition> {\n const parsed = parseJsonField<Record<string, SeverityDefinition>>(\n this.severityDefinitions,\n {},\n );\n return Object.keys(parsed).length > 0\n ? parsed\n : { ...DEFAULT_SEVERITY_DEFINITIONS };\n }\n\n setSeverityDefinitions(defs: Record<string, SeverityDefinition>): void {\n this.severityDefinitions = JSON.stringify(defs ?? {});\n }\n\n getTargets(): Record<string, Partial<ServiceTargetMinutes>> {\n return parseJsonField(this.targets, {});\n }\n\n setTargets(targets: Record<string, Partial<ServiceTargetMinutes>>): void {\n this.targets = JSON.stringify(targets ?? {});\n }\n\n /** Effective target minutes for a severity, with defaults applied. */\n targetsForSeverity(severity: string): ServiceTargetMinutes {\n const configured = this.getTargets()[severity] ?? {};\n return { ...DEFAULT_TARGET_MINUTES, ...configured };\n }\n\n getPauseStatuses(): string[] {\n return parseStringArrayField(this.pauseStatuses);\n }\n\n setPauseStatuses(statuses: string[]): void {\n this.pauseStatuses = JSON.stringify(statuses ?? []);\n }\n\n getEscalationPolicy(): EscalationStep[] {\n const parsed = parseJsonField<EscalationStep[]>(this.escalationPolicy, []);\n return Array.isArray(parsed) ? parsed : [];\n }\n\n setEscalationPolicy(steps: EscalationStep[]): void {\n this.escalationPolicy = JSON.stringify(steps ?? []);\n }\n\n getTimeApprovalPolicy(): TimeApprovalPolicy {\n const parsed = parseJsonField<Partial<TimeApprovalPolicy>>(\n this.timeApprovalPolicy,\n {},\n );\n return { mode: 'operator', ...parsed };\n }\n\n setTimeApprovalPolicy(policy: TimeApprovalPolicy): void {\n this.timeApprovalPolicy = JSON.stringify(policy ?? {});\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n\n /**\n * The plan terms captured onto a case at apply time (`planSnapshot`), so\n * later plan edits never rewrite the terms a case was handled under.\n */\n snapshotTerms(): Record<string, unknown> {\n return {\n planId: this.id,\n planKey: this.planKey,\n name: this.name,\n timezone: this.timezone,\n channels: this.getChannels(),\n coverage: this.getCoverage(),\n holidays: this.getHolidays(),\n severityDefinitions: this.getSeverityDefinitions(),\n targets: this.getTargets(),\n pauseStatuses: this.getPauseStatuses(),\n escalationPolicy: this.getEscalationPolicy(),\n availabilityFeeAmount: this.availabilityFeeAmount,\n currency: this.currency,\n includedMinutes: this.includedMinutes,\n overageHourlyRate: this.overageHourlyRate,\n onCallHourlyRate: this.onCallHourlyRate,\n timeApprovalPolicy: this.getTimeApprovalPolicy(),\n subscriptionPlanKey: this.subscriptionPlanKey,\n capturedAt: new Date().toISOString(),\n };\n }\n}\n\nexport class SupportPlanCollection extends SmrtCollection<SupportPlan> {\n static readonly _itemClass = SupportPlan;\n\n async byPlanKey(planKey: string): Promise<SupportPlan | null> {\n const matches = await this.list({ where: { planKey }, limit: 1 });\n return matches[0] ?? null;\n }\n}\n\nexport default SupportPlan;\n","/**\n * SupportPolicy — governs what the AI support workflow may do without a human\n * (FR-28a/FR-28b): which phases run automatically, whether low-risk\n * resolutions may complete autonomously, the confidence floor, sensitive\n * categories, and the tool whitelist for troubleshooting.\n *\n * Resolution is most-specific-wins per case: `(planId, projectId)` exact →\n * `planId` → `projectId` → tenant default (both null) → the package's\n * conservative built-in defaults (see `DEFAULT_SUPPORT_POLICY`). Policies are\n * operator configuration, so they expose full generated CRUD.\n */\n\nimport {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { parseJsonField, parseStringArrayField } from '../types.js';\n\n/**\n * Conservative defaults applied when no SupportPolicy row matches: the AI\n * acknowledges, classifies, and answers, but never closes a case or executes\n * tools until a policy explicitly enables it. Handoff triggers are always\n * active regardless of policy.\n */\nexport const DEFAULT_SUPPORT_POLICY = {\n autoAcknowledge: true,\n autoClassify: true,\n autoAnswer: true,\n autoTroubleshoot: false,\n autoResolve: false,\n autoResolveMaxSeverity: '',\n confidenceThreshold: 0.7,\n maxAutoAttempts: 1,\n autoSendEmailReplies: false,\n sensitiveCategories: [] as string[],\n allowedTools: [] as string[],\n} as const;\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_policies',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['tenant_id', 'name'],\n})\nexport class SupportPolicy extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Policy name, unique per tenant (`conflictColumns`). */\n name: string = '';\n\n /** Scope: applies to cases under this plan (null = any plan). */\n @foreignKey('SupportPlan')\n planId: string | null = null;\n\n /** Scope: applies to cases for this app-defined project (null = any). */\n @field({ type: 'text', nullable: true })\n projectId: string | null = null;\n\n @field({ type: 'boolean' })\n enabled: boolean = true;\n\n /** Immediately acknowledge new inbound cases. */\n @field({ type: 'boolean' })\n autoAcknowledge: boolean = true;\n\n /** Classify severity/category/sensitivity on intake. */\n @field({ type: 'boolean' })\n autoClassify: boolean = true;\n\n /** Post an automated answer using scoped knowledge. */\n @field({ type: 'boolean' })\n autoAnswer: boolean = true;\n\n /** Run tool-executing troubleshooting (gated OFF by default). */\n @field({ type: 'boolean' })\n autoTroubleshoot: boolean = false;\n\n /** Allow the AI to resolve (close out) low-risk cases (OFF by default). */\n @field({ type: 'boolean' })\n autoResolve: boolean = false;\n\n /**\n * Highest severity the AI may auto-resolve (e.g. `sev4` = questions only).\n * Empty means \"no severity is eligible\" even when `autoResolve` is true.\n */\n @field({ type: 'text' })\n autoResolveMaxSeverity: string = '';\n\n /**\n * Confidence floor for automated answers/resolutions; below it the workflow\n * triggers a `low_confidence` Human Handoff.\n */\n confidenceThreshold: number = 0.7;\n\n /** Max automated resolution attempts before a `failed_resolution` handoff. */\n maxAutoAttempts: number = 1;\n\n /** Whether automated email replies may be sent (vs drafted) — OFF default. */\n @field({ type: 'boolean' })\n autoSendEmailReplies: boolean = false;\n\n /**\n * Categories considered sensitive: classification into one of these always\n * triggers a Human Handoff (FR-28b). JSON array of category keys.\n */\n @field({ type: 'text' })\n sensitiveCategories: string = '[]';\n\n /** Tool ids the troubleshooting phase may execute. JSON array, fail-closed. */\n @field({ type: 'text' })\n allowedTools: string = '[]';\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getSensitiveCategories(): string[] {\n return parseStringArrayField(this.sensitiveCategories);\n }\n\n setSensitiveCategories(categories: string[]): void {\n this.sensitiveCategories = JSON.stringify(categories ?? []);\n }\n\n getAllowedTools(): string[] {\n return parseStringArrayField(this.allowedTools);\n }\n\n setAllowedTools(tools: string[]): void {\n this.allowedTools = JSON.stringify(tools ?? []);\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n\n /** Specificity rank for resolution: exact scope wins over partial. */\n scopeRank(): number {\n let rank = 0;\n if (this.planId) rank += 2;\n if (this.projectId) rank += 1;\n return rank;\n }\n}\n\nexport class SupportPolicyCollection extends SmrtCollection<SupportPolicy> {\n static readonly _itemClass = SupportPolicy;\n\n /**\n * Resolve the effective policy for a case scope, most-specific-wins.\n * Returns `null` when no enabled policy matches (callers then apply\n * {@link DEFAULT_SUPPORT_POLICY}).\n */\n async resolveForScope(options: {\n planId?: string | null;\n projectId?: string | null;\n }): Promise<SupportPolicy | null> {\n const policies = await this.list({ where: { enabled: true } });\n const planId = options.planId ?? null;\n const projectId = options.projectId ?? null;\n const applicable = policies.filter((policy) => {\n if (policy.planId && policy.planId !== planId) return false;\n if (policy.projectId && policy.projectId !== projectId) return false;\n return true;\n });\n applicable.sort((a, b) => b.scopeRank() - a.scopeRank());\n return applicable[0] ?? null;\n }\n}\n\nexport default SupportPolicy;\n","/**\n * Service Target clocks and escalations (FR-30/FR-31).\n *\n * - **SupportServiceTarget** — one acknowledgement/response/update/resolution\n * clock on a case, derived from the applicable plan's targets, coverage\n * calendar, and pause rules. `escalationJobId` holds the one-shot\n * `_smrt_jobs` row scheduled at `dueAt` so satisfying the target can cancel\n * the pending escalation.\n * - **SupportEscalation** — the auditable record of one escalation firing\n * (target breach, target risk, manual, or AI-triggered).\n *\n * Both are written by the target engine / case service only; generated\n * surfaces are read-only.\n */\n\nimport {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { backgroundEligible } from '@happyvertical/smrt-jobs';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n parseJsonField,\n parseStringArrayField,\n type ServiceTargetStatus,\n type ServiceTargetType,\n type SupportEscalationReason,\n} from '../types.js';\n\n/** Outcome of one {@link SupportServiceTarget.checkAndEscalate} delivery. */\nexport interface CheckAndEscalateResult {\n outcome: 'breached' | 'escalated' | 'rescheduled' | 'noop' | 'missing';\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_service_targets',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['case_id', 'target_type', 'cycle'],\n})\nexport class SupportServiceTarget extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportCase', { required: true })\n caseId: string = '';\n\n @field({ type: 'text' })\n targetType: ServiceTargetType = 'acknowledgement';\n\n /**\n * Recurrence cycle for repeating clocks (`update` restarts after each\n * update). One-shot clocks stay at cycle 0.\n */\n cycle: number = 0;\n\n /** Severity key the minutes were derived from (audit). */\n @field({ type: 'text' })\n severity: string = '';\n\n /** Configured clock length in covered minutes. */\n baseMinutes: number = 0;\n\n /** When the clock started running. */\n startedAt: Date = new Date();\n\n /** Current deadline, in wall-clock time (recomputed on pause/resume). */\n dueAt: Date = new Date();\n\n @field({ type: 'text' })\n status: ServiceTargetStatus = 'pending';\n\n /** Set while the clock is paused (plan pause rules). */\n pausedAt: Date | null = null;\n\n /** Accumulated paused time, excluded from the clock. */\n pausedTotalSeconds: number = 0;\n\n satisfiedAt: Date | null = null;\n\n breachedAt: Date | null = null;\n\n cancelledAt: Date | null = null;\n\n /** One-shot `_smrt_jobs` id scheduled for `dueAt` (cancel on satisfy). */\n @field({ type: 'text' })\n escalationJobId: string = '';\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n\n isRunning(): boolean {\n return this.status === 'pending';\n }\n\n /**\n * One-shot escalation job entry point, delivered at-least-once by the\n * jobs runner at `dueAt` (and again for delayed policy steps). Reloads its\n * own persisted state first, so stale deliveries are harmless:\n *\n * - not `pending` and no due pending-escalation marker → no-op (already\n * satisfied/cancelled/paused, or an already-breached clock without a due\n * follow-up step);\n * - `pending` with `dueAt` in the future → no-op (the clock was\n * rescheduled after this job was enqueued);\n * - `pending` and overdue → mark breached and escalate through the plan's\n * escalation policy;\n * - `breached` with a due pending-escalation marker → continue with the\n * next policy step.\n *\n * The engine is imported dynamically to avoid a static model → service\n * cycle. `args.at` (ISO string or `Date`) pins \"now\" for deterministic\n * tests; the runner passes no args, so production uses wall-clock time.\n */\n @backgroundEligible()\n async checkAndEscalate(\n args?: Record<string, unknown>,\n _context?: unknown,\n ): Promise<CheckAndEscalateResult> {\n const rawAt = args?.at;\n const at =\n rawAt instanceof Date\n ? rawAt\n : typeof rawAt === 'string' && !Number.isNaN(new Date(rawAt).getTime())\n ? new Date(rawAt)\n : new Date();\n\n if (!this.id) {\n return { outcome: 'missing' };\n }\n // Load fresh, authoritative state — the enqueued row may be stale.\n const targets = await SupportServiceTargetCollection.create({\n db: this.db,\n });\n const fresh = await targets.get({ id: this.id });\n if (!fresh) {\n return { outcome: 'missing' };\n }\n\n if (fresh.status === 'pending') {\n if (fresh.dueAt.getTime() > at.getTime()) {\n return { outcome: 'rescheduled' };\n }\n fresh.breachedAt = at;\n fresh.status = 'breached';\n await fresh.save();\n const { ServiceTargetEngine } = await import(\n '../services/service-target-engine.js'\n );\n const engine = await ServiceTargetEngine.create({ db: this.db });\n await engine.escalateForBreach(fresh, { at });\n return { outcome: 'breached' };\n }\n\n if (fresh.status === 'breached') {\n const { ServiceTargetEngine } = await import(\n '../services/service-target-engine.js'\n );\n const engine = await ServiceTargetEngine.create({ db: this.db });\n const continued = await engine.continueEscalation(fresh, at);\n return { outcome: continued ? 'escalated' : 'noop' };\n }\n\n return { outcome: 'noop' };\n }\n}\n\nexport class SupportServiceTargetCollection extends SmrtCollection<SupportServiceTarget> {\n static readonly _itemClass = SupportServiceTarget;\n\n async forCase(caseId: string): Promise<SupportServiceTarget[]> {\n return this.list({ where: { caseId }, orderBy: 'created_at ASC' });\n }\n\n /** Active clock of a type on a case (highest cycle wins). */\n async activeTarget(\n caseId: string,\n targetType: ServiceTargetType,\n ): Promise<SupportServiceTarget | null> {\n const matches = await this.list({\n where: { caseId, targetType, 'status in': ['pending', 'paused'] },\n orderBy: 'cycle DESC',\n limit: 1,\n });\n return matches[0] ?? null;\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_escalations',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class SupportEscalation extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportCase', { required: true })\n caseId: string = '';\n\n /** Escalation level applied (1-based, follows the plan's policy steps). */\n level: number = 1;\n\n @field({ type: 'text' })\n reason: SupportEscalationReason = 'target_breach';\n\n /** Breached/at-risk target that triggered this, when applicable. */\n @foreignKey('SupportServiceTarget')\n targetId: string | null = null;\n\n @field({ type: 'text' })\n targetType: string = '';\n\n /** Escalation action taken: `notify` | `reassign`. */\n @field({ type: 'text' })\n action: string = 'notify';\n\n @foreignKey('SupportSpecialist')\n fromSpecialistId: string | null = null;\n\n @foreignKey('SupportSpecialist')\n toSpecialistId: string | null = null;\n\n /** Profiles notified (JSON array of profile ids). */\n @field({ type: 'text' })\n notifiedProfileIds: string = '[]';\n\n occurredAt: Date = new Date();\n\n @field({ type: 'text' })\n note: string = '';\n\n getNotifiedProfileIds(): string[] {\n return parseStringArrayField(this.notifiedProfileIds);\n }\n\n setNotifiedProfileIds(ids: string[]): void {\n this.notifiedProfileIds = JSON.stringify(ids ?? []);\n }\n}\n\nexport class SupportEscalationCollection extends SmrtCollection<SupportEscalation> {\n static readonly _itemClass = SupportEscalation;\n\n async forCase(caseId: string): Promise<SupportEscalation[]> {\n return this.list({ where: { caseId }, orderBy: 'occurred_at ASC' });\n }\n}\n\nexport default SupportServiceTarget;\n","/**\n * Derived commercial snapshots for approved Service Time Entries\n * (FR-32/FR-35, issue #1930). Two deliberately separate rows per entry:\n *\n * - **SupportCharge** — what the Client owes, derived from the Managed\n * Support Plan (included-time consumption, then metered overage).\n * - **SupportCompensation** — what the delivering Participant earns, derived\n * from the Support Compensation Plan.\n *\n * Margin = charge − compensation, computed by readers; the two records are\n * never merged so client pricing can't leak provider terms. Rows snapshot the\n * rate terms at derivation time (`rateSnapshot`) — later plan edits never\n * rewrite them; corrections void/supersede and re-derive from the correcting\n * entry. Settlement (invoicing, ledger posting) is out of scope here and\n * composes at the app/accounting boundary (#1925).\n *\n * Append-only surfaces: `list`/`get` generated routes only; writes go through\n * `TimeEntryApprovalService`.\n */\n\nimport {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { parseJsonField, type SupportSettlementStatus } from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_charges',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['time_entry_id'],\n})\nexport class SupportCharge extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('ServiceTimeEntry', { required: true })\n timeEntryId: string = '';\n\n @foreignKey('SupportCase')\n caseId: string | null = null;\n\n /** Plan the pricing derived from (null = no plan; zero-amount charge). */\n @foreignKey('SupportPlan')\n planId: string | null = null;\n\n /** Client charge amount for the billable time. */\n amount: number = 0.0;\n\n @field({ type: 'text' })\n currency: string = 'USD';\n\n /** Seconds priced at the metered rate (after included-time consumption). */\n billableSeconds: number = 0;\n\n /** Seconds absorbed by the plan's included support time. */\n includedSecondsApplied: number = 0;\n\n /**\n * JSON snapshot of the pricing terms used: hourly rate, rate source\n * (`overage` | `on_call` | `none`), plan key, included-time state,\n * derivation timestamp. Never rewritten by later plan edits.\n */\n @field({ type: 'text' })\n rateSnapshot: string = '{}';\n\n @field({ type: 'text' })\n status: SupportSettlementStatus = 'pending';\n\n finalizedAt: Date | null = null;\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getRateSnapshot(): Record<string, unknown> {\n return parseJsonField(this.rateSnapshot, {});\n }\n\n setRateSnapshot(value: Record<string, unknown>): void {\n this.rateSnapshot = JSON.stringify(value ?? {});\n }\n}\n\nexport class SupportChargeCollection extends SmrtCollection<SupportCharge> {\n static readonly _itemClass = SupportCharge;\n\n async forTimeEntry(timeEntryId: string): Promise<SupportCharge | null> {\n const matches = await this.list({ where: { timeEntryId }, limit: 1 });\n return matches[0] ?? null;\n }\n\n async forCase(caseId: string): Promise<SupportCharge[]> {\n return this.list({ where: { caseId }, orderBy: 'created_at ASC' });\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_compensations',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['time_entry_id'],\n})\nexport class SupportCompensation extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('ServiceTimeEntry', { required: true })\n timeEntryId: string = '';\n\n @foreignKey('SupportSpecialist')\n specialistId: string | null = null;\n\n /** Compensation plan the earning derived from (null = no plan resolved). */\n @foreignKey('SupportCompensationPlan')\n compensationPlanId: string | null = null;\n\n /** Provider earning amount for the payable time. */\n amount: number = 0.0;\n\n @field({ type: 'text' })\n currency: string = 'USD';\n\n /** Seconds compensated. */\n payableSeconds: number = 0;\n\n /** JSON snapshot of the earning terms used at derivation time. */\n @field({ type: 'text' })\n rateSnapshot: string = '{}';\n\n @field({ type: 'text' })\n status: SupportSettlementStatus = 'pending';\n\n finalizedAt: Date | null = null;\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getRateSnapshot(): Record<string, unknown> {\n return parseJsonField(this.rateSnapshot, {});\n }\n\n setRateSnapshot(value: Record<string, unknown>): void {\n this.rateSnapshot = JSON.stringify(value ?? {});\n }\n}\n\nexport class SupportCompensationCollection extends SmrtCollection<SupportCompensation> {\n static readonly _itemClass = SupportCompensation;\n\n async forTimeEntry(timeEntryId: string): Promise<SupportCompensation | null> {\n const matches = await this.list({ where: { timeEntryId }, limit: 1 });\n return matches[0] ?? null;\n }\n\n async forSpecialist(specialistId: string): Promise<SupportCompensation[]> {\n return this.list({ where: { specialistId }, orderBy: 'created_at ASC' });\n }\n}\n\nexport default SupportCharge;\n","/**\n * Support Specialist models (FR-30): the specialist role record layered on a\n * Profile (the `commerce Customer` role-record idiom), per-project\n * qualifications, and availability/on-call windows.\n *\n * - **SupportSpecialist** — a Participant qualified to handle first-tier\n * support; workload cap, languages, and timezone drive routing.\n * - **SupportQualification** — recognition that a specialist is trained and\n * approved for a specific app-defined project (effective-dated).\n * - **SupportAvailability** — weekly working windows, on-call spans, and\n * time-off exceptions used by routing and coverage checks.\n *\n * These are operator configuration surfaces, so they expose generated CRUD.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n parseJsonField,\n parseStringArrayField,\n type SupportAvailabilityKind,\n type SupportQualificationLevel,\n type SupportSpecialistStatus,\n} from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_specialists',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['tenant_id', 'profile_id'],\n})\nexport class SupportSpecialist extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** The Participant (Person profile) this specialist role belongs to. */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { required: true })\n profileId: string = '';\n\n /** Display name for queues/routing rationale (denormalized from Profile). */\n @field({ type: 'text' })\n displayName: string = '';\n\n @field({ type: 'text' })\n status: SupportSpecialistStatus = 'active';\n\n /** Languages the specialist supports (JSON array of BCP-47-ish codes). */\n @field({ type: 'text' })\n languages: string = '[\"en\"]';\n\n /** IANA timezone the weekly availability windows are expressed in. */\n @field({ type: 'text' })\n timezone: string = 'UTC';\n\n /** Routing workload cap: open cases beyond this exclude the specialist. */\n maxConcurrentCases: number = 10;\n\n /** Tie-break priority for on-call ranking (higher = preferred). */\n onCallPriority: number = 0;\n\n @field({ type: 'text' })\n notes: string = '';\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getLanguages(): string[] {\n return parseStringArrayField(this.languages);\n }\n\n setLanguages(languages: string[]): void {\n this.languages = JSON.stringify(languages ?? []);\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n\n isActive(): boolean {\n return this.status === 'active';\n }\n}\n\nexport class SupportSpecialistCollection extends SmrtCollection<SupportSpecialist> {\n static readonly _itemClass = SupportSpecialist;\n\n async findActive(): Promise<SupportSpecialist[]> {\n return this.list({ where: { status: 'active' } });\n }\n\n async byProfile(profileId: string): Promise<SupportSpecialist | null> {\n const matches = await this.list({ where: { profileId }, limit: 1 });\n return matches[0] ?? null;\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_qualifications',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['specialist_id', 'project_id'],\n})\nexport class SupportQualification extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportSpecialist', { required: true })\n specialistId: string = '';\n\n /** App-defined Project this qualification covers. */\n @field({ type: 'text' })\n projectId: string = '';\n\n @field({ type: 'text' })\n level: SupportQualificationLevel = 'qualified';\n\n /** Effective-dating: null bounds mean open-ended. */\n effectiveFrom: Date | null = null;\n\n effectiveTo: Date | null = null;\n\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { nullable: true })\n grantedByProfileId: string | null = null;\n\n @field({ type: 'text' })\n notes: string = '';\n\n /** Whether the qualification is effective at the given instant. */\n isEffectiveAt(at: Date = new Date()): boolean {\n if (this.effectiveFrom && at < this.effectiveFrom) return false;\n if (this.effectiveTo && at >= this.effectiveTo) return false;\n return true;\n }\n}\n\nexport class SupportQualificationCollection extends SmrtCollection<SupportQualification> {\n static readonly _itemClass = SupportQualification;\n\n async forSpecialist(specialistId: string): Promise<SupportQualification[]> {\n return this.list({ where: { specialistId } });\n }\n\n async forProject(projectId: string): Promise<SupportQualification[]> {\n return this.list({ where: { projectId } });\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_availability_windows',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class SupportAvailability extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportSpecialist', { required: true })\n specialistId: string = '';\n\n @field({ type: 'text' })\n kind: SupportAvailabilityKind = 'weekly';\n\n /** Weekly windows: 0 = Sunday … 6 = Saturday. Null for span kinds. */\n @field({ type: 'integer', nullable: true })\n weekday: number | null = null;\n\n /** Weekly windows: minutes from midnight, inclusive start. */\n startMinute: number = 0;\n\n /** Weekly windows: minutes from midnight, exclusive end. */\n endMinute: number = 0;\n\n /** Span kinds (`on_call`, `time_off`): absolute start. */\n startsAt: Date | null = null;\n\n /** Span kinds (`on_call`, `time_off`): absolute end. */\n endsAt: Date | null = null;\n\n @field({ type: 'text' })\n notes: string = '';\n}\n\nexport class SupportAvailabilityCollection extends SmrtCollection<SupportAvailability> {\n static readonly _itemClass = SupportAvailability;\n\n async forSpecialist(specialistId: string): Promise<SupportAvailability[]> {\n return this.list({ where: { specialistId } });\n }\n}\n\nexport default SupportSpecialist;\n","/**\n * SupportWorkLink — links a Support Case to separately tracked internal work\n * (FR-29/FR-29a): a **Support Work Item** on the operational board, or a\n * **Development Work Item** created through a Delivery Handoff.\n *\n * Boundary: Support owns the Case and this link; Delivery Operations owns the\n * linked work's execution (agents, previews, merges, deployments). The\n * `status` field here is a read-only echo the delivery side reports back —\n * this package never drives the linked item's lifecycle. Targets are\n * addressed by qualified type + id (e.g. `@happyvertical/smrt-projects:Issue`)\n * so no runtime dependency on the owning package is needed.\n */\n\nimport {\n field,\n foreignKey,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { parseJsonField, type SupportWorkLinkKind } from '../types.js';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'support_work_links',\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n conflictColumns: ['case_id', 'target_type', 'target_id'],\n})\nexport class SupportWorkLink extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('SupportCase', { required: true })\n caseId: string = '';\n\n @field({ type: 'text' })\n linkKind: SupportWorkLinkKind = 'support_work_item';\n\n /**\n * Qualified class of the linked work item, e.g.\n * `@happyvertical/smrt-projects:Issue`.\n */\n @field({ type: 'text' })\n targetType: string = '';\n\n @field({ type: 'text' })\n targetId: string = '';\n\n /** Display label for the case timeline (e.g. `repo#123: fix login`). */\n @field({ type: 'text' })\n targetLabel: string = '';\n\n @field({ type: 'text' })\n externalUrl: string = '';\n\n /**\n * Status echo reported back from the owning domain (`open`,\n * `in_progress`, `done`, …). Informational only.\n */\n @field({ type: 'text' })\n status: string = 'open';\n\n lastStatusAt: Date | null = null;\n\n @field({ type: 'text' })\n metadata: string = '{}';\n\n getMetadata(): Record<string, unknown> {\n return parseJsonField(this.metadata, {});\n }\n\n setMetadata(value: Record<string, unknown>): void {\n this.metadata = JSON.stringify(value ?? {});\n }\n}\n\nexport class SupportWorkLinkCollection extends SmrtCollection<SupportWorkLink> {\n static readonly _itemClass = SupportWorkLink;\n\n async forCase(caseId: string): Promise<SupportWorkLink[]> {\n return this.list({ where: { caseId }, orderBy: 'created_at ASC' });\n }\n}\n\nexport default SupportWorkLink;\n","/**\n * Coverage-calendar math for Service Target clocks (FR-30/FR-31): pure,\n * deterministic business-time arithmetic over a Managed Support Plan's\n * coverage windows, holidays, and IANA timezone.\n *\n * Semantics:\n * - An empty `windows` array means 24×7 coverage; with no holidays either,\n * covered time is plain wall-clock time (the fast path).\n * - Holidays (`YYYY-MM-DD` in the calendar's timezone) exclude whole days\n * from coverage, including under 24×7 coverage.\n * - All functions take explicit instants — no `Date.now()` inside — so the\n * engine and its tests stay deterministic.\n *\n * Timezone handling uses `Intl.DateTimeFormat#formatToParts` only (no date\n * library): {@link zonedParts} projects a UTC instant onto the calendar\n * timezone's wall clock, and an iterative inverse converts wall-clock day\n * positions back to UTC instants. DST transition days therefore keep exact\n * window boundaries; a window boundary falling inside a nonexistent\n * spring-forward hour lands on the later real instant so clocks never gain\n * time.\n */\n\nimport type { CoverageWindow } from '../types.js';\n\n/** The coverage terms a Service Target clock runs against. */\nexport interface CoverageCalendar {\n /** Weekly coverage windows; empty means 24×7 coverage. */\n windows: CoverageWindow[];\n /** Holiday dates excluded from coverage (`YYYY-MM-DD`). */\n holidays: string[];\n /** IANA timezone the windows and holidays are expressed in. */\n timezone: string;\n}\n\n/** Wall-clock projection of a UTC instant in a target timezone. */\nexport interface ZonedParts {\n /** Calendar year in the target timezone. */\n year: number;\n /** Calendar month in the target timezone (1–12). */\n month: number;\n /** Calendar day of month in the target timezone (1–31). */\n day: number;\n /** 0 = Sunday … 6 = Saturday (matches {@link CoverageWindow} docs). */\n weekday: number;\n /** Minutes from local midnight (0–1439). */\n minuteOfDay: number;\n /** `YYYY-MM-DD` date key in the target timezone (holiday comparisons). */\n dateKey: string;\n}\n\n/**\n * Hard ceiling on how many calendar days {@link addCoveredMinutes} will walk\n * forward looking for coverage before failing loudly (a plan whose calendar\n * never covers anything would otherwise loop forever).\n */\nexport const MAX_COVERAGE_SCAN_DAYS = 400;\n\nconst MINUTE_MS = 60_000;\nconst DAY_MS = 86_400_000;\nconst MINUTES_PER_DAY = 1_440;\n\nconst WEEKDAY_INDEX: Record<string, number> = {\n Sun: 0,\n Mon: 1,\n Tue: 2,\n Wed: 3,\n Thu: 4,\n Fri: 5,\n Sat: 6,\n};\n\n/** One `Intl.DateTimeFormat` per timezone — construction is expensive. */\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\n\nfunction getFormatter(timeZone: string): Intl.DateTimeFormat {\n const cached = formatterCache.get(timeZone);\n if (cached) {\n return cached;\n }\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n hourCycle: 'h23',\n weekday: 'short',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n });\n formatterCache.set(timeZone, formatter);\n return formatter;\n}\n\n/**\n * Project a UTC instant onto a timezone's wall clock via\n * `Intl.DateTimeFormat#formatToParts`. Throws a `RangeError` for an invalid\n * IANA timezone name (surfacing plan misconfiguration immediately).\n */\nexport function zonedParts(date: Date, timeZone: string): ZonedParts {\n const parts = getFormatter(timeZone).formatToParts(date);\n const byType: Record<string, string> = {};\n for (const part of parts) {\n byType[part.type] = part.value;\n }\n const year = Number(byType.year);\n const month = Number(byType.month);\n const day = Number(byType.day);\n const hour = Number(byType.hour) % 24;\n const minute = Number(byType.minute);\n return {\n year,\n month,\n day,\n weekday: WEEKDAY_INDEX[byType.weekday ?? ''] ?? 0,\n minuteOfDay: hour * 60 + minute,\n dateKey: `${byType.year}-${byType.month}-${byType.day}`,\n };\n}\n\n/** Wall-clock milliseconds since the UTC epoch, as if the zone were UTC. */\nfunction wallClockMs(parts: ZonedParts): number {\n return (\n Date.UTC(parts.year, parts.month - 1, parts.day) +\n parts.minuteOfDay * MINUTE_MS\n );\n}\n\n/**\n * Convert a wall-clock position (`year`/`month`/`day` + minutes from local\n * midnight) in `timeZone` to the UTC instant it names. Converges in one or\n * two steps for real wall times; a nonexistent spring-forward time lands on\n * the later real instant (clocks never gain covered time).\n */\nfunction wallTimeToUtcMs(\n year: number,\n month: number,\n day: number,\n minuteOfDay: number,\n timeZone: string,\n): number {\n const targetWallMs = Date.UTC(year, month - 1, day) + minuteOfDay * MINUTE_MS;\n let guessMs = targetWallMs;\n for (let i = 0; i < 4; i++) {\n const wallMs = wallClockMs(zonedParts(new Date(guessMs), timeZone));\n if (wallMs === targetWallMs) {\n return guessMs;\n }\n guessMs += targetWallMs - wallMs;\n }\n // Nonexistent wall time (DST gap): step onto the later real instant.\n const finalWallMs = wallClockMs(zonedParts(new Date(guessMs), timeZone));\n if (finalWallMs < targetWallMs) {\n guessMs += targetWallMs - finalWallMs;\n }\n return guessMs;\n}\n\n/** A covered span of real time within one calendar day, in UTC ms. */\ninterface CoveredSpan {\n startMs: number;\n endMs: number;\n}\n\n/** Clamp, sort, and merge a day's window minutes into disjoint spans. */\nfunction normalizeWindowMinutes(\n windows: Array<Pick<CoverageWindow, 'startMinute' | 'endMinute'>>,\n): Array<{ startMinute: number; endMinute: number }> {\n const clamped = windows\n .map((window) => ({\n startMinute: Math.max(0, Math.min(window.startMinute, MINUTES_PER_DAY)),\n endMinute: Math.max(0, Math.min(window.endMinute, MINUTES_PER_DAY)),\n }))\n .filter((window) => window.endMinute > window.startMinute)\n .sort((a, b) => a.startMinute - b.startMinute);\n\n const merged: Array<{ startMinute: number; endMinute: number }> = [];\n for (const window of clamped) {\n const last = merged[merged.length - 1];\n if (last && window.startMinute <= last.endMinute) {\n last.endMinute = Math.max(last.endMinute, window.endMinute);\n } else {\n merged.push({ ...window });\n }\n }\n return merged;\n}\n\n/**\n * The covered spans of one calendar day (identified by its wall-clock\n * year/month/day in the calendar timezone), as UTC instants.\n */\nfunction coveredSpansForDay(\n calendar: CoverageCalendar,\n parts: ZonedParts,\n): CoveredSpan[] {\n if (calendar.holidays.includes(parts.dateKey)) {\n return [];\n }\n // The civil weekday of a Y-M-D is timezone-independent.\n const weekday = new Date(\n Date.UTC(parts.year, parts.month - 1, parts.day),\n ).getUTCDay();\n const dayWindows =\n calendar.windows.length === 0\n ? [{ startMinute: 0, endMinute: MINUTES_PER_DAY }]\n : calendar.windows.filter((window) => window.weekday === weekday);\n\n return normalizeWindowMinutes(dayWindows).map((window) => ({\n startMs: wallTimeToUtcMs(\n parts.year,\n parts.month,\n parts.day,\n window.startMinute,\n calendar.timezone,\n ),\n endMs: wallTimeToUtcMs(\n parts.year,\n parts.month,\n parts.day,\n window.endMinute,\n calendar.timezone,\n ),\n }));\n}\n\n/** UTC instant of the calendar timezone's next local midnight after `parts`. */\nfunction nextZoneMidnightMs(\n calendar: CoverageCalendar,\n parts: ZonedParts,\n): number {\n const nextDate = new Date(\n Date.UTC(parts.year, parts.month - 1, parts.day) + DAY_MS,\n );\n return wallTimeToUtcMs(\n nextDate.getUTCFullYear(),\n nextDate.getUTCMonth() + 1,\n nextDate.getUTCDate(),\n 0,\n calendar.timezone,\n );\n}\n\nfunction isAlwaysCovered(calendar: CoverageCalendar): boolean {\n return calendar.windows.length === 0 && calendar.holidays.length === 0;\n}\n\n/**\n * Covered minutes between two instants (fractional; `0` when `to <= from`).\n *\n * Walks forward day by day through the calendar timezone, summing the overlap\n * of `[from, to)` with each day's covered spans.\n */\nexport function coveredMinutesBetween(\n calendar: CoverageCalendar,\n from: Date,\n to: Date,\n): number {\n const fromMs = from.getTime();\n const toMs = to.getTime();\n if (toMs <= fromMs) {\n return 0;\n }\n if (isAlwaysCovered(calendar)) {\n return (toMs - fromMs) / MINUTE_MS;\n }\n\n let totalMs = 0;\n let cursorMs = fromMs;\n while (cursorMs < toMs) {\n const parts = zonedParts(new Date(cursorMs), calendar.timezone);\n for (const span of coveredSpansForDay(calendar, parts)) {\n const start = Math.max(span.startMs, cursorMs);\n const end = Math.min(span.endMs, toMs);\n if (end > start) {\n totalMs += end - start;\n }\n }\n const nextMidnightMs = nextZoneMidnightMs(calendar, parts);\n if (nextMidnightMs <= cursorMs) {\n throw new Error(\n `coveredMinutesBetween: calendar for timezone '${calendar.timezone}' ` +\n 'failed to advance past ' +\n new Date(cursorMs).toISOString(),\n );\n }\n cursorMs = nextMidnightMs;\n }\n return totalMs / MINUTE_MS;\n}\n\n/**\n * The instant at which `minutes` of covered time have elapsed from `from` —\n * the Service Target due-time computation. Walks forward day by day through\n * coverage windows, skipping holidays; throws a descriptive error after\n * {@link MAX_COVERAGE_SCAN_DAYS} days without accumulating enough coverage\n * (an effectively-empty calendar).\n */\nexport function addCoveredMinutes(\n calendar: CoverageCalendar,\n from: Date,\n minutes: number,\n): Date {\n if (minutes <= 0) {\n return new Date(from.getTime());\n }\n if (isAlwaysCovered(calendar)) {\n return new Date(from.getTime() + minutes * MINUTE_MS);\n }\n\n let remainingMs = minutes * MINUTE_MS;\n let cursorMs = from.getTime();\n for (let dayScan = 0; dayScan <= MAX_COVERAGE_SCAN_DAYS; dayScan++) {\n const parts = zonedParts(new Date(cursorMs), calendar.timezone);\n for (const span of coveredSpansForDay(calendar, parts)) {\n const start = Math.max(span.startMs, cursorMs);\n if (span.endMs <= start) {\n continue;\n }\n const spanMs = span.endMs - start;\n if (spanMs >= remainingMs) {\n return new Date(start + remainingMs);\n }\n remainingMs -= spanMs;\n }\n const nextMidnightMs = nextZoneMidnightMs(calendar, parts);\n if (nextMidnightMs <= cursorMs) {\n throw new Error(\n `addCoveredMinutes: calendar for timezone '${calendar.timezone}' ` +\n 'failed to advance past ' +\n new Date(cursorMs).toISOString(),\n );\n }\n cursorMs = nextMidnightMs;\n }\n throw new Error(\n `addCoveredMinutes: no coverage found within ${MAX_COVERAGE_SCAN_DAYS} ` +\n `days after ${from.toISOString()} for ${minutes} covered minute(s) — ` +\n 'the coverage calendar appears to never cover any time ' +\n `(windows: ${JSON.stringify(calendar.windows)}, ` +\n `holidays: ${calendar.holidays.length}, timezone: '${calendar.timezone}').`,\n );\n}\n","/**\n * SupportCaseService — the closed write facade for Support Cases (the\n * `smrt-chat` `ChatService` idiom): case creation, interaction recording,\n * lifecycle transitions, assignment, resolution, reopening, human requests,\n * and work links all flow through here so the audit trail\n * ({@link SupportCaseEvent}) and the lifecycle guard stay coherent. The\n * generated CRUD surface on the case-side models is read-only.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n generateCaseNumber,\n type SupportCase,\n SupportCaseCollection,\n} from '../models/support-case.js';\nimport {\n type SupportCaseEvent,\n SupportCaseEventCollection,\n} from '../models/support-case-event.js';\nimport {\n type SupportInteraction,\n SupportInteractionCollection,\n} from '../models/support-interaction.js';\nimport {\n type SupportPlan,\n SupportPlanCollection,\n} from '../models/support-plan.js';\nimport {\n type SupportWorkLink,\n SupportWorkLinkCollection,\n} from '../models/support-work-link.js';\nimport type {\n SupportActorKind,\n SupportCaseEventType,\n SupportCaseStatus,\n SupportChannelKind,\n SupportInteractionDirection,\n SupportWorkLinkKind,\n} from '../types.js';\n\nexport interface OpenCaseInput {\n tenantId?: string | null;\n subject: string;\n description?: string;\n priority?: string;\n severity?: string;\n channelKind?: SupportChannelKind | '';\n clientProfileId?: string | null;\n openedByProfileId?: string | null;\n projectId?: string | null;\n bindingId?: string | null;\n threadKey?: string;\n planId?: string | null;\n preferredSpecialistId?: string | null;\n metadata?: Record<string, unknown>;\n}\n\nexport interface RecordInteractionInput {\n direction: SupportInteractionDirection;\n channelKind: SupportChannelKind;\n actorKind: SupportActorKind;\n authorProfileId?: string | null;\n body: string;\n occurredAt?: Date;\n sourceType?: string | null;\n sourceId?: string | null;\n /** Idempotency key; generated (`manual:<uuid>`) when omitted. */\n sourceKey?: string;\n /** RFC 822 Message-ID for email interactions (keyed reply threading). */\n rfcMessageId?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface CaseActor {\n actorKind: SupportActorKind;\n actorProfileId?: string | null;\n}\n\nexport interface CaseTimelineItem {\n kind: 'interaction' | 'event';\n occurredAt: Date;\n interaction?: SupportInteraction;\n event?: SupportCaseEvent;\n}\n\n/**\n * The write facade. Construct with {@link SupportCaseService.create}.\n */\nexport class SupportCaseService {\n readonly cases: SupportCaseCollection;\n readonly interactions: SupportInteractionCollection;\n readonly events: SupportCaseEventCollection;\n readonly workLinks: SupportWorkLinkCollection;\n readonly plans: SupportPlanCollection;\n\n protected constructor(collections: {\n cases: SupportCaseCollection;\n interactions: SupportInteractionCollection;\n events: SupportCaseEventCollection;\n workLinks: SupportWorkLinkCollection;\n plans: SupportPlanCollection;\n }) {\n this.cases = collections.cases;\n this.interactions = collections.interactions;\n this.events = collections.events;\n this.workLinks = collections.workLinks;\n this.plans = collections.plans;\n }\n\n static async create(options: SmrtObjectOptions): Promise<SupportCaseService> {\n const [cases, interactions, events, workLinks, plans] = await Promise.all([\n SupportCaseCollection.create(options),\n SupportInteractionCollection.create(options),\n SupportCaseEventCollection.create(options),\n SupportWorkLinkCollection.create(options),\n SupportPlanCollection.create(options),\n ]);\n return new SupportCaseService({\n cases,\n interactions,\n events,\n workLinks,\n plans,\n });\n }\n\n /** Load a case or throw a descriptive error. */\n async getCase(caseId: string): Promise<SupportCase> {\n const found = await this.cases.get({ id: caseId });\n if (!found) {\n throw new Error(`SupportCase not found: ${caseId}`);\n }\n return found;\n }\n\n /** The persisted id of a saved case (all facade entry points save first). */\n private caseIdOf(supportCase: SupportCase): string {\n if (!supportCase.id) {\n throw new Error('SupportCase has no id — was it saved?');\n }\n return supportCase.id;\n }\n\n /**\n * Open a new Support Case. Applies the plan snapshot when a plan is given\n * and writes the `created` audit event.\n */\n async openCase(input: OpenCaseInput): Promise<SupportCase> {\n // Resolve and validate the plan BEFORE the insert — a dangling id or a\n // foreign tenant's plan must never persist onto a case, even partially.\n let plan: SupportPlan | null = null;\n if (input.planId) {\n plan = await this.plans.get({ id: input.planId });\n if (!plan) {\n throw new Error(`SupportPlan not found: ${input.planId}`);\n }\n if (plan.tenantId && plan.tenantId !== (input.tenantId ?? null)) {\n throw new Error(\n `SupportPlan '${plan.planKey || plan.id}' belongs to tenant '${plan.tenantId}' ` +\n `and cannot govern a case in tenant '${input.tenantId ?? null}'.`,\n );\n }\n }\n\n const supportCase = await this.cases.create({\n tenantId: input.tenantId ?? null,\n caseNumber: generateCaseNumber(),\n subject: input.subject,\n description: input.description ?? '',\n status: 'new' as SupportCaseStatus,\n priority: input.priority ?? 'normal',\n severity: input.severity ?? '',\n channelKind: input.channelKind ?? '',\n clientProfileId: input.clientProfileId ?? null,\n openedByProfileId: input.openedByProfileId ?? null,\n projectId: input.projectId ?? null,\n bindingId: input.bindingId ?? null,\n threadKey: input.threadKey ?? '',\n planId: null,\n preferredSpecialistId: input.preferredSpecialistId ?? null,\n metadata: JSON.stringify(input.metadata ?? {}),\n });\n\n if (plan) {\n await this.applyPlan(supportCase, plan);\n }\n\n await this.recordEvent(supportCase, 'created', {\n actorKind: 'system',\n summary: `Case ${supportCase.caseNumber} opened`,\n payload: {\n channelKind: supportCase.channelKind,\n clientProfileId: supportCase.clientProfileId,\n projectId: supportCase.projectId,\n },\n });\n\n return supportCase;\n }\n\n /**\n * Capture the plan terms onto the case (`planSnapshot`) so later plan edits\n * never rewrite what this case was handled under. A tenant-owned plan only\n * applies to that tenant's cases (global NULL-tenant template plans apply\n * anywhere) — a foreign plan would snapshot another tenant's pricing,\n * approval, and escalation terms.\n */\n async applyPlan(supportCase: SupportCase, plan: SupportPlan): Promise<void> {\n if (plan.tenantId && plan.tenantId !== supportCase.tenantId) {\n throw new Error(\n `SupportPlan '${plan.planKey || plan.id}' belongs to tenant '${plan.tenantId}' ` +\n `and cannot govern a case in tenant '${supportCase.tenantId}'.`,\n );\n }\n supportCase.planId = plan.id ?? null;\n supportCase.setPlanSnapshot(plan.snapshotTerms());\n await supportCase.save();\n await this.recordEvent(supportCase, 'plan_applied', {\n actorKind: 'system',\n summary: `Plan '${plan.name || plan.planKey}' applied`,\n payload: { planId: plan.id, planKey: plan.planKey },\n });\n }\n\n /**\n * Record a channel interaction on a case (idempotent on `sourceKey`).\n * Inbound client activity un-parks `waiting_on_client` cases; the first\n * outbound reply stamps `acknowledgedAt`/`firstRespondedAt`.\n */\n async recordInteraction(\n caseRef: SupportCase | string,\n input: RecordInteractionInput,\n ): Promise<SupportInteraction> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n\n const sourceKey = input.sourceKey ?? `manual:${crypto.randomUUID()}`;\n const existing = await this.interactions.bySourceKey(sourceKey);\n if (existing) {\n return existing;\n }\n\n const interaction = await this.interactions.create({\n tenantId: supportCase.tenantId,\n caseId: this.caseIdOf(supportCase),\n direction: input.direction,\n channelKind: input.channelKind,\n actorKind: input.actorKind,\n authorProfileId: input.authorProfileId ?? null,\n occurredAt: input.occurredAt ?? new Date(),\n body: input.body,\n sourceType: input.sourceType ?? null,\n sourceId: input.sourceId ?? null,\n sourceKey,\n rfcMessageId: input.rfcMessageId ?? '',\n metadata: JSON.stringify(input.metadata ?? {}),\n });\n\n let caseDirty = false;\n // Drafts (metadata.draft) were never delivered to the client — they\n // stamp nothing.\n if (input.direction === 'outbound' && input.metadata?.draft !== true) {\n if (!supportCase.acknowledgedAt) {\n supportCase.acknowledgedAt = interaction.occurredAt;\n caseDirty = true;\n }\n // Templated receipts (metadata.acknowledgement) acknowledge but are\n // not the substantive first response.\n if (\n !supportCase.firstRespondedAt &&\n (input.actorKind === 'specialist' || input.actorKind === 'agent') &&\n input.metadata?.acknowledgement !== true\n ) {\n supportCase.firstRespondedAt = interaction.occurredAt;\n caseDirty = true;\n }\n }\n if (\n input.direction === 'inbound' &&\n supportCase.status === 'waiting_on_client'\n ) {\n supportCase.status = 'in_progress';\n caseDirty = true;\n }\n if (caseDirty) {\n await supportCase.save();\n }\n\n await this.recordEvent(supportCase, 'interaction', {\n actorKind: input.actorKind,\n actorProfileId: input.authorProfileId ?? null,\n summary:\n input.direction === 'inbound'\n ? `Inbound ${input.channelKind} interaction`\n : `${input.direction} ${input.channelKind} interaction`,\n payload: { interactionId: interaction.id, direction: input.direction },\n });\n\n return interaction;\n }\n\n /** Transition a case's lifecycle status (guarded by the model). */\n async transition(\n caseRef: SupportCase | string,\n to: SupportCaseStatus,\n actor: CaseActor & { reason?: string },\n ): Promise<SupportCase> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n const from = supportCase.status;\n if (from === to) {\n return supportCase;\n }\n supportCase.status = to;\n await supportCase.save();\n await this.recordEvent(supportCase, 'transition', {\n actorKind: actor.actorKind,\n actorProfileId: actor.actorProfileId ?? null,\n summary: `Status ${from} → ${to}`,\n payload: { from, to, reason: actor.reason ?? null },\n });\n return supportCase;\n }\n\n /** Assign (or reassign) a case to a specialist. */\n async assign(\n caseRef: SupportCase | string,\n input: CaseActor & {\n specialistId: string;\n rationale?: Record<string, unknown>;\n },\n ): Promise<SupportCase> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n const previousSpecialistId = supportCase.assignedSpecialistId;\n supportCase.assignedSpecialistId = input.specialistId;\n supportCase.assignedAt = new Date();\n if (supportCase.status === 'new' || supportCase.status === 'triaged') {\n supportCase.status = 'assigned';\n }\n await supportCase.save();\n await this.recordEvent(supportCase, 'assignment', {\n actorKind: input.actorKind,\n actorProfileId: input.actorProfileId ?? null,\n summary: previousSpecialistId ? 'Case reassigned' : 'Case assigned',\n payload: {\n specialistId: input.specialistId,\n previousSpecialistId,\n rationale: input.rationale ?? null,\n },\n });\n return supportCase;\n }\n\n /** Resolve a case with a resolution summary. */\n async resolve(\n caseRef: SupportCase | string,\n input: CaseActor & {\n summary: string;\n resolutionKind?: 'automated' | 'human' | 'delivery';\n },\n ): Promise<SupportCase> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n const from = supportCase.status;\n supportCase.status = 'resolved';\n supportCase.resolvedAt = new Date();\n supportCase.resolvedByProfileId = input.actorProfileId ?? null;\n supportCase.resolutionKind = input.resolutionKind ?? 'human';\n supportCase.resolutionSummary = input.summary;\n await supportCase.save();\n await this.recordEvent(supportCase, 'transition', {\n actorKind: input.actorKind,\n actorProfileId: input.actorProfileId ?? null,\n summary: `Status ${from} → resolved`,\n payload: {\n from,\n to: 'resolved',\n resolutionKind: supportCase.resolutionKind,\n resolutionSummary: input.summary,\n },\n });\n return supportCase;\n }\n\n /** Close a resolved case. */\n async close(\n caseRef: SupportCase | string,\n actor: CaseActor & { reason?: string },\n ): Promise<SupportCase> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n supportCase.closedAt = new Date();\n return this.transition(supportCase, 'closed', actor);\n }\n\n /**\n * Reopen a resolved/closed case, preserving the prior resolution in the\n * reopen event (FR-29b) and bumping `reopenCount`.\n */\n async reopen(\n caseRef: SupportCase | string,\n actor: CaseActor & { reason?: string; to?: SupportCaseStatus },\n ): Promise<SupportCase> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n // Only terminal cases reopen, and only into an active state — anything\n // else would corrupt the reopen history for a case that never closed.\n if (supportCase.status !== 'resolved' && supportCase.status !== 'closed') {\n throw new Error(\n `SupportCase ${supportCase.caseNumber || supportCase.id}: only ` +\n `resolved or closed cases can be reopened (status is '${supportCase.status}').`,\n );\n }\n const to = actor.to ?? 'triaged';\n if (to === 'resolved' || to === 'closed') {\n throw new Error(\n `SupportCase: a reopen must land in an active status (got '${to}').`,\n );\n }\n const priorResolution = {\n resolvedAt: supportCase.resolvedAt?.toISOString() ?? null,\n resolvedByProfileId: supportCase.resolvedByProfileId,\n resolutionKind: supportCase.resolutionKind,\n resolutionSummary: supportCase.resolutionSummary,\n closedAt: supportCase.closedAt?.toISOString() ?? null,\n reopenIndex: supportCase.reopenCount + 1,\n };\n const from = supportCase.status;\n supportCase.status = to;\n supportCase.reopenCount += 1;\n supportCase.lastReopenedAt = new Date();\n supportCase.resolvedAt = null;\n supportCase.resolvedByProfileId = null;\n supportCase.resolutionKind = '';\n supportCase.resolutionSummary = '';\n supportCase.closedAt = null;\n await supportCase.save();\n await this.recordEvent(supportCase, 'reopened', {\n actorKind: actor.actorKind,\n actorProfileId: actor.actorProfileId ?? null,\n summary: `Case reopened (from ${from})`,\n payload: {\n from,\n to: supportCase.status,\n reason: actor.reason ?? null,\n priorResolution,\n },\n });\n return supportCase;\n }\n\n /**\n * Record that the Client explicitly requested a human (FR-28b). The actual\n * handoff (routing + context transfer) is performed by the handoff service;\n * this stamps the request and its audit event.\n */\n async requestHuman(\n caseRef: SupportCase | string,\n input: { byProfileId?: string | null; note?: string },\n ): Promise<SupportCase> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n if (!supportCase.humanRequestedAt) {\n supportCase.humanRequestedAt = new Date();\n await supportCase.save();\n }\n await this.recordEvent(supportCase, 'human_requested', {\n actorKind: 'client',\n actorProfileId: input.byProfileId ?? null,\n summary: 'Client requested a human',\n payload: { note: input.note ?? null },\n });\n return supportCase;\n }\n\n /**\n * Link separately tracked work to the case (Support Work Item or a\n * Delivery Handoff's Development Work Item). Support keeps the Case as the\n * canonical client record; the linked item's execution belongs to its\n * owning domain (FR-29a).\n */\n async linkWork(\n caseRef: SupportCase | string,\n input: CaseActor & {\n linkKind: SupportWorkLinkKind;\n targetType: string;\n targetId: string;\n targetLabel?: string;\n externalUrl?: string;\n metadata?: Record<string, unknown>;\n },\n ): Promise<SupportWorkLink> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n const link = await this.workLinks.create({\n tenantId: supportCase.tenantId,\n caseId: this.caseIdOf(supportCase),\n linkKind: input.linkKind,\n targetType: input.targetType,\n targetId: input.targetId,\n targetLabel: input.targetLabel ?? '',\n externalUrl: input.externalUrl ?? '',\n metadata: JSON.stringify(input.metadata ?? {}),\n });\n await this.recordEvent(supportCase, 'work_linked', {\n actorKind: input.actorKind,\n actorProfileId: input.actorProfileId ?? null,\n summary:\n input.linkKind === 'development_work_item'\n ? `Delivery Handoff → ${input.targetLabel || input.targetId}`\n : `Support work item linked: ${input.targetLabel || input.targetId}`,\n payload: {\n linkId: link.id,\n linkKind: input.linkKind,\n targetType: input.targetType,\n targetId: input.targetId,\n },\n });\n return link;\n }\n\n /**\n * Record a status echo reported back from the domain owning a linked work\n * item (Delivery Operations reports progress; Support never drives it).\n */\n async recordWorkStatus(\n linkId: string,\n input: { status: string; note?: string },\n ): Promise<SupportWorkLink> {\n const link = await this.workLinks.get({ id: linkId });\n if (!link) {\n throw new Error(`SupportWorkLink not found: ${linkId}`);\n }\n link.status = input.status;\n link.lastStatusAt = new Date();\n await link.save();\n const supportCase = await this.getCase(link.caseId);\n await this.recordEvent(supportCase, 'work_linked', {\n actorKind: 'system',\n summary: `Linked work '${link.targetLabel || link.targetId}' → ${input.status}`,\n payload: {\n linkId: link.id,\n status: input.status,\n note: input.note ?? null,\n },\n });\n return link;\n }\n\n /** Append an audit event to a case. */\n async recordEvent(\n caseRef: SupportCase | string,\n eventType: SupportCaseEventType,\n input: {\n actorKind: SupportActorKind;\n actorProfileId?: string | null;\n summary: string;\n payload?: Record<string, unknown>;\n occurredAt?: Date;\n },\n ): Promise<SupportCaseEvent> {\n const supportCase =\n typeof caseRef === 'string' ? await this.getCase(caseRef) : caseRef;\n return this.events.create({\n tenantId: supportCase.tenantId,\n caseId: this.caseIdOf(supportCase),\n eventType,\n actorKind: input.actorKind,\n actorProfileId: input.actorProfileId ?? null,\n occurredAt: input.occurredAt ?? new Date(),\n summary: input.summary,\n payload: JSON.stringify(input.payload ?? {}),\n });\n }\n\n /**\n * The merged case timeline: interactions and audit events in one\n * chronological stream (the queue/detail UI's data shape, and the context\n * package a Human Handoff transfers).\n */\n async getTimeline(caseId: string): Promise<CaseTimelineItem[]> {\n const [interactions, events] = await Promise.all([\n this.interactions.forCase(caseId),\n this.events.forCase(caseId),\n ]);\n const items: CaseTimelineItem[] = [\n ...interactions.map((interaction) => ({\n kind: 'interaction' as const,\n occurredAt: interaction.occurredAt,\n interaction,\n })),\n ...events.map((event) => ({\n kind: 'event' as const,\n occurredAt: event.occurredAt,\n event,\n })),\n ];\n return items.sort(\n (a, b) => a.occurredAt.getTime() - b.occurredAt.getTime(),\n );\n }\n}\n\nexport default SupportCaseService;\n","/**\n * HumanHandoffService — the lossless Human Handoff (FR-28b, issue #1928).\n * Whatever pulls the trigger (an explicit client request, low answer\n * confidence, a sensitive classification, high severity, failed automation,\n * a policy cap, or a manual escalation), the handoff transfers the FULL case\n * context — current state, the merged interaction/event timeline, and every\n * prior {@link SupportAiRun} — so the Client never repeats themselves and the\n * Support Specialist starts with the complete history.\n *\n * A no-repeat guarantee keeps at most one handoff active per case\n * (`metadata.activeHandoff`): repeat triggers while one is pending only\n * append a deduped audit event. Routing is a seam — the #1929 routing\n * service plugs in via `assignSpecialist`; without it the case is queued for\n * manual pickup (`new` → `triaged`).\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n type SupportAiRun,\n SupportAiRunCollection,\n} from '../models/support-ai-run.js';\nimport type { SupportCase } from '../models/support-case.js';\nimport type { HumanHandoffTrigger } from '../types.js';\nimport {\n type CaseTimelineItem,\n SupportCaseService,\n} from './support-case-service.js';\n\n/** One compact timeline entry inside a {@link HandoffContextPackage}. */\nexport interface HandoffTimelineItem {\n kind: 'interaction' | 'event';\n /** ISO-8601 timestamp (the package is embedded in a JSON event payload). */\n occurredAt: string;\n actorKind: string;\n summary: string;\n /** Interaction body — the Client's own words travel with the handoff. */\n body?: string;\n}\n\n/** One compact AI-run entry inside a {@link HandoffContextPackage}. */\nexport interface HandoffAiRunSummary {\n phase: string;\n outcome: string;\n confidence: number | null;\n classification: Record<string, unknown>;\n error: string | null;\n}\n\n/**\n * The lossless context a Human Handoff transfers (FR-28b): the case's current\n * state plus the merged timeline and every prior automated-work audit row.\n * This is what the assigned Support Specialist receives — the Client never\n * repeats themselves.\n */\nexport interface HandoffContextPackage {\n caseNumber: string;\n subject: string;\n status: string;\n severity: string;\n category: string;\n clientProfileId: string | null;\n projectId: string | null;\n planId: string | null;\n timeline: HandoffTimelineItem[];\n aiRuns: HandoffAiRunSummary[];\n}\n\n/**\n * The routing seam (#1929): pick the Support Specialist for a handed-off\n * case. Return `null` to leave the case in the queue for manual assignment.\n */\nexport type SpecialistRouter = (\n supportCase: SupportCase,\n context: { trigger: HumanHandoffTrigger },\n) => Promise<{\n specialistId: string;\n rationale?: Record<string, unknown>;\n} | null>;\n\n/** Options for {@link HumanHandoffService.create}. */\nexport interface HumanHandoffServiceOptions extends SmrtObjectOptions {\n /** Routing seam; omitted → handed-off cases queue for manual assignment. */\n assignSpecialist?: SpecialistRouter;\n}\n\n/** Input for one {@link HumanHandoffService.handoff} call. */\nexport interface HumanHandoffInput {\n trigger: HumanHandoffTrigger;\n /** Free-form context for the audit event (why the trigger fired). */\n note?: string;\n /** The requesting Client profile, for `client_request` triggers. */\n requestedByProfileId?: string | null;\n}\n\n/** Result of one {@link HumanHandoffService.handoff} call. */\nexport interface HumanHandoffResult {\n supportCase: SupportCase;\n /** True when an earlier handoff was still active (no-repeat guarantee). */\n alreadyActive: boolean;\n}\n\n/**\n * The Human Handoff engine. Construct with {@link HumanHandoffService.create}.\n */\nexport class HumanHandoffService {\n readonly caseService: SupportCaseService;\n readonly aiRuns: SupportAiRunCollection;\n private readonly assignSpecialist?: SpecialistRouter;\n\n protected constructor(deps: {\n caseService: SupportCaseService;\n aiRuns: SupportAiRunCollection;\n assignSpecialist?: SpecialistRouter;\n }) {\n this.caseService = deps.caseService;\n this.aiRuns = deps.aiRuns;\n this.assignSpecialist = deps.assignSpecialist;\n }\n\n static async create(\n options: HumanHandoffServiceOptions,\n ): Promise<HumanHandoffService> {\n const [caseService, aiRuns] = await Promise.all([\n SupportCaseService.create(options),\n SupportAiRunCollection.create(options),\n ]);\n return new HumanHandoffService({\n caseService,\n aiRuns,\n assignSpecialist: options.assignSpecialist,\n });\n }\n\n /**\n * Assemble the lossless context package for a case: current state, the\n * merged interaction/event timeline, and every prior AI run (FR-28b).\n */\n async buildContextPackage(caseId: string): Promise<HandoffContextPackage> {\n const supportCase = await this.caseService.getCase(caseId);\n const [timeline, runs] = await Promise.all([\n this.caseService.getTimeline(caseId),\n this.aiRuns.forCase(caseId),\n ]);\n return {\n caseNumber: supportCase.caseNumber,\n subject: supportCase.subject,\n status: supportCase.status,\n severity: supportCase.severity,\n category: supportCase.category,\n clientProfileId: supportCase.clientProfileId,\n projectId: supportCase.projectId,\n planId: supportCase.planId,\n timeline: timeline.map((item) => compactTimelineItem(item)),\n aiRuns: runs.map((run) => compactAiRun(run)),\n };\n }\n\n /**\n * Hand the case to a human: enforce the no-repeat guarantee, stamp\n * `metadata.activeHandoff`, record the `handoff` audit event carrying the\n * full context package, then route via the `assignSpecialist` seam (or\n * queue the case as `triaged` when unrouted).\n */\n async handoff(\n caseRef: SupportCase | string,\n input: HumanHandoffInput,\n ): Promise<HumanHandoffResult> {\n const supportCase =\n typeof caseRef === 'string'\n ? await this.caseService.getCase(caseRef)\n : caseRef;\n const caseId = this.caseIdOf(supportCase);\n\n // No-repeat guarantee: while one handoff is pending, repeat triggers only\n // append a deduped audit event — assignment never runs twice.\n if (this.isHandoffActive(supportCase)) {\n await this.caseService.recordEvent(supportCase, 'handoff', {\n actorKind: 'system',\n summary: `Human handoff (${input.trigger}) deduped — one already active`,\n payload: { trigger: input.trigger, deduped: true },\n });\n return { supportCase, alreadyActive: true };\n }\n\n supportCase.updateMetadata({\n activeHandoff: { trigger: input.trigger, at: new Date().toISOString() },\n });\n await supportCase.save();\n\n if (input.trigger === 'client_request') {\n await this.caseService.requestHuman(supportCase, {\n byProfileId: input.requestedByProfileId ?? null,\n note: input.note,\n });\n }\n\n const contextPackage = await this.buildContextPackage(caseId);\n await this.caseService.recordEvent(supportCase, 'handoff', {\n actorKind: 'system',\n summary: `Human handoff triggered (${input.trigger})`,\n payload: {\n trigger: input.trigger,\n note: input.note ?? null,\n contextPackage,\n },\n });\n\n let routed: Awaited<ReturnType<SpecialistRouter>> = null;\n if (this.assignSpecialist) {\n routed = await this.assignSpecialist(supportCase, {\n trigger: input.trigger,\n });\n }\n if (routed) {\n await this.caseService.assign(supportCase, {\n actorKind: 'system',\n specialistId: routed.specialistId,\n rationale: routed.rationale,\n });\n } else if (supportCase.status === 'new') {\n await this.caseService.transition(supportCase, 'triaged', {\n actorKind: 'system',\n reason: 'human handoff queued',\n });\n }\n\n return { supportCase, alreadyActive: false };\n }\n\n /**\n * Clear the active-handoff flag so a later trigger can hand off again.\n * Apps call this when the handoff concludes (assignment accepted or the\n * case resolves); a resolve-then-reopen also invalidates the flag\n * automatically inside {@link handoff}.\n */\n async releaseHandoff(caseRef: SupportCase | string): Promise<SupportCase> {\n const supportCase =\n typeof caseRef === 'string'\n ? await this.caseService.getCase(caseRef)\n : caseRef;\n if (supportCase.getMetadata().activeHandoff) {\n supportCase.updateMetadata({ activeHandoff: null });\n await supportCase.save();\n }\n return supportCase;\n }\n\n /**\n * Whether a handoff is still pending on the case. A flag survives only\n * while the case stays open; a resolve-and-reopen since the flag was set\n * makes it stale (the new conversation may hand off afresh).\n */\n private isHandoffActive(supportCase: SupportCase): boolean {\n const active = supportCase.getMetadata().activeHandoff;\n if (!active || typeof active !== 'object') {\n return false;\n }\n if (!supportCase.isOpen()) {\n return false;\n }\n const at = Date.parse(String((active as Record<string, unknown>).at ?? ''));\n if (\n Number.isFinite(at) &&\n supportCase.lastReopenedAt &&\n supportCase.lastReopenedAt.getTime() > at\n ) {\n return false;\n }\n return true;\n }\n\n /** The persisted id of a saved case. */\n private caseIdOf(supportCase: SupportCase): string {\n if (!supportCase.id) {\n throw new Error('SupportCase has no id — was it saved?');\n }\n return supportCase.id;\n }\n}\n\n/** Compact one merged-timeline item for the context package. */\nfunction compactTimelineItem(item: CaseTimelineItem): HandoffTimelineItem {\n if (item.kind === 'interaction' && item.interaction) {\n return {\n kind: 'interaction',\n occurredAt: item.occurredAt.toISOString(),\n actorKind: item.interaction.actorKind,\n summary: `${item.interaction.direction} ${item.interaction.channelKind}`,\n body: item.interaction.body,\n };\n }\n return {\n kind: 'event',\n occurredAt: item.occurredAt.toISOString(),\n actorKind: item.event?.actorKind ?? 'system',\n summary: item.event?.summary ?? '',\n };\n}\n\n/** Compact one AI run for the context package. */\nfunction compactAiRun(run: SupportAiRun): HandoffAiRunSummary {\n return {\n phase: run.phase,\n outcome: run.outcome,\n confidence: run.confidence,\n classification: run.getClassification(),\n error: run.error || null,\n };\n}\n\nexport default HumanHandoffService;\n","/**\n * KeyedMutex — serialize async work per key within one process.\n *\n * Used where a read-then-write decision must not interleave for the same\n * logical resource: intake's create-or-join per conversation key, and\n * included-time consumption per case during time-entry approval. Keys chain\n * promises, so concurrent callers for the same key run strictly in arrival\n * order while different keys stay fully parallel; entries clean up when the\n * last waiter finishes.\n *\n * Scope: in-process only. It covers the deployment shape these paths run in\n * (one app process owns intake interceptors / approval actions); multi-\n * replica deployments serialize at the app layer (sticky routing or a\n * DB-level claim), which composes above these seams.\n */\nexport class KeyedMutex {\n private readonly tails = new Map<string, Promise<unknown>>();\n\n /** Run `fn` exclusively for `key`, after every earlier holder of it. */\n async run<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const previous = this.tails.get(key) ?? Promise.resolve();\n // Chain regardless of the predecessor's outcome — a failed holder must\n // not poison the queue behind it.\n const turn = previous.then(fn, fn);\n // Track completion (success or failure) so cleanup always runs.\n const settled = turn.then(\n () => undefined,\n () => undefined,\n );\n this.tails.set(key, settled);\n try {\n return await turn;\n } finally {\n if (this.tails.get(key) === settled) {\n this.tails.delete(key);\n }\n }\n }\n}\n\nexport default KeyedMutex;\n","/**\n * SupportRoutingService — Project-qualified Support Case routing (FR-30).\n *\n * Ranks Support Specialists for a case with an **explainable** score: every\n * hard eligibility check (active status, effective Project Support\n * Qualification, workload cap, availability) is recorded as a factor on the\n * ranked entry, so the queue UI can show *why* someone was or wasn't chosen.\n * Ineligible specialists stay in the returned ranking (`eligible: false`)\n * for that rationale display.\n *\n * `autoAssign` writes the assignment through {@link SupportCaseService} with\n * the ranking rationale in the audit event; `reassign` is the manual\n * override, gated by the `support.reassign-case` permission split and a\n * cross-tenant guard.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport type { SupportCase } from '../models/support-case.js';\nimport type { SupportAvailability } from '../models/support-specialist.js';\nimport {\n SupportAvailabilityCollection,\n SupportQualificationCollection,\n type SupportSpecialist,\n SupportSpecialistCollection,\n} from '../models/support-specialist.js';\nimport {\n REASSIGN_CASE_PERMISSION,\n type SupportPrincipal,\n} from '../permissions.js';\nimport type { SupportActorKind, SupportQualificationLevel } from '../types.js';\nimport { zonedParts } from './coverage-calendar.js';\nimport { SupportCaseService } from './support-case-service.js';\n\n/**\n * Named scoring weights, so the ranking is auditable against the code.\n * Factors carry the raw signals; the score composes them with these weights.\n */\nexport const ROUTING_WEIGHTS = {\n /** Case/plan preferred specialist (FR-30) — considered first. */\n PREFERRED_SPECIALIST: 100,\n /** Effective `expert` Project Support Qualification. */\n QUALIFICATION_EXPERT: 30,\n /** Effective `qualified` Project Support Qualification. */\n QUALIFICATION_QUALIFIED: 20,\n /** Effective `trainee` Project Support Qualification. */\n QUALIFICATION_TRAINEE: 5,\n /** An active on-call span at the routing instant. */\n ON_CALL: 25,\n /** A weekly availability window covering the routing instant. */\n WEEKLY_AVAILABLE: 15,\n /** Maximum workload-headroom bonus (scaled by free capacity). */\n WORKLOAD_HEADROOM_MAX: 10,\n /** Case language ∈ specialist languages. */\n LANGUAGE_MATCH: 10,\n} as const;\n\nconst QUALIFICATION_WEIGHTS: Record<SupportQualificationLevel, number> = {\n expert: ROUTING_WEIGHTS.QUALIFICATION_EXPERT,\n qualified: ROUTING_WEIGHTS.QUALIFICATION_QUALIFIED,\n trainee: ROUTING_WEIGHTS.QUALIFICATION_TRAINEE,\n};\n\n/** Strongest-first ordering for picking the effective qualification level. */\nconst QUALIFICATION_RANK: Record<SupportQualificationLevel, number> = {\n expert: 3,\n qualified: 2,\n trainee: 1,\n};\n\n/** One specialist's routing evaluation for a case. */\nexport interface RankedSpecialist {\n specialistId: string;\n displayName: string;\n score: number;\n /** Whether every hard eligibility filter passed. */\n eligible: boolean;\n /** The raw signals behind the score/eligibility (rationale display). */\n factors: Record<string, number | string | boolean>;\n}\n\n/** Result of an {@link SupportRoutingService.autoAssign} attempt. */\nexport interface AutoAssignResult {\n assigned: boolean;\n specialistId?: string;\n ranking: RankedSpecialist[];\n}\n\n/** Thrown when a manual reassignment is refused (permission or tenant). */\nexport class ReassignDeniedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ReassignDeniedError';\n }\n}\n\n/** Options for {@link SupportRoutingService.create}. */\nexport interface SupportRoutingServiceOptions extends SmrtObjectOptions {\n /** Share an existing case service (and its collections) with the router. */\n caseService?: SupportCaseService;\n}\n\n/** Compact ranking rows embedded in assignment rationale payloads. */\nfunction compactRanking(\n ranking: RankedSpecialist[],\n limit: number,\n): Array<\n Pick<RankedSpecialist, 'specialistId' | 'displayName' | 'score' | 'eligible'>\n> {\n return ranking.slice(0, limit).map((entry) => ({\n specialistId: entry.specialistId,\n displayName: entry.displayName,\n score: entry.score,\n eligible: entry.eligible,\n }));\n}\n\n/** Whether an absolute availability span covers the instant. */\nfunction spanActiveAt(window: SupportAvailability, at: Date): boolean {\n if (!window.startsAt) {\n return false; // A span without a start never activates.\n }\n if (window.startsAt.getTime() > at.getTime()) {\n return false;\n }\n return window.endsAt === null || at.getTime() < window.endsAt.getTime();\n}\n\n/**\n * The routing engine. Construct with {@link SupportRoutingService.create}.\n */\nexport class SupportRoutingService {\n readonly caseService: SupportCaseService;\n readonly specialists: SupportSpecialistCollection;\n readonly qualifications: SupportQualificationCollection;\n readonly availabilities: SupportAvailabilityCollection;\n\n protected constructor(collections: {\n caseService: SupportCaseService;\n specialists: SupportSpecialistCollection;\n qualifications: SupportQualificationCollection;\n availabilities: SupportAvailabilityCollection;\n }) {\n this.caseService = collections.caseService;\n this.specialists = collections.specialists;\n this.qualifications = collections.qualifications;\n this.availabilities = collections.availabilities;\n }\n\n static async create(\n options: SupportRoutingServiceOptions,\n ): Promise<SupportRoutingService> {\n const { caseService, ...smrtOptions } = options;\n const [service, specialists, qualifications, availabilities] =\n await Promise.all([\n caseService ?? SupportCaseService.create(smrtOptions),\n SupportSpecialistCollection.create(smrtOptions),\n SupportQualificationCollection.create(smrtOptions),\n SupportAvailabilityCollection.create(smrtOptions),\n ]);\n return new SupportRoutingService({\n caseService: service,\n specialists,\n qualifications,\n availabilities,\n });\n }\n\n private async resolveCase(\n caseRef: SupportCase | string,\n ): Promise<SupportCase> {\n return typeof caseRef === 'string'\n ? this.caseService.getCase(caseRef)\n : caseRef;\n }\n\n /**\n * Rank every Support Specialist for a case at an instant.\n *\n * Hard eligibility filters — each recorded as a factor:\n * - `status` must be `active`;\n * - when the case carries a `projectId`, an *effective* Project Support\n * Qualification for that project (`projectQualification` is the level,\n * or `'expired'`/`'none'` on failure);\n * - open assigned cases below `maxConcurrentCases` (`openCases`,\n * `workloadExceeded`);\n * - available at `at`: a weekly window in the specialist's own timezone or\n * an active on-call span, and not inside a time-off span\n * (`weeklyAvailable`, `onCall`, `timeOff`).\n *\n * Ineligible specialists are returned too (`eligible: false`) so the UI\n * can show the failing factor. Sorted by score descending, then display\n * name, then id.\n */\n async rankSpecialists(\n caseRef: SupportCase | string,\n opts: { at?: Date; exclude?: string[] } = {},\n ): Promise<RankedSpecialist[]> {\n const supportCase = await this.resolveCase(caseRef);\n const at = opts.at ?? new Date();\n const excluded = new Set(opts.exclude ?? []);\n\n const specialists = await this.specialists.list({});\n const ranked: RankedSpecialist[] = [];\n\n for (const specialist of specialists) {\n ranked.push(\n await this.evaluateSpecialist(supportCase, specialist, at, excluded),\n );\n }\n\n return ranked.sort(\n (a, b) =>\n b.score - a.score ||\n a.displayName.localeCompare(b.displayName) ||\n a.specialistId.localeCompare(b.specialistId),\n );\n }\n\n private async evaluateSpecialist(\n supportCase: SupportCase,\n specialist: SupportSpecialist,\n at: Date,\n excluded: Set<string>,\n ): Promise<RankedSpecialist> {\n const specialistId = specialist.id ?? '';\n const factors: Record<string, number | string | boolean> = {};\n let eligible = true;\n let score = 0;\n\n factors.status = specialist.status;\n if (!specialist.isActive()) {\n eligible = false;\n }\n\n // Tenant boundary (hard filter): a case never routes to another\n // tenant's specialist, even when ranking runs without an ambient\n // tenant context (system/job paths list across tenants).\n if (\n specialist.tenantId &&\n supportCase.tenantId &&\n specialist.tenantId !== supportCase.tenantId\n ) {\n factors.tenantMismatch = true;\n eligible = false;\n }\n\n if (excluded.has(specialistId)) {\n factors.excluded = true;\n eligible = false;\n }\n\n // Project Support Qualification (hard filter when the case has a project).\n if (supportCase.projectId) {\n const qualifications = (\n await this.qualifications.forSpecialist(specialistId)\n ).filter((q) => q.projectId === supportCase.projectId);\n const effective = qualifications.filter((q) => q.isEffectiveAt(at));\n if (effective.length > 0) {\n const level = effective\n .map((q) => q.level)\n .sort((a, b) => QUALIFICATION_RANK[b] - QUALIFICATION_RANK[a])[0];\n factors.projectQualification = level;\n score += QUALIFICATION_WEIGHTS[level];\n } else {\n factors.projectQualification =\n qualifications.length > 0 ? 'expired' : 'none';\n eligible = false;\n }\n }\n\n // Workload cap.\n const openCases = await this.caseService.cases.findQueue({\n assignedSpecialistId: specialistId,\n openOnly: true,\n limit: 1000,\n });\n factors.openCases = openCases.length;\n factors.maxConcurrentCases = specialist.maxConcurrentCases;\n if (openCases.length >= specialist.maxConcurrentCases) {\n factors.workloadExceeded = true;\n eligible = false;\n } else if (specialist.maxConcurrentCases > 0) {\n score += Math.round(\n (ROUTING_WEIGHTS.WORKLOAD_HEADROOM_MAX *\n (specialist.maxConcurrentCases - openCases.length)) /\n specialist.maxConcurrentCases,\n );\n }\n\n // Availability at `at`, in the specialist's own timezone.\n const windows = await this.availabilities.forSpecialist(specialistId);\n const wall = zonedParts(at, specialist.timezone || 'UTC');\n const weeklyAvailable = windows.some(\n (window) =>\n window.kind === 'weekly' &&\n window.weekday === wall.weekday &&\n wall.minuteOfDay >= window.startMinute &&\n wall.minuteOfDay < window.endMinute,\n );\n const onCall = windows.some(\n (window) => window.kind === 'on_call' && spanActiveAt(window, at),\n );\n const timeOff = windows.some(\n (window) => window.kind === 'time_off' && spanActiveAt(window, at),\n );\n factors.weeklyAvailable = weeklyAvailable;\n factors.onCall = onCall;\n factors.timeOff = timeOff;\n if (timeOff || (!weeklyAvailable && !onCall)) {\n eligible = false;\n }\n if (onCall) {\n score += ROUTING_WEIGHTS.ON_CALL;\n }\n if (weeklyAvailable) {\n score += ROUTING_WEIGHTS.WEEKLY_AVAILABLE;\n }\n\n // Soft preferences.\n if (\n supportCase.preferredSpecialistId &&\n supportCase.preferredSpecialistId === specialistId\n ) {\n factors.preferred = true;\n score += ROUTING_WEIGHTS.PREFERRED_SPECIALIST;\n }\n\n const language = supportCase.getMetadata().language;\n if (\n typeof language === 'string' &&\n language.length > 0 &&\n specialist.getLanguages().includes(language)\n ) {\n factors.languageMatch = true;\n score += ROUTING_WEIGHTS.LANGUAGE_MATCH;\n }\n\n // Final tie-break: on-call priority added directly.\n if (specialist.onCallPriority !== 0) {\n factors.onCallPriority = specialist.onCallPriority;\n score += specialist.onCallPriority;\n }\n\n return {\n specialistId,\n displayName: specialist.displayName || specialistId,\n score,\n eligible,\n factors,\n };\n }\n\n /**\n * Assign the top eligible ranked specialist, recording the rationale\n * (winning factors plus the compact top of the ranking) on the assignment\n * event. When no one is eligible, records an unrouted `note` event with\n * the full ranking and returns `assigned: false`.\n */\n async autoAssign(\n caseRef: SupportCase | string,\n opts: {\n at?: Date;\n exclude?: string[];\n actorKind?: SupportActorKind;\n } = {},\n ): Promise<AutoAssignResult> {\n const supportCase = await this.resolveCase(caseRef);\n const ranking = await this.rankSpecialists(supportCase, {\n at: opts.at,\n exclude: opts.exclude,\n });\n const top = ranking.find((entry) => entry.eligible);\n\n if (!top) {\n await this.caseService.recordEvent(supportCase, 'note', {\n actorKind: opts.actorKind ?? 'system',\n occurredAt: opts.at,\n summary: 'Routing found no eligible specialist',\n payload: {\n unrouted: true,\n ranking: compactRanking(ranking, ranking.length),\n },\n });\n return { assigned: false, ranking };\n }\n\n await this.caseService.assign(supportCase, {\n actorKind: opts.actorKind ?? 'system',\n specialistId: top.specialistId,\n rationale: {\n factors: top.factors,\n ranking: compactRanking(ranking, 3),\n },\n });\n return { assigned: true, specialistId: top.specialistId, ranking };\n }\n\n /**\n * Manual reassignment override (FR-30). Requires the\n * `support.reassign-case` permission split on the acting principal and\n * refuses cross-tenant acts; the assignment event carries\n * `{ manual: true, note }` as its rationale.\n */\n async reassign(\n caseRef: SupportCase | string,\n input: {\n specialistId: string;\n principal: SupportPrincipal;\n note?: string;\n },\n ): Promise<SupportCase> {\n if (!input.principal.can(REASSIGN_CASE_PERMISSION)) {\n throw new ReassignDeniedError(\n `Reassignment denied: principal lacks '${REASSIGN_CASE_PERMISSION}'.`,\n );\n }\n const supportCase = await this.resolveCase(caseRef);\n if (\n input.principal.tenantId &&\n supportCase.tenantId &&\n input.principal.tenantId !== supportCase.tenantId\n ) {\n throw new ReassignDeniedError(\n 'Reassignment denied: principal tenant does not match the case tenant.',\n );\n }\n // The target must be a real specialist in the case's tenant — an\n // authorized caller must not be able to assign (and audit) a foreign\n // tenant's specialist onto this case.\n const specialist = await this.specialists.get({ id: input.specialistId });\n if (!specialist) {\n throw new ReassignDeniedError(\n `Reassignment denied: unknown specialist '${input.specialistId}'.`,\n );\n }\n if (\n specialist.tenantId &&\n supportCase.tenantId &&\n specialist.tenantId !== supportCase.tenantId\n ) {\n throw new ReassignDeniedError(\n 'Reassignment denied: specialist tenant does not match the case tenant.',\n );\n }\n return this.caseService.assign(supportCase, {\n actorKind: 'specialist',\n actorProfileId: input.principal.id ?? null,\n specialistId: input.specialistId,\n rationale: { manual: true, note: input.note ?? null },\n });\n }\n}\n\nexport default SupportRoutingService;\n","/**\n * ServiceTargetEngine — Service Target clocks with timed escalation\n * (FR-30/FR-31).\n *\n * Owns the full clock lifecycle on a case:\n * - **start** — one clock per configured target type from the case's plan\n * terms (the frozen `planSnapshot` wins over the live plan; sensible\n * defaults with no plan), `dueAt` computed in *covered* time through the\n * plan's coverage calendar, plus a one-shot `_smrt_jobs` row\n * (`SupportServiceTarget.checkAndEscalate`) scheduled at `dueAt`;\n * - **satisfy** — outbound specialist/agent interactions satisfy the\n * acknowledgement/response clocks and roll the recurring update clock to\n * its next cycle; satisfied clocks cancel their escalation job;\n * - **pause/resume** — case statuses listed in the plan's `pauseStatuses`\n * freeze clocks (FR-29b: waiting periods pause clocks *only when the plan\n * says so*); resume recomputes `dueAt` from the remaining covered minutes\n * and enqueues a fresh job;\n * - **resolve/reopen** — resolution satisfies the resolution clock and\n * cancels the rest; reopening starts fresh clocks on a new cycle;\n * - **breach** — an overdue clock is marked breached and escalated through\n * the plan's escalation policy (notify or reassign via\n * {@link SupportRoutingService}), with optional delayed follow-up steps.\n *\n * The engine is invoked by the application around\n * {@link SupportCaseService} calls; it deliberately takes explicit `at`\n * instants so behavior is deterministic and testable.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { type SmrtJob, SmrtJobCollection } from '@happyvertical/smrt-jobs';\nimport type { SupportCase } from '../models/support-case.js';\nimport type { SupportInteraction } from '../models/support-interaction.js';\nimport { DEFAULT_TARGET_MINUTES } from '../models/support-plan.js';\nimport {\n SupportEscalationCollection,\n type SupportServiceTarget,\n SupportServiceTargetCollection,\n} from '../models/support-service-target.js';\nimport {\n type EscalationStep,\n OPEN_SUPPORT_CASE_STATUSES,\n type ServiceTargetMinutes,\n type ServiceTargetType,\n type SupportCaseStatus,\n} from '../types.js';\nimport {\n addCoveredMinutes,\n type CoverageCalendar,\n coveredMinutesBetween,\n} from './coverage-calendar.js';\nimport { SupportCaseService } from './support-case-service.js';\nimport { SupportRoutingService } from './support-routing-service.js';\n\n/** Queue name for support escalation jobs. */\nexport const SUPPORT_JOB_QUEUE = 'support';\n\n/** Priority for escalation jobs (`high` — breaches are time-critical). */\nexport const ESCALATION_JOB_PRIORITY = 75;\n\n/** Severity assumed when a case has not been assigned one yet. */\nexport const DEFAULT_SEVERITY_KEY = 'sev3';\n\n/** Case statuses that pause clocks when the plan doesn't say otherwise. */\nconst DEFAULT_PAUSE_STATUSES = ['waiting_on_client'];\n\nconst MINUTE_MS = 60_000;\n\n/** Clock target types in start order. */\nconst TARGET_TYPES: ServiceTargetType[] = [\n 'acknowledgement',\n 'response',\n 'update',\n 'resolution',\n];\n\n/** The plan terms a case's clocks run against (snapshot-first). */\nexport interface ResolvedTargetPlanTerms {\n calendar: CoverageCalendar;\n pauseStatuses: string[];\n escalationPolicy: EscalationStep[];\n targetsForSeverity(severity: string): ServiceTargetMinutes;\n}\n\n/** Options for {@link ServiceTargetEngine.create}. */\nexport interface ServiceTargetEngineOptions extends SmrtObjectOptions {\n /** Share an existing case service (and its collections) with the engine. */\n caseService?: SupportCaseService;\n /**\n * Whether to enqueue real `_smrt_jobs` escalation rows (default `true`).\n * Disable only where a jobs table is unavailable by design.\n */\n scheduleJobs?: boolean;\n}\n\nfunction targetMinutesFor(\n minutes: ServiceTargetMinutes,\n targetType: ServiceTargetType,\n): number | null {\n switch (targetType) {\n case 'acknowledgement':\n return minutes.acknowledgementMinutes;\n case 'response':\n return minutes.responseMinutes;\n case 'update':\n return minutes.updateMinutes;\n case 'resolution':\n return minutes.resolutionMinutes;\n default:\n return null;\n }\n}\n\nfunction asStringArray(value: unknown): string[] | null {\n if (!Array.isArray(value)) {\n return null;\n }\n return value.filter((entry): entry is string => typeof entry === 'string');\n}\n\nfunction asFiniteNumber(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n\nfunction parseIsoDate(value: unknown): Date | null {\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? null : value;\n }\n if (typeof value === 'string' && value) {\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n }\n return null;\n}\n\n/**\n * The clock engine. Construct with {@link ServiceTargetEngine.create}.\n */\nexport class ServiceTargetEngine {\n readonly caseService: SupportCaseService;\n readonly targets: SupportServiceTargetCollection;\n readonly escalations: SupportEscalationCollection;\n readonly jobs: SmrtJobCollection;\n readonly scheduleJobs: boolean;\n\n private readonly options: SmrtObjectOptions;\n private routing?: SupportRoutingService;\n\n protected constructor(deps: {\n options: SmrtObjectOptions;\n caseService: SupportCaseService;\n targets: SupportServiceTargetCollection;\n escalations: SupportEscalationCollection;\n jobs: SmrtJobCollection;\n scheduleJobs: boolean;\n }) {\n this.options = deps.options;\n this.caseService = deps.caseService;\n this.targets = deps.targets;\n this.escalations = deps.escalations;\n this.jobs = deps.jobs;\n this.scheduleJobs = deps.scheduleJobs;\n }\n\n static async create(\n options: ServiceTargetEngineOptions,\n ): Promise<ServiceTargetEngine> {\n const { caseService, scheduleJobs, ...smrtOptions } = options;\n const [service, targets, escalations, jobs] = await Promise.all([\n caseService ?? SupportCaseService.create(smrtOptions),\n SupportServiceTargetCollection.create(smrtOptions),\n SupportEscalationCollection.create(smrtOptions),\n SmrtJobCollection.create(smrtOptions),\n ]);\n return new ServiceTargetEngine({\n options: smrtOptions,\n caseService: service,\n targets,\n escalations,\n jobs,\n scheduleJobs: scheduleJobs ?? true,\n });\n }\n\n private async resolveCase(\n caseRef: SupportCase | string,\n ): Promise<SupportCase> {\n return typeof caseRef === 'string'\n ? this.caseService.getCase(caseRef)\n : caseRef;\n }\n\n private requireId(entity: { id?: string | null }, label: string): string {\n if (!entity.id) {\n throw new Error(`${label} has no id — was it saved?`);\n }\n return entity.id;\n }\n\n private async getRoutingService(): Promise<SupportRoutingService> {\n if (!this.routing) {\n this.routing = await SupportRoutingService.create({\n ...this.options,\n caseService: this.caseService,\n });\n }\n return this.routing;\n }\n\n /**\n * Resolve the plan terms governing a case's clocks: the frozen\n * `planSnapshot` when present, else the loaded plan, else conservative\n * defaults (24×7 calendar, default target minutes, pause on\n * `waiting_on_client`, empty escalation policy).\n */\n async resolvePlanTerms(\n supportCase: SupportCase,\n ): Promise<ResolvedTargetPlanTerms> {\n const snapshot = supportCase.getPlanSnapshot();\n if (Object.keys(snapshot).length > 0) {\n const coverage = Array.isArray(snapshot.coverage)\n ? (snapshot.coverage as CoverageCalendar['windows'])\n : [];\n const calendar: CoverageCalendar = {\n windows: coverage,\n holidays: asStringArray(snapshot.holidays) ?? [],\n timezone:\n typeof snapshot.timezone === 'string' && snapshot.timezone\n ? snapshot.timezone\n : 'UTC',\n };\n const targets =\n snapshot.targets && typeof snapshot.targets === 'object'\n ? (snapshot.targets as Record<string, Partial<ServiceTargetMinutes>>)\n : {};\n return {\n calendar,\n // An explicit (even empty) snapshot list is authoritative — FR-29b:\n // clocks pause only when the plan says so.\n pauseStatuses:\n asStringArray(snapshot.pauseStatuses) ?? DEFAULT_PAUSE_STATUSES,\n escalationPolicy: Array.isArray(snapshot.escalationPolicy)\n ? (snapshot.escalationPolicy as EscalationStep[])\n : [],\n targetsForSeverity: (severity: string) => ({\n ...DEFAULT_TARGET_MINUTES,\n ...(targets[severity] ?? {}),\n }),\n };\n }\n\n if (supportCase.planId) {\n const plan = await this.caseService.plans.get({ id: supportCase.planId });\n if (plan) {\n return {\n calendar: {\n windows: plan.getCoverage(),\n holidays: plan.getHolidays(),\n timezone: plan.timezone || 'UTC',\n },\n pauseStatuses: plan.getPauseStatuses(),\n escalationPolicy: plan.getEscalationPolicy(),\n targetsForSeverity: (severity: string) =>\n plan.targetsForSeverity(severity),\n };\n }\n }\n\n return {\n calendar: { windows: [], holidays: [], timezone: 'UTC' },\n pauseStatuses: [...DEFAULT_PAUSE_STATUSES],\n escalationPolicy: [],\n targetsForSeverity: () => ({ ...DEFAULT_TARGET_MINUTES }),\n };\n }\n\n /**\n * Start the case's Service Target clocks (idempotent: target types that\n * already have an active clock are skipped). One clock per non-null\n * configured minutes for the case severity — the `update` clock only when\n * the plan configures `updateMinutes`. A first start creates cycle 0;\n * starting again after clocks settled (e.g. a reopen) creates the next\n * cycle, preserving the settled rows.\n */\n async startTargetsForCase(\n caseRef: SupportCase | string,\n opts: { at?: Date } = {},\n ): Promise<SupportServiceTarget[]> {\n const supportCase = await this.resolveCase(caseRef);\n return this.startTargets(supportCase, opts.at ?? new Date());\n }\n\n private async startTargets(\n supportCase: SupportCase,\n at: Date,\n ): Promise<SupportServiceTarget[]> {\n const caseId = this.requireId(supportCase, 'SupportCase');\n const terms = await this.resolvePlanTerms(supportCase);\n const severity = supportCase.severity || DEFAULT_SEVERITY_KEY;\n const minutes = terms.targetsForSeverity(severity);\n const existing = await this.targets.forCase(caseId);\n\n const created: SupportServiceTarget[] = [];\n for (const targetType of TARGET_TYPES) {\n const baseMinutes = targetMinutesFor(minutes, targetType);\n if (baseMinutes === null) {\n continue;\n }\n const active = await this.targets.activeTarget(caseId, targetType);\n if (active) {\n continue;\n }\n // Next cycle above any prior (settled) row of this type — first start\n // is cycle 0; a reopen starts the next cycle. Never reuse a settled\n // row's cycle: `(case_id, target_type, cycle)` are conflict columns,\n // so an equal-cycle create would upsert over the historical row.\n const prior = existing.filter(\n (target) => target.targetType === targetType,\n );\n const cycle =\n prior.length > 0\n ? Math.max(...prior.map((target) => target.cycle)) + 1\n : 0;\n created.push(\n await this.startTarget(supportCase, terms, {\n targetType,\n cycle,\n severity,\n baseMinutes,\n at,\n }),\n );\n }\n return created;\n }\n\n private async startTarget(\n supportCase: SupportCase,\n terms: ResolvedTargetPlanTerms,\n input: {\n targetType: ServiceTargetType;\n cycle: number;\n severity: string;\n baseMinutes: number;\n at: Date;\n },\n ): Promise<SupportServiceTarget> {\n const dueAt = addCoveredMinutes(\n terms.calendar,\n input.at,\n input.baseMinutes,\n );\n const target = await this.targets.create({\n tenantId: supportCase.tenantId,\n caseId: this.requireId(supportCase, 'SupportCase'),\n targetType: input.targetType,\n cycle: input.cycle,\n severity: input.severity,\n baseMinutes: input.baseMinutes,\n startedAt: input.at,\n dueAt,\n status: 'pending',\n metadata: JSON.stringify({ consumedMinutes: 0 }),\n });\n await this.scheduleEscalationJob(target, dueAt);\n await this.caseService.recordEvent(supportCase, 'target_scheduled', {\n actorKind: 'system',\n occurredAt: input.at,\n summary: `${input.targetType} target scheduled (cycle ${input.cycle}, due ${dueAt.toISOString()})`,\n payload: {\n targetId: target.id,\n targetType: input.targetType,\n cycle: input.cycle,\n severity: input.severity,\n baseMinutes: input.baseMinutes,\n dueAt: dueAt.toISOString(),\n },\n });\n return target;\n }\n\n /**\n * Enqueue the one-shot escalation job for a clock at `runAt` and record\n * its id on the target (`escalationJobId`) so satisfying can cancel it.\n */\n private async scheduleEscalationJob(\n target: SupportServiceTarget,\n runAt: Date,\n ): Promise<void> {\n if (!this.scheduleJobs) {\n return;\n }\n const job = await this.jobs.enqueueJob(\n {\n tenantId: target.tenantId ?? null,\n queue: SUPPORT_JOB_QUEUE,\n objectType: 'SupportServiceTarget',\n objectId: this.requireId(target, 'SupportServiceTarget'),\n method: 'checkAndEscalate',\n args: {},\n runAt,\n priority: ESCALATION_JOB_PRIORITY,\n },\n // Trusted internal caller: escalation jobs carry clock correctness, so\n // they must not be dropped by the per-tenant creation cap. Their count\n // is bounded by the tenant's active clocks.\n { tenantJobCap: 0 },\n );\n target.escalationJobId = job.id ?? '';\n await target.save();\n }\n\n /**\n * Cancel a clock's pending escalation job, tolerating jobs that already\n * ran, were cancelled, or are missing (at-least-once semantics).\n */\n private async cancelEscalationJob(\n target: SupportServiceTarget,\n ): Promise<void> {\n if (!target.escalationJobId) {\n return;\n }\n try {\n const job: SmrtJob | null = await this.jobs.get({\n id: target.escalationJobId,\n });\n if (job && (job.status === 'pending' || job.status === 'running')) {\n await job.cancel();\n }\n } catch {\n // Already terminal or missing — the target state is authoritative.\n }\n }\n\n /**\n * React to a recorded interaction. Outbound specialist/agent interactions\n * satisfy the acknowledgement clock (first outbound of any kind), the\n * response clock (first substantive outbound — the same trigger), and the\n * active update-cycle clock (which then restarts at the next cycle from\n * the interaction instant). Inbound client interactions never satisfy\n * clocks.\n */\n async onInteractionRecorded(\n caseRef: SupportCase | string,\n interaction: SupportInteraction,\n ): Promise<SupportServiceTarget[]> {\n if (interaction.direction !== 'outbound') {\n return [];\n }\n if (\n interaction.actorKind !== 'specialist' &&\n interaction.actorKind !== 'agent'\n ) {\n return [];\n }\n const supportCase = await this.resolveCase(caseRef);\n const caseId = this.requireId(supportCase, 'SupportCase');\n const at = interaction.occurredAt ?? new Date();\n const satisfied: SupportServiceTarget[] = [];\n\n // Drafts (`metadata.draft`) were never delivered — they satisfy nothing.\n const interactionMeta = interaction.getMetadata();\n if (interactionMeta.draft === true) {\n return [];\n }\n // A templated receipt (`metadata.acknowledgement`) satisfies only the\n // acknowledgement clock; response/update clocks wait for a substantive\n // reply — otherwise the instant AI ack would neuter response targets.\n const ackOnly = interactionMeta.acknowledgement === true;\n const satisfiableTypes = ackOnly\n ? (['acknowledgement'] as const)\n : (['acknowledgement', 'response'] as const);\n\n for (const targetType of satisfiableTypes) {\n const target = await this.targets.activeTarget(caseId, targetType);\n if (target) {\n await this.satisfyTarget(supportCase, target, at);\n satisfied.push(target);\n }\n }\n\n const update = ackOnly\n ? null\n : await this.targets.activeTarget(caseId, 'update');\n if (update) {\n await this.satisfyTarget(supportCase, update, at);\n satisfied.push(update);\n const terms = await this.resolvePlanTerms(supportCase);\n const severity = supportCase.severity || DEFAULT_SEVERITY_KEY;\n const updateMinutes = terms.targetsForSeverity(severity).updateMinutes;\n if (updateMinutes !== null) {\n await this.startTarget(supportCase, terms, {\n targetType: 'update',\n cycle: update.cycle + 1,\n severity,\n baseMinutes: updateMinutes,\n at,\n });\n }\n }\n\n return satisfied;\n }\n\n private async satisfyTarget(\n supportCase: SupportCase,\n target: SupportServiceTarget,\n at: Date,\n ): Promise<void> {\n if (target.pausedAt) {\n target.pausedTotalSeconds += Math.max(\n 0,\n Math.round((at.getTime() - target.pausedAt.getTime()) / 1000),\n );\n target.pausedAt = null;\n }\n target.status = 'satisfied';\n target.satisfiedAt = at;\n await target.save();\n await this.cancelEscalationJob(target);\n await this.caseService.recordEvent(supportCase, 'target_satisfied', {\n actorKind: 'system',\n occurredAt: at,\n summary: `${target.targetType} target satisfied`,\n payload: {\n targetId: target.id,\n targetType: target.targetType,\n cycle: target.cycle,\n },\n });\n }\n\n /**\n * React to a case lifecycle transition:\n * - to `resolved` — satisfy the resolution clock, cancel every other\n * active clock (and its job);\n * - to `closed` — cancel all remaining active clocks;\n * - reopen (`resolved`/`closed` → an open status) — start fresh clocks on\n * the next cycle for every type;\n * - entering a plan pause status — freeze pending clocks (FR-29b: only\n * when the plan says so);\n * - leaving a pause status — resume paused clocks with `dueAt` recomputed\n * from the remaining covered minutes and a fresh escalation job.\n */\n async onCaseTransition(\n caseRef: SupportCase | string,\n from: SupportCaseStatus,\n to: SupportCaseStatus,\n opts: { at?: Date } = {},\n ): Promise<void> {\n const supportCase = await this.resolveCase(caseRef);\n const at = opts.at ?? new Date();\n\n if (to === 'resolved' || to === 'closed') {\n await this.settleTargetsForTerminal(supportCase, to, at);\n return;\n }\n\n const reopened =\n (from === 'resolved' || from === 'closed') &&\n OPEN_SUPPORT_CASE_STATUSES.includes(to);\n if (reopened) {\n await this.startTargets(supportCase, at);\n return;\n }\n\n const terms = await this.resolvePlanTerms(supportCase);\n const wasPaused = terms.pauseStatuses.includes(from);\n const nowPaused = terms.pauseStatuses.includes(to);\n if (!wasPaused && nowPaused) {\n await this.pauseTargets(supportCase, terms, at);\n } else if (wasPaused && !nowPaused) {\n await this.resumeTargets(supportCase, terms, at);\n }\n }\n\n private async settleTargetsForTerminal(\n supportCase: SupportCase,\n to: 'resolved' | 'closed',\n at: Date,\n ): Promise<void> {\n const caseId = this.requireId(supportCase, 'SupportCase');\n if (to === 'resolved') {\n const resolution = await this.targets.activeTarget(caseId, 'resolution');\n if (resolution) {\n await this.satisfyTarget(supportCase, resolution, at);\n }\n }\n const remaining = await this.targets.list({\n where: { caseId, 'status in': ['pending', 'paused'] },\n });\n for (const target of remaining) {\n if (target.pausedAt) {\n target.pausedTotalSeconds += Math.max(\n 0,\n Math.round((at.getTime() - target.pausedAt.getTime()) / 1000),\n );\n target.pausedAt = null;\n }\n target.status = 'cancelled';\n target.cancelledAt = at;\n await target.save();\n await this.cancelEscalationJob(target);\n }\n }\n\n private async pauseTargets(\n supportCase: SupportCase,\n terms: ResolvedTargetPlanTerms,\n at: Date,\n ): Promise<void> {\n const caseId = this.requireId(supportCase, 'SupportCase');\n const pending = await this.targets.list({\n where: { caseId, status: 'pending' },\n });\n for (const target of pending) {\n const meta = target.getMetadata();\n // Covered minutes consumed by the segment that just ended (since the\n // last resume, or the clock start), accumulated across segments so\n // resume can compute the remaining covered minutes exactly.\n const segmentStart = parseIsoDate(meta.lastResumedAt) ?? target.startedAt;\n const consumedMinutes =\n asFiniteNumber(meta.consumedMinutes) +\n coveredMinutesBetween(terms.calendar, segmentStart, at);\n target.setMetadata({ ...meta, consumedMinutes });\n target.pausedAt = at;\n target.status = 'paused';\n await target.save();\n await this.cancelEscalationJob(target);\n await this.caseService.recordEvent(supportCase, 'target_paused', {\n actorKind: 'system',\n occurredAt: at,\n summary: `${target.targetType} target paused`,\n payload: {\n targetId: target.id,\n targetType: target.targetType,\n cycle: target.cycle,\n consumedMinutes,\n },\n });\n }\n }\n\n private async resumeTargets(\n supportCase: SupportCase,\n terms: ResolvedTargetPlanTerms,\n at: Date,\n ): Promise<void> {\n const caseId = this.requireId(supportCase, 'SupportCase');\n const paused = await this.targets.list({\n where: { caseId, status: 'paused' },\n });\n for (const target of paused) {\n const meta = target.getMetadata();\n const remainingMinutes = Math.max(\n target.baseMinutes - asFiniteNumber(meta.consumedMinutes),\n 0,\n );\n if (target.pausedAt) {\n target.pausedTotalSeconds += Math.max(\n 0,\n Math.round((at.getTime() - target.pausedAt.getTime()) / 1000),\n );\n }\n target.pausedAt = null;\n target.status = 'pending';\n const dueAt = addCoveredMinutes(terms.calendar, at, remainingMinutes);\n target.dueAt = dueAt;\n target.setMetadata({ ...meta, lastResumedAt: at.toISOString() });\n await target.save();\n await this.scheduleEscalationJob(target, dueAt);\n await this.caseService.recordEvent(supportCase, 'target_resumed', {\n actorKind: 'system',\n occurredAt: at,\n summary: `${target.targetType} target resumed (due ${dueAt.toISOString()})`,\n payload: {\n targetId: target.id,\n targetType: target.targetType,\n cycle: target.cycle,\n remainingMinutes,\n dueAt: dueAt.toISOString(),\n },\n });\n }\n }\n\n /**\n * Escalate a breached clock through the plan's escalation policy: write\n * the {@link SupportEscalation} audit row, apply the step action\n * (`notify` records the profiles to notify; `reassign` routes to the next\n * eligible specialist, excluding the current assignee), bump the case\n * escalation level, transition the case to `escalated` (unless already\n * resolved/closed), and schedule the next policy step when it declares a\n * `delayMinutes`.\n */\n async escalateForBreach(\n target: SupportServiceTarget,\n opts: { at?: Date } = {},\n ): Promise<void> {\n const at = opts.at ?? target.breachedAt ?? new Date();\n const supportCase = await this.caseService.getCase(target.caseId);\n const terms = await this.resolvePlanTerms(supportCase);\n const level = supportCase.escalationLevel + 1;\n const step: EscalationStep = terms.escalationPolicy[level - 1] ?? {\n level,\n action: 'notify',\n };\n\n // Escalation is idempotent per (target, level): at-least-once job\n // delivery (or a concurrent duplicate claim) must not stack duplicate\n // escalation records, notifications, or reassignments.\n const already = await this.escalations.list({\n where: { targetId: target.id, level },\n limit: 1,\n });\n if (already.length > 0) {\n return;\n }\n\n const escalation = await this.escalations.create({\n tenantId: supportCase.tenantId,\n caseId: this.requireId(supportCase, 'SupportCase'),\n level,\n reason: 'target_breach',\n targetId: target.id,\n targetType: target.targetType,\n action: step.action,\n fromSpecialistId: supportCase.assignedSpecialistId,\n notifiedProfileIds: JSON.stringify(step.notifyProfileIds ?? []),\n occurredAt: at,\n });\n\n let reassignedTo: string | null = null;\n if (step.action === 'reassign') {\n const routing = await this.getRoutingService();\n const exclude = supportCase.assignedSpecialistId\n ? [supportCase.assignedSpecialistId]\n : [];\n const result = await routing.autoAssign(supportCase, {\n at,\n exclude,\n actorKind: 'system',\n });\n if (result.assigned && result.specialistId) {\n reassignedTo = result.specialistId;\n escalation.toSpecialistId = result.specialistId;\n await escalation.save();\n }\n }\n\n supportCase.escalationLevel = level;\n supportCase.escalatedAt = at;\n const terminal =\n supportCase.status === 'resolved' || supportCase.status === 'closed';\n if (!terminal && supportCase.status !== 'escalated') {\n await this.caseService.transition(supportCase, 'escalated', {\n actorKind: 'system',\n reason: `${target.targetType} target breached`,\n });\n } else {\n await supportCase.save();\n }\n\n await this.caseService.recordEvent(supportCase, 'target_breached', {\n actorKind: 'system',\n occurredAt: at,\n summary: `${target.targetType} target breached`,\n payload: {\n targetId: target.id,\n targetType: target.targetType,\n cycle: target.cycle,\n dueAt: target.dueAt.toISOString(),\n },\n });\n await this.caseService.recordEvent(supportCase, 'escalation', {\n actorKind: 'system',\n occurredAt: at,\n summary: `Escalated to level ${level} (${step.action})`,\n payload: {\n escalationId: escalation.id,\n level,\n action: step.action,\n targetId: target.id,\n targetType: target.targetType,\n notifiedProfileIds: step.notifyProfileIds ?? [],\n reassignedTo,\n },\n });\n\n // Delayed follow-up: the next policy step (when it declares a delay)\n // re-fires checkAndEscalate on the same target at `at + delay` wall\n // time; the pending level marker gates the breached-target branch.\n const next = terms.escalationPolicy[level];\n if (next && next.delayMinutes != null && next.delayMinutes >= 0) {\n const runAt = new Date(at.getTime() + next.delayMinutes * MINUTE_MS);\n const meta = target.getMetadata();\n target.setMetadata({\n ...meta,\n pendingEscalationLevel: next.level,\n pendingEscalationAt: runAt.toISOString(),\n });\n await target.save();\n if (this.scheduleJobs) {\n await this.jobs.enqueueJob(\n {\n tenantId: target.tenantId ?? null,\n queue: SUPPORT_JOB_QUEUE,\n objectType: 'SupportServiceTarget',\n objectId: this.requireId(target, 'SupportServiceTarget'),\n method: 'checkAndEscalate',\n args: {},\n runAt,\n priority: ESCALATION_JOB_PRIORITY,\n },\n { tenantJobCap: 0 },\n );\n }\n }\n }\n\n /**\n * Continue a delayed escalation on an already-breached clock: consumes the\n * pending-level marker and escalates the next policy step, unless the case\n * has since left its open states.\n */\n async continueEscalation(\n target: SupportServiceTarget,\n at: Date,\n ): Promise<boolean> {\n const meta = target.getMetadata();\n const pendingAt = parseIsoDate(meta.pendingEscalationAt);\n if (meta.pendingEscalationLevel == null || pendingAt === null) {\n return false;\n }\n if (at.getTime() < pendingAt.getTime()) {\n return false;\n }\n const {\n pendingEscalationLevel: _level,\n pendingEscalationAt: _at,\n ...rest\n } = meta;\n target.setMetadata(rest);\n await target.save();\n\n const supportCase = await this.caseService.getCase(target.caseId);\n if (!supportCase.isOpen()) {\n return false;\n }\n await this.escalateForBreach(target, { at });\n return true;\n }\n}\n\nexport default ServiceTargetEngine;\n","/**\n * ServiceTimeEntryService — the shared recording contract for Service Time\n * Entries (FR-40, issue #1930): timer, manual, imported, and automatic agent\n * entries all pass ONE validation and audit gate, so no source can smuggle in\n * an incoherent duration, participant, or work context.\n *\n * Recording is deliberately money-free: entries capture the work (duration,\n * period, evidence, participant, context) and land as `draft`; pricing and\n * earnings derive later, at approval time, in `TimeEntryApprovalService`.\n * Case-attached entries append a `time_recorded` audit event so the case\n * timeline shows the work as it happens.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n type ServiceTimeEntry,\n ServiceTimeEntryCollection,\n} from '../models/service-time-entry.js';\nimport type { SupportCase } from '../models/support-case.js';\nimport type {\n ServiceTimeEntrySource,\n SupportParticipantKind,\n TimeEntryEvidence,\n} from '../types.js';\nimport { SupportCaseService } from './support-case-service.js';\n\n/** Options for {@link ServiceTimeEntryService.create}. */\nexport interface ServiceTimeEntryServiceOptions extends SmrtObjectOptions {\n /** Share an existing case facade (otherwise one is created internally). */\n caseService?: SupportCaseService;\n}\n\n/** Input for {@link ServiceTimeEntryService.record} — all four sources. */\nexport interface RecordServiceTimeEntryInput {\n tenantId?: string | null;\n /** Support Case context (either this or the work ref pair is required). */\n caseId?: string | null;\n /** Polymorphic work context: qualified class name + id, together. */\n workRefType?: string | null;\n workRefId?: string | null;\n /** Specialist role record, when the deliverer is a Support Specialist. */\n specialistId?: string | null;\n participantKind: SupportParticipantKind;\n /** Delivering human Participant — required when `participantKind: 'human'`. */\n participantProfileId?: string | null;\n /** Delivering agent identity — required when `participantKind: 'agent'`. */\n agentRef?: string;\n source: ServiceTimeEntrySource;\n description?: string;\n startedAt?: Date;\n endedAt?: Date;\n /** Explicit duration; derived from `endedAt − startedAt` when omitted. */\n durationSeconds?: number;\n evidence?: TimeEntryEvidence[];\n metadata?: Record<string, unknown>;\n}\n\n/** Input for {@link ServiceTimeEntryService.submit}. */\nexport interface SubmitServiceTimeEntryInput {\n byProfileId?: string | null;\n}\n\n/**\n * The write facade for recording and submitting Service Time Entries.\n * Construct with {@link ServiceTimeEntryService.create}.\n */\nexport class ServiceTimeEntryService {\n readonly entries: ServiceTimeEntryCollection;\n readonly caseService: SupportCaseService;\n\n protected constructor(collections: {\n entries: ServiceTimeEntryCollection;\n caseService: SupportCaseService;\n }) {\n this.entries = collections.entries;\n this.caseService = collections.caseService;\n }\n\n static async create(\n options: ServiceTimeEntryServiceOptions,\n ): Promise<ServiceTimeEntryService> {\n const caseService =\n options.caseService ?? (await SupportCaseService.create(options));\n const entries = await ServiceTimeEntryCollection.create(options);\n return new ServiceTimeEntryService({ entries, caseService });\n }\n\n /** Load an entry or throw a descriptive error. */\n async getEntry(\n entryRef: ServiceTimeEntry | string,\n ): Promise<ServiceTimeEntry> {\n if (typeof entryRef !== 'string') {\n return entryRef;\n }\n const found = await this.entries.get({ id: entryRef });\n if (!found) {\n throw new Error(`ServiceTimeEntry not found: ${entryRef}`);\n }\n return found;\n }\n\n /**\n * Record one Service Time Entry as a `draft`. One validation contract for\n * all four sources (FR-40):\n *\n * - a work context is required: `caseId` and/or the `workRefType`/`workRefId`\n * pair (a case-attached entry copies the case's tenant unless given);\n * - the participant must be coherent: `human` needs `participantProfileId`,\n * `agent` needs `agentRef`;\n * - `timer` entries need both period bounds; every entry needs a positive\n * duration — explicit `durationSeconds` or derived `endedAt − startedAt`.\n *\n * Case-attached entries append a `time_recorded` audit event.\n */\n async record(input: RecordServiceTimeEntryInput): Promise<ServiceTimeEntry> {\n const hasWorkRef = Boolean(input.workRefType) || Boolean(input.workRefId);\n if (hasWorkRef && !(input.workRefType && input.workRefId)) {\n throw new Error(\n 'ServiceTimeEntry: workRefType and workRefId must be provided together.',\n );\n }\n if (!input.caseId && !hasWorkRef) {\n throw new Error(\n 'ServiceTimeEntry: a work context is required — provide caseId or the workRefType/workRefId pair.',\n );\n }\n\n if (input.participantKind === 'human' && !input.participantProfileId) {\n throw new Error(\n \"ServiceTimeEntry: participantKind 'human' requires participantProfileId (the delivering Participant).\",\n );\n }\n if (input.participantKind === 'agent' && !input.agentRef) {\n throw new Error(\n \"ServiceTimeEntry: participantKind 'agent' requires agentRef (the delivering agent identity).\",\n );\n }\n\n if (input.source === 'timer' && !(input.startedAt && input.endedAt)) {\n throw new Error(\n \"ServiceTimeEntry: source 'timer' requires both startedAt and endedAt.\",\n );\n }\n if (\n input.startedAt &&\n input.endedAt &&\n input.startedAt.getTime() >= input.endedAt.getTime()\n ) {\n throw new Error('ServiceTimeEntry: startedAt must be before endedAt.');\n }\n\n const durationSeconds =\n input.durationSeconds ??\n (input.startedAt && input.endedAt\n ? Math.round(\n (input.endedAt.getTime() - input.startedAt.getTime()) / 1000,\n )\n : undefined);\n if (durationSeconds === undefined) {\n throw new Error(\n 'ServiceTimeEntry: a duration is required — provide durationSeconds or both startedAt and endedAt.',\n );\n }\n if (!(durationSeconds > 0)) {\n throw new Error(\n `ServiceTimeEntry: durationSeconds must be greater than zero (got ${durationSeconds}).`,\n );\n }\n\n let tenantId = input.tenantId ?? null;\n let supportCase: SupportCase | null = null;\n if (input.caseId) {\n supportCase = await this.caseService.getCase(input.caseId);\n if (input.tenantId === undefined) {\n tenantId = supportCase.tenantId;\n } else if ((input.tenantId ?? null) !== supportCase.tenantId) {\n // An explicit tenant must agree with the case — otherwise the entry\n // would be a cross-tenant reference onto another tenant's case.\n throw new Error(\n `ServiceTimeEntry: tenantId '${input.tenantId}' does not match the case's tenant '${supportCase.tenantId}'.`,\n );\n }\n }\n\n const entry = await this.entries.create({\n tenantId,\n caseId: input.caseId ?? null,\n workRefType: input.workRefType ?? null,\n workRefId: input.workRefId ?? null,\n specialistId: input.specialistId ?? null,\n participantKind: input.participantKind,\n participantProfileId: input.participantProfileId ?? null,\n agentRef: input.agentRef ?? '',\n source: input.source,\n description: input.description ?? '',\n startedAt: input.startedAt ?? null,\n endedAt: input.endedAt ?? null,\n durationSeconds,\n evidence: JSON.stringify(input.evidence ?? []),\n status: 'draft',\n metadata: JSON.stringify(input.metadata ?? {}),\n });\n\n if (supportCase) {\n await this.caseService.recordEvent(supportCase, 'time_recorded', {\n actorKind: input.participantKind === 'agent' ? 'agent' : 'specialist',\n actorProfileId: input.participantProfileId ?? null,\n summary: `Time recorded: ${(durationSeconds / 3600).toFixed(2)}h (${input.source})`,\n payload: {\n timeEntryId: entry.id,\n durationSeconds,\n source: input.source,\n participantKind: input.participantKind,\n },\n });\n }\n\n return entry;\n }\n\n /**\n * Submit a `draft` (or resubmit a `rejected`) entry for approval, stamping\n * the submitter. Approval itself is `TimeEntryApprovalService`'s job.\n */\n async submit(\n entryRef: ServiceTimeEntry | string,\n input: SubmitServiceTimeEntryInput = {},\n ): Promise<ServiceTimeEntry> {\n const entry = await this.getEntry(entryRef);\n if (entry.status !== 'draft' && entry.status !== 'rejected') {\n throw new Error(\n `ServiceTimeEntry ${entry.id}: only 'draft' or 'rejected' entries can be submitted (status is '${entry.status}').`,\n );\n }\n entry.status = 'submitted';\n entry.submittedAt = new Date();\n entry.submittedByProfileId = input.byProfileId ?? null;\n await entry.save();\n return entry;\n }\n}\n\nexport default ServiceTimeEntryService;\n","/**\n * SupportAiWorkflow — the Automated Support Response pipeline (FR-28a, issue\n * #1928): on intake a case is immediately acknowledged, classified, and\n * answered from scoped knowledge; only when the resolved {@link SupportPolicy}\n * explicitly allows it may the workflow resolve a low-risk case autonomously.\n * Every phase — run or skipped — writes an append-only {@link SupportAiRun}\n * audit row, and every risk signal (client request, low confidence,\n * sensitivity, high severity, boundary failure, attempt cap) routes through\n * {@link HumanHandoffService} so a Support Specialist takes over with the\n * complete context (FR-28b).\n *\n * The AI call is an injected {@link SupportAiBoundary} and knowledge\n * retrieval an injected {@link SupportKnowledgeProvider} (the `smrt-personas`\n * `ReflectionRunner` injected-boundary idiom) — tests mock only these seams,\n * and the package stays dependency-free: apps plug `smrt-facts` /\n * `smrt-content`-backed providers.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n type SupportAiRun,\n SupportAiRunCollection,\n} from '../models/support-ai-run.js';\nimport type { SupportCase } from '../models/support-case.js';\nimport { SupportInteractionCollection } from '../models/support-interaction.js';\nimport {\n DEFAULT_SUPPORT_POLICY,\n SupportPolicyCollection,\n} from '../models/support-policy.js';\nimport {\n DEFAULT_SEVERITY_DEFINITIONS,\n type HumanHandoffTrigger,\n type SupportAiRunOutcome,\n type SupportAiRunPhase,\n type SupportChannelKind,\n} from '../types.js';\nimport { HumanHandoffService } from './human-handoff-service.js';\nimport { SupportCaseService } from './support-case-service.js';\nimport type { IntakeResult } from './support-intake-service.js';\n\n/** One knowledge source consulted while drafting an automated answer. */\nexport interface KnowledgeSnippet {\n /** Source kind, e.g. `fact`, `article`, `document`. */\n kind: string;\n /** Source reference (id, slug, or URL) for the audit trail. */\n ref: string;\n label?: string;\n content: string;\n}\n\n/** Classification returned by {@link SupportAiBoundary.classify}. */\nexport interface SupportAiClassifyResult {\n /** One of the offered severity keys, or `''` when undetermined. */\n severity: string;\n /** Short free-vocabulary category, or `''` when undetermined. */\n category: string;\n /** Whether the matter is sensitive (always triggers a Human Handoff). */\n sensitive: boolean;\n /** Confidence in `[0, 1]`. */\n confidence: number;\n /** Model identifier, when the boundary knows it. */\n model?: string;\n}\n\n/** Draft answer returned by {@link SupportAiBoundary.answer}. */\nexport interface SupportAiAnswerResult {\n reply: string;\n /** Confidence in `[0, 1]`; below the policy threshold → Human Handoff. */\n confidence: number;\n /** Whether the reply would fully resolve the request (FR-28a). */\n proposedResolution: boolean;\n /** Model identifier, when the boundary knows it. */\n model?: string;\n}\n\n/**\n * THE AI seam: the only thing tests mock. The default implementation\n * ({@link createDefaultAiBoundary}) delegates to the case's own `do()` AI\n * operation from `smrt-core`.\n */\nexport interface SupportAiBoundary {\n classify(input: {\n subject: string;\n body: string;\n severityKeys: string[];\n sensitiveCategories: string[];\n }): Promise<SupportAiClassifyResult>;\n answer(input: {\n subject: string;\n body: string;\n knowledge: KnowledgeSnippet[];\n caseSummary: string;\n }): Promise<SupportAiAnswerResult>;\n}\n\n/**\n * The knowledge seam: retrieve the snippets an automated answer may cite.\n * The default provider returns nothing, keeping this package dependency-free\n * — apps plug `smrt-facts` / `smrt-content`-backed providers.\n */\nexport interface SupportKnowledgeProvider {\n retrieve(input: {\n subject: string;\n body: string;\n projectId: string | null;\n tenantId: string | null;\n }): Promise<KnowledgeSnippet[]>;\n}\n\n/** Notification passed to `onHandoff` after each handoff attempt. */\nexport interface SupportHandoffNotice {\n supportCase: SupportCase;\n trigger: HumanHandoffTrigger;\n /** True when the no-repeat guarantee deduped this trigger. */\n alreadyActive: boolean;\n}\n\n/** Options for {@link SupportAiWorkflow.create}. */\nexport interface SupportAiWorkflowOptions extends SmrtObjectOptions {\n /** The AI seam; omitted → {@link createDefaultAiBoundary} per case. */\n boundary?: SupportAiBoundary;\n /** The knowledge seam; omitted → a no-op provider. */\n knowledge?: SupportKnowledgeProvider;\n /** Handoff engine to share; omitted → one is created lazily. */\n handoffService?: HumanHandoffService;\n /** Called after every handoff attempt (app notification seam). */\n onHandoff?: (notice: SupportHandoffNotice) => Promise<void>;\n}\n\n/** The resolved policy shape a pass runs under (row or built-in defaults). */\ninterface EffectiveSupportPolicy {\n policyId: string | null;\n autoAcknowledge: boolean;\n autoClassify: boolean;\n autoAnswer: boolean;\n autoTroubleshoot: boolean;\n autoResolve: boolean;\n autoResolveMaxSeverity: string;\n confidenceThreshold: number;\n maxAutoAttempts: number;\n autoSendEmailReplies: boolean;\n sensitiveCategories: string[];\n allowedTools: string[];\n}\n\n/** Severity ranks at or below this are `high_severity` handoff triggers. */\nconst HIGH_SEVERITY_MAX_RANK = 2;\n\n/**\n * Numeric rank of a severity key by its numeric suffix (`sev1` → 1; lower is\n * more severe). Returns `NaN` for keys without a numeric suffix, which makes\n * every comparison false — auto-resolution fails closed and the\n * high-severity trigger stays quiet for unknown vocabularies.\n */\nexport function severityRank(severityKey: string | null | undefined): number {\n const match = /(\\d+)\\s*$/.exec((severityKey ?? '').trim());\n return match ? Number.parseInt(match[1] ?? '', 10) : Number.NaN;\n}\n\n/**\n * The Automated Support Response pipeline. Construct with\n * {@link SupportAiWorkflow.create}; wire {@link processIntake} into\n * `SupportIntakeService`'s `onCaseIntake` hook.\n */\nexport class SupportAiWorkflow {\n readonly caseService: SupportCaseService;\n readonly policies: SupportPolicyCollection;\n readonly aiRuns: SupportAiRunCollection;\n readonly interactions: SupportInteractionCollection;\n readonly handoffs: HumanHandoffService;\n private readonly boundary: SupportAiBoundary | null;\n private readonly knowledge: SupportKnowledgeProvider;\n private readonly onHandoff?: (notice: SupportHandoffNotice) => Promise<void>;\n\n protected constructor(deps: {\n caseService: SupportCaseService;\n policies: SupportPolicyCollection;\n aiRuns: SupportAiRunCollection;\n interactions: SupportInteractionCollection;\n handoffs: HumanHandoffService;\n boundary: SupportAiBoundary | null;\n knowledge: SupportKnowledgeProvider;\n onHandoff?: (notice: SupportHandoffNotice) => Promise<void>;\n }) {\n this.caseService = deps.caseService;\n this.policies = deps.policies;\n this.aiRuns = deps.aiRuns;\n this.interactions = deps.interactions;\n this.handoffs = deps.handoffs;\n this.boundary = deps.boundary;\n this.knowledge = deps.knowledge;\n this.onHandoff = deps.onHandoff;\n }\n\n static async create(\n options: SupportAiWorkflowOptions,\n ): Promise<SupportAiWorkflow> {\n const [caseService, policies, aiRuns, interactions, handoffs] =\n await Promise.all([\n SupportCaseService.create(options),\n SupportPolicyCollection.create(options),\n SupportAiRunCollection.create(options),\n SupportInteractionCollection.create(options),\n options.handoffService\n ? Promise.resolve(options.handoffService)\n : HumanHandoffService.create(options),\n ]);\n return new SupportAiWorkflow({\n caseService,\n policies,\n aiRuns,\n interactions,\n handoffs,\n boundary: options.boundary ?? null,\n knowledge: options.knowledge ?? createNoopKnowledgeProvider(),\n onHandoff: options.onHandoff,\n });\n }\n\n /**\n * Entry point for `SupportIntakeService.onCaseIntake`: run one automated\n * pass over the case an inbound interaction created or joined.\n */\n async processIntake(result: IntakeResult): Promise<SupportAiRun[]> {\n const caseId = result.supportCase?.id;\n if (!caseId || result.outcome === 'skipped') {\n return [];\n }\n return this.processCase(caseId, {\n interactionId: result.interaction?.id ?? null,\n });\n }\n\n /**\n * Run one Automated Support Response pass over a case: acknowledge →\n * classify → answer → troubleshoot → resolve, each gated by the resolved\n * policy and audited as a {@link SupportAiRun}. Cases with `aiEnabled`\n * off, or no longer open, are left untouched.\n */\n async processCase(\n caseId: string,\n opts: { interactionId?: string | null } = {},\n ): Promise<SupportAiRun[]> {\n const supportCase = await this.caseService.getCase(caseId);\n if (supportCase.aiEnabled === false || !supportCase.isOpen()) {\n return [];\n }\n\n const policy = await this.resolveEffectivePolicy(supportCase);\n const boundary =\n this.boundary ?? createDefaultAiBoundary(() => supportCase);\n const interactionId = opts.interactionId ?? null;\n const body = await this.requestBodyOf(supportCase, interactionId);\n const runs: SupportAiRun[] = [];\n\n // Phase: acknowledge (FR-28a — the client hears back immediately).\n runs.push(await this.runAcknowledge(supportCase, policy, interactionId));\n\n // Phase: classify (severity / category / sensitivity triage).\n if (!policy.autoClassify) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'classify',\n outcome: 'skipped',\n interactionId,\n metadata: { reason: 'policy-disabled' },\n }),\n );\n } else {\n const startedAt = new Date();\n let classification: SupportAiClassifyResult;\n try {\n classification = await boundary.classify({\n subject: supportCase.subject,\n body,\n severityKeys: this.severityKeysOf(supportCase),\n sensitiveCategories: policy.sensitiveCategories,\n });\n } catch (error) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'classify',\n outcome: 'failed',\n interactionId,\n startedAt,\n error: errorMessage(error),\n }),\n );\n await this.triggerHandoff(\n supportCase,\n 'failed_resolution',\n `AI classification failed: ${errorMessage(error)}`,\n );\n return runs;\n }\n const classifiedSensitive =\n classification.sensitive ||\n (classification.category !== '' &&\n policy.sensitiveCategories.includes(classification.category));\n // Persist triage only into empty fields — a human's triage is never\n // overwritten by the model.\n let caseDirty = false;\n if (!supportCase.severity && classification.severity) {\n supportCase.severity = classification.severity;\n caseDirty = true;\n }\n if (!supportCase.category && classification.category) {\n supportCase.category = classification.category;\n caseDirty = true;\n }\n if (!supportCase.sensitive && classifiedSensitive) {\n supportCase.sensitive = true;\n caseDirty = true;\n }\n if (caseDirty) {\n await supportCase.save();\n }\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'classify',\n outcome: 'completed',\n interactionId,\n startedAt,\n confidence: classification.confidence,\n classification: {\n severity: classification.severity,\n category: classification.category,\n sensitive: classifiedSensitive,\n confidence: classification.confidence,\n },\n model: classification.model,\n }),\n );\n }\n\n // Always-on triggers (FR-28b): sensitivity stops autonomous answering;\n // high severity routes a human while answering continues in parallel.\n const sensitive = supportCase.sensitive;\n if (sensitive) {\n await this.triggerHandoff(\n supportCase,\n 'sensitive',\n 'Case involves a sensitive matter',\n );\n }\n const rank = severityRank(supportCase.severity);\n if (Number.isFinite(rank) && rank <= HIGH_SEVERITY_MAX_RANK) {\n await this.triggerHandoff(\n supportCase,\n 'high_severity',\n `Severity ${supportCase.severity} requires a Support Specialist`,\n );\n }\n\n // Phase: answer (knowledge-grounded reply above the confidence floor).\n let answer: SupportAiAnswerResult | null = null;\n let answerPosted = false;\n if (!policy.autoAnswer) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'answer',\n outcome: 'skipped',\n interactionId,\n metadata: { reason: 'policy-disabled' },\n }),\n );\n } else if (sensitive) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'answer',\n outcome: 'handed_off',\n interactionId,\n metadata: { reason: 'sensitive' },\n }),\n );\n } else if (supportCase.humanRequestedAt) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'answer',\n outcome: 'handed_off',\n interactionId,\n metadata: { reason: 'human-requested' },\n }),\n );\n await this.triggerHandoff(\n supportCase,\n 'client_request',\n 'Client explicitly requested a human',\n );\n } else {\n const priorAttempts = await this.countAnswerAttempts(caseId);\n if (priorAttempts >= policy.maxAutoAttempts) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'answer',\n outcome: 'skipped',\n interactionId,\n metadata: {\n reason: 'max-attempts',\n attempts: priorAttempts,\n maxAutoAttempts: policy.maxAutoAttempts,\n },\n }),\n );\n await this.triggerHandoff(\n supportCase,\n 'policy',\n `Automated answer attempts exhausted (${priorAttempts}/${policy.maxAutoAttempts})`,\n );\n } else {\n const startedAt = new Date();\n let snippets: KnowledgeSnippet[] = [];\n try {\n snippets = await this.knowledge.retrieve({\n subject: supportCase.subject,\n body,\n projectId: supportCase.projectId,\n tenantId: supportCase.tenantId,\n });\n answer = await boundary.answer({\n subject: supportCase.subject,\n body,\n knowledge: snippets,\n caseSummary: this.caseSummaryOf(supportCase),\n });\n } catch (error) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'answer',\n outcome: 'failed',\n interactionId,\n startedAt,\n knowledgeRefs: snippets,\n error: errorMessage(error),\n }),\n );\n await this.triggerHandoff(\n supportCase,\n 'failed_resolution',\n `AI answer failed: ${errorMessage(error)}`,\n );\n return runs;\n }\n if (answer.confidence >= policy.confidenceThreshold) {\n const attemptNumber = priorAttempts + 1;\n const draft =\n supportCase.channelKind === 'email' && !policy.autoSendEmailReplies;\n const interaction = await this.caseService.recordInteraction(\n supportCase,\n {\n direction: 'outbound',\n channelKind: (supportCase.channelKind ||\n 'chat') as SupportChannelKind,\n actorKind: 'agent',\n body: answer.reply,\n sourceKey: `ai:answer:${caseId}:${attemptNumber}`,\n metadata: { draft },\n },\n );\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'answer',\n outcome: 'completed',\n interactionId,\n startedAt,\n confidence: answer.confidence,\n knowledgeRefs: snippets,\n responseInteractionId: interaction.id ?? null,\n model: answer.model,\n metadata: { attempt: attemptNumber, draft },\n }),\n );\n // A drafted email was never delivered to the client — it must not\n // count as a posted answer, so autonomous resolution stays gated\n // until the reply actually goes out (codex P1, PR #1943).\n answerPosted = !draft;\n } else {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'answer',\n outcome: 'handed_off',\n interactionId,\n startedAt,\n confidence: answer.confidence,\n knowledgeRefs: snippets,\n model: answer.model,\n // The unsent draft travels in the audit metadata so the\n // Specialist still sees the automated work (FR-28b).\n metadata: { reason: 'low-confidence', unsentReply: answer.reply },\n }),\n );\n await this.triggerHandoff(\n supportCase,\n 'low_confidence',\n `Answer confidence ${answer.confidence} below threshold ${policy.confidenceThreshold}`,\n );\n }\n }\n }\n\n // Phase: troubleshoot — tool execution ships behind `allowedTools` in a\n // later slice; #1928 always records the gate decision.\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'troubleshoot',\n outcome: 'skipped',\n interactionId,\n metadata: {\n reason: policy.autoTroubleshoot\n ? 'not-implemented'\n : 'policy-disabled',\n },\n }),\n );\n\n // Phase: resolve — autonomous only when the policy explicitly allows it\n // and every risk gate passes.\n const skipReason = this.resolveSkipReason(supportCase, policy, {\n answer,\n answerPosted,\n sensitive,\n });\n if (skipReason !== null || !answer) {\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'resolve',\n outcome: 'skipped',\n interactionId,\n metadata: { reason: skipReason ?? 'no-answer' },\n }),\n );\n } else {\n await this.caseService.resolve(supportCase, {\n actorKind: 'agent',\n summary: answer.reply,\n resolutionKind: 'automated',\n });\n runs.push(\n await this.writeRun(supportCase, {\n phase: 'resolve',\n outcome: 'completed',\n interactionId,\n confidence: answer.confidence,\n }),\n );\n }\n\n return runs;\n }\n\n /** Acknowledge phase: post the templated receipt once per case. */\n private async runAcknowledge(\n supportCase: SupportCase,\n policy: EffectiveSupportPolicy,\n interactionId: string | null,\n ): Promise<SupportAiRun> {\n if (!policy.autoAcknowledge) {\n return this.writeRun(supportCase, {\n phase: 'acknowledge',\n outcome: 'skipped',\n interactionId,\n metadata: { reason: 'policy-disabled' },\n });\n }\n if (supportCase.acknowledgedAt) {\n return this.writeRun(supportCase, {\n phase: 'acknowledge',\n outcome: 'skipped',\n interactionId,\n metadata: { reason: 'already-acknowledged' },\n });\n }\n const caseId = this.caseIdOf(supportCase);\n // Email receipts are drafts unless the policy sends automated mail — an\n // undelivered receipt must not stamp `acknowledgedAt` or satisfy the\n // acknowledgement Service Target.\n const draft =\n supportCase.channelKind === 'email' && !policy.autoSendEmailReplies;\n const interaction = await this.caseService.recordInteraction(supportCase, {\n direction: 'outbound',\n channelKind: (supportCase.channelKind || 'chat') as SupportChannelKind,\n actorKind: 'agent',\n body: `Thanks for reaching out — we've opened case ${supportCase.caseNumber} and are looking into it now.`,\n sourceKey: `ai:ack:${caseId}`,\n // A templated receipt is an acknowledgement only — it must not satisfy\n // response Service Targets or stamp `firstRespondedAt` (the substantive\n // answer does that).\n metadata: { acknowledgement: true, draft },\n });\n return this.writeRun(supportCase, {\n phase: 'acknowledge',\n outcome: 'completed',\n interactionId,\n responseInteractionId: interaction.id ?? null,\n });\n }\n\n /** Why autonomous resolution must not run, or `null` when it may. */\n private resolveSkipReason(\n supportCase: SupportCase,\n policy: EffectiveSupportPolicy,\n outcome: {\n answer: SupportAiAnswerResult | null;\n answerPosted: boolean;\n sensitive: boolean;\n },\n ): string | null {\n if (!policy.autoResolve) {\n return 'policy-disabled';\n }\n const caseRank = severityRank(supportCase.severity);\n const maxRank = severityRank(policy.autoResolveMaxSeverity);\n if (\n !Number.isFinite(caseRank) ||\n !Number.isFinite(maxRank) ||\n caseRank < maxRank\n ) {\n return 'severity-ineligible';\n }\n if (!outcome.answerPosted || !outcome.answer) {\n return 'no-answer';\n }\n if (outcome.answer.confidence < policy.confidenceThreshold) {\n return 'low-confidence';\n }\n if (!outcome.answer.proposedResolution) {\n return 'no-proposed-resolution';\n }\n if (outcome.sensitive) {\n return 'sensitive';\n }\n if (supportCase.humanRequestedAt) {\n return 'human-requested';\n }\n return null;\n }\n\n /** Resolve the governing policy: matching row, else built-in defaults. */\n private async resolveEffectivePolicy(\n supportCase: SupportCase,\n ): Promise<EffectiveSupportPolicy> {\n const policy = await this.policies.resolveForScope({\n planId: supportCase.planId,\n projectId: supportCase.projectId,\n });\n if (!policy) {\n return {\n policyId: null,\n ...DEFAULT_SUPPORT_POLICY,\n sensitiveCategories: [...DEFAULT_SUPPORT_POLICY.sensitiveCategories],\n allowedTools: [...DEFAULT_SUPPORT_POLICY.allowedTools],\n };\n }\n return {\n policyId: policy.id ?? null,\n autoAcknowledge: policy.autoAcknowledge,\n autoClassify: policy.autoClassify,\n autoAnswer: policy.autoAnswer,\n autoTroubleshoot: policy.autoTroubleshoot,\n autoResolve: policy.autoResolve,\n autoResolveMaxSeverity: policy.autoResolveMaxSeverity,\n confidenceThreshold: policy.confidenceThreshold,\n maxAutoAttempts: policy.maxAutoAttempts,\n autoSendEmailReplies: policy.autoSendEmailReplies,\n sensitiveCategories: policy.getSensitiveCategories(),\n allowedTools: policy.getAllowedTools(),\n };\n }\n\n /**\n * Prior boundary-consuming answer attempts. Low-confidence answers consume\n * the boundary too (`handed_off`), so they count toward the attempt budget\n * — only `skipped` runs (which never called the boundary) are free.\n */\n private async countAnswerAttempts(caseId: string): Promise<number> {\n const runs = await this.aiRuns.forCase(caseId);\n return runs.filter(\n (run) => run.phase === 'answer' && run.outcome !== 'skipped',\n ).length;\n }\n\n /** The request text a pass reasons over: triggering interaction, else the\n * case description. */\n private async requestBodyOf(\n supportCase: SupportCase,\n interactionId: string | null,\n ): Promise<string> {\n if (interactionId) {\n const interaction = await this.interactions.get({ id: interactionId });\n if (interaction?.body) {\n return interaction.body;\n }\n }\n return supportCase.description;\n }\n\n /** Severity vocabulary: the case's plan snapshot, else the defaults. */\n private severityKeysOf(supportCase: SupportCase): string[] {\n const defs = supportCase.getPlanSnapshot().severityDefinitions;\n if (defs && typeof defs === 'object' && !Array.isArray(defs)) {\n const keys = Object.keys(defs as Record<string, unknown>);\n if (keys.length > 0) {\n return keys;\n }\n }\n return Object.keys(DEFAULT_SEVERITY_DEFINITIONS);\n }\n\n /** One-line case summary handed to the answer boundary. */\n private caseSummaryOf(supportCase: SupportCase): string {\n const parts = [\n `Case ${supportCase.caseNumber}`,\n `status ${supportCase.status}`,\n ];\n if (supportCase.severity) {\n parts.push(`severity ${supportCase.severity}`);\n }\n if (supportCase.category) {\n parts.push(`category ${supportCase.category}`);\n }\n return `${parts.join(', ')} — ${supportCase.subject}`;\n }\n\n /** Route a trigger through the handoff engine and notify the app seam. */\n private async triggerHandoff(\n supportCase: SupportCase,\n trigger: HumanHandoffTrigger,\n note: string,\n ): Promise<void> {\n const { alreadyActive } = await this.handoffs.handoff(supportCase, {\n trigger,\n note,\n });\n if (this.onHandoff) {\n await this.onHandoff({ supportCase, trigger, alreadyActive });\n }\n }\n\n /** Append one audit run plus its `ai_run` case event. */\n private async writeRun(\n supportCase: SupportCase,\n input: {\n phase: SupportAiRunPhase;\n outcome: SupportAiRunOutcome;\n interactionId?: string | null;\n confidence?: number | null;\n classification?: Record<string, unknown>;\n responseInteractionId?: string | null;\n knowledgeRefs?: KnowledgeSnippet[];\n error?: string;\n model?: string;\n startedAt?: Date;\n metadata?: Record<string, unknown>;\n },\n ): Promise<SupportAiRun> {\n const run = await this.aiRuns.create({\n tenantId: supportCase.tenantId,\n caseId: this.caseIdOf(supportCase),\n interactionId: input.interactionId ?? null,\n phase: input.phase,\n outcome: input.outcome,\n confidence: input.confidence ?? null,\n classification: JSON.stringify(input.classification ?? {}),\n responseInteractionId: input.responseInteractionId ?? null,\n knowledgeRefs: JSON.stringify(input.knowledgeRefs ?? []),\n error: input.error ?? '',\n correlationId: crypto.randomUUID(),\n model: input.model ?? '',\n startedAt: input.startedAt ?? new Date(),\n completedAt: new Date(),\n metadata: JSON.stringify(input.metadata ?? {}),\n });\n await this.caseService.recordEvent(supportCase, 'ai_run', {\n actorKind: 'agent',\n summary: `AI ${input.phase} → ${input.outcome}`,\n payload: {\n runId: run.id,\n phase: input.phase,\n outcome: input.outcome,\n confidence: input.confidence ?? null,\n },\n });\n return run;\n }\n\n /** The persisted id of a saved case. */\n private caseIdOf(supportCase: SupportCase): string {\n if (!supportCase.id) {\n throw new Error('SupportCase has no id — was it saved?');\n }\n return supportCase.id;\n }\n}\n\n/**\n * The default knowledge provider: retrieves nothing. Keeps the package\n * dependency-free; apps supply providers backed by `smrt-facts`,\n * `smrt-content`, or their own corpus.\n */\nexport function createNoopKnowledgeProvider(): SupportKnowledgeProvider {\n return {\n retrieve: () => Promise.resolve([]),\n };\n}\n\n/**\n * The default AI boundary: delegates to the case's own `do()` AI operation\n * (smrt-core) asking for strict JSON, parsed defensively — a malformed\n * classification comes back sensitive with zero confidence, failing toward\n * the human.\n */\nexport function createDefaultAiBoundary(\n getCase: () => SupportCase,\n): SupportAiBoundary {\n return {\n async classify(input) {\n const raw = String(await getCase().do(buildClassifyInstructions(input)));\n const parsed = extractJsonObject(raw);\n if (!parsed) {\n return { severity: '', category: '', sensitive: true, confidence: 0 };\n }\n return {\n severity: typeof parsed.severity === 'string' ? parsed.severity : '',\n category: typeof parsed.category === 'string' ? parsed.category : '',\n sensitive: parsed.sensitive === true,\n confidence: clampConfidence(parsed.confidence),\n };\n },\n async answer(input) {\n const raw = String(await getCase().do(buildAnswerInstructions(input)));\n const parsed = extractJsonObject(raw);\n if (!parsed || typeof parsed.reply !== 'string' || !parsed.reply.trim()) {\n return { reply: '', confidence: 0, proposedResolution: false };\n }\n return {\n reply: parsed.reply,\n confidence: clampConfidence(parsed.confidence),\n proposedResolution: parsed.proposedResolution === true,\n };\n },\n };\n}\n\n/** Build the strict-JSON classification instructions for `do()`. */\nfunction buildClassifyInstructions(input: {\n subject: string;\n body: string;\n severityKeys: string[];\n sensitiveCategories: string[];\n}): string {\n const sensitiveList =\n input.sensitiveCategories.length > 0\n ? input.sensitiveCategories.join(', ')\n : '(none configured)';\n return [\n 'You are triaging a client support request.',\n `Severity keys, most severe first: ${input.severityKeys.join(', ')}.`,\n `Sensitive categories: ${sensitiveList}.`,\n `Subject: ${input.subject}`,\n 'Request:',\n input.body,\n '',\n 'Respond with ONLY a strict JSON object:',\n '{\"severity\": \"<one severity key>\", \"category\": \"<short-kebab-case>\",',\n '\"sensitive\": <true when the matter touches a sensitive category, legal',\n 'or security exposure, or personal data>, \"confidence\": <number 0..1>}',\n ].join('\\n');\n}\n\n/** Build the strict-JSON answer instructions for `do()`. */\nfunction buildAnswerInstructions(input: {\n subject: string;\n body: string;\n knowledge: KnowledgeSnippet[];\n caseSummary: string;\n}): string {\n const knowledgeBlock =\n input.knowledge.length > 0\n ? input.knowledge\n .map(\n (snippet) =>\n `- [${snippet.kind}:${snippet.ref}]${\n snippet.label ? ` ${snippet.label}:` : ''\n } ${snippet.content}`,\n )\n .join('\\n')\n : '(no knowledge available)';\n return [\n 'You are drafting a support reply grounded ONLY in the knowledge below.',\n `Context: ${input.caseSummary}`,\n `Subject: ${input.subject}`,\n 'Request:',\n input.body,\n 'Knowledge:',\n knowledgeBlock,\n '',\n 'Respond with ONLY a strict JSON object:',\n '{\"reply\": \"<the reply to send>\", \"confidence\": <number 0..1>,',\n '\"proposedResolution\": <true when the reply fully resolves the request>}',\n ].join('\\n');\n}\n\n/** Best-effort strict-JSON extraction from a raw model response. */\nfunction extractJsonObject(raw: string): Record<string, unknown> | null {\n const text = raw.trim();\n const candidates = [text];\n const embedded = /\\{[\\s\\S]*\\}/.exec(text);\n if (embedded && embedded[0] !== text) {\n candidates.push(embedded[0]);\n }\n for (const candidate of candidates) {\n try {\n const parsed = JSON.parse(candidate);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // Try the next candidate.\n }\n }\n return null;\n}\n\n/** Clamp a model-reported confidence into `[0, 1]` (non-numbers → 0). */\nfunction clampConfidence(value: unknown): number {\n const num = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(num)) {\n return 0;\n }\n return Math.min(1, Math.max(0, num));\n}\n\n/** Normalise an unknown thrown value into an audit-friendly message. */\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport default SupportAiWorkflow;\n","/**\n * SupportIntakeService — channel intake: every inbound interaction on a bound\n * transport container deterministically creates or joins one canonical\n * Support Case (FR-28, issue #1926).\n *\n * Channels are transports, not systems of record: the ChatMessage / Email row\n * stays canonical in its package; intake links it as a\n * {@link SupportInteraction} (idempotent on `sourceKey`) and drives the\n * create-or-join decision by conversation key:\n *\n * - chat: `chat:<roomId>[:<threadId>]` — a bound room is one client\n * conversation; threads open distinct cases.\n * - email: the RFC thread — a reply (`inReplyTo`) joins the case holding the\n * referenced message; otherwise the message id starts a new thread key.\n *\n * Join semantics: open case → join; resolved case → reopen + join (the client\n * replied to a \"resolved\" conversation); closed case → a fresh case.\n *\n * Intake never throws into the source package's save path — the opt-in\n * interceptor (`registerSupportIntake`) swallows and logs intake errors.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtObject, SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { GlobalInterceptors } from '@happyvertical/smrt-core';\nimport type { SupportCase } from '../models/support-case.js';\nimport {\n type SupportChannelBinding,\n SupportChannelBindingCollection,\n} from '../models/support-channel-binding.js';\nimport type { SupportInteraction } from '../models/support-interaction.js';\nimport { KeyedMutex } from './keyed-mutex.js';\nimport { SupportCaseService } from './support-case-service.js';\n\n/** Qualified source types intake understands. */\nexport const CHAT_MESSAGE_SOURCE_TYPE = '@happyvertical/smrt-chat:ChatMessage';\nexport const CHAT_ROOM_TARGET_TYPE = '@happyvertical/smrt-chat:ChatRoom';\nexport const EMAIL_SOURCE_TYPE = '@happyvertical/smrt-messages:Email';\nexport const EMAIL_ACCOUNT_TARGET_TYPE =\n '@happyvertical/smrt-messages:EmailAccount';\n\nexport interface InboundChatMessage {\n messageId: string;\n roomId: string;\n threadId?: string | null;\n senderProfileId: string;\n /** Chat role; only `user` messages are client inbound. */\n role?: string;\n content: string;\n tenantId?: string | null;\n occurredAt?: Date;\n}\n\nexport interface InboundEmail {\n emailId: string;\n accountId: string;\n fromAddress: string;\n fromName?: string;\n subject?: string;\n body: string;\n /** RFC 822 Message-ID of this email. */\n rfcMessageId?: string | null;\n /** RFC 822 Message-ID this email replies to. */\n inReplyTo?: string | null;\n tenantId?: string | null;\n occurredAt?: Date;\n}\n\nexport interface IntakeResult {\n outcome: 'created' | 'joined' | 'reopened' | 'skipped';\n reason?: string;\n supportCase?: SupportCase;\n interaction?: SupportInteraction;\n binding?: SupportChannelBinding;\n}\n\nexport interface SupportIntakeOptions extends SmrtObjectOptions {\n /**\n * Resolve the Client profile for an inbound email sender. When omitted and\n * the binding has no default client, the case is parked with\n * `metadata.unresolvedClient = true` for operator triage.\n */\n resolveClientProfileId?: (input: {\n email: string;\n name?: string;\n tenantId?: string | null;\n }) => Promise<string | null>;\n /**\n * Called after intake creates or updates a case — the AI workflow and the\n * target engine attach here without a hard dependency cycle.\n */\n onCaseIntake?: (result: IntakeResult) => Promise<void>;\n}\n\nexport class SupportIntakeService {\n readonly caseService: SupportCaseService;\n readonly bindings: SupportChannelBindingCollection;\n private readonly options: SupportIntakeOptions;\n private readonly conversationMutex = new KeyedMutex();\n\n protected constructor(\n caseService: SupportCaseService,\n bindings: SupportChannelBindingCollection,\n options: SupportIntakeOptions,\n ) {\n this.caseService = caseService;\n this.bindings = bindings;\n this.options = options;\n }\n\n static async create(\n options: SupportIntakeOptions,\n ): Promise<SupportIntakeService> {\n const [caseService, bindings] = await Promise.all([\n SupportCaseService.create(options),\n SupportChannelBindingCollection.create(options),\n ]);\n return new SupportIntakeService(caseService, bindings, options);\n }\n\n /** Conversation key for a chat room/thread. */\n static chatThreadKey(roomId: string, threadId?: string | null): string {\n return threadId ? `chat:${roomId}:${threadId}` : `chat:${roomId}`;\n }\n\n /** Idempotency key for a chat message. */\n static chatSourceKey(messageId: string): string {\n return `chat:${messageId}`;\n }\n\n /** Idempotency key for an email. */\n static emailSourceKey(emailId: string): string {\n return `email:${emailId}`;\n }\n\n /**\n * Ingest one inbound chat message from a bound room: create-or-join the\n * conversation's case and record the interaction.\n */\n async ingestChatMessage(input: InboundChatMessage): Promise<IntakeResult> {\n if (input.role && input.role !== 'user') {\n return { outcome: 'skipped', reason: `role:${input.role}` };\n }\n const binding = await this.bindings.findForTarget(\n CHAT_ROOM_TARGET_TYPE,\n input.roomId,\n );\n if (!binding) {\n return { outcome: 'skipped', reason: 'no-binding' };\n }\n\n const threadKey = SupportIntakeService.chatThreadKey(\n input.roomId,\n input.threadId,\n );\n const sourceKey = SupportIntakeService.chatSourceKey(input.messageId);\n const clientProfileId = binding.clientProfileId ?? input.senderProfileId;\n\n return this.createOrJoin({\n binding,\n threadKey,\n sourceKey,\n tenantId: input.tenantId ?? binding.tenantId ?? null,\n subject: truncateSubject(input.content),\n body: input.content,\n clientProfileId,\n openedByProfileId: input.senderProfileId,\n authorProfileId: input.senderProfileId,\n occurredAt: input.occurredAt,\n sourceType: CHAT_MESSAGE_SOURCE_TYPE,\n sourceId: input.messageId,\n interactionMetadata: {\n roomId: input.roomId,\n threadId: input.threadId ?? null,\n },\n });\n }\n\n /**\n * Ingest one inbound email from a bound account: joins the RFC thread's\n * case when `inReplyTo` references a known interaction, else creates a new\n * case keyed by the message id. Mail sent FROM the binding's own addresses\n * is skipped (our outbound side of the conversation).\n */\n async ingestEmail(input: InboundEmail): Promise<IntakeResult> {\n const binding = await this.bindings.findForTarget(\n EMAIL_ACCOUNT_TARGET_TYPE,\n input.accountId,\n );\n if (!binding) {\n return { outcome: 'skipped', reason: 'no-binding' };\n }\n const from = input.fromAddress.trim().toLowerCase();\n if (binding.getSelfAddresses().includes(from)) {\n return { outcome: 'skipped', reason: 'self-address' };\n }\n\n const sourceKey = SupportIntakeService.emailSourceKey(input.emailId);\n\n // A reply joins the case that holds the referenced message.\n let threadKey: string | null = null;\n if (input.inReplyTo) {\n const parent = await this.findInteractionByRfcMessageId(input.inReplyTo);\n if (parent) {\n const parentCase = await this.caseService.cases.get({\n id: parent.caseId,\n });\n if (parentCase) {\n threadKey = parentCase.threadKey;\n }\n }\n }\n if (!threadKey) {\n threadKey = `email:${input.rfcMessageId || input.emailId}`;\n }\n\n let clientProfileId = binding.clientProfileId ?? null;\n let unresolvedClient = false;\n if (!clientProfileId && this.options.resolveClientProfileId) {\n clientProfileId = await this.options.resolveClientProfileId({\n email: input.fromAddress,\n name: input.fromName,\n tenantId: input.tenantId ?? binding.tenantId ?? null,\n });\n }\n if (!clientProfileId) {\n unresolvedClient = true;\n }\n\n return this.createOrJoin({\n binding,\n threadKey,\n sourceKey,\n tenantId: input.tenantId ?? binding.tenantId ?? null,\n subject: input.subject?.trim() || truncateSubject(input.body),\n body: input.body,\n clientProfileId,\n openedByProfileId: clientProfileId,\n authorProfileId: clientProfileId,\n occurredAt: input.occurredAt,\n sourceType: EMAIL_SOURCE_TYPE,\n sourceId: input.emailId,\n rfcMessageId: input.rfcMessageId ?? null,\n caseMetadata: unresolvedClient\n ? { unresolvedClient: true, fromAddress: input.fromAddress }\n : undefined,\n interactionMetadata: {\n fromAddress: input.fromAddress,\n fromName: input.fromName ?? null,\n rfcMessageId: input.rfcMessageId ?? null,\n inReplyTo: input.inReplyTo ?? null,\n },\n });\n }\n\n /** The shared create-or-join core (deterministic per conversation key). */\n private async createOrJoin(input: {\n binding: SupportChannelBinding;\n threadKey: string;\n sourceKey: string;\n tenantId: string | null;\n subject: string;\n body: string;\n clientProfileId: string | null;\n openedByProfileId: string | null;\n authorProfileId: string | null;\n occurredAt?: Date;\n sourceType: string;\n sourceId: string;\n rfcMessageId?: string | null;\n caseMetadata?: Record<string, unknown>;\n interactionMetadata?: Record<string, unknown>;\n }): Promise<IntakeResult> {\n // Serialize per conversation: two concurrent first messages for the same\n // room/thread must not both observe \"no open case\" and split one\n // conversation across duplicate canonical cases (their source keys\n // differ, so row uniqueness alone cannot catch this).\n const conversationKey = `${input.binding.id ?? ''}:${input.threadKey}`;\n return this.conversationMutex.run(conversationKey, () =>\n this.createOrJoinExclusive(input),\n );\n }\n\n private async createOrJoinExclusive(input: {\n binding: SupportChannelBinding;\n threadKey: string;\n sourceKey: string;\n tenantId: string | null;\n subject: string;\n body: string;\n clientProfileId: string | null;\n openedByProfileId: string | null;\n authorProfileId: string | null;\n occurredAt?: Date;\n sourceType: string;\n sourceId: string;\n rfcMessageId?: string | null;\n caseMetadata?: Record<string, unknown>;\n interactionMetadata?: Record<string, unknown>;\n }): Promise<IntakeResult> {\n const { binding } = input;\n\n // Idempotency: the same transport record never produces two interactions.\n const existing = await this.caseService.interactions.bySourceKey(\n input.sourceKey,\n );\n if (existing) {\n const supportCase = await this.caseService.cases.get({\n id: existing.caseId,\n });\n return {\n outcome: 'joined',\n reason: 'duplicate-source',\n supportCase: supportCase ?? undefined,\n interaction: existing,\n binding,\n };\n }\n\n let outcome: IntakeResult['outcome'];\n let supportCase = await this.caseService.cases.findOpenByThreadKey(\n input.threadKey,\n { bindingId: binding.id },\n );\n\n if (supportCase) {\n outcome = 'joined';\n } else {\n const resolved = await this.findResolvedByThreadKey(\n input.threadKey,\n binding.id ?? null,\n );\n if (resolved) {\n supportCase = await this.caseService.reopen(resolved, {\n actorKind: 'client',\n actorProfileId: input.authorProfileId,\n reason: 'new inbound interaction on resolved case',\n });\n outcome = 'reopened';\n } else {\n supportCase = await this.caseService.openCase({\n tenantId: input.tenantId,\n subject: input.subject,\n description: input.body,\n channelKind: binding.channelKind,\n clientProfileId: input.clientProfileId,\n openedByProfileId: input.openedByProfileId,\n projectId: binding.projectId ?? null,\n bindingId: binding.id ?? null,\n threadKey: input.threadKey,\n planId: binding.planId ?? null,\n metadata: input.caseMetadata,\n });\n outcome = 'created';\n }\n }\n\n const interaction = await this.caseService.recordInteraction(supportCase, {\n direction: 'inbound',\n channelKind: binding.channelKind,\n actorKind: 'client',\n authorProfileId: input.authorProfileId,\n body: input.body,\n occurredAt: input.occurredAt,\n sourceType: input.sourceType,\n sourceId: input.sourceId,\n sourceKey: input.sourceKey,\n rfcMessageId: input.rfcMessageId ?? '',\n metadata: input.interactionMetadata,\n });\n\n const result: IntakeResult = {\n outcome,\n supportCase,\n interaction,\n binding,\n };\n if (this.options.onCaseIntake) {\n await this.options.onCaseIntake(result);\n }\n return result;\n }\n\n private async findResolvedByThreadKey(\n threadKey: string,\n bindingId: string | null,\n ): Promise<SupportCase | null> {\n const where: Record<string, unknown> = { threadKey, status: 'resolved' };\n if (bindingId) {\n where.bindingId = bindingId;\n }\n const matches = await this.caseService.cases.list({\n where,\n orderBy: 'updated_at DESC',\n limit: 1,\n });\n return matches[0] ?? null;\n }\n\n private async findInteractionByRfcMessageId(\n rfcMessageId: string,\n ): Promise<SupportInteraction | null> {\n // Keyed lookup on the first-class column — reply threading must not\n // depend on how much traffic arrived since the parent message.\n return this.caseService.interactions.byRfcMessageId(rfcMessageId);\n }\n}\n\nfunction truncateSubject(body: string, max = 120): string {\n const firstLine = (body ?? '').split('\\n', 1)[0]?.trim() ?? '';\n if (firstLine.length <= max) {\n return firstLine || '(no subject)';\n }\n return `${firstLine.slice(0, max - 1)}…`;\n}\n\n/** Interceptor name used for registration/unregistration. */\nexport const SUPPORT_INTAKE_INTERCEPTOR = 'smrt-support:intake';\n\n/**\n * Opt-in ambient intake: observe saves of `ChatMessage` and `Email` rows via\n * core `GlobalInterceptors` and feed them through the intake service. Only\n * containers with an enabled {@link SupportChannelBinding} produce cases;\n * everything else passes through untouched. Errors are logged, never thrown —\n * intake must not break the source package's save path.\n *\n * @returns An unregister function.\n */\nexport function registerSupportIntake(\n intake: SupportIntakeService,\n options: { onError?: (error: unknown, instance: SmrtObject) => void } = {},\n): () => void {\n const handleError = (error: unknown, instance: SmrtObject) => {\n if (options.onError) {\n options.onError(error, instance);\n return;\n }\n // Lazy logger: created on first failure, never at module scope (a\n // module-scope createLogger reads process.env and breaks browser builds).\n createLogger(true).error('[smrt-support] intake failed', { error });\n };\n\n GlobalInterceptors.register({\n name: SUPPORT_INTAKE_INTERCEPTOR,\n async afterSave(instance) {\n const record = instance as unknown as Record<string, unknown>;\n try {\n const className = instance.constructor?.name;\n if (className === 'ChatMessage') {\n // Only freshly created user messages are inbound client activity.\n if (record.isDeleted === true) return;\n await intake.ingestChatMessage({\n messageId: String(record.id ?? ''),\n roomId: String(record.roomId ?? ''),\n threadId: (record.threadId as string | null) ?? null,\n senderProfileId: String(record.senderProfileId ?? ''),\n role: (record.role as string | undefined) ?? 'user',\n content: String(record.content ?? ''),\n tenantId: (record.tenantId as string | null) ?? null,\n occurredAt:\n record.created_at instanceof Date ? record.created_at : undefined,\n });\n } else if (className === 'Email') {\n await intake.ingestEmail({\n emailId: String(record.id ?? ''),\n accountId: String(record.accountId ?? ''),\n fromAddress: String(record.fromAddress ?? ''),\n fromName: (record.fromName as string | undefined) ?? undefined,\n subject: (record.subject as string | undefined) ?? undefined,\n body: String(record.textBody ?? record.body ?? ''),\n rfcMessageId: (record.messageId as string | null) ?? null,\n inReplyTo: (record.inReplyTo as string | null) ?? null,\n tenantId: (record.tenantId as string | null) ?? null,\n occurredAt: record.date instanceof Date ? record.date : undefined,\n });\n }\n } catch (error) {\n handleError(error, instance);\n }\n },\n });\n\n return () => {\n GlobalInterceptors.unregister(SUPPORT_INTAKE_INTERCEPTOR);\n };\n}\n\nexport default SupportIntakeService;\n","/**\n * SupportPlanAdminService — the permission-gated write surface for\n * commercial terms: Managed Support Plans (client pricing/service terms) and\n * Support Compensation Plans (provider earning terms).\n *\n * The `support.manage-plans` split is the gate (the\n * `personas.activate-directive` idiom): the generated CRUD routes on both\n * models are read-only, so every plan write flows through here, carries an\n * accountable principal, and refuses cross-tenant acts. Plan history stays\n * safe regardless — cases snapshot plan terms at apply time and settlements\n * snapshot rates at approval time — but only holders of the split may shape\n * future terms.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n type SupportCompensationPlan,\n SupportCompensationPlanCollection,\n} from '../models/support-compensation-plan.js';\nimport {\n type SupportPlan,\n SupportPlanCollection,\n} from '../models/support-plan.js';\nimport {\n MANAGE_PLANS_PERMISSION,\n type SupportPrincipal,\n} from '../permissions.js';\n\n/** Thrown when a plan write is attempted without the manage-plans split. */\nexport class PlanAdminDeniedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PlanAdminDeniedError';\n }\n}\n\nexport interface PlanAdminActor {\n principal: SupportPrincipal;\n}\n\nexport class SupportPlanAdminService {\n readonly plans: SupportPlanCollection;\n readonly compensationPlans: SupportCompensationPlanCollection;\n\n protected constructor(collections: {\n plans: SupportPlanCollection;\n compensationPlans: SupportCompensationPlanCollection;\n }) {\n this.plans = collections.plans;\n this.compensationPlans = collections.compensationPlans;\n }\n\n static async create(\n options: SmrtObjectOptions,\n ): Promise<SupportPlanAdminService> {\n const [plans, compensationPlans] = await Promise.all([\n SupportPlanCollection.create(options),\n SupportCompensationPlanCollection.create(options),\n ]);\n return new SupportPlanAdminService({ plans, compensationPlans });\n }\n\n /** Create (or overwrite via natural key) a Managed Support Plan. */\n async savePlan(\n input: PlanAdminActor & { fields: Record<string, unknown> },\n ): Promise<SupportPlan> {\n this.assertManager(input.principal, 'writing a Managed Support Plan');\n this.assertTenant(\n input.principal,\n (input.fields.tenantId as string | null | undefined) ?? null,\n );\n return this.plans.create(input.fields);\n }\n\n /** Update an existing Managed Support Plan's fields. */\n async updatePlan(\n planId: string,\n input: PlanAdminActor & { fields: Record<string, unknown> },\n ): Promise<SupportPlan> {\n this.assertManager(input.principal, 'updating a Managed Support Plan');\n const plan = await this.plans.get({ id: planId });\n if (!plan) {\n throw new Error(`SupportPlan not found: ${planId}`);\n }\n this.assertTenant(input.principal, plan.tenantId);\n Object.assign(plan, input.fields);\n await plan.save();\n return plan;\n }\n\n /** Archive a Managed Support Plan (soft retirement; history untouched). */\n async archivePlan(\n planId: string,\n input: PlanAdminActor,\n ): Promise<SupportPlan> {\n return this.updatePlan(planId, {\n principal: input.principal,\n fields: { status: 'archived' },\n });\n }\n\n /** Create a Support Compensation Plan (effective-dated earning terms). */\n async saveCompensationPlan(\n input: PlanAdminActor & { fields: Record<string, unknown> },\n ): Promise<SupportCompensationPlan> {\n this.assertManager(input.principal, 'writing a Support Compensation Plan');\n this.assertTenant(\n input.principal,\n (input.fields.tenantId as string | null | undefined) ?? null,\n );\n return this.compensationPlans.create(input.fields);\n }\n\n /** Update an existing Support Compensation Plan's fields. */\n async updateCompensationPlan(\n planId: string,\n input: PlanAdminActor & { fields: Record<string, unknown> },\n ): Promise<SupportCompensationPlan> {\n this.assertManager(input.principal, 'updating a Support Compensation Plan');\n const plan = await this.compensationPlans.get({ id: planId });\n if (!plan) {\n throw new Error(`SupportCompensationPlan not found: ${planId}`);\n }\n this.assertTenant(input.principal, plan.tenantId);\n Object.assign(plan, input.fields);\n await plan.save();\n return plan;\n }\n\n /** Archive a Support Compensation Plan. */\n async archiveCompensationPlan(\n planId: string,\n input: PlanAdminActor,\n ): Promise<SupportCompensationPlan> {\n return this.updateCompensationPlan(planId, {\n principal: input.principal,\n fields: { status: 'archived' },\n });\n }\n\n protected assertManager(\n principal: SupportPrincipal | undefined,\n act: string,\n ): void {\n if (!principal?.can(MANAGE_PLANS_PERMISSION)) {\n throw new PlanAdminDeniedError(\n `Denied ${act}: principal lacks '${MANAGE_PLANS_PERMISSION}'.`,\n );\n }\n }\n\n /** Cross-tenant plan writes are refused when both sides carry a tenant. */\n protected assertTenant(\n principal: SupportPrincipal,\n planTenantId: string | null,\n ): void {\n if (!principal.tenantId || !planTenantId) {\n return;\n }\n if (principal.tenantId !== planTenantId) {\n throw new PlanAdminDeniedError(\n `Denied: principal tenant '${principal.tenantId}' does not match the plan tenant '${planTenantId}'.`,\n );\n }\n }\n}\n\nexport default SupportPlanAdminService;\n","/**\n * TimeEntryApprovalService — the approval gate for Service Time Entries and\n * the ONLY writer of their commercial snapshots (FR-36/FR-32/FR-35, issue\n * #1930).\n *\n * Approval paths come from the governing Managed Support Plan's\n * `timeApprovalPolicy`: `automatic` (no principal needed under the\n * thresholds), `threshold` (an over-threshold automatic entry needs an\n * operator), `operator` (the `support.approve-time-entry` split), and\n * `client` (the case's Client, with the operator split as override).\n *\n * On approval two deliberately separate rows derive per entry:\n *\n * - **SupportCharge** — client side, from the Managed Support Plan only:\n * included time consumes first (per case; period windows arrive with usage\n * pricing #1925), the remainder meters at the overage (or on-call) rate.\n * Rate math uses the CASE's `planSnapshot` when present so later plan edits\n * never change history.\n * - **SupportCompensation** — provider side, from the effective-dated Support\n * Compensation Plan only.\n *\n * Margin (charge − compensation) stays computable by readers and is NEVER\n * stored; case-visible audit events carry the charge amount only, never the\n * compensation. Approved entries freeze (model-enforced); disputes flow\n * through {@link TimeEntryApprovalService.correct}, which supersedes the\n * original and re-derives fresh snapshots from the correcting entry.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n type ServiceTimeEntry,\n ServiceTimeEntryCollection,\n} from '../models/service-time-entry.js';\nimport type { SupportCase } from '../models/support-case.js';\nimport {\n type SupportCompensationPlan,\n SupportCompensationPlanCollection,\n} from '../models/support-compensation-plan.js';\nimport type { SupportPlan } from '../models/support-plan.js';\nimport {\n type SupportCharge,\n SupportChargeCollection,\n type SupportCompensation,\n SupportCompensationCollection,\n} from '../models/support-settlement.js';\nimport {\n APPROVE_TIME_ENTRY_PERMISSION,\n type SupportPrincipal,\n} from '../permissions.js';\nimport type {\n TimeApprovalPolicy,\n TimeEntryApprovalPath,\n TimeEntryEvidence,\n} from '../types.js';\nimport { KeyedMutex } from './keyed-mutex.js';\nimport { SupportCaseService } from './support-case-service.js';\n\n/** Thrown when an approval-path or tenant gate refuses the acting principal. */\nexport class TimeEntryApprovalDeniedError extends Error {\n constructor(reason: string) {\n super(`Time entry approval denied: ${reason}`);\n this.name = 'TimeEntryApprovalDeniedError';\n }\n}\n\n/** Options for {@link TimeEntryApprovalService.create}. */\nexport interface TimeEntryApprovalServiceOptions extends SmrtObjectOptions {\n /** Share an existing case facade (otherwise one is created internally). */\n caseService?: SupportCaseService;\n}\n\n/**\n * The plan terms governing one entry's approval and pricing, resolved from\n * the case's frozen `planSnapshot` first, then the live plan row, else\n * zero-rate defaults (operator approval, nothing charged).\n */\nexport interface ResolvedPlanTerms {\n policy: TimeApprovalPolicy;\n currency: string;\n includedMinutes: number;\n overageHourlyRate: number;\n onCallHourlyRate: number;\n planId: string | null;\n planKey: string;\n}\n\n/** Input for {@link TimeEntryApprovalService.approve}. */\nexport interface ApproveTimeEntryInput {\n /** Acting principal; optional only for the `automatic` path. */\n principal?: SupportPrincipal;\n note?: string;\n /** Approval instant (defaults to now); also the derivation timestamp. */\n at?: Date;\n}\n\n/** Result of {@link TimeEntryApprovalService.approve}. */\nexport interface ApproveTimeEntryResult {\n entry: ServiceTimeEntry;\n charge: SupportCharge;\n compensation: SupportCompensation;\n path: TimeEntryApprovalPath;\n}\n\n/** Input for {@link TimeEntryApprovalService.reject}. */\nexport interface RejectTimeEntryInput {\n principal: SupportPrincipal;\n reason: string;\n}\n\n/** Input for {@link TimeEntryApprovalService.correct}. */\nexport interface CorrectTimeEntryInput {\n principal: SupportPrincipal;\n patch: Partial<{\n durationSeconds: number;\n startedAt: Date;\n endedAt: Date;\n description: string;\n evidence: TimeEntryEvidence[];\n }>;\n note?: string;\n}\n\n/** Result of {@link TimeEntryApprovalService.correct}. */\nexport interface CorrectTimeEntryResult {\n /** The superseded entry, now `corrected` with its snapshot intact. */\n original: ServiceTimeEntry;\n /** The new `draft` entry carrying the patch (flows submit → approve). */\n correction: ServiceTimeEntry;\n}\n\n/** Charge math derived from the plan terms before any row is written. */\ninterface ChargeDerivation {\n billableSeconds: number;\n includedSecondsBefore: number;\n includedSecondsApplied: number;\n hourlyRate: number;\n rateSource: 'overage' | 'on_call' | 'none';\n amount: number;\n}\n\n/** Round a currency amount to 2 decimals. */\nfunction roundMoney(value: number): number {\n return Math.round((value + Number.EPSILON) * 100) / 100;\n}\n\n/**\n * The approval gate. Construct with {@link TimeEntryApprovalService.create}.\n */\nexport class TimeEntryApprovalService {\n readonly entries: ServiceTimeEntryCollection;\n readonly charges: SupportChargeCollection;\n readonly compensations: SupportCompensationCollection;\n readonly compensationPlans: SupportCompensationPlanCollection;\n readonly caseService: SupportCaseService;\n private readonly approvalMutex = new KeyedMutex();\n\n protected constructor(collections: {\n entries: ServiceTimeEntryCollection;\n charges: SupportChargeCollection;\n compensations: SupportCompensationCollection;\n compensationPlans: SupportCompensationPlanCollection;\n caseService: SupportCaseService;\n }) {\n this.entries = collections.entries;\n this.charges = collections.charges;\n this.compensations = collections.compensations;\n this.compensationPlans = collections.compensationPlans;\n this.caseService = collections.caseService;\n }\n\n static async create(\n options: TimeEntryApprovalServiceOptions,\n ): Promise<TimeEntryApprovalService> {\n const caseService =\n options.caseService ?? (await SupportCaseService.create(options));\n const [entries, charges, compensations, compensationPlans] =\n await Promise.all([\n ServiceTimeEntryCollection.create(options),\n SupportChargeCollection.create(options),\n SupportCompensationCollection.create(options),\n SupportCompensationPlanCollection.create(options),\n ]);\n return new TimeEntryApprovalService({\n entries,\n charges,\n compensations,\n compensationPlans,\n caseService,\n });\n }\n\n /** Load an entry or throw a descriptive error. */\n async getEntry(\n entryRef: ServiceTimeEntry | string,\n ): Promise<ServiceTimeEntry> {\n if (typeof entryRef !== 'string') {\n return entryRef;\n }\n const found = await this.entries.get({ id: entryRef });\n if (!found) {\n throw new Error(`ServiceTimeEntry not found: ${entryRef}`);\n }\n return found;\n }\n\n /**\n * Resolve the plan terms governing an entry: the case's frozen\n * `planSnapshot` wins field-by-field (history never rewritten by plan\n * edits), the live plan row fills gaps, and with no case or plan at all the\n * terms fall back to zero rates under operator approval.\n */\n async resolvePlanTerms(\n entry: ServiceTimeEntry,\n supportCase?: SupportCase | null,\n ): Promise<ResolvedPlanTerms> {\n const zeroRate: ResolvedPlanTerms = {\n policy: { mode: 'operator' },\n currency: 'USD',\n includedMinutes: 0,\n overageHourlyRate: 0,\n onCallHourlyRate: 0,\n planId: null,\n planKey: '',\n };\n if (!entry.caseId) {\n return zeroRate;\n }\n const owningCase =\n supportCase ?? (await this.caseService.getCase(entry.caseId));\n const snapshot = owningCase.getPlanSnapshot();\n const plan: SupportPlan | null = owningCase.planId\n ? await this.caseService.plans.get({ id: owningCase.planId })\n : null;\n if (Object.keys(snapshot).length === 0 && !plan) {\n return zeroRate;\n }\n\n const num = (snapshotValue: unknown, planValue: number | undefined) =>\n typeof snapshotValue === 'number' ? snapshotValue : (planValue ?? 0);\n const policySource =\n snapshot.timeApprovalPolicy &&\n typeof snapshot.timeApprovalPolicy === 'object'\n ? (snapshot.timeApprovalPolicy as Partial<TimeApprovalPolicy>)\n : plan?.getTimeApprovalPolicy();\n\n return {\n policy: { mode: 'operator', ...(policySource ?? {}) },\n currency:\n typeof snapshot.currency === 'string' && snapshot.currency\n ? snapshot.currency\n : (plan?.currency ?? 'USD'),\n includedMinutes: num(snapshot.includedMinutes, plan?.includedMinutes),\n overageHourlyRate: num(\n snapshot.overageHourlyRate,\n plan?.overageHourlyRate,\n ),\n onCallHourlyRate: num(snapshot.onCallHourlyRate, plan?.onCallHourlyRate),\n planId:\n typeof snapshot.planId === 'string' && snapshot.planId\n ? snapshot.planId\n : (plan?.id ?? null),\n planKey:\n typeof snapshot.planKey === 'string'\n ? snapshot.planKey\n : (plan?.planKey ?? ''),\n };\n }\n\n /**\n * Approve a `submitted` entry: gate the acting principal by the plan's\n * approval policy, freeze the entry, and derive its charge and compensation\n * snapshots. Throws {@link TimeEntryApprovalDeniedError} when the gate\n * refuses.\n */\n async approve(\n entryRef: ServiceTimeEntry | string,\n input: ApproveTimeEntryInput = {},\n ): Promise<ApproveTimeEntryResult> {\n const entry = await this.getEntry(entryRef);\n // Serialize per work context: concurrent approvals against the same case\n // must not each read the same remaining included allowance and both\n // consume it (over-granting included time / under-billing overage).\n const lockKey = entry.caseId ?? `entry:${entry.id ?? ''}`;\n return this.approvalMutex.run(lockKey, () =>\n this.approveExclusive(entry, input),\n );\n }\n\n private async approveExclusive(\n entry: ServiceTimeEntry,\n input: ApproveTimeEntryInput,\n ): Promise<ApproveTimeEntryResult> {\n if (entry.status !== 'submitted') {\n throw new Error(\n `ServiceTimeEntry ${entry.id}: only 'submitted' entries can be approved (status is '${entry.status}').`,\n );\n }\n const at = input.at ?? new Date();\n const principal = input.principal;\n this.assertTenantMatch(entry, principal);\n\n const supportCase = entry.caseId\n ? await this.caseService.getCase(entry.caseId)\n : null;\n const terms = await this.resolvePlanTerms(entry, supportCase);\n const derivation = await this.deriveCharge(entry, terms);\n const path = this.determinePath(\n entry,\n terms.policy,\n derivation.amount,\n principal,\n supportCase,\n );\n\n // Settlement rows are written BEFORE the entry flips to 'approved' so a\n // transient failure leaves the entry 'submitted' and the whole approval\n // retryable (codex P1, PR #1943): both writes are upserts keyed on the\n // entry (one charge/compensation per entry), the consumption query above\n // excludes the entry's own row, and the status flip lands last.\n const charge = await this.upsertChargeRow(entry, {\n tenantId: entry.tenantId,\n timeEntryId: entry.id ?? '',\n caseId: entry.caseId,\n planId: terms.planId,\n amount: derivation.amount,\n currency: terms.currency,\n billableSeconds: derivation.billableSeconds,\n includedSecondsApplied: derivation.includedSecondsApplied,\n rateSnapshot: JSON.stringify({\n hourlyRate: derivation.hourlyRate,\n rateSource: derivation.rateSource,\n planId: terms.planId,\n planKey: terms.planKey,\n includedMinutes: terms.includedMinutes,\n includedSecondsBefore: derivation.includedSecondsBefore,\n includedSecondsApplied: derivation.includedSecondsApplied,\n derivedAt: at.toISOString(),\n scope: 'per-case (period accounting arrives with usage pricing #1925)',\n }),\n status: 'final',\n finalizedAt: at,\n });\n\n const compensation = await this.deriveCompensation(entry, at);\n\n // Set every approval field BEFORE the save — the model's freeze engages\n // the moment the row persists as 'approved'.\n entry.approvedAt = at;\n entry.approvedByProfileId = principal?.id ?? null;\n entry.approvalPath = path;\n entry.status = 'approved';\n await entry.save();\n\n if (supportCase) {\n // Case-visible audit carries the client-side amount ONLY — provider\n // compensation never leaks into a case event; it is queryable through\n // its own read model.\n await this.caseService.recordEvent(supportCase, 'time_recorded', {\n actorKind:\n path === 'client' ? 'client' : principal ? 'specialist' : 'system',\n actorProfileId: principal?.id ?? null,\n summary: `Time entry approved (${path})`,\n payload: {\n approved: true,\n path,\n timeEntryId: entry.id ?? '',\n chargeAmount: charge.amount,\n },\n occurredAt: at,\n });\n }\n\n return { entry, charge, compensation, path };\n }\n\n /**\n * Reject a `submitted` entry with a reason (requires the\n * `support.approve-time-entry` split).\n */\n async reject(\n entryRef: ServiceTimeEntry | string,\n input: RejectTimeEntryInput,\n ): Promise<ServiceTimeEntry> {\n const entry = await this.getEntry(entryRef);\n if (entry.status !== 'submitted') {\n throw new Error(\n `ServiceTimeEntry ${entry.id}: only 'submitted' entries can be rejected (status is '${entry.status}').`,\n );\n }\n if (!input.reason?.trim()) {\n throw new Error('ServiceTimeEntry: a rejection reason is required.');\n }\n this.assertTenantMatch(entry, input.principal);\n this.assertOperator(input.principal, 'rejecting a time entry');\n\n entry.rejectedAt = new Date();\n entry.rejectedByProfileId = input.principal.id ?? null;\n entry.rejectionReason = input.reason;\n entry.status = 'rejected';\n await entry.save();\n return entry;\n }\n\n /**\n * Correct an `approved` entry (requires the `support.approve-time-entry`\n * split): a NEW `draft` entry copies the original's context, participant,\n * and source with the patch applied and links back via `correctionOfId`;\n * the original flips to `corrected` with its frozen fields and snapshots\n * untouched, and its charge/compensation rows are marked `corrected`. The\n * correction then flows submit → approve normally, deriving fresh\n * snapshots.\n */\n async correct(\n entryRef: ServiceTimeEntry | string,\n input: CorrectTimeEntryInput,\n ): Promise<CorrectTimeEntryResult> {\n const original = await this.getEntry(entryRef);\n if (original.status !== 'approved') {\n throw new Error(\n `ServiceTimeEntry ${original.id}: only 'approved' entries can be corrected (status is '${original.status}').`,\n );\n }\n this.assertTenantMatch(original, input.principal);\n this.assertOperator(input.principal, 'correcting an approved time entry');\n\n const patch = input.patch ?? {};\n const startedAt =\n patch.startedAt !== undefined ? patch.startedAt : original.startedAt;\n const endedAt =\n patch.endedAt !== undefined ? patch.endedAt : original.endedAt;\n const durationSeconds = patch.durationSeconds ?? original.durationSeconds;\n if (!(durationSeconds > 0)) {\n throw new Error(\n `ServiceTimeEntry: correction durationSeconds must be greater than zero (got ${durationSeconds}).`,\n );\n }\n if (startedAt && endedAt && startedAt.getTime() >= endedAt.getTime()) {\n throw new Error(\n 'ServiceTimeEntry: correction startedAt must be before endedAt.',\n );\n }\n\n const correction = await this.entries.create({\n tenantId: original.tenantId,\n caseId: original.caseId,\n workRefType: original.workRefType,\n workRefId: original.workRefId,\n specialistId: original.specialistId,\n participantKind: original.participantKind,\n participantProfileId: original.participantProfileId,\n agentRef: original.agentRef,\n source: original.source,\n description: patch.description ?? original.description,\n startedAt,\n endedAt,\n durationSeconds,\n evidence:\n patch.evidence !== undefined\n ? JSON.stringify(patch.evidence)\n : original.evidence,\n status: 'draft',\n correctionOfId: original.id,\n metadata: JSON.stringify({\n ...original.getMetadata(),\n correctionNote: input.note ?? null,\n }),\n });\n\n // Only the status flips — the frozen work-defining fields stay intact\n // (the model allows exactly this approved → corrected exit).\n original.status = 'corrected';\n await original.save();\n\n const originalId = original.id ?? '';\n const [charge, compensation] = await Promise.all([\n this.charges.forTimeEntry(originalId),\n this.compensations.forTimeEntry(originalId),\n ]);\n if (charge) {\n charge.status = 'corrected';\n await charge.save();\n }\n if (compensation) {\n compensation.status = 'corrected';\n await compensation.save();\n }\n\n return { original, correction };\n }\n\n /**\n * Determine the approval path for an entry under a policy, or throw\n * {@link TimeEntryApprovalDeniedError} when the acting principal does not\n * satisfy it.\n */\n protected determinePath(\n entry: ServiceTimeEntry,\n policy: TimeApprovalPolicy,\n chargeAmount: number,\n principal: SupportPrincipal | undefined,\n supportCase: SupportCase | null,\n ): TimeEntryApprovalPath {\n const isOperator = principal?.can(APPROVE_TIME_ENTRY_PERMISSION) ?? false;\n\n if (policy.mode === 'automatic') {\n const minutes = entry.durationSeconds / 60;\n const overMinutes =\n policy.thresholdMinutes !== undefined &&\n minutes > policy.thresholdMinutes;\n const overAmount =\n policy.thresholdAmount !== undefined &&\n chargeAmount > policy.thresholdAmount;\n if (!overMinutes && !overAmount) {\n return 'automatic';\n }\n if (!isOperator) {\n throw new TimeEntryApprovalDeniedError(\n `the entry exceeds the automatic-approval ${overMinutes ? 'duration' : 'amount'} threshold and requires an operator holding '${APPROVE_TIME_ENTRY_PERMISSION}'.`,\n );\n }\n return 'threshold';\n }\n\n if (policy.mode === 'client') {\n if (!principal) {\n throw new TimeEntryApprovalDeniedError(\n 'client approval requires an acting principal.',\n );\n }\n const clientProfileId = supportCase?.clientProfileId ?? null;\n if (clientProfileId && principal.id === clientProfileId) {\n return 'client';\n }\n if (isOperator) {\n return 'client';\n }\n throw new TimeEntryApprovalDeniedError(\n `client approval requires the case's Client${clientProfileId ? ` (${clientProfileId})` : ''} or an operator holding '${APPROVE_TIME_ENTRY_PERMISSION}'.`,\n );\n }\n\n // 'operator' — the default mode, including zero-rate fallback terms.\n if (!isOperator) {\n throw new TimeEntryApprovalDeniedError(\n `operator approval requires '${APPROVE_TIME_ENTRY_PERMISSION}'.`,\n );\n }\n return 'operator';\n }\n\n /**\n * Charge math from the Managed Support Plan terms: remaining included time\n * (per case — period windows are #1925's scope) absorbs first, the\n * remainder meters at the overage (or on-call) hourly rate.\n */\n protected async deriveCharge(\n entry: ServiceTimeEntry,\n terms: ResolvedPlanTerms,\n ): Promise<ChargeDerivation> {\n const billableSeconds = entry.durationSeconds;\n let includedSecondsBefore = 0;\n if (entry.caseId && terms.includedMinutes > 0) {\n const existing = await this.charges.forCase(entry.caseId);\n // Only live charges consume included time: `corrected`/`voided` rows\n // release their allowance back to the case (codex P1, PR #1943), and\n // the entry's own row is excluded so an approval retry after a partial\n // failure never double-counts itself.\n const consumed = existing\n .filter(\n (charge) =>\n (charge.status === 'final' || charge.status === 'pending') &&\n charge.timeEntryId !== entry.id,\n )\n .reduce((sum, charge) => sum + (charge.includedSecondsApplied ?? 0), 0);\n includedSecondsBefore = Math.max(\n 0,\n terms.includedMinutes * 60 - consumed,\n );\n }\n const includedSecondsApplied = Math.min(\n includedSecondsBefore,\n billableSeconds,\n );\n const onCall = Boolean(entry.getMetadata().onCall);\n const hourlyRate = onCall\n ? terms.onCallHourlyRate || terms.overageHourlyRate\n : terms.overageHourlyRate;\n const amount = roundMoney(\n ((billableSeconds - includedSecondsApplied) / 3600) * hourlyRate,\n );\n return {\n billableSeconds,\n includedSecondsBefore,\n includedSecondsApplied,\n hourlyRate,\n rateSource: onCall ? 'on_call' : hourlyRate > 0 ? 'overage' : 'none',\n amount,\n };\n }\n\n /**\n * Provider earning from the Support Compensation Plan effective at the work\n * instant (`endedAt`, falling back to the approval instant). No specialist\n * or no plan resolves to a zero-amount row so the entry still settles.\n */\n protected async deriveCompensation(\n entry: ServiceTimeEntry,\n at: Date,\n ): Promise<SupportCompensation> {\n const plan: SupportCompensationPlan | null = entry.specialistId\n ? await this.compensationPlans.resolveForSpecialist(\n entry.specialistId,\n entry.endedAt ?? at,\n // Scope defaults to the entry's tenant — approval may run without\n // an ambient tenant context, and another tenant's default plan\n // must never price this tenant's work.\n { tenantId: entry.tenantId ?? null },\n )\n : null;\n const payableSeconds = entry.durationSeconds;\n\n if (!plan) {\n return this.upsertCompensationRow(entry, {\n tenantId: entry.tenantId,\n timeEntryId: entry.id ?? '',\n specialistId: entry.specialistId,\n compensationPlanId: null,\n amount: 0,\n currency: 'USD',\n payableSeconds,\n rateSnapshot: JSON.stringify({\n rateSource: 'none',\n derivedAt: at.toISOString(),\n }),\n status: 'final',\n finalizedAt: at,\n });\n }\n\n const amount = roundMoney((payableSeconds / 3600) * plan.hourlyRate);\n return this.upsertCompensationRow(entry, {\n tenantId: entry.tenantId,\n timeEntryId: entry.id ?? '',\n specialistId: entry.specialistId,\n compensationPlanId: plan.id,\n amount,\n currency: plan.currency,\n payableSeconds,\n rateSnapshot: JSON.stringify({\n hourlyRate: plan.hourlyRate,\n currency: plan.currency,\n planId: plan.id,\n effectiveFrom: plan.effectiveFrom?.toISOString() ?? null,\n effectiveTo: plan.effectiveTo?.toISOString() ?? null,\n derivedAt: at.toISOString(),\n }),\n status: 'final',\n finalizedAt: at,\n });\n }\n\n /**\n * Idempotent write of the entry's single charge row: an approval retry\n * after a partial failure refreshes the existing row instead of colliding\n * on the `time_entry_id` unique key.\n */\n protected async upsertChargeRow(\n entry: ServiceTimeEntry,\n fields: Record<string, unknown>,\n ): Promise<SupportCharge> {\n const existing = entry.id\n ? await this.charges.forTimeEntry(entry.id)\n : null;\n if (!existing) {\n return this.charges.create(fields);\n }\n Object.assign(existing, fields);\n await existing.save();\n return existing;\n }\n\n /** Idempotent write of the entry's single compensation row (see above). */\n protected async upsertCompensationRow(\n entry: ServiceTimeEntry,\n fields: Record<string, unknown>,\n ): Promise<SupportCompensation> {\n const existing = entry.id\n ? await this.compensations.forTimeEntry(entry.id)\n : null;\n if (!existing) {\n return this.compensations.create(fields);\n }\n Object.assign(existing, fields);\n await existing.save();\n return existing;\n }\n\n /** Cross-tenant acts are refused when both sides carry a tenant. */\n protected assertTenantMatch(\n entry: ServiceTimeEntry,\n principal: SupportPrincipal | undefined,\n ): void {\n if (!principal?.tenantId || !entry.tenantId) {\n return;\n }\n if (principal.tenantId !== entry.tenantId) {\n throw new TimeEntryApprovalDeniedError(\n `principal tenant '${principal.tenantId}' does not match the entry's tenant '${entry.tenantId}'.`,\n );\n }\n }\n\n /** Require the operator permission split for a privileged act. */\n protected assertOperator(\n principal: SupportPrincipal | undefined,\n act: string,\n ): void {\n if (!principal?.can(APPROVE_TIME_ENTRY_PERMISSION)) {\n throw new TimeEntryApprovalDeniedError(\n `${act} requires '${APPROVE_TIME_ENTRY_PERMISSION}'.`,\n );\n }\n }\n}\n\nexport default TimeEntryApprovalService;\n","/**\n * @happyvertical/smrt-support\n *\n * AI-first Support Cases for the SMRT framework (epic #1934): channel-neutral\n * Support Cases over chat and email transports, the standard case lifecycle\n * with reopen history, immediate AI assistance with lossless Human Handoff,\n * Project-qualified routing, Service Targets with timed escalation, and\n * auditable Service Time Entries with separated client charges and provider\n * compensation.\n *\n * Boundary: Support owns tickets, interactions, routing, and service targets.\n * Delivery Operations owns repository work — a Delivery Handoff links a case\n * to a Development Work Item without driving its execution.\n *\n * @packageDocumentation\n */\n\n// Self-register this package's manifest before any @smrt() decorator fires\n// downstream. Must come first so the side effect runs ahead of the class\n// module loads below (issue #1132 context in __smrt-register__.ts).\nimport './__smrt-register__.js';\n\nimport { ensureSupportPermissionsRegistered } from './permissions.js';\n\nexport * from './models/index.js';\nexport {\n APPROVE_TIME_ENTRY_PERMISSION,\n ensureSupportPermissionsRegistered,\n MANAGE_PLANS_PERMISSION,\n REASSIGN_CASE_PERMISSION,\n registerSupportPermissions,\n SUPPORT_PERMISSION_DEFS,\n type SupportPrincipal,\n supportPrincipalFromPermissions,\n} from './permissions.js';\nexport * from './services/index.js';\nexport * from './types.js';\n\n// Contribute the support permission slugs to the runtime catalog on import,\n// so any consumer of this package sees the gates' slugs.\nensureSupportPermissionsRegistered();\n"],"mappings":"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"}