@xema/omni-protocol 0.1.2 → 0.1.3

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 CHANGED
@@ -290,6 +290,8 @@ export type TaskCapabilities<C extends Channel = Channel> = C extends "voice" ?
290
290
  mute?: true;
291
291
  hold?: true;
292
292
  agentDisconnect?: true;
293
+ /** Reach the party again while `completing`; the task returns to `in-progress`. */
294
+ callback?: true;
293
295
  blindTransfer?: true | DestinationDirectory;
294
296
  conference?: true | DestinationDirectory;
295
297
  recording?: true;
@@ -426,7 +428,7 @@ export type TaskOutcome = {
426
428
  failure: ProtocolFailure;
427
429
  };
428
430
  export declare const TASK_COMMAND_NAMES: {
429
- readonly voice: readonly ["answer", "decline", "start-call", "mute", "hold", "resume", "disconnect", "transfer", "conference", "recording", "complete"];
431
+ readonly voice: readonly ["answer", "decline", "start-call", "mute", "hold", "resume", "disconnect", "callback", "transfer", "conference", "recording", "complete"];
430
432
  readonly chat: readonly ["accept", "reject", "pause", "resume", "complete"];
431
433
  readonly email: readonly ["accept", "reject", "complete"];
432
434
  };
@@ -450,6 +452,10 @@ export type VoiceTaskCommand = {
450
452
  type: "resume";
451
453
  } | {
452
454
  type: "disconnect";
455
+ }
456
+ /** Issuable only in `completing`, under the `callback` capability. Carries no destination. */
457
+ | {
458
+ type: "callback";
453
459
  } | {
454
460
  type: "transfer";
455
461
  destination: string;
package/dist/index.js CHANGED
@@ -55,7 +55,7 @@ export function isAllowedBrowserUrl(url) {
55
55
  // ---------------------------------------------------------------------------
56
56
  export const TASK_COMMAND_NAMES = {
57
57
  voice: ["answer", "decline", "start-call", "mute", "hold", "resume", "disconnect",
58
- "transfer", "conference", "recording", "complete"],
58
+ "callback", "transfer", "conference", "recording", "complete"],
59
59
  chat: ["accept", "reject", "pause", "resume", "complete"],
60
60
  email: ["accept", "reject", "complete"],
61
61
  };
package/dist/testing.d.ts CHANGED
@@ -39,6 +39,10 @@ export declare function assertReconnectWithMissedAssignments<C extends Channel>(
39
39
  * There is no `denied` approval: a refusal returns the agent to `not-requested`, because a
40
40
  * pending request nobody is coming to decide is worse than none. So the scenario is a request
41
41
  * that goes back to not-requested, and a later one that is granted.
42
+ *
43
+ * A request shows as `awaiting-decision` where a person decides, and as `granted` at once where
44
+ * the provider decides alone; either is the request being made, and the rule is the same for
45
+ * both.
42
46
  */
43
47
  export declare function assertDeniedAndRetriedBreak(approvals: readonly BreakApproval[]): void;
44
48
  /** Verifies that retrying one dial command cannot place a second call. */
package/dist/testing.js CHANGED
@@ -209,9 +209,13 @@ export function assertReconnectWithMissedAssignments(before, reconnect, missedTa
209
209
  * There is no `denied` approval: a refusal returns the agent to `not-requested`, because a
210
210
  * pending request nobody is coming to decide is worse than none. So the scenario is a request
211
211
  * that goes back to not-requested, and a later one that is granted.
212
+ *
213
+ * A request shows as `awaiting-decision` where a person decides, and as `granted` at once where
214
+ * the provider decides alone; either is the request being made, and the rule is the same for
215
+ * both.
212
216
  */
213
217
  export function assertDeniedAndRetriedBreak(approvals) {
214
- const asked = approvals.indexOf("awaiting-decision");
218
+ const asked = approvals.findIndex(approval => approval === "awaiting-decision" || approval === "granted");
215
219
  if (asked < 0)
216
220
  throw new Error("Break retry scenario requires an initial request");
217
221
  const refused = approvals.indexOf("not-requested", asked + 1);
@@ -79,7 +79,7 @@ const ISOLATION_SCHEME_VALUES = Object.values(BROWSER_ISOLATION_SCHEMES);
79
79
  const TASK_CAPABILITIES = {
80
80
  voice: membersOf({
81
81
  browsers: true, dispositions: true, custom: true, decline: true, mute: true, hold: true,
82
- agentDisconnect: true, blindTransfer: true, conference: true, recording: true,
82
+ agentDisconnect: true, callback: true, blindTransfer: true, conference: true, recording: true,
83
83
  }),
84
84
  chat: membersOf({ browsers: true, dispositions: true, custom: true, reject: true, hold: true }),
85
85
  email: membersOf({ browsers: true, dispositions: true, custom: true, reject: true }),
package/guide.md CHANGED
@@ -335,6 +335,7 @@ type TaskCapabilities<C extends Channel = Channel> =
335
335
  mute?: true;
336
336
  hold?: true;
337
337
  agentDisconnect?: true;
338
+ callback?: true;
338
339
  blindTransfer?: true | DestinationDirectory;
339
340
  conference?: true | DestinationDirectory;
340
341
  recording?: true;
@@ -459,6 +460,7 @@ const TASK_COMMAND_NAMES = {
459
460
  "hold",
460
461
  "resume",
461
462
  "disconnect",
463
+ "callback",
462
464
  "transfer",
463
465
  "conference",
464
466
  "recording",
@@ -481,6 +483,7 @@ type VoiceTaskCommand =
481
483
  | { type: "hold" }
482
484
  | { type: "resume" }
483
485
  | { type: "disconnect" }
486
+ | { type: "callback" }
484
487
  | { type: "transfer"; destination: string }
485
488
  | { type: "conference"; participant: string; action: "add" | "remove" }
486
489
  | { type: "recording"; action: "start" | "pause" | "resume" | "stop" }
@@ -1658,6 +1661,7 @@ The canonical task transitions are:
1658
1661
  | `in-progress` | Provider or agent pauses the task | `paused` |
1659
1662
  | `paused` | Provider or agent resumes the task | `in-progress` |
1660
1663
  | `in-progress` or `paused` | Contact handling ends and follow-up work remains | `completing` |
1664
+ | `completing` | Agent calls the party back (`callback`) | `in-progress` |
1661
1665
  | Any phase | Provider emits `task-ended` | Removed |
1662
1666
 
1663
1667
  Allocation, acceptance, and progress are distinct. Acceptance follows `autoAcceptTasks` and the
@@ -1712,6 +1716,42 @@ and the agent takes as long as the work needs. Moving the task to `completing` l
1712
1716
  deadline is not an alternative on a media channel: the clock starts at a real event, and delaying
1713
1717
  that event would falsify the phase and everything timed from it.
1714
1718
 
1719
+ #### Calling back during completion
1720
+
1721
+ A task that declares `callback` lets the agent reach the party again while the task is
1722
+ `completing` -- to finish what the call left unfinished, on the same task rather than a new one.
1723
+ Omni issues `{ type: "callback" }`; it is issuable only in `completing`, and only where the
1724
+ capability is declared. The provider knows who the party is; the command carries no destination.
1725
+
1726
+ On `applied` the provider is placing the call and the task returns to `in-progress`: the agent is
1727
+ working again, and the completion allowance is **discarded, not paused**. From there the call is
1728
+ reported as any call is -- `paused`, `in-progress`, and when its media ends, `task-media-ended`
1729
+ again, which starts a fresh allowance from that instant. A party who does not answer is a call
1730
+ whose media ended: the task returns to `completing` through the same event and the clock starts
1731
+ again from there. At no point is an agent dialling against a deadline.
1732
+
1733
+ **The control exists only while there is a window to use it in.** Under `agent-command` the task
1734
+ stays `completing` until the agent completes it, so the window is open for as long as they need.
1735
+ Under `provider-automatic` the window is the allowance -- and with `completionAllowance: 0` there
1736
+ is none: the provider disposes the task at provider end, and Omni does not offer Call back, whatever
1737
+ the task declares. A capability names a control that can be used; on a task with no `completing`
1738
+ window it cannot, and declaring it there changes nothing.
1739
+
1740
+ ```ts
1741
+ const callbackCapable = {
1742
+ channel: "voice",
1743
+ capabilities: { hold: true, callback: true, dispositions: true },
1744
+ phase: "completing",
1745
+ completionMode: "provider-automatic",
1746
+ completionAllowance: 30,
1747
+ } satisfies Pick<Task<"voice">, "channel" | "capabilities" | "phase" | "completionMode" | "completionAllowance">;
1748
+ ```
1749
+
1750
+ With ten seconds of the thirty left, the agent presses Call back: `execute({ command: { type:
1751
+ "callback" } })` returns `applied`, the task is `in-progress`, and the thirty seconds are gone.
1752
+ The second call ends: `task-media-ended`, the task is `completing`, and a new thirty seconds runs
1753
+ from that instant.
1754
+
1715
1755
  ```ts
1716
1756
  const immediateProviderCompletion = {
1717
1757
  completionMode: "provider-automatic",
@@ -1905,6 +1945,7 @@ const taskCapabilities = {
1905
1945
  | `mute` | Primary toggle: Mute | Omni may mute and unmute the agent's outbound audio. |
1906
1946
  | `hold` | Primary toggle: Hold | Omni may issue voice-task `hold` and `resume` commands. |
1907
1947
  | `agentDisconnect` | Primary button: Disconnect | Omni may disconnect real-time media without disposing the task. |
1948
+ | `callback` | Completing-task button: Call back | Omni may have the provider call the task's party back 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 **Calling back during completion**. |
1908
1949
  | `blindTransfer` | Secondary menu item: Blind transfer | Omni may transfer the caller directly to a destination. |
1909
1950
  | `conference` | Secondary button: Conference | Omni may add or remove participants from the active call. |
1910
1951
  | `recording` | Overflow menu item: Recording | Omni may expose start, pause, resume, and stop recording controls. |
@@ -2619,6 +2660,7 @@ declared:
2619
2660
  | `decline`, `reject` | The channel's decline or reject capability, **and** Omni provisioning permitting rejection. |
2620
2661
  | `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. |
2621
2662
  | `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. |
2663
+ | `callback` | The `callback` 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. |
2622
2664
  | Everything else | Its own named capability. |
2623
2665
 
2624
2666
  Declining or rejecting a pending offer ends it without accepting or completing it. The provider
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",