@hainiva/agent-sdk 0.0.0-stage → 0.2.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/CHANGELOG.md +54 -0
- package/LICENSE +21 -0
- package/README.md +201 -2
- package/SECURITY.md +33 -0
- package/dist/cjs/agent.cjs +316 -0
- package/dist/cjs/agent.d.cts +235 -0
- package/dist/cjs/calls.cjs +277 -0
- package/dist/cjs/calls.d.cts +232 -0
- package/dist/cjs/contract.cjs +14 -0
- package/dist/cjs/contract.d.cts +215 -0
- package/dist/cjs/embed.cjs +11 -0
- package/dist/cjs/embed.d.cts +13 -0
- package/dist/cjs/emitter.cjs +50 -0
- package/dist/cjs/emitter.d.cts +22 -0
- package/dist/cjs/errors.cjs +28 -0
- package/dist/cjs/errors.d.cts +15 -0
- package/dist/cjs/events.cjs +97 -0
- package/dist/cjs/events.d.cts +97 -0
- package/dist/cjs/index.cjs +72 -0
- package/dist/cjs/index.d.cts +28 -0
- package/dist/cjs/livekit.cjs +56 -0
- package/dist/cjs/livekit.d.cts +35 -0
- package/dist/cjs/log.cjs +15 -0
- package/dist/cjs/log.d.cts +5 -0
- package/dist/cjs/react-native.cjs +27 -0
- package/dist/cjs/react-native.d.cts +38 -0
- package/dist/cjs/redact.cjs +71 -0
- package/dist/cjs/redact.d.cts +21 -0
- package/dist/cjs/rest.cjs +280 -0
- package/dist/cjs/rest.d.cts +166 -0
- package/dist/cjs/session.cjs +159 -0
- package/dist/cjs/session.d.cts +101 -0
- package/dist/cjs/singleFlight.cjs +49 -0
- package/dist/cjs/singleFlight.d.cts +32 -0
- package/dist/cjs/url.cjs +39 -0
- package/dist/cjs/url.d.cts +13 -0
- package/dist/cjs/web.cjs +28 -0
- package/dist/cjs/web.d.cts +23 -0
- package/dist/cjs/ws.cjs +246 -0
- package/dist/cjs/ws.d.cts +134 -0
- package/dist/esm/agent.d.ts +236 -0
- package/dist/esm/agent.d.ts.map +1 -0
- package/dist/esm/agent.js +313 -0
- package/dist/esm/agent.js.map +1 -0
- package/dist/esm/calls.d.ts +233 -0
- package/dist/esm/calls.d.ts.map +1 -0
- package/dist/esm/calls.js +271 -0
- package/dist/esm/calls.js.map +1 -0
- package/dist/esm/contract.d.ts +216 -0
- package/dist/esm/contract.d.ts.map +1 -0
- package/dist/esm/contract.js +12 -0
- package/dist/esm/contract.js.map +1 -0
- package/dist/esm/embed.d.ts +14 -0
- package/dist/esm/embed.d.ts.map +1 -0
- package/dist/esm/embed.js +9 -0
- package/dist/esm/embed.js.map +1 -0
- package/dist/esm/emitter.d.ts +23 -0
- package/dist/esm/emitter.d.ts.map +1 -0
- package/dist/esm/emitter.js +47 -0
- package/dist/esm/emitter.js.map +1 -0
- package/dist/esm/errors.d.ts +16 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +25 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/events.d.ts +98 -0
- package/dist/esm/events.d.ts.map +1 -0
- package/dist/esm/events.js +90 -0
- package/dist/esm/events.js.map +1 -0
- package/dist/esm/index.d.ts +29 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +29 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/livekit.d.ts +36 -0
- package/dist/esm/livekit.d.ts.map +1 -0
- package/dist/esm/livekit.js +54 -0
- package/dist/esm/livekit.js.map +1 -0
- package/dist/esm/log.d.ts +6 -0
- package/dist/esm/log.d.ts.map +1 -0
- package/dist/esm/log.js +13 -0
- package/dist/esm/log.js.map +1 -0
- package/dist/esm/react-native.d.ts +39 -0
- package/dist/esm/react-native.d.ts.map +1 -0
- package/dist/esm/react-native.js +23 -0
- package/dist/esm/react-native.js.map +1 -0
- package/dist/esm/redact.d.ts +22 -0
- package/dist/esm/redact.d.ts.map +1 -0
- package/dist/esm/redact.js +66 -0
- package/dist/esm/redact.js.map +1 -0
- package/dist/esm/rest.d.ts +167 -0
- package/dist/esm/rest.d.ts.map +1 -0
- package/dist/esm/rest.js +275 -0
- package/dist/esm/rest.js.map +1 -0
- package/dist/esm/session.d.ts +102 -0
- package/dist/esm/session.d.ts.map +1 -0
- package/dist/esm/session.js +155 -0
- package/dist/esm/session.js.map +1 -0
- package/dist/esm/singleFlight.d.ts +33 -0
- package/dist/esm/singleFlight.d.ts.map +1 -0
- package/dist/esm/singleFlight.js +46 -0
- package/dist/esm/singleFlight.js.map +1 -0
- package/dist/esm/url.d.ts +14 -0
- package/dist/esm/url.d.ts.map +1 -0
- package/dist/esm/url.js +37 -0
- package/dist/esm/url.js.map +1 -0
- package/dist/esm/web.d.ts +24 -0
- package/dist/esm/web.d.ts.map +1 -0
- package/dist/esm/web.js +23 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/esm/ws.d.ts +135 -0
- package/dist/esm/ws.d.ts.map +1 -0
- package/dist/esm/ws.js +242 -0
- package/dist/esm/ws.js.map +1 -0
- package/package.json +80 -4
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { TypedEmitter } from "./emitter.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* The agent's calls as a pure state machine — ported from the HAINIVA Agent app
|
|
4
|
+
* (apps/agent-mobile/src/calls.ts), without React Native, LiveKit or HTTP: the
|
|
5
|
+
* host feeds it events (a ring push, the user's answer, "media connected", the
|
|
6
|
+
* server's cancel…) and acts on the effects it returns (stop the native ring,
|
|
7
|
+
* leave the room). Same rules as the app:
|
|
8
|
+
*
|
|
9
|
+
* - one call at a time: a ring while on a call is turned away ("busy"), and
|
|
10
|
+
* answering one ring turns away the others;
|
|
11
|
+
* - a ring ends at its expiresAt ("missed"), when declined, or when the server
|
|
12
|
+
* cancels it (taken elsewhere, caller gone);
|
|
13
|
+
* - while answering, "taken elsewhere" is ignored (the server's answer race
|
|
14
|
+
* decides, via accept_failed) but the caller hanging up ends it, and media
|
|
15
|
+
* that connects after that is left at once;
|
|
16
|
+
* - hold, mute and warm/blind transfer as on the desk.
|
|
17
|
+
*
|
|
18
|
+
* ringing ──accept──▶ connecting ──connected──▶ active ⇄ held
|
|
19
|
+
* │ │ │ └─transfer_start─▶ transferring
|
|
20
|
+
* ├─decline/expire/ └─accept_failed/ │ │
|
|
21
|
+
* │ ring_cancelled remote_end/hang_up └─remote_end/hang_up ├─transfer_completed
|
|
22
|
+
* ▼ ▼ ▼ ▼
|
|
23
|
+
* ended ◀──────────────────────────────────────────────────────────── ended
|
|
24
|
+
*/
|
|
25
|
+
export type CallDirection = "inbound" | "outbound";
|
|
26
|
+
/** direct / queue: a ring; transfer: a colleague's transfer invite; out: placed here. */
|
|
27
|
+
export type CallPath = "direct" | "queue" | "transfer" | "out";
|
|
28
|
+
export interface CallInfo {
|
|
29
|
+
/** The interaction id (or a client id for an outgoing call until the server's arrives). */
|
|
30
|
+
id: string;
|
|
31
|
+
conversationId?: string;
|
|
32
|
+
direction: CallDirection;
|
|
33
|
+
path: CallPath;
|
|
34
|
+
/** Caller (inbound) or callee (outbound) as shown. */
|
|
35
|
+
from: string;
|
|
36
|
+
line?: string;
|
|
37
|
+
/** Epoch ms when an unanswered ring ends. */
|
|
38
|
+
expiresAt?: number;
|
|
39
|
+
/** A transfer invite: how the colleague is handing it over. */
|
|
40
|
+
transfer?: {
|
|
41
|
+
mode: "warm" | "blind";
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export type CallPhase = "ringing" | "connecting" | "active" | "held" | "transferring" | "ended";
|
|
45
|
+
export type EndReason = "declined" | "missed" | "answered_elsewhere" | "remote_ended" | "hung_up" | "transferred" | "busy" | "failed" | "cancelled";
|
|
46
|
+
export type TransferTarget = {
|
|
47
|
+
kind: "agent";
|
|
48
|
+
agentId: string;
|
|
49
|
+
name: string;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "queue";
|
|
52
|
+
queueId: string;
|
|
53
|
+
name: string;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "external";
|
|
56
|
+
number: string;
|
|
57
|
+
};
|
|
58
|
+
export interface TransferState {
|
|
59
|
+
mode: "warm" | "blind";
|
|
60
|
+
target: TransferTarget;
|
|
61
|
+
/** Warm: the colleague / number is in the call. */
|
|
62
|
+
joined: boolean;
|
|
63
|
+
startedAt: number;
|
|
64
|
+
/** Where a cancelled transfer returns to. */
|
|
65
|
+
resumeTo: "active" | "held";
|
|
66
|
+
}
|
|
67
|
+
export interface Call {
|
|
68
|
+
readonly info: CallInfo;
|
|
69
|
+
readonly phase: CallPhase;
|
|
70
|
+
readonly muted: boolean;
|
|
71
|
+
/** When talking started; null while an outgoing call still rings at the far end. */
|
|
72
|
+
readonly since: number | null;
|
|
73
|
+
readonly ringingSince: number | null;
|
|
74
|
+
readonly transfer: TransferState | null;
|
|
75
|
+
/** A short note for the UI (e.g. "colleague left"). */
|
|
76
|
+
readonly notice: string | null;
|
|
77
|
+
readonly endReason: EndReason | null;
|
|
78
|
+
readonly endedAt: number | null;
|
|
79
|
+
readonly error: string | null;
|
|
80
|
+
}
|
|
81
|
+
export interface CallsState {
|
|
82
|
+
readonly calls: Readonly<Record<string, Call>>;
|
|
83
|
+
/** The call that holds the line (connecting, active, held, transferring), if any. */
|
|
84
|
+
readonly activeId: string | null;
|
|
85
|
+
}
|
|
86
|
+
export declare const initialCallsState: CallsState;
|
|
87
|
+
export type CallEvent = {
|
|
88
|
+
type: "ring";
|
|
89
|
+
call: CallInfo;
|
|
90
|
+
at: number;
|
|
91
|
+
} | {
|
|
92
|
+
type: "dial";
|
|
93
|
+
call: CallInfo;
|
|
94
|
+
at: number;
|
|
95
|
+
} | {
|
|
96
|
+
type: "accept";
|
|
97
|
+
id: string;
|
|
98
|
+
at: number;
|
|
99
|
+
} | {
|
|
100
|
+
type: "decline";
|
|
101
|
+
id: string;
|
|
102
|
+
at: number;
|
|
103
|
+
} | {
|
|
104
|
+
type: "expire";
|
|
105
|
+
id: string;
|
|
106
|
+
at: number;
|
|
107
|
+
} | {
|
|
108
|
+
type: "ring_cancelled";
|
|
109
|
+
id: string;
|
|
110
|
+
reason: "answered_elsewhere" | "remote_ended" | "cancelled";
|
|
111
|
+
at: number;
|
|
112
|
+
} | {
|
|
113
|
+
type: "accept_failed";
|
|
114
|
+
id: string;
|
|
115
|
+
reason: "answered_elsewhere" | "gone" | "failed";
|
|
116
|
+
error?: string;
|
|
117
|
+
at: number;
|
|
118
|
+
} | {
|
|
119
|
+
type: "connected";
|
|
120
|
+
id: string;
|
|
121
|
+
answered: boolean;
|
|
122
|
+
at: number;
|
|
123
|
+
} | {
|
|
124
|
+
type: "answered";
|
|
125
|
+
id: string;
|
|
126
|
+
at: number;
|
|
127
|
+
} | {
|
|
128
|
+
type: "mute";
|
|
129
|
+
id: string;
|
|
130
|
+
muted: boolean;
|
|
131
|
+
} | {
|
|
132
|
+
type: "hold";
|
|
133
|
+
id: string;
|
|
134
|
+
} | {
|
|
135
|
+
type: "resume";
|
|
136
|
+
id: string;
|
|
137
|
+
} | {
|
|
138
|
+
type: "transfer_start";
|
|
139
|
+
id: string;
|
|
140
|
+
mode: "warm" | "blind";
|
|
141
|
+
target: TransferTarget;
|
|
142
|
+
at: number;
|
|
143
|
+
} | {
|
|
144
|
+
type: "transfer_joined";
|
|
145
|
+
id: string;
|
|
146
|
+
} | {
|
|
147
|
+
type: "transfer_cancelled";
|
|
148
|
+
id: string;
|
|
149
|
+
notice?: string;
|
|
150
|
+
} | {
|
|
151
|
+
type: "transfer_completed";
|
|
152
|
+
id: string;
|
|
153
|
+
at: number;
|
|
154
|
+
} | {
|
|
155
|
+
type: "remote_end";
|
|
156
|
+
id: string;
|
|
157
|
+
at: number;
|
|
158
|
+
} | {
|
|
159
|
+
type: "hang_up";
|
|
160
|
+
id: string;
|
|
161
|
+
at: number;
|
|
162
|
+
} | {
|
|
163
|
+
type: "rekey";
|
|
164
|
+
id: string;
|
|
165
|
+
newId: string;
|
|
166
|
+
conversationId?: string;
|
|
167
|
+
} | {
|
|
168
|
+
type: "dismiss";
|
|
169
|
+
id: string;
|
|
170
|
+
};
|
|
171
|
+
/** What the host must do after a transition. */
|
|
172
|
+
export type CallEffect = {
|
|
173
|
+
type: "ring.show";
|
|
174
|
+
call: CallInfo;
|
|
175
|
+
}
|
|
176
|
+
/** Stop this device ringing for the call (native ring / CallKit). */
|
|
177
|
+
| {
|
|
178
|
+
type: "ring.cancel";
|
|
179
|
+
id: string;
|
|
180
|
+
}
|
|
181
|
+
/** Leave the call's media room (or never join it). */
|
|
182
|
+
| {
|
|
183
|
+
type: "media.leave";
|
|
184
|
+
id: string;
|
|
185
|
+
} | {
|
|
186
|
+
type: "ended";
|
|
187
|
+
id: string;
|
|
188
|
+
reason: EndReason;
|
|
189
|
+
};
|
|
190
|
+
export interface Transition {
|
|
191
|
+
state: CallsState;
|
|
192
|
+
effects: CallEffect[];
|
|
193
|
+
/** False when the event did not apply (state is the same object). */
|
|
194
|
+
changed: boolean;
|
|
195
|
+
}
|
|
196
|
+
/** Apply one event. Pure: never mutates `state`. */
|
|
197
|
+
export declare function transition(state: CallsState, ev: CallEvent): Transition;
|
|
198
|
+
/** The call holding the line, if any. */
|
|
199
|
+
export declare function activeCall(state: CallsState): Call | null;
|
|
200
|
+
/** Calls ringing now, oldest first. */
|
|
201
|
+
export declare function ringingCalls(state: CallsState): Call[];
|
|
202
|
+
export interface CallStoreEvents {
|
|
203
|
+
change: CallsState;
|
|
204
|
+
effect: CallEffect;
|
|
205
|
+
}
|
|
206
|
+
export interface CallStoreOptions {
|
|
207
|
+
now?: () => number;
|
|
208
|
+
/** Remove ended calls this long after they end (0 = keep until dismissed). Default 0. */
|
|
209
|
+
forgetEndedAfterMs?: number;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The state machine with a clock: rings expire on their own, and listeners hear
|
|
213
|
+
* every change and effect. Still free of React Native, LiveKit and HTTP.
|
|
214
|
+
*/
|
|
215
|
+
export declare class CallStore {
|
|
216
|
+
private readonly o;
|
|
217
|
+
readonly events: TypedEmitter<CallStoreEvents>;
|
|
218
|
+
private st;
|
|
219
|
+
private readonly timers;
|
|
220
|
+
private readonly now;
|
|
221
|
+
constructor(o?: CallStoreOptions);
|
|
222
|
+
get state(): CallsState;
|
|
223
|
+
/** Apply an event. `at` is filled from the clock when omitted. */
|
|
224
|
+
dispatch(ev: DistributiveOmit<CallEvent, "at"> & {
|
|
225
|
+
at?: number;
|
|
226
|
+
}): Transition;
|
|
227
|
+
/** Stop every timer (e.g. on sign-out). */
|
|
228
|
+
dispose(): void;
|
|
229
|
+
private syncTimers;
|
|
230
|
+
}
|
|
231
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
232
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GENERATED from the HAINIVA monorepo (@aicc/types agentApi.ts and
|
|
4
|
+
* agentEvents.ts) — do not edit by hand. The Agent API's response shapes and
|
|
5
|
+
* event names: additive changes only within a major version.
|
|
6
|
+
*/
|
|
7
|
+
/* eslint-disable */
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AGENT_EVENT_NAMES = void 0;
|
|
10
|
+
// ── The event stream (/agent/v1/ws) ──────────────────────────────────────────
|
|
11
|
+
exports.AGENT_EVENT_NAMES = [
|
|
12
|
+
"call.ringing", "call.ended", "conversation.assigned", "message.created",
|
|
13
|
+
"wrapup.required", "presence.changed", "supervisor.alert",
|
|
14
|
+
];
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED from the HAINIVA monorepo (@aicc/types agentApi.ts and
|
|
3
|
+
* agentEvents.ts) — do not edit by hand. The Agent API's response shapes and
|
|
4
|
+
* event names: additive changes only within a major version.
|
|
5
|
+
*/
|
|
6
|
+
export type AgentPresenceStatus = "online" | "busy" | "away" | "offline";
|
|
7
|
+
export type AgentRole = "agent" | "supervisor";
|
|
8
|
+
export interface AgentMeDto {
|
|
9
|
+
id: string;
|
|
10
|
+
externalId: string | null;
|
|
11
|
+
displayName: string | null;
|
|
12
|
+
role: AgentRole;
|
|
13
|
+
/** Team ids this member works in; null = the whole organisation. */
|
|
14
|
+
teams: string[] | null;
|
|
15
|
+
status: AgentPresenceStatus | null;
|
|
16
|
+
session: {
|
|
17
|
+
id: string;
|
|
18
|
+
agentAppId: string;
|
|
19
|
+
mode: "live" | "test";
|
|
20
|
+
expiresAt: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface AgentRingDto {
|
|
24
|
+
callId: string;
|
|
25
|
+
conversationId: string;
|
|
26
|
+
kind: "voice" | "video" | "chat";
|
|
27
|
+
channel: string;
|
|
28
|
+
contactName: string | null;
|
|
29
|
+
callerNumber: string | null;
|
|
30
|
+
/** direct = routed to you; queue = offered to your queue; escalation = a live call handed to you. */
|
|
31
|
+
path: "direct" | "queue" | "escalation";
|
|
32
|
+
startedAt: string;
|
|
33
|
+
}
|
|
34
|
+
export interface AgentCallDto {
|
|
35
|
+
callId: string;
|
|
36
|
+
conversationId: string;
|
|
37
|
+
kind: "voice" | "video" | "chat";
|
|
38
|
+
channel: string;
|
|
39
|
+
direction: "inbound" | "outbound" | "internal";
|
|
40
|
+
status: string;
|
|
41
|
+
assignedToMe: boolean;
|
|
42
|
+
startedAt: string;
|
|
43
|
+
endedAt: string | null;
|
|
44
|
+
contactName: string | null;
|
|
45
|
+
callerNumber: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface AgentMediaTokenDto {
|
|
48
|
+
token: string;
|
|
49
|
+
roomName: string;
|
|
50
|
+
livekitUrl: string;
|
|
51
|
+
expiresAt: string;
|
|
52
|
+
}
|
|
53
|
+
export interface AgentConversationDto {
|
|
54
|
+
id: string;
|
|
55
|
+
status: string;
|
|
56
|
+
subject: string | null;
|
|
57
|
+
channel: string | null;
|
|
58
|
+
chatType: string | null;
|
|
59
|
+
contact: {
|
|
60
|
+
id: string;
|
|
61
|
+
name: string | null;
|
|
62
|
+
phone: string | null;
|
|
63
|
+
};
|
|
64
|
+
assignedAgentId: string | null;
|
|
65
|
+
assignedToMe: boolean;
|
|
66
|
+
queueId: string | null;
|
|
67
|
+
/** The open interaction a message or file goes to (null when none is open). */
|
|
68
|
+
activeInteractionId: string | null;
|
|
69
|
+
lastMessageAt: string | null;
|
|
70
|
+
createdAt: string;
|
|
71
|
+
closedAt: string | null;
|
|
72
|
+
}
|
|
73
|
+
export interface AgentMessageDto {
|
|
74
|
+
id: string;
|
|
75
|
+
conversationId: string;
|
|
76
|
+
interactionId: string;
|
|
77
|
+
direction: string;
|
|
78
|
+
senderType: string;
|
|
79
|
+
/** agents.id of the sender when an agent wrote it. */
|
|
80
|
+
senderAgentId: string | null;
|
|
81
|
+
senderName: string | null;
|
|
82
|
+
type: string;
|
|
83
|
+
text: string | null;
|
|
84
|
+
attachment: {
|
|
85
|
+
name: string | null;
|
|
86
|
+
mimeType: string | null;
|
|
87
|
+
size: number | null;
|
|
88
|
+
} | null;
|
|
89
|
+
createdAt: string;
|
|
90
|
+
}
|
|
91
|
+
export interface AgentDispositionDto {
|
|
92
|
+
code: string;
|
|
93
|
+
label: string;
|
|
94
|
+
channel: string | null;
|
|
95
|
+
required: boolean;
|
|
96
|
+
}
|
|
97
|
+
export interface SupervisorLiveCallDto {
|
|
98
|
+
callId: string;
|
|
99
|
+
conversationId: string;
|
|
100
|
+
agentId: string | null;
|
|
101
|
+
agentName: string | null;
|
|
102
|
+
channel: string;
|
|
103
|
+
status: string;
|
|
104
|
+
startedAt: string;
|
|
105
|
+
contactName: string | null;
|
|
106
|
+
callerNumber: string | null;
|
|
107
|
+
queueId: string | null;
|
|
108
|
+
}
|
|
109
|
+
export interface SupervisorAgentDto {
|
|
110
|
+
id: string;
|
|
111
|
+
displayName: string;
|
|
112
|
+
externalId: string | null;
|
|
113
|
+
role: AgentRole;
|
|
114
|
+
teamId: string | null;
|
|
115
|
+
status: AgentPresenceStatus;
|
|
116
|
+
activeCalls: number;
|
|
117
|
+
openConversations: number;
|
|
118
|
+
}
|
|
119
|
+
export interface AgentPage<T> {
|
|
120
|
+
data: T[];
|
|
121
|
+
nextCursor: string | null;
|
|
122
|
+
}
|
|
123
|
+
/** POST /agent/v1/sessions (201) and POST /agent/v1/sessions/refresh (200). */
|
|
124
|
+
export interface AgentSessionDto {
|
|
125
|
+
accessToken: string;
|
|
126
|
+
accessExpiresAt: string;
|
|
127
|
+
/** hnv_rt_…, rotating: each refresh answers a new one and the old one stops working. */
|
|
128
|
+
refreshToken: string;
|
|
129
|
+
refreshExpiresAt: string;
|
|
130
|
+
sessionId: string;
|
|
131
|
+
member: {
|
|
132
|
+
id: string;
|
|
133
|
+
externalId: string | null;
|
|
134
|
+
displayName: string;
|
|
135
|
+
role: AgentRole;
|
|
136
|
+
};
|
|
137
|
+
app: {
|
|
138
|
+
id: string;
|
|
139
|
+
name: string;
|
|
140
|
+
mode: "live" | "test";
|
|
141
|
+
};
|
|
142
|
+
/** The event socket on the host the app reached; SDKs derive their own from their base URL. */
|
|
143
|
+
wsUrl: string;
|
|
144
|
+
}
|
|
145
|
+
/** POST /agent/v1/calls/:id/accept. `media` is null for a call without a room (chat). */
|
|
146
|
+
export interface AgentAcceptDto {
|
|
147
|
+
callId: string;
|
|
148
|
+
accepted: true;
|
|
149
|
+
media: AgentMediaTokenDto | null;
|
|
150
|
+
}
|
|
151
|
+
/** POST /agent/v1/supervisor/calls/:id/supervise. */
|
|
152
|
+
export interface SupervisorSuperviseDto {
|
|
153
|
+
callId: string;
|
|
154
|
+
mode: "monitor" | "whisper" | "barge";
|
|
155
|
+
media: AgentMediaTokenDto;
|
|
156
|
+
}
|
|
157
|
+
/** POST /agent/v1/push/subscriptions body. */
|
|
158
|
+
export interface AgentPushSubscriptionInput {
|
|
159
|
+
platform: "ios" | "android";
|
|
160
|
+
/** FCM registration token (Android) or APNs device token, hex (iPhone). */
|
|
161
|
+
token: string;
|
|
162
|
+
/** voip = rings (PushKit on iPhone, high-priority data on Android); alert = ordinary notices. */
|
|
163
|
+
kind: "voip" | "alert";
|
|
164
|
+
/** Must be one of the agent app's allowedBundleIds. */
|
|
165
|
+
bundleId: string;
|
|
166
|
+
/** A stable id for this install: a new token from the same install replaces the old one. */
|
|
167
|
+
deviceId?: string;
|
|
168
|
+
}
|
|
169
|
+
/** POST /agent/v1/push/subscriptions (201). The token is never echoed. */
|
|
170
|
+
export interface AgentPushSubscriptionDto {
|
|
171
|
+
id: string;
|
|
172
|
+
platform: "ios" | "android";
|
|
173
|
+
kind: "voip" | "alert";
|
|
174
|
+
bundleId: string;
|
|
175
|
+
/** Whether the agent app has working push credentials for this platform right now. */
|
|
176
|
+
deliverable: boolean;
|
|
177
|
+
createdAt: string;
|
|
178
|
+
}
|
|
179
|
+
/** Event data of the stream's call.ringing. */
|
|
180
|
+
export interface AgentCallRingingData {
|
|
181
|
+
callId: string;
|
|
182
|
+
conversationId: string;
|
|
183
|
+
path: "direct" | "queue" | "transfer";
|
|
184
|
+
channel?: string | null;
|
|
185
|
+
line?: string | null;
|
|
186
|
+
contactName: string | null;
|
|
187
|
+
callerNumber?: string | null;
|
|
188
|
+
fromAgentId?: string | null;
|
|
189
|
+
fromAgentName?: string | null;
|
|
190
|
+
transferMode?: "warm" | "blind";
|
|
191
|
+
/** ISO time the ring ends, when the server knows it. */
|
|
192
|
+
expiresAt: string | null;
|
|
193
|
+
}
|
|
194
|
+
/** Event data of the stream's call.ended. callId is null when only the conversation is known. */
|
|
195
|
+
export interface AgentCallEndedData {
|
|
196
|
+
callId: string | null;
|
|
197
|
+
conversationId: string | null;
|
|
198
|
+
reason: "ended" | "failed" | "answered_elsewhere" | "declined" | "expired" | "transfer_ended";
|
|
199
|
+
answeredBy?: string | null;
|
|
200
|
+
failure?: {
|
|
201
|
+
reason: string;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
export declare const AGENT_EVENT_NAMES: readonly ["call.ringing", "call.ended", "conversation.assigned", "message.created", "wrapup.required", "presence.changed", "supervisor.alert"];
|
|
205
|
+
export type AgentEventName = (typeof AGENT_EVENT_NAMES)[number];
|
|
206
|
+
export interface AgentEvent {
|
|
207
|
+
event: AgentEventName;
|
|
208
|
+
data: Record<string, unknown>;
|
|
209
|
+
}
|
|
210
|
+
/** What the stream delivers: the event plus its position (resume from it). */
|
|
211
|
+
export interface AgentStreamFrame extends AgentEvent {
|
|
212
|
+
type: "event";
|
|
213
|
+
seq: number;
|
|
214
|
+
at: string;
|
|
215
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @hainiva/agent-sdk/embed — the embeddable HAINIVA desk (iframe / WebView with a
|
|
4
|
+
* postMessage bridge). Planned for milestone M3; not part of this release.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.mountDesk = mountDesk;
|
|
8
|
+
/** Not yet available: throws. */
|
|
9
|
+
function mountDesk(_options) {
|
|
10
|
+
throw new Error("@hainiva/agent-sdk/embed is not yet available");
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hainiva/agent-sdk/embed — the embeddable HAINIVA desk (iframe / WebView with a
|
|
3
|
+
* postMessage bridge). Planned for milestone M3; not part of this release.
|
|
4
|
+
*/
|
|
5
|
+
export interface MountDeskOptions {
|
|
6
|
+
container: unknown;
|
|
7
|
+
getAccessToken: () => Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
export interface MountedDesk {
|
|
10
|
+
destroy(): void;
|
|
11
|
+
}
|
|
12
|
+
/** Not yet available: throws. */
|
|
13
|
+
export declare function mountDesk(_options?: MountDeskOptions): MountedDesk;
|
|
@@ -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
|
+
}
|