@xema/omni-protocol 0.1.90 → 0.1.92

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
@@ -1254,13 +1254,13 @@ export interface BreakRequest {
1254
1254
  reasonId?: string;
1255
1255
  }
1256
1256
  /**
1257
- * A break placed on the agent rather than requested by them.
1257
+ * A break forced on the agent rather than requested by them.
1258
1258
  *
1259
1259
  * `by` is required in both arms. Who put somebody off the floor survives whether or not the
1260
- * break ends on a clock -- an imposed break with no origin is a state the agent cannot reason
1260
+ * break ends on a clock -- a forced break with no origin is a state the agent cannot reason
1261
1261
  * about, and one that ends on a condition is still somebody's decision.
1262
1262
  */
1263
- export type ImposedBreak = {
1263
+ export type ForcedBreak = {
1264
1264
  by: UserId;
1265
1265
  endsAutomatically: true;
1266
1266
  endsAt: IsoTimestamp;
@@ -1279,9 +1279,9 @@ export interface BreakState {
1279
1279
  retryAfterMs?: number;
1280
1280
  /** Not-ready codes this provider offers. Omitted when it defines none. */
1281
1281
  reasons?: BreakReason[];
1282
- /** Which reason the current break is on, a published `BreakReason.id`. Omitted when there is no break; required on a break in effect or starting after the task where the provider publishes `reasons`, an imposed one included. */
1282
+ /** Which reason the current break is on, a published `BreakReason.id`. Omitted when there is no break; required on a break in effect or starting after the task where the provider publishes `reasons`, a forced one included. */
1283
1283
  activeReasonId?: string;
1284
- imposed?: ImposedBreak;
1284
+ forced?: ForcedBreak;
1285
1285
  }
1286
1286
  /** Capacity is a statement, not a request: it is taken, never refused. A provider that cannot carry the count allocates within what it can and says so on a `diagnostic`. */
1287
1287
  export type CapacityResult = {
@@ -1398,9 +1398,9 @@ export type TeamBreakCommand = {
1398
1398
  type: "policy";
1399
1399
  policy: "ask" | "auto-approve" | "suspended";
1400
1400
  }
1401
- /** `reasonId` names a published `BreakReason.id`, required whenever the provider publishes reasons: the member's imposed break carries it as `activeReasonId`. */
1401
+ /** `reasonId` names a published `BreakReason.id`, required whenever the provider publishes reasons: the member's forced break carries it as `activeReasonId`. */
1402
1402
  | {
1403
- type: "place";
1403
+ type: "force";
1404
1404
  memberId: UserId;
1405
1405
  reasonId?: string;
1406
1406
  reason?: string;
@@ -1692,7 +1692,7 @@ export interface Connection<C extends Channel = Channel> {
1692
1692
  setCapacity(capacity: AgentCapacity): Promise<CapacityResult>;
1693
1693
  execute(request: TaskCommandRequest<C>): Promise<TaskCommandResult>;
1694
1694
  disconnect(): Promise<void>;
1695
- /** Required of any adapter publishing a `UserId` -- on an imposed break, a team member list, or history. */
1695
+ /** Required of any adapter publishing a `UserId` -- on a forced break, a team member list, or history. */
1696
1696
  describeUsers?(ids: UserId[]): Promise<User[]>;
1697
1697
  /** Required when the manifest declares `idleCapabilities.dial`. */
1698
1698
  dial?(request: DialRequest): Promise<DialResult>;
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 team member list, each
8
8
  * declared contribution, and each event type.
9
9
  */
10
- declare const STATE_SUBJECTS: readonly ["tasks", "task.browsers", "task.attributes", "task.interactionHistory", "task.onCall", "task.leadAssist", "task.assisting", "task.listening", "task.media", "task.acceptance", "task.outcomes", "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.listening", "task.media", "task.acceptance", "task.outcomes", "task.destinations", "task.custom", "task.locked", "break.reasons", "break.forced", "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[];
@@ -202,7 +202,7 @@ export declare class BreakStream {
202
202
  /**
203
203
  * A break follows its requests. Given a provider's stream -- seeded with its initial snapshot,
204
204
  * either supplied separately or carried by a snapshot event -- every `break-state` moves the way the guide describes: a commit's states only
205
- * after a grant, never backwards, and a break placed on the agent arriving in effect with `imposed`.
205
+ * after a grant, never backwards, and a break forced on the agent arriving in effect with `forced`.
206
206
  */
207
207
  export declare function assertBreakFollowsItsRequests(envelopes: readonly ProviderEventEnvelope[], snapshot?: Snapshot): void;
208
208
  /**
package/dist/testing.js CHANGED
@@ -23,7 +23,7 @@ const STATE_SUBJECTS = [
23
23
  "task.custom",
24
24
  "task.locked",
25
25
  "break.reasons",
26
- "break.imposed",
26
+ "break.forced",
27
27
  "team.members",
28
28
  "team.requests",
29
29
  "contacts",
@@ -85,8 +85,8 @@ function observeBreak(value, seen) {
85
85
  return;
86
86
  if (some(value.reasons))
87
87
  seen.add("break.reasons");
88
- if (value.imposed !== undefined)
89
- seen.add("break.imposed");
88
+ if (value.forced !== undefined)
89
+ seen.add("break.forced");
90
90
  }
91
91
  function observeTeam(value, seen) {
92
92
  if (!isRecord(value))
@@ -636,7 +636,7 @@ export async function exerciseAdapter(adapter, context, options = {}) {
636
636
  * its shape.
637
637
  */
638
638
  function publishesUserIds(snapshot) {
639
- if (snapshot?.break?.imposed?.by !== undefined)
639
+ if (snapshot?.break?.forced?.by !== undefined)
640
640
  return true;
641
641
  if (teamNamesUsers(snapshot?.team))
642
642
  return true;
@@ -644,12 +644,12 @@ function publishesUserIds(snapshot) {
644
644
  return false;
645
645
  return snapshot.tasks.some(taskNamesUsers);
646
646
  }
647
- /** Every UserId a snapshot publishes: on the record, the room, a lead request, an imposed break, the team member list. */
647
+ /** Every UserId a snapshot publishes: on the record, the room, a lead request, a forced break, the team member list. */
648
648
  function userIdsIn(snapshot) {
649
649
  const ids = new Set();
650
650
  const add = (value) => { if (typeof value === "string" && value.length > 0)
651
651
  ids.add(value); };
652
- add(snapshot?.break?.imposed?.by);
652
+ add(snapshot?.break?.forced?.by);
653
653
  const team = snapshot?.team;
654
654
  if (isRecord(team)) {
655
655
  for (const member of Array.isArray(team.members) ? team.members : [])
@@ -692,7 +692,7 @@ function eventNamesUsers(envelope) {
692
692
  return false;
693
693
  switch (event.type) {
694
694
  case "snapshot": return publishesUserIds(event.snapshot);
695
- case "break-state": return isRecord(event.break) && isRecord(event.break.imposed) && event.break.imposed.by !== undefined;
695
+ case "break-state": return isRecord(event.break) && isRecord(event.break.forced) && event.break.forced.by !== undefined;
696
696
  case "task-offered":
697
697
  case "task-updated": return taskNamesUsers(event.task);
698
698
  case "team-updated": return teamNamesUsers(event.team);
@@ -1867,8 +1867,8 @@ async function driveOneCall(drive) {
1867
1867
  }
1868
1868
  // A request goes not-requested -> awaiting-decision | granted; a commit goes granted ->
1869
1869
  // starting-after-task | in-effect; work ending goes starting-after-task -> in-effect; a denial,
1870
- // a cancel, an end or a release goes back to not-requested; a placed break arrives in-effect
1871
- // with `imposed`. Nothing else is a move the guide describes.
1870
+ // a cancel, an end or a release goes back to not-requested; a forced break arrives in-effect
1871
+ // with `forced`. Nothing else is a move the guide describes.
1872
1872
  /** What a stream has said about the agent's break, and the moves it may not make. */
1873
1873
  export class BreakStream {
1874
1874
  state;
@@ -1914,7 +1914,7 @@ export class BreakStream {
1914
1914
  /**
1915
1915
  * A break follows its requests. Given a provider's stream -- seeded with its initial snapshot,
1916
1916
  * either supplied separately or carried by a snapshot event -- every `break-state` moves the way the guide describes: a commit's states only
1917
- * after a grant, never backwards, and a break placed on the agent arriving in effect with `imposed`.
1917
+ * after a grant, never backwards, and a break forced on the agent arriving in effect with `forced`.
1918
1918
  */
1919
1919
  export function assertBreakFollowsItsRequests(envelopes, snapshot) {
1920
1920
  const stream = new BreakStream();
@@ -51,7 +51,7 @@ export declare function validateTask(task: unknown, context: TaskValidationConte
51
51
  export declare function validateBreakStatus(state: unknown, tasks: unknown, path?: string): ProtocolViolation[];
52
52
  /**
53
53
  * Checks lead break dispatch using current authentication, transport, team member list (`team`) and,
54
- * for place/release, the target's full `memberBreak`. The provider must authorize the target
54
+ * for force/release, the target's full `memberBreak`. The provider must authorize the target
55
55
  * and recheck the decision atomically; a team member list is not authority to act after it has changed.
56
56
  */
57
57
  export declare function validateTeamBreakCommand(request: unknown, context: unknown, path?: string): ProtocolViolation[];
@@ -1293,23 +1293,23 @@ function validateTaskInto(task, context, path, into) {
1293
1293
  // ---------------------------------------------------------------------------
1294
1294
  // Breaks, team, snapshot.
1295
1295
  // ---------------------------------------------------------------------------
1296
- function validateImposedBreak(value, path, into) {
1296
+ function validateForcedBreak(value, path, into) {
1297
1297
  if (!isPlainObject(value)) {
1298
- into.add("break.imposed.shape", path, "an imposed break must be an object");
1298
+ into.add("break.forced.shape", path, "a forced break must be an object");
1299
1299
  return;
1300
1300
  }
1301
1301
  // `by` is required either way. Who put somebody off the floor survives whether or not the
1302
- // break ends on a clock -- an imposed break with no origin is a state the agent cannot
1302
+ // break ends on a clock -- a forced break with no origin is a state the agent cannot
1303
1303
  // reason about.
1304
- into.require(isUserId(value.by), "break.imposed.by", `${path}.by`, "an imposed break must say who placed it");
1304
+ into.require(isUserId(value.by), "break.forced.by", `${path}.by`, "a forced break must say who forced it");
1305
1305
  if (value.endsAutomatically === true) {
1306
- into.timestamp(value.endsAt, "break.imposed.endsAt", `${path}.endsAt`);
1306
+ into.timestamp(value.endsAt, "break.forced.endsAt", `${path}.endsAt`);
1307
1307
  }
1308
1308
  else if (value.endsAutomatically === false) {
1309
- into.require(value.endsAt === undefined, "break.imposed.endsAt.unexpected", `${path}.endsAt`, "a break that does not end automatically must not carry an end time");
1309
+ into.require(value.endsAt === undefined, "break.forced.endsAt.unexpected", `${path}.endsAt`, "a break that does not end automatically must not carry an end time");
1310
1310
  }
1311
1311
  else {
1312
- into.add("break.imposed.endsAutomatically", `${path}.endsAutomatically`, "an imposed break must say whether it ends automatically");
1312
+ into.add("break.forced.endsAutomatically", `${path}.endsAutomatically`, "a forced break must say whether it ends automatically");
1313
1313
  }
1314
1314
  }
1315
1315
  /** Checks break status against the full currently retained task set, after a transaction. */
@@ -1350,7 +1350,7 @@ export function validateBreakStatus(state, tasks, path = "snapshot") {
1350
1350
  }
1351
1351
  /**
1352
1352
  * Checks lead break dispatch using current authentication, transport, team member list (`team`) and,
1353
- * for place/release, the target's full `memberBreak`. The provider must authorize the target
1353
+ * for force/release, the target's full `memberBreak`. The provider must authorize the target
1354
1354
  * and recheck the decision atomically; a team member list is not authority to act after it has changed.
1355
1355
  */
1356
1356
  export function validateTeamBreakCommand(request, context, path = "teamBreakCommand") {
@@ -1367,7 +1367,7 @@ export function validateTeamBreakCommand(request, context, path = "teamBreakComm
1367
1367
  const command = request.command;
1368
1368
  const allowed = {
1369
1369
  decide: ["type", "memberId", "decision", "reason"], policy: ["type", "policy"],
1370
- place: ["type", "memberId", "reasonId", "reason"], release: ["type", "memberId"],
1370
+ force: ["type", "memberId", "reasonId", "reason"], release: ["type", "memberId"],
1371
1371
  };
1372
1372
  const fields = typeof command.type === "string" && Object.hasOwn(allowed, command.type) ? allowed[command.type] : undefined;
1373
1373
  if (!fields) {
@@ -1398,12 +1398,12 @@ export function validateTeamBreakCommand(request, context, path = "teamBreakComm
1398
1398
  validateBreakState(context.memberBreak, `${path}.memberBreak`, into);
1399
1399
  const state = isPlainObject(context.memberBreak) ? context.memberBreak : {};
1400
1400
  if (command.type === "release")
1401
- into.require(state.imposed !== undefined && (state.approval === "in-effect" || state.approval === "starting-after-task"), "team.break.command.release", path, "release a currently imposed break");
1402
- if (command.type === "place") {
1401
+ into.require(state.forced !== undefined && (state.approval === "in-effect" || state.approval === "starting-after-task"), "team.break.command.release", path, "release a currently forced break");
1402
+ if (command.type === "force") {
1403
1403
  if (command.reasonId !== undefined)
1404
1404
  into.filled(command.reasonId, "team.break.command.reasonId", path, "reasonId must not be empty");
1405
1405
  const reasons = Array.isArray(state.reasons) ? state.reasons : [];
1406
- into.require(state.reasons === undefined ? command.reasonId === undefined : reasons.some((r) => isPlainObject(r) && r.id === command.reasonId), "team.break.command.reasonId", path, "place uses the target's currently published reason codes");
1406
+ into.require(state.reasons === undefined ? command.reasonId === undefined : reasons.some((r) => isPlainObject(r) && r.id === command.reasonId), "team.break.command.reasonId", path, "force uses the target's currently published reason codes");
1407
1407
  }
1408
1408
  }
1409
1409
  return into.violations;
@@ -1459,7 +1459,7 @@ export function validateBreakCommand(method, request, state, context, path = "br
1459
1459
  into.require(approval === "awaiting-decision" || approval === "granted", "break.command.cancel.precommit", path, "cancel only a pre-commit request; a raced commit requires commit recovery");
1460
1460
  if (method === "endBreak") {
1461
1461
  into.require(approval === "starting-after-task" || approval === "in-effect", "break.command.end.started", path, "end a committed break, including a returning provider still finishing work");
1462
- into.require(state.imposed === undefined, "break.command.end.imposed", path, "an agent cannot end an imposed break; an authorized lead releases it");
1462
+ into.require(state.forced === undefined, "break.command.end.forced", path, "an agent cannot end a forced break; an authorized lead releases it");
1463
1463
  }
1464
1464
  }
1465
1465
  return into.violations;
@@ -1480,7 +1480,7 @@ export function validateBreakTransition(before, after, path = "break") {
1480
1480
  const from = before.approval;
1481
1481
  const to = after.approval;
1482
1482
  const committed = to === "starting-after-task" || to === "in-effect";
1483
- if (committed && (from === "not-requested" || from === "awaiting-decision") && after.imposed === undefined) {
1483
+ if (committed && (from === "not-requested" || from === "awaiting-decision") && after.forced === undefined) {
1484
1484
  into.add("stream.breakState.commitBeforeGrant", `${path}.after.approval`, `${to} follows a commit, and a commit follows granted; the break stood at ${from}`);
1485
1485
  }
1486
1486
  const backwards = (from === "in-effect" && (to === "awaiting-decision" || to === "granted" || to === "starting-after-task")) ||
@@ -1495,6 +1495,7 @@ function validateBreakState(value, path, into) {
1495
1495
  into.add("break.shape", path, "break state must be an object");
1496
1496
  return;
1497
1497
  }
1498
+ into.require(!Object.hasOwn(value, "imposed"), "break.forced.renamed", `${path}.imposed`, "imposed was renamed to forced; use only forced");
1498
1499
  into.oneOf(value.approval, BREAK_APPROVALS, "break.approval", `${path}.approval`);
1499
1500
  into.require(typeof value.mayAsk === "boolean", "break.mayAsk", `${path}.mayAsk`, "mayAsk says whether the agent may ask for a break: a boolean");
1500
1501
  for (const field of ["refusedReason", "decisionReason"]) {
@@ -1515,12 +1516,12 @@ function validateBreakState(value, path, into) {
1515
1516
  // break that is not happening.
1516
1517
  into.require(value.approval !== "not-requested", "break.activeReasonId.approval", `${path}.activeReasonId`, "activeReasonId must be omitted when no break is requested or in effect");
1517
1518
  }
1518
- if (value.imposed !== undefined) {
1519
- validateImposedBreak(value.imposed, `${path}.imposed`, into);
1520
- // An imposed break is a break somebody placed; beside `not-requested` there is no break.
1521
- // An imposed break is a break in progress or about to be: it travels with in-effect or
1519
+ if (value.forced !== undefined) {
1520
+ validateForcedBreak(value.forced, `${path}.forced`, into);
1521
+ // A forced break is a break somebody forced; beside `not-requested` there is no break.
1522
+ // A forced break is a break in progress or about to be: it travels with in-effect or
1522
1523
  // starting-after-task and nothing else. Beside granted, the host would commit a break nobody asked for.
1523
- into.require(value.approval === "in-effect" || value.approval === "starting-after-task", "break.imposed.approval", `${path}.imposed`, `an imposed break is in effect or starting after the task; ${describeValue(value.approval)} says the agent asked, or that there is none`);
1524
+ into.require(value.approval === "in-effect" || value.approval === "starting-after-task", "break.forced.approval", `${path}.forced`, `a forced break is in effect or starting after the task; ${describeValue(value.approval)} says the agent asked, or that there is none`);
1524
1525
  }
1525
1526
  if (value.reasons === undefined)
1526
1527
  return;
@@ -1556,7 +1557,7 @@ function validateBreakState(value, path, into) {
1556
1557
  into.require(seen.has(value.activeReasonId), "break.activeReasonId.known", `${path}.activeReasonId`, `activeReasonId names a reason the provider did not publish: ${value.activeReasonId}`);
1557
1558
  }
1558
1559
  // A break in effect, or about to be, on a provider that publishes reasons is on one of them: an
1559
- // imposed one included, since the lead's place named it. A break of no kind is a break whose
1560
+ // forced one included, since the lead's force command named it. A break of no kind is a break whose
1560
1561
  // rules -- who may listen through it, whether it counts -- nobody can apply.
1561
1562
  ruleEvaluated("break.activeReasonId.required");
1562
1563
  if (seen.size > 0 && (value.approval === "in-effect" || value.approval === "starting-after-task")) {
package/guide.md CHANGED
@@ -949,7 +949,7 @@ type BreakRequest = {
949
949
  reasonId?: string;
950
950
  };
951
951
 
952
- type ImposedBreak =
952
+ type ForcedBreak =
953
953
  | { by: UserId; endsAutomatically: true; endsAt: IsoTimestamp }
954
954
  | { by: UserId; endsAutomatically: false; endsAt?: never };
955
955
 
@@ -961,7 +961,7 @@ type BreakState = {
961
961
  retryAfterMs?: number;
962
962
  reasons?: BreakReason[];
963
963
  activeReasonId?: string;
964
- imposed?: ImposedBreak;
964
+ forced?: ForcedBreak;
965
965
  };
966
966
 
967
967
  type BreakRequestResult =
@@ -1036,7 +1036,7 @@ type TeamLeadAssistCommand =
1036
1036
  type TeamBreakCommand =
1037
1037
  | { type: "decide"; memberId: UserId; decision: "granted" | "denied"; reason?: string }
1038
1038
  | { type: "policy"; policy: "ask" | "auto-approve" | "suspended" }
1039
- | { type: "place"; memberId: UserId; reasonId?: string; reason?: string }
1039
+ | { type: "force"; memberId: UserId; reasonId?: string; reason?: string }
1040
1040
  | { type: "release"; memberId: UserId };
1041
1041
 
1042
1042
  type TeamBreakCommandRequest = {
@@ -1924,7 +1924,7 @@ them from what arrives later.
1924
1924
  | --- | --- |
1925
1925
  | `breaks` | This login may request a break. Requires the four break methods on the connection. |
1926
1926
  | `team` | This login leads a team. The provider publishes a `TeamMembers` object to it on every snapshot — `members: []` when nobody is in it — and to nobody else. |
1927
- | `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`. |
1927
+ | `team.breakControl` | This lead may act on their team's breaks through `executeTeamBreak` — force, 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`. |
1928
1928
  | `team.leadAssistControl` | This lead may join a member's call on request. Requires `executeTeamLeadAssist`. |
1929
1929
  | `team.listeningControl` | This lead may listen to a member's call unasked, in the listed modes and no others: `listen`, `coach`, `join-call`. The list always includes `listen`, since the other two begin from one. Requires `executeTeamListen`. See **Listening to a call**. |
1930
1930
  | `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 team member list carries `policies`. |
@@ -2023,7 +2023,7 @@ failure:
2023
2023
  ```
2024
2024
 
2025
2025
  The `User` it carries is the **root of this provider's user namespace**. Every other person this
2026
- provider names — a team member, the manager on an imposed break, the agent on an interaction step —
2026
+ provider names — a team member, the manager on a forced break, the agent on an interaction step —
2027
2027
  is identified from the same directory and carries the same `UserId` type.
2028
2028
 
2029
2029
  | Field | Contract |
@@ -2131,7 +2131,7 @@ a capability it agrees with the login: a lead's snapshot carries `team`, nobody
2131
2131
  | --- | --- |
2132
2132
  | `transport` | Current `TransportStatus` — whether this provider's transport can serve the login. Defined under **`transport-status`**. |
2133
2133
  | `loginId` | Identity of this login. It must match the connection context. |
2134
- | `break` | Complete break state, including approval, whether the agent may ask, reasons, retry details, and any imposed break. |
2134
+ | `break` | Complete break state, including approval, whether the agent may ask, reasons, retry details, and any forced break. |
2135
2135
  | `tasks` | Complete set of tasks currently offered to or owned by this agent. |
2136
2136
  | `taskCount` | The provider's own count of those tasks, stated rather than inferred, and it must equal `tasks.length`. A snapshot with no work says `taskCount: 0` in so many words — a blank or unanswered state lacks the count and cannot pass as a confirmed empty. |
2137
2137
  | `contacts` | Required complete contact contribution when the manifest declares `contacts`; `[]` clears it. Omitted only when it does not. |
@@ -2153,7 +2153,7 @@ surface in one place, and what obliges an adapter to implement each one.
2153
2153
  | `refused(report)` | Always. The host tells the adapter what it would not take -- a snapshot it did not replace its state with, an event it dropped -- with every rule broken, so a refusal is visible on both sides. See **What the host does with what it refuses**. |
2154
2154
  | `setCapacity(capacity)` | Always. Nothing may be allocated until a capacity is stated, so there is no connection that does not receive it. |
2155
2155
  | `execute(request)` | Always. Every channel has commands no capability gates — see **Which commands need a capability**. |
2156
- | `describeUsers(ids)` | The adapter publishes any `UserId`: on `ImposedBreak.by`, a team member list, or `interactionHistory[].by`. Each `User` carries its `timeZone`; a person whose zone the provider cannot name is omitted from the answer, as any unresolvable id is. |
2156
+ | `describeUsers(ids)` | The adapter publishes any `UserId`: on `ForcedBreak.by`, a team member list, or `interactionHistory[].by`. Each `User` carries its `timeZone`; a person whose zone the provider cannot name is omitted from the answer, as any unresolvable id is. |
2157
2157
  | `dial(request)` | The manifest declares `idleCapabilities.dial`, and with it `dialOutcomes`. |
2158
2158
  | `requestBreak(request)` | The login declares `capabilities.breaks`. |
2159
2159
  | `commitBreak()` | The login declares `capabilities.breaks`. Commit and cancel are not optional halves of it. |
@@ -2211,7 +2211,7 @@ Turns `UserId` values into something an agent can read.
2211
2211
  describeUsers(ids: UserId[]): Promise<User[]>
2212
2212
  ```
2213
2213
 
2214
- Required of any adapter that publishes a `UserId` — on `ImposedBreak.by`, a team member list, or
2214
+ Required of any adapter that publishes a `UserId` — on `ForcedBreak.by`, a team member list, or
2215
2215
  `interactionHistory[].by`. Publishing an identifier Omni cannot resolve puts a name on screen
2216
2216
  that reads as a database key.
2217
2217
 
@@ -2856,7 +2856,7 @@ Four rules a provider has to keep:
2856
2856
  conversation, and on live data that is the ordinary case rather than an edge. A zero is rejected.
2857
2857
  - **`by` is a bare `UserId`, and not necessarily an agent.** A lead or a manager takes part
2858
2858
  during an interaction too — a transfer accepted, a call conferenced in — so the field names whoever it was,
2859
- the same way `ImposedBreak.by` does. It comes from this provider's own directory, the same
2859
+ the same way `ForcedBreak.by` does. It comes from this provider's own directory, the same
2860
2860
  namespace as `AuthenticationState.identity.id` and the team member list, so entries pair
2861
2861
  within a provider and never across one.
2862
2862
  - **A task carries no names.** Omni resolves what to display with `describeUsers()`. Two people
@@ -3613,8 +3613,8 @@ nothing while none are being accepted — so they are not published separately.
3613
3613
  | `decisionReason` | The words whoever decided attached, from `decide.reason`. About one request and one decision, not a standing gate. |
3614
3614
  | `retryAfterMs` | How long until the agent may retry, when the provider can say. |
3615
3615
  | `reasons` | Not-ready codes this provider offers. Omitted when it defines none; an empty list is refused, being a second spelling of the same fact. |
3616
- | `activeReasonId` | The `BreakReason.id` the current break is on. Omitted when there is no break. Required on a break `in-effect` or `starting-after-task` where the provider publishes `reasons`, an imposed break included (`break.activeReasonId.required`): a break with a kind the provider cannot name is a break whose rules nobody can apply. |
3617
- | `imposed` | Set when the break was placed on the agent rather than requested. |
3616
+ | `activeReasonId` | The `BreakReason.id` the current break is on. Omitted when there is no break. Required on a break `in-effect` or `starting-after-task` where the provider publishes `reasons`, a forced break included (`break.activeReasonId.required`): a break with a kind the provider cannot name is a break whose rules nobody can apply. |
3617
+ | `forced` | Set when the break was forced on the agent rather than requested. |
3618
3618
 
3619
3619
  A request can be waiting for two unrelated things, and they are separate values because
3620
3620
  rendering one as the other tells an agent to wait for somebody who is never coming:
@@ -3640,42 +3640,50 @@ it; it sends the commit again only from a reconnect snapshot that shows the gran
3640
3640
  refused. A `BreakReason` marked `alwaysAvailable` survives it: a mandatory rest period is not
3641
3641
  something a busy hour can cancel, and Omni keeps offering those while the rest are withdrawn.
3642
3642
 
3643
- ### Imposed breaks
3643
+ ### Forced breaks
3644
3644
 
3645
- `ImposedBreak` says who placed the break, whether automatic ending is enabled, and, when enabled,
3645
+ Migration: ImposedBreak is now `ForcedBreak`, and the former imposed field is now
3646
+ `BreakState.forced`. Diagnostic and harness coverage names use `break.forced`; the agent-end
3647
+ diagnostic is `break.command.end.forced`. Update hosts and providers together. The old field
3648
+ is rejected, including when both spellings are sent; no compatibility alias is provided.
3649
+ The team break command formerly named place is now `force`. Hosts and providers must
3650
+ adopt the new command together; the former spelling is rejected without an alias.
3651
+ The `release` command and break ordering rules are unchanged.
3652
+
3653
+ `ForcedBreak` says who forced the break, whether automatic ending is enabled, and, when enabled,
3646
3654
  when the provider will end it. A break the agent did not choose is not manually resumable by them.
3647
3655
 
3648
- **Every imposed break has a person behind it.** A lead or a manager placed it; there is no such
3649
- thing as a break the platform imposed on its own. Where a platform applies one automatically, it is
3656
+ **Every forced break has a person behind it.** A lead or a manager forced it; there is no such
3657
+ thing as a break the platform forced on its own. Where a platform applies one automatically, it is
3650
3658
  executing a preference somebody configured, and that person is the owner of the action — `by` names
3651
3659
  them, not the machinery that carried it out.
3652
3660
 
3653
3661
  Omni resolves the name to show with `describeUsers()`, so a provider sends the identifier and never
3654
3662
  a display name.
3655
3663
 
3656
- **An imposed break travels with `in-effect` or `starting-after-task`, and nothing else.** It is a
3664
+ **A forced break travels with `in-effect` or `starting-after-task`, and nothing else.** It is a
3657
3665
  break in progress or about to be; beside `granted` or `awaiting-decision` the host would read a
3658
- request the agent never made and commit it (`break.imposed.approval`). Where the provider publishes
3659
- `reasons`, the lead's `place` named one, and the member's state carries it as `activeReasonId`.
3666
+ request the agent never made and commit it (`break.forced.approval`). Where the provider publishes
3667
+ `reasons`, the lead's `force` named one, and the member's state carries it as `activeReasonId`.
3660
3668
 
3661
3669
  For example:
3662
3670
 
3663
3671
  ```ts
3664
- imposed: {
3672
+ forced: {
3665
3673
  by: "manager-1042",
3666
3674
  endsAutomatically: true,
3667
3675
  endsAt: "2026-08-21T10:00:00.000Z"
3668
3676
  }
3669
3677
  ```
3670
3678
 
3671
- The presence of `imposed` means the agent cannot end the break manually, so Omni withdraws its
3679
+ The presence of `forced` means the agent cannot end the break manually, so Omni withdraws its
3672
3680
  Resume control from that agent. With `endsAutomatically: true`, the provider ends the break at
3673
3681
  `endsAt`; with `endsAutomatically: false`, it does not end the break on a timer. An authorized lead
3674
- may end either form with **Resume**, not only whoever placed it. Omni shows **Stopped by <who>**,
3682
+ may end either form with **Resume**, not only whoever forced it. Omni shows **Stopped by <who>**,
3675
3683
  resolving the name with `describeUsers()`, and shows when the break will end where automatic ending
3676
3684
  is enabled.
3677
3685
 
3678
- A break applies to the **agent**, not to one provider. When a provider imposes one, Omni immediately
3686
+ A break applies to the **agent**, not to one provider. When a provider forces one, Omni immediately
3679
3687
  requests a break on every other connected provider, or they would keep routing work to somebody who
3680
3688
  is not there. Providers should expect that follow-on request.
3681
3689
 
@@ -3760,13 +3768,13 @@ request object only to the request method, and undefined to the other three.
3760
3768
  | `requestBreak` | `not-requested`; selected current reason code when codes exist; `mayAsk` or the selected reason's `alwaysAvailable` exception. Free text does not replace a code. |
3761
3769
  | `commitBreak` | `granted`, or already committed for an idempotent repeat. A later change to `mayAsk` does not revoke the grant. |
3762
3770
  | `cancelBreak` | `awaiting-decision` or `granted`. A concurrent commit winning still answers `omni.break-already-committed` and requires recovery. |
3763
- | `endBreak` | `in-effect` or `starting-after-task` during reconciliation; an agent cannot end an imposed break. |
3771
+ | `endBreak` | `in-effect` or `starting-after-task` during reconciliation; an agent cannot end a forced break. |
3764
3772
 
3765
- Use `validateTeamBreakCommand(request, context)` for lead decisions, placement, release and
3773
+ Use `validateTeamBreakCommand(request, context)` for lead decisions, forcing a break, release and
3766
3774
  policy commands. It requires the live lead capability and active transport, a current target
3767
- team member list for member commands, and the target's complete break state for placement/release.
3768
- Approve/deny requires an awaiting decision; placement uses the target's reason codes; release
3769
- requires an imposed committed break. The context's target state must belong to the named member;
3775
+ team member list for member commands, and the target's complete break state for forcing/releasing a break.
3776
+ Approve/deny requires an awaiting decision; forcing a break uses the target's reason codes; release
3777
+ requires a forced committed break. The context's target state must belong to the named member;
3770
3778
  that association and backend authorization are provider responsibilities.
3771
3779
 
3772
3780
  Use `validateBreakStatus(state, tasks)` on the complete retained task view after each transaction,
@@ -3854,8 +3862,8 @@ and the other scheduled maintenance, match on it, and nothing could tell the dif
3854
3862
  specificity: about this agent's own work is `coaching`, else something they must know afterwards is
3855
3863
  `training`, else `meeting`.
3856
3864
 
3857
- **A break somebody placed on the agent is not automatically one of these.** Where the agent was
3858
- stopped rather than choosing to stop — see **Imposed breaks** — set `BreakState.imposed` and prefer
3865
+ **A break somebody forced on the agent is not automatically one of these.** Where the agent was
3866
+ stopped rather than choosing to stop — see **Forced breaks** — set `BreakState.forced` and prefer
3859
3867
  omitting `kind` to reaching for `other`. None of the ten describes "something was done to this
3860
3868
  agent", and `other` claims a classification that was never made.
3861
3869
 
@@ -3984,8 +3992,8 @@ If cancel races with a late approval, the provider remains on the cancel path. I
3984
3992
  won, cancel returns `omni.break-already-committed`, and Omni resumes commit recovery rather than
3985
3993
  returning the agent to `working`.
3986
3994
 
3987
- An imposed break is not rolled back by this algorithm. If one appears during either the request or
3988
- cancellation, Omni follows the imposed-break rule on the other providers and commits each grant as
3995
+ A forced break is not rolled back by this algorithm. If one appears during either the request or
3996
+ cancellation, Omni follows the forced-break rule on the other providers and commits each grant as
3989
3997
  soon as it reaches `granted`; it does not wait for unanimity because the agent has already
3990
3998
  stopped elsewhere.
3991
3999
 
@@ -4094,8 +4102,8 @@ One method, `executeTeamBreak`, taking a discriminated command exactly as `execu
4094
4102
  | --- | --- |
4095
4103
  | `{ type: "decide", memberId: UserId, decision, reason? }` | Settles one pending request. `decision` is `granted` or `denied`. A grant moves the member to `granted`; a denial ends the request and moves it directly to `not-requested`. |
4096
4104
  | `{ type: "policy", policy }` | `ask`, `auto-approve`, or `suspended`. |
4097
- | `{ type: "place", memberId: UserId, reasonId?, reason? }` | Puts a member on a break they did not ask for. `reasonId` names a published `BreakReason.id` and is required whenever the provider publishes `reasons`; the member's imposed break carries it as `activeReasonId`, so its kind is known. |
4098
- | `{ type: "release", memberId: UserId }` | Ends an imposed break on that member, whoever placed it. |
4105
+ | `{ type: "force", memberId: UserId, reasonId?, reason? }` | Puts a member on a break they did not ask for. `reasonId` names a published `BreakReason.id` and is required whenever the provider publishes `reasons`; the member's forced break carries it as `activeReasonId`, so its kind is known. |
4106
+ | `{ type: "release", memberId: UserId }` | Ends a forced break on that member, whoever forced it. |
4099
4107
 
4100
4108
  `memberId` is this provider's own identifier for the member, as published on its team member list. It is
4101
4109
  never an identifier from another provider, and Omni does not translate between them; names come
@@ -4103,7 +4111,7 @@ from `describeUsers()`.
4103
4111
 
4104
4112
  `suspended` means requests are **rejected outright** rather than left pending — nobody is coming to
4105
4113
  approve them. A provider that suspends breaks must also publish `mayAsk: false` to the team's
4106
- agents so they see it before asking. A `place` must likewise reach that member as an `imposed` break
4114
+ agents so they see it before asking. A `force` must likewise reach that member as a `forced` break
4107
4115
  on their own `BreakState`, or they are stopped from working with no way to see why.
4108
4116
 
4109
4117
  What happens when no lead is online — auto-approving, for instance — is the provider's decision and is
@@ -4938,11 +4946,11 @@ covers a `diagnostic`: shown where the agent works, and counted.
4938
4946
  Replaces this provider's complete `break` object. Its `approval` uses the canonical
4939
4947
  `not-requested`, `awaiting-decision`, `granted`, `starting-after-task` and
4940
4948
  `in-effect` states defined under Breaks; the event also carries the corresponding may-ask state,
4941
- reasons, retry details, and any imposed break.
4949
+ reasons, retry details, and any forced break.
4942
4950
 
4943
4951
  Each state is also held to the one before it. A commit's states, `starting-after-task` and
4944
- `in-effect`, follow a grant — the one arrival in a committed state nobody asked for is a placed
4945
- break, which says so with `imposed`: in effect at once, or `starting-after-task` while the member
4952
+ `in-effect`, follow a grant — the one arrival in a committed state nobody asked for is a forced
4953
+ break, which says so with `forced`: in effect at once, or `starting-after-task` while the member
4946
4954
  finishes the call they are on (`stream.breakState.commitBeforeGrant`); and a break never moves backwards —
4947
4955
  from `in-effect` or `starting-after-task` to a grant or a request, or from `granted` to
4948
4956
  `awaiting-decision` — a new request passes through `not-requested` (`stream.breakState.backwards`).
@@ -4975,8 +4983,8 @@ Normal order is `not-requested` → `awaiting-decision` → `granted` →
4975
4983
  `starting-after-task` → `in-effect` → `not-requested`. Auto-approval may go directly to
4976
4984
  `granted`; a commit with no outstanding work may go directly to `in-effect`. Denial/cancel
4977
4985
  returns a precommit request to `not-requested`; an authorized end/release returns a committed
4978
- break there. Same-state restatements are allowed. An evidenced imposed break is the explicit
4979
- exception to requesting/granting, and must carry its imposed actor/state. Neither skipped
4986
+ break there. Same-state restatements are allowed. An evidenced forced break is the explicit
4987
+ exception to requesting/granting, and must carry its forced actor/state. Neither skipped
4980
4988
  publication nor a host-local guess creates another exception. A later normal attempt starts
4981
4989
  from `not-requested`, never by regressing an active break into a request.
4982
4990
 
@@ -5145,7 +5153,7 @@ issued by each provider independently, so two providers will eventually issue th
5145
5153
  different people. Encode and join before storing or comparing.
5146
5154
 
5147
5155
  Use it for every `UserId` — `interactionHistory[].by`, team members, `memberId` on a
5148
- lead command, `ImposedBreak.by`. A bare one is only ever compared against another from the **same** provider; anything
5156
+ lead command, `ForcedBreak.by`. A bare one is only ever compared against another from the **same** provider; anything
5149
5157
  wider goes through this key.
5150
5158
 
5151
5159
  ### `sameCapabilities(a, b)`
@@ -5256,7 +5264,7 @@ A capability granted by a later login requires its methods just as one declared
5256
5264
 
5257
5265
  `result.notExercised` lists what the run never reached — one subject per family of rules: each
5258
5266
  optional part of a task (`task.browsers`, `task.interactionHistory`, `task.leadAssist`, …), the break's
5259
- `reasons` and `imposed`, the team member list's `members` and `requests`, each declared contribution, and
5267
+ `reasons` and `forced`, the team member list's `members` and `requests`, each declared contribution, and
5260
5268
  each event type (`event.task-ended`, …) — and so what a clean `violations` says nothing about.
5261
5269
  Nothing there is a violation: an adapter with no team has nothing to exercise. But a fixture with
5262
5270
  no tasks exercises no task rule, and a pass over it reads as coverage it is not.
@@ -5374,7 +5382,7 @@ cannot be established from TypeScript structure alone.
5374
5382
  | `assertReconnectWithMissedAssignments(before, reconnect, ids)` | A reconnect snapshot restores assignments received while offline. |
5375
5383
  | `stillHost(report?, guarantees?, mute?)` | A host that reports one thing and never changes, for a test context: `{ online: true }` by default, a report with audio for a softphone voice adapter, and never for a desk phone. |
5376
5384
  | `TaskStream`, `BreakStream` | The cross-event models the harness applies after the connect snapshot, exported for a host that wants the same rules at its boundary: `seed(snapshot)`, then `apply(envelope)` returns the violations. |
5377
- | `assertBreakFollowsItsRequests(envelopes, snapshot?)` | A break follows its requests: a commit's states only after a grant, never backwards, and a placed break arriving in effect with `imposed`. The harness applies the same rules after the connect snapshot. |
5385
+ | `assertBreakFollowsItsRequests(envelopes, snapshot?)` | A break follows its requests: a commit's states only after a grant, never backwards, and a forced break arriving in effect with `forced`. The harness applies the same rules after the connect snapshot. |
5378
5386
  | `assertMediaFollowsTheTask(envelopes, snapshot?)` | The media follows the task and never decides it: every task is introduced once, `task-media-started` and `task-media-ended` alternate on work that has begun, media ends only where it arrived, and what follows the media ending is `completing` or `task-ended`. The harness applies the same rules to every event after the connect snapshot (`stream.*`). A sequence with no media satisfies it by never testing it — pair it with the assertion that the media end is present. |
5379
5387
  | `assertBreakAttemptProviders(candidates, asked)` | A break attempt asks every usable provider holding capacity, `refreshing` included, and nothing of a provider whose login is `expired`. |
5380
5388
  | `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. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",