@xema/omni-protocol 0.1.83 → 0.1.84

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
@@ -29,7 +29,7 @@ export type TaskId = string;
29
29
  /**
30
30
  * One life of a task on the wire. A platform retires a task id minutes after closing it and a
31
31
  * requeue takes seconds, so the same `TaskId` comes back for another customer while a late dial
32
- * outcome or a late handling report for the first is still in flight. The allocation id is minted
32
+ * outcome or a late interaction report for the first is still in flight. The allocation id is minted
33
33
  * once per offer and never reused for the life of the login, and every event, command and report
34
34
  * that names a task names its allocation too, so nothing lands on the next life of the id.
35
35
  */
@@ -180,13 +180,13 @@ export interface Manifest<C extends Channel = Channel> {
180
180
  */
181
181
  runningStepReports?: true;
182
182
  /**
183
- * How long after an applied disposal -- `complete`, or a lead's `take-over` -- the provider's
183
+ * How long after an applied completion -- `complete`, or a lead's `take-over` -- the provider's
184
184
  * `task-ended` is owed, in milliseconds. A warm transfer's `complete` is not one: the agent's
185
185
  * wrap runs after it as after any call. `applied` says the provider
186
- * has disposed of the task; the ending follows within this, or the host resyncs and shows the
186
+ * has completed the task; the ending follows within this, or the host resyncs and shows the
187
187
  * task as unsettled. Stated per provider, since platforms settle at different speeds.
188
188
  */
189
- disposalSettleMs: number;
189
+ completionSettleMs: number;
190
190
  }
