@xema/omni-protocol 0.1.101 → 0.1.103
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/index.d.ts +92 -81
- package/dist/index.js +8 -6
- package/dist/testing.d.ts +26 -1
- package/dist/testing.js +224 -35
- package/dist/validation.d.ts +8 -3
- package/dist/validation.js +178 -159
- package/guide.md +316 -210
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -164,7 +164,7 @@ export interface Manifest<C extends Channel = Channel> {
|
|
|
164
164
|
/**
|
|
165
165
|
* The outcomes this platform distinguishes when a dial ends, `answered` and at least one way of
|
|
166
166
|
* not reaching the destination. Required of a provider that dials at all -- an idle dialpad, or
|
|
167
|
-
* a task that may
|
|
167
|
+
* a task that may conference or call back -- and forbidden off voice, where nothing dials.
|
|
168
168
|
*/
|
|
169
169
|
dialOutcomes?: C extends "voice" ? DialOutcome[] : never;
|
|
170
170
|
/**
|
|
@@ -179,13 +179,12 @@ export interface Manifest<C extends Channel = Channel> {
|
|
|
179
179
|
*/
|
|
180
180
|
runningStepReports?: true;
|
|
181
181
|
/**
|
|
182
|
-
* How long after
|
|
183
|
-
* `task-ended`
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* task as unsettled. Stated per provider, since platforms settle at different speeds.
|
|
182
|
+
* How long after `applied` the wire shows what the provider did, in milliseconds: the
|
|
183
|
+
* `task-ended` after a `complete` or a lead's `take-over-call`, the `calendar-updated` after a
|
|
184
|
+
* `schedule`. Past it the host resyncs and shows the thing as unsettled. Stated per provider,
|
|
185
|
+
* since platforms settle at different speeds.
|
|
187
186
|
*/
|
|
188
|
-
|
|
187
|
+
settleMs: number;
|
|
189
188
|
}
|
|
190
189
|
export interface SecretStore {
|
|
191
190
|
get(key: string): Promise<string | undefined>;
|
|
@@ -251,9 +250,10 @@ export interface UserCapabilities {
|
|
|
251
250
|
/**
|
|
252
251
|
* This login leads a team. The flag alone turns on the team feature in the agent application:
|
|
253
252
|
* the lead sees their members with their tasks and stats, and acts on them through `executeTeam`
|
|
254
|
-
* with the authority the flag carries. No per-action permission rides beside it.
|
|
255
|
-
*
|
|
256
|
-
*
|
|
253
|
+
* with the authority the flag carries. No per-action permission rides beside it. The provider
|
|
254
|
+
* assumes nothing of the switch: the application sends `lead-features` on every connect, and
|
|
255
|
+
* only while it is on does the team reach the lead -- `team-updated` whole after the switch and
|
|
256
|
+
* on any snapshot, then one member at a time as members change; to nobody else, ever.
|
|
257
257
|
*/
|
|
258
258
|
lead?: true;
|
|
259
259
|
}
|
|
@@ -458,7 +458,6 @@ export type RecordingState = {
|
|
|
458
458
|
export type RecordingCommand = {
|
|
459
459
|
type: "recording";
|
|
460
460
|
source: RecordingSource;
|
|
461
|
-
requestId: string;
|
|
462
461
|
/** Compare-and-set against current evidence; never act on a replacement recording. */
|
|
463
462
|
observationId: string;
|
|
464
463
|
} & ({
|
|
@@ -622,7 +621,7 @@ export interface Destination {
|
|
|
622
621
|
id: string;
|
|
623
622
|
label: string;
|
|
624
623
|
}
|
|
625
|
-
/** The items a
|
|
624
|
+
/** The items a conference control offers. Nothing is typed; the agent picks one. */
|
|
626
625
|
export interface DestinationDirectory {
|
|
627
626
|
destinations: Destination[];
|
|
628
627
|
}
|
|
@@ -653,14 +652,13 @@ export interface SharedTaskCapabilities {
|
|
|
653
652
|
export type TaskCapabilities<C extends Channel = Channel> = C extends "voice" ? SharedTaskCapabilities & {
|
|
654
653
|
decline?: Lockable<true>;
|
|
655
654
|
hold?: Lockable<true>;
|
|
656
|
-
/** The provider ends the caller connection and agent-added channels owned by this interaction, including inherited channels after
|
|
655
|
+
/** The provider ends the caller connection and agent-added channels owned by this interaction, including inherited channels after a take-over. Interaction completion remains separate. */
|
|
657
656
|
endCall?: Lockable<true>;
|
|
658
657
|
terminateCall?: Lockable<true>;
|
|
659
658
|
/** Connect back to the party while `completing`, whoever placed the call; the task returns to `in-progress`. */
|
|
660
659
|
connectBack?: Lockable<true>;
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
warmTransfer?: Lockable<DestinationDirectory>;
|
|
660
|
+
/** Put a time on the calendar for this party, from the call: a follow-up the agent promised. */
|
|
661
|
+
schedule?: Lockable<true>;
|
|
664
662
|
/** Ask a lead to join this call, with a note. The lead's decision arrives on `Task.leadAssist`. */
|
|
665
663
|
leadAssist?: Lockable<true>;
|
|
666
664
|
conference?: Lockable<DestinationDirectory>;
|
|
@@ -668,8 +666,10 @@ export type TaskCapabilities<C extends Channel = Channel> = C extends "voice" ?
|
|
|
668
666
|
} : C extends "chat" ? SharedTaskCapabilities & {
|
|
669
667
|
decline?: Lockable<true>;
|
|
670
668
|
hold?: Lockable<true>;
|
|
669
|
+
schedule?: Lockable<true>;
|
|
671
670
|
} : SharedTaskCapabilities & {
|
|
672
671
|
decline?: Lockable<true>;
|
|
672
|
+
schedule?: Lockable<true>;
|
|
673
673
|
};
|
|
674
674
|
/**
|
|
675
675
|
* How a reusing browser's session is keyed.
|
|
@@ -721,7 +721,7 @@ export type TaskPhase =
|
|
|
721
721
|
| "confirmed"
|
|
722
722
|
/**
|
|
723
723
|
* Voice only: the customer's record is available for preparation, then dialing before answer. The agent
|
|
724
|
-
* presses Call, or `
|
|
724
|
+
* presses Call, or `previewEndsInSeconds` runs out and `atDeadline` says what the system does instead.
|
|
725
725
|
*/
|
|
726
726
|
| "preview" | "in-progress" | "paused"
|
|
727
727
|
/** The work is over and the agent is finishing up. */
|
|
@@ -807,19 +807,18 @@ export type TaskCompletion = {
|
|
|
807
807
|
completionMode: "provider-automatic";
|
|
808
808
|
wrapAllowance: DurationSeconds;
|
|
809
809
|
};
|
|
810
|
-
export type OnCallRole = "party" | "agent" | "
|
|
810
|
+
export type OnCallRole = "party" | "agent" | "conferenced";
|
|
811
811
|
/** Where a dialled entry stands: `ringing` until its dial is answered, `joined` after. */
|
|
812
812
|
export type OnCallStage = "ringing" | "joined";
|
|
813
|
-
export declare const ON_CALL_ROLES: readonly ["party", "agent", "
|
|
813
|
+
export declare const ON_CALL_ROLES: readonly ["party", "agent", "conferenced"];
|
|
814
814
|
/**
|
|
815
815
|
* Somebody on the call, or being brought onto it, as the provider states it: who, since when, and
|
|
816
|
-
* whether they are held aside. `party` is the customer; `agent` a person, by user id; `
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
* one `consulted`: the consult commands name neither because there is exactly one. The `party`
|
|
816
|
+
* whether they are held aside. `party` is the customer; `agent` a person, by user id; `conferenced`
|
|
817
|
+
* someone a dial is bringing in -- listed from the moment the dial is placed, so the agent can call
|
|
818
|
+
* it off while it rings -- carrying the dial when a host placed it, the address either way, and the
|
|
819
|
+
* `stage` it has reached: `ringing` until the dial is answered, `joined` after, stated so a snapshot
|
|
820
|
+
* says who is present without anyone having seen the outcome. Nobody ringing is held. `label` names
|
|
821
|
+
* a destination -- a person, a queue -- not a phrase. At most one `party`. The `party`
|
|
823
822
|
* carries a `stage` while being dialled -- by any host dial, a dialpad call, a preview Call or a
|
|
824
823
|
* connect-back, carrying its `dialId`, or by a callback the platform places itself, carrying none --
|
|
825
824
|
* `ringing` from the moment the dial is placed, `joined` on the publication that follows its
|
|
@@ -842,7 +841,7 @@ export type OnCall = {
|
|
|
842
841
|
role: "agent";
|
|
843
842
|
userId: UserId;
|
|
844
843
|
} | {
|
|
845
|
-
role: "
|
|
844
|
+
role: "conferenced";
|
|
846
845
|
destinationId: string;
|
|
847
846
|
stage: OnCallStage;
|
|
848
847
|
dialId?: DialId;
|
|
@@ -952,12 +951,21 @@ export type Task<C extends Channel = Channel> = {
|
|
|
952
951
|
*/
|
|
953
952
|
acceptance?: AcceptanceMode;
|
|
954
953
|
/**
|
|
955
|
-
* In `
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
954
|
+
* In `pending` only: how long the offer has left, in whole seconds from this publication,
|
|
955
|
+
* restated on every publication that carries it so a reconnect snapshot says what is actually
|
|
956
|
+
* left. Absent, the offer stands until the provider says otherwise. Runs out: the provider ends
|
|
957
|
+
* the offer `expired` naming `pending`.
|
|
958
|
+
*/
|
|
959
|
+
expiresInSeconds?: DurationSeconds;
|
|
960
|
+
/**
|
|
961
|
+
* In `preview` only, and together: how long the preparation has left, in whole seconds from
|
|
962
|
+
* this publication -- restated on every publication that carries it, so a resync starts the
|
|
963
|
+
* countdown afresh and no clock is compared -- and behavior when it runs out, and who acts then:
|
|
964
|
+
* `provider-dials` makes the provider initiate dialing, `host-dials` makes the host issue Call,
|
|
965
|
+
* and `waits` keeps waiting for the agent without dialing or ending the task. Absent, the agent
|
|
966
|
+
* has as long as they need, without a preparation countdown.
|
|
959
967
|
*/
|
|
960
|
-
|
|
968
|
+
previewEndsInSeconds?: DurationSeconds;
|
|
961
969
|
atDeadline?: PreviewDeadline;
|
|
962
970
|
/** The identifier an agent reads back to a customer, where the provider has one. */
|
|
963
971
|
reference?: string;
|
|
@@ -989,11 +997,6 @@ export type TaskOutcome = {
|
|
|
989
997
|
type: "completed";
|
|
990
998
|
by: "agent" | "provider";
|
|
991
999
|
}
|
|
992
|
-
/** A cold transfer to a destination the agent chose: a directory item's id. */
|
|
993
|
-
| {
|
|
994
|
-
type: "transferred";
|
|
995
|
-
destinationId?: string;
|
|
996
|
-
}
|
|
997
1000
|
/** A lead took the call over and the agent has wrapped it: the outcome at the agent's own completion. Named by the lead, since a lead is not a directory item. */
|
|
998
1001
|
| {
|
|
999
1002
|
type: "taken-over";
|
|
@@ -1005,18 +1008,18 @@ export type TaskOutcome = {
|
|
|
1005
1008
|
by: "agent" | "provider" | "party";
|
|
1006
1009
|
reason?: string;
|
|
1007
1010
|
}
|
|
1008
|
-
/** Only the phases in which somebody is still being waited on can expire
|
|
1011
|
+
/** Only the phases in which somebody is still being waited on can expire: an offer whose `expiresInSeconds` runs out names `pending`, accepted work the platform gave up on names `confirmed`. A preview never lapses: withdrawn, it is `cancelled` by the provider. */
|
|
1009
1012
|
| {
|
|
1010
1013
|
type: "expired";
|
|
1011
|
-
phase: "pending" | "confirmed"
|
|
1014
|
+
phase: "pending" | "confirmed";
|
|
1012
1015
|
} | {
|
|
1013
1016
|
type: "failed";
|
|
1014
1017
|
failure: ProtocolFailure;
|
|
1015
1018
|
};
|
|
1016
1019
|
export declare const TASK_COMMAND_NAMES: {
|
|
1017
|
-
readonly voice: readonly ["answer", "decline", "dial", "hold", "resume", "end-call", "terminate-call", "connect-back", "
|
|
1018
|
-
readonly chat: readonly ["accept", "decline", "pause", "resume", "complete"];
|
|
1019
|
-
readonly email: readonly ["accept", "decline", "complete"];
|
|
1020
|
+
readonly voice: readonly ["answer", "decline", "dial", "hold", "resume", "end-call", "terminate-call", "connect-back", "lead-assist", "conference", "recording", "schedule", "complete"];
|
|
1021
|
+
readonly chat: readonly ["accept", "decline", "pause", "resume", "schedule", "complete"];
|
|
1022
|
+
readonly email: readonly ["accept", "decline", "schedule", "complete"];
|
|
1020
1023
|
};
|
|
1021
1024
|
export type TaskCommandName<C extends keyof typeof TASK_COMMAND_NAMES> = (typeof TASK_COMMAND_NAMES)[C][number];
|
|
1022
1025
|
export interface OutcomePayload {
|
|
@@ -1049,31 +1052,7 @@ export type VoiceTaskCommand = {
|
|
|
1049
1052
|
| {
|
|
1050
1053
|
type: "connect-back";
|
|
1051
1054
|
dialId: DialId;
|
|
1052
|
-
}
|
|
1053
|
-
/** Cold: hand the customer to the directory item `destinationId` with nobody spoken to first. Gated by `coldTransfer`. */
|
|
1054
|
-
| {
|
|
1055
|
-
type: "transfer";
|
|
1056
|
-
action: "cold";
|
|
1057
|
-
dialId: DialId;
|
|
1058
|
-
destinationId: string;
|
|
1059
|
-
}
|
|
1060
|
-
/** Warm: park the customer and call the directory item `destinationId` first. Gated by `warmTransfer`. */
|
|
1061
|
-
| {
|
|
1062
|
-
type: "transfer";
|
|
1063
|
-
action: "warm";
|
|
1064
|
-
dialId: DialId;
|
|
1065
|
-
destinationId: string;
|
|
1066
|
-
}
|
|
1067
|
-
/** Hand the customer to the consulted destination and leave. Needs a `consulted` entry on `Task.onCall`. */
|
|
1068
|
-
| {
|
|
1069
|
-
type: "transfer";
|
|
1070
|
-
action: "complete";
|
|
1071
|
-
}
|
|
1072
|
-
/** Drop the consulted destination and return to the customer. Needs a `consulted` entry on `Task.onCall`. */
|
|
1073
|
-
| {
|
|
1074
|
-
type: "transfer";
|
|
1075
|
-
action: "cancel";
|
|
1076
|
-
}
|
|
1055
|
+
} | ScheduleCommand
|
|
1077
1056
|
/** Ask a lead to join, with a note. Gated by `leadAssist`. */
|
|
1078
1057
|
| {
|
|
1079
1058
|
type: "lead-assist";
|
|
@@ -1113,6 +1092,18 @@ export type VoiceTaskCommand = {
|
|
|
1113
1092
|
}) | ({
|
|
1114
1093
|
type: "complete";
|
|
1115
1094
|
} & OutcomePayload);
|
|
1095
|
+
/**
|
|
1096
|
+
* Put a time on the calendar for this party: a follow-up promised on the call or in the
|
|
1097
|
+
* conversation, or written up in wrap. Gated by `schedule` on any channel, issuable in
|
|
1098
|
+
* `in-progress`, `paused` or `completing`, under a manifest that declares `calendar` for it to
|
|
1099
|
+
* land on; `applied` says it is on the calendar, and the `calendar-updated` that shows it follows
|
|
1100
|
+
* within the manifest's `settleMs`.
|
|
1101
|
+
*/
|
|
1102
|
+
export type ScheduleCommand = {
|
|
1103
|
+
type: "schedule";
|
|
1104
|
+
at: IsoTimestamp;
|
|
1105
|
+
note?: string;
|
|
1106
|
+
};
|
|
1116
1107
|
export type ChatTaskCommand = {
|
|
1117
1108
|
type: "accept";
|
|
1118
1109
|
} | {
|
|
@@ -1121,14 +1112,14 @@ export type ChatTaskCommand = {
|
|
|
1121
1112
|
type: "pause";
|
|
1122
1113
|
} | {
|
|
1123
1114
|
type: "resume";
|
|
1124
|
-
} | ({
|
|
1115
|
+
} | ScheduleCommand | ({
|
|
1125
1116
|
type: "complete";
|
|
1126
1117
|
} & OutcomePayload);
|
|
1127
1118
|
export type EmailTaskCommand = {
|
|
1128
1119
|
type: "accept";
|
|
1129
1120
|
} | {
|
|
1130
1121
|
type: "decline";
|
|
1131
|
-
} | ({
|
|
1122
|
+
} | ScheduleCommand | ({
|
|
1132
1123
|
type: "complete";
|
|
1133
1124
|
} & OutcomePayload);
|
|
1134
1125
|
export interface CustomTaskCommand {
|
|
@@ -1206,9 +1197,15 @@ export interface BreakRequest {
|
|
|
1206
1197
|
/** The chosen `BreakReason.id`, where the provider publishes codes. */
|
|
1207
1198
|
reasonId?: string;
|
|
1208
1199
|
}
|
|
1209
|
-
/**
|
|
1200
|
+
/**
|
|
1201
|
+
* A break forced on the agent; the agent must explicitly resume when ready. A lead's `force-break`
|
|
1202
|
+
* is a command to the provider, which forces the break: `by` names the lead who asked it to, or
|
|
1203
|
+
* `provider` where the platform itself did, on its own rule or a schedule. The agent sees who, a
|
|
1204
|
+
* person is named only when there is one, and a break the platform imposed is the platform's to
|
|
1205
|
+
* lift: a lead's `end-forced-break` on it fails with `omni.break-forced-by-provider`.
|
|
1206
|
+
*/
|
|
1210
1207
|
export type ForcedBreak = {
|
|
1211
|
-
by: UserId;
|
|
1208
|
+
by: UserId | "provider";
|
|
1212
1209
|
/** Optional expected time on break, in milliseconds from actual start. Advisory only. */
|
|
1213
1210
|
expectedDurationMs?: number;
|
|
1214
1211
|
};
|
|
@@ -1271,10 +1268,11 @@ export interface TeamMember {
|
|
|
1271
1268
|
listening?: MemberListening;
|
|
1272
1269
|
/** The member's own history for the day: sign-in, sign-out, breaks and productivity. Omitted where the provider cannot say. */
|
|
1273
1270
|
shift?: MemberShift;
|
|
1271
|
+
/** The member's standing ask for a lead on one of their calls; absent when they are not asking. */
|
|
1272
|
+
request?: MemberRequest;
|
|
1274
1273
|
}
|
|
1275
|
-
/**
|
|
1276
|
-
export interface
|
|
1277
|
-
memberId: UserId;
|
|
1274
|
+
/** This member asking their lead to join a call: which assignment, the note they wrote, since when. */
|
|
1275
|
+
export interface MemberRequest {
|
|
1278
1276
|
assignmentId: AssignmentId;
|
|
1279
1277
|
note?: string;
|
|
1280
1278
|
since: IsoTimestamp;
|
|
@@ -1337,19 +1335,20 @@ export interface MemberShift {
|
|
|
1337
1335
|
signedOutAt?: IsoTimestamp;
|
|
1338
1336
|
talkSeconds?: DurationSeconds;
|
|
1339
1337
|
holdSeconds?: DurationSeconds;
|
|
1338
|
+
/** Every break the member was on today, including time held elsewhere for a break another provider forced: the provider counts it under a break kind of its own. */
|
|
1340
1339
|
breakSeconds?: DurationSeconds;
|
|
1341
1340
|
tasksHandled?: number;
|
|
1342
1341
|
events?: ShiftEvent[];
|
|
1343
1342
|
}
|
|
1344
1343
|
/**
|
|
1345
1344
|
* The lead's view of their team: published to a login that declares `capabilities.lead` while the
|
|
1346
|
-
* lead has the team feature on,
|
|
1347
|
-
*
|
|
1345
|
+
* lead has the team feature on, and to nobody else. Whole on `team-updated` -- after the switch,
|
|
1346
|
+
* and on any snapshot -- and from then on one member at a time: `team-member-updated` carries the
|
|
1347
|
+
* member whole, so applying it twice changes nothing, and `team-member-removed` takes one off.
|
|
1348
|
+
* A team of two hundred is never resent because one of them held.
|
|
1348
1349
|
*/
|
|
1349
1350
|
export interface TeamMembers {
|
|
1350
1351
|
members: TeamMember[];
|
|
1351
|
-
/** Members asking a lead to join their call; `[]` when nobody is asking. */
|
|
1352
|
-
requests: LeadRequest[];
|
|
1353
1352
|
/** The team's policy per capability, as it stands. Present where the provider offers policy control; absent where it does not. */
|
|
1354
1353
|
policies?: TeamPolicies;
|
|
1355
1354
|
}
|
|
@@ -1605,7 +1604,6 @@ export type ProviderEvent<C extends Channel = Channel> = {
|
|
|
1605
1604
|
} | {
|
|
1606
1605
|
type: "task-offered";
|
|
1607
1606
|
task: Task<C>;
|
|
1608
|
-
assignmentExpiresAt?: IsoTimestamp;
|
|
1609
1607
|
} | {
|
|
1610
1608
|
type: "task-updated";
|
|
1611
1609
|
task: Task<C>;
|
|
@@ -1648,9 +1646,22 @@ export type ProviderEvent<C extends Channel = Channel> = {
|
|
|
1648
1646
|
expected: string;
|
|
1649
1647
|
observed: string;
|
|
1650
1648
|
assignmentId?: AssignmentId;
|
|
1651
|
-
}
|
|
1649
|
+
}
|
|
1650
|
+
/** The whole team, once: after `lead-features` comes on, and on every snapshot while it is. */
|
|
1651
|
+
| {
|
|
1652
1652
|
type: "team-updated";
|
|
1653
1653
|
team: TeamMembers;
|
|
1654
|
+
}
|
|
1655
|
+
/** One member, whole, as they now stand: replaces the member of that id, or adds them. */
|
|
1656
|
+
| {
|
|
1657
|
+
type: "team-member-updated";
|
|
1658
|
+
member: TeamMember;
|
|
1659
|
+
} | {
|
|
1660
|
+
type: "team-member-removed";
|
|
1661
|
+
memberId: UserId;
|
|
1662
|
+
} | {
|
|
1663
|
+
type: "team-policies-updated";
|
|
1664
|
+
policies: TeamPolicies;
|
|
1654
1665
|
} | {
|
|
1655
1666
|
type: "contacts-updated";
|
|
1656
1667
|
contacts: Contact[];
|
|
@@ -1670,7 +1681,7 @@ export interface ProviderEventEnvelope<C extends Channel = Channel> {
|
|
|
1670
1681
|
occurredAt: IsoTimestamp;
|
|
1671
1682
|
event: ProviderEvent<C>;
|
|
1672
1683
|
}
|
|
1673
|
-
export declare const OMNI_FAILURE_CODES: readonly ["omni.not-authenticated", "omni.capability-not-enabled", "omni.assignment-not-found", "omni.destination-not-permitted", "omni.phone-not-permitted", "omni.rate-limited", "omni.unavailable", "omni.break-already-committed"];
|
|
1684
|
+
export declare const OMNI_FAILURE_CODES: readonly ["omni.not-authenticated", "omni.capability-not-enabled", "omni.assignment-not-found", "omni.destination-not-permitted", "omni.phone-not-permitted", "omni.rate-limited", "omni.unavailable", "omni.break-already-committed", "omni.recording-unsettled", "omni.break-forced-by-provider"];
|
|
1674
1685
|
export type OmniFailureCode = (typeof OMNI_FAILURE_CODES)[number];
|
|
1675
1686
|
/**
|
|
1676
1687
|
* What the host refused, told to the adapter that published it: a snapshot the host would not
|
package/dist/index.js
CHANGED
|
@@ -56,7 +56,7 @@ export function isAllowedBrowserUrl(url) {
|
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
export const ON_CALL_ROLES = ["party", "agent", "
|
|
59
|
+
export const ON_CALL_ROLES = ["party", "agent", "conferenced"];
|
|
60
60
|
/**
|
|
61
61
|
* The levels a typical organisation has: the ladder in force when a manifest declares no
|
|
62
62
|
* `orgLevels`. A manifest that declares any states its whole ladder outright.
|
|
@@ -77,16 +77,14 @@ export const CAPABILITY_SOURCES = ["queue", "nobody", "not-yet-read"];
|
|
|
77
77
|
// ---------------------------------------------------------------------------
|
|
78
78
|
export const TASK_COMMAND_NAMES = {
|
|
79
79
|
voice: ["answer", "decline", "dial", "hold", "resume", "end-call", "terminate-call",
|
|
80
|
-
"connect-back", "
|
|
81
|
-
chat: ["accept", "decline", "pause", "resume", "complete"],
|
|
82
|
-
email: ["accept", "decline", "complete"],
|
|
80
|
+
"connect-back", "lead-assist", "conference", "recording", "schedule", "complete"],
|
|
81
|
+
chat: ["accept", "decline", "pause", "resume", "schedule", "complete"],
|
|
82
|
+
email: ["accept", "decline", "schedule", "complete"],
|
|
83
83
|
};
|
|
84
84
|
/** The dial a command places, by the host's identity for it; `undefined` for a command that dials nothing. */
|
|
85
85
|
export function commandDialId(command) {
|
|
86
86
|
if (command.type === "connect-back" || command.type === "dial")
|
|
87
87
|
return command.dialId;
|
|
88
|
-
if (command.type === "transfer" && (command.action === "cold" || command.action === "warm"))
|
|
89
|
-
return command.dialId;
|
|
90
88
|
if (command.type === "conference" && command.action === "add")
|
|
91
89
|
return command.dialId;
|
|
92
90
|
return undefined;
|
|
@@ -105,6 +103,10 @@ export const OMNI_FAILURE_CODES = [
|
|
|
105
103
|
"omni.rate-limited",
|
|
106
104
|
"omni.unavailable",
|
|
107
105
|
"omni.break-already-committed",
|
|
106
|
+
/** A recording command the provider could not settle either way: no start, stop, pause or resume it can vouch for. */
|
|
107
|
+
"omni.recording-unsettled",
|
|
108
|
+
/** A lead asked to lift a break the platform itself imposed; the platform lifts its own. */
|
|
109
|
+
"omni.break-forced-by-provider",
|
|
108
110
|
];
|
|
109
111
|
/**
|
|
110
112
|
* Preserves the adapter's inferred concrete type while checking it implements `Adapter`.
|
package/dist/testing.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { ProtocolConformanceError, assertNoViolations, type ProtocolViolation }
|
|
|
7
7
|
* rules -- each optional part of a task, each optional part of the break state and team member list, each
|
|
8
8
|
* declared contribution, and each event type.
|
|
9
9
|
*/
|
|
10
|
-
declare const STATE_SUBJECTS: readonly ["tasks", "task.browsers", "task.attributes", "task.history", "task.onCall", "task.leadAssist", "task.takenOver", "task.audio", "task.acceptance", "task.outcomes", "task.destinations", "task.custom", "task.locked", "break.reasons", "break.forced", "team.members", "team.
|
|
10
|
+
declare const STATE_SUBJECTS: readonly ["tasks", "task.browsers", "task.attributes", "task.history", "task.onCall", "task.leadAssist", "task.takenOver", "task.audio", "task.acceptance", "task.outcomes", "task.destinations", "task.custom", "task.locked", "break.reasons", "break.forced", "team.members", "team.request", "team.tasks", "team.listening", "team.shift", "contacts", "calendar", "team.policies"];
|
|
11
11
|
export type ContractSubject = (typeof STATE_SUBJECTS)[number] | `event.${ProviderEvent["type"]}`;
|
|
12
12
|
export interface AdapterContractResult {
|
|
13
13
|
events: ProviderEventEnvelope[];
|
|
@@ -149,6 +149,8 @@ export declare class TaskStream {
|
|
|
149
149
|
private readonly tasks;
|
|
150
150
|
private readonly assignments;
|
|
151
151
|
private readonly endedAssignments;
|
|
152
|
+
/** Every assignment a `task-offered` introduced, as against one a snapshot carried: each is owed its ending. */
|
|
153
|
+
private readonly offered;
|
|
152
154
|
private readonly dials;
|
|
153
155
|
/** The host placed a dial: its outcome, whenever it arrives, is one the stream expects. */
|
|
154
156
|
dialled(dialId: string): void;
|
|
@@ -160,6 +162,8 @@ export declare class TaskStream {
|
|
|
160
162
|
private notOpen;
|
|
161
163
|
/** How many tasks the stream currently holds open. */
|
|
162
164
|
openCount(): number;
|
|
165
|
+
/** The assignments an offer introduced that are still open: each was owed a `task-ended` that has not come. */
|
|
166
|
+
unended(): string[];
|
|
163
167
|
/** Whether an offered task is a dial arriving -- the host's own call, a connect-back -- which counts against no capacity: an entry on its call carrying a dial. */
|
|
164
168
|
static carriesDial(task: unknown): boolean;
|
|
165
169
|
/**
|
|
@@ -188,6 +192,20 @@ export declare class TaskStream {
|
|
|
188
192
|
apply(envelope: unknown, path?: string): ProtocolViolation[];
|
|
189
193
|
}
|
|
190
194
|
/** What a stream has said about the agent's break, and the moves it may not make. */
|
|
195
|
+
/**
|
|
196
|
+
* The team as the events left it: whole on `team-updated` and on any snapshot carrying one, then
|
|
197
|
+
* one member at a time. A member event before the whole team, or a removal of a member the team
|
|
198
|
+
* never carried, is a change to a team the lead was never given.
|
|
199
|
+
*/
|
|
200
|
+
export declare class TeamStream {
|
|
201
|
+
private members;
|
|
202
|
+
private static ids;
|
|
203
|
+
/** A snapshot carrying a team is the team whole; one carrying none leaves the lead with none. */
|
|
204
|
+
seed(snapshot: unknown): void;
|
|
205
|
+
/** Whether the team currently carries this member, as the events left it. */
|
|
206
|
+
has(id: string): boolean;
|
|
207
|
+
apply(envelope: unknown, path?: string): ProtocolViolation[];
|
|
208
|
+
}
|
|
191
209
|
export declare class BreakStream {
|
|
192
210
|
private state;
|
|
193
211
|
private recovery;
|
|
@@ -231,6 +249,13 @@ export interface BreakCandidate {
|
|
|
231
249
|
/** Whether the agent can currently receive work from it: connected, with a capacity stated. */
|
|
232
250
|
holdsCapacity: boolean;
|
|
233
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* A break one provider forces stops the agent everywhere: the host states a capacity of zero on
|
|
254
|
+
* every other usable provider holding capacity -- taken, never refused, and nothing the agent can
|
|
255
|
+
* cancel -- and on the forcing provider states nothing, since the break itself holds the agent
|
|
256
|
+
* there. A provider whose login is not usable is left alone, as a break attempt leaves it.
|
|
257
|
+
*/
|
|
258
|
+
export declare function assertForcedBreakStopsTheRest(forcedOn: string, candidates: readonly BreakCandidate[], stopped: readonly string[]): void;
|
|
234
259
|
/**
|
|
235
260
|
* A break attempt asks every connected provider from which the agent can currently receive work.
|
|
236
261
|
* A provider whose login is not usable -- `expired` above all -- is not asked, whatever else is
|