@xema/omni-protocol 0.1.36 → 0.1.38

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
@@ -173,8 +173,8 @@ export interface TeamCapabilities {
173
173
  * `omni.capability-not-enabled`. Requires `executeTeamBreak`.
174
174
  */
175
175
  breakControl?: true;
176
- /** This lead may join a member's call on request. Requires `executeTeamConsult`. */
177
- consultControl?: true;
176
+ /** This lead may join a member's call on request. Requires `executeTeamLeadAssist`. */
177
+ leadAssistControl?: true;
178
178
  /** This lead sets the team's policy per capability -- on, off, or the agent's -- within what the queue allows. Requires `executeTeamPolicy`. */
179
179
  policyControl?: true;
180
180
  }
@@ -442,8 +442,8 @@ export type TaskCapabilities<C extends Channel = Channel> = C extends "voice" ?
442
442
  blindTransfer?: Lockable<true | DestinationDirectory>;
443
443
  /** Park the customer and call a destination first; then `complete` or `cancel`. */
444
444
  consultTransfer?: Lockable<true | DestinationDirectory>;
445
- /** Ask a lead to join this call, with a note. The lead's decision arrives on `Task.lead`. */
446
- consultLead?: Lockable<true>;
445
+ /** Ask a lead to join this call, with a note. The lead's decision arrives on `Task.leadAssist`. */
446
+ leadAssist?: Lockable<true>;
447
447
  conference?: Lockable<true | DestinationDirectory>;
448
448
  recording?: Lockable<true>;
