@xema/omni-protocol 0.1.69 → 0.1.70
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 +7 -3
- package/dist/testing.js +20 -0
- package/dist/validation.d.ts +2 -0
- package/dist/validation.js +10 -0
- package/guide.md +27 -11
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1397,11 +1397,15 @@ export interface Snapshot<C extends Channel = Channel> {
|
|
|
1397
1397
|
/**
|
|
1398
1398
|
* How many tasks this provider may have allocated to the agent at once.
|
|
1399
1399
|
*
|
|
1400
|
-
* An absolute ceiling
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1400
|
+
* An absolute ceiling standing until Omni restates it. The provider counts its own outstanding
|
|
1401
|
+
* tasks against it and needs no new signal when one ends. The agent is one person on several
|
|
1402
|
+
* providers, and the host divides their capacity among them: `0` is host-stopped -- the agent's
|
|
1403
|
+
* capacity is elsewhere for now, this provider allocates nothing and shows the member as
|
|
1404
|
+
* `on-task` -- and is not a break, which is the agent not working at all. The host restates a
|
|
1405
|
+
* count of one or more when this provider has the agent's capacity again.
|
|
1403
1406
|
*/
|
|
1404
1407
|
export interface AgentCapacity {
|
|
1408
|
+
/** A whole number, zero or more; zero is host-stopped. */
|
|
1405
1409
|
count: number;
|
|
1406
1410
|
}
|
|
1407
1411
|
export interface SummaryMetric {
|
package/dist/testing.js
CHANGED
|
@@ -452,6 +452,26 @@ export async function exerciseAdapter(adapter, context, options = {}) {
|
|
|
452
452
|
context: connected, secrets: authenticationSecrets, reader, rebuild: options.rebuild, held: watched.held, reloading,
|
|
453
453
|
}));
|
|
454
454
|
}
|
|
455
|
+
// Capacity supersedes rather than accumulates, so a decrease is as ordinary as an increase: the
|
|
456
|
+
// host raises it, lowers it, and takes it away, and the provider takes each as the ceiling it is.
|
|
457
|
+
// A provider whose ceiling can only rise passes a zero it special-cases and still cannot go from
|
|
458
|
+
// five to three, so the exercise moves the axis both ways. Zero is host-stopped: the agent's
|
|
459
|
+
// capacity is elsewhere, and the provider allocates nothing and refuses nothing for it. Any offer
|
|
460
|
+
// after a lower count is caught against it (stream.taskOffered.overCapacity).
|
|
461
|
+
ruleEvaluated("connection.setCapacity.lowered", "connection.setCapacity.zero");
|
|
462
|
+
for (const count of [2, 1, 0]) {
|
|
463
|
+
capacityStated = count;
|
|
464
|
+
const restated = await live.setCapacity({ count });
|
|
465
|
+
const shape = validateResult(restated, "setCapacity", "connection.setCapacity");
|
|
466
|
+
violations.push(...shape);
|
|
467
|
+
if (shape.length === 0 && restated.status === "failed") {
|
|
468
|
+
violations.push({ rule: count === 0 ? "connection.setCapacity.zero" : "connection.setCapacity.lowered", path: "connection.setCapacity",
|
|
469
|
+
message: count === 0
|
|
470
|
+
? `the provider would not take a capacity of zero: ${restated.failure.code}; zero is host-stopped, the agent's capacity being elsewhere, and a provider allocates nothing and refuses nothing for it`
|
|
471
|
+
: `the provider would not take a capacity of ${count} after a higher one: ${restated.failure.code}; capacity supersedes, and a decrease is as ordinary as an increase` });
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
455
475
|
}
|
|
456
476
|
finally {
|
|
457
477
|
let clean = true;
|
package/dist/validation.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export declare function validateEventEnvelope(envelope: unknown, manifest: unkno
|
|
|
64
64
|
* phone the microphone is the phone's, and off voice there is none. Neither the value nor its
|
|
65
65
|
* absence is inferred from what kind of application the host is.
|
|
66
66
|
*/
|
|
67
|
+
/** What the host states as capacity: a whole number, zero or more, zero being host-stopped. */
|
|
68
|
+
export declare function validateCapacity(capacity: unknown, path?: string): ProtocolViolation[];
|
|
67
69
|
/** The login's store, which the host provides on every connection: three functions, and nothing else is a store. */
|
|
68
70
|
export declare function validateLoginStore(store: unknown, path?: string): ProtocolViolation[];
|
|
69
71
|
export declare function validateHostMute(mute: unknown, softphone: boolean, path?: string): ProtocolViolation[];
|
package/dist/validation.js
CHANGED
|
@@ -1700,6 +1700,16 @@ const HOST_MUTES = membersOf({ stream: true, station: true });
|
|
|
1700
1700
|
* phone the microphone is the phone's, and off voice there is none. Neither the value nor its
|
|
1701
1701
|
* absence is inferred from what kind of application the host is.
|
|
1702
1702
|
*/
|
|
1703
|
+
/** What the host states as capacity: a whole number, zero or more, zero being host-stopped. */
|
|
1704
|
+
export function validateCapacity(capacity, path = "capacity") {
|
|
1705
|
+
const into = new Collector();
|
|
1706
|
+
if (!isPlainObject(capacity)) {
|
|
1707
|
+
into.add("capacity.shape", path, "a capacity is an object carrying count");
|
|
1708
|
+
return into.violations;
|
|
1709
|
+
}
|
|
1710
|
+
into.require(typeof capacity.count === "number" && Number.isInteger(capacity.count) && capacity.count >= 0, "capacity.count", `${path}.count`, "count is a whole number, zero or more: how many tasks this provider may allocate at once, zero while the agent's capacity is elsewhere");
|
|
1711
|
+
return into.violations;
|
|
1712
|
+
}
|
|
1703
1713
|
/** The login's store, which the host provides on every connection: three functions, and nothing else is a store. */
|
|
1704
1714
|
export function validateLoginStore(store, path = "store") {
|
|
1705
1715
|
const into = new Collector();
|
package/guide.md
CHANGED
|
@@ -425,7 +425,7 @@ type Snapshot = {
|
|
|
425
425
|
};
|
|
426
426
|
|
|
427
427
|
type AgentCapacity = {
|
|
428
|
-
count: number; // absolute ceiling,
|
|
428
|
+
count: number; // absolute ceiling, zero or more; zero is host-stopped
|
|
429
429
|
};
|
|
430
430
|
|
|
431
431
|
type CapacityResult =
|
|
@@ -3370,9 +3370,10 @@ hold applied. A `held` result would repeat the discriminant that travelled with
|
|
|
3370
3370
|
|
|
3371
3371
|
States how many tasks this provider may have allocated to the agent **at once**.
|
|
3372
3372
|
|
|
3373
|
-
`count` is an absolute ceiling, not an increment
|
|
3374
|
-
property of the agent, not of the moment: it is
|
|
3375
|
-
when
|
|
3373
|
+
`count` is an absolute ceiling, not an increment, a whole number of zero or more
|
|
3374
|
+
(`capacity.count`). An agent's capacity is a property of the agent, not of the moment: it is
|
|
3375
|
+
stated when the agent is set up and restated only when it genuinely changes, which is a
|
|
3376
|
+
provisioning change rather than a task starting or ending -- with one exception below.
|
|
3376
3377
|
|
|
3377
3378
|
**The provider counts its own outstanding tasks against it.** Allocate while you hold fewer than
|
|
3378
3379
|
`count` tasks for this agent, and stop when you hold that many; when one of yours ends you have
|
|
@@ -3380,9 +3381,21 @@ room again and need no new signal to know it. Omni does not re-state capacity as
|
|
|
3380
3381
|
go, and a provider that waits for it will stall.
|
|
3381
3382
|
|
|
3382
3383
|
Your own tasks are the only ones you count. What the agent holds at other providers is not your
|
|
3383
|
-
concern — Omni set `count` knowing it
|
|
3384
|
-
|
|
3385
|
-
|
|
3384
|
+
concern — Omni set `count` knowing it, and this is how: the agent is one person on several
|
|
3385
|
+
providers, and the host divides their capacity among them rather than telling each the whole. A
|
|
3386
|
+
provider that has none of it for now is told **`count: 0`, host-stopped**: allocate nothing, show
|
|
3387
|
+
the member as `on-task` on the roster, since the agent is working, and take the next count as any
|
|
3388
|
+
other when the host has capacity for this provider again. Zero is not a refusal to state, and a
|
|
3389
|
+
provider that answers it `failed` is named (`connection.setCapacity.zero`); it is the one restatement
|
|
3390
|
+
that follows work rather than provisioning.
|
|
3391
|
+
|
|
3392
|
+
Capacity supersedes rather than accumulates: the latest value is the ceiling, and a decrease is as
|
|
3393
|
+
ordinary as an increase. A provider whose ceiling can only rise -- one that keeps the highest count
|
|
3394
|
+
it was ever told, or returns early on a small one -- cannot be told to take less work, and a host
|
|
3395
|
+
taking capacity away is answered `applied` while the work keeps coming. The harness moves the axis
|
|
3396
|
+
both ways after the drive, two then one then nought, and names a provider that will not come down
|
|
3397
|
+
(`connection.setCapacity.lowered`, `connection.setCapacity.zero`); an offer after a lower count is
|
|
3398
|
+
caught against it (`stream.taskOffered.overCapacity`).
|
|
3386
3399
|
|
|
3387
3400
|
**Capacity gates what the provider allocates, not what the agent starts.** A call placed from the
|
|
3388
3401
|
idle dialpad arrives through `task-offered` like any other task, and a full agent does not forbid
|
|
@@ -3974,10 +3987,12 @@ anything to become available.
|
|
|
3974
3987
|
| Omni commits a break | `commitBreak`. The break stops allocation, not the ceiling. |
|
|
3975
3988
|
| Agent returns from break | `endBreak` |
|
|
3976
3989
|
|
|
3977
|
-
**
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3990
|
+
**A break is not a capacity of zero, and neither is the reverse.** Capacity says how much of this
|
|
3991
|
+
agent this provider may carry; a break says they are not working at all. A provider told
|
|
3992
|
+
`count: 0` shows an agent whose capacity is elsewhere, `on-task`; a provider told a break shows
|
|
3993
|
+
`on-break`. Collapsing the two would leave a provider unable to tell an agent working on another
|
|
3994
|
+
provider from an agent who has gone to lunch, and only one of those needs a reason, a decision and
|
|
3995
|
+
a return.
|
|
3981
3996
|
|
|
3982
3997
|
### How the agent hears the call
|
|
3983
3998
|
|
|
@@ -4587,6 +4602,7 @@ same exported checks are used by Omni and adapter tests so their interpretations
|
|
|
4587
4602
|
| `validateHostReport(report)` | The host's own report as published to an adapter: `online`, and where there is audio, an input that is `available` with the microphone and `flowing`, or `unavailable` with a reason and the failure that says why, and an output that is `available` or `unavailable` with its failure. The harness validates whatever host a test hands the adapter; `stillHost(report)` builds one that never changes. |
|
|
4588
4603
|
| `validateHostMute(mute, softphone)` | What the host's Mute does, stated on a softphone login and nowhere else: `stream` or `station` (`host.mute`), required where the host holds a microphone (`host.mute.required`) and refused where it does not (`host.mute.unexpected`). The harness holds `ConnectContext.host.mute` to it. |
|
|
4589
4604
|
| `validateLoginStore(store)` | The login's store the host hands every connection: an object with `get`, `set` and `delete` (`store.shape`, `store.get`, `.set`, `.delete`). The harness holds `ConnectContext.store` to it. |
|
|
4605
|
+
| `validateCapacity(capacity)` | What the host states as capacity: a whole number of zero or more (`capacity.count`), zero being host-stopped. The harness states one on connect and two, one and zero after the drive, and names a provider that will not come down (`connection.setCapacity.lowered`) or take zero (`connection.setCapacity.zero`). |
|
|
4590
4606
|
| `validateAuthenticationResult(result, method)` | What `start()` or `complete()` answered: a challenge or a rejection, a login or a rejection. A rejection's failure is held to its rules -- an `omni.` code the contract lists, and `omni.phone-not-permitted` never retryable, since the agent's station is configuration. `validateAuthenticationFailure(failure)` is the same check on a failure alone. |
|
|
4591
4607
|
| `validateTaskCommand(command, task?)` | What a command needs to be issuable, against the task it names: its own shape -- a dial's `dialId`, a transfer's item, a remove naming exactly one person -- and, with the task, the capability the table above gates it on (`command.capability.<name>`, `.locked`), the phase it belongs to (`command.phase.*`, `command.phase.handling` for every control on the call or the conversation), and the state that has to stand: a consulted entry, a lead requested, somebody else still on the call (`command.conference.remove.alone`). A host validates before sending and an adapter before acting. |
|
|
4592
4608
|
| `validateResult(result, method)` | What a connection method answered: the status it gives, a failure where the status says so and nowhere else, the failure's shape, and that an `omni.` code is one this contract names. |
|