@xema/omni-protocol 0.1.29 → 0.1.30

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
@@ -1108,6 +1108,11 @@ export type ProviderEvent<C extends Channel = Channel> = {
1108
1108
  } | {
1109
1109
  type: "queue-summary";
1110
1110
  summary: QueueSummary;
1111
+ } | {
1112
+ type: "diagnostic";
1113
+ expected: string;
1114
+ observed: string;
1115
+ taskId?: TaskId;
1111
1116
  } | {
1112
1117
  type: "team-updated";
1113
1118
  team: TeamRoster;
package/dist/testing.js CHANGED
@@ -33,7 +33,7 @@ const STATE_SUBJECTS = [
33
33
  // `ProviderEvent` without a row here, or a row it lacks, is a compile error.
34
34
  const EVENT_TYPES = {
35
35
  snapshot: true, "transport-status": true, "break-state": true, "task-offered": true, "task-updated": true,
36
- "task-media-started": true, "task-media-ended": true, "task-ended": true, announcement: true, "queue-summary": true,
36
+ "task-media-started": true, "task-media-ended": true, "task-ended": true, announcement: true, "queue-summary": true, diagnostic: true,
37
37
  "team-updated": true, "contacts-updated": true, "calendar-updated": true,
38
38
  };
39
39
  const CONTRACT_SUBJECTS = [
@@ -281,6 +281,12 @@ export async function exerciseAdapter(adapter, context, options = {}) {
281
281
  unsubscribe = connection.subscribe(envelope => {
282
282
  observeEvent(envelope, seen);
283
283
  violations.push(...validateEventEnvelope(envelope, adapter.manifest, "event", reader()));
284
+ // A diagnostic is informational to a host and a failure to a conformance run: the platform
285
+ // under test broke a rule the adapter relies on, and a green result must not paper over it.
286
+ if (isRecord(envelope?.event) && envelope.event.type === "diagnostic") {
287
+ violations.push({ rule: "diagnostic.raised", path: "event.diagnostic",
288
+ message: `the provider reported a diagnostic: expected ${String(envelope.event.expected)}; observed ${String(envelope.event.observed)}` });
289
+ }
284
290
  if (eventNamesUsers(envelope))
285
291
  requireMethod(live, "describeUsers", "an event publishes a UserId");
286
292
  // Cross-event rules apply once the stream has a beginning: the connect snapshot.
@@ -1242,6 +1242,12 @@ export function validateEventEnvelope(envelope, manifest, path = "event", contex
1242
1242
  into.require(typeof event.html === "string", "event.announcement.html", `${at}.html`, "html must be a string when present");
1243
1243
  }
1244
1244
  break;
1245
+ case "diagnostic":
1246
+ into.filled(event.expected, "event.diagnostic.expected", `${at}.expected`, "a diagnostic states the rule that was broken, as a sentence");
1247
+ into.filled(event.observed, "event.diagnostic.observed", `${at}.observed`, "a diagnostic states what was observed instead");
1248
+ if (event.taskId !== undefined)
1249
+ into.require(isTaskId(event.taskId), "event.diagnostic.taskId", `${at}.taskId`, "taskId must be a task id when present");
1250
+ break;
1245
1251
  case "queue-summary":
1246
1252
  validateQueueSummary(event.summary, `${at}.summary`, into);
1247
1253
  break;
package/guide.md CHANGED
@@ -951,6 +951,7 @@ type ProviderEvent =
951
951
  | { type: "task-ended"; taskId: TaskId; outcome: TaskOutcome }
952
952
  | { type: "announcement"; text: string; html?: string; announcedAt: IsoTimestamp; expiresAt?: IsoTimestamp }
953
953
  | { type: "queue-summary"; summary: QueueSummary }
954
+ | { type: "diagnostic"; expected: string; observed: string; taskId?: TaskId }
954
955
  | { type: "team-updated"; team: TeamRoster }
955
956
  | { type: "contacts-updated"; contacts: Contact[] }
956
957
  | { type: "calendar-updated"; scheduledActivities: ScheduledActivity[] };
@@ -3466,7 +3467,8 @@ Omni apply state the snapshot already superseded.
3466
3467
  `transport-status` is the only signal Omni has that a transport died. An adapter must emit
3467
3468
  `transport-status` with `connecting` or `error` as soon as it loses its transport, rather than
3468
3469
  leaving a stale `active` in place while it retries internally; Omni cannot distinguish a quiet
3469
- healthy provider from a dead one.
3470
+ healthy provider from a dead one. And what Omni is told, the agent is shown — see **The status is
3471
+ seen, not merely known** under `transport-status`.
3470
3472
 
3471
3473
  #### Requesting a resync
3472
3474
 
@@ -3523,6 +3525,12 @@ authentication state, not here.
3523
3525
  adapter that leaves a stale `active` in place is telling Omni to keep waiting for work that cannot
3524
3526
  come — see **Liveness**.
3525
3527
 
3528
+ **The status is seen, not merely known.** A host renders the transport's status where the agent
3529
+ works — `connecting` from the moment it is reported, `error` with what revives it — and never a
3530
+ healthy workspace over a transport that cannot serve it: an agent talking on a desk whose transport
3531
+ died was an incident, and it happened because the desk knew and did not say. The same obligation
3532
+ covers a `diagnostic`: shown where the agent works, and counted.
3533
+
3526
3534
  ### `break-state`
3527
3535
 
3528
3536
  Replaces this provider's complete `break` object. Its `approval` uses the canonical
@@ -3606,6 +3614,26 @@ Publishes an agent-facing message. `text` is always required and is the accessib
3606
3614
  Optional HTML is sanitized by Omni. `announcedAt` and optional `expiresAt` are RFC-3339 times with
3607
3615
  explicit timezones.
3608
3616
 
3617
+ ### `diagnostic`
3618
+
3619
+ The provider's report that something its platform answered broke a rule the adapter relies on — a
3620
+ state read that contradicted itself, a `task-ended` naming a task nobody held, a party arriving
3621
+ without a name. `expected` is the rule as a sentence, `observed` is what came instead, and
3622
+ `taskId` names the task where there is one. **Informational, never behavioural**: the provider has
3623
+ already done the safe thing before it speaks, and nothing in the task or break machinery reacts to
3624
+ a diagnostic. It exists so that the loudness reaches a person — a host renders each one where the
3625
+ agent works and keeps a count an operator can read, because a healthy workspace over a shouting
3626
+ console is the silence problem one layer up. One event per occurrence; the host counts, the
3627
+ provider does not batch. `exerciseAdapter` treats a diagnostic delivered during a run as a
3628
+ violation (`diagnostic.raised`): a conformance run against a platform that is breaking its rules
3629
+ fails loudly rather than passing with a note.
3630
+
3631
+ | Field | Contract |
3632
+ | --- | --- |
3633
+ | `expected` | Required. The rule that was broken, as a sentence a person can read. |
3634
+ | `observed` | Required. What the platform answered instead. |
3635
+ | `taskId` | Optional. The task concerned, where there is one. |
3636
+
3609
3637
  ### `queue-summary`
3610
3638
 
3611
3639
  Publishes the provider's current dashboard contribution. Omni combines only the latest summary from
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",