npm.io
3.6.0 • Published 21h ago

@healthcloudai/hc-ams

Licence
MIT
Version
3.6.0
Deps
0
Size
152 kB
Vulns
0
Weekly
0

HealthCloud AMS SDK for NodeJS / TypeScript

Typed access to AMS Services: agent/department/team/skill/policy/guardrail CRUD, the data-source and tool catalogs, synthetic test-patient personas, conversation history, and the real-time WebSocket agent-chat runtime.

Installation

npm install @healthcloudai/hc-ams

Quick start

import { HCAmsSDK } from "@healthcloudai/hc-ams";

const sdk = new HCAmsSDK({ environment: "dev" });

const agents = await sdk.ams.getAgentsForTenant("<TENANT_ID>");
// { public: [...], orchestrator: [...] }

Authentication

ams.getAgentsForTenant is public and requires no access token.

Every other REST client (agents, departments, teams, skills, policies, guardrails, dataSources, tools, testPersonas) requires a Tenant Admin Bearer token, set via accessToken in the constructor or sdk.setAccessToken(token). This SDK does not obtain that token itself — get one from AMS's own sdk.auth.login(...) or from @healthcloudai/hc-tenantadmin, and pass it in.

The WebSocket runtime (sdk.connectAgentChat(...) / WssClient) is authenticated with a patient Health Cloud access token instead — obtain one via sdk.testPersonas.login(credentialId) for testing, or from your own patient auth flow in production.

REST clients

Client Resource Notes
sdk.ams Public application-agent discovery No auth required
sdk.agents Agent CRUD, transfer candidates, conversation history create/update/delete may 405 if the tenant has disable_direct_agent_mutations enabled (see below)
sdk.departments Department CRUD + activate/deactivate
sdk.teams Team CRUD
sdk.skills Skill CRUD
sdk.policies Policy CRUD
sdk.guardrails Guardrail CRUD
sdk.dataSources Data source catalog Read-only — AMS exposes no create/update/delete
sdk.tools Tool catalog Read-only — AMS exposes no create/update/delete
sdk.testPersonas Synthetic QA patient persona CRUD + login login() mints a real Health Cloud patient token
sdk.auth AMS's own Tenant Admin auth proxy (/api/auth/*) Resolves against a different base path than every other client
const sdk = new HCAmsSDK({ environment: "dev", accessToken: tenantAdminToken });

const department = await sdk.departments.create({ name: "Cardiology", created_by_user_id: "admin-1" });
const agent = await sdk.agents.create({ name: "Cardiology Intake", department_id: department.id });
const history = await sdk.agents.getConversation(agent.id, `${tenantId}/${patientId}`);
Direct agent mutation

Some tenants run with disable_direct_agent_mutations enabled, in which case AMS's plain POST/PUT/DELETE /agents routes respond 405 and real agent authoring happens through the Vibe Coder plan/apply flow instead. That flow is intentionally out of scope for this SDK — sdk.agents.create/update/delete only cover the plain-CRUD path.

WebSocket agent-chat runtime

