@xema/omni-protocol 0.1.60 → 0.1.61
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/testing.js +19 -13
- package/guide.md +14 -6
- package/package.json +1 -1
package/dist/testing.js
CHANGED
|
@@ -1000,7 +1000,25 @@ async function driveOneCall(drive) {
|
|
|
1000
1000
|
if (await updated(t => t.phase === "in-progress" || t.phase === "completing", "the task leaving preview after Call") === undefined)
|
|
1001
1001
|
return found;
|
|
1002
1002
|
}
|
|
1003
|
+
// The other direction of step 4, wherever the task stands outside the handling phases with the
|
|
1004
|
+
// control still declared: a host holds it back (command.phase.handling), and an adapter that
|
|
1005
|
+
// receives it anyway must refuse it -- so the drive sends it past the validator and expects failed.
|
|
1006
|
+
const holdRefusedOutsideHandling = async () => {
|
|
1007
|
+
const phase = String(latestTask().phase);
|
|
1008
|
+
let answer;
|
|
1009
|
+
try {
|
|
1010
|
+
answer = await drive.connection.execute({ taskId, command: { type: "hold" } });
|
|
1011
|
+
}
|
|
1012
|
+
catch (error) {
|
|
1013
|
+
refuse("drive.command.rejected", "drive.command.hold", `execute rejected rather than answered: ${String(error)}`);
|
|
1014
|
+
}
|
|
1015
|
+
if (isRecord(answer) && answer.status !== "failed") {
|
|
1016
|
+
refuse("drive.command.handling", "drive.command.hold", `the provider applied hold on a ${phase} task: a control on the contact belongs to in-progress or paused, and the adapter is the second gate`);
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1003
1019
|
if (latestTask().phase === "confirmed") {
|
|
1020
|
+
if (offers("hold"))
|
|
1021
|
+
await holdRefusedOutsideHandling();
|
|
1004
1022
|
if (await updated(t => t.phase !== "confirmed", "the task leaving confirmed") === undefined)
|
|
1005
1023
|
return found;
|
|
1006
1024
|
}
|
|
@@ -1086,19 +1104,7 @@ async function driveOneCall(drive) {
|
|
|
1086
1104
|
if (mediaEnded !== undefined)
|
|
1087
1105
|
cursor = mediaEnded.at;
|
|
1088
1106
|
if (await updated(t => t.phase === "completing", "the task completing after its media ended") !== undefined && offers("hold")) {
|
|
1089
|
-
|
|
1090
|
-
// A host holds it back (command.phase.handling), and an adapter that receives it anyway
|
|
1091
|
-
// must refuse it -- so the drive sends it past the validator and expects failed.
|
|
1092
|
-
let answer;
|
|
1093
|
-
try {
|
|
1094
|
-
answer = await drive.connection.execute({ taskId, command: { type: "hold" } });
|
|
1095
|
-
}
|
|
1096
|
-
catch (error) {
|
|
1097
|
-
refuse("drive.command.rejected", "drive.command.hold", `execute rejected rather than answered: ${String(error)}`);
|
|
1098
|
-
}
|
|
1099
|
-
if (isRecord(answer) && answer.status !== "failed") {
|
|
1100
|
-
refuse("drive.command.handling", "drive.command.hold", `the provider applied hold on a completing task: a control on the contact belongs to in-progress or paused, and the adapter is the second gate`);
|
|
1101
|
-
}
|
|
1107
|
+
await holdRefusedOutsideHandling();
|
|
1102
1108
|
}
|
|
1103
1109
|
}
|
|
1104
1110
|
}
|
package/guide.md
CHANGED
|
@@ -2465,8 +2465,13 @@ Steps are `queued`, `offered`, `answered`, `held`, `muted`, `transferred`, `conf
|
|
|
2465
2465
|
**The record is one entry per occurrence, oldest first.** Each hold is its own `held` entry — `at`
|
|
2466
2466
|
when it began, `seconds` once it ended and omitted while it runs — and a second hold is a second
|
|
2467
2467
|
entry after the first, never a revision of it. The same goes for every step: two mutes are two
|
|
2468
|
-
`muted` entries
|
|
2469
|
-
|
|
2468
|
+
`muted` entries, and a call that joined two queues -- a menu's, then this one -- has two `queued`
|
|
2469
|
+
entries, one per join. Order is enforced: an entry earlier than the one before it is refused
|
|
2470
|
+
(`task.handlingHistory.order`). **Intervals between steps are the host's to subtract**, never a
|
|
2471
|
+
total the provider adds: *time to offer*, from the call's arrival to the agent's screen lighting
|
|
2472
|
+
up, is the `offered` entry's `at` minus the last `queued` entry's, and the ring is outside it.
|
|
2473
|
+
`queueSeconds` keeps the industry's meaning -- the whole wait until somebody answered -- and is not
|
|
2474
|
+
that interval. **Handle time is anchored, not restarted.** It runs from the
|
|
2470
2475
|
`answered` step's `at` — from the task's first `in-progress` where the provider reports no
|
|
2471
2476
|
history — until the task's media ends, and a hold neither pauses nor resets it: the hold's own
|
|
2472
2477
|
duration is the `held` entry's `seconds`, and a desk that restarts its counter on resume is
|
|
@@ -4540,10 +4545,13 @@ violation (`drive.command.failed`), and an event the provider owes and never sen
|
|
|
4540
4545
|
the drive mutes it for one second and reports the leg through `recordStep`, begun and then ended,
|
|
4541
4546
|
expecting each report `recorded` (`drive.recordStep.failed`, `.rejected`); and where the provider
|
|
4542
4547
|
restates the task's record afterwards, the leg is in it or the hole is named
|
|
4543
|
-
(`drive.recordStep.history`).
|
|
4544
|
-
`
|
|
4545
|
-
|
|
4546
|
-
|
|
4548
|
+
(`drive.recordStep.history`). Outside the handling phases with `hold` still declared -- in
|
|
4549
|
+
`confirmed`, where the provider publishes it, and in `completing` once the call has ended -- the
|
|
4550
|
+
drive sends `hold` past the validator that would hold it back, and expects `failed`: the adapter
|
|
4551
|
+
is the second gate on a control on the contact, and one that applies it where there is nothing to
|
|
4552
|
+
hold is named (`drive.command.handling`). The drive cannot put a task into a phase the provider
|
|
4553
|
+
never publishes, so a provider that goes straight from `pending` to `in-progress` is checked in
|
|
4554
|
+
`completing` alone. A task the agent completes is
|
|
4547
4555
|
completed from wherever it stands once the drive has nothing left to do on it -- from `completing`
|
|
4548
4556
|
after an `end-call`, or from `in-progress` where there is no call to end, which is every chat and
|
|
4549
4557
|
email and a voice task offering no `endCall` -- so a conversation reaches its end as a call does.
|