@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/dist/telemetry.js CHANGED
@@ -62,12 +62,60 @@ export const CARECOMPASS_TELEMETRY_ERROR_CODES = {
62
62
  CLI_COMMAND_UNKNOWN: "CLI_COMMAND_UNKNOWN",
63
63
  UNKNOWN_ERROR: "UNKNOWN_ERROR",
64
64
  };
65
- const DEPLOYMENT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{2,127}$/;
66
65
  const POSTHOG_PROJECT_TOKEN_RE = /^phc_[A-Za-z0-9_-]{8,}$/;
67
66
  const POSTHOG_HOSTNAMES = new Set(["us.i.posthog.com", "eu.i.posthog.com"]);
68
67
  const CARECOMPASS_DEFAULT_POSTHOG_API_HOST = "https://us.i.posthog.com";
69
68
  const CARECOMPASS_DEFAULT_POSTHOG_PROJECT_TOKEN = "phc_vYl3rpIu6Iz5vCeJUG62BBA3nlPCeF4RSYpCb8FjnOl";
70
69
  const TELEMETRY_CAPTURE_TIMEOUT_MS = 1_000;
70
+ const ANONYMOUS_DISTINCT_ID = "carecompass-sdk-anonymous";
71
+ const TELEMETRY_EVENTS = new Set(Object.values(CARECOMPASS_TELEMETRY_EVENTS));
72
+ const TELEMETRY_ERROR_CODES = new Set(Object.values(CARECOMPASS_TELEMETRY_ERROR_CODES));
73
+ const FAILURE_CATEGORIES = new Set([
74
+ "authentication",
75
+ "auth_error",
76
+ "baa_not_signed",
77
+ "configuration",
78
+ "config_error",
79
+ "inference",
80
+ "model_error",
81
+ "network",
82
+ "network_error",
83
+ "permission_error",
84
+ "rate_limit",
85
+ "rate_limit_error",
86
+ "rendering",
87
+ "timeout",
88
+ "unknown",
89
+ ]);
90
+ const AUTH_METHODS = new Set(["api_key", "env_var", "config_file"]);
91
+ const BAA_STATUSES = new Set(["signed", "pending", "not_required"]);
92
+ const COMMAND_NAMES = new Set([
93
+ "auth",
94
+ "completion",
95
+ "config",
96
+ "configure",
97
+ "doctor",
98
+ "embed",
99
+ "health",
100
+ "help",
101
+ "init",
102
+ "login",
103
+ "logout",
104
+ "query",
105
+ "setup",
106
+ "snippet",
107
+ "unknown_command",
108
+ "validate",
109
+ "version",
110
+ ]);
111
+ const EXIT_REASONS = new Set([
112
+ "user_exit",
113
+ "error",
114
+ "timeout",
115
+ "signal",
116
+ ]);
117
+ const AUTH_RESULTS = new Set(["success", "failure"]);
118
+ const KEY_SOURCES = new Set(["env_var", "config_file", "cli_flag"]);
71
119
  const disabledTelemetry = {
72
120
  enabled: false,
73
121
  capture: () => undefined,
@@ -107,15 +155,19 @@ export const careCompassTelemetryDurationBucket = (durationMs) => {
107
155
  return "5_to_29_999_ms";
108
156
  return "30_s_or_more";
109
157
  };
110
- const integerMs = (value) => Math.max(0, Math.round(value));
111
- const integerCount = (value) => Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
158
+ const integerMs = (value) => typeof value === "number" && Number.isFinite(value)
159
+ ? Math.max(0, Math.round(value))
160
+ : undefined;
161
+ const integerCount = (value) => typeof value === "number" && Number.isFinite(value)
162
+ ? Math.max(0, Math.trunc(value))
163
+ : undefined;
164
+ const allowedValue = (value, values) => typeof value === "string" && values.has(value) ? value : undefined;
165
+ const allowedEnvironment = (value) => value === "development" || value === "staging" || value === "production"
166
+ ? value
167
+ : "unknown";
112
168
  function createTelemetry(context, options = {}) {
113
169
  const projectToken = CARECOMPASS_DEFAULT_POSTHOG_PROJECT_TOKEN;
114
- // Invalid identifiers disable telemetry instead of producing malformed events
115
- // or sending data under an ambiguous deployment identity.
116
- if (!DEPLOYMENT_ID_RE.test(context.deploymentId) ||
117
- !DEPLOYMENT_ID_RE.test(context.distinctId) ||
118
- !POSTHOG_PROJECT_TOKEN_RE.test(projectToken)) {
170
+ if (!POSTHOG_PROJECT_TOKEN_RE.test(projectToken)) {
119
171
  return disabledTelemetry;
120
172
  }
121
173
  const fetcher = options.fetcher ?? fetch;
@@ -124,90 +176,77 @@ function createTelemetry(context, options = {}) {
124
176
  if (!apiHost)
125
177
  return disabledTelemetry;
126
178
  const captureAsync = async (event, captureOptions = {}) => {
179
+ if (!TELEMETRY_EVENTS.has(event))
180
+ return;
127
181
  const eventTimestamp = now().toISOString();
182
+ const durationMs = integerMs(captureOptions.durationMs);
183
+ const failureCategory = captureOptions.failureCategory === null
184
+ ? null
185
+ : allowedValue(captureOptions.failureCategory, FAILURE_CATEGORIES);
186
+ const errorCode = allowedValue(captureOptions.errorCode, TELEMETRY_ERROR_CODES);
187
+ const initDurationMs = integerMs(captureOptions.initDurationMs);
188
+ const authMethod = allowedValue(captureOptions.authMethod, AUTH_METHODS);
189
+ const baaStatus = allowedValue(captureOptions.baaStatus, BAA_STATUSES);
190
+ const commandName = allowedValue(captureOptions.commandName, COMMAND_NAMES);
191
+ const commandArgsCount = integerCount(captureOptions.commandArgsCount);
192
+ const commandDurationMs = integerMs(captureOptions.commandDurationMs);
193
+ const tokensUsed = captureOptions.tokensUsed === null
194
+ ? null
195
+ : integerCount(captureOptions.tokensUsed);
196
+ const sessionDurationMs = integerMs(captureOptions.sessionDurationMs);
197
+ const commandsAttempted = integerCount(captureOptions.commandsAttempted);
198
+ const commandsSucceeded = integerCount(captureOptions.commandsSucceeded);
199
+ const commandsFailed = integerCount(captureOptions.commandsFailed);
200
+ const exitReason = allowedValue(captureOptions.exitReason, EXIT_REASONS);
201
+ const authResult = allowedValue(captureOptions.authResult, AUTH_RESULTS);
202
+ const keySource = allowedValue(captureOptions.keySource, KEY_SOURCES);
128
203
  // Properties are allowlisted here by design. Do not add prompts, responses,
129
- // user context, credentials, or raw exception messages to this payload.
204
+ // user context, credentials, raw exception messages, or stable identifiers.
130
205
  const properties = {
131
206
  $process_person_profile: false,
132
207
  contract_version: CARECOMPASS_TELEMETRY_CONTRACT_VERSION,
133
208
  sdk_version: CARECOMPASS_SDK_VERSION,
134
209
  component_version: CARECOMPASS_COMPONENT_VERSION,
135
210
  platform: context.platform,
136
- environment: context.environment,
137
- payer_id: context.payerId,
138
- deployment_id: context.deploymentId,
211
+ environment: allowedEnvironment(context.environment),
139
212
  event_timestamp: eventTimestamp,
140
- ...(context.cliSessionId ? { cli_session_id: context.cliSessionId } : {}),
141
- ...(captureOptions.durationMs === undefined
213
+ ...(durationMs === undefined
142
214
  ? {}
143
215
  : {
144
- duration_bucket: careCompassTelemetryDurationBucket(captureOptions.durationMs),
216
+ duration_bucket: careCompassTelemetryDurationBucket(durationMs),
145
217
  }),
146
- ...(captureOptions.failureCategory === undefined
218
+ ...(failureCategory === undefined
147
219
  ? {}
148
- : { failure_category: captureOptions.failureCategory }),
149
- ...(captureOptions.errorCode
150
- ? { error_code: captureOptions.errorCode }
151
- : {}),
152
- ...(captureOptions.initDurationMs === undefined
220
+ : { failure_category: failureCategory }),
221
+ ...(errorCode ? { error_code: errorCode } : {}),
222
+ ...(initDurationMs === undefined
153
223
  ? {}
154
- : { init_duration_ms: integerMs(captureOptions.initDurationMs) }),
155
- ...(captureOptions.authMethod
156
- ? { auth_method: captureOptions.authMethod }
157
- : {}),
158
- ...(captureOptions.baaStatus
159
- ? { baa_status: captureOptions.baaStatus }
160
- : {}),
161
- ...(captureOptions.commandName
162
- ? { command_name: captureOptions.commandName }
163
- : {}),
164
- ...(captureOptions.commandArgsCount === undefined
224
+ : { init_duration_ms: initDurationMs }),
225
+ ...(authMethod ? { auth_method: authMethod } : {}),
226
+ ...(baaStatus ? { baa_status: baaStatus } : {}),
227
+ ...(commandName ? { command_name: commandName } : {}),
228
+ ...(commandArgsCount === undefined
165
229
  ? {}
166
- : {
167
- command_args_count: integerCount(captureOptions.commandArgsCount),
168
- }),
169
- ...(captureOptions.commandDurationMs === undefined
170
- ? {}
171
- : {
172
- command_duration_ms: integerMs(captureOptions.commandDurationMs),
173
- }),
174
- ...(captureOptions.tokensUsed === undefined
175
- ? {}
176
- : {
177
- tokens_used: captureOptions.tokensUsed === null
178
- ? null
179
- : integerCount(captureOptions.tokensUsed),
180
- }),
181
- ...(captureOptions.modelId === undefined
230
+ : { command_args_count: commandArgsCount }),
231
+ ...(commandDurationMs === undefined
182
232
  ? {}
183
- : { model_id: captureOptions.modelId }),
184
- ...(captureOptions.sessionDurationMs === undefined
233
+ : { command_duration_ms: commandDurationMs }),
234
+ ...(tokensUsed === undefined ? {} : { tokens_used: tokensUsed }),
235
+ ...(sessionDurationMs === undefined
185
236
  ? {}
186
- : {
187
- session_duration_ms: integerMs(captureOptions.sessionDurationMs),
188
- }),
189
- ...(captureOptions.commandsAttempted === undefined
237
+ : { session_duration_ms: sessionDurationMs }),
238
+ ...(commandsAttempted === undefined
190
239
  ? {}
191
- : {
192
- commands_attempted: integerCount(captureOptions.commandsAttempted),
193
- }),
194
- ...(captureOptions.commandsSucceeded === undefined
240
+ : { commands_attempted: commandsAttempted }),
241
+ ...(commandsSucceeded === undefined
195
242
  ? {}
196
- : {
197
- commands_succeeded: integerCount(captureOptions.commandsSucceeded),
198
- }),
199
- ...(captureOptions.commandsFailed === undefined
243
+ : { commands_succeeded: commandsSucceeded }),
244
+ ...(commandsFailed === undefined
200
245
  ? {}
201
- : { commands_failed: integerCount(captureOptions.commandsFailed) }),
202
- ...(captureOptions.exitReason
203
- ? { exit_reason: captureOptions.exitReason }
204
- : {}),
205
- ...(captureOptions.authResult
206
- ? { auth_result: captureOptions.authResult }
207
- : {}),
208
- ...(captureOptions.keySource
209
- ? { key_source: captureOptions.keySource }
210
- : {}),
246
+ : { commands_failed: commandsFailed }),
247
+ ...(exitReason ? { exit_reason: exitReason } : {}),
248
+ ...(authResult ? { auth_result: authResult } : {}),
249
+ ...(keySource ? { key_source: keySource } : {}),
211
250
  };
212
251
  const controller = new AbortController();
213
252
  const timeout = setTimeout(() => controller.abort(), TELEMETRY_CAPTURE_TIMEOUT_MS);
@@ -217,7 +256,7 @@ function createTelemetry(context, options = {}) {
217
256
  headers: { "content-type": "application/json" },
218
257
  body: JSON.stringify({
219
258
  api_key: projectToken,
220
- distinct_id: context.distinctId,
259
+ distinct_id: ANONYMOUS_DISTINCT_ID,
221
260
  event,
222
261
  properties,
223
262
  timestamp: eventTimestamp,
@@ -239,24 +278,17 @@ function createTelemetry(context, options = {}) {
239
278
  };
240
279
  }
241
280
  export function createCareCompassTelemetry(config, options = {}) {
242
- const deploymentId = `${config.payerId}-${config.environment}-web`;
243
281
  return createTelemetry({
244
- deploymentId,
245
- distinctId: deploymentId,
246
282
  environment: config.environment,
247
- payerId: config.payerId,
248
283
  platform: "web",
249
284
  }, options);
250
285
  }
251
286
  export function createCareCompassCliTelemetry(sessionId, context = {}, options = {}) {
287
+ // Keep the public signature stable, but session IDs must never reach telemetry.
288
+ void sessionId;
252
289
  const environment = context.environment ?? "unknown";
253
- const payerId = context.payerId ?? "anonymous";
254
290
  return createTelemetry({
255
- cliSessionId: sessionId,
256
- deploymentId: `${payerId}-${environment}-cli`,
257
- distinctId: sessionId,
258
291
  environment,
259
- payerId,
260
292
  platform: "cli",
261
293
  }, options);
262
294
  }
package/dist/ui.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { CareCompassChat, CareCompassEmbed } from "./component.js";
2
- export type { CareCompassChatProps } from "./component.js";
1
+ export { CareCompassChat, CareCompassEmbed, useCareCompass, } from "./component.js";
2
+ export type { CareCompassChatProps, CareCompassReactController, UseCareCompassOptions, } from "./component.js";
3
3
  export { mountCareCompass } from "./mount.js";
package/dist/ui.js CHANGED
@@ -1,2 +1,2 @@
1
- export { CareCompassChat, CareCompassEmbed } from "./component.js";
1
+ export { CareCompassChat, CareCompassEmbed, useCareCompass, } from "./component.js";
2
2
  export { mountCareCompass } from "./mount.js";
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export declare const CARECOMPASS_SDK_VERSION: string;
2
2
  export declare const CARECOMPASS_COMPONENT_VERSION = "1.0.0";
3
- export declare const CARECOMPASS_TELEMETRY_CONTRACT_VERSION = "1.0.0";
3
+ export declare const CARECOMPASS_TELEMETRY_CONTRACT_VERSION = "1.1.0";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
- export const CARECOMPASS_SDK_VERSION = "0.4.0-beta.15"; // x-release-please-version
1
+ export const CARECOMPASS_SDK_VERSION = "0.5.0"; // x-release-please-version
2
2
  export const CARECOMPASS_COMPONENT_VERSION = "1.0.0";
3
- export const CARECOMPASS_TELEMETRY_CONTRACT_VERSION = "1.0.0";
3
+ export const CARECOMPASS_TELEMETRY_CONTRACT_VERSION = "1.1.0";
@@ -0,0 +1,13 @@
1
+ import type { CareCompassRuntimeTokenProvider } from "./client.js";
2
+ import { type CareCompassConfig } from "./schema.js";
3
+ export declare const CARECOMPASS_ELEMENT_TAG = "carecompass-chat";
4
+ export type CareCompassWebComponentElement = HTMLElement & {
5
+ config: CareCompassConfig | undefined;
6
+ runtimeTokenProvider: CareCompassRuntimeTokenProvider | undefined;
7
+ };
8
+ /**
9
+ * Register a framework-neutral embed backed directly by the headless core.
10
+ * Deferring the HTMLElement subclass creation keeps this entry point safe to
11
+ * import during server rendering.
12
+ */
13
+ export declare function defineCareCompassElement(tagName?: string): CustomElementConstructor | undefined;
@@ -0,0 +1,236 @@
1
+ import { createCareCompassCore, } from "./core.js";
2
+ import { validateCareCompassConfig } from "./schema.js";
3
+ export const CARECOMPASS_ELEMENT_TAG = "carecompass-chat";
4
+ /**
5
+ * Register a framework-neutral embed backed directly by the headless core.
6
+ * Deferring the HTMLElement subclass creation keeps this entry point safe to
7
+ * import during server rendering.
8
+ */
9
+ export function defineCareCompassElement(tagName = CARECOMPASS_ELEMENT_TAG) {
10
+ if (typeof customElements === "undefined" ||
11
+ typeof HTMLElement === "undefined") {
12
+ return undefined;
13
+ }
14
+ const existing = customElements.get(tagName);
15
+ if (existing)
16
+ return existing;
17
+ class CareCompassElement extends HTMLElement {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.draft = "";
21
+ this.hasPropertyConfig = false;
22
+ }
23
+ static get observedAttributes() {
24
+ return ["config"];
25
+ }
26
+ get config() {
27
+ return this.configuredValue;
28
+ }
29
+ set config(value) {
30
+ this.hasPropertyConfig = true;
31
+ this.configuredValue = value;
32
+ this.remount();
33
+ }
34
+ get runtimeTokenProvider() {
35
+ return this.runtimeTokenProviderValue;
36
+ }
37
+ set runtimeTokenProvider(value) {
38
+ this.runtimeTokenProviderValue = value;
39
+ this.remount();
40
+ }
41
+ connectedCallback() {
42
+ this.remount();
43
+ }
44
+ disconnectedCallback() {
45
+ this.unmount();
46
+ }
47
+ attributeChangedCallback(name, _oldValue, value) {
48
+ if (name !== "config" || this.hasPropertyConfig)
49
+ return;
50
+ if (value === null) {
51
+ this.configuredValue = undefined;
52
+ this.remount();
53
+ return;
54
+ }
55
+ try {
56
+ this.configuredValue = JSON.parse(value);
57
+ this.remount();
58
+ }
59
+ catch {
60
+ this.reportConfigurationError();
61
+ }
62
+ }
63
+ unmount() {
64
+ this.unsubscribe?.();
65
+ this.unsubscribe = undefined;
66
+ this.core?.dispose();
67
+ this.core = undefined;
68
+ this.replaceChildren();
69
+ }
70
+ remount() {
71
+ this.unmount();
72
+ if (!this.isConnected || !this.configuredValue)
73
+ return;
74
+ const validation = validateCareCompassConfig(this.configuredValue);
75
+ if (!validation.ok) {
76
+ this.reportConfigurationError();
77
+ return;
78
+ }
79
+ this.core = createCareCompassCore(validation.config, {
80
+ onAuditEvent: (event) => this.dispatchMetadataEvent("carecompass-audit", event),
81
+ onSafetyEvent: (event) => this.dispatchMetadataEvent("carecompass-safety", event),
82
+ runtimeTokenProvider: this.runtimeTokenProviderValue,
83
+ });
84
+ this.unsubscribe = this.core.subscribe((snapshot) => this.renderSnapshot(validation.config, snapshot));
85
+ this.renderSnapshot(validation.config, this.core.getSnapshot());
86
+ }
87
+ dispatchMetadataEvent(name, event) {
88
+ // Core audit and safety records intentionally contain metadata and PHI
89
+ // summaries only; never interpolate message text into DOM events.
90
+ this.dispatchEvent(new CustomEvent(name, {
91
+ bubbles: true,
92
+ detail: event,
93
+ }));
94
+ }
95
+ renderSnapshot(config, snapshot) {
96
+ const section = document.createElement("section");
97
+ section.setAttribute("aria-label", config.labels.title);
98
+ Object.assign(section.style, {
99
+ background: config.theme.backgroundColor,
100
+ border: `1px solid ${config.theme.textColor}22`,
101
+ borderRadius: config.theme.borderRadius,
102
+ color: config.theme.textColor,
103
+ display: "flex",
104
+ flexDirection: "column",
105
+ fontFamily: config.theme.fontFamily,
106
+ maxWidth: config.placement.mode === "sidebar" ? "420px" : "720px",
107
+ minHeight: "420px",
108
+ overflow: "hidden",
109
+ });
110
+ const header = document.createElement("header");
111
+ Object.assign(header.style, {
112
+ borderBottom: `1px solid ${config.theme.textColor}22`,
113
+ padding: "16px",
114
+ });
115
+ const title = document.createElement("strong");
116
+ title.textContent = config.labels.title;
117
+ const brand = document.createElement("div");
118
+ brand.textContent = config.branding.displayName;
119
+ brand.style.fontSize = "12px";
120
+ brand.style.opacity = "0.72";
121
+ header.append(title, brand);
122
+ const messages = document.createElement("div");
123
+ messages.setAttribute("aria-live", "polite");
124
+ Object.assign(messages.style, {
125
+ display: "flex",
126
+ flex: "1",
127
+ flexDirection: "column",
128
+ gap: "12px",
129
+ overflowY: "auto",
130
+ padding: "16px",
131
+ });
132
+ for (const message of snapshot.messages) {
133
+ const bubble = document.createElement("div");
134
+ bubble.textContent = message.text;
135
+ Object.assign(bubble.style, {
136
+ alignSelf: message.role === "user" ? "flex-end" : "flex-start",
137
+ background: message.role === "user"
138
+ ? config.theme.primaryColor
139
+ : `${config.theme.textColor}12`,
140
+ borderRadius: "12px",
141
+ color: message.role === "user" ? "#ffffff" : "inherit",
142
+ maxWidth: "82%",
143
+ padding: "10px 12px",
144
+ whiteSpace: "pre-wrap",
145
+ });
146
+ messages.append(bubble);
147
+ }
148
+ if (snapshot.loading) {
149
+ const thinking = document.createElement("div");
150
+ thinking.textContent = "Thinking...";
151
+ thinking.style.fontSize = "13px";
152
+ thinking.style.opacity = "0.72";
153
+ messages.append(thinking);
154
+ }
155
+ const form = document.createElement("form");
156
+ Object.assign(form.style, {
157
+ borderTop: `1px solid ${config.theme.textColor}22`,
158
+ display: "flex",
159
+ flexDirection: "column",
160
+ gap: "8px",
161
+ padding: "12px",
162
+ });
163
+ form.addEventListener("submit", (event) => {
164
+ event.preventDefault();
165
+ void this.sendMessage(snapshot);
166
+ });
167
+ const controls = document.createElement("div");
168
+ controls.style.display = "flex";
169
+ controls.style.gap = "8px";
170
+ const input = document.createElement("input");
171
+ input.setAttribute("aria-label", config.labels.placeholder);
172
+ input.disabled = snapshot.loading;
173
+ input.placeholder = config.labels.placeholder;
174
+ input.value = this.draft;
175
+ input.style.flex = "1";
176
+ input.style.minWidth = "0";
177
+ input.style.padding = "10px 12px";
178
+ input.addEventListener("input", () => {
179
+ this.draft = input.value;
180
+ send.disabled = snapshot.loading || !this.draft.trim();
181
+ });
182
+ const send = document.createElement("button");
183
+ send.disabled = snapshot.loading || !this.draft.trim();
184
+ send.textContent = config.labels.sendButton;
185
+ send.type = "submit";
186
+ Object.assign(send.style, {
187
+ background: config.theme.primaryColor,
188
+ border: "0",
189
+ borderRadius: "8px",
190
+ color: "#ffffff",
191
+ fontWeight: "700",
192
+ padding: "0 14px",
193
+ });
194
+ controls.append(input, send);
195
+ form.append(controls);
196
+ if (snapshot.error) {
197
+ const error = document.createElement("div");
198
+ error.setAttribute("role", "alert");
199
+ error.textContent = snapshot.error.message;
200
+ error.style.color = "#b91c1c";
201
+ error.style.fontSize = "12px";
202
+ form.append(error);
203
+ }
204
+ const disclaimer = document.createElement("div");
205
+ disclaimer.textContent = config.labels.disclaimer;
206
+ disclaimer.style.fontSize = "11px";
207
+ disclaimer.style.opacity = "0.7";
208
+ form.append(disclaimer);
209
+ section.append(header, messages, form);
210
+ this.replaceChildren(section);
211
+ }
212
+ async sendMessage(snapshot) {
213
+ const prompt = this.draft.trim();
214
+ if (!prompt || snapshot.loading || !this.core)
215
+ return;
216
+ this.draft = "";
217
+ try {
218
+ this.core.recordWidgetAction();
219
+ await this.core.sendMessage(prompt);
220
+ }
221
+ catch {
222
+ // The snapshot already carries the safe, user-visible error state.
223
+ }
224
+ }
225
+ reportConfigurationError() {
226
+ // Do not include raw config or validation detail in DOM events; a config
227
+ // can contain user-controlled branding and labels.
228
+ this.dispatchEvent(new CustomEvent("carecompass-error", {
229
+ bubbles: true,
230
+ detail: { code: "configuration_invalid" },
231
+ }));
232
+ }
233
+ }
234
+ customElements.define(tagName, CareCompassElement);
235
+ return CareCompassElement;
236
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexalink/carecompass-sdk",
3
- "version": "0.4.0-beta.15",
3
+ "version": "0.5.0",
4
4
  "description": "Embeddable CareCompass SDK and CLI for healthcare ecosystem integrations with Nexalink onboarding, BAA coverage, and API keys.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.9",
@@ -42,11 +42,24 @@
42
42
  "./ui": {
43
43
  "types": "./dist/ui.d.ts",
44
44
  "import": "./dist/ui.js"
45
+ },
46
+ "./react": {
47
+ "types": "./dist/react.d.ts",
48
+ "import": "./dist/react.js"
49
+ },
50
+ "./rcm": {
51
+ "types": "./dist/rcm.d.ts",
52
+ "import": "./dist/rcm.js"
53
+ },
54
+ "./web-component": {
55
+ "types": "./dist/web-component.d.ts",
56
+ "import": "./dist/web-component.js"
45
57
  }
46
58
  },
47
59
  "files": [
48
60
  "dist/**/*.js",
49
61
  "dist/**/*.d.ts",
62
+ "qa-fixtures/DEMO-ENC-1042-rcm-policy.md",
50
63
  "README.md",
51
64
  "LICENSE"
52
65
  ],
@@ -80,26 +93,28 @@
80
93
  }
81
94
  },
82
95
  "devDependencies": {
83
- "@eslint/js": "^9.39.4",
84
- "@testing-library/jest-dom": "^6.9.1",
96
+ "@eslint/js": "^9.39.5",
97
+ "@testing-library/jest-dom": "^6.10.0",
85
98
  "@testing-library/react": "^16.3.2",
86
99
  "@types/jest": "^30.0.0",
87
- "@types/node": "^25.9.3",
88
- "@types/react": "^19.2.15",
100
+ "@types/node": "^25.9.5",
101
+ "@types/react": "^19.2.17",
89
102
  "@types/react-dom": "^19.2.3",
90
- "eslint": "^9.39.4",
103
+ "eslint": "^9.39.5",
91
104
  "globals": "^16.5.0",
92
105
  "jest": "^30.4.2",
93
106
  "jest-environment-jsdom": "^30.4.1",
94
- "prettier": "^3.8.3",
95
- "react": "^19.2.6",
96
- "react-dom": "^19.2.6",
97
- "ts-jest": "^29.4.1",
107
+ "prettier": "^3.9.6",
108
+ "react": "^19.2.8",
109
+ "react-dom": "^19.2.8",
110
+ "ts-jest": "^29.4.11",
98
111
  "typescript": "^5.9.3",
99
- "typescript-eslint": "^8.48.1"
112
+ "typescript-eslint": "^8.65.0"
100
113
  },
101
114
  "overrides": {
102
- "js-yaml": "4.2.0"
115
+ "js-yaml": "4.3.0",
116
+ "minimatch": "10.2.5",
117
+ "brace-expansion": "5.0.9"
103
118
  },
104
119
  "publishConfig": {
105
120
  "access": "public",
@@ -0,0 +1,27 @@
1
+ # Synthetic RCM coding-review policy
2
+
3
+ **Document ID:** `synthetic-rcm-knee-policy`
4
+ **Version:** `2026.1-demo`
5
+ **Effective date:** `2026-07-01`
6
+ **Policy type:** coding-review
7
+ **Classification:** synthetic, non-PHI demonstration material
8
+
9
+ ## Purpose and boundary
10
+
11
+ This document is intentionally synthetic. It is for a CareCompass demonstration
12
+ only and is not a payer policy, coding manual, claim-submission instruction, or
13
+ clinical protocol. It does not authorize a final coding, billing, diagnostic,
14
+ treatment, or claim-submission decision.
15
+
16
+ ## Synthetic encounter DEMO-ENC-1042
17
+
18
+ For the synthetic outpatient encounter `DEMO-ENC-1042`, a qualified coder may
19
+ review a right-knee-pain candidate represented in the demo as `M25.561` when
20
+ the note supports persistent knee pain after activity and a focused
21
+ musculoskeletal evaluation. The assistant must present this only as a
22
+ human-review suggestion, cite this document, and state any documentation gaps.
23
+
24
+ If laterality, clinician assessment, medical necessity, procedure detail, or
25
+ other support is unclear, the assistant must abstain or escalate with no coding
26
+ recommendations. A qualified coder remains responsible for verifying all source
27
+ material and making any final decision.