@nexalink/carecompass-sdk 0.4.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 +282 -3
- package/dist/cli.js +0 -0
- 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 +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 +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 +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 +27 -12
- package/qa-fixtures/DEMO-ENC-1042-rcm-policy.md +27 -0
package/dist/component.d.ts
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { type CareCompassContext, type CareCompassCoreEvent, type CareCompassMessage } from "./core.js";
|
|
2
|
+
import { type CareCompassContext, type CareCompassCore, type CareCompassCoreEvent, type CareCompassCoreOptions, type CareCompassCoreSnapshot, type CareCompassMessage, type CareCompassSendMessageOptions } from "./core.js";
|
|
3
|
+
import type { CareCompassRuntimeTokenProvider } from "./client.js";
|
|
3
4
|
import type { CareCompassConfig } from "./schema.js";
|
|
5
|
+
import type { CareCompassAuditListener, CareCompassSafetyListener } from "./compliance.js";
|
|
4
6
|
export type CareCompassChatProps = {
|
|
5
7
|
config: CareCompassConfig;
|
|
6
8
|
initialContext?: CareCompassContext;
|
|
9
|
+
onAuditEvent?: CareCompassAuditListener;
|
|
7
10
|
onEvent?: (event: CareCompassCoreEvent) => void;
|
|
11
|
+
onSafetyEvent?: CareCompassSafetyListener;
|
|
8
12
|
pollIntervalMs?: number;
|
|
9
13
|
renderMessage?: (message: CareCompassMessage) => React.ReactNode;
|
|
10
14
|
retryLabel?: string;
|
|
15
|
+
runtimeTokenProvider?: CareCompassRuntimeTokenProvider;
|
|
11
16
|
};
|
|
12
|
-
|
|
17
|
+
/** Options for the headless React hook. Keep object-valued options stable with
|
|
18
|
+
* `useMemo` when the calling component rerenders. */
|
|
19
|
+
export type UseCareCompassOptions = CareCompassCoreOptions;
|
|
20
|
+
export type CareCompassReactController = {
|
|
21
|
+
core: CareCompassCore;
|
|
22
|
+
snapshot: CareCompassCoreSnapshot;
|
|
23
|
+
sendMessage: (prompt: string, options?: CareCompassSendMessageOptions) => Promise<CareCompassCoreSnapshot>;
|
|
24
|
+
streamMessage: CareCompassCore["streamMessage"];
|
|
25
|
+
retryLast: CareCompassCore["retryLast"];
|
|
26
|
+
reset: CareCompassCore["reset"];
|
|
27
|
+
setContext: CareCompassCore["setContext"];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* React adapter for the headless CareCompass core. It owns the core lifecycle
|
|
31
|
+
* and subscribes React to its snapshots, leaving hosts free to render any UI.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useCareCompass(config: CareCompassConfig, options?: UseCareCompassOptions): CareCompassReactController;
|
|
34
|
+
export declare function CareCompassChat({ config, initialContext, onAuditEvent, onEvent, onSafetyEvent, pollIntervalMs, renderMessage, retryLabel, runtimeTokenProvider, }: CareCompassChatProps): React.JSX.Element;
|
|
13
35
|
export declare const CareCompassEmbed: typeof CareCompassChat;
|
package/dist/component.js
CHANGED
|
@@ -26,11 +26,68 @@ class CareCompassErrorBoundary extends React.Component {
|
|
|
26
26
|
return this.props.children;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* React adapter for the headless CareCompass core. It owns the core lifecycle
|
|
31
|
+
* and subscribes React to its snapshots, leaving hosts free to render any UI.
|
|
32
|
+
*/
|
|
33
|
+
export function useCareCompass(config, options = {}) {
|
|
34
|
+
const { compliance, fetcher, initialContext, now, onAuditEvent, onSafetyEvent, pollIntervalMs, runtimeTokenProvider, timeoutMs, } = options;
|
|
30
35
|
const core = React.useMemo(() => createCareCompassCore(config, {
|
|
36
|
+
compliance,
|
|
37
|
+
fetcher,
|
|
31
38
|
initialContext,
|
|
39
|
+
now,
|
|
40
|
+
onAuditEvent,
|
|
41
|
+
onSafetyEvent,
|
|
32
42
|
pollIntervalMs,
|
|
33
|
-
|
|
43
|
+
runtimeTokenProvider,
|
|
44
|
+
timeoutMs,
|
|
45
|
+
}), [
|
|
46
|
+
compliance,
|
|
47
|
+
config,
|
|
48
|
+
fetcher,
|
|
49
|
+
initialContext,
|
|
50
|
+
now,
|
|
51
|
+
onAuditEvent,
|
|
52
|
+
onSafetyEvent,
|
|
53
|
+
pollIntervalMs,
|
|
54
|
+
runtimeTokenProvider,
|
|
55
|
+
timeoutMs,
|
|
56
|
+
]);
|
|
57
|
+
const [snapshot, setSnapshot] = React.useState(() => core.getSnapshot());
|
|
58
|
+
React.useEffect(() => {
|
|
59
|
+
const unsubscribe = core.subscribe(setSnapshot);
|
|
60
|
+
setSnapshot(core.getSnapshot());
|
|
61
|
+
return () => {
|
|
62
|
+
unsubscribe();
|
|
63
|
+
core.dispose();
|
|
64
|
+
};
|
|
65
|
+
}, [core]);
|
|
66
|
+
return React.useMemo(() => ({
|
|
67
|
+
core,
|
|
68
|
+
snapshot,
|
|
69
|
+
sendMessage: core.sendMessage,
|
|
70
|
+
streamMessage: core.streamMessage,
|
|
71
|
+
retryLast: core.retryLast,
|
|
72
|
+
reset: core.reset,
|
|
73
|
+
setContext: core.setContext,
|
|
74
|
+
}), [core, snapshot]);
|
|
75
|
+
}
|
|
76
|
+
function CareCompassChatContent({ config, initialContext, onAuditEvent, onEvent, onSafetyEvent, telemetry, pollIntervalMs = 1500, renderMessage, retryLabel = "Retry", runtimeTokenProvider, }) {
|
|
77
|
+
const core = React.useMemo(() => createCareCompassCore(config, {
|
|
78
|
+
initialContext,
|
|
79
|
+
onAuditEvent,
|
|
80
|
+
onSafetyEvent,
|
|
81
|
+
pollIntervalMs,
|
|
82
|
+
runtimeTokenProvider,
|
|
83
|
+
}), [
|
|
84
|
+
config,
|
|
85
|
+
initialContext,
|
|
86
|
+
onAuditEvent,
|
|
87
|
+
onSafetyEvent,
|
|
88
|
+
pollIntervalMs,
|
|
89
|
+
runtimeTokenProvider,
|
|
90
|
+
]);
|
|
34
91
|
const [snapshot, setSnapshot] = React.useState(() => core.getSnapshot());
|
|
35
92
|
const [input, setInput] = React.useState("");
|
|
36
93
|
React.useEffect(() => {
|
|
@@ -51,6 +108,7 @@ function CareCompassChatContent({ config, initialContext, onEvent, telemetry, po
|
|
|
51
108
|
return;
|
|
52
109
|
setInput("");
|
|
53
110
|
try {
|
|
111
|
+
core.recordWidgetAction();
|
|
54
112
|
for await (const coreEvent of core.streamMessage(prompt)) {
|
|
55
113
|
onEvent?.(coreEvent);
|
|
56
114
|
}
|
|
@@ -62,6 +120,7 @@ function CareCompassChatContent({ config, initialContext, onEvent, telemetry, po
|
|
|
62
120
|
}
|
|
63
121
|
async function retryMessage() {
|
|
64
122
|
try {
|
|
123
|
+
core.recordWidgetAction();
|
|
65
124
|
await core.retryLast();
|
|
66
125
|
}
|
|
67
126
|
catch {
|
|
@@ -154,10 +213,10 @@ function CareCompassChatContent({ config, initialContext, onEvent, telemetry, po
|
|
|
154
213
|
padding: "4px 8px",
|
|
155
214
|
}, type: "button", children: retryLabel })) : null] })) : null, _jsx("div", { style: { fontSize: 11, opacity: 0.7 }, children: config.labels.disclaimer })] })] }));
|
|
156
215
|
}
|
|
157
|
-
export function CareCompassChat({ config, initialContext, onEvent, pollIntervalMs = 1500, renderMessage, retryLabel, }) {
|
|
216
|
+
export function CareCompassChat({ config, initialContext, onAuditEvent, onEvent, onSafetyEvent, pollIntervalMs = 1500, renderMessage, retryLabel, runtimeTokenProvider, }) {
|
|
158
217
|
// Telemetry is scoped to the validated public config; no per-message content
|
|
159
218
|
// is passed through this component-level telemetry object.
|
|
160
219
|
const telemetry = React.useMemo(() => createCareCompassTelemetry(config), [config]);
|
|
161
|
-
return (_jsx(CareCompassErrorBoundary, { config: config, telemetry: telemetry, children: _jsx(CareCompassChatContent, { config: config, initialContext: initialContext, onEvent: onEvent, pollIntervalMs: pollIntervalMs, renderMessage: renderMessage, retryLabel: retryLabel, telemetry: telemetry }) }));
|
|
220
|
+
return (_jsx(CareCompassErrorBoundary, { config: config, telemetry: telemetry, children: _jsx(CareCompassChatContent, { config: config, initialContext: initialContext, onAuditEvent: onAuditEvent, onEvent: onEvent, onSafetyEvent: onSafetyEvent, pollIntervalMs: pollIntervalMs, renderMessage: renderMessage, retryLabel: retryLabel, runtimeTokenProvider: runtimeTokenProvider, telemetry: telemetry }) }));
|
|
162
221
|
}
|
|
163
222
|
export const CareCompassEmbed = CareCompassChat;
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type CareCompassStatusResponse } from "./client.js";
|
|
1
|
+
import { type CareCompassRuntimeTokenProvider, type CareCompassStatusResponse, type CareCompassWorkflowId } from "./client.js";
|
|
2
|
+
import { type CareCompassAuditEvent, type CareCompassAuditListener, type CareCompassComplianceRuntimeOptions, type CareCompassSafetyDecision, type CareCompassSafetyListener } from "./compliance.js";
|
|
2
3
|
import type { CareCompassConfig } from "./schema.js";
|
|
3
4
|
type Fetcher = typeof fetch;
|
|
4
5
|
export type CareCompassContext = Record<string, unknown>;
|
|
@@ -8,15 +9,18 @@ export type CareCompassMessage = {
|
|
|
8
9
|
text: string;
|
|
9
10
|
createdAt: number;
|
|
10
11
|
status: "complete" | "failed" | "pending";
|
|
12
|
+
retrieval?: CareCompassStatusResponse["retrieval"];
|
|
11
13
|
};
|
|
12
14
|
export type CareCompassLifecyclePhase = "idle" | "submitting" | "streaming" | "complete" | "error" | "disposed";
|
|
13
|
-
export type CareCompassCoreErrorCode = "request_failed" | "timeout" | "disposed";
|
|
15
|
+
export type CareCompassCoreErrorCode = "request_failed" | "safety_blocked" | "timeout" | "disposed";
|
|
14
16
|
export type CareCompassCoreError = {
|
|
15
17
|
code: CareCompassCoreErrorCode;
|
|
16
18
|
message: string;
|
|
17
19
|
};
|
|
18
20
|
export type CareCompassRetryMetadata = {
|
|
19
21
|
prompt: string;
|
|
22
|
+
requestId?: string;
|
|
23
|
+
workflow?: CareCompassWorkflowId;
|
|
20
24
|
context: CareCompassContext;
|
|
21
25
|
attempts: number;
|
|
22
26
|
};
|
|
@@ -55,18 +59,31 @@ export type CareCompassCoreEvent = {
|
|
|
55
59
|
snapshot: CareCompassCoreSnapshot;
|
|
56
60
|
};
|
|
57
61
|
export type CareCompassCoreOptions = {
|
|
62
|
+
compliance?: CareCompassComplianceRuntimeOptions;
|
|
58
63
|
fetcher?: Fetcher;
|
|
59
64
|
initialContext?: CareCompassContext;
|
|
60
65
|
now?: () => number;
|
|
66
|
+
onAuditEvent?: CareCompassAuditListener;
|
|
67
|
+
onSafetyEvent?: CareCompassSafetyListener;
|
|
61
68
|
pollIntervalMs?: number;
|
|
69
|
+
runtimeTokenProvider?: CareCompassRuntimeTokenProvider;
|
|
62
70
|
timeoutMs?: number;
|
|
63
71
|
};
|
|
64
72
|
export type CareCompassSendMessageOptions = {
|
|
73
|
+
/** Reuse this value only when retrying the same hosted submission. */
|
|
74
|
+
requestId?: string;
|
|
75
|
+
workflow?: CareCompassWorkflowId;
|
|
65
76
|
context?: CareCompassContext;
|
|
66
77
|
};
|
|
67
78
|
export type CareCompassCore = {
|
|
79
|
+
exportAuditTrail: () => CareCompassAuditEvent[];
|
|
68
80
|
getSnapshot: () => CareCompassCoreSnapshot;
|
|
81
|
+
recordSafetyDecision: (decision: Exclude<CareCompassSafetyDecision, "redacted" | "blocked">, source?: "tool_call" | "widget_action") => void;
|
|
82
|
+
recordToolCall: (outcome?: "completed" | "failed") => void;
|
|
83
|
+
recordWidgetAction: (outcome?: "recorded" | "completed") => void;
|
|
69
84
|
subscribe: (listener: (snapshot: CareCompassCoreSnapshot) => void) => () => void;
|
|
85
|
+
subscribeAudit: (listener: CareCompassAuditListener) => () => void;
|
|
86
|
+
subscribeSafety: (listener: CareCompassSafetyListener) => () => void;
|
|
70
87
|
setContext: (context: CareCompassContext | ((current: CareCompassContext) => CareCompassContext)) => void;
|
|
71
88
|
sendMessage: (prompt: string, options?: CareCompassSendMessageOptions) => Promise<CareCompassCoreSnapshot>;
|
|
72
89
|
streamMessage: (prompt: string, options?: CareCompassSendMessageOptions) => AsyncIterable<CareCompassCoreEvent>;
|
package/dist/core.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createCareCompassClient, } from "./client.js";
|
|
2
|
+
import { phiSummary, resolveCareCompassCompliancePolicy, sanitizeCareCompassValue, } from "./compliance.js";
|
|
2
3
|
const sleep = (durationMs) => new Promise((resolve) => setTimeout(resolve, durationMs));
|
|
3
4
|
const errorFrom = (error, code = "request_failed") => ({
|
|
4
5
|
code,
|
|
@@ -11,7 +12,16 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
11
12
|
const client = createCareCompassClient(config, {
|
|
12
13
|
fetcher: options.fetcher,
|
|
13
14
|
now,
|
|
15
|
+
runtimeTokenProvider: options.runtimeTokenProvider,
|
|
14
16
|
});
|
|
17
|
+
const compliancePolicy = resolveCareCompassCompliancePolicy(config.compliance);
|
|
18
|
+
const auditEvents = [];
|
|
19
|
+
const auditListeners = new Set();
|
|
20
|
+
const safetyListeners = new Set();
|
|
21
|
+
if (options.onAuditEvent)
|
|
22
|
+
auditListeners.add(options.onAuditEvent);
|
|
23
|
+
if (options.onSafetyEvent)
|
|
24
|
+
safetyListeners.add(options.onSafetyEvent);
|
|
15
25
|
const listeners = new Set();
|
|
16
26
|
let sequence = 0;
|
|
17
27
|
let disposed = false;
|
|
@@ -30,6 +40,44 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
30
40
|
},
|
|
31
41
|
];
|
|
32
42
|
const nextId = (prefix) => `${prefix}-${now()}-${++sequence}`;
|
|
43
|
+
const emitAudit = (type, outcome, source, matches = []) => {
|
|
44
|
+
if (!compliancePolicy.auditEnabled)
|
|
45
|
+
return;
|
|
46
|
+
const auditEvent = {
|
|
47
|
+
id: nextId("audit"),
|
|
48
|
+
occurredAt: now(),
|
|
49
|
+
outcome,
|
|
50
|
+
phi: phiSummary(matches),
|
|
51
|
+
...(source ? { source } : {}),
|
|
52
|
+
type,
|
|
53
|
+
};
|
|
54
|
+
auditEvents.push(auditEvent);
|
|
55
|
+
for (const listener of auditListeners)
|
|
56
|
+
listener(auditEvent);
|
|
57
|
+
};
|
|
58
|
+
const emitSafety = (decision, source, matches = []) => {
|
|
59
|
+
const safetyEvent = {
|
|
60
|
+
decision,
|
|
61
|
+
occurredAt: now(),
|
|
62
|
+
phi: phiSummary(matches),
|
|
63
|
+
source,
|
|
64
|
+
};
|
|
65
|
+
for (const listener of safetyListeners)
|
|
66
|
+
listener(safetyEvent);
|
|
67
|
+
emitAudit("safety_decision", decision === "policy_sensitive" ? "recorded" : decision, source, matches);
|
|
68
|
+
};
|
|
69
|
+
const applyPhiPolicy = (value, source) => {
|
|
70
|
+
const sanitized = sanitizeCareCompassValue(value, source, compliancePolicy, options.compliance?.phi);
|
|
71
|
+
if (sanitized.matches.length === 0)
|
|
72
|
+
return sanitized;
|
|
73
|
+
const decision = compliancePolicy.phiAction === "block"
|
|
74
|
+
? "blocked"
|
|
75
|
+
: compliancePolicy.phiAction === "redact"
|
|
76
|
+
? "redacted"
|
|
77
|
+
: "policy_sensitive";
|
|
78
|
+
emitSafety(decision, source, sanitized.matches);
|
|
79
|
+
return sanitized;
|
|
80
|
+
};
|
|
33
81
|
const getSnapshot = () => ({
|
|
34
82
|
messages: messages.map((message) => ({ ...message })),
|
|
35
83
|
phase,
|
|
@@ -40,6 +88,8 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
40
88
|
retry: retry
|
|
41
89
|
? {
|
|
42
90
|
prompt: retry.prompt,
|
|
91
|
+
requestId: retry.requestId,
|
|
92
|
+
workflow: retry.workflow,
|
|
43
93
|
context: { ...retry.context },
|
|
44
94
|
attempts: retry.attempts,
|
|
45
95
|
}
|
|
@@ -56,6 +106,7 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
56
106
|
error = coreError;
|
|
57
107
|
phase = disposed ? "disposed" : "error";
|
|
58
108
|
activeJobId = null;
|
|
109
|
+
emitAudit("error", "failed");
|
|
59
110
|
notify();
|
|
60
111
|
return event({ type: "error", error: coreError });
|
|
61
112
|
};
|
|
@@ -72,14 +123,33 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
72
123
|
const prompt = promptInput.trim();
|
|
73
124
|
if (!prompt)
|
|
74
125
|
return;
|
|
75
|
-
const
|
|
126
|
+
const protectedPrompt = applyPhiPolicy(prompt, "message");
|
|
127
|
+
const protectedContext = applyPhiPolicy({
|
|
76
128
|
...context,
|
|
77
129
|
...(sendOptions.context ?? {}),
|
|
78
|
-
};
|
|
130
|
+
}, "context");
|
|
131
|
+
if (compliancePolicy.phiAction === "block" &&
|
|
132
|
+
(protectedPrompt.matches.length > 0 ||
|
|
133
|
+
protectedContext.matches.length > 0)) {
|
|
134
|
+
emitAudit("message", "blocked", "message", protectedPrompt.matches);
|
|
135
|
+
const blocked = {
|
|
136
|
+
code: "safety_blocked",
|
|
137
|
+
message: "CareCompass blocked this request because detectable PHI is not allowed in strict compliance mode.",
|
|
138
|
+
};
|
|
139
|
+
retry = null;
|
|
140
|
+
yield fail(blocked);
|
|
141
|
+
throw new Error(blocked.message);
|
|
142
|
+
}
|
|
143
|
+
const requestContext = protectedContext.value;
|
|
144
|
+
const protectedPromptValue = protectedPrompt.value;
|
|
145
|
+
const requestId = sendOptions.requestId ??
|
|
146
|
+
(typeof globalThis.crypto?.randomUUID === "function"
|
|
147
|
+
? globalThis.crypto.randomUUID()
|
|
148
|
+
: nextId("request"));
|
|
79
149
|
const userMessage = {
|
|
80
150
|
id: nextId("user"),
|
|
81
151
|
role: "user",
|
|
82
|
-
text:
|
|
152
|
+
text: protectedPromptValue,
|
|
83
153
|
createdAt: now(),
|
|
84
154
|
status: "complete",
|
|
85
155
|
};
|
|
@@ -89,12 +159,18 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
89
159
|
activeJobId = null;
|
|
90
160
|
retry = null;
|
|
91
161
|
notify();
|
|
162
|
+
emitAudit("message", protectedPrompt.matches.length > 0 &&
|
|
163
|
+
compliancePolicy.phiAction === "redact"
|
|
164
|
+
? "redacted"
|
|
165
|
+
: "submitted", "message", protectedPrompt.matches);
|
|
92
166
|
yield event({ type: "message_submitted", message: userMessage });
|
|
93
167
|
const startedAt = now();
|
|
94
168
|
let failureAlreadyCaptured = false;
|
|
95
169
|
try {
|
|
96
170
|
const { jobId } = await client.ask({
|
|
97
|
-
prompt,
|
|
171
|
+
prompt: protectedPromptValue,
|
|
172
|
+
requestId,
|
|
173
|
+
workflow: sendOptions.workflow,
|
|
98
174
|
userContext: requestContext,
|
|
99
175
|
});
|
|
100
176
|
activeJobId = jobId;
|
|
@@ -104,12 +180,25 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
104
180
|
const status = await client.status(jobId);
|
|
105
181
|
yield event({ type: "status", jobId, status: status.status });
|
|
106
182
|
if (status.status === "complete" && status.result) {
|
|
183
|
+
const protectedResponse = applyPhiPolicy(status.result, "response");
|
|
184
|
+
if (compliancePolicy.phiAction === "block" &&
|
|
185
|
+
protectedResponse.matches.length > 0) {
|
|
186
|
+
const blocked = {
|
|
187
|
+
code: "safety_blocked",
|
|
188
|
+
message: "CareCompass blocked a response because detectable PHI is not allowed in strict compliance mode.",
|
|
189
|
+
};
|
|
190
|
+
retry = null;
|
|
191
|
+
yield fail(blocked);
|
|
192
|
+
failureAlreadyCaptured = true;
|
|
193
|
+
throw new Error(blocked.message);
|
|
194
|
+
}
|
|
107
195
|
const assistantMessage = {
|
|
108
196
|
id: nextId("assistant"),
|
|
109
197
|
role: "assistant",
|
|
110
|
-
text:
|
|
198
|
+
text: protectedResponse.value,
|
|
111
199
|
createdAt: now(),
|
|
112
200
|
status: "complete",
|
|
201
|
+
retrieval: status.retrieval,
|
|
113
202
|
};
|
|
114
203
|
messages = [...messages, assistantMessage];
|
|
115
204
|
phase = "complete";
|
|
@@ -117,6 +206,10 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
117
206
|
activeJobId = null;
|
|
118
207
|
retry = null;
|
|
119
208
|
notify();
|
|
209
|
+
emitAudit("message", protectedResponse.matches.length > 0 &&
|
|
210
|
+
compliancePolicy.phiAction === "redact"
|
|
211
|
+
? "redacted"
|
|
212
|
+
: "completed", "response", protectedResponse.matches);
|
|
120
213
|
yield event({
|
|
121
214
|
type: "message_completed",
|
|
122
215
|
message: assistantMessage,
|
|
@@ -125,7 +218,12 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
125
218
|
}
|
|
126
219
|
if (status.status === "failed") {
|
|
127
220
|
const failed = errorFrom(new Error(status.result ?? config.labels.errorMessage));
|
|
128
|
-
retry = {
|
|
221
|
+
retry = {
|
|
222
|
+
prompt: protectedPromptValue,
|
|
223
|
+
workflow: sendOptions.workflow,
|
|
224
|
+
context: requestContext,
|
|
225
|
+
attempts: 0,
|
|
226
|
+
};
|
|
129
227
|
yield fail(failed);
|
|
130
228
|
failureAlreadyCaptured = true;
|
|
131
229
|
throw new Error(failed.message);
|
|
@@ -135,7 +233,13 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
135
233
|
}
|
|
136
234
|
client.recordInferenceTimeout(jobId, now() - startedAt);
|
|
137
235
|
const timedOut = errorFrom(new Error("CareCompass request timed out."), "timeout");
|
|
138
|
-
retry = {
|
|
236
|
+
retry = {
|
|
237
|
+
prompt: protectedPromptValue,
|
|
238
|
+
requestId,
|
|
239
|
+
workflow: sendOptions.workflow,
|
|
240
|
+
context: requestContext,
|
|
241
|
+
attempts: 0,
|
|
242
|
+
};
|
|
139
243
|
yield fail(timedOut);
|
|
140
244
|
failureAlreadyCaptured = true;
|
|
141
245
|
throw new Error(timedOut.message);
|
|
@@ -143,20 +247,44 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
143
247
|
catch (caught) {
|
|
144
248
|
if (failureAlreadyCaptured)
|
|
145
249
|
throw caught;
|
|
146
|
-
retry = {
|
|
250
|
+
retry = {
|
|
251
|
+
prompt: protectedPromptValue,
|
|
252
|
+
requestId,
|
|
253
|
+
workflow: sendOptions.workflow,
|
|
254
|
+
context: requestContext,
|
|
255
|
+
attempts: 0,
|
|
256
|
+
};
|
|
147
257
|
const requestError = errorFrom(caught);
|
|
148
258
|
yield fail(requestError);
|
|
149
259
|
throw caught instanceof Error ? caught : new Error(requestError.message);
|
|
150
260
|
}
|
|
151
261
|
}
|
|
152
262
|
return {
|
|
263
|
+
exportAuditTrail: () => auditEvents.map((auditEvent) => ({
|
|
264
|
+
...auditEvent,
|
|
265
|
+
phi: {
|
|
266
|
+
categories: [...auditEvent.phi.categories],
|
|
267
|
+
count: auditEvent.phi.count,
|
|
268
|
+
},
|
|
269
|
+
})),
|
|
153
270
|
getSnapshot,
|
|
271
|
+
recordSafetyDecision: (decision, source = "tool_call") => emitSafety(decision, source),
|
|
272
|
+
recordToolCall: (outcome = "completed") => emitAudit("tool_call", outcome, "tool_call"),
|
|
273
|
+
recordWidgetAction: (outcome = "recorded") => emitAudit("widget_action", outcome, "widget_action"),
|
|
154
274
|
subscribe: (listener) => {
|
|
155
275
|
listeners.add(listener);
|
|
156
276
|
return () => {
|
|
157
277
|
listeners.delete(listener);
|
|
158
278
|
};
|
|
159
279
|
},
|
|
280
|
+
subscribeAudit: (listener) => {
|
|
281
|
+
auditListeners.add(listener);
|
|
282
|
+
return () => auditListeners.delete(listener);
|
|
283
|
+
},
|
|
284
|
+
subscribeSafety: (listener) => {
|
|
285
|
+
safetyListeners.add(listener);
|
|
286
|
+
return () => safetyListeners.delete(listener);
|
|
287
|
+
},
|
|
160
288
|
setContext: (nextContext) => {
|
|
161
289
|
ensureAvailable();
|
|
162
290
|
context =
|
|
@@ -206,10 +334,14 @@ export function createCareCompassCore(config, options = {}) {
|
|
|
206
334
|
phase = "disposed";
|
|
207
335
|
activeJobId = null;
|
|
208
336
|
listeners.clear();
|
|
337
|
+
auditListeners.clear();
|
|
338
|
+
safetyListeners.clear();
|
|
209
339
|
},
|
|
210
340
|
};
|
|
211
341
|
async function streamRetry(retryRequest) {
|
|
212
342
|
const iterator = streamMessage(retryRequest.prompt, {
|
|
343
|
+
requestId: retryRequest.requestId,
|
|
344
|
+
workflow: retryRequest.workflow,
|
|
213
345
|
context: retryRequest.context,
|
|
214
346
|
})[Symbol.asyncIterator]();
|
|
215
347
|
while (!(await iterator.next()).done) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
export { createCareCompassClient } from "./client.js";
|
|
2
|
-
export type { CareCompassAskRequest, CareCompassAskResponse, CareCompassStatusResponse, } from "./client.js";
|
|
3
|
-
export { CareCompassChat, CareCompassEmbed } from "./component.js";
|
|
4
|
-
export type { CareCompassChatProps } from "./component.js";
|
|
2
|
+
export type { CareCompassAskRequest, CareCompassAskResponse, CareCompassClientOptions, CareCompassRetrievalMetadata, CareCompassRetrievalReference, CareCompassRuntimeTokenProvider, CareCompassRuntimeTokenRequest, CareCompassStatusResponse, CareCompassWorkflowId, } from "./client.js";
|
|
3
|
+
export { CareCompassChat, CareCompassEmbed, useCareCompass, } from "./component.js";
|
|
4
|
+
export type { CareCompassChatProps, CareCompassReactController, UseCareCompassOptions, } from "./component.js";
|
|
5
5
|
export { createCareCompassCore } from "./core.js";
|
|
6
6
|
export type { CareCompassContext, CareCompassCore, CareCompassCoreError, CareCompassCoreErrorCode, CareCompassCoreEvent, CareCompassCoreOptions, CareCompassCoreSnapshot, CareCompassLifecyclePhase, CareCompassMessage, CareCompassRetryMetadata, CareCompassSendMessageOptions, } from "./core.js";
|
|
7
|
+
export { CARECOMPASS_RCM_SYNTHETIC_DEMO, createRcmCodingWorkflow, RcmCodingAssistant, } from "./rcm.js";
|
|
8
|
+
export type { RcmCodeRecommendation, RcmCodeSystem, RcmCodingAssistanceInput, RcmCodingAssistanceResult, RcmCodingAssistantProps, RcmCodingWorkflow, RcmCodingWorkflowOptions, RcmConfidence, RcmCustomerRules, RcmPolicyReference, RcmPolicyRetriever, RcmReviewDisposition, } from "./rcm.js";
|
|
7
9
|
export { mountCareCompass } from "./mount.js";
|
|
10
|
+
export { CARECOMPASS_ELEMENT_TAG, defineCareCompassElement, } from "./web-component.js";
|
|
11
|
+
export type { CareCompassWebComponentElement } from "./web-component.js";
|
|
8
12
|
export { generateCareCompassIframeSnippet, generateCareCompassSnippet, } from "./snippet.js";
|
|
9
|
-
export { careCompassApiBaseUrlSchema, careCompassConfigSchema, createDefaultCareCompassConfig, validateCareCompassConfig, } from "./schema.js";
|
|
10
|
-
export type { CareCompassConfig, CareCompassConfigInput, CareCompassLabels, CareCompassPromptConfig, CareCompassTheme, CareCompassValidationResult, } from "./schema.js";
|
|
13
|
+
export { careCompassApiBaseUrlSchema, careCompassComplianceConfigSchema, careCompassConfigSchema, careCompassFeatureFlagsSchema, careCompassModelConfigSchema, createDefaultCareCompassConfig, validateCareCompassConfig, } from "./schema.js";
|
|
14
|
+
export type { CareCompassComplianceConfig, CareCompassConfig, CareCompassConfigInput, CareCompassFeatureFlags, CareCompassLabels, CareCompassModelConfig, CareCompassPromptConfig, CareCompassTheme, CareCompassValidationResult, } from "./schema.js";
|
|
15
|
+
export { CARECOMPASS_COMPLIANCE_PRESETS, detectCareCompassPhi, redactCareCompassPhi, resolveCareCompassCompliancePolicy, } from "./compliance.js";
|
|
16
|
+
export type { CareCompassAuditEvent, CareCompassAuditEventType, CareCompassAuditListener, CareCompassAuditOutcome, CareCompassAuditSource, CareCompassCompliancePolicy, CareCompassCompliancePreset, CareCompassComplianceRuntimeOptions, CareCompassPhiAction, CareCompassPhiCategory, CareCompassPhiDetectionContext, CareCompassPhiDetector, CareCompassPhiHooks, CareCompassPhiMatch, CareCompassPhiRedactor, CareCompassPhiSource, CareCompassSafetyDecision, CareCompassSafetyEvent, CareCompassSafetyListener, } from "./compliance.js";
|
|
11
17
|
export { CARECOMPASS_TELEMETRY_ERROR_CODES, CARECOMPASS_TELEMETRY_EVENTS, careCompassTelemetryDurationBucket, createCareCompassCliTelemetry, createCareCompassTelemetry, } from "./telemetry.js";
|
|
12
18
|
export type { CareCompassTelemetry, CareCompassTelemetryCaptureOptions, CareCompassTelemetryDurationBucket, CareCompassTelemetryErrorCode, CareCompassTelemetryEvent, CareCompassTelemetryFailureCategory, } from "./telemetry.js";
|
|
13
19
|
export { CARECOMPASS_COMPONENT_VERSION, CARECOMPASS_SDK_VERSION, CARECOMPASS_TELEMETRY_CONTRACT_VERSION, } from "./version.js";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export { createCareCompassClient } from "./client.js";
|
|
2
|
-
export { CareCompassChat, CareCompassEmbed } from "./component.js";
|
|
2
|
+
export { CareCompassChat, CareCompassEmbed, useCareCompass, } from "./component.js";
|
|
3
3
|
export { createCareCompassCore } from "./core.js";
|
|
4
|
+
export { CARECOMPASS_RCM_SYNTHETIC_DEMO, createRcmCodingWorkflow, RcmCodingAssistant, } from "./rcm.js";
|
|
4
5
|
export { mountCareCompass } from "./mount.js";
|
|
6
|
+
export { CARECOMPASS_ELEMENT_TAG, defineCareCompassElement, } from "./web-component.js";
|
|
5
7
|
export { generateCareCompassIframeSnippet, generateCareCompassSnippet, } from "./snippet.js";
|
|
6
|
-
export { careCompassApiBaseUrlSchema, careCompassConfigSchema, createDefaultCareCompassConfig, validateCareCompassConfig, } from "./schema.js";
|
|
8
|
+
export { careCompassApiBaseUrlSchema, careCompassComplianceConfigSchema, careCompassConfigSchema, careCompassFeatureFlagsSchema, careCompassModelConfigSchema, createDefaultCareCompassConfig, validateCareCompassConfig, } from "./schema.js";
|
|
9
|
+
export { CARECOMPASS_COMPLIANCE_PRESETS, detectCareCompassPhi, redactCareCompassPhi, resolveCareCompassCompliancePolicy, } from "./compliance.js";
|
|
7
10
|
export { CARECOMPASS_TELEMETRY_ERROR_CODES, CARECOMPASS_TELEMETRY_EVENTS, careCompassTelemetryDurationBucket, createCareCompassCliTelemetry, createCareCompassTelemetry, } from "./telemetry.js";
|
|
8
11
|
export { CARECOMPASS_COMPONENT_VERSION, CARECOMPASS_SDK_VERSION, CARECOMPASS_TELEMETRY_CONTRACT_VERSION, } from "./version.js";
|
package/dist/markdown.d.ts
CHANGED
package/dist/mount.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import type { CareCompassRuntimeTokenProvider } from "./client.js";
|
|
1
2
|
import type { CareCompassConfig } from "./schema.js";
|
|
2
|
-
export declare function mountCareCompass(element: Element | null, config: CareCompassConfig
|
|
3
|
+
export declare function mountCareCompass(element: Element | null, config: CareCompassConfig, options?: {
|
|
4
|
+
runtimeTokenProvider?: CareCompassRuntimeTokenProvider;
|
|
5
|
+
}): {
|
|
3
6
|
unmount: () => void;
|
|
4
7
|
};
|
package/dist/mount.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
3
|
import { CareCompassEmbed } from "./component.js";
|
|
4
4
|
import { CARECOMPASS_TELEMETRY_ERROR_CODES, CARECOMPASS_TELEMETRY_EVENTS, createCareCompassTelemetry, } from "./telemetry.js";
|
|
5
|
-
export function mountCareCompass(element, config) {
|
|
5
|
+
export function mountCareCompass(element, config, options = {}) {
|
|
6
6
|
if (!element) {
|
|
7
7
|
// Missing mount points usually mean a copied snippet and page markup drifted
|
|
8
8
|
// apart, so capture the setup error before throwing to the caller.
|
|
@@ -13,7 +13,7 @@ export function mountCareCompass(element, config) {
|
|
|
13
13
|
throw new Error("CareCompass mount element was not found.");
|
|
14
14
|
}
|
|
15
15
|
const root = createRoot(element);
|
|
16
|
-
root.render(_jsx(CareCompassEmbed, { config: config }));
|
|
16
|
+
root.render(_jsx(CareCompassEmbed, { config: config, runtimeTokenProvider: options.runtimeTokenProvider }));
|
|
17
17
|
return {
|
|
18
18
|
// Return the small React root facade so non-React hosts can clean up embeds.
|
|
19
19
|
unmount: () => root.unmount(),
|
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;
|