@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/README.md +285 -3
- package/dist/cli.js +0 -0
- package/dist/client.d.ts +32 -2
- package/dist/client.js +45 -3
- package/dist/compliance.d.ts +69 -0
- package/dist/compliance.js +121 -0
- package/dist/component.d.ts +24 -2
- package/dist/component.js +63 -4
- package/dist/core.d.ts +19 -2
- package/dist/core.js +140 -8
- package/dist/index.d.ts +11 -5
- package/dist/index.js +5 -2
- package/dist/markdown.d.ts +2 -1
- package/dist/mount.d.ts +4 -1
- package/dist/mount.js +2 -2
- package/dist/rcm.d.ts +95 -0
- package/dist/rcm.js +288 -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.d.ts +3 -2
- package/dist/telemetry.js +127 -114
- package/dist/ui.d.ts +2 -2
- package/dist/ui.js +1 -1
- 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 +30 -14
- package/qa-fixtures/DEMO-ENC-1042-rcm-policy.md +27 -0
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ Nexalink-hosted CareCompass APIs
|
|
|
79
79
|
CareCompass AI runtime
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
The browser receives only public embed configuration: tenant identifier, environment, public embed key, allowed origins, branding, labels, theme, placement, and approved prompt settings.
|
|
82
|
+
The browser receives only public embed configuration: tenant identifier, environment, public embed key, allowed origins, endpoint, model preferences, feature flags, branding, labels, theme, placement, compliance, and approved prompt settings.
|
|
83
83
|
|
|
84
84
|
Secret API keys stay in CLI, server-side, or trusted setup workflows. The browser never receives model credentials or Nexalink API keys.
|
|
85
85
|
|
|
@@ -97,6 +97,7 @@ The core manages:
|
|
|
97
97
|
- loading, error, timeout, and retry state
|
|
98
98
|
- context injection
|
|
99
99
|
- subscriptions for custom renderers
|
|
100
|
+
- PHI preflight, safety callbacks, and metadata-only audit trails
|
|
100
101
|
- endpoint calls and telemetry integration
|
|
101
102
|
|
|
102
103
|
```ts
|
|
@@ -172,7 +173,177 @@ Custom message rendering:
|
|
|
172
173
|
/>
|
|
173
174
|
```
|
|
174
175
|
|
|
175
|
-
###
|
|
176
|
+
### RCM coding-assistance pilot workflow
|
|
177
|
+
|
|
178
|
+
The RCM kit is a narrow, structured coding-review workflow built on the same
|
|
179
|
+
headless core, compliance preflight, and metadata-only audit events. It returns
|
|
180
|
+
candidate codes for qualified human review; it never makes a final coding,
|
|
181
|
+
billing, claim-submission, diagnosis, or treatment decision.
|
|
182
|
+
|
|
183
|
+
The SDK sends `workflow: "rcm-coding-assistance"` as an explicit top-level ask
|
|
184
|
+
field. The hosted runtime authorizes the workflow from the issued embed key and
|
|
185
|
+
server-side tenant entitlement. Values in user context do not authorize a
|
|
186
|
+
workflow.
|
|
187
|
+
|
|
188
|
+
Development and staging RCM requests must include `claimContext.stage` set to
|
|
189
|
+
the configured SDK environment (`"development"` or `"staging"`). Production
|
|
190
|
+
may omit the field. The hosted runtime validates any supplied value against the
|
|
191
|
+
authenticated key environment; this marker never grants workflow access.
|
|
192
|
+
|
|
193
|
+
For the hosted RCM path, configure the tenant's RCM policy corpus in the
|
|
194
|
+
CareCompass tenant portal before embedding: an authorized administrator must
|
|
195
|
+
have an executed BAA, enable the RCM entitlement, set up the corpus, upload a
|
|
196
|
+
customer-owned policy version, explicitly approve it, and wait for ingestion to
|
|
197
|
+
become active. The browser sends structured encounter context only; it never
|
|
198
|
+
sends a corpus ID, policy text, or policy references.
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import {
|
|
202
|
+
CARECOMPASS_RCM_SYNTHETIC_DEMO,
|
|
203
|
+
RcmCodingAssistant,
|
|
204
|
+
} from "@nexalink/carecompass-sdk/rcm";
|
|
205
|
+
|
|
206
|
+
<RcmCodingAssistant
|
|
207
|
+
config={config}
|
|
208
|
+
initialInput={CARECOMPASS_RCM_SYNTHETIC_DEMO}
|
|
209
|
+
workflowOptions={{
|
|
210
|
+
customerRules: { requirePolicySupport: true },
|
|
211
|
+
retrieval: "hosted",
|
|
212
|
+
}}
|
|
213
|
+
onAuditEvent={(event) => hostAudit(event)}
|
|
214
|
+
onSafetyEvent={(event) => hostSafetyMonitor(event)}
|
|
215
|
+
/>;
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The included demo input is synthetic and contains no patient data. Its same-day
|
|
219
|
+
observation scenario is designed to retrieve the official CMS E/M evidence for
|
|
220
|
+
CPT 99236 from the Nexalink base corpus. Tenant policies may supplement the
|
|
221
|
+
base, but the runtime still requires an approved, active corpus and grounded
|
|
222
|
+
citations before returning a candidate.
|
|
223
|
+
|
|
224
|
+
A production tenant must have an executed BAA, an RCM entitlement, and a
|
|
225
|
+
healthy active corpus; otherwise the hosted workflow fails closed with a
|
|
226
|
+
structured human-review escalation.
|
|
227
|
+
|
|
228
|
+
In hosted mode, the result's `policyReferences` are the sanitized citations
|
|
229
|
+
resolved by the runtime. Render those citations alongside a coder-review
|
|
230
|
+
suggestion. A `review_required` result requires policy support; empty retrieval,
|
|
231
|
+
invalid citations, guardrail intervention, or invalid JSON becomes an
|
|
232
|
+
`abstain`/`escalate` result with no recommendations. Hosts receive the normal
|
|
233
|
+
metadata-only audit and safety events, including authorization, retrieval,
|
|
234
|
+
validation, and escalation outcomes.
|
|
235
|
+
|
|
236
|
+
For a customer-managed trusted retrieval boundary, pass an `RcmPolicyRetriever`
|
|
237
|
+
instead of `"hosted"`. Its `approvedCorpusIds` allowlist is enforced by the SDK;
|
|
238
|
+
unapproved or missing references produce a human-review escalation.
|
|
239
|
+
|
|
240
|
+
To evaluate the pilot, compare **manual research minutes per case** before and
|
|
241
|
+
after assisted, human-reviewed use. The target is to measure a 25% or greater
|
|
242
|
+
reduction, not to make or submit a coding decision.
|
|
243
|
+
|
|
244
|
+
### 3. React Hook
|
|
245
|
+
|
|
246
|
+
Use `useCareCompass` when your React app needs its own visual design but does
|
|
247
|
+
not want to manage subscriptions, retries, or teardown by hand.
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
import { useCareCompass } from "@nexalink/carecompass-sdk/react";
|
|
251
|
+
import config from "./carecompass/carecompass.config.json";
|
|
252
|
+
|
|
253
|
+
export function CareCompassPanel() {
|
|
254
|
+
const { snapshot, sendMessage, retryLast } = useCareCompass(config, {
|
|
255
|
+
initialContext: { workflow: "member-portal" },
|
|
256
|
+
onSafetyEvent: (event) => monitorSafety(event),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<section>
|
|
261
|
+
{snapshot.messages.map((message) => (
|
|
262
|
+
<p key={message.id}>{message.text}</p>
|
|
263
|
+
))}
|
|
264
|
+
<button
|
|
265
|
+
disabled={snapshot.loading}
|
|
266
|
+
onClick={() => void sendMessage("Find an in-network doctor")}
|
|
267
|
+
>
|
|
268
|
+
Find care
|
|
269
|
+
</button>
|
|
270
|
+
{snapshot.canRetry && (
|
|
271
|
+
<button onClick={() => void retryLast()}>Retry</button>
|
|
272
|
+
)}
|
|
273
|
+
</section>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The hook returns the underlying `core` for audit export, safety/audit
|
|
279
|
+
subscriptions, tool/widget events, and the lifecycle `streamMessage` API.
|
|
280
|
+
|
|
281
|
+
### 4. Web Component (Vue-compatible)
|
|
282
|
+
|
|
283
|
+
The Web Component is rendered directly from the headless core and exposes a
|
|
284
|
+
standard custom-element property, making it a good fit for Vue, Angular, CMS
|
|
285
|
+
shells, and mixed-framework pages without requiring a React runtime. Register
|
|
286
|
+
it only in the browser.
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import { defineCareCompassElement } from "@nexalink/carecompass-sdk/web-component";
|
|
290
|
+
|
|
291
|
+
defineCareCompassElement();
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
```vue
|
|
295
|
+
<script setup lang="ts">
|
|
296
|
+
import { onMounted, ref } from "vue";
|
|
297
|
+
import { defineCareCompassElement } from "@nexalink/carecompass-sdk/web-component";
|
|
298
|
+
import config from "./carecompass/carecompass.config.json";
|
|
299
|
+
|
|
300
|
+
const chat = ref<HTMLElement & { config?: typeof config }>();
|
|
301
|
+
onMounted(() => {
|
|
302
|
+
defineCareCompassElement();
|
|
303
|
+
if (chat.value) chat.value.config = config;
|
|
304
|
+
});
|
|
305
|
+
</script>
|
|
306
|
+
|
|
307
|
+
<template><carecompass-chat ref="chat" /></template>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
For static hosts, the element also accepts a JSON `config` attribute. Prefer the
|
|
311
|
+
property assignment above in application code: it avoids HTML escaping and keeps
|
|
312
|
+
the configuration in your typed build pipeline.
|
|
313
|
+
|
|
314
|
+
### 5. Script-tag Quickstart
|
|
315
|
+
|
|
316
|
+
The existing `mountCareCompass` path remains supported for quick static pages
|
|
317
|
+
and CMS integrations. Generate a script snippet from the same config:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { generateCareCompassSnippet } from "@nexalink/carecompass-sdk";
|
|
321
|
+
|
|
322
|
+
const html = generateCareCompassSnippet(config, { mode: "cdn" });
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The generated module calls `mountCareCompass` and does not require a React app.
|
|
326
|
+
|
|
327
|
+
### 6. Iframe Fallback
|
|
328
|
+
|
|
329
|
+
Use an iframe when the host page needs strong CSS/JavaScript isolation or cannot
|
|
330
|
+
run a module script. The CLI `init` flow generates an iframe page, or you can
|
|
331
|
+
create the host markup with `generateCareCompassIframeSnippet(config)`.
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { generateCareCompassIframeSnippet } from "@nexalink/carecompass-sdk";
|
|
335
|
+
|
|
336
|
+
const html = generateCareCompassIframeSnippet(config, {
|
|
337
|
+
src: "/carecompass/carecompass-embed.html",
|
|
338
|
+
});
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
An iframe isolates page styles and scripts, but it adds a separate document for
|
|
342
|
+
focus management, analytics, responsive sizing, and host-to-widget context.
|
|
343
|
+
Choose the React hook or Web Component when the assistant needs rich in-page
|
|
344
|
+
coordination; choose the iframe for constrained or high-isolation environments.
|
|
345
|
+
|
|
346
|
+
### 7. Existing Embed Compatibility
|
|
176
347
|
|
|
177
348
|
Existing embed integrations can keep using the root package exports.
|
|
178
349
|
|
|
@@ -192,6 +363,40 @@ import config from "./carecompass/carecompass.config.json";
|
|
|
192
363
|
mountCareCompass(document.getElementById("carecompass-root"), config);
|
|
193
364
|
```
|
|
194
365
|
|
|
366
|
+
### Production runtime tokens
|
|
367
|
+
|
|
368
|
+
Production embeds require a fresh runtime token from an integrator-controlled
|
|
369
|
+
backend-for-frontend (BFF). Do not place a Nexalink API key, runtime-token
|
|
370
|
+
secret, or a reusable token in `carecompass.config.json` or browser code.
|
|
371
|
+
|
|
372
|
+
Provide `runtimeTokenProvider`; the SDK calls it before validation, every
|
|
373
|
+
inference request, and every status poll. The provider should call your BFF,
|
|
374
|
+
which holds the Nexalink API key and obtains a short-lived token for the
|
|
375
|
+
current browser origin and workflow.
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
const runtimeTokenProvider = async ({ workflow }: { workflow?: string }) => {
|
|
379
|
+
const response = await fetch("/api/carecompass/runtime-token", {
|
|
380
|
+
method: "POST",
|
|
381
|
+
credentials: "same-origin",
|
|
382
|
+
headers: { "content-type": "application/json" },
|
|
383
|
+
body: JSON.stringify({ workflow }),
|
|
384
|
+
});
|
|
385
|
+
if (!response.ok) throw new Error("Could not authorize CareCompass.");
|
|
386
|
+
return ((await response.json()) as { token: string }).token;
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
<CareCompassEmbed
|
|
390
|
+
config={config}
|
|
391
|
+
runtimeTokenProvider={runtimeTokenProvider}
|
|
392
|
+
/>;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
For static hosts, pass the provider as the third argument to
|
|
396
|
+
`mountCareCompass`. For Web Components, assign it to the
|
|
397
|
+
`runtimeTokenProvider` property before setting `config`. Production requests
|
|
398
|
+
fail closed when the provider is absent, fails, or returns an empty token.
|
|
399
|
+
|
|
195
400
|
## Quick Start
|
|
196
401
|
|
|
197
402
|
Live regulated use requires Nexalink onboarding, issued credentials, and BAA coverage where PHI may be involved. The package is publicly installable for developer convenience, but runtime access is credentialed.
|
|
@@ -249,6 +454,17 @@ npx @nexalink/carecompass-sdk doctor --config carecompass/carecompass.config.jso
|
|
|
249
454
|
"welcomeMessage": "How can I help?",
|
|
250
455
|
"allowUserContext": false
|
|
251
456
|
},
|
|
457
|
+
"compliance": {
|
|
458
|
+
"preset": "healthcare"
|
|
459
|
+
},
|
|
460
|
+
"model": {
|
|
461
|
+
"id": "care-navigation-v2",
|
|
462
|
+
"version": "2026-07"
|
|
463
|
+
},
|
|
464
|
+
"featureFlags": {
|
|
465
|
+
"care-plan-summary": true,
|
|
466
|
+
"provider-search": true
|
|
467
|
+
},
|
|
252
468
|
"placement": {
|
|
253
469
|
"mode": "inline"
|
|
254
470
|
},
|
|
@@ -256,6 +472,72 @@ npx @nexalink/carecompass-sdk doctor --config carecompass/carecompass.config.jso
|
|
|
256
472
|
}
|
|
257
473
|
```
|
|
258
474
|
|
|
475
|
+
## Compliance Controls And Audit Trails
|
|
476
|
+
|
|
477
|
+
Compliance controls are local SDK preflight checks, not a substitute for Nexalink runtime guardrails, a BAA, or the host application's own HIPAA program. They detect common direct identifiers (email addresses, US phone numbers, SSNs, DOBs, member/policy IDs, and MRNs) in messages, responses, and string-valued context before the SDK stores or sends them.
|
|
478
|
+
|
|
479
|
+
Choose a preset in public SDK configuration, then override only the behavior you need:
|
|
480
|
+
|
|
481
|
+
| Preset | PHI behavior | Audit retention | Tradeoff |
|
|
482
|
+
| ------------ | --------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
483
|
+
| `standard` | Detects and emits a `policy_sensitive` safety callback; does not modify requests. | Off | Preserves existing SDK behavior; the host must opt into audit retention. |
|
|
484
|
+
| `healthcare` | Replaces detected values with `[REDACTED]` before requests and UI state. | On | Recommended default for PHI-oriented browser integrations; local detection cannot identify every form of PHI. |
|
|
485
|
+
| `strict` | Blocks a request or response with detectable PHI. | On | Stronger browser-side control, but legitimate identifier-bearing workflows need a host-approved alternative. |
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
import { createCareCompassCore } from "@nexalink/carecompass-sdk/core";
|
|
489
|
+
|
|
490
|
+
const core = createCareCompassCore({
|
|
491
|
+
...config,
|
|
492
|
+
compliance: {
|
|
493
|
+
preset: "healthcare",
|
|
494
|
+
phi: { replacement: "[PHI REMOVED]" },
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const unsubscribeAudit = core.subscribeAudit((event) => {
|
|
499
|
+
// Persist to your audit system. Events contain type, outcome, timestamp,
|
|
500
|
+
// source, PHI category/count, and a generated ID—never message text,
|
|
501
|
+
// context values, identifiers, or raw errors.
|
|
502
|
+
persistAuditEvent(event);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
const unsubscribeSafety = core.subscribeSafety((event) => {
|
|
506
|
+
// `blocked`, `redacted`, `escalated`, or `policy_sensitive` with only
|
|
507
|
+
// source plus PHI category/count for safe monitoring.
|
|
508
|
+
monitorSafetyEvent(event);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// Export a copy of the in-memory, metadata-only audit trail on demand.
|
|
512
|
+
const auditTrail = core.exportAuditTrail();
|
|
513
|
+
|
|
514
|
+
unsubscribeAudit();
|
|
515
|
+
unsubscribeSafety();
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
For host-owned tools and UI actions that happen outside the core request lifecycle, add their metadata-only events explicitly:
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
core.recordToolCall("completed");
|
|
522
|
+
core.recordWidgetAction();
|
|
523
|
+
core.recordSafetyDecision("escalated");
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
If your organization has an approved detector or redaction format, pass it through `createCareCompassCore` options. These hooks receive the raw value only inside your host application; the SDK still emits only PHI categories and counts in audit and safety events.
|
|
527
|
+
|
|
528
|
+
```ts
|
|
529
|
+
const core = createCareCompassCore(config, {
|
|
530
|
+
compliance: {
|
|
531
|
+
phi: {
|
|
532
|
+
detect: (value) => approvedPhiDetector(value),
|
|
533
|
+
redact: (value, matches) => approvedRedactor(value, matches),
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
The React UI kit accepts `onAuditEvent` and `onSafetyEvent` with the same safe payloads. Its submit and retry actions are automatically represented as widget audit events when auditing is enabled.
|
|
540
|
+
|
|
259
541
|
## Credential Model
|
|
260
542
|
|
|
261
543
|
CareCompass uses two key types:
|
|
@@ -316,7 +598,7 @@ The SDK is designed to keep sensitive operational details out of the browser:
|
|
|
316
598
|
- no secret API keys in frontend config
|
|
317
599
|
- no model endpoint credentials in browser code
|
|
318
600
|
- no customer-managed telemetry switches in runtime config
|
|
319
|
-
- no prompt, response, chat history, user context, PHI/PII, credentials, raw errors, stack traces, CLI arguments, terminal input, or file paths in SDK telemetry
|
|
601
|
+
- no prompt, response, chat history, user context, PHI/PII, payer, tenant, deployment, session, or user identifiers, credentials, raw errors, stack traces, CLI arguments, terminal input, or file paths in SDK telemetry
|
|
320
602
|
- generated snippets escape serialized config
|
|
321
603
|
- default React rendering escapes raw HTML
|
|
322
604
|
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/client.d.ts
CHANGED
|
@@ -1,21 +1,51 @@
|
|
|
1
1
|
import { type CareCompassConfig } from "./schema.js";
|
|
2
2
|
export type CareCompassAskRequest = {
|
|
3
3
|
prompt: string;
|
|
4
|
+
/** Stable client-generated id used to make a submit/retry idempotent. */
|
|
5
|
+
requestId?: string;
|
|
6
|
+
workflow?: CareCompassWorkflowId;
|
|
4
7
|
sessionId?: string;
|
|
5
8
|
userContext?: Record<string, unknown>;
|
|
6
9
|
};
|
|
10
|
+
export type CareCompassWorkflowId = "rcm-coding-assistance";
|
|
7
11
|
export type CareCompassAskResponse = {
|
|
8
12
|
jobId: string;
|
|
9
13
|
};
|
|
14
|
+
export type CareCompassRetrievalReference = {
|
|
15
|
+
referenceId: string;
|
|
16
|
+
documentId: string;
|
|
17
|
+
title: string;
|
|
18
|
+
version: string;
|
|
19
|
+
excerpt: string;
|
|
20
|
+
};
|
|
21
|
+
export type CareCompassRetrievalMetadata = {
|
|
22
|
+
corpusId: string;
|
|
23
|
+
corpusVersion: string;
|
|
24
|
+
outcome: string;
|
|
25
|
+
latencyMs: number;
|
|
26
|
+
references: CareCompassRetrievalReference[];
|
|
27
|
+
};
|
|
10
28
|
export type CareCompassStatusResponse = {
|
|
11
29
|
status: "queued" | "running" | "complete" | "failed";
|
|
12
30
|
result?: string;
|
|
31
|
+
retrieval?: CareCompassRetrievalMetadata;
|
|
13
32
|
};
|
|
14
33
|
type Fetcher = typeof fetch;
|
|
15
|
-
export
|
|
34
|
+
export type CareCompassRuntimeTokenRequest = {
|
|
35
|
+
payerId: string;
|
|
36
|
+
environment: CareCompassConfig["environment"];
|
|
37
|
+
embedKey: string;
|
|
38
|
+
workflow?: CareCompassWorkflowId;
|
|
39
|
+
jobId?: string;
|
|
40
|
+
};
|
|
41
|
+
/** Obtains a short-lived production token from the integrator's BFF. */
|
|
42
|
+
export type CareCompassRuntimeTokenProvider = (request: CareCompassRuntimeTokenRequest) => Promise<string> | string;
|
|
43
|
+
export type CareCompassClientOptions = {
|
|
16
44
|
fetcher?: Fetcher;
|
|
17
45
|
now?: () => number;
|
|
18
|
-
|
|
46
|
+
runtimeTokenProvider?: CareCompassRuntimeTokenProvider;
|
|
47
|
+
};
|
|
48
|
+
export declare function createCareCompassClient(inputConfig: CareCompassConfig, options?: CareCompassClientOptions): {
|
|
19
49
|
validateConfig: () => Promise<{
|
|
20
50
|
ok: true;
|
|
21
51
|
}>;
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { validateCareCompassConfig } from "./schema.js";
|
|
2
2
|
import { CARECOMPASS_TELEMETRY_ERROR_CODES, CARECOMPASS_TELEMETRY_EVENTS, createCareCompassTelemetry, } from "./telemetry.js";
|
|
3
|
+
import { CARECOMPASS_SDK_VERSION } from "./version.js";
|
|
3
4
|
// Internal error metadata keeps telemetry classification separate from messages,
|
|
4
5
|
// which may include user-visible API details that should not be emitted.
|
|
5
6
|
class CareCompassClientError extends Error {
|
|
@@ -105,6 +106,30 @@ export function createCareCompassClient(inputConfig, options = {}) {
|
|
|
105
106
|
"x-carecompass-environment": config.environment,
|
|
106
107
|
};
|
|
107
108
|
const jobStartedAt = new Map();
|
|
109
|
+
const jobWorkflows = new Map();
|
|
110
|
+
async function requestHeaders(request = {}) {
|
|
111
|
+
if (config.environment !== "production")
|
|
112
|
+
return headers;
|
|
113
|
+
if (!options.runtimeTokenProvider) {
|
|
114
|
+
throw new CareCompassClientError("Production CareCompass embeds require a runtimeTokenProvider backed by your server.", CARECOMPASS_TELEMETRY_ERROR_CODES.AUTHENTICATION_FAILED, "authentication");
|
|
115
|
+
}
|
|
116
|
+
let token;
|
|
117
|
+
try {
|
|
118
|
+
token = await options.runtimeTokenProvider({
|
|
119
|
+
payerId: config.payerId,
|
|
120
|
+
environment: config.environment,
|
|
121
|
+
embedKey: config.embedKey,
|
|
122
|
+
...request,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
throw new CareCompassClientError("CareCompass runtime authorization could not be obtained.", CARECOMPASS_TELEMETRY_ERROR_CODES.AUTHENTICATION_FAILED, "authentication");
|
|
127
|
+
}
|
|
128
|
+
if (typeof token !== "string" || !token.trim()) {
|
|
129
|
+
throw new CareCompassClientError("CareCompass runtime authorization could not be obtained.", CARECOMPASS_TELEMETRY_ERROR_CODES.AUTHENTICATION_FAILED, "authentication");
|
|
130
|
+
}
|
|
131
|
+
return { ...headers, "x-carecompass-runtime-token": token.trim() };
|
|
132
|
+
}
|
|
108
133
|
telemetry.capture(CARECOMPASS_TELEMETRY_EVENTS.SDK_INITIALIZED);
|
|
109
134
|
telemetry.capture(CARECOMPASS_TELEMETRY_EVENTS.THEME_LOADED);
|
|
110
135
|
telemetry.capture(CARECOMPASS_TELEMETRY_EVENTS.PROMPT_CONFIGURATION_LOADED);
|
|
@@ -137,7 +162,7 @@ export function createCareCompassClient(inputConfig, options = {}) {
|
|
|
137
162
|
try {
|
|
138
163
|
const response = await send(joinUrl(config.apiBaseUrl, "/api/carecompass/embed/config/validate"), {
|
|
139
164
|
method: "POST",
|
|
140
|
-
headers,
|
|
165
|
+
headers: await requestHeaders(),
|
|
141
166
|
body: JSON.stringify({ config }),
|
|
142
167
|
});
|
|
143
168
|
const result = await parseJson(response);
|
|
@@ -155,18 +180,27 @@ export function createCareCompassClient(inputConfig, options = {}) {
|
|
|
155
180
|
try {
|
|
156
181
|
const response = await send(joinUrl(config.apiBaseUrl, "/api/carecompass/embed/ask"), {
|
|
157
182
|
method: "POST",
|
|
158
|
-
headers,
|
|
183
|
+
headers: await requestHeaders({ workflow: request.workflow }),
|
|
159
184
|
body: JSON.stringify({
|
|
160
185
|
prompt: request.prompt,
|
|
186
|
+
requestId: request.requestId,
|
|
187
|
+
workflow: request.workflow,
|
|
161
188
|
sessionId: request.sessionId,
|
|
189
|
+
sdkVersion: CARECOMPASS_SDK_VERSION,
|
|
190
|
+
applicationSurface: config.deploymentTarget,
|
|
162
191
|
userContext: request.userContext,
|
|
163
192
|
promptConfig: config.prompt,
|
|
193
|
+
// Public routing preferences; the hosted runtime validates model
|
|
194
|
+
// availability and may ignore disabled feature flags.
|
|
195
|
+
model: config.model,
|
|
196
|
+
featureFlags: config.featureFlags,
|
|
164
197
|
}),
|
|
165
198
|
});
|
|
166
199
|
const result = await parseJson(response);
|
|
167
200
|
// Status polling is asynchronous, so retain the submit timestamp until
|
|
168
201
|
// the final job state lets telemetry report end-to-end latency.
|
|
169
202
|
jobStartedAt.set(result.jobId, startedAt);
|
|
203
|
+
jobWorkflows.set(result.jobId, request.workflow);
|
|
170
204
|
return result;
|
|
171
205
|
}
|
|
172
206
|
catch (error) {
|
|
@@ -177,16 +211,23 @@ export function createCareCompassClient(inputConfig, options = {}) {
|
|
|
177
211
|
status: async (jobId) => {
|
|
178
212
|
const startedAt = jobStartedAt.get(jobId) ?? now();
|
|
179
213
|
try {
|
|
180
|
-
const response = await send(joinUrl(config.apiBaseUrl, `/api/carecompass/embed/status/${encodeURIComponent(jobId)}`), {
|
|
214
|
+
const response = await send(joinUrl(config.apiBaseUrl, `/api/carecompass/embed/status/${encodeURIComponent(jobId)}`), {
|
|
215
|
+
headers: await requestHeaders({
|
|
216
|
+
jobId,
|
|
217
|
+
workflow: jobWorkflows.get(jobId),
|
|
218
|
+
}),
|
|
219
|
+
});
|
|
181
220
|
const result = await parseJson(response);
|
|
182
221
|
if (result.status === "complete") {
|
|
183
222
|
jobStartedAt.delete(jobId);
|
|
223
|
+
jobWorkflows.delete(jobId);
|
|
184
224
|
telemetry.capture(CARECOMPASS_TELEMETRY_EVENTS.INFERENCE_SUCCEEDED, {
|
|
185
225
|
durationMs: now() - startedAt,
|
|
186
226
|
});
|
|
187
227
|
}
|
|
188
228
|
else if (result.status === "failed") {
|
|
189
229
|
jobStartedAt.delete(jobId);
|
|
230
|
+
jobWorkflows.delete(jobId);
|
|
190
231
|
telemetry.capture(CARECOMPASS_TELEMETRY_EVENTS.INFERENCE_FAILED, {
|
|
191
232
|
durationMs: now() - startedAt,
|
|
192
233
|
errorCode: CARECOMPASS_TELEMETRY_ERROR_CODES.INFERENCE_FAILED,
|
|
@@ -197,6 +238,7 @@ export function createCareCompassClient(inputConfig, options = {}) {
|
|
|
197
238
|
}
|
|
198
239
|
catch (error) {
|
|
199
240
|
jobStartedAt.delete(jobId);
|
|
241
|
+
jobWorkflows.delete(jobId);
|
|
200
242
|
captureInferenceFailure(telemetry, error, now() - startedAt);
|
|
201
243
|
throw error;
|
|
202
244
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { CareCompassComplianceConfig } from "./schema.js";
|
|
2
|
+
/** The identifier categories detected by the SDK's local PHI preflight. */
|
|
3
|
+
export type CareCompassPhiCategory = "date_of_birth" | "email" | "medical_record_number" | "member_id" | "phone" | "ssn";
|
|
4
|
+
export type CareCompassPhiMatch = {
|
|
5
|
+
category: CareCompassPhiCategory;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
};
|
|
9
|
+
export type CareCompassPhiSource = "message" | "response" | "context";
|
|
10
|
+
export type CareCompassPhiDetectionContext = {
|
|
11
|
+
source: CareCompassPhiSource;
|
|
12
|
+
};
|
|
13
|
+
export type CareCompassPhiDetector = (value: string, context: CareCompassPhiDetectionContext) => CareCompassPhiMatch[];
|
|
14
|
+
export type CareCompassPhiRedactor = (value: string, matches: CareCompassPhiMatch[], replacement: string) => string;
|
|
15
|
+
export type CareCompassPhiHooks = {
|
|
16
|
+
/** Replaces the built-in detector. The hook receives raw content; never emit it. */
|
|
17
|
+
detect?: CareCompassPhiDetector;
|
|
18
|
+
/** Replaces the built-in redactor. The hook receives raw content; never emit it. */
|
|
19
|
+
redact?: CareCompassPhiRedactor;
|
|
20
|
+
};
|
|
21
|
+
export type CareCompassComplianceRuntimeOptions = {
|
|
22
|
+
phi?: CareCompassPhiHooks;
|
|
23
|
+
};
|
|
24
|
+
export type CareCompassCompliancePreset = "standard" | "healthcare" | "strict";
|
|
25
|
+
export type CareCompassPhiAction = "allow" | "redact" | "block";
|
|
26
|
+
export type CareCompassCompliancePolicy = {
|
|
27
|
+
auditEnabled: boolean;
|
|
28
|
+
phiAction: CareCompassPhiAction;
|
|
29
|
+
preset: CareCompassCompliancePreset;
|
|
30
|
+
replacement: string;
|
|
31
|
+
};
|
|
32
|
+
export declare const CARECOMPASS_COMPLIANCE_PRESETS: Readonly<Record<CareCompassCompliancePreset, Omit<CareCompassCompliancePolicy, "preset">>>;
|
|
33
|
+
export type CareCompassAuditEventType = "message" | "tool_call" | "widget_action" | "error" | "safety_decision";
|
|
34
|
+
export type CareCompassAuditOutcome = "submitted" | "completed" | "failed" | "blocked" | "redacted" | "escalated" | "recorded";
|
|
35
|
+
export type CareCompassAuditSource = CareCompassPhiSource | "tool_call" | "widget_action";
|
|
36
|
+
export type CareCompassAuditEvent = {
|
|
37
|
+
id: string;
|
|
38
|
+
occurredAt: number;
|
|
39
|
+
outcome: CareCompassAuditOutcome;
|
|
40
|
+
phi: {
|
|
41
|
+
categories: CareCompassPhiCategory[];
|
|
42
|
+
count: number;
|
|
43
|
+
};
|
|
44
|
+
source?: CareCompassAuditSource;
|
|
45
|
+
type: CareCompassAuditEventType;
|
|
46
|
+
};
|
|
47
|
+
export type CareCompassSafetyDecision = "blocked" | "escalated" | "redacted" | "policy_sensitive";
|
|
48
|
+
export type CareCompassSafetyEvent = {
|
|
49
|
+
decision: CareCompassSafetyDecision;
|
|
50
|
+
occurredAt: number;
|
|
51
|
+
phi: {
|
|
52
|
+
categories: CareCompassPhiCategory[];
|
|
53
|
+
count: number;
|
|
54
|
+
};
|
|
55
|
+
source: CareCompassPhiSource | "tool_call" | "widget_action";
|
|
56
|
+
};
|
|
57
|
+
export type CareCompassAuditListener = (event: CareCompassAuditEvent) => void;
|
|
58
|
+
export type CareCompassSafetyListener = (event: CareCompassSafetyEvent) => void;
|
|
59
|
+
export declare const detectCareCompassPhi: CareCompassPhiDetector;
|
|
60
|
+
export declare const redactCareCompassPhi: CareCompassPhiRedactor;
|
|
61
|
+
export declare const resolveCareCompassCompliancePolicy: (config: CareCompassComplianceConfig | undefined) => CareCompassCompliancePolicy;
|
|
62
|
+
export declare const phiSummary: (matches: CareCompassPhiMatch[]) => {
|
|
63
|
+
categories: CareCompassPhiCategory[];
|
|
64
|
+
count: number;
|
|
65
|
+
};
|
|
66
|
+
export declare const sanitizeCareCompassValue: (value: unknown, source: CareCompassPhiSource, policy: CareCompassCompliancePolicy, hooks: CareCompassPhiHooks | undefined) => {
|
|
67
|
+
matches: CareCompassPhiMatch[];
|
|
68
|
+
value: unknown;
|
|
69
|
+
};
|