@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 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, never less than 1, standing until Omni restates it. The provider counts
1401
- * its own outstanding tasks against it and needs no new signal when one ends. What the agent
1402
- * holds at other providers is not this provider's concern -- Omni set `count` knowing it.
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;
@@ -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[];
@@ -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, at least 1
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 and never less than 1. An agent's capacity is a
3374
- property of the agent, not of the moment: it is stated when the agent is set up and restated only
3375
- when it genuinely changes, which is a provisioning change rather than a task starting or ending.
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
- Capacity supersedes rather than accumulates: the latest value is the ceiling.
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
- **Stopping is a break, not a capacity of zero.** Capacity says how much this agent can carry at
3978
- once; a break says they are not working. Collapsing the two would leave a provider unable to tell
3979
- an agent at their limit from an agent who has gone to lunch, and only one of those needs a reason,
3980
- a decision and a return.
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. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.69",
3
+ "version": "0.1.70",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",