449
449
  } : C extends "chat" ? SharedTaskCapabilities & {
@@ -608,7 +608,7 @@ export type OnCall = {
608
608
  * The agent's request for a lead, from asking until the lead leaves or the request ends.
609
609
  * `requested` while nobody has joined; `joined`, with `leadId`, once somebody has.
610
610
  */
611
- export interface TaskLead {
611
+ export interface TaskLeadAssist {
612
612
  stage: "requested" | "joined";
613
613
  leadId?: UserId;
614
614
  note?: string;
@@ -616,7 +616,7 @@ export interface TaskLead {
616
616
  }
617
617
  /**
618
618
  * On the lead's own task for a call they joined: which member asked, with their note. Its
619
- * presence is what makes `lead` `take-over` and `leave` issuable.
619
+ * presence is what makes `lead-assist` `take-over` and `leave` issuable.
620
620
  */
621
621
  export interface TaskAssisting {
622
622
  memberId: UserId;
@@ -691,12 +691,12 @@ export type Task<C extends Channel = Channel> = {
691
691
  handlingHistory?: TaskHandlingHistory;
692
692
  } & TaskCompletion & (C extends "voice" ? {
693
693
  onCall?: OnCall[];
694
- lead?: TaskLead;
694
+ leadAssist?: TaskLeadAssist;
695
695
  assisting?: TaskAssisting;
696
696
  media?: TaskMediaState;
697
697
  } : {
698
698
  onCall?: never;
699
- lead?: never;
699
+ leadAssist?: never;
700
700
  assisting?: never;
701
701
  media?: never;
702
702
  });
@@ -730,7 +730,7 @@ export type TaskOutcome = {
730
730
  failure: ProtocolFailure;
731
731
  };
732
732
  export declare const TASK_COMMAND_NAMES: {
733
- readonly voice: readonly ["answer", "decline", "start-call", "mute", "hold", "resume", "disconnect", "callback", "transfer", "lead", "conference", "recording", "complete"];
733
+ readonly voice: readonly ["answer", "decline", "start-call", "mute", "hold", "resume", "disconnect", "callback", "transfer", "lead-assist", "conference", "recording", "complete"];
734
734
  readonly chat: readonly ["accept", "reject", "pause", "resume", "complete"];
735
735
  readonly email: readonly ["accept", "reject", "complete"];
736
736
  };
@@ -784,25 +784,25 @@ export type VoiceTaskCommand = {
784
784
  type: "transfer";
785
785
  action: "cancel";
786
786
  }
787
- /** Ask a lead to join, with a note. Gated by `consultLead`. */
787
+ /** Ask a lead to join, with a note. Gated by `leadAssist`. */
788
788
  | {
789
- type: "lead";
789
+ type: "lead-assist";
790
790
  action: "request";
791
791
  note?: string;
792
792
  }
793
- /** Withdraw a standing request. Needs `Task.lead` with status `requested`. */
793
+ /** Withdraw a standing request. Needs `Task.leadAssist` with status `requested`. */
794
794
  | {
795
- type: "lead";
795
+ type: "lead-assist";
796
796
  action: "cancel";
797
797
  }
798
798
  /** The lead keeps the customer; the agent's task ends `transferred`. Needs `Task.assisting`. */
799
799
  | {
800
- type: "lead";
800
+ type: "lead-assist";
801
801
  action: "take-over";
802
802
  }
803
803
  /** The lead drops; the agent continues. The lead's task ends `left`. Needs `Task.assisting`. */
804
804
  | {
805
- type: "lead";
805
+ type: "lead-assist";
806
806
  action: "leave";
807
807
  }
808
808
  /** Dial `destination` into the call. Gated by `conference`. */
@@ -999,7 +999,7 @@ export interface LeadRequest {
999
999
  */
1000
1000
  export interface TeamRoster {
1001
1001
  members: TeamMember[];
1002
- /** Omitted when the login lacks `team.consultControl`; `[]` when nobody is asking. */
1002
+ /** Omitted when the login lacks `team.leadAssistControl`; `[]` when nobody is asking. */
1003
1003
  requests?: LeadRequest[];
1004
1004
  /** The team's policy per capability, as it stands. Present exactly when the login declares `team.policyControl`. */
1005
1005
  policies?: TeamPolicies;
@@ -1021,7 +1021,7 @@ export type TeamPolicyCommand = {
1021
1021
  export interface TeamPolicyCommandRequest {
1022
1022
  command: TeamPolicyCommand;
1023
1023
  }
1024
- export type TeamConsultCommand = {
1024
+ export type TeamLeadAssistCommand = {
1025
1025
  type: "join";
1026
1026
  requestId: string;
1027
1027
  } | {
@@ -1029,8 +1029,8 @@ export type TeamConsultCommand = {
1029
1029
  requestId: string;
1030
1030
  reason?: string;
1031
1031
  };
1032
- export interface TeamConsultCommandRequest {
1033
- command: TeamConsultCommand;
1032
+ export interface TeamLeadAssistCommandRequest {
1033
+ command: TeamLeadAssistCommand;
1034
1034
  }
1035
1035
  export type TeamBreakCommand = {
1036
1036
  type: "decide";
@@ -1294,8 +1294,8 @@ export interface Connection<C extends Channel = Channel> {
1294
1294
  endBreak?(): Promise<BreakEndResult>;
1295
1295
  /** Required when the login declares `capabilities.team.breakControl`. */
1296
1296
  executeTeamBreak?(request: TeamBreakCommandRequest): Promise<TeamCommandResult>;
1297
- /** Required when the login declares `capabilities.team.consultControl`. */
1298
- executeTeamConsult?(request: TeamConsultCommandRequest): Promise<TeamCommandResult>;
1297
+ /** Required when the login declares `capabilities.team.leadAssistControl`. */
1298
+ executeTeamLeadAssist?(request: TeamLeadAssistCommandRequest): Promise<TeamCommandResult>;
1299
1299
  /** Required when the login declares `capabilities.team.policyControl`. */
1300
1300
  executeTeamPolicy?(request: TeamPolicyCommandRequest): Promise<TeamCommandResult>;
1301
1301
  /** Required of every voice adapter: all voice audio lands in Omni. */
package/dist/index.js CHANGED
@@ -71,7 +71,7 @@ export function effectiveLevels(declared) {
71
71
  // ---------------------------------------------------------------------------
72
72
  export const TASK_COMMAND_NAMES = {
73
73
  voice: ["answer", "decline", "start-call", "mute", "hold", "resume", "disconnect",
74
- "callback", "transfer", "lead", "conference", "recording", "complete"],
74
+ "callback", "transfer", "lead-assist", "conference", "recording", "complete"],
75
75
  chat: ["accept", "reject", "pause", "resume", "complete"],
76
76
  email: ["accept", "reject", "complete"],
77
77
  };
@@ -170,7 +170,7 @@ export function sameCapabilities(a, b) {
170
170
  return a.breaks === b.breaks &&
171
171
  (a.team === undefined) === (b.team === undefined) &&
172
172
  a.team?.breakControl === b.team?.breakControl &&
173
- a.team?.consultControl === b.team?.consultControl;
173
+ a.team?.leadAssistControl === b.team?.leadAssistControl;
174
174
  }
175
175
  /**
176
176
  * The storage-profile key a reusing browser shares, or `undefined` where it shares nothing.
package/dist/testing.d.ts CHANGED
@@ -7,7 +7,7 @@ export { ProtocolConformanceError, assertNoViolations, type ProtocolViolation }
7
7
  * rules -- each optional part of a task, each optional part of the break state and roster, each
8
8
  * declared contribution, and each event type.
9
9
  */
10
- declare const STATE_SUBJECTS: readonly ["tasks", "task.browsers", "task.attributes", "task.handlingHistory", "task.onCall", "task.lead", "task.assisting", "task.media", "task.acceptance", "task.dispositions", "task.destinations", "task.custom", "task.locked", "break.reasons", "break.imposed", "team.members", "team.requests", "contacts", "scheduledActivities", "team.policies"];
10
+ declare const STATE_SUBJECTS: readonly ["tasks", "task.browsers", "task.attributes", "task.handlingHistory", "task.onCall", "task.leadAssist", "task.assisting", "task.media", "task.acceptance", "task.dispositions", "task.destinations", "task.custom", "task.locked", "break.reasons", "break.imposed", "team.members", "team.requests", "contacts", "scheduledActivities", "team.policies"];
11
11
  export type ContractSubject = (typeof STATE_SUBJECTS)[number] | `event.${ProviderEvent["type"]}`;
12
12
  export interface AdapterContractResult {
13
13
  events: ProviderEventEnvelope[];
package/dist/testing.js CHANGED
@@ -13,7 +13,7 @@ const STATE_SUBJECTS = [
13
13
  "task.attributes",
14
14
  "task.handlingHistory",
15
15
  "task.onCall",
16
- "task.lead",
16
+ "task.leadAssist",
17
17
  "task.assisting",
18
18
  "task.media",
19
19
  "task.acceptance",
@@ -56,8 +56,8 @@ function observeTask(value, seen) {
56
56
  seen.add("task.handlingHistory");
57
57
  if (some(value.onCall))
58
58
  seen.add("task.onCall");
59
- if (value.lead !== undefined)
60
- seen.add("task.lead");
59
+ if (value.leadAssist !== undefined)
60
+ seen.add("task.leadAssist");
61
61
  if (value.assisting !== undefined)
62
62
  seen.add("task.assisting");
63
63
  if (value.media !== undefined)
@@ -223,8 +223,8 @@ export async function exerciseAdapter(adapter, context, options = {}) {
223
223
  }
224
224
  if (capabilities.team?.breakControl === true)
225
225
  requireMethod(on, "executeTeamBreak", "the login declares capabilities.team.breakControl");
226
- if (capabilities.team?.consultControl === true)
227
- requireMethod(on, "executeTeamConsult", "the login declares capabilities.team.consultControl");
226
+ if (capabilities.team?.leadAssistControl === true)
227
+ requireMethod(on, "executeTeamLeadAssist", "the login declares capabilities.team.leadAssistControl");
228
228
  if (capabilities.team?.policyControl === true)
229
229
  requireMethod(on, "executeTeamPolicy", "the login declares capabilities.team.policyControl");
230
230
  if (some(capabilities.preferences))
@@ -404,7 +404,7 @@ function publishesUserIds(snapshot) {
404
404
  }
405
405
  const teamNamesUsers = (team) => isRecord(team) && (some(team.members) || some(team.requests));
406
406
  const taskNamesUsers = (task) => isRecord(task) && ((isRecord(task.handlingHistory) && Array.isArray(task.handlingHistory.steps) && task.handlingHistory.steps.some(step => isRecord(step) && step.by !== undefined)) ||
407
- (isRecord(task.lead) && task.lead.leadId !== undefined) ||
407
+ (isRecord(task.leadAssist) && task.leadAssist.leadId !== undefined) ||
408
408
  isRecord(task.assisting));
409
409
  /** Mute is the leg the host performs, so a task that allows it obliges the provider to take the record. */
410
410
  const taskDeclaresMute = (task) => isRecord(task) && isRecord(task.capabilities) && task.capabilities.mute !== undefined;
@@ -517,7 +517,7 @@ export function assertCapabilityWithdrawal(states, snapshot, manifest) {
517
517
  const withdrawn = (before.breaks === true && after.breaks !== true) ||
518
518
  (before.team !== undefined && after.team === undefined) ||
519
519
  (before.team?.breakControl === true && after.team?.breakControl !== true) ||
520
- (before.team?.consultControl === true && after.team?.consultControl !== true);
520
+ (before.team?.leadAssistControl === true && after.team?.leadAssistControl !== true);
521
521
  if (!withdrawn) {
522
522
  throw new Error("Capability withdrawal must end with at least one capability the first login declared withdrawn");
523
523
  }
@@ -619,7 +619,7 @@ export class TaskStream {
619
619
  tasks = new Map();
620
620
  // Every dial the stream can place an outcome against: one the host said it placed, or one a
621
621
  // task carried on `onCall` or in its record -- which is how a dial made before a transfer is known
622
- // to whoever holds the task now. `ended` once its outcome arrived, since an outcome comes once.
622
+ // to whoever holds the task now. `answered` or `ended` once its outcome arrived, since it comes once.
623
623
  dials = new Map();
624
624
  /** The host placed a dial: its outcome, whenever it arrives, is one the stream expects. */
625
625
  dialled(dialId) {
@@ -640,7 +640,16 @@ export class TaskStream {
640
640
  }
641
641
  static stated(task) {
642
642
  const media = isRecord(task) && (task.media === "started" || task.media === "ended") ? task.media : "none";
643
- return { phase: String(isRecord(task) ? task.phase : undefined), media };
643
+ // The stage of every dialled entry the room names by its dial, so an update can be held to the
644
+ // outcome that moves it.
645
+ const stages = new Map();
646
+ if (isRecord(task) && Array.isArray(task.onCall)) {
647
+ for (const entry of task.onCall) {
648
+ if (isRecord(entry) && typeof entry.dialId === "string" && typeof entry.stage === "string")
649
+ stages.set(entry.dialId, entry.stage);
650
+ }
651
+ }
652
+ return { phase: String(isRecord(task) ? task.phase : undefined), media, stages };
644
653
  }
645
654
  /** Replaces what is known with a snapshot's tasks, as a snapshot replaces Omni's state. */
646
655
  seed(snapshot) {
@@ -696,6 +705,14 @@ export class TaskStream {
696
705
  if (known.media !== next.media && !(known.media === "ended" && next.media === "none")) {
697
706
  refuse("stream.taskUpdated.media", `${at}.task.media`, `a task-updated re-states media, it does not move it: ${id} held ${known.media} and the update says ${next.media}; audio arrives on task-media-started and ends on task-media-ended`);
698
707
  }
708
+ // The same pairing for a dialled entry: the dial-outcome is the transition, the stage is
709
+ // the state, and the outcome comes first. An update that joins an entry on its own is
710
+ // moving what only an answered outcome moves.
711
+ for (const [dialId, stage] of next.stages) {
712
+ if (stage === "joined" && known.stages.get(dialId) === "ringing" && this.dials.get(dialId) !== "answered") {
713
+ refuse("stream.taskUpdated.stage", `${at}.task.onCall`, `a task-updated re-states a stage, it does not move it: ${dialId} was ringing and the update says joined before any answered dial-outcome for it`);
714
+ }
715
+ }
699
716
  this.tasks.set(id, next);
700
717
  }
701
718
  this.noteDials(event.task);
@@ -746,10 +763,10 @@ export class TaskStream {
746
763
  if (dial === undefined) {
747
764
  refuse("stream.dialOutcome.unknown", `${at}.dialId`, `${event.dialId} is not a dial the host placed or a task carried: an outcome ends a dial somebody made`);
748
765
  }
749
- else if (dial === "ended") {
766
+ else if (dial !== "placed") {
750
767
  refuse("stream.dialOutcome.duplicate", `${at}.dialId`, `${event.dialId} already has its outcome; a dial ends once`);
751
768
  }
752
- this.dials.set(event.dialId, "ended");
769
+ this.dials.set(event.dialId, event.outcome === "answered" ? "answered" : "ended");
753
770
  break;
754
771
  }
755
772
  default:
@@ -33,7 +33,7 @@ export interface ReaderContext {
33
33
  self?: UserId;
34
34
  /**
35
35
  * The login's `AuthenticationState.capabilities`. The login is the permission: a lead's
36
- * snapshot carries a roster, nobody else's does, and `requests` need `team.consultControl`.
36
+ * snapshot carries a roster, nobody else's does, and `requests` need `team.leadAssistControl`.
37
37
  */
38
38
  capabilities?: UserCapabilities;
39
39
  /** The level ids in force. Filled from the manifest by `validateSnapshot` and `validateEventEnvelope`; the defaults otherwise. */
@@ -59,7 +59,7 @@ export declare function validateHostGuarantees(guarantees: unknown, path?: strin
59
59
  export declare function validateHandlingReport(report: unknown, path?: string, manifest?: unknown): ProtocolViolation[];
60
60
  export declare function validateHostReport(report: unknown, path?: string): ProtocolViolation[];
61
61
  /** The connection methods whose results `validateResult` knows. */
62
- export type ResultMethod = "execute" | "dial" | "setCapacity" | "requestBreak" | "commitBreak" | "cancelBreak" | "endBreak" | "executeTeamBreak" | "executeTeamConsult" | "openMedia" | "setPreference" | "recordStep" | "executeTeamPolicy";
62
+ export type ResultMethod = "execute" | "dial" | "setCapacity" | "requestBreak" | "commitBreak" | "cancelBreak" | "endBreak" | "executeTeamBreak" | "executeTeamLeadAssist" | "openMedia" | "setPreference" | "recordStep" | "executeTeamPolicy";
63
63
  /**
64
64
  * Validates what a connection method answered. A result is untrusted for the same reason a
65
65
  * snapshot is: it comes from an adapter that may be compiled against another version, and Omni
@@ -84,7 +84,7 @@ const MEMBER_BREAKS = membersOf({
84
84
  const OFFERABLE_PHASES = membersOf({
85
85
  pending: true, confirmed: true, preparing: true,
86
86
  });
87
- const TEAM_CAPABILITIES = membersOf({ breakControl: true, consultControl: true, policyControl: true });
87
+ const TEAM_CAPABILITIES = membersOf({ breakControl: true, leadAssistControl: true, policyControl: true });
88
88
  const COMPLETED_BY = membersOf({ agent: true, provider: true });
89
89
  const EXPIRABLE_PHASES = membersOf({
90
90
  pending: true, confirmed: true, preparing: true,
@@ -94,7 +94,7 @@ const ISOLATION_SCHEME_VALUES = Object.values(BROWSER_ISOLATION_SCHEMES);
94
94
  const TASK_CAPABILITIES = {
95
95
  voice: membersOf({
96
96
  browsers: true, dispositions: true, custom: true, decline: true, mute: true, hold: true,
97
- agentDisconnect: true, callback: true, blindTransfer: true, consultTransfer: true, consultLead: true, conference: true, recording: true,
97
+ agentDisconnect: true, callback: true, blindTransfer: true, consultTransfer: true, leadAssist: true, conference: true, recording: true,
98
98
  }),
99
99
  chat: membersOf({ browsers: true, dispositions: true, custom: true, reject: true, hold: true }),
100
100
  email: membersOf({ browsers: true, dispositions: true, custom: true, reject: true }),
@@ -799,26 +799,26 @@ function validateOnCall(value, channel, path, into) {
799
799
  }
800
800
  const LEAD_STAGES = membersOf({ requested: true, joined: true });
801
801
  /** The agent's request for a lead. Voice only; `joined` names the lead, `requested` cannot. */
802
- function validateLead(value, channel, path, into) {
802
+ function validateLeadAssist(value, channel, path, into) {
803
803
  if (value === undefined)
804
804
  return;
805
- if (!into.require(channel === "voice", "task.lead.channel", path, `a ${channel} task cannot carry a lead request`))
805
+ if (!into.require(channel === "voice", "task.leadAssist.channel", path, `a ${channel} task cannot carry a lead request`))
806
806
  return;
807
807
  if (!isPlainObject(value)) {
808
- into.add("task.lead.shape", path, "lead must be an object when present");
808
+ into.add("task.leadAssist.shape", path, "lead must be an object when present");
809
809
  return;
810
810
  }
811
- if (into.oneOf(value.stage, LEAD_STAGES, "task.lead.stage", `${path}.stage`)) {
811
+ if (into.oneOf(value.stage, LEAD_STAGES, "task.leadAssist.stage", `${path}.stage`)) {
812
812
  if (value.stage === "joined") {
813
- into.require(isUserId(value.leadId), "task.lead.leadId", `${path}.leadId`, "a joined lead is named by their user id");
813
+ into.require(isUserId(value.leadId), "task.leadAssist.leadId", `${path}.leadId`, "a joined lead is named by their user id");
814
814
  }
815
815
  else {
816
- into.require(value.leadId === undefined, "task.lead.leadId.unexpected", `${path}.leadId`, "nobody has joined a requested lead, so there is no lead to name");
816
+ into.require(value.leadId === undefined, "task.leadAssist.leadId.unexpected", `${path}.leadId`, "nobody has joined a requested lead, so there is no lead to name");
817
817
  }
818
818
  }
819
819
  if (value.note !== undefined)
820
- into.filled(value.note, "task.lead.note", `${path}.note`, "a note must not be empty when present");
821
- into.timestamp(value.since, "task.lead.since", `${path}.since`);
820
+ into.filled(value.note, "task.leadAssist.note", `${path}.note`, "a note must not be empty when present");
821
+ into.timestamp(value.since, "task.leadAssist.since", `${path}.since`);
822
822
  }
823
823
  /** The lead's own task for a call they joined. Voice only. */
824
824
  function validateAssisting(value, channel, path, into) {
@@ -886,7 +886,7 @@ function validateTaskInto(task, context, path, into) {
886
886
  validateHandlingHistory(task.handlingHistory, `${path}.handlingHistory`, into);
887
887
  validateOnCall(task.onCall, context.channel, `${path}.onCall`, into);
888
888
  validateTaskMedia(task.media, context.channel, `${path}.media`, into);
889
- validateLead(task.lead, context.channel, `${path}.lead`, into);
889
+ validateLeadAssist(task.leadAssist, context.channel, `${path}.leadAssist`, into);
890
890
  validateAssisting(task.assisting, context.channel, `${path}.assisting`, into);
891
891
  const capabilities = task.capabilities;
892
892
  if (!isPlainObject(capabilities)) {
@@ -1055,15 +1055,15 @@ function validateTeamRosterInto(roster, path, context, into) {
1055
1055
  if (roster.requests === undefined) {
1056
1056
  // `[]` says nobody is asking; omission says the lead may not be asked. A login that may be
1057
1057
  // asked therefore always carries the list.
1058
- if (context.capabilities?.team?.consultControl === true) {
1059
- into.add("team.requests.required", `${path}.requests`, "the login declares team.consultControl, so the roster carries requests: [] when nobody is asking");
1058
+ if (context.capabilities?.team?.leadAssistControl === true) {
1059
+ into.add("team.requests.required", `${path}.requests`, "the login declares team.leadAssistControl, so the roster carries requests: [] when nobody is asking");
1060
1060
  }
1061
1061
  }
1062
1062
  else {
1063
1063
  // Requests are what a lead acts on, so a lead who may not act has no business receiving them.
1064
1064
  // Whether they may is on the login, so the check needs the login in hand.
1065
1065
  if (context.capabilities !== undefined) {
1066
- into.require(context.capabilities.team?.consultControl === true, "team.requests.capability", `${path}.requests`, "requests require team.consultControl on the login: a lead who may not join has nothing to decide");
1066
+ into.require(context.capabilities.team?.leadAssistControl === true, "team.requests.capability", `${path}.requests`, "requests require team.leadAssistControl on the login: a lead who may not join has nothing to decide");
1067
1067
  }
1068
1068
  if (!Array.isArray(roster.requests)) {
1069
1069
  into.add("team.requests.shape", `${path}.requests`, "requests must be an array when present");
@@ -1621,7 +1621,7 @@ const RESULT_STATUSES = {
1621
1621
  cancelBreak: { success: "cancelled", failure: "failed" },
1622
1622
  endBreak: { success: "ended", failure: "failed" },
1623
1623
  executeTeamBreak: { success: "applied", failure: "failed" },
1624
- executeTeamConsult: { success: "applied", failure: "failed" },
1624
+ executeTeamLeadAssist: { success: "applied", failure: "failed" },
1625
1625
  openMedia: { success: "opened", failure: "unavailable" },
1626
1626
  setPreference: { success: "applied", failure: "failed" },
1627
1627
  recordStep: { success: "recorded", failure: "failed" },
package/guide.md CHANGED
@@ -231,7 +231,7 @@ type AuthenticationContext = {
231
231
 
232
232
  type TeamCapabilities = {
233
233
  breakControl?: true;
234
- consultControl?: true;
234
+ leadAssistControl?: true;
235
235
  policyControl?: true;
236
236
  };
237
237
 
@@ -479,7 +479,7 @@ type TaskCapabilities<C extends Channel = Channel> =
479
479
  callback?: Lockable<true>;
480
480
  blindTransfer?: Lockable<true | DestinationDirectory>;
481
481
  consultTransfer?: Lockable<true | DestinationDirectory>;
482
- consultLead?: Lockable<true>;
482
+ leadAssist?: Lockable<true>;
483
483
  conference?: Lockable<true | DestinationDirectory>;
484
484
  recording?: Lockable<true>;
485
485
  }
@@ -597,7 +597,7 @@ type OnCall = { since: IsoTimestamp; held?: true } & (
597
597
  | { role: "consulted" | "conferenced"; destination: string; stage: OnCallStage; dialId?: DialId; label?: string }
598
598
  );
599
599
 
600
- type TaskLead = {
600
+ type TaskLeadAssist = {
601
601
  stage: "requested" | "joined";
602
602
  leadId?: UserId;
603
603
  note?: string;
@@ -641,8 +641,8 @@ type Task<C extends Channel = Channel> = {
641
641
  handlingHistory?: TaskHandlingHistory;
642
642
  } & TaskCompletion & (
643
643
  C extends "voice"
644
- ? { onCall?: OnCall[]; lead?: TaskLead; assisting?: TaskAssisting; media?: TaskMediaState }
645
- : { onCall?: never; lead?: never; assisting?: never; media?: never }
644
+ ? { onCall?: OnCall[]; leadAssist?: TaskLeadAssist; assisting?: TaskAssisting; media?: TaskMediaState }
645
+ : { onCall?: never; leadAssist?: never; assisting?: never; media?: never }
646
646
  );
647
647
 
648
648
  type AcceptanceMode =
@@ -673,7 +673,7 @@ const TASK_COMMAND_NAMES = {
673
673
  "disconnect",
674
674
  "callback",
675
675
  "transfer",
676
- "lead",
676
+ "lead-assist",
677
677
  "conference",
678
678
  "recording",
679
679
  "complete",
@@ -700,10 +700,10 @@ type VoiceTaskCommand =
700
700
  | { type: "transfer"; action: "consult"; dialId: DialId; destination: string }
701
701
  | { type: "transfer"; action: "complete" }
702
702
  | { type: "transfer"; action: "cancel" }
703
- | { type: "lead"; action: "request"; note?: string }
704
- | { type: "lead"; action: "cancel" }
705
- | { type: "lead"; action: "take-over" }
706
- | { type: "lead"; action: "leave" }
703
+ | { type: "lead-assist"; action: "request"; note?: string }
704
+ | { type: "lead-assist"; action: "cancel" }
705
+ | { type: "lead-assist"; action: "take-over" }
706
+ | { type: "lead-assist"; action: "leave" }
707
707
  | { type: "conference"; action: "add"; dialId: DialId; destination: string }
708
708
  | { type: "conference"; action: "remove"; destination: string }
709
709
  | { type: "recording"; action: "start" | "pause" | "resume" | "stop" }
@@ -910,7 +910,7 @@ type TeamPolicyCommandRequest = {
910
910
  command: TeamPolicyCommand;
911
911
  };
912
912
 
913
- type TeamConsultCommand =
913
+ type TeamLeadAssistCommand =
914
914
  | { type: "join"; requestId: string }
915
915
  | { type: "decline"; requestId: string; reason?: string };
916
916
 
@@ -924,8 +924,8 @@ type TeamBreakCommandRequest = {
924
924
  command: TeamBreakCommand;
925
925
  };
926
926
 
927
- type TeamConsultCommandRequest = {
928
- command: TeamConsultCommand;
927
+ type TeamLeadAssistCommandRequest = {
928
+ command: TeamLeadAssistCommand;
929
929
  };
930
930
 
931
931
  type TeamCommandResult =
@@ -1021,7 +1021,7 @@ type Connection<C extends Channel = Channel> = {
1021
1021
  endBreak?(): Promise<BreakEndResult>;
1022
1022
 
1023
1023
  executeTeamBreak?(request: TeamBreakCommandRequest): Promise<TeamCommandResult>;
1024
- executeTeamConsult?(request: TeamConsultCommandRequest): Promise<TeamCommandResult>;
1024
+ executeTeamLeadAssist?(request: TeamLeadAssistCommandRequest): Promise<TeamCommandResult>;
1025
1025
  openMedia?(request: OpenMediaRequest): Promise<OpenMediaResult>;
1026
1026
  setPreference?(request: SetPreferenceRequest): Promise<PreferenceResult>;
1027
1027
  recordStep?(report: HandlingReport): Promise<HandlingReportResult>;
@@ -1731,7 +1731,7 @@ them from what arrives later.
1731
1731
  | `breaks` | This login may request a break. Requires the four break methods on the connection. |
1732
1732
  | `team` | This login leads a team. The provider publishes a `TeamRoster` to it on every snapshot — `[]` when nobody is in it — and to nobody else. |
1733
1733
  | `team.breakControl` | This lead may act on their team's breaks through `executeTeamBreak` — place, release, decide, set policy — as far as the provider supports; a command it lacks answers `omni.capability-not-enabled`. Omni asks for a decision only against a member whose `break` is `awaiting-decision`, so a provider that grants on request is never asked to decide. Requires `executeTeamBreak`. |
1734
- | `team.consultControl` | This lead may join a member's call on request. Requires `executeTeamConsult`. |
1734
+ | `team.leadAssistControl` | This lead may join a member's call on request. Requires `executeTeamLeadAssist`. |
1735
1735
  | `team.policyControl` | This lead sets the team's policy per capability — on, off, or the person's — within what the queue allows. Requires `executeTeamPolicy`; the roster carries `policies`. |
1736
1736
  | `preferences` | What the team left to this person, with where each stands and who set it. Omitted when nothing was. Requires `setPreference`. See **Who decides what an agent may do**. |
1737
1737
 
@@ -1927,7 +1927,7 @@ surface in one place, and what obliges an adapter to implement each one.
1927
1927
  | `cancelBreak()` | The login declares `capabilities.breaks`. |
1928
1928
  | `endBreak()` | The login declares `capabilities.breaks`. |
1929
1929
  | `executeTeamBreak(command)` | The login declares `capabilities.team.breakControl`. |
1930
- | `executeTeamConsult(command)` | The login declares `capabilities.team.consultControl`. |
1930
+ | `executeTeamLeadAssist(command)` | The login declares `capabilities.team.leadAssistControl`. |
1931
1931
  | `setPreference(request)` | The login declares `capabilities.preferences`: the person's choice has to have somewhere to go. |
1932
1932
  | `recordStep(report)` | A task declares `mute`: the host performs that leg, and the provider's record has to have somewhere to take it. See **The host records what it performs**. |
1933
1933
  | `executeTeamPolicy(command)` | The login declares `capabilities.team.policyControl`. |
@@ -2136,8 +2136,8 @@ time. Runtime conformance checks also require the task channel to match its prov
2136
2136
  | `attributes` | Optional ordered, typed `TaskAttribute` entries with keys unique within the task. Each contact or timestamp is a separate array item; new attribute shapes require new union members. |
2137
2137
  | `handlingHistory` | The call record: `steps` — the ordered handling history of this open task, one entry per occurrence, oldest first — and what they add up to before this agent, `handleSeconds`, `holdSeconds`, `queueSeconds`, `transfers`, each present when the provider knows it. Live task data restated with the task, not a permanent archive. See **How a task has been handled**. |
2138
2138
  | `onCall` | Voice only. Who is on the call, or being brought onto it, as the provider states it, replaced whole with the task: `party` is the customer, `agent` a person by user id, `consulted` and `conferenced` somebody a dial is bringing in, listed from the moment the dial is placed -- with the `destination` dialled, the `dialId` where a host placed it, the `stage` reached (`ringing` until answered, `joined` after), and `held: true` on anyone joined and parked. A `consulted` entry is what makes `transfer` `complete` and `cancel` issuable. `label` names a destination -- a person, a queue -- not a phrase; the host supplies the verb. Present when the provider knows the room, absent when it does not. See **Every dial has an outcome**. |
2139
- | `lead` | Voice only. Present from the agent's request for a lead until the lead leaves or the request ends: `requested` while nobody has joined, `joined` with the lead's `leadId` once somebody has. See **Consulting a lead**. |
2140
- | `assisting` | Voice only, on the lead's own task for a call they joined: which member asked, with their note. Its presence is what makes `lead` `take-over` and `leave` issuable. See **Consulting a lead**. |
2139
+ | `leadAssist` | Voice only. Present from the agent's request for a lead until the lead leaves or the request ends: `requested` while nobody has joined, `joined` with the lead's `leadId` once somebody has. See **Lead assist**. |
2140
+ | `assisting` | Voice only, on the lead's own task for a call they joined: which member asked, with their note. Its presence is what makes `lead-assist` `take-over` and `leave` issuable. See **Lead assist**. |
2141
2141
 
2142
2142
  `TaskAttribute` entries carry typed detail alongside the task:
2143
2143
 
@@ -2563,7 +2563,7 @@ const taskCapabilities = {
2563
2563
  | `callback` | Completing-task button: Call back | Omni may have the provider call the task's party back while the task is `completing`, returning it to `in-progress` on the same task. Not offered where there is no `completing` window: `provider-automatic` with a zero allowance disposes at provider end. See **Calling back during completion**. |
2564
2564
  | `blindTransfer` | Secondary menu item: Blind transfer | Omni may transfer the caller directly to a destination. |
2565
2565
  | `consultTransfer` | Secondary menu item: Consult transfer | Omni may park the customer and call a destination first, then hand the customer over or cancel back. See **Consult transfer**. |
2566
- | `consultLead` | Secondary menu item: Consult lead | Omni may ask a lead to join this call, with a note. The lead's decision reaches the agent on `Task.lead`. See **Consulting a lead**. |
2566
+ | `leadAssist` | Secondary menu item: Lead assist | Omni may ask a lead to join this call, with a note. The lead's decision reaches the agent on `Task.leadAssist`. See **Lead assist**. |
2567
2567
  | `conference` | Secondary button: Conference | Omni may dial a destination into the active call, and drop a `conferenced` entry from it -- one still ringing included, which calls the dial off. |
2568
2568
  | `recording` | Overflow menu item: Recording | Omni may expose start, pause, resume, and stop recording controls. |
2569
2569
  | `dispositions` | Primary button: Complete | Omni may request task disposal with a provider disposition and notes. |
@@ -2859,7 +2859,11 @@ still-ringing `destination` calls that dial off, with `cancelled` as its outcome
2859
2859
  **The entry says where it stands.** A dialled entry carries `stage`: `ringing` until its dial is
2860
2860
  answered, `joined` after. The `dial-outcome` is the transition and the stage is the state, the same
2861
2861
  pairing as `task-media-started` and `media`: an `answered` outcome and the task restated with the
2862
- entry `joined` say one thing twice, and any other outcome removes the entry. It is stated rather
2862
+ entry `joined` say one thing twice, and any other outcome removes the entry. The outcome comes
2863
+ first: a `task-updated` that itself moves an entry from `ringing` to `joined` before an `answered`
2864
+ outcome for its dial is refused (`stream.taskUpdated.stage`), as an update that moves `media` is.
2865
+ The rule keys on the entry's `dialId`, so an entry the platform added itself, with none, is one
2866
+ whose move the stream cannot hold to an outcome: a clean stream proves nothing about ordering there. It is stated rather
2863
2867
  than left to whoever placed the dial because that knowledge lives in one process: after a reload,
2864
2868
  on a second session, or on a supervisor's screen the snapshot is the only source, and a room that
2865
2869
  cannot tell ringing from joined says it contains someone who may never arrive. Nobody ringing is
@@ -3248,7 +3252,7 @@ A lead who also takes calls sees their team on the idle dashboard. `Snapshot.tea
3248
3252
  | Field | Contract |
3249
3253
  | --- | --- |
3250
3254
  | `members` | Every member of this lead's team, whatever their state. `[]` says the lead has a team with nobody in it; omitting the roster says something else entirely — see **The login is the permission** below. |
3251
- | `requests` | The members currently asking this lead to join a call, each with the task and the note. Required when the login declares `team.consultControl`, `[]` when nobody is asking; omitted when it does not. See **Consulting a lead**. |
3255
+ | `requests` | The members currently asking this lead to join a call, each with the task and the note. Required when the login declares `team.leadAssistControl`, `[]` when nobody is asking; omitted when it does not. See **Lead assist**. |
3252
3256
  | `policies` | The team's policy per capability as it stands — the setting, who set it, and `lockedBy` where a level above the team made it theirs to keep. Required when the login declares `team.policyControl`; omitted when it does not. See **Who decides what an agent may do**. |
3253
3257
 
3254
3258
  | `TeamMember` field | Contract |
@@ -3282,12 +3286,12 @@ that value does.
3282
3286
  every snapshot, and to nobody else. Omni never decides who leads a team: the provider said so at
3283
3287
  sign-in, and the roster agrees with it — present, `[]` included, for a lead; absent for everybody
3284
3288
  else, which is the correct rendering for an agent who leads nobody. What the lead may do with the
3285
- roster is on the login too, `team.breakControl` and `team.consultControl`, never on the roster.
3289
+ roster is on the login too, `team.breakControl` and `team.leadAssistControl`, never on the roster.
3286
3290
 
3287
3291
  **The roster never carries the agent it is published to — not in `members`, and not in
3288
3292
  `requests`.** A lead does not report to themself: their own break request and their own ask for a
3289
3293
  lead go up to whoever leads them and appear on *that* person's roster, while the requester sees
3290
- only their own `BreakState` and their task's `lead` move. An adapter whose platform lists the lead
3294
+ only their own `BreakState` and their task's `leadAssist` move. An adapter whose platform lists the lead
3291
3295
  among their own members filters the signed-in identity out before publishing. **Being a lead is a
3292
3296
  role the provider knows, never inferred from who is listed:** it is declared at sign-in, a lead
3293
3297
  with nobody in their team publishes `[]`, an agent with no such role publishes nothing, and no
@@ -3317,37 +3321,37 @@ on their own `BreakState`, or they are stopped from working with no way to see w
3317
3321
  What happens when no lead is online — auto-approving, for instance — is the provider's decision and is
3318
3322
  never expressed here.
3319
3323
 
3320
- ### Consulting a lead
3324
+ ### Lead assist
3321
3325
 
3322
3326
  An agent on a call may ask a lead to join it -- a dispute that needs approval, a customer who
3323
- asks for a manager, a moment the agent wants a second pair of ears. The capability is
3324
- `consultLead` on the task; the lead's side is the roster, which is already the lead's view of the
3327
+ asks for a manager, a moment the agent wants a second pair of ears. A call centre calls this
3328
+ assistance or escalation, and the name says who assists: the capability is `leadAssist` on the task; the lead's side is the roster, which is already the lead's view of the
3325
3329
  team, and a second lead method beside `executeTeamBreak`:
3326
3330
 
3327
3331
  ```ts
3328
- executeTeamConsult({ command: TeamConsultCommand }): Promise<TeamCommandResult>
3332
+ executeTeamLeadAssist({ command: TeamLeadAssistCommand }): Promise<TeamCommandResult>
3329
3333
  ```
3330
3334
 
3331
- Required when the login declares `capabilities.team.consultControl`, and gated by it exactly as
3335
+ Required when the login declares `capabilities.team.leadAssistControl`, and gated by it exactly as
3332
3336
  `executeTeamBreak` is by `team.breakControl`. The flow, in order:
3333
3337
 
3334
3338
  ```ts
3335
- // 1. The agent asks, with a small note. Their task carries `lead` from here on.
3336
- execute({ taskId: "call-42", command: { type: "lead", action: "request", note: "Refund dispute, needs approval" } })
3337
- // task.lead = { status: "requested", note: "Refund dispute, needs approval", since }
3339
+ // 1. The agent asks, with a small note. Their task carries `leadAssist` from here on.
3340
+ execute({ taskId: "call-42", command: { type: "lead-assist", action: "request", note: "Refund dispute, needs approval" } })
3341
+ // task.leadAssist = { status: "requested", note: "Refund dispute, needs approval", since }
3338
3342
 
3339
3343
  // 2. Every lead entitled to it sees the request on their roster.
3340
3344
  // team-updated: requests: [{ id: "req-7", memberId: "A-1", taskId: "call-42", note, since }]
3341
3345
 
3342
3346
  // 3. A lead joins, or declines.
3343
- executeTeamConsult({ command: { type: "join", requestId: "req-7" } })
3344
- executeTeamConsult({ command: { type: "decline", requestId: "req-7", reason: "In a call" } })
3347
+ executeTeamLeadAssist({ command: { type: "join", requestId: "req-7" } })
3348
+ executeTeamLeadAssist({ command: { type: "decline", requestId: "req-7", reason: "In a call" } })
3345
3349
  ```
3346
3350
 
3347
3351
  **On `join` the provider bridges three parties and the lead is on a task of their own**, on the
3348
3352
  same task id, arriving on the lead's connection as `task-offered` with `automatic`
3349
3353
  -- the way a call an agent placed themselves arrives -- and carrying `assisting`. The agent's task
3350
- moves to `lead: { stage: "joined", leadId }`. A join is the lead's own act, so capacity does not
3354
+ moves to `leadAssist: { stage: "joined", leadId }`. A join is the lead's own act, so capacity does not
3351
3355
  trigger it; but from then on it is an outstanding task the provider counts against the lead's
3352
3356
  stated ceiling like any other, nothing more is allocated to the lead while it stands, and a
3353
3357
  provider whose lead is already at the ceiling answers the join `failed`.
@@ -3362,8 +3366,8 @@ and a join is work. Omni offers Join to a lead only while their own `BreakState.
3362
3366
  finish is not given more -- and a provider answers a `join` from a lead on such a break `failed`.
3363
3367
  The request stands for another lead, as it does when this one is already on a call.
3364
3368
 
3365
- **On `decline`, or a request the agent withdraws with `{ type: "lead", action: "cancel" }`, the
3366
- provider clears `lead` from the agent's task** and drops the request from every roster. Nothing
3369
+ **On `decline`, or a request the agent withdraws with `{ type: "lead-assist", action: "cancel" }`, the
3370
+ provider clears `leadAssist` from the agent's task** and drops the request from every roster. Nothing
3367
3371
  else changes; the agent is still on the call.
3368
3372
 
3369
3373
  The lead then has two commands on their copy, gated by `assisting` being present, and a third
@@ -3371,8 +3375,8 @@ choice that is no command at all:
3371
3375
 
3372
3376
  | The lead | The agent's task | The lead's task |
3373
3377
  | --- | --- | --- |
3374
- | `{ type: "lead", action: "take-over" }` | `task-ended` with `{ type: "transferred", destination: leadId }`, straight from `in-progress`: **no `completing` window**, the agent is idle at once | Continues alone, and ends as any call does |
3375
- | `{ type: "lead", action: "leave" }` | Continues; `lead` is cleared | `task-ended` with `{ type: "left" }` -- the call goes on without them |
3378
+ | `{ type: "lead-assist", action: "take-over" }` | `task-ended` with `{ type: "transferred", destination: leadId }`, straight from `in-progress`: **no `completing` window**, the agent is idle at once | Continues alone, and ends as any call does |
3379
+ | `{ type: "lead-assist", action: "leave" }` | Continues; `leadAssist` is cleared | `task-ended` with `{ type: "left" }` -- the call goes on without them |
3376
3380
  | Stays until the customer hangs up | `task-media-ended`, `completing`, its own disposition | The same, independently: **both have the disposal window** |
3377
3381
 
3378
3382
  `left` is the one outcome that ends a task without ending the call: this agent left a call that
@@ -3380,12 +3384,12 @@ continues without them. It reads as neither a completion nor a cancellation, bec
3380
3384
  neither.
3381
3385
 
3382
3386
  ```ts
3383
- const consultLeadCapable = {
3387
+ const leadAssistCapable = {
3384
3388
  channel: "voice",
3385
- capabilities: { hold: true, consultLead: true, dispositions: true },
3389
+ capabilities: { hold: true, leadAssist: true, dispositions: true },
3386
3390
  phase: "in-progress",
3387
- lead: { stage: "joined", leadId: "L-9", note: "Refund dispute, needs approval", since: "2026-08-21T09:04:00Z" },
3388
- } satisfies Pick<Task<"voice">, "channel" | "capabilities" | "phase" | "lead">;
3391
+ leadAssist: { stage: "joined", leadId: "L-9", note: "Refund dispute, needs approval", since: "2026-08-21T09:04:00Z" },
3392
+ } satisfies Pick<Task<"voice">, "channel" | "capabilities" | "phase" | "leadAssist">;
3389
3393
  ```
3390
3394
 
3391
3395
  Lead and member alike are `UserId`s of this provider, so an adapter publishing them implements
@@ -3610,8 +3614,8 @@ declared:
3610
3614
  | `callback` | The `callback` capability **and** the `completing` phase. It exists to reach the party again after the call, so it has no meaning while the call is up. |
3611
3615
  | `transfer` with `action: "consult"` | The `consultTransfer` capability. Blind `transfer` is gated by `blindTransfer`; the two are declared and offered separately. |
3612
3616
  | `transfer` with `action: "complete"` or `"cancel"` | A consultation in progress -- a `consulted` entry on `Task.onCall`. Without one there is nothing to complete or cancel, and a provider that receives either answers `failed`. |
3613
- | `lead` with `action: "request"` or `"cancel"` | The `consultLead` capability. `cancel` needs a request standing -- `Task.lead` with status `requested`. |
3614
- | `lead` with `action: "take-over"` or `"leave"` | The lead's own task, on a call they joined -- `Task.assisting` present. An agent's task never has it, and a provider that receives either without it answers `failed`. |
3617
+ | `lead-assist` with `action: "request"` or `"cancel"` | The `leadAssist` capability. `cancel` needs a request standing -- `Task.leadAssist` with status `requested`. |
3618
+ | `lead-assist` with `action: "take-over"` or `"leave"` | The lead's own task, on a call they joined -- `Task.assisting` present. An agent's task never has it, and a provider that receives either without it answers `failed`. |
3615
3619
  | Everything else | Its own named capability. |
3616
3620
 
3617
3621
  Declining or rejecting a pending offer ends it without accepting or completing it. The provider
@@ -3854,7 +3858,7 @@ Every outcome ends the task for this agent. On `task-ended`, Omni:
3854
3858
  - selects another task or returns to the idle workspace.
3855
3859
 
3856
3860
  A `left` outcome ends the task for this agent alone: the call continues without them, as it does
3857
- when a lead who joined it leaves -- see **Consulting a lead**.
3861
+ when a lead who joined it leaves -- see **Lead assist**.
3858
3862
 
3859
3863
  A successful `complete` or `transfer` command does not clear the task. Omni waits for `task-ended`.
3860
3864
  The `task-media-ended` event and the `completing` phase are likewise non-terminal. A replacement
@@ -3978,7 +3982,7 @@ Some rules need to know who is reading. `validateTeamRoster`, `validateSnapshot`
3978
3982
  `AuthenticationState.identity.id` and their login's `capabilities`. Given `self`, a roster that
3979
3983
  carries that agent reports `team.member.self` or `team.request.self`. Given `capabilities`, a lead's
3980
3984
  snapshot without a roster reports `team.required`, a roster published to a login that does not lead
3981
- reports `team.unentitled`, `requests` on a roster whose login lacks `team.consultControl` reports
3985
+ reports `team.unentitled`, `requests` on a roster whose login lacks `team.leadAssistControl` reports
3982
3986
  `team.requests.capability`, and a roster without them on a login that declares it reports
3983
3987
  `team.requests.required`. Without them those rules are not checked, because they cannot be.
3984
3988
  `exerciseAdapter` always passes both.
@@ -4020,7 +4024,7 @@ latest the session published during the run, which differs only when the adapter
4020
4024
  A capability granted by a later login requires its methods just as one declared at sign-in does.
4021
4025
 
4022
4026
  `result.notExercised` lists what the run never reached — one subject per family of rules: each
4023
- optional part of a task (`task.browsers`, `task.handlingHistory`, `task.lead`, …), the break's
4027
+ optional part of a task (`task.browsers`, `task.handlingHistory`, `task.leadAssist`, …), the break's
4024
4028
  `reasons` and `imposed`, the roster's `members` and `requests`, each declared contribution, and
4025
4029
  each event type (`event.task-ended`, …) — and so what a clean `violations` says nothing about.
4026
4030
  Nothing there is a violation: an adapter with no team has nothing to exercise. But a fixture with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",