@xema/omni-protocol 0.1.35 → 0.1.36
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 +8 -4
- package/dist/validation.js +6 -1
- package/guide.md +24 -10
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -576,15 +576,18 @@ export type TaskCompletion = {
|
|
|
576
576
|
wrapAllowance: DurationSeconds;
|
|
577
577
|
};
|
|
578
578
|
export type OnCallRole = "party" | "agent" | "consulted" | "conferenced";
|
|
579
|
+
/** Where a dialled entry stands: `ringing` until its dial is answered, `joined` after. */
|
|
580
|
+
export type OnCallStage = "ringing" | "joined";
|
|
579
581
|
export declare const ON_CALL_ROLES: readonly ["party", "agent", "consulted", "conferenced"];
|
|
580
582
|
/**
|
|
581
583
|
* Somebody on the call, or being brought onto it, as the provider states it: who, since when, and
|
|
582
584
|
* whether they are held aside. `party` is the customer; `agent` a person, by user id; `consulted`
|
|
583
585
|
* and `conferenced` someone a dial is bringing in -- listed from the moment the dial is placed, so
|
|
584
|
-
* the agent can call it off while it rings -- carrying the dial when a host placed it
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
586
|
+
* the agent can call it off while it rings -- carrying the dial when a host placed it, the address
|
|
587
|
+
* either way, and the `stage` it has reached: `ringing` until the dial is answered, `joined` after,
|
|
588
|
+
* stated so a snapshot says who is present without anyone having seen the outcome. Nobody ringing is
|
|
589
|
+
* held. `label` names a destination -- a person, a queue -- not a phrase. At most one `party` and
|
|
590
|
+
* one `consulted`: the consult commands name neither because there is exactly one.
|
|
588
591
|
*/
|
|
589
592
|
export type OnCall = {
|
|
590
593
|
since: IsoTimestamp;
|
|
@@ -597,6 +600,7 @@ export type OnCall = {
|
|
|
597
600
|
} | {
|
|
598
601
|
role: "consulted" | "conferenced";
|
|
599
602
|
destination: string;
|
|
603
|
+
stage: OnCallStage;
|
|
600
604
|
dialId?: DialId;
|
|
601
605
|
label?: string;
|
|
602
606
|
});
|
package/dist/validation.js
CHANGED
|
@@ -71,6 +71,7 @@ const DIAL_OUTCOMES = membersOf({ answered: true, busy: true, "no-answer": true,
|
|
|
71
71
|
/** The outcomes that name a cause. `unexplained` is declarable only beside one of these. */
|
|
72
72
|
const EXPLAINED_FAILURES = ["busy", "no-answer", "unreachable", "rejected", "cancelled"];
|
|
73
73
|
const ON_CALL_ROLES = membersOf({ party: true, agent: true, consulted: true, conferenced: true });
|
|
74
|
+
const ON_CALL_STAGES = membersOf({ ringing: true, joined: true });
|
|
74
75
|
/** The task capabilities under which a command dials, and so need the manifest to say how a dial ends. */
|
|
75
76
|
const DIALLING_CAPABILITIES = ["callback", "blindTransfer", "consultTransfer", "conference"];
|
|
76
77
|
const SNAPSHOT_REASONS = membersOf({
|
|
@@ -778,13 +779,17 @@ function validateOnCall(value, channel, path, into) {
|
|
|
778
779
|
}
|
|
779
780
|
if (role === "consulted" || role === "conferenced") {
|
|
780
781
|
into.filled(entry.destination, "task.onCall.destination", `${at}.destination`, `a ${role} entry names the destination that was dialled`);
|
|
782
|
+
// Stated, so a snapshot says who is present without anyone having seen the dial's outcome.
|
|
783
|
+
if (into.oneOf(entry.stage, ON_CALL_STAGES, "task.onCall.stage", `${at}.stage`)) {
|
|
784
|
+
into.require(!(entry.stage === "ringing" && entry.held === true), "task.onCall.held.ringing", `${at}.held`, "nobody ringing is held; held belongs to somebody who has joined");
|
|
785
|
+
}
|
|
781
786
|
if (entry.dialId !== undefined)
|
|
782
787
|
into.filled(entry.dialId, "task.onCall.dialId", `${at}.dialId`, "dialId must not be empty when present");
|
|
783
788
|
if (entry.label !== undefined)
|
|
784
789
|
into.filled(entry.label, "task.onCall.label", `${at}.label`, "a label must not be empty when present");
|
|
785
790
|
}
|
|
786
791
|
else {
|
|
787
|
-
for (const field of ["destination", "dialId", "label"]) {
|
|
792
|
+
for (const field of ["destination", "dialId", "label", "stage"]) {
|
|
788
793
|
into.require(entry[field] === undefined, `task.onCall.${field}.unexpected`, `${at}.${field}`, `a ${role} was dialled from nowhere; ${field} belongs on consulted or conferenced`);
|
|
789
794
|
}
|
|
790
795
|
}
|
package/guide.md
CHANGED
|
@@ -589,10 +589,12 @@ type TaskCompletion =
|
|
|
589
589
|
|
|
590
590
|
type OnCallRole = "party" | "agent" | "consulted" | "conferenced";
|
|
591
591
|
|
|
592
|
+
type OnCallStage = "ringing" | "joined";
|
|
593
|
+
|
|
592
594
|
type OnCall = { since: IsoTimestamp; held?: true } & (
|
|
593
595
|
| { role: "party" }
|
|
594
596
|
| { role: "agent"; userId: UserId }
|
|
595
|
-
| { role: "consulted" | "conferenced"; destination: string; dialId?: DialId; label?: string }
|
|
597
|
+
| { role: "consulted" | "conferenced"; destination: string; stage: OnCallStage; dialId?: DialId; label?: string }
|
|
596
598
|
);
|
|
597
599
|
|
|
598
600
|
type TaskLead = {
|
|
@@ -2133,7 +2135,7 @@ time. Runtime conformance checks also require the task channel to match its prov
|
|
|
2133
2135
|
| `wrapAllowance` | Fixed time allowed to complete the task after primary handling ends. For real-time media, it begins after `task-media-ended`. Required under `provider-automatic`, where the provider acts on it. Optional under `agent-command`: omitted says the provider imposes no deadline, and Omni counts nothing down. |
|
|
2134
2136
|
| `attributes` | Optional ordered, typed `TaskAttribute` entries with keys unique within the task. Each contact or timestamp is a separate array item; new attribute shapes require new union members. |
|
|
2135
2137
|
| `handlingHistory` | The call record: `steps` — the ordered handling history of this open task, one entry per occurrence, oldest first — and what they add up to before this agent, `handleSeconds`, `holdSeconds`, `queueSeconds`, `transfers`, each present when the provider knows it. Live task data restated with the task, not a permanent archive. See **How a task has been handled**. |
|
|
2136
|
-
| `onCall` | Voice only. Who is on the call, or being brought onto it, as the provider states it, replaced whole with the task: `party` is the customer, `agent` a person by user id, `consulted` and `conferenced` somebody a dial is bringing in, listed from the moment the dial is placed -- with the `destination` dialled, the `dialId` where a host placed it, and `held: true` on anyone parked. A `consulted` entry is what makes `transfer` `complete` and `cancel` issuable. `label` names a destination -- a person, a queue -- not a phrase; the host supplies the verb. Present when the provider knows the room, absent when it does not. See **Every dial has an outcome**. |
|
|
2138
|
+
| `onCall` | Voice only. Who is on the call, or being brought onto it, as the provider states it, replaced whole with the task: `party` is the customer, `agent` a person by user id, `consulted` and `conferenced` somebody a dial is bringing in, listed from the moment the dial is placed -- with the `destination` dialled, the `dialId` where a host placed it, the `stage` reached (`ringing` until answered, `joined` after), and `held: true` on anyone joined and parked. A `consulted` entry is what makes `transfer` `complete` and `cancel` issuable. `label` names a destination -- a person, a queue -- not a phrase; the host supplies the verb. Present when the provider knows the room, absent when it does not. See **Every dial has an outcome**. |
|
|
2137
2139
|
| `lead` | Voice only. Present from the agent's request for a lead until the lead leaves or the request ends: `requested` while nobody has joined, `joined` with the lead's `leadId` once somebody has. See **Consulting a lead**. |
|
|
2138
2140
|
| `assisting` | Voice only, on the lead's own task for a call they joined: which member asked, with their note. Its presence is what makes `lead` `take-over` and `leave` issuable. See **Consulting a lead**. |
|
|
2139
2141
|
|
|
@@ -2787,10 +2789,13 @@ set: `answered`, `busy`, `no-answer`, `unreachable`, `rejected`, `cancelled`, `u
|
|
|
2787
2789
|
`answered` is stated rather than read off somebody appearing on the call, because a host that infers
|
|
2788
2790
|
success cannot tell "reached" from "still ringing". `cancelled` is the dialler calling the dial off
|
|
2789
2791
|
before anyone answered -- the agent hanging up while a consult still rings -- which is not a
|
|
2790
|
-
`no-answer` the destination never gave. `unexplained` is the switch dropping the call
|
|
2791
|
-
|
|
2792
|
-
the switch's own words, optional, and shown to the agent attributed to the switch rather
|
|
2793
|
-
Omni.
|
|
2792
|
+
`no-answer` the destination never gave. `unexplained` is the switch dropping the call with no cause among
|
|
2793
|
+
those named here: a statement about the switch, sent instead of the nearest cause it did not give.
|
|
2794
|
+
`reason` is the switch's own words, optional, and shown to the agent attributed to the switch rather
|
|
2795
|
+
than to Omni. The two are different things and may travel together: a switch can say "could not
|
|
2796
|
+
create dialog" in words and still name no cause a code would carry, so `unexplained` with a
|
|
2797
|
+
`reason` is a dial the switch described but did not classify, and a desk renders the words without
|
|
2798
|
+
inventing the class.
|
|
2794
2799
|
|
|
2795
2800
|
**`taskId` is the task the dial was placed on**, resolved from the dial's own identity and never from
|
|
2796
2801
|
whatever task the agent is looking at when the outcome arrives. That task may already have ended,
|
|
@@ -2832,7 +2837,8 @@ knows the room:
|
|
|
2832
2837
|
const onCall: OnCall[] = [
|
|
2833
2838
|
{ role: "party", since: "2026-08-21T09:12:00Z" },
|
|
2834
2839
|
{ role: "agent", userId: "A-12", since: "2026-08-21T09:12:04Z" },
|
|
2835
|
-
{ role: "conferenced", destination: "+14155550111", dialId: "dial-7f2", held: true, since: "2026-08-21T09:15:30Z" },
|
|
2840
|
+
{ role: "conferenced", destination: "+14155550111", dialId: "dial-7f2", stage: "joined", held: true, since: "2026-08-21T09:15:30Z" },
|
|
2841
|
+
{ role: "conferenced", destination: "+14155550199", dialId: "dial-3c9", stage: "ringing", since: "2026-08-21T09:16:02Z" },
|
|
2836
2842
|
];
|
|
2837
2843
|
```
|
|
2838
2844
|
|
|
@@ -2847,9 +2853,17 @@ never saw.
|
|
|
2847
2853
|
what makes `transfer` `cancel` issuable and what `conference` `remove` names, and a destination the
|
|
2848
2854
|
agent cannot call off while it rings is a parked customer with no way back -- which is the very case
|
|
2849
2855
|
`cancelled` exists for. Consult and conference behave alike here: the `consulted` or `conferenced`
|
|
2850
|
-
entry appears with its `dialId` when the dial is placed,
|
|
2851
|
-
|
|
2852
|
-
|
|
2856
|
+
entry appears with its `dialId` when the dial is placed, and a `conference` `remove` naming a
|
|
2857
|
+
still-ringing `destination` calls that dial off, with `cancelled` as its outcome.
|
|
2858
|
+
|
|
2859
|
+
**The entry says where it stands.** A dialled entry carries `stage`: `ringing` until its dial is
|
|
2860
|
+
answered, `joined` after. The `dial-outcome` is the transition and the stage is the state, the same
|
|
2861
|
+
pairing as `task-media-started` and `media`: an `answered` outcome and the task restated with the
|
|
2862
|
+
entry `joined` say one thing twice, and any other outcome removes the entry. It is stated rather
|
|
2863
|
+
than left to whoever placed the dial because that knowledge lives in one process: after a reload,
|
|
2864
|
+
on a second session, or on a supervisor's screen the snapshot is the only source, and a room that
|
|
2865
|
+
cannot tell ringing from joined says it contains someone who may never arrive. Nobody ringing is
|
|
2866
|
+
held (`task.onCall.held.ringing`). A consult destination that never answers is a dial that
|
|
2853
2867
|
ended: the provider returns the task to `in-progress` with no `consulted` entry, as **Consult
|
|
2854
2868
|
transfer** states, so the customer is never left parked by a conforming provider.
|
|
2855
2869
|
|