@opengeni/contracts 0.40.0 → 0.44.1
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/atlassian.d.ts +278 -0
- package/dist/atlassian.js +55 -0
- package/dist/atlassian.js.map +1 -0
- package/dist/chunk-52SGB2QO.js +688 -0
- package/dist/chunk-52SGB2QO.js.map +1 -0
- package/dist/{chunk-7JZMI22I.js → chunk-5UDNP7AO.js} +34 -2
- package/dist/chunk-5UDNP7AO.js.map +1 -0
- package/dist/chunk-BVGIYW7D.js +11 -0
- package/dist/chunk-BVGIYW7D.js.map +1 -0
- package/dist/chunk-CADSX6UN.js +1145 -0
- package/dist/chunk-CADSX6UN.js.map +1 -0
- package/dist/chunk-D2HKF2EF.js +1460 -0
- package/dist/chunk-D2HKF2EF.js.map +1 -0
- package/dist/chunk-FJBG5E4O.js +194 -0
- package/dist/chunk-FJBG5E4O.js.map +1 -0
- package/dist/chunk-H5BVCZKF.js +382 -0
- package/dist/chunk-H5BVCZKF.js.map +1 -0
- package/dist/chunk-LSB7GRLG.js +779 -0
- package/dist/chunk-LSB7GRLG.js.map +1 -0
- package/dist/chunk-MYBZRDTY.js +340 -0
- package/dist/chunk-MYBZRDTY.js.map +1 -0
- package/dist/chunk-RIY7GYQS.js +1204 -0
- package/dist/chunk-RIY7GYQS.js.map +1 -0
- package/dist/chunk-TZ6YEYN4.js +386 -0
- package/dist/chunk-TZ6YEYN4.js.map +1 -0
- package/dist/chunk-VA22KRHS.js +15177 -0
- package/dist/chunk-VA22KRHS.js.map +1 -0
- package/dist/chunk-Y3MLSLB4.js +392 -0
- package/dist/chunk-Y3MLSLB4.js.map +1 -0
- package/dist/chunk-YRKRZ6OF.js +134 -0
- package/dist/chunk-YRKRZ6OF.js.map +1 -0
- package/dist/company-profile.d.ts +629 -0
- package/dist/document-artifact-commands.d.ts +282 -0
- package/dist/document-artifact-commands.js +54 -0
- package/dist/document-artifact-commands.js.map +1 -0
- package/dist/document-artifact-query.d.ts +2 -0
- package/dist/document-artifact-query.js +54 -0
- package/dist/document-artifact-query.js.map +1 -0
- package/dist/editable-artifact-binary.d.ts +56 -0
- package/dist/editable-artifact-causal-frontier.d.ts +12 -0
- package/dist/editable-artifact-causal-frontier.js +18 -0
- package/dist/editable-artifact-causal-frontier.js.map +1 -0
- package/dist/editable-artifact-codec-registry.d.ts +48 -0
- package/dist/editable-artifact-codec-registry.js +20 -0
- package/dist/editable-artifact-codec-registry.js.map +1 -0
- package/dist/editable-artifact-committed-transaction.d.ts +35 -0
- package/dist/editable-artifact-committed-transaction.js +19 -0
- package/dist/editable-artifact-committed-transaction.js.map +1 -0
- package/dist/editable-artifact-live.d.ts +276 -0
- package/dist/editable-artifact-live.js +907 -0
- package/dist/editable-artifact-live.js.map +1 -0
- package/dist/editable-artifact-serialized-commit.d.ts +53 -0
- package/dist/editable-artifact-serialized-commit.js +26 -0
- package/dist/editable-artifact-serialized-commit.js.map +1 -0
- package/dist/editable-artifact-versions.d.ts +6 -0
- package/dist/editable-artifact-versions.js +11 -0
- package/dist/editable-artifact-versions.js.map +1 -0
- package/dist/editable-artifacts.d.ts +46 -0
- package/dist/editable-artifacts.js +236 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/event-preview.d.ts +1 -1
- package/dist/google-drive.d.ts +114 -2
- package/dist/google-drive.js +70 -14
- package/dist/google-drive.js.map +1 -1
- package/dist/image-generation.d.ts +70 -0
- package/dist/index.d.ts +4460 -155
- package/dist/index.js +1127 -30
- package/dist/interaction.d.ts +6895 -0
- package/dist/memory-slack-delivery.d.ts +397 -0
- package/dist/presentation-artifact-commands.d.ts +349 -0
- package/dist/presentation-artifact-commands.js +66 -0
- package/dist/presentation-artifact-commands.js.map +1 -0
- package/dist/presentation-artifact-query.d.ts +2 -0
- package/dist/presentation-artifact-query.js +66 -0
- package/dist/presentation-artifact-query.js.map +1 -0
- package/dist/retained-output.d.ts +24 -0
- package/dist/sandbox-snapshots.d.ts +9 -0
- package/dist/scoped-knowledge.d.ts +14 -13
- package/dist/slack-bot-scopes.d.ts +4 -0
- package/dist/slack-bot-scopes.js +3 -1
- package/dist/spreadsheet-artifact-commands.d.ts +101 -0
- package/dist/spreadsheet-artifact-commands.js +27 -0
- package/dist/spreadsheet-artifact-commands.js.map +1 -0
- package/dist/spreadsheet-artifact-date.d.ts +4 -0
- package/dist/spreadsheet-artifact-query.d.ts +120 -0
- package/dist/spreadsheet-artifact-query.js +48 -0
- package/dist/spreadsheet-artifact-query.js.map +1 -0
- package/dist/tool-catalog.d.ts +576 -0
- package/dist/video-generation.d.ts +532 -0
- package/dist/video-generation.js +51 -0
- package/dist/video-generation.js.map +1 -0
- package/dist/workspace-learning-policy.d.ts +208 -0
- package/package.json +61 -1
- package/src/atlassian.ts +160 -0
- package/src/company-profile.ts +306 -0
- package/src/document-artifact-commands.ts +1709 -0
- package/src/document-artifact-query.ts +2 -0
- package/src/editable-artifact-binary.ts +396 -0
- package/src/editable-artifact-causal-frontier.ts +154 -0
- package/src/editable-artifact-codec-registry.ts +261 -0
- package/src/editable-artifact-committed-transaction.ts +478 -0
- package/src/editable-artifact-live.ts +1379 -0
- package/src/editable-artifact-serialized-commit.ts +494 -0
- package/src/editable-artifact-versions.ts +8 -0
- package/src/editable-artifacts.ts +473 -0
- package/src/google-drive.ts +68 -15
- package/src/image-generation.ts +106 -0
- package/src/index.ts +2326 -325
- package/src/interaction.ts +2292 -0
- package/src/memory-slack-delivery.ts +183 -0
- package/src/presentation-artifact-commands.ts +2035 -0
- package/src/presentation-artifact-query.ts +2 -0
- package/src/retained-output.ts +149 -2
- package/src/sandbox-snapshots.ts +28 -1
- package/src/scoped-knowledge.ts +18 -4
- package/src/slack-bot-scopes.ts +34 -1
- package/src/spreadsheet-artifact-commands.ts +999 -0
- package/src/spreadsheet-artifact-date.ts +21 -0
- package/src/spreadsheet-artifact-query.ts +1411 -0
- package/src/tool-catalog.ts +334 -0
- package/src/video-generation.ts +394 -0
- package/src/workspace-learning-policy.ts +233 -0
- package/dist/chunk-5HR2GPSC.js +0 -11031
- package/dist/chunk-5HR2GPSC.js.map +0 -1
- package/dist/chunk-7JZMI22I.js.map +0 -1
|
@@ -0,0 +1,2292 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { RetainedArtifactReferenceSchema } from "./retained-output";
|
|
3
|
+
|
|
4
|
+
export const INTERACTION_PROTOCOL_VERSION = 1 as const;
|
|
5
|
+
export const BROWSER_CONTROL_PROTOCOL_VERSION = INTERACTION_PROTOCOL_VERSION;
|
|
6
|
+
export const BROWSER_CONTROL_WEBSOCKET_PROTOCOL = "opengeni.browser.v1" as const;
|
|
7
|
+
export const COMPUTER_CONTROL_WEBSOCKET_PROTOCOL = "opengeni.computer.v1" as const;
|
|
8
|
+
export const BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX = "opengeni.auth." as const;
|
|
9
|
+
export const BROWSER_CONTROL_MAX_JSON_BYTES = 40 * 1024 * 1024;
|
|
10
|
+
export const BROWSER_CONTROL_MAX_FRAME_HEADER_BYTES = 64 * 1024;
|
|
11
|
+
export const BROWSER_PROFILE_ARTIFACT_FORMAT =
|
|
12
|
+
"opengeni.chromium-profile.v1+gzip+aes-256-gcm" as const;
|
|
13
|
+
export const BROWSER_STATE_ARTIFACT_CONTENT_TYPE =
|
|
14
|
+
"application/vnd.opengeni.browser-profile+octet-stream" as const;
|
|
15
|
+
export const INTERACTION_MAX_SEMANTIC_NODES = 10_000;
|
|
16
|
+
export const INTERACTION_MAX_CHANGED_NODES = 2_000;
|
|
17
|
+
export const INTERACTION_MAX_DIAGNOSTIC_ENTRIES = 1_000;
|
|
18
|
+
export const INTERACTION_MAX_ACTIONS_PER_BATCH = 32;
|
|
19
|
+
|
|
20
|
+
const opaqueGeneration = z
|
|
21
|
+
.string()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(256)
|
|
24
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u);
|
|
25
|
+
const boundedOpaqueId = z
|
|
26
|
+
.string()
|
|
27
|
+
.min(1)
|
|
28
|
+
.max(512)
|
|
29
|
+
.regex(/^[^\u0000-\u001f\u007f]+$/u);
|
|
30
|
+
const boundedUrl = z
|
|
31
|
+
.string()
|
|
32
|
+
.min(1)
|
|
33
|
+
.max(16_384)
|
|
34
|
+
.refine(
|
|
35
|
+
(value) => {
|
|
36
|
+
try {
|
|
37
|
+
return new URL(value).href.length > 0;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{ message: "URL must be absolute" },
|
|
43
|
+
);
|
|
44
|
+
const boundedHttpUrl = boundedUrl.refine(
|
|
45
|
+
(value) => {
|
|
46
|
+
const url = new URL(value);
|
|
47
|
+
return (
|
|
48
|
+
(url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password
|
|
49
|
+
);
|
|
50
|
+
},
|
|
51
|
+
{ message: "URL must use HTTP(S) and cannot contain credentials" },
|
|
52
|
+
);
|
|
53
|
+
const canonicalWebOrigin = z
|
|
54
|
+
.string()
|
|
55
|
+
.min(1)
|
|
56
|
+
.max(2_048)
|
|
57
|
+
.refine(
|
|
58
|
+
(value) => {
|
|
59
|
+
try {
|
|
60
|
+
const url = new URL(value);
|
|
61
|
+
return (
|
|
62
|
+
(url.protocol === "http:" || url.protocol === "https:") &&
|
|
63
|
+
!url.username &&
|
|
64
|
+
!url.password &&
|
|
65
|
+
value === url.origin
|
|
66
|
+
);
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{ message: "origin must be a canonical HTTP(S) origin" },
|
|
72
|
+
);
|
|
73
|
+
const sha256Hex = z.string().regex(/^[0-9a-f]{64}$/u);
|
|
74
|
+
|
|
75
|
+
export const InteractionPlacement = z.discriminatedUnion("kind", [
|
|
76
|
+
z
|
|
77
|
+
.object({
|
|
78
|
+
kind: z.literal("sandbox_group"),
|
|
79
|
+
sandboxGroupId: z.string().uuid(),
|
|
80
|
+
})
|
|
81
|
+
.strict(),
|
|
82
|
+
z
|
|
83
|
+
.object({
|
|
84
|
+
kind: z.literal("connected_machine"),
|
|
85
|
+
sandboxId: z.string().uuid(),
|
|
86
|
+
})
|
|
87
|
+
.strict(),
|
|
88
|
+
z
|
|
89
|
+
.object({
|
|
90
|
+
kind: z.literal("attached_device"),
|
|
91
|
+
deviceId: z.string().uuid(),
|
|
92
|
+
})
|
|
93
|
+
.strict(),
|
|
94
|
+
z
|
|
95
|
+
.object({
|
|
96
|
+
kind: z.literal("external_provider"),
|
|
97
|
+
providerId: boundedOpaqueId,
|
|
98
|
+
placementId: boundedOpaqueId,
|
|
99
|
+
})
|
|
100
|
+
.strict(),
|
|
101
|
+
]);
|
|
102
|
+
export type InteractionPlacement = z.infer<typeof InteractionPlacement>;
|
|
103
|
+
|
|
104
|
+
export const InteractionControllerBinding = z
|
|
105
|
+
.object({
|
|
106
|
+
controllerId: boundedOpaqueId,
|
|
107
|
+
controllerGeneration: opaqueGeneration,
|
|
108
|
+
placementInstanceId: boundedOpaqueId,
|
|
109
|
+
})
|
|
110
|
+
.strict();
|
|
111
|
+
export type InteractionControllerBinding = z.infer<typeof InteractionControllerBinding>;
|
|
112
|
+
|
|
113
|
+
export const InteractionAssociation = z
|
|
114
|
+
.object({
|
|
115
|
+
sessionId: z.string().uuid(),
|
|
116
|
+
turnId: z.string().uuid().nullable(),
|
|
117
|
+
attemptId: z.string().uuid().nullable(),
|
|
118
|
+
relationship: z.enum(["created", "using", "observing", "related"]),
|
|
119
|
+
actorSubjectId: z.string().min(1).max(1_024),
|
|
120
|
+
lastUsedAt: z.string().datetime({ offset: true }),
|
|
121
|
+
})
|
|
122
|
+
.strict();
|
|
123
|
+
export type InteractionAssociation = z.infer<typeof InteractionAssociation>;
|
|
124
|
+
|
|
125
|
+
export const InteractionActor = z
|
|
126
|
+
.object({
|
|
127
|
+
kind: z.enum(["agent", "human", "system"]),
|
|
128
|
+
subjectId: z.string().min(1).max(1_024),
|
|
129
|
+
sessionId: z.string().uuid().optional(),
|
|
130
|
+
turnId: z.string().uuid().optional(),
|
|
131
|
+
attemptId: z.string().uuid().optional(),
|
|
132
|
+
executionGeneration: z.number().int().positive().optional(),
|
|
133
|
+
})
|
|
134
|
+
.strict()
|
|
135
|
+
.superRefine((actor, context) => {
|
|
136
|
+
const attemptFields = [actor.turnId, actor.attemptId, actor.executionGeneration];
|
|
137
|
+
const attemptFieldCount = attemptFields.filter((value) => value !== undefined).length;
|
|
138
|
+
if (attemptFieldCount !== 0 && (attemptFieldCount !== 3 || actor.sessionId === undefined)) {
|
|
139
|
+
context.addIssue({
|
|
140
|
+
code: "custom",
|
|
141
|
+
message: "turnId, attemptId, executionGeneration, and sessionId must be supplied together",
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
export type InteractionActor = z.infer<typeof InteractionActor>;
|
|
146
|
+
|
|
147
|
+
export const InteractionLifecycle = z.enum([
|
|
148
|
+
"starting",
|
|
149
|
+
"active",
|
|
150
|
+
"suspending",
|
|
151
|
+
"suspended",
|
|
152
|
+
"restoring",
|
|
153
|
+
"repair_required",
|
|
154
|
+
"lost",
|
|
155
|
+
"ending",
|
|
156
|
+
"ended",
|
|
157
|
+
"failed",
|
|
158
|
+
]);
|
|
159
|
+
export type InteractionLifecycle = z.infer<typeof InteractionLifecycle>;
|
|
160
|
+
|
|
161
|
+
export const BrowserSessionCapabilities = z
|
|
162
|
+
.object({
|
|
163
|
+
semanticObservation: z.boolean(),
|
|
164
|
+
screenshots: z.boolean(),
|
|
165
|
+
liveFrames: z.boolean(),
|
|
166
|
+
humanInput: z.boolean(),
|
|
167
|
+
tabs: z.boolean(),
|
|
168
|
+
downloads: z.boolean(),
|
|
169
|
+
uploads: z.boolean(),
|
|
170
|
+
clipboard: z.boolean(),
|
|
171
|
+
diagnostics: z.boolean(),
|
|
172
|
+
rawCdp: z.boolean(),
|
|
173
|
+
linkedComputer: z.boolean(),
|
|
174
|
+
privateCheckpoint: z.boolean(),
|
|
175
|
+
identityPublication: z.boolean(),
|
|
176
|
+
parallelTargets: z.boolean(),
|
|
177
|
+
})
|
|
178
|
+
.strict();
|
|
179
|
+
export type BrowserSessionCapabilities = z.infer<typeof BrowserSessionCapabilities>;
|
|
180
|
+
|
|
181
|
+
export const BrowserSession = z
|
|
182
|
+
.object({
|
|
183
|
+
id: z.string().uuid(),
|
|
184
|
+
accountId: z.string().uuid(),
|
|
185
|
+
workspaceId: z.string().uuid(),
|
|
186
|
+
name: z.string().trim().min(1).max(200),
|
|
187
|
+
lifecycle: InteractionLifecycle,
|
|
188
|
+
placement: InteractionPlacement,
|
|
189
|
+
controller: InteractionControllerBinding.nullable(),
|
|
190
|
+
driverId: boundedOpaqueId,
|
|
191
|
+
engine: z.enum(["chromium", "chrome", "firefox", "webkit", "lightpanda", "external"]),
|
|
192
|
+
engineVersion: z.string().min(1).max(256).nullable(),
|
|
193
|
+
headless: z.boolean(),
|
|
194
|
+
identityId: z.string().uuid().nullable(),
|
|
195
|
+
baseRevisionId: z.string().uuid().nullable(),
|
|
196
|
+
networkRouteId: z.string().uuid().nullable(),
|
|
197
|
+
linkedComputerSessionId: z.string().uuid().nullable(),
|
|
198
|
+
capabilities: BrowserSessionCapabilities,
|
|
199
|
+
associations: z.array(InteractionAssociation).max(1_000),
|
|
200
|
+
createdBySubjectId: z.string().min(1).max(1_024),
|
|
201
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
202
|
+
lastUsedAt: z.string().datetime({ offset: true }),
|
|
203
|
+
failureCode: boundedOpaqueId.nullable(),
|
|
204
|
+
})
|
|
205
|
+
.strict();
|
|
206
|
+
export type BrowserSession = z.infer<typeof BrowserSession>;
|
|
207
|
+
|
|
208
|
+
/** One live Chrome-profile bridge installed on an enrolled machine. This is a
|
|
209
|
+
* transport endpoint, not saved browser/login state: BrowserIdentity remains
|
|
210
|
+
* the immutable, reusable state abstraction. */
|
|
211
|
+
export const AttachedBrowserDeviceCapabilities = z
|
|
212
|
+
.object({
|
|
213
|
+
tabInventory: z.boolean(),
|
|
214
|
+
debuggerAttachment: z.boolean(),
|
|
215
|
+
semanticObservation: z.boolean(),
|
|
216
|
+
screenshots: z.boolean(),
|
|
217
|
+
liveFrames: z.boolean(),
|
|
218
|
+
humanInput: z.boolean(),
|
|
219
|
+
diagnostics: z.boolean(),
|
|
220
|
+
rawCdp: z.boolean(),
|
|
221
|
+
linkedComputer: z.boolean(),
|
|
222
|
+
})
|
|
223
|
+
.strict();
|
|
224
|
+
export type AttachedBrowserDeviceCapabilities = z.infer<typeof AttachedBrowserDeviceCapabilities>;
|
|
225
|
+
|
|
226
|
+
export const AttachedBrowserDeviceAnnouncement = z
|
|
227
|
+
.object({
|
|
228
|
+
id: z.string().uuid(),
|
|
229
|
+
name: z.string().trim().min(1).max(200),
|
|
230
|
+
profileLabel: z.string().trim().min(1).max(200).nullable(),
|
|
231
|
+
browserName: z.string().trim().min(1).max(100),
|
|
232
|
+
browserVersion: z.string().trim().min(1).max(256),
|
|
233
|
+
extensionVersion: z.string().trim().min(1).max(256),
|
|
234
|
+
platform: z.enum(["linux", "macos", "windows"]),
|
|
235
|
+
architecture: z.enum(["x64", "arm64"]),
|
|
236
|
+
connectionGeneration: opaqueGeneration,
|
|
237
|
+
inventoryRevision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
238
|
+
tabCount: z.number().int().nonnegative().max(100_000),
|
|
239
|
+
capabilities: AttachedBrowserDeviceCapabilities,
|
|
240
|
+
})
|
|
241
|
+
.strict();
|
|
242
|
+
export type AttachedBrowserDeviceAnnouncement = z.infer<typeof AttachedBrowserDeviceAnnouncement>;
|
|
243
|
+
|
|
244
|
+
export const AttachedBrowserInventorySnapshot = z
|
|
245
|
+
.object({
|
|
246
|
+
bridgeGeneration: opaqueGeneration,
|
|
247
|
+
revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
248
|
+
devices: z.array(AttachedBrowserDeviceAnnouncement).max(10_000),
|
|
249
|
+
})
|
|
250
|
+
.strict()
|
|
251
|
+
.superRefine((snapshot, context) => {
|
|
252
|
+
const ids = new Set<string>();
|
|
253
|
+
for (const [index, device] of snapshot.devices.entries()) {
|
|
254
|
+
if (ids.has(device.id)) {
|
|
255
|
+
context.addIssue({
|
|
256
|
+
code: "custom",
|
|
257
|
+
path: ["devices", index, "id"],
|
|
258
|
+
message: "device ids must be unique within one inventory snapshot",
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
ids.add(device.id);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
export type AttachedBrowserInventorySnapshot = z.infer<typeof AttachedBrowserInventorySnapshot>;
|
|
265
|
+
|
|
266
|
+
export const AttachedBrowserDevice = z
|
|
267
|
+
.object({
|
|
268
|
+
id: z.string().uuid(),
|
|
269
|
+
accountId: z.string().uuid(),
|
|
270
|
+
workspaceId: z.string().uuid(),
|
|
271
|
+
enrollmentId: z.string().uuid(),
|
|
272
|
+
name: z.string().trim().min(1).max(200),
|
|
273
|
+
profileLabel: z.string().trim().min(1).max(200).nullable(),
|
|
274
|
+
browserName: z.string().trim().min(1).max(100),
|
|
275
|
+
browserVersion: z.string().trim().min(1).max(256),
|
|
276
|
+
extensionVersion: z.string().trim().min(1).max(256),
|
|
277
|
+
platform: z.enum(["linux", "macos", "windows"]),
|
|
278
|
+
architecture: z.enum(["x64", "arm64"]),
|
|
279
|
+
state: z.enum(["connected", "disconnected"]),
|
|
280
|
+
connectionGeneration: opaqueGeneration,
|
|
281
|
+
inventoryRevision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
282
|
+
tabCount: z.number().int().nonnegative().max(100_000),
|
|
283
|
+
capabilities: AttachedBrowserDeviceCapabilities,
|
|
284
|
+
lastSeenAt: z.string().datetime({ offset: true }),
|
|
285
|
+
disconnectedAt: z.string().datetime({ offset: true }).nullable(),
|
|
286
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
287
|
+
updatedAt: z.string().datetime({ offset: true }),
|
|
288
|
+
})
|
|
289
|
+
.strict()
|
|
290
|
+
.superRefine((device, context) => {
|
|
291
|
+
if ((device.state === "connected") !== (device.disconnectedAt === null)) {
|
|
292
|
+
context.addIssue({
|
|
293
|
+
code: "custom",
|
|
294
|
+
path: ["disconnectedAt"],
|
|
295
|
+
message: "connected state and disconnectedAt must agree",
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
export type AttachedBrowserDevice = z.infer<typeof AttachedBrowserDevice>;
|
|
300
|
+
|
|
301
|
+
export const AttachedBrowserDeviceListResponse = z
|
|
302
|
+
.object({
|
|
303
|
+
revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
304
|
+
devices: z.array(AttachedBrowserDevice).max(10_000),
|
|
305
|
+
})
|
|
306
|
+
.strict();
|
|
307
|
+
export type AttachedBrowserDeviceListResponse = z.infer<typeof AttachedBrowserDeviceListResponse>;
|
|
308
|
+
|
|
309
|
+
export const AttachedBrowserTab = z
|
|
310
|
+
.object({
|
|
311
|
+
id: boundedOpaqueId,
|
|
312
|
+
windowId: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
313
|
+
index: z.number().int().nonnegative().max(100_000),
|
|
314
|
+
title: z.string().max(8_192),
|
|
315
|
+
url: boundedUrl.nullable(),
|
|
316
|
+
active: z.boolean(),
|
|
317
|
+
pinned: z.boolean(),
|
|
318
|
+
incognito: z.boolean(),
|
|
319
|
+
audible: z.boolean(),
|
|
320
|
+
discarded: z.boolean(),
|
|
321
|
+
controllable: z.boolean(),
|
|
322
|
+
unavailableReason: z.string().trim().min(1).max(2_048).nullable(),
|
|
323
|
+
})
|
|
324
|
+
.strict()
|
|
325
|
+
.superRefine((tab, context) => {
|
|
326
|
+
if (tab.controllable === (tab.unavailableReason !== null)) {
|
|
327
|
+
context.addIssue({
|
|
328
|
+
code: "custom",
|
|
329
|
+
path: ["unavailableReason"],
|
|
330
|
+
message: "unavailableReason is required exactly when a tab is not controllable",
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
export type AttachedBrowserTab = z.infer<typeof AttachedBrowserTab>;
|
|
335
|
+
|
|
336
|
+
export const AttachedBrowserTabListResponse = z
|
|
337
|
+
.object({
|
|
338
|
+
deviceId: z.string().uuid(),
|
|
339
|
+
connectionGeneration: opaqueGeneration,
|
|
340
|
+
inventoryRevision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
341
|
+
tabs: z.array(AttachedBrowserTab).max(100_000),
|
|
342
|
+
})
|
|
343
|
+
.strict();
|
|
344
|
+
export type AttachedBrowserTabListResponse = z.infer<typeof AttachedBrowserTabListResponse>;
|
|
345
|
+
|
|
346
|
+
export const BrowserIdentityStatus = z.enum(["active", "archived"]);
|
|
347
|
+
export type BrowserIdentityStatus = z.infer<typeof BrowserIdentityStatus>;
|
|
348
|
+
|
|
349
|
+
export const BrowserRevisionComponentKind = z.enum([
|
|
350
|
+
"chromium_profile",
|
|
351
|
+
"normalized_web_state",
|
|
352
|
+
"provider_snapshot",
|
|
353
|
+
]);
|
|
354
|
+
export type BrowserRevisionComponentKind = z.infer<typeof BrowserRevisionComponentKind>;
|
|
355
|
+
|
|
356
|
+
export const BrowserRevisionPortability = z.enum(["portable", "provider_bound", "placement_bound"]);
|
|
357
|
+
export type BrowserRevisionPortability = z.infer<typeof BrowserRevisionPortability>;
|
|
358
|
+
|
|
359
|
+
/** Public, non-secret compatibility declaration for one immutable state
|
|
360
|
+
* component. Object keys, provider snapshot handles, and encryption material
|
|
361
|
+
* never enter this contract. */
|
|
362
|
+
export const BrowserRevisionMaterialization = z
|
|
363
|
+
.object({
|
|
364
|
+
portability: BrowserRevisionPortability,
|
|
365
|
+
reason: z.string().trim().min(1).max(2_048).nullable(),
|
|
366
|
+
platform: z.enum(["linux", "macos", "windows"]).nullable(),
|
|
367
|
+
architecture: z.enum(["x64", "arm64"]).nullable(),
|
|
368
|
+
engine: z.enum(["chromium", "chrome", "firefox", "webkit", "lightpanda", "external"]),
|
|
369
|
+
engineVersion: z.string().min(1).max(256).nullable(),
|
|
370
|
+
driverId: boundedOpaqueId,
|
|
371
|
+
driverSchemaVersion: z.number().int().positive().max(1_000_000),
|
|
372
|
+
profileCrypto: z.enum(["chromium_basic", "chromium_mock_keychain", "platform_bound"]),
|
|
373
|
+
providerId: boundedOpaqueId.nullable(),
|
|
374
|
+
placement: InteractionPlacement.nullable(),
|
|
375
|
+
})
|
|
376
|
+
.strict()
|
|
377
|
+
.superRefine((value, context) => {
|
|
378
|
+
if (value.portability === "portable" && (value.providerId || value.placement)) {
|
|
379
|
+
context.addIssue({
|
|
380
|
+
code: "custom",
|
|
381
|
+
message: "portable state cannot carry a binding",
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (
|
|
385
|
+
value.portability === "provider_bound" &&
|
|
386
|
+
(!value.providerId || value.placement || !value.reason)
|
|
387
|
+
) {
|
|
388
|
+
context.addIssue({
|
|
389
|
+
code: "custom",
|
|
390
|
+
message: "provider-bound state requires one provider and reason",
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
if (
|
|
394
|
+
value.portability === "placement_bound" &&
|
|
395
|
+
(!value.placement || value.providerId || !value.reason)
|
|
396
|
+
) {
|
|
397
|
+
context.addIssue({
|
|
398
|
+
code: "custom",
|
|
399
|
+
message: "placement-bound state requires one placement and reason",
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (value.profileCrypto === "platform_bound" && value.portability !== "placement_bound") {
|
|
403
|
+
context.addIssue({
|
|
404
|
+
code: "custom",
|
|
405
|
+
message: "platform-bound profile encryption requires placement-bound state",
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
export type BrowserRevisionMaterialization = z.infer<typeof BrowserRevisionMaterialization>;
|
|
410
|
+
|
|
411
|
+
export const BrowserRevisionComponent = z
|
|
412
|
+
.object({
|
|
413
|
+
id: z.string().uuid(),
|
|
414
|
+
kind: BrowserRevisionComponentKind,
|
|
415
|
+
format: boundedOpaqueId,
|
|
416
|
+
artifactDigest: sha256Hex,
|
|
417
|
+
sizeBytes: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
418
|
+
materialization: BrowserRevisionMaterialization,
|
|
419
|
+
})
|
|
420
|
+
.strict();
|
|
421
|
+
export type BrowserRevisionComponent = z.infer<typeof BrowserRevisionComponent>;
|
|
422
|
+
|
|
423
|
+
export const BrowserRevision = z
|
|
424
|
+
.object({
|
|
425
|
+
id: z.string().uuid(),
|
|
426
|
+
accountId: z.string().uuid(),
|
|
427
|
+
workspaceId: z.string().uuid(),
|
|
428
|
+
identityId: z.string().uuid(),
|
|
429
|
+
parentRevisionId: z.string().uuid().nullable(),
|
|
430
|
+
ordinal: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
431
|
+
sourceBrowserSessionId: z.string().uuid(),
|
|
432
|
+
manifestDigest: sha256Hex,
|
|
433
|
+
components: z.array(BrowserRevisionComponent).min(1).max(16),
|
|
434
|
+
createdBySubjectId: z.string().min(1).max(1_024),
|
|
435
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
436
|
+
})
|
|
437
|
+
.strict();
|
|
438
|
+
export type BrowserRevision = z.infer<typeof BrowserRevision>;
|
|
439
|
+
|
|
440
|
+
export const BrowserIdentity = z
|
|
441
|
+
.object({
|
|
442
|
+
id: z.string().uuid(),
|
|
443
|
+
accountId: z.string().uuid(),
|
|
444
|
+
workspaceId: z.string().uuid(),
|
|
445
|
+
name: z.string().trim().min(1).max(200),
|
|
446
|
+
status: BrowserIdentityStatus,
|
|
447
|
+
defaultRevisionId: z.string().uuid().nullable(),
|
|
448
|
+
headGeneration: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
449
|
+
revisionCount: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
450
|
+
createdBySubjectId: z.string().min(1).max(1_024),
|
|
451
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
452
|
+
updatedAt: z.string().datetime({ offset: true }),
|
|
453
|
+
})
|
|
454
|
+
.strict();
|
|
455
|
+
export type BrowserIdentity = z.infer<typeof BrowserIdentity>;
|
|
456
|
+
|
|
457
|
+
/** A non-secret reference to credential authority held by OpenGeni Connections.
|
|
458
|
+
* The subject and provider bindings are copied when the browser-auth resource is
|
|
459
|
+
* configured so a later agent cannot swap the UUID to another credential. */
|
|
460
|
+
export const InteractionCredentialAuthorityRef = z
|
|
461
|
+
.object({
|
|
462
|
+
connectionId: z.string().uuid(),
|
|
463
|
+
connectionSubjectId: z.string().min(1).max(1_024).nullable(),
|
|
464
|
+
providerDomain: z.string().trim().min(1).max(512),
|
|
465
|
+
})
|
|
466
|
+
.strict();
|
|
467
|
+
export type InteractionCredentialAuthorityRef = z.infer<typeof InteractionCredentialAuthorityRef>;
|
|
468
|
+
|
|
469
|
+
export const NetworkRouteStatus = z.enum(["active", "archived"]);
|
|
470
|
+
export type NetworkRouteStatus = z.infer<typeof NetworkRouteStatus>;
|
|
471
|
+
|
|
472
|
+
export const NetworkRouteConfiguration = z.discriminatedUnion("kind", [
|
|
473
|
+
z.object({ kind: z.literal("direct") }).strict(),
|
|
474
|
+
z
|
|
475
|
+
.object({
|
|
476
|
+
kind: z.literal("proxy"),
|
|
477
|
+
protocol: z.enum(["http", "https", "socks5"]),
|
|
478
|
+
host: z.string().trim().min(1).max(253),
|
|
479
|
+
port: z.number().int().min(1).max(65_535),
|
|
480
|
+
credential: InteractionCredentialAuthorityRef.nullable(),
|
|
481
|
+
})
|
|
482
|
+
.strict(),
|
|
483
|
+
z
|
|
484
|
+
.object({
|
|
485
|
+
kind: z.literal("managed"),
|
|
486
|
+
providerId: boundedOpaqueId,
|
|
487
|
+
routeId: boundedOpaqueId,
|
|
488
|
+
egressClass: z.enum(["datacenter", "residential", "isp"]),
|
|
489
|
+
region: z.string().trim().min(1).max(128).nullable(),
|
|
490
|
+
credential: InteractionCredentialAuthorityRef.nullable(),
|
|
491
|
+
})
|
|
492
|
+
.strict(),
|
|
493
|
+
z
|
|
494
|
+
.object({
|
|
495
|
+
kind: z.literal("tunnel"),
|
|
496
|
+
placement: InteractionPlacement,
|
|
497
|
+
tunnelId: boundedOpaqueId,
|
|
498
|
+
})
|
|
499
|
+
.strict(),
|
|
500
|
+
]);
|
|
501
|
+
export type NetworkRouteConfiguration = z.infer<typeof NetworkRouteConfiguration>;
|
|
502
|
+
|
|
503
|
+
export const NetworkRouteConsistency = z
|
|
504
|
+
.object({
|
|
505
|
+
dns: z.enum(["placement", "proxy", "provider"]),
|
|
506
|
+
expectedPublicIp: z.string().trim().min(1).max(128).nullable(),
|
|
507
|
+
expectedRegion: z.string().trim().min(1).max(128).nullable(),
|
|
508
|
+
locale: z.string().trim().min(1).max(64).nullable(),
|
|
509
|
+
timezone: z.string().trim().min(1).max(128).nullable(),
|
|
510
|
+
geolocation: z
|
|
511
|
+
.object({
|
|
512
|
+
latitude: z.number().finite().min(-90).max(90),
|
|
513
|
+
longitude: z.number().finite().min(-180).max(180),
|
|
514
|
+
accuracyMeters: z.number().finite().positive().max(1_000_000),
|
|
515
|
+
})
|
|
516
|
+
.strict()
|
|
517
|
+
.nullable(),
|
|
518
|
+
webRtc: z.enum(["default", "disable_non_proxied_udp", "proxy_only"]),
|
|
519
|
+
stability: z.enum(["session", "sticky", "persistent"]),
|
|
520
|
+
})
|
|
521
|
+
.strict();
|
|
522
|
+
export type NetworkRouteConsistency = z.infer<typeof NetworkRouteConsistency>;
|
|
523
|
+
|
|
524
|
+
export const NetworkRoute = z
|
|
525
|
+
.object({
|
|
526
|
+
id: z.string().uuid(),
|
|
527
|
+
accountId: z.string().uuid(),
|
|
528
|
+
workspaceId: z.string().uuid(),
|
|
529
|
+
name: z.string().trim().min(1).max(200),
|
|
530
|
+
status: NetworkRouteStatus,
|
|
531
|
+
configuration: NetworkRouteConfiguration,
|
|
532
|
+
consistency: NetworkRouteConsistency,
|
|
533
|
+
version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
534
|
+
createdBySubjectId: z.string().min(1).max(1_024),
|
|
535
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
536
|
+
updatedAt: z.string().datetime({ offset: true }),
|
|
537
|
+
})
|
|
538
|
+
.strict();
|
|
539
|
+
export type NetworkRoute = z.infer<typeof NetworkRoute>;
|
|
540
|
+
|
|
541
|
+
export const NetworkRouteListResponse = z
|
|
542
|
+
.object({
|
|
543
|
+
revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
544
|
+
routes: z.array(NetworkRoute).max(10_000),
|
|
545
|
+
})
|
|
546
|
+
.strict();
|
|
547
|
+
export type NetworkRouteListResponse = z.infer<typeof NetworkRouteListResponse>;
|
|
548
|
+
|
|
549
|
+
export const CreateNetworkRouteRequest = z
|
|
550
|
+
.object({
|
|
551
|
+
operationId: z.string().uuid(),
|
|
552
|
+
name: z.string().trim().min(1).max(200),
|
|
553
|
+
configuration: NetworkRouteConfiguration,
|
|
554
|
+
consistency: NetworkRouteConsistency,
|
|
555
|
+
})
|
|
556
|
+
.strict();
|
|
557
|
+
export type CreateNetworkRouteRequest = z.infer<typeof CreateNetworkRouteRequest>;
|
|
558
|
+
|
|
559
|
+
export const UpdateNetworkRouteRequest = z
|
|
560
|
+
.object({
|
|
561
|
+
operationId: z.string().uuid(),
|
|
562
|
+
expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
563
|
+
name: z.string().trim().min(1).max(200).optional(),
|
|
564
|
+
status: NetworkRouteStatus.optional(),
|
|
565
|
+
configuration: NetworkRouteConfiguration.optional(),
|
|
566
|
+
consistency: NetworkRouteConsistency.optional(),
|
|
567
|
+
})
|
|
568
|
+
.strict()
|
|
569
|
+
.refine(
|
|
570
|
+
(value) =>
|
|
571
|
+
value.name !== undefined ||
|
|
572
|
+
value.status !== undefined ||
|
|
573
|
+
value.configuration !== undefined ||
|
|
574
|
+
value.consistency !== undefined,
|
|
575
|
+
{ message: "network route update is empty" },
|
|
576
|
+
);
|
|
577
|
+
export type UpdateNetworkRouteRequest = z.infer<typeof UpdateNetworkRouteRequest>;
|
|
578
|
+
|
|
579
|
+
export const NetworkRouteMutationResponse = z
|
|
580
|
+
.object({
|
|
581
|
+
route: NetworkRoute,
|
|
582
|
+
operationId: z.string().uuid(),
|
|
583
|
+
replayed: z.boolean(),
|
|
584
|
+
})
|
|
585
|
+
.strict();
|
|
586
|
+
export type NetworkRouteMutationResponse = z.infer<typeof NetworkRouteMutationResponse>;
|
|
587
|
+
|
|
588
|
+
export const SiteAuthFieldPurpose = z.enum(["identifier", "password", "secret", "totp"]);
|
|
589
|
+
export type SiteAuthFieldPurpose = z.infer<typeof SiteAuthFieldPurpose>;
|
|
590
|
+
|
|
591
|
+
export const SiteAuthAuthority = z.discriminatedUnion("kind", [
|
|
592
|
+
z
|
|
593
|
+
.object({
|
|
594
|
+
id: boundedOpaqueId,
|
|
595
|
+
kind: z.literal("connection_fields"),
|
|
596
|
+
label: z.string().trim().min(1).max(200),
|
|
597
|
+
credential: InteractionCredentialAuthorityRef,
|
|
598
|
+
fields: z
|
|
599
|
+
.array(
|
|
600
|
+
z
|
|
601
|
+
.object({
|
|
602
|
+
id: boundedOpaqueId,
|
|
603
|
+
purpose: SiteAuthFieldPurpose,
|
|
604
|
+
credentialKey: z.string().trim().min(1).max(256),
|
|
605
|
+
digits: z.number().int().min(6).max(10).optional(),
|
|
606
|
+
periodSeconds: z.number().int().min(15).max(300).optional(),
|
|
607
|
+
algorithm: z.enum(["sha1", "sha256", "sha512"]).optional(),
|
|
608
|
+
})
|
|
609
|
+
.strict()
|
|
610
|
+
.superRefine((field, context) => {
|
|
611
|
+
const totp = field.purpose === "totp";
|
|
612
|
+
if (!totp && (field.digits || field.periodSeconds || field.algorithm)) {
|
|
613
|
+
context.addIssue({
|
|
614
|
+
code: "custom",
|
|
615
|
+
message: "TOTP parameters require purpose=totp",
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}),
|
|
619
|
+
)
|
|
620
|
+
.min(1)
|
|
621
|
+
.max(32),
|
|
622
|
+
})
|
|
623
|
+
.strict(),
|
|
624
|
+
z
|
|
625
|
+
.object({
|
|
626
|
+
id: boundedOpaqueId,
|
|
627
|
+
kind: z.literal("human"),
|
|
628
|
+
label: z.string().trim().min(1).max(200),
|
|
629
|
+
fields: z
|
|
630
|
+
.array(z.object({ id: boundedOpaqueId, purpose: SiteAuthFieldPurpose }).strict())
|
|
631
|
+
.max(32),
|
|
632
|
+
})
|
|
633
|
+
.strict(),
|
|
634
|
+
z
|
|
635
|
+
.object({
|
|
636
|
+
id: boundedOpaqueId,
|
|
637
|
+
kind: z.literal("external_provider"),
|
|
638
|
+
label: z.string().trim().min(1).max(200),
|
|
639
|
+
adapterId: boundedOpaqueId,
|
|
640
|
+
credential: InteractionCredentialAuthorityRef.nullable(),
|
|
641
|
+
})
|
|
642
|
+
.strict(),
|
|
643
|
+
]);
|
|
644
|
+
export type SiteAuthAuthority = z.infer<typeof SiteAuthAuthority>;
|
|
645
|
+
|
|
646
|
+
export const SiteAuthMethod = z
|
|
647
|
+
.object({
|
|
648
|
+
id: boundedOpaqueId,
|
|
649
|
+
kind: z.enum(["password", "sso", "magic_link", "passkey", "external"]),
|
|
650
|
+
label: z.string().trim().min(1).max(200),
|
|
651
|
+
authorityIds: z.array(boundedOpaqueId).min(1).max(32),
|
|
652
|
+
})
|
|
653
|
+
.strict();
|
|
654
|
+
export type SiteAuthMethod = z.infer<typeof SiteAuthMethod>;
|
|
655
|
+
|
|
656
|
+
export const SiteAuthHealthPolicy = z
|
|
657
|
+
.object({
|
|
658
|
+
mode: z.enum(["on_use", "maintained"]),
|
|
659
|
+
intervalSeconds: z.number().int().min(60).max(31_536_000).nullable(),
|
|
660
|
+
automaticRepair: z.boolean(),
|
|
661
|
+
})
|
|
662
|
+
.strict()
|
|
663
|
+
.superRefine((policy, context) => {
|
|
664
|
+
if (policy.mode === "maintained" && policy.intervalSeconds === null) {
|
|
665
|
+
context.addIssue({ code: "custom", message: "maintained auth requires an interval" });
|
|
666
|
+
}
|
|
667
|
+
if (policy.mode === "on_use" && policy.intervalSeconds !== null) {
|
|
668
|
+
context.addIssue({ code: "custom", message: "on-use auth cannot have an interval" });
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
export type SiteAuthHealthPolicy = z.infer<typeof SiteAuthHealthPolicy>;
|
|
672
|
+
|
|
673
|
+
export const SiteAuthVerificationState = z.enum(["unknown", "verified", "needs_repair", "failed"]);
|
|
674
|
+
export type SiteAuthVerificationState = z.infer<typeof SiteAuthVerificationState>;
|
|
675
|
+
|
|
676
|
+
const SiteAuthConnectionConfiguration = z
|
|
677
|
+
.object({
|
|
678
|
+
name: z.string().trim().min(1).max(200),
|
|
679
|
+
accountLabel: z.string().trim().min(1).max(200),
|
|
680
|
+
origins: z.array(canonicalWebOrigin).min(1).max(64),
|
|
681
|
+
loginUrl: boundedHttpUrl.nullable(),
|
|
682
|
+
verificationUrlPrefixes: z.array(boundedHttpUrl).max(32),
|
|
683
|
+
authorities: z.array(SiteAuthAuthority).min(1).max(32),
|
|
684
|
+
methods: z.array(SiteAuthMethod).min(1).max(32),
|
|
685
|
+
preferredIdentityId: z.string().uuid().nullable(),
|
|
686
|
+
preferredPlacement: InteractionPlacement.nullable(),
|
|
687
|
+
preferredNetworkRouteId: z.string().uuid().nullable(),
|
|
688
|
+
healthPolicy: SiteAuthHealthPolicy,
|
|
689
|
+
})
|
|
690
|
+
.strict();
|
|
691
|
+
|
|
692
|
+
function validateSiteAuthConfiguration(
|
|
693
|
+
connection: z.infer<typeof SiteAuthConnectionConfiguration>,
|
|
694
|
+
context: z.RefinementCtx,
|
|
695
|
+
): void {
|
|
696
|
+
if (new Set(connection.origins).size !== connection.origins.length) {
|
|
697
|
+
context.addIssue({ code: "custom", path: ["origins"], message: "origins repeat" });
|
|
698
|
+
}
|
|
699
|
+
if (
|
|
700
|
+
new Set(connection.verificationUrlPrefixes).size !== connection.verificationUrlPrefixes.length
|
|
701
|
+
) {
|
|
702
|
+
context.addIssue({
|
|
703
|
+
code: "custom",
|
|
704
|
+
path: ["verificationUrlPrefixes"],
|
|
705
|
+
message: "verification URL prefixes repeat",
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
const allowedOrigins = new Set(connection.origins);
|
|
709
|
+
const checkedUrls = [
|
|
710
|
+
...(connection.loginUrl ? [{ path: ["loginUrl"], value: connection.loginUrl }] : []),
|
|
711
|
+
...connection.verificationUrlPrefixes.map((value, index) => ({
|
|
712
|
+
path: ["verificationUrlPrefixes", index],
|
|
713
|
+
value,
|
|
714
|
+
})),
|
|
715
|
+
];
|
|
716
|
+
for (const checked of checkedUrls) {
|
|
717
|
+
if (!allowedOrigins.has(new URL(checked.value).origin)) {
|
|
718
|
+
context.addIssue({
|
|
719
|
+
code: "custom",
|
|
720
|
+
path: checked.path,
|
|
721
|
+
message: "URL origin must be explicitly allowed",
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const authorityIds = new Set(connection.authorities.map((authority) => authority.id));
|
|
726
|
+
if (authorityIds.size !== connection.authorities.length) {
|
|
727
|
+
context.addIssue({ code: "custom", path: ["authorities"], message: "authority ids repeat" });
|
|
728
|
+
}
|
|
729
|
+
const methodIds = new Set(connection.methods.map((method) => method.id));
|
|
730
|
+
if (methodIds.size !== connection.methods.length) {
|
|
731
|
+
context.addIssue({ code: "custom", path: ["methods"], message: "method ids repeat" });
|
|
732
|
+
}
|
|
733
|
+
for (const [index, method] of connection.methods.entries()) {
|
|
734
|
+
if (new Set(method.authorityIds).size !== method.authorityIds.length) {
|
|
735
|
+
context.addIssue({
|
|
736
|
+
code: "custom",
|
|
737
|
+
path: ["methods", index, "authorityIds"],
|
|
738
|
+
message: "method authority ids repeat",
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
if (method.authorityIds.some((id) => !authorityIds.has(id))) {
|
|
742
|
+
context.addIssue({
|
|
743
|
+
code: "custom",
|
|
744
|
+
path: ["methods", index, "authorityIds"],
|
|
745
|
+
message: "method references an unknown authority",
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
for (const [index, authority] of connection.authorities.entries()) {
|
|
750
|
+
if (authority.kind === "external_provider") continue;
|
|
751
|
+
const fieldIds = authority.fields.map((field) => field.id);
|
|
752
|
+
if (new Set(fieldIds).size !== fieldIds.length) {
|
|
753
|
+
context.addIssue({
|
|
754
|
+
code: "custom",
|
|
755
|
+
path: ["authorities", index, "fields"],
|
|
756
|
+
message: "authority field ids repeat",
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
export const SiteAuthConnection = SiteAuthConnectionConfiguration.extend({
|
|
763
|
+
id: z.string().uuid(),
|
|
764
|
+
accountId: z.string().uuid(),
|
|
765
|
+
workspaceId: z.string().uuid(),
|
|
766
|
+
status: z.enum(["active", "archived"]),
|
|
767
|
+
verificationState: SiteAuthVerificationState,
|
|
768
|
+
lastVerifiedAt: z.string().datetime({ offset: true }).nullable(),
|
|
769
|
+
lastVerifiedUrl: boundedUrl.nullable(),
|
|
770
|
+
repairCode: boundedOpaqueId.nullable(),
|
|
771
|
+
version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
772
|
+
createdBySubjectId: z.string().min(1).max(1_024),
|
|
773
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
774
|
+
updatedAt: z.string().datetime({ offset: true }),
|
|
775
|
+
}).superRefine(validateSiteAuthConfiguration);
|
|
776
|
+
export type SiteAuthConnection = z.infer<typeof SiteAuthConnection>;
|
|
777
|
+
|
|
778
|
+
export const SiteAuthConnectionListResponse = z
|
|
779
|
+
.object({
|
|
780
|
+
revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
781
|
+
connections: z.array(SiteAuthConnection).max(10_000),
|
|
782
|
+
})
|
|
783
|
+
.strict();
|
|
784
|
+
export type SiteAuthConnectionListResponse = z.infer<typeof SiteAuthConnectionListResponse>;
|
|
785
|
+
|
|
786
|
+
export const CreateSiteAuthConnectionRequest = SiteAuthConnectionConfiguration.extend({
|
|
787
|
+
operationId: z.string().uuid(),
|
|
788
|
+
}).superRefine(validateSiteAuthConfiguration);
|
|
789
|
+
export type CreateSiteAuthConnectionRequest = z.infer<typeof CreateSiteAuthConnectionRequest>;
|
|
790
|
+
|
|
791
|
+
export const UpdateSiteAuthConnectionRequest = SiteAuthConnectionConfiguration.partial()
|
|
792
|
+
.extend({
|
|
793
|
+
operationId: z.string().uuid(),
|
|
794
|
+
expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
795
|
+
status: z.enum(["active", "archived"]).optional(),
|
|
796
|
+
})
|
|
797
|
+
.strict()
|
|
798
|
+
.refine(
|
|
799
|
+
(value) => Object.keys(value).some((key) => !["operationId", "expectedVersion"].includes(key)),
|
|
800
|
+
{
|
|
801
|
+
message: "site auth connection update is empty",
|
|
802
|
+
},
|
|
803
|
+
);
|
|
804
|
+
export type UpdateSiteAuthConnectionRequest = z.infer<typeof UpdateSiteAuthConnectionRequest>;
|
|
805
|
+
|
|
806
|
+
export const SiteAuthConnectionMutationResponse = z
|
|
807
|
+
.object({
|
|
808
|
+
connection: SiteAuthConnection,
|
|
809
|
+
operationId: z.string().uuid(),
|
|
810
|
+
replayed: z.boolean(),
|
|
811
|
+
})
|
|
812
|
+
.strict();
|
|
813
|
+
export type SiteAuthConnectionMutationResponse = z.infer<typeof SiteAuthConnectionMutationResponse>;
|
|
814
|
+
|
|
815
|
+
export const AuthRunState = z.enum([
|
|
816
|
+
"discovering",
|
|
817
|
+
"awaiting_choice",
|
|
818
|
+
"awaiting_secret",
|
|
819
|
+
"awaiting_external_action",
|
|
820
|
+
"working",
|
|
821
|
+
"verified",
|
|
822
|
+
"failed",
|
|
823
|
+
"cancelled",
|
|
824
|
+
]);
|
|
825
|
+
export type AuthRunState = z.infer<typeof AuthRunState>;
|
|
826
|
+
|
|
827
|
+
export const AuthRunChoice = z
|
|
828
|
+
.object({
|
|
829
|
+
id: boundedOpaqueId,
|
|
830
|
+
label: z.string().trim().min(1).max(200),
|
|
831
|
+
methodId: boundedOpaqueId,
|
|
832
|
+
})
|
|
833
|
+
.strict();
|
|
834
|
+
export type AuthRunChoice = z.infer<typeof AuthRunChoice>;
|
|
835
|
+
|
|
836
|
+
export const AuthRunPendingField = z
|
|
837
|
+
.object({
|
|
838
|
+
id: boundedOpaqueId,
|
|
839
|
+
label: z.string().trim().min(1).max(200),
|
|
840
|
+
purpose: SiteAuthFieldPurpose,
|
|
841
|
+
})
|
|
842
|
+
.strict();
|
|
843
|
+
export type AuthRunPendingField = z.infer<typeof AuthRunPendingField>;
|
|
844
|
+
|
|
845
|
+
export const AuthRunExternalAction = z
|
|
846
|
+
.object({
|
|
847
|
+
kind: z.enum(["push", "security_key", "passkey", "device", "human", "other"]),
|
|
848
|
+
label: z.string().trim().min(1).max(500),
|
|
849
|
+
expiresAt: z.string().datetime({ offset: true }).nullable(),
|
|
850
|
+
})
|
|
851
|
+
.strict();
|
|
852
|
+
export type AuthRunExternalAction = z.infer<typeof AuthRunExternalAction>;
|
|
853
|
+
|
|
854
|
+
export const AuthRun = z
|
|
855
|
+
.object({
|
|
856
|
+
id: z.string().uuid(),
|
|
857
|
+
accountId: z.string().uuid(),
|
|
858
|
+
workspaceId: z.string().uuid(),
|
|
859
|
+
siteAuthConnectionId: z.string().uuid(),
|
|
860
|
+
browserSessionId: z.string().uuid(),
|
|
861
|
+
targetId: boundedOpaqueId,
|
|
862
|
+
controllerGeneration: opaqueGeneration,
|
|
863
|
+
targetGeneration: opaqueGeneration,
|
|
864
|
+
documentGeneration: opaqueGeneration.nullable(),
|
|
865
|
+
methodId: boundedOpaqueId.nullable(),
|
|
866
|
+
authorityId: boundedOpaqueId.nullable(),
|
|
867
|
+
state: AuthRunState,
|
|
868
|
+
choices: z.array(AuthRunChoice).max(64),
|
|
869
|
+
pendingFields: z.array(AuthRunPendingField).max(64),
|
|
870
|
+
externalAction: AuthRunExternalAction.nullable(),
|
|
871
|
+
interventionId: z.string().uuid().nullable(),
|
|
872
|
+
verifiedUrl: boundedUrl.nullable(),
|
|
873
|
+
failureCode: boundedOpaqueId.nullable(),
|
|
874
|
+
version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
875
|
+
operationId: z.string().uuid(),
|
|
876
|
+
createdBySubjectId: z.string().min(1).max(1_024),
|
|
877
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
878
|
+
updatedAt: z.string().datetime({ offset: true }),
|
|
879
|
+
settledAt: z.string().datetime({ offset: true }).nullable(),
|
|
880
|
+
})
|
|
881
|
+
.strict()
|
|
882
|
+
.superRefine((run, context) => {
|
|
883
|
+
const settled = run.state === "verified" || run.state === "failed" || run.state === "cancelled";
|
|
884
|
+
if (settled !== (run.settledAt !== null)) {
|
|
885
|
+
context.addIssue({
|
|
886
|
+
code: "custom",
|
|
887
|
+
path: ["settledAt"],
|
|
888
|
+
message: "settled state and timestamp must agree",
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
if ((run.state === "verified") !== (run.verifiedUrl !== null)) {
|
|
892
|
+
context.addIssue({
|
|
893
|
+
code: "custom",
|
|
894
|
+
path: ["verifiedUrl"],
|
|
895
|
+
message: "verified URL requires verified state",
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
if ((run.state === "failed") !== (run.failureCode !== null)) {
|
|
899
|
+
context.addIssue({
|
|
900
|
+
code: "custom",
|
|
901
|
+
path: ["failureCode"],
|
|
902
|
+
message: "failure code requires failed state",
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
if ((run.state === "awaiting_choice") !== run.choices.length > 0) {
|
|
906
|
+
context.addIssue({
|
|
907
|
+
code: "custom",
|
|
908
|
+
path: ["choices"],
|
|
909
|
+
message: "choices require awaiting-choice state",
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
if ((run.state === "awaiting_secret") !== run.pendingFields.length > 0) {
|
|
913
|
+
context.addIssue({
|
|
914
|
+
code: "custom",
|
|
915
|
+
path: ["pendingFields"],
|
|
916
|
+
message: "pending fields require awaiting-secret state",
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
if ((run.state === "awaiting_external_action") !== (run.externalAction !== null)) {
|
|
920
|
+
context.addIssue({
|
|
921
|
+
code: "custom",
|
|
922
|
+
path: ["externalAction"],
|
|
923
|
+
message: "external action requires its waiting state",
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
export type AuthRun = z.infer<typeof AuthRun>;
|
|
928
|
+
|
|
929
|
+
export const AuthRunListResponse = z.object({ runs: z.array(AuthRun).max(10_000) }).strict();
|
|
930
|
+
export type AuthRunListResponse = z.infer<typeof AuthRunListResponse>;
|
|
931
|
+
|
|
932
|
+
export const AuthRunMutationResponse = z
|
|
933
|
+
.object({
|
|
934
|
+
run: AuthRun,
|
|
935
|
+
operationId: z.string().uuid(),
|
|
936
|
+
replayed: z.boolean(),
|
|
937
|
+
})
|
|
938
|
+
.strict();
|
|
939
|
+
export type AuthRunMutationResponse = z.infer<typeof AuthRunMutationResponse>;
|
|
940
|
+
|
|
941
|
+
export const StartAuthRunRequest = z
|
|
942
|
+
.object({
|
|
943
|
+
operationId: z.string().uuid(),
|
|
944
|
+
siteAuthConnectionId: z.string().uuid(),
|
|
945
|
+
targetId: boundedOpaqueId,
|
|
946
|
+
expectedTargetGeneration: opaqueGeneration,
|
|
947
|
+
expectedDocumentGeneration: opaqueGeneration.nullable(),
|
|
948
|
+
methodId: boundedOpaqueId.optional(),
|
|
949
|
+
authorityId: boundedOpaqueId.optional(),
|
|
950
|
+
})
|
|
951
|
+
.strict();
|
|
952
|
+
export type StartAuthRunRequest = z.infer<typeof StartAuthRunRequest>;
|
|
953
|
+
|
|
954
|
+
export const ReportAuthRunRequest = z
|
|
955
|
+
.object({
|
|
956
|
+
operationId: z.string().uuid(),
|
|
957
|
+
expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
958
|
+
methodId: boundedOpaqueId.optional(),
|
|
959
|
+
authorityId: boundedOpaqueId.optional(),
|
|
960
|
+
state: z.enum([
|
|
961
|
+
"awaiting_choice",
|
|
962
|
+
"awaiting_secret",
|
|
963
|
+
"awaiting_external_action",
|
|
964
|
+
"working",
|
|
965
|
+
"failed",
|
|
966
|
+
"cancelled",
|
|
967
|
+
]),
|
|
968
|
+
choices: z.array(AuthRunChoice).max(64).optional(),
|
|
969
|
+
pendingFields: z.array(AuthRunPendingField).max(64).optional(),
|
|
970
|
+
externalAction: AuthRunExternalAction.nullable().optional(),
|
|
971
|
+
failureCode: boundedOpaqueId.nullable().optional(),
|
|
972
|
+
})
|
|
973
|
+
.strict()
|
|
974
|
+
.superRefine((report, context) => {
|
|
975
|
+
if ((report.state === "awaiting_choice") !== Boolean(report.choices?.length)) {
|
|
976
|
+
context.addIssue({
|
|
977
|
+
code: "custom",
|
|
978
|
+
path: ["choices"],
|
|
979
|
+
message: "choices require awaiting-choice state",
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
if ((report.state === "awaiting_secret") !== Boolean(report.pendingFields?.length)) {
|
|
983
|
+
context.addIssue({
|
|
984
|
+
code: "custom",
|
|
985
|
+
path: ["pendingFields"],
|
|
986
|
+
message: "pending fields require awaiting-secret state",
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
if ((report.state === "awaiting_external_action") !== Boolean(report.externalAction)) {
|
|
990
|
+
context.addIssue({
|
|
991
|
+
code: "custom",
|
|
992
|
+
path: ["externalAction"],
|
|
993
|
+
message: "external action requires its waiting state",
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
if ((report.state === "failed") !== Boolean(report.failureCode)) {
|
|
997
|
+
context.addIssue({
|
|
998
|
+
code: "custom",
|
|
999
|
+
path: ["failureCode"],
|
|
1000
|
+
message: "failure code requires failed state",
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
export type ReportAuthRunRequest = z.infer<typeof ReportAuthRunRequest>;
|
|
1005
|
+
|
|
1006
|
+
export const ProtectedAuthField = z
|
|
1007
|
+
.object({ fieldId: boundedOpaqueId, locator: z.lazy(() => BrowserLocator) })
|
|
1008
|
+
.strict();
|
|
1009
|
+
export type ProtectedAuthField = z.infer<typeof ProtectedAuthField>;
|
|
1010
|
+
|
|
1011
|
+
export const ProtectedAuthSubmit = z.discriminatedUnion("type", [
|
|
1012
|
+
z.object({ type: z.literal("none") }).strict(),
|
|
1013
|
+
z.object({ type: z.literal("click"), locator: z.lazy(() => BrowserLocator) }).strict(),
|
|
1014
|
+
z
|
|
1015
|
+
.object({
|
|
1016
|
+
type: z.literal("press"),
|
|
1017
|
+
key: z.string().min(1).max(256),
|
|
1018
|
+
locator: z.lazy(() => BrowserLocator).optional(),
|
|
1019
|
+
})
|
|
1020
|
+
.strict(),
|
|
1021
|
+
]);
|
|
1022
|
+
export type ProtectedAuthSubmit = z.infer<typeof ProtectedAuthSubmit>;
|
|
1023
|
+
|
|
1024
|
+
export const ProtectedAuthFillRequest = z
|
|
1025
|
+
.object({
|
|
1026
|
+
operationId: z.string().uuid(),
|
|
1027
|
+
expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
1028
|
+
expectedTargetGeneration: opaqueGeneration,
|
|
1029
|
+
expectedDocumentGeneration: opaqueGeneration.nullable(),
|
|
1030
|
+
expectedFrameId: opaqueGeneration.nullable(),
|
|
1031
|
+
authorityId: boundedOpaqueId,
|
|
1032
|
+
fields: z.array(ProtectedAuthField).min(1).max(32),
|
|
1033
|
+
submit: ProtectedAuthSubmit.default({ type: "none" }),
|
|
1034
|
+
})
|
|
1035
|
+
.strict();
|
|
1036
|
+
export type ProtectedAuthFillRequest = z.infer<typeof ProtectedAuthFillRequest>;
|
|
1037
|
+
|
|
1038
|
+
export const ProtectedAuthFillResponse = z
|
|
1039
|
+
.object({
|
|
1040
|
+
run: AuthRun,
|
|
1041
|
+
status: z.enum(["submitted", "working", "needs_human", "stale", "failed"]),
|
|
1042
|
+
operationId: z.string().uuid(),
|
|
1043
|
+
replayed: z.boolean(),
|
|
1044
|
+
})
|
|
1045
|
+
.strict();
|
|
1046
|
+
export type ProtectedAuthFillResponse = z.infer<typeof ProtectedAuthFillResponse>;
|
|
1047
|
+
|
|
1048
|
+
export const VerifyAuthRunRequest = z
|
|
1049
|
+
.object({
|
|
1050
|
+
operationId: z.string().uuid(),
|
|
1051
|
+
expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
1052
|
+
})
|
|
1053
|
+
.strict();
|
|
1054
|
+
export type VerifyAuthRunRequest = z.infer<typeof VerifyAuthRunRequest>;
|
|
1055
|
+
|
|
1056
|
+
export const InteractionInterventionStatus = z.enum([
|
|
1057
|
+
"open",
|
|
1058
|
+
"completed",
|
|
1059
|
+
"dismissed",
|
|
1060
|
+
"expired",
|
|
1061
|
+
"cancelled",
|
|
1062
|
+
]);
|
|
1063
|
+
export type InteractionInterventionStatus = z.infer<typeof InteractionInterventionStatus>;
|
|
1064
|
+
|
|
1065
|
+
export const InteractionIntervention = z
|
|
1066
|
+
.object({
|
|
1067
|
+
id: z.string().uuid(),
|
|
1068
|
+
accountId: z.string().uuid(),
|
|
1069
|
+
workspaceId: z.string().uuid(),
|
|
1070
|
+
resourceKind: z.enum(["browser_session", "computer_session"]),
|
|
1071
|
+
resourceId: z.string().uuid(),
|
|
1072
|
+
targetId: boundedOpaqueId,
|
|
1073
|
+
controllerGeneration: opaqueGeneration,
|
|
1074
|
+
targetGeneration: opaqueGeneration,
|
|
1075
|
+
documentGeneration: opaqueGeneration.nullable(),
|
|
1076
|
+
kind: z.enum(["manual_login", "mfa", "external_action", "confirmation", "other"]),
|
|
1077
|
+
reason: z.string().trim().min(1).max(2_048),
|
|
1078
|
+
status: InteractionInterventionStatus,
|
|
1079
|
+
authRunId: z.string().uuid().nullable(),
|
|
1080
|
+
originatingSessionId: z.string().uuid(),
|
|
1081
|
+
originatingTurnId: z.string().uuid().nullable(),
|
|
1082
|
+
originatingAttemptId: z.string().uuid().nullable(),
|
|
1083
|
+
originatingToolOperationId: z.string().uuid().nullable(),
|
|
1084
|
+
responseActorSubjectId: z.string().min(1).max(1_024).nullable(),
|
|
1085
|
+
version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
1086
|
+
operationId: z.string().uuid(),
|
|
1087
|
+
expiresAt: z.string().datetime({ offset: true }),
|
|
1088
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
1089
|
+
updatedAt: z.string().datetime({ offset: true }),
|
|
1090
|
+
settledAt: z.string().datetime({ offset: true }).nullable(),
|
|
1091
|
+
})
|
|
1092
|
+
.strict();
|
|
1093
|
+
export type InteractionIntervention = z.infer<typeof InteractionIntervention>;
|
|
1094
|
+
|
|
1095
|
+
export const InteractionInterventionListResponse = z
|
|
1096
|
+
.object({ interventions: z.array(InteractionIntervention).max(10_000) })
|
|
1097
|
+
.strict();
|
|
1098
|
+
export type InteractionInterventionListResponse = z.infer<
|
|
1099
|
+
typeof InteractionInterventionListResponse
|
|
1100
|
+
>;
|
|
1101
|
+
|
|
1102
|
+
export const CreateInteractionInterventionRequest = z
|
|
1103
|
+
.object({
|
|
1104
|
+
operationId: z.string().uuid(),
|
|
1105
|
+
resourceKind: z.enum(["browser_session", "computer_session"]),
|
|
1106
|
+
resourceId: z.string().uuid(),
|
|
1107
|
+
targetId: boundedOpaqueId,
|
|
1108
|
+
expectedControllerGeneration: opaqueGeneration,
|
|
1109
|
+
expectedTargetGeneration: opaqueGeneration,
|
|
1110
|
+
expectedDocumentGeneration: opaqueGeneration.nullable(),
|
|
1111
|
+
kind: z.enum(["manual_login", "mfa", "external_action", "confirmation", "other"]),
|
|
1112
|
+
reason: z.string().trim().min(1).max(2_048),
|
|
1113
|
+
authRunId: z.string().uuid().optional(),
|
|
1114
|
+
expiresInSeconds: z.number().int().min(30).max(86_400).default(900),
|
|
1115
|
+
})
|
|
1116
|
+
.strict();
|
|
1117
|
+
export type CreateInteractionInterventionRequest = z.infer<
|
|
1118
|
+
typeof CreateInteractionInterventionRequest
|
|
1119
|
+
>;
|
|
1120
|
+
|
|
1121
|
+
export const ResolveInteractionInterventionRequest = z
|
|
1122
|
+
.object({
|
|
1123
|
+
operationId: z.string().uuid(),
|
|
1124
|
+
expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
1125
|
+
outcome: z.enum(["completed", "dismissed"]),
|
|
1126
|
+
})
|
|
1127
|
+
.strict();
|
|
1128
|
+
export type ResolveInteractionInterventionRequest = z.infer<
|
|
1129
|
+
typeof ResolveInteractionInterventionRequest
|
|
1130
|
+
>;
|
|
1131
|
+
|
|
1132
|
+
export const InteractionInterventionMutationResponse = z
|
|
1133
|
+
.object({
|
|
1134
|
+
intervention: InteractionIntervention,
|
|
1135
|
+
operationId: z.string().uuid(),
|
|
1136
|
+
replayed: z.boolean(),
|
|
1137
|
+
})
|
|
1138
|
+
.strict();
|
|
1139
|
+
export type InteractionInterventionMutationResponse = z.infer<
|
|
1140
|
+
typeof InteractionInterventionMutationResponse
|
|
1141
|
+
>;
|
|
1142
|
+
|
|
1143
|
+
export const BrowserIdentityListResponse = z
|
|
1144
|
+
.object({
|
|
1145
|
+
revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
1146
|
+
identities: z.array(BrowserIdentity).max(10_000),
|
|
1147
|
+
})
|
|
1148
|
+
.strict();
|
|
1149
|
+
export type BrowserIdentityListResponse = z.infer<typeof BrowserIdentityListResponse>;
|
|
1150
|
+
|
|
1151
|
+
export const BrowserRevisionListResponse = z
|
|
1152
|
+
.object({
|
|
1153
|
+
identity: BrowserIdentity,
|
|
1154
|
+
revisions: z.array(BrowserRevision).max(10_000),
|
|
1155
|
+
})
|
|
1156
|
+
.strict();
|
|
1157
|
+
export type BrowserRevisionListResponse = z.infer<typeof BrowserRevisionListResponse>;
|
|
1158
|
+
|
|
1159
|
+
export const CreateBrowserIdentityRequest = z
|
|
1160
|
+
.object({
|
|
1161
|
+
operationId: z.string().uuid(),
|
|
1162
|
+
name: z.string().trim().min(1).max(200),
|
|
1163
|
+
})
|
|
1164
|
+
.strict();
|
|
1165
|
+
export type CreateBrowserIdentityRequest = z.infer<typeof CreateBrowserIdentityRequest>;
|
|
1166
|
+
|
|
1167
|
+
export const BrowserIdentityMutationResponse = z
|
|
1168
|
+
.object({
|
|
1169
|
+
identity: BrowserIdentity,
|
|
1170
|
+
operationId: z.string().uuid(),
|
|
1171
|
+
replayed: z.boolean(),
|
|
1172
|
+
})
|
|
1173
|
+
.strict();
|
|
1174
|
+
export type BrowserIdentityMutationResponse = z.infer<typeof BrowserIdentityMutationResponse>;
|
|
1175
|
+
|
|
1176
|
+
/** Capture the exact live working profile. The controller derives the parent
|
|
1177
|
+
* from the session's current saved base; callers cannot forge lineage. */
|
|
1178
|
+
export const PublishBrowserRevisionRequest = z
|
|
1179
|
+
.object({
|
|
1180
|
+
operationId: z.string().uuid(),
|
|
1181
|
+
identityId: z.string().uuid(),
|
|
1182
|
+
expectedHeadGeneration: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
1183
|
+
advanceDefault: z.boolean().default(true),
|
|
1184
|
+
})
|
|
1185
|
+
.strict();
|
|
1186
|
+
export type PublishBrowserRevisionRequest = z.infer<typeof PublishBrowserRevisionRequest>;
|
|
1187
|
+
|
|
1188
|
+
export const PublishBrowserRevisionResponse = z
|
|
1189
|
+
.object({
|
|
1190
|
+
identity: BrowserIdentity,
|
|
1191
|
+
revision: BrowserRevision,
|
|
1192
|
+
outcome: z.enum(["saved_as_default", "saved_not_default"]),
|
|
1193
|
+
replayed: z.boolean(),
|
|
1194
|
+
})
|
|
1195
|
+
.strict();
|
|
1196
|
+
export type PublishBrowserRevisionResponse = z.infer<typeof PublishBrowserRevisionResponse>;
|
|
1197
|
+
|
|
1198
|
+
export const BrowserTarget = z
|
|
1199
|
+
.object({
|
|
1200
|
+
id: boundedOpaqueId,
|
|
1201
|
+
browserSessionId: z.string().uuid(),
|
|
1202
|
+
controllerGeneration: opaqueGeneration,
|
|
1203
|
+
targetGeneration: opaqueGeneration,
|
|
1204
|
+
documentGeneration: opaqueGeneration.nullable(),
|
|
1205
|
+
kind: z.enum(["page", "popup", "background_page", "worker"]),
|
|
1206
|
+
title: z.string().max(4_096),
|
|
1207
|
+
url: boundedUrl,
|
|
1208
|
+
selected: z.boolean(),
|
|
1209
|
+
attached: z.boolean(),
|
|
1210
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
1211
|
+
})
|
|
1212
|
+
.strict();
|
|
1213
|
+
export type BrowserTarget = z.infer<typeof BrowserTarget>;
|
|
1214
|
+
|
|
1215
|
+
export const InteractionRect = z
|
|
1216
|
+
.object({
|
|
1217
|
+
x: z.number().finite(),
|
|
1218
|
+
y: z.number().finite(),
|
|
1219
|
+
width: z.number().finite().nonnegative(),
|
|
1220
|
+
height: z.number().finite().nonnegative(),
|
|
1221
|
+
})
|
|
1222
|
+
.strict();
|
|
1223
|
+
export type InteractionRect = z.infer<typeof InteractionRect>;
|
|
1224
|
+
|
|
1225
|
+
export const InteractionRedactedValue = z
|
|
1226
|
+
.object({
|
|
1227
|
+
redacted: z.literal(true),
|
|
1228
|
+
reason: z.enum(["password", "payment", "private", "policy"]),
|
|
1229
|
+
})
|
|
1230
|
+
.strict();
|
|
1231
|
+
export type InteractionRedactedValue = z.infer<typeof InteractionRedactedValue>;
|
|
1232
|
+
|
|
1233
|
+
export type InteractionSemanticNodeValue = {
|
|
1234
|
+
ref: string;
|
|
1235
|
+
role: string;
|
|
1236
|
+
identifier?: string | undefined;
|
|
1237
|
+
name?: string | undefined;
|
|
1238
|
+
description?: string | undefined;
|
|
1239
|
+
value?: string | InteractionRedactedValue | undefined;
|
|
1240
|
+
states: string[];
|
|
1241
|
+
bounds?: InteractionRect | undefined;
|
|
1242
|
+
actions: string[];
|
|
1243
|
+
children?: InteractionSemanticNodeValue[] | undefined;
|
|
1244
|
+
native?: { platform: "dom" | "mac_ax" | "at_spi" | "uia"; data: unknown } | undefined;
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
export const InteractionSemanticNode: z.ZodType<InteractionSemanticNodeValue> = z.lazy(() =>
|
|
1248
|
+
z
|
|
1249
|
+
.object({
|
|
1250
|
+
ref: boundedOpaqueId,
|
|
1251
|
+
role: z.string().min(1).max(256),
|
|
1252
|
+
identifier: z.string().min(1).max(2_048).optional(),
|
|
1253
|
+
name: z.string().max(8_192).optional(),
|
|
1254
|
+
description: z.string().max(8_192).optional(),
|
|
1255
|
+
value: z.union([z.string().max(32_768), InteractionRedactedValue]).optional(),
|
|
1256
|
+
states: z.array(z.string().min(1).max(128)).max(64),
|
|
1257
|
+
bounds: InteractionRect.optional(),
|
|
1258
|
+
actions: z.array(z.string().min(1).max(128)).max(64),
|
|
1259
|
+
children: z.array(InteractionSemanticNode).optional(),
|
|
1260
|
+
native: z
|
|
1261
|
+
.object({
|
|
1262
|
+
platform: z.enum(["dom", "mac_ax", "at_spi", "uia"]),
|
|
1263
|
+
data: z.json(),
|
|
1264
|
+
})
|
|
1265
|
+
.strict()
|
|
1266
|
+
.optional(),
|
|
1267
|
+
})
|
|
1268
|
+
.strict(),
|
|
1269
|
+
);
|
|
1270
|
+
|
|
1271
|
+
export const InteractionSemanticSnapshot = z
|
|
1272
|
+
.object({
|
|
1273
|
+
kind: z.literal("snapshot"),
|
|
1274
|
+
roots: z.array(InteractionSemanticNode),
|
|
1275
|
+
nodeCount: z.number().int().nonnegative().max(INTERACTION_MAX_SEMANTIC_NODES),
|
|
1276
|
+
})
|
|
1277
|
+
.strict();
|
|
1278
|
+
|
|
1279
|
+
export const InteractionSemanticDiff = z
|
|
1280
|
+
.object({
|
|
1281
|
+
kind: z.literal("diff"),
|
|
1282
|
+
baseObservationId: boundedOpaqueId,
|
|
1283
|
+
removedRefs: z.array(boundedOpaqueId).max(INTERACTION_MAX_CHANGED_NODES),
|
|
1284
|
+
changed: z.array(InteractionSemanticNode).max(INTERACTION_MAX_CHANGED_NODES),
|
|
1285
|
+
})
|
|
1286
|
+
.strict();
|
|
1287
|
+
|
|
1288
|
+
export const InteractionDiagnosticSummary = z
|
|
1289
|
+
.object({
|
|
1290
|
+
consoleErrorCount: z.number().int().nonnegative(),
|
|
1291
|
+
failedRequestCount: z.number().int().nonnegative(),
|
|
1292
|
+
downloadCount: z.number().int().nonnegative(),
|
|
1293
|
+
pageErrorCount: z.number().int().nonnegative(),
|
|
1294
|
+
})
|
|
1295
|
+
.strict();
|
|
1296
|
+
|
|
1297
|
+
export const BrowserDialog = z
|
|
1298
|
+
.object({
|
|
1299
|
+
type: z.enum(["alert", "confirm", "prompt", "beforeunload"]),
|
|
1300
|
+
message: z.string().max(8_192),
|
|
1301
|
+
defaultPrompt: z.string().max(32_768),
|
|
1302
|
+
openedAt: z.string().datetime({ offset: true }),
|
|
1303
|
+
})
|
|
1304
|
+
.strict();
|
|
1305
|
+
export type BrowserDialog = z.infer<typeof BrowserDialog>;
|
|
1306
|
+
|
|
1307
|
+
export const BrowserObservation = z
|
|
1308
|
+
.object({
|
|
1309
|
+
protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
|
|
1310
|
+
observationId: boundedOpaqueId,
|
|
1311
|
+
browserSessionId: z.string().uuid(),
|
|
1312
|
+
target: BrowserTarget,
|
|
1313
|
+
frameId: opaqueGeneration.nullable(),
|
|
1314
|
+
semantic: z.union([InteractionSemanticSnapshot, InteractionSemanticDiff]).nullable(),
|
|
1315
|
+
screenshot: RetainedArtifactReferenceSchema.nullable(),
|
|
1316
|
+
focusedRef: boundedOpaqueId.nullable(),
|
|
1317
|
+
changedRegions: z.array(InteractionRect).max(INTERACTION_MAX_CHANGED_NODES),
|
|
1318
|
+
diagnostics: InteractionDiagnosticSummary,
|
|
1319
|
+
dialog: BrowserDialog.nullable(),
|
|
1320
|
+
observedAt: z.string().datetime({ offset: true }),
|
|
1321
|
+
})
|
|
1322
|
+
.strict();
|
|
1323
|
+
export type BrowserObservation = z.infer<typeof BrowserObservation>;
|
|
1324
|
+
|
|
1325
|
+
export const BrowserDiagnosticKind = z.enum([
|
|
1326
|
+
"console",
|
|
1327
|
+
"page_error",
|
|
1328
|
+
"failed_request",
|
|
1329
|
+
"download",
|
|
1330
|
+
]);
|
|
1331
|
+
export type BrowserDiagnosticKind = z.infer<typeof BrowserDiagnosticKind>;
|
|
1332
|
+
|
|
1333
|
+
export const BrowserDiagnosticEntry = z
|
|
1334
|
+
.object({
|
|
1335
|
+
sequence: z.number().int().positive(),
|
|
1336
|
+
kind: BrowserDiagnosticKind,
|
|
1337
|
+
level: z.enum(["debug", "info", "warning", "error"]).nullable(),
|
|
1338
|
+
message: z.string().max(8_192),
|
|
1339
|
+
url: boundedUrl.nullable(),
|
|
1340
|
+
method: z.string().min(1).max(32).nullable(),
|
|
1341
|
+
status: z.number().int().min(100).max(999).nullable(),
|
|
1342
|
+
filename: z.string().max(4_096).nullable(),
|
|
1343
|
+
occurredAt: z.string().datetime({ offset: true }),
|
|
1344
|
+
})
|
|
1345
|
+
.strict();
|
|
1346
|
+
export type BrowserDiagnosticEntry = z.infer<typeof BrowserDiagnosticEntry>;
|
|
1347
|
+
|
|
1348
|
+
export const BrowserDiagnosticBatch = z
|
|
1349
|
+
.object({
|
|
1350
|
+
browserSessionId: z.string().uuid(),
|
|
1351
|
+
controllerGeneration: opaqueGeneration,
|
|
1352
|
+
targetId: boundedOpaqueId,
|
|
1353
|
+
targetGeneration: opaqueGeneration,
|
|
1354
|
+
entries: z.array(BrowserDiagnosticEntry).max(INTERACTION_MAX_DIAGNOSTIC_ENTRIES),
|
|
1355
|
+
cursor: z.number().int().nonnegative(),
|
|
1356
|
+
truncated: z.boolean(),
|
|
1357
|
+
})
|
|
1358
|
+
.strict();
|
|
1359
|
+
export type BrowserDiagnosticBatch = z.infer<typeof BrowserDiagnosticBatch>;
|
|
1360
|
+
|
|
1361
|
+
export const BrowserLocator = z.discriminatedUnion("kind", [
|
|
1362
|
+
z.object({ kind: z.literal("ref"), ref: boundedOpaqueId }).strict(),
|
|
1363
|
+
z
|
|
1364
|
+
.object({
|
|
1365
|
+
kind: z.literal("role"),
|
|
1366
|
+
role: z.string().min(1).max(256),
|
|
1367
|
+
name: z.string().max(8_192).optional(),
|
|
1368
|
+
exact: z.boolean().optional(),
|
|
1369
|
+
})
|
|
1370
|
+
.strict(),
|
|
1371
|
+
z.object({ kind: z.literal("label"), text: z.string().min(1).max(8_192) }).strict(),
|
|
1372
|
+
z.object({ kind: z.literal("text"), text: z.string().min(1).max(8_192) }).strict(),
|
|
1373
|
+
z
|
|
1374
|
+
.object({
|
|
1375
|
+
kind: z.literal("placeholder"),
|
|
1376
|
+
text: z.string().min(1).max(8_192),
|
|
1377
|
+
})
|
|
1378
|
+
.strict(),
|
|
1379
|
+
z.object({ kind: z.literal("test_id"), value: z.string().min(1).max(2_048) }).strict(),
|
|
1380
|
+
z.object({ kind: z.literal("css"), selector: z.string().min(1).max(8_192) }).strict(),
|
|
1381
|
+
]);
|
|
1382
|
+
export type BrowserLocator = z.infer<typeof BrowserLocator>;
|
|
1383
|
+
|
|
1384
|
+
/** Provider-neutral locator for native accessibility trees. Native automation
|
|
1385
|
+
* identifiers are intentionally distinct from DOM selectors/test ids: AX,
|
|
1386
|
+
* AT-SPI, and UIA adapters resolve them inside the exact observed target. */
|
|
1387
|
+
export const ComputerLocator = z.discriminatedUnion("kind", [
|
|
1388
|
+
z.object({ kind: z.literal("ref"), ref: boundedOpaqueId }).strict(),
|
|
1389
|
+
z
|
|
1390
|
+
.object({
|
|
1391
|
+
kind: z.literal("role"),
|
|
1392
|
+
role: z.string().min(1).max(256),
|
|
1393
|
+
name: z.string().max(8_192).optional(),
|
|
1394
|
+
exact: z.boolean().optional(),
|
|
1395
|
+
})
|
|
1396
|
+
.strict(),
|
|
1397
|
+
z
|
|
1398
|
+
.object({
|
|
1399
|
+
kind: z.literal("label"),
|
|
1400
|
+
text: z.string().min(1).max(8_192),
|
|
1401
|
+
exact: z.boolean().optional(),
|
|
1402
|
+
})
|
|
1403
|
+
.strict(),
|
|
1404
|
+
z
|
|
1405
|
+
.object({
|
|
1406
|
+
kind: z.literal("text"),
|
|
1407
|
+
text: z.string().min(1).max(8_192),
|
|
1408
|
+
exact: z.boolean().optional(),
|
|
1409
|
+
})
|
|
1410
|
+
.strict(),
|
|
1411
|
+
z
|
|
1412
|
+
.object({
|
|
1413
|
+
kind: z.literal("identifier"),
|
|
1414
|
+
value: z.string().min(1).max(2_048),
|
|
1415
|
+
})
|
|
1416
|
+
.strict(),
|
|
1417
|
+
]);
|
|
1418
|
+
export type ComputerLocator = z.infer<typeof ComputerLocator>;
|
|
1419
|
+
|
|
1420
|
+
const browserActionVariants = [
|
|
1421
|
+
z.object({ type: z.literal("navigate"), url: boundedUrl }).strict(),
|
|
1422
|
+
z
|
|
1423
|
+
.object({
|
|
1424
|
+
type: z.literal("click"),
|
|
1425
|
+
locator: BrowserLocator,
|
|
1426
|
+
button: z.enum(["left", "right", "middle"]).optional(),
|
|
1427
|
+
})
|
|
1428
|
+
.strict(),
|
|
1429
|
+
z.object({ type: z.literal("double_click"), locator: BrowserLocator }).strict(),
|
|
1430
|
+
z.object({ type: z.literal("hover"), locator: BrowserLocator }).strict(),
|
|
1431
|
+
z
|
|
1432
|
+
.object({
|
|
1433
|
+
type: z.literal("fill"),
|
|
1434
|
+
locator: BrowserLocator,
|
|
1435
|
+
value: z.string().max(1_000_000),
|
|
1436
|
+
})
|
|
1437
|
+
.strict(),
|
|
1438
|
+
z
|
|
1439
|
+
.object({
|
|
1440
|
+
type: z.literal("type"),
|
|
1441
|
+
locator: BrowserLocator.optional(),
|
|
1442
|
+
text: z.string().max(1_000_000),
|
|
1443
|
+
})
|
|
1444
|
+
.strict(),
|
|
1445
|
+
z
|
|
1446
|
+
.object({
|
|
1447
|
+
type: z.literal("press"),
|
|
1448
|
+
locator: BrowserLocator.optional(),
|
|
1449
|
+
key: z.string().min(1).max(256),
|
|
1450
|
+
})
|
|
1451
|
+
.strict(),
|
|
1452
|
+
z
|
|
1453
|
+
.object({
|
|
1454
|
+
type: z.literal("select"),
|
|
1455
|
+
locator: BrowserLocator,
|
|
1456
|
+
values: z.array(z.string().max(8_192)).min(1).max(100),
|
|
1457
|
+
})
|
|
1458
|
+
.strict(),
|
|
1459
|
+
z
|
|
1460
|
+
.object({
|
|
1461
|
+
type: z.literal("check"),
|
|
1462
|
+
locator: BrowserLocator,
|
|
1463
|
+
checked: z.boolean(),
|
|
1464
|
+
})
|
|
1465
|
+
.strict(),
|
|
1466
|
+
z
|
|
1467
|
+
.object({
|
|
1468
|
+
type: z.literal("scroll"),
|
|
1469
|
+
locator: BrowserLocator.optional(),
|
|
1470
|
+
deltaX: z.number().finite(),
|
|
1471
|
+
deltaY: z.number().finite(),
|
|
1472
|
+
})
|
|
1473
|
+
.strict(),
|
|
1474
|
+
z
|
|
1475
|
+
.object({
|
|
1476
|
+
type: z.literal("drag"),
|
|
1477
|
+
from: BrowserLocator,
|
|
1478
|
+
to: BrowserLocator,
|
|
1479
|
+
})
|
|
1480
|
+
.strict(),
|
|
1481
|
+
z
|
|
1482
|
+
.object({
|
|
1483
|
+
type: z.literal("pointer"),
|
|
1484
|
+
action: z.enum(["click", "double_click", "move", "scroll", "drag"]),
|
|
1485
|
+
x: z.number().finite().nonnegative().max(1_000_000),
|
|
1486
|
+
y: z.number().finite().nonnegative().max(1_000_000),
|
|
1487
|
+
endX: z.number().finite().nonnegative().max(1_000_000).optional(),
|
|
1488
|
+
endY: z.number().finite().nonnegative().max(1_000_000).optional(),
|
|
1489
|
+
deltaX: z.number().finite().min(-1_000_000).max(1_000_000).optional(),
|
|
1490
|
+
deltaY: z.number().finite().min(-1_000_000).max(1_000_000).optional(),
|
|
1491
|
+
button: z.enum(["left", "right", "middle"]).optional(),
|
|
1492
|
+
})
|
|
1493
|
+
.strict()
|
|
1494
|
+
.superRefine((action, context) => {
|
|
1495
|
+
const hasEnd = action.endX !== undefined || action.endY !== undefined;
|
|
1496
|
+
const hasDelta = action.deltaX !== undefined || action.deltaY !== undefined;
|
|
1497
|
+
if (action.action === "drag" && (action.endX === undefined || action.endY === undefined)) {
|
|
1498
|
+
context.addIssue({
|
|
1499
|
+
code: "custom",
|
|
1500
|
+
message: "pointer drag requires endX and endY",
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
if (action.action !== "drag" && hasEnd) {
|
|
1504
|
+
context.addIssue({
|
|
1505
|
+
code: "custom",
|
|
1506
|
+
message: "pointer end coordinates require drag",
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
if (
|
|
1510
|
+
action.action === "scroll" &&
|
|
1511
|
+
action.deltaX === undefined &&
|
|
1512
|
+
action.deltaY === undefined
|
|
1513
|
+
) {
|
|
1514
|
+
context.addIssue({
|
|
1515
|
+
code: "custom",
|
|
1516
|
+
message: "pointer scroll requires a delta",
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
if (action.action !== "scroll" && hasDelta) {
|
|
1520
|
+
context.addIssue({
|
|
1521
|
+
code: "custom",
|
|
1522
|
+
message: "pointer deltas require scroll",
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
}),
|
|
1526
|
+
z
|
|
1527
|
+
.object({
|
|
1528
|
+
type: z.literal("handle_dialog"),
|
|
1529
|
+
response: z.enum(["accept", "dismiss"]),
|
|
1530
|
+
promptText: z.string().max(32_768).optional(),
|
|
1531
|
+
})
|
|
1532
|
+
.strict(),
|
|
1533
|
+
z
|
|
1534
|
+
.object({
|
|
1535
|
+
type: z.literal("upload"),
|
|
1536
|
+
locator: BrowserLocator,
|
|
1537
|
+
workspaceFileIds: z.array(z.string().uuid()).min(1).max(100),
|
|
1538
|
+
})
|
|
1539
|
+
.strict(),
|
|
1540
|
+
z
|
|
1541
|
+
.object({
|
|
1542
|
+
type: z.literal("wait"),
|
|
1543
|
+
condition: z.enum(["load", "network_idle", "visible", "hidden"]),
|
|
1544
|
+
locator: BrowserLocator.optional(),
|
|
1545
|
+
timeoutMs: z.number().int().positive().max(120_000).optional(),
|
|
1546
|
+
})
|
|
1547
|
+
.strict(),
|
|
1548
|
+
] as const;
|
|
1549
|
+
|
|
1550
|
+
export const BrowserAction = z.discriminatedUnion("type", browserActionVariants);
|
|
1551
|
+
export type BrowserAction = z.infer<typeof BrowserAction>;
|
|
1552
|
+
|
|
1553
|
+
export const BrowserActionBatch = z
|
|
1554
|
+
.object({
|
|
1555
|
+
type: z.literal("batch"),
|
|
1556
|
+
actions: z.array(BrowserAction).min(1).max(INTERACTION_MAX_ACTIONS_PER_BATCH),
|
|
1557
|
+
})
|
|
1558
|
+
.strict();
|
|
1559
|
+
export type BrowserActionBatch = z.infer<typeof BrowserActionBatch>;
|
|
1560
|
+
|
|
1561
|
+
export const InteractionError = z
|
|
1562
|
+
.object({
|
|
1563
|
+
code: z.enum([
|
|
1564
|
+
"resource_not_found",
|
|
1565
|
+
"resource_unavailable",
|
|
1566
|
+
"controller_stale",
|
|
1567
|
+
"target_not_found",
|
|
1568
|
+
"target_stale",
|
|
1569
|
+
"observation_stale",
|
|
1570
|
+
"document_stale",
|
|
1571
|
+
"frame_stale",
|
|
1572
|
+
"locator_not_found",
|
|
1573
|
+
"locator_ambiguous",
|
|
1574
|
+
"unsupported",
|
|
1575
|
+
"permission_denied",
|
|
1576
|
+
"machine_locked",
|
|
1577
|
+
"attempt_stale",
|
|
1578
|
+
"operation_conflict",
|
|
1579
|
+
"outcome_unknown",
|
|
1580
|
+
"invalid_action",
|
|
1581
|
+
"timeout",
|
|
1582
|
+
"controller_lost",
|
|
1583
|
+
"driver_failed",
|
|
1584
|
+
]),
|
|
1585
|
+
message: z.string().min(1).max(8_192),
|
|
1586
|
+
retryable: z.boolean(),
|
|
1587
|
+
details: z.record(z.string().max(256), z.json()).optional(),
|
|
1588
|
+
})
|
|
1589
|
+
.strict();
|
|
1590
|
+
export type InteractionError = z.infer<typeof InteractionError>;
|
|
1591
|
+
|
|
1592
|
+
export const BrowserActionCommand = z
|
|
1593
|
+
.object({
|
|
1594
|
+
protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
|
|
1595
|
+
operationId: z.string().uuid(),
|
|
1596
|
+
browserSessionId: z.string().uuid(),
|
|
1597
|
+
controllerGeneration: opaqueGeneration,
|
|
1598
|
+
targetId: boundedOpaqueId,
|
|
1599
|
+
expectedTargetGeneration: opaqueGeneration,
|
|
1600
|
+
expectedDocumentGeneration: opaqueGeneration.nullable(),
|
|
1601
|
+
expectedFrameId: opaqueGeneration.nullable(),
|
|
1602
|
+
actor: InteractionActor,
|
|
1603
|
+
action: z.union([BrowserAction, BrowserActionBatch]),
|
|
1604
|
+
})
|
|
1605
|
+
.strict();
|
|
1606
|
+
export type BrowserActionCommand = z.infer<typeof BrowserActionCommand>;
|
|
1607
|
+
|
|
1608
|
+
export const InteractionOperationState = z.enum([
|
|
1609
|
+
"prepared",
|
|
1610
|
+
"dispatched",
|
|
1611
|
+
"completed",
|
|
1612
|
+
"failed",
|
|
1613
|
+
"outcome_unknown",
|
|
1614
|
+
]);
|
|
1615
|
+
export type InteractionOperationState = z.infer<typeof InteractionOperationState>;
|
|
1616
|
+
|
|
1617
|
+
export const InteractionLifecycleOperationKind = z.enum([
|
|
1618
|
+
"create",
|
|
1619
|
+
"resume",
|
|
1620
|
+
"suspend",
|
|
1621
|
+
"end",
|
|
1622
|
+
"publish",
|
|
1623
|
+
]);
|
|
1624
|
+
export type InteractionLifecycleOperationKind = z.infer<typeof InteractionLifecycleOperationKind>;
|
|
1625
|
+
|
|
1626
|
+
export const InteractionLifecycleOperationReceipt = z
|
|
1627
|
+
.object({
|
|
1628
|
+
operationId: z.string().uuid(),
|
|
1629
|
+
resourceKind: z.enum(["browser_session", "computer_session"]),
|
|
1630
|
+
resourceId: z.string().uuid(),
|
|
1631
|
+
kind: InteractionLifecycleOperationKind,
|
|
1632
|
+
state: InteractionOperationState,
|
|
1633
|
+
replayed: z.boolean(),
|
|
1634
|
+
error: InteractionError.nullable(),
|
|
1635
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
1636
|
+
dispatchedAt: z.string().datetime({ offset: true }).nullable(),
|
|
1637
|
+
settledAt: z.string().datetime({ offset: true }).nullable(),
|
|
1638
|
+
})
|
|
1639
|
+
.strict()
|
|
1640
|
+
.superRefine((receipt, context) => {
|
|
1641
|
+
if (receipt.state === "completed" && (receipt.error !== null || receipt.settledAt === null)) {
|
|
1642
|
+
context.addIssue({
|
|
1643
|
+
code: "custom",
|
|
1644
|
+
message: "completed operation must settle without error",
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
if (
|
|
1648
|
+
(receipt.state === "failed" || receipt.state === "outcome_unknown") &&
|
|
1649
|
+
(receipt.error === null || receipt.settledAt === null)
|
|
1650
|
+
) {
|
|
1651
|
+
context.addIssue({
|
|
1652
|
+
code: "custom",
|
|
1653
|
+
message: "terminal operation error requires error and time",
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
if (receipt.state === "dispatched" && receipt.dispatchedAt === null) {
|
|
1657
|
+
context.addIssue({
|
|
1658
|
+
code: "custom",
|
|
1659
|
+
message: "dispatched operation requires dispatch time",
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
export type InteractionLifecycleOperationReceipt = z.infer<
|
|
1664
|
+
typeof InteractionLifecycleOperationReceipt
|
|
1665
|
+
>;
|
|
1666
|
+
|
|
1667
|
+
export const CreateBrowserSessionRequest = z
|
|
1668
|
+
.object({
|
|
1669
|
+
operationId: z.string().uuid(),
|
|
1670
|
+
sessionId: z.string().uuid(),
|
|
1671
|
+
name: z.string().trim().min(1).max(200).optional(),
|
|
1672
|
+
initialUrl: boundedUrl.optional(),
|
|
1673
|
+
headless: z.boolean().default(true),
|
|
1674
|
+
placement: InteractionPlacement.optional(),
|
|
1675
|
+
identityId: z.string().uuid().optional(),
|
|
1676
|
+
baseRevisionId: z.string().uuid().optional(),
|
|
1677
|
+
networkRouteId: z.string().uuid().optional(),
|
|
1678
|
+
linkedComputerSessionId: z.string().uuid().optional(),
|
|
1679
|
+
})
|
|
1680
|
+
.strict()
|
|
1681
|
+
.superRefine((value, context) => {
|
|
1682
|
+
if (value.baseRevisionId && !value.identityId) {
|
|
1683
|
+
context.addIssue({
|
|
1684
|
+
code: "custom",
|
|
1685
|
+
path: ["baseRevisionId"],
|
|
1686
|
+
message: "baseRevisionId requires identityId",
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
if (value.linkedComputerSessionId && value.headless) {
|
|
1690
|
+
context.addIssue({
|
|
1691
|
+
code: "custom",
|
|
1692
|
+
path: ["linkedComputerSessionId"],
|
|
1693
|
+
message: "a linked computer requires a headed browser",
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
if (value.placement?.kind === "attached_device") {
|
|
1697
|
+
if (value.linkedComputerSessionId) {
|
|
1698
|
+
context.addIssue({
|
|
1699
|
+
code: "custom",
|
|
1700
|
+
path: ["linkedComputerSessionId"],
|
|
1701
|
+
message: "attached Chrome does not expose an exact linked computer yet",
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
if (value.headless) {
|
|
1705
|
+
context.addIssue({
|
|
1706
|
+
code: "custom",
|
|
1707
|
+
path: ["headless"],
|
|
1708
|
+
message: "attached browser placement is always headed",
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
if (value.identityId || value.baseRevisionId) {
|
|
1712
|
+
context.addIssue({
|
|
1713
|
+
code: "custom",
|
|
1714
|
+
path: ["identityId"],
|
|
1715
|
+
message: "attached browser placement already has a live profile identity",
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
});
|
|
1720
|
+
export type CreateBrowserSessionRequest = z.infer<typeof CreateBrowserSessionRequest>;
|
|
1721
|
+
|
|
1722
|
+
export const BrowserSessionListResponse = z
|
|
1723
|
+
.object({
|
|
1724
|
+
revision: z.number().int().nonnegative(),
|
|
1725
|
+
sessions: z.array(BrowserSession).max(10_000),
|
|
1726
|
+
})
|
|
1727
|
+
.strict();
|
|
1728
|
+
export type BrowserSessionListResponse = z.infer<typeof BrowserSessionListResponse>;
|
|
1729
|
+
|
|
1730
|
+
export const BrowserSessionMutationResponse = z
|
|
1731
|
+
.object({
|
|
1732
|
+
session: BrowserSession,
|
|
1733
|
+
operation: InteractionLifecycleOperationReceipt,
|
|
1734
|
+
})
|
|
1735
|
+
.strict();
|
|
1736
|
+
export type BrowserSessionMutationResponse = z.infer<typeof BrowserSessionMutationResponse>;
|
|
1737
|
+
|
|
1738
|
+
export const BrowserSessionLifecycleRequest = z
|
|
1739
|
+
.object({
|
|
1740
|
+
operationId: z.string().uuid(),
|
|
1741
|
+
})
|
|
1742
|
+
.strict();
|
|
1743
|
+
export type BrowserSessionLifecycleRequest = z.infer<typeof BrowserSessionLifecycleRequest>;
|
|
1744
|
+
|
|
1745
|
+
export const BrowserOpenTargetRequest = z
|
|
1746
|
+
.object({
|
|
1747
|
+
url: boundedUrl.optional(),
|
|
1748
|
+
})
|
|
1749
|
+
.strict();
|
|
1750
|
+
export type BrowserOpenTargetRequest = z.infer<typeof BrowserOpenTargetRequest>;
|
|
1751
|
+
|
|
1752
|
+
export const BrowserTargetListResponse = z
|
|
1753
|
+
.object({
|
|
1754
|
+
browserSessionId: z.string().uuid(),
|
|
1755
|
+
controllerGeneration: opaqueGeneration,
|
|
1756
|
+
targets: z.array(BrowserTarget).max(10_000),
|
|
1757
|
+
})
|
|
1758
|
+
.strict();
|
|
1759
|
+
export type BrowserTargetListResponse = z.infer<typeof BrowserTargetListResponse>;
|
|
1760
|
+
|
|
1761
|
+
export const InteractionFrameStreamOptions = z
|
|
1762
|
+
.object({
|
|
1763
|
+
format: z.enum(["jpeg", "png"]).optional(),
|
|
1764
|
+
quality: z.number().int().min(1).max(100).optional(),
|
|
1765
|
+
maxWidth: z.number().int().min(1).max(4_096).optional(),
|
|
1766
|
+
maxHeight: z.number().int().min(1).max(4_096).optional(),
|
|
1767
|
+
everyNthFrame: z.number().int().min(1).max(60).optional(),
|
|
1768
|
+
})
|
|
1769
|
+
.strict();
|
|
1770
|
+
export type InteractionFrameStreamOptions = z.infer<typeof InteractionFrameStreamOptions>;
|
|
1771
|
+
|
|
1772
|
+
export const BrowserFrameStreamOptions = InteractionFrameStreamOptions;
|
|
1773
|
+
export type BrowserFrameStreamOptions = z.infer<typeof BrowserFrameStreamOptions>;
|
|
1774
|
+
|
|
1775
|
+
/** Placement-neutral controller attachment. Browser and Computer viewers use
|
|
1776
|
+
* the same authenticated transport shape while negotiating distinct stream
|
|
1777
|
+
* protocols and resource generations. */
|
|
1778
|
+
const DirectInteractionFrameStreamAttachment = z
|
|
1779
|
+
.object({
|
|
1780
|
+
kind: z.literal("direct_websocket"),
|
|
1781
|
+
url: boundedUrl,
|
|
1782
|
+
protocols: z.array(z.string().min(1).max(2_048)).length(2),
|
|
1783
|
+
})
|
|
1784
|
+
.strict();
|
|
1785
|
+
|
|
1786
|
+
function relayInteractionFrameStreamAttachment<const Kind extends 3 | 4>(kind: Kind) {
|
|
1787
|
+
return z
|
|
1788
|
+
.object({
|
|
1789
|
+
kind: z.literal("relay"),
|
|
1790
|
+
url: boundedUrl,
|
|
1791
|
+
token: z.string().min(32).max(8_192),
|
|
1792
|
+
channel: z
|
|
1793
|
+
.object({
|
|
1794
|
+
channelId: boundedOpaqueId,
|
|
1795
|
+
workspaceId: z.string().uuid(),
|
|
1796
|
+
agentId: boundedOpaqueId,
|
|
1797
|
+
kind: z.literal(kind),
|
|
1798
|
+
port: z.number().int().min(1).max(65_535),
|
|
1799
|
+
})
|
|
1800
|
+
.strict(),
|
|
1801
|
+
})
|
|
1802
|
+
.strict();
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
export const BrowserFrameStreamAttachment = z.discriminatedUnion("kind", [
|
|
1806
|
+
DirectInteractionFrameStreamAttachment,
|
|
1807
|
+
relayInteractionFrameStreamAttachment(3),
|
|
1808
|
+
]);
|
|
1809
|
+
export type BrowserFrameStreamAttachment = z.infer<typeof BrowserFrameStreamAttachment>;
|
|
1810
|
+
|
|
1811
|
+
export const ComputerFrameStreamAttachment = z.discriminatedUnion("kind", [
|
|
1812
|
+
DirectInteractionFrameStreamAttachment,
|
|
1813
|
+
relayInteractionFrameStreamAttachment(4),
|
|
1814
|
+
]);
|
|
1815
|
+
export type ComputerFrameStreamAttachment = z.infer<typeof ComputerFrameStreamAttachment>;
|
|
1816
|
+
|
|
1817
|
+
export const InteractionFrameStreamAttachment = z.union([
|
|
1818
|
+
BrowserFrameStreamAttachment,
|
|
1819
|
+
ComputerFrameStreamAttachment,
|
|
1820
|
+
]);
|
|
1821
|
+
export type InteractionFrameStreamAttachment = z.infer<typeof InteractionFrameStreamAttachment>;
|
|
1822
|
+
|
|
1823
|
+
export const BrowserSessionAttachment = z
|
|
1824
|
+
.object({
|
|
1825
|
+
browserSessionId: z.string().uuid(),
|
|
1826
|
+
controllerGeneration: opaqueGeneration,
|
|
1827
|
+
targetId: boundedOpaqueId,
|
|
1828
|
+
stream: BrowserFrameStreamAttachment,
|
|
1829
|
+
expiresAt: z.string().datetime({ offset: true }),
|
|
1830
|
+
})
|
|
1831
|
+
.strict();
|
|
1832
|
+
export type BrowserSessionAttachment = z.infer<typeof BrowserSessionAttachment>;
|
|
1833
|
+
|
|
1834
|
+
export const BrowserSessionAttachmentRequest = z
|
|
1835
|
+
.object({
|
|
1836
|
+
targetId: boundedOpaqueId,
|
|
1837
|
+
expiresInSeconds: z.number().int().min(15).max(600).default(120),
|
|
1838
|
+
stream: BrowserFrameStreamOptions.optional(),
|
|
1839
|
+
})
|
|
1840
|
+
.strict();
|
|
1841
|
+
export type BrowserSessionAttachmentRequest = z.infer<typeof BrowserSessionAttachmentRequest>;
|
|
1842
|
+
|
|
1843
|
+
/** Human/API action request. The API supplies resource/controller/actor fields
|
|
1844
|
+
* from authenticated durable authority before dispatching the canonical
|
|
1845
|
+
* BrowserActionCommand to the placement. */
|
|
1846
|
+
export const BrowserActionRequest = z
|
|
1847
|
+
.object({
|
|
1848
|
+
operationId: z.string().uuid(),
|
|
1849
|
+
targetId: boundedOpaqueId,
|
|
1850
|
+
expectedTargetGeneration: opaqueGeneration,
|
|
1851
|
+
expectedDocumentGeneration: opaqueGeneration.nullable(),
|
|
1852
|
+
expectedFrameId: opaqueGeneration.nullable(),
|
|
1853
|
+
action: z.union([BrowserAction, BrowserActionBatch]),
|
|
1854
|
+
})
|
|
1855
|
+
.strict();
|
|
1856
|
+
export type BrowserActionRequest = z.infer<typeof BrowserActionRequest>;
|
|
1857
|
+
|
|
1858
|
+
export const BrowserSessionHeartbeatResponse = z
|
|
1859
|
+
.object({
|
|
1860
|
+
browserSessionId: z.string().uuid(),
|
|
1861
|
+
controllerGeneration: opaqueGeneration,
|
|
1862
|
+
alive: z.literal(true),
|
|
1863
|
+
})
|
|
1864
|
+
.strict();
|
|
1865
|
+
export type BrowserSessionHeartbeatResponse = z.infer<typeof BrowserSessionHeartbeatResponse>;
|
|
1866
|
+
|
|
1867
|
+
export const BrowserActionReceipt = z
|
|
1868
|
+
.object({
|
|
1869
|
+
protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
|
|
1870
|
+
operationId: z.string().uuid(),
|
|
1871
|
+
browserSessionId: z.string().uuid(),
|
|
1872
|
+
controllerGeneration: opaqueGeneration,
|
|
1873
|
+
targetId: boundedOpaqueId,
|
|
1874
|
+
state: InteractionOperationState,
|
|
1875
|
+
dispatchedAt: z.string().datetime({ offset: true }).nullable(),
|
|
1876
|
+
settledAt: z.string().datetime({ offset: true }).nullable(),
|
|
1877
|
+
observation: BrowserObservation.nullable(),
|
|
1878
|
+
error: InteractionError.nullable(),
|
|
1879
|
+
})
|
|
1880
|
+
.strict()
|
|
1881
|
+
.superRefine((receipt, context) => {
|
|
1882
|
+
if (receipt.state === "completed" && (receipt.error !== null || receipt.settledAt === null)) {
|
|
1883
|
+
context.addIssue({
|
|
1884
|
+
code: "custom",
|
|
1885
|
+
message: "completed receipt must settle without error",
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
if (
|
|
1889
|
+
(receipt.state === "failed" || receipt.state === "outcome_unknown") &&
|
|
1890
|
+
(receipt.error === null || receipt.settledAt === null)
|
|
1891
|
+
) {
|
|
1892
|
+
context.addIssue({
|
|
1893
|
+
code: "custom",
|
|
1894
|
+
message: "terminal error receipt requires error and time",
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
if (receipt.state === "dispatched" && receipt.dispatchedAt === null) {
|
|
1898
|
+
context.addIssue({
|
|
1899
|
+
code: "custom",
|
|
1900
|
+
message: "dispatched receipt requires dispatch time",
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1904
|
+
export type BrowserActionReceipt = z.infer<typeof BrowserActionReceipt>;
|
|
1905
|
+
|
|
1906
|
+
export const ComputerSessionCapabilities = z
|
|
1907
|
+
.object({
|
|
1908
|
+
semanticObservation: z.boolean(),
|
|
1909
|
+
appDiscovery: z.boolean(),
|
|
1910
|
+
appLaunch: z.boolean(),
|
|
1911
|
+
windowCapture: z.boolean(),
|
|
1912
|
+
screenCapture: z.boolean(),
|
|
1913
|
+
semanticActions: z.boolean(),
|
|
1914
|
+
pointerInput: z.boolean(),
|
|
1915
|
+
keyboardInput: z.boolean(),
|
|
1916
|
+
backgroundActions: z.boolean(),
|
|
1917
|
+
parallelApps: z.boolean(),
|
|
1918
|
+
})
|
|
1919
|
+
.strict();
|
|
1920
|
+
export type ComputerSessionCapabilities = z.infer<typeof ComputerSessionCapabilities>;
|
|
1921
|
+
|
|
1922
|
+
export const ComputerSession = z
|
|
1923
|
+
.object({
|
|
1924
|
+
id: z.string().uuid(),
|
|
1925
|
+
accountId: z.string().uuid(),
|
|
1926
|
+
workspaceId: z.string().uuid(),
|
|
1927
|
+
name: z.string().trim().min(1).max(200),
|
|
1928
|
+
lifecycle: InteractionLifecycle,
|
|
1929
|
+
placement: InteractionPlacement,
|
|
1930
|
+
controller: InteractionControllerBinding.nullable(),
|
|
1931
|
+
platform: z.enum(["linux", "macos", "windows"]).nullable(),
|
|
1932
|
+
adapter: boundedOpaqueId.nullable(),
|
|
1933
|
+
seatId: boundedOpaqueId.nullable(),
|
|
1934
|
+
displayId: boundedOpaqueId.nullable(),
|
|
1935
|
+
capabilities: ComputerSessionCapabilities.nullable(),
|
|
1936
|
+
associations: z.array(InteractionAssociation).max(1_000),
|
|
1937
|
+
createdBySubjectId: z.string().min(1).max(1_024),
|
|
1938
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
1939
|
+
lastUsedAt: z.string().datetime({ offset: true }),
|
|
1940
|
+
failureCode: boundedOpaqueId.nullable(),
|
|
1941
|
+
})
|
|
1942
|
+
.strict()
|
|
1943
|
+
.superRefine((session, context) => {
|
|
1944
|
+
const nativeBinding = [
|
|
1945
|
+
session.platform,
|
|
1946
|
+
session.adapter,
|
|
1947
|
+
session.seatId,
|
|
1948
|
+
session.displayId,
|
|
1949
|
+
session.capabilities,
|
|
1950
|
+
];
|
|
1951
|
+
const populated = nativeBinding.filter((value) => value !== null).length;
|
|
1952
|
+
if (populated !== 0 && populated !== nativeBinding.length) {
|
|
1953
|
+
context.addIssue({
|
|
1954
|
+
code: "custom",
|
|
1955
|
+
message: "ComputerSession native binding must be absent or complete",
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
if (session.lifecycle === "active" && (session.controller === null || populated === 0)) {
|
|
1959
|
+
context.addIssue({
|
|
1960
|
+
code: "custom",
|
|
1961
|
+
message: "active ComputerSession requires controller and native bindings",
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
});
|
|
1965
|
+
export type ComputerSession = z.infer<typeof ComputerSession>;
|
|
1966
|
+
|
|
1967
|
+
export const ComputerTarget = z
|
|
1968
|
+
.object({
|
|
1969
|
+
id: boundedOpaqueId,
|
|
1970
|
+
computerSessionId: z.string().uuid(),
|
|
1971
|
+
controllerGeneration: opaqueGeneration,
|
|
1972
|
+
targetGeneration: opaqueGeneration,
|
|
1973
|
+
kind: z.enum(["app", "window", "screen"]),
|
|
1974
|
+
applicationId: z.string().max(1_024).nullable(),
|
|
1975
|
+
processId: z.number().int().positive().nullable(),
|
|
1976
|
+
title: z.string().max(4_096),
|
|
1977
|
+
bounds: InteractionRect.nullable(),
|
|
1978
|
+
focused: z.boolean(),
|
|
1979
|
+
})
|
|
1980
|
+
.strict();
|
|
1981
|
+
export type ComputerTarget = z.infer<typeof ComputerTarget>;
|
|
1982
|
+
|
|
1983
|
+
export const ComputerObservation = z
|
|
1984
|
+
.object({
|
|
1985
|
+
protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
|
|
1986
|
+
observationId: boundedOpaqueId,
|
|
1987
|
+
computerSessionId: z.string().uuid(),
|
|
1988
|
+
target: ComputerTarget,
|
|
1989
|
+
frameId: opaqueGeneration.nullable(),
|
|
1990
|
+
semantic: z.union([InteractionSemanticSnapshot, InteractionSemanticDiff]).nullable(),
|
|
1991
|
+
screenshot: RetainedArtifactReferenceSchema.nullable(),
|
|
1992
|
+
focusedRef: boundedOpaqueId.nullable(),
|
|
1993
|
+
changedRegions: z.array(InteractionRect).max(INTERACTION_MAX_CHANGED_NODES),
|
|
1994
|
+
observedAt: z.string().datetime({ offset: true }),
|
|
1995
|
+
})
|
|
1996
|
+
.strict();
|
|
1997
|
+
export type ComputerObservation = z.infer<typeof ComputerObservation>;
|
|
1998
|
+
|
|
1999
|
+
export const ComputerAction = z.discriminatedUnion("type", [
|
|
2000
|
+
z
|
|
2001
|
+
.object({
|
|
2002
|
+
type: z.literal("semantic"),
|
|
2003
|
+
locator: ComputerLocator,
|
|
2004
|
+
action: z.enum([
|
|
2005
|
+
"invoke",
|
|
2006
|
+
"focus",
|
|
2007
|
+
"set_value",
|
|
2008
|
+
"increment",
|
|
2009
|
+
"decrement",
|
|
2010
|
+
"select",
|
|
2011
|
+
"deselect",
|
|
2012
|
+
"expand",
|
|
2013
|
+
"collapse",
|
|
2014
|
+
"show_menu",
|
|
2015
|
+
"scroll_into_view",
|
|
2016
|
+
]),
|
|
2017
|
+
value: z.union([z.string().max(1_000_000), z.number().finite(), z.boolean()]).optional(),
|
|
2018
|
+
})
|
|
2019
|
+
.strict()
|
|
2020
|
+
.superRefine((action, context) => {
|
|
2021
|
+
if ((action.action === "set_value") !== (action.value !== undefined)) {
|
|
2022
|
+
context.addIssue({
|
|
2023
|
+
code: "custom",
|
|
2024
|
+
path: ["value"],
|
|
2025
|
+
message: "value is required exactly for set_value",
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
}),
|
|
2029
|
+
z
|
|
2030
|
+
.object({
|
|
2031
|
+
type: z.literal("pointer"),
|
|
2032
|
+
frameId: opaqueGeneration,
|
|
2033
|
+
action: z.enum(["click", "double_click", "move", "scroll", "drag"]),
|
|
2034
|
+
x: z.number().finite(),
|
|
2035
|
+
y: z.number().finite(),
|
|
2036
|
+
endX: z.number().finite().optional(),
|
|
2037
|
+
endY: z.number().finite().optional(),
|
|
2038
|
+
deltaX: z.number().finite().optional(),
|
|
2039
|
+
deltaY: z.number().finite().optional(),
|
|
2040
|
+
button: z.enum(["left", "right", "middle"]).optional(),
|
|
2041
|
+
})
|
|
2042
|
+
.strict()
|
|
2043
|
+
.superRefine((action, context) => {
|
|
2044
|
+
const hasEnd = action.endX !== undefined || action.endY !== undefined;
|
|
2045
|
+
const hasDelta = action.deltaX !== undefined || action.deltaY !== undefined;
|
|
2046
|
+
if (action.action === "drag" && (action.endX === undefined || action.endY === undefined)) {
|
|
2047
|
+
context.addIssue({
|
|
2048
|
+
code: "custom",
|
|
2049
|
+
message: "pointer drag requires endX and endY",
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
if (action.action !== "drag" && hasEnd) {
|
|
2053
|
+
context.addIssue({
|
|
2054
|
+
code: "custom",
|
|
2055
|
+
message: "pointer end coordinates require drag",
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
if (
|
|
2059
|
+
action.action === "scroll" &&
|
|
2060
|
+
action.deltaX === undefined &&
|
|
2061
|
+
action.deltaY === undefined
|
|
2062
|
+
) {
|
|
2063
|
+
context.addIssue({
|
|
2064
|
+
code: "custom",
|
|
2065
|
+
message: "pointer scroll requires a delta",
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
if (action.action !== "scroll" && hasDelta) {
|
|
2069
|
+
context.addIssue({
|
|
2070
|
+
code: "custom",
|
|
2071
|
+
message: "pointer deltas require scroll",
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
}),
|
|
2075
|
+
z
|
|
2076
|
+
.object({
|
|
2077
|
+
type: z.literal("keyboard"),
|
|
2078
|
+
action: z.enum(["type", "press"]),
|
|
2079
|
+
value: z.string().max(1_000_000),
|
|
2080
|
+
})
|
|
2081
|
+
.strict(),
|
|
2082
|
+
z.object({ type: z.literal("focus"), targetId: boundedOpaqueId }).strict(),
|
|
2083
|
+
z
|
|
2084
|
+
.object({
|
|
2085
|
+
type: z.literal("launch"),
|
|
2086
|
+
applicationId: z.string().min(1).max(1_024),
|
|
2087
|
+
})
|
|
2088
|
+
.strict(),
|
|
2089
|
+
]);
|
|
2090
|
+
export type ComputerAction = z.infer<typeof ComputerAction>;
|
|
2091
|
+
|
|
2092
|
+
export const ComputerActionCommand = z
|
|
2093
|
+
.object({
|
|
2094
|
+
protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
|
|
2095
|
+
operationId: z.string().uuid(),
|
|
2096
|
+
computerSessionId: z.string().uuid(),
|
|
2097
|
+
controllerGeneration: opaqueGeneration,
|
|
2098
|
+
targetId: boundedOpaqueId,
|
|
2099
|
+
expectedTargetGeneration: opaqueGeneration,
|
|
2100
|
+
expectedObservationId: boundedOpaqueId.nullable(),
|
|
2101
|
+
expectedFrameId: opaqueGeneration.nullable(),
|
|
2102
|
+
actor: InteractionActor,
|
|
2103
|
+
action: ComputerAction,
|
|
2104
|
+
})
|
|
2105
|
+
.strict()
|
|
2106
|
+
.superRefine((command, context) => {
|
|
2107
|
+
if (
|
|
2108
|
+
command.action.type === "semantic" &&
|
|
2109
|
+
command.action.locator.kind === "ref" &&
|
|
2110
|
+
command.expectedObservationId === null
|
|
2111
|
+
) {
|
|
2112
|
+
context.addIssue({
|
|
2113
|
+
code: "custom",
|
|
2114
|
+
path: ["expectedObservationId"],
|
|
2115
|
+
message: "semantic ref actions require expectedObservationId",
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
if (command.action.type === "pointer") {
|
|
2119
|
+
if (command.expectedFrameId !== command.action.frameId) {
|
|
2120
|
+
context.addIssue({
|
|
2121
|
+
code: "custom",
|
|
2122
|
+
path: ["expectedFrameId"],
|
|
2123
|
+
message: "pointer action frameId must match expectedFrameId",
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
} else if (command.expectedFrameId !== null) {
|
|
2127
|
+
context.addIssue({
|
|
2128
|
+
code: "custom",
|
|
2129
|
+
path: ["expectedFrameId"],
|
|
2130
|
+
message: "expectedFrameId is only valid for pointer actions",
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
});
|
|
2134
|
+
export type ComputerActionCommand = z.infer<typeof ComputerActionCommand>;
|
|
2135
|
+
|
|
2136
|
+
export const ComputerActionReceipt = z
|
|
2137
|
+
.object({
|
|
2138
|
+
protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
|
|
2139
|
+
operationId: z.string().uuid(),
|
|
2140
|
+
computerSessionId: z.string().uuid(),
|
|
2141
|
+
controllerGeneration: opaqueGeneration,
|
|
2142
|
+
targetId: boundedOpaqueId,
|
|
2143
|
+
state: InteractionOperationState,
|
|
2144
|
+
dispatchedAt: z.string().datetime({ offset: true }).nullable(),
|
|
2145
|
+
settledAt: z.string().datetime({ offset: true }).nullable(),
|
|
2146
|
+
observation: ComputerObservation.nullable(),
|
|
2147
|
+
error: InteractionError.nullable(),
|
|
2148
|
+
})
|
|
2149
|
+
.strict()
|
|
2150
|
+
.superRefine((receipt, context) => {
|
|
2151
|
+
if (receipt.state === "completed" && (receipt.error !== null || receipt.settledAt === null)) {
|
|
2152
|
+
context.addIssue({
|
|
2153
|
+
code: "custom",
|
|
2154
|
+
message: "completed receipt must settle without error",
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
if (
|
|
2158
|
+
(receipt.state === "failed" || receipt.state === "outcome_unknown") &&
|
|
2159
|
+
(receipt.error === null || receipt.settledAt === null)
|
|
2160
|
+
) {
|
|
2161
|
+
context.addIssue({
|
|
2162
|
+
code: "custom",
|
|
2163
|
+
message: "terminal error receipt requires error and time",
|
|
2164
|
+
});
|
|
2165
|
+
}
|
|
2166
|
+
if (receipt.state === "dispatched" && receipt.dispatchedAt === null) {
|
|
2167
|
+
context.addIssue({
|
|
2168
|
+
code: "custom",
|
|
2169
|
+
message: "dispatched receipt requires dispatch time",
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
});
|
|
2173
|
+
export type ComputerActionReceipt = z.infer<typeof ComputerActionReceipt>;
|
|
2174
|
+
|
|
2175
|
+
/** Public ComputerSession creation request. Placement chooses the concrete
|
|
2176
|
+
* platform adapter and allocates its default physical or isolated virtual seat;
|
|
2177
|
+
* callers may discover and reuse an existing ComputerSession instead. */
|
|
2178
|
+
export const CreateComputerSessionRequest = z
|
|
2179
|
+
.object({
|
|
2180
|
+
operationId: z.string().uuid(),
|
|
2181
|
+
sessionId: z.string().uuid(),
|
|
2182
|
+
name: z.string().trim().min(1).max(200).optional(),
|
|
2183
|
+
placement: InteractionPlacement.optional(),
|
|
2184
|
+
})
|
|
2185
|
+
.strict();
|
|
2186
|
+
export type CreateComputerSessionRequest = z.infer<typeof CreateComputerSessionRequest>;
|
|
2187
|
+
|
|
2188
|
+
export const ComputerSessionListResponse = z
|
|
2189
|
+
.object({
|
|
2190
|
+
revision: z.number().int().nonnegative(),
|
|
2191
|
+
sessions: z.array(ComputerSession).max(10_000),
|
|
2192
|
+
})
|
|
2193
|
+
.strict();
|
|
2194
|
+
export type ComputerSessionListResponse = z.infer<typeof ComputerSessionListResponse>;
|
|
2195
|
+
|
|
2196
|
+
export const ComputerSessionMutationResponse = z
|
|
2197
|
+
.object({
|
|
2198
|
+
session: ComputerSession,
|
|
2199
|
+
operation: InteractionLifecycleOperationReceipt,
|
|
2200
|
+
})
|
|
2201
|
+
.strict();
|
|
2202
|
+
export type ComputerSessionMutationResponse = z.infer<typeof ComputerSessionMutationResponse>;
|
|
2203
|
+
|
|
2204
|
+
export const ComputerSessionLifecycleRequest = z
|
|
2205
|
+
.object({
|
|
2206
|
+
operationId: z.string().uuid(),
|
|
2207
|
+
})
|
|
2208
|
+
.strict();
|
|
2209
|
+
export type ComputerSessionLifecycleRequest = z.infer<typeof ComputerSessionLifecycleRequest>;
|
|
2210
|
+
|
|
2211
|
+
export const ComputerTargetListResponse = z
|
|
2212
|
+
.object({
|
|
2213
|
+
computerSessionId: z.string().uuid(),
|
|
2214
|
+
controllerGeneration: opaqueGeneration,
|
|
2215
|
+
targets: z.array(ComputerTarget).max(10_000),
|
|
2216
|
+
})
|
|
2217
|
+
.strict();
|
|
2218
|
+
export type ComputerTargetListResponse = z.infer<typeof ComputerTargetListResponse>;
|
|
2219
|
+
|
|
2220
|
+
export const ComputerFrameStreamOptions = InteractionFrameStreamOptions;
|
|
2221
|
+
export type ComputerFrameStreamOptions = z.infer<typeof ComputerFrameStreamOptions>;
|
|
2222
|
+
|
|
2223
|
+
export const ComputerSessionAttachment = z
|
|
2224
|
+
.object({
|
|
2225
|
+
computerSessionId: z.string().uuid(),
|
|
2226
|
+
controllerGeneration: opaqueGeneration,
|
|
2227
|
+
targetId: boundedOpaqueId,
|
|
2228
|
+
stream: ComputerFrameStreamAttachment,
|
|
2229
|
+
expiresAt: z.string().datetime({ offset: true }),
|
|
2230
|
+
})
|
|
2231
|
+
.strict();
|
|
2232
|
+
export type ComputerSessionAttachment = z.infer<typeof ComputerSessionAttachment>;
|
|
2233
|
+
|
|
2234
|
+
export const ComputerSessionAttachmentRequest = z
|
|
2235
|
+
.object({
|
|
2236
|
+
targetId: boundedOpaqueId,
|
|
2237
|
+
expiresInSeconds: z.number().int().min(15).max(600).default(120),
|
|
2238
|
+
stream: ComputerFrameStreamOptions.optional(),
|
|
2239
|
+
})
|
|
2240
|
+
.strict();
|
|
2241
|
+
export type ComputerSessionAttachmentRequest = z.infer<typeof ComputerSessionAttachmentRequest>;
|
|
2242
|
+
|
|
2243
|
+
/** Human/API action request. Authenticated durable authority supplies the
|
|
2244
|
+
* resource, controller and actor fences before controller dispatch. */
|
|
2245
|
+
export const ComputerActionRequest = z
|
|
2246
|
+
.object({
|
|
2247
|
+
operationId: z.string().uuid(),
|
|
2248
|
+
targetId: boundedOpaqueId,
|
|
2249
|
+
expectedTargetGeneration: opaqueGeneration,
|
|
2250
|
+
expectedObservationId: boundedOpaqueId.nullable(),
|
|
2251
|
+
expectedFrameId: opaqueGeneration.nullable(),
|
|
2252
|
+
action: ComputerAction,
|
|
2253
|
+
})
|
|
2254
|
+
.strict()
|
|
2255
|
+
.superRefine((request, context) => {
|
|
2256
|
+
if (
|
|
2257
|
+
request.action.type === "semantic" &&
|
|
2258
|
+
request.action.locator.kind === "ref" &&
|
|
2259
|
+
request.expectedObservationId === null
|
|
2260
|
+
) {
|
|
2261
|
+
context.addIssue({
|
|
2262
|
+
code: "custom",
|
|
2263
|
+
path: ["expectedObservationId"],
|
|
2264
|
+
message: "semantic ref actions require expectedObservationId",
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
if (request.action.type === "pointer") {
|
|
2268
|
+
if (request.expectedFrameId !== request.action.frameId) {
|
|
2269
|
+
context.addIssue({
|
|
2270
|
+
code: "custom",
|
|
2271
|
+
path: ["expectedFrameId"],
|
|
2272
|
+
message: "pointer action frameId must match expectedFrameId",
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
} else if (request.expectedFrameId !== null) {
|
|
2276
|
+
context.addIssue({
|
|
2277
|
+
code: "custom",
|
|
2278
|
+
path: ["expectedFrameId"],
|
|
2279
|
+
message: "expectedFrameId is only valid for pointer actions",
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
export type ComputerActionRequest = z.infer<typeof ComputerActionRequest>;
|
|
2284
|
+
|
|
2285
|
+
export const ComputerSessionHeartbeatResponse = z
|
|
2286
|
+
.object({
|
|
2287
|
+
computerSessionId: z.string().uuid(),
|
|
2288
|
+
controllerGeneration: opaqueGeneration,
|
|
2289
|
+
alive: z.literal(true),
|
|
2290
|
+
})
|
|
2291
|
+
.strict();
|
|
2292
|
+
export type ComputerSessionHeartbeatResponse = z.infer<typeof ComputerSessionHeartbeatResponse>;
|