@nexalink/carecompass-sdk 0.3.0-beta.15 → 0.5.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/README.md +479 -128
- package/dist/cli.js +185 -32
- package/dist/client.d.ts +32 -2
- package/dist/client.js +42 -3
- package/dist/compliance.d.ts +69 -0
- package/dist/compliance.js +121 -0
- package/dist/component.d.ts +32 -2
- package/dist/component.js +117 -56
- package/dist/core.d.ts +95 -0
- package/dist/core.js +352 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.js +6 -2
- package/dist/markdown.d.ts +4 -0
- package/dist/markdown.js +183 -0
- package/dist/mount.d.ts +4 -1
- package/dist/mount.js +2 -2
- package/dist/rcm.d.ts +93 -0
- package/dist/rcm.js +284 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +1 -0
- package/dist/schema.d.ts +49 -0
- package/dist/schema.js +36 -0
- package/dist/telemetry.js +114 -82
- package/dist/ui.d.ts +3 -0
- package/dist/ui.js +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/dist/web-component.d.ts +13 -0
- package/dist/web-component.js +236 -0
- package/package.json +43 -12
- package/qa-fixtures/DEMO-ENC-1042-rcm-policy.md +27 -0
package/dist/rcm.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
payerName?: string;
|
|
14
|
+
submittedCodes?: string[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type RcmCodeSystem = "ICD-10-CM" | "CPT" | "HCPCS";
|
|
18
|
+
export type RcmConfidence = "low" | "medium" | "high";
|
|
19
|
+
export type RcmReviewDisposition = "review_required" | "abstain" | "escalate";
|
|
20
|
+
export type RcmPolicyReference = {
|
|
21
|
+
corpusId: string;
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
excerpt: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
};
|
|
27
|
+
export type RcmCodeRecommendation = {
|
|
28
|
+
code: string;
|
|
29
|
+
codeSystem: RcmCodeSystem;
|
|
30
|
+
confidence: RcmConfidence;
|
|
31
|
+
rationale: string;
|
|
32
|
+
policyReferenceIds: string[];
|
|
33
|
+
};
|
|
34
|
+
export type RcmCodingAssistanceResult = {
|
|
35
|
+
disposition: RcmReviewDisposition;
|
|
36
|
+
humanReviewRequired: true;
|
|
37
|
+
recommendations: RcmCodeRecommendation[];
|
|
38
|
+
documentationGaps: string[];
|
|
39
|
+
policyReferences: RcmPolicyReference[];
|
|
40
|
+
rationale: string;
|
|
41
|
+
/** A host-facing, measurable pilot hypothesis—not a claim of realized savings. */
|
|
42
|
+
operationalHypothesis: {
|
|
43
|
+
metric: "manual_research_minutes_per_case";
|
|
44
|
+
target: "Measure whether assisted review reduces manual research time by 25% or more.";
|
|
45
|
+
};
|
|
46
|
+
processingTimeMs: number;
|
|
47
|
+
};
|
|
48
|
+
export type RcmCustomerRules = {
|
|
49
|
+
allowedCodeSystems?: RcmCodeSystem[];
|
|
50
|
+
escalateOnLowConfidence?: boolean;
|
|
51
|
+
requirePolicySupport?: boolean;
|
|
52
|
+
};
|
|
53
|
+
export type RcmPolicyRetriever = {
|
|
54
|
+
/** Corpus IDs are allowlisted by the embed owner; retrieval stays in the host's trusted boundary. */
|
|
55
|
+
approvedCorpusIds: readonly string[];
|
|
56
|
+
search: (request: {
|
|
57
|
+
input: RcmCodingAssistanceInput;
|
|
58
|
+
query: string;
|
|
59
|
+
}) => Promise<RcmPolicyReference[]>;
|
|
60
|
+
};
|
|
61
|
+
export type RcmCodingWorkflowOptions = {
|
|
62
|
+
core?: CareCompassCore;
|
|
63
|
+
coreOptions?: CareCompassCoreOptions;
|
|
64
|
+
customerRules?: RcmCustomerRules;
|
|
65
|
+
now?: () => number;
|
|
66
|
+
onResult?: (result: RcmCodingAssistanceResult) => void;
|
|
67
|
+
/**
|
|
68
|
+
* `hosted` delegates retrieval to the tenant-authorized CareCompass runtime.
|
|
69
|
+
* The browser never supplies policy text or corpus identifiers in that mode.
|
|
70
|
+
*/
|
|
71
|
+
retrieval?: RcmPolicyRetriever | "hosted";
|
|
72
|
+
};
|
|
73
|
+
export type RcmCodingWorkflow = {
|
|
74
|
+
core: CareCompassCore;
|
|
75
|
+
review: (input: RcmCodingAssistanceInput) => Promise<RcmCodingAssistanceResult>;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Creates a narrow RCM coding-review workflow on the shared CareCompass core.
|
|
79
|
+
* Retrieval can run in the CareCompass tenant boundary or be injected by a
|
|
80
|
+
* customer that maintains its own approved, server-side retrieval boundary.
|
|
81
|
+
*/
|
|
82
|
+
export declare function createRcmCodingWorkflow(config: CareCompassConfig, options?: RcmCodingWorkflowOptions): RcmCodingWorkflow;
|
|
83
|
+
export declare const CARECOMPASS_RCM_SYNTHETIC_DEMO: RcmCodingAssistanceInput;
|
|
84
|
+
export type RcmCodingAssistantProps = {
|
|
85
|
+
config: CareCompassConfig;
|
|
86
|
+
initialInput?: RcmCodingAssistanceInput;
|
|
87
|
+
onAuditEvent?: CareCompassAuditListener;
|
|
88
|
+
onResult?: (result: RcmCodingAssistanceResult) => void;
|
|
89
|
+
onSafetyEvent?: CareCompassSafetyListener;
|
|
90
|
+
workflowOptions?: Omit<RcmCodingWorkflowOptions, "core" | "coreOptions" | "onResult">;
|
|
91
|
+
};
|
|
92
|
+
/** A small branded form suitable for embedding the pilot workflow in a host app. */
|
|
93
|
+
export declare function RcmCodingAssistant({ config, initialInput, onAuditEvent, onResult, onSafetyEvent, workflowOptions, }: RcmCodingAssistantProps): React.JSX.Element;
|
package/dist/rcm.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
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 outpatient visit for persistent knee pain after activity.",
|
|
245
|
+
procedureNarrative: "Synthetic clinician documentation describes a focused musculoskeletal evaluation.",
|
|
246
|
+
serviceDate: "2026-07-01",
|
|
247
|
+
placeOfService: "11",
|
|
248
|
+
claimContext: { payerName: "Demo Health Plan", submittedCodes: [] },
|
|
249
|
+
};
|
|
250
|
+
/** A small branded form suitable for embedding the pilot workflow in a host app. */
|
|
251
|
+
export function RcmCodingAssistant({ config, initialInput = CARECOMPASS_RCM_SYNTHETIC_DEMO, onAuditEvent, onResult, onSafetyEvent, workflowOptions, }) {
|
|
252
|
+
const workflow = React.useMemo(() => createRcmCodingWorkflow(config, {
|
|
253
|
+
...workflowOptions,
|
|
254
|
+
coreOptions: {
|
|
255
|
+
onAuditEvent,
|
|
256
|
+
onSafetyEvent,
|
|
257
|
+
},
|
|
258
|
+
onResult,
|
|
259
|
+
}), [config, onAuditEvent, onResult, onSafetyEvent, workflowOptions]);
|
|
260
|
+
const [input, setInput] = React.useState(initialInput);
|
|
261
|
+
const [result, setResult] = React.useState(null);
|
|
262
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
263
|
+
React.useEffect(() => () => workflow.core.dispose(), [workflow]);
|
|
264
|
+
const submit = async (event) => {
|
|
265
|
+
event.preventDefault();
|
|
266
|
+
setSubmitting(true);
|
|
267
|
+
try {
|
|
268
|
+
setResult(await workflow.review(input));
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
setSubmitting(false);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const update = (field, value) => setInput((current) => ({ ...current, [field]: value }));
|
|
275
|
+
return (_jsxs("section", { "aria-label": `${config.labels.title} coding assistance`, style: {
|
|
276
|
+
background: config.theme.backgroundColor,
|
|
277
|
+
border: `1px solid ${config.theme.primaryColor}`,
|
|
278
|
+
borderRadius: config.theme.borderRadius,
|
|
279
|
+
color: config.theme.textColor,
|
|
280
|
+
fontFamily: config.theme.fontFamily,
|
|
281
|
+
maxWidth: 720,
|
|
282
|
+
padding: 20,
|
|
283
|
+
}, 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}`)))] }))] }))] }));
|
|
284
|
+
}
|
package/dist/react.d.ts
ADDED
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,
|