@xema/omni-protocol 0.1.99 → 0.1.101

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/guide.md CHANGED
@@ -50,7 +50,7 @@ state use `status`; name the object when discussing them to avoid ambiguity.
50
50
  | Word | Belongs to | Values |
51
51
  | --- | --- | --- |
52
52
  | `phase` | A task | `pending`, `confirmed`, `preview`, `in-progress`, `paused`, `completing` |
53
- | `media` | A task's audio | `started`, `ended` |
53
+ | `audio` | A task's audio | `started`, `ended` |
54
54
  | `transport` | A connection | `connecting`, `active`, `error` |
55
55
  | `status` | An authentication session | `signed-out`, `authenticating`, `authenticated`, `refreshing`, `expired` |
56
56
  | `status` | The agent’s break lifecycle | `not-requested`, `awaiting-approval`, `granted`, `starting-after-task`, `on-break` |
@@ -302,7 +302,7 @@ interface RecordingActions {
302
302
  }
303
303
  interface TaskRecordingPolicy {
304
304
  provider?: RecordingActions;
305
- host?: RecordingActions & { destinationId: string };
305
+ host?: RecordingActions & { storageId: string };
306
306
  }
307
307
  type RecordingState =
308
308
  | { status: "unknown"; observationId?: never; observedAt?: never; validUntil?: never; recordingId?: never }
