@nexalink/carecompass-sdk 0.4.0-beta.15 → 0.6.0

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.
package/dist/rcm.d.ts ADDED
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import { type CareCompassCore, type CareCompassCoreOptions } from "./core.js";
3
+ import type { CareCompassAuditListener, CareCompassSafetyListener } from "./compliance.js";
4
+ import type { CareCompassConfig } from "./schema.js";
5
+ /** A narrow, structured input for coding review. Do not include direct patient identifiers. */
6
+ export type RcmCodingAssistanceInput = {
7
+ encounterId: string;
8
+ diagnosisNarrative: string;
9
+ procedureNarrative?: string;
10
+ serviceDate?: string;
11
+ placeOfService?: string;
12
+ claimContext?: {
13
+ /** Required in development and staging; when supplied, it must match the authenticated SDK environment. */
14
+ stage?: CareCompassConfig["environment"];
15
+ payerName?: string;
16
+ submittedCodes?: string[];
17
+ };
18
+ };
19
+ export type RcmCodeSystem = "ICD-10-CM" | "CPT" | "HCPCS";
20
+ export type RcmConfidence = "low" | "medium" | "high";
21
+ export type RcmReviewDisposition = "review_required" | "abstain" | "escalate";
22
+ export type RcmPolicyReference = {
23
+ corpusId: string;
24
+ id: string;
25
+ title: string;
26
+ excerpt: string;
27
+ version?: string;
28
+ };
29
+ export type RcmCodeRecommendation = {
30
+ code: string;
31
+ codeSystem: RcmCodeSystem;
32
+ confidence: RcmConfidence;
33
+ rationale: string;
34
+ policyReferenceIds: string[];
35
+ };
36
+ export type RcmCodingAssistanceResult = {
37
+ disposition: RcmReviewDisposition;
38
+ humanReviewRequired: true;
39
+ recommendations: RcmCodeRecommendation[];
40
+ documentationGaps: string[];
41
+ policyReferences: RcmPolicyReference[];
42
+ rationale: string;
43
+ /** A host-facing, measurable pilot hypothesis—not a claim of realized savings. */
44
+ operationalHypothesis: {
45
+ metric: "manual_research_minutes_per_case";
46
+ target: "Measure whether assisted review reduces manual research time by 25% or more.";
47
+ };
48
+ processingTimeMs: number;
49
+ };
50
+ export type RcmCustomerRules = {
51
+ allowedCodeSystems?: RcmCodeSystem[];
52
+ escalateOnLowConfidence?: boolean;
53
+ requirePolicySupport?: boolean;
54
+ };
55
+ export type RcmPolicyRetriever = {
56
+ /** Corpus IDs are allowlisted by the embed owner; retrieval stays in the host's trusted boundary. */
57
+ approvedCorpusIds: readonly string[];
58
+ search: (request: {
59
+ input: RcmCodingAssistanceInput;
60
+ query: string;
61
+ }) => Promise<RcmPolicyReference[]>;
62
+ };
63
+ export type RcmCodingWorkflowOptions = {
64
+ core?: CareCompassCore;
65
+ coreOptions?: CareCompassCoreOptions;
66
+ customerRules?: RcmCustomerRules;
67
+ now?: () => number;
68
+ onResult?: (result: RcmCodingAssistanceResult) => void;
69
+ /**
70
+ * `hosted` delegates retrieval to the tenant-authorized CareCompass runtime.
71
+ * The browser never supplies policy text or corpus identifiers in that mode.
72
+ */
73
+ retrieval?: RcmPolicyRetriever | "hosted";
74
+ };
75
+ export type RcmCodingWorkflow = {
76
+ core: CareCompassCore;
77
+ review: (input: RcmCodingAssistanceInput) => Promise<RcmCodingAssistanceResult>;
78
+ };
79
+ /**
80
+ * Creates a narrow RCM coding-review workflow on the shared CareCompass core.
81
+ * Retrieval can run in the CareCompass tenant boundary or be injected by a
82
+ * customer that maintains its own approved, server-side retrieval boundary.
83
+ */
84
+ export declare function createRcmCodingWorkflow(config: CareCompassConfig, options?: RcmCodingWorkflowOptions): RcmCodingWorkflow;
85
+ export declare const CARECOMPASS_RCM_SYNTHETIC_DEMO: RcmCodingAssistanceInput;
86
+ export type RcmCodingAssistantProps = {
87
+ config: CareCompassConfig;
88
+ initialInput?: RcmCodingAssistanceInput;
89
+ onAuditEvent?: CareCompassAuditListener;
90
+ onResult?: (result: RcmCodingAssistanceResult) => void;
91
+ onSafetyEvent?: CareCompassSafetyListener;
92
+ workflowOptions?: Omit<RcmCodingWorkflowOptions, "core" | "coreOptions" | "onResult">;
93
+ };
94
+ /** A small branded form suitable for embedding the pilot workflow in a host app. */
95
+ export declare function RcmCodingAssistant({ config, initialInput, onAuditEvent, onResult, onSafetyEvent, workflowOptions, }: RcmCodingAssistantProps): React.JSX.Element;
package/dist/rcm.js ADDED
@@ -0,0 +1,288 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { createCareCompassCore, } from "./core.js";
4
+ const OPERATIONAL_HYPOTHESIS = {
5
+ metric: "manual_research_minutes_per_case",
6
+ target: "Measure whether assisted review reduces manual research time by 25% or more.",
7
+ };
8
+ const CODE_SYSTEMS = ["ICD-10-CM", "CPT", "HCPCS"];
9
+ const CONFIDENCES = ["low", "medium", "high"];
10
+ const DISPOSITIONS = [
11
+ "review_required",
12
+ "abstain",
13
+ "escalate",
14
+ ];
15
+ const asRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
16
+ ? value
17
+ : null;
18
+ const stringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string")
19
+ ? value
20
+ : null;
21
+ const extractJson = (value) => {
22
+ const trimmed = value.trim();
23
+ const json = trimmed.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
24
+ return JSON.parse(json);
25
+ };
26
+ const isOneOf = (value, options) => typeof value === "string" && options.includes(value);
27
+ const parseResult = (value) => {
28
+ try {
29
+ const body = asRecord(extractJson(value));
30
+ if (!body || body.humanReviewRequired !== true)
31
+ return null;
32
+ if (!isOneOf(body.disposition, DISPOSITIONS))
33
+ return null;
34
+ if (typeof body.rationale !== "string")
35
+ return null;
36
+ const documentationGaps = stringArray(body.documentationGaps);
37
+ if (!documentationGaps || !Array.isArray(body.recommendations))
38
+ return null;
39
+ const recommendations = body.recommendations.map((candidate) => {
40
+ const entry = asRecord(candidate);
41
+ if (!entry ||
42
+ typeof entry.code !== "string" ||
43
+ !/^[A-Za-z0-9.]{2,12}$/.test(entry.code) ||
44
+ !isOneOf(entry.codeSystem, CODE_SYSTEMS) ||
45
+ !isOneOf(entry.confidence, CONFIDENCES) ||
46
+ typeof entry.rationale !== "string") {
47
+ return null;
48
+ }
49
+ const policyReferenceIds = stringArray(entry.policyReferenceIds);
50
+ const evidenceIds = stringArray(entry.evidenceIds);
51
+ return policyReferenceIds
52
+ ? {
53
+ code: entry.code,
54
+ codeSystem: entry.codeSystem,
55
+ confidence: entry.confidence,
56
+ rationale: entry.rationale,
57
+ policyReferenceIds,
58
+ }
59
+ : evidenceIds
60
+ ? {
61
+ code: entry.code,
62
+ codeSystem: entry.codeSystem,
63
+ confidence: entry.confidence,
64
+ rationale: entry.rationale,
65
+ policyReferenceIds: evidenceIds,
66
+ }
67
+ : null;
68
+ });
69
+ if (recommendations.some((candidate) => candidate === null))
70
+ return null;
71
+ if ((body.disposition === "review_required" &&
72
+ recommendations.length === 0) ||
73
+ (body.disposition !== "review_required" && recommendations.length > 0)) {
74
+ return null;
75
+ }
76
+ return {
77
+ disposition: body.disposition,
78
+ humanReviewRequired: true,
79
+ recommendations: recommendations,
80
+ documentationGaps,
81
+ rationale: body.rationale,
82
+ };
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ };
88
+ const promptFor = (input, policyReferences, rules) => [
89
+ "You are a revenue-cycle coding-assistance workflow.",
90
+ "Return only JSON. This is a suggestion for a qualified human coder, never a final billing, coding, or clinical decision.",
91
+ 'Use this shape: {"disposition":"review_required|abstain|escalate","humanReviewRequired":true,"recommendations":[{"code":"string","codeSystem":"ICD-10-CM|CPT|HCPCS","confidence":"low|medium|high","rationale":"string","evidenceIds":["reference-1"]}],"documentationGaps":["string"],"rationale":"string"}.',
92
+ "If documentation or policy support is insufficient, abstain or escalate with an empty recommendations array.",
93
+ `Customer rules: ${JSON.stringify(rules)}.`,
94
+ `Encounter context: ${JSON.stringify(input)}.`,
95
+ `Approved supporting references: ${JSON.stringify(policyReferences.map((reference, index) => ({
96
+ referenceId: `reference-${index + 1}`,
97
+ title: reference.title,
98
+ excerpt: reference.excerpt,
99
+ version: reference.version,
100
+ })))}.`,
101
+ ].join("\n");
102
+ const fallback = (reason, policyReferences, processingTimeMs) => ({
103
+ disposition: "escalate",
104
+ humanReviewRequired: true,
105
+ recommendations: [],
106
+ documentationGaps: [],
107
+ policyReferences,
108
+ rationale: reason,
109
+ operationalHypothesis: OPERATIONAL_HYPOTHESIS,
110
+ processingTimeMs,
111
+ });
112
+ const enforceRules = (result, policyReferences, rules, processingTimeMs) => {
113
+ if (!result) {
114
+ return fallback("The response was not a valid review recommendation. Route this encounter to a qualified coder.", policyReferences, processingTimeMs);
115
+ }
116
+ const referenceIds = new Set(policyReferences.map((reference) => reference.id));
117
+ const missingRequiredPolicySupport = result.disposition === "review_required" &&
118
+ rules.requirePolicySupport === true &&
119
+ (policyReferences.length === 0 ||
120
+ result.recommendations.some((recommendation) => recommendation.policyReferenceIds.length === 0 ||
121
+ recommendation.policyReferenceIds.some((id) => !referenceIds.has(id))));
122
+ const violatesRules = missingRequiredPolicySupport ||
123
+ result.recommendations.some((recommendation) => (rules.allowedCodeSystems !== undefined &&
124
+ !rules.allowedCodeSystems.includes(recommendation.codeSystem)) ||
125
+ (rules.escalateOnLowConfidence === true &&
126
+ recommendation.confidence === "low"));
127
+ if (violatesRules) {
128
+ return fallback("Customer review rules require escalation before any coding recommendation can be used.", policyReferences, processingTimeMs);
129
+ }
130
+ return {
131
+ ...result,
132
+ policyReferences,
133
+ operationalHypothesis: OPERATIONAL_HYPOTHESIS,
134
+ processingTimeMs,
135
+ };
136
+ };
137
+ /**
138
+ * Creates a narrow RCM coding-review workflow on the shared CareCompass core.
139
+ * Retrieval can run in the CareCompass tenant boundary or be injected by a
140
+ * customer that maintains its own approved, server-side retrieval boundary.
141
+ */
142
+ export function createRcmCodingWorkflow(config, options = {}) {
143
+ if (options.core && options.coreOptions) {
144
+ throw new Error("Provide either an existing core or coreOptions, not both.");
145
+ }
146
+ const core = options.core ?? createCareCompassCore(config, options.coreOptions);
147
+ const now = options.now ?? Date.now;
148
+ const rules = options.customerRules ?? {};
149
+ const hostedRetrieval = options.retrieval === "hosted";
150
+ return {
151
+ core,
152
+ review: async (input) => {
153
+ const startedAt = now();
154
+ const processingTime = () => Math.max(0, now() - startedAt);
155
+ let policyReferences = [];
156
+ try {
157
+ if (options.retrieval && options.retrieval !== "hosted") {
158
+ policyReferences = await options.retrieval.search({
159
+ input,
160
+ query: [input.diagnosisNarrative, input.procedureNarrative]
161
+ .filter(Boolean)
162
+ .join(" "),
163
+ });
164
+ const approved = new Set(options.retrieval.approvedCorpusIds);
165
+ if (policyReferences.some((reference) => !approved.has(reference.corpusId))) {
166
+ throw new Error("Retrieval returned a reference outside the approved corpus.");
167
+ }
168
+ core.recordToolCall("completed");
169
+ }
170
+ }
171
+ catch {
172
+ core.recordToolCall("failed");
173
+ core.recordSafetyDecision("escalated", "tool_call");
174
+ const result = fallback("Approved policy retrieval was unavailable or returned an unapproved source. Route this encounter to a qualified coder.", policyReferences, processingTime());
175
+ options.onResult?.(result);
176
+ return result;
177
+ }
178
+ if (rules.requirePolicySupport &&
179
+ !hostedRetrieval &&
180
+ policyReferences.length === 0) {
181
+ core.recordSafetyDecision("escalated", "tool_call");
182
+ const result = fallback("No approved policy support was retrieved. Route this encounter to a qualified coder.", policyReferences, processingTime());
183
+ options.onResult?.(result);
184
+ return result;
185
+ }
186
+ core.recordWidgetAction("recorded");
187
+ try {
188
+ await core.sendMessage(promptFor(input, policyReferences, rules), {
189
+ workflow: "rcm-coding-assistance",
190
+ context: {
191
+ rcmCodingInput: input,
192
+ rcmSupportingReferences: policyReferences.map((reference, index) => ({
193
+ referenceId: `reference-${index + 1}`,
194
+ title: reference.title,
195
+ excerpt: reference.excerpt,
196
+ version: reference.version,
197
+ })),
198
+ },
199
+ });
200
+ }
201
+ catch {
202
+ core.recordSafetyDecision("escalated", "widget_action");
203
+ const result = fallback("The coding-assistance request could not be completed safely. Route this encounter to a qualified coder.", policyReferences, processingTime());
204
+ options.onResult?.(result);
205
+ return result;
206
+ }
207
+ const response = core
208
+ .getSnapshot()
209
+ .messages.slice()
210
+ .reverse()
211
+ .find((message) => message.role === "assistant" && message.id !== "welcome");
212
+ if (hostedRetrieval && response?.retrieval) {
213
+ policyReferences = response.retrieval.references.map((reference) => ({
214
+ corpusId: response.retrieval?.corpusId ?? "",
215
+ id: reference.referenceId,
216
+ title: reference.title,
217
+ excerpt: reference.excerpt,
218
+ version: reference.version,
219
+ }));
220
+ }
221
+ const parsed = response ? parseResult(response.text) : null;
222
+ const evidenceIds = new Map(policyReferences.map((reference, index) => [
223
+ `reference-${index + 1}`,
224
+ reference.id,
225
+ ]));
226
+ const result = enforceRules(parsed && {
227
+ ...parsed,
228
+ recommendations: parsed.recommendations.map((recommendation) => ({
229
+ ...recommendation,
230
+ policyReferenceIds: recommendation.policyReferenceIds.map((id) => evidenceIds.get(id) ?? id),
231
+ })),
232
+ }, policyReferences, hostedRetrieval ? { ...rules, requirePolicySupport: true } : rules, processingTime());
233
+ if (result.disposition === "escalate" ||
234
+ result.disposition === "abstain") {
235
+ core.recordSafetyDecision("escalated", "widget_action");
236
+ }
237
+ options.onResult?.(result);
238
+ return result;
239
+ },
240
+ };
241
+ }
242
+ export const CARECOMPASS_RCM_SYNTHETIC_DEMO = {
243
+ encounterId: "DEMO-ENC-1042",
244
+ diagnosisNarrative: "Synthetic Medicare hospital observation encounter with same-day admission and discharge.",
245
+ procedureNarrative: "Synthetic clinician documented 110 minutes of qualifying same-day admission and discharge management time.",
246
+ serviceDate: "2026-07-01",
247
+ placeOfService: "22",
248
+ claimContext: {
249
+ stage: "development",
250
+ payerName: "Synthetic Medicare demo",
251
+ submittedCodes: ["99236"],
252
+ },
253
+ };
254
+ /** A small branded form suitable for embedding the pilot workflow in a host app. */
255
+ export function RcmCodingAssistant({ config, initialInput = CARECOMPASS_RCM_SYNTHETIC_DEMO, onAuditEvent, onResult, onSafetyEvent, workflowOptions, }) {
256
+ const workflow = React.useMemo(() => createRcmCodingWorkflow(config, {
257
+ ...workflowOptions,
258
+ coreOptions: {
259
+ onAuditEvent,
260
+ onSafetyEvent,
261
+ },
262
+ onResult,
263
+ }), [config, onAuditEvent, onResult, onSafetyEvent, workflowOptions]);
264
+ const [input, setInput] = React.useState(initialInput);
265
+ const [result, setResult] = React.useState(null);
266
+ const [submitting, setSubmitting] = React.useState(false);
267
+ React.useEffect(() => () => workflow.core.dispose(), [workflow]);
268
+ const submit = async (event) => {
269
+ event.preventDefault();
270
+ setSubmitting(true);
271
+ try {
272
+ setResult(await workflow.review(input));
273
+ }
274
+ finally {
275
+ setSubmitting(false);
276
+ }
277
+ };
278
+ const update = (field, value) => setInput((current) => ({ ...current, [field]: value }));
279
+ return (_jsxs("section", { "aria-label": `${config.labels.title} coding assistance`, style: {
280
+ background: config.theme.backgroundColor,
281
+ border: `1px solid ${config.theme.primaryColor}`,
282
+ borderRadius: config.theme.borderRadius,
283
+ color: config.theme.textColor,
284
+ fontFamily: config.theme.fontFamily,
285
+ maxWidth: 720,
286
+ padding: 20,
287
+ }, children: [_jsxs("header", { children: [_jsx("strong", { children: config.branding.displayName }), _jsx("h2", { style: { margin: "4px 0" }, children: "RCM Coding Assistance" }), _jsx("p", { children: "Suggestions require qualified human coding review before any billing action." })] }), _jsxs("form", { onSubmit: submit, children: [_jsxs("label", { children: ["Encounter reference", _jsx("input", { "aria-label": "Encounter reference", value: input.encounterId, onChange: (event) => update("encounterId", event.target.value) })] }), _jsxs("label", { children: ["Diagnosis context", _jsx("textarea", { "aria-label": "Diagnosis context", required: true, value: input.diagnosisNarrative, onChange: (event) => update("diagnosisNarrative", event.target.value) })] }), _jsxs("label", { children: ["Procedure context", _jsx("textarea", { "aria-label": "Procedure context", value: input.procedureNarrative ?? "", onChange: (event) => update("procedureNarrative", event.target.value) })] }), _jsx("button", { disabled: submitting, style: { background: config.theme.primaryColor, color: "#fff" }, type: "submit", children: submitting ? "Reviewing…" : "Prepare for coder review" })] }), result && (_jsxs("section", { "aria-live": "polite", children: [_jsx("p", { role: "alert", children: "Human review required \u2014 no billing or coding decision has been made." }), _jsx("p", { children: result.rationale }), result.recommendations.map((recommendation) => (_jsxs("article", { children: [_jsxs("strong", { children: [recommendation.codeSystem, " ", recommendation.code] }), _jsxs("span", { children: [" (", recommendation.confidence, " confidence)"] }), _jsx("p", { children: recommendation.rationale })] }, `${recommendation.codeSystem}-${recommendation.code}`))), result.policyReferences.length > 0 && (_jsxs("section", { "aria-label": "Supporting policy sources", children: [_jsx("h3", { children: "Supporting policy sources" }), result.policyReferences.map((reference) => (_jsxs("article", { children: [_jsx("strong", { children: reference.title }), reference.version && (_jsxs("span", { children: [" (version ", reference.version, ")"] })), _jsx("p", { children: reference.excerpt })] }, `${reference.corpusId}-${reference.id}`)))] }))] }))] }));
288
+ }
@@ -0,0 +1,2 @@
1
+ export { CareCompassChat, CareCompassEmbed, useCareCompass, } from "./component.js";
2
+ export type { CareCompassChatProps, CareCompassReactController, UseCareCompassOptions, } from "./component.js";
package/dist/react.js ADDED
@@ -0,0 +1 @@
1
+ export { CareCompassChat, CareCompassEmbed, useCareCompass, } from "./component.js";
package/dist/schema.d.ts CHANGED
@@ -34,6 +34,29 @@ export declare const careCompassPlacementSchema: z.ZodObject<{
34
34
  }>>;
35
35
  selector: z.ZodOptional<z.ZodString>;
36
36
  }, z.core.$strip>;
