@xema/omni-protocol 0.1.41 → 0.1.43
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/README.md +7 -8
- package/dist/index.d.ts +15 -7
- package/dist/index.js +7 -1
- package/dist/testing.js +1 -1
- package/dist/validation.js +27 -7
- package/guide.md +38 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,17 +97,16 @@ const task = {
|
|
|
97
97
|
};
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
**
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
without a cast. Dropping an arm then fails the build — indirectly, as a missing return.
|
|
100
|
+
**Switch on `command.action` with no `default`.** A `default` turns a future action into whatever
|
|
101
|
+
the default does, silently; naming every arm means dropping one fails the build — indirectly, as a
|
|
102
|
+
missing return — and keeps the narrowing that lets you read `destination` without a cast.
|
|
104
103
|
|
|
105
104
|
```ts
|
|
106
105
|
switch (command.action) {
|
|
107
|
-
case
|
|
108
|
-
case "
|
|
109
|
-
case "complete": return
|
|
110
|
-
case "cancel": return
|
|
106
|
+
case "cold": return coldTransfer(command.destination);
|
|
107
|
+
case "warm": return warmTransfer(command.destination);
|
|
108
|
+
case "complete": return completeWarmTransfer();
|
|
109
|
+
case "cancel": return cancelWarmTransfer();
|
|
111
110
|
}
|
|
112
111
|
```
|
|
113
112
|
|
package/dist/index.d.ts
CHANGED
|
@@ -451,9 +451,9 @@ export type TaskCapabilities<C extends Channel = Channel> = C extends "voice" ?
|
|
|
451
451
|
agentDisconnect?: Lockable<true>;
|
|
452
452
|
/** Connect back to the party while `completing`, whoever placed the call; the task returns to `in-progress`. */
|
|
453
453
|
connectBack?: Lockable<true>;
|
|
454
|
-
|
|
454
|
+
coldTransfer?: Lockable<true | DestinationDirectory>;
|
|
455
455
|
/** Park the customer and call a destination first; then `complete` or `cancel`. */
|
|
456
|
-
|
|
456
|
+
warmTransfer?: Lockable<true | DestinationDirectory>;
|
|
457
457
|
/** Ask a lead to join this call, with a note. The lead's decision arrives on `Task.leadAssist`. */
|
|
458
458
|
leadAssist?: Lockable<true>;
|
|
459
459
|
conference?: Lockable<true | DestinationDirectory>;
|
|
@@ -638,7 +638,9 @@ export interface TaskAssisting {
|
|
|
638
638
|
/**
|
|
639
639
|
* On the lead's own task while they listen to a member's call: whose call, which call, and how the
|
|
640
640
|
* lead is heard, restated on every change of mode. Nothing of it reaches the member's task. There
|
|
641
|
-
* is no take-over here; a lead who wants the call uses lead assist.
|
|
641
|
+
* is no take-over here; a lead who wants the call uses lead assist. It is the lead's only task:
|
|
642
|
+
* a lead listens while holding no work of their own, idle or on a break of a kind in
|
|
643
|
+
* `MONITORING_BREAK_KINDS`.
|
|
642
644
|
*/
|
|
643
645
|
export interface TaskMonitoring {
|
|
644
646
|
memberId: UserId;
|
|
@@ -785,17 +787,17 @@ export type VoiceTaskCommand = {
|
|
|
785
787
|
type: "connect-back";
|
|
786
788
|
dialId: DialId;
|
|
787
789
|
}
|
|
788
|
-
/**
|
|
790
|
+
/** Cold: hand the customer to `destination` with nobody spoken to first. Gated by `coldTransfer`. */
|
|
789
791
|
| {
|
|
790
792
|
type: "transfer";
|
|
793
|
+
action: "cold";
|
|
791
794
|
dialId: DialId;
|
|
792
795
|
destination: string;
|
|
793
|
-
action?: never;
|
|
794
796
|
}
|
|
795
|
-
/**
|
|
797
|
+
/** Warm: park the customer and call `destination` first. Gated by `warmTransfer`. */
|
|
796
798
|
| {
|
|
797
799
|
type: "transfer";
|
|
798
|
-
action: "
|
|
800
|
+
action: "warm";
|
|
799
801
|
dialId: DialId;
|
|
800
802
|
destination: string;
|
|
801
803
|
}
|
|
@@ -927,6 +929,12 @@ export declare const BREAK_KINDS: readonly ["short-break", "meal", "rest", "trai
|
|
|
927
929
|
* problem one level up. `other` matches nothing, including another provider's `other`.
|
|
928
930
|
*/
|
|
929
931
|
export type BreakKind = (typeof BREAK_KINDS)[number];
|
|
932
|
+
/**
|
|
933
|
+
* The breaks during which a lead may listen to a member's call: work of another sort, not rest.
|
|
934
|
+
* A monitoring task is the one task a break in effect may hold, and only on one of these.
|
|
935
|
+
*/
|
|
936
|
+
export declare const MONITORING_BREAK_KINDS: readonly ["coaching", "administrative", "training"];
|
|
937
|
+
export declare const breakKindAllowsMonitoring: (kind: BreakKind | undefined) => boolean;
|
|
930
938
|
export interface BreakReason {
|
|
931
939
|
id: string;
|
|
932
940
|
label: string;
|
package/dist/index.js
CHANGED
|
@@ -80,7 +80,7 @@ export const TASK_COMMAND_NAMES = {
|
|
|
80
80
|
export function commandDialId(command) {
|
|
81
81
|
if (command.type === "connect-back")
|
|
82
82
|
return command.dialId;
|
|
83
|
-
if (command.type === "transfer" && (command.action ===
|
|
83
|
+
if (command.type === "transfer" && (command.action === "cold" || command.action === "warm"))
|
|
84
84
|
return command.dialId;
|
|
85
85
|
if (command.type === "conference" && command.action === "add")
|
|
86
86
|
return command.dialId;
|
|
@@ -90,6 +90,12 @@ export const BREAK_KINDS = [
|
|
|
90
90
|
"short-break", "meal", "rest", "training", "coaching",
|
|
91
91
|
"meeting", "administrative", "technical", "personal", "other",
|
|
92
92
|
];
|
|
93
|
+
/**
|
|
94
|
+
* The breaks during which a lead may listen to a member's call: work of another sort, not rest.
|
|
95
|
+
* A monitoring task is the one task a break in effect may hold, and only on one of these.
|
|
96
|
+
*/
|
|
97
|
+
export const MONITORING_BREAK_KINDS = ["coaching", "administrative", "training"];
|
|
98
|
+
export const breakKindAllowsMonitoring = (kind) => kind !== undefined && MONITORING_BREAK_KINDS.includes(kind);
|
|
93
99
|
export const OMNI_FAILURE_CODES = [
|
|
94
100
|
"omni.not-authenticated",
|
|
95
101
|
"omni.capability-not-enabled",
|
package/dist/testing.js
CHANGED
|
@@ -70,7 +70,7 @@ function observeTask(value, seen) {
|
|
|
70
70
|
const capabilities = isRecord(value.capabilities) ? value.capabilities : {};
|
|
71
71
|
if (isRecord(capabilities.dispositions))
|
|
72
72
|
seen.add("task.dispositions");
|
|
73
|
-
for (const directory of ["
|
|
73
|
+
for (const directory of ["coldTransfer", "warmTransfer", "conference"]) {
|
|
74
74
|
const declared = capabilities[directory];
|
|
75
75
|
if (isRecord(declared) && some(declared.destinations))
|
|
76
76
|
seen.add("task.destinations");
|
package/dist/validation.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// Each list is pinned to its type both ways -- a member the type lacks, or a member the list
|
|
10
10
|
// lacks, fails to compile -- so what the validators accept cannot drift from what the
|
|
11
11
|
// declarations say.
|
|
12
|
-
import { ALLOWED_BROWSER_URL_SCHEMES, BREAK_KINDS, BROWSER_ISOLATION_SCHEMES, HANDLING_STEPS_THAT_DIAL, IDLE_CAPABILITIES, OMNI_FAILURE_CODES, OMNI_SUPPORTED_PROTOCOL_VERSIONS, negotiateProtocolVersion, DEFAULT_LEVELS, effectiveLevels, } from "./index.js";
|
|
12
|
+
import { ALLOWED_BROWSER_URL_SCHEMES, BREAK_KINDS, MONITORING_BREAK_KINDS, BROWSER_ISOLATION_SCHEMES, HANDLING_STEPS_THAT_DIAL, IDLE_CAPABILITIES, OMNI_FAILURE_CODES, OMNI_SUPPORTED_PROTOCOL_VERSIONS, negotiateProtocolVersion, DEFAULT_LEVELS, effectiveLevels, } from "./index.js";
|
|
13
13
|
export class ProtocolConformanceError extends Error {
|
|
14
14
|
violations;
|
|
15
15
|
constructor(violations, summary = "Adapter violates the Omni protocol") {
|
|
@@ -73,7 +73,7 @@ const EXPLAINED_FAILURES = ["busy", "no-answer", "unreachable", "rejected", "can
|
|
|
73
73
|
const ON_CALL_ROLES = membersOf({ party: true, agent: true, consulted: true, conferenced: true });
|
|
74
74
|
const ON_CALL_STAGES = membersOf({ ringing: true, joined: true });
|
|
75
75
|
/** The task capabilities under which a command dials, and so need the manifest to say how a dial ends. */
|
|
76
|
-
const DIALLING_CAPABILITIES = ["connectBack", "
|
|
76
|
+
const DIALLING_CAPABILITIES = ["connectBack", "coldTransfer", "warmTransfer", "conference"];
|
|
77
77
|
const SNAPSHOT_REASONS = membersOf({
|
|
78
78
|
reconnected: true, "provider-requested": true,
|
|
79
79
|
});
|
|
@@ -95,7 +95,7 @@ const ISOLATION_SCHEME_VALUES = Object.values(BROWSER_ISOLATION_SCHEMES);
|
|
|
95
95
|
const TASK_CAPABILITIES = {
|
|
96
96
|
voice: membersOf({
|
|
97
97
|
browsers: true, dispositions: true, custom: true, decline: true, mute: true, hold: true,
|
|
98
|
-
agentDisconnect: true, connectBack: true,
|
|
98
|
+
agentDisconnect: true, connectBack: true, coldTransfer: true, warmTransfer: true, leadAssist: true, conference: true, recording: true,
|
|
99
99
|
}),
|
|
100
100
|
chat: membersOf({ browsers: true, dispositions: true, custom: true, decline: true, hold: true }),
|
|
101
101
|
email: membersOf({ browsers: true, dispositions: true, custom: true, decline: true }),
|
|
@@ -948,8 +948,8 @@ function validateTaskInto(task, context, path, into) {
|
|
|
948
948
|
case "custom":
|
|
949
949
|
validateCustomCapabilities(declared, `${path}.capabilities.custom`, into);
|
|
950
950
|
break;
|
|
951
|
-
case "
|
|
952
|
-
case "
|
|
951
|
+
case "coldTransfer":
|
|
952
|
+
case "warmTransfer":
|
|
953
953
|
case "conference":
|
|
954
954
|
validateDestinationDirectory(declared, `${path}.capabilities.${name}`, into);
|
|
955
955
|
break;
|
|
@@ -1188,11 +1188,31 @@ export function validateSnapshot(snapshot, manifest, path = "snapshot", context
|
|
|
1188
1188
|
}
|
|
1189
1189
|
});
|
|
1190
1190
|
}
|
|
1191
|
+
// A lead listens while holding no work of their own: a monitoring task is the only task.
|
|
1192
|
+
if (Array.isArray(snapshot.tasks) && snapshot.tasks.length > 1) {
|
|
1193
|
+
snapshot.tasks.forEach((task, index) => {
|
|
1194
|
+
if (isPlainObject(task) && task.monitoring !== undefined) {
|
|
1195
|
+
into.add("snapshot.monitoring.alone", `${path}.tasks[${index}].monitoring`, "a lead listens only while holding no task of their own");
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1191
1199
|
// A break in effect begins when the work ends, so it holds no task. A snapshot reporting both
|
|
1192
|
-
// describes a state the agent cannot be in, whichever half is stale.
|
|
1200
|
+
// describes a state the agent cannot be in, whichever half is stale. The one exception is a
|
|
1201
|
+
// lead listening to a call during a break that is work of another sort -- coaching,
|
|
1202
|
+
// administrative, training -- and on no other.
|
|
1193
1203
|
if (isPlainObject(snapshot.break) && snapshot.break.approval === "in-effect"
|
|
1194
1204
|
&& Array.isArray(snapshot.tasks) && snapshot.tasks.length > 0) {
|
|
1195
|
-
|
|
1205
|
+
const listening = snapshot.tasks.every((task) => isPlainObject(task) && task.monitoring !== undefined);
|
|
1206
|
+
if (!listening) {
|
|
1207
|
+
into.add("break.in-effect.tasks", `${path}.tasks`, "a break in effect holds no task: it begins when the work ends, and until then the state is starting-after-task");
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
const state = snapshot.break;
|
|
1211
|
+
const reasons = Array.isArray(state.reasons) ? state.reasons : [];
|
|
1212
|
+
const active = reasons.find((reason) => isPlainObject(reason) && reason.id === state.activeReasonId);
|
|
1213
|
+
const kind = isPlainObject(active) ? active.kind : undefined;
|
|
1214
|
+
into.require(typeof kind === "string" && MONITORING_BREAK_KINDS.includes(kind), "snapshot.monitoring.break", `${path}.tasks`, `a lead listens during a ${MONITORING_BREAK_KINDS.join(", ")} break and no other; the active reason is ${kind === undefined ? "unstated" : String(kind)}`);
|
|
1215
|
+
}
|
|
1196
1216
|
}
|
|
1197
1217
|
// Presence is the permission, and it cuts both ways: data a provider never declared a
|
|
1198
1218
|
// capability for is data Omni would show against a control the agent does not have.
|
package/guide.md
CHANGED
|
@@ -35,7 +35,7 @@ are used precisely throughout and mean nothing looser here.
|
|
|
35
35
|
| **Event** | One completed transaction reported after a snapshot established the baseline. |
|
|
36
36
|
| **Break** | A reported, supervised state in which the agent is not working — one with a reason, a decision behind it and a return. It covers what a platform may call *not-ready*, including equipment trouble. An agent who is merely at capacity is not on a break. |
|
|
37
37
|
| **Workspace** | What Omni shows the agent. The **task workspace** holds the selected task, its controls and its browsers; the **idle workspace** holds what a provider contributes when no task is selected — dialpad, contacts, calendar, roster. |
|
|
38
|
-
| **Dial** | One outbound call the host asks a provider to place — from the idle dialpad, a transfer, a conference add, or a connect-back. Identified by the host's `dialId`, accepted as `dialling`, and ended by exactly one `dial-outcome`. See **Every dial has an outcome**. |
|
|
38
|
+
| **Dial** | One outbound call the host asks a provider to place — from the idle dialpad, a cold or warm transfer, a conference add, or a connect-back. Identified by the host's `dialId`, accepted as `dialling`, and ended by exactly one `dial-outcome`. See **Every dial has an outcome**. |
|
|
39
39
|
| **Monitor** | A lead listening to a member's call unasked, from the roster: `monitor` in silence, `whisper` heard by the agent alone, `barge` heard by everyone. Nothing of it reaches the member's task, and there is no take-over in it. See **Monitoring a call**. |
|
|
40
40
|
| **On the call** | Who a voice task's audio joins, or is bringing in, as the provider states it on `Task.onCall`: the party, the agents, and anyone consulted or conferenced in from the moment their dial is placed. |
|
|
41
41
|
|
|
@@ -481,8 +481,8 @@ type TaskCapabilities<C extends Channel = Channel> =
|
|
|
481
481
|
hold?: Lockable<true>;
|
|
482
482
|
agentDisconnect?: Lockable<true>;
|
|
483
483
|
connectBack?: Lockable<true>;
|
|
484
|
-
|
|
485
|
-
|
|
484
|
+
coldTransfer?: Lockable<true | DestinationDirectory>;
|
|
485
|
+
warmTransfer?: Lockable<true | DestinationDirectory>;
|
|
486
486
|
leadAssist?: Lockable<true>;
|
|
487
487
|
conference?: Lockable<true | DestinationDirectory>;
|
|
488
488
|
recording?: Lockable<true>;
|
|
@@ -707,8 +707,8 @@ type VoiceTaskCommand =
|
|
|
707
707
|
| { type: "resume" }
|
|
708
708
|
| { type: "disconnect" }
|
|
709
709
|
| { type: "connect-back"; dialId: DialId }
|
|
710
|
-
| { type: "transfer"; dialId: DialId; destination: string
|
|
711
|
-
| { type: "transfer"; action: "
|
|
710
|
+
| { type: "transfer"; action: "cold"; dialId: DialId; destination: string }
|
|
711
|
+
| { type: "transfer"; action: "warm"; dialId: DialId; destination: string }
|
|
712
712
|
| { type: "transfer"; action: "complete" }
|
|
713
713
|
| { type: "transfer"; action: "cancel" }
|
|
714
714
|
| { type: "lead-assist"; action: "request"; note?: string }
|
|
@@ -2198,9 +2198,9 @@ The canonical task transitions are:
|
|
|
2198
2198
|
| `pending` | Provider withdraws the allocation | Removed by `task-ended` with `cancelled` outcome |
|
|
2199
2199
|
| No task | Snapshot reports work already underway | `in-progress` |
|
|
2200
2200
|
| `in-progress` | Provider or agent pauses the task | `paused` |
|
|
2201
|
-
| `in-progress` | Agent
|
|
2201
|
+
| `in-progress` | Agent starts a warm transfer (`transfer` `warm`); the customer is parked | `paused` |
|
|
2202
2202
|
| `paused` | Provider or agent resumes the task | `in-progress` |
|
|
2203
|
-
| `paused` | Agent cancels a
|
|
2203
|
+
| `paused` | Agent cancels a warm transfer (`transfer` `cancel`) | `in-progress` |
|
|
2204
2204
|
| `in-progress` or `paused` | Contact handling ends and follow-up work remains | `completing` |
|
|
2205
2205
|
| `completing` | Agent connects back to the party (`connect-back`) | `in-progress` |
|
|
2206
2206
|
| Any phase | Provider emits `task-ended` | Removed |
|
|
@@ -2591,8 +2591,8 @@ const taskCapabilities = {
|
|
|
2591
2591
|
| `hold` | Primary toggle: Hold | Omni may issue voice-task `hold` and `resume` commands. |
|
|
2592
2592
|
| `agentDisconnect` | Primary button: Disconnect | Omni may disconnect real-time media without disposing the task. |
|
|
2593
2593
|
| `connectBack` | Completing-task button: Connect back | Omni may have the provider connect the agent back to the task's party while the task is `completing`, returning it to `in-progress` on the same task. Not offered where there is no `completing` window: `provider-automatic` with a zero allowance disposes at provider end. See **Connecting back during completion**. |
|
|
2594
|
-
| `
|
|
2595
|
-
| `
|
|
2594
|
+
| `coldTransfer` | Secondary menu item: Cold transfer | Omni may hand the customer straight to a destination, with nobody spoken to first. |
|
|
2595
|
+
| `warmTransfer` | Secondary menu item: Warm transfer | Omni may park the customer and call a destination first, then hand the customer over or cancel back. See **Warm transfer**. |
|
|
2596
2596
|
| `leadAssist` | Secondary menu item: Lead assist | Omni may ask a lead to join this call, with a note. The lead's decision reaches the agent on `Task.leadAssist`. See **Lead assist**. |
|
|
2597
2597
|
| `conference` | Secondary button: Conference | Omni may dial a destination into the active call, and drop a `conferenced` entry from it -- one still ringing included, which calls the dial off. |
|
|
2598
2598
|
| `recording` | Overflow menu item: Recording | Omni may expose start, pause, resume, and stop recording controls. |
|
|
@@ -2627,11 +2627,11 @@ capabilities: {
|
|
|
2627
2627
|
With `dispositions: true` Omni shows a Complete control and sends `complete` with no code, because
|
|
2628
2628
|
the provider published none.
|
|
2629
2629
|
|
|
2630
|
-
#### `
|
|
2630
|
+
#### `coldTransfer`, `warmTransfer` and `conference`
|
|
2631
2631
|
|
|
2632
2632
|
```ts
|
|
2633
2633
|
capabilities: {
|
|
2634
|
-
|
|
2634
|
+
coldTransfer: {
|
|
2635
2635
|
allowManualEntry: false,
|
|
2636
2636
|
destinations: [
|
|
2637
2637
|
{ id: "tier2", label: "Tier 2 support", address: "+14155550111", kind: "queue" },
|
|
@@ -2659,18 +2659,19 @@ A destination the agent types is not in the directory and has no `kind`. Omni tr
|
|
|
2659
2659
|
`external` unless the provider says otherwise in its response, because that is the assumption that
|
|
2660
2660
|
does not overstate what the provider can still see.
|
|
2661
2661
|
|
|
2662
|
-
####
|
|
2662
|
+
#### Warm transfer
|
|
2663
2663
|
|
|
2664
|
-
A
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2664
|
+
A warm transfer parks the customer, calls the destination so the agent can speak to it first, and
|
|
2665
|
+
then either hands the customer over or returns to them. Cold and warm are the words agents use;
|
|
2666
|
+
telephony says blind and consult. It is its own capability, distinct from `coldTransfer` (a
|
|
2667
|
+
hand-over with nobody spoken to first) and from `conference` (everybody on one call): a queue may
|
|
2668
|
+
offer any of the three without the others, and each is declared on its own.
|
|
2668
2669
|
|
|
2669
2670
|
```ts
|
|
2670
|
-
// 1.
|
|
2671
|
+
// 1. Warm. A dial: the provider parks the customer and calls the destination, answers
|
|
2671
2672
|
// `dialling`, and the task reports `paused` with the destination `consulted` on `onCall`
|
|
2672
2673
|
// while the call to it stands.
|
|
2673
|
-
{ type: "transfer", action: "
|
|
2674
|
+
{ type: "transfer", action: "warm", dialId: "dial-7f2", destination: "+14155550111" }
|
|
2674
2675
|
|
|
2675
2676
|
// 2a. Hand the customer to the consulted destination and leave.
|
|
2676
2677
|
{ type: "transfer", action: "complete" }
|
|
@@ -2679,7 +2680,7 @@ call): a queue may offer any of the three without the others, and each is declar
|
|
|
2679
2680
|
{ type: "transfer", action: "cancel" }
|
|
2680
2681
|
```
|
|
2681
2682
|
|
|
2682
|
-
`
|
|
2683
|
+
`warm` is gated by the `warmTransfer` capability and takes a destination exactly as a cold
|
|
2683
2684
|
transfer does, from the same kind of directory. It is a dial, so it carries the host's `dialId`
|
|
2684
2685
|
and is answered `dialling`, and its `dial-outcome` says whether the destination was reached. While
|
|
2685
2686
|
the destination is on the line the task carries a `consulted` entry on `onCall`, and that presence
|
|
@@ -2687,7 +2688,7 @@ is what makes `complete` and `cancel` issuable -- they name no destination becau
|
|
|
2687
2688
|
one they could mean, which is why a task carries at most one `consulted` entry
|
|
2688
2689
|
(`task.onCall.consulted.single`). A consultation that could be started but not finished would
|
|
2689
2690
|
strand the customer and the destination both, which is why all three are commands and a provider
|
|
2690
|
-
that offers `
|
|
2691
|
+
that offers `warmTransfer` implements all three.
|
|
2691
2692
|
|
|
2692
2693
|
`applied` on `complete` says the provider is bridging the customer to the destination and
|
|
2693
2694
|
dropping the agent's leg. What follows is what follows any transfer: the agent's media ends and
|
|
@@ -2836,7 +2837,7 @@ call it belonged to and never against the next one.
|
|
|
2836
2837
|
The stream knows a dial from three places: `TaskStream.dialled(dialId)`, which a host calls when it
|
|
2837
2838
|
places one; an entry on a task's `onCall`; and a step in its `handlingHistory`. Since a dialled entry
|
|
2838
2839
|
is listed from placement, `dialled()` is load-bearing only for a dial that never has an entry -- a
|
|
2839
|
-
|
|
2840
|
+
cold transfer, a connect-back, an idle dialpad call -- or whose entry has already left the room; an
|
|
2840
2841
|
outcome for a dial nobody placed and no task mentions is what `stream.dialOutcome.unknown` refuses.
|
|
2841
2842
|
|
|
2842
2843
|
**`answered` says what happened to the dial; `onCall` says who is in the room.** They are two
|
|
@@ -2855,7 +2856,7 @@ provider does report -- `busy`, `no-answer`, `unreachable`, `rejected` or `cance
|
|
|
2855
2856
|
switch gave none, while one that reports none would be sending the generic failure this set exists
|
|
2856
2857
|
to refuse. A provider that dials at all declares it: an idle
|
|
2857
2858
|
dialpad requires it (`manifest.dialOutcomes.required`), and a task that declares `connectBack`,
|
|
2858
|
-
`
|
|
2859
|
+
`coldTransfer`, `warmTransfer` or `conference` under a manifest without it is refused
|
|
2859
2860
|
(`task.capability.dialOutcomes.required`). Off voice nothing dials, and the field is a compile
|
|
2860
2861
|
error there.
|
|
2861
2862
|
|
|
@@ -2933,7 +2934,7 @@ rendering one as the other tells an agent to wait for somebody who is never comi
|
|
|
2933
2934
|
| `awaiting-decision` | A person has to decide. The agent is waiting on somebody. |
|
|
2934
2935
|
| `granted` | A person decided yes. Omni may now tell this provider to stop the agent; until it does, work continues normally, and this says nothing about why Omni has not. |
|
|
2935
2936
|
| `starting-after-task` | Omni has told the provider to stop; the break begins when the current task ends. No new work arrives meanwhile, and nobody needs to act. |
|
|
2936
|
-
| `in-effect` | The agent is on the break now. It holds no task: a break begins when the work ends, so a snapshot reporting `in-effect` beside a task is refused as `break.in-effect.tasks`. |
|
|
2937
|
+
| `in-effect` | The agent is on the break now. It holds no task: a break begins when the work ends, so a snapshot reporting `in-effect` beside a task is refused as `break.in-effect.tasks`. The one exception is a lead's monitoring task during a `coaching`, `administrative` or `training` break -- see **Monitoring a call**. |
|
|
2937
2938
|
|
|
2938
2939
|
A denial is a decision, not a standing approval state. The provider transitions the request directly
|
|
2939
2940
|
to `not-requested`; Omni returns the agent to idle and never asks again on their behalf. They saw the
|
|
@@ -3465,10 +3466,19 @@ executeTeamMonitor({ command: { type: "leave" } })
|
|
|
3465
3466
|
provider bridges the lead's leg into the member's call in the mode stated. `monitoring.mode` is
|
|
3466
3467
|
the state, restated on every change, and a `whisper` or `barge` the login's list does not include
|
|
3467
3468
|
is answered `failed`. **A lead listens to one call at a time** (`snapshot.monitoring.single`), and
|
|
3468
|
-
a task is a joined call or a monitored one, never both (`task.monitoring.assisting`).
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3469
|
+
a task is a joined call or a monitored one, never both (`task.monitoring.assisting`). The member's
|
|
3470
|
+
call ending ends the lead's task as it ends the member's, with `left`, since the lead was never
|
|
3471
|
+
handling it.
|
|
3472
|
+
|
|
3473
|
+
**A lead listens only while holding no work of their own.** A monitoring task is the lead's only
|
|
3474
|
+
task, and a snapshot carrying one beside any other is refused (`snapshot.monitoring.alone`); Omni
|
|
3475
|
+
offers Monitor to a lead with no task. That includes a lead on a break -- unlike joining, which a
|
|
3476
|
+
break forbids -- but only a break that is work of another sort: `coaching`, `administrative` or
|
|
3477
|
+
`training`, the kinds in `MONITORING_BREAK_KINDS`, and never a meal or a rest. A break in effect
|
|
3478
|
+
otherwise holds no task; the monitoring task is its one exception, and a snapshot carrying one
|
|
3479
|
+
during a break whose active reason is of any other kind, or of no stated kind, is refused
|
|
3480
|
+
(`snapshot.monitoring.break`). The kind decides, not the reason's name: a centre that wants leads
|
|
3481
|
+
listening during a break declares that break `coaching`, `administrative` or `training`.
|
|
3472
3482
|
|
|
3473
3483
|
**Nothing reaches the member.** The member's task carries no trace of a monitoring lead in any
|
|
3474
3484
|
mode: not on `onCall`, not in the record. Whether a whisper is announced to the agent is the
|
|
@@ -3700,7 +3710,7 @@ declared:
|
|
|
3700
3710
|
| `start-call` | The `preparing` phase. It starts the contact a preview gave the agent time to read, so the phase is the gate and there is no capability. |
|
|
3701
3711
|
| `complete` | `completionMode: "agent-command"`. The `dispositions` capability decides whether a code travels with the command, never whether the command exists — a task Omni cannot complete never ends. |
|
|
3702
3712
|
| `connect-back` | The `connectBack` capability **and** the `completing` phase. It exists to reach the party again after the call, so it has no meaning while the call is up. |
|
|
3703
|
-
| `transfer` with `action: "
|
|
3713
|
+
| `transfer` with `action: "warm"` | The `warmTransfer` capability. `action: "cold"` is gated by `coldTransfer`; the two are declared and offered separately. |
|
|
3704
3714
|
| `transfer` with `action: "complete"` or `"cancel"` | A consultation in progress -- a `consulted` entry on `Task.onCall`. Without one there is nothing to complete or cancel, and a provider that receives either answers `failed`. |
|
|
3705
3715
|
| `lead-assist` with `action: "request"` or `"cancel"` | The `leadAssist` capability. `cancel` needs a request standing -- `Task.leadAssist` with status `requested`. |
|
|
3706
3716
|
| `lead-assist` with `action: "take-over"` or `"leave"` | The lead's own task, on a call they joined -- `Task.assisting` present. An agent's task never has it, and a provider that receives either without it answers `failed`. |
|