@@ -326,7 +326,7 @@ interface HostRecordingReport {
326
326
  interface HostRecording {
327
327
  announcesToCaller?: true;
328
328
  actions: RecordingAction[];
329
- destinationIds: string[];
329
+ storageIds: string[];
330
330
  execute(request: HostRecordingRequest): Promise<RecordingCommandResult>;
331
331
  }
332
332
  type RecordingCommandResult = Exclude<TaskCommandResult, { status: "dialling" }>;
@@ -479,7 +479,7 @@ type Snapshot = {
479
479
  tasks: Task[];
480
480
  taskCount: number;
481
481
  contacts?: Contact[];
482
- scheduledActivities?: ScheduledActivity[];
482
+ calendar?: ScheduledActivity[];
483
483
  team?: TeamMembers;
484
484
  };
485
485
 
@@ -710,9 +710,9 @@ type Locked = {
710
710
 
711
711
  type Lockable<T> = T | Locked;
712
712
 
713
- type TaskMediaState = "started" | "ended";
713
+ type TaskAudioState = "started" | "ended";
714
714
 
715
- type CapabilitySource = "queue" | "ungoverned" | "undetermined";
715
+ type CapabilitySource = "queue" | "nobody" | "not-yet-read";
716
716
 
717
717
  type Task<C extends Channel = Channel> = {
718
718
  assignmentId: AssignmentId;
@@ -732,11 +732,11 @@ type Task<C extends Channel = Channel> = {
732
732
  history?: TaskHistory;
733
733
  } & TaskCompletion & (
734
734
  C extends "voice"
735
- ? { recording?: { provider?: RecordingState }; onCall?: OnCall[]; leadAssist?: TaskLeadAssist; takenOver?: TaskTakenOver; media?: TaskMediaState }
736
- : { recording?: never; onCall?: never; leadAssist?: never; takenOver?: never; media?: never }
735
+ ? { recording?: { provider?: RecordingState }; onCall?: OnCall[]; leadAssist?: TaskLeadAssist; takenOver?: TaskTakenOver; audio?: TaskAudioState }
736
+ : { recording?: never; onCall?: never; leadAssist?: never; takenOver?: never; audio?: never }
737
737
  );
738
738
 
739
- type PreviewDeadline = "calls" | "host-calls" | "waits";
739
+ type PreviewDeadline = "provider-dials" | "host-dials" | "waits";
740
740
 
741
741
  type AcceptanceMode =
742
742
  | "no-preference"
@@ -759,7 +759,7 @@ const TASK_COMMAND_NAMES = {
759
759
  voice: [
760
760
  "answer",
761
761
  "decline",
762
- "call",
762
+ "dial",
763
763
  "hold",
764
764
  "resume",
765
765
  "end-call",
@@ -783,7 +783,7 @@ type OutcomePayload = { outcome?: string; notes?: string };
783
783
  type VoiceTaskCommand =
784
784
  | { type: "answer" }
785
785
  | { type: "decline" }
786
- | { type: "call"; dialId: DialId }
786
+ | { type: "dial"; dialId: DialId }
787
787
  | { type: "hold" }
788
788
  | { type: "resume" }
789
789
  | { type: "end-call" }
@@ -968,7 +968,7 @@ type BreakEndResult =
968
968
  ### Team
969
969
 
970
970
  ```ts
971
- type TeamMemberAvailability = "ready" | "on-task" | "on-break" | "reserved" | "signed-out";
971
+ type TeamMemberAvailability = "ready" | "on-task" | "on-break" | "elsewhere" | "signed-out";
972
972
 
973
973
  type TeamMember = {
974
974
  id: UserId;
@@ -977,6 +977,7 @@ type TeamMember = {
977
977
  break?: Extract<BreakStatus, "awaiting-approval" | "granted" | "starting-after-task">;
978
978
  tasks?: MemberTask[];
979
979
  listening?: MemberListening;
980
+ shift?: MemberShift;
980
981
  };
981
982
 
982
983
  type LeadRequest = {
@@ -1002,14 +1003,32 @@ type MemberTask<C extends Channel = Channel> = {
1002
1003
  completionMode?: CompletionMode;
1003
1004
  wrapAllowance?: DurationSeconds;
1004
1005
  } & (C extends "voice"
1005
- ? { onCall?: OnCall[]; leadAssist?: TaskLeadAssist; takenOver?: TaskTakenOver; media?: TaskMediaState }
1006
- : { onCall?: never; leadAssist?: never; takenOver?: never; media?: never });
1006
+ ? { onCall?: OnCall[]; leadAssist?: TaskLeadAssist; takenOver?: TaskTakenOver; audio?: TaskAudioState }
1007
+ : { onCall?: never; leadAssist?: never; takenOver?: never; audio?: never });
1007
1008
 
1008
1009
  type MemberListening = {
1010
+ assignmentId: AssignmentId;
1009
1011
  mode: ListeningMode;
1010
1012
  since: IsoTimestamp;
1011
1013
  };
1012
1014
 
1015
+ type ShiftEventKind = "signed-in" | "signed-out" | "break-started" | "break-ended";
1016
+
1017
+ type ShiftEvent = {
1018
+ at: IsoTimestamp;
1019
+ kind: ShiftEventKind;
1020
+ };
1021
+
1022
+ type MemberShift = {
1023
+ signedInAt: IsoTimestamp;
1024
+ signedOutAt?: IsoTimestamp;
1025
+ talkSeconds?: DurationSeconds;
1026
+ holdSeconds?: DurationSeconds;
1027
+ breakSeconds?: DurationSeconds;
1028
+ tasksHandled?: number;
1029
+ events?: ShiftEvent[];
1030
+ };
1031
+
1013
1032
  type TeamMembers = {
1014
1033
  members: TeamMember[];
1015
1034
  requests: LeadRequest[];
@@ -1061,20 +1080,20 @@ type TeamCommandResult =
1061
1080
  | { status: "failed"; failure: ProtocolFailure };
1062
1081
  ```
1063
1082
 
1064
- ### Media
1083
+ ### Audio
1065
1084
 
1066
1085
  ```ts
1067
- type VoiceMediaSession = {
1086
+ type CallAudio = {
1068
1087
  remoteAudio: MediaStream;
1069
1088
  setMuted(muted: boolean): void;
1070
1089
  close(): void;
1071
1090
  };
1072
1091
 
1073
- type OpenMediaResult =
1074
- | { status: "opened"; session: VoiceMediaSession }
1092
+ type OpenAudioResult =
1093
+ | { status: "opened"; audio: CallAudio }
1075
1094
  | { status: "unavailable"; failure: ProtocolFailure };
1076
1095
 
1077
- type OpenMediaRequest = {
1096
+ type OpenAudioRequest = {
1078
1097
  assignmentId: AssignmentId;
1079
1098
  localAudio?: MediaStream;
1080
1099
  };
@@ -1106,10 +1125,8 @@ type ProviderEvent =
1106
1125
  assignmentExpiresAt?: IsoTimestamp;
1107
1126
  }
1108
1127
  | { type: "task-updated"; task: Task }
1109
- | { type: "task-media-started"; assignmentId: AssignmentId }
1110
- | { type: "task-media-ended"; assignmentId: AssignmentId }
1111
- | { type: "team-media-started"; memberId: UserId; assignmentId: AssignmentId }
1112
- | { type: "team-media-ended"; memberId: UserId; assignmentId: AssignmentId }
1128
+ | { type: "task-audio-started"; assignmentId: AssignmentId }
1129
+ | { type: "task-audio-ended"; assignmentId: AssignmentId }
1113
1130
  | { type: "task-ended"; assignmentId: AssignmentId; outcome: TaskOutcome }
1114
1131
  | { type: "dial-outcome"; dialId: DialId; outcome: DialOutcome; assignmentId?: AssignmentId; destinationId?: string; reason?: string }
1115
1132
  | { type: "announcement"; text: string; html?: string; announcedAt: IsoTimestamp; expiresAt?: IsoTimestamp }
@@ -1117,7 +1134,7 @@ type ProviderEvent =
1117
1134
  | { type: "diagnostic"; expected: string; observed: string; assignmentId?: AssignmentId }
1118
1135
  | { type: "team-updated"; team: TeamMembers }
1119
1136
  | { type: "contacts-updated"; contacts: Contact[] }
1120
- | { type: "calendar-updated"; scheduledActivities: ScheduledActivity[] };
1137
+ | { type: "calendar-updated"; calendar: ScheduledActivity[] };
1121
1138
 
1122
1139
  type ProviderEventEnvelope = {
1123
1140
  id: string;
@@ -1172,7 +1189,7 @@ type Connection<C extends Channel = Channel> = {
1172
1189
  endBreak?(): Promise<BreakEndResult>;
1173
1190
 
1174
1191
  executeTeam?(request: TeamCommandRequest): Promise<TeamCommandResult>;
1175
- openMedia?(request: OpenMediaRequest): Promise<OpenMediaResult>;
1192
+ openAudio?(request: OpenAudioRequest): Promise<OpenAudioResult>;
1176
1193
  setPreference?(request: SetPreferenceRequest): Promise<PreferenceResult>;
1177
1194
  recordStep?(report: HistoryReport): Promise<HistoryReportResult>;
1178
1195
  };
@@ -1531,7 +1548,7 @@ Request/response polling does not have those properties and is not a transport f
1531
1548
 
1532
1549
  Everything an adapter publishes about a task is derived from what the platform states, or kept in
1533
1550
  the login's store where the platform cannot hold it. Per offer, the `assignmentId` comes from what
1534
- the offer itself states; per call, the media follows `task-media-started` and `task-media-ended`,
1551
+ the offer itself states; per call, the audio follows `task-audio-started` and `task-audio-ended`,
1535
1552
  never a flag set when the audio was opened; per session, the phone the agent holds is what the
1536
1553
  platform restates on activation, not a mapping read once over HTTP. What the platform cannot hold
1537
1554
  -- an agent application's muted leg -- goes in `ConnectContext.store`, keyed by the task and gone with it.
@@ -1539,11 +1556,11 @@ platform restates on activation, not a mapping read once over HTTP. What the pla
1539
1556
  A fact read once over a connection and kept in the adapter object dies with the client. An agent application
1540
1557
  reload is the first client dying and a second coming up for the same login, and the second
1541
1558
  publishes the same truth about the same task as the first did, or the first was publishing
1542
- something it made up: an assignment minted for the connection, media remembered as a boolean, a
1559
+ something it made up: an assignment minted for the connection, audio remembered as a boolean, a
1543
1560
  phone the second never read. Three pieces of exactly that state were found in one adapter in an
1544
1561
  afternoon, and none of it was visible until the adapter was built twice. The drive's `rebuild` is the test of this
1545
1562
  principle: given a way to build the adapter again, the run hands the login over and holds the
1546
- second to what the first published (`drive.reload.snapshot`, `.history`, `.openMedia`, and the
1563
+ second to what the first published (`drive.reload.snapshot`, `.history`, `.openAudio`, and the
1547
1564
  stream's own rules across the resync). An adapter that cannot be built twice against its platform
1548
1565
  and publish the same task is not a conformant adapter yet, whatever a single run says.
1549
1566
 
@@ -1753,7 +1770,7 @@ contributions from active providers into one agent-facing calendar while retaini
1753
1770
  provider identity for each activity.
1754
1771
 
1755
1772
  When declared, the provider publishes its authoritative list through
1756
- `Snapshot.scheduledActivities` and replaces it with a `calendar-updated` event when it
1773
+ `Snapshot.calendar` and replaces it with a `calendar-updated` event when it
1757
1774
  changes.
1758
1775
 
1759
1776
  | Field | Contract |
@@ -2127,13 +2144,13 @@ a capability it agrees with the login: a lead's snapshot carries `team`, nobody
2127
2144
  | `tasks` | Complete set of tasks currently offered to or owned by this agent. |
2128
2145
  | `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. |
2129
2146
  | `contacts` | Required complete contact contribution when the manifest declares `contacts`; `[]` clears it. Omitted only when it does not. |
2130
- | `scheduledActivities` | Required complete calendar contribution when the manifest declares `calendar`; `[]` clears it. Omitted only when it does not. |
2147
+ | `calendar` | Required complete calendar contribution when the manifest declares `calendar`; `[]` clears it. Omitted only when it does not. |
2131
2148
  | `team` | Required `TeamMembers` when the login declares `capabilities.lead` and the lead has the team feature on, `members: []` when nobody is in it. Forbidden otherwise — the login is the permission, and a lead who turned the feature off gets nothing of the team. |
2132
2149
 
2133
2150
  ## Live connection
2134
2151
 
2135
2152
  `Connection` is what `connect()` returns. Its methods are documented in the sections that follow
2136
- and under **Breaks**, **Team leads**, **Real-time media** and **Task commands**; this is the whole
2153
+ and under **Breaks**, **Team leads**, **Real-time audio** and **Task commands**; this is the whole
2137
2154
  surface in one place, and what obliges an adapter to implement each one.
2138
2155
 
2139
2156
  | Method | Implement it when |
@@ -2154,7 +2171,7 @@ surface in one place, and what obliges an adapter to implement each one.
2154
2171
  | `executeTeam(request)` | The login declares `capabilities.lead`: every lead act, on the team surface and nowhere else. See **Lead commands**. |
2155
2172
  | `setPreference(request)` | The login declares `capabilities.preferences`: the person's choice has to have somewhere to go. |
2156
2173
  | `recordStep(report)` | The manifest lists `softphone` among its `phones`. On a softphone the agent application mutes its own microphone on any call, and the provider's record has to have somewhere to take that leg; a desk phone's microphone is the phone's. See **The agent application records what it performs**. |
2157
- | `openMedia(request)` | The manifest lists `softphone` among its `phones`. On a softphone the call's audio lands in Omni, so the adapter has to open it; a platform of desk phones alone never does. |
2174
+ | `openAudio(request)` | The manifest lists `softphone` among its `phones`. On a softphone the call's audio lands in Omni, so the adapter has to open it; a platform of desk phones alone never does. |
2158
2175
 
2159
2176
  **The four break methods stand or fall together.** Declaring `capabilities.breaks` at login and then
2160
2177
  implementing `requestBreak` without `commitBreak` leaves an agent granted a break that can never
@@ -2168,7 +2185,7 @@ Returns the provider's complete authoritative state at one point in time.
2168
2185
  - Omni registers `subscribe()` before awaiting the initial snapshot. An event delivered while the
2169
2186
  snapshot is read is held until it lands, and what happens to it then depends on what it is. A
2170
2187
  snapshot restates state, so an event of a state-replacing kind -- `task-offered`, `task-updated`,
2171
- `task-ended`, `task-media-started`, `task-media-ended`, `break-state`, `team-updated`,
2188
+ `task-ended`, `task-audio-started`, `task-audio-ended`, `break-state`, `team-updated`,
2172
2189
  `contacts-updated`, `calendar-updated`, `transport-status`, `snapshot` -- is dropped, because the
2173
2190
  snapshot accounts for it. An event that reports a transaction no snapshot carries --
2174
2191
  `dial-outcome`, `diagnostic`, `announcement`, `queue-summary` -- is applied after the snapshot,
@@ -2224,7 +2241,7 @@ Stops the connection and releases adapter-owned resources.
2224
2241
 
2225
2242
  - Must be safe after partial startup and safe to call once during normal shutdown.
2226
2243
  - Must stop automatic reconnect.
2227
- - Must remove event handlers and release media resources owned by the adapter.
2244
+ - Must remove event handlers and release audio resources owned by the adapter.
2228
2245
  - Does not imply that active tasks were completed or removed.
2229
2246
 
2230
2247
  ## Task assignment lifecycle
@@ -2310,7 +2327,7 @@ const assignment = {
2310
2327
  ```
2311
2328
 
2312
2329
  The rule the phase exists to express: **nothing is acquired on the agent's behalf while a task
2313
- is pending.** An agent application that carries media must not open the microphone until the task is
2330
+ is pending.** An agent application that carries audio must not open the microphone until the task is
2314
2331
  accepted. Omni does not open the task's browsers either — a task that rings out costs nothing.
2315
2332
 
2316
2333
  When consent is required, Omni offers the agent an **Accept** control. The call is the
@@ -2362,27 +2379,27 @@ time. Runtime conformance checks also require the task channel to match its prov
2362
2379
 
2363
2380
  | Field | Contract |
2364
2381
  | --- | --- |
2365
- | `assignmentId` | Required `AssignmentId`: the assignment this task is the record of, and its one identity. Issued by the provider, unique within the provider, never reused. Every event, command and report that names a task names it by this, so a late dial outcome or a late history report for one customer never lands on the next. The stream refuses an assignment introduced twice (`stream.taskOffered.duplicate`) and a media or ending event naming one that has ended or that nobody has seen (`stream.assignment.ended`, `.unknown`); a `dial-outcome` may name an ended assignment, since a dial placed late routinely outlives its call, and the agent application routes it there. See **A task's life on the wire**. |
2382
+ | `assignmentId` | Required `AssignmentId`: the assignment this task is the record of, and its one identity. Issued by the provider, unique within the provider, never reused. Every event, command and report that names a task names it by this, so a late dial outcome or a late history report for one customer never lands on the next. The stream refuses an assignment introduced twice (`stream.taskOffered.duplicate`) and an audio or ending event naming one that has ended or that nobody has seen (`stream.assignment.ended`, `.unknown`); a `dial-outcome` may name an ended assignment, since a dial placed late routinely outlives its call, and the agent application routes it there. See **A task's life on the wire**. |
2366
2383
  | `title` | Agent-facing task title. |
2367
2384
  | `channel` | Channel used for this task. It must equal the source provider's manifest channel. |
2368
2385
  | `taskType` | Required provider-defined source or category of work, such as a voice `Queue Name`, `Mailbox Folder`, `Chat Source`, `Support`, `Billing`, or `Returns`. |
2369
2386
  | `capabilities` | Controls and workspace features available for this specific task. |
2370
- | `capabilitySource` | Required. Who chose the capabilities: `queue` when somebody configured these terms, `ungoverned` when nothing handed the work over -- an agent's own outbound -- and `undetermined` when a queue was named and its terms could not be read, in which case `capabilities` is what the provider will honour, not what the platform permits. An agent application shows `undetermined` where the agent works. See **Task capabilities**. |
2387
+ | `capabilitySource` | Required. Who chose the capabilities: `queue` when somebody configured these terms, `nobody` when nothing handed the work over -- an agent's own outbound -- and `not-yet-read` when a queue was named and its terms could not be read, in which case `capabilities` is what the provider will honour, not what the platform permits. An agent application shows `not-yet-read` where the agent works. See **Task capabilities**. |
2371
2388
  | `browsers` | Named browser definitions for the task workspace: at least one when the task declares the `browsers` capability, empty when it does not. |
2372
2389
  | `party` | The person or entity on the other end of this task, as a `Contact`: often a name and one address; a withheld caller ID may leave nothing to send at all. Optional. The party is who the task is *with*; `contacts` is the directory. |
2373
2390
  | `phase` | Current canonical task phase: `pending`, `confirmed`, `preview`, `in-progress`, `paused`, or `completing`. `preview` is voice only. |
2374
- | `media` | Voice only. The task's real-time audio as the provider holds it: `started` while audio is attached, `ended` once it ended, omitted while none is. The provider's word — see **`task-media-started`**. Media names a task whose work has begun, or whose party the agent application is dialling: on a `pending`, `confirmed` or `preview` task with nobody ringing it is refused (`task.media.beforeWork`), on a snapshot as on the event, since an agent application opens the microphone on it; with the party ringing by an agent application dial or provider-triggered preview dial, actual ring-back media may precede answer. |
2391
+ | `audio` | Voice only. The task's real-time audio as the provider holds it: `started` while audio is attached, `ended` once it ended, omitted while none is. The provider's word — see **`task-audio-started`**. Audio names a task whose work has begun, or whose party the agent application is dialling: on a `pending`, `confirmed` or `preview` task with nobody ringing it is refused (`task.audio.beforeWork`), on a snapshot as on the event, since an agent application opens the microphone on it; with the party ringing by an agent application dial or provider-triggered preview dial, actual ring-back audio may precede answer. |
2375
2392
  | `acceptance` | How this offer is accepted — `no-preference`, `consent`, or `automatic` — stated on the pending task so a reconnect snapshot says it too. Required while `pending` when `autoAcceptTasks` was `true`, forbidden when it was `false`, and absent past `pending`. See **Acceptance modes**. |
2376
2393
  | `previewEndsAt` | Voice only, in `preview`: the preparation target instant. Absent, the agent has as long as they need without a preparation countdown. Always with `atDeadline`. See **Preview: the agent presses Call**. |
2377
- | `atDeadline` | Voice only, in `preview`, with `previewEndsAt`: what the system does at the deadline -- `calls` makes the provider initiate dialing, `host-calls` makes the agent application issue Call, `waits` keeps the task in preview awaiting the agent. |
2394
+ | `atDeadline` | Voice only, in `preview`, with `previewEndsAt`: what the system does at the deadline -- `provider-dials` makes the provider initiate dialing, `host-dials` makes the agent application issue Call, `waits` keeps the task in preview awaiting the agent. |
2378
2395
  | `reference` | Optional agent-facing reference such as a case, call, conversation, ticket, or message number. It is distinct from the protocol `id`. |
2379
2396
  | `completionMode` | `agent-command` waits for the channel's `complete` command; `provider-automatic` completes without one. |
2380
- | `wrapAllowance` | Fixed time allowed to complete the task after primary interaction ends. For real-time media, it begins after `task-media-ended`. Required under `provider-automatic`, where the provider acts on it. Optional under `agent-command`: omitted says the provider imposes no deadline, and Omni counts nothing down. |
2397
+ | `wrapAllowance` | Fixed time allowed to complete the task after primary interaction ends. For real-time audio, it begins after `task-audio-ended`. Required under `provider-automatic`, where the provider acts on it. Optional under `agent-command`: omitted says the provider imposes no deadline, and Omni counts nothing down. |
2381
2398
  | `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. |
2382
2399
  | `history` | The call record: `steps` — the ordered interaction history of this open task, one entry per occurrence, oldest first — and what they add up to before this agent, `interactionSeconds`, `holdSeconds`, `queueSeconds`, `transfers`, each present when the provider knows it. Live task data restated with the task, not a permanent archive. See **Interaction history**. |
2383
2400
  | `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 -- carrying a `stage` while being dialled again on the same task, a connect-back with the agent application's `dialId` or a platform's callback without, ringing from the moment the dial is placed and joined on its answered outcome --, `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 `destinationId` dialled, the `dialId` where an agent application 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 agent application supplies the verb. Present when the provider knows the room, absent when it does not. See **Every dial has an outcome**. |
2384
2401
  | `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**. |
2385
- | `takenOver` | Voice only, on a task that reached this agent by a lead's take-over: which member the call was taken from, and when. It is an ordinary assignment with the call's history, offered whatever break the lead is on and counted against no capacity. See **Lead assist**. |
2402
+ | `takenOver` | Voice only, on a task that reached this agent by a lead's take-over: which member the call was taken from, and when. It is an ordinary assignment with the call's history, offered whatever break the lead is on and counted against their capacity like any other call. See **Lead assist**. |
2386
2403
 
2387
2404
  `TaskAttribute` entries carry typed detail alongside the task:
2388
2405
 
@@ -2414,7 +2431,7 @@ The canonical task transitions are:
2414
2431
  | `pending` | Task is accepted | `confirmed` |
2415
2432
  | `confirmed` | The customer's record is put in front of the agent before any call goes out (voice) | `preview` |
2416
2433
  | `confirmed` | Work begins | `in-progress` |
2417
- | `preview` | Agent presses Call (`call`) and the customer answers, or the deadline `calls` and they answer | `in-progress` |
2434
+ | `preview` | Agent presses Call (`dial`) and the customer answers, or the deadline `provider-dials` and they answer | `in-progress` |
2418
2435
  | `preview` | The call goes out and nobody answers | `completing` |
2419
2436
  | `preview` | The preparation target passes with `atDeadline: "waits"` | Remains `preview`, waiting for the agent to press Call |
2420
2437
  | `pending` | Provider withdraws the assignment, the agent declines, or the party abandons the ring | Removed by `task-ended` with `cancelled` outcome, `by` saying which |
@@ -2433,7 +2450,7 @@ The canonical task transitions are:
2433
2450
  A task has one name, the assignment it is the record of, and the provider issues it. Platforms
2434
2451
  reuse their own handles: a closed one is retired minutes later, a requeue takes seconds, and on
2435
2452
  one platform a call was offered twice, thirteen seconds apart, under one handle, through a close
2436
- and a re-offer. Everything that names a task after the fact -- a dial outcome, a media event, an
2453
+ and a re-offer. Everything that names a task after the fact -- a dial outcome, an audio event, an
2437
2454
  ending, a history report, a command -- would land on whichever assignment is open when it arrives.
2438
2455
  So the assignment id is unique within the provider and never reused: where the platform's handle
2439
2456
  never comes back, the adapter passes it through; where it does, the adapter mints the assignment id
@@ -2442,7 +2459,7 @@ belongs to or is refused, and a task-scoped browser session (`PROVIDER_NAME__ASS
2442
2459
  lives one assignment, never the next customer's cookies. A dial the agent application placed was
2443
2460
  already safe, since its outcome is placed by the agent application's own `dialId`; where the
2444
2461
  assignment earns its place is everything the agent application does not mint --
2445
- `task-media-started`, `task-media-ended`, `task-ended`, a `recordStep` naming a task -- any of which
2462
+ `task-audio-started`, `task-audio-ended`, `task-ended`, a `recordStep` naming a task -- any of which
2446
2463
  would otherwise find the next customer under a reused handle and act on them. A minted id is part
2447
2464
  of the task the adapter keeps, not a field held in memory beside it: it lives in the login's
2448
2465
  `store` with the task, or is derived from a platform handle that itself never reuses, so a rebuilt
@@ -2469,19 +2486,19 @@ const previewed = {
2469
2486
  phase: "preview",
2470
2487
  party: { name: "Maya Rao", number: "+919876543210" },
2471
2488
  previewEndsAt: "2026-08-25T10:40:37.000Z",
2472
- atDeadline: "calls",
2489
+ atDeadline: "provider-dials",
2473
2490
  } satisfies Pick<Task<"voice">, "channel" | "capabilities" | "phase" | "party" | "previewEndsAt" | "atDeadline">;
2474
2491
 
2475
2492
  // The agent presses Call. It is a dial like any other.
2476
- const pressed: TaskCommand<"voice"> = { type: "call", dialId: "dial-7f2" };
2493
+ const pressed: TaskCommand<"voice"> = { type: "dial", dialId: "dial-7f2" };
2477
2494
  ```
2478
2495
 
2479
2496
  **Call is a dial.** It carries the agent application's `dialId`, is answered `dialling`, and ends in exactly one
2480
2497
  `dial-outcome`; the phase is its gate and there is no capability, since a record put in front of an
2481
- agent is there to be called. Its media starts on `dialling`, as every agent application-placed dial's does --
2482
- ring-back is audio the agent hears -- so `task-media-started` arrives on the `preview` task with
2483
- its party ringing. On `answered` the task is `in-progress`; on any other outcome the media ends,
2484
- `task-media-ended` starts the wrap clock as on every voice task, and the task goes to
2498
+ agent is there to be called. Its audio starts on `dialling`, as every agent application-placed dial's does --
2499
+ ring-back is audio the agent hears -- so `task-audio-started` arrives on the `preview` task with
2500
+ its party ringing. On `answered` the task is `in-progress`; on any other outcome the audio ends,
2501
+ `task-audio-ended` starts the wrap clock as on every voice task, and the task goes to
2485
2502
  `completing`, so the agent records the
2486
2503
  no-answer as the outcome it is -- in a campaign that is the commonest outcome there is, and it
2487
2504
  is work, not a cancellation. A `preview` task therefore needs a manifest that says how a dial ends
@@ -2493,8 +2510,8 @@ carries the provider's authoritative preparation-end instant and exactly one tri
2493
2510
 
2494
2511
  | Declaration | At preparation end |
2495
2512
  | --- | --- |
2496
- | `atDeadline: "calls"` | The provider initiates dialing. The agent application never sends a timer-triggered Call. |
2497
- | `atDeadline: "host-calls"` | The agent application sends the ordinary `call` command with a fresh agent application `dialId`. The provider does not independently auto-dial this task. |
2513
+ | `atDeadline: "provider-dials"` | The provider initiates dialing. The agent application never sends a timer-triggered Call. |
2514
+ | `atDeadline: "host-dials"` | The agent application sends the ordinary `dial` command with a fresh agent application `dialId`. The provider does not independently auto-dial this task. |
2498
2515
  | `atDeadline: "waits"` | Neither side auto-dials. The task remains in preview until the agent presses Call. |
2499
2516
 
2500
2517
  The agent may press Call early in either fixed-preparation dialing mode, or at any time in
@@ -2502,7 +2519,7 @@ The agent may press Call early in either fixed-preparation dialing mode, or at a
2502
2519
  zero the UI says "Waiting for agent" and keeps Call available. There is no automatic task-ended,
2503
2520
  completion, dialing or phase change. No repeating timer action occurs on later snapshots. This differs
2504
2521
  from unlimited preparation only by displaying a preparation target. The former `expires` deadline
2505
- value is not supported; elapsed preparation must not withdraw the task. For `calls` and `host-calls`, the deadline ends
2522
+ value is not supported; elapsed preparation must not withdraw the task. For `provider-dials` and `host-dials`, the deadline ends
2506
2523
  preparation and triggers initiation; it does not promise ringing, playable audio or customer
2507
2524
  answer at that exact instant. Scheduling/dispatch delay must remain visible, and a failed or
2508
2525
  prevented initiation must be reported rather than leaving a silently expired countdown.
@@ -2513,7 +2530,7 @@ time; clock estimation is allowed only under the explicit agent application-trig
2513
2530
  Without usable provider-domain time
2514
2531
  it cannot safely auto-trigger and must expose the uncertainty and reconcile. No zero-duration,
2515
2532
  receipt-time or local-default deadline is inferred. Neither elapsed time nor submission changes
2516
- the task phase: subsequent authoritative events state ringing, answer, media and completion.
2533
+ the task phase: subsequent authoritative events state ringing, answer, audio and completion.
2517
2534
 
2518
2535
  An agent application must serialize manual clicks and its timer under the exact provider/login/task/assignment
2519
2536
  scope, allowing at most one unresolved submission. Recheck the current phase, deadline owner,
@@ -2527,11 +2544,11 @@ command as ownership of an already-running independent attempt.
2527
2544
  Agent application-triggered automatic dialing is an ordinary agent application dial: its result is `dialling` with the same
2528
2545
  agent application `dialId`, followed by exactly one correlated terminal `dial-outcome`. A provider-triggered
2529
2546
  dial has no invented agent application ID or agent application dial outcome. It reports source-evidenced party ringing,
2530
- actual media and customer answer separately. In preview with `atDeadline: "calls"`, a ringing
2531
- party without an agent application ID may carry actual pre-answer media; the deadline alone permits no media.
2532
- Media is still introduced/ended by the corresponding events and must not imply customer answer.
2547
+ actual audio and customer answer separately. In preview with `atDeadline: "provider-dials"`, a ringing
2548
+ party without an agent application ID may carry actual pre-answer audio; the deadline alone permits no audio.
2549
+ Audio is still introduced/ended by the corresponding events and must not imply customer answer.
2533
2550
  Submission acceptance is never an answered outcome or permission to publish `in-progress` early.
2534
- Unsupported source correlation/outcome/media evidence remains an integration blocker.
2551
+ Unsupported source correlation/outcome/audio evidence remains an integration blocker.
2535
2552
 
2536
2553
  **Drop both fields when the phase moves.** A provider that builds the `in-progress` task by
2537
2554
  spreading the `preview` one carries `previewEndsAt` and `atDeadline` with it, and the agent application refuses
@@ -2549,7 +2566,7 @@ recording contract; this distinction does not remove it or move provider call op
2549
2566
 
2550
2567
  A caller journey may revisit the same agent while an earlier interaction still wraps. Each is its
2551
2568
  own assignment, and two open at once carry two assignment ids. Restore them unchanged on
2552
- reconnect. Commands, media, endings and history reports name the exact assignment; never retarget
2569
+ reconnect. Commands, audio, endings and history reports name the exact assignment; never retarget
2553
2570
  an old command to the newest interaction of a journey. `validateTaskCommandRequest` checks the
2554
2571
  request's assignment against the supplied published task and then its command prerequisites. The
2555
2572
  caller must select that task within the correct provider/login and recheck at the provider; the
@@ -2559,33 +2576,33 @@ portion must not be cleared because this task ended. Snapshot counts include all
2559
2576
  and active interactions; task completion is not caller hangup.
2560
2577
 
2561
2578
  **Voice describes the agent's interaction within the wider call.** The task defines that agent's
2562
- workspace, tools, permissions, media participation and completion work. The caller's channel may
2579
+ workspace, tools, permissions, audio participation and completion work. The caller's channel may
2563
2580
  continue through IVR, queues, other agents, holds or conferences while this interaction ends or wraps.
2564
2581
  Completing this task ends this interaction responsibility; it is not evidence that the caller's channel
2565
- or journey ended. Task media describes this agent's attachment, not the lifetime of every party's
2582
+ or journey ended. Task audio describes this agent's attachment, not the lifetime of every party's
2566
2583
  connection. Commands retain their explicit targets and effects; task completion must not silently
2567
2584
  become a caller-disconnect operation.
2568
2585
 
2569
2586
  **A task is never its audio.** A voice task represents the agent’s interaction: the call is offered when it is
2570
2587
  routed to the agent and accepted as its `acceptance` dictates, and its presence and phase follow
2571
2588
  the provider's reports about the work — never the audio. Wherever audio moves — an offer, a hold, a
2572
- consult, a conference leg joining or leaving, a transfer, a connect-back — the media follows
2573
- separately, arriving on `task-media-started`, attaching through `openMedia` and ending with
2574
- `task-media-ended`. Omni does not ring,
2589
+ consult, a conference leg joining or leaving, a transfer, a connect-back — the audio follows
2590
+ separately, arriving on `task-audio-started`, attaching through `openAudio` and ending with
2591
+ `task-audio-ended`. Omni does not ring,
2575
2592
  bridge, or hold a line. How the phone rings, whether it rings at all, and where legs join and leave
2576
2593
  are the adapter's and the platform's, transient, and decide neither when a task exists nor what
2577
2594
  phase it is in.
2578
2595
 
2579
- The line runs between the provider's word and Omni's own senses. `task-media-ended` is the
2596
+ The line runs between the provider's word and Omni's own senses. `task-audio-ended` is the
2580
2597
  provider's report that primary interaction ended — a fact about the work, which is why the completion
2581
2598
  allowance starts on it and the Connect back control appears on it — and Omni follows that report as it
2582
- follows any other. What Omni never does is derive a task's state from its own media session: a
2599
+ follows any other. What Omni never does is derive a task's state from its own audio session: a
2583
2600
  stream that drops, a track that ends, a transport that disconnects, a microphone that fails, an
2584
2601
  endpoint re-registering change nothing about the task until the provider says so. Structurally:
2585
- `task-media-started` and `task-media-ended` alternate on a task whose work has begun, media ends
2586
- only where it arrived, what follows the media ending is `completing` or `task-ended`, and every
2602
+ `task-audio-started` and `task-audio-ended` alternate on a task whose work has begun, audio ends
2603
+ only where it arrived, what follows the audio ending is `completing` or `task-ended`, and every
2587
2604
  task is introduced once — `exerciseAdapter` holds the stream to that from the connect snapshot on,
2588
- and `assertMediaFollowsTheTask` holds any sequence.
2605
+ and `assertAudioFollowsTheTask` holds any sequence.
2589
2606
 
2590
2607
  #### Completion timing
2591
2608
 
@@ -2594,16 +2611,16 @@ the task open until Omni sends the channel's `complete` command. With `provider-
2594
2611
  provider may complete the task without receiving that command.
2595
2612
 
2596
2613
  `wrapAllowance` is independent of that decision. It is fixed, and when it starts depends on
2597
- whether the channel carries real-time media:
2614
+ whether the channel carries real-time audio:
2598
2615
 
2599
2616
  | Channel | Wrap allowance starts at |
2600
2617
  | --- | --- |
2601
- | Voice and any channel with real-time media | The `task-media-ended` event |
2618
+ | Voice and any channel with real-time audio | The `task-audio-ended` event |
2602
2619
  | Chat | The `task-updated` that moves the task to `completing`: the provider's word that the conversation is closed on its side, the allowance running from that publication's `occurredAt` |
2603
2620
  | Email | The `task-updated` that moves the task to `completing`: the provider's word that the platform has accepted the outgoing message, the allowance running from that publication's `occurredAt` |
2604
- | Other non-media channels | The `task-updated` that moves the task to `completing` |
2621
+ | Other non-audio channels | The `task-updated` that moves the task to `completing` |
2605
2622
 
2606
- **Off voice, `completing` is the provider's word that interaction ended.** There is no media event to
2623
+ **Off voice, `completing` is the provider's word that interaction ended.** There is no audio event to
2607
2624
  carry it, so the phase does; an agent application starts the wrap clock at that publication and nowhere else. It
2608
2625
  is optional: a conversation with nothing to wrap moves from `in-progress` to `task-ended` and
2609
2626
  `completing` is never published. Under `provider-automatic` with a non-zero `wrapAllowance` it is
@@ -2638,9 +2655,9 @@ const untimedWrap = {
2638
2655
  } satisfies Pick<Task<"voice">, "completionMode" | "wrapAllowance">;
2639
2656
  ```
2640
2657
 
2641
- Here the customer has hung up, `task-media-ended` has been sent on time, the task is `completing`,
2658
+ Here the customer has hung up, `task-audio-ended` has been sent on time, the task is `completing`,
2642
2659
  and the agent takes as long as the work needs. Moving the task to `completing` late to avoid a
2643
- deadline is not an alternative on a media channel: the clock starts at a real event, and delaying
2660
+ deadline is not an alternative on an audio channel: the clock starts at a real event, and delaying
2644
2661
  that event would falsify the phase and everything timed from it.
2645
2662
 
2646
2663
  #### Connecting back during completion
@@ -2655,9 +2672,9 @@ is; the command carries no destination, and it is a dial like any other, so it c
2655
2672
 
2656
2673
  On `dialling` the provider is placing the call and the task returns to `in-progress`: the agent is
2657
2674
  working again, and the wrap allowance is **discarded, not paused**. From there the call is
2658
- reported as any call is -- `paused`, `in-progress`, and when its media ends, `task-media-ended`
2675
+ reported as any call is -- `paused`, `in-progress`, and when its audio ends, `task-audio-ended`
2659
2676
  again, which starts a fresh allowance from that instant. A party who does not answer is a dial
2660
- whose outcome says so and a call whose media ended: the task returns to `completing` through the
2677
+ whose outcome says so and a call whose audio ended: the task returns to `completing` through the
2661
2678
  same event and the clock starts again from there. At no point is an agent dialling against a
2662
2679
  deadline.
2663
2680
 
@@ -2693,7 +2710,7 @@ const connectBackCapable = {
2693
2710
  With ten seconds of the thirty left, the agent presses Connect back: `execute({ command: { type:
2694
2711
  "connect-back", dialId } })` returns `dialling`, the task is `in-progress`, and the thirty seconds
2695
2712
  are gone.
2696
- The second call ends: `task-media-ended`, the task is `completing`, and a new thirty seconds runs
2713
+ The second call ends: `task-audio-ended`, the task is `completing`, and a new thirty seconds runs
2697
2714
  from that instant.
2698
2715
 
2699
2716
  ```ts
@@ -2729,6 +2746,12 @@ Migration from the earlier spellings:
2729
2746
  | Task.id, TaskId, and `taskId` on every event, command, report and lead request | gone: a task is named by `assignmentId` alone, and `assignmentKey(providerId, assignmentId)` scopes it |
2730
2747
  | taskKey, omni.task-not-found, PROVIDER_NAME__TASK_ID__TAB_NAME (ProviderName.TaskId.TabName) | `assignmentKey`, `omni.assignment-not-found`, `PROVIDER_NAME__ASSIGNMENT_ID__TAB_NAME` (`ProviderName.AssignmentId.TabName`) |
2731
2748
  | Manifest.disposalSettleMs | `Manifest.completionSettleMs` |
2749
+ | the call command, atDeadline calls and host-calls | `dial`, `"provider-dials"`, `"host-dials"` |
2750
+ | media: task-media-started/-ended, team-media-*, Task.media, TaskMediaState, openMedia, OpenMediaRequest/Result, VoiceMediaSession and its session field | audio: `task-audio-started`, `task-audio-ended`, `audio` on the task, `TaskAudioState`, `openAudio`, `OpenAudioRequest`, `OpenAudioResult`, `CallAudio` on the result's `audio`; there is no team audio, the lead's follows `listening` on the member |
2751
+ | capabilitySource values ungoverned and undetermined | `nobody`, `not-yet-read` (`capabilitySource.notYetRead`) |
2752
+ | Snapshot.scheduledActivities, and scheduledActivities on calendar-updated | `calendar` in both |
2753
+ | availability reserved | `elsewhere` |
2754
+ | recording destinationId, HostRecording.destinationIds | `storageId`, `storageIds` (`recording.storage`, `recording.host.storage`) |
2732
2755
 
2733
2756
  Update producers, consumers, saved task snapshots, and validation-rule assertions together.
2734
2757
  History and report rule names use `history` and `historyReport`; assignment rules use
@@ -2772,16 +2795,16 @@ Steps are `queued`, `offered`, `answered`, `held`, `muted`, `transferred`, `conf
2772
2795
  when it began, `seconds` once it ended and omitted while it runs — and a second hold is a second
2773
2796
  entry after the first, never a revision of it. A leg that has ended states its duration, and the
2774
2797
  task says whether a leg can still be running: a hold runs only while the task is `paused`, a mute
2775
- only while its media is up, so a `held` entry without `seconds` on a task that is not paused, or a
2798
+ only while its audio is up, so a `held` entry without `seconds` on a task that is not paused, or a
2776
2799
  `muted` one on a call that is over, is a leg nobody closed and reads exactly like a leg running now
2777
2800
  (`task.history.held.open`, `.muted.open`). Whoever performs the leg closes it — the
2778
2801
  provider whose platform parks the caller, the agent application whose microphone it is — by restating the entry
2779
2802
  with its duration, and **an entry that cannot be closed is not written**: open for ever is a
2780
- plausible nought one level down from a total. **A leg still open when the media ends is closed by
2781
- the provider**, at that instant, in the same publication that says the media ended: the provider
2803
+ plausible nought one level down from a total. **A leg still open when the audio ends is closed by
2804
+ the provider**, at that instant, in the same publication that says the audio ended: the provider
2782
2805
  is the one that knows the instant, and the agent application can only learn of it afterwards. Agents end calls
2783
- muted, so the agent application's leg is routinely open at media end; the provider closes it with the duration
2784
- from the leg's `at` to the media's end, and the agent application's own closing report for that leg, which
2806
+ muted, so the agent application's leg is routinely open at audio end; the provider closes it with the duration
2807
+ from the leg's `at` to the audio's end, and the agent application's own closing report for that leg, which
2785
2808
  follows what it hears, is answered `recorded` and changes nothing, the entry standing as the
2786
2809
  provider closed it. One publisher of the closed leg, and no order between them to get right. There is no resumed step; a resume is the end of a
2787
2810
  hold, at the entry's `at` plus its `seconds`, and nothing is lost by not naming it twice. The same goes for every step: two mutes are two
@@ -2828,7 +2851,7 @@ or combine them: report the representation conflict. No ambiguous event is silen
2828
2851
 
2829
2852
  **Handle time is anchored, not restarted.** It runs from the
2830
2853
  `answered` step's `at` — from the task's first `in-progress` where the provider reports no
2831
- history — until the task's media ends, and a hold neither pauses nor resets it: the hold's own
2854
+ history — until the task's audio ends, and a hold neither pauses nor resets it: the hold's own
2832
2855
  duration is the `held` entry's `seconds`, and a desk that restarts its counter on resume is
2833
2856
  counting the wrong thing.
2834
2857
 
@@ -2928,7 +2951,7 @@ the agent application mutes nothing and records nothing.
2928
2951
  **The provider's confirmed end is decisive.** The agent application keeps local state for interaction and
2929
2952
  reporting, but follows the provider's authoritative state. When the provider publishes the end
2930
2953
  of the current mute leg, the agent application ends that local mute and releases its agent application-controlled mute
2931
- on the matching task's media. It does not wait for its own timer, reopen the leg, replace the
2954
+ on the matching task's audio. It does not wait for its own timer, reopen the leg, replace the
2932
2955
  provider timestamp, or overwrite the provider's final duration with a later agent application report.
2933
2956
  A later agent mute is a new leg, never a reopening of the ended one.
2934
2957
 
@@ -2940,7 +2963,7 @@ retain the current provider view and apply the matching closure once correlation
2940
2963
  never guess a match from arrival order or nearest timestamp. Failure to release an agent application-controlled
2941
2964
  mute is reported visibly; local device reports still describe the actual device state.
2942
2965
 
2943
- At a provider-confirmed task/media end, the agent application also stops the associated local mute and reports
2966
+ At a provider-confirmed task/audio end, the agent application also stops the associated local mute and reports
2944
2967
  its observed ending where the report is still accepted. An agent application closing report repeats its original
2945
2968
  key and may include its measured duration, but cannot reverse an already confirmed provider end.
2946
2969
  The provider acknowledges a known closed leg without rewriting its final record; after the task
@@ -3068,7 +3091,7 @@ from no other source -- not the queue the task came from, not the login, not its
3068
3091
  about this task, and a client with two sources and a rule for choosing between them is the shape
3069
3092
  that produces two consumers disagreeing about one fact. The task is the one source, and the
3070
3093
  provider puts on the task what the platform permits for it -- and says, in `capabilitySource`, who
3071
- chose those terms. `queue` says somebody configured them. `ungoverned` says nothing handed the work
3094
+ chose those terms. `queue` says somebody configured them. `nobody` says nothing handed the work
3072
3095
  over: an agent's own outbound call has no queue behind it, and the provider states what it permits
3073
3096
  for such a call. Neither changes where an agent application reads the capabilities from, which is the task; the
3074
3097
  source is one more published fact about them, and the one that lets an agent application tell a fact from a fault
@@ -3106,15 +3129,15 @@ direction: the task as offered and as republished, and one command that was issu
3106
3129
  first and is refused under the last for want of the capability withdrawn -- and nothing else.
3107
3130
 
3108
3131
  **An empty capability set is a statement, not a shrug.** `capabilities: {}` under `queue` or
3109
- `ungoverned` says the platform permits nothing capability-gated on this task, and an agent application draws
3132
+ `nobody` says the platform permits nothing capability-gated on this task, and an agent application draws
3110
3133
  nothing beyond what the phase and `completionMode` require. A provider that has not yet learned
3111
3134
  what the platform permits -- a queue's configuration that has not reached it -- knows nothing of
3112
3135
  the kind, and must not publish the task as if it did, neither as `{}` nor as every control it has.
3113
- It publishes the task under `capabilitySource: "undetermined"`, with the capabilities it will
3136
+ It publishes the task under `capabilitySource: "not-yet-read"`, with the capabilities it will
3114
3137
  honour until it knows, and an agent application shows that where the agent works, beside the controls it draws
3115
3138
  from them: "no queue governs this call" is a fact, "the configuration has not arrived" is a fault,
3116
3139
  and the set alone cannot tell them apart, so the provider says which. What a provider honours
3117
- under undetermined terms is its own call -- a floor that would rather an agent briefly hold a
3140
+ under not-yet-read terms is its own call -- a floor that would rather an agent briefly hold a
3118
3141
  control the platform might not have granted than lose hold or hang-up mid-call over a slow
3119
3142
  configuration read publishes those -- and the protocol chooses no default set for it. The fault is
3120
3143
  also reported as a `diagnostic` naming the task, one per occurrence, so an operator counts it. When
@@ -3122,12 +3145,12 @@ the terms arrive, the task is republished under `queue` with the set as it now s
3122
3145
  republish as any other permission that changed while the task was open. The move goes one way.
3123
3146
  Terms once read stay read: a re-read that fails mid-task is not a new fact about the task, so the
3124
3147
  last statement stands and the failure is a `diagnostic`, and a task that was published under
3125
- `queue` or `ungoverned` never returns to `undetermined`, on an update (`stream.taskUpdated.capabilitySource`)
3148
+ `queue` or `nobody` never returns to `not-yet-read`, on an update (`stream.taskUpdated.capabilitySource`)
3126
3149
  or on a resync snapshot (`stream.snapshot.capabilitySource`); a record once read never loses an
3127
3150
  entry the same two ways (`stream.taskUpdated.history`, `stream.snapshot.history`);
3128
- a snapshot carrying a task still at work does not forget the audio the stream held up, since media
3129
- ends on `task-media-ended` and the call moves on (`stream.snapshot.media`); a voice task ends after
3130
- its audio ends, never around it, whatever the outcome (`stream.taskEnded.mediaOpen`); and a task does not go
3151
+ a snapshot carrying a task still at work does not forget the audio the stream held up, since audio
3152
+ ends on `task-audio-ended` and the call moves on (`stream.snapshot.audio`); a voice task ends after
3153
+ its audio ends, never around it, whatever the outcome (`stream.taskEnded.audioOpen`); and a task does not go
3131
3154
  backwards, on an update or on a resync (`stream.taskUpdated.phase`, `stream.snapshot.phase`): the
3132
3155
  stream sees publications, not
3133
3156
  transitions, and a task may pass through a phase between two, so `pending` to `in-progress` stands
@@ -3136,32 +3159,32 @@ the transition table -- back to `pending`, back to `confirmed` or `preview` once
3136
3159
  `completing` except by the party being dialled again, a connect-back or a platform's callback,
3137
3160
  which the update itself shows: the party ringing, or joined by a dial whose answered outcome the
3138
3161
  stream saw in this life. A completing task republished as `in-progress` from a stale copy carries
3139
- no such stage, and that is the ending the agent never saw. Audio arrives only on a task at work: `task-media-started` on a `completing` task
3140
- is refused as it is on a pending one (`stream.taskMediaStarted.beforeWork`), since a connect-back
3141
- returns the task to `in-progress` before any media. And a task completes after its audio ends,
3142
- never around it: an update moving a task to `completing` while the stream holds its media as
3143
- started is refused (`stream.taskUpdated.mediaOpen`), whoever caused the ending, and a task stating
3144
- `completing` with `media: "started"` contradicts itself on any snapshot or update
3145
- (`task.media.completing`). The consequence the rule exists for is concrete: the customer's audio
3162
+ no such stage, and that is the ending the agent never saw. Audio arrives only on a task at work: `task-audio-started` on a `completing` task
3163
+ is refused as it is on a pending one (`stream.taskAudioStarted.beforeWork`), since a connect-back
3164
+ returns the task to `in-progress` before any audio. And a task completes after its audio ends,
3165
+ never around it: an update moving a task to `completing` while the stream holds its audio as
3166
+ started is refused (`stream.taskUpdated.audioOpen`), whoever caused the ending, and a task stating
3167
+ `completing` with `audio: "started"` contradicts itself on any snapshot or update
3168
+ (`task.audio.completing`). The consequence the rule exists for is concrete: the customer's audio
3146
3169
  keeps playing through the agent's wrap-up.
3147
3170
 
3148
3171
  What the agent is told differs by source, and only one source tells them anything. Under `queue`
3149
- and `ungoverned` the agent sees controls and nothing about where they came from: both are facts,
3172
+ and `nobody` the agent sees controls and nothing about where they came from: both are facts,
3150
3173
  and an agent working a call has no use for the name of the rule behind its buttons. Under
3151
- `undetermined` the agent is told, beside the controls, that these are what the provider will honour
3174
+ `not-yet-read` the agent is told, beside the controls, that these are what the provider will honour
3152
3175
  until the queue's terms arrive, and that the controls may change when they do -- a statement about
3153
3176
  the buttons in front of them now, not about the provider, because that is what changes when the
3154
3177
  republish lands. It stays for as long as the set is provisional, beside the controls it qualifies;
3155
3178
  a message that shows and clears has said nothing about the buttons still on the screen. The two
3156
- words are close in English and far apart on the desk: `ungoverned` is silence, `undetermined` is a
3179
+ words are close in English and far apart on the desk: `nobody` is silence, `not-yet-read` is a
3157
3180
  standing notice.
3158
3181
 
3159
- One consequence for whoever builds the agent application: because a conformance run fails on `undetermined`, a
3160
- conformant adapter never shows an agent application `undetermined` under test, and a clean `exerciseAdapter`
3182
+ One consequence for whoever builds the agent application: because a conformance run fails on `not-yet-read`, a
3183
+ conformant adapter never shows an agent application `not-yet-read` under test, and a clean `exerciseAdapter`
3161
3184
  result says nothing about how the agent application renders it. That rendering is tested against a fixture --
3162
- a task published under `undetermined` shows the notice, the same task under `queue` shows none --
3185
+ a task published under `not-yet-read` shows the notice, the same task under `queue` shows none --
3163
3186
  and never against an adapter, even in principle. `exerciseAdapter` treats a
3164
- task published under `undetermined` as a violation (`capabilitySource.undetermined`), as it treats
3187
+ task published under `not-yet-read` as a violation (`capabilitySource.notYetRead`), as it treats
3165
3188
  a diagnostic: a conformance run against a platform that cannot say what it permits fails loudly
3166
3189
  rather than passing with a note.
3167
3190
 
@@ -3188,7 +3211,7 @@ See **Which commands need a capability**.
3188
3211
  | `decline` | Pending-task button: Decline | The provider can decline a pending voice offer. Omni shows it only when local policy also permits declining. |
3189
3212
  | `hold` | Primary toggle: Hold | Omni may issue voice-task `hold` and `resume` commands. |
3190
3213
  | `endCall` | Primary button: End call | The provider ends the agent's channel and every channel the agent added; the caller continues on the provider's path. The task goes to its wrap. See **Ending a call, and removing one person from it**. |
3191
- | `terminateCall` | Primary button: Terminate call | The provider ends the caller channel. The task goes to its wrap. See **Ending a call, and removing one person from it**. |
3214
+ | `terminateCall` | Primary button: Terminate call | The provider ends the whole call: every channel on it, the agent's, the caller's, any colleague's and anyone else's. The task goes to its wrap. See **Ending a call, and removing one person from it**. |
3192
3215
  | `connectBack` | Completing-task button: Connect back | Omni may have the provider connect the agent back to the task's party 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 completes the task at provider end. See **Connecting back during completion**. |
3193
3216
  | `coldTransfer` | Secondary menu item: Cold transfer | Omni may hand the customer straight to a destination, with nobody spoken to first. |
3194
3217
  | `warmTransfer` | Secondary menu item: Warm transfer | Omni may park the customer and call a destination first, then hand the customer over or cancel back. See **Warm transfer**. |
@@ -3288,7 +3311,7 @@ provider. Two of them end the agent's part; they differ in what happens to the c
3288
3311
 
3289
3312
  ```ts
3290
3313
  { type: "end-call" } // the agent's channel and the channels the agent added end; the caller continues
3291
- { type: "terminate-call" } // the caller channel ends
3314
+ { type: "terminate-call" } // every channel on the call ends
3292
3315
  { type: "conference", action: "remove", party: true } // the customer leaves; the agent stays with the colleague
3293
3316
  { type: "conference", action: "remove", destinationId: "tier2" } // the conferenced person leaves; ringing, this calls the dial off
3294
3317
  ```
@@ -3297,12 +3320,13 @@ provider. Two of them end the agent's part; they differ in what happens to the c
3297
3320
  conferenced colleague, a consulted destination -- end, and the caller continues on the path the
3298
3321
  provider decides: another IVR, a queue, a survey, or the end of the call. Gated by `endCall`.
3299
3322
 
3300
- **`terminate-call` ends the caller channel.** The call is over for the caller. Gated by
3301
- `terminateCall`, a separate permission, since ending a customer's call is a different thing from
3302
- putting my own side down.
3323
+ **`terminate-call` ends the whole call.** Every channel on it ends at the provider: the agent's,
3324
+ the caller's, any colleague the agent added, and anyone else on the call. Nobody is left on the
3325
+ line. Gated by `terminateCall`, a separate permission, since ending a customer's call is a
3326
+ different thing from putting my own side down.
3303
3327
 
3304
3328
  Both are commands to the provider through `execute`, and nothing happens on the desk until the
3305
- provider reports it: the agent's media ends on `task-media-ended`, the task moves to `completing`,
3329
+ provider reports it: the agent's audio ends on `task-audio-ended`, the task moves to `completing`,
3306
3330
  any wrap allowance runs, and the agent completes. Neither is `task-ended`, and `complete` is
3307
3331
  neither of them. Both capabilities are the queue's to grant and a level's to lock, and both may
3308
3332
  change while the task is open: the provider republishes the task at the moment a permission
@@ -3352,7 +3376,7 @@ that offers `warmTransfer` implements all three.
3352
3376
 
3353
3377
  `applied` on `complete` says the provider is bridging the customer to the destination and
3354
3378
  dropping the agent's leg. What follows is what follows any call the agent leaves: the agent's
3355
- media ends and the provider reports `task-media-ended`, the task moves to `completing`, any wrap
3379
+ audio ends and the provider reports `task-audio-ended`, the task moves to `completing`, any wrap
3356
3380
  allowance runs, and the task ends `completed` as any call does. A warm complete is not a completion:
3357
3381
  the agent has a wrap to do, so nothing is owed within `completionSettleMs`. `transferred` names a
3358
3382
  cold transfer alone, where the agent had no wrap. `applied` on `cancel` says the destination is
@@ -3438,7 +3462,7 @@ inside Omni.
3438
3462
 
3439
3463
  ## Every dial has an outcome
3440
3464
 
3441
- Five commands place a call: `dial` from the idle dialpad, `call` from preview, a cold or warm `transfer`, a
3465
+ Five commands place a call: `dial()` from the idle dialpad, the `dial` command from preview, a cold or warm `transfer`, a
3442
3466
  `conference` `add`, and `connect-back`. Each is accepted or refused at once, and each then ends later
3443
3467
  and apart from its answer -- the destination picks up, is busy, or never does -- and a dial placed
3444
3468
  late in a call routinely outlives the call. Nothing in between is reported: the wire says
@@ -3544,7 +3568,7 @@ never saw.
3544
3568
 
3545
3569
  **`onCall` describes this agent's current interaction.** When the caller disconnects but the agent
3546
3570
  and added channels remain connected, the remaining room is still valid. When this interaction's
3547
- media ends or the task enters `completing`, clear its `onCall` view; absence is used only by a
3571
+ audio ends or the task enters `completing`, clear its `onCall` view; absence is used only by a
3548
3572
  provider that never publishes the room. This does not assert that the caller, bridge or other
3549
3573
  agents' channels ended. A published room receives a final empty view for this interaction, and the
3550
3574
  task may remain open for wrap. Completion is a separate task action, not the caller's disconnect.
@@ -3569,10 +3593,10 @@ still-ringing `destinationId` calls that dial off, with `cancelled` as its outco
3569
3593
 
3570
3594
  **The entry says where it stands.** A dialled entry carries `stage`: `ringing` until its dial is
3571
3595
  answered, `joined` after. The `dial-outcome` is the transition and the stage is the state, the same
3572
- pairing as `task-media-started` and `media`: an `answered` outcome and the task restated with the
3596
+ pairing as `task-audio-started` and `audio`: an `answered` outcome and the task restated with the
3573
3597
  entry `joined` say one thing twice, and any other outcome removes the entry. The outcome comes
3574
3598
  first: a `task-updated` that itself moves an entry from `ringing` to `joined` before an `answered`
3575
- outcome for its dial is refused (`stream.taskUpdated.stage`), as an update that moves `media` is.
3599
+ outcome for its dial is refused (`stream.taskUpdated.stage`), as an update that moves `audio` is.
3576
3600
  The rule keys on the entry's `dialId`, so an entry the platform added itself, with none, is one
3577
3601
  whose move the stream cannot hold to an outcome: a clean stream proves nothing about ordering there. It is stated rather
3578
3602
  than left to whoever placed the dial because that knowledge lives in one process: after a reload,
@@ -3789,7 +3813,7 @@ Your own tasks are the only ones you count. What the agent holds at other provid
3789
3813
  concern — Omni set `count` knowing it, and this is how: the agent is one person on several
3790
3814
  providers, and the agent application divides their capacity among them rather than telling each the whole. A
3791
3815
  provider that has none of it for now is told **`count: 0`, agent application-stopped**: assign nothing, show
3792
- the member as `reserved` on the team member list -- signed in here, capacity held by the agent application for elsewhere,
3816
+ the member as `elsewhere` on the team member list -- signed in here, capacity held by the agent application for elsewhere,
3793
3817
  a fact this provider holds, where `on-task` would assert work it cannot see -- and take the next
3794
3818
  count as any other when the agent application has capacity for this provider again. Zero is the one restatement
3795
3819
  that follows work rather than local policy.
@@ -4125,8 +4149,9 @@ for nothing and computes nothing.
4125
4149
  | `availability` | Required. What the member is doing now. |
4126
4150
  | `since` | Optional. When the current `availability` began — not when they signed in, and not when the team member list was read. |
4127
4151
  | `break` | Present only while the member has an outstanding break request. See **A member waiting for a break**. |
4128
- | `tasks` | The member's open tasks as `MemberTask`s: the same task the member's desk holds, trimmed by the provider. The assignment, title, task type, phase, party, room, media and the full history are what the lead reads; the workspace — controls, their source, browsers, completion terms — is the member's and travels only if the provider sends it. `[]` when the member holds none; omitted only where the provider cannot see them. |
4129
- | `listening` | Present while this lead's channel is in this member's call — listening, coaching or joined — with the `mode` they are heard in and since when. One member's call at a time (`team.listening.single`). |
4152
+ | `tasks` | The member's open tasks as `MemberTask`s: the same task the member's desk holds, trimmed by the provider. The assignment, title, task type, phase, party, room and audio are what the lead reads; the task's history is carried in the same shape as on the member's desk, as much of it as the provider chooses to send; the workspace — controls, their source, browsers, completion terms — is the member's and travels only if the provider sends it. `[]` when the member holds none; omitted only where the provider cannot see them. |
4153
+ | `listening` | Present while this lead's channel is in this member's call — listening, coaching or joined — naming which of the member's calls by `assignmentId`, the `mode` they are heard in, and since when. Its appearance is what the lead's audio opens on. One member's call at a time (`team.listening.single`). |
4154
+ | `shift` | The member's own history for the day, about the person rather than any one call: `signedInAt`, `signedOutAt` once it has happened, today's totals as the provider counts them — `talkSeconds`, `holdSeconds`, `breakSeconds`, `tasksHandled`, each present only when the provider knows it — and the day's sign-in, sign-out and break `events`, oldest first. Omitted where the provider cannot say. |
4130
4155
 
4131
4156
  Each availability value means one thing:
4132
4157
 
@@ -4135,7 +4160,7 @@ Each availability value means one thing:
4135
4160
  | `ready` | Signed in, able to take work, none assigned. |
4136
4161
  | `on-task` | Working on at least one task. It says nothing about how many, and nothing about whether more will fit. |
4137
4162
  | `on-break` | Stopped and not taking work, whether they asked or somebody stopped them. The reason lives on their own `BreakState`, not here. |
4138
- | `reserved` | Signed in here, and the agent application holds this agent's capacity for another provider (`count: 0`, agent application-stopped): not receiving this provider's work, and not on a break. See **Capacity**. |
4163
+ | `elsewhere` | Signed in here, and the agent application holds this agent's capacity for another provider (`count: 0`, agent application-stopped): not receiving this provider's work, and not on a break. See **Capacity**. |
4139
4164
  | `signed-out` | Known to this team but not signed in to this provider. |
4140
4165
 
4141
4166
  **Always publish the complete team member list, never a change to it.** Team presence typically reaches an
@@ -4233,8 +4258,8 @@ executeTeam({ command: { type: "decline", memberId: "A-1", reason: "In a call" }
4233
4258
 
4234
4259
  **On `join` the provider bridges the lead's channel into the call.** The member's task moves to
4235
4260
  `leadAssist: { stage: "joined", leadId }`, the member on the team member list carries
4236
- `listening: { mode: "join-call", since }`, and the lead's audio arrives as **Listening to a call**
4237
- describes. Nothing is a task on the lead's desk: a join is the lead's act on the team surface, not
4261
+ `listening: { assignmentId, mode: "join-call", since }`, and the lead's audio opens on it as
4262
+ **Listening to a call** describes. Nothing is a task on the lead's desk: a join is the lead's act on the team surface, not
4238
4263
  work assigned to them.
4239
4264
 
4240
4265
  **A lead is on one member's call at a time.** A `join` from a lead already on a call -- their own
@@ -4255,15 +4280,17 @@ listening unasked -- and one act at the end of either. What follows is the same
4255
4280
 
4256
4281
  | | The member's task | The lead's desk |
4257
4282
  | --- | --- | --- |
4258
- | Take-over | `task-media-ended`, then `completing`: the member's interaction is over and their wrap runs, exactly as after `end-call`. The audio ends first, as before every voice ending (`stream.taskEnded.mediaOpen`). | An ordinary assignment arrives: `task-offered` with `acceptance: "automatic"`, carrying the call's history and `takenOver: { memberId, since }`. |
4283
+ | Take-over | `task-audio-ended`, then `completing`: the member's interaction is over and their wrap runs, exactly as after `end-call`. The audio ends first, as before every voice ending (`stream.taskEnded.audioOpen`). The member loses `listening` on the team member list no later than the offer, and the lead's listening audio closes. | An ordinary assignment arrives: `task-offered` with `acceptance: "automatic"`, carrying the call's history and `takenOver: { memberId, since }`, within the lead's capacity. |
4259
4284
  | The member completes | `task-ended` with `{ type: "taken-over", leadId }`, at the member's own completion. The lead is named by user id, since a lead is not a directory item. | The lead works the call as any agent would, and ends it as any call ends. |
4260
4285
 
4261
- **The taken-over call is offered whatever break the lead is on.** A lead working as lead alone is
4262
- on a break of a working kind, and the provider assigns the call regardless: it is the one task a
4263
- break in effect may hold (`break.on-break.tasks` allows it, and nothing else). It is the lead's own
4264
- act, so it counts against no capacity, like the agent's own dial; the only conditions are that the
4265
- lead is not already on another call and their voice channel is free. A lead with the team feature
4266
- off cannot take over, since they have no member's assignment to name.
4286
+ **The taken-over call is offered whatever break the lead is on, and counted like any other.** A
4287
+ lead working as lead alone is on a break of a working kind, and the provider assigns the call
4288
+ regardless: it is the one task a break in effect may hold (`break.on-break.tasks` allows it, and
4289
+ nothing else). It is a call landing on the lead's desk, so it counts against their capacity as
4290
+ every call does, with no exemption: a lead at capacity cannot take over, and the provider answers
4291
+ `failed`. The other conditions are that the lead is not already on another call and their voice
4292
+ channel is free. A lead with the team feature off cannot take over, since they have no member's
4293
+ assignment to name.
4267
4294
 
4268
4295
  ```ts
4269
4296
  const leadAssistCapable = {
@@ -4297,34 +4324,47 @@ history. The three modes determine who hears the lead:
4297
4324
  | `coach` | The agent alone. The customer hears nothing. |
4298
4325
  | `join-call` | Everyone on the call. |
4299
4326
 
4300
- All three come with the `lead` flag; a centre reserves none of them per lead. The flow:
4327
+ All three come with the `lead` flag; a centre reserves none of them per lead.
4301
4328
 
4302
- ```ts
4303
- // 1. The lead picks a member from the team member list and starts silent.
4304
- executeTeam({ command: { type: "listen", memberId: "A-1" } })
4305
- // team-updated: the member carries listening: { mode: "listen", since }
4306
- // team-media-started: { memberId: "A-1", assignmentId: "alloc-42" } -- the lead's audio attaches
4329
+ **There is no team audio.** Audio is the agent's or the lead's, and the lead's audio while
4330
+ listening follows the provider's published state exactly as a task's does: `listening` on the
4331
+ member is the provider's word that the lead's channel is in the call. When it appears, the
4332
+ application opens `openAudio({ assignmentId })` on the lead's connection for the call it names,
4333
+ and plays the `CallAudio` returned; when it goes, the application closes it. Nothing else says
4334
+ when the lead's audio is up, and a reload reopens it from the snapshot's team member list as a task
4335
+ carried with `audio: "started"` is reopened.
4336
+
4337
+ The flow, end to end:
4307
4338
 
4308
- // 2. The lead changes how they are heard; the provider restates the member with the new mode.
4339
+ ```ts
4340
+ // 1. The lead, with the feature on, sees a member on-task with their tasks, and picks a call.
4341
+ // 2. The lead's channel joins it in silence. Conditions: the lead's voice channel is free --
4342
+ // not on a call, not listening elsewhere. Capacity is not involved: nothing is assigned.
4343
+ executeTeam({ command: { type: "listen", memberId: "A-1", assignmentId: "alloc-42" } })
4344
+ // 3. The provider republishes the team member list; the member carries
4345
+ // listening: { assignmentId: "alloc-42", mode: "listen", since }
4346
+ // -- the application opens openAudio({ assignmentId: "alloc-42" }) on the lead's connection.
4347
+ // 4. The lead changes how they are heard; the provider restates listening.mode. The audio stays open.
4309
4348
  executeTeam({ command: { type: "coach", memberId: "A-1" } })
4310
4349
  executeTeam({ command: { type: "join-call", memberId: "A-1" } })
4311
-
4312
- // 3. The lead leaves, or takes the call.
4350
+ // 5. The lead leaves: the provider republishes the member without listening; the application closes the audio.
4313
4351
  executeTeam({ command: { type: "leave", memberId: "A-1" } })
4352
+ // 6. The member's call ends: the same as leave, from the provider's side.
4353
+ // 7. The lead takes the call: the member loses listening no later than the offer, the application
4354
+ // closes the listening audio, and the lead's own call arrives as an ordinary assignment marked
4355
+ // takenOver, counted against capacity, with its audio following task-audio-started as any call's does.
4356
+ // At capacity the take-over is refused and the lead stays listening.
4314
4357
  executeTeam({ command: { type: "take-over-call", memberId: "A-1" } })
4358
+ // 8. A join on the member's request is the same flow entered at step 3 with mode: "join-call",
4359
+ // and the member's task shows leadAssist: joined.
4360
+ // 9. A reload of the lead's application: the snapshot's team member list still carries listening,
4361
+ // so the audio is reopened.
4315
4362
  ```
4316
4363
 
4317
- **The lead's audio arrives through team media.** While the lead's channel is in a member's call
4318
- -- listening, coaching or joined on request -- the lead's connection receives `team-media-started`
4319
- naming the member and the member's assignment, and on a softphone the agent application opens
4320
- `openMedia` on that assignment as it does for a task; `team-media-ended` closes it. Voice only, and
4321
- to a login that declares `lead` (`event.teamMedia.*`). The member's own media events are the
4322
- member's and never reach the lead.
4323
-
4324
- **`listening` on the member is the state**, restated on every change of mode, and a lead is on
4325
- one member's call at a time (`team.listening.single`). The member's call ending ends the lead's
4326
- listening with it: the provider republishes the member without `listening` and sends
4327
- `team-media-ended`.
4364
+ **`listening` on the member is the state**, restated on every change of mode, naming the call so
4365
+ the application knows which audio to open (`team.member.listening.assignmentId`, and one the
4366
+ member's tasks carry, `team.member.listening.assignment`), and a lead is on one member's call at a
4367
+ time (`team.listening.single`).
4328
4368
 
4329
4369
  **A lead listens while holding no call of their own.** Omni offers Listen to a lead whose voice
4330
4370
  channel is free, and a provider answers a `listen` from one whose channel is not `failed`. That
@@ -4369,20 +4409,20 @@ A request needing *more than one* approval is not something this contract descri
4369
4409
  no partial state to report and no progress to display: a request is either still owed a
4370
4410
  decision or it is not.
4371
4411
 
4372
- ## Real-time media
4412
+ ## Real-time audio
4373
4413
 
4374
- Every voice provider has media: `channel: "voice"` says audio exists. Where the agent hears it
4414
+ Every voice provider has audio: `channel: "voice"` says audio exists. Where the agent hears it
4375
4415
  depends on the `phone` the agent application selected at authentication and connect from the manifest's
4376
4416
  `phones`. On a softphone, audio lands in Omni; on a desk phone, it lands on the handset and Omni
4377
- opens no audio. See **How the agent hears the call**. Media transitions are voice-only;
4378
- chat and email publish none (`event.media.channel`, `stream.taskMedia.channel`).
4417
+ opens no audio. See **How the agent hears the call**. Audio transitions are voice-only;
4418
+ chat and email publish none (`event.audio.channel`, `stream.taskAudio.channel`).
4379
4419
 
4380
4420
  The provider owns signalling and the platform's endpoint configuration. The agent application selects the
4381
4421
  declared phone mode; it does not enumerate or reconfigure the platform's devices. The selected
4382
4422
  mode stays with the login rather than being inferred from a snapshot or a device failure.
4383
4423
 
4384
4424
  Nor does the audio ever stand in for the task: a task's presence and
4385
- phase follow the provider's reports about the work, and the media — attaching, moving through a
4425
+ phase follow the provider's reports about the work, and the audio — attaching, moving through a
4386
4426
  hold, a consult, a conference or a transfer, and ending — is transient beside it. See **A task is
4387
4427
  never its audio** under **Task assignment lifecycle**.
4388
4428
 
@@ -4419,7 +4459,7 @@ refuses a false one, as it refuses a name this contract does not list.
4419
4459
  | --- | --- |
4420
4460
  | `online` | Whether the agent application has a network interface up. Not a claim that anything is reachable — the adapter knows whether it can reach its own platform far better than the agent application does — so `false` is a reason not to go ready and `true` is not a reason to. |
4421
4461
  | `audio` | Present on a voice connection, absent where there is no audio. |
4422
- | `audio.input` | `available` with `localAudio` — the microphone as captured, the same stream `openMedia` receives — and `flowing`, false while no audio moves through it, when `mutedBy` says who stopped it: `host`, the agent application's own Mute on an agent application that mutes the station, or `station`, a slider on the headset or the operating system, which the agent application observed and did not do. An adapter treats `host` as the agent's act on a call and `station` as a condition of the station. `unavailable` with `reason`, since each wants a different fix from the agent: `no-device`; `denied`; `not-asked`, which an agent application that asks at connect never publishes; `in-use`, a device present and permitted that another application holds — on an agent desktop the commonest of all; `lost`, a capture that ended. An agent application decides the reason from the devices before the error name: a browser can report a permission error on a machine with no microphone at all, and "grant permission" is the wrong instruction for an agent who needs to plug one in. `failure` carries the words Omni showed them. |
4462
+ | `audio.input` | `available` with `localAudio` — the microphone as captured, the same stream `openAudio` receives — and `flowing`, false while no audio moves through it, when `mutedBy` says who stopped it: `host`, the agent application's own Mute on an agent application that mutes the station, or `station`, a slider on the headset or the operating system, which the agent application observed and did not do. An adapter treats `host` as the agent's act on a call and `station` as a condition of the station. `unavailable` with `reason`, since each wants a different fix from the agent: `no-device`; `denied`; `not-asked`, which an agent application that asks at connect never publishes; `in-use`, a device present and permitted that another application holds — on an agent desktop the commonest of all; `lost`, a capture that ended. An agent application decides the reason from the devices before the error name: a browser can report a permission error on a machine with no microphone at all, and "grant permission" is the wrong instruction for an agent who needs to plug one in. `failure` carries the words Omni showed them. |
4423
4463
  | `audio.output` | `available`, with `flowing` where the agent application can know whether audio reaches the speaker — a browser mostly cannot, and omits it; a native agent application reads the endpoint — false while it is silenced, when `mutedBy` says who did it, as on input. Or `unavailable` with `reason` — `no-device`, or `lost` for one removed — and `failure`: an agent who cannot hear is as unable to take a call as one who cannot speak. |
4424
4464
 
4425
4465
  Omni republishes the report whenever it changes — a permission granted late, a headset unplugged,
@@ -4488,10 +4528,10 @@ A voice manifest lists its `phones` and any other channel lists none (`manifest.
4488
4528
  `manifest.phones.channel`). The agent application's `phone` is required on a voice login, absent on any other,
4489
4529
  and one the manifest listed (`context.phone.required`, `.unexpected`, `.unsupported`). Everything
4490
4530
  about audio then follows the phone rather than the channel: on a softphone the agent application reports its
4491
- audio, the adapter implements `openMedia`, and `task-media-started` is the word to open it; on a
4492
- desk phone the agent application reports no audio, opens nothing, and `task-media-started` still marks the
4531
+ audio, the adapter implements `openAudio`, and `task-audio-started` is the word to open it; on a
4532
+ desk phone the agent application reports no audio, opens nothing, and `task-audio-started` still marks the
4493
4533
  moment the call is live so the desk shows it, with the sound on the handset. A platform that lists
4494
- `deskPhone` alone never implements `openMedia`; one that lists `softphone` always does.
4534
+ `deskPhone` alone never implements `openAudio`; one that lists `softphone` always does.
4495
4535
 
4496
4536
  **The mode comes from the agent application; the status comes from the provider.** The agent application knows which kind
4497
4537
  of station the agent signed in at, because the person chose it, and nothing else can know that.
@@ -4503,10 +4543,10 @@ the handset is momentarily unregistered, and demanding a microphone of it, is th
4503
4543
  sentence exists to refuse. What the provider does when the handset is not registered is what it
4504
4544
  does for any station that cannot take a call -- hold the agent not-ready and say why.
4505
4545
 
4506
- **On a desk-phone login the agent application never calls `openMedia`.** There is no stream to hand over and
4546
+ **On a desk-phone login the agent application never calls `openAudio`.** There is no stream to hand over and
4507
4547
  no audio to attach; an agent application that calls it anyway is in error, and an adapter that receives the call
4508
4548
  answers `unavailable` with a non-retryable failure, since waiting changes nothing about a station
4509
- that is a telephone. The harness requires no `openMedia` of such an adapter and never calls it.
4549
+ that is a telephone. The harness requires no `openAudio` of such an adapter and never calls it.
4510
4550
 
4511
4551
  **Which handset a desk-phone login rings is the platform's configuration for that agent**, and
4512
4552
  this wire never asks the agent for it: an agent application declares `phone` and nothing more. What a platform
@@ -4529,11 +4569,11 @@ is the administrator's, and a login is not a request to reconfigure an agent.
4529
4569
 
4530
4570
  ### Opening the audio
4531
4571
 
4532
- `openMedia` hands Omni the remote audio for one task. Every adapter whose manifest lists
4572
+ `openAudio` hands Omni the remote audio for one task. Every adapter whose manifest lists
4533
4573
  `softphone` implements it, because on a softphone the call's audio lands in Omni:
4534
4574
 
4535
4575
  ```ts
4536
- openMedia({ assignmentId, localAudio }): Promise<OpenMediaResult>
4576
+ openAudio({ assignmentId, localAudio }): Promise<OpenAudioResult>
4537
4577
  // { status: "opened", session } | { status: "unavailable", failure }
4538
4578
  ```
4539
4579
 
@@ -4556,8 +4596,8 @@ carries as `audio.input.localAudio`, and absent while that input is `unavailable
4556
4596
  bridges audio without an application-side input may ignore it; one that needs it and finds it absent
4557
4597
  answers `unavailable` with a failure Omni shows the agent.
4558
4598
 
4559
- **When to ask is the provider's word, not Omni's guess.** On a softphone login, Omni opens media on `task-media-started`,
4560
- and on a task arriving with `media: "started"` on a snapshot; it closes on `task-media-ended` and
4599
+ **When to ask is the provider's word, not Omni's guess.** On a softphone login, Omni opens audio on `task-audio-started`,
4600
+ and on a task arriving with `audio: "started"` on a snapshot; it closes on `task-audio-ended` and
4561
4601
  when the task ends. Between those words, nothing Omni's own senses report — a stream that drops, a
4562
4602
  track that ends — moves the task or its audio.
4563
4603
 
@@ -4607,8 +4647,8 @@ agent application's. So mute is not a capability a provider declares, not a cont
4607
4647
  locks, not a preference the person keeps with the provider, and not a command: nothing about the
4608
4648
  station crosses to the provider except two things -- the station's condition, in the agent application report,
4609
4649
  and the record of when the agent could not be heard, through `recordStep`. An agent application offers Mute on
4610
- every voice task with media open, under its own local policy, in `in-progress` and `paused`
4611
- alone, and performs it through `VoiceMediaSession.setMuted()`. A task carrying
4650
+ every voice task with audio open, under its own local policy, in `in-progress` and `paused`
4651
+ alone, and performs it through `CallAudio.setMuted()`. A task carrying
4612
4652
  `capabilities.mute` is refused (`task.capability.unknown`), and so is a `mute` command
4613
4653
  (`command.type`), a `mute` preference (`preference.id`) and a `mute` policy (`team.policy.key`).
4614
4654
 
@@ -4644,9 +4684,9 @@ forty seconds" and "the agent's headset was muted for forty seconds" as the diff
4644
4684
  are. See **The agent application records what it performs**.
4645
4685
 
4646
4686
  **Mute has a lifecycle, and none of it is inferred.** A call that starts while the station is
4647
- already muted begins a `muted` leg the moment its media starts, `mutedBy: "station"`. Media that
4687
+ already muted begins a `muted` leg the moment its audio starts, `mutedBy: "station"`. Audio that
4648
4688
  ends while any leg is open ends the leg at that instant, as every agent application-performed leg ends. And the
4649
- agent application's own Mute starts off on every call, which is every `task-media-started` -- a connect-back
4689
+ agent application's own Mute starts off on every call, which is every `task-audio-started` -- a connect-back
4650
4690
  opens a second call on the same task with no offer -- so an agent is never muted by the call before.
4651
4691
 
4652
4692
  **Every press on a headset is the agent's own press**, performed the agent application's way, and the lights
@@ -4683,10 +4723,10 @@ declared:
4683
4723
  | --- | --- |
4684
4724
  | `answer`, `accept` | Nothing. A task that was offered can be accepted, or offering it meant nothing. |
4685
4725
  | `end-call` | The `endCall` capability: ends my part, the caller continues. |
4686
- | `terminate-call` | The `terminateCall` capability: ends the caller channel. |
4726
+ | `terminate-call` | The `terminateCall` capability: ends the whole call, every channel on it. |
4687
4727
  | `conference` with `action: "remove"` | The `conference` capability, and somebody else on the call: a remove that would leave the agent alone is `end-call`, and a provider answers it `failed`. |
4688
4728
  | `decline` | The `decline` capability on any channel, **and** Omni local policy permitting it. One word for refusing an offer, whatever the channel. |
4689
- | `call` | The `preview` phase. A record put in front of an agent is there to be called, so the phase is the gate and there is no capability. It is a dial, with a `dialId` and a `dial-outcome`. |
4729
+ | `dial` | The `preview` phase. A record put in front of an agent is there to be called, so the phase is the gate and there is no capability. It is a dial, with a `dialId` and a `dial-outcome`. |
4690
4730
  | `complete` | `completionMode: "agent-command"`. The `outcomes` capability decides whether a code travels with the command, never whether the command exists — a task Omni cannot complete never ends. What travels is what the capability published: a code from its list where it has one (`command.complete.outcome.unknown`), a code at all where it requires one (`.outcome.required`), notes as it said (`.notes.required`, `.notes.unexpected`), and neither where the task declares no outcomes (`.outcome.unexpected`). |
4691
4731
  | `connect-back` | The `connectBack` 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. |
4692
4732
  | `transfer` with `action: "warm"` | The `warmTransfer` capability. `action: "cold"` is gated by `coldTransfer`; the two are declared and offered separately. |
@@ -4706,7 +4746,7 @@ declared while the task remains open; the phase prevents this task from controll
4706
4746
  it no longer owns. Omni shows these controls only in the two interaction phases, and
4707
4747
  `validateTaskCommand` refuses them outside those phases (`command.phase.interaction`).
4708
4748
  The commands with a phase of their own -- `answer`, `accept` and `decline` in `pending`,
4709
- `call` in `preview`, `connect-back` in `completing`, `complete` in any -- are not among them.
4749
+ `dial` in `preview`, `connect-back` in `completing`, `complete` in any -- are not among them.
4710
4750
 
4711
4751
  `validateTaskCommand(command, task)` holds a command to this table at runtime, both ways: the
4712
4752
  capability it needs, the phase it belongs to, and the state that has to stand. The task it wants
@@ -5055,30 +5095,30 @@ snapshots until it ends.
5055
5095
  Replaces the current representation of one provider-local task. It is a full task value, not a
5056
5096
  partial patch.
5057
5097
 
5058
- ### `task-media-started`
5098
+ ### `task-audio-started`
5059
5099
 
5060
- The provider's word that the task's audio should now attach. Omni calls `openMedia` on it — and on
5061
- a task carried with `media: "started"`, which is how a reconnect snapshot reattaches audio an
5100
+ The provider's word that the task's audio should now attach. Omni calls `openAudio` on it — and on
5101
+ a task carried with `audio: "started"`, which is how a reconnect snapshot reattaches audio an
5062
5102
  earlier event brought — and renders the call as live from that word, never from its own senses. It
5063
- precedes `openMedia` and is never a reply to it: a provider whose media state comes from the
5103
+ precedes `openAudio` and is never a reply to it: a provider whose audio state comes from the
5064
5104
  platform, a station going in use the moment a call is answered, sends it then, before any agent application has
5065
- opened anything, and `openMedia` has its own answer for what the agent application did. It
5066
- names a task whose work has begun, and it alternates with `task-media-ended`: media that never
5105
+ opened anything, and `openAudio` has its own answer for what the agent application did. It
5106
+ names a task whose work has begun, and it alternates with `task-audio-ended`: audio that never
5067
5107
  started cannot end, so a live call whose provider says nothing about its audio is a provider in
5068
5108
  breach, not a state a desk fills in from its own devices.
5069
5109
 
5070
- The event is the transition and the task's `media` field is the state. A `task-updated` re-states
5071
- the media its task already holds — republishing `started` on a hold is a statement, not a second
5110
+ The event is the transition and the task's `audio` field is the state. A `task-updated` re-states
5111
+ the audio its task already holds — republishing `started` on a hold is a statement, not a second
5072
5112
  arrival — but it does not move it: an update that itself flips the field is refused
5073
- (`stream.taskUpdated.media`), and the pairing at the moment audio arrives is the phase change
5113
+ (`stream.taskUpdated.audio`), and the pairing at the moment audio arrives is the phase change
5074
5114
  without the field, then the event. Releasing `ended` is the one move an update may make, since
5075
5115
  wrapped audio has nothing left to end.
5076
5116
 
5077
- ### `task-media-ended`
5117
+ ### `task-audio-ended`
5078
5118
 
5079
- Signals that a task's real-time media ended. For voice and similar channels, this starts the fixed
5080
- completion timer. It does not remove the task, and it ends only audio that `task-media-started` — or
5081
- a task carried with `media: "started"` — attached.
5119
+ Signals that a task's real-time audio ended. For voice and similar channels, this starts the fixed
5120
+ completion timer. It does not remove the task, and it ends only audio that `task-audio-started` — or
5121
+ a task carried with `audio: "started"` — attached.
5082
5122
 
5083
5123
  ### `task-ended`
5084
5124
 
@@ -5086,7 +5126,7 @@ Every outcome ends the task for this agent. On `task-ended`, Omni:
5086
5126
 
5087
5127
  - removes the task from its current provider view;
5088
5128
  - clears the task workspace when it is selected;
5089
- - stops task timers and media;
5129
+ - stops task timers and audio;
5090
5130
  - releases task-scoped resources; and
5091
5131
  - selects another task or returns to the idle workspace.
5092
5132
 
@@ -5101,7 +5141,7 @@ performed. Past the bound the agent application calls `snapshot()`: a snapshot s
5101
5141
  held open by a provider that said it was done, and the desk shows it as unsettled -- "Completing...
5102
5142
  the provider has not confirmed" -- naming the command; a snapshot no longer carrying it clears the
5103
5143
  task, since the ending was owed and lost. The drive holds a provider to the same bound
5104
- (`drive.completion.unsettled`). The `task-media-ended` event and the `completing` phase are likewise
5144
+ (`drive.completion.unsettled`). The `task-audio-ended` event and the `completing` phase are likewise
5105
5145
  non-terminal. A replacement
5106
5146
  snapshot that no longer contains the task also clears it. Repeated `task-ended` delivery with the
5107
5147
  same envelope ID is harmless, and a `task-ended` naming an assignment that has already ended is
@@ -5135,8 +5175,8 @@ console is the silence problem one layer up. One event per occurrence; the agent
5135
5175
  provider does not batch. `exerciseAdapter` treats a diagnostic delivered during a run as a
5136
5176
  violation (`diagnostic.raised`): a conformance run against a platform that is breaking its rules
5137
5177
  fails loudly rather than passing with a note. A task published under `capabilitySource:
5138
- "undetermined"` is the same kind of fact, stated on the task instead, and fails a run the same way
5139
- (`capabilitySource.undetermined`); see **Task capabilities**.
5178
+ "not-yet-read"` is the same kind of fact, stated on the task instead, and fails a run the same way
5179
+ (`capabilitySource.notYetRead`); see **Task capabilities**.
5140
5180
 
5141
5181
  | Field | Contract |
5142
5182
  | --- | --- |
@@ -5306,10 +5346,10 @@ Nothing there is a violation: an adapter with no team has nothing to exercise. B
5306
5346
  no tasks exercises no task rule, and a pass over it reads as coverage it is not.
5307
5347
 
5308
5348
  **A static run never answers a call.** It states a capacity and disconnects, so everything
5309
- downstream of `answer` -- the room, the stage, media, every phase past `pending`, every dial
5349
+ downstream of `answer` -- the room, the stage, audio, every phase past `pending`, every dial
5310
5350
  outcome -- stays in `notExercised` for every adapter, and a rule about a live call is enforced only
5311
5351
  in each adopter's own tests. `{ drive: true }` closes that: the exercise takes the first task the
5312
- provider offers through one ordinary lifecycle -- accept it, wait for its media and open it on a
5352
+ provider offers through one ordinary lifecycle -- accept it, wait for its audio and open it on a
5313
5353
  softphone, hold and resume where the task offers `hold`, end the call where it offers `endCall`,
5314
5354
  complete it with an outcome where the agent completes -- and holds every step to the rules a
5315
5355
  agent application holds a provider to. Each command is validated against the task as published
@@ -5324,17 +5364,17 @@ nothing (`getUserDetails.user.*`, `getUserDetails.unasked`, `connection.getUserD
5324
5364
  the second adapter a `rebuild` gives comes up signed in as the same login from the secrets alone
5325
5365
  before it reads anything (`drive.reload.login`), and is then held to everything the first was on
5326
5366
  connect: the methods its declarations call for, the agent application's report, a capacity stated to it, its
5327
- snapshot read as the connect snapshot was, and its media opened afresh on a task carried with
5328
- `media: "started"`, since the first client's session died with it. The result also says which rules the
5367
+ snapshot read as the connect snapshot was, and its audio opened afresh on a task carried with
5368
+ `audio: "started"`, since the first client's session died with it. The result also says which rules the
5329
5369
  run evaluated, pass or fail, in `rulesEvaluated`: the validators' as each was applied, the stream's
5330
5370
  as each case was considered, so a test that needs a rule to have run asserts it there rather than
5331
5371
  inferring it from an empty `violations`, and a rule absent from it was never looked at, which is a
5332
5372
  gap and not a pass. With the audio open on a softphone,
5333
5373
  the drive mutes it for one second and reports the leg through `recordStep`, begun and then ended,
5334
5374
  expecting each report `recorded` with the provider-selected history `at` (`drive.recordStep.failed`, `.rejected`, `result.recordStep.at`); then it mutes again and
5335
- ends the call muted, as agents do, so the leg is open when the media ends, the provider closes it
5375
+ ends the call muted, as agents do, so the leg is open when the audio ends, the provider closes it
5336
5376
  in the completing publication or the open entry is refused (`task.history.muted.open`),
5337
- and the drive's closing report after the media ended is expected `recorded` and to change nothing:
5377
+ and the drive's closing report after the audio ended is expected `recorded` and to change nothing:
5338
5378
  a record restated afterwards with that leg's duration altered is named (`drive.recordStep.overwritten`).
5339
5379
  Where the provider restates the task's record afterwards, each leg is in it or the hole is named
5340
5380
  (`drive.recordStep.history`). Given `rebuild`, a way to build the adapter again as an agent application reload
@@ -5347,7 +5387,7 @@ the record in memory has nothing and is named (`drive.reload.snapshot`, `drive.r
5347
5387
  `.rejected`). The second adapter's snapshot is taken as any resync is: held to what the stream knew
5348
5388
  before it replaces it, so a phase gone backwards, a record that shrank or audio forgotten on a task
5349
5389
  still at work is named by the stream's own rules (`stream.snapshot.phase`, `.history`,
5350
- `.media`) and a reload is a place those rules keep working, not one where they stop. The second
5390
+ `.audio`) and a reload is a place those rules keep working, not one where they stop. The second
5351
5391
  adapter is held to what the first was: the same provider
5352
5392
  (`drive.reload.manifest`), a snapshot that stands as any snapshot must, and a record that lost none
5353
5393
  of the entries the first had published -- a record once read is not unread across a reload either.
@@ -5374,15 +5414,15 @@ completed from wherever it stands once the drive has nothing left to do on it --
5374
5414
  after an `end-call`, or from `in-progress` where there is no call to end, which is every chat and
5375
5415
  email and a voice task offering no `endCall` -- so a conversation reaches its end as a call does.
5376
5416
  The drive stops where the task offers no way on and says nothing about what it could not reach. A
5377
- softphone adapter written for a browser needs its media APIs supplied by whatever runs the
5378
- harness: the drive opens media outside a browser, and a provider that quietly stopped carrying audio
5417
+ softphone adapter written for a browser needs its audio APIs supplied by whatever runs the
5418
+ harness: the drive opens audio outside a browser, and a provider that quietly stopped carrying audio
5379
5419
  where `AudioContext` was missing would be lying about the one thing the channel is for. It is off by default because it issues
5380
5420
  commands against whatever platform the adapter is connected to: turn it on against a test backend.
5381
5421
 
5382
5422
  ```ts
5383
5423
  const driven = await exerciseAdapter(adapter, context, { collectOnly: true, drive: true });
5384
5424
  expect(driven.violations).toEqual([]);
5385
- assertReached(driven, ["task.onCall", "task.media", "event.task-ended"]);
5425
+ assertReached(driven, ["task.onCall", "task.audio", "event.task-ended"]);
5386
5426
  ```
5387
5427
  `assertReached(result, subjects)` is the paired assertion: it throws naming every subject the run
5388
5428
  never met, so a test that meant to check a team member list cannot pass on a fixture that never produced one.
@@ -5419,11 +5459,11 @@ cannot be established from TypeScript structure alone.
5419
5459
  | `stillHost(report?, guarantees?, mute?)` | An agent application 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. |
5420
5460
  | `TaskStream`, `BreakStream` | The cross-event models the harness applies after the connect snapshot, exported for an agent application that wants the same rules at its boundary: `seed(snapshot)`, then `apply(envelope)` returns the violations. |
5421
5461
  | `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. |
5422
- | `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. |
5462
+ | `assertAudioFollowsTheTask(envelopes, snapshot?)` | The audio follows the task and never decides it: every task is introduced once, `task-audio-started` and `task-audio-ended` alternate on work that has begun, audio ends only where it arrived, and what follows the audio ending is `completing` or `task-ended`. The harness applies the same rules to every event after the connect snapshot (`stream.*`). A sequence with no audio satisfies it by never testing it — pair it with the assertion that the audio end is present. |
5423
5463
  | `assertBreakAttemptProviders(candidates, asked)` | A break attempt asks every usable provider holding capacity, `refreshing` included, and nothing of a provider whose login is `expired`. |
5424
5464
  | `assertBreakBeginsAfterTask(steps)` | A break asked for on a task is committed as `starting-after-task` while work remains and reaches `on-break` only once nothing is outstanding — never beside a task, never later than the step that has none. |
5425
5465
  | `assertDeniedAndRetriedBreak(states)` | A denial transitions directly to `not-requested`; a later request can still be granted. |
5426
- | `assertWrapTimeout(task, mediaEndedAt, deadline, toleranceMs?)` | The wrap deadline equals media end plus the task allowance, within a tolerance that defaults to 1000ms; a task with no allowance has no deadline, and one observed is the violation. |
5466
+ | `assertWrapTimeout(task, audioEndedAt, deadline, toleranceMs?)` | The wrap deadline equals audio end plus the task allowance, within a tolerance that defaults to 1000ms; a task with no allowance has no deadline, and one observed is the violation. |
5427
5467
  | `assertBrowserSessionIsolation(left, right, expected)` | Browser reuse follows only the declared isolation scheme. |
5428
5468
  | `assertNoBrowserSessionKeyCollisions(scenarios)` | No two distinct scenarios derive the same session key. Feed it adversarial names. |
5429
5469
 
@@ -5456,11 +5496,11 @@ action sets. The outer capability may be locked. Absence grants no permission, a
5456
5496
  exist without any permission. A provider may offer only stop for a recording started automatically,
5457
5497
  or withdraw a control on a later task update. There is no global recording mode and no automatic
5458
5498
  start from a capability declaration. Agent application support is declared on `ConnectContext.host.recording`,
5459
- not the provider-owned manifest. The task chooses an explicitly provisioned agent application destination;
5460
- unknown destinations or unsupported actions are refused visibly, never redirected to provider
5461
- recording or a default upload location. Initial agent application support requires voice softphone media and
5499
+ not the provider-owned manifest. The task names one of the storage places the agent application provisioned, by `storageId`;
5500
+ an unknown storage place or an unsupported action is refused visibly, never redirected to provider
5501
+ recording or a default upload location. Initial agent application support requires voice softphone audio and
5462
5502
  capture of both local and remote audio; an agent application unable to capture either must refuse start/resume.
5463
- This package declares that contract; it supplies no recorder, media mixing, storage or upload.
5503
+ This package declares that contract; it supplies no recorder, audio mixing, storage or upload.
5464
5504
 
5465
5505
  | Action | Required current recording state | Confirmed outcome |
5466
5506
  | --- | --- | --- |
@@ -5491,7 +5531,7 @@ There is no automatic restart, transfer to another recorder, or stop on task hol
5491
5531
  Task removal does not prove recording stopped: outstanding agent application recorders remain tracked by the
5492
5532
  agent application until its executor reconciles/finishes them, with visible unresolved cleanup failures.
5493
5533
 
5494
- Only start/resume require an in-progress or paused task with started media. Pause/stop/cancel may
5534
+ Only start/resume require an in-progress or paused task with started audio. Pause/stop/cancel may
5495
5535
  also finish an independently observed recorder while the task is completing. A pending task may
5496
5536
  show active recording, but agent controls wait until interaction begins. The two recording paths may
5497
5537
  both be active. A command to either path has no implied effect on the other.
@@ -5534,9 +5574,9 @@ This change does not publish a package or enable recording in any existing agent
5534
5574
  semantics, including pause/resume identity and rejection of a dialling result. It cannot prove audio
5535
5575
  retention/completion or source truth from a status flag; those remain executor obligations.
5536
5576
 
5537
- Agent application destination selection binds storage when start actually creates a recording. Existing recordings
5577
+ The storage place named on the task is bound when start actually creates a recording. Existing recordings
5538
5578
  keep that binding through pause/resume/stop/cancel; a later policy cannot redirect their stored audio.
5539
- The executor rejects a mismatched destination rather than moving or discarding another binding.
5579
+ The executor rejects a mismatched storage place rather than moving or discarding another binding.
5540
5580
  Action permission does not authorize unattended invocation: agent application controls require the agent's explicit
5541
5581
  act, and provider authorization remains enforced at its authenticated command boundary.
5542
5582