@xema/omni-protocol 0.1.18 → 0.1.19
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 +137 -3
- package/package.json +1 -1
package/guide.md
CHANGED
|
@@ -279,6 +279,50 @@ type ConnectContext = {
|
|
|
279
279
|
};
|
|
280
280
|
|
|
281
281
|
type ConnectionStatus = "connecting" | "active" | "error";
|
|
282
|
+
|
|
283
|
+
type CredentialField = {
|
|
284
|
+
name: string;
|
|
285
|
+
label: string;
|
|
286
|
+
type: "text" | "password";
|
|
287
|
+
required?: boolean;
|
|
288
|
+
autocomplete?: string;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
type AuthenticationChallenge =
|
|
292
|
+
| { flowId: string; method: "browser-sso"; authorizationUrl: string; browser: "system" | "omni" }
|
|
293
|
+
| { flowId: string; method: "credentials"; fields: CredentialField[] };
|
|
294
|
+
|
|
295
|
+
type StartAuthenticationRequest =
|
|
296
|
+
| { requestId: string; method: "browser-sso"; callbackUrl: string }
|
|
297
|
+
| { requestId: string; method: "credentials" };
|
|
298
|
+
|
|
299
|
+
type StartAuthenticationResult =
|
|
300
|
+
| { status: "interaction-required"; challenge: AuthenticationChallenge }
|
|
301
|
+
| { status: "rejected"; failure: AuthenticationFailure };
|
|
302
|
+
|
|
303
|
+
type CompleteAuthenticationRequest =
|
|
304
|
+
| { flowId: string; method: "browser-sso"; callbackUrl: string }
|
|
305
|
+
| { flowId: string; method: "credentials"; values: Readonly<Record<string, string>> };
|
|
306
|
+
|
|
307
|
+
type CompleteAuthenticationResult =
|
|
308
|
+
| { status: "authenticated"; identity: User; capabilities: SessionCapabilities; expiresAt?: IsoTimestamp }
|
|
309
|
+
| { status: "rejected"; failure: AuthenticationFailure };
|
|
310
|
+
|
|
311
|
+
type AuthenticationActionResult =
|
|
312
|
+
| { status: "accepted" }
|
|
313
|
+
| { status: "failed"; failure: AuthenticationFailure };
|
|
314
|
+
|
|
315
|
+
type Unsubscribe = () => void;
|
|
316
|
+
|
|
317
|
+
type AuthenticationSession = {
|
|
318
|
+
state(): AuthenticationState | Promise<AuthenticationState>;
|
|
319
|
+
subscribe(listener: (state: AuthenticationState) => void): Unsubscribe;
|
|
320
|
+
start(request: StartAuthenticationRequest): Promise<StartAuthenticationResult>;
|
|
321
|
+
complete(request: CompleteAuthenticationRequest): Promise<CompleteAuthenticationResult>;
|
|
322
|
+
cancelAuthentication(flowId: string): Promise<AuthenticationActionResult>;
|
|
323
|
+
signOut(): Promise<AuthenticationActionResult>;
|
|
324
|
+
close(): Promise<void>;
|
|
325
|
+
};
|
|
282
326
|
```
|
|
283
327
|
|
|
284
328
|
### Provider state
|
|
@@ -297,6 +341,10 @@ type Snapshot = {
|
|
|
297
341
|
type AgentCapacity = {
|
|
298
342
|
count: number; // absolute ceiling, at least 1
|
|
299
343
|
};
|
|
344
|
+
|
|
345
|
+
type CapacityResult =
|
|
346
|
+
| { status: "accepted" }
|
|
347
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
300
348
|
```
|
|
301
349
|
|
|
302
350
|
### Idle contributions
|
|
@@ -317,6 +365,14 @@ type ScheduledActivity = {
|
|
|
317
365
|
contact?: Contact;
|
|
318
366
|
attributes?: Attribute[];
|
|
319
367
|
};
|
|
368
|
+
|
|
369
|
+
type DialRequest = {
|
|
370
|
+
destination: string;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
type DialResult =
|
|
374
|
+
| { status: "dialled" }
|
|
375
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
320
376
|
```
|
|
321
377
|
|
|
322
378
|
### Task capabilities
|
|
@@ -393,15 +449,24 @@ const BROWSER_ISOLATION_SCHEMES = {
|
|
|
393
449
|
type BrowserIsolationScheme =
|
|
394
450
|
(typeof BROWSER_ISOLATION_SCHEMES)[keyof typeof BROWSER_ISOLATION_SCHEMES];
|
|
395
451
|
|
|
396
|
-
type
|
|
452
|
+
type TaskBrowserBase = {
|
|
397
453
|
id: string;
|
|
398
454
|
name: string;
|
|
399
455
|
purpose: string;
|
|
400
456
|
url: string;
|
|
401
|
-
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
type TaskBrowser = TaskBrowserBase & (
|
|
402
460
|
| { reuse: false; isolationScheme?: never }
|
|
403
461
|
| { reuse: true; isolationScheme: BrowserIsolationScheme }
|
|
404
462
|
);
|
|
463
|
+
|
|
464
|
+
type BrowserSessionKeyInput = {
|
|
465
|
+
providerId: string;
|
|
466
|
+
taskId: TaskId;
|
|
467
|
+
taskType: string;
|
|
468
|
+
browser: TaskBrowser;
|
|
469
|
+
};
|
|
405
470
|
```
|
|
406
471
|
|
|
407
472
|
That union is what makes a reusing browser with no scheme fail to compile rather than inherit a
|
|
@@ -574,6 +639,10 @@ type TaskCommandRequest<C extends Channel = Channel> = {
|
|
|
574
639
|
taskId: TaskId;
|
|
575
640
|
command: TaskCommand<C>;
|
|
576
641
|
};
|
|
642
|
+
|
|
643
|
+
type TaskCommandResult =
|
|
644
|
+
| { status: "applied" }
|
|
645
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
577
646
|
```
|
|
578
647
|
|
|
579
648
|
### Breaks
|
|
@@ -613,6 +682,22 @@ type BreakState = {
|
|
|
613
682
|
activeReasonId?: string;
|
|
614
683
|
imposed?: ImposedBreak;
|
|
615
684
|
};
|
|
685
|
+
|
|
686
|
+
type BreakRequestResult =
|
|
687
|
+
| { status: "requested" }
|
|
688
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
689
|
+
|
|
690
|
+
type BreakCommitResult =
|
|
691
|
+
| { status: "committed" }
|
|
692
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
693
|
+
|
|
694
|
+
type BreakCancelResult =
|
|
695
|
+
| { status: "cancelled" }
|
|
696
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
697
|
+
|
|
698
|
+
type BreakEndResult =
|
|
699
|
+
| { status: "ended" }
|
|
700
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
616
701
|
```
|
|
617
702
|
|
|
618
703
|
### Team
|
|
@@ -649,6 +734,18 @@ type TeamBreakCommand =
|
|
|
649
734
|
| { type: "policy"; policy: "ask" | "auto-approve" | "suspended" }
|
|
650
735
|
| { type: "place"; memberId: UserId; reason?: string }
|
|
651
736
|
| { type: "release"; memberId: UserId };
|
|
737
|
+
|
|
738
|
+
type TeamBreakCommandRequest = {
|
|
739
|
+
command: TeamBreakCommand;
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
type TeamConsultCommandRequest = {
|
|
743
|
+
command: TeamConsultCommand;
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
type TeamCommandResult =
|
|
747
|
+
| { status: "applied" }
|
|
748
|
+
| { status: "failed"; failure: ProtocolFailure };
|
|
652
749
|
```
|
|
653
750
|
|
|
654
751
|
### Media
|
|
@@ -663,6 +760,11 @@ type VoiceMediaSession = {
|
|
|
663
760
|
type OpenMediaResult =
|
|
664
761
|
| { status: "opened"; session: VoiceMediaSession }
|
|
665
762
|
| { status: "unavailable"; failure: ProtocolFailure };
|
|
763
|
+
|
|
764
|
+
type OpenMediaRequest = {
|
|
765
|
+
taskId: TaskId;
|
|
766
|
+
localAudio?: MediaStream;
|
|
767
|
+
};
|
|
666
768
|
```
|
|
667
769
|
|
|
668
770
|
### Events
|
|
@@ -710,12 +812,43 @@ type ProviderEventEnvelope = {
|
|
|
710
812
|
reason rather than a naming one: `Event` is a DOM global, and a bare one would shadow it for every
|
|
711
813
|
adapter compiled against the browser lib.
|
|
712
814
|
|
|
815
|
+
### Adapter and connection
|
|
816
|
+
|
|
817
|
+
```ts
|
|
818
|
+
type Connection<C extends Channel = Channel> = {
|
|
819
|
+
snapshot(): Snapshot<C> | Promise<Snapshot<C>>;
|
|
820
|
+
subscribe(listener: (envelope: ProviderEventEnvelope<C>) => void): Unsubscribe;
|
|
821
|
+
setCapacity(capacity: AgentCapacity): Promise<CapacityResult>;
|
|
822
|
+
execute(request: TaskCommandRequest<C>): Promise<TaskCommandResult>;
|
|
823
|
+
disconnect(): Promise<void>;
|
|
824
|
+
|
|
825
|
+
describeUsers?(ids: UserId[]): Promise<User[]>;
|
|
826
|
+
dial?(request: DialRequest): Promise<DialResult>;
|
|
827
|
+
|
|
828
|
+
requestBreak?(request: BreakRequest): Promise<BreakRequestResult>;
|
|
829
|
+
commitBreak?(): Promise<BreakCommitResult>;
|
|
830
|
+
cancelBreak?(): Promise<BreakCancelResult>;
|
|
831
|
+
endBreak?(): Promise<BreakEndResult>;
|
|
832
|
+
|
|
833
|
+
executeTeamBreak?(request: TeamBreakCommandRequest): Promise<TeamCommandResult>;
|
|
834
|
+
executeTeamConsult?(request: TeamConsultCommandRequest): Promise<TeamCommandResult>;
|
|
835
|
+
openMedia?(request: OpenMediaRequest): Promise<OpenMediaResult>;
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
type Adapter<C extends Channel = Channel> = {
|
|
839
|
+
manifest: Manifest<C>;
|
|
840
|
+
createAuthenticationSession(context: AuthenticationContext): Promise<AuthenticationSession> | AuthenticationSession;
|
|
841
|
+
connect(context: ConnectContext): Promise<Connection<C>>;
|
|
842
|
+
};
|
|
843
|
+
```
|
|
844
|
+
|
|
713
845
|
### Published constants
|
|
714
846
|
|
|
715
847
|
```ts
|
|
716
848
|
const ALLOWED_BROWSER_URL_SCHEMES = ["http:", "https:"] as const;
|
|
717
849
|
|
|
718
850
|
const IDLE_CAPABILITIES = ["dial", "personalBrowser", "calendar", "contacts"] as const;
|
|
851
|
+
type IdleCapability = (typeof IDLE_CAPABILITIES)[number];
|
|
719
852
|
|
|
720
853
|
const IDLE_CAPABILITY_UI = {
|
|
721
854
|
dial: "Dialpad",
|
|
@@ -758,6 +891,7 @@ const OMNI_FAILURE_CODES = [
|
|
|
758
891
|
"omni.unavailable",
|
|
759
892
|
"omni.break-already-committed",
|
|
760
893
|
] as const;
|
|
894
|
+
type OmniFailureCode = (typeof OMNI_FAILURE_CODES)[number];
|
|
761
895
|
```
|
|
762
896
|
|
|
763
897
|
`HANDLING_STEPS_WITH_A_PERSON` is every `HandlingStep` except `queued`, which is the one nobody
|
|
@@ -2863,7 +2997,7 @@ what failed, and reports. It never decides for the adapter what a missing microp
|
|
|
2863
2997
|
| --- | --- |
|
|
2864
2998
|
| `online` | Whether the host 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 host does — so `false` is a reason not to go ready and `true` is not a reason to. |
|
|
2865
2999
|
| `audio` | Present on a voice connection, absent where there is no audio. |
|
|
2866
|
-
| `audio.input` | `ready` with `localAudio` — the microphone as captured, the same stream `openMedia` receives — and `flowing`, false while the hardware or OS says no audio moves through it (a headset's own mute switch, which Omni's Mute control never touches). `unavailable` with `reason`, since each wants a different fix from the agent: `no-device`; `denied`; `not-asked`, which a host 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. `failure` carries the words Omni showed them. |
|
|
3000
|
+
| `audio.input` | `ready` with `localAudio` — the microphone as captured, the same stream `openMedia` receives — and `flowing`, false while the hardware or OS says no audio moves through it (a headset's own mute switch, which Omni's Mute control never touches). `unavailable` with `reason`, since each wants a different fix from the agent: `no-device`; `denied`; `not-asked`, which a host 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. A host 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. |
|
|
2867
3001
|
| `audio.output` | `ready`, 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. |
|
|
2868
3002
|
|
|
2869
3003
|
Omni republishes the report whenever it changes — a permission granted late, a headset unplugged,
|