37
+ export declare const careCompassComplianceConfigSchema: z.ZodObject<{
38
+ preset: z.ZodDefault<z.ZodEnum<{
39
+ standard: "standard";
40
+ healthcare: "healthcare";
41
+ strict: "strict";
42
+ }>>;
43
+ audit: z.ZodDefault<z.ZodObject<{
44
+ enabled: z.ZodOptional<z.ZodBoolean>;
45
+ }, z.core.$strip>>;
46
+ phi: z.ZodDefault<z.ZodObject<{
47
+ action: z.ZodOptional<z.ZodEnum<{
48
+ allow: "allow";
49
+ redact: "redact";
50
+ block: "block";
51
+ }>>;
52
+ replacement: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$strip>>;
54
+ }, z.core.$strip>;
55
+ export declare const careCompassModelConfigSchema: z.ZodObject<{
56
+ id: z.ZodOptional<z.ZodString>;
57
+ version: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strip>;
59
+ export declare const careCompassFeatureFlagsSchema: z.ZodRecord<z.ZodString, z.ZodBoolean>;
37
60
  export declare const careCompassConfigSchema: z.ZodObject<{
38
61
  payerId: z.ZodString;
39
62
  environment: z.ZodEnum<{
@@ -69,6 +92,29 @@ export declare const careCompassConfigSchema: z.ZodObject<{
69
92
  welcomeMessage: z.ZodDefault<z.ZodString>;
70
93
  allowUserContext: z.ZodDefault<z.ZodBoolean>;
71
94
  }, z.core.$strip>>;
95
+ compliance: z.ZodDefault<z.ZodObject<{
96
+ preset: z.ZodDefault<z.ZodEnum<{
97
+ standard: "standard";
98
+ healthcare: "healthcare";
99
+ strict: "strict";
100
+ }>>;
101
+ audit: z.ZodDefault<z.ZodObject<{
102
+ enabled: z.ZodOptional<z.ZodBoolean>;
103
+ }, z.core.$strip>>;
104
+ phi: z.ZodDefault<z.ZodObject<{
105
+ action: z.ZodOptional<z.ZodEnum<{
106
+ allow: "allow";
107
+ redact: "redact";
108
+ block: "block";
109
+ }>>;
110
+ replacement: z.ZodOptional<z.ZodString>;
111
+ }, z.core.$strip>>;
112
+ }, z.core.$strip>>;
113
+ model: z.ZodDefault<z.ZodObject<{
114
+ id: z.ZodOptional<z.ZodString>;
115
+ version: z.ZodOptional<z.ZodString>;
116
+ }, z.core.$strip>>;
117
+ featureFlags: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
72
118
  placement: z.ZodDefault<z.ZodObject<{
73
119
  mode: z.ZodDefault<z.ZodEnum<{
74
120
  inline: "inline";
@@ -88,6 +134,9 @@ export declare const careCompassConfigSchema: z.ZodObject<{
88
134
  export type CareCompassTheme = z.infer<typeof careCompassThemeSchema>;
89
135
  export type CareCompassLabels = z.infer<typeof careCompassLabelsSchema>;
90
136
  export type CareCompassPromptConfig = z.infer<typeof careCompassPromptConfigSchema>;
137
+ export type CareCompassComplianceConfig = z.infer<typeof careCompassComplianceConfigSchema>;
138
+ export type CareCompassModelConfig = z.infer<typeof careCompassModelConfigSchema>;
139
+ export type CareCompassFeatureFlags = z.infer<typeof careCompassFeatureFlagsSchema>;
91
140
  export type CareCompassConfig = z.infer<typeof careCompassConfigSchema>;
92
141
  export type CareCompassConfigInput = z.input<typeof careCompassConfigSchema>;
93
142
  export type CareCompassValidationResult = {
package/dist/schema.js CHANGED
@@ -127,6 +127,33 @@ export const careCompassPlacementSchema = z.object({
127
127
  mode: z.enum(["inline", "floating", "sidebar"]).default("inline"),
128
128
  selector: z.string().min(1).max(120).optional(),
129
129
  });
130
+ export const careCompassComplianceConfigSchema = z.object({
131
+ preset: z.enum(["standard", "healthcare", "strict"]).default("standard"),
132
+ audit: z
133
+ .object({
134
+ enabled: z.boolean().optional(),
135
+ })
136
+ .default({}),
137
+ phi: z
138
+ .object({
139
+ action: z.enum(["allow", "redact", "block"]).optional(),
140
+ replacement: z.string().min(1).max(80).optional(),
141
+ })
142
+ .default({}),
143
+ });
144
+ // Model selection is descriptive rather than credential-bearing. The hosted
145
+ // runtime remains the authority for which models a tenant may use.
146
+ export const careCompassModelConfigSchema = z.object({
147
+ id: z.string().min(1).max(120).optional(),
148
+ version: z.string().min(1).max(120).optional(),
149
+ });
150
+ // A portable public config can carry feature switches across every integration
151
+ // option without requiring framework-specific configuration.
152
+ export const careCompassFeatureFlagsSchema = z.record(z
153
+ .string()
154
+ .min(1)
155
+ .max(80)
156
+ .regex(/^[A-Za-z][A-Za-z0-9._-]*$/, "Feature flag keys must start with a letter and use letters, numbers, periods, underscores, or hyphens."), z.boolean());
130
157
  export const careCompassConfigSchema = z
131
158
  .object({
132
159
  payerId: z.string().regex(PAYER_ID_RE, "Invalid payer ID."),
@@ -149,6 +176,13 @@ export const careCompassConfigSchema = z
149
176
  welcomeMessage: "How can I help you navigate your care today?",
150
177
  allowUserContext: false,
151
178
  }),
179
+ compliance: careCompassComplianceConfigSchema.default({
180
+ preset: "standard",
181
+ audit: {},
182
+ phi: {},
183
+ }),
184
+ model: careCompassModelConfigSchema.default({}),
185
+ featureFlags: careCompassFeatureFlagsSchema.default({}),
152
186
  placement: careCompassPlacementSchema.default({ mode: "inline" }),
153
187
  deploymentTarget: z
154
188
  .enum(["react", "nextjs", "static", "web", "other"])
@@ -212,6 +246,8 @@ export function createDefaultCareCompassConfig(overrides = {}) {
212
246
  },
213
247
  labels: {},
214
248
  prompt: {},
249
+ model: {},
250
+ featureFlags: {},
215
251
  placement: {},
216
252
  deploymentTarget: "web",
217
253
  ...overrides,
@@ -1,4 +1,4 @@
1
- import type { CareCompassConfig } from "./schema.js";
1
+ import { type CareCompassConfig } from "./schema.js";
2
2
  export declare const CARECOMPASS_TELEMETRY_EVENTS: {
3
3
  readonly SDK_INITIALIZED: "carecompass_sdk_initialized";
4
4
  readonly SDK_INITIALIZATION_FAILED: "carecompass_sdk_initialization_failed";
@@ -98,11 +98,12 @@ export type CareCompassTelemetry = {
98
98
  captureAsync: (event: CareCompassTelemetryEvent, options?: CareCompassTelemetryCaptureOptions) => Promise<void>;
99
99
  };
100
100
  export declare const careCompassTelemetryDurationBucket: (durationMs: number) => CareCompassTelemetryDurationBucket;
101
- export declare function createCareCompassTelemetry(config: Pick<CareCompassConfig, "environment" | "payerId">, options?: {
101
+ export declare function createCareCompassTelemetry(config: Pick<CareCompassConfig, "apiBaseUrl" | "environment" | "payerId">, options?: {
102
102
  fetcher?: Fetcher;
103
103
  now?: () => Date;
104
104
  }): CareCompassTelemetry;
105
105
  export declare function createCareCompassCliTelemetry(sessionId: string, context?: {
106
+ apiBaseUrl?: string;
106
107
  environment?: CareCompassConfig["environment"];
107
108
  payerId?: string;
108
109
  }, options?: {