@hainiva/agent-sdk 0.0.0-stage → 0.3.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +21 -0
  3. package/README.md +278 -2
  4. package/SECURITY.md +33 -0
  5. package/dist/cjs/agent.cjs +320 -0
  6. package/dist/cjs/agent.d.cts +239 -0
  7. package/dist/cjs/calls.cjs +277 -0
  8. package/dist/cjs/calls.d.cts +232 -0
  9. package/dist/cjs/contract.cjs +14 -0
  10. package/dist/cjs/contract.d.cts +225 -0
  11. package/dist/cjs/embed.cjs +253 -0
  12. package/dist/cjs/embed.d.cts +187 -0
  13. package/dist/cjs/emitter.cjs +50 -0
  14. package/dist/cjs/emitter.d.cts +22 -0
  15. package/dist/cjs/errors.cjs +28 -0
  16. package/dist/cjs/errors.d.cts +15 -0
  17. package/dist/cjs/events.cjs +97 -0
  18. package/dist/cjs/events.d.cts +97 -0
  19. package/dist/cjs/index.cjs +72 -0
  20. package/dist/cjs/index.d.cts +28 -0
  21. package/dist/cjs/livekit.cjs +56 -0
  22. package/dist/cjs/livekit.d.cts +35 -0
  23. package/dist/cjs/log.cjs +15 -0
  24. package/dist/cjs/log.d.cts +5 -0
  25. package/dist/cjs/react-native.cjs +32 -0
  26. package/dist/cjs/react-native.d.cts +39 -0
  27. package/dist/cjs/reactNativeDesk.cjs +107 -0
  28. package/dist/cjs/reactNativeDesk.d.cts +53 -0
  29. package/dist/cjs/redact.cjs +71 -0
  30. package/dist/cjs/redact.d.cts +21 -0
  31. package/dist/cjs/rest.cjs +286 -0
  32. package/dist/cjs/rest.d.cts +172 -0
  33. package/dist/cjs/session.cjs +159 -0
  34. package/dist/cjs/session.d.cts +101 -0
  35. package/dist/cjs/singleFlight.cjs +49 -0
  36. package/dist/cjs/singleFlight.d.cts +32 -0
  37. package/dist/cjs/url.cjs +39 -0
  38. package/dist/cjs/url.d.cts +13 -0
  39. package/dist/cjs/web.cjs +28 -0
  40. package/dist/cjs/web.d.cts +23 -0
  41. package/dist/cjs/ws.cjs +246 -0
  42. package/dist/cjs/ws.d.cts +134 -0
  43. package/dist/esm/agent.d.ts +240 -0
  44. package/dist/esm/agent.d.ts.map +1 -0
  45. package/dist/esm/agent.js +317 -0
  46. package/dist/esm/agent.js.map +1 -0
  47. package/dist/esm/calls.d.ts +233 -0
  48. package/dist/esm/calls.d.ts.map +1 -0
  49. package/dist/esm/calls.js +271 -0
  50. package/dist/esm/calls.js.map +1 -0
  51. package/dist/esm/contract.d.ts +226 -0
  52. package/dist/esm/contract.d.ts.map +1 -0
  53. package/dist/esm/contract.js +12 -0
  54. package/dist/esm/contract.js.map +1 -0
  55. package/dist/esm/embed.d.ts +188 -0
  56. package/dist/esm/embed.d.ts.map +1 -0
  57. package/dist/esm/embed.js +247 -0
  58. package/dist/esm/embed.js.map +1 -0
  59. package/dist/esm/emitter.d.ts +23 -0
  60. package/dist/esm/emitter.d.ts.map +1 -0
  61. package/dist/esm/emitter.js +47 -0
  62. package/dist/esm/emitter.js.map +1 -0
  63. package/dist/esm/errors.d.ts +16 -0
  64. package/dist/esm/errors.d.ts.map +1 -0
  65. package/dist/esm/errors.js +25 -0
  66. package/dist/esm/errors.js.map +1 -0
  67. package/dist/esm/events.d.ts +98 -0
  68. package/dist/esm/events.d.ts.map +1 -0
  69. package/dist/esm/events.js +90 -0
  70. package/dist/esm/events.js.map +1 -0
  71. package/dist/esm/index.d.ts +29 -0
  72. package/dist/esm/index.d.ts.map +1 -0
  73. package/dist/esm/index.js +29 -0
  74. package/dist/esm/index.js.map +1 -0
  75. package/dist/esm/livekit.d.ts +36 -0
  76. package/dist/esm/livekit.d.ts.map +1 -0
  77. package/dist/esm/livekit.js +54 -0
  78. package/dist/esm/livekit.js.map +1 -0
  79. package/dist/esm/log.d.ts +6 -0
  80. package/dist/esm/log.d.ts.map +1 -0
  81. package/dist/esm/log.js +13 -0
  82. package/dist/esm/log.js.map +1 -0
  83. package/dist/esm/react-native.d.ts +40 -0
  84. package/dist/esm/react-native.d.ts.map +1 -0
  85. package/dist/esm/react-native.js +26 -0
  86. package/dist/esm/react-native.js.map +1 -0
  87. package/dist/esm/reactNativeDesk.d.ts +54 -0
  88. package/dist/esm/reactNativeDesk.d.ts.map +1 -0
  89. package/dist/esm/reactNativeDesk.js +103 -0
  90. package/dist/esm/reactNativeDesk.js.map +1 -0
  91. package/dist/esm/redact.d.ts +22 -0
  92. package/dist/esm/redact.d.ts.map +1 -0
  93. package/dist/esm/redact.js +66 -0
  94. package/dist/esm/redact.js.map +1 -0
  95. package/dist/esm/rest.d.ts +173 -0
  96. package/dist/esm/rest.d.ts.map +1 -0
  97. package/dist/esm/rest.js +281 -0
  98. package/dist/esm/rest.js.map +1 -0
  99. package/dist/esm/session.d.ts +102 -0
  100. package/dist/esm/session.d.ts.map +1 -0
  101. package/dist/esm/session.js +155 -0
  102. package/dist/esm/session.js.map +1 -0
  103. package/dist/esm/singleFlight.d.ts +33 -0
  104. package/dist/esm/singleFlight.d.ts.map +1 -0
  105. package/dist/esm/singleFlight.js +46 -0
  106. package/dist/esm/singleFlight.js.map +1 -0
  107. package/dist/esm/url.d.ts +14 -0
  108. package/dist/esm/url.d.ts.map +1 -0
  109. package/dist/esm/url.js +37 -0
  110. package/dist/esm/url.js.map +1 -0
  111. package/dist/esm/web.d.ts +24 -0
  112. package/dist/esm/web.d.ts.map +1 -0
  113. package/dist/esm/web.js +23 -0
  114. package/dist/esm/web.js.map +1 -0
  115. package/dist/esm/ws.d.ts +135 -0
  116. package/dist/esm/ws.d.ts.map +1 -0
  117. package/dist/esm/ws.js +242 -0
  118. package/dist/esm/ws.js.map +1 -0
  119. package/package.json +117 -4
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypedEmitter = void 0;
4
+ /**
5
+ * A small typed event emitter. `Events` maps each event name to its payload:
6
+ *
7
+ * const e = new TypedEmitter<{ state: ConnectionState; ready: undefined }>();
8
+ * const off = e.on("state", (s) => …); e.emit("state", "open"); e.emit("ready", undefined);
9
+ *
10
+ * A listener that throws does not stop the others; the error goes to `onError`.
11
+ */
12
+ class TypedEmitter {
13
+ constructor(onError) {
14
+ this.onError = onError;
15
+ this.listeners = new Map();
16
+ }
17
+ on(event, fn) {
18
+ let set = this.listeners.get(event);
19
+ if (!set) {
20
+ set = new Set();
21
+ this.listeners.set(event, set);
22
+ }
23
+ set.add(fn);
24
+ return () => this.off(event, fn);
25
+ }
26
+ once(event, fn) {
27
+ const off = this.on(event, (p) => { off(); fn(p); });
28
+ return off;
29
+ }
30
+ /** Remove a listener added with on(). (A once() listener is removed by the function once() returned.) */
31
+ off(event, fn) {
32
+ this.listeners.get(event)?.delete(fn);
33
+ }
34
+ emit(event, payload) {
35
+ const set = this.listeners.get(event);
36
+ if (!set)
37
+ return;
38
+ for (const fn of [...set]) {
39
+ try {
40
+ fn(payload);
41
+ }
42
+ catch (e) {
43
+ this.onError?.(e, event);
44
+ }
45
+ }
46
+ }
47
+ listenerCount(event) { return this.listeners.get(event)?.size ?? 0; }
48
+ removeAll() { this.listeners.clear(); }
49
+ }
50
+ exports.TypedEmitter = TypedEmitter;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A small typed event emitter. `Events` maps each event name to its payload:
3
+ *
4
+ * const e = new TypedEmitter<{ state: ConnectionState; ready: undefined }>();
5
+ * const off = e.on("state", (s) => …); e.emit("state", "open"); e.emit("ready", undefined);
6
+ *
7
+ * A listener that throws does not stop the others; the error goes to `onError`.
8
+ */
9
+ export declare class TypedEmitter<Events extends {
10
+ [K in keyof Events]: unknown;
11
+ }> {
12
+ private readonly onError?;
13
+ private readonly listeners;
14
+ constructor(onError?: ((error: unknown, event: keyof Events) => void) | undefined);
15
+ on<K extends keyof Events>(event: K, fn: (payload: Events[K]) => void): () => void;
16
+ once<K extends keyof Events>(event: K, fn: (payload: Events[K]) => void): () => void;
17
+ /** Remove a listener added with on(). (A once() listener is removed by the function once() returned.) */
18
+ off<K extends keyof Events>(event: K, fn: (payload: Events[K]) => void): void;
19
+ emit<K extends keyof Events>(event: K, payload: Events[K]): void;
20
+ listenerCount(event: keyof Events): number;
21
+ removeAll(): void;
22
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HainivaAgentError = void 0;
4
+ const redact_js_1 = require("./redact.cjs");
5
+ /**
6
+ * Every error the SDK raises. The message is only `status code`, and the body is
7
+ * redacted when the error is made: a partner who logs errors (or sends them to a
8
+ * crash reporter) never ships a token with them.
9
+ */
10
+ class HainivaAgentError extends Error {
11
+ /** status 0 = raised by the SDK itself (configuration, network), not by HAINIVA. */
12
+ constructor(status, code, body = {}, redactor) {
13
+ const c = redactor ? redactor.string(code) : (0, redact_js_1.redactString)(code);
14
+ super(`${status} ${c}`);
15
+ this.name = "HainivaAgentError";
16
+ this.status = status;
17
+ this.code = c;
18
+ this.body = (redactor ? redactor.value(body) : (0, redact_js_1.redactValue)(body));
19
+ }
20
+ /** Wrap anything thrown below the SDK (fetch, WebSocket, LiveKit) without carrying its secrets along. */
21
+ static from(e, code = "failed", redactor) {
22
+ if (e instanceof HainivaAgentError)
23
+ return e;
24
+ const message = e instanceof Error ? e.message : String(e);
25
+ return new HainivaAgentError(0, code, { cause: message }, redactor);
26
+ }
27
+ }
28
+ exports.HainivaAgentError = HainivaAgentError;
@@ -0,0 +1,15 @@
1
+ import { Redactor } from "./redact.cjs";
2
+ /**
3
+ * Every error the SDK raises. The message is only `status code`, and the body is
4
+ * redacted when the error is made: a partner who logs errors (or sends them to a
5
+ * crash reporter) never ships a token with them.
6
+ */
7
+ export declare class HainivaAgentError extends Error {
8
+ readonly status: number;
9
+ readonly code: string;
10
+ readonly body: Record<string, unknown>;
11
+ /** status 0 = raised by the SDK itself (configuration, network), not by HAINIVA. */
12
+ constructor(status: number, code: string, body?: Record<string, unknown>, redactor?: Redactor);
13
+ /** Wrap anything thrown below the SDK (fetch, WebSocket, LiveKit) without carrying its secrets along. */
14
+ static from(e: unknown, code?: string, redactor?: Redactor): HainivaAgentError;
15
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isKnownEvent = exports.AGENT_CLOSE_CODES = void 0;
4
+ exports.agentSocketBindings = agentSocketBindings;
5
+ exports.isEventFrame = isEventFrame;
6
+ exports.ringInfo = ringInfo;
7
+ exports.toCallEvents = toCallEvents;
8
+ const contract_js_1 = require("./contract.cjs");
9
+ exports.AGENT_CLOSE_CODES = { tokenExpired: 4001, revoked: 4003, authTimeout: 4008, tooManySockets: 4009, rateLimited: 4029 };
10
+ let tokenFrameSeq = 0;
11
+ /** The protocol as ReconnectingSocket options. `lastSeq()` is read when the auth frame is built. */
12
+ function agentSocketBindings(lastSeq) {
13
+ return {
14
+ authFrame: (token) => {
15
+ const s = lastSeq();
16
+ return s > 0 ? { type: "auth", token, lastSeq: s } : { type: "auth", token };
17
+ },
18
+ isReady: (f) => f?.type === "ready",
19
+ // Resume rides in the auth frame (above): no separate frame after ready.
20
+ resume: () => null,
21
+ seqOf: (f) => (f?.type === "event" && typeof f.seq === "number" && Number.isInteger(f.seq) ? f.seq : undefined),
22
+ tokenFrame: (token) => ({ type: "token.refresh", token, id: `t${++tokenFrameSeq}` }),
23
+ closeCodes: { tokenExpired: exports.AGENT_CLOSE_CODES.tokenExpired, revoked: exports.AGENT_CLOSE_CODES.revoked, fatal: [exports.AGENT_CLOSE_CODES.tooManySockets, 1003] },
24
+ // The server closes a socket that sends more than 120 frames a minute; 20 s is 3.
25
+ heartbeat: { intervalMs: 20000, timeoutMs: 60000, frame: () => ({ type: "ping" }) },
26
+ };
27
+ }
28
+ /** Is this a well-formed event frame with a name this SDK knows? Unknown names are passed on as `event.unknown`. */
29
+ function isEventFrame(f) {
30
+ const x = f;
31
+ return !!x && x["type"] === "event" && typeof x["event"] === "string" && typeof x["seq"] === "number"
32
+ && !!x["data"] && typeof x["data"] === "object" && !Array.isArray(x["data"]);
33
+ }
34
+ const isKnownEvent = (name) => contract_js_1.AGENT_EVENT_NAMES.includes(name);
35
+ exports.isKnownEvent = isKnownEvent;
36
+ const str = (v) => (typeof v === "string" && v ? v : null);
37
+ /** Pure: call.ringing data → the state machine's CallInfo (null when malformed). */
38
+ function ringInfo(d, now, defaultRingMs) {
39
+ const r = d;
40
+ const callId = str(r.callId);
41
+ if (!callId)
42
+ return null;
43
+ const path = r.path === "direct" || r.path === "queue" || r.path === "transfer" ? r.path : "queue";
44
+ const exp = str(r.expiresAt) ? Date.parse(r.expiresAt) : NaN;
45
+ const conversationId = str(r.conversationId);
46
+ const line = str(r.line);
47
+ return {
48
+ id: callId,
49
+ ...(conversationId ? { conversationId } : {}),
50
+ direction: "inbound",
51
+ path,
52
+ from: str(r.contactName) ?? str(r.callerNumber) ?? str(r.fromAgentName) ?? "",
53
+ ...(line ? { line } : {}),
54
+ // A ring without an end the server knows still ends: a lost cancel must not leave a ghost ring.
55
+ expiresAt: Number.isFinite(exp) ? exp : now + defaultRingMs,
56
+ ...(path === "transfer" ? { transfer: { mode: r.transferMode === "blind" ? "blind" : "warm" } } : {}),
57
+ };
58
+ }
59
+ /**
60
+ * Pure: an event → the state-machine events it causes (empty when it is not
61
+ * about calls this device knows). `state` resolves a call.ended that names only
62
+ * the conversation.
63
+ */
64
+ function toCallEvents(frame, state, now, defaultRingMs = 60000) {
65
+ if (frame.event === "call.ringing") {
66
+ const call = ringInfo(frame.data, now, defaultRingMs);
67
+ return call ? [{ type: "ring", call, at: now }] : [];
68
+ }
69
+ if (frame.event !== "call.ended")
70
+ return [];
71
+ const d = frame.data;
72
+ const ids = [];
73
+ const callId = str(d.callId);
74
+ if (callId)
75
+ ids.push(callId);
76
+ else if (str(d.conversationId)) {
77
+ for (const c of Object.values(state.calls))
78
+ if (c.info.conversationId === d.conversationId && c.phase !== "ended")
79
+ ids.push(c.info.id);
80
+ }
81
+ const out = [];
82
+ for (const id of ids) {
83
+ const c = state.calls[id];
84
+ if (!c || c.phase === "ended")
85
+ continue;
86
+ if (c.phase === "ringing" || c.phase === "connecting") {
87
+ const reason = d.reason === "answered_elsewhere" ? "answered_elsewhere"
88
+ : d.reason === "ended" || d.reason === "failed" ? "remote_ended" : "cancelled";
89
+ out.push({ type: "ring_cancelled", id, reason, at: now });
90
+ }
91
+ else if (d.reason !== "answered_elsewhere") {
92
+ // On the call: the caller hung up, the server ended it, or a blind transfer handed it over.
93
+ out.push({ type: "remote_end", id, at: now });
94
+ }
95
+ }
96
+ return out;
97
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The `/agent/v1/ws` protocol (v1) as ReconnectingSocket options, and the
3
+ * mapping of its events to the SDK's typed events and to the call state machine.
4
+ *
5
+ * client → auth {token, lastSeq?} FIRST frame (the token never rides in the URL)
6
+ * → token.refresh {token, id} swap in a refreshed access token
7
+ * → ping
8
+ * server → ready {seq, expiresAt}
9
+ * → event {seq, event, data, at}
10
+ * → resync_required {fromSeq} events after your lastSeq are gone: refetch over REST
11
+ * → ack {id, ok, error?} · session.expiring {expiresAt} · pong · error {code}
12
+ * close 4001 token refused/expired (refresh, reconnect) · 4003 revoked (stop)
13
+ * 4008 auth timeout · 4029 rate limited (reconnect with backoff)
14
+ * 4009 too many sockets · 1003 binary frame (stop: retrying cannot fix them)
15
+ *
16
+ * The resume position travels in the auth frame, so the server replays from it
17
+ * atomically with authentication (no window in which a live event overtakes
18
+ * the replay).
19
+ */
20
+ import type { CallEvent, CallInfo, CallsState } from "./calls.cjs";
21
+ import type { AgentEventName } from "./contract.cjs";
22
+ import type { SocketOptions } from "./ws.cjs";
23
+ export type { AgentEventName } from "./contract.cjs";
24
+ /** One event frame from the server. */
25
+ export interface AgentEventFrame<D = Record<string, unknown>> {
26
+ v?: number;
27
+ type: "event";
28
+ event: AgentEventName;
29
+ seq: number;
30
+ at: string;
31
+ data: D;
32
+ }
33
+ /** Any frame from the server. */
34
+ export type AgentServerFrame = AgentEventFrame | {
35
+ v?: number;
36
+ type: "ready";
37
+ seq: number;
38
+ expiresAt?: string;
39
+ protocol?: number;
40
+ } | {
41
+ v?: number;
42
+ type: "resync_required";
43
+ fromSeq: number;
44
+ } | {
45
+ v?: number;
46
+ type: "ack";
47
+ id: string;
48
+ ok: boolean;
49
+ error?: string;
50
+ expiresAt?: string;
51
+ } | {
52
+ v?: number;
53
+ type: "session.expiring";
54
+ expiresAt: string;
55
+ } | {
56
+ v?: number;
57
+ type: "pong";
58
+ } | {
59
+ v?: number;
60
+ type: "error";
61
+ code?: string;
62
+ };
63
+ export type AgentClientFrame = {
64
+ type: "auth";
65
+ token: string;
66
+ lastSeq?: number;
67
+ } | {
68
+ type: "token.refresh";
69
+ token: string;
70
+ id: string;
71
+ } | {
72
+ type: "resume";
73
+ lastSeq: number;
74
+ id?: string;
75
+ } | {
76
+ type: "ping";
77
+ };
78
+ export declare const AGENT_CLOSE_CODES: {
79
+ readonly tokenExpired: 4001;
80
+ readonly revoked: 4003;
81
+ readonly authTimeout: 4008;
82
+ readonly tooManySockets: 4009;
83
+ readonly rateLimited: 4029;
84
+ };
85
+ /** The protocol as ReconnectingSocket options. `lastSeq()` is read when the auth frame is built. */
86
+ export declare function agentSocketBindings(lastSeq: () => number): Pick<SocketOptions<AgentServerFrame, AgentClientFrame>, "authFrame" | "isReady" | "resume" | "seqOf" | "tokenFrame" | "closeCodes" | "heartbeat">;
87
+ /** Is this a well-formed event frame with a name this SDK knows? Unknown names are passed on as `event.unknown`. */
88
+ export declare function isEventFrame(f: unknown): f is AgentEventFrame;
89
+ export declare const isKnownEvent: (name: string) => name is AgentEventName;
90
+ /** Pure: call.ringing data → the state machine's CallInfo (null when malformed). */
91
+ export declare function ringInfo(d: Record<string, unknown>, now: number, defaultRingMs: number): CallInfo | null;
92
+ /**
93
+ * Pure: an event → the state-machine events it causes (empty when it is not
94
+ * about calls this device knows). `state` resolves a call.ended that names only
95
+ * the conversation.
96
+ */
97
+ export declare function toCallEvents(frame: AgentEventFrame, state: CallsState, now: number, defaultRingMs?: number): CallEvent[];
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ /**
3
+ * @hainiva/agent-sdk — build your own agent app on HAINIVA AICC: staff sign-in
4
+ * with your backend's assertion, the live event stream, and calls.
5
+ *
6
+ * Entry points:
7
+ * "@hainiva/agent-sdk" transport-agnostic core (this file) — no LiveKit, no DOM, no React Native
8
+ * "@hainiva/agent-sdk/web" localStorage store (opt-in), LiveKit call media
9
+ * "@hainiva/agent-sdk/react-native" secure-store session, LiveKit call media, <HainivaDesk/>
10
+ * "@hainiva/agent-sdk/embed" mountDesk(): the HAINIVA desk in your web page (iframe)
11
+ *
12
+ * Start with HainivaAgent (./agent): it wires the session, the REST client,
13
+ * the event socket and the call state machine together. The parts are exported
14
+ * too, for apps that want their own wiring.
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
28
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
29
+ };
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.toCallEvents = exports.ringInfo = exports.isEventFrame = exports.agentSocketBindings = exports.AGENT_CLOSE_CODES = exports.newIdempotencyKey = exports.errorFromResponse = exports.AgentRestClient = exports.HainivaAgent = exports.initialCallsState = exports.ringingCalls = exports.activeCall = exports.transition = exports.CallStore = exports.backoffDelay = exports.ReconnectingSocket = exports.memoryStore = exports.SessionManager = exports.createTokenRefresher = exports.singleFlight = exports.TypedEmitter = exports.assertSafeUrl = exports.safeLogger = exports.redactValue = exports.redactString = exports.Redactor = exports.HainivaAgentError = exports.SDK_VERSION = void 0;
32
+ exports.SDK_VERSION = "0.3.0";
33
+ var errors_js_1 = require("./errors.cjs");
34
+ Object.defineProperty(exports, "HainivaAgentError", { enumerable: true, get: function () { return errors_js_1.HainivaAgentError; } });
35
+ var redact_js_1 = require("./redact.cjs");
36
+ Object.defineProperty(exports, "Redactor", { enumerable: true, get: function () { return redact_js_1.Redactor; } });
37
+ Object.defineProperty(exports, "redactString", { enumerable: true, get: function () { return redact_js_1.redactString; } });
38
+ Object.defineProperty(exports, "redactValue", { enumerable: true, get: function () { return redact_js_1.redactValue; } });
39
+ var log_js_1 = require("./log.cjs");
40
+ Object.defineProperty(exports, "safeLogger", { enumerable: true, get: function () { return log_js_1.safeLogger; } });
41
+ var url_js_1 = require("./url.cjs");
42
+ Object.defineProperty(exports, "assertSafeUrl", { enumerable: true, get: function () { return url_js_1.assertSafeUrl; } });
43
+ var emitter_js_1 = require("./emitter.cjs");
44
+ Object.defineProperty(exports, "TypedEmitter", { enumerable: true, get: function () { return emitter_js_1.TypedEmitter; } });
45
+ var singleFlight_js_1 = require("./singleFlight.cjs");
46
+ Object.defineProperty(exports, "singleFlight", { enumerable: true, get: function () { return singleFlight_js_1.singleFlight; } });
47
+ Object.defineProperty(exports, "createTokenRefresher", { enumerable: true, get: function () { return singleFlight_js_1.createTokenRefresher; } });
48
+ var session_js_1 = require("./session.cjs");
49
+ Object.defineProperty(exports, "SessionManager", { enumerable: true, get: function () { return session_js_1.SessionManager; } });
50
+ Object.defineProperty(exports, "memoryStore", { enumerable: true, get: function () { return session_js_1.memoryStore; } });
51
+ var ws_js_1 = require("./ws.cjs");
52
+ Object.defineProperty(exports, "ReconnectingSocket", { enumerable: true, get: function () { return ws_js_1.ReconnectingSocket; } });
53
+ Object.defineProperty(exports, "backoffDelay", { enumerable: true, get: function () { return ws_js_1.backoffDelay; } });
54
+ var calls_js_1 = require("./calls.cjs");
55
+ Object.defineProperty(exports, "CallStore", { enumerable: true, get: function () { return calls_js_1.CallStore; } });
56
+ Object.defineProperty(exports, "transition", { enumerable: true, get: function () { return calls_js_1.transition; } });
57
+ Object.defineProperty(exports, "activeCall", { enumerable: true, get: function () { return calls_js_1.activeCall; } });
58
+ Object.defineProperty(exports, "ringingCalls", { enumerable: true, get: function () { return calls_js_1.ringingCalls; } });
59
+ Object.defineProperty(exports, "initialCallsState", { enumerable: true, get: function () { return calls_js_1.initialCallsState; } });
60
+ var agent_js_1 = require("./agent.cjs");
61
+ Object.defineProperty(exports, "HainivaAgent", { enumerable: true, get: function () { return agent_js_1.HainivaAgent; } });
62
+ var rest_js_1 = require("./rest.cjs");
63
+ Object.defineProperty(exports, "AgentRestClient", { enumerable: true, get: function () { return rest_js_1.AgentRestClient; } });
64
+ Object.defineProperty(exports, "errorFromResponse", { enumerable: true, get: function () { return rest_js_1.errorFromResponse; } });
65
+ Object.defineProperty(exports, "newIdempotencyKey", { enumerable: true, get: function () { return rest_js_1.newIdempotencyKey; } });
66
+ var events_js_1 = require("./events.cjs");
67
+ Object.defineProperty(exports, "AGENT_CLOSE_CODES", { enumerable: true, get: function () { return events_js_1.AGENT_CLOSE_CODES; } });
68
+ Object.defineProperty(exports, "agentSocketBindings", { enumerable: true, get: function () { return events_js_1.agentSocketBindings; } });
69
+ Object.defineProperty(exports, "isEventFrame", { enumerable: true, get: function () { return events_js_1.isEventFrame; } });
70
+ Object.defineProperty(exports, "ringInfo", { enumerable: true, get: function () { return events_js_1.ringInfo; } });
71
+ Object.defineProperty(exports, "toCallEvents", { enumerable: true, get: function () { return events_js_1.toCallEvents; } });
72
+ __exportStar(require("./contract.cjs"), exports);
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @hainiva/agent-sdk — build your own agent app on HAINIVA AICC: staff sign-in
3
+ * with your backend's assertion, the live event stream, and calls.
4
+ *
5
+ * Entry points:
6
+ * "@hainiva/agent-sdk" transport-agnostic core (this file) — no LiveKit, no DOM, no React Native
7
+ * "@hainiva/agent-sdk/web" localStorage store (opt-in), LiveKit call media
8
+ * "@hainiva/agent-sdk/react-native" secure-store session, LiveKit call media, <HainivaDesk/>
9
+ * "@hainiva/agent-sdk/embed" mountDesk(): the HAINIVA desk in your web page (iframe)
10
+ *
11
+ * Start with HainivaAgent (./agent): it wires the session, the REST client,
12
+ * the event socket and the call state machine together. The parts are exported
13
+ * too, for apps that want their own wiring.
14
+ */
15
+ export declare const SDK_VERSION = "0.3.0";
16
+ export { HainivaAgentError } from "./errors.cjs";
17
+ export { Redactor, redactString, redactValue } from "./redact.cjs";
18
+ export { safeLogger, type Logger } from "./log.cjs";
19
+ export { assertSafeUrl, type UrlRules } from "./url.cjs";
20
+ export { TypedEmitter } from "./emitter.cjs";
21
+ export { singleFlight, createTokenRefresher, type RefreshedToken, type TokenRefresher, type TokenRefresherOptions } from "./singleFlight.cjs";
22
+ export { SessionManager, memoryStore, type AgentSession, type GetAssertion, type SessionEvents, type SessionManagerOptions, type SessionStore, type SessionTransport, } from "./session.cjs";
23
+ export { ReconnectingSocket, backoffDelay, type ConnectionState, type SocketEvents, type SocketOptions, type WebSocketCtor, type WebSocketLike, } from "./ws.cjs";
24
+ export { CallStore, transition, activeCall, ringingCalls, initialCallsState, type Call, type CallDirection, type CallEffect, type CallEvent, type CallInfo, type CallPath, type CallPhase, type CallsState, type CallStoreEvents, type CallStoreOptions, type EndReason, type Transition, type TransferState, type TransferTarget, } from "./calls.cjs";
25
+ export { HainivaAgent, type HainivaAgentEvents, type HainivaAgentOptions, type JoinMedia, type MediaSession } from "./agent.cjs";
26
+ export { AgentRestClient, errorFromResponse, newIdempotencyKey, type AgentRestOptions, type AgentStatus, type CallTransferTarget, type ConversationListQuery, type MediaCredentials, type RequestOptions, type TokenSource, } from "./rest.cjs";
27
+ export { AGENT_CLOSE_CODES, agentSocketBindings, isEventFrame, ringInfo, toCallEvents, type AgentClientFrame, type AgentEventFrame, type AgentServerFrame, } from "./events.cjs";
28
+ export * from "./contract.cjs";
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.joinCallRoom = joinCallRoom;
4
+ /**
5
+ * Joining a call's LiveKit room. Used by the /web and /react-native entry points
6
+ * only: `livekit-client` is a peer dependency, and the core never imports it.
7
+ */
8
+ const livekit_client_1 = require("livekit-client");
9
+ const errors_js_1 = require("./errors.cjs");
10
+ const redact_js_1 = require("./redact.cjs");
11
+ const url_js_1 = require("./url.cjs");
12
+ /**
13
+ * Join with the call's media credentials, microphone on. The token is handed to
14
+ * LiveKit only; errors are redacted (LiveKit's own error text can contain its
15
+ * signalling URL, which carries the token).
16
+ */
17
+ async function joinCallRoom(join, o = {}) {
18
+ const redactor = o.redactor ?? new redact_js_1.Redactor();
19
+ redactor.add(join.token);
20
+ (0, url_js_1.assertSafeUrl)(join.url, { kind: "ws", ...(o.allowInsecureLocalhost ? { allowInsecureLocalhost: true } : {}) });
21
+ if (!join.token)
22
+ throw new errors_js_1.HainivaAgentError(0, "no_media_token");
23
+ const room = new livekit_client_1.Room(o.roomOptions);
24
+ try {
25
+ await room.connect(join.url, join.token, { autoSubscribe: true, ...o.connectOptions });
26
+ if (o.microphone !== false)
27
+ await room.localParticipant.setMicrophoneEnabled(true);
28
+ }
29
+ catch (e) {
30
+ await room.disconnect().catch(() => undefined);
31
+ throw errors_js_1.HainivaAgentError.from(e, "media_connect_failed", redactor);
32
+ }
33
+ let left = false;
34
+ return {
35
+ room,
36
+ async setMuted(muted) {
37
+ try {
38
+ await room.localParticipant.setMicrophoneEnabled(!muted);
39
+ }
40
+ catch (e) {
41
+ throw errors_js_1.HainivaAgentError.from(e, "media_mute_failed", redactor);
42
+ }
43
+ },
44
+ async leave() {
45
+ if (left)
46
+ return;
47
+ left = true;
48
+ await room.disconnect().catch(() => undefined);
49
+ },
50
+ onDisconnected(fn) {
51
+ const h = () => fn();
52
+ room.on(livekit_client_1.RoomEvent.Disconnected, h);
53
+ return () => { room.off(livekit_client_1.RoomEvent.Disconnected, h); };
54
+ },
55
+ };
56
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Joining a call's LiveKit room. Used by the /web and /react-native entry points
3
+ * only: `livekit-client` is a peer dependency, and the core never imports it.
4
+ */
5
+ import { Room, type RoomConnectOptions, type RoomOptions } from "livekit-client";
6
+ import { Redactor } from "./redact.cjs";
7
+ export interface MediaJoin {
8
+ /** wss:// LiveKit URL. */
9
+ url: string;
10
+ /** Room token for this call — kept out of every error. */
11
+ token: string;
12
+ }
13
+ export interface JoinOptions {
14
+ /** Start with the microphone on. Default true. */
15
+ microphone?: boolean;
16
+ roomOptions?: RoomOptions;
17
+ connectOptions?: RoomConnectOptions;
18
+ /** ws://localhost in development. Default false. */
19
+ allowInsecureLocalhost?: boolean;
20
+ redactor?: Redactor;
21
+ }
22
+ export interface MediaCall {
23
+ readonly room: Room;
24
+ setMuted(muted: boolean): Promise<void>;
25
+ /** Leave the room (idempotent). */
26
+ leave(): Promise<void>;
27
+ /** The room closed (far end hung up, server ended it, network lost). Returns an unsubscribe. */
28
+ onDisconnected(fn: () => void): () => void;
29
+ }
30
+ /**
31
+ * Join with the call's media credentials, microphone on. The token is handed to
32
+ * LiveKit only; errors are redacted (LiveKit's own error text can contain its
33
+ * signalling URL, which carries the token).
34
+ */
35
+ export declare function joinCallRoom(join: MediaJoin, o?: JoinOptions): Promise<MediaCall>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safeLogger = safeLogger;
4
+ const redact_js_1 = require("./redact.cjs");
5
+ /** A logger that redacts every detail before the partner's logger sees it. */
6
+ function safeLogger(log, redactor = new redact_js_1.Redactor()) {
7
+ if (!log)
8
+ return () => { };
9
+ return (event, detail) => {
10
+ try {
11
+ log(redactor.string(event), detail === undefined ? undefined : redactor.value(detail));
12
+ }
13
+ catch { /* a broken logger must not break a call */ }
14
+ };
15
+ }
@@ -0,0 +1,5 @@
1
+ import { Redactor } from "./redact.cjs";
2
+ /** The partner's logger. The SDK logs nothing unless one is given. */
3
+ export type Logger = (event: string, detail?: unknown) => void;
4
+ /** A logger that redacts every detail before the partner's logger sees it. */
5
+ export declare function safeLogger(log: Logger | undefined, redactor?: Redactor): Logger;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deskBridgeScript = exports.HainivaDesk = exports.joinCallRoom = void 0;
4
+ exports.secureStoreSessionStore = secureStoreSessionStore;
5
+ var livekit_js_1 = require("./livekit.cjs");
6
+ Object.defineProperty(exports, "joinCallRoom", { enumerable: true, get: function () { return livekit_js_1.joinCallRoom; } });
7
+ /**
8
+ * The session (and so the refresh token) in secure storage only. There is
9
+ * deliberately no fallback to AsyncStorage or another plain store: if secure
10
+ * storage fails, the session stays in memory and the next launch signs in again
11
+ * with a fresh assertion.
12
+ *
13
+ * import * as SecureStore from "expo-secure-store";
14
+ * const store = secureStoreSessionStore(SecureStore);
15
+ */
16
+ function secureStoreSessionStore(secure, o = {}) {
17
+ const key = o.key ?? "hainiva.agent.session";
18
+ if (!/^[A-Za-z0-9._-]+$/.test(key))
19
+ throw new Error("secureStoreSessionStore: key may use only letters, digits, '.', '-' and '_'");
20
+ const access = o.keychainAccessible ?? secure.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY;
21
+ const opts = access === undefined ? {} : { keychainAccessible: access };
22
+ return {
23
+ get: () => secure.getItemAsync(key, opts),
24
+ set: (v) => secure.setItemAsync(key, v, opts),
25
+ remove: () => secure.deleteItemAsync(key, opts),
26
+ };
27
+ }
28
+ // The embedded HAINIVA desk in a WebView (react-native-webview, an optional
29
+ // peer), signed in with your agent session; calls stay native in your app.
30
+ var reactNativeDesk_js_1 = require("./reactNativeDesk.cjs");
31
+ Object.defineProperty(exports, "HainivaDesk", { enumerable: true, get: function () { return reactNativeDesk_js_1.HainivaDesk; } });
32
+ Object.defineProperty(exports, "deskBridgeScript", { enumerable: true, get: function () { return reactNativeDesk_js_1.deskBridgeScript; } });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @hainiva/agent-sdk/react-native — React Native adapters: the session in the
3
+ * Keychain / Android Keystore (expo-secure-store) and LiveKit call media.
4
+ *
5
+ * Call `registerGlobals()` from `@livekit/react-native` once at startup before
6
+ * joining a room. Native ringing (CallKit / Android call notification) is
7
+ * `@hainiva/react-native-call`.
8
+ */
9
+ import type { SessionStore } from "./session.cjs";
10
+ export { joinCallRoom, type JoinOptions, type MediaCall, type MediaJoin } from "./livekit.cjs";
11
+ /** The part of `expo-secure-store` this adapter uses: pass the module itself. */
12
+ export interface SecureStoreModule {
13
+ getItemAsync(key: string, options?: Record<string, unknown>): Promise<string | null>;
14
+ setItemAsync(key: string, value: string, options?: Record<string, unknown>): Promise<void>;
15
+ deleteItemAsync(key: string, options?: Record<string, unknown>): Promise<void>;
16
+ /** expo-secure-store's constant; used as the default accessibility. */
17
+ AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY?: unknown;
18
+ }
19
+ export interface SecureStoreSessionOptions {
20
+ /** Default "hainiva.agent.session". Letters, digits, ".", "-" and "_" only. */
21
+ key?: string;
22
+ /**
23
+ * iPhone Keychain accessibility. Default AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY:
24
+ * readable while the phone is locked (a call that wakes the app must find the
25
+ * session) and never migrated to another device.
26
+ */
27
+ keychainAccessible?: unknown;
28
+ }
29
+ /**
30
+ * The session (and so the refresh token) in secure storage only. There is
31
+ * deliberately no fallback to AsyncStorage or another plain store: if secure
32
+ * storage fails, the session stays in memory and the next launch signs in again
33
+ * with a fresh assertion.
34
+ *
35
+ * import * as SecureStore from "expo-secure-store";
36
+ * const store = secureStoreSessionStore(SecureStore);
37
+ */
38
+ export declare function secureStoreSessionStore(secure: SecureStoreModule, o?: SecureStoreSessionOptions): SessionStore;
39
+ export { HainivaDesk, deskBridgeScript, type HainivaDeskProps, type WebViewLike } from "./reactNativeDesk.cjs";