@xema/omni-protocol 0.1.111 → 0.1.113

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/README.md CHANGED
@@ -15,7 +15,7 @@ system.
15
15
  | `guide/` | One file per role and channel: `agent.md`, `lead.md`, `queue.md`, `breaks.md`, `phone.md`, `voice.md`, `chat.md`, `email.md`. Each points back into `guide.md` for what it relies on. |
16
16
  | `src/index.ts` | The TypeScript declarations. |
17
17
  | `src/validation.ts` | Runtime validators Omni applies to adapter output. |
18
- | `src/testing.ts` | Conformance helpers an adapter runs against its own test state. |
18
+ | `src/testing.ts` | Testing an adapter an adapter runs against its own test state. |
19
19
  | `tests/` | One test file per source module, plus the guards over the guide and the repository's own text. |
20
20
 
21
21
  ## Entry points
@@ -23,7 +23,7 @@ system.
23
23
  ```ts
24
24
  import { defineAdapter } from "@xema/omni-protocol";
25
25
  import { validateSnapshot, assertNoViolations } from "@xema/omni-protocol/validation";
26
- import { exerciseAdapter } from "@xema/omni-protocol/testing";
26
+ import { testAdapter } from "@xema/omni-protocol/testing";
27
27
  ```
28
28
 
29
29
  ## Validation is not only for tests
@@ -49,7 +49,7 @@ is reading, or what their login declares, from the snapshot alone. `validateTeam
49
49
  `validateSnapshot`, and `validateEventEnvelope` each take an optional final `{ self, capabilities }`
50
50
  from the `authenticated` state; given them, they report `team.member.self`, `team.request.self`,
51
51
  `team.required`, `team.unentitled`, `team.requests.capability`, and `team.requests.required`.
52
- Without them those rules are not checked. `exerciseAdapter` always passes both.
52
+ Without them those rules are not checked. `testAdapter` always passes both.
53
53
 
54
54
  ```ts
55
55
  const { identity, capabilities } = authenticated;