Since 3.5.1: the patient token is sent as the first WebSocket frame ({"type":"auth","token":"..."}) right after the socket opens, never in the connect URL or as a WS subprotocol — chat.connect() doesn't resolve until the server replies auth_ok. This is handled internally; you don't need to change any calling code that already passes healthcloudToken or getAccessToken. It does require the AMS backend to be on the matching version (the corresponding server-side change is healthcloudservices/healthcloud-ams-services#280, merged via #288) — against an older backend, connect() will reject with an auth timeout instead of connecting. WssClientOptions.tokenTransport (0.0.1) is removed; if your code set it explicitly, delete that line.

const sdk = new HCAmsSDK({ environment: "dev", tenantId: "<TENANT_ID>", accessToken: tenantAdminToken });

// Mint a patient token for a synthetic test persona.
const session = await sdk.testPersonas.login(personaCredentialId);

// Connect and hold a multi-turn conversation with one agent.
const chat = sdk.connectAgentChat({
  patientId: session.patient_id,
  healthcloudToken: session.access_token,
  agentId: "<AGENT_ID>", // omit to use the tenant's default agent
});
await chat.connect();

const turn = await chat.sendAndAwaitTurn("Hi, I'd like to schedule a check-up.");
console.log(turn.message, turn.transfers, turn.artifacts, turn.notifications, turn.activity);

chat.close();

Note: the HCAmsSDK instance's own accessToken (Tenant Admin) above is only needed because this example uses testPersonas.login to mint a synthetic test patient — a testing convenience, not a production pattern. connectAgentChat/WssClient never read that accessToken; they only ever use the healthcloudToken you pass them explicitly. A real patient-facing app needs no Tenant Admin token at all — get the patient token from your own patient auth (@healthcloudai/hc-sdk), e.g.:

import { HCSDK } from "@healthcloudai/hc-sdk";
import { HCAmsSDK } from "@healthcloudai/hc-ams";

const patientSdk = new HCSDK({ environment: "dev", tenantId: "<TENANT_ID>" });
const login = await patientSdk.auth.login({ email, password }); // real patient credentials

const amsSdk = new HCAmsSDK({ environment: "dev", tenantId: "<TENANT_ID>" }); // no accessToken needed
const chat = amsSdk.connectAgentChat({
  patientId: login.fhir_patient_id,
  healthcloudToken: login.access_token,
  agentId: "<AGENT_ID>",
});
await chat.connect();

WssClient also exposes raw typed event subscription for finer-grained control:

chat.on("turn_started", (e) => console.log("turn started", e.turn_id));
chat.on("chat", (e) => process.stdout.write(e.content));
chat.on("transfer", (e) => console.log("agent switched", e.to_agent_id));

The entire multi-agent conversation (orchestrator plus any number of downstream specialist agents) lives on this one WebSocket connection — agent switches arrive as transfer events, not reconnects.

For a reconnect (network drop, or your own periodic re-auth), pass getAccessToken instead of a static healthcloudToken so a fresh token is resolved on every connect() call rather than replaying a captured, possibly-expired one:

const chat = sdk.connectAgentChat({
  patientId,
  getAccessToken: () => myAuthStore.getCurrentPatientToken(), // called on every connect()
  agentId,
});
Guest chat before sign-in, then handoff to an authenticated conversation

For an agent configured with requires_auth: false (a public, pre-login agent), no patient login or access token is needed. The connection still requires a patientId to form its session ID; generate a random guest ID for that — it is not a real FHIR patient ID, and the server never resolves it against a real patient record for a requires_auth: false agent.

const { public: publicAgents, orchestrator } = await amsSdk.ams.getAgentsForTenant(tenantId);

const guestId = crypto.randomUUID();
const guestChat = amsSdk.connectAgentChat({
  patientId: guestId,
  healthcloudToken: "", // no token needed for a requires_auth: false agent
  agentId: publicAgents[0],
});
await guestChat.connect();

const guestTurn = await guestChat.sendAndAwaitTurn("What services are available?");
console.log(guestTurn.message);

Once the patient signs in, start a new connection with the real patientId/healthcloudToken — and pass handoff with the guest transcript so the orchestrator's first greeting can acknowledge it, instead of the conversation starting as if nothing was said:

guestChat.close();

const login = await patientSdk.auth.login({ email, password });

const chat = amsSdk.connectAgentChat({
  patientId: login.fhir_patient_id,
  healthcloudToken: login.access_token,
  agentId: orchestrator[0],
  handoff: [
    { role: "user", content: "What services are available?" },
    { role: "assistant", content: guestTurn.message },
  ],
});
await chat.connect();

handoff is sent once, at connect time — the server reads it before generating the first greeting, so the greeting itself can reference the handed-off content (e.g. "I see you were asking about X — let's continue from there"). It is not the same as sendHandoff(messages) (a post-connect frame kept only for callers on the older contract): that one can only silently append to history after the greeting has already gone out.

On Node < 21 (no global WebSocket), pass a compatible implementation:

import WebSocket from "ws";

const chat = sdk.connectAgentChat({
  patientId,
  healthcloudToken,
  WebSocketImpl: WebSocket as any,
});

See TEST_LIST.md for a full end-to-end flow: creating a department/team/agents/test-persona, logging in as the persona, and validating a multi-turn conversation's turns, transfers, artifacts, notifications, and activity.

Configuration

Option Description
environment "dev" | "uat" | "prod" — resolves the AMS base URL ({env-}amsservices.health.cloud/api/ams, no prefix for prod).
tenantId Optional default tenant ID, used by connectAgentChat when no tenantId is passed per-call.
accessToken Optional Tenant Admin bearer token for authenticated REST methods.
baseUrlOverride Overrides the resolved AMS base URL (e.g. for local/staging testing).
timeout Request timeout in milliseconds. Defaults to 30000.

Keywords