@xema/omni-protocol 0.1.26 → 0.1.27

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
@@ -342,7 +342,8 @@ export interface ScheduledActivity {
342
342
  title: string;
343
343
  startsAt: IsoTimestamp;
344
344
  endsAt?: IsoTimestamp;
345
- contact?: Contact;
345
+ /** The person the activity reaches -- a callback's customer. */
346
+ party?: Contact;
346
347
  attributes?: Attribute[];
347
348
  }
348
349
  export interface DispositionCode {
@@ -443,10 +444,10 @@ export type TaskBrowser = Browser & {
443
444
  /** Hide this tab's URL from the agent in Omni's chrome. Omitted, the URL shows as any browser's does; a provider says `hidden` where the URL carries what the agent may not read. */
444
445
  urlVisibility?: UrlVisibility;
445
446
  } & ({
446
- reuse: false;
447
+ sharedSession: false;
447
448
  isolationScheme?: never;
448
449
  } | {
449
- reuse: true;
450
+ sharedSession: true;
450
451
  isolationScheme: BrowserIsolationScheme;
451
452
  });
452
453
  /** A tab the agent opened in the personal workspace: theirs, as many as they like, and never on the wire. */
@@ -473,7 +474,7 @@ export type TaskAttribute = TaskAttributeBase & ({
473
474
  value: string;
474
475
  } | {
475
476
  type: "contact";
476
- contact: Contact;
477
+ party: Contact;
477
478
  } | {
478
479
  type: "timestamp";
479
480
  at: IsoTimestamp;
@@ -590,7 +591,8 @@ export type Task<C extends Channel = Channel> = {
590
591
  taskType: string;
591
592
  capabilities: TaskCapabilities<C>;
592
593
  browsers: TaskBrowser[];
593
- contact?: Contact;
594
+ /** The person or entity on the other end of this task. Who the task is with; `contacts` on the snapshot is the directory. */
595
+ party?: Contact;
594
596
  phase: TaskPhase;
595
597
  /** The identifier an agent reads back to a customer, where the provider has one. */
596
598
  reference?: string;
@@ -1031,7 +1033,7 @@ export interface SummaryMetric {
1031
1033
  label: string;
1032
1034
  value: string;
1033
1035
  }
1034
- export interface ProviderSummary {
1036
+ export interface QueueSummary {
1035
1037
  title: string;
1036
1038
  subtitle?: string;
1037
1039
  waitingCount: number;
@@ -1080,8 +1082,8 @@ export type ProviderEvent<C extends Channel = Channel> = {
1080
1082
  announcedAt: IsoTimestamp;
1081
1083
  expiresAt?: IsoTimestamp;
1082
1084
  } | {
1083
- type: "provider-summary";
1084
- summary: ProviderSummary;
1085
+ type: "queue-summary";
1086
+ summary: QueueSummary;
1085
1087
  } | {
1086
1088
  type: "team-updated";
1087
1089
  team: TeamRoster;
@@ -1229,7 +1231,7 @@ export declare function sameCapabilities(a: UserCapabilities, b: UserCapabilitie
1229
1231
  /**
1230
1232
  * The storage-profile key a reusing browser shares, or `undefined` where it shares nothing.
1231
1233
  *
1232
- * Fails closed. A browser with `reuse: false` has no key; nor does a reusing one whose scheme is
1234
+ * Fails closed. A browser with `sharedSession: false` has no key; nor does a reusing one whose scheme is
1233
1235
  * missing or unknown -- the type forbids that, but an adapter compiled against another version can
1234
1236
  * still send it, and the safe reading is "do not share", never "share with everyone named the
1235
1237
  * same". Every part is encoded, separator included, before joining, so a tab called `a.b`
package/dist/index.js CHANGED
@@ -157,7 +157,7 @@ export function sameCapabilities(a, b) {
157
157
  /**
158
158
  * The storage-profile key a reusing browser shares, or `undefined` where it shares nothing.
159
159
  *
160
- * Fails closed. A browser with `reuse: false` has no key; nor does a reusing one whose scheme is
160
+ * Fails closed. A browser with `sharedSession: false` has no key; nor does a reusing one whose scheme is
161
161
  * missing or unknown -- the type forbids that, but an adapter compiled against another version can
162
162
  * still send it, and the safe reading is "do not share", never "share with everyone named the
163
163
  * same". Every part is encoded, separator included, before joining, so a tab called `a.b`
@@ -165,7 +165,7 @@ export function sameCapabilities(a, b) {
165
165
  */
166
166
  export function browserSessionKey(input) {
167
167
  const { providerId, taskId, taskType, browser } = input;
168
- if (browser.reuse !== true)
168
+ if (browser.sharedSession !== true)
169
169
  return undefined;
170
170
  // `encodeURIComponent` leaves `.` untouched, and `.` is the separator: a raw join would let
171
171
  // provider `Acme.Voice` with type `Support` forge the key of `Acme` with `Voice.Support`.
package/dist/testing.d.ts CHANGED
@@ -166,7 +166,7 @@ export declare function assertWrapTimeout(task: Pick<TaskCompletion, "completion
166
166
  /** One browser in one task of one provider. `providerId` is `Manifest.id`, never `displayName`. */
167
167
  export type BrowserIsolationScenario = BrowserSessionKeyInput;
168
168
  /** Validates whether two task-browser definitions should share one browser session. */
169
- export declare function assertBrowserIsolationAndReuse(left: BrowserIsolationScenario, right: BrowserIsolationScenario, expectedReuse: boolean): void;
169
+ export declare function assertBrowserSessionIsolation(left: BrowserIsolationScenario, right: BrowserIsolationScenario, expectedReuse: boolean): void;
170
170
  /**
171
171
  * Asserts that no two distinct scenarios in `scenarios` derive the same session key.
172
172
  * Feed it adversarial names — a provider called `A.B` against a task type called
package/dist/testing.js CHANGED
@@ -32,7 +32,7 @@ const STATE_SUBJECTS = [
32
32
  // `ProviderEvent` without a row here, or a row it lacks, is a compile error.
33
33
  const EVENT_TYPES = {
34
34
  snapshot: true, "transport-status": true, "break-state": true, "task-offered": true, "task-updated": true,
35
- "task-media-started": true, "task-media-ended": true, "task-ended": true, announcement: true, "provider-summary": true,
35
+ "task-media-started": true, "task-media-ended": true, "task-ended": true, announcement: true, "queue-summary": true,
36
36
  "team-updated": true, "contacts-updated": true, "calendar-updated": true,
37
37
  };
38
38
  const CONTRACT_SUBJECTS = [
@@ -854,14 +854,14 @@ export function assertWrapTimeout(task, mediaEndedAt, observedDeadline, toleranc
854
854
  /** The session key one scenario derives, or `undefined` where the browser shares nothing. */
855
855
  const sessionKeyFor = (scenario) => browserSessionKey(scenario);
856
856
  /** Validates whether two task-browser definitions should share one browser session. */
857
- export function assertBrowserIsolationAndReuse(left, right, expectedReuse) {
857
+ export function assertBrowserSessionIsolation(left, right, expectedReuse) {
858
858
  const leftKey = sessionKeyFor(left);
859
859
  const rightKey = sessionKeyFor(right);
860
- // A browser that does not reuse has no session key at all, so two of them never share one.
861
- // Treating "no key" as a match would report reuse nobody asked for.
860
+ // A browser that does not share its session has no session key at all, so two of them never share one.
861
+ // Treating "no key" as a match would report sharing nobody asked for.
862
862
  const actualReuse = leftKey !== undefined && leftKey === rightKey;
863
863
  if (actualReuse !== expectedReuse) {
864
- throw new Error(`Browser reuse mismatch: expected ${expectedReuse}, received ${actualReuse} (${String(leftKey)} vs ${String(rightKey)})`);
864
+ throw new Error(`Browser session sharing mismatch: expected ${expectedReuse}, received ${actualReuse} (${String(leftKey)} vs ${String(rightKey)})`);
865
865
  }
866
866
  }
867
867
  /**
@@ -200,8 +200,8 @@ function validateScheduledActivityInto(activity, path, into) {
200
200
  into.require(Date.parse(activity.endsAt) >= Date.parse(activity.startsAt), "activity.endsAt.order", `${path}.endsAt`, "endsAt must not precede startsAt");
201
201
  }
202
202
  }
203
- if (activity.contact !== undefined)
204
- validateContactInto(activity.contact, `${path}.contact`, into);
203
+ if (activity.party !== undefined)
204
+ validateContactInto(activity.party, `${path}.party`, into);
205
205
  validateAttributes(activity.attributes, `${path}.attributes`, into);
206
206
  }
207
207
  // ---------------------------------------------------------------------------
@@ -572,7 +572,7 @@ function validateBrowsers(value, path, into) {
572
572
  // Reuse and its scheme travel together. A reusing browser with no scheme would otherwise
573
573
  // inherit whatever a host happened to default to, which is how two tasks end up sharing a
574
574
  // session nobody intended. The guide names the rule for the missing case.
575
- if (browser.reuse === true) {
575
+ if (browser.sharedSession === true) {
576
576
  if (browser.isolationScheme === undefined) {
577
577
  into.add("task.browser.isolationScheme.required", `${at}.isolationScheme`, `a reusing browser must declare one of: ${ISOLATION_SCHEME_VALUES.join(", ")}`);
578
578
  }
@@ -580,11 +580,11 @@ function validateBrowsers(value, path, into) {
580
580
  into.require(ISOLATION_SCHEME_VALUES.includes(browser.isolationScheme), "task.browser.isolationScheme", `${at}.isolationScheme`, `an isolation scheme must be one of: ${ISOLATION_SCHEME_VALUES.join(", ")}`);
581
581
  }
582
582
  }
583
- else if (browser.reuse === false) {
584
- into.require(browser.isolationScheme === undefined, "task.browser.isolationScheme.unexpected", `${at}.isolationScheme`, "a browser that does not reuse must not declare an isolation scheme");
583
+ else if (browser.sharedSession === false) {
584
+ into.require(browser.isolationScheme === undefined, "task.browser.isolationScheme.unexpected", `${at}.isolationScheme`, "a browser that does not share its session must not declare an isolation scheme");
585
585
  }
586
586
  else {
587
- into.add("task.browser.reuse", `${at}.reuse`, "a browser must say whether it reuses a session");
587
+ into.add("task.browser.sharedSession", `${at}.sharedSession`, "a browser must say whether its session is shared across tasks");
588
588
  }
589
589
  });
590
590
  }
@@ -745,8 +745,8 @@ function validateTaskInto(task, context, path, into) {
745
745
  if (task.reference !== undefined) {
746
746
  into.filled(task.reference, "task.reference", `${path}.reference`, "a reference must not be empty when present");
747
747
  }
748
- if (task.contact !== undefined)
749
- validateContactInto(task.contact, `${path}.contact`, into, context.levels);
748
+ if (task.party !== undefined)
749
+ validateContactInto(task.party, `${path}.party`, into, context.levels);
750
750
  validateBrowsers(task.browsers, `${path}.browsers`, into);
751
751
  validateTaskAttributes(task.attributes, `${path}.attributes`, into);
752
752
  validateHandlingHistory(task.handlingHistory, `${path}.handlingHistory`, into);
@@ -1117,7 +1117,7 @@ function validateTaskOutcome(value, path, into) {
1117
1117
  into.add("event.taskEnded.outcome.type", `${path}.type`, `unsupported outcome: ${String(value.type)}`);
1118
1118
  }
1119
1119
  }
1120
- function validateProviderSummary(value, path, into) {
1120
+ function validateQueueSummary(value, path, into) {
1121
1121
  if (!isPlainObject(value)) {
1122
1122
  into.add("event.summary.shape", path, "a provider summary must be an object");
1123
1123
  return;
@@ -1238,8 +1238,8 @@ export function validateEventEnvelope(envelope, manifest, path = "event", contex
1238
1238
  into.require(typeof event.html === "string", "event.announcement.html", `${at}.html`, "html must be a string when present");
1239
1239
  }
1240
1240
  break;
1241
- case "provider-summary":
1242
- validateProviderSummary(event.summary, `${at}.summary`, into);
1241
+ case "queue-summary":
1242
+ validateQueueSummary(event.summary, `${at}.summary`, into);
1243
1243
  break;
1244
1244
  case "team-updated":
1245
1245
  validateTeamRosterInto(event.team, `${at}.team`, { ...context, levels }, into);
package/guide.md CHANGED
@@ -393,7 +393,7 @@ type ScheduledActivity = {
393
393
  title: string;
394
394
  startsAt: IsoTimestamp;
395
395
  endsAt?: IsoTimestamp;
396
- contact?: Contact;
396
+ party?: Contact;
397
397
  attributes?: Attribute[];
398
398
  };
399
399
 
@@ -492,8 +492,8 @@ type TaskBrowser = Browser & {
492
492
  purpose: string;
493
493
  urlVisibility?: UrlVisibility;
494
494
  } & (
495
- | { reuse: false; isolationScheme?: never }
496
- | { reuse: true; isolationScheme: BrowserIsolationScheme }
495
+ | { sharedSession: false; isolationScheme?: never }
496
+ | { sharedSession: true; isolationScheme: BrowserIsolationScheme }
497
497
  );
498
498
 
499
499
  type PersonalBrowser = Browser;
@@ -507,7 +507,7 @@ type BrowserSessionKeyInput = {
507
507
  ```
508
508
 
509
509
  That union is what makes a reusing browser with no scheme fail to compile rather than inherit a
510
- default — see **Choosing a reuse scheme**.
510
+ default — see **Choosing an isolation scheme**.
511
511
 
512
512
  ### Task
513
513
 
@@ -529,7 +529,7 @@ type TaskAttributeBase = {
529
529
 
530
530
  type TaskAttribute = TaskAttributeBase & (
531
531
  | { type: "text"; value: string }
532
- | { type: "contact"; contact: Contact }
532
+ | { type: "contact"; party: Contact }
533
533
  | { type: "timestamp"; at: IsoTimestamp }
534
534
  );
535
535
 
@@ -596,7 +596,7 @@ type Task<C extends Channel = Channel> = {
596
596
  taskType: string;
597
597
  capabilities: TaskCapabilities<C>;
598
598
  browsers: TaskBrowser[];
599
- contact?: Contact;
599
+ party?: Contact;
600
600
  phase: TaskPhase;
601
601
  reference?: string;
602
602
  attributes?: TaskAttribute[];
@@ -917,7 +917,7 @@ type OpenMediaRequest = {
917
917
  ```ts
918
918
  type SummaryMetric = { id: string; label: string; value: string };
919
919
 
920
- type ProviderSummary = {
920
+ type QueueSummary = {
921
921
  title: string;
922
922
  subtitle?: string;
923
923
  waitingCount: number;
@@ -944,7 +944,7 @@ type ProviderEvent =
944
944
  | { type: "task-media-ended"; taskId: TaskId }
945
945
  | { type: "task-ended"; taskId: TaskId; outcome: TaskOutcome }
946
946
  | { type: "announcement"; text: string; html?: string; announcedAt: IsoTimestamp; expiresAt?: IsoTimestamp }
947
- | { type: "provider-summary"; summary: ProviderSummary }
947
+ | { type: "queue-summary"; summary: QueueSummary }
948
948
  | { type: "team-updated"; team: TeamRoster }
949
949
  | { type: "contacts-updated"; contacts: Contact[] }
950
950
  | { type: "calendar-updated"; scheduledActivities: ScheduledActivity[] };
@@ -1526,7 +1526,7 @@ changes.
1526
1526
  | `title` | Required agent-facing activity title. |
1527
1527
  | `startsAt` | Required RFC-3339 start time with an explicit timezone. |
1528
1528
  | `endsAt` | Optional RFC-3339 end time with an explicit timezone. |
1529
- | `contact` | Optional related `Contact`. |
1529
+ | `party` | The person the activity reaches — a callback's customer — as a `Contact`. Optional. |
1530
1530
  | `attributes` | Optional ordered `Attribute` entries. Keys must be non-empty. |
1531
1531
 
1532
1532
  **There is no `type` field**, for the reason there is none on `Contact`: an open category is
@@ -2071,7 +2071,7 @@ time. Runtime conformance checks also require the task channel to match its prov
2071
2071
  | `taskType` | Required provider-defined source or category of work, such as a voice `Queue Name`, `Mailbox Folder`, `Chat Source`, `Support`, `Billing`, or `Returns`. |
2072
2072
  | `capabilities` | Controls and workspace features available for this specific task. |
2073
2073
  | `browsers` | Named browser definitions for the task workspace: at least one when the task declares the `browsers` capability, empty when it does not. |
2074
- | `contact` | Optional `Contact` for the person or entity on this task. Often a name and one address; a withheld caller ID may leave nothing to send at all. |
2074
+ | `party` | The person or entity on the other end of this task, as a `Contact`: often a name and one address; a withheld caller ID may leave nothing to send at all. Optional. The party is who the task is *with*; `contacts` is the directory. |
2075
2075
  | `phase` | Current canonical task phase: `pending`, `confirmed`, `preparing`, `in-progress`, `paused`, or `completing`. |
2076
2076
  | `media` | Voice only. The task's real-time audio as the provider holds it: `started` while audio is attached, `ended` once it ended, omitted while none is. The provider's word — see **`task-media-started`**. |
2077
2077
  | `reference` | Optional agent-facing reference such as a case, call, conversation, ticket, or message number. It is distinct from the protocol `id`. |
@@ -2091,7 +2091,7 @@ const attributes: TaskAttribute[] = [
2091
2091
  key: "related-contact",
2092
2092
  label: "Related contact",
2093
2093
  type: "contact",
2094
- contact: { name: "Asha Rao", number: "+919876543210" },
2094
+ party: { name: "Asha Rao", number: "+919876543210" },
2095
2095
  },
2096
2096
  {
2097
2097
  key: "answered",
@@ -2329,14 +2329,14 @@ Each `TaskBrowser` defines one named browser in the task workspace.
2329
2329
  | `name` | Agent-facing tab label, unique within the task, and an input to schemes containing `TAB_NAME`. |
2330
2330
  | `purpose` | Human-readable explanation of the browser's role. |
2331
2331
  | `url` | Initial URL. Must use `http:` or `https:`; see below. Later navigation comes from Chromium. |
2332
- | `reuse` | Required. `false` creates a task-specific browser session. |
2333
- | `isolationScheme` | **Required when `reuse` is `true`**, and rejected when it is `false`. There is no default: see below. |
2332
+ | `sharedSession` | Required. `false` creates a task-specific browser session. |
2333
+ | `isolationScheme` | **Required when `sharedSession` is `true`**, and rejected when it is `false`. There is no default: see below. |
2334
2334
  | `urlVisibility` | What the agent sees of this tab's URL in Omni's chrome: `hidden`, `domain`, or `full`. Omitted, the URL shows as any browser's does; a provider says `hidden` where the URL carries what the agent may not read — a caller's number, a CRM token. Per browser, on the provider's word; Omni honours it tab by tab. |
2335
2335
 
2336
- ##### Choosing a reuse scheme
2336
+ ##### Choosing an isolation scheme
2337
2337
 
2338
2338
  Every scheme is supported and the provider picks the one its deployment needs. There is no
2339
- default, and a `reuse: true` browser that declares none is invalid — the type will not compile
2339
+ default, and a `sharedSession: true` browser that declares none is invalid — the type will not compile
2340
2340
  it and `validateSnapshot` reports `task.browser.isolationScheme.required`.
2341
2341
 
2342
2342
  That is deliberate. Sharing a signed-in session decides **who else may see those credentials**,
@@ -2358,7 +2358,7 @@ page rather than following a disallowed URL, and `isAllowedBrowserUrl()` is the
2358
2358
 
2359
2359
  ##### Reuse and isolation
2360
2360
 
2361
- With `reuse: true`, definitions producing the same isolation key share one **storage profile**:
2361
+ With `sharedSession: true`, definitions producing the same isolation key share one **storage profile**:
2362
2362
  cookies, local storage, session storage, permissions, and cached credentials. Different keys are
2363
2363
  isolated from one another.
2364
2364
 
@@ -2382,7 +2382,7 @@ browsers: [
2382
2382
  name: "CRM",
2383
2383
  purpose: "Contact record",
2384
2384
  url: "https://crm.example.com/contact/42",
2385
- reuse: true,
2385
+ sharedSession: true,
2386
2386
  isolationScheme: BROWSER_ISOLATION_SCHEMES.PROVIDER_NAME__TASK_TYPE_NAME__TAB_NAME,
2387
2387
  }
2388
2388
  ]
@@ -3580,7 +3580,7 @@ Publishes an agent-facing message. `text` is always required and is the accessib
3580
3580
  Optional HTML is sanitized by Omni. `announcedAt` and optional `expiresAt` are RFC-3339 times with
3581
3581
  explicit timezones.
3582
3582
 
3583
- ### `provider-summary`
3583
+ ### `queue-summary`
3584
3584
 
3585
3585
  Publishes the provider's current dashboard contribution. Omni combines only the latest summary from
3586
3586
  each connected provider.
@@ -3742,7 +3742,7 @@ cannot be established from TypeScript structure alone.
3742
3742
  | `assertBreakBeginsAfterTask(steps)` | A break asked for on a task is committed as `starting-after-task` while work remains and reaches `in-effect` only once nothing is outstanding — never beside a task, never later than the step that has none. |
3743
3743
  | `assertDeniedAndRetriedBreak(states)` | A denial transitions directly to `not-requested`; a later request can still be granted. |
3744
3744
  | `assertWrapTimeout(task, mediaEndedAt, deadline, toleranceMs?)` | The wrap deadline equals media end plus the task allowance, within a tolerance that defaults to 1000ms; a task with no allowance has no deadline, and one observed is the violation. |
3745
- | `assertBrowserIsolationAndReuse(left, right, expected)` | Browser reuse follows only the declared isolation scheme. |
3745
+ | `assertBrowserSessionIsolation(left, right, expected)` | Browser reuse follows only the declared isolation scheme. |
3746
3746
  | `assertNoBrowserSessionKeyCollisions(scenarios)` | No two distinct scenarios derive the same session key. Feed it adversarial names. |
3747
3747
 
3748
3748
  Adapters should run the relevant scenarios against deterministic test state before publishing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",