@@ -58,17 +58,17 @@ validateSnapshot(snapshot, manifest, "snapshot", { self: identity.id, capabiliti
58
58
 
59
59
  ## Conformance
60
60
 
61
- `exerciseAdapter` validates the manifest, opens an authenticated session, connects, checks
61
+ `testAdapter` validates the manifest, opens an authenticated session, connects, checks
62
62
  required capability methods, subscribes, validates the snapshot, every delivered event, and every
63
63
  authentication state published during the run — against the latest login — states a capacity,
64
- then unsubscribes and disconnects. `result.notExercised` names what the run never reached — each
64
+ then unsubscribes and disconnects. `result.notTested` names what the run never reached — each
65
65
  optional part of a task, of the break state and team member list, each contribution, each event type — so a
66
66
  clean result is read for what it covers and not for the whole contract; `assertReached(result,
67
67
  subjects)` is the paired assertion.
68
68
 
69
69
  ```ts
70
70
  const context = { protocolVersion: OMNI_PROTOCOL_VERSION, loginId: "session-1", host: stillHost(report) };
71
- const result = await exerciseAdapter(adapter, context, { collectOnly: true });
71
+ const result = await testAdapter(adapter, context, { collectOnly: true });
72
72
  expect(result.violations).toEqual([]);
73
73
  expect(result.disconnectWasClean).toBe(true);
74
74
  ```
package/dist/index.d.ts CHANGED
@@ -1662,6 +1662,12 @@ export type HistoryReport = {
1662
1662
  ended?: never;
1663
1663
  seconds?: DurationSeconds;
1664
1664
  });
1665
+ /**
1666
+ * `at` is the provider's instant for the leg, on its own clock, assigned when the provider writes
1667
+ * the leg into its record and never an echo of the host's: the host keeps the answer as the leg's
1668
+ * identity from then on, and names it on the closing report. A provider that echoes the host's
1669
+ * instant has assigned nothing, and the test names it (`test.recordStep.identity`).
1670
+ */
1665
1671
  export type HistoryReportResult = {
1666
1672
  status: "recorded";
1667
1673
  at: IsoTimestamp;
package/dist/testing.d.ts CHANGED
@@ -26,32 +26,32 @@ export interface AdapterContractResult {
26
26
  * tasks exercises no task rule, and an adapter's own test asserts that the subjects it meant to
27
27
  * reach are absent from this list.
28
28
  */
29
- notExercised: readonly ContractSubject[];
29
+ notTested: readonly ContractSubject[];
30
30
  /** Every violation observed. Non-empty only when `collectOnly` suppressed the throw. */
31
31
  violations: readonly ProtocolViolation[];
32
32
  /**
33
33
  * Every rule the run evaluated, pass or fail: the validators' as each was applied, the stream's
34
- * and the drive's as each case was considered. A rule absent here was never looked at, which a
34
+ * and the test's as each case was considered. A rule absent here was never looked at, which a
35
35
  * clean `violations` says nothing about; a test that needs a rule to have run asserts it here.
36
36
  */
37
- rulesEvaluated: ReadonlySet<string>;
37
+ rulesTested: ReadonlySet<string>;
38
38
  }
39
- export interface ExerciseAdapterOptions {
39
+ export interface TestAdapterOptions {
40
40
  /** Return violations in the result instead of throwing. Defaults to `false`. */
41
41
  collectOnly?: boolean;
42
42
  /**
43
- * Drive one ordinary lifecycle on the first task the provider offers -- accept it, open its
43
+ * Test one ordinary lifecycle on the first task the provider offers -- accept it, open its
44
44
  * audio on a softphone, hold and resume where offered, end the call where offered, complete it
45
45
  * where the agent completes -- so the rules about a live call are reached rather than listed
46
- * under `notExercised`. Off by default, since it issues commands against whatever platform the
46
+ * under `notTested`. Off by default, since it issues commands against whatever platform the
47
47
  * adapter is connected to: turn it on against a test backend.
48
48
  */
49
- drive?: boolean;
50
- /** How long the drive waits for each thing the provider owes it. Defaults to 5000. */
51
- driveTimeoutMs?: number;
49
+ withCall?: boolean;
50
+ /** How long the test waits for each thing the provider owes it. Defaults to 5000. */
51
+ timeoutMs?: number;
52
52
  /**
53
53
  * Builds the adapter again, as a host reload does: a new object with no memory, for the same
54
- * login. Given it, the drive -- once the host's muted leg is recorded -- connects a second adapter
54
+ * login. Given it, the test -- once the host's muted leg is recorded -- connects a second adapter
55
55
  * with the same context and the same store and expects its snapshot to carry the task with that
56
56
  * leg. A platform that holds the record hands it back; an adapter that composed the record in
57
57
  * memory has nothing, and is named. Without it the store is required and never read.
@@ -77,7 +77,7 @@ export interface ExerciseAdapterOptions {
77
77
  * for a synchronous emitter, and is swallowed as an unhandled rejection for an
78
78
  * asynchronous one — which would let a non-conforming async adapter pass.
79
79
  */
80
- export declare function exerciseAdapter<C extends Channel>(adapter: Adapter<C>, context: ConnectContext, options?: ExerciseAdapterOptions): Promise<AdapterContractResult>;
80
+ export declare function testAdapter<C extends Channel>(adapter: Adapter<C>, context: ConnectContext, options?: TestAdapterOptions): Promise<AdapterContractResult>;
81
81
  /**
82
82
  * Validates restored authentication followed by a refresh failure or expiry. Every state is
83
83
  * validated, and a `refreshing` state must carry over the login it refreshes.
@@ -151,6 +151,14 @@ export declare class TaskStream {
151
151
  private readonly endedAssignments;
152
152
  /** Every assignment a `task-offered` introduced, as against one a snapshot carried: each is owed its ending. */
153
153
  private readonly offered;
154
+ /** The last seconds-left value the stream read per task and field, with the provider instant it was published at. */
155
+ private readonly countdowns;
156
+ /**
157
+ * A seconds-left value is the provider's arithmetic at the instant of the publication, never a
158
+ * value copied from an earlier one: between two publications the countdown loses at least the
159
+ * seconds the provider's own clock says passed, within a second of rounding.
160
+ */
161
+ private holdCountdowns;
154
162
  private readonly dials;
155
163
  /** The host placed a dial: its outcome, whenever it arrives, is one the stream expects. */
156
164
  dialled(dialId: string): void;
@@ -229,10 +237,10 @@ export declare function assertBreakFollowsItsRequests(envelopes: readonly Provid
229
237
  * follows the audio ending is `completing` or `task-ended`.
230
238
  */
231
239
  export declare function assertAudioFollowsTheTask(envelopes: readonly ProviderEventEnvelope[], snapshot?: Snapshot): void;
232
- /** A login store that lives in memory for the test: what most adapter tests hand `exerciseAdapter` as `context.store`. */
240
+ /** A login store that lives in memory for the test: what most adapter tests hand `testAdapter` as `context.store`. */
233
241
  export declare function memoryStore(): LoginStore;
234
242
  /**
235
- * A host that reports one thing and never changes: what most adapter tests hand `exerciseAdapter`.
243
+ * A host that reports one thing and never changes: what most adapter tests hand `testAdapter`.
236
244
  * A softphone host states what its Mute does; a desk-phone or conversation host has no microphone and states nothing.
237
245
  */
238
246
  export declare function stillHost(report: HostReport, guarantees: HostGuarantees, mute: HostMute): Host & {