@oxvo/ai-live-assist 7.3.4 → 7.4.15
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/cjs/AiLiveAssist.d.ts +506 -6
- package/cjs/AiLiveAssist.js +12280 -1267
- package/cjs/client.d.ts +51 -10
- package/cjs/client.js +193 -58
- package/cjs/context.d.ts +15 -5
- package/cjs/context.js +568 -73
- package/cjs/control.d.ts +72 -7
- package/cjs/control.js +598 -71
- package/cjs/dialogFocusScope.d.ts +37 -0
- package/cjs/dialogFocusScope.js +227 -0
- package/cjs/experienceState.js +1 -1
- package/cjs/index.d.ts +1 -1
- package/cjs/index.js +2 -2
- package/cjs/media.d.ts +13 -3
- package/cjs/media.js +125 -19
- package/cjs/messages.js +22 -3
- package/cjs/messengerLoader.d.ts +3 -0
- package/cjs/messengerLoader.js +156 -0
- package/cjs/pageAssistancePreference.d.ts +78 -0
- package/cjs/pageAssistancePreference.js +488 -0
- package/cjs/preview.d.ts +33 -0
- package/cjs/preview.js +567 -0
- package/cjs/tabLock.d.ts +54 -3
- package/cjs/tabLock.js +752 -72
- package/cjs/types.d.ts +101 -3
- package/cjs/ui.d.ts +192 -10
- package/cjs/ui.js +6683 -543
- package/cjs/version.d.ts +1 -1
- package/cjs/version.js +1 -1
- package/cjs/voicePresenceUi.d.ts +42 -9
- package/cjs/voicePresenceUi.js +757 -125
- package/lib/AiLiveAssist.d.ts +506 -6
- package/lib/AiLiveAssist.js +12285 -1275
- package/lib/client.d.ts +51 -10
- package/lib/client.js +190 -57
- package/lib/context.d.ts +15 -5
- package/lib/context.js +566 -72
- package/lib/control.d.ts +72 -7
- package/lib/control.js +598 -71
- package/lib/dialogFocusScope.d.ts +37 -0
- package/lib/dialogFocusScope.js +223 -0
- package/lib/experienceState.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -2
- package/lib/media.d.ts +13 -3
- package/lib/media.js +125 -19
- package/lib/messages.js +22 -3
- package/lib/messengerLoader.d.ts +3 -0
- package/lib/messengerLoader.js +151 -0
- package/lib/pageAssistancePreference.d.ts +78 -0
- package/lib/pageAssistancePreference.js +482 -0
- package/lib/preview.d.ts +33 -0
- package/lib/preview.js +561 -0
- package/lib/tabLock.d.ts +54 -3
- package/lib/tabLock.js +752 -72
- package/lib/types.d.ts +101 -3
- package/lib/ui.d.ts +192 -10
- package/lib/ui.js +6684 -544
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/voicePresenceUi.d.ts +42 -9
- package/lib/voicePresenceUi.js +757 -125
- package/package.json +8 -3
- package/cjs/recordingPolicy.d.ts +0 -2
- package/cjs/recordingPolicy.js +0 -12
- package/lib/recordingPolicy.d.ts +0 -2
- package/lib/recordingPolicy.js +0 -8
package/cjs/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistMode, BootstrapConfig, ConsentScope, LaunchResult, ExperienceMode } from
|
|
1
|
+
import type { AssistMode, BootstrapConfig, ConsentScope, LaunchResult, ExperienceMode } from "./types.js";
|
|
2
2
|
export declare class RuntimeClientError extends Error {
|
|
3
3
|
readonly code: string;
|
|
4
4
|
readonly retryable: boolean;
|
|
@@ -6,17 +6,22 @@ export declare class RuntimeClientError extends Error {
|
|
|
6
6
|
readonly status: number;
|
|
7
7
|
constructor(code: string, message: string, retryable: boolean, retryAfterMs: number | null, status: number);
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
export declare const MAX_ASSIST_SESSION_GRANT_LENGTH = 4096;
|
|
10
|
+
export declare const isValidReplaySessionId: (value: unknown) => value is string;
|
|
11
|
+
export declare const isValidAssistSessionGrant: (value: unknown) => value is string;
|
|
12
|
+
export type ReplayLaunchContext = {
|
|
13
|
+
sessionId: string;
|
|
14
|
+
tabId: string;
|
|
15
|
+
grant: string;
|
|
16
|
+
};
|
|
17
|
+
export type LaunchInput = {
|
|
10
18
|
siteKey: string;
|
|
11
19
|
configRevision: number;
|
|
12
20
|
path: string;
|
|
13
21
|
tabId: string;
|
|
14
22
|
mode: AssistMode;
|
|
15
23
|
anonymousId: string;
|
|
16
|
-
replay
|
|
17
|
-
sessionId: string;
|
|
18
|
-
tabId: string;
|
|
19
|
-
};
|
|
24
|
+
replay: ReplayLaunchContext;
|
|
20
25
|
disclosureChecksum: string;
|
|
21
26
|
scopes: ConsentScope[];
|
|
22
27
|
locale: string;
|
|
@@ -26,6 +31,39 @@ type LaunchInput = {
|
|
|
26
31
|
experienceMode: ExperienceMode;
|
|
27
32
|
fallbackIntent?: "standard_assist" | "human_handoff";
|
|
28
33
|
lowPower: boolean;
|
|
34
|
+
idempotencyKey: string;
|
|
35
|
+
};
|
|
36
|
+
export type ModeUpgradeResumeInput = {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
resumeCredential: string;
|
|
39
|
+
path: string;
|
|
40
|
+
protocolVersion: 1 | 2;
|
|
41
|
+
requestId: string;
|
|
42
|
+
idempotencyKey: string;
|
|
43
|
+
};
|
|
44
|
+
export type ModeUpgradeRecoveryResult = {
|
|
45
|
+
schemaVersion: 1;
|
|
46
|
+
status: "voice_committed";
|
|
47
|
+
resume: LaunchResult;
|
|
48
|
+
} | {
|
|
49
|
+
schemaVersion: 1;
|
|
50
|
+
status: "terminalized";
|
|
51
|
+
sessionId: string;
|
|
52
|
+
state: "ended" | "failed" | "revoked";
|
|
53
|
+
};
|
|
54
|
+
export type RuntimeSessionState = "created" | "authorizing" | "connecting" | "active_text" | "active_voice" | "paused" | "reconnecting" | "handoff_pending" | "human_controlled" | "ending" | "ended" | "failed" | "revoked";
|
|
55
|
+
export type PauseCommandIdentity = {
|
|
56
|
+
operationId: string;
|
|
57
|
+
clientSequence: number;
|
|
58
|
+
};
|
|
59
|
+
export type PauseCommandResult = {
|
|
60
|
+
schemaVersion: 1;
|
|
61
|
+
operationId: string;
|
|
62
|
+
clientSequence: number;
|
|
63
|
+
fencingToken: number;
|
|
64
|
+
action: "pause" | "resume";
|
|
65
|
+
status: "applied" | "replayed" | "superseded";
|
|
66
|
+
state: RuntimeSessionState;
|
|
29
67
|
};
|
|
30
68
|
export declare const idempotencyKey: (prefix: string) => string;
|
|
31
69
|
export declare class RuntimeClient {
|
|
@@ -35,19 +73,22 @@ export declare class RuntimeClient {
|
|
|
35
73
|
bootstrap(siteKey: string, path: string, anonymousId: string, signal: AbortSignal): Promise<BootstrapConfig>;
|
|
36
74
|
launch(input: LaunchInput, signal: AbortSignal): Promise<LaunchResult>;
|
|
37
75
|
resume(sessionId: string, credential: string, path: string, protocolVersion: 1 | 2, signal: AbortSignal): Promise<LaunchResult>;
|
|
38
|
-
|
|
76
|
+
resumeModeUpgrade(input: ModeUpgradeResumeInput, signal: AbortSignal): Promise<LaunchResult>;
|
|
77
|
+
reconcileModeUpgrade(input: ModeUpgradeResumeInput, signal?: AbortSignal, keepalive?: boolean): Promise<ModeUpgradeRecoveryResult>;
|
|
78
|
+
offer(result: LaunchResult, offerSdp: string, signal: AbortSignal, offerIdempotencyKey?: string): Promise<{
|
|
39
79
|
answerSdp: string;
|
|
40
80
|
fencingToken: number;
|
|
81
|
+
sessionState: "active_text" | "active_voice" | null;
|
|
41
82
|
}>;
|
|
42
83
|
end(result: LaunchResult, fencingToken: number, signal?: AbortSignal): Promise<void>;
|
|
43
|
-
|
|
84
|
+
recoverAndEnd(sessionId: string, resumeCredential: string, protocolVersion: 1 | 2, signal?: AbortSignal): Promise<void>;
|
|
85
|
+
pauseOrResume(result: LaunchResult, fencingToken: number, operation: "pause" | "resume", identity: PauseCommandIdentity, signal?: AbortSignal): Promise<PauseCommandResult>;
|
|
44
86
|
feedback(result: LaunchResult, payload: {
|
|
45
87
|
rating: number | null;
|
|
46
|
-
outcome:
|
|
88
|
+
outcome: "resolved" | "partially_resolved" | "not_resolved" | "handoff";
|
|
47
89
|
}, signal?: AbortSignal): Promise<void>;
|
|
48
90
|
private postEmpty;
|
|
49
91
|
private postJson;
|
|
50
92
|
private parseJson;
|
|
51
93
|
private throwResponse;
|
|
52
94
|
}
|
|
53
|
-
export {};
|
package/cjs/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RuntimeClient = exports.idempotencyKey = exports.RuntimeClientError = void 0;
|
|
3
|
+
exports.RuntimeClient = exports.idempotencyKey = exports.isValidAssistSessionGrant = exports.isValidReplaySessionId = exports.MAX_ASSIST_SESSION_GRANT_LENGTH = exports.RuntimeClientError = void 0;
|
|
4
4
|
class RuntimeClientError extends Error {
|
|
5
5
|
constructor(code, message, retryable, retryAfterMs, status) {
|
|
6
6
|
super(message);
|
|
@@ -8,10 +8,40 @@ class RuntimeClientError extends Error {
|
|
|
8
8
|
this.retryable = retryable;
|
|
9
9
|
this.retryAfterMs = retryAfterMs;
|
|
10
10
|
this.status = status;
|
|
11
|
-
this.name =
|
|
11
|
+
this.name = "RuntimeClientError";
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
exports.RuntimeClientError = RuntimeClientError;
|
|
15
|
+
const MAX_SIGNED_INT64 = "9223372036854775807";
|
|
16
|
+
const CONTROL_OPERATION_ID_PATTERN = /^[A-Za-z0-9_.:-]{8,128}$/;
|
|
17
|
+
exports.MAX_ASSIST_SESSION_GRANT_LENGTH = 4096;
|
|
18
|
+
const isValidReplaySessionId = (value) => {
|
|
19
|
+
if (typeof value !== "string" || !/^[1-9]\d{0,18}$/.test(value)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return value.length < MAX_SIGNED_INT64.length || value <= MAX_SIGNED_INT64;
|
|
23
|
+
};
|
|
24
|
+
exports.isValidReplaySessionId = isValidReplaySessionId;
|
|
25
|
+
const isValidAssistSessionGrant = (value) => typeof value === "string" &&
|
|
26
|
+
value.length > 0 &&
|
|
27
|
+
value.length <= exports.MAX_ASSIST_SESSION_GRANT_LENGTH &&
|
|
28
|
+
value.trim() === value;
|
|
29
|
+
exports.isValidAssistSessionGrant = isValidAssistSessionGrant;
|
|
30
|
+
const RUNTIME_SESSION_STATES = new Set([
|
|
31
|
+
"created",
|
|
32
|
+
"authorizing",
|
|
33
|
+
"connecting",
|
|
34
|
+
"active_text",
|
|
35
|
+
"active_voice",
|
|
36
|
+
"paused",
|
|
37
|
+
"reconnecting",
|
|
38
|
+
"handoff_pending",
|
|
39
|
+
"human_controlled",
|
|
40
|
+
"ending",
|
|
41
|
+
"ended",
|
|
42
|
+
"failed",
|
|
43
|
+
"revoked",
|
|
44
|
+
]);
|
|
15
45
|
const requestId = () => `req_${crypto.randomUUID()}`;
|
|
16
46
|
const idempotencyKey = (prefix) => `${prefix}_${crypto.randomUUID()}`;
|
|
17
47
|
exports.idempotencyKey = idempotencyKey;
|
|
@@ -19,31 +49,53 @@ class RuntimeClient {
|
|
|
19
49
|
constructor(runtimeUrl, clientVersion) {
|
|
20
50
|
this.clientVersion = clientVersion;
|
|
21
51
|
const url = new URL(runtimeUrl, window.location.href);
|
|
22
|
-
if (![
|
|
23
|
-
throw new Error(
|
|
52
|
+
if (!["http:", "https:"].includes(url.protocol)) {
|
|
53
|
+
throw new Error("AI Guide runtime URL must use HTTP or HTTPS.");
|
|
24
54
|
}
|
|
25
|
-
this.baseUrl = url.origin + url.pathname.replace(/\/$/,
|
|
55
|
+
this.baseUrl = url.origin + url.pathname.replace(/\/$/, "");
|
|
26
56
|
}
|
|
27
57
|
async bootstrap(siteKey, path, anonymousId, signal) {
|
|
28
58
|
const url = new URL(`${this.baseUrl}/ai-live-assist/v1/public/bootstrap`);
|
|
29
|
-
url.searchParams.set(
|
|
30
|
-
url.searchParams.set(
|
|
59
|
+
url.searchParams.set("siteKey", siteKey);
|
|
60
|
+
url.searchParams.set("path", path);
|
|
31
61
|
const response = await fetch(url, {
|
|
32
|
-
method:
|
|
33
|
-
mode:
|
|
34
|
-
credentials:
|
|
35
|
-
cache:
|
|
62
|
+
method: "GET",
|
|
63
|
+
mode: "cors",
|
|
64
|
+
credentials: "omit",
|
|
65
|
+
cache: "no-store",
|
|
36
66
|
headers: {
|
|
37
|
-
Accept:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
Accept: "application/json",
|
|
68
|
+
"X-Oxvo-Ai-Client-Version": this.clientVersion,
|
|
69
|
+
"X-Oxvo-Ai-Visitor-Id": anonymousId,
|
|
70
|
+
"X-Request-Id": requestId(),
|
|
41
71
|
},
|
|
42
72
|
signal,
|
|
43
73
|
});
|
|
44
74
|
return this.parseJson(response);
|
|
45
75
|
}
|
|
46
76
|
async launch(input, signal) {
|
|
77
|
+
if (!/^[A-Za-z0-9_.:-]{8,128}$/.test(input.idempotencyKey)) {
|
|
78
|
+
throw new Error("Invalid AI Guide launch idempotency key.");
|
|
79
|
+
}
|
|
80
|
+
const replaySessionId = input.replay?.sessionId;
|
|
81
|
+
const replayTabId = input.replay?.tabId;
|
|
82
|
+
const replayGrant = input.replay?.grant;
|
|
83
|
+
if (!(0, exports.isValidReplaySessionId)(replaySessionId)) {
|
|
84
|
+
throw new Error("Invalid AI Guide replay session ID.");
|
|
85
|
+
}
|
|
86
|
+
if (typeof replayTabId !== "string" ||
|
|
87
|
+
replayTabId !== input.tabId ||
|
|
88
|
+
!/^[A-Za-z0-9_.:-]{1,128}$/.test(replayTabId)) {
|
|
89
|
+
throw new Error("Invalid AI Guide replay tab ID.");
|
|
90
|
+
}
|
|
91
|
+
if (!(0, exports.isValidAssistSessionGrant)(replayGrant)) {
|
|
92
|
+
throw new Error("Invalid AI Guide replay grant.");
|
|
93
|
+
}
|
|
94
|
+
const replay = {
|
|
95
|
+
sessionId: replaySessionId,
|
|
96
|
+
tabId: replayTabId,
|
|
97
|
+
grant: replayGrant,
|
|
98
|
+
};
|
|
47
99
|
const shared = {
|
|
48
100
|
clientVersion: this.clientVersion,
|
|
49
101
|
siteKey: input.siteKey,
|
|
@@ -52,7 +104,7 @@ class RuntimeClient {
|
|
|
52
104
|
tabId: input.tabId,
|
|
53
105
|
mode: input.mode,
|
|
54
106
|
visitor: { anonymousId: input.anonymousId },
|
|
55
|
-
|
|
107
|
+
replay,
|
|
56
108
|
consent: {
|
|
57
109
|
disclosureChecksum: input.disclosureChecksum,
|
|
58
110
|
scopes: input.scopes,
|
|
@@ -70,13 +122,12 @@ class RuntimeClient {
|
|
|
70
122
|
...shared,
|
|
71
123
|
capabilitySummary: {
|
|
72
124
|
microphone: input.microphone,
|
|
73
|
-
webrtc: typeof RTCPeerConnection !==
|
|
74
|
-
audioPlayback: typeof HTMLAudioElement !==
|
|
75
|
-
webAudio: typeof AudioContext !==
|
|
76
|
-
typeof window
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
visualViewport: typeof window.visualViewport !== 'undefined',
|
|
125
|
+
webrtc: typeof RTCPeerConnection !== "undefined",
|
|
126
|
+
audioPlayback: typeof HTMLAudioElement !== "undefined",
|
|
127
|
+
webAudio: typeof AudioContext !== "undefined" ||
|
|
128
|
+
typeof window.webkitAudioContext !== "undefined",
|
|
129
|
+
svgOverlay: typeof SVGElement !== "undefined",
|
|
130
|
+
visualViewport: typeof window.visualViewport !== "undefined",
|
|
80
131
|
reducedMotion: input.reducedMotion,
|
|
81
132
|
lowPower: input.lowPower,
|
|
82
133
|
},
|
|
@@ -87,79 +138,163 @@ class RuntimeClient {
|
|
|
87
138
|
...shared,
|
|
88
139
|
capabilitySummary: {
|
|
89
140
|
microphone: input.microphone,
|
|
90
|
-
webrtc: typeof RTCPeerConnection !==
|
|
141
|
+
webrtc: typeof RTCPeerConnection !== "undefined",
|
|
91
142
|
reducedMotion: input.reducedMotion,
|
|
92
143
|
},
|
|
93
144
|
};
|
|
94
|
-
return this.postJson(
|
|
95
|
-
|
|
145
|
+
return this.postJson("/ai-live-assist/v1/public/launch", body, {
|
|
146
|
+
"Idempotency-Key": input.idempotencyKey,
|
|
96
147
|
}, signal);
|
|
97
148
|
}
|
|
98
149
|
async resume(sessionId, credential, path, protocolVersion, signal) {
|
|
99
|
-
|
|
150
|
+
const request = this.postJson(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(sessionId)}/resume`, {
|
|
100
151
|
schemaVersion: protocolVersion,
|
|
101
152
|
protocolVersion,
|
|
102
153
|
path,
|
|
103
|
-
reason:
|
|
154
|
+
reason: "navigation",
|
|
104
155
|
}, { Authorization: `Bearer ${credential}` }, signal);
|
|
156
|
+
try {
|
|
157
|
+
return await request;
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
if (error instanceof TypeError) {
|
|
161
|
+
throw new RuntimeClientError("RUNTIME_UNAVAILABLE", "AI Guide is temporarily unavailable.", true, null, 0);
|
|
162
|
+
}
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async resumeModeUpgrade(input, signal) {
|
|
167
|
+
if (!/^[A-Za-z0-9_.:-]{8,128}$/.test(input.requestId) ||
|
|
168
|
+
!/^[A-Za-z0-9_.:-]{8,128}$/.test(input.idempotencyKey)) {
|
|
169
|
+
throw new Error("Invalid AI Guide mode-upgrade identity.");
|
|
170
|
+
}
|
|
171
|
+
return this.postJson(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(input.sessionId)}/resume`, {
|
|
172
|
+
schemaVersion: input.protocolVersion,
|
|
173
|
+
protocolVersion: input.protocolVersion,
|
|
174
|
+
path: input.path,
|
|
175
|
+
reason: "mode_upgrade",
|
|
176
|
+
modeUpgradeRequestId: input.requestId,
|
|
177
|
+
}, {
|
|
178
|
+
Authorization: `Bearer ${input.resumeCredential}`,
|
|
179
|
+
"Idempotency-Key": input.idempotencyKey,
|
|
180
|
+
}, signal);
|
|
105
181
|
}
|
|
106
|
-
async
|
|
182
|
+
async reconcileModeUpgrade(input, signal, keepalive = false) {
|
|
183
|
+
if (!/^[A-Za-z0-9_.:-]{8,128}$/.test(input.requestId) ||
|
|
184
|
+
!/^[A-Za-z0-9_.:-]{8,128}$/.test(input.idempotencyKey)) {
|
|
185
|
+
throw new Error("Invalid AI Guide mode-upgrade recovery identity.");
|
|
186
|
+
}
|
|
187
|
+
return this.postJson(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(input.sessionId)}/mode-upgrade/reconcile`, {
|
|
188
|
+
schemaVersion: input.protocolVersion,
|
|
189
|
+
protocolVersion: input.protocolVersion,
|
|
190
|
+
path: input.path,
|
|
191
|
+
modeUpgradeRequestId: input.requestId,
|
|
192
|
+
}, {
|
|
193
|
+
Authorization: `Bearer ${input.resumeCredential}`,
|
|
194
|
+
"Idempotency-Key": input.idempotencyKey,
|
|
195
|
+
}, signal, keepalive);
|
|
196
|
+
}
|
|
197
|
+
async offer(result, offerSdp, signal, offerIdempotencyKey = (0, exports.idempotencyKey)("offer")) {
|
|
198
|
+
if (!/^[A-Za-z0-9_.:-]{8,128}$/.test(offerIdempotencyKey)) {
|
|
199
|
+
throw new Error("Invalid AI Guide realtime-offer idempotency key.");
|
|
200
|
+
}
|
|
107
201
|
const response = await fetch(result.realtimeOfferUrl, {
|
|
108
|
-
method:
|
|
109
|
-
mode:
|
|
110
|
-
credentials:
|
|
111
|
-
cache:
|
|
202
|
+
method: "POST",
|
|
203
|
+
mode: "cors",
|
|
204
|
+
credentials: "omit",
|
|
205
|
+
cache: "no-store",
|
|
112
206
|
headers: {
|
|
113
|
-
Accept:
|
|
207
|
+
Accept: "application/sdp",
|
|
114
208
|
Authorization: `Bearer ${result.launchCredential}`,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
209
|
+
"Content-Type": "application/sdp",
|
|
210
|
+
"Idempotency-Key": offerIdempotencyKey,
|
|
211
|
+
"X-Oxvo-Ai-Client-Version": this.clientVersion,
|
|
212
|
+
"X-Request-Id": requestId(),
|
|
119
213
|
},
|
|
120
214
|
body: offerSdp,
|
|
121
215
|
signal,
|
|
122
216
|
});
|
|
123
217
|
if (!response.ok)
|
|
124
218
|
await this.throwResponse(response);
|
|
125
|
-
const fencingToken = Number(response.headers.get(
|
|
219
|
+
const fencingToken = Number(response.headers.get("X-Oxvo-Fencing-Token"));
|
|
126
220
|
if (!Number.isSafeInteger(fencingToken) || fencingToken < 1) {
|
|
127
|
-
throw new RuntimeClientError(
|
|
221
|
+
throw new RuntimeClientError("PROTOCOL_INVALID", "The runtime returned an invalid control lease.", false, null, 502);
|
|
128
222
|
}
|
|
129
|
-
|
|
223
|
+
const sessionStateHeader = response.headers.get("X-Oxvo-Session-State");
|
|
224
|
+
const sessionState = sessionStateHeader === "active_text" ||
|
|
225
|
+
sessionStateHeader === "active_voice"
|
|
226
|
+
? sessionStateHeader
|
|
227
|
+
: null;
|
|
228
|
+
return {
|
|
229
|
+
answerSdp: await response.text(),
|
|
230
|
+
fencingToken,
|
|
231
|
+
sessionState,
|
|
232
|
+
};
|
|
130
233
|
}
|
|
131
234
|
async end(result, fencingToken, signal) {
|
|
132
|
-
await this.postEmpty(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(result.sessionId)}/end`, result.controlCredential, fencingToken, result.protocolVersion, signal);
|
|
235
|
+
await this.postEmpty(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(result.sessionId)}/end`, result.controlCredential, fencingToken, result.protocolVersion, signal, true);
|
|
133
236
|
}
|
|
134
|
-
async
|
|
135
|
-
await this.
|
|
237
|
+
async recoverAndEnd(sessionId, resumeCredential, protocolVersion, signal) {
|
|
238
|
+
await this.postJson(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(sessionId)}/end`, {
|
|
239
|
+
schemaVersion: protocolVersion,
|
|
240
|
+
protocolVersion,
|
|
241
|
+
fencingToken: 0,
|
|
242
|
+
terminalRecovery: true,
|
|
243
|
+
}, { Authorization: `Bearer ${resumeCredential}` }, signal, true);
|
|
244
|
+
}
|
|
245
|
+
async pauseOrResume(result, fencingToken, operation, identity, signal) {
|
|
246
|
+
if (!CONTROL_OPERATION_ID_PATTERN.test(identity.operationId) ||
|
|
247
|
+
!Number.isSafeInteger(identity.clientSequence) ||
|
|
248
|
+
identity.clientSequence < 1) {
|
|
249
|
+
throw new Error("Invalid AI Guide pause command identity.");
|
|
250
|
+
}
|
|
251
|
+
const response = await this.postJson(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(result.sessionId)}/actions/${operation}`, {
|
|
252
|
+
schemaVersion: result.protocolVersion,
|
|
253
|
+
protocolVersion: result.protocolVersion,
|
|
254
|
+
fencingToken,
|
|
255
|
+
operationId: identity.operationId,
|
|
256
|
+
clientSequence: identity.clientSequence,
|
|
257
|
+
}, { Authorization: `Bearer ${result.controlCredential}` }, signal);
|
|
258
|
+
if (!response ||
|
|
259
|
+
typeof response !== "object" ||
|
|
260
|
+
response.schemaVersion !== 1 ||
|
|
261
|
+
response.operationId !== identity.operationId ||
|
|
262
|
+
response.clientSequence !== identity.clientSequence ||
|
|
263
|
+
response.fencingToken !== fencingToken ||
|
|
264
|
+
response.action !== operation ||
|
|
265
|
+
!["applied", "replayed", "superseded"].includes(response.status) ||
|
|
266
|
+
!RUNTIME_SESSION_STATES.has(response.state)) {
|
|
267
|
+
throw new RuntimeClientError("PROTOCOL_INVALID", "The runtime returned an invalid pause command result.", false, null, 502);
|
|
268
|
+
}
|
|
269
|
+
return response;
|
|
136
270
|
}
|
|
137
271
|
async feedback(result, payload, signal) {
|
|
138
272
|
await this.postJson(`/ai-live-assist/v1/public/sessions/${encodeURIComponent(result.sessionId)}/feedback`, payload, {
|
|
139
273
|
Authorization: `Bearer ${result.feedbackCredential}`,
|
|
140
274
|
}, signal);
|
|
141
275
|
}
|
|
142
|
-
async postEmpty(path, credential, fencingToken, protocolVersion, signal) {
|
|
276
|
+
async postEmpty(path, credential, fencingToken, protocolVersion, signal, keepalive = false) {
|
|
143
277
|
await this.postJson(path, {
|
|
144
278
|
schemaVersion: protocolVersion,
|
|
145
279
|
protocolVersion,
|
|
146
280
|
fencingToken,
|
|
147
|
-
}, { Authorization: `Bearer ${credential}` }, signal);
|
|
281
|
+
}, { Authorization: `Bearer ${credential}` }, signal, keepalive);
|
|
148
282
|
}
|
|
149
|
-
async postJson(path, body, extraHeaders, signal) {
|
|
283
|
+
async postJson(path, body, extraHeaders, signal, keepalive = false) {
|
|
150
284
|
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
151
|
-
method:
|
|
152
|
-
mode:
|
|
153
|
-
credentials:
|
|
154
|
-
cache:
|
|
285
|
+
method: "POST",
|
|
286
|
+
mode: "cors",
|
|
287
|
+
credentials: "omit",
|
|
288
|
+
cache: "no-store",
|
|
155
289
|
headers: {
|
|
156
|
-
Accept:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
290
|
+
Accept: "application/json",
|
|
291
|
+
"Content-Type": "application/json",
|
|
292
|
+
"X-Oxvo-Ai-Client-Version": this.clientVersion,
|
|
293
|
+
"X-Request-Id": requestId(),
|
|
160
294
|
...extraHeaders,
|
|
161
295
|
},
|
|
162
296
|
body: JSON.stringify(body),
|
|
297
|
+
...(keepalive ? { keepalive: true } : {}),
|
|
163
298
|
...(signal ? { signal } : {}),
|
|
164
299
|
});
|
|
165
300
|
if (response.status === 204)
|
|
@@ -173,7 +308,7 @@ class RuntimeClient {
|
|
|
173
308
|
return (await response.json());
|
|
174
309
|
}
|
|
175
310
|
catch {
|
|
176
|
-
throw new RuntimeClientError(
|
|
311
|
+
throw new RuntimeClientError("PROTOCOL_INVALID", "The runtime returned an invalid response.", false, null, response.status);
|
|
177
312
|
}
|
|
178
313
|
}
|
|
179
314
|
async throwResponse(response) {
|
|
@@ -185,9 +320,9 @@ class RuntimeClient {
|
|
|
185
320
|
catch {
|
|
186
321
|
// Keep the public fallback generic; response bodies may come from a proxy.
|
|
187
322
|
}
|
|
188
|
-
throw new RuntimeClientError(typeof error.code ===
|
|
323
|
+
throw new RuntimeClientError(typeof error.code === "string" ? error.code : "RUNTIME_UNAVAILABLE", typeof error.message === "string"
|
|
189
324
|
? error.message
|
|
190
|
-
:
|
|
325
|
+
: "AI Guide is temporarily unavailable.", error.retryable === true, typeof error.retryAfterMs === "number" ? error.retryAfterMs : null, response.status);
|
|
191
326
|
}
|
|
192
327
|
}
|
|
193
328
|
exports.RuntimeClient = RuntimeClient;
|
package/cjs/context.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
import type { ActionExecutionResult, ActionGrant, PageContextSnapshot, PresentationTarget, VisualContextCapture } from './types.js';
|
|
1
|
+
import type { ActionExecutionResult, ActionGrant, PageContextSnapshot, SanitizedTarget, PresentationTarget, VisualContextCapture } from './types.js';
|
|
2
|
+
export declare const actionTargetFingerprint: (target: SanitizedTarget, subtle?: Pick<SubtleCrypto, "digest"> | undefined) => Promise<string | null>;
|
|
2
3
|
type SelectorRegionRule = {
|
|
3
4
|
digest: string;
|
|
4
5
|
selector: string;
|
|
5
6
|
};
|
|
6
7
|
type ContextCallbacks = {
|
|
7
8
|
onSnapshot: (snapshot: PageContextSnapshot) => void | Promise<void>;
|
|
8
|
-
|
|
9
|
+
onActionMutationApplied?: (actionId: string, revision: number, url: string) => void;
|
|
10
|
+
onCausalNavigationObserved?: (pageId: string, revision: number, url: string, causalActionId: string) => void;
|
|
11
|
+
onNavigation: (pageId: string, revision: number, url: string, causalActionId?: string) => boolean | void | Promise<boolean | void>;
|
|
9
12
|
onHighlight: (element: Element | null, status: 'focus' | 'success' | 'failure') => void;
|
|
10
13
|
};
|
|
11
14
|
export declare class PageContextCollector {
|
|
12
15
|
private readonly callbacks;
|
|
13
16
|
private readonly selectorRegionRules;
|
|
14
17
|
private readonly policyVersion;
|
|
18
|
+
private readonly trustedGuideRoot;
|
|
19
|
+
private readonly statefulButtonActionProof;
|
|
15
20
|
private pageId;
|
|
16
21
|
private revision;
|
|
17
22
|
private running;
|
|
@@ -28,19 +33,21 @@ export declare class PageContextCollector {
|
|
|
28
33
|
private pendingNavigation;
|
|
29
34
|
private pendingCapture;
|
|
30
35
|
private deferredNavigation;
|
|
36
|
+
private navigationSettlementGeneration;
|
|
37
|
+
private navigationSettlement;
|
|
31
38
|
private recentSelection;
|
|
32
39
|
private latestSemanticSignature;
|
|
33
|
-
constructor(callbacks: ContextCallbacks, selectorRegionRules?: SelectorRegionRule[], policyVersion?: number);
|
|
40
|
+
constructor(callbacks: ContextCallbacks, selectorRegionRules?: SelectorRegionRule[], policyVersion?: number, trustedGuideRoot?: Element | null, statefulButtonActionProof?: boolean);
|
|
34
41
|
get currentRevision(): number;
|
|
35
42
|
get currentPageId(): string;
|
|
36
43
|
presentationTarget(targetId: string, revision: number): PresentationTarget | null;
|
|
37
44
|
start(): void;
|
|
38
45
|
stop(): void;
|
|
39
|
-
captureAndPublish(): Promise<PageContextSnapshot>;
|
|
46
|
+
captureAndPublish(): Promise<PageContextSnapshot | null>;
|
|
40
47
|
capture(): PageContextSnapshot;
|
|
41
48
|
private captureSnapshot;
|
|
42
49
|
captureVisualContext(): VisualContextCapture | null;
|
|
43
|
-
execute(grant: ActionGrant): Promise<ActionExecutionResult>;
|
|
50
|
+
execute(grant: ActionGrant, authorityIsCurrent?: () => boolean): Promise<ActionExecutionResult>;
|
|
44
51
|
completeActionExecution(actionId: string): Promise<string | null>;
|
|
45
52
|
private click;
|
|
46
53
|
private type;
|
|
@@ -59,6 +66,9 @@ export declare class PageContextCollector {
|
|
|
59
66
|
private handleControlStateChange;
|
|
60
67
|
private readSafeSelection;
|
|
61
68
|
private handleNavigation;
|
|
69
|
+
private beginNavigationSettlement;
|
|
70
|
+
private observeCausalNavigationAfterMutation;
|
|
71
|
+
private reportCausalNavigationObservation;
|
|
62
72
|
private installNavigationHooks;
|
|
63
73
|
private restoreNavigationHooks;
|
|
64
74
|
private reducedMotion;
|