191
191
  export interface SecretStore {
192
192
  get(key: string): Promise<string | undefined>;
@@ -195,7 +195,7 @@ export interface SecretStore {
195
195
  }
196
196
  /**
197
197
  * A small store for one login's operational state -- what an adapter holds about open work that
198
- * its platform cannot hold for it, such as the handling legs a host reported. Kept by the host for
198
+ * its platform cannot hold for it, such as the interaction legs a host reported. Kept by the host for
199
199
  * the life of the login, across a reload of the host, and cleared at sign-out. Never for anything
200
200
  * sensitive: that is `SecretStore`, whose contract is that a host may clear it aggressively.
201
201
  */
@@ -370,7 +370,7 @@ export interface AuthenticationSession {
370
370
  /**
371
371
  * Who silenced a device that is present and permitted: the host, by its own Mute, or the station --
372
372
  * a slider on the headset, the operating system -- which the host observes and, on a browser,
373
- * cannot touch. The same word on a `muted` handling leg says whose the silence was.
373
+ * cannot touch. The same word on a `muted` interaction leg says whose the silence was.
374
374
  */
375
375
  export type MutedBy = "host" | "station";
376
376
  export declare const MUTED_BY: readonly ["host", "station"];
@@ -669,7 +669,7 @@ export interface SharedTaskCapabilities {
669
669
  export type TaskCapabilities<C extends Channel = Channel> = C extends "voice" ? SharedTaskCapabilities & {
670
670
  decline?: Lockable<true>;
671
671
  hold?: Lockable<true>;
672
- /** The provider ends the caller connection and agent-added channels owned by this handling, including inherited channels after transfer/takeover. Handling disposal remains separate. */
672
+ /** The provider ends the caller connection and agent-added channels owned by this interaction, including inherited channels after transfer/takeover. Interaction completion remains separate. */
673
673
  endCall?: Lockable<true>;
674
674
  /** Connect back to the party while `completing`, whoever placed the call; the task returns to `in-progress`. */
675
675
  connectBack?: Lockable<true>;
@@ -765,7 +765,7 @@ export type TaskAttribute = TaskAttributeBase & ({
765
765
  type: "timestamp";
766
766
  at: IsoTimestamp;
767
767
  });
768
- export type HandlingStep = "queued" | "offered" | "answered" | "held" | "muted" | "transferred" | "conferenced" | "unanswered";
768
+ export type InteractionStep = "queued" | "offered" | "answered" | "held" | "muted" | "transferred" | "conferenced" | "unanswered";
769
769
  /**
770
770
  * The call record: the steps that brought the task here, one entry per occurrence and oldest
771
771
  * first, and what they add up to before this agent -- stated by the provider from its own record,
@@ -773,10 +773,10 @@ export type HandlingStep = "queued" | "offered" | "answered" | "held" | "muted"
773
773
  * absent when it does not; a plausible nought is the fallback the no-fallbacks rule forbids. The
774
774
  * record rides on the task and is replaced with it, so a late entry corrects the sums.
775
775
  */
776
- export interface TaskHandlingHistory {
777
- steps: TaskHandlingStep[];
778
- /** Seconds others spent handling it before this agent. */
779
- handleSeconds?: DurationSeconds;
776
+ export interface TaskInteractionHistory {
777
+ steps: TaskInteractionStep[];
778
+ /** Seconds others spent working on it before this agent. */
779
+ interactionSeconds?: DurationSeconds;
780
780
  /** Seconds the caller spent on hold at others' hands. */
781
781
  holdSeconds?: DurationSeconds;
782
782
  /** Seconds waiting before anyone answered. */
@@ -784,8 +784,8 @@ export interface TaskHandlingHistory {
784
784
  /** How many times the task changed hands. */
785
785
  transfers?: number;
786
786
  }
787
- export interface TaskHandlingStep {
788
- step: HandlingStep;
787
+ export interface TaskInteractionStep {
788
+ step: InteractionStep;
789
789
  at: IsoTimestamp;
790
790
  /**
791
791
  * On a step that dialled -- `transferred`, `conferenced`, `unanswered` -- the host's identity for
@@ -808,7 +808,7 @@ export interface TaskHandlingStep {
808
808
  mutedBy?: MutedBy;
809
809
  }
810
810
  /**
811
- * How the task ends, and how long after handling the provider allows for it.
811
+ * How the task ends, and how long after interaction the provider allows for it.
812
812
  *
813
813
  * The allowance is coupled to the mode. Under `provider-automatic` the provider acts on it, so it
814
814
  * is required. Under `agent-command` the provider will not complete the task itself, so it may
@@ -991,7 +991,7 @@ export type Task<C extends Channel = Channel> = {
991
991
  /** The identifier an agent reads back to a customer, where the provider has one. */
992
992
  reference?: string;
993
993
  attributes?: TaskAttribute[];
994
- handlingHistory?: TaskHandlingHistory;
994
+ interactionHistory?: TaskInteractionHistory;
995
995
  } & TaskCompletion & (C extends "voice" ? {
996
996
  recording?: {
997
997
  provider?: RecordingState;
@@ -1072,7 +1072,7 @@ export type VoiceTaskCommand = {
1072
1072
  } | {
1073
1073
  type: "resume";
1074
1074
  }
1075
- /** End the caller connection and all agent-added channels owned or inherited by this handling. Wrap/disposal remain separate. Gated by `endCall`. */
1075
+ /** End the caller connection and all agent-added channels owned or inherited by this interaction. Wrap/completion remain separate. Gated by `endCall`. */
1076
1076
  | {
1077
1077
  type: "end-call";
1078
1078
  }
@@ -1419,7 +1419,7 @@ export interface TeamBreakCommandRequest {
1419
1419
  }
1420
1420
  /**
1421
1421
  * The task's real-time audio as the provider holds it: `started` while audio should be attached,
1422
- * `ended` once primary handling's audio ended, and the field omitted while none should be. The
1422
+ * `ended` once primary interaction's audio ended, and the field omitted while none should be. The
1423
1423
  * provider's word -- a desk attaches and renders audio from it, never from its own senses.
1424
1424
  */
1425
1425
  export type TaskMediaState = "started" | "ended";
@@ -1481,14 +1481,14 @@ export type SetPreferenceRequest = {
1481
1481
  inherit: true;
1482
1482
  };
1483
1483
  /**
1484
- * The host's report of a handling leg it performed itself -- a mute, which is the host's and
1484
+ * The host's report of an interaction leg it performed itself -- a mute, which is the host's and
1485
1485
  * never the provider's -- so the provider's record has an account of it. Keyed by `step` and `at`: the
1486
1486
  * same entry is reported when it begins, as often as the host cares to while it runs, and once
1487
1487
  * more with `ended`, when `seconds` is the final duration. The host is the authority for the
1488
1488
  * legs it performs, so `seconds` may say how long so far at any time; the end is stated, never
1489
1489
  * inferred from a number's presence. What the adapter forwards upstream, and how often, is its own.
1490
1490
  */
1491
- export type HandlingReport = {
1491
+ export type InteractionReport = {
1492
1492
  taskId: TaskId;
1493
1493
  allocationId: AllocationId;
1494
1494
  at: IsoTimestamp;
@@ -1498,7 +1498,7 @@ export type HandlingReport = {
1498
1498
  step: "muted";
1499
1499
  mutedBy: MutedBy;
1500
1500
  } | {
1501
- step: Exclude<HandlingStep, "muted">;
1501
+ step: Exclude<InteractionStep, "muted">;
1502
1502
  mutedBy?: never;
1503
1503
  }) & ({
1504
1504
  ended: true;
@@ -1507,7 +1507,7 @@ export type HandlingReport = {
1507
1507
  ended?: never;
1508
1508
  seconds?: DurationSeconds;
1509
1509
  });
1510
- export type HandlingReportResult = {
1510
+ export type InteractionReportResult = {
1511
1511
  status: "recorded";
1512
1512
  at: IsoTimestamp;
1513
1513
  } | {
@@ -1717,8 +1717,8 @@ export interface Connection<C extends Channel = Channel> {
1717
1717
  openMedia?(request: OpenMediaRequest): Promise<OpenMediaResult>;
1718
1718
  /** Required when the login declares `capabilities.preferences`: the person's own choice, kept by the provider and republished as `authenticated`. */
1719
1719
  setPreference?(request: SetPreferenceRequest): Promise<PreferenceResult>;
1720
- /** Records a handling leg the host performed. Required of a softphone login's connection: the host mutes its microphone on any call, and the record is the provider's. */
1721
- recordStep?(report: HandlingReport): Promise<HandlingReportResult>;
1720
+ /** Records an interaction leg the host performed. Required of a softphone login's connection: the host mutes its microphone on any call, and the record is the provider's. */
1721
+ recordStep?(report: InteractionReport): Promise<InteractionReportResult>;
1722
1722
  }
1723
1723
  export interface Adapter<C extends Channel = Channel> {
1724
1724
  manifest: Manifest<C>;
@@ -1789,13 +1789,13 @@ export declare const taskKey: (providerId: string, taskId: TaskId) => string;
1789
1789
  * same provider; anything wider goes through this.
1790
1790
  */
1791
1791
  export declare const userKey: (providerId: string, userId: UserId) => string;
1792
- /** Every handling step somebody takes part in. `queued` is the one nobody does. */
1793
- export declare const HANDLING_STEPS_WITH_A_PERSON: readonly ["offered", "answered", "held", "muted", "transferred", "conferenced", "unanswered"];
1792
+ /** Every interaction step somebody takes part in. `queued` is the one nobody does. */
1793
+ export declare const INTERACTION_STEPS_WITH_A_PERSON: readonly ["offered", "answered", "held", "muted", "transferred", "conferenced", "unanswered"];
1794
1794
  /** The steps a dial writes, and so the only ones that carry a `dialId` and a `destinationId`. */
1795
- export declare const HANDLING_STEPS_THAT_DIAL: readonly ["transferred", "conferenced", "unanswered"];
1796
- export declare const handlingStepDials: (step: HandlingStep) => boolean;
1795
+ export declare const INTERACTION_STEPS_THAT_DIAL: readonly ["transferred", "conferenced", "unanswered"];
1796
+ export declare const interactionStepDials: (step: InteractionStep) => boolean;
1797
1797
  /** Whether an absent `by` means "could not attribute" rather than "nobody was involved". */
1798
- export declare function handlingStepExpectsAPerson(step: HandlingStep): boolean;
1798
+ export declare function interactionStepExpectsAPerson(step: InteractionStep): boolean;
1799
1799
  export interface BrowserSessionKeyInput {
1800
1800
  /** `Manifest.id`, never `displayName`: only the id is unique across an installation and stable. */
1801
1801
  providerId: string;
package/dist/index.js CHANGED
@@ -159,19 +159,19 @@ export const taskKey = (providerId, taskId) => `${encodeURIComponent(providerId)
159
159
  * same provider; anything wider goes through this.
160
160
  */
161
161
  export const userKey = (providerId, userId) => `${encodeURIComponent(providerId)}:${encodeURIComponent(userId)}`;
162
- /** Every handling step somebody takes part in. `queued` is the one nobody does. */
163
- export const HANDLING_STEPS_WITH_A_PERSON = [
162
+ /** Every interaction step somebody takes part in. `queued` is the one nobody does. */
163
+ export const INTERACTION_STEPS_WITH_A_PERSON = [
164
164
  "offered", "answered", "held", "muted", "transferred", "conferenced", "unanswered",
165
165
  ];
166
166
  /** The steps a dial writes, and so the only ones that carry a `dialId` and a `destinationId`. */
167
- export const HANDLING_STEPS_THAT_DIAL = ["transferred", "conferenced", "unanswered"];
168
- export const handlingStepDials = (step) => HANDLING_STEPS_THAT_DIAL.includes(step);
169
- // Pinned both ways: a step added to `HandlingStep` has to be placed here, and a step listed here
167
+ export const INTERACTION_STEPS_THAT_DIAL = ["transferred", "conferenced", "unanswered"];
168
+ export const interactionStepDials = (step) => INTERACTION_STEPS_THAT_DIAL.includes(step);
169
+ // Pinned both ways: a step added to `InteractionStep` has to be placed here, and a step listed here
170
170
  // has to exist there. `satisfies` on the list covers the second; this statement covers the first.
171
171
  true;
172
172
  /** Whether an absent `by` means "could not attribute" rather than "nobody was involved". */
173
- export function handlingStepExpectsAPerson(step) {
174
- return HANDLING_STEPS_WITH_A_PERSON.includes(step);
173
+ export function interactionStepExpectsAPerson(step) {
174
+ return INTERACTION_STEPS_WITH_A_PERSON.includes(step);
175
175
  }
176
176
  /**
177
177
  * Whether two logins declare the same capabilities, field by field. The comparison an adapter
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.leadAssist", "task.assisting", "task.monitoring", "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.interactionHistory", "task.onCall", "task.leadAssist", "task.assisting", "task.monitoring", "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
@@ -1,5 +1,5 @@
1
1
  import { browserSessionKey, effectiveLevels, sameCapabilities, } from "./index.js";
2
- import { assertNoViolations, validateAuthenticationState, validateBreakTransition, validateEventEnvelope, validateHostGuarantees, validateHostReport, validateDescribedUsers, observeRules, ruleEvaluated, validateHostMute, validateHostRecording, validateLoginStore, validateHandlingReport, validateManifest, validateResult, validateSnapshot, validateTask, validateTimeZone, validatePhone, validateTaskCommand, isTimeZone, sameTimeZone, } from "./validation.js";
2
+ import { assertNoViolations, validateAuthenticationState, validateBreakTransition, validateEventEnvelope, validateHostGuarantees, validateHostReport, validateDescribedUsers, observeRules, ruleEvaluated, validateHostMute, validateHostRecording, validateLoginStore, validateInteractionReport, validateManifest, validateResult, validateSnapshot, validateTask, validateTimeZone, validatePhone, validateTaskCommand, isTimeZone, sameTimeZone, } from "./validation.js";
3
3
  export { ProtocolConformanceError, assertNoViolations } from "./validation.js";
4
4
  /**
5
5
  * A part of the contract a run may never reach: state nothing obliges an adapter to publish, so
@@ -11,7 +11,7 @@ const STATE_SUBJECTS = [
11
11
  "tasks",
12
12
  "task.browsers",
13
13
  "task.attributes",
14
- "task.handlingHistory",
14
+ "task.interactionHistory",
15
15
  "task.onCall",
16
16
  "task.leadAssist",
17
17
  "task.assisting",
@@ -53,8 +53,8 @@ function observeTask(value, seen) {
53
53
  seen.add("task.browsers");
54
54
  if (some(value.attributes))
55
55
  seen.add("task.attributes");
56
- if (isRecord(value.handlingHistory) && some(value.handlingHistory.steps))
57
- seen.add("task.handlingHistory");
56
+ if (isRecord(value.interactionHistory) && some(value.interactionHistory.steps))
57
+ seen.add("task.interactionHistory");
58
58
  if (some(value.onCall))
59
59
  seen.add("task.onCall");
60
60
  if (value.leadAssist !== undefined)
@@ -661,8 +661,8 @@ function userIdsIn(snapshot) {
661
661
  }
662
662
  for (const task of Array.isArray(snapshot?.tasks) ? snapshot.tasks : []) {
663
663
  const t = task;
664
- if (isRecord(t.handlingHistory) && Array.isArray(t.handlingHistory.steps))
665
- for (const step of t.handlingHistory.steps)
664
+ if (isRecord(t.interactionHistory) && Array.isArray(t.interactionHistory.steps))
665
+ for (const step of t.interactionHistory.steps)
666
666
  if (isRecord(step))
667
667
  add(step.by);
668
668
  if (Array.isArray(t.onCall))
@@ -681,7 +681,7 @@ function userIdsIn(snapshot) {
681
681
  return [...ids];
682
682
  }
683
683
  const teamNamesUsers = (team) => isRecord(team) && (some(team.members) || some(team.requests));
684
- const taskNamesUsers = (task) => isRecord(task) && ((isRecord(task.handlingHistory) && Array.isArray(task.handlingHistory.steps) && task.handlingHistory.steps.some(step => isRecord(step) && step.by !== undefined)) ||
684
+ const taskNamesUsers = (task) => isRecord(task) && ((isRecord(task.interactionHistory) && Array.isArray(task.interactionHistory.steps) && task.interactionHistory.steps.some(step => isRecord(step) && step.by !== undefined)) ||
685
685
  (isRecord(task.leadAssist) && task.leadAssist.leadId !== undefined) ||
686
686
  isRecord(task.assisting) ||
687
687
  isRecord(task.monitoring));
@@ -1007,7 +1007,7 @@ export class TaskStream {
1007
1007
  return;
1008
1008
  const entries = [
1009
1009
  ...(Array.isArray(task.onCall) ? task.onCall : []),
1010
- ...(isRecord(task.handlingHistory) && Array.isArray(task.handlingHistory.steps) ? task.handlingHistory.steps : []),
1010
+ ...(isRecord(task.interactionHistory) && Array.isArray(task.interactionHistory.steps) ? task.interactionHistory.steps : []),
1011
1011
  ];
1012
1012
  for (const entry of entries) {
1013
1013
  if (isRecord(entry) && typeof entry.dialId === "string")
@@ -1052,9 +1052,9 @@ export class TaskStream {
1052
1052
  }
1053
1053
  /** The entries of a task's record, each by step and instant, or undefined where the task carries no record. */
1054
1054
  static record(task) {
1055
- if (!isRecord(task) || !isRecord(task.handlingHistory) || !Array.isArray(task.handlingHistory.steps))
1055
+ if (!isRecord(task) || !isRecord(task.interactionHistory) || !Array.isArray(task.interactionHistory.steps))
1056
1056
  return undefined;
1057
- return new Set(task.handlingHistory.steps.filter(isRecord).map(entry => `${String(entry.step)}@${String(entry.at)}`));
1057
+ return new Set(task.interactionHistory.steps.filter(isRecord).map(entry => `${String(entry.step)}@${String(entry.at)}`));
1058
1058
  }
1059
1059
  /** What a restated record lost of the one read before it: nothing, or the entries by step and instant. */
1060
1060
  static lost(was, now) {
@@ -1092,7 +1092,7 @@ export class TaskStream {
1092
1092
  resync(snapshot, at) {
1093
1093
  const found = [];
1094
1094
  const refuse = (rule, where, message) => found.push({ rule, path: where, message });
1095
- ruleEvaluated("stream.snapshot.capabilitySource", "stream.snapshot.handlingHistory", "stream.snapshot.phase", "stream.snapshot.media");
1095
+ ruleEvaluated("stream.snapshot.capabilitySource", "stream.snapshot.interactionHistory", "stream.snapshot.phase", "stream.snapshot.media");
1096
1096
  if (isRecord(snapshot) && Array.isArray(snapshot.tasks)) {
1097
1097
  snapshot.tasks.forEach((task, index) => {
1098
1098
  if (!isRecord(task) || typeof task.id !== "string")
@@ -1106,7 +1106,7 @@ export class TaskStream {
1106
1106
  // A record once read is not unread: a resync restates it whole, or with more, never with less.
1107
1107
  const lost = TaskStream.lost(was.record, TaskStream.record(task));
1108
1108
  if (lost.length > 0) {
1109
- refuse("stream.snapshot.handlingHistory", `${at}.tasks[${index}].handlingHistory`, `${task.id}'s record lost ${lost.join(", ")} on the snapshot: an entry read by the host stays in the record until the task ends`);
1109
+ refuse("stream.snapshot.interactionHistory", `${at}.tasks[${index}].interactionHistory`, `${task.id}'s record lost ${lost.join(", ")} on the snapshot: an entry read by the host stays in the record until the task ends`);
1110
1110
  }
1111
1111
  const to = String(task.phase);
1112
1112
  const reachable = REACHABLE_PHASES[was.phase];
@@ -1178,7 +1178,7 @@ export class TaskStream {
1178
1178
  break;
1179
1179
  }
1180
1180
  // The rules about a known task are evaluated only once there is one.
1181
- ruleEvaluated("stream.taskUpdated.capabilitySource", "stream.taskUpdated.phase", "stream.taskUpdated.allocation", "stream.taskUpdated.mediaOpen", "stream.taskUpdated.handlingHistory", "stream.taskMediaEnded.follow", "stream.taskUpdated.media", "stream.taskUpdated.stage", "stream.taskUpdated.stage.lingering");
1181
+ ruleEvaluated("stream.taskUpdated.capabilitySource", "stream.taskUpdated.phase", "stream.taskUpdated.allocation", "stream.taskUpdated.mediaOpen", "stream.taskUpdated.interactionHistory", "stream.taskMediaEnded.follow", "stream.taskUpdated.media", "stream.taskUpdated.stage", "stream.taskUpdated.stage.lingering");
1182
1182
  // Terms once read stay read. A re-read that fails is not a new fact about the task, so the
1183
1183
  // last statement stands and the failure is a diagnostic; undetermined is a place a task
1184
1184
  // starts from, never one it returns to.
@@ -1210,7 +1210,7 @@ export class TaskStream {
1210
1210
  {
1211
1211
  const lost = TaskStream.lost(known.record, TaskStream.record(event.task));
1212
1212
  if (lost.length > 0) {
1213
- refuse("stream.taskUpdated.handlingHistory", `${at}.task.handlingHistory`, `${id}'s record lost ${lost.join(", ")} on the update: an entry read by the host stays in the record until the task ends`);
1213
+ refuse("stream.taskUpdated.interactionHistory", `${at}.task.interactionHistory`, `${id}'s record lost ${lost.join(", ")} on the update: an entry read by the host stays in the record until the task ends`);
1214
1214
  }
1215
1215
  }
1216
1216
  // A task completes after its media ends, never around it: an update that moves a task to
@@ -1319,13 +1319,13 @@ export class TaskStream {
1319
1319
  refuse("stream.taskEnded.mediaOpen", `${at}.taskId`, `${id} ended with its media still started: the audio ends first, on task-media-ended, whatever the outcome`);
1320
1320
  }
1321
1321
  // Off voice there is no media event, so the update that moves a task to completing is the
1322
- // provider's word that handling ended and the moment the wrap allowance starts. A provider
1322
+ // provider's word that interaction ended and the moment the wrap allowance starts. A provider
1323
1323
  // that completes the task itself with an allowance to run has to have started the clock:
1324
1324
  // completed from in-progress, the allowance it stated was never given.
1325
1325
  ruleEvaluated("stream.taskEnded.unwrapped");
1326
1326
  if (known.channel !== "voice" && known.completionMode === "provider-automatic" && known.wrapAllowance !== undefined && known.wrapAllowance > 0
1327
1327
  && known.phase !== "completing" && isRecord(event.outcome) && event.outcome.type === "completed") {
1328
- refuse("stream.taskEnded.unwrapped", `${at}.outcome`, `${id} was completed from ${known.phase} with a wrap allowance of ${known.wrapAllowance}s under provider-automatic: off voice, completing is the provider's word that handling ended and the allowance's start, and it was never published`);
1328
+ refuse("stream.taskEnded.unwrapped", `${at}.outcome`, `${id} was completed from ${known.phase} with a wrap allowance of ${known.wrapAllowance}s under provider-automatic: off voice, completing is the provider's word that interaction ended and the allowance's start, and it was never published`);
1329
1329
  }
1330
1330
  this.endedAllocations.add(known.allocation);
1331
1331
  this.tasks.delete(id);
@@ -1456,10 +1456,10 @@ async function driveOneCall(drive) {
1456
1456
  if (carried.allocationId !== allocationOf()) {
1457
1457
  refuse("drive.reload.allocation", "drive.reload.allocation", `a second adapter built from the same login carries ${taskId} as allocation ${String(carried.allocationId)}; the first published ${allocationOf()}, and a life does not change its name on a reload`);
1458
1458
  }
1459
- const history = carried.handlingHistory;
1459
+ const history = carried.interactionHistory;
1460
1460
  const steps = isRecord(history) && Array.isArray(history.steps) ? history.steps : [];
1461
1461
  // A record once read is not unread across a reload either: every entry the first adapter published is here.
1462
- const before = latestTask().handlingHistory;
1462
+ const before = latestTask().interactionHistory;
1463
1463
  const wasRead = isRecord(before) && Array.isArray(before.steps) ? before.steps.filter(isRecord) : [];
1464
1464
  const lost = wasRead.filter(entry => !steps.some(now => isRecord(now) && now.step === entry.step && now.at === entry.at))
1465
1465
  .map(entry => `${String(entry.step)}@${String(entry.at)}`);
@@ -1612,11 +1612,11 @@ async function driveOneCall(drive) {
1612
1612
  if (await updated(t => t.phase === "in-progress" || t.phase === "completing", "the task leaving preview after Call") === undefined)
1613
1613
  return found;
1614
1614
  }
1615
- // The other direction of step 4, wherever the task stands outside the handling phases with the
1616
- // control still declared: a host holds it back (command.phase.handling), and an adapter that
1615
+ // The other direction of step 4, wherever the task stands outside the interaction phases with the
1616
+ // control still declared: a host holds it back (command.phase.interaction), and an adapter that
1617
1617
  // receives it anyway must refuse it -- so the drive sends it past the validator and expects failed.
1618
- const holdRefusedOutsideHandling = async () => {
1619
- ruleEvaluated("drive.command.rejected", "drive.command.handling");
1618
+ const holdRefusedOutsideInteraction = async () => {
1619
+ ruleEvaluated("drive.command.rejected", "drive.command.interaction");
1620
1620
  const phase = String(latestTask().phase);
1621
1621
  let answer;
1622
1622
  try {
@@ -1626,12 +1626,12 @@ async function driveOneCall(drive) {
1626
1626
  refuse("drive.command.rejected", "drive.command.hold", `execute rejected rather than answered: ${String(error)}`);
1627
1627
  }
1628
1628
  if (isRecord(answer) && answer.status !== "failed") {
1629
- refuse("drive.command.handling", "drive.command.hold", `the provider applied hold on a ${phase} task: a control on the contact belongs to in-progress or paused, and the adapter is the second gate`);
1629
+ refuse("drive.command.interaction", "drive.command.hold", `the provider applied hold on a ${phase} task: a control on the contact belongs to in-progress or paused, and the adapter is the second gate`);
1630
1630
  }
1631
1631
  };
1632
1632
  if (latestTask().phase === "confirmed") {
1633
1633
  if (offers("hold"))
1634
- await holdRefusedOutsideHandling();
1634
+ await holdRefusedOutsideInteraction();
1635
1635
  if (await updated(t => t.phase !== "confirmed", "the task leaving confirmed") === undefined)
1636
1636
  return found;
1637
1637
  }
@@ -1661,7 +1661,7 @@ async function driveOneCall(drive) {
1661
1661
  const reportedLegs = [];
1662
1662
  /** The duration the provider's record states for the leg at `at`, as the task stands now. */
1663
1663
  const closedAs = (at) => {
1664
- const history = latestTask().handlingHistory;
1664
+ const history = latestTask().interactionHistory;
1665
1665
  const leg = isRecord(history) && Array.isArray(history.steps)
1666
1666
  ? history.steps.find(entry => isRecord(entry) && entry.step === "muted" && entry.at === canonicalTimes.get(at)) : undefined;
1667
1667
  return typeof leg?.seconds === "number" ? leg.seconds : undefined;
@@ -1671,7 +1671,7 @@ async function driveOneCall(drive) {
1671
1671
  ruleEvaluated("drive.recordStep.rejected", "drive.recordStep.failed");
1672
1672
  // The drive holds its own report to the contract before it crosses, as a host must.
1673
1673
  const leg = { taskId, allocationId: allocationOf(), step: "muted", mutedBy: "host", ...body };
1674
- const own = validateHandlingReport(leg, "drive.recordStep.report", drive.manifest);
1674
+ const own = validateInteractionReport(leg, "drive.recordStep.report", drive.manifest);
1675
1675
  found.push(...own);
1676
1676
  if (own.length > 0)
1677
1677
  return;
@@ -1775,7 +1775,7 @@ async function driveOneCall(drive) {
1775
1775
  reportedLegs.push({ at: canonicalTimes.get(openLeg.at), task: openLeg.task, closedAs: before });
1776
1776
  }
1777
1777
  if (completing !== undefined && offers("hold")) {
1778
- await holdRefusedOutsideHandling();
1778
+ await holdRefusedOutsideInteraction();
1779
1779
  }
1780
1780
  }
1781
1781
  }
@@ -1798,11 +1798,11 @@ async function driveOneCall(drive) {
1798
1798
  command.disposition = dispositions.codes[0].id;
1799
1799
  }
1800
1800
  if (await send(command) !== undefined) {
1801
- // applied says the provider has disposed of the task, and its ending follows within the bound
1801
+ // applied says the provider has completed the task, and its ending follows within the bound
1802
1802
  // the manifest stated. Past it the host resyncs: a snapshot still carrying the task is a task
1803
1803
  // held open by a provider that said it was done, and the desk shows it as unsettled.
1804
- ruleEvaluated("drive.disposal.unsettled");
1805
- const settle = Number(drive.manifest.disposalSettleMs);
1804
+ ruleEvaluated("drive.completion.unsettled");
1805
+ const settle = Number(drive.manifest.completionSettleMs);
1806
1806
  let unsettled = false;
1807
1807
  const end = await ended({ ms: settle, onExpiry: () => { unsettled = true; } });
1808
1808
  if (end !== undefined)
@@ -1813,11 +1813,11 @@ async function driveOneCall(drive) {
1813
1813
  resync = await drive.connection.snapshot();
1814
1814
  }
1815
1815
  catch (error) {
1816
- refuse("drive.command.rejected", "drive.disposal", `snapshot() after an unsettled disposal rejected: ${String(error)}`);
1816
+ refuse("drive.command.rejected", "drive.completion", `snapshot() after an unsettled completion rejected: ${String(error)}`);
1817
1817
  }
1818
1818
  const still = isRecord(resync) && Array.isArray(resync.tasks) && resync.tasks.some(t => isRecord(t) && t.id === taskId && t.allocationId === allocationOf());
1819
- refuse("drive.disposal.unsettled", "drive.disposal", still
1820
- ? `complete was applied and ${settle}ms later the task-ended has not come and a snapshot still carries ${taskId}: applied says the provider disposed of the task, and it has not`
1819
+ refuse("drive.completion.unsettled", "drive.completion", still
1820
+ ? `complete was applied and ${settle}ms later the task-ended has not come and a snapshot still carries ${taskId}: applied says the provider completed the task, and it has not`
1821
1821
  : `complete was applied and ${settle}ms later the task-ended has not come; a snapshot no longer carries ${taskId}, so the ending was owed and never sent`);
1822
1822
  }
1823
1823
  }
@@ -1842,7 +1842,7 @@ async function driveOneCall(drive) {
1842
1842
  const published = lastPublished();
1843
1843
  if (published === reported.task)
1844
1844
  continue;
1845
- const history = published.handlingHistory;
1845
+ const history = published.interactionHistory;
1846
1846
  if (isRecord(history) && Array.isArray(history.steps)) {
1847
1847
  const { at } = reported;
1848
1848
  const leg = history.steps.find(entry => isRecord(entry) && entry.step === "muted" && entry.at === at);
@@ -122,9 +122,9 @@ export declare function validateHostGuarantees(guarantees: unknown, path?: strin
122
122
  * a task, a step, when it began, how long so far if the host says, and an explicit end that
123
123
  * carries the final duration.
124
124
  */
125
- export declare function validateHandlingReport(report: unknown, path?: string, manifest?: unknown): ProtocolViolation[];
125
+ export declare function validateInteractionReport(report: unknown, path?: string, manifest?: unknown): ProtocolViolation[];
126
126
  export declare function validateHostReport(report: unknown, path?: string): ProtocolViolation[];
127
- /** Validate the exact handling/allocation target before provider command dispatch. */
127
+ /** Validate the exact interaction/allocation target before provider command dispatch. */
128
128
  export declare function validateTaskCommandRequest(request: unknown, task: unknown, path?: string, taskContext?: Omit<TaskValidationContext, "channel">): ProtocolViolation[];
129
129
  /**
130
130
  * What a command needs to be issuable, checked against the task it names: the capability the