@xema/omni-protocol 0.1.97 → 0.1.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/guide.md +12 -11
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export type UserId = string;
|
|
|
30
30
|
* still speaking about it -- a lead names a member's assignment from another login, so the scope is
|
|
31
31
|
* the provider, not the login. What the platform calls its own record, and whether it reuses that
|
|
32
32
|
* name, is the adapter's business: a platform that reoffers a closed call under the same handle
|
|
33
|
-
* seconds later is why the adapter, not the desk, is held to this. Scope it with `
|
|
33
|
+
* seconds later is why the adapter, not the desk, is held to this. Scope it with `assignmentKey()`.
|
|
34
34
|
*/
|
|
35
35
|
export type AssignmentId = string;
|
|
36
36
|
/**
|
|
@@ -690,7 +690,7 @@ export type TaskCapabilities<C extends Channel = Channel> = C extends "voice" ?
|
|
|
690
690
|
* mistype and unreadable as an argument.
|
|
691
691
|
*/
|
|
692
692
|
export declare const BROWSER_ISOLATION_SCHEMES: {
|
|
693
|
-
readonly
|
|
693
|
+
readonly PROVIDER_NAME__ASSIGNMENT_ID__TAB_NAME: "ProviderName.AssignmentId.TabName";
|
|
694
694
|
readonly TAB_NAME: "TabName";
|
|
695
695
|
readonly PROVIDER_NAME__TASK_TYPE_NAME__TAB_NAME: "ProviderName.TaskTypeName.TabName";
|
|
696
696
|
readonly PROVIDER_NAME__TAB_NAME: "ProviderName.TabName";
|
|
@@ -1629,7 +1629,7 @@ export interface ProviderEventEnvelope<C extends Channel = Channel> {
|
|
|
1629
1629
|
occurredAt: IsoTimestamp;
|
|
1630
1630
|
event: ProviderEvent<C>;
|
|
1631
1631
|
}
|
|
1632
|
-
export declare const OMNI_FAILURE_CODES: readonly ["omni.not-authenticated", "omni.capability-not-enabled", "omni.
|
|
1632
|
+
export declare const OMNI_FAILURE_CODES: readonly ["omni.not-authenticated", "omni.capability-not-enabled", "omni.assignment-not-found", "omni.destination-not-permitted", "omni.phone-not-permitted", "omni.rate-limited", "omni.unavailable", "omni.break-already-committed"];
|
|
1633
1633
|
export type OmniFailureCode = (typeof OMNI_FAILURE_CODES)[number];
|
|
1634
1634
|
/**
|
|
1635
1635
|
* What the host refused, told to the adapter that published it: a snapshot the host would not
|
|
@@ -1762,7 +1762,7 @@ export declare const DEFAULT_TASK_TYPE_PRESENTATION: {
|
|
|
1762
1762
|
* string. Encoding before joining is what stops a provider id containing a separator from
|
|
1763
1763
|
* forging another provider's key.
|
|
1764
1764
|
*/
|
|
1765
|
-
export declare const
|
|
1765
|
+
export declare const assignmentKey: (providerId: string, assignmentId: AssignmentId) => string;
|
|
1766
1766
|
/**
|
|
1767
1767
|
* The same treatment for a `UserId`, and needed for the same reason.
|
|
1768
1768
|
*
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ export const RECORDING_ACTIONS = ["start", "pause", "resume", "stop", "cancel"];
|
|
|
40
40
|
* mistype and unreadable as an argument.
|
|
41
41
|
*/
|
|
42
42
|
export const BROWSER_ISOLATION_SCHEMES = {
|
|
43
|
-
|
|
43
|
+
PROVIDER_NAME__ASSIGNMENT_ID__TAB_NAME: "ProviderName.AssignmentId.TabName",
|
|
44
44
|
TAB_NAME: "TabName",
|
|
45
45
|
PROVIDER_NAME__TASK_TYPE_NAME__TAB_NAME: "ProviderName.TaskTypeName.TabName",
|
|
46
46
|
PROVIDER_NAME__TAB_NAME: "ProviderName.TabName",
|
|
@@ -104,7 +104,7 @@ export const breakKindAllowsListening = (kind) => kind !== undefined && LISTENIN
|
|
|
104
104
|
export const OMNI_FAILURE_CODES = [
|
|
105
105
|
"omni.not-authenticated",
|
|
106
106
|
"omni.capability-not-enabled",
|
|
107
|
-
"omni.
|
|
107
|
+
"omni.assignment-not-found",
|
|
108
108
|
"omni.destination-not-permitted",
|
|
109
109
|
"omni.phone-not-permitted",
|
|
110
110
|
"omni.rate-limited",
|
|
@@ -150,7 +150,7 @@ export const DEFAULT_TASK_TYPE_PRESENTATION = {
|
|
|
150
150
|
* string. Encoding before joining is what stops a provider id containing a separator from
|
|
151
151
|
* forging another provider's key.
|
|
152
152
|
*/
|
|
153
|
-
export const
|
|
153
|
+
export const assignmentKey = (providerId, assignmentId) => `${encodeURIComponent(providerId)}:${encodeURIComponent(assignmentId)}`;
|
|
154
154
|
/**
|
|
155
155
|
* The same treatment for a `UserId`, and needed for the same reason.
|
|
156
156
|
*
|
|
@@ -209,7 +209,7 @@ export function browserSessionKey(input) {
|
|
|
209
209
|
// provider `Acme.Voice` with type `Support` forge the key of `Acme` with `Voice.Support`.
|
|
210
210
|
const part = (value) => encodeURIComponent(value).replaceAll(".", "%2E");
|
|
211
211
|
switch (browser.isolationScheme) {
|
|
212
|
-
case BROWSER_ISOLATION_SCHEMES.
|
|
212
|
+
case BROWSER_ISOLATION_SCHEMES.PROVIDER_NAME__ASSIGNMENT_ID__TAB_NAME:
|
|
213
213
|
return `${part(providerId)}.${part(assignmentId)}.${part(browser.name)}`;
|
|
214
214
|
case BROWSER_ISOLATION_SCHEMES.TAB_NAME:
|
|
215
215
|
return part(browser.name);
|
package/guide.md
CHANGED
|
@@ -114,7 +114,7 @@ type DurationSeconds = number;
|
|
|
114
114
|
| --- | --- | --- |
|
|
115
115
|
| `IsoTimestamp` | `string` | An RFC-3339 timestamp with `Z` or an explicit numeric offset. Timezone-less values are invalid. It must pass the shared runtime validator. A JavaScript `Date` never crosses the protocol boundary. |
|
|
116
116
|
| `UserId` | `string` | A non-empty, opaque, stable identifier for a person, **issued by the provider** and drawn from the same directory as `AuthenticationState.identity.id`. It names agents and managers alike; the role is established by where the value appears, not by its type. Compare it exactly and only within one provider; do not parse it or infer meaning from its format. |
|
|
117
|
-
| `AssignmentId` | `string` | A non-empty, opaque identifier for one assignment, **issued by the provider**: unique within the provider and never reused while the provider is still speaking about it. Whether it is the platform's own handle passed through or one the adapter minted is the adapter's business. Omni scopes it with the provider ID -- see `
|
|
117
|
+
| `AssignmentId` | `string` | A non-empty, opaque identifier for one assignment, **issued by the provider**: unique within the provider and never reused while the provider is still speaking about it. Whether it is the platform's own handle passed through or one the adapter minted is the adapter's business. Omni scopes it with the provider ID -- see `assignmentKey()`. |
|
|
118
118
|
| `DurationSeconds` | `number` | A non-negative integer duration measured in seconds. |
|
|
119
119
|
|
|
120
120
|
### There is no Omni-wide user identity
|
|
@@ -127,7 +127,7 @@ So a `UserId` means nothing outside the provider that issued it. Provider A's
|
|
|
127
127
|
`history[].by` and provider B's team member list `memberId` are unrelated strings that will
|
|
128
128
|
eventually collide, and one person on several providers has several identities that nothing here
|
|
129
129
|
pairs. Scope every user identifier with its provider ID before storing or comparing it, exactly as
|
|
130
|
-
`
|
|
130
|
+
`assignmentKey()` already does for tasks — see `userKey()` under **Utilities**.
|
|
131
131
|
|
|
132
132
|
## Shapes
|
|
133
133
|
|
|
@@ -587,7 +587,7 @@ The channel arms are why `Task<"email">` rejects `hold` at compile time rather t
|
|
|
587
587
|
|
|
588
588
|
```ts
|
|
589
589
|
const BROWSER_ISOLATION_SCHEMES = {
|
|
590
|
-
|
|
590
|
+
PROVIDER_NAME__ASSIGNMENT_ID__TAB_NAME: "ProviderName.AssignmentId.TabName",
|
|
591
591
|
TAB_NAME: "TabName",
|
|
592
592
|
PROVIDER_NAME__TASK_TYPE_NAME__TAB_NAME: "ProviderName.TaskTypeName.TabName",
|
|
593
593
|
PROVIDER_NAME__TAB_NAME: "ProviderName.TabName",
|
|
@@ -1235,7 +1235,7 @@ const HISTORY_STEPS_WITH_A_PERSON = [
|
|
|
1235
1235
|
const OMNI_FAILURE_CODES = [
|
|
1236
1236
|
"omni.not-authenticated",
|
|
1237
1237
|
"omni.capability-not-enabled",
|
|
1238
|
-
"omni.
|
|
1238
|
+
"omni.assignment-not-found",
|
|
1239
1239
|
"omni.destination-not-permitted",
|
|
1240
1240
|
"omni.phone-not-permitted",
|
|
1241
1241
|
"omni.rate-limited",
|
|
@@ -2073,7 +2073,7 @@ Creates one live provider connection for the signed-in agent.
|
|
|
2073
2073
|
| `autoAcceptTasks` | Agent local policy policy relayed to the provider at login, stated by the agent application on every connection and never assumed from its absence. When `true`, a pending task states its `acceptance`; when `false`, every task requires agent acceptance. Fixed for this connection, like everything else here: the provider states or omits `acceptance` by the value it was sent, and Omni validates by that same value, not by a policy that has since moved — a change reaches the provider through a fresh `connect()`. |
|
|
2074
2074
|
| `timeZone` | The same value passed as `AuthenticationContext.timeZone`. The provider stores it on the agent and carries it on the identity. See **The agent's day**. |
|
|
2075
2075
|
| `phone` | The same value passed as `AuthenticationContext.phone`: how this login hears its calls. The type ties `host.mute` to it: a `softphone` login's agent application states what its Mute does, and a desk-phone or conversation login's agent application cannot, so the omission is a compile error rather than a live seat's discovery. See **The station is the agent application's**. |
|
|
2076
|
-
| `store` | The login's operational store, kept by the agent application for the life of the login, across a reload of the agent application, and cleared at sign-out: where an adapter that composes a record keeps what its platform cannot hold for it, such as the interaction legs an agent application reported. Three functions, by key. Never for anything sensitive, which is `AuthenticationContext.secrets`, a store an agent application may clear aggressively. **A task's keys carry its assignment id and go with the task**: a key written about a task names the assignment id in the key, and is deleted before the task's end is published, so that nothing of a closed task survives its ending and nothing is left for whatever comes next -- a record with legs the agent application never reported against it. A login-scoped key carries no assignment id and outlives any task. The harness requires the store of every connection (`store.shape`, `store.get`, `.set`, `.delete`), watches the one it hands over, and names a task's key still held after `task-ended` (`drive.store.retained`) or written about the task after its end -- a persist hung off a timer that saw the task as it was (`drive.store.late`). The ordinary late write is the agent application's, not the adapter's: an agent application reports a leg without waiting for the answer, so an unmute can follow `complete` by a tick, and the adapter answers a report about a task that has ended `failed` with `omni.
|
|
2076
|
+
| `store` | The login's operational store, kept by the agent application for the life of the login, across a reload of the agent application, and cleared at sign-out: where an adapter that composes a record keeps what its platform cannot hold for it, such as the interaction legs an agent application reported. Three functions, by key. Never for anything sensitive, which is `AuthenticationContext.secrets`, a store an agent application may clear aggressively. **A task's keys carry its assignment id and go with the task**: a key written about a task names the assignment id in the key, and is deleted before the task's end is published, so that nothing of a closed task survives its ending and nothing is left for whatever comes next -- a record with legs the agent application never reported against it. A login-scoped key carries no assignment id and outlives any task. The harness requires the store of every connection (`store.shape`, `store.get`, `.set`, `.delete`), watches the one it hands over, and names a task's key still held after `task-ended` (`drive.store.retained`) or written about the task after its end -- a persist hung off a timer that saw the task as it was (`drive.store.late`). The ordinary late write is the agent application's, not the adapter's: an agent application reports a leg without waiting for the answer, so an unmute can follow `complete` by a tick, and the adapter answers a report about a task that has ended `failed` with `omni.assignment-not-found` and writes nothing -- the agent application ends its own open legs at the task's end, so such a report is the agent application's error to see, and an agent application reads every `recordStep` answer and awaits the one for the leg it closes at a task's end, the only moment a refusal is expected, since a report nobody waits for is an error nobody can see; an adapter that names no task in its keys gets no cleanup check, which is a gap rather than a pass, never an exemption: the obligation is that nothing of a closed task survives its ending, and an adapter that keeps every open task in one login-scoped value owes exactly that inside the value, where the harness cannot look. One key per task, named for it, is the shape the harness can hold, and the shape to reach for. The store lists nothing, so an adapter that needs to find its tasks keeps a login-scoped index of ids beside them; at the task's end it deletes the body first and reindexes after, since a crash between the two then leaves an index naming a task with no body, which a reader skips, where the other order leaves a body for a task that has ended, which is the hazard itself. A reader of the index tolerates an id with no body as an ending that was underway, not as corruption. |
|
|
2077
2077
|
| `host` | The agent application's report of the agent's station — devices, permissions, network — to consult before declaring the agent ready to the platform, and on every change. See **The agent application reports, the adapter decides**. |
|
|
2078
2078
|
| `signal` | Optional cancellation signal. Stop startup promptly when aborted and do not begin new work. |
|
|
2079
2079
|
| `log` | Optional structured logging callback. Never include credentials, tokens, or sensitive contact data. |
|
|
@@ -2448,7 +2448,7 @@ ending, a history report, a command -- would land on whichever assignment is ope
|
|
|
2448
2448
|
So the assignment id is unique within the provider and never reused: where the platform's handle
|
|
2449
2449
|
never comes back, the adapter passes it through; where it does, the adapter mints the assignment id
|
|
2450
2450
|
and keeps the mapping, and the desk never learns which. A late event finds the assignment it
|
|
2451
|
-
belongs to or is refused, and a task-scoped browser session (`
|
|
2451
|
+
belongs to or is refused, and a task-scoped browser session (`PROVIDER_NAME__ASSIGNMENT_ID__TAB_NAME`)
|
|
2452
2452
|
lives one assignment, never the next customer's cookies. A dial the agent application placed was
|
|
2453
2453
|
already safe, since its outcome is placed by the agent application's own `dialId`; where the
|
|
2454
2454
|
assignment earns its place is everything the agent application does not mint --
|
|
@@ -2736,7 +2736,8 @@ Migration from the earlier spellings:
|
|
|
2736
2736
|
| handlingStepExpectsAPerson, handlingStepDials, and their interaction spellings | `historyStepExpectsAPerson`, `historyStepDials` |
|
|
2737
2737
|
| handleSeconds | `interactionSeconds` |
|
|
2738
2738
|
| Task.allocationId, AllocationId, allocationExpiresAt | `Task.assignmentId`, `AssignmentId`, `assignmentExpiresAt` |
|
|
2739
|
-
| Task.id, TaskId, and `taskId` on every event, command, report and lead request | gone: a task is named by `assignmentId` alone, and `
|
|
2739
|
+
| 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 |
|
|
2740
|
+
| 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`) |
|
|
2740
2741
|
| Manifest.disposalSettleMs | `Manifest.completionSettleMs` |
|
|
2741
2742
|
|
|
2742
2743
|
Update producers, consumers, saved task snapshots, and validation-rule assertions together.
|
|
@@ -2953,7 +2954,7 @@ At a provider-confirmed task/media end, the agent application also stops the ass
|
|
|
2953
2954
|
its observed ending where the report is still accepted. An agent application closing report repeats its original
|
|
2954
2955
|
key and may include its measured duration, but cannot reverse an already confirmed provider end.
|
|
2955
2956
|
The provider acknowledges a known closed leg without rewriting its final record; after the task
|
|
2956
|
-
has been completed, it may refuse the report as
|
|
2957
|
+
has been completed, it may refuse the report as assignment-not-found. The agent application reads that response rather
|
|
2957
2958
|
than retrying or recreating the task. An observed agent application end before a provider closure is still
|
|
2958
2959
|
reported normally; the provider decides the final timestamp and publishes the record.
|
|
2959
2960
|
|
|
@@ -3049,7 +3050,7 @@ The supported `BrowserIsolationScheme` values, declared under **Shapes**, key as
|
|
|
3049
3050
|
|
|
3050
3051
|
| Enum member | Example session key |
|
|
3051
3052
|
| --- | --- |
|
|
3052
|
-
| `
|
|
3053
|
+
| `PROVIDER_NAME__ASSIGNMENT_ID__TAB_NAME` | `mailflow.EMAIL-829102%2Ea1.CRM` -- the task's assignment id, so one assignment, never the next customer's cookies |
|
|
3053
3054
|
| `TAB_NAME` | `CRM` |
|
|
3054
3055
|
| `PROVIDER_NAME__TASK_TYPE_NAME__TAB_NAME` | `mailflow.Support.CRM` |
|
|
3055
3056
|
| `PROVIDER_NAME__TAB_NAME` | `mailflow.CRM` |
|
|
@@ -4812,7 +4813,7 @@ react rather than only display the message:
|
|
|
4812
4813
|
| --- | --- |
|
|
4813
4814
|
| `omni.not-authenticated` | The provider session is no longer usable. The adapter has published `expired` at or before this answer — the state is what Omni surfaces reauthentication from; the code says why this action failed, and is never the only signal. |
|
|
4814
4815
|
| `omni.capability-not-enabled` | The action targets a capability this task, manifest, or login did not declare — including a lead command from a login whose `capabilities` no longer carry it. |
|
|
4815
|
-
| `omni.
|
|
4816
|
+
| `omni.assignment-not-found` | The assignment named is not one the provider holds, typically after the task already ended. |
|
|
4816
4817
|
| `omni.phone-not-permitted` | The agent application declared a `phone` the platform does not permit for this agent -- a softphone for an agent configured for a desk phone, or the reverse. The login is refused at authentication, and the provider never reconfigures the agent to make the declaration true. See **How the agent hears the call**. |
|
|
4817
4818
|
| `omni.destination-not-permitted` | The dialled number, or the `destinationId` named, is not one the provider offers this agent. |
|
|
4818
4819
|
| `omni.rate-limited` | The action was throttled. Pair with `retryAfterMs`. |
|
|
@@ -5224,7 +5225,7 @@ declares the `calendar` idle capability.
|
|
|
5224
5225
|
|
|
5225
5226
|
## Utilities
|
|
5226
5227
|
|
|
5227
|
-
### `
|
|
5228
|
+
### `assignmentKey(providerId, assignmentId)`
|
|
5228
5229
|
|
|
5229
5230
|
Returns a collision-safe global task key by encoding and joining the provider id and the
|
|
5230
5231
|
assignment id. Use this key in Omni state; an assignment id is unique within its provider and
|