@canonmsg/agent-sdk 12.0.0 → 12.1.0
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/README.md +97 -7
- package/dist/chunks/{chunk-FPQFTTND.js → chunk-7N6OPPW4.js} +1616 -1085
- package/dist/chunks/chunk-N722GWCP.js +4897 -0
- package/dist/chunks/{chunk-OLJ7H4E2.js → chunk-QZ4PPRQW.js} +6462 -6920
- package/dist/chunks/{mls-crypto-NZDNSPAO.js → mls-crypto-LMNQZOS6.js} +3 -3
- package/dist/cli.js +403 -29
- package/dist/drivers/claude.d.ts +8 -2
- package/dist/drivers/codex.d.ts +11 -2
- package/dist/drivers/connector.d.ts +34 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +26 -4
- package/dist/internal/backend-contracts/artifactActions.d.ts +0 -151
- package/dist/internal/backend-contracts/clientFloor.d.ts +41 -0
- package/dist/internal/backend-contracts/communication.d.ts +15 -0
- package/dist/internal/backend-contracts/connectorRelay.d.ts +179 -0
- package/dist/internal/backend-contracts/endpoint.d.ts +17 -0
- package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +15 -4
- package/dist/internal/backend-contracts/index.d.ts +7 -1
- package/dist/internal/backend-contracts/media-url.d.ts +14 -0
- package/dist/internal/backend-contracts/message.d.ts +1 -1
- package/dist/internal/backend-contracts/mlsDevApplication.d.ts +90 -2
- package/dist/internal/backend-contracts/mlsDevCredential.d.ts +5 -2
- package/dist/internal/backend-contracts/mlsDevDeviceCustody.d.ts +2 -1
- package/dist/internal/backend-contracts/mlsDevEnrollment.d.ts +3 -2
- package/dist/internal/backend-contracts/mlsDevFeedReceipt.d.ts +43 -0
- package/dist/internal/backend-contracts/mlsDevGroup.d.ts +129 -5
- package/dist/internal/backend-contracts/mlsDevGroupFeed.d.ts +6 -2
- package/dist/internal/backend-contracts/mlsDevInteraction.d.ts +159 -0
- package/dist/internal/backend-contracts/mlsEnvironment.d.ts +35 -0
- package/dist/internal/backend-contracts/mlsPrincipalAlias.d.ts +23 -0
- package/dist/internal/backend-contracts/runtimeCardFields.d.ts +2 -2
- package/dist/internal/backend-contracts/upload-mime-types.d.ts +17 -0
- package/dist/internal/backend-contracts/verbContract.d.ts +15 -7
- package/dist/internal/backend-contracts/verbSchemas.d.ts +35 -25
- package/dist/internal/core/agent-profiles.d.ts +0 -4
- package/dist/internal/core/approval-format.d.ts +1 -5
- package/dist/internal/core/approval-types.d.ts +1 -2
- package/dist/internal/core/client.d.ts +2 -56
- package/dist/internal/core/constants.d.ts +0 -3
- package/dist/internal/core/endpoint-client.d.ts +14 -1
- package/dist/internal/core/execution-environment-mode.d.ts +0 -2
- package/dist/internal/core/gifs.d.ts +0 -6
- package/dist/internal/core/host-runtime.d.ts +2 -206
- package/dist/internal/core/index.d.ts +21 -60
- package/dist/internal/core/registration.d.ts +1 -17
- package/dist/internal/core/retry.d.ts +0 -9
- package/dist/internal/core/rtdb-rest.d.ts +0 -4
- package/dist/internal/core/runtime-cards.d.ts +0 -45
- package/dist/internal/core/runtime-descriptor.d.ts +1 -60
- package/dist/internal/core/runtime-presentation.d.ts +1 -27
- package/dist/internal/core/runtime-request-manager.d.ts +0 -1
- package/dist/internal/core/self-context.d.ts +0 -39
- package/dist/internal/core/silent-turn.d.ts +1 -38
- package/dist/internal/core/stream.d.ts +2 -1
- package/dist/internal/core/turn-protocol.d.ts +0 -5
- package/dist/internal/core/turn-verbosity.d.ts +0 -12
- package/dist/internal/core/types.d.ts +0 -111
- package/dist/internal/endpoint/engine.d.ts +31 -1
- package/dist/internal/endpoint/index.d.ts +1 -1
- package/dist/internal/endpoint/media.d.ts +5 -0
- package/dist/internal/endpoint/mls-crypto.d.ts +12 -5
- package/dist/internal/endpoint/mls-dev-client.d.ts +9 -1
- package/dist/internal/endpoint/mls-dev-device.d.ts +2 -0
- package/dist/internal/endpoint/mls-dev-groups.d.ts +65 -5
- package/dist/internal/endpoint/mls-dev-interactions.d.ts +99 -0
- package/dist/internal/endpoint/mls-dev-key-package-service.d.ts +13 -0
- package/dist/internal/endpoint/mls-dev-key-packages.d.ts +23 -3
- package/dist/internal/endpoint/mls-dev-text.d.ts +67 -0
- package/dist/internal/endpoint/mls-dev-transport.d.ts +2 -1
- package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +5 -0
- package/dist/internal/endpoint/transport.d.ts +6 -1
- package/dist/media.js +2 -2
- package/dist/runner/errands.d.ts +118 -0
- package/dist/runner/mls.d.ts +97 -0
- package/dist/runner/relay.d.ts +87 -0
- package/dist/runner/runner.d.ts +61 -0
- package/dist/runner/types.d.ts +51 -4
- package/package.json +1 -1
- package/dist/chunks/chunk-6VK4UZHJ.js +0 -2806
- package/dist/internal/backend-contracts/workSessions.d.ts +0 -205
- package/dist/internal/core/agent-resolver.d.ts +0 -49
- package/dist/internal/core/agent-session.d.ts +0 -12
- package/dist/internal/core/artifacts.d.ts +0 -262
- package/dist/internal/core/attached-native-session.d.ts +0 -174
- package/dist/internal/core/attached-session-file-store.d.ts +0 -3
- package/dist/internal/core/control-poller.d.ts +0 -221
- package/dist/internal/core/exclusive-json-file-store.d.ts +0 -7
- package/dist/internal/core/execution-environment.d.ts +0 -55
- package/dist/internal/core/group-context.d.ts +0 -25
- package/dist/internal/core/inbound-frame.d.ts +0 -64
- package/dist/internal/core/local-runtime-catalog.d.ts +0 -131
- package/dist/internal/core/message-format.d.ts +0 -13
- package/dist/internal/core/native-session-interaction.d.ts +0 -44
- package/dist/internal/core/provenance.d.ts +0 -31
- package/dist/internal/core/registration-cli.d.ts +0 -40
- package/dist/internal/core/turn-output-controller.d.ts +0 -69
- package/dist/internal/core/typing-status-publisher.d.ts +0 -14
- package/dist/internal/core/work-session-provider.d.ts +0 -29
- package/dist/internal/core/workspace-discovery.d.ts +0 -17
|
@@ -27,6 +27,22 @@ function getCanonEnvironmentContract(value) {
|
|
|
27
27
|
}
|
|
28
28
|
__name(getCanonEnvironmentContract, "getCanonEnvironmentContract");
|
|
29
29
|
|
|
30
|
+
// ../backend-contracts/src/clientFloor.ts
|
|
31
|
+
var CANON_CLIENT_HEADER = "X-Canon-Client";
|
|
32
|
+
var CLIENT_UPDATE_REQUIRED = "CLIENT_UPDATE_REQUIRED";
|
|
33
|
+
var CANON_CLIENT_KINDS = ["app-ios", "app-android", "web", "runner", "hermes", "core", "connector"];
|
|
34
|
+
var VERSION = /^(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})\.(0|[1-9]\d{0,5})$/;
|
|
35
|
+
var isKind = /* @__PURE__ */ __name((value) => CANON_CLIENT_KINDS.includes(value), "isKind");
|
|
36
|
+
function isCanonClientVersion(value) {
|
|
37
|
+
return typeof value === "string" && VERSION.test(value);
|
|
38
|
+
}
|
|
39
|
+
__name(isCanonClientVersion, "isCanonClientVersion");
|
|
40
|
+
function formatCanonClientHeader(client) {
|
|
41
|
+
if (!isKind(client.kind) || !isCanonClientVersion(client.version)) throw new Error("Invalid Canon client identity");
|
|
42
|
+
return `${client.kind}/${client.version}`;
|
|
43
|
+
}
|
|
44
|
+
__name(formatCanonClientHeader, "formatCanonClientHeader");
|
|
45
|
+
|
|
30
46
|
// ../backend-contracts/src/media.ts
|
|
31
47
|
function sanitizeDownloadFileName(value, fallback = "attachment") {
|
|
32
48
|
const baseName = typeof value === "string" ? value.trim().split(/[\\/]/).filter(Boolean).pop() ?? "" : "";
|
|
@@ -45,6 +61,193 @@ function sanitizeDownloadFileName(value, fallback = "attachment") {
|
|
|
45
61
|
return `${safeStem.replace(/[. -]+$/g, "")}${extension}`;
|
|
46
62
|
}
|
|
47
63
|
__name(sanitizeDownloadFileName, "sanitizeDownloadFileName");
|
|
64
|
+
function describeAttachment(attachment) {
|
|
65
|
+
if (!attachment) return null;
|
|
66
|
+
if (attachment.kind === "image") return "\u{1F4F7} Image";
|
|
67
|
+
if (attachment.kind === "audio") return "\u{1F3A4} Voice message";
|
|
68
|
+
if (attachment.kind === "video") return "\u{1F3AC} Video";
|
|
69
|
+
return attachment.fileName ? `\u{1F4CE} ${attachment.fileName}` : "\u{1F4CE} File";
|
|
70
|
+
}
|
|
71
|
+
__name(describeAttachment, "describeAttachment");
|
|
72
|
+
|
|
73
|
+
// ../backend-contracts/src/media-origins.generated.ts
|
|
74
|
+
var MEDIA_ORIGINS = {
|
|
75
|
+
"canonmail-dev.firebasestorage.app": "https://dev.api.canonmail.com",
|
|
76
|
+
"canonmail-prod.firebasestorage.app": "https://api.canonmail.com"
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// ../backend-contracts/src/media-url.ts
|
|
80
|
+
function validMediaPath(path) {
|
|
81
|
+
const parts = path.split("/");
|
|
82
|
+
return parts.length === 4 && parts[0] === "media" && parts.every((part) => Boolean(part) && part !== "." && part !== ".." && !/[\u0000-\u001f\u007f\\]/.test(part));
|
|
83
|
+
}
|
|
84
|
+
__name(validMediaPath, "validMediaPath");
|
|
85
|
+
function parseMediaStoragePath(value, bucket) {
|
|
86
|
+
if (typeof value !== "string") return null;
|
|
87
|
+
try {
|
|
88
|
+
const url = new URL(value);
|
|
89
|
+
if (url.protocol !== "https:" || url.username || url.password) return null;
|
|
90
|
+
let path;
|
|
91
|
+
const prefix = `/v0/b/${bucket}/o/`;
|
|
92
|
+
if (url.origin === "https://firebasestorage.googleapis.com" && url.pathname.startsWith(prefix)) {
|
|
93
|
+
path = decodeURIComponent(url.pathname.slice(prefix.length));
|
|
94
|
+
} else if (url.origin === MEDIA_ORIGINS[bucket] && url.pathname.startsWith("/files/")) {
|
|
95
|
+
path = `media/${decodeURIComponent(url.pathname.slice("/files/".length))}`;
|
|
96
|
+
} else return null;
|
|
97
|
+
return validMediaPath(path) ? path : null;
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
__name(parseMediaStoragePath, "parseMediaStoragePath");
|
|
103
|
+
function isCanonMediaOrigin(url) {
|
|
104
|
+
try {
|
|
105
|
+
return Object.values(MEDIA_ORIGINS).includes(new URL(url).origin);
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
__name(isCanonMediaOrigin, "isCanonMediaOrigin");
|
|
111
|
+
function isCanonUploadUrl(url, input) {
|
|
112
|
+
try {
|
|
113
|
+
const bucket = `${getCanonEnvironmentContract(input.environmentId).projectId}.firebasestorage.app`;
|
|
114
|
+
const parsed = new URL(url);
|
|
115
|
+
if (parsed.origin !== MEDIA_ORIGINS[bucket] || parsed.hash || [...parsed.searchParams.keys()].join() !== "token" || !parsed.searchParams.get("token")) return false;
|
|
116
|
+
const path = parseMediaStoragePath(url, bucket);
|
|
117
|
+
const [, conversationId, , file] = path?.split("/") ?? [];
|
|
118
|
+
const expected = input.poster ? `resumable-${input.uploadId}-poster.jpg` : null;
|
|
119
|
+
return conversationId === input.conversationId && (expected ? file === expected : !!file && file.startsWith(`resumable-${input.uploadId}.`) && /^[a-z0-9]{1,8}$/.test(file.slice(`resumable-${input.uploadId}.`.length)));
|
|
120
|
+
} catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
__name(isCanonUploadUrl, "isCanonUploadUrl");
|
|
125
|
+
|
|
126
|
+
// ../backend-contracts/src/upload-mime-types.ts
|
|
127
|
+
var MIME_TYPE_RE = /^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i;
|
|
128
|
+
var SAFE_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
129
|
+
"image/jpeg",
|
|
130
|
+
"image/png",
|
|
131
|
+
"image/webp",
|
|
132
|
+
"image/gif"
|
|
133
|
+
]);
|
|
134
|
+
var SAFE_AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
135
|
+
"audio/mp4",
|
|
136
|
+
"audio/x-m4a",
|
|
137
|
+
"audio/mpeg",
|
|
138
|
+
"audio/webm",
|
|
139
|
+
"audio/ogg",
|
|
140
|
+
"audio/wav",
|
|
141
|
+
// ADTS AAC — the future Android streamable-memo lane; harmless standalone.
|
|
142
|
+
"audio/aac"
|
|
143
|
+
]);
|
|
144
|
+
var SAFE_VIDEO_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
145
|
+
"video/mp4",
|
|
146
|
+
"video/quicktime",
|
|
147
|
+
"video/webm",
|
|
148
|
+
"video/x-m4v"
|
|
149
|
+
]);
|
|
150
|
+
var SAFE_FILE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
151
|
+
"application/pdf",
|
|
152
|
+
"text/plain",
|
|
153
|
+
"text/markdown",
|
|
154
|
+
"text/x-markdown",
|
|
155
|
+
"text/csv",
|
|
156
|
+
"application/csv",
|
|
157
|
+
"application/json",
|
|
158
|
+
"application/zip",
|
|
159
|
+
"application/vnd.ms-excel",
|
|
160
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
161
|
+
]);
|
|
162
|
+
var SAFE_FILE_MIME_BY_EXTENSION = {
|
|
163
|
+
csv: "text/csv",
|
|
164
|
+
json: "application/json",
|
|
165
|
+
md: "text/markdown",
|
|
166
|
+
markdown: "text/markdown",
|
|
167
|
+
pdf: "application/pdf",
|
|
168
|
+
txt: "text/plain",
|
|
169
|
+
xls: "application/vnd.ms-excel",
|
|
170
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
171
|
+
zip: "application/zip"
|
|
172
|
+
};
|
|
173
|
+
var SAFE_VIDEO_MIME_BY_EXTENSION = {
|
|
174
|
+
m4v: "video/x-m4v",
|
|
175
|
+
mov: "video/quicktime",
|
|
176
|
+
mp4: "video/mp4",
|
|
177
|
+
webm: "video/webm"
|
|
178
|
+
};
|
|
179
|
+
var SAFE_AUDIO_MIME_BY_EXTENSION = {
|
|
180
|
+
aac: "audio/aac",
|
|
181
|
+
m4a: "audio/x-m4a",
|
|
182
|
+
mp3: "audio/mpeg",
|
|
183
|
+
oga: "audio/ogg",
|
|
184
|
+
ogg: "audio/ogg",
|
|
185
|
+
wav: "audio/wav"
|
|
186
|
+
};
|
|
187
|
+
function normalizeMimeType(value) {
|
|
188
|
+
if (typeof value !== "string") return null;
|
|
189
|
+
const normalized = value.split(";")[0].trim().toLowerCase();
|
|
190
|
+
if (!MIME_TYPE_RE.test(normalized)) return null;
|
|
191
|
+
if (normalized === "image/jpg") return "image/jpeg";
|
|
192
|
+
return normalized;
|
|
193
|
+
}
|
|
194
|
+
__name(normalizeMimeType, "normalizeMimeType");
|
|
195
|
+
function inferSafeFileMimeTypeFromName(value) {
|
|
196
|
+
if (typeof value !== "string") return null;
|
|
197
|
+
const extension = value.trim().split(/[\\/]/).pop()?.split(".").pop()?.toLowerCase() ?? "";
|
|
198
|
+
return SAFE_FILE_MIME_BY_EXTENSION[extension] ?? null;
|
|
199
|
+
}
|
|
200
|
+
__name(inferSafeFileMimeTypeFromName, "inferSafeFileMimeTypeFromName");
|
|
201
|
+
function inferSafeVideoMimeTypeFromName(value) {
|
|
202
|
+
if (typeof value !== "string") return null;
|
|
203
|
+
const extension = value.trim().split(/[\\/]/).pop()?.split(".").pop()?.toLowerCase() ?? "";
|
|
204
|
+
return SAFE_VIDEO_MIME_BY_EXTENSION[extension] ?? null;
|
|
205
|
+
}
|
|
206
|
+
__name(inferSafeVideoMimeTypeFromName, "inferSafeVideoMimeTypeFromName");
|
|
207
|
+
function inferSafeAudioMimeTypeFromName(value) {
|
|
208
|
+
if (typeof value !== "string") return null;
|
|
209
|
+
const extension = value.trim().split(/[\\/]/).pop()?.split(".").pop()?.toLowerCase() ?? "";
|
|
210
|
+
return SAFE_AUDIO_MIME_BY_EXTENSION[extension] ?? null;
|
|
211
|
+
}
|
|
212
|
+
__name(inferSafeAudioMimeTypeFromName, "inferSafeAudioMimeTypeFromName");
|
|
213
|
+
function shouldTrustSafeFileName(mimeType) {
|
|
214
|
+
return !mimeType || mimeType === "application/octet-stream" || mimeType === "application/vnd.ms-excel" || mimeType === "text/plain";
|
|
215
|
+
}
|
|
216
|
+
__name(shouldTrustSafeFileName, "shouldTrustSafeFileName");
|
|
217
|
+
function classifyAllowedUploadMimeType(value) {
|
|
218
|
+
const mimeType = normalizeMimeType(value);
|
|
219
|
+
if (!mimeType) return null;
|
|
220
|
+
if (SAFE_IMAGE_MIME_TYPES.has(mimeType)) return "image";
|
|
221
|
+
if (SAFE_AUDIO_MIME_TYPES.has(mimeType)) return "audio";
|
|
222
|
+
if (SAFE_VIDEO_MIME_TYPES.has(mimeType)) return "video";
|
|
223
|
+
if (SAFE_FILE_MIME_TYPES.has(mimeType)) return "file";
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
__name(classifyAllowedUploadMimeType, "classifyAllowedUploadMimeType");
|
|
227
|
+
function resolveAllowedUploadMimeType(input) {
|
|
228
|
+
const normalizedMimeType = normalizeMimeType(input.mimeType);
|
|
229
|
+
const hasExplicitMimeType = typeof input.mimeType === "string" && input.mimeType.trim().length > 0;
|
|
230
|
+
const inferredVideoMimeType = inferSafeVideoMimeTypeFromName(input.fileName);
|
|
231
|
+
const inferredAudioMimeType = inferSafeAudioMimeTypeFromName(input.fileName);
|
|
232
|
+
const inferredFileMimeType = inferSafeFileMimeTypeFromName(input.fileName);
|
|
233
|
+
if (hasExplicitMimeType && !normalizedMimeType) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
if (inferredVideoMimeType && (!normalizedMimeType || normalizedMimeType === "application/octet-stream")) {
|
|
237
|
+
return inferredVideoMimeType;
|
|
238
|
+
}
|
|
239
|
+
if (inferredAudioMimeType && (!normalizedMimeType || normalizedMimeType === "application/octet-stream")) {
|
|
240
|
+
return inferredAudioMimeType;
|
|
241
|
+
}
|
|
242
|
+
if (inferredFileMimeType && shouldTrustSafeFileName(normalizedMimeType)) {
|
|
243
|
+
return inferredFileMimeType;
|
|
244
|
+
}
|
|
245
|
+
if (normalizedMimeType && classifyAllowedUploadMimeType(normalizedMimeType)) {
|
|
246
|
+
return normalizedMimeType;
|
|
247
|
+
}
|
|
248
|
+
return normalizedMimeType ? null : inferredFileMimeType;
|
|
249
|
+
}
|
|
250
|
+
__name(resolveAllowedUploadMimeType, "resolveAllowedUploadMimeType");
|
|
48
251
|
|
|
49
252
|
// ../backend-contracts/src/mlsDevValidation.ts
|
|
50
253
|
function invalid() {
|
|
@@ -57,14 +260,14 @@ function readMlsDataRecord(value, keys2, alternateKeys) {
|
|
|
57
260
|
if (prototype !== Object.prototype && prototype !== null) invalid();
|
|
58
261
|
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
59
262
|
const ownKeys = Reflect.ownKeys(descriptors);
|
|
60
|
-
const shape = [keys2, ...alternateKeys ? [alternateKeys] : []].find((candidate) => ownKeys.length === candidate.length && ownKeys.every((
|
|
263
|
+
const shape = [keys2, ...alternateKeys ? [alternateKeys] : []].find((candidate) => ownKeys.length === candidate.length && ownKeys.every((key) => typeof key === "string" && candidate.includes(key)));
|
|
61
264
|
if (!shape) invalid();
|
|
62
265
|
const snapshot = /* @__PURE__ */ Object.create(null);
|
|
63
|
-
for (const
|
|
64
|
-
if (typeof
|
|
65
|
-
const descriptor = descriptors[
|
|
266
|
+
for (const key of ownKeys) {
|
|
267
|
+
if (typeof key !== "string") invalid();
|
|
268
|
+
const descriptor = descriptors[key];
|
|
66
269
|
if (!descriptor.enumerable || !Object.hasOwn(descriptor, "value")) invalid();
|
|
67
|
-
snapshot[
|
|
270
|
+
snapshot[key] = descriptor.value;
|
|
68
271
|
}
|
|
69
272
|
return snapshot;
|
|
70
273
|
}
|
|
@@ -85,6 +288,51 @@ function requireMlsUuid(value) {
|
|
|
85
288
|
}
|
|
86
289
|
__name(requireMlsUuid, "requireMlsUuid");
|
|
87
290
|
|
|
291
|
+
// ../backend-contracts/src/mlsEnvironment.ts
|
|
292
|
+
var MLS_ENVIRONMENT_PROFILES = Object.freeze({
|
|
293
|
+
"canon-dev-v1": Object.freeze({
|
|
294
|
+
issuer: "canon-mls-dev-v1",
|
|
295
|
+
credentialType: "canon-mls-dev-credential+jws",
|
|
296
|
+
issuerKeyPath: "projects/canonmail-dev/locations/europe-west1/keyRings/canon-mls/cryptoKeys/device-issuer",
|
|
297
|
+
receiptKeyPath: "projects/canonmail-dev/locations/europe-west1/keyRings/canon-mls/cryptoKeys/feed-receipt"
|
|
298
|
+
}),
|
|
299
|
+
"canon-prod-v1": Object.freeze({
|
|
300
|
+
issuer: "canon-mls-prod-v1",
|
|
301
|
+
credentialType: "canon-mls-prod-credential+jws",
|
|
302
|
+
issuerKeyPath: "projects/canonmail-prod/locations/europe-west1/keyRings/canon-mls/cryptoKeys/device-issuer",
|
|
303
|
+
receiptKeyPath: "projects/canonmail-prod/locations/europe-west1/keyRings/canon-mls/cryptoKeys/feed-receipt"
|
|
304
|
+
})
|
|
305
|
+
});
|
|
306
|
+
function requireMlsEnvironmentId(value) {
|
|
307
|
+
if (!isMlsEnvironmentId(value)) throw new Error("INVALID_MLS_DATA");
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
__name(requireMlsEnvironmentId, "requireMlsEnvironmentId");
|
|
311
|
+
function isMlsEnvironmentId(value) {
|
|
312
|
+
return typeof value === "string" && Object.hasOwn(MLS_ENVIRONMENT_PROFILES, value);
|
|
313
|
+
}
|
|
314
|
+
__name(isMlsEnvironmentId, "isMlsEnvironmentId");
|
|
315
|
+
function mlsEnvironmentProfile(environmentId) {
|
|
316
|
+
return MLS_ENVIRONMENT_PROFILES[environmentId];
|
|
317
|
+
}
|
|
318
|
+
__name(mlsEnvironmentProfile, "mlsEnvironmentProfile");
|
|
319
|
+
function mlsEnvironmentOfIssuerKeyVersion(value) {
|
|
320
|
+
return environmentOfKeyVersion(value, "issuerKeyPath");
|
|
321
|
+
}
|
|
322
|
+
__name(mlsEnvironmentOfIssuerKeyVersion, "mlsEnvironmentOfIssuerKeyVersion");
|
|
323
|
+
function mlsEnvironmentOfReceiptKeyVersion(value) {
|
|
324
|
+
return environmentOfKeyVersion(value, "receiptKeyPath");
|
|
325
|
+
}
|
|
326
|
+
__name(mlsEnvironmentOfReceiptKeyVersion, "mlsEnvironmentOfReceiptKeyVersion");
|
|
327
|
+
function environmentOfKeyVersion(value, key) {
|
|
328
|
+
for (const [environmentId, profile] of Object.entries(MLS_ENVIRONMENT_PROFILES)) {
|
|
329
|
+
const prefix = profile[key] + "/cryptoKeyVersions/";
|
|
330
|
+
if (value.startsWith(prefix) && /^[1-9][0-9]{0,9}$/.test(value.slice(prefix.length))) return environmentId;
|
|
331
|
+
}
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
__name(environmentOfKeyVersion, "environmentOfKeyVersion");
|
|
335
|
+
|
|
88
336
|
// ../backend-contracts/src/mlsDevEnrollment.ts
|
|
89
337
|
var MLS_DEV_ENROLLMENT_CHALLENGE_TTL_MS = 3e5;
|
|
90
338
|
var MLS_DEV_ENROLLMENT_MAX_BYTES = 4096;
|
|
@@ -209,7 +457,7 @@ function parseMlsDevEnrollmentChallenge(value) {
|
|
|
209
457
|
version: requireMlsLiteral(input.version, 1),
|
|
210
458
|
challengeId: requireMlsUuid(input.challengeId),
|
|
211
459
|
deviceId: requireMlsUuid(input.deviceId),
|
|
212
|
-
environmentId:
|
|
460
|
+
environmentId: requireMlsEnvironmentId(input.environmentId),
|
|
213
461
|
principal: principal(input.principal),
|
|
214
462
|
installationId: installation(input.installationId),
|
|
215
463
|
ciphersuite: requireMlsLiteral(input.ciphersuite, 1),
|
|
@@ -242,7 +490,7 @@ function parseMlsDevEnrollmentResult(value) {
|
|
|
242
490
|
device: Object.freeze({
|
|
243
491
|
version: requireMlsLiteral(device.version, 1),
|
|
244
492
|
deviceId: requireMlsUuid(device.deviceId),
|
|
245
|
-
environmentId:
|
|
493
|
+
environmentId: requireMlsEnvironmentId(device.environmentId),
|
|
246
494
|
principal: principal(device.principal),
|
|
247
495
|
installationId: installation(device.installationId),
|
|
248
496
|
ciphersuite: requireMlsLiteral(device.ciphersuite, 1),
|
|
@@ -333,11 +581,11 @@ function encoded2(value, bytes) {
|
|
|
333
581
|
return value;
|
|
334
582
|
}
|
|
335
583
|
__name(encoded2, "encoded");
|
|
336
|
-
function parseMlsDevIssuerKeyVersion(value) {
|
|
584
|
+
function parseMlsDevIssuerKeyVersion(value, environmentId) {
|
|
337
585
|
return boundary2(() => {
|
|
338
586
|
if (typeof value !== "string" || value.length > 200) invalid3();
|
|
339
|
-
const
|
|
340
|
-
if (!
|
|
587
|
+
const owner = mlsEnvironmentOfIssuerKeyVersion(value);
|
|
588
|
+
if (!owner || environmentId !== void 0 && owner !== environmentId) invalid3();
|
|
341
589
|
return value;
|
|
342
590
|
});
|
|
343
591
|
}
|
|
@@ -348,11 +596,12 @@ function parseMlsDevCredentialClaims(value) {
|
|
|
348
596
|
const issuedAtMs = requireMlsInteger(input.issuedAtMs);
|
|
349
597
|
const expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
350
598
|
if (expiresAtMs - issuedAtMs !== MLS_DEV_CREDENTIAL_TTL_MS) invalid3();
|
|
599
|
+
const device = parseMlsDevEnrollmentResult({ version: 1, authorization: "registration_only", device: input.device }).device;
|
|
351
600
|
return bounded2({
|
|
352
601
|
version: requireMlsLiteral(input.version, 1),
|
|
353
602
|
credentialId: requireMlsUuid(input.credentialId),
|
|
354
|
-
issuer: requireMlsLiteral(input.issuer,
|
|
355
|
-
device
|
|
603
|
+
issuer: requireMlsLiteral(input.issuer, mlsEnvironmentProfile(device.environmentId).issuer),
|
|
604
|
+
device,
|
|
356
605
|
generation: requireMlsInteger(input.generation, 1),
|
|
357
606
|
issuedAtMs,
|
|
358
607
|
expiresAtMs
|
|
@@ -363,7 +612,9 @@ __name(parseMlsDevCredentialClaims, "parseMlsDevCredentialClaims");
|
|
|
363
612
|
function parseMlsDevCredentialUnsigned(value) {
|
|
364
613
|
return boundary2(() => {
|
|
365
614
|
const input = readMlsDataRecord(value, ["keyVersion", "claims"]);
|
|
366
|
-
|
|
615
|
+
const keyVersion = parseMlsDevIssuerKeyVersion(input.keyVersion), claims = parseMlsDevCredentialClaims(input.claims);
|
|
616
|
+
if (mlsEnvironmentOfIssuerKeyVersion(keyVersion) !== claims.device.environmentId) invalid3();
|
|
617
|
+
return bounded2({ keyVersion, claims });
|
|
367
618
|
});
|
|
368
619
|
}
|
|
369
620
|
__name(parseMlsDevCredentialUnsigned, "parseMlsDevCredentialUnsigned");
|
|
@@ -423,7 +674,7 @@ function parseMlsDevCredentialRevocationReceipt(value) {
|
|
|
423
674
|
}
|
|
424
675
|
__name(parseMlsDevCredentialRevocationReceipt, "parseMlsDevCredentialRevocationReceipt");
|
|
425
676
|
function signingInput(unsigned2) {
|
|
426
|
-
const header = { alg: "Ed25519", typ:
|
|
677
|
+
const header = { alg: "Ed25519", typ: mlsEnvironmentProfile(unsigned2.claims.device.environmentId).credentialType, kid: unsigned2.keyVersion };
|
|
427
678
|
const result = encodeCanonicalMlsBase64Url(utf82.encode(JSON.stringify(header))) + "." + encodeCanonicalMlsBase64Url(utf82.encode(JSON.stringify(unsigned2.claims)));
|
|
428
679
|
if (result.length > MLS_DEV_CREDENTIAL_MAX_BYTES) invalid3();
|
|
429
680
|
return result;
|
|
@@ -445,8 +696,8 @@ function parseMlsDevCredentialSigningInput(value) {
|
|
|
445
696
|
if (parts.length !== 2) invalid3();
|
|
446
697
|
const header = readMlsDataRecord(jsonSegment(parts[0]), ["alg", "typ", "kid"]);
|
|
447
698
|
requireMlsLiteral(header.alg, "Ed25519");
|
|
448
|
-
requireMlsLiteral(header.typ, "canon-mls-dev-credential+jws");
|
|
449
699
|
const unsigned2 = parseMlsDevCredentialUnsigned({ keyVersion: header.kid, claims: jsonSegment(parts[1]) });
|
|
700
|
+
requireMlsLiteral(header.typ, mlsEnvironmentProfile(unsigned2.claims.device.environmentId).credentialType);
|
|
450
701
|
if (signingInput(unsigned2) !== value) invalid3();
|
|
451
702
|
return unsigned2;
|
|
452
703
|
});
|
|
@@ -476,6 +727,54 @@ function parseMlsDevCredentialReceipt(value) {
|
|
|
476
727
|
}
|
|
477
728
|
__name(parseMlsDevCredentialReceipt, "parseMlsDevCredentialReceipt");
|
|
478
729
|
|
|
730
|
+
// ../backend-contracts/src/mlsDevFeedReceipt.ts
|
|
731
|
+
var MlsDevFeedReceiptValidationError = class extends Error {
|
|
732
|
+
static {
|
|
733
|
+
__name(this, "MlsDevFeedReceiptValidationError");
|
|
734
|
+
}
|
|
735
|
+
code = "INVALID_MLS_DEV_FEED_RECEIPT";
|
|
736
|
+
constructor() {
|
|
737
|
+
super("INVALID_MLS_DEV_FEED_RECEIPT");
|
|
738
|
+
this.name = "MlsDevFeedReceiptValidationError";
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
function invalid4() {
|
|
742
|
+
throw new MlsDevFeedReceiptValidationError();
|
|
743
|
+
}
|
|
744
|
+
__name(invalid4, "invalid");
|
|
745
|
+
function boundary3(read) {
|
|
746
|
+
try {
|
|
747
|
+
return read();
|
|
748
|
+
} catch {
|
|
749
|
+
return invalid4();
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
__name(boundary3, "boundary");
|
|
753
|
+
function parseMlsDevReceiptKeyVersion(value, environmentId) {
|
|
754
|
+
return boundary3(() => {
|
|
755
|
+
if (typeof value !== "string" || value.length > 200) invalid4();
|
|
756
|
+
const owner = mlsEnvironmentOfReceiptKeyVersion(value);
|
|
757
|
+
if (!owner || environmentId !== void 0 && owner !== environmentId) invalid4();
|
|
758
|
+
return value;
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
__name(parseMlsDevReceiptKeyVersion, "parseMlsDevReceiptKeyVersion");
|
|
762
|
+
function parseMlsDevFeedReceiptProof(value) {
|
|
763
|
+
return boundary3(() => {
|
|
764
|
+
const input = readMlsDataRecord(value, ["keyVersion", "signature"]);
|
|
765
|
+
decodeCanonicalMlsBase64Url(input.signature, 64);
|
|
766
|
+
return Object.freeze({ keyVersion: parseMlsDevReceiptKeyVersion(input.keyVersion), signature: input.signature });
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
__name(parseMlsDevFeedReceiptProof, "parseMlsDevFeedReceiptProof");
|
|
770
|
+
var MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW = "MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW";
|
|
771
|
+
function isMlsDevReceiptClockSkew(value) {
|
|
772
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
773
|
+
const keys2 = Object.keys(value).sort().join(), input = value;
|
|
774
|
+
return (keys2 === "error,ok,retryable" || keys2 === "error,ok,retryable,version" && input.version === 1) && input.ok === false && input.error === MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW && input.retryable === true;
|
|
775
|
+
}
|
|
776
|
+
__name(isMlsDevReceiptClockSkew, "isMlsDevReceiptClockSkew");
|
|
777
|
+
|
|
479
778
|
// ../backend-contracts/src/mlsDevGroupFeed.ts
|
|
480
779
|
var MLS_DEV_GROUP_FEED_MAX_ENTRIES = 4;
|
|
481
780
|
var MLS_DEV_GROUP_FEED_MAX_BYTES = 4 * 1024 * 1024;
|
|
@@ -489,99 +788,104 @@ var MlsDevGroupFeedValidationError = class extends Error {
|
|
|
489
788
|
this.name = "MlsDevGroupFeedValidationError";
|
|
490
789
|
}
|
|
491
790
|
};
|
|
492
|
-
function
|
|
791
|
+
function invalid5() {
|
|
493
792
|
throw new MlsDevGroupFeedValidationError();
|
|
494
793
|
}
|
|
495
|
-
__name(
|
|
496
|
-
function
|
|
794
|
+
__name(invalid5, "invalid");
|
|
795
|
+
function boundary4(read) {
|
|
497
796
|
try {
|
|
498
797
|
return read();
|
|
499
798
|
} catch {
|
|
500
|
-
return
|
|
799
|
+
return invalid5();
|
|
501
800
|
}
|
|
502
801
|
}
|
|
503
|
-
__name(
|
|
802
|
+
__name(boundary4, "boundary");
|
|
504
803
|
function id(value) {
|
|
505
804
|
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
506
|
-
|
|
805
|
+
invalid5();
|
|
507
806
|
return value;
|
|
508
807
|
}
|
|
509
808
|
__name(id, "id");
|
|
510
809
|
function boolean(value) {
|
|
511
810
|
if (typeof value !== "boolean")
|
|
512
|
-
|
|
811
|
+
invalid5();
|
|
513
812
|
return value;
|
|
514
813
|
}
|
|
515
814
|
__name(boolean, "boolean");
|
|
516
815
|
function bounded3(value) {
|
|
517
816
|
if (new TextEncoder().encode(JSON.stringify(value)).length > MLS_DEV_GROUP_FEED_MAX_BYTES)
|
|
518
|
-
|
|
817
|
+
invalid5();
|
|
519
818
|
return Object.freeze(value);
|
|
520
819
|
}
|
|
521
820
|
__name(bounded3, "bounded");
|
|
522
821
|
function array(value) {
|
|
523
822
|
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > MLS_DEV_GROUP_FEED_MAX_ENTRIES)
|
|
524
|
-
|
|
823
|
+
invalid5();
|
|
525
824
|
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
526
825
|
if (Reflect.ownKeys(descriptors).length !== value.length + 1)
|
|
527
|
-
|
|
826
|
+
invalid5();
|
|
528
827
|
return Array.from({ length: value.length }, (_, index) => {
|
|
529
828
|
const field = descriptors[index];
|
|
530
829
|
if (!field || !field.enumerable || !Object.hasOwn(field, "value"))
|
|
531
|
-
|
|
830
|
+
invalid5();
|
|
532
831
|
return field.value;
|
|
533
832
|
});
|
|
534
833
|
}
|
|
535
834
|
__name(array, "array");
|
|
536
835
|
function parseMlsDevGroupFeedEntry(value) {
|
|
537
|
-
return
|
|
538
|
-
const
|
|
836
|
+
return boundary4(() => {
|
|
837
|
+
const signed = value !== null && typeof value === "object" && Object.hasOwn(value, "receiptProof") ? ["receiptProof"] : [];
|
|
838
|
+
const input = readMlsDataRecord(value, ["version", "ordinal", "kind", "accepted", ...signed], ["version", "ordinal", "kind", "receipt", "envelope", ...signed]);
|
|
539
839
|
const version = requireMlsLiteral(input.version, 1), ordinal = requireMlsInteger(input.ordinal, 1);
|
|
840
|
+
const proof = signed.length ? { receiptProof: parseMlsDevFeedReceiptProof(input.receiptProof) } : {};
|
|
540
841
|
if (input.kind === "control") {
|
|
541
842
|
if (!Object.hasOwn(input, "accepted"))
|
|
542
|
-
|
|
543
|
-
return bounded3({ version, ordinal, kind: "control", accepted: parseMlsDevGroupAcceptedControl(input.accepted) });
|
|
843
|
+
invalid5();
|
|
844
|
+
return bounded3({ version, ordinal, kind: "control", accepted: parseMlsDevGroupAcceptedControl(input.accepted), ...proof });
|
|
544
845
|
}
|
|
545
846
|
const kind = requireMlsLiteral(input.kind, "application"), receipt = parseMlsDevApplicationReceipt(input.receipt), envelope = parseMlsDevApplicationEnvelope(input.envelope);
|
|
546
847
|
if (ordinal !== receipt.feedOrdinal || receipt.conversationId !== envelope.conversationId || receipt.groupId !== envelope.groupId || receipt.messageId !== envelope.messageId || JSON.stringify(receipt.head) !== JSON.stringify(envelope.expectedHead))
|
|
547
|
-
|
|
548
|
-
return bounded3({ version, ordinal, kind, receipt, envelope });
|
|
848
|
+
invalid5();
|
|
849
|
+
return bounded3({ version, ordinal, kind, receipt, envelope, ...proof });
|
|
549
850
|
});
|
|
550
851
|
}
|
|
551
852
|
__name(parseMlsDevGroupFeedEntry, "parseMlsDevGroupFeedEntry");
|
|
552
853
|
function parseMlsDevGroupFeedQuery(value) {
|
|
553
|
-
return
|
|
854
|
+
return boundary4(() => {
|
|
554
855
|
const input = readMlsDataRecord(value, ["version", "conversationId", "credential", "afterOrdinal", "limit"]);
|
|
555
|
-
const
|
|
556
|
-
if (
|
|
557
|
-
|
|
856
|
+
const limit2 = requireMlsInteger(input.limit, 1);
|
|
857
|
+
if (limit2 > MLS_DEV_GROUP_FEED_MAX_ENTRIES)
|
|
858
|
+
invalid5();
|
|
558
859
|
parseMlsDevCredentialToken(input.credential);
|
|
559
|
-
return Object.freeze({ version: requireMlsLiteral(input.version, 1), conversationId: id(input.conversationId), credential: input.credential, afterOrdinal: requireMlsInteger(input.afterOrdinal), limit });
|
|
860
|
+
return Object.freeze({ version: requireMlsLiteral(input.version, 1), conversationId: id(input.conversationId), credential: input.credential, afterOrdinal: requireMlsInteger(input.afterOrdinal), limit: limit2 });
|
|
560
861
|
});
|
|
561
862
|
}
|
|
562
863
|
__name(parseMlsDevGroupFeedQuery, "parseMlsDevGroupFeedQuery");
|
|
563
864
|
function parseMlsDevGroupFeedResult(value) {
|
|
564
|
-
return
|
|
865
|
+
return boundary4(() => {
|
|
565
866
|
const input = readMlsDataRecord(value, ["version", "protocol", "conversationId", "marker", "membershipRevision", "admissionId", "roster", "minimumOrdinal", "latestOrdinal", "nextOrdinal", "entries", "hasMore", "terminal"]);
|
|
566
867
|
const conversationId = id(input.conversationId), marker = input.marker === null ? null : parseMlsDevGroupMarker(input.marker);
|
|
567
868
|
const minimumOrdinal = input.minimumOrdinal === null ? null : requireMlsInteger(input.minimumOrdinal, 1);
|
|
568
869
|
const latestOrdinal = requireMlsInteger(input.latestOrdinal), nextOrdinal = requireMlsInteger(input.nextOrdinal);
|
|
569
870
|
const entries = array(input.entries).map(parseMlsDevGroupFeedEntry), hasMore = boolean(input.hasMore), terminal = boolean(input.terminal);
|
|
570
871
|
if (nextOrdinal > latestOrdinal || minimumOrdinal !== null && minimumOrdinal > latestOrdinal || minimumOrdinal === null && (entries.length || nextOrdinal || hasMore || terminal) || !marker && (latestOrdinal || minimumOrdinal !== null || entries.length || terminal))
|
|
571
|
-
|
|
872
|
+
invalid5();
|
|
873
|
+
const protocol = isMlsDevTextProtocol(input.protocol) ? input.protocol : invalid5();
|
|
572
874
|
for (let i = 0; i < entries.length; i++) {
|
|
573
875
|
const entry = entries[i], subject = entry.kind === "control" ? entry.accepted.control : entry.envelope;
|
|
876
|
+
if (entry.kind === "application" && entry.envelope.interaction && !mlsDevProtocolCarriesCards(protocol))
|
|
877
|
+
invalid5();
|
|
574
878
|
if (!marker || minimumOrdinal === null || entry.ordinal < minimumOrdinal || entry.ordinal > latestOrdinal || subject.conversationId !== conversationId || subject.groupId !== marker.groupId || i > 0 && entry.ordinal !== entries[i - 1].ordinal + 1)
|
|
575
|
-
|
|
879
|
+
invalid5();
|
|
576
880
|
const head = entry.kind === "control" ? entry.accepted.head : entry.receipt.head;
|
|
577
881
|
if (head.sequence > marker.head.sequence || head.epoch > marker.head.epoch)
|
|
578
|
-
|
|
882
|
+
invalid5();
|
|
579
883
|
}
|
|
580
884
|
if (entries.length && entries.at(-1).ordinal !== nextOrdinal || hasMore !== (minimumOrdinal !== null && nextOrdinal < latestOrdinal))
|
|
581
|
-
|
|
885
|
+
invalid5();
|
|
582
886
|
return bounded3({
|
|
583
887
|
version: requireMlsLiteral(input.version, 1),
|
|
584
|
-
protocol
|
|
888
|
+
protocol,
|
|
585
889
|
conversationId,
|
|
586
890
|
marker,
|
|
587
891
|
membershipRevision: requireMlsInteger(input.membershipRevision),
|
|
@@ -611,25 +915,25 @@ var MlsDevKeyPackagesValidationError = class extends Error {
|
|
|
611
915
|
this.name = "MlsDevKeyPackagesValidationError";
|
|
612
916
|
}
|
|
613
917
|
};
|
|
614
|
-
function
|
|
918
|
+
function invalid6() {
|
|
615
919
|
throw new MlsDevKeyPackagesValidationError();
|
|
616
920
|
}
|
|
617
|
-
__name(
|
|
618
|
-
function
|
|
921
|
+
__name(invalid6, "invalid");
|
|
922
|
+
function boundary5(read) {
|
|
619
923
|
try {
|
|
620
924
|
return read();
|
|
621
925
|
} catch {
|
|
622
|
-
return
|
|
926
|
+
return invalid6();
|
|
623
927
|
}
|
|
624
928
|
}
|
|
625
|
-
__name(
|
|
929
|
+
__name(boundary5, "boundary");
|
|
626
930
|
function bounded4(value) {
|
|
627
|
-
if (new TextEncoder().encode(JSON.stringify(value)).length > MLS_DEV_KEY_PACKAGES_MAX_BYTES)
|
|
931
|
+
if (new TextEncoder().encode(JSON.stringify(value)).length > MLS_DEV_KEY_PACKAGES_MAX_BYTES) invalid6();
|
|
628
932
|
return Object.freeze(value);
|
|
629
933
|
}
|
|
630
934
|
__name(bounded4, "bounded");
|
|
631
935
|
function id2(value) {
|
|
632
|
-
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
936
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..") invalid6();
|
|
633
937
|
return value;
|
|
634
938
|
}
|
|
635
939
|
__name(id2, "id");
|
|
@@ -639,18 +943,18 @@ function ref(value) {
|
|
|
639
943
|
}
|
|
640
944
|
__name(ref, "ref");
|
|
641
945
|
function parseMlsDevKeyPackageWire(value) {
|
|
642
|
-
return
|
|
643
|
-
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_KEY_PACKAGE_TLS_MAX_BYTES * 4 / 3))
|
|
946
|
+
return boundary5(() => {
|
|
947
|
+
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_KEY_PACKAGE_TLS_MAX_BYTES * 4 / 3)) invalid6();
|
|
644
948
|
const bytes = Math.floor(value.length * 3 / 4);
|
|
645
|
-
if (bytes < 1 || bytes > MLS_DEV_KEY_PACKAGE_TLS_MAX_BYTES || value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
949
|
+
if (bytes < 1 || bytes > MLS_DEV_KEY_PACKAGE_TLS_MAX_BYTES || value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value)) invalid6();
|
|
646
950
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
647
|
-
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
951
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid6();
|
|
648
952
|
return value;
|
|
649
953
|
});
|
|
650
954
|
}
|
|
651
955
|
__name(parseMlsDevKeyPackageWire, "parseMlsDevKeyPackageWire");
|
|
652
956
|
function parseMlsDevKeyPackageClaimPreconditions(value) {
|
|
653
|
-
return
|
|
957
|
+
return boundary5(() => {
|
|
654
958
|
const input = readMlsDataRecord(value, ["conversationId", "membershipRevision", "admissionId", "targetAdmissionId"]);
|
|
655
959
|
return Object.freeze({
|
|
656
960
|
conversationId: id2(input.conversationId),
|
|
@@ -662,17 +966,17 @@ function parseMlsDevKeyPackageClaimPreconditions(value) {
|
|
|
662
966
|
}
|
|
663
967
|
__name(parseMlsDevKeyPackageClaimPreconditions, "parseMlsDevKeyPackageClaimPreconditions");
|
|
664
968
|
function parseMlsDevKeyPackagePublish(value) {
|
|
665
|
-
return
|
|
969
|
+
return boundary5(() => {
|
|
666
970
|
const input = readMlsDataRecord(value, ["version", "requestId", "keyPackage"]);
|
|
667
971
|
return bounded4({ version: requireMlsLiteral(input.version, 1), requestId: requireMlsUuid(input.requestId), keyPackage: parseMlsDevKeyPackageWire(input.keyPackage) });
|
|
668
972
|
});
|
|
669
973
|
}
|
|
670
974
|
__name(parseMlsDevKeyPackagePublish, "parseMlsDevKeyPackagePublish");
|
|
671
975
|
function parseMlsDevKeyPackagePublicationReceipt(value) {
|
|
672
|
-
return
|
|
976
|
+
return boundary5(() => {
|
|
673
977
|
const input = readMlsDataRecord(value, ["version", "authorization", "requestId", "keyPackageRef", "deviceId", "publishedAtMs", "expiresAtMs"]);
|
|
674
978
|
const publishedAtMs = requireMlsInteger(input.publishedAtMs), expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
675
|
-
if (publishedAtMs >= expiresAtMs)
|
|
979
|
+
if (publishedAtMs >= expiresAtMs) invalid6();
|
|
676
980
|
return bounded4({
|
|
677
981
|
version: requireMlsLiteral(input.version, 1),
|
|
678
982
|
authorization: requireMlsLiteral(input.authorization, "key_package_publication_only"),
|
|
@@ -686,7 +990,7 @@ function parseMlsDevKeyPackagePublicationReceipt(value) {
|
|
|
686
990
|
}
|
|
687
991
|
__name(parseMlsDevKeyPackagePublicationReceipt, "parseMlsDevKeyPackagePublicationReceipt");
|
|
688
992
|
function parseMlsDevKeyPackageClaim(value) {
|
|
689
|
-
return
|
|
993
|
+
return boundary5(() => {
|
|
690
994
|
const input = readMlsDataRecord(value, ["version", "requestId", "claimantCredential", "recipientDeviceId", "preconditions"]);
|
|
691
995
|
parseMlsDevCredentialToken(input.claimantCredential);
|
|
692
996
|
return bounded4({
|
|
@@ -700,7 +1004,7 @@ function parseMlsDevKeyPackageClaim(value) {
|
|
|
700
1004
|
}
|
|
701
1005
|
__name(parseMlsDevKeyPackageClaim, "parseMlsDevKeyPackageClaim");
|
|
702
1006
|
function parseMlsDevKeyPackageClaimReceipt(value) {
|
|
703
|
-
return
|
|
1007
|
+
return boundary5(() => {
|
|
704
1008
|
const input = readMlsDataRecord(value, [
|
|
705
1009
|
"version",
|
|
706
1010
|
"authorization",
|
|
@@ -713,7 +1017,7 @@ function parseMlsDevKeyPackageClaimReceipt(value) {
|
|
|
713
1017
|
"claimedAtMs"
|
|
714
1018
|
]);
|
|
715
1019
|
const claimantDeviceId = requireMlsUuid(input.claimantDeviceId), recipientDeviceId = requireMlsUuid(input.recipientDeviceId);
|
|
716
|
-
if (claimantDeviceId === recipientDeviceId)
|
|
1020
|
+
if (claimantDeviceId === recipientDeviceId) invalid6();
|
|
717
1021
|
return bounded4({
|
|
718
1022
|
version: requireMlsLiteral(input.version, 1),
|
|
719
1023
|
authorization: requireMlsLiteral(input.authorization, "key_package_claim_only"),
|
|
@@ -729,6 +1033,228 @@ function parseMlsDevKeyPackageClaimReceipt(value) {
|
|
|
729
1033
|
}
|
|
730
1034
|
__name(parseMlsDevKeyPackageClaimReceipt, "parseMlsDevKeyPackageClaimReceipt");
|
|
731
1035
|
|
|
1036
|
+
// ../backend-contracts/src/mlsDevInteraction.ts
|
|
1037
|
+
var MLS_DEV_INTERACTION_LIMITS = Object.freeze({ title: 200, details: 8e3, options: 10, optionLabel: 120, text: 4e3 });
|
|
1038
|
+
var MLS_DEV_SECRET_ANSWER_MAX_BYTES = MLS_DEV_INTERACTION_LIMITS.text * 4 + 16;
|
|
1039
|
+
var MLS_DEV_INTERACTION_WITHDRAW_REASONS = ["stopped", "turn_ended", "expired", "restart"];
|
|
1040
|
+
var MlsDevInteractionValidationError = class extends Error {
|
|
1041
|
+
static {
|
|
1042
|
+
__name(this, "MlsDevInteractionValidationError");
|
|
1043
|
+
}
|
|
1044
|
+
code = "INVALID_MLS_DEV_INTERACTION";
|
|
1045
|
+
constructor() {
|
|
1046
|
+
super("INVALID_MLS_DEV_INTERACTION");
|
|
1047
|
+
this.name = "MlsDevInteractionValidationError";
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
var utf83 = new TextEncoder();
|
|
1051
|
+
function invalid7() {
|
|
1052
|
+
throw new MlsDevInteractionValidationError();
|
|
1053
|
+
}
|
|
1054
|
+
__name(invalid7, "invalid");
|
|
1055
|
+
function boundary6(read) {
|
|
1056
|
+
try {
|
|
1057
|
+
return read();
|
|
1058
|
+
} catch {
|
|
1059
|
+
return invalid7();
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
__name(boundary6, "boundary");
|
|
1063
|
+
function id3(value) {
|
|
1064
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..") invalid7();
|
|
1065
|
+
return value;
|
|
1066
|
+
}
|
|
1067
|
+
__name(id3, "id");
|
|
1068
|
+
function interactionId(value) {
|
|
1069
|
+
if (typeof value !== "string" || !MLS_DEV_INTERACTION_ID_PATTERN.test(value) || value === "." || value === "..") invalid7();
|
|
1070
|
+
return value;
|
|
1071
|
+
}
|
|
1072
|
+
__name(interactionId, "interactionId");
|
|
1073
|
+
function encoded3(value, bytes) {
|
|
1074
|
+
decodeCanonicalMlsBase64Url(value, bytes);
|
|
1075
|
+
return value;
|
|
1076
|
+
}
|
|
1077
|
+
__name(encoded3, "encoded");
|
|
1078
|
+
function wire(value, min, max) {
|
|
1079
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) invalid7();
|
|
1080
|
+
const bytes = Math.floor(value.length * 3 / 4);
|
|
1081
|
+
if (bytes < min || bytes > max) invalid7();
|
|
1082
|
+
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
1083
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid7();
|
|
1084
|
+
return value;
|
|
1085
|
+
}
|
|
1086
|
+
__name(wire, "wire");
|
|
1087
|
+
function text(value, max) {
|
|
1088
|
+
if (typeof value !== "string" || !value.trim() || [...value].length > max || new TextDecoder("utf-8", { fatal: true }).decode(utf83.encode(value)) !== value) invalid7();
|
|
1089
|
+
return value;
|
|
1090
|
+
}
|
|
1091
|
+
__name(text, "text");
|
|
1092
|
+
function boolean2(value) {
|
|
1093
|
+
if (typeof value !== "boolean") invalid7();
|
|
1094
|
+
return value;
|
|
1095
|
+
}
|
|
1096
|
+
__name(boolean2, "boolean");
|
|
1097
|
+
function array2(value, min, max) {
|
|
1098
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length < min || value.length > max) invalid7();
|
|
1099
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
1100
|
+
if (Reflect.ownKeys(descriptors).length !== value.length + 1) invalid7();
|
|
1101
|
+
return Array.from({ length: value.length }, (_, index) => {
|
|
1102
|
+
const field = descriptors[index];
|
|
1103
|
+
if (!field || !field.enumerable || !Object.hasOwn(field, "value")) invalid7();
|
|
1104
|
+
return field.value;
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
__name(array2, "array");
|
|
1108
|
+
function closed(value, required, optional) {
|
|
1109
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) invalid7();
|
|
1110
|
+
const present2 = Reflect.ownKeys(value);
|
|
1111
|
+
if (present2.some((key) => typeof key !== "string" || !required.includes(key) && !optional.includes(key))) invalid7();
|
|
1112
|
+
return readMlsDataRecord(value, present2);
|
|
1113
|
+
}
|
|
1114
|
+
__name(closed, "closed");
|
|
1115
|
+
function ordered(value) {
|
|
1116
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).filter(([, field]) => field !== void 0)));
|
|
1117
|
+
}
|
|
1118
|
+
__name(ordered, "ordered");
|
|
1119
|
+
function parseMlsDevInteractionRequestBody(value) {
|
|
1120
|
+
return boundary6(() => {
|
|
1121
|
+
const input = closed(
|
|
1122
|
+
value,
|
|
1123
|
+
["requestId", "kind", "title", "responderIds", "expiresAtMs"],
|
|
1124
|
+
["details", "options", "multiple", "risk", "allowSession", "scopeKey", "secretKey"]
|
|
1125
|
+
);
|
|
1126
|
+
const kind = input.kind;
|
|
1127
|
+
if (typeof kind !== "string" || !MLS_DEV_INTERACTION_KINDS.includes(kind)) invalid7();
|
|
1128
|
+
const has = /* @__PURE__ */ __name((key) => Object.hasOwn(input, key), "has");
|
|
1129
|
+
const options = has("options") ? array2(input.options, 1, MLS_DEV_INTERACTION_LIMITS.options).map((option) => {
|
|
1130
|
+
const fields2 = readMlsDataRecord(option, ["id", "label"]);
|
|
1131
|
+
if (typeof fields2.id !== "string" || !/^[A-Za-z0-9_.:-]{1,64}$/.test(fields2.id)) invalid7();
|
|
1132
|
+
return Object.freeze({ id: fields2.id, label: text(fields2.label, MLS_DEV_INTERACTION_LIMITS.optionLabel) });
|
|
1133
|
+
}) : void 0;
|
|
1134
|
+
if (options && new Set(options.map((option) => option.id)).size !== options.length) invalid7();
|
|
1135
|
+
const binding3 = parseMlsDevInteractionBinding({ role: "request", requestId: input.requestId, kind, responderIds: input.responderIds, expiresAtMs: input.expiresAtMs });
|
|
1136
|
+
if (binding3.role !== "request") invalid7();
|
|
1137
|
+
if (kind !== "approval" && ["risk", "allowSession", "scopeKey"].some(has)) invalid7();
|
|
1138
|
+
if (kind !== "question" && (has("options") || has("multiple"))) invalid7();
|
|
1139
|
+
if (has("multiple") && !options) invalid7();
|
|
1140
|
+
if (kind === "secret" !== has("secretKey")) invalid7();
|
|
1141
|
+
const allowSession = has("allowSession") ? boolean2(input.allowSession) : void 0;
|
|
1142
|
+
if (has("scopeKey") && (typeof input.scopeKey !== "string" || !/^[A-Za-z0-9_:-]{1,64}$/.test(input.scopeKey))) invalid7();
|
|
1143
|
+
if (allowSession === true && !has("scopeKey")) invalid7();
|
|
1144
|
+
if (has("risk") && input.risk !== "normal" && input.risk !== "destructive") invalid7();
|
|
1145
|
+
const secretKey = has("secretKey") ? (() => {
|
|
1146
|
+
const key = readMlsDataRecord(input.secretKey, ["kem", "publicKey"]);
|
|
1147
|
+
return Object.freeze({ kem: requireMlsLiteral(key.kem, "x25519"), publicKey: encoded3(key.publicKey, 32) });
|
|
1148
|
+
})() : void 0;
|
|
1149
|
+
return ordered({
|
|
1150
|
+
requestId: binding3.requestId,
|
|
1151
|
+
kind: binding3.kind,
|
|
1152
|
+
title: text(input.title, MLS_DEV_INTERACTION_LIMITS.title),
|
|
1153
|
+
details: has("details") ? text(input.details, MLS_DEV_INTERACTION_LIMITS.details) : void 0,
|
|
1154
|
+
options: options ? Object.freeze(options) : void 0,
|
|
1155
|
+
multiple: has("multiple") ? boolean2(input.multiple) : void 0,
|
|
1156
|
+
risk: input.risk,
|
|
1157
|
+
allowSession,
|
|
1158
|
+
scopeKey: input.scopeKey,
|
|
1159
|
+
responderIds: binding3.responderIds,
|
|
1160
|
+
expiresAtMs: binding3.expiresAtMs,
|
|
1161
|
+
secretKey
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
__name(parseMlsDevInteractionRequestBody, "parseMlsDevInteractionRequestBody");
|
|
1166
|
+
function parseMlsDevInteractionAnswer(value) {
|
|
1167
|
+
return boundary6(() => {
|
|
1168
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) invalid7();
|
|
1169
|
+
const keys2 = Reflect.ownKeys(value);
|
|
1170
|
+
if (keys2.includes("decision")) {
|
|
1171
|
+
const input = readMlsDataRecord(value, ["decision", "scope"]);
|
|
1172
|
+
if (input.decision !== "allow" && input.decision !== "deny" || input.scope !== "once" && input.scope !== "session") invalid7();
|
|
1173
|
+
return Object.freeze({ decision: input.decision, scope: input.scope });
|
|
1174
|
+
}
|
|
1175
|
+
if (keys2.includes("choices")) {
|
|
1176
|
+
const choices = array2(readMlsDataRecord(value, ["choices"]).choices, 1, MLS_DEV_INTERACTION_LIMITS.options).map((choice) => {
|
|
1177
|
+
if (typeof choice !== "string" || !/^[A-Za-z0-9_.:-]{1,64}$/.test(choice)) invalid7();
|
|
1178
|
+
return choice;
|
|
1179
|
+
});
|
|
1180
|
+
if (new Set(choices).size !== choices.length) invalid7();
|
|
1181
|
+
return Object.freeze({ choices: Object.freeze(choices) });
|
|
1182
|
+
}
|
|
1183
|
+
if (keys2.includes("text")) return Object.freeze({ text: text(readMlsDataRecord(value, ["text"]).text, MLS_DEV_INTERACTION_LIMITS.text) });
|
|
1184
|
+
if (keys2.includes("sealed")) {
|
|
1185
|
+
const sealed = readMlsDataRecord(readMlsDataRecord(value, ["sealed"]).sealed, ["enc", "ciphertext"]);
|
|
1186
|
+
return Object.freeze({ sealed: Object.freeze({ enc: encoded3(sealed.enc, 32), ciphertext: wire(sealed.ciphertext, 17, MLS_DEV_SECRET_ANSWER_MAX_BYTES) }) });
|
|
1187
|
+
}
|
|
1188
|
+
if (readMlsDataRecord(value, ["cancelled"]).cancelled !== true) invalid7();
|
|
1189
|
+
return Object.freeze({ cancelled: true });
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
__name(parseMlsDevInteractionAnswer, "parseMlsDevInteractionAnswer");
|
|
1193
|
+
function parseMlsDevInteractionResponseBody(value) {
|
|
1194
|
+
return boundary6(() => {
|
|
1195
|
+
const input = readMlsDataRecord(value, ["requestId", "agentId", "answer"]);
|
|
1196
|
+
return Object.freeze({ requestId: interactionId(input.requestId), agentId: id3(input.agentId), answer: parseMlsDevInteractionAnswer(input.answer) });
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
__name(parseMlsDevInteractionResponseBody, "parseMlsDevInteractionResponseBody");
|
|
1200
|
+
function parseMlsDevInteractionWithdrawBody(value) {
|
|
1201
|
+
return boundary6(() => {
|
|
1202
|
+
const input = readMlsDataRecord(value, ["requestId", "reason"]);
|
|
1203
|
+
if (!MLS_DEV_INTERACTION_WITHDRAW_REASONS.includes(input.reason)) invalid7();
|
|
1204
|
+
return Object.freeze({ requestId: interactionId(input.requestId), reason: input.reason });
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
__name(parseMlsDevInteractionWithdrawBody, "parseMlsDevInteractionWithdrawBody");
|
|
1208
|
+
var payloadFields = ["version", "kind", "conversationId", "groupId", "messageId", "operationId", "senderDeviceId"];
|
|
1209
|
+
function parseMlsDevApplicationPayload(value) {
|
|
1210
|
+
return boundary6(() => {
|
|
1211
|
+
const kind = value && typeof value === "object" ? Object.getOwnPropertyDescriptor(value, "kind") : void 0;
|
|
1212
|
+
if (!kind || !Object.hasOwn(kind, "value")) invalid7();
|
|
1213
|
+
if (kind.value === "text") return parseMlsDevTextPayload(value);
|
|
1214
|
+
if (kind.value === "item") return parseMlsDevItemPayload(value);
|
|
1215
|
+
const input = readMlsDataRecord(value, [...payloadFields, "interaction"]);
|
|
1216
|
+
const ids = {
|
|
1217
|
+
version: requireMlsLiteral(input.version, 1),
|
|
1218
|
+
kind: input.kind,
|
|
1219
|
+
conversationId: id3(input.conversationId),
|
|
1220
|
+
groupId: encoded3(input.groupId, 32),
|
|
1221
|
+
messageId: id3(input.messageId),
|
|
1222
|
+
operationId: id3(input.operationId),
|
|
1223
|
+
senderDeviceId: requireMlsUuid(input.senderDeviceId)
|
|
1224
|
+
};
|
|
1225
|
+
if (ids.messageId.startsWith("membership-")) invalid7();
|
|
1226
|
+
const interaction = kind.value === "interaction.request" ? parseMlsDevInteractionRequestBody(input.interaction) : kind.value === "interaction.response" ? parseMlsDevInteractionResponseBody(input.interaction) : kind.value === "interaction.withdraw" ? parseMlsDevInteractionWithdrawBody(input.interaction) : invalid7();
|
|
1227
|
+
const payload = Object.freeze({ ...ids, interaction });
|
|
1228
|
+
if (utf83.encode(JSON.stringify(payload)).length > MLS_DEV_TEXT_MAX_BYTES) invalid7();
|
|
1229
|
+
return payload;
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
__name(parseMlsDevApplicationPayload, "parseMlsDevApplicationPayload");
|
|
1233
|
+
function assertMlsDevInteractionPayloadBinding(payload, binding3) {
|
|
1234
|
+
boundary6(() => {
|
|
1235
|
+
if (payload.kind === "text" || payload.kind === "item") {
|
|
1236
|
+
if (binding3 !== void 0) invalid7();
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
if (!binding3 || payload.interaction.requestId !== binding3.requestId) invalid7();
|
|
1240
|
+
if (payload.kind === "interaction.request") {
|
|
1241
|
+
if (binding3.role !== "request" || payload.interaction.kind !== binding3.kind || payload.interaction.expiresAtMs !== binding3.expiresAtMs || payload.interaction.responderIds.length !== 1 || payload.interaction.responderIds[0] !== binding3.responderIds[0]) invalid7();
|
|
1242
|
+
} else if (payload.kind === "interaction.response") {
|
|
1243
|
+
if (binding3.role !== "response" || payload.interaction.agentId !== binding3.agentId) invalid7();
|
|
1244
|
+
} else if (binding3.role !== "withdraw") invalid7();
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
__name(assertMlsDevInteractionPayloadBinding, "assertMlsDevInteractionPayloadBinding");
|
|
1248
|
+
function isMlsDevInteractionAnswerAllowed(request, answer) {
|
|
1249
|
+
if ("cancelled" in answer) return request.kind !== "approval";
|
|
1250
|
+
if (request.kind === "approval") return "decision" in answer && (answer.scope !== "session" || request.allowSession === true);
|
|
1251
|
+
if (request.kind === "secret") return "sealed" in answer;
|
|
1252
|
+
if ("text" in answer) return !request.options;
|
|
1253
|
+
if (!("choices" in answer) || !request.options) return false;
|
|
1254
|
+
return (request.multiple === true || answer.choices.length === 1) && answer.choices.every((choice) => request.options.some((option) => option.id === choice));
|
|
1255
|
+
}
|
|
1256
|
+
__name(isMlsDevInteractionAnswerAllowed, "isMlsDevInteractionAnswerAllowed");
|
|
1257
|
+
|
|
732
1258
|
// ../backend-contracts/src/mlsDevGroup.ts
|
|
733
1259
|
var MLS_DEV_GROUP_ROSTER_MAX = 32;
|
|
734
1260
|
var MLS_DEV_GROUP_TLS_MAX_BYTES = 256 * 1024;
|
|
@@ -737,6 +1263,7 @@ var MLS_DEV_GROUP_WRAPPED_MAX_BYTES = 3 * 1024 * 1024;
|
|
|
737
1263
|
var MLS_DEV_GROUP_PREPARATION_MAX_BYTES = 4 * 1024 * 1024;
|
|
738
1264
|
var MLS_DEV_GROUP_QUERY_MAX_BYTES = 4 * 1024 * 1024;
|
|
739
1265
|
var MLS_DEV_GROUP_QUERY_MAX_CONTROLS = 4;
|
|
1266
|
+
var MLS_DEV_GROUP_ADMISSION_MAX_CANDIDATES = 64;
|
|
740
1267
|
var MlsDevGroupValidationError = class extends Error {
|
|
741
1268
|
static {
|
|
742
1269
|
__name(this, "MlsDevGroupValidationError");
|
|
@@ -747,119 +1274,169 @@ var MlsDevGroupValidationError = class extends Error {
|
|
|
747
1274
|
this.name = "MlsDevGroupValidationError";
|
|
748
1275
|
}
|
|
749
1276
|
};
|
|
750
|
-
var
|
|
751
|
-
function
|
|
1277
|
+
var utf84 = new TextEncoder();
|
|
1278
|
+
function invalid8() {
|
|
752
1279
|
throw new MlsDevGroupValidationError();
|
|
753
1280
|
}
|
|
754
|
-
__name(
|
|
755
|
-
function
|
|
1281
|
+
__name(invalid8, "invalid");
|
|
1282
|
+
function boundary7(read) {
|
|
756
1283
|
try {
|
|
757
1284
|
return read();
|
|
758
1285
|
} catch {
|
|
759
|
-
return
|
|
1286
|
+
return invalid8();
|
|
760
1287
|
}
|
|
761
1288
|
}
|
|
762
|
-
__name(
|
|
1289
|
+
__name(boundary7, "boundary");
|
|
763
1290
|
function bounded5(value, maximum = MLS_DEV_GROUP_CONTROL_MAX_BYTES) {
|
|
764
|
-
if (
|
|
765
|
-
|
|
1291
|
+
if (utf84.encode(JSON.stringify(value)).length > maximum)
|
|
1292
|
+
invalid8();
|
|
766
1293
|
return Object.freeze(value);
|
|
767
1294
|
}
|
|
768
1295
|
__name(bounded5, "bounded");
|
|
769
|
-
function
|
|
1296
|
+
function id4(value) {
|
|
770
1297
|
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
771
|
-
|
|
1298
|
+
invalid8();
|
|
772
1299
|
return value;
|
|
773
1300
|
}
|
|
774
|
-
__name(
|
|
775
|
-
function
|
|
1301
|
+
__name(id4, "id");
|
|
1302
|
+
function encoded4(value, bytes) {
|
|
776
1303
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
777
1304
|
return value;
|
|
778
1305
|
}
|
|
779
|
-
__name(
|
|
780
|
-
function
|
|
1306
|
+
__name(encoded4, "encoded");
|
|
1307
|
+
function boolean3(value) {
|
|
781
1308
|
if (typeof value !== "boolean")
|
|
782
|
-
|
|
1309
|
+
invalid8();
|
|
783
1310
|
return value;
|
|
784
1311
|
}
|
|
785
|
-
__name(
|
|
1312
|
+
__name(boolean3, "boolean");
|
|
1313
|
+
function textProtocol(value) {
|
|
1314
|
+
if (!isMlsDevTextProtocol(value))
|
|
1315
|
+
invalid8();
|
|
1316
|
+
return value;
|
|
1317
|
+
}
|
|
1318
|
+
__name(textProtocol, "textProtocol");
|
|
786
1319
|
function token(value) {
|
|
787
1320
|
parseMlsDevCredentialToken(value);
|
|
788
1321
|
return value;
|
|
789
1322
|
}
|
|
790
1323
|
__name(token, "token");
|
|
791
|
-
function
|
|
1324
|
+
function wire2(value) {
|
|
792
1325
|
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_GROUP_TLS_MAX_BYTES * 4 / 3))
|
|
793
|
-
|
|
1326
|
+
invalid8();
|
|
794
1327
|
const bytes = Math.floor(value.length * 3 / 4);
|
|
795
1328
|
if (value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
796
|
-
|
|
1329
|
+
invalid8();
|
|
797
1330
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
798
1331
|
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
799
|
-
|
|
1332
|
+
invalid8();
|
|
800
1333
|
return value;
|
|
801
1334
|
}
|
|
802
|
-
__name(
|
|
803
|
-
function
|
|
1335
|
+
__name(wire2, "wire");
|
|
1336
|
+
function plaintextJson(value) {
|
|
1337
|
+
if (typeof value !== "string" || !value || utf84.encode(value).length > MLS_DEV_TEXT_MAX_BYTES || new TextDecoder("utf-8", { fatal: true }).decode(utf84.encode(value)) !== value)
|
|
1338
|
+
invalid8();
|
|
1339
|
+
return value;
|
|
1340
|
+
}
|
|
1341
|
+
__name(plaintextJson, "plaintextJson");
|
|
1342
|
+
function present(value, keys2) {
|
|
1343
|
+
return value && typeof value === "object" ? keys2.filter((key) => Object.hasOwn(value, key)) : [];
|
|
1344
|
+
}
|
|
1345
|
+
__name(present, "present");
|
|
1346
|
+
var pick = /* @__PURE__ */ __name((value, keys2) => Object.fromEntries(keys2.map((key) => [key, value[key]])), "pick");
|
|
1347
|
+
var textExtras = /* @__PURE__ */ __name((text5) => ({ ...text5.replyTo ? { replyTo: text5.replyTo } : {}, ...text5.mentions ? { mentions: text5.mentions } : {} }), "textExtras");
|
|
1348
|
+
function sealedCiphertext(value) {
|
|
1349
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1) invalid8();
|
|
1350
|
+
const bytes = Math.floor(value.length * 3 / 4);
|
|
1351
|
+
if (bytes < 17 || bytes > MLS_DEV_SECRET_ANSWER_MAX_BYTES) invalid8();
|
|
1352
|
+
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
1353
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid8();
|
|
1354
|
+
return value;
|
|
1355
|
+
}
|
|
1356
|
+
__name(sealedCiphertext, "sealedCiphertext");
|
|
1357
|
+
function array3(value, maximum) {
|
|
804
1358
|
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > maximum)
|
|
805
|
-
|
|
1359
|
+
invalid8();
|
|
806
1360
|
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
807
1361
|
if (Reflect.ownKeys(descriptors).length !== value.length + 1)
|
|
808
|
-
|
|
1362
|
+
invalid8();
|
|
809
1363
|
return Array.from({ length: value.length }, (_, index) => {
|
|
810
1364
|
const descriptor = descriptors[index];
|
|
811
1365
|
if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, "value"))
|
|
812
|
-
|
|
1366
|
+
invalid8();
|
|
813
1367
|
return descriptor.value;
|
|
814
1368
|
});
|
|
815
1369
|
}
|
|
816
|
-
__name(
|
|
1370
|
+
__name(array3, "array");
|
|
817
1371
|
function parseMlsDevGroupCreateConversation(value) {
|
|
818
|
-
return
|
|
1372
|
+
return boundary7(() => {
|
|
819
1373
|
const input = readMlsDataRecord(value, ["name", "memberIds", "mlsDev"]), config = readMlsDataRecord(input.mlsDev, ["version", "conversationId", "credential"], ["version", "conversationId", "credential", "protocol"]);
|
|
820
1374
|
if (typeof input.name !== "string" || input.name.trim().length > 100)
|
|
821
|
-
|
|
822
|
-
const memberIds =
|
|
1375
|
+
invalid8();
|
|
1376
|
+
const memberIds = array3(input.memberIds, 31).map((value2) => parseMlsDevEnrollmentPrincipal({ type: "human", id: value2 }).id);
|
|
823
1377
|
if (!memberIds.length || new Set(memberIds).size !== memberIds.length || memberIds.some((value2) => value2.includes("/") || value2 === "." || value2 === ".."))
|
|
824
|
-
|
|
825
|
-
return bounded5({ name: input.name, memberIds: Object.freeze(memberIds), mlsDev: Object.freeze({ version: requireMlsLiteral(config.version, 1), conversationId:
|
|
1378
|
+
invalid8();
|
|
1379
|
+
return bounded5({ name: input.name, memberIds: Object.freeze(memberIds), mlsDev: Object.freeze({ version: requireMlsLiteral(config.version, 1), conversationId: id4(config.conversationId), credential: token(config.credential), ...Object.hasOwn(config, "protocol") ? { protocol: textProtocol(config.protocol) } : {} }) }, 16 * 1024);
|
|
826
1380
|
});
|
|
827
1381
|
}
|
|
828
1382
|
__name(parseMlsDevGroupCreateConversation, "parseMlsDevGroupCreateConversation");
|
|
829
1383
|
function parseMlsDevGroupHead(value) {
|
|
830
|
-
return
|
|
1384
|
+
return boundary7(() => {
|
|
831
1385
|
const input = readMlsDataRecord(value, ["sequence", "epoch", "hash"]);
|
|
832
|
-
return Object.freeze({ sequence: requireMlsInteger(input.sequence, 1), epoch: requireMlsInteger(input.epoch), hash:
|
|
1386
|
+
return Object.freeze({ sequence: requireMlsInteger(input.sequence, 1), epoch: requireMlsInteger(input.epoch), hash: encoded4(input.hash, 32) });
|
|
833
1387
|
});
|
|
834
1388
|
}
|
|
835
1389
|
__name(parseMlsDevGroupHead, "parseMlsDevGroupHead");
|
|
836
1390
|
function parseMlsDevGroupMarker(value) {
|
|
837
|
-
return
|
|
1391
|
+
return boundary7(() => {
|
|
838
1392
|
const input = readMlsDataRecord(value, ["version", "groupId", "head"]);
|
|
839
|
-
return Object.freeze({ version: requireMlsLiteral(input.version, 1), groupId:
|
|
1393
|
+
return Object.freeze({ version: requireMlsLiteral(input.version, 1), groupId: encoded4(input.groupId, 32), head: parseMlsDevGroupHead(input.head) });
|
|
840
1394
|
});
|
|
841
1395
|
}
|
|
842
1396
|
__name(parseMlsDevGroupMarker, "parseMlsDevGroupMarker");
|
|
843
1397
|
function parseMlsDevGroupRoster(value) {
|
|
844
|
-
return
|
|
845
|
-
const values =
|
|
846
|
-
const entry = readMlsDataRecord(
|
|
847
|
-
return Object.freeze({ device: parseMlsDevEnrollmentResult({ version: 1, authorization: "registration_only", device: entry.device }).device, admissionId:
|
|
1398
|
+
return boundary7(() => {
|
|
1399
|
+
const values = array3(value, MLS_DEV_GROUP_ROSTER_MAX).map((item2) => {
|
|
1400
|
+
const entry = readMlsDataRecord(item2, ["device", "admissionId"]);
|
|
1401
|
+
return Object.freeze({ device: parseMlsDevEnrollmentResult({ version: 1, authorization: "registration_only", device: entry.device }).device, admissionId: id4(entry.admissionId) });
|
|
848
1402
|
});
|
|
849
1403
|
for (let i = 1; i < values.length; i++)
|
|
850
1404
|
if (values[i - 1].device.deviceId >= values[i].device.deviceId)
|
|
851
|
-
|
|
1405
|
+
invalid8();
|
|
852
1406
|
return Object.freeze(values);
|
|
853
1407
|
});
|
|
854
1408
|
}
|
|
855
1409
|
__name(parseMlsDevGroupRoster, "parseMlsDevGroupRoster");
|
|
1410
|
+
var MLS_DEV_ROOM_REMOVAL_DEADLINE_MS = 72 * 60 * 60 * 1e3;
|
|
1411
|
+
function parseMlsDevRoomRemoval(value) {
|
|
1412
|
+
return boundary7(() => {
|
|
1413
|
+
const input = readMlsDataRecord(value, ["version", "status", "pending", "closedAtMs"]), devices = /* @__PURE__ */ new Set(), principals = /* @__PURE__ */ new Set();
|
|
1414
|
+
const status = ["pending", "closing", "closed"].includes(input.status) ? input.status : invalid8();
|
|
1415
|
+
const pending = array3(input.pending, MLS_DEV_GROUP_ROSTER_MAX).map((item2) => {
|
|
1416
|
+
const entry = readMlsDataRecord(item2, ["principalId", "deviceIds", "deadlineAtMs"]);
|
|
1417
|
+
const principalId2 = parseMlsDevEnrollmentPrincipal({ type: "human", id: entry.principalId }).id;
|
|
1418
|
+
const deviceIds = array3(entry.deviceIds, MLS_DEV_GROUP_ROSTER_MAX).map(requireMlsUuid);
|
|
1419
|
+
if (principals.has(principalId2)) invalid8();
|
|
1420
|
+
principals.add(principalId2);
|
|
1421
|
+
for (const deviceId of deviceIds) {
|
|
1422
|
+
if (devices.has(deviceId)) invalid8();
|
|
1423
|
+
devices.add(deviceId);
|
|
1424
|
+
}
|
|
1425
|
+
return Object.freeze({ principalId: principalId2, deviceIds: Object.freeze(deviceIds), deadlineAtMs: requireMlsInteger(entry.deadlineAtMs, 1) });
|
|
1426
|
+
});
|
|
1427
|
+
const closedAtMs = input.closedAtMs === null ? null : requireMlsInteger(input.closedAtMs, 1);
|
|
1428
|
+
if (status === "pending" ? !pending.length || closedAtMs !== null : closedAtMs === null || status === "closed" && pending.length) invalid8();
|
|
1429
|
+
return Object.freeze({ version: requireMlsLiteral(input.version, 1), status, pending: Object.freeze(pending), closedAtMs });
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
__name(parseMlsDevRoomRemoval, "parseMlsDevRoomRemoval");
|
|
856
1433
|
function parseMlsDevGroupPreconditions(value) {
|
|
857
|
-
return
|
|
1434
|
+
return boundary7(() => {
|
|
858
1435
|
const input = readMlsDataRecord(value, ["membershipRevision", "admissionId", "targetAdmissionId"]);
|
|
859
1436
|
return Object.freeze({
|
|
860
1437
|
membershipRevision: requireMlsInteger(input.membershipRevision),
|
|
861
|
-
admissionId:
|
|
862
|
-
targetAdmissionId: input.targetAdmissionId === null ? null :
|
|
1438
|
+
admissionId: id4(input.admissionId),
|
|
1439
|
+
targetAdmissionId: input.targetAdmissionId === null ? null : id4(input.targetAdmissionId)
|
|
863
1440
|
});
|
|
864
1441
|
});
|
|
865
1442
|
}
|
|
@@ -883,37 +1460,40 @@ var unsignedFields = [
|
|
|
883
1460
|
];
|
|
884
1461
|
function unsigned(input) {
|
|
885
1462
|
if (typeof input.kind !== "string" || !["create", "add", "remove"].includes(input.kind))
|
|
886
|
-
|
|
1463
|
+
invalid8();
|
|
887
1464
|
const kind = input.kind;
|
|
888
1465
|
const senderCredential = token(input.senderCredential), sender = parseMlsDevCredentialToken(senderCredential).unsigned.claims.device;
|
|
889
1466
|
const expectedHead = input.expectedHead === null ? null : parseMlsDevGroupHead(input.expectedHead);
|
|
890
1467
|
const epoch = requireMlsInteger(input.epoch), preconditions = parseMlsDevGroupPreconditions(input.preconditions), roster = parseMlsDevGroupRoster(input.roster);
|
|
891
|
-
const
|
|
892
|
-
|
|
1468
|
+
const environmentId = requireMlsEnvironmentId(input.environmentId);
|
|
1469
|
+
if (sender.environmentId !== environmentId || roster.some((entry) => entry.device.environmentId !== environmentId))
|
|
1470
|
+
invalid8();
|
|
1471
|
+
const groupInfo = input.groupInfo === null ? null : wire2(input.groupInfo), commit = input.commit === null ? null : wire2(input.commit);
|
|
1472
|
+
const welcome = input.welcome === null ? null : wire2(input.welcome);
|
|
893
1473
|
const keyPackageClaim = input.keyPackageClaim === null ? null : parseMlsDevKeyPackageClaimReceipt(input.keyPackageClaim);
|
|
894
1474
|
const removedDeviceId = input.removedDeviceId === null ? null : requireMlsUuid(input.removedDeviceId);
|
|
895
1475
|
const own = roster.find((entry) => entry.device.deviceId === sender.deviceId);
|
|
896
1476
|
if (!own || JSON.stringify(own.device) !== JSON.stringify(sender) || own.admissionId !== preconditions.admissionId)
|
|
897
|
-
|
|
1477
|
+
invalid8();
|
|
898
1478
|
if (kind === "create") {
|
|
899
1479
|
if (expectedHead !== null || epoch !== 0 || roster.length !== 1 || !groupInfo || commit !== null || welcome !== null || keyPackageClaim !== null || removedDeviceId !== null || preconditions.targetAdmissionId !== null)
|
|
900
|
-
|
|
1480
|
+
invalid8();
|
|
901
1481
|
} else {
|
|
902
1482
|
if (!expectedHead || expectedHead.sequence >= Number.MAX_SAFE_INTEGER || expectedHead.epoch >= Number.MAX_SAFE_INTEGER || epoch !== expectedHead.epoch + 1 || !commit || preconditions.targetAdmissionId === null)
|
|
903
|
-
|
|
1483
|
+
invalid8();
|
|
904
1484
|
if (kind === "add") {
|
|
905
1485
|
const target = roster.find((entry) => entry.device.deviceId === keyPackageClaim?.recipientDeviceId);
|
|
906
1486
|
if (!welcome || !keyPackageClaim || removedDeviceId !== null || !target || keyPackageClaim.claimantDeviceId !== sender.deviceId || keyPackageClaim.preconditions.conversationId !== input.conversationId || keyPackageClaim.preconditions.membershipRevision !== preconditions.membershipRevision || keyPackageClaim.preconditions.admissionId !== preconditions.admissionId || keyPackageClaim.preconditions.targetAdmissionId !== preconditions.targetAdmissionId || target.admissionId !== preconditions.targetAdmissionId)
|
|
907
|
-
|
|
1487
|
+
invalid8();
|
|
908
1488
|
} else if (!removedDeviceId || removedDeviceId === sender.deviceId || roster.some((entry) => entry.device.deviceId === removedDeviceId) || welcome !== null || keyPackageClaim !== null)
|
|
909
|
-
|
|
1489
|
+
invalid8();
|
|
910
1490
|
}
|
|
911
1491
|
return bounded5({
|
|
912
1492
|
version: requireMlsLiteral(input.version, 1),
|
|
913
|
-
environmentId
|
|
1493
|
+
environmentId,
|
|
914
1494
|
kind,
|
|
915
|
-
conversationId:
|
|
916
|
-
groupId:
|
|
1495
|
+
conversationId: id4(input.conversationId),
|
|
1496
|
+
groupId: encoded4(input.groupId, 32),
|
|
917
1497
|
senderCredential,
|
|
918
1498
|
expectedHead,
|
|
919
1499
|
epoch,
|
|
@@ -928,25 +1508,25 @@ function unsigned(input) {
|
|
|
928
1508
|
}
|
|
929
1509
|
__name(unsigned, "unsigned");
|
|
930
1510
|
function parseMlsDevGroupControl(value) {
|
|
931
|
-
return
|
|
1511
|
+
return boundary7(() => {
|
|
932
1512
|
const input = readMlsDataRecord(value, [...unsignedFields, "proof"]);
|
|
933
|
-
return bounded5({ ...unsigned(input), proof:
|
|
1513
|
+
return bounded5({ ...unsigned(input), proof: encoded4(input.proof, 64) });
|
|
934
1514
|
});
|
|
935
1515
|
}
|
|
936
1516
|
__name(parseMlsDevGroupControl, "parseMlsDevGroupControl");
|
|
937
1517
|
function parseMlsDevGroupAcceptedControl(value) {
|
|
938
|
-
return
|
|
1518
|
+
return boundary7(() => {
|
|
939
1519
|
const input = readMlsDataRecord(value, ["version", "authorization", "operationId", "control", "head", "acceptedAtMs", "membershipRevision"]);
|
|
940
1520
|
const control = parseMlsDevGroupControl(input.control), head = parseMlsDevGroupHead(input.head);
|
|
941
1521
|
if (head.sequence !== (control.expectedHead?.sequence ?? 0) + 1 || head.epoch !== control.epoch)
|
|
942
|
-
|
|
1522
|
+
invalid8();
|
|
943
1523
|
const membershipRevision = requireMlsInteger(input.membershipRevision);
|
|
944
1524
|
if (membershipRevision !== control.preconditions.membershipRevision && !(control.kind === "remove" && membershipRevision === control.preconditions.membershipRevision + 1))
|
|
945
|
-
|
|
1525
|
+
invalid8();
|
|
946
1526
|
return bounded5({
|
|
947
1527
|
version: requireMlsLiteral(input.version, 1),
|
|
948
1528
|
authorization: requireMlsLiteral(input.authorization, "group_control_only"),
|
|
949
|
-
operationId:
|
|
1529
|
+
operationId: id4(input.operationId),
|
|
950
1530
|
control,
|
|
951
1531
|
head,
|
|
952
1532
|
acceptedAtMs: requireMlsInteger(input.acceptedAtMs),
|
|
@@ -955,40 +1535,65 @@ function parseMlsDevGroupAcceptedControl(value) {
|
|
|
955
1535
|
});
|
|
956
1536
|
}
|
|
957
1537
|
__name(parseMlsDevGroupAcceptedControl, "parseMlsDevGroupAcceptedControl");
|
|
1538
|
+
function parseMlsDevGroupAdmissionsResult(value) {
|
|
1539
|
+
return boundary7(() => {
|
|
1540
|
+
const input = readMlsDataRecord(value, ["version", "conversationId", "membershipRevision", "candidates", "waitingPrincipalIds"]);
|
|
1541
|
+
const candidates = array3(input.candidates, MLS_DEV_GROUP_ADMISSION_MAX_CANDIDATES).map((raw) => {
|
|
1542
|
+
const entry = readMlsDataRecord(raw, ["principal", "deviceId", "targetAdmissionId"]), principal2 = readMlsDataRecord(entry.principal, ["type", "id"]);
|
|
1543
|
+
if (principal2.type !== "human" && principal2.type !== "ai_agent") invalid8();
|
|
1544
|
+
return Object.freeze({
|
|
1545
|
+
principal: Object.freeze({ type: principal2.type, id: id4(principal2.id) }),
|
|
1546
|
+
deviceId: requireMlsUuid(entry.deviceId),
|
|
1547
|
+
targetAdmissionId: id4(entry.targetAdmissionId)
|
|
1548
|
+
});
|
|
1549
|
+
});
|
|
1550
|
+
if (new Set(candidates.map((entry) => entry.deviceId)).size !== candidates.length) invalid8();
|
|
1551
|
+
const waiting = array3(input.waitingPrincipalIds, 32).map(id4);
|
|
1552
|
+
if (new Set(waiting).size !== waiting.length) invalid8();
|
|
1553
|
+
return bounded5(Object.freeze({
|
|
1554
|
+
version: requireMlsLiteral(input.version, 1),
|
|
1555
|
+
conversationId: id4(input.conversationId),
|
|
1556
|
+
membershipRevision: requireMlsInteger(input.membershipRevision, 1),
|
|
1557
|
+
candidates: Object.freeze(candidates),
|
|
1558
|
+
waitingPrincipalIds: Object.freeze(waiting)
|
|
1559
|
+
}), 64 * 1024);
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
__name(parseMlsDevGroupAdmissionsResult, "parseMlsDevGroupAdmissionsResult");
|
|
958
1563
|
function parseMlsDevGroupQuery(value) {
|
|
959
|
-
return
|
|
1564
|
+
return boundary7(() => {
|
|
960
1565
|
const input = readMlsDataRecord(value, ["version", "conversationId", "credential", "afterSequence", "limit"]);
|
|
961
|
-
const
|
|
962
|
-
if (
|
|
963
|
-
|
|
1566
|
+
const limit2 = requireMlsInteger(input.limit, 1);
|
|
1567
|
+
if (limit2 > MLS_DEV_GROUP_QUERY_MAX_CONTROLS)
|
|
1568
|
+
invalid8();
|
|
964
1569
|
return bounded5({
|
|
965
1570
|
version: requireMlsLiteral(input.version, 1),
|
|
966
|
-
conversationId:
|
|
1571
|
+
conversationId: id4(input.conversationId),
|
|
967
1572
|
credential: token(input.credential),
|
|
968
1573
|
afterSequence: requireMlsInteger(input.afterSequence),
|
|
969
|
-
limit
|
|
1574
|
+
limit: limit2
|
|
970
1575
|
}, 16 * 1024);
|
|
971
1576
|
});
|
|
972
1577
|
}
|
|
973
1578
|
__name(parseMlsDevGroupQuery, "parseMlsDevGroupQuery");
|
|
974
1579
|
function parseMlsDevGroupQueryResult(value) {
|
|
975
|
-
return
|
|
1580
|
+
return boundary7(() => {
|
|
976
1581
|
const input = readMlsDataRecord(value, ["version", "conversationId", "marker", "membershipRevision", "admissionId", "roster", "controls", "nextSequence", "hasMore", "terminal"]);
|
|
977
|
-
const conversationId =
|
|
978
|
-
const controls =
|
|
979
|
-
const nextSequence = requireMlsInteger(input.nextSequence), terminal =
|
|
1582
|
+
const conversationId = id4(input.conversationId), marker = input.marker === null ? null : parseMlsDevGroupMarker(input.marker);
|
|
1583
|
+
const controls = array3(input.controls, MLS_DEV_GROUP_QUERY_MAX_CONTROLS).map(parseMlsDevGroupAcceptedControl);
|
|
1584
|
+
const nextSequence = requireMlsInteger(input.nextSequence), terminal = boolean3(input.terminal), hasMore = boolean3(input.hasMore);
|
|
980
1585
|
for (let i = 0; i < controls.length; i++) {
|
|
981
1586
|
if (!marker || controls[i].control.conversationId !== conversationId || controls[i].control.groupId !== marker.groupId || controls[i].head.sequence > marker.head.sequence || i > 0 && controls[i].head.sequence !== controls[i - 1].head.sequence + 1)
|
|
982
|
-
|
|
1587
|
+
invalid8();
|
|
983
1588
|
}
|
|
984
1589
|
if (controls.length && controls.at(-1).head.sequence !== nextSequence || marker && nextSequence > marker.head.sequence || !marker && (controls.length || nextSequence || terminal || hasMore))
|
|
985
|
-
|
|
1590
|
+
invalid8();
|
|
986
1591
|
return bounded5({
|
|
987
1592
|
version: requireMlsLiteral(input.version, 1),
|
|
988
1593
|
conversationId,
|
|
989
1594
|
marker,
|
|
990
1595
|
membershipRevision: requireMlsInteger(input.membershipRevision),
|
|
991
|
-
admissionId:
|
|
1596
|
+
admissionId: id4(input.admissionId),
|
|
992
1597
|
roster: parseMlsDevGroupRoster(input.roster),
|
|
993
1598
|
controls: Object.freeze(controls),
|
|
994
1599
|
nextSequence,
|
|
@@ -999,42 +1604,43 @@ function parseMlsDevGroupQueryResult(value) {
|
|
|
999
1604
|
}
|
|
1000
1605
|
__name(parseMlsDevGroupQueryResult, "parseMlsDevGroupQueryResult");
|
|
1001
1606
|
function parseMlsDevGroupContext(value) {
|
|
1002
|
-
return
|
|
1607
|
+
return boundary7(() => {
|
|
1003
1608
|
const input = readMlsDataRecord(value, ["version", "environmentId", "conversationId", "groupId", "deviceId", "installationId", "revision"], ["version", "environmentId", "conversationId", "groupId", "deviceId", "installationId", "revision", "protocol"]);
|
|
1004
1609
|
const revision2 = requireMlsInteger(input.revision);
|
|
1005
1610
|
if (revision2 >= Number.MAX_SAFE_INTEGER)
|
|
1006
|
-
|
|
1611
|
+
invalid8();
|
|
1007
1612
|
return Object.freeze({
|
|
1008
1613
|
version: requireMlsLiteral(input.version, 1),
|
|
1009
|
-
environmentId:
|
|
1010
|
-
conversationId:
|
|
1011
|
-
groupId:
|
|
1614
|
+
environmentId: requireMlsEnvironmentId(input.environmentId),
|
|
1615
|
+
conversationId: id4(input.conversationId),
|
|
1616
|
+
groupId: encoded4(input.groupId, 32),
|
|
1012
1617
|
deviceId: requireMlsUuid(input.deviceId),
|
|
1013
1618
|
installationId: parseMlsDevInstallationId(input.installationId),
|
|
1014
1619
|
revision: revision2,
|
|
1015
|
-
...Object.hasOwn(input, "protocol") ? { protocol:
|
|
1620
|
+
...Object.hasOwn(input, "protocol") ? { protocol: textProtocol(input.protocol) } : {}
|
|
1016
1621
|
});
|
|
1017
1622
|
});
|
|
1018
1623
|
}
|
|
1019
1624
|
__name(parseMlsDevGroupContext, "parseMlsDevGroupContext");
|
|
1020
1625
|
function parseMlsDevGroupCommand(value) {
|
|
1021
|
-
return
|
|
1626
|
+
return boundary7(() => {
|
|
1022
1627
|
if (!value || typeof value !== "object")
|
|
1023
|
-
|
|
1628
|
+
invalid8();
|
|
1024
1629
|
const descriptor = Object.getOwnPropertyDescriptor(value, "op");
|
|
1025
1630
|
if (!descriptor || !Object.hasOwn(descriptor, "value"))
|
|
1026
|
-
|
|
1631
|
+
invalid8();
|
|
1027
1632
|
const op = descriptor.value;
|
|
1028
1633
|
if (op === "process_application") {
|
|
1029
1634
|
const input2 = readMlsDataRecord(value, ["op", "entry"]), entry = parseMlsDevGroupFeedEntry(input2.entry);
|
|
1030
|
-
if (entry.kind !== "application")
|
|
1031
|
-
|
|
1635
|
+
if (entry.kind !== "application" || !entry.receiptProof)
|
|
1636
|
+
invalid8();
|
|
1032
1637
|
return bounded5({ op, entry });
|
|
1033
1638
|
}
|
|
1034
1639
|
if (op === "encrypt_text") {
|
|
1035
|
-
const
|
|
1640
|
+
const extras = present(value, ["replyTo", "mentions"]);
|
|
1641
|
+
const input2 = readMlsDataRecord(value, ["op", "operationId", "messageId", "credential", "expectedHead", "preconditions", "text", ...extras]);
|
|
1036
1642
|
const credential = token(input2.credential), senderDeviceId = parseMlsDevCredentialToken(credential).unsigned.claims.device.deviceId;
|
|
1037
|
-
const
|
|
1643
|
+
const text5 = parseMlsDevTextPayload({
|
|
1038
1644
|
version: 1,
|
|
1039
1645
|
kind: "text",
|
|
1040
1646
|
conversationId: "validation",
|
|
@@ -1042,117 +1648,283 @@ function parseMlsDevGroupCommand(value) {
|
|
|
1042
1648
|
messageId: input2.messageId,
|
|
1043
1649
|
operationId: input2.operationId,
|
|
1044
1650
|
senderDeviceId,
|
|
1045
|
-
text: input2.text
|
|
1651
|
+
text: input2.text,
|
|
1652
|
+
...pick(input2, extras)
|
|
1046
1653
|
});
|
|
1047
1654
|
return bounded5({
|
|
1048
1655
|
op,
|
|
1049
|
-
operationId:
|
|
1050
|
-
messageId:
|
|
1656
|
+
operationId: text5.operationId,
|
|
1657
|
+
messageId: text5.messageId,
|
|
1051
1658
|
credential,
|
|
1052
1659
|
expectedHead: parseMlsDevGroupHead(input2.expectedHead),
|
|
1053
1660
|
preconditions: parseMlsDevApplicationPreconditions(input2.preconditions),
|
|
1054
|
-
text:
|
|
1661
|
+
text: text5.text,
|
|
1662
|
+
...textExtras(text5)
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
if (op === "encrypt_item") {
|
|
1666
|
+
const input2 = readMlsDataRecord(value, ["op", "operationId", "messageId", "credential", "expectedHead", "preconditions", "item"]);
|
|
1667
|
+
const credential = token(input2.credential), senderDeviceId = parseMlsDevCredentialToken(credential).unsigned.claims.device.deviceId;
|
|
1668
|
+
const parsed = parseMlsDevItemPayload({
|
|
1669
|
+
version: 1,
|
|
1670
|
+
kind: "item",
|
|
1671
|
+
conversationId: "validation",
|
|
1672
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1673
|
+
messageId: input2.messageId,
|
|
1674
|
+
operationId: input2.operationId,
|
|
1675
|
+
senderDeviceId,
|
|
1676
|
+
item: input2.item
|
|
1677
|
+
});
|
|
1678
|
+
return bounded5({
|
|
1679
|
+
op,
|
|
1680
|
+
operationId: parsed.operationId,
|
|
1681
|
+
messageId: parsed.messageId,
|
|
1682
|
+
credential,
|
|
1683
|
+
expectedHead: parseMlsDevGroupHead(input2.expectedHead),
|
|
1684
|
+
preconditions: parseMlsDevApplicationPreconditions(input2.preconditions),
|
|
1685
|
+
item: parsed.item
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
if (op === "encrypt_interaction") {
|
|
1689
|
+
const input2 = readMlsDataRecord(value, ["op", "operationId", "messageId", "credential", "expectedHead", "preconditions", "interaction", "body", "seal"]);
|
|
1690
|
+
const credential = token(input2.credential), interaction = parseMlsDevInteractionBinding(input2.interaction);
|
|
1691
|
+
const body = readMlsDataRecord(input2.body, Reflect.ownKeys(input2.body ?? {}).filter((key) => typeof key === "string"));
|
|
1692
|
+
let seal = null;
|
|
1693
|
+
if (interaction.role === "request") {
|
|
1694
|
+
if (Object.hasOwn(body, "secretKey") || input2.seal !== null)
|
|
1695
|
+
invalid8();
|
|
1696
|
+
const parsed = parseMlsDevInteractionRequestBody(interaction.kind === "secret" ? { ...body, secretKey: { kem: "x25519", publicKey: "A".repeat(43) } } : body);
|
|
1697
|
+
if (parsed.requestId !== interaction.requestId || parsed.kind !== interaction.kind || parsed.expiresAtMs !== interaction.expiresAtMs || parsed.responderIds[0] !== interaction.responderIds[0])
|
|
1698
|
+
invalid8();
|
|
1699
|
+
} else if (interaction.role === "response") {
|
|
1700
|
+
if (input2.seal === null) {
|
|
1701
|
+
const parsed = parseMlsDevInteractionResponseBody(body);
|
|
1702
|
+
if (parsed.requestId !== interaction.requestId || parsed.agentId !== interaction.agentId || "sealed" in parsed.answer)
|
|
1703
|
+
invalid8();
|
|
1704
|
+
} else {
|
|
1705
|
+
const fields3 = readMlsDataRecord(input2.seal, ["text", "publicKey", "requesterDeviceId"]), ids2 = readMlsDataRecord(body, ["requestId", "agentId"]);
|
|
1706
|
+
if (ids2.requestId !== interaction.requestId || ids2.agentId !== interaction.agentId || typeof fields3.text !== "string" || !fields3.text.trim() || [...fields3.text].length > MLS_DEV_INTERACTION_LIMITS.text || utf84.encode(fields3.text).length > MLS_DEV_SECRET_ANSWER_MAX_BYTES - 16 || new TextDecoder("utf-8", { fatal: true }).decode(utf84.encode(fields3.text)) !== fields3.text)
|
|
1707
|
+
invalid8();
|
|
1708
|
+
seal = Object.freeze({ text: fields3.text, publicKey: encoded4(fields3.publicKey, 32), requesterDeviceId: requireMlsUuid(fields3.requesterDeviceId) });
|
|
1709
|
+
}
|
|
1710
|
+
} else if (input2.seal !== null || parseMlsDevInteractionWithdrawBody(body).requestId !== interaction.requestId)
|
|
1711
|
+
invalid8();
|
|
1712
|
+
const ids = parseMlsDevTextPayload({
|
|
1713
|
+
version: 1,
|
|
1714
|
+
kind: "text",
|
|
1715
|
+
conversationId: "validation",
|
|
1716
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1717
|
+
messageId: input2.messageId,
|
|
1718
|
+
operationId: input2.operationId,
|
|
1719
|
+
senderDeviceId: parseMlsDevCredentialToken(credential).unsigned.claims.device.deviceId,
|
|
1720
|
+
text: "x"
|
|
1721
|
+
});
|
|
1722
|
+
if (utf84.encode(JSON.stringify(body)).length > MLS_DEV_TEXT_MAX_BYTES)
|
|
1723
|
+
invalid8();
|
|
1724
|
+
return bounded5({
|
|
1725
|
+
op,
|
|
1726
|
+
operationId: ids.operationId,
|
|
1727
|
+
messageId: ids.messageId,
|
|
1728
|
+
credential,
|
|
1729
|
+
expectedHead: parseMlsDevGroupHead(input2.expectedHead),
|
|
1730
|
+
preconditions: parseMlsDevApplicationPreconditions(input2.preconditions),
|
|
1731
|
+
interaction,
|
|
1732
|
+
body: Object.freeze(body),
|
|
1733
|
+
seal
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
if (op === "open_secret_answer") {
|
|
1737
|
+
const input2 = readMlsDataRecord(value, ["op", "requestId", "responderId", "responderDeviceId", "sealed"]), sealed = readMlsDataRecord(input2.sealed, ["enc", "ciphertext"]);
|
|
1738
|
+
const binding3 = parseMlsDevInteractionBinding({ role: "response", requestId: input2.requestId, agentId: input2.responderId });
|
|
1739
|
+
return bounded5({
|
|
1740
|
+
op,
|
|
1741
|
+
requestId: binding3.requestId,
|
|
1742
|
+
responderId: id4(input2.responderId),
|
|
1743
|
+
responderDeviceId: requireMlsUuid(input2.responderDeviceId),
|
|
1744
|
+
sealed: Object.freeze({ enc: encoded4(sealed.enc, 32), ciphertext: sealedCiphertext(sealed.ciphertext) })
|
|
1055
1745
|
});
|
|
1056
1746
|
}
|
|
1057
1747
|
if (op === "discard") {
|
|
1058
1748
|
const input2 = readMlsDataRecord(value, ["op", "operationId", "control"]);
|
|
1059
|
-
return bounded5({ op, operationId:
|
|
1749
|
+
return bounded5({ op, operationId: id4(input2.operationId), control: parseMlsDevGroupControl(input2.control) });
|
|
1060
1750
|
}
|
|
1061
1751
|
if (op === "finalize") {
|
|
1062
1752
|
const input2 = readMlsDataRecord(value, ["op", "accepted"]);
|
|
1063
1753
|
return bounded5({ op, accepted: parseMlsDevGroupAcceptedControl(input2.accepted) });
|
|
1064
1754
|
}
|
|
1065
1755
|
if (op === "join" || op === "process") {
|
|
1066
|
-
const input2 = readMlsDataRecord(value, op === "join" ? ["op", "credential", "accepted", "keyPackageId"] : ["op", "credential", "accepted"]);
|
|
1067
|
-
const common2 = {
|
|
1756
|
+
const input2 = readMlsDataRecord(value, op === "join" ? ["op", "credential", "accepted", "receiptProof", "feedOrdinal", "keyPackageId"] : ["op", "credential", "accepted", "receiptProof", "feedOrdinal"]);
|
|
1757
|
+
const common2 = {
|
|
1758
|
+
credential: token(input2.credential),
|
|
1759
|
+
accepted: parseMlsDevGroupAcceptedControl(input2.accepted),
|
|
1760
|
+
receiptProof: parseMlsDevFeedReceiptProof(input2.receiptProof),
|
|
1761
|
+
feedOrdinal: requireMlsInteger(input2.feedOrdinal, 1)
|
|
1762
|
+
};
|
|
1068
1763
|
if (op === "join") {
|
|
1069
1764
|
if (common2.accepted.control.kind !== "add")
|
|
1070
|
-
|
|
1765
|
+
invalid8();
|
|
1071
1766
|
return bounded5({ op, ...common2, keyPackageId: requireMlsUuid(input2.keyPackageId) });
|
|
1072
1767
|
}
|
|
1073
1768
|
return bounded5({ op, ...common2 });
|
|
1074
1769
|
}
|
|
1075
1770
|
const common = ["op", "operationId", "credential", "preconditions"];
|
|
1076
1771
|
const input = readMlsDataRecord(value, op === "create" ? common : op === "add" ? [...common, "expectedHead", "keyPackageClaim"] : op === "remove" ? [...common, "expectedHead", "removedDeviceId"] : []);
|
|
1077
|
-
const
|
|
1772
|
+
const fields2 = { operationId: id4(input.operationId), credential: token(input.credential), preconditions: parseMlsDevGroupPreconditions(input.preconditions) };
|
|
1078
1773
|
if (op === "create") {
|
|
1079
|
-
if (
|
|
1080
|
-
|
|
1081
|
-
return bounded5({ op, ...
|
|
1774
|
+
if (fields2.preconditions.targetAdmissionId !== null)
|
|
1775
|
+
invalid8();
|
|
1776
|
+
return bounded5({ op, ...fields2 });
|
|
1082
1777
|
}
|
|
1083
|
-
if (
|
|
1084
|
-
|
|
1778
|
+
if (fields2.preconditions.targetAdmissionId === null)
|
|
1779
|
+
invalid8();
|
|
1085
1780
|
const expectedHead = parseMlsDevGroupHead(input.expectedHead);
|
|
1086
1781
|
if (op === "add")
|
|
1087
1782
|
return bounded5({
|
|
1088
1783
|
op,
|
|
1089
|
-
...
|
|
1784
|
+
...fields2,
|
|
1090
1785
|
expectedHead,
|
|
1091
1786
|
keyPackageClaim: parseMlsDevKeyPackageClaimReceipt(input.keyPackageClaim)
|
|
1092
1787
|
});
|
|
1093
1788
|
if (op === "remove")
|
|
1094
1789
|
return bounded5({
|
|
1095
1790
|
op,
|
|
1096
|
-
...
|
|
1791
|
+
...fields2,
|
|
1097
1792
|
expectedHead,
|
|
1098
1793
|
removedDeviceId: requireMlsUuid(input.removedDeviceId)
|
|
1099
1794
|
});
|
|
1100
|
-
return
|
|
1795
|
+
return invalid8();
|
|
1101
1796
|
});
|
|
1102
1797
|
}
|
|
1103
1798
|
__name(parseMlsDevGroupCommand, "parseMlsDevGroupCommand");
|
|
1104
1799
|
function parseMlsDevGroupPreparation(value) {
|
|
1105
|
-
return
|
|
1800
|
+
return boundary7(() => {
|
|
1106
1801
|
const input = readMlsDataRecord(value, ["wrappedState", "payload"]);
|
|
1107
1802
|
if (typeof input.wrappedState !== "string" || input.wrappedState.length > MLS_DEV_GROUP_WRAPPED_MAX_BYTES || !input.wrappedState.startsWith("v1:"))
|
|
1108
|
-
|
|
1803
|
+
invalid8();
|
|
1109
1804
|
const body = input.wrappedState.slice(3);
|
|
1110
1805
|
if (body.length < 40 || body.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(body))
|
|
1111
|
-
|
|
1806
|
+
invalid8();
|
|
1112
1807
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1113
1808
|
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0 || !body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0)
|
|
1114
|
-
|
|
1809
|
+
invalid8();
|
|
1115
1810
|
const kind = Object.getOwnPropertyDescriptor(input.payload ?? {}, "kind");
|
|
1116
1811
|
if (!kind || !Object.hasOwn(kind, "value"))
|
|
1117
|
-
|
|
1812
|
+
invalid8();
|
|
1118
1813
|
let payload;
|
|
1119
1814
|
if (kind.value === "application") {
|
|
1120
|
-
const
|
|
1121
|
-
payload = Object.freeze({
|
|
1815
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "envelope", ...present(input.payload, ["payload"])]);
|
|
1816
|
+
payload = Object.freeze({
|
|
1817
|
+
kind: "application",
|
|
1818
|
+
envelope: parseMlsDevApplicationEnvelope(fields2.envelope),
|
|
1819
|
+
...fields2.payload === void 0 ? {} : { payload: plaintextJson(fields2.payload) }
|
|
1820
|
+
});
|
|
1821
|
+
} else if (kind.value === "item") {
|
|
1822
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "payload", "feedOrdinal"]);
|
|
1823
|
+
const ids = parseMlsDevTextPayload({
|
|
1824
|
+
version: 1,
|
|
1825
|
+
kind: "text",
|
|
1826
|
+
conversationId: "validation",
|
|
1827
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1828
|
+
messageId: fields2.messageId,
|
|
1829
|
+
operationId: fields2.operationId,
|
|
1830
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1831
|
+
text: "x"
|
|
1832
|
+
});
|
|
1833
|
+
payload = Object.freeze({
|
|
1834
|
+
kind: "item",
|
|
1835
|
+
messageId: ids.messageId,
|
|
1836
|
+
operationId: ids.operationId,
|
|
1837
|
+
senderDeviceId: ids.senderDeviceId,
|
|
1838
|
+
payload: plaintextJson(fields2.payload),
|
|
1839
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1840
|
+
});
|
|
1122
1841
|
} else if (kind.value === "text") {
|
|
1123
|
-
const
|
|
1124
|
-
const
|
|
1842
|
+
const extras = present(input.payload, ["replyTo", "mentions"]);
|
|
1843
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "text", "feedOrdinal", ...extras]);
|
|
1844
|
+
const text5 = parseMlsDevTextPayload({
|
|
1125
1845
|
version: 1,
|
|
1126
1846
|
kind: "text",
|
|
1127
1847
|
conversationId: "validation",
|
|
1128
1848
|
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1129
|
-
messageId:
|
|
1130
|
-
operationId:
|
|
1131
|
-
senderDeviceId:
|
|
1132
|
-
text:
|
|
1849
|
+
messageId: fields2.messageId,
|
|
1850
|
+
operationId: fields2.operationId,
|
|
1851
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1852
|
+
text: fields2.text,
|
|
1853
|
+
...pick(fields2, extras)
|
|
1133
1854
|
});
|
|
1134
1855
|
payload = Object.freeze({
|
|
1135
1856
|
kind: "text",
|
|
1136
|
-
messageId:
|
|
1137
|
-
operationId:
|
|
1138
|
-
senderDeviceId:
|
|
1139
|
-
text:
|
|
1140
|
-
|
|
1857
|
+
messageId: text5.messageId,
|
|
1858
|
+
operationId: text5.operationId,
|
|
1859
|
+
senderDeviceId: text5.senderDeviceId,
|
|
1860
|
+
text: text5.text,
|
|
1861
|
+
...textExtras(text5),
|
|
1862
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1863
|
+
});
|
|
1864
|
+
} else if (kind.value === "interaction_application") {
|
|
1865
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "envelope", "payload"]);
|
|
1866
|
+
payload = Object.freeze({ kind: "interaction_application", envelope: parseMlsDevApplicationEnvelope(fields2.envelope), payload: plaintextJson(fields2.payload) });
|
|
1867
|
+
if (!payload.envelope.interaction)
|
|
1868
|
+
invalid8();
|
|
1869
|
+
} else if (kind.value === "interaction") {
|
|
1870
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "payload", "feedOrdinal"]);
|
|
1871
|
+
const ids = parseMlsDevTextPayload({
|
|
1872
|
+
version: 1,
|
|
1873
|
+
kind: "text",
|
|
1874
|
+
conversationId: "validation",
|
|
1875
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1876
|
+
messageId: fields2.messageId,
|
|
1877
|
+
operationId: fields2.operationId,
|
|
1878
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1879
|
+
text: "x"
|
|
1880
|
+
});
|
|
1881
|
+
payload = Object.freeze({
|
|
1882
|
+
kind: "interaction",
|
|
1883
|
+
messageId: ids.messageId,
|
|
1884
|
+
operationId: ids.operationId,
|
|
1885
|
+
senderDeviceId: ids.senderDeviceId,
|
|
1886
|
+
payload: plaintextJson(fields2.payload),
|
|
1887
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1141
1888
|
});
|
|
1889
|
+
} else if (kind.value === "invalid") {
|
|
1890
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "feedOrdinal"]);
|
|
1891
|
+
const ids = parseMlsDevTextPayload({
|
|
1892
|
+
version: 1,
|
|
1893
|
+
kind: "text",
|
|
1894
|
+
conversationId: "validation",
|
|
1895
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1896
|
+
messageId: fields2.messageId,
|
|
1897
|
+
operationId: fields2.operationId,
|
|
1898
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1899
|
+
text: "x"
|
|
1900
|
+
});
|
|
1901
|
+
payload = Object.freeze({
|
|
1902
|
+
kind: "invalid",
|
|
1903
|
+
messageId: ids.messageId,
|
|
1904
|
+
operationId: ids.operationId,
|
|
1905
|
+
senderDeviceId: ids.senderDeviceId,
|
|
1906
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1907
|
+
});
|
|
1908
|
+
} else if (kind.value === "secret") {
|
|
1909
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "requestId", "text"]);
|
|
1910
|
+
const binding3 = parseMlsDevInteractionBinding({ role: "withdraw", requestId: fields2.requestId });
|
|
1911
|
+
if (typeof fields2.text !== "string" || !fields2.text.trim() || [...fields2.text].length > MLS_DEV_INTERACTION_LIMITS.text || new TextDecoder("utf-8", { fatal: true }).decode(utf84.encode(fields2.text)) !== fields2.text)
|
|
1912
|
+
invalid8();
|
|
1913
|
+
payload = Object.freeze({ kind: "secret", requestId: binding3.requestId, text: fields2.text });
|
|
1142
1914
|
} else if (kind.value === "control") {
|
|
1143
|
-
const
|
|
1144
|
-
payload = Object.freeze({ kind: "control", control: parseMlsDevGroupControl(
|
|
1915
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "control"]);
|
|
1916
|
+
payload = Object.freeze({ kind: "control", control: parseMlsDevGroupControl(fields2.control) });
|
|
1145
1917
|
} else if (kind.value === "discarded") {
|
|
1146
|
-
const
|
|
1147
|
-
payload = Object.freeze({ kind: "discarded", operationId:
|
|
1918
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "operationId", "head"]);
|
|
1919
|
+
payload = Object.freeze({ kind: "discarded", operationId: id4(fields2.operationId), head: fields2.head === null ? null : parseMlsDevGroupHead(fields2.head) });
|
|
1148
1920
|
} else {
|
|
1149
|
-
const
|
|
1921
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "head", "roster", "removed", "consumedKeyPackageId"]);
|
|
1150
1922
|
payload = Object.freeze({
|
|
1151
|
-
kind: requireMlsLiteral(
|
|
1152
|
-
head: parseMlsDevGroupHead(
|
|
1153
|
-
roster: parseMlsDevGroupRoster(
|
|
1154
|
-
removed:
|
|
1155
|
-
consumedKeyPackageId:
|
|
1923
|
+
kind: requireMlsLiteral(fields2.kind, "applied"),
|
|
1924
|
+
head: parseMlsDevGroupHead(fields2.head),
|
|
1925
|
+
roster: parseMlsDevGroupRoster(fields2.roster),
|
|
1926
|
+
removed: boolean3(fields2.removed),
|
|
1927
|
+
consumedKeyPackageId: fields2.consumedKeyPackageId === null ? null : requireMlsUuid(fields2.consumedKeyPackageId)
|
|
1156
1928
|
});
|
|
1157
1929
|
}
|
|
1158
1930
|
return bounded5({ wrappedState: input.wrappedState, payload }, MLS_DEV_GROUP_PREPARATION_MAX_BYTES);
|
|
@@ -1162,9 +1934,25 @@ __name(parseMlsDevGroupPreparation, "parseMlsDevGroupPreparation");
|
|
|
1162
1934
|
|
|
1163
1935
|
// ../backend-contracts/src/mlsDevApplication.ts
|
|
1164
1936
|
var MLS_DEV_TEXT_PROTOCOL = "canon.mls.dev-text.v1";
|
|
1937
|
+
var MLS_DEV_TEXT_PROTOCOL_V2 = "canon.mls.dev-text.v2";
|
|
1938
|
+
var MLS_DEV_TEXT_PROTOCOL_V3 = "canon.mls.dev-text.v3";
|
|
1939
|
+
var MLS_DEV_TEXT_PROTOCOLS = [MLS_DEV_TEXT_PROTOCOL, MLS_DEV_TEXT_PROTOCOL_V2, MLS_DEV_TEXT_PROTOCOL_V3];
|
|
1940
|
+
function isMlsDevTextProtocol(value) {
|
|
1941
|
+
return MLS_DEV_TEXT_PROTOCOLS.some((protocol) => protocol === value);
|
|
1942
|
+
}
|
|
1943
|
+
__name(isMlsDevTextProtocol, "isMlsDevTextProtocol");
|
|
1944
|
+
function mlsDevProtocolCarriesCards(value) {
|
|
1945
|
+
return value === MLS_DEV_TEXT_PROTOCOL_V2 || value === MLS_DEV_TEXT_PROTOCOL_V3;
|
|
1946
|
+
}
|
|
1947
|
+
__name(mlsDevProtocolCarriesCards, "mlsDevProtocolCarriesCards");
|
|
1948
|
+
var MLS_DEV_REPLY_QUOTE_MAX_CHARACTERS = 1024;
|
|
1949
|
+
var MLS_DEV_MENTIONS_MAX = 32;
|
|
1950
|
+
var MLS_DEV_INTERACTION_ID_PATTERN = /^[A-Za-z0-9_.:-]{1,160}$/;
|
|
1951
|
+
var MLS_DEV_INTERACTION_KINDS = ["approval", "question", "secret"];
|
|
1165
1952
|
var MLS_DEV_APPLICATION_TLS_MAX_BYTES = 256 * 1024;
|
|
1166
1953
|
var MLS_DEV_APPLICATION_MAX_BYTES = 384 * 1024;
|
|
1167
1954
|
var MLS_DEV_TEXT_MAX_BYTES = 32 * 1024;
|
|
1955
|
+
var MLS_DEV_ITEM_TYPES = ["image", "video", "audio", "file"];
|
|
1168
1956
|
var MlsDevApplicationValidationError = class extends Error {
|
|
1169
1957
|
static {
|
|
1170
1958
|
__name(this, "MlsDevApplicationValidationError");
|
|
@@ -1174,125 +1962,245 @@ var MlsDevApplicationValidationError = class extends Error {
|
|
|
1174
1962
|
super("INVALID_MLS_DEV_APPLICATION");
|
|
1175
1963
|
this.name = "MlsDevApplicationValidationError";
|
|
1176
1964
|
}
|
|
1177
|
-
};
|
|
1178
|
-
var
|
|
1179
|
-
function
|
|
1965
|
+
};
|
|
1966
|
+
var utf85 = new TextEncoder();
|
|
1967
|
+
function invalid9() {
|
|
1180
1968
|
throw new MlsDevApplicationValidationError();
|
|
1181
1969
|
}
|
|
1182
|
-
__name(
|
|
1183
|
-
function
|
|
1970
|
+
__name(invalid9, "invalid");
|
|
1971
|
+
function boundary8(read) {
|
|
1184
1972
|
try {
|
|
1185
1973
|
return read();
|
|
1186
1974
|
} catch {
|
|
1187
|
-
return
|
|
1975
|
+
return invalid9();
|
|
1188
1976
|
}
|
|
1189
1977
|
}
|
|
1190
|
-
__name(
|
|
1978
|
+
__name(boundary8, "boundary");
|
|
1191
1979
|
function bounded6(value) {
|
|
1192
|
-
if (
|
|
1193
|
-
|
|
1980
|
+
if (utf85.encode(JSON.stringify(value)).length > MLS_DEV_APPLICATION_MAX_BYTES)
|
|
1981
|
+
invalid9();
|
|
1194
1982
|
return Object.freeze(value);
|
|
1195
1983
|
}
|
|
1196
1984
|
__name(bounded6, "bounded");
|
|
1197
|
-
function
|
|
1985
|
+
function id5(value) {
|
|
1198
1986
|
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
1199
|
-
|
|
1987
|
+
invalid9();
|
|
1200
1988
|
return value;
|
|
1201
1989
|
}
|
|
1202
|
-
__name(
|
|
1990
|
+
__name(id5, "id");
|
|
1991
|
+
function isMlsDevMessageId(value) {
|
|
1992
|
+
return typeof value === "string" && /^[A-Za-z0-9_.:~-]{1,200}$/.test(value) && value !== "." && value !== ".." && !value.startsWith("membership-");
|
|
1993
|
+
}
|
|
1994
|
+
__name(isMlsDevMessageId, "isMlsDevMessageId");
|
|
1203
1995
|
function messageId(value) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
return result;
|
|
1996
|
+
if (!isMlsDevMessageId(value))
|
|
1997
|
+
invalid9();
|
|
1998
|
+
return value;
|
|
1208
1999
|
}
|
|
1209
2000
|
__name(messageId, "messageId");
|
|
1210
|
-
function
|
|
2001
|
+
function interactionId2(value) {
|
|
2002
|
+
if (typeof value !== "string" || !MLS_DEV_INTERACTION_ID_PATTERN.test(value) || value === "." || value === "..")
|
|
2003
|
+
invalid9();
|
|
2004
|
+
return value;
|
|
2005
|
+
}
|
|
2006
|
+
__name(interactionId2, "interactionId");
|
|
2007
|
+
function parseMlsDevInteractionBinding(value) {
|
|
2008
|
+
return boundary8(() => {
|
|
2009
|
+
const role = value && typeof value === "object" ? Object.getOwnPropertyDescriptor(value, "role") : void 0;
|
|
2010
|
+
if (!role || !Object.hasOwn(role, "value"))
|
|
2011
|
+
invalid9();
|
|
2012
|
+
if (role.value === "request") {
|
|
2013
|
+
const input2 = readMlsDataRecord(value, ["role", "requestId", "kind", "responderIds", "expiresAtMs"]);
|
|
2014
|
+
if (typeof input2.kind !== "string" || !MLS_DEV_INTERACTION_KINDS.includes(input2.kind))
|
|
2015
|
+
invalid9();
|
|
2016
|
+
const responders = input2.responderIds;
|
|
2017
|
+
if (!Array.isArray(responders) || Object.getPrototypeOf(responders) !== Array.prototype || responders.length !== 1 || Reflect.ownKeys(Object.getOwnPropertyDescriptors(responders)).length !== 2)
|
|
2018
|
+
invalid9();
|
|
2019
|
+
const only = Object.getOwnPropertyDescriptor(responders, 0);
|
|
2020
|
+
if (!only || !only.enumerable || !Object.hasOwn(only, "value"))
|
|
2021
|
+
invalid9();
|
|
2022
|
+
const expiresAtMs = requireMlsInteger(input2.expiresAtMs, 1);
|
|
2023
|
+
return Object.freeze({
|
|
2024
|
+
role: "request",
|
|
2025
|
+
requestId: interactionId2(input2.requestId),
|
|
2026
|
+
kind: input2.kind,
|
|
2027
|
+
responderIds: Object.freeze([id5(only.value)]),
|
|
2028
|
+
expiresAtMs
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
if (role.value === "response") {
|
|
2032
|
+
const input2 = readMlsDataRecord(value, ["role", "requestId", "agentId"]);
|
|
2033
|
+
return Object.freeze({ role: "response", requestId: interactionId2(input2.requestId), agentId: id5(input2.agentId) });
|
|
2034
|
+
}
|
|
2035
|
+
const input = readMlsDataRecord(value, ["role", "requestId"]);
|
|
2036
|
+
return Object.freeze({ role: requireMlsLiteral(input.role, "withdraw"), requestId: interactionId2(input.requestId) });
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
__name(parseMlsDevInteractionBinding, "parseMlsDevInteractionBinding");
|
|
2040
|
+
function encoded5(value, bytes) {
|
|
1211
2041
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
1212
2042
|
return value;
|
|
1213
2043
|
}
|
|
1214
|
-
__name(
|
|
2044
|
+
__name(encoded5, "encoded");
|
|
1215
2045
|
function parseMlsDevApplicationWire(value) {
|
|
1216
|
-
return
|
|
2046
|
+
return boundary8(() => {
|
|
1217
2047
|
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_APPLICATION_TLS_MAX_BYTES * 4 / 3))
|
|
1218
|
-
|
|
2048
|
+
invalid9();
|
|
1219
2049
|
const bytes = Math.floor(value.length * 3 / 4);
|
|
1220
2050
|
if (bytes > MLS_DEV_APPLICATION_TLS_MAX_BYTES || value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
1221
|
-
|
|
2051
|
+
invalid9();
|
|
1222
2052
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
1223
2053
|
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
1224
|
-
|
|
2054
|
+
invalid9();
|
|
1225
2055
|
return value;
|
|
1226
2056
|
});
|
|
1227
2057
|
}
|
|
1228
2058
|
__name(parseMlsDevApplicationWire, "parseMlsDevApplicationWire");
|
|
1229
2059
|
function parseMlsDevApplicationPreconditions(value) {
|
|
1230
|
-
return
|
|
2060
|
+
return boundary8(() => {
|
|
1231
2061
|
const input = readMlsDataRecord(value, ["membershipRevision", "admissionId"]);
|
|
1232
|
-
return Object.freeze({ membershipRevision: requireMlsInteger(input.membershipRevision), admissionId:
|
|
2062
|
+
return Object.freeze({ membershipRevision: requireMlsInteger(input.membershipRevision), admissionId: id5(input.admissionId) });
|
|
1233
2063
|
});
|
|
1234
2064
|
}
|
|
1235
2065
|
__name(parseMlsDevApplicationPreconditions, "parseMlsDevApplicationPreconditions");
|
|
1236
2066
|
var bindingFields = ["version", "environmentId", "conversationId", "groupId", "messageId", "senderCredential", "expectedHead", "preconditions"];
|
|
2067
|
+
var interactionBindingFields = [...bindingFields, "interaction"];
|
|
1237
2068
|
function binding(input) {
|
|
1238
|
-
|
|
2069
|
+
const environmentId = requireMlsEnvironmentId(input.environmentId);
|
|
2070
|
+
if (parseMlsDevCredentialToken(input.senderCredential).unsigned.claims.device.environmentId !== environmentId) invalid9();
|
|
1239
2071
|
return {
|
|
1240
2072
|
version: requireMlsLiteral(input.version, 1),
|
|
1241
|
-
environmentId
|
|
1242
|
-
conversationId:
|
|
1243
|
-
groupId:
|
|
2073
|
+
environmentId,
|
|
2074
|
+
conversationId: id5(input.conversationId),
|
|
2075
|
+
groupId: encoded5(input.groupId, 32),
|
|
1244
2076
|
messageId: messageId(input.messageId),
|
|
1245
2077
|
senderCredential: input.senderCredential,
|
|
1246
2078
|
expectedHead: parseMlsDevGroupHead(input.expectedHead),
|
|
1247
|
-
preconditions: parseMlsDevApplicationPreconditions(input.preconditions)
|
|
2079
|
+
preconditions: parseMlsDevApplicationPreconditions(input.preconditions),
|
|
2080
|
+
...Object.hasOwn(input, "interaction") ? { interaction: parseMlsDevInteractionBinding(input.interaction) } : {}
|
|
1248
2081
|
};
|
|
1249
2082
|
}
|
|
1250
2083
|
__name(binding, "binding");
|
|
2084
|
+
function fields(value, extra) {
|
|
2085
|
+
return readMlsDataRecord(value, [...bindingFields, ...extra], [...interactionBindingFields, ...extra]);
|
|
2086
|
+
}
|
|
2087
|
+
__name(fields, "fields");
|
|
1251
2088
|
function parseMlsDevApplicationEnvelope(value) {
|
|
1252
|
-
return
|
|
1253
|
-
const input =
|
|
1254
|
-
return bounded6({ ...binding(input), ciphertext: parseMlsDevApplicationWire(input.ciphertext), proof:
|
|
2089
|
+
return boundary8(() => {
|
|
2090
|
+
const input = fields(value, ["ciphertext", "proof"]);
|
|
2091
|
+
return bounded6({ ...binding(input), ciphertext: parseMlsDevApplicationWire(input.ciphertext), proof: encoded5(input.proof, 64) });
|
|
1255
2092
|
});
|
|
1256
2093
|
}
|
|
1257
2094
|
__name(parseMlsDevApplicationEnvelope, "parseMlsDevApplicationEnvelope");
|
|
1258
2095
|
function parseMlsDevApplicationReceipt(value) {
|
|
1259
|
-
return
|
|
2096
|
+
return boundary8(() => {
|
|
1260
2097
|
const input = readMlsDataRecord(value, ["version", "authorization", "operationId", "conversationId", "groupId", "messageId", "feedOrdinal", "head", "acceptedAtMs", "envelopeHash"]);
|
|
1261
2098
|
return bounded6({
|
|
1262
2099
|
version: requireMlsLiteral(input.version, 1),
|
|
1263
2100
|
authorization: requireMlsLiteral(input.authorization, "encrypted_text_only"),
|
|
1264
|
-
operationId:
|
|
1265
|
-
conversationId:
|
|
1266
|
-
groupId:
|
|
2101
|
+
operationId: id5(input.operationId),
|
|
2102
|
+
conversationId: id5(input.conversationId),
|
|
2103
|
+
groupId: encoded5(input.groupId, 32),
|
|
1267
2104
|
messageId: messageId(input.messageId),
|
|
1268
2105
|
feedOrdinal: requireMlsInteger(input.feedOrdinal, 1),
|
|
1269
2106
|
head: parseMlsDevGroupHead(input.head),
|
|
1270
2107
|
acceptedAtMs: requireMlsInteger(input.acceptedAtMs),
|
|
1271
|
-
envelopeHash:
|
|
2108
|
+
envelopeHash: encoded5(input.envelopeHash, 32)
|
|
1272
2109
|
});
|
|
1273
2110
|
});
|
|
1274
2111
|
}
|
|
1275
2112
|
__name(parseMlsDevApplicationReceipt, "parseMlsDevApplicationReceipt");
|
|
2113
|
+
function text2(value) {
|
|
2114
|
+
if (typeof value !== "string" || !value.trim() || utf85.encode(value).length > MLS_DEV_TEXT_MAX_BYTES || new TextDecoder("utf-8", { fatal: true }).decode(utf85.encode(value)) !== value)
|
|
2115
|
+
invalid9();
|
|
2116
|
+
return value;
|
|
2117
|
+
}
|
|
2118
|
+
__name(text2, "text");
|
|
2119
|
+
function textExtras2(value) {
|
|
2120
|
+
const extras = {};
|
|
2121
|
+
if (Object.hasOwn(value, "replyTo")) {
|
|
2122
|
+
const reply = readMlsDataRecord(value.replyTo, ["messageId", "quote"]);
|
|
2123
|
+
if ([...text2(reply.quote)].length > MLS_DEV_REPLY_QUOTE_MAX_CHARACTERS)
|
|
2124
|
+
invalid9();
|
|
2125
|
+
extras.replyTo = Object.freeze({ messageId: messageId(reply.messageId), quote: reply.quote });
|
|
2126
|
+
}
|
|
2127
|
+
if (Object.hasOwn(value, "mentions")) {
|
|
2128
|
+
const mentions = value.mentions;
|
|
2129
|
+
if (!Array.isArray(mentions) || mentions.length < 1 || mentions.length > MLS_DEV_MENTIONS_MAX || new Set(mentions).size !== mentions.length)
|
|
2130
|
+
invalid9();
|
|
2131
|
+
extras.mentions = Object.freeze(mentions.map(id5));
|
|
2132
|
+
}
|
|
2133
|
+
return extras;
|
|
2134
|
+
}
|
|
2135
|
+
__name(textExtras2, "textExtras");
|
|
2136
|
+
var optionalKeys = /* @__PURE__ */ __name((value, keys2) => value && typeof value === "object" ? keys2.filter((key) => Object.hasOwn(value, key)) : [], "optionalKeys");
|
|
1276
2137
|
function parseMlsDevTextPayload(value) {
|
|
1277
|
-
return
|
|
1278
|
-
const input = readMlsDataRecord(value, ["version", "kind", "conversationId", "groupId", "messageId", "operationId", "senderDeviceId", "text"]);
|
|
1279
|
-
if (typeof input.text !== "string" || !input.text.trim() || utf84.encode(input.text).length > MLS_DEV_TEXT_MAX_BYTES || new TextDecoder("utf-8", { fatal: true }).decode(utf84.encode(input.text)) !== input.text)
|
|
1280
|
-
invalid7();
|
|
2138
|
+
return boundary8(() => {
|
|
2139
|
+
const input = readMlsDataRecord(value, ["version", "kind", "conversationId", "groupId", "messageId", "operationId", "senderDeviceId", "text", ...optionalKeys(value, ["replyTo", "mentions"])]);
|
|
1281
2140
|
return bounded6({
|
|
1282
2141
|
version: requireMlsLiteral(input.version, 1),
|
|
1283
2142
|
kind: requireMlsLiteral(input.kind, "text"),
|
|
1284
|
-
conversationId:
|
|
1285
|
-
groupId:
|
|
2143
|
+
conversationId: id5(input.conversationId),
|
|
2144
|
+
groupId: encoded5(input.groupId, 32),
|
|
1286
2145
|
messageId: messageId(input.messageId),
|
|
1287
|
-
operationId:
|
|
2146
|
+
operationId: id5(input.operationId),
|
|
1288
2147
|
senderDeviceId: requireMlsUuid(input.senderDeviceId),
|
|
1289
|
-
text: input.text
|
|
2148
|
+
text: text2(input.text),
|
|
2149
|
+
...textExtras2(input)
|
|
1290
2150
|
});
|
|
1291
2151
|
});
|
|
1292
2152
|
}
|
|
1293
2153
|
__name(parseMlsDevTextPayload, "parseMlsDevTextPayload");
|
|
1294
|
-
|
|
1295
|
-
|
|
2154
|
+
var itemFields = ["type", "uploadId", "url", "mimeType", "sizeBytes", "fileName", "width", "height", "durationMs", "thumbnailUrl", "caption", "replyTo", "mentions"];
|
|
2155
|
+
function httpsUrl(value) {
|
|
2156
|
+
if (typeof value !== "string" || value.length > 2048) invalid9();
|
|
2157
|
+
const url = boundary8(() => new URL(value));
|
|
2158
|
+
if (url.protocol !== "https:" || url.username || url.password) invalid9();
|
|
2159
|
+
return value;
|
|
2160
|
+
}
|
|
2161
|
+
__name(httpsUrl, "httpsUrl");
|
|
2162
|
+
var dimension = /* @__PURE__ */ __name((value) => requireMlsInteger(value, 0) <= 1e5 ? value : invalid9(), "dimension");
|
|
2163
|
+
function item(value) {
|
|
2164
|
+
const input = readMlsDataRecord(value, ["type", "uploadId", "url", "mimeType", "sizeBytes", ...optionalKeys(value, itemFields.slice(5))]);
|
|
2165
|
+
if (!MLS_DEV_ITEM_TYPES.some((type) => type === input.type) || typeof input.mimeType !== "string" || !/^[a-z0-9.+-]{1,64}\/[a-z0-9.+-]{1,128}$/i.test(input.mimeType))
|
|
2166
|
+
invalid9();
|
|
2167
|
+
if (input.fileName !== void 0 && (typeof input.fileName !== "string" || !input.fileName.trim() || [...input.fileName].length > 240))
|
|
2168
|
+
invalid9();
|
|
2169
|
+
return Object.freeze({
|
|
2170
|
+
type: input.type,
|
|
2171
|
+
uploadId: requireMlsUuid(input.uploadId),
|
|
2172
|
+
url: httpsUrl(input.url),
|
|
2173
|
+
mimeType: input.mimeType,
|
|
2174
|
+
sizeBytes: requireMlsInteger(input.sizeBytes, 1),
|
|
2175
|
+
...input.fileName === void 0 ? {} : { fileName: input.fileName },
|
|
2176
|
+
...Object.fromEntries(["width", "height", "durationMs"].filter((key) => input[key] !== void 0).map((key) => [key, key === "durationMs" ? requireMlsInteger(input[key], 0) : dimension(input[key])])),
|
|
2177
|
+
...input.thumbnailUrl === void 0 ? {} : { thumbnailUrl: httpsUrl(input.thumbnailUrl) },
|
|
2178
|
+
...input.caption === void 0 ? {} : { caption: text2(input.caption) },
|
|
2179
|
+
...textExtras2(input)
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
__name(item, "item");
|
|
2183
|
+
function parseMlsDevItemPayload(value) {
|
|
2184
|
+
return boundary8(() => {
|
|
2185
|
+
const input = readMlsDataRecord(value, ["version", "kind", "conversationId", "groupId", "messageId", "operationId", "senderDeviceId", "item"]);
|
|
2186
|
+
const payload = {
|
|
2187
|
+
version: requireMlsLiteral(input.version, 1),
|
|
2188
|
+
kind: requireMlsLiteral(input.kind, "item"),
|
|
2189
|
+
conversationId: id5(input.conversationId),
|
|
2190
|
+
groupId: encoded5(input.groupId, 32),
|
|
2191
|
+
messageId: messageId(input.messageId),
|
|
2192
|
+
operationId: id5(input.operationId),
|
|
2193
|
+
senderDeviceId: requireMlsUuid(input.senderDeviceId),
|
|
2194
|
+
item: item(input.item)
|
|
2195
|
+
};
|
|
2196
|
+
if (utf85.encode(JSON.stringify(payload)).length > MLS_DEV_TEXT_MAX_BYTES)
|
|
2197
|
+
invalid9();
|
|
2198
|
+
return bounded6(payload);
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
__name(parseMlsDevItemPayload, "parseMlsDevItemPayload");
|
|
2202
|
+
function framed(domain4, value, operationId) {
|
|
2203
|
+
const operation = utf85.encode(id5(operationId)), json = utf85.encode(JSON.stringify(value)), prefix = utf85.encode(domain4);
|
|
1296
2204
|
const result = new Uint8Array(prefix.length + 8 + operation.length + json.length), view = new DataView(result.buffer);
|
|
1297
2205
|
let offset = 0;
|
|
1298
2206
|
result.set(prefix, offset);
|
|
@@ -1308,7 +2216,7 @@ function framed(domain3, value, operationId) {
|
|
|
1308
2216
|
}
|
|
1309
2217
|
__name(framed, "framed");
|
|
1310
2218
|
function encodeMlsDevApplicationEnvelopeHash(value, operationId) {
|
|
1311
|
-
return
|
|
2219
|
+
return boundary8(() => framed("canon.mls.dev-application-envelope.v1\0", parseMlsDevApplicationEnvelope(value), operationId));
|
|
1312
2220
|
}
|
|
1313
2221
|
__name(encodeMlsDevApplicationEnvelopeHash, "encodeMlsDevApplicationEnvelopeHash");
|
|
1314
2222
|
|
|
@@ -1329,12 +2237,6 @@ var RUNTIME_CARD_FIELD_TYPES = [
|
|
|
1329
2237
|
"searchSelect",
|
|
1330
2238
|
"lineItems"
|
|
1331
2239
|
];
|
|
1332
|
-
var RUNTIME_CARD_LIMITS = {
|
|
1333
|
-
detailsRows: 32,
|
|
1334
|
-
lineItemRows: 50,
|
|
1335
|
-
lineItemColumns: 8,
|
|
1336
|
-
searchSelectChoices: 100
|
|
1337
|
-
};
|
|
1338
2240
|
var RUNTIME_CARD_LINE_ITEM_COLUMN_TYPES = [
|
|
1339
2241
|
"text",
|
|
1340
2242
|
"number",
|
|
@@ -1526,7 +2428,7 @@ var VERB_NATIVE_METADATA_KEYS = [
|
|
|
1526
2428
|
"handles"
|
|
1527
2429
|
];
|
|
1528
2430
|
var VERB_NATIVE_STRING_KEYS = VERB_NATIVE_METADATA_KEYS.filter(
|
|
1529
|
-
(
|
|
2431
|
+
(key) => key !== "handles"
|
|
1530
2432
|
);
|
|
1531
2433
|
var RUNTIME_CORRELATION_VALUE_PATTERN = new RegExp(
|
|
1532
2434
|
VERB_ID_PATTERNS.runtimeCorrelationValue
|
|
@@ -1728,7 +2630,7 @@ var sessionRuleDef = {
|
|
|
1728
2630
|
};
|
|
1729
2631
|
var runtimeCardDef = {
|
|
1730
2632
|
type: "object",
|
|
1731
|
-
description: `ENVELOPE-ONLY validation of a canon.card.v1 document: block structures are NOT checked here.
|
|
2633
|
+
description: `ENVELOPE-ONLY validation of a canon.card.v1 document: block structures are NOT checked here. Full document contract: @canonmsg/rich-cards RUNTIME_CARD_JSON_SCHEMA_V1 ($id https://canonmsg.com/schemas/canon.card.v1.json) \u2014 authoring caps title 120 / fallbackText 500 / blocks 24. Server envelope acceptance (interactionCard.ts): <= ${VERB_LIMITS.cardEnvelopeBytes} bytes serialized, title <= ${VERB_LIMITS.cardServerTitleChars}, fallbackText <= ${VERB_LIMITS.cardServerFallbackTextChars}, blocks <= ${VERB_LIMITS.cardServerBlocks}; no secret-bearing top-level keys.`,
|
|
1732
2634
|
required: ["schema", "title", "fallbackText", "blocks"],
|
|
1733
2635
|
additionalProperties: true,
|
|
1734
2636
|
properties: {
|
|
@@ -1861,6 +2763,20 @@ var send_to_result = {
|
|
|
1861
2763
|
sessionSelection: { type: "string" }
|
|
1862
2764
|
}
|
|
1863
2765
|
},
|
|
2766
|
+
{
|
|
2767
|
+
type: "object",
|
|
2768
|
+
required: ["status", "conversationId", "messageId", "openingText"],
|
|
2769
|
+
additionalProperties: true,
|
|
2770
|
+
description: "The room is encrypted, so Canon stored no opening text: the sender sends it encrypted under messageId.",
|
|
2771
|
+
properties: {
|
|
2772
|
+
status: { const: "opened" },
|
|
2773
|
+
conversationId: { type: "string" },
|
|
2774
|
+
messageId: { type: "string" },
|
|
2775
|
+
openingText: { const: "deferred" },
|
|
2776
|
+
created: { type: "boolean" },
|
|
2777
|
+
reused: { type: "boolean" }
|
|
2778
|
+
}
|
|
2779
|
+
},
|
|
1864
2780
|
{
|
|
1865
2781
|
type: "object",
|
|
1866
2782
|
required: ["status", "requestId"],
|
|
@@ -1871,6 +2787,12 @@ var send_to_result = {
|
|
|
1871
2787
|
deferredIntentId: {
|
|
1872
2788
|
type: ["string", "null"],
|
|
1873
2789
|
description: "Present when the send is deferred until the contact request resolves."
|
|
2790
|
+
},
|
|
2791
|
+
conversationId: { type: "string" },
|
|
2792
|
+
messageId: { type: "string" },
|
|
2793
|
+
openingText: {
|
|
2794
|
+
const: "deferred",
|
|
2795
|
+
description: "The room is encrypted, so Canon stored no opening text: the sender sends it encrypted under messageId."
|
|
1874
2796
|
}
|
|
1875
2797
|
}
|
|
1876
2798
|
},
|
|
@@ -2755,9 +3677,9 @@ function splitTurnMetadata(metadata) {
|
|
|
2755
3677
|
if (!metadata || typeof metadata !== "object") return { turn: void 0, rest: void 0 };
|
|
2756
3678
|
const turn = {};
|
|
2757
3679
|
const rest = {};
|
|
2758
|
-
for (const [
|
|
2759
|
-
if (TURN_KEYS.includes(
|
|
2760
|
-
else rest[
|
|
3680
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
3681
|
+
if (TURN_KEYS.includes(key)) turn[key] = value;
|
|
3682
|
+
else rest[key] = value;
|
|
2761
3683
|
}
|
|
2762
3684
|
return {
|
|
2763
3685
|
turn: Object.keys(turn).length ? turn : void 0,
|
|
@@ -2765,10 +3687,10 @@ function splitTurnMetadata(metadata) {
|
|
|
2765
3687
|
};
|
|
2766
3688
|
}
|
|
2767
3689
|
__name(splitTurnMetadata, "splitTurnMetadata");
|
|
2768
|
-
function compact(
|
|
3690
|
+
function compact(record5) {
|
|
2769
3691
|
const out = {};
|
|
2770
|
-
for (const [
|
|
2771
|
-
if (value !== void 0) out[
|
|
3692
|
+
for (const [key, value] of Object.entries(record5)) {
|
|
3693
|
+
if (value !== void 0) out[key] = value;
|
|
2772
3694
|
}
|
|
2773
3695
|
return out;
|
|
2774
3696
|
}
|
|
@@ -2948,12 +3870,12 @@ function parseDirectConversationSelection(value) {
|
|
|
2948
3870
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2949
3871
|
throw new Error("selection must be an object");
|
|
2950
3872
|
}
|
|
2951
|
-
const
|
|
2952
|
-
if (
|
|
2953
|
-
return { mode:
|
|
3873
|
+
const record5 = value;
|
|
3874
|
+
if (record5.mode === "latest_or_new" || record5.mode === "new") {
|
|
3875
|
+
return { mode: record5.mode };
|
|
2954
3876
|
}
|
|
2955
|
-
if (
|
|
2956
|
-
const conversationId = typeof
|
|
3877
|
+
if (record5.mode === "specific") {
|
|
3878
|
+
const conversationId = typeof record5.conversationId === "string" ? record5.conversationId.trim() : "";
|
|
2957
3879
|
if (!conversationId) throw new Error("selection.conversationId is required");
|
|
2958
3880
|
return { mode: "specific", conversationId };
|
|
2959
3881
|
}
|
|
@@ -2978,10 +3900,10 @@ function record(value) {
|
|
|
2978
3900
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2979
3901
|
}
|
|
2980
3902
|
__name(record, "record");
|
|
2981
|
-
function
|
|
3903
|
+
function text3(value, max) {
|
|
2982
3904
|
return typeof value === "string" && value.length > 0 && value.length <= max ? value : void 0;
|
|
2983
3905
|
}
|
|
2984
|
-
__name(
|
|
3906
|
+
__name(text3, "text");
|
|
2985
3907
|
function member(value, choices) {
|
|
2986
3908
|
return choices.includes(value) ? value : void 0;
|
|
2987
3909
|
}
|
|
@@ -3016,19 +3938,19 @@ function normalizeDispatch(value) {
|
|
|
3016
3938
|
return primitive ? { kind: "primitive", primitive } : void 0;
|
|
3017
3939
|
}
|
|
3018
3940
|
case "control": {
|
|
3019
|
-
const controlId =
|
|
3020
|
-
return controlId ? defined({ kind: "control", controlId, value:
|
|
3941
|
+
const controlId = text3(dispatch.controlId, 128);
|
|
3942
|
+
return controlId ? defined({ kind: "control", controlId, value: text3(dispatch.value, 1024) }) : void 0;
|
|
3021
3943
|
}
|
|
3022
3944
|
case "text_passthrough": {
|
|
3023
|
-
const template =
|
|
3945
|
+
const template = text3(dispatch.template, 2048);
|
|
3024
3946
|
return template ? { kind: "text_passthrough", template } : void 0;
|
|
3025
3947
|
}
|
|
3026
3948
|
case "compose": {
|
|
3027
|
-
const content =
|
|
3949
|
+
const content = text3(dispatch.text, 2048);
|
|
3028
3950
|
return content ? { kind: "compose", text: content } : void 0;
|
|
3029
3951
|
}
|
|
3030
3952
|
case "open_details":
|
|
3031
|
-
return defined({ kind: "open_details", target:
|
|
3953
|
+
return defined({ kind: "open_details", target: text3(dispatch.target, 128) });
|
|
3032
3954
|
default:
|
|
3033
3955
|
return void 0;
|
|
3034
3956
|
}
|
|
@@ -3036,21 +3958,21 @@ function normalizeDispatch(value) {
|
|
|
3036
3958
|
__name(normalizeDispatch, "normalizeDispatch");
|
|
3037
3959
|
function normalizeArgument(value) {
|
|
3038
3960
|
const arg = record(value);
|
|
3039
|
-
const
|
|
3040
|
-
const label =
|
|
3961
|
+
const id7 = text3(arg.id, 128);
|
|
3962
|
+
const label = text3(arg.label, 160);
|
|
3041
3963
|
const kind = member(arg.kind, ["string", "enum", "boolean"]);
|
|
3042
|
-
if (!
|
|
3043
|
-
if (["required", "captureRemaining"].some((
|
|
3964
|
+
if (!id7 || !label || !kind) return void 0;
|
|
3965
|
+
if (["required", "captureRemaining"].some((key) => arg[key] !== void 0 && typeof arg[key] !== "boolean")) return void 0;
|
|
3044
3966
|
if (arg.choices !== void 0 && !Array.isArray(arg.choices)) return void 0;
|
|
3045
3967
|
const choices = Array.isArray(arg.choices) ? arg.choices.slice(0, 128).flatMap((value2) => {
|
|
3046
3968
|
const choice = record(value2);
|
|
3047
|
-
const entry =
|
|
3048
|
-
const label2 =
|
|
3049
|
-
return entry && label2 ? [defined({ value: entry, label: label2, description:
|
|
3969
|
+
const entry = text3(choice.value, 1024);
|
|
3970
|
+
const label2 = text3(choice.label, 160);
|
|
3971
|
+
return entry && label2 ? [defined({ value: entry, label: label2, description: text3(choice.description, 1024) })] : [];
|
|
3050
3972
|
}) : void 0;
|
|
3051
3973
|
if (kind === "enum" && !choices?.length) return void 0;
|
|
3052
3974
|
return defined({
|
|
3053
|
-
id:
|
|
3975
|
+
id: id7,
|
|
3054
3976
|
label,
|
|
3055
3977
|
kind,
|
|
3056
3978
|
choices,
|
|
@@ -3064,15 +3986,15 @@ function normalizePublicRuntimeCommands(value) {
|
|
|
3064
3986
|
const seenIds = /* @__PURE__ */ new Set();
|
|
3065
3987
|
const seenAliases = /* @__PURE__ */ new Set();
|
|
3066
3988
|
const result = [];
|
|
3067
|
-
for (const
|
|
3068
|
-
const command = record(
|
|
3069
|
-
const
|
|
3070
|
-
const label =
|
|
3989
|
+
for (const item2 of value.slice(0, MAX_PUBLIC_RUNTIME_COMMANDS)) {
|
|
3990
|
+
const command = record(item2);
|
|
3991
|
+
const id7 = text3(command.id, 128);
|
|
3992
|
+
const label = text3(command.label, 160);
|
|
3071
3993
|
const dispatch = normalizeDispatch(command.dispatch);
|
|
3072
|
-
if (!
|
|
3073
|
-
if (["ownerOnly", "sensitive"].some((
|
|
3074
|
-
if (["aliases", "args", "placements", "availability"].some((
|
|
3075
|
-
const rawAliases = Array.isArray(command.aliases) && command.aliases.length ? command.aliases : [
|
|
3994
|
+
if (!id7 || !label || !dispatch || seenIds.has(id7) || command.visibility === "hidden" || command.sensitive === true) continue;
|
|
3995
|
+
if (["ownerOnly", "sensitive"].some((key) => command[key] !== void 0 && typeof command[key] !== "boolean")) continue;
|
|
3996
|
+
if (["aliases", "args", "placements", "availability"].some((key) => command[key] !== void 0 && !Array.isArray(command[key]))) continue;
|
|
3997
|
+
const rawAliases = Array.isArray(command.aliases) && command.aliases.length ? command.aliases : [id7];
|
|
3076
3998
|
const aliases = [...new Set(rawAliases.slice(0, 8).flatMap((value2) => {
|
|
3077
3999
|
const alias = typeof value2 === "string" && value2.length <= 128 ? normalizeRuntimeCommandAlias(value2) : null;
|
|
3078
4000
|
return alias && /^[A-Za-z0-9:_-]+$/.test(alias) && !seenAliases.has(alias.toLowerCase()) ? [alias] : [];
|
|
@@ -3080,20 +4002,20 @@ function normalizePublicRuntimeCommands(value) {
|
|
|
3080
4002
|
if (!aliases.length) continue;
|
|
3081
4003
|
const args = Array.isArray(command.args) ? command.args.slice(0, 12).map(normalizeArgument) : void 0;
|
|
3082
4004
|
if (args?.some((arg) => !arg) || Array.isArray(command.args) && command.args.length > 12) continue;
|
|
3083
|
-
seenIds.add(
|
|
4005
|
+
seenIds.add(id7);
|
|
3084
4006
|
aliases.forEach((alias) => seenAliases.add(alias.toLowerCase()));
|
|
3085
4007
|
result.push(defined({
|
|
3086
|
-
id:
|
|
4008
|
+
id: id7,
|
|
3087
4009
|
label,
|
|
3088
4010
|
aliases,
|
|
3089
4011
|
dispatch,
|
|
3090
4012
|
args,
|
|
3091
|
-
description:
|
|
4013
|
+
description: text3(command.description, 1024),
|
|
3092
4014
|
category: member(command.category, ["plan", "turn", "session", "runtime", "details", "skill", "custom"]),
|
|
3093
4015
|
placements: members(command.placements, ["composer_slash", "command_palette", "session_strip"]),
|
|
3094
4016
|
availability: members(command.availability, ["idle", "busy", "busy_with_queue", "waiting_input", "always"]),
|
|
3095
4017
|
ownerOnly: typeof command.ownerOnly === "boolean" ? command.ownerOnly : void 0,
|
|
3096
|
-
disabledReason:
|
|
4018
|
+
disabledReason: text3(command.disabledReason, 1024),
|
|
3097
4019
|
trailingTextBehavior: member(command.trailingTextBehavior, ["ignore", "send_as_prompt"]),
|
|
3098
4020
|
primitive: member(command.primitive, PRIMITIVES),
|
|
3099
4021
|
tier: member(command.tier, ["primary", "detail", "diagnostic"])
|
|
@@ -3127,14 +4049,14 @@ function normalizePublicRuntimeFacts(value) {
|
|
|
3127
4049
|
const seen = /* @__PURE__ */ new Set();
|
|
3128
4050
|
return value.slice(0, 16).flatMap((entry) => {
|
|
3129
4051
|
const fact = record(entry);
|
|
3130
|
-
const
|
|
3131
|
-
const group =
|
|
3132
|
-
const label =
|
|
3133
|
-
const content =
|
|
3134
|
-
if (!
|
|
3135
|
-
seen.add(
|
|
4052
|
+
const id7 = text3(fact.id, 64);
|
|
4053
|
+
const group = id7 && Object.hasOwn(PUBLIC_RUNTIME_FACT_GROUPS, id7) ? PUBLIC_RUNTIME_FACT_GROUPS[id7] : void 0;
|
|
4054
|
+
const label = text3(fact.label, 80);
|
|
4055
|
+
const content = text3(fact.value, 512);
|
|
4056
|
+
if (!id7 || !group || !label || !content || seen.has(id7) || fact.sensitive === true || fact.visibility === "hidden") return [];
|
|
4057
|
+
seen.add(id7);
|
|
3136
4058
|
return [defined({
|
|
3137
|
-
id:
|
|
4059
|
+
id: id7,
|
|
3138
4060
|
group,
|
|
3139
4061
|
label,
|
|
3140
4062
|
value: content,
|
|
@@ -3147,13 +4069,8 @@ function normalizePublicRuntimeFacts(value) {
|
|
|
3147
4069
|
}
|
|
3148
4070
|
__name(normalizePublicRuntimeFacts, "normalizePublicRuntimeFacts");
|
|
3149
4071
|
|
|
3150
|
-
// ../backend-contracts/src/workSessions.ts
|
|
3151
|
-
var WORK_SESSION_REQUEST_TTL_MS = 5 * 6e4;
|
|
3152
|
-
var SETTINGS_KEYS = ["projectId", "modelId", "reasoningEffort", "permissionMode", "executionMode"];
|
|
3153
|
-
var DETAIL_FIELDS = [...SETTINGS_KEYS, "projectLabel", "workingDirectory", "branch", "worktreePath", "sandbox", "approvalPolicy", "nativeThreadBehavior"];
|
|
3154
|
-
|
|
3155
4072
|
// ../backend-contracts/src/endpointContentPolicy.ts
|
|
3156
|
-
var MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS = ["
|
|
4073
|
+
var MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS = ["forward_message"];
|
|
3157
4074
|
var ENDPOINT_CONTENT_ACTIONS = [
|
|
3158
4075
|
"mls_application",
|
|
3159
4076
|
"mls_group_control",
|
|
@@ -3161,8 +4078,6 @@ var ENDPOINT_CONTENT_ACTIONS = [
|
|
|
3161
4078
|
"append_artifact_command",
|
|
3162
4079
|
"retain_artifact_asset",
|
|
3163
4080
|
"sync_phone_contacts",
|
|
3164
|
-
"request_work_session",
|
|
3165
|
-
"resolve_work_session_request",
|
|
3166
4081
|
"update_agent_behavior_policy",
|
|
3167
4082
|
"update_conversation_agent_behavior_policy",
|
|
3168
4083
|
"send_message",
|
|
@@ -3215,6 +4130,8 @@ var ENDPOINT_CONTENT_ACTIONS = [
|
|
|
3215
4130
|
var ENDPOINT_CONTENT_FUNCTION_QUERIES = [
|
|
3216
4131
|
"mls_group_feed",
|
|
3217
4132
|
"mls_group_controls",
|
|
4133
|
+
"mls_group_admissions",
|
|
4134
|
+
"mls_interactions",
|
|
3218
4135
|
"list_artifacts",
|
|
3219
4136
|
"get_artifact_log",
|
|
3220
4137
|
"get_artifact_asset",
|
|
@@ -3222,9 +4139,6 @@ var ENDPOINT_CONTENT_FUNCTION_QUERIES = [
|
|
|
3222
4139
|
"get_unread_summary",
|
|
3223
4140
|
"peek_runtime_signal",
|
|
3224
4141
|
"list_runtime_primitives",
|
|
3225
|
-
"get_work_session_catalog",
|
|
3226
|
-
"get_work_session_details",
|
|
3227
|
-
"get_work_session_request",
|
|
3228
4142
|
"get_agent_behavior_policy",
|
|
3229
4143
|
"get_conversation_agent_behavior_policy",
|
|
3230
4144
|
"get_ephemeral",
|
|
@@ -3246,7 +4160,8 @@ var ENDPOINT_CONTENT_FUNCTION_QUERIES = [
|
|
|
3246
4160
|
"get_media_message",
|
|
3247
4161
|
"get_voice_session",
|
|
3248
4162
|
"get_image_generation",
|
|
3249
|
-
"get_image_generation_status"
|
|
4163
|
+
"get_image_generation_status",
|
|
4164
|
+
"same_owner_agents"
|
|
3250
4165
|
];
|
|
3251
4166
|
var ENDPOINT_CONTENT_QUERIES = [...ENDPOINT_CONTENT_FUNCTION_QUERIES, "admit_messages"];
|
|
3252
4167
|
var ENDPOINT_CONTENT_EPHEMERALS = [
|
|
@@ -3360,7 +4275,8 @@ var actionRouting = {
|
|
|
3360
4275
|
send_message: ["conversationId", "messageId", "replyTo", "mentions.*"],
|
|
3361
4276
|
delete_message: ["conversationId", "messageId"],
|
|
3362
4277
|
forward_message: ["sourceConversationId", "targetConversationId", "messageId"],
|
|
3363
|
-
|
|
4278
|
+
// A reaction is an action on a message's public stub, so its emoji is server-visible in encrypted rooms too (ruling 3).
|
|
4279
|
+
react: ["conversationId", "messageId", "emoji"],
|
|
3364
4280
|
send_contextual_message: ["conversationId"],
|
|
3365
4281
|
create_interaction: ["conversationId", "requestId", "inputId", "approvalId", "cardId", "planId", "responseUserId"],
|
|
3366
4282
|
consume_interaction: ["conversationId", "inputId", "approvalId", "cardId", "planId"],
|
|
@@ -3381,13 +4297,19 @@ var actionRouting = {
|
|
|
3381
4297
|
set_block: ["targetUserId"],
|
|
3382
4298
|
save_contact: ["contactUserId"],
|
|
3383
4299
|
remove_contact: ["contactId", "contactUserId"],
|
|
3384
|
-
|
|
4300
|
+
// `signal.type` is the closed Stop family (interrupt, stop_and_drop, new_session) the server must read to deliver it;
|
|
4301
|
+
// a runtime `primitive` (commands, arguments) stays private, so it remains refused in encrypted rooms.
|
|
4302
|
+
session_control: ["conversationId", "agentId", "signal.type"],
|
|
3385
4303
|
approve_contact_request: ["requestId"],
|
|
3386
4304
|
reject_contact_request: ["requestId"],
|
|
3387
4305
|
cancel_direct_request: ["requestId"],
|
|
3388
4306
|
update_message_disposition: ["conversationId", "messageId"],
|
|
3389
|
-
|
|
4307
|
+
// An encrypted room's item uploads its bytes as in a plaintext room (they are not end-to-end encrypted, payload v3
|
|
4308
|
+
// §4): the type, size and display hints are public; its file name and caption ride only inside the item.
|
|
4309
|
+
create_upload: ["conversationId", "mimeType", "sizeBytes", "width", "height", "durationMs"],
|
|
3390
4310
|
finalize_upload: ["uploadId"],
|
|
4311
|
+
// An encrypted item's application names its uploads beside the ciphertext, for Canon to retain on acceptance.
|
|
4312
|
+
mls_application: ["mediaUploadIds.*"],
|
|
3391
4313
|
delete_upload: ["uploadId"],
|
|
3392
4314
|
retry_video_processing: ["uploadId"],
|
|
3393
4315
|
stage_media_message: ["conversationId", "messageId", "files.*.fileId", "contentVersion", ...preparedRoutingPaths.map((path) => `preparedContent.${path}`)],
|
|
@@ -3395,19 +4317,18 @@ var actionRouting = {
|
|
|
3395
4317
|
renew_media_message_upload: ["conversationId", "messageId", "fileId", "generation"],
|
|
3396
4318
|
finalize_memo_stream: ["conversationId", "streamId"],
|
|
3397
4319
|
abort_memo_stream: ["streamId"],
|
|
3398
|
-
create_voice_session: ["conversationId"],
|
|
4320
|
+
create_voice_session: ["conversationId", "media"],
|
|
3399
4321
|
join_voice_session: ["conversationId", "sessionId"],
|
|
3400
4322
|
decline_voice_session: ["conversationId", "sessionId"],
|
|
3401
4323
|
end_voice_session: ["conversationId", "sessionId"],
|
|
3402
4324
|
consume_runtime_signal: ["conversationId", "expectedSignalId"],
|
|
3403
4325
|
consume_runtime_primitive: ["conversationId", "requestId", "expectedRevision"],
|
|
3404
4326
|
generate_image: ["conversationId", "requestId"],
|
|
3405
|
-
request_work_session: ["conversationId", "agentId", "requestId"],
|
|
3406
|
-
resolve_work_session_request: ["requestId"],
|
|
3407
4327
|
update_agent_behavior_policy: ["agentId"],
|
|
3408
4328
|
update_conversation_agent_behavior_policy: ["conversationId", "agentId"]
|
|
3409
4329
|
};
|
|
3410
4330
|
var queryRouting = {
|
|
4331
|
+
mls_interactions: ["version", "conversationId"],
|
|
3411
4332
|
list_artifacts: ["conversationId", "cursor", "limit"],
|
|
3412
4333
|
get_artifact_log: ["conversationId", "artifactId", "afterSequence", "cursor", "limit"],
|
|
3413
4334
|
get_artifact_asset: ["conversationId", "artifactId", "assetId"],
|
|
@@ -3433,12 +4354,19 @@ var queryRouting = {
|
|
|
3433
4354
|
get_media_message: ["conversationId", "messageId", "installationId"],
|
|
3434
4355
|
get_voice_session: ["conversationId", "sessionId"],
|
|
3435
4356
|
get_image_generation: ["requestId"],
|
|
3436
|
-
|
|
3437
|
-
get_work_session_details: ["agentId", "conversationId"],
|
|
3438
|
-
get_work_session_request: ["requestId"],
|
|
4357
|
+
same_owner_agents: ["agentIds.*"],
|
|
3439
4358
|
get_agent_behavior_policy: ["agentId"],
|
|
3440
4359
|
get_conversation_agent_behavior_policy: ["conversationId", "agentId"]
|
|
3441
4360
|
};
|
|
4361
|
+
var ENDPOINT_TURN_PRESENCE_STATES = ["thinking", "waiting_input", "interrupted", "idle"];
|
|
4362
|
+
var timestamp = /* @__PURE__ */ __name((value) => value === null || Number.isSafeInteger(value) && value >= 0, "timestamp");
|
|
4363
|
+
var turnPresence = {
|
|
4364
|
+
state: /* @__PURE__ */ __name((value) => ENDPOINT_TURN_PRESENCE_STATES.includes(value), "state"),
|
|
4365
|
+
// `turn-` and 32 random hex digits per encrypted turn: never the runner's own turn id, which seeds its cards' request ids.
|
|
4366
|
+
turnId: /* @__PURE__ */ __name((value) => value === null || typeof value === "string" && /^turn-[0-9a-f]{32}$/.test(value), "turnId"),
|
|
4367
|
+
openedAt: timestamp,
|
|
4368
|
+
turnUpdatedAt: timestamp
|
|
4369
|
+
};
|
|
3442
4370
|
var transientRouting = {
|
|
3443
4371
|
create_memo_stream: ["conversationId", "streamId"],
|
|
3444
4372
|
append_memo_chunk: ["streamId"],
|
|
@@ -3478,7 +4406,7 @@ function endpointOperationRequestRetention(action, body, envelope) {
|
|
|
3478
4406
|
if (action !== "respond_interaction") return "journal";
|
|
3479
4407
|
if (envelope && Object.keys(envelope).length > 0) return "memory";
|
|
3480
4408
|
const routing = ["kind", "conversationId", "agentId", "requestId"];
|
|
3481
|
-
const hasOnly = /* @__PURE__ */ __name((
|
|
4409
|
+
const hasOnly = /* @__PURE__ */ __name((fields2) => Object.keys(body).every((field) => fields2.includes(field)), "hasOnly");
|
|
3482
4410
|
if (routing.some((field) => body[field] !== void 0 && typeof body[field] !== "string")) return "memory";
|
|
3483
4411
|
if ((body.kind === "input" || body.kind === "card") && hasOnly([...routing, "status", "actionId"])) {
|
|
3484
4412
|
if (!["submitted", "cancelled"].includes(String(body.status)) || body.actionId !== void 0 && typeof body.actionId !== "string") return "memory";
|
|
@@ -3501,8 +4429,8 @@ function endpointOperationRequestRetention(action, body, envelope) {
|
|
|
3501
4429
|
__name(endpointOperationRequestRetention, "endpointOperationRequestRetention");
|
|
3502
4430
|
function endpointContentBoundary(lane, name, payload) {
|
|
3503
4431
|
if (!namesForLane[lane]?.includes(name)) throw new EndpointContentPolicyError(lane, name);
|
|
3504
|
-
const
|
|
3505
|
-
if (lane === "query.request" && name === "get_ephemeral" &&
|
|
4432
|
+
const record5 = payload && typeof payload === "object" ? payload : void 0;
|
|
4433
|
+
if (lane === "query.request" && name === "get_ephemeral" && record5 && !ENDPOINT_CONTENT_EPHEMERAL_FAMILIES.includes(record5.family)) {
|
|
3506
4434
|
throw new EndpointContentPolicyError(lane, `${name}/unknown-family`);
|
|
3507
4435
|
}
|
|
3508
4436
|
const requestRoutingPaths = (actionRouting[name] ?? []).flatMap((field) => [`body.value.${field}`, `envelope.${field}`]);
|
|
@@ -3510,7 +4438,7 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3510
4438
|
if (path.some((part) => secretSegments.has(part))) return "restricted-secret";
|
|
3511
4439
|
if (lane === "operation.request" && ["respond_interaction", "sync_phone_contacts"].includes(name) && matches(path, "preparedContent.payload")) return "restricted-secret";
|
|
3512
4440
|
if (lane === "operation.request" && ["respond_interaction", "sync_phone_contacts"].includes(name) && path[0] === "body" && path[1] === "value" && !publicMatch(path, requestRoutingPaths)) return "restricted-secret";
|
|
3513
|
-
if (lane === "operation.receipt" && path[0] === "result" && (["consume_interaction", "respond_interaction"].includes(name) ||
|
|
4441
|
+
if (lane === "operation.receipt" && path[0] === "result" && (["consume_interaction", "respond_interaction"].includes(name) || record5?.resultReference?.kind === "interaction")) return "restricted-secret";
|
|
3514
4442
|
let selected = payload;
|
|
3515
4443
|
for (const part of path) selected = selected && typeof selected === "object" && Object.hasOwn(selected, part) ? selected[part] : void 0;
|
|
3516
4444
|
if (selected !== null && typeof selected === "object") return "private-content";
|
|
@@ -3519,8 +4447,8 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3519
4447
|
if (name === "respond_interaction" && path[0] === "body" && path[1] === "value") return "restricted-secret";
|
|
3520
4448
|
if (name === "sync_phone_contacts" && path[0] === "body" && path[1] === "value") return "restricted-secret";
|
|
3521
4449
|
if (name === "verb_execute") {
|
|
3522
|
-
const
|
|
3523
|
-
const spec = VERB_WIRE_ENVELOPE_FIELDS[
|
|
4450
|
+
const wire4 = record5?.body?.value;
|
|
4451
|
+
const spec = VERB_WIRE_ENVELOPE_FIELDS[wire4?.verb];
|
|
3524
4452
|
if (path[0] === "body" && path[1] === "value") {
|
|
3525
4453
|
if (path.length === 3 && ["wire", "verb"].includes(path[2])) return "public-metadata";
|
|
3526
4454
|
if (path[2] === "envelope" && spec) {
|
|
@@ -3532,7 +4460,7 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3532
4460
|
}
|
|
3533
4461
|
} else if (lane === "operation.receipt") {
|
|
3534
4462
|
if (publicMatch(path, [...protocolPaths, "status", "createdAt", "updatedAt", "error.code", "error.status", "error.retryable", "error.retryAfterMs", "resultError.code", "resultError.status", "resultReference.kind", "resultReference.conversationId", "resultReference.artifactId", "resultReference.assetId", "resultReference.uploadId", "resultReference.sessionId", "resultReference.requestId"])) return "public-metadata";
|
|
3535
|
-
if (path[0] === "result" && (name === "consume_interaction" || name === "respond_interaction" ||
|
|
4463
|
+
if (path[0] === "result" && (name === "consume_interaction" || name === "respond_interaction" || record5?.resultReference?.kind === "interaction")) return "restricted-secret";
|
|
3536
4464
|
} else if (lane === "query.request") {
|
|
3537
4465
|
if (publicMatch(path, queryRouting[name] ?? [])) return "public-metadata";
|
|
3538
4466
|
} else if (lane === "query.result" || lane === "ephemeral.result") {
|
|
@@ -3540,6 +4468,7 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3540
4468
|
if (name === "get_ephemeral" && path[0] === "value") return "private-content";
|
|
3541
4469
|
} else if (lane === "ephemeral.request") {
|
|
3542
4470
|
if (publicMatch(path, transientRouting[name] ?? [])) return "public-metadata";
|
|
4471
|
+
if (name === "publish_turn" && path.length === 1 && Object.hasOwn(turnPresence, path[0]) && turnPresence[path[0]](selected)) return "public-metadata";
|
|
3543
4472
|
} else if (lane === "event") {
|
|
3544
4473
|
if (publicMatch(path, eventRouting(name))) return "public-metadata";
|
|
3545
4474
|
}
|
|
@@ -3563,12 +4492,12 @@ var PreparedContentError = class extends Error {
|
|
|
3563
4492
|
__name(this, "PreparedContentError");
|
|
3564
4493
|
}
|
|
3565
4494
|
};
|
|
3566
|
-
function
|
|
4495
|
+
function invalid10() {
|
|
3567
4496
|
throw new PreparedContentError("INVALID_PREPARED_CONTENT");
|
|
3568
4497
|
}
|
|
3569
|
-
__name(
|
|
4498
|
+
__name(invalid10, "invalid");
|
|
3570
4499
|
var record2 = /* @__PURE__ */ __name((value) => !!value && typeof value === "object" && !Array.isArray(value), "record");
|
|
3571
|
-
var keys = /* @__PURE__ */ __name((value, allowed) => Object.keys(value).every((
|
|
4500
|
+
var keys = /* @__PURE__ */ __name((value, allowed) => Object.keys(value).every((key) => allowed.includes(key)), "keys");
|
|
3572
4501
|
var revision = /* @__PURE__ */ __name((value) => Number.isSafeInteger(value) && value >= 0, "revision");
|
|
3573
4502
|
var blobIdentity = /* @__PURE__ */ __name((value) => typeof value === "string" && value.length > 0 && value.length <= 1024 && !/[\u0000-\u001f]/.test(value), "blobIdentity");
|
|
3574
4503
|
var identity = /* @__PURE__ */ __name((value) => typeof value === "string" && ENDPOINT_ID_PATTERN.test(value), "identity");
|
|
@@ -3579,33 +4508,33 @@ function canonicalEndpointJson(value) {
|
|
|
3579
4508
|
function encode(input) {
|
|
3580
4509
|
if (input === null || typeof input !== "object") return JSON.stringify(input);
|
|
3581
4510
|
if (Array.isArray(input)) return `[${input.map(encode).join(",")}]`;
|
|
3582
|
-
const
|
|
3583
|
-
return `{${Object.keys(
|
|
4511
|
+
const object = input;
|
|
4512
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${encode(object[key])}`).join(",")}}`;
|
|
3584
4513
|
}
|
|
3585
4514
|
__name(encode, "encode");
|
|
3586
4515
|
return encode(normalized);
|
|
3587
4516
|
}
|
|
3588
4517
|
__name(canonicalEndpointJson, "canonicalEndpointJson");
|
|
3589
4518
|
function validateMetadata(value) {
|
|
3590
|
-
if (!record2(value))
|
|
4519
|
+
if (!record2(value)) invalid10();
|
|
3591
4520
|
if (value.version !== PREPARED_CONTENT_VERSION) throw new PreparedContentError("UNSUPPORTED_PREPARED_CONTENT_VERSION");
|
|
3592
4521
|
if (value.codec !== PREPARED_JSON_CODEC) throw new PreparedContentError("UNSUPPORTED_PREPARED_CONTENT_CODEC");
|
|
3593
|
-
if (!keys(value, ["version", "context", "codec", "payload", "blobs", "expected"]))
|
|
4522
|
+
if (!keys(value, ["version", "context", "codec", "payload", "blobs", "expected"])) invalid10();
|
|
3594
4523
|
const context = value.context;
|
|
3595
|
-
if (!record2(context) || !keys(context, ["action", "conversationId", "messageId"]) || typeof context.action !== "string" || !ENDPOINT_ACTION_PATTERN.test(context.action) || context.conversationId !== void 0 && !identity(context.conversationId) || context.messageId !== void 0 && !identity(context.messageId))
|
|
4524
|
+
if (!record2(context) || !keys(context, ["action", "conversationId", "messageId"]) || typeof context.action !== "string" || !ENDPOINT_ACTION_PATTERN.test(context.action) || context.conversationId !== void 0 && !identity(context.conversationId) || context.messageId !== void 0 && !identity(context.messageId)) invalid10();
|
|
3596
4525
|
const expected = value.expected;
|
|
3597
|
-
if (!record2(expected) || !keys(expected, ["installationGeneration", "protocolRevision", "admission"]) || !revision(expected.installationGeneration) || !revision(expected.protocolRevision))
|
|
4526
|
+
if (!record2(expected) || !keys(expected, ["installationGeneration", "protocolRevision", "admission"]) || !revision(expected.installationGeneration) || !revision(expected.protocolRevision)) invalid10();
|
|
3598
4527
|
if (expected.admission !== void 0) {
|
|
3599
4528
|
const admission = expected.admission;
|
|
3600
|
-
if (!record2(admission) || !keys(admission, ["conversationId", "membershipRevision", "admissionId", "targetAdmissionId"]) || ["conversationId", "admissionId", "targetAdmissionId"].some((
|
|
4529
|
+
if (!record2(admission) || !keys(admission, ["conversationId", "membershipRevision", "admissionId", "targetAdmissionId"]) || ["conversationId", "admissionId", "targetAdmissionId"].some((key) => admission[key] !== void 0 && !identity(admission[key])) || admission.membershipRevision !== void 0 && !revision(admission.membershipRevision) || context.conversationId !== void 0 && admission.conversationId !== void 0 && admission.conversationId !== context.conversationId) invalid10();
|
|
3601
4530
|
}
|
|
3602
|
-
if (!Array.isArray(value.blobs))
|
|
4531
|
+
if (!Array.isArray(value.blobs)) invalid10();
|
|
3603
4532
|
const ids = /* @__PURE__ */ new Set();
|
|
3604
4533
|
for (const [index, blob] of value.blobs.entries()) {
|
|
3605
|
-
if (!record2(blob) || !keys(blob, ["id", "index"]) || !blobIdentity(blob.id) || blob.index !== index || ids.has(blob.id))
|
|
4534
|
+
if (!record2(blob) || !keys(blob, ["id", "index"]) || !blobIdentity(blob.id) || blob.index !== index || ids.has(blob.id)) invalid10();
|
|
3606
4535
|
ids.add(blob.id);
|
|
3607
4536
|
}
|
|
3608
|
-
if (typeof value.payload !== "string")
|
|
4537
|
+
if (typeof value.payload !== "string") invalid10();
|
|
3609
4538
|
}
|
|
3610
4539
|
__name(validateMetadata, "validateMetadata");
|
|
3611
4540
|
function decodePreparedJsonContent(value) {
|
|
@@ -3614,18 +4543,18 @@ function decodePreparedJsonContent(value) {
|
|
|
3614
4543
|
try {
|
|
3615
4544
|
decoded = JSON.parse(value.payload);
|
|
3616
4545
|
} catch {
|
|
3617
|
-
|
|
4546
|
+
invalid10();
|
|
3618
4547
|
}
|
|
3619
|
-
if (!record2(decoded) || !keys(decoded, ["body", "envelope"]) || !record2(decoded.body) || decoded.envelope !== void 0 && !record2(decoded.envelope))
|
|
3620
|
-
if (canonicalEndpointJson(decoded) !== value.payload)
|
|
4548
|
+
if (!record2(decoded) || !keys(decoded, ["body", "envelope"]) || !record2(decoded.body) || decoded.envelope !== void 0 && !record2(decoded.envelope)) invalid10();
|
|
4549
|
+
if (canonicalEndpointJson(decoded) !== value.payload) invalid10();
|
|
3621
4550
|
const { body, envelope } = decoded;
|
|
3622
4551
|
if (endpointOperationRequestRetention(value.context.action, body, envelope) === "memory") {
|
|
3623
4552
|
throw new PreparedContentError("PREPARED_CONTENT_MEMORY_ONLY");
|
|
3624
4553
|
}
|
|
3625
4554
|
const effective = { ...body, ...envelope };
|
|
3626
|
-
for (const
|
|
3627
|
-
const bound = value.context[
|
|
3628
|
-
if (bound !== void 0 && effective[
|
|
4555
|
+
for (const key of ["conversationId", "messageId"]) {
|
|
4556
|
+
const bound = value.context[key];
|
|
4557
|
+
if (bound !== void 0 && effective[key] !== void 0 && effective[key] !== bound) invalid10();
|
|
3629
4558
|
}
|
|
3630
4559
|
return envelope === void 0 ? { body } : { body, envelope };
|
|
3631
4560
|
}
|
|
@@ -3655,7 +4584,7 @@ function prepareJsonContent(input) {
|
|
|
3655
4584
|
}
|
|
3656
4585
|
__name(prepareJsonContent, "prepareJsonContent");
|
|
3657
4586
|
function logicalMediaBlobId(conversationId, messageId2, index) {
|
|
3658
|
-
if (!identity(conversationId) || !identity(messageId2) || !revision(index))
|
|
4587
|
+
if (!identity(conversationId) || !identity(messageId2) || !revision(index)) invalid10();
|
|
3659
4588
|
return JSON.stringify([conversationId, messageId2, index]);
|
|
3660
4589
|
}
|
|
3661
4590
|
__name(logicalMediaBlobId, "logicalMediaBlobId");
|
|
@@ -3671,10 +4600,130 @@ function isEndpointOperationReceipt(value) {
|
|
|
3671
4600
|
}
|
|
3672
4601
|
__name(isEndpointOperationReceipt, "isEndpointOperationReceipt");
|
|
3673
4602
|
|
|
4603
|
+
// ../backend-contracts/src/connectorRelay.ts
|
|
4604
|
+
var CONNECTOR_RELAY_INSTALLATION_PREFIX = "canon-relay:";
|
|
4605
|
+
var CONNECTOR_RELAY_INSTALLATION_PATTERN = /^canon-relay:[0-9a-f]{40}$/;
|
|
4606
|
+
function isConnectorRelayInstallation(installationId) {
|
|
4607
|
+
return typeof installationId === "string" && installationId.startsWith(CONNECTOR_RELAY_INSTALLATION_PREFIX);
|
|
4608
|
+
}
|
|
4609
|
+
__name(isConnectorRelayInstallation, "isConnectorRelayInstallation");
|
|
4610
|
+
var CONNECTOR_RELAY_CREDENTIAL_PATTERN = /^canon_relay_[A-Za-z0-9_-]{43}$/;
|
|
4611
|
+
function isConnectorRelayCredential(value) {
|
|
4612
|
+
return typeof value === "string" && CONNECTOR_RELAY_CREDENTIAL_PATTERN.test(value);
|
|
4613
|
+
}
|
|
4614
|
+
__name(isConnectorRelayCredential, "isConnectorRelayCredential");
|
|
4615
|
+
var CONNECTOR_RELAY_INPUT_ID_PATTERN = /^relay:[0-9a-f]{40}$/;
|
|
4616
|
+
var CONNECTOR_RELAY_MAX_BODY_BYTES = 256 * 1024;
|
|
4617
|
+
var CONNECTOR_RELAY_MAX_TEXT_BYTES = MLS_DEV_TEXT_MAX_BYTES;
|
|
4618
|
+
var CONNECTOR_RELAY_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
4619
|
+
var CONNECTOR_RELAY_ANSWER_WITHIN_MS = 7e3;
|
|
4620
|
+
function connectorRelayResponseBytes(value) {
|
|
4621
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
4622
|
+
}
|
|
4623
|
+
__name(connectorRelayResponseBytes, "connectorRelayResponseBytes");
|
|
4624
|
+
var ConnectorRelayRequestError = class extends Error {
|
|
4625
|
+
static {
|
|
4626
|
+
__name(this, "ConnectorRelayRequestError");
|
|
4627
|
+
}
|
|
4628
|
+
name = "ConnectorRelayRequestError";
|
|
4629
|
+
};
|
|
4630
|
+
function invalid11(what) {
|
|
4631
|
+
throw new ConnectorRelayRequestError(`Invalid relay request: ${what}`);
|
|
4632
|
+
}
|
|
4633
|
+
__name(invalid11, "invalid");
|
|
4634
|
+
function record3(value, required, optional = []) {
|
|
4635
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) invalid11("expected an object");
|
|
4636
|
+
const result = value;
|
|
4637
|
+
for (const key of Object.keys(result)) if (!required.includes(key) && !optional.includes(key)) invalid11(`unexpected ${key}`);
|
|
4638
|
+
for (const key of required) if (!Object.hasOwn(result, key)) invalid11(`missing ${key}`);
|
|
4639
|
+
return result;
|
|
4640
|
+
}
|
|
4641
|
+
__name(record3, "record");
|
|
4642
|
+
function id6(value, what, pattern = ENDPOINT_ID_PATTERN) {
|
|
4643
|
+
if (typeof value !== "string" || !pattern.test(value)) invalid11(what);
|
|
4644
|
+
return value;
|
|
4645
|
+
}
|
|
4646
|
+
__name(id6, "id");
|
|
4647
|
+
function doc(value, what) {
|
|
4648
|
+
if (typeof value !== "string" || !value || value.length > 200 || value.includes("/")) invalid11(what);
|
|
4649
|
+
return value;
|
|
4650
|
+
}
|
|
4651
|
+
__name(doc, "doc");
|
|
4652
|
+
var room = /* @__PURE__ */ __name((value) => doc(value, "conversationId"), "room");
|
|
4653
|
+
function text4(value) {
|
|
4654
|
+
if (typeof value !== "string" || !value.trim() || new TextEncoder().encode(value).length > CONNECTOR_RELAY_MAX_TEXT_BYTES) invalid11("text");
|
|
4655
|
+
return value;
|
|
4656
|
+
}
|
|
4657
|
+
__name(text4, "text");
|
|
4658
|
+
function limit(value) {
|
|
4659
|
+
if (value === void 0) return void 0;
|
|
4660
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > 100) invalid11("limit");
|
|
4661
|
+
return value;
|
|
4662
|
+
}
|
|
4663
|
+
__name(limit, "limit");
|
|
4664
|
+
function parseConnectorRelayRequest(value) {
|
|
4665
|
+
const body = record3(value, ["version", "agentId", "grantId", "op", "args"]);
|
|
4666
|
+
if (body.version !== 1) invalid11("version");
|
|
4667
|
+
const head = { version: 1, agentId: doc(body.agentId, "agentId"), grantId: id6(body.grantId, "grantId") };
|
|
4668
|
+
switch (body.op) {
|
|
4669
|
+
case "pending": {
|
|
4670
|
+
const args = record3(body.args, ["conversationId"], ["limit"]);
|
|
4671
|
+
return { ...head, op: "pending", args: { conversationId: room(args.conversationId), ...args.limit === void 0 ? {} : { limit: limit(args.limit) } } };
|
|
4672
|
+
}
|
|
4673
|
+
case "claim": {
|
|
4674
|
+
const args = record3(body.args, ["inputId", "requestId"]);
|
|
4675
|
+
return { ...head, op: "claim", args: { inputId: id6(args.inputId, "inputId", CONNECTOR_RELAY_INPUT_ID_PATTERN), requestId: id6(args.requestId, "requestId") } };
|
|
4676
|
+
}
|
|
4677
|
+
case "reply": {
|
|
4678
|
+
const args = record3(body.args, ["inputId", "requestId", "text"]);
|
|
4679
|
+
return { ...head, op: "reply", args: {
|
|
4680
|
+
inputId: id6(args.inputId, "inputId", CONNECTOR_RELAY_INPUT_ID_PATTERN),
|
|
4681
|
+
requestId: id6(args.requestId, "requestId"),
|
|
4682
|
+
text: text4(args.text)
|
|
4683
|
+
} };
|
|
4684
|
+
}
|
|
4685
|
+
case "settle": {
|
|
4686
|
+
const args = record3(body.args, ["inputId", "requestId", "outcome"]);
|
|
4687
|
+
if (!["completed", "no_reply", "failed"].includes(args.outcome)) invalid11("outcome");
|
|
4688
|
+
return { ...head, op: "settle", args: {
|
|
4689
|
+
inputId: id6(args.inputId, "inputId", CONNECTOR_RELAY_INPUT_ID_PATTERN),
|
|
4690
|
+
requestId: id6(args.requestId, "requestId"),
|
|
4691
|
+
outcome: args.outcome
|
|
4692
|
+
} };
|
|
4693
|
+
}
|
|
4694
|
+
case "read": {
|
|
4695
|
+
const args = record3(body.args, ["conversationId"], ["limit", "before"]);
|
|
4696
|
+
if (args.before !== void 0 && (typeof args.before !== "string" || !/^[1-9][0-9]{0,15}$/.test(args.before))) invalid11("before");
|
|
4697
|
+
return { ...head, op: "read", args: {
|
|
4698
|
+
conversationId: room(args.conversationId),
|
|
4699
|
+
...args.limit === void 0 ? {} : { limit: limit(args.limit) },
|
|
4700
|
+
...args.before === void 0 ? {} : { before: args.before }
|
|
4701
|
+
} };
|
|
4702
|
+
}
|
|
4703
|
+
case "send": {
|
|
4704
|
+
const args = record3(body.args, ["conversationId", "messageId", "text"]);
|
|
4705
|
+
return { ...head, op: "send", args: { conversationId: room(args.conversationId), messageId: id6(args.messageId, "messageId"), text: text4(args.text) } };
|
|
4706
|
+
}
|
|
4707
|
+
case "credential": {
|
|
4708
|
+
const args = record3(body.args, ["installationId", "generation", "credential"]);
|
|
4709
|
+
if (!Number.isSafeInteger(args.generation) || args.generation < 1) invalid11("generation");
|
|
4710
|
+
if (!isConnectorRelayCredential(args.credential)) invalid11("credential");
|
|
4711
|
+
return { ...head, op: "credential", args: {
|
|
4712
|
+
installationId: id6(args.installationId, "installationId", CONNECTOR_RELAY_INSTALLATION_PATTERN),
|
|
4713
|
+
generation: args.generation,
|
|
4714
|
+
credential: args.credential
|
|
4715
|
+
} };
|
|
4716
|
+
}
|
|
4717
|
+
default:
|
|
4718
|
+
return invalid11("op");
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
__name(parseConnectorRelayRequest, "parseConnectorRelayRequest");
|
|
4722
|
+
|
|
3674
4723
|
// ../backend-contracts/src/deliveryObservation.ts
|
|
3675
4724
|
var isTerminalDeliveryOutcome = /* @__PURE__ */ __name((outcome) => !["unknown", "pending"].includes(outcome), "isTerminalDeliveryOutcome");
|
|
3676
4725
|
function mergeDeliveryObservation(current, next, expected) {
|
|
3677
|
-
if (next.schemaVersion !== 2 || !Number.isSafeInteger(next.revision) || next.revision < 0 || !["unknown", "pending", "published", "cancelled", "expired", "rejected"].includes(next.outcome) || Object.entries(expected).some(([
|
|
4726
|
+
if (next.schemaVersion !== 2 || !Number.isSafeInteger(next.revision) || next.revision < 0 || !["unknown", "pending", "published", "cancelled", "expired", "rejected"].includes(next.outcome) || Object.entries(expected).some(([key, value]) => next[key] !== value)) return current;
|
|
3678
4727
|
if (current && (next.revision < current.revision || Object.keys(expected).length < 5 && (current.installationId !== next.installationId || current.environmentId !== next.environmentId) || isTerminalDeliveryOutcome(current.outcome) && next.outcome !== current.outcome || next.revision === current.revision && next.outcome !== current.outcome)) return current;
|
|
3679
4728
|
return next;
|
|
3680
4729
|
}
|
|
@@ -3695,7 +4744,7 @@ __name(classifyTransferHttpFailure, "classifyTransferHttpFailure");
|
|
|
3695
4744
|
function classifyTransferReceipt(value, expected) {
|
|
3696
4745
|
if (!value || typeof value !== "object" || Array.isArray(value)) return "invalid";
|
|
3697
4746
|
const receipt = value;
|
|
3698
|
-
if (Object.entries(expected).some(([
|
|
4747
|
+
if (Object.entries(expected).some(([key, field]) => receipt[key] !== field)) return "invalid";
|
|
3699
4748
|
switch (receipt.status) {
|
|
3700
4749
|
case "succeeded":
|
|
3701
4750
|
return "resultError" in receipt || !receipt.result || typeof receipt.result !== "object" || Array.isArray(receipt.result) ? "uncertain" : "result";
|
|
@@ -3712,17 +4761,8 @@ function classifyTransferReceipt(value, expected) {
|
|
|
3712
4761
|
__name(classifyTransferReceipt, "classifyTransferReceipt");
|
|
3713
4762
|
|
|
3714
4763
|
// ../backend-contracts/src/artifacts.ts
|
|
3715
|
-
var ARTIFACT_PROTOCOL_VERSION = 2;
|
|
3716
|
-
var ARTIFACT_PROTOCOL_MIN_VERSION = 2;
|
|
3717
|
-
var artifactSchemaName = /* @__PURE__ */ __name((version) => `canon.artifact.v${version}`, "artifactSchemaName");
|
|
3718
4764
|
var ARTIFACT_COMMAND_MAX_BYTES = 64 * 1024;
|
|
3719
4765
|
var ARTIFACT_STATE_MAX_BYTES = 128 * 1024;
|
|
3720
|
-
var ARTIFACT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,159}$/;
|
|
3721
|
-
var ARTIFACT_KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
3722
|
-
var RESERVED_KEYS = /* @__PURE__ */ new Set(["constructor", "prototype", "toString", "__proto__", "hasOwnProperty", "valueOf"]);
|
|
3723
|
-
var ARTIFACT_KINDS = ["chess", "form", "image"];
|
|
3724
|
-
var ARTIFACT_COMMAND_TYPES = ["create", "chess.move", "chess.resign", "form.set", "form.fields", "image.add", "image.select", "archive"];
|
|
3725
|
-
var ARTIFACT_FORM_FIELD_TYPES = ["text", "textarea", "number", "date", "select", "checkbox", "email", "tel", "url"];
|
|
3726
4766
|
var ARTIFACT_LIMITS = Object.freeze({
|
|
3727
4767
|
titleChars: 120,
|
|
3728
4768
|
chessMaxPlies: 600,
|
|
@@ -3747,604 +4787,7 @@ var ARTIFACT_LIMITS = Object.freeze({
|
|
|
3747
4787
|
outcomes: 200,
|
|
3748
4788
|
outcomeMessageChars: 500
|
|
3749
4789
|
});
|
|
3750
|
-
function isArtifactId(value) {
|
|
3751
|
-
return typeof value === "string" && ARTIFACT_ID_PATTERN.test(value);
|
|
3752
|
-
}
|
|
3753
|
-
__name(isArtifactId, "isArtifactId");
|
|
3754
|
-
function isArtifactKey(value) {
|
|
3755
|
-
return typeof value === "string" && ARTIFACT_KEY_PATTERN.test(value) && !RESERVED_KEYS.has(value);
|
|
3756
|
-
}
|
|
3757
|
-
__name(isArtifactKey, "isArtifactKey");
|
|
3758
|
-
var ArtifactValidationError = class extends Error {
|
|
3759
|
-
static {
|
|
3760
|
-
__name(this, "ArtifactValidationError");
|
|
3761
|
-
}
|
|
3762
|
-
constructor(message) {
|
|
3763
|
-
super(message);
|
|
3764
|
-
this.name = "ArtifactValidationError";
|
|
3765
|
-
}
|
|
3766
|
-
};
|
|
3767
|
-
var isObject = /* @__PURE__ */ __name((value) => value !== null && typeof value === "object" && !Array.isArray(value), "isObject");
|
|
3768
|
-
function quoteArtifactText(value) {
|
|
3769
|
-
if (typeof value === "number" || typeof value === "boolean") return JSON.stringify(String(value));
|
|
3770
|
-
if (typeof value !== "string") return value === null ? "(null)" : value === void 0 ? "(nothing)" : Array.isArray(value) ? "(a list)" : typeof value === "object" ? "(an object)" : "(an unsupported value)";
|
|
3771
|
-
return JSON.stringify(value.length > 80 ? `${value.slice(0, 80)}\u2026` : value);
|
|
3772
|
-
}
|
|
3773
|
-
__name(quoteArtifactText, "quoteArtifactText");
|
|
3774
|
-
var CONTROL_RUNS = /[\u0000-\u001F\u007F-\u009F\u2028\u2029]+/g;
|
|
3775
|
-
function artifactSingleLine(value) {
|
|
3776
|
-
return value.replace(CONTROL_RUNS, " ").trim();
|
|
3777
|
-
}
|
|
3778
|
-
__name(artifactSingleLine, "artifactSingleLine");
|
|
3779
|
-
var fail = /* @__PURE__ */ __name((message) => {
|
|
3780
|
-
throw new ArtifactValidationError(message);
|
|
3781
|
-
}, "fail");
|
|
3782
|
-
function exactKeys(value, allowed, where) {
|
|
3783
|
-
for (const key2 of Object.keys(value)) if (!allowed.includes(key2)) fail(`Unexpected property ${quoteArtifactText(key2)} in ${where}`);
|
|
3784
|
-
}
|
|
3785
|
-
__name(exactKeys, "exactKeys");
|
|
3786
|
-
function boundedText(value, maximum, what, options = {}) {
|
|
3787
|
-
if (value === void 0 && !options.required) return void 0;
|
|
3788
|
-
if (typeof value !== "string") return fail(`${what} must be text`);
|
|
3789
|
-
const text3 = artifactSingleLine(value);
|
|
3790
|
-
if (options.required && !text3) return fail(`${what} must not be empty`);
|
|
3791
|
-
if (text3.length > maximum) return fail(`${what} must be at most ${maximum} characters`);
|
|
3792
|
-
return text3 || void 0;
|
|
3793
|
-
}
|
|
3794
|
-
__name(boundedText, "boundedText");
|
|
3795
|
-
var ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
3796
|
-
function isCalendarDate(value) {
|
|
3797
|
-
const match = ISO_DATE.exec(value);
|
|
3798
|
-
if (!match) return false;
|
|
3799
|
-
const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
3800
|
-
if (month < 1 || month > 12 || day < 1) return false;
|
|
3801
|
-
const days = [31, year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1];
|
|
3802
|
-
return day <= days;
|
|
3803
|
-
}
|
|
3804
|
-
__name(isCalendarDate, "isCalendarDate");
|
|
3805
|
-
function roundFormNumber(value, decimals) {
|
|
3806
|
-
if (!Number.isFinite(value) || !Number.isSafeInteger(decimals) || decimals < 0 || decimals > 20) return value;
|
|
3807
|
-
const [mantissa, exponent = "0"] = String(Math.abs(value)).split("e");
|
|
3808
|
-
const shifted = Math.round(Number(`${mantissa}e${Number(exponent) + decimals}`));
|
|
3809
|
-
const [m2, e2 = "0"] = String(shifted).split("e");
|
|
3810
|
-
const rounded = Number(`${m2}e${Number(e2) - decimals}`);
|
|
3811
|
-
if (!Number.isFinite(rounded)) return value;
|
|
3812
|
-
const signed = value < 0 ? -rounded : rounded;
|
|
3813
|
-
return signed === 0 ? 0 : signed;
|
|
3814
|
-
}
|
|
3815
|
-
__name(roundFormNumber, "roundFormNumber");
|
|
3816
4790
|
var NUMBER_MAX_TEXT = String(ARTIFACT_LIMITS.formNumberMax).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
3817
|
-
function validateFormValue(spec, value) {
|
|
3818
|
-
const name = spec.label ? quoteArtifactText(spec.label) : "Value";
|
|
3819
|
-
if (value === void 0 || value === null || value === "") return null;
|
|
3820
|
-
switch (spec.type) {
|
|
3821
|
-
case "text":
|
|
3822
|
-
case "textarea":
|
|
3823
|
-
case "email":
|
|
3824
|
-
case "tel":
|
|
3825
|
-
case "url":
|
|
3826
|
-
if (typeof value !== "string") return fail(`${name} must be text`);
|
|
3827
|
-
if (value.length > ARTIFACT_LIMITS.formValueChars) return fail(`${name} must be at most ${ARTIFACT_LIMITS.formValueChars} characters`);
|
|
3828
|
-
return value;
|
|
3829
|
-
case "date":
|
|
3830
|
-
if (typeof value !== "string" || !isCalendarDate(value)) return fail(`${name} must be a calendar date (YYYY-MM-DD)`);
|
|
3831
|
-
return value;
|
|
3832
|
-
case "select":
|
|
3833
|
-
if (typeof value !== "string" || !spec.options?.includes(value)) return fail(`${name} must be one of: ${(spec.options ?? []).slice(0, 10).map(quoteArtifactText).join(", ")}${(spec.options?.length ?? 0) > 10 ? ", \u2026" : ""}`);
|
|
3834
|
-
return value;
|
|
3835
|
-
case "number":
|
|
3836
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return fail(`${name} must be a number`);
|
|
3837
|
-
if (Math.abs(value) > ARTIFACT_LIMITS.formNumberMax) return fail(`${name} must be between -${NUMBER_MAX_TEXT} and ${NUMBER_MAX_TEXT}`);
|
|
3838
|
-
return spec.decimals === void 0 ? value === 0 ? 0 : value : roundFormNumber(value, spec.decimals);
|
|
3839
|
-
case "checkbox":
|
|
3840
|
-
if (typeof value !== "boolean") return fail(`${name} must be true or false`);
|
|
3841
|
-
return value;
|
|
3842
|
-
default:
|
|
3843
|
-
return fail(`${name} has an unknown type`);
|
|
3844
|
-
}
|
|
3845
|
-
}
|
|
3846
|
-
__name(validateFormValue, "validateFormValue");
|
|
3847
|
-
function formValuesEqual(a, b) {
|
|
3848
|
-
const left = a === void 0 || a === "" ? null : a;
|
|
3849
|
-
const right = b === void 0 || b === "" ? null : b;
|
|
3850
|
-
return left === right;
|
|
3851
|
-
}
|
|
3852
|
-
__name(formValuesEqual, "formValuesEqual");
|
|
3853
|
-
function formatFormValue(value) {
|
|
3854
|
-
if (value === void 0 || value === null || value === "") return "empty";
|
|
3855
|
-
if (typeof value === "boolean") return value ? "checked" : "unchecked";
|
|
3856
|
-
if (typeof value === "number") return String(value);
|
|
3857
|
-
return quoteArtifactText(value);
|
|
3858
|
-
}
|
|
3859
|
-
__name(formatFormValue, "formatFormValue");
|
|
3860
|
-
function normalizeOptions(value, type, where) {
|
|
3861
|
-
if (type !== "select") {
|
|
3862
|
-
if (value !== void 0) fail(`Only select inputs have options (${where})`);
|
|
3863
|
-
return void 0;
|
|
3864
|
-
}
|
|
3865
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > ARTIFACT_LIMITS.formOptions) return fail(`${where} needs 1 to ${ARTIFACT_LIMITS.formOptions} options`);
|
|
3866
|
-
const options = value.map((option) => boundedText(option, ARTIFACT_LIMITS.formOptionChars, `An option of ${where}`, { required: true }));
|
|
3867
|
-
if (new Set(options).size !== options.length) fail(`Options of ${where} must be unique`);
|
|
3868
|
-
return options;
|
|
3869
|
-
}
|
|
3870
|
-
__name(normalizeOptions, "normalizeOptions");
|
|
3871
|
-
function normalizeDecimals(value, type, where) {
|
|
3872
|
-
if (value === void 0) return void 0;
|
|
3873
|
-
if (type !== "number") return fail(`Only number inputs have decimals (${where})`);
|
|
3874
|
-
if (!Number.isSafeInteger(value) || value < 0 || value > ARTIFACT_LIMITS.formMaxDecimals) return fail(`Decimals of ${where} must be a whole number from 0 to ${ARTIFACT_LIMITS.formMaxDecimals}`);
|
|
3875
|
-
return value;
|
|
3876
|
-
}
|
|
3877
|
-
__name(normalizeDecimals, "normalizeDecimals");
|
|
3878
|
-
function normalizeFormField(value) {
|
|
3879
|
-
if (!isObject(value)) return fail("Each field must be an object");
|
|
3880
|
-
if (!isArtifactKey(value.id)) return fail(`Field ID ${quoteArtifactText(value.id ?? "")} must be 1-64 letters, digits, '-' or '_'`);
|
|
3881
|
-
const where = `field ${quoteArtifactText(value.id)}`;
|
|
3882
|
-
exactKeys(value, ["id", "label", "type", "options", "required", "placeholder", "help", "decimals"], where);
|
|
3883
|
-
const label = boundedText(value.label, ARTIFACT_LIMITS.formLabelChars, `The label of ${where}`, { required: true });
|
|
3884
|
-
if (typeof value.type !== "string" || !ARTIFACT_FORM_FIELD_TYPES.includes(value.type)) return fail(`The type of ${where} must be one of ${ARTIFACT_FORM_FIELD_TYPES.join(", ")}`);
|
|
3885
|
-
const type = value.type;
|
|
3886
|
-
if (value.required !== void 0 && typeof value.required !== "boolean") fail(`required of ${where} must be true or false`);
|
|
3887
|
-
const options = normalizeOptions(value.options, type, where);
|
|
3888
|
-
const decimals = normalizeDecimals(value.decimals, type, where);
|
|
3889
|
-
const placeholder = boundedText(value.placeholder, ARTIFACT_LIMITS.formHintChars, `The placeholder of ${where}`);
|
|
3890
|
-
const help = boundedText(value.help, ARTIFACT_LIMITS.formHintChars, `The help text of ${where}`);
|
|
3891
|
-
return {
|
|
3892
|
-
id: value.id,
|
|
3893
|
-
label,
|
|
3894
|
-
type,
|
|
3895
|
-
...options ? { options } : {},
|
|
3896
|
-
...value.required === true ? { required: true } : {},
|
|
3897
|
-
...placeholder ? { placeholder } : {},
|
|
3898
|
-
...help ? { help } : {},
|
|
3899
|
-
...decimals !== void 0 ? { decimals } : {}
|
|
3900
|
-
};
|
|
3901
|
-
}
|
|
3902
|
-
__name(normalizeFormField, "normalizeFormField");
|
|
3903
|
-
function normalizeColumn(value) {
|
|
3904
|
-
if (!isObject(value)) return fail("Each table column must be an object");
|
|
3905
|
-
if (!isArtifactKey(value.id)) return fail(`Column ID ${quoteArtifactText(value.id ?? "")} must be 1-64 letters, digits, '-' or '_'`);
|
|
3906
|
-
const where = `column ${quoteArtifactText(value.id)}`;
|
|
3907
|
-
exactKeys(value, ["id", "label", "type", "options", "decimals", "sum"], where);
|
|
3908
|
-
const label = boundedText(value.label, ARTIFACT_LIMITS.formLabelChars, `The label of ${where}`, { required: true });
|
|
3909
|
-
if (typeof value.type !== "string" || value.type === "textarea" || !ARTIFACT_FORM_FIELD_TYPES.includes(value.type)) return fail(`The type of ${where} must be one of ${ARTIFACT_FORM_FIELD_TYPES.filter((type2) => type2 !== "textarea").join(", ")}`);
|
|
3910
|
-
const type = value.type;
|
|
3911
|
-
const options = normalizeOptions(value.options, type, where);
|
|
3912
|
-
const decimals = normalizeDecimals(value.decimals, type, where);
|
|
3913
|
-
if (value.sum !== void 0 && typeof value.sum !== "boolean") fail(`sum of ${where} must be true or false`);
|
|
3914
|
-
if (value.sum === true && type !== "number") fail(`Only number columns can be summed (${where})`);
|
|
3915
|
-
return { id: value.id, label, type, ...options ? { options } : {}, ...decimals !== void 0 ? { decimals } : {}, ...value.sum === true ? { sum: true } : {} };
|
|
3916
|
-
}
|
|
3917
|
-
__name(normalizeColumn, "normalizeColumn");
|
|
3918
|
-
function normalizeFormTable(value) {
|
|
3919
|
-
if (!isObject(value)) return fail("table must be { label?, columns }");
|
|
3920
|
-
exactKeys(value, ["label", "columns"], "the table");
|
|
3921
|
-
const label = boundedText(value.label, ARTIFACT_LIMITS.formLabelChars, "The table label");
|
|
3922
|
-
if (!Array.isArray(value.columns) || value.columns.length < 1 || value.columns.length > ARTIFACT_LIMITS.formColumns) return fail(`A table needs 1 to ${ARTIFACT_LIMITS.formColumns} columns`);
|
|
3923
|
-
const columns = value.columns.map(normalizeColumn);
|
|
3924
|
-
if (new Set(columns.map((column) => column.id)).size !== columns.length) fail("Column IDs must be unique");
|
|
3925
|
-
return { ...label ? { label } : {}, columns };
|
|
3926
|
-
}
|
|
3927
|
-
__name(normalizeFormTable, "normalizeFormTable");
|
|
3928
|
-
function normalizeFormCells(table, value, where) {
|
|
3929
|
-
if (!isObject(value)) return fail(`The cells of ${where} must be an object`);
|
|
3930
|
-
const columns = new Map(table.columns.map((column) => [column.id, column]));
|
|
3931
|
-
for (const key2 of Object.keys(value)) if (!columns.has(key2)) fail(`Unknown column ${quoteArtifactText(key2)} in ${where}. Columns: ${table.columns.map((column) => column.id).join(", ")}`);
|
|
3932
|
-
const cells = {};
|
|
3933
|
-
for (const column of table.columns) cells[column.id] = validateFormValue(column, Object.hasOwn(value, column.id) ? value[column.id] : null);
|
|
3934
|
-
return cells;
|
|
3935
|
-
}
|
|
3936
|
-
__name(normalizeFormCells, "normalizeFormCells");
|
|
3937
|
-
function normalizeFormInput(input) {
|
|
3938
|
-
if (!isObject(input)) return fail("A form needs { fields, values?, table?, rows? }");
|
|
3939
|
-
exactKeys(input, ["fields", "values", "table", "rows"], "the form");
|
|
3940
|
-
if (!Array.isArray(input.fields) || input.fields.length > ARTIFACT_LIMITS.formFields) return fail(`A form needs a fields list of at most ${ARTIFACT_LIMITS.formFields} fields`);
|
|
3941
|
-
const fields = input.fields.map(normalizeFormField);
|
|
3942
|
-
const ids = /* @__PURE__ */ new Set();
|
|
3943
|
-
for (const field of fields) {
|
|
3944
|
-
if (ids.has(field.id)) fail(`Field ID ${quoteArtifactText(field.id)} is used twice`);
|
|
3945
|
-
ids.add(field.id);
|
|
3946
|
-
}
|
|
3947
|
-
const values = {};
|
|
3948
|
-
if (input.values !== void 0 && !isObject(input.values)) fail("values must be an object keyed by field ID");
|
|
3949
|
-
const given = input.values ?? {};
|
|
3950
|
-
for (const key2 of Object.keys(given)) if (!ids.has(key2)) fail(`Unknown field ${quoteArtifactText(key2)} in values. Fields: ${fields.map((field) => field.id).join(", ") || "none"}`);
|
|
3951
|
-
for (const field of fields) values[field.id] = validateFormValue(field, Object.hasOwn(given, field.id) ? given[field.id] : null);
|
|
3952
|
-
const table = input.table === void 0 ? void 0 : normalizeFormTable(input.table);
|
|
3953
|
-
const rows = [];
|
|
3954
|
-
if (input.rows !== void 0) {
|
|
3955
|
-
if (!Array.isArray(input.rows)) return fail("rows must be a list");
|
|
3956
|
-
if (input.rows.length && !table) fail("Rows need a table with columns");
|
|
3957
|
-
if (input.rows.length > ARTIFACT_LIMITS.formRows) fail(`A table holds at most ${ARTIFACT_LIMITS.formRows} rows`);
|
|
3958
|
-
const rowIds = /* @__PURE__ */ new Set();
|
|
3959
|
-
input.rows.forEach((row, index) => {
|
|
3960
|
-
if (!isObject(row)) fail(`Row ${index + 1} must be { id?, cells }`);
|
|
3961
|
-
const value = row;
|
|
3962
|
-
exactKeys(value, ["id", "cells"], `row ${index + 1}`);
|
|
3963
|
-
const id6 = value.id === void 0 ? `r-${index + 1}` : value.id;
|
|
3964
|
-
if (!isArtifactKey(id6)) fail(`Row ID ${quoteArtifactText(id6)} must be 1-64 letters, digits, '-' or '_'`);
|
|
3965
|
-
if (rowIds.has(id6)) fail(`Row ID ${quoteArtifactText(id6)} is used twice`);
|
|
3966
|
-
rowIds.add(id6);
|
|
3967
|
-
rows.push({ id: id6, cells: normalizeFormCells(table, value.cells ?? {}, `row ${index + 1}`) });
|
|
3968
|
-
});
|
|
3969
|
-
}
|
|
3970
|
-
return { fields, values, ...table ? { table } : {}, rows };
|
|
3971
|
-
}
|
|
3972
|
-
__name(normalizeFormInput, "normalizeFormInput");
|
|
3973
|
-
|
|
3974
|
-
// ../backend-contracts/src/artifactActions.ts
|
|
3975
|
-
var ARTIFACT_UPDATE_ACTION_TYPES = ["chess.move", "chess.resign", "form.set", "form.fields", "image.select", "archive"];
|
|
3976
|
-
function object(properties, required = [], description) {
|
|
3977
|
-
return { type: "object", properties, required, additionalProperties: false, ...description ? { description } : {} };
|
|
3978
|
-
}
|
|
3979
|
-
__name(object, "object");
|
|
3980
|
-
function id5(description) {
|
|
3981
|
-
return { type: "string", pattern: ARTIFACT_ID_PATTERN.source, description };
|
|
3982
|
-
}
|
|
3983
|
-
__name(id5, "id");
|
|
3984
|
-
function key(description) {
|
|
3985
|
-
return { type: "string", pattern: ARTIFACT_KEY_PATTERN.source, description };
|
|
3986
|
-
}
|
|
3987
|
-
__name(key, "key");
|
|
3988
|
-
function scalarSchema() {
|
|
3989
|
-
return { type: ["string", "number", "boolean", "null"] };
|
|
3990
|
-
}
|
|
3991
|
-
__name(scalarSchema, "scalarSchema");
|
|
3992
|
-
function valueMap(description) {
|
|
3993
|
-
return { type: "object", additionalProperties: scalarSchema(), description };
|
|
3994
|
-
}
|
|
3995
|
-
__name(valueMap, "valueMap");
|
|
3996
|
-
function text2(max, description) {
|
|
3997
|
-
return { type: "string", minLength: 1, maxLength: max, ...description ? { description } : {} };
|
|
3998
|
-
}
|
|
3999
|
-
__name(text2, "text");
|
|
4000
|
-
function fieldSchemas() {
|
|
4001
|
-
const options = { type: "array", items: text2(ARTIFACT_LIMITS.formOptionChars), minItems: 1, maxItems: ARTIFACT_LIMITS.formOptions, description: "The choices (select only)." };
|
|
4002
|
-
const decimals = { type: "integer", minimum: 0, maximum: ARTIFACT_LIMITS.formMaxDecimals, description: "Number only: round to this many decimals." };
|
|
4003
|
-
return {
|
|
4004
|
-
field: object({
|
|
4005
|
-
id: key("Stable field ID: letters, digits, - or _."),
|
|
4006
|
-
label: text2(ARTIFACT_LIMITS.formLabelChars),
|
|
4007
|
-
type: { enum: [...ARTIFACT_FORM_FIELD_TYPES] },
|
|
4008
|
-
options,
|
|
4009
|
-
required: { type: "boolean", description: "Marked as required (a hint; empty values are allowed)." },
|
|
4010
|
-
placeholder: text2(ARTIFACT_LIMITS.formHintChars),
|
|
4011
|
-
help: text2(ARTIFACT_LIMITS.formHintChars),
|
|
4012
|
-
decimals
|
|
4013
|
-
}, ["id", "label", "type"]),
|
|
4014
|
-
column: object({
|
|
4015
|
-
id: key("Stable column ID."),
|
|
4016
|
-
label: text2(ARTIFACT_LIMITS.formLabelChars),
|
|
4017
|
-
type: { enum: ARTIFACT_FORM_FIELD_TYPES.filter((type) => type !== "textarea") },
|
|
4018
|
-
options,
|
|
4019
|
-
decimals,
|
|
4020
|
-
sum: { type: "boolean", description: "Number only: show the column total." }
|
|
4021
|
-
}, ["id", "label", "type"])
|
|
4022
|
-
};
|
|
4023
|
-
}
|
|
4024
|
-
__name(fieldSchemas, "fieldSchemas");
|
|
4025
|
-
var ARTIFACT_ID_DESCRIPTION = "The work\u2019s artifactId (from artifact_list, artifact_create or a message reference).";
|
|
4026
|
-
var LAST_ATTACHMENT_INDEX = VERB_LIMITS.messageAttachments - 1;
|
|
4027
|
-
function artifactActionSchemas() {
|
|
4028
|
-
const { field, column } = fieldSchemas();
|
|
4029
|
-
const fraction = /* @__PURE__ */ __name(() => ({ type: "number", minimum: 0, maximum: 1 }), "fraction");
|
|
4030
|
-
return {
|
|
4031
|
-
create: object({
|
|
4032
|
-
kind: { enum: ["chess", "form", "image"] },
|
|
4033
|
-
title: text2(ARTIFACT_LIMITS.titleChars),
|
|
4034
|
-
chess: object({ playAs: { enum: ["white", "black"], description: "Your seat. Omit for an open board." }, fen: text2(100, "Optional starting position (FEN).") }, [], 'Only for kind "chess".'),
|
|
4035
|
-
form: object({
|
|
4036
|
-
fields: { type: "array", maxItems: ARTIFACT_LIMITS.formFields, items: field },
|
|
4037
|
-
values: valueMap("Initial values keyed by field ID."),
|
|
4038
|
-
table: object({ label: text2(ARTIFACT_LIMITS.formLabelChars), columns: { type: "array", minItems: 1, maxItems: ARTIFACT_LIMITS.formColumns, items: column } }, ["columns"], "Optional table of line items."),
|
|
4039
|
-
rows: { type: "array", maxItems: ARTIFACT_LIMITS.formRows, items: object({ id: key("Optional row ID."), cells: valueMap("Cell values keyed by column ID.") }, ["cells"]) }
|
|
4040
|
-
}, ["fields"], 'Required for kind "form".')
|
|
4041
|
-
}, ["kind", "title"]),
|
|
4042
|
-
update: {
|
|
4043
|
-
description: "What to change. Pick one action by its type.",
|
|
4044
|
-
oneOf: [
|
|
4045
|
-
object({ type: { const: "chess.move" }, move: text2(16, 'Your move in SAN ("Nf3", "exd5", "O-O", "e8=Q") or UCI ("g1f3").') }, ["type", "move"]),
|
|
4046
|
-
object({ type: { const: "chess.resign" } }, ["type"]),
|
|
4047
|
-
object({
|
|
4048
|
-
type: { const: "form.set" },
|
|
4049
|
-
values: valueMap('Field values keyed by field ID. Numbers may be given as numeric text; "" or null empties a value.'),
|
|
4050
|
-
cells: { type: "array", maxItems: ARTIFACT_LIMITS.formChanges, items: object({ row: key("Row ID"), column: key("Column ID"), value: scalarSchema() }, ["row", "column", "value"]) },
|
|
4051
|
-
addRows: { type: "array", maxItems: ARTIFACT_LIMITS.formChanges, items: object({ id: key("Optional row ID; generated when omitted (returned as addedRowIds)."), cells: valueMap("Cell values keyed by column ID.") }, ["cells"]) },
|
|
4052
|
-
removeRows: { type: "array", maxItems: ARTIFACT_LIMITS.formChanges, items: key("Row ID") }
|
|
4053
|
-
}, ["type"]),
|
|
4054
|
-
object({ type: { const: "form.fields" }, add: { type: "array", maxItems: ARTIFACT_LIMITS.formFields, items: field }, remove: { type: "array", maxItems: ARTIFACT_LIMITS.formFields, items: key("Field ID") } }, ["type"]),
|
|
4055
|
-
object({ type: { const: "image.select" }, variantId: id5("The version to make current.") }, ["type", "variantId"]),
|
|
4056
|
-
object({ type: { const: "archive" }, archived: { type: "boolean", description: "true archives the work (read-only); false restores it." } }, ["type", "archived"])
|
|
4057
|
-
]
|
|
4058
|
-
},
|
|
4059
|
-
generateImage: object({
|
|
4060
|
-
prompt: text2(ARTIFACT_LIMITS.imagePromptChars, "What to generate or change."),
|
|
4061
|
-
label: text2(ARTIFACT_LIMITS.imageLabelChars, 'A short name for the new version, e.g. "darker sky".'),
|
|
4062
|
-
from: {
|
|
4063
|
-
description: '"current", "new", or the version to edit (a version ID, or { variantId }).',
|
|
4064
|
-
anyOf: [{ type: "string", pattern: ARTIFACT_ID_PATTERN.source }, object({ variantId: id5("The version to edit.") }, ["variantId"])]
|
|
4065
|
-
},
|
|
4066
|
-
region: object({ x: fraction(), y: fraction(), width: fraction(), height: fraction() }, ["x", "y", "width", "height"], "Optional area to change.")
|
|
4067
|
-
}, ["prompt", "label", "from"]),
|
|
4068
|
-
addImage: object({
|
|
4069
|
-
label: text2(ARTIFACT_LIMITS.imageLabelChars, "A short name for the version."),
|
|
4070
|
-
prompt: text2(ARTIFACT_LIMITS.imagePromptChars, "Optional: how the image was made."),
|
|
4071
|
-
sourceVariantId: id5("Optional: the version this one was derived from."),
|
|
4072
|
-
path: text2(4096, "Absolute path of a local JPEG or PNG file (only where the host allows local files)."),
|
|
4073
|
-
messageId: id5("A message in this conversation with an image attached."),
|
|
4074
|
-
attachmentIndex: { type: "integer", minimum: 0, maximum: LAST_ATTACHMENT_INDEX, description: "Which attachment of that message (default: its first image)." }
|
|
4075
|
-
}, ["label"])
|
|
4076
|
-
};
|
|
4077
|
-
}
|
|
4078
|
-
__name(artifactActionSchemas, "artifactActionSchemas");
|
|
4079
|
-
function withArtifactId(schema) {
|
|
4080
|
-
return { ...schema, properties: { artifactId: id5(ARTIFACT_ID_DESCRIPTION), ...schema.properties }, required: ["artifactId", ...schema.required] };
|
|
4081
|
-
}
|
|
4082
|
-
__name(withArtifactId, "withArtifactId");
|
|
4083
|
-
function artifactToolInputSchemas() {
|
|
4084
|
-
const actions = artifactActionSchemas();
|
|
4085
|
-
return {
|
|
4086
|
-
artifact_list: object({}),
|
|
4087
|
-
artifact_read: object({
|
|
4088
|
-
artifactId: id5(ARTIFACT_ID_DESCRIPTION),
|
|
4089
|
-
revisionId: id5("Optional earlier revision to read (from a message reference)."),
|
|
4090
|
-
variantId: id5("Image work: show this version instead of the current one.")
|
|
4091
|
-
}, ["artifactId"]),
|
|
4092
|
-
artifact_create: actions.create,
|
|
4093
|
-
artifact_update: object({
|
|
4094
|
-
artifactId: id5(ARTIFACT_ID_DESCRIPTION),
|
|
4095
|
-
action: actions.update,
|
|
4096
|
-
basedOnRevisionId: id5("Rarely needed: base the change on this revision instead of the version you last saw.")
|
|
4097
|
-
}, ["artifactId", "action"]),
|
|
4098
|
-
artifact_generate_image: withArtifactId(actions.generateImage),
|
|
4099
|
-
artifact_add_image: withArtifactId(actions.addImage)
|
|
4100
|
-
};
|
|
4101
|
-
}
|
|
4102
|
-
__name(artifactToolInputSchemas, "artifactToolInputSchemas");
|
|
4103
|
-
var isObject2 = /* @__PURE__ */ __name((input) => input !== null && typeof input === "object" && !Array.isArray(input), "isObject");
|
|
4104
|
-
var fail2 = /* @__PURE__ */ __name((message) => {
|
|
4105
|
-
throw new ArtifactValidationError(message);
|
|
4106
|
-
}, "fail");
|
|
4107
|
-
var quote = quoteArtifactText;
|
|
4108
|
-
function exactKeys2(input, allowed, where) {
|
|
4109
|
-
for (const name of Object.keys(input)) if (!allowed.includes(name)) fail2(`Unexpected ${JSON.stringify(name.slice(0, 40))} in ${where}`);
|
|
4110
|
-
}
|
|
4111
|
-
__name(exactKeys2, "exactKeys");
|
|
4112
|
-
function textArg(input, name, max, required = false) {
|
|
4113
|
-
if (input === void 0 && !required) return void 0;
|
|
4114
|
-
if (typeof input !== "string" || !input.trim() || input.length > max) return fail2(`${name} must be text of at most ${max} characters`);
|
|
4115
|
-
return input;
|
|
4116
|
-
}
|
|
4117
|
-
__name(textArg, "textArg");
|
|
4118
|
-
function lineArg(input, name, max) {
|
|
4119
|
-
const line = artifactSingleLine(textArg(input, name, max, true));
|
|
4120
|
-
return line || fail2(`${name} must be text of at most ${max} characters`);
|
|
4121
|
-
}
|
|
4122
|
-
__name(lineArg, "lineArg");
|
|
4123
|
-
function idArg(input, name) {
|
|
4124
|
-
if (input === void 0) return void 0;
|
|
4125
|
-
return isArtifactId(input) ? input : fail2(`${name} must be an ID (letters, digits, _ . : -)`);
|
|
4126
|
-
}
|
|
4127
|
-
__name(idArg, "idArg");
|
|
4128
|
-
var isScalar = /* @__PURE__ */ __name((input) => input === null || typeof input === "string" || typeof input === "boolean" || typeof input === "number" && Number.isFinite(input), "isScalar");
|
|
4129
|
-
function scalar(input, where) {
|
|
4130
|
-
return isScalar(input) ? input : fail2(`${where} must be text, a number, true, false or null`);
|
|
4131
|
-
}
|
|
4132
|
-
__name(scalar, "scalar");
|
|
4133
|
-
function scalars(input, what, known) {
|
|
4134
|
-
return Object.fromEntries(Object.entries(input).map(([name, item]) => {
|
|
4135
|
-
if (known && !known.ids.includes(name)) fail2(`Unknown ${what} ${quote(name)} in ${known.where}. ${known.list}: ${known.ids.join(", ") || "none"}`);
|
|
4136
|
-
return [name, scalar(item, `The value of ${what} ${quote(name)}`)];
|
|
4137
|
-
}));
|
|
4138
|
-
}
|
|
4139
|
-
__name(scalars, "scalars");
|
|
4140
|
-
function uniqueFields(fields) {
|
|
4141
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4142
|
-
for (const field of fields) {
|
|
4143
|
-
if (seen.has(field.id)) fail2(`Field ID ${quote(field.id)} is used twice`);
|
|
4144
|
-
seen.add(field.id);
|
|
4145
|
-
}
|
|
4146
|
-
}
|
|
4147
|
-
__name(uniqueFields, "uniqueFields");
|
|
4148
|
-
function chessAction(title, input, prefix) {
|
|
4149
|
-
if (input.playAs !== void 0 && input.playAs !== "white" && input.playAs !== "black") fail2(`${prefix}playAs must be "white" or "black"`);
|
|
4150
|
-
if (input.fen !== void 0 && (typeof input.fen !== "string" || !input.fen.trim() || input.fen.length > 100)) fail2(`${prefix}fen must be a FEN string`);
|
|
4151
|
-
if (input.opponent !== void 0 && !isArtifactId(input.opponent)) fail2("opponent must be a participant ID");
|
|
4152
|
-
return {
|
|
4153
|
-
kind: "chess",
|
|
4154
|
-
title,
|
|
4155
|
-
...input.playAs !== void 0 ? { playAs: input.playAs } : {},
|
|
4156
|
-
...input.opponent !== void 0 ? { opponent: input.opponent } : {},
|
|
4157
|
-
...input.fen !== void 0 ? { fen: input.fen.trim() } : {}
|
|
4158
|
-
};
|
|
4159
|
-
}
|
|
4160
|
-
__name(chessAction, "chessAction");
|
|
4161
|
-
function formAction(title, input) {
|
|
4162
|
-
if (!Array.isArray(input.fields) || input.fields.length > ARTIFACT_LIMITS.formFields) return fail2(`A form needs a fields list of at most ${ARTIFACT_LIMITS.formFields} fields`);
|
|
4163
|
-
const fields = input.fields.map(normalizeFormField);
|
|
4164
|
-
uniqueFields(fields);
|
|
4165
|
-
const fieldIds = fields.map((field) => field.id);
|
|
4166
|
-
if (input.values !== void 0 && !isObject2(input.values)) fail2("values must be an object keyed by field ID");
|
|
4167
|
-
const values = input.values === void 0 ? void 0 : scalars(input.values, "field", { ids: fieldIds, where: "values", list: "Fields" });
|
|
4168
|
-
const table = input.table === void 0 ? void 0 : normalizeFormTable(input.table);
|
|
4169
|
-
let rows;
|
|
4170
|
-
if (input.rows !== void 0) {
|
|
4171
|
-
if (!Array.isArray(input.rows)) return fail2("rows must be a list");
|
|
4172
|
-
if (input.rows.length && !table) fail2("Rows need a table with columns");
|
|
4173
|
-
if (input.rows.length > ARTIFACT_LIMITS.formRows) fail2(`A table holds at most ${ARTIFACT_LIMITS.formRows} rows`);
|
|
4174
|
-
const columnIds = table?.columns.map((column) => column.id) ?? [];
|
|
4175
|
-
const rowIds = /* @__PURE__ */ new Set();
|
|
4176
|
-
rows = input.rows.map((row, index) => {
|
|
4177
|
-
const where = `row ${index + 1}`;
|
|
4178
|
-
if (!isObject2(row)) return fail2(`Row ${index + 1} must be { id?, cells }`);
|
|
4179
|
-
exactKeys2(row, ["id", "cells"], where);
|
|
4180
|
-
if (row.id !== void 0) {
|
|
4181
|
-
if (!isArtifactKey(row.id)) fail2(`Row ID ${quote(row.id)} must be 1-64 letters, digits, '-' or '_'`);
|
|
4182
|
-
if (rowIds.has(row.id)) fail2(`Row ID ${quote(row.id)} is used twice`);
|
|
4183
|
-
rowIds.add(row.id);
|
|
4184
|
-
}
|
|
4185
|
-
if (row.cells !== void 0 && !isObject2(row.cells)) fail2(`The cells of ${where} must be an object`);
|
|
4186
|
-
return { ...row.id !== void 0 ? { id: row.id } : {}, cells: scalars(row.cells ?? {}, "column", { ids: columnIds, where, list: "Columns" }) };
|
|
4187
|
-
});
|
|
4188
|
-
}
|
|
4189
|
-
return { kind: "form", title, fields, ...values ? { values } : {}, ...table ? { table } : {}, ...rows ? { rows } : {} };
|
|
4190
|
-
}
|
|
4191
|
-
__name(formAction, "formAction");
|
|
4192
|
-
function parseArtifactCreateAction(input) {
|
|
4193
|
-
if (!isObject2(input)) return fail2("Describe the work to create: { kind, title, ... }");
|
|
4194
|
-
const title = lineArg(input.title, "title", ARTIFACT_LIMITS.titleChars);
|
|
4195
|
-
switch (input.kind) {
|
|
4196
|
-
case "chess":
|
|
4197
|
-
exactKeys2(input, ["kind", "title", "playAs", "opponent", "fen"], "a chess game");
|
|
4198
|
-
return chessAction(title, input, "");
|
|
4199
|
-
case "form":
|
|
4200
|
-
exactKeys2(input, ["kind", "title", "fields", "values", "table", "rows"], "a form");
|
|
4201
|
-
return formAction(title, input);
|
|
4202
|
-
case "image":
|
|
4203
|
-
exactKeys2(input, ["kind", "title"], "an image");
|
|
4204
|
-
return { kind: "image", title };
|
|
4205
|
-
default:
|
|
4206
|
-
return fail2('kind must be "chess", "form" or "image"');
|
|
4207
|
-
}
|
|
4208
|
-
}
|
|
4209
|
-
__name(parseArtifactCreateAction, "parseArtifactCreateAction");
|
|
4210
|
-
function formSetAction(input) {
|
|
4211
|
-
exactKeys2(input, ["type", "values", "cells", "addRows", "removeRows"], "form.set");
|
|
4212
|
-
const action = { type: "form.set" };
|
|
4213
|
-
let count = 0;
|
|
4214
|
-
if (input.values !== void 0) {
|
|
4215
|
-
if (!isObject2(input.values)) fail2("values must be an object keyed by field ID");
|
|
4216
|
-
action.values = scalars(input.values, "field");
|
|
4217
|
-
count += Object.keys(action.values).length;
|
|
4218
|
-
}
|
|
4219
|
-
if (input.cells !== void 0) {
|
|
4220
|
-
if (!Array.isArray(input.cells)) fail2("cells must be a list of { row, column, value }");
|
|
4221
|
-
action.cells = input.cells.map((cell) => {
|
|
4222
|
-
if (!isObject2(cell) || typeof cell.row !== "string" || typeof cell.column !== "string" || !Object.hasOwn(cell, "value")) return fail2("Each cell must be { row, column, value }");
|
|
4223
|
-
exactKeys2(cell, ["row", "column", "value"], "a cell");
|
|
4224
|
-
return { row: cell.row, column: cell.column, value: scalar(cell.value, `The value of column ${quote(cell.column)} in row ${quote(cell.row)}`) };
|
|
4225
|
-
});
|
|
4226
|
-
count += action.cells.length;
|
|
4227
|
-
}
|
|
4228
|
-
if (input.addRows !== void 0) {
|
|
4229
|
-
if (!Array.isArray(input.addRows)) fail2("addRows must be a list of { id?, cells }");
|
|
4230
|
-
const taken = /* @__PURE__ */ new Set();
|
|
4231
|
-
action.addRows = input.addRows.map((row) => {
|
|
4232
|
-
if (!isObject2(row) || row.cells !== void 0 && !isObject2(row.cells)) return fail2("Each added row must be { id?, cells }");
|
|
4233
|
-
exactKeys2(row, ["id", "cells"], "an added row");
|
|
4234
|
-
if (row.id !== void 0) {
|
|
4235
|
-
if (!isArtifactKey(row.id)) fail2(`Row ID ${quote(row.id)} must be 1-64 letters, digits, '-' or '_'`);
|
|
4236
|
-
if (taken.has(row.id)) fail2(`Row ID ${quote(row.id)} is already used`);
|
|
4237
|
-
taken.add(row.id);
|
|
4238
|
-
}
|
|
4239
|
-
return { ...row.id !== void 0 ? { id: row.id } : {}, cells: scalars(row.cells ?? {}, "column") };
|
|
4240
|
-
});
|
|
4241
|
-
count += action.addRows.length;
|
|
4242
|
-
}
|
|
4243
|
-
if (input.removeRows !== void 0) {
|
|
4244
|
-
if (!Array.isArray(input.removeRows) || input.removeRows.some((item) => typeof item !== "string")) fail2("removeRows must be a list of row IDs");
|
|
4245
|
-
action.removeRows = [...input.removeRows];
|
|
4246
|
-
count += action.removeRows.length;
|
|
4247
|
-
}
|
|
4248
|
-
if (!count) fail2("form.set needs values, cells, addRows or removeRows");
|
|
4249
|
-
if (count > ARTIFACT_LIMITS.formChanges) fail2(`One form.set can make at most ${ARTIFACT_LIMITS.formChanges} changes`);
|
|
4250
|
-
return action;
|
|
4251
|
-
}
|
|
4252
|
-
__name(formSetAction, "formSetAction");
|
|
4253
|
-
function formFieldsAction(input) {
|
|
4254
|
-
exactKeys2(input, ["type", "add", "remove"], "form.fields");
|
|
4255
|
-
if (input.add !== void 0 && !Array.isArray(input.add)) fail2("add must be a list of fields");
|
|
4256
|
-
if (input.remove !== void 0 && (!Array.isArray(input.remove) || input.remove.some((item) => typeof item !== "string"))) fail2("remove must be a list of field IDs");
|
|
4257
|
-
const add = input.add ?? [];
|
|
4258
|
-
const remove = input.remove ?? [];
|
|
4259
|
-
if (!add.length && !remove.length) fail2("form.fields needs fields to add or field IDs to remove");
|
|
4260
|
-
if (add.length > ARTIFACT_LIMITS.formFields) fail2(`A form holds at most ${ARTIFACT_LIMITS.formFields} fields`);
|
|
4261
|
-
const fields = add.map(normalizeFormField);
|
|
4262
|
-
uniqueFields(fields);
|
|
4263
|
-
const removed = /* @__PURE__ */ new Set();
|
|
4264
|
-
for (const name of remove) {
|
|
4265
|
-
if (removed.has(name)) fail2(`Field ${quote(name)} is removed twice`);
|
|
4266
|
-
removed.add(name);
|
|
4267
|
-
}
|
|
4268
|
-
for (const field of fields) if (removed.has(field.id)) fail2(`Field ${quote(field.id)} cannot be removed and added again in one command; field types do not change`);
|
|
4269
|
-
return { type: "form.fields", ...fields.length ? { add: fields } : {}, ...remove.length ? { remove: [...remove] } : {} };
|
|
4270
|
-
}
|
|
4271
|
-
__name(formFieldsAction, "formFieldsAction");
|
|
4272
|
-
function parseArtifactUpdateAction(input) {
|
|
4273
|
-
if (!isObject2(input) || typeof input.type !== "string" || !ARTIFACT_UPDATE_ACTION_TYPES.includes(input.type)) {
|
|
4274
|
-
return fail2(`action.type must be one of ${ARTIFACT_UPDATE_ACTION_TYPES.join(", ")}`);
|
|
4275
|
-
}
|
|
4276
|
-
const type = input.type;
|
|
4277
|
-
switch (type) {
|
|
4278
|
-
case "chess.move": {
|
|
4279
|
-
exactKeys2(input, ["type", "move"], "chess.move");
|
|
4280
|
-
const move = typeof input.move === "string" ? input.move.trim() : "";
|
|
4281
|
-
if (!move || move.length > 16) return fail2('move must be a move in SAN (for example "Nf3") or UCI (for example "g1f3")');
|
|
4282
|
-
return { type, move };
|
|
4283
|
-
}
|
|
4284
|
-
case "chess.resign":
|
|
4285
|
-
exactKeys2(input, ["type"], "chess.resign");
|
|
4286
|
-
return { type };
|
|
4287
|
-
case "image.select":
|
|
4288
|
-
exactKeys2(input, ["type", "variantId"], "image.select");
|
|
4289
|
-
if (!isArtifactId(input.variantId)) return fail2("variantId must be a version ID from the image work");
|
|
4290
|
-
return { type, variantId: input.variantId };
|
|
4291
|
-
case "archive":
|
|
4292
|
-
exactKeys2(input, ["type", "archived"], "archive");
|
|
4293
|
-
if (typeof input.archived !== "boolean") return fail2("archived must be true or false");
|
|
4294
|
-
return { type, archived: input.archived };
|
|
4295
|
-
case "form.fields":
|
|
4296
|
-
return formFieldsAction(input);
|
|
4297
|
-
case "form.set":
|
|
4298
|
-
return formSetAction(input);
|
|
4299
|
-
}
|
|
4300
|
-
}
|
|
4301
|
-
__name(parseArtifactUpdateAction, "parseArtifactUpdateAction");
|
|
4302
|
-
var REGION_MESSAGE = "The region must be fractions of the image: x, y, width and height between 0 and 1";
|
|
4303
|
-
function parseArtifactImageRegion(input) {
|
|
4304
|
-
if (!isObject2(input)) return fail2(REGION_MESSAGE);
|
|
4305
|
-
exactKeys2(input, ["x", "y", "width", "height"], "region");
|
|
4306
|
-
const { x, y, width, height } = input;
|
|
4307
|
-
if ([x, y, width, height].some((item) => typeof item !== "number" || !Number.isFinite(item) || item < 0 || item > 1) || width <= 0 || height <= 0 || x + width > 1 || y + height > 1) return fail2(REGION_MESSAGE);
|
|
4308
|
-
return { x, y, width, height };
|
|
4309
|
-
}
|
|
4310
|
-
__name(parseArtifactImageRegion, "parseArtifactImageRegion");
|
|
4311
|
-
function parseArtifactImageSource(input) {
|
|
4312
|
-
if (input === "current" || input === "new") return input;
|
|
4313
|
-
if (isArtifactId(input)) return { variantId: input };
|
|
4314
|
-
if (isObject2(input) && Object.keys(input).length === 1 && isArtifactId(input.variantId)) return { variantId: input.variantId };
|
|
4315
|
-
return fail2("from must be 'current', 'new' or a version ID");
|
|
4316
|
-
}
|
|
4317
|
-
__name(parseArtifactImageSource, "parseArtifactImageSource");
|
|
4318
|
-
function parseArtifactGenerateImageAction(input) {
|
|
4319
|
-
if (!isObject2(input)) return fail2("Describe the image to generate");
|
|
4320
|
-
exactKeys2(input, ["prompt", "label", "from", "region"], "the image request");
|
|
4321
|
-
const prompt = textArg(input.prompt, "prompt", ARTIFACT_LIMITS.imagePromptChars, true).trim();
|
|
4322
|
-
const label = lineArg(input.label, "label", ARTIFACT_LIMITS.imageLabelChars);
|
|
4323
|
-
const from = parseArtifactImageSource(input.from);
|
|
4324
|
-
return { prompt, label, from, ...input.region !== void 0 ? { region: parseArtifactImageRegion(input.region) } : {} };
|
|
4325
|
-
}
|
|
4326
|
-
__name(parseArtifactGenerateImageAction, "parseArtifactGenerateImageAction");
|
|
4327
|
-
function parseArtifactAddImageAction(input) {
|
|
4328
|
-
if (!isObject2(input)) return fail2("Describe the image to add: { label, path | messageId }");
|
|
4329
|
-
exactKeys2(input, ["label", "prompt", "sourceVariantId", "path", "messageId", "attachmentIndex"], "the image");
|
|
4330
|
-
const label = lineArg(input.label, "label", ARTIFACT_LIMITS.imageLabelChars);
|
|
4331
|
-
const prompt = textArg(input.prompt, "prompt", ARTIFACT_LIMITS.imagePromptChars)?.trim();
|
|
4332
|
-
const sourceVariantId = idArg(input.sourceVariantId, "sourceVariantId");
|
|
4333
|
-
const path = textArg(input.path, "path", 4096);
|
|
4334
|
-
const messageId2 = idArg(input.messageId, "messageId");
|
|
4335
|
-
const index = input.attachmentIndex;
|
|
4336
|
-
if (index !== void 0 && (!Number.isSafeInteger(index) || index < 0 || index > LAST_ATTACHMENT_INDEX)) fail2(`attachmentIndex must be from 0 to ${LAST_ATTACHMENT_INDEX}`);
|
|
4337
|
-
if (path && (messageId2 || index !== void 0)) fail2("Give either path or messageId (with attachmentIndex), not both");
|
|
4338
|
-
return {
|
|
4339
|
-
label,
|
|
4340
|
-
...prompt ? { prompt } : {},
|
|
4341
|
-
...sourceVariantId ? { sourceVariantId } : {},
|
|
4342
|
-
...path ? { path } : {},
|
|
4343
|
-
...messageId2 ? { messageId: messageId2 } : {},
|
|
4344
|
-
...index !== void 0 ? { attachmentIndex: index } : {}
|
|
4345
|
-
};
|
|
4346
|
-
}
|
|
4347
|
-
__name(parseArtifactAddImageAction, "parseArtifactAddImageAction");
|
|
4348
4791
|
|
|
4349
4792
|
// ../backend-contracts/src/mlsDevCredentialReconciliation.ts
|
|
4350
4793
|
var MLS_DEV_CREDENTIAL_RECONCILIATION_CHALLENGE_TTL_MS = 3e5;
|
|
@@ -4359,32 +4802,32 @@ var MlsDevCredentialReconciliationValidationError = class extends Error {
|
|
|
4359
4802
|
this.name = "MlsDevCredentialReconciliationValidationError";
|
|
4360
4803
|
}
|
|
4361
4804
|
};
|
|
4362
|
-
var
|
|
4363
|
-
var domain2 =
|
|
4364
|
-
function
|
|
4805
|
+
var utf86 = new TextEncoder();
|
|
4806
|
+
var domain2 = utf86.encode("canon.mls.credential-reconciliation-proof.v1\0");
|
|
4807
|
+
function invalid12() {
|
|
4365
4808
|
throw new MlsDevCredentialReconciliationValidationError();
|
|
4366
4809
|
}
|
|
4367
|
-
__name(
|
|
4368
|
-
function
|
|
4810
|
+
__name(invalid12, "invalid");
|
|
4811
|
+
function boundary9(read) {
|
|
4369
4812
|
try {
|
|
4370
4813
|
return read();
|
|
4371
4814
|
} catch {
|
|
4372
|
-
return
|
|
4815
|
+
return invalid12();
|
|
4373
4816
|
}
|
|
4374
4817
|
}
|
|
4375
|
-
__name(
|
|
4818
|
+
__name(boundary9, "boundary");
|
|
4376
4819
|
function bounded7(value) {
|
|
4377
|
-
if (
|
|
4820
|
+
if (utf86.encode(JSON.stringify(value)).length > MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES) invalid12();
|
|
4378
4821
|
return Object.freeze(value);
|
|
4379
4822
|
}
|
|
4380
4823
|
__name(bounded7, "bounded");
|
|
4381
|
-
function
|
|
4824
|
+
function encoded6(value, bytes) {
|
|
4382
4825
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
4383
4826
|
return value;
|
|
4384
4827
|
}
|
|
4385
|
-
__name(
|
|
4828
|
+
__name(encoded6, "encoded");
|
|
4386
4829
|
function parseMlsDevCredentialReconciliationBegin(value) {
|
|
4387
|
-
return
|
|
4830
|
+
return boundary9(() => {
|
|
4388
4831
|
const input = readMlsDataRecord(value, ["version", "deviceId", "requestId", "expectedCredentialGeneration"]);
|
|
4389
4832
|
return bounded7({
|
|
4390
4833
|
version: requireMlsLiteral(input.version, 1),
|
|
@@ -4396,7 +4839,7 @@ function parseMlsDevCredentialReconciliationBegin(value) {
|
|
|
4396
4839
|
}
|
|
4397
4840
|
__name(parseMlsDevCredentialReconciliationBegin, "parseMlsDevCredentialReconciliationBegin");
|
|
4398
4841
|
function parseMlsDevCredentialReconciliationChallenge(value) {
|
|
4399
|
-
return
|
|
4842
|
+
return boundary9(() => {
|
|
4400
4843
|
const input = readMlsDataRecord(value, [
|
|
4401
4844
|
"version",
|
|
4402
4845
|
"challengeId",
|
|
@@ -4409,12 +4852,12 @@ function parseMlsDevCredentialReconciliationChallenge(value) {
|
|
|
4409
4852
|
]);
|
|
4410
4853
|
const device = parseMlsDevEnrollmentResult({ version: 1, authorization: "registration_only", device: input.device }).device;
|
|
4411
4854
|
const issuedAtMs = requireMlsInteger(input.issuedAtMs), expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
4412
|
-
if (issuedAtMs < device.registeredAtMs || expiresAtMs - issuedAtMs !== MLS_DEV_CREDENTIAL_RECONCILIATION_CHALLENGE_TTL_MS)
|
|
4855
|
+
if (issuedAtMs < device.registeredAtMs || expiresAtMs - issuedAtMs !== MLS_DEV_CREDENTIAL_RECONCILIATION_CHALLENGE_TTL_MS) invalid12();
|
|
4413
4856
|
return bounded7({
|
|
4414
4857
|
version: requireMlsLiteral(input.version, 1),
|
|
4415
4858
|
challengeId: requireMlsUuid(input.challengeId),
|
|
4416
4859
|
requestId: requireMlsUuid(input.requestId),
|
|
4417
|
-
nonce:
|
|
4860
|
+
nonce: encoded6(input.nonce, 32),
|
|
4418
4861
|
device,
|
|
4419
4862
|
expectedCredentialGeneration: requireMlsInteger(input.expectedCredentialGeneration),
|
|
4420
4863
|
issuedAtMs,
|
|
@@ -4424,18 +4867,18 @@ function parseMlsDevCredentialReconciliationChallenge(value) {
|
|
|
4424
4867
|
}
|
|
4425
4868
|
__name(parseMlsDevCredentialReconciliationChallenge, "parseMlsDevCredentialReconciliationChallenge");
|
|
4426
4869
|
function parseMlsDevCredentialReconciliationComplete(value) {
|
|
4427
|
-
return
|
|
4870
|
+
return boundary9(() => {
|
|
4428
4871
|
const input = readMlsDataRecord(value, ["version", "challengeId", "proof"]);
|
|
4429
|
-
return bounded7({ version: requireMlsLiteral(input.version, 1), challengeId: requireMlsUuid(input.challengeId), proof:
|
|
4872
|
+
return bounded7({ version: requireMlsLiteral(input.version, 1), challengeId: requireMlsUuid(input.challengeId), proof: encoded6(input.proof, 64) });
|
|
4430
4873
|
});
|
|
4431
4874
|
}
|
|
4432
4875
|
__name(parseMlsDevCredentialReconciliationComplete, "parseMlsDevCredentialReconciliationComplete");
|
|
4433
4876
|
function parseMlsDevCredentialReconciliationReceipt(value) {
|
|
4434
|
-
return
|
|
4877
|
+
return boundary9(() => {
|
|
4435
4878
|
const input = readMlsDataRecord(value, ["version", "authorization", "challenge", "observedCredentialGeneration", "observedAtMs"]);
|
|
4436
4879
|
const challenge = parseMlsDevCredentialReconciliationChallenge(input.challenge);
|
|
4437
4880
|
const observedCredentialGeneration = requireMlsInteger(input.observedCredentialGeneration), observedAtMs = requireMlsInteger(input.observedAtMs);
|
|
4438
|
-
if (observedCredentialGeneration < challenge.expectedCredentialGeneration || observedAtMs < challenge.issuedAtMs || observedAtMs >= challenge.expiresAtMs)
|
|
4881
|
+
if (observedCredentialGeneration < challenge.expectedCredentialGeneration || observedAtMs < challenge.issuedAtMs || observedAtMs >= challenge.expiresAtMs) invalid12();
|
|
4439
4882
|
return bounded7({
|
|
4440
4883
|
version: requireMlsLiteral(input.version, 1),
|
|
4441
4884
|
authorization: requireMlsLiteral(input.authorization, "credential_generation_observation_only"),
|
|
@@ -4461,51 +4904,51 @@ var MlsDevDeviceCustodyValidationError = class extends Error {
|
|
|
4461
4904
|
this.name = "MlsDevDeviceCustodyValidationError";
|
|
4462
4905
|
}
|
|
4463
4906
|
};
|
|
4464
|
-
var
|
|
4465
|
-
function
|
|
4907
|
+
var utf87 = new TextEncoder();
|
|
4908
|
+
function invalid13() {
|
|
4466
4909
|
throw new MlsDevDeviceCustodyValidationError();
|
|
4467
4910
|
}
|
|
4468
|
-
__name(
|
|
4469
|
-
function
|
|
4911
|
+
__name(invalid13, "invalid");
|
|
4912
|
+
function boundary10(read) {
|
|
4470
4913
|
try {
|
|
4471
4914
|
return read();
|
|
4472
4915
|
} catch {
|
|
4473
|
-
return
|
|
4916
|
+
return invalid13();
|
|
4474
4917
|
}
|
|
4475
4918
|
}
|
|
4476
|
-
__name(
|
|
4477
|
-
function bounded8(value,
|
|
4478
|
-
if (
|
|
4919
|
+
__name(boundary10, "boundary");
|
|
4920
|
+
function bounded8(value, limit2) {
|
|
4921
|
+
if (utf87.encode(JSON.stringify(value)).length > limit2) invalid13();
|
|
4479
4922
|
return Object.freeze(value);
|
|
4480
4923
|
}
|
|
4481
4924
|
__name(bounded8, "bounded");
|
|
4482
4925
|
function binding2(input) {
|
|
4483
4926
|
return {
|
|
4484
4927
|
version: requireMlsLiteral(input.version, 1),
|
|
4485
|
-
environmentId:
|
|
4928
|
+
environmentId: requireMlsEnvironmentId(input.environmentId),
|
|
4486
4929
|
principal: parseMlsDevEnrollmentPrincipal(input.principal),
|
|
4487
4930
|
installationId: parseMlsDevInstallationId(input.installationId)
|
|
4488
4931
|
};
|
|
4489
4932
|
}
|
|
4490
4933
|
__name(binding2, "binding");
|
|
4491
4934
|
function parseMlsDevDeviceBinding(value) {
|
|
4492
|
-
return
|
|
4935
|
+
return boundary10(() => bounded8(binding2(readMlsDataRecord(value, ["version", "environmentId", "principal", "installationId"])), MLS_DEV_DEVICE_CONTEXT_MAX_BYTES));
|
|
4493
4936
|
}
|
|
4494
4937
|
__name(parseMlsDevDeviceBinding, "parseMlsDevDeviceBinding");
|
|
4495
4938
|
function parseMlsDevDeviceContext(value) {
|
|
4496
|
-
return
|
|
4939
|
+
return boundary10(() => {
|
|
4497
4940
|
const input = readMlsDataRecord(value, ["version", "environmentId", "principal", "installationId", "revision"]);
|
|
4498
4941
|
const revision2 = requireMlsInteger(input.revision);
|
|
4499
|
-
if (revision2 >= Number.MAX_SAFE_INTEGER)
|
|
4942
|
+
if (revision2 >= Number.MAX_SAFE_INTEGER) invalid13();
|
|
4500
4943
|
return bounded8({ ...binding2(input), revision: revision2 }, MLS_DEV_DEVICE_CONTEXT_MAX_BYTES);
|
|
4501
4944
|
});
|
|
4502
4945
|
}
|
|
4503
4946
|
__name(parseMlsDevDeviceContext, "parseMlsDevDeviceContext");
|
|
4504
4947
|
function parseMlsDevDeviceCommand(value) {
|
|
4505
|
-
return
|
|
4506
|
-
if (value === null || typeof value !== "object")
|
|
4948
|
+
return boundary10(() => {
|
|
4949
|
+
if (value === null || typeof value !== "object") invalid13();
|
|
4507
4950
|
const descriptor = Object.getOwnPropertyDescriptor(value, "op");
|
|
4508
|
-
if (!descriptor || !Object.hasOwn(descriptor, "value"))
|
|
4951
|
+
if (!descriptor || !Object.hasOwn(descriptor, "value")) invalid13();
|
|
4509
4952
|
if (descriptor.value === "create") {
|
|
4510
4953
|
const input2 = readMlsDataRecord(value, ["op"]);
|
|
4511
4954
|
return Object.freeze({ op: requireMlsLiteral(input2.op, "create") });
|
|
@@ -4521,19 +4964,19 @@ function parseMlsDevDeviceCommand(value) {
|
|
|
4521
4964
|
const input2 = readMlsDataRecord(value, ["op", "challenge", "expectedCredentialGeneration"]);
|
|
4522
4965
|
const challenge2 = parseMlsDevCredentialReconciliationChallenge(input2.challenge);
|
|
4523
4966
|
const expectedCredentialGeneration2 = requireMlsInteger(input2.expectedCredentialGeneration);
|
|
4524
|
-
if (challenge2.expectedCredentialGeneration !== expectedCredentialGeneration2)
|
|
4967
|
+
if (challenge2.expectedCredentialGeneration !== expectedCredentialGeneration2) invalid13();
|
|
4525
4968
|
return bounded8({
|
|
4526
4969
|
op: requireMlsLiteral(input2.op, "credential_reconciliation_proof"),
|
|
4527
4970
|
challenge: challenge2,
|
|
4528
4971
|
expectedCredentialGeneration: expectedCredentialGeneration2
|
|
4529
4972
|
}, MLS_DEV_DEVICE_COMMAND_MAX_BYTES);
|
|
4530
4973
|
}
|
|
4531
|
-
if (descriptor.value !== "credential_proof")
|
|
4974
|
+
if (descriptor.value !== "credential_proof") invalid13();
|
|
4532
4975
|
const input = readMlsDataRecord(value, ["op", "challenge", "registrationResult", "expectedCredentialGeneration"]);
|
|
4533
4976
|
const challenge = parseMlsDevCredentialChallenge(input.challenge);
|
|
4534
4977
|
const registrationResult = parseMlsDevEnrollmentResult(input.registrationResult);
|
|
4535
4978
|
const expectedCredentialGeneration = requireMlsInteger(input.expectedCredentialGeneration);
|
|
4536
|
-
if (expectedCredentialGeneration >= Number.MAX_SAFE_INTEGER || challenge.credential.claims.generation !== expectedCredentialGeneration + 1 || JSON.stringify(challenge.credential.claims.device) !== JSON.stringify(registrationResult.device))
|
|
4979
|
+
if (expectedCredentialGeneration >= Number.MAX_SAFE_INTEGER || challenge.credential.claims.generation !== expectedCredentialGeneration + 1 || JSON.stringify(challenge.credential.claims.device) !== JSON.stringify(registrationResult.device)) invalid13();
|
|
4537
4980
|
return bounded8({
|
|
4538
4981
|
op: requireMlsLiteral(input.op, "credential_proof"),
|
|
4539
4982
|
challenge,
|
|
@@ -4543,29 +4986,29 @@ function parseMlsDevDeviceCommand(value) {
|
|
|
4543
4986
|
});
|
|
4544
4987
|
}
|
|
4545
4988
|
__name(parseMlsDevDeviceCommand, "parseMlsDevDeviceCommand");
|
|
4546
|
-
function
|
|
4989
|
+
function encoded7(value, bytes) {
|
|
4547
4990
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
4548
4991
|
return value;
|
|
4549
4992
|
}
|
|
4550
|
-
__name(
|
|
4993
|
+
__name(encoded7, "encoded");
|
|
4551
4994
|
function wrapped(value) {
|
|
4552
|
-
if (typeof value !== "string" || value.length > MLS_DEV_DEVICE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:"))
|
|
4995
|
+
if (typeof value !== "string" || value.length > MLS_DEV_DEVICE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:")) invalid13();
|
|
4553
4996
|
const body = value.slice(3);
|
|
4554
|
-
if (body.length < 40 || body.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(body))
|
|
4997
|
+
if (body.length < 40 || body.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(body)) invalid13();
|
|
4555
4998
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4556
|
-
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0)
|
|
4557
|
-
if (!body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0)
|
|
4999
|
+
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0) invalid13();
|
|
5000
|
+
if (!body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0) invalid13();
|
|
4558
5001
|
return value;
|
|
4559
5002
|
}
|
|
4560
5003
|
__name(wrapped, "wrapped");
|
|
4561
5004
|
function parseMlsDevDevicePreparation(value, op) {
|
|
4562
|
-
return
|
|
4563
|
-
if (!["create", "enrollment_proof", "credential_proof", "credential_reconciliation_proof"].includes(op))
|
|
5005
|
+
return boundary10(() => {
|
|
5006
|
+
if (!["create", "enrollment_proof", "credential_proof", "credential_reconciliation_proof"].includes(op)) invalid13();
|
|
4564
5007
|
const input = readMlsDataRecord(value, ["wrappedState", "payload"]);
|
|
4565
5008
|
const payload = readMlsDataRecord(input.payload, op === "create" ? ["signaturePublicKey"] : ["signaturePublicKey", "proof"]);
|
|
4566
5009
|
return bounded8({ wrappedState: wrapped(input.wrappedState), payload: Object.freeze({
|
|
4567
|
-
signaturePublicKey:
|
|
4568
|
-
...op === "create" ? {} : { proof:
|
|
5010
|
+
signaturePublicKey: encoded7(payload.signaturePublicKey, 32),
|
|
5011
|
+
...op === "create" ? {} : { proof: encoded7(payload.proof, 64) }
|
|
4569
5012
|
}) }, MLS_DEV_DEVICE_COMMAND_MAX_BYTES);
|
|
4570
5013
|
});
|
|
4571
5014
|
}
|
|
@@ -4586,56 +5029,56 @@ var MlsDevKeyPackageValidationError = class extends Error {
|
|
|
4586
5029
|
this.name = "MlsDevKeyPackageValidationError";
|
|
4587
5030
|
}
|
|
4588
5031
|
};
|
|
4589
|
-
var
|
|
4590
|
-
function
|
|
5032
|
+
var utf88 = new TextEncoder();
|
|
5033
|
+
function invalid14() {
|
|
4591
5034
|
throw new MlsDevKeyPackageValidationError();
|
|
4592
5035
|
}
|
|
4593
|
-
__name(
|
|
4594
|
-
function
|
|
5036
|
+
__name(invalid14, "invalid");
|
|
5037
|
+
function boundary11(read) {
|
|
4595
5038
|
try {
|
|
4596
5039
|
return read();
|
|
4597
5040
|
} catch {
|
|
4598
|
-
return
|
|
5041
|
+
return invalid14();
|
|
4599
5042
|
}
|
|
4600
5043
|
}
|
|
4601
|
-
__name(
|
|
5044
|
+
__name(boundary11, "boundary");
|
|
4602
5045
|
function bounded9(value, maximum) {
|
|
4603
|
-
if (
|
|
5046
|
+
if (utf88.encode(JSON.stringify(value)).length > maximum) invalid14();
|
|
4604
5047
|
return Object.freeze(value);
|
|
4605
5048
|
}
|
|
4606
5049
|
__name(bounded9, "bounded");
|
|
4607
|
-
function
|
|
5050
|
+
function encoded8(value, bytes) {
|
|
4608
5051
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
4609
5052
|
return value;
|
|
4610
5053
|
}
|
|
4611
|
-
__name(
|
|
4612
|
-
function
|
|
4613
|
-
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_KEY_PACKAGE_WIRE_MAX_BYTES * 4 / 3))
|
|
5054
|
+
__name(encoded8, "encoded");
|
|
5055
|
+
function wire3(value) {
|
|
5056
|
+
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_KEY_PACKAGE_WIRE_MAX_BYTES * 4 / 3)) invalid14();
|
|
4614
5057
|
const bytes = Math.floor(value.length * 3 / 4);
|
|
4615
|
-
if (bytes < 1 || bytes > MLS_DEV_KEY_PACKAGE_WIRE_MAX_BYTES || value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
5058
|
+
if (bytes < 1 || bytes > MLS_DEV_KEY_PACKAGE_WIRE_MAX_BYTES || value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value)) invalid14();
|
|
4616
5059
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
4617
|
-
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
5060
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid14();
|
|
4618
5061
|
return value;
|
|
4619
5062
|
}
|
|
4620
|
-
__name(
|
|
5063
|
+
__name(wire3, "wire");
|
|
4621
5064
|
function wrapped2(value) {
|
|
4622
|
-
if (typeof value !== "string" || value.length > MLS_DEV_KEY_PACKAGE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:"))
|
|
5065
|
+
if (typeof value !== "string" || value.length > MLS_DEV_KEY_PACKAGE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:")) invalid14();
|
|
4623
5066
|
const body = value.slice(3);
|
|
4624
|
-
if (body.length < 40 || body.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(body))
|
|
5067
|
+
if (body.length < 40 || body.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(body)) invalid14();
|
|
4625
5068
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
4626
|
-
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0)
|
|
4627
|
-
if (!body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0)
|
|
5069
|
+
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0) invalid14();
|
|
5070
|
+
if (!body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0) invalid14();
|
|
4628
5071
|
const bytes = body.length / 4 * 3 - (body.endsWith("==") ? 2 : body.endsWith("=") ? 1 : 0);
|
|
4629
|
-
if (bytes > 128 * 1024 + 28)
|
|
5072
|
+
if (bytes > 128 * 1024 + 28) invalid14();
|
|
4630
5073
|
return value;
|
|
4631
5074
|
}
|
|
4632
5075
|
__name(wrapped2, "wrapped");
|
|
4633
5076
|
function parseMlsDevKeyPackageId(value) {
|
|
4634
|
-
return
|
|
5077
|
+
return boundary11(() => requireMlsUuid(value));
|
|
4635
5078
|
}
|
|
4636
5079
|
__name(parseMlsDevKeyPackageId, "parseMlsDevKeyPackageId");
|
|
4637
5080
|
function parseMlsDevKeyPackageCommand(value) {
|
|
4638
|
-
return
|
|
5081
|
+
return boundary11(() => {
|
|
4639
5082
|
const input = readMlsDataRecord(value, ["version", "op", "keyPackageId", "credential"]);
|
|
4640
5083
|
const token2 = parseMlsDevCredentialToken(input.credential);
|
|
4641
5084
|
parseMlsDevEnrollmentPrincipal(token2.unsigned.claims.device.principal);
|
|
@@ -4649,7 +5092,7 @@ function parseMlsDevKeyPackageCommand(value) {
|
|
|
4649
5092
|
}
|
|
4650
5093
|
__name(parseMlsDevKeyPackageCommand, "parseMlsDevKeyPackageCommand");
|
|
4651
5094
|
function parseMlsDevKeyPackagePayload(value) {
|
|
4652
|
-
return
|
|
5095
|
+
return boundary11(() => {
|
|
4653
5096
|
const input = readMlsDataRecord(value, [
|
|
4654
5097
|
"version",
|
|
4655
5098
|
"keyPackageId",
|
|
@@ -4664,7 +5107,7 @@ function parseMlsDevKeyPackagePayload(value) {
|
|
|
4664
5107
|
"expiresAtMs"
|
|
4665
5108
|
]);
|
|
4666
5109
|
const notBeforeMs = requireMlsInteger(input.notBeforeMs), expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
4667
|
-
if (notBeforeMs % 1e3 !== 0 || expiresAtMs % 1e3 !== 0 || expiresAtMs <= notBeforeMs || expiresAtMs - notBeforeMs > 864e5)
|
|
5110
|
+
if (notBeforeMs % 1e3 !== 0 || expiresAtMs % 1e3 !== 0 || expiresAtMs <= notBeforeMs || expiresAtMs - notBeforeMs > 864e5) invalid14();
|
|
4668
5111
|
return bounded9({
|
|
4669
5112
|
version: requireMlsLiteral(input.version, 1),
|
|
4670
5113
|
keyPackageId: requireMlsUuid(input.keyPackageId),
|
|
@@ -4672,9 +5115,9 @@ function parseMlsDevKeyPackagePayload(value) {
|
|
|
4672
5115
|
credentialId: requireMlsUuid(input.credentialId),
|
|
4673
5116
|
credentialGeneration: requireMlsInteger(input.credentialGeneration, 1),
|
|
4674
5117
|
ciphersuite: requireMlsLiteral(input.ciphersuite, 1),
|
|
4675
|
-
signaturePublicKey:
|
|
4676
|
-
keyPackage:
|
|
4677
|
-
keyPackageRef:
|
|
5118
|
+
signaturePublicKey: encoded8(input.signaturePublicKey, 32),
|
|
5119
|
+
keyPackage: wire3(input.keyPackage),
|
|
5120
|
+
keyPackageRef: encoded8(input.keyPackageRef, 32),
|
|
4678
5121
|
notBeforeMs,
|
|
4679
5122
|
expiresAtMs
|
|
4680
5123
|
}, MLS_DEV_KEY_PACKAGE_PREPARATION_MAX_BYTES);
|
|
@@ -4682,7 +5125,7 @@ function parseMlsDevKeyPackagePayload(value) {
|
|
|
4682
5125
|
}
|
|
4683
5126
|
__name(parseMlsDevKeyPackagePayload, "parseMlsDevKeyPackagePayload");
|
|
4684
5127
|
function parseMlsDevKeyPackagePreparation(value) {
|
|
4685
|
-
return
|
|
5128
|
+
return boundary11(() => {
|
|
4686
5129
|
const input = readMlsDataRecord(value, ["wrappedState", "payload"]);
|
|
4687
5130
|
return bounded9(
|
|
4688
5131
|
{ wrappedState: wrapped2(input.wrappedState), payload: parseMlsDevKeyPackagePayload(input.payload) },
|
|
@@ -4692,6 +5135,71 @@ function parseMlsDevKeyPackagePreparation(value) {
|
|
|
4692
5135
|
}
|
|
4693
5136
|
__name(parseMlsDevKeyPackagePreparation, "parseMlsDevKeyPackagePreparation");
|
|
4694
5137
|
|
|
5138
|
+
// ../backend-contracts/src/mlsPrincipalAlias.ts
|
|
5139
|
+
var MLS_PRINCIPAL_ALIAS_PREFIX = "mlsa_";
|
|
5140
|
+
var ALIAS_PATTERN = /^mlsa_[A-Za-z0-9_-]{43}$/;
|
|
5141
|
+
var utf89 = new TextEncoder();
|
|
5142
|
+
var domain3 = utf89.encode("canon.mls.principal-alias.v1\0");
|
|
5143
|
+
function invalid15() {
|
|
5144
|
+
throw new Error("INVALID_MLS_PRINCIPAL_ALIAS");
|
|
5145
|
+
}
|
|
5146
|
+
__name(invalid15, "invalid");
|
|
5147
|
+
function isMlsPrincipalAlias(value) {
|
|
5148
|
+
return typeof value === "string" && ALIAS_PATTERN.test(value);
|
|
5149
|
+
}
|
|
5150
|
+
__name(isMlsPrincipalAlias, "isMlsPrincipalAlias");
|
|
5151
|
+
function accountId(value) {
|
|
5152
|
+
if (typeof value !== "string" || value.length < 1 || /[\u0000-\u001f\u007f-\u009f/]/.test(value) || value === "." || value === ".." || utf89.encode(value).length > 512 || /[\ud800-\udfff]/.test(value.replace(/[\ud800-\udbff][\udc00-\udfff]/g, ""))) invalid15();
|
|
5153
|
+
if (ALIAS_PATTERN.test(value)) invalid15();
|
|
5154
|
+
return value;
|
|
5155
|
+
}
|
|
5156
|
+
__name(accountId, "accountId");
|
|
5157
|
+
function parseMlsPrincipalOpening(value) {
|
|
5158
|
+
try {
|
|
5159
|
+
const input = readMlsDataRecord(value, ["type", "accountId", "salt"]);
|
|
5160
|
+
if (input.type !== "human" && input.type !== "ai_agent") invalid15();
|
|
5161
|
+
decodeCanonicalMlsBase64Url(input.salt, 32);
|
|
5162
|
+
return Object.freeze({ type: input.type, accountId: accountId(input.accountId), salt: input.salt });
|
|
5163
|
+
} catch {
|
|
5164
|
+
return invalid15();
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
__name(parseMlsPrincipalOpening, "parseMlsPrincipalOpening");
|
|
5168
|
+
function encodeMlsPrincipalAliasInput(environmentId, opening) {
|
|
5169
|
+
const value = parseMlsPrincipalOpening(opening);
|
|
5170
|
+
const environment = utf89.encode(requireMlsEnvironmentId(environmentId));
|
|
5171
|
+
const id7 = utf89.encode(value.accountId);
|
|
5172
|
+
const output = new Uint8Array(domain3.length + 4 + environment.length + 1 + 4 + id7.length + 32);
|
|
5173
|
+
const view = new DataView(output.buffer);
|
|
5174
|
+
let offset = 0;
|
|
5175
|
+
const raw = /* @__PURE__ */ __name((bytes) => {
|
|
5176
|
+
output.set(bytes, offset);
|
|
5177
|
+
offset += bytes.length;
|
|
5178
|
+
}, "raw");
|
|
5179
|
+
const lp = /* @__PURE__ */ __name((bytes) => {
|
|
5180
|
+
view.setUint32(offset, bytes.length);
|
|
5181
|
+
offset += 4;
|
|
5182
|
+
raw(bytes);
|
|
5183
|
+
}, "lp");
|
|
5184
|
+
raw(domain3);
|
|
5185
|
+
lp(environment);
|
|
5186
|
+
output[offset++] = value.type === "human" ? 0 : 1;
|
|
5187
|
+
lp(id7);
|
|
5188
|
+
raw(decodeCanonicalMlsBase64Url(value.salt, 32));
|
|
5189
|
+
return output;
|
|
5190
|
+
}
|
|
5191
|
+
__name(encodeMlsPrincipalAliasInput, "encodeMlsPrincipalAliasInput");
|
|
5192
|
+
function parseMlsRoomIdentities(value) {
|
|
5193
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) invalid15();
|
|
5194
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
5195
|
+
for (const [alias, opening] of Object.entries(readMlsDataRecord(value, Object.keys(value)))) {
|
|
5196
|
+
if (!isMlsPrincipalAlias(alias)) invalid15();
|
|
5197
|
+
output[alias] = parseMlsPrincipalOpening(opening);
|
|
5198
|
+
}
|
|
5199
|
+
return Object.freeze(output);
|
|
5200
|
+
}
|
|
5201
|
+
__name(parseMlsRoomIdentities, "parseMlsRoomIdentities");
|
|
5202
|
+
|
|
4695
5203
|
// ../endpoint/src/runtime-api-origins.generated.ts
|
|
4696
5204
|
var RUNTIME_API_ORIGINS = {
|
|
4697
5205
|
"canon-dev-v1": "https://dev.api.canonmail.com",
|
|
@@ -4699,76 +5207,86 @@ var RUNTIME_API_ORIGINS = {
|
|
|
4699
5207
|
};
|
|
4700
5208
|
|
|
4701
5209
|
// ../endpoint/src/mls-crypto.ts
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
}
|
|
4709
|
-
|
|
5210
|
+
function mlsDevDeviceHttpConfiguration(environmentId) {
|
|
5211
|
+
if (!isMlsEnvironmentId(environmentId)) invalid16();
|
|
5212
|
+
const origin = RUNTIME_API_ORIGINS[environmentId];
|
|
5213
|
+
return Object.freeze({
|
|
5214
|
+
enrollmentUrl: `${origin}/mls/enrollment`,
|
|
5215
|
+
credentialUrl: `${origin}/mls/credential`,
|
|
5216
|
+
reconciliationUrl: `${origin}/mls/reconciliation`,
|
|
5217
|
+
keyPackagesUrl: `${origin}/mls/key-packages`,
|
|
5218
|
+
trustedOrigins: Object.freeze([origin])
|
|
5219
|
+
});
|
|
5220
|
+
}
|
|
5221
|
+
__name(mlsDevDeviceHttpConfiguration, "mlsDevDeviceHttpConfiguration");
|
|
5222
|
+
function invalid16() {
|
|
4710
5223
|
throw new Error("MLS_DEV_CLIENT_CONFIGURATION_INVALID");
|
|
4711
5224
|
}
|
|
4712
|
-
__name(
|
|
4713
|
-
function
|
|
4714
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
4715
|
-
const
|
|
4716
|
-
if (Reflect.ownKeys(
|
|
5225
|
+
__name(invalid16, "invalid");
|
|
5226
|
+
function record4(value, keys2) {
|
|
5227
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) invalid16();
|
|
5228
|
+
const fields2 = Object.getOwnPropertyDescriptors(value);
|
|
5229
|
+
if (Reflect.ownKeys(fields2).length !== keys2.length || keys2.some((key) => !Object.hasOwn(fields2, key) || !Object.hasOwn(fields2[key], "value"))) invalid16();
|
|
4717
5230
|
return value;
|
|
4718
5231
|
}
|
|
4719
|
-
__name(
|
|
5232
|
+
__name(record4, "record");
|
|
4720
5233
|
function primeOrderPoint(value) {
|
|
4721
5234
|
const point = ed25519.Point.fromBytes(value, false);
|
|
4722
5235
|
return !point.isSmallOrder() && point.isTorsionFree();
|
|
4723
5236
|
}
|
|
4724
5237
|
__name(primeOrderPoint, "primeOrderPoint");
|
|
4725
|
-
function issuerKeys(value) {
|
|
4726
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > 8)
|
|
5238
|
+
function issuerKeys(value, environmentId, receipt = false) {
|
|
5239
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 8) invalid16();
|
|
4727
5240
|
const seen = /* @__PURE__ */ new Set();
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
const
|
|
4731
|
-
|
|
5241
|
+
let environment = environmentId;
|
|
5242
|
+
return Object.freeze(Array.from(value, (item2) => {
|
|
5243
|
+
const key = record4(item2, ["keyVersion", "signaturePublicKey"]);
|
|
5244
|
+
const keyVersion = receipt ? parseMlsDevReceiptKeyVersion(key.keyVersion, environment) : parseMlsDevIssuerKeyVersion(key.keyVersion, environment);
|
|
5245
|
+
environment ??= receipt ? mlsEnvironmentOfReceiptKeyVersion(keyVersion) : mlsEnvironmentOfIssuerKeyVersion(keyVersion);
|
|
5246
|
+
if (seen.has(keyVersion) || typeof key.signaturePublicKey !== "string" || !primeOrderPoint(decodeCanonicalMlsBase64Url(key.signaturePublicKey, 32))) invalid16();
|
|
4732
5247
|
seen.add(keyVersion);
|
|
4733
|
-
return Object.freeze({ keyVersion, signaturePublicKey:
|
|
5248
|
+
return Object.freeze({ keyVersion, signaturePublicKey: key.signaturePublicKey });
|
|
4734
5249
|
}));
|
|
4735
5250
|
}
|
|
4736
5251
|
__name(issuerKeys, "issuerKeys");
|
|
4737
5252
|
function parseMlsDevClientConfiguration(value) {
|
|
4738
5253
|
try {
|
|
4739
|
-
const
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
5254
|
+
const listed = !!value && typeof value === "object" && Object.hasOwn(value, "principalIds");
|
|
5255
|
+
const input = record4(value, ["version", "environmentId", "enabled", ...listed ? ["principalIds"] : [], "issuerKeys", "receiptKeys"]);
|
|
5256
|
+
if (input.version !== 1 || !isMlsEnvironmentId(input.environmentId) || input.enabled !== true || listed && (!Array.isArray(input.principalIds) || input.principalIds.length < 1 || input.principalIds.length > 100)) invalid16();
|
|
5257
|
+
const environmentId = input.environmentId;
|
|
5258
|
+
const principalIds = listed ? Array.from(input.principalIds, (id7) => parseMlsDevEnrollmentPrincipal({ type: "human", id: id7 }).id) : void 0;
|
|
5259
|
+
if (principalIds && new Set(principalIds).size !== principalIds.length) invalid16();
|
|
4743
5260
|
return Object.freeze({
|
|
4744
5261
|
version: 1,
|
|
4745
|
-
environmentId
|
|
5262
|
+
environmentId,
|
|
4746
5263
|
enabled: true,
|
|
4747
|
-
principalIds: Object.freeze(principalIds),
|
|
4748
|
-
issuerKeys: issuerKeys(input.issuerKeys)
|
|
5264
|
+
...principalIds ? { principalIds: Object.freeze(principalIds) } : {},
|
|
5265
|
+
issuerKeys: issuerKeys(input.issuerKeys, environmentId),
|
|
5266
|
+
receiptKeys: issuerKeys(input.receiptKeys, environmentId, true)
|
|
4749
5267
|
});
|
|
4750
5268
|
} catch {
|
|
4751
|
-
return
|
|
5269
|
+
return invalid16();
|
|
4752
5270
|
}
|
|
4753
5271
|
}
|
|
4754
5272
|
__name(parseMlsDevClientConfiguration, "parseMlsDevClientConfiguration");
|
|
4755
5273
|
function createMlsDevCredentialVerifier(options) {
|
|
4756
5274
|
let trusted;
|
|
4757
5275
|
try {
|
|
4758
|
-
trusted = new Map(issuerKeys(options.issuerKeys).map((
|
|
5276
|
+
trusted = new Map(issuerKeys(options.issuerKeys).map((key) => [key.keyVersion, key.signaturePublicKey]));
|
|
4759
5277
|
} catch {
|
|
4760
|
-
return
|
|
5278
|
+
return invalid16();
|
|
4761
5279
|
}
|
|
4762
5280
|
const clock = options.clock ?? Date.now;
|
|
4763
5281
|
return async (value) => {
|
|
4764
5282
|
try {
|
|
4765
5283
|
const receipt = parseMlsDevCredentialReceipt(value);
|
|
4766
5284
|
const parsed = parseMlsDevCredentialToken(receipt.credential);
|
|
4767
|
-
const
|
|
5285
|
+
const key = trusted.get(parsed.unsigned.keyVersion);
|
|
4768
5286
|
const now = clock();
|
|
4769
5287
|
const { issuedAtMs, expiresAtMs } = parsed.unsigned.claims;
|
|
4770
|
-
if (!
|
|
4771
|
-
const publicKey = decodeCanonicalMlsBase64Url(
|
|
5288
|
+
if (!key || !Number.isSafeInteger(now) || now < issuedAtMs || now >= expiresAtMs) return false;
|
|
5289
|
+
const publicKey = decodeCanonicalMlsBase64Url(key, 32);
|
|
4772
5290
|
const signature = decodeCanonicalMlsBase64Url(parsed.signature, 64);
|
|
4773
5291
|
if (!primeOrderPoint(publicKey) || !primeOrderPoint(signature.subarray(0, 32))) return false;
|
|
4774
5292
|
return ed25519.verify(signature, new TextEncoder().encode(parsed.signingInput), publicKey, { zip215: false });
|
|
@@ -4782,7 +5300,16 @@ __name(createMlsDevCredentialVerifier, "createMlsDevCredentialVerifier");
|
|
|
4782
5300
|
export {
|
|
4783
5301
|
__name,
|
|
4784
5302
|
getCanonEnvironmentContract,
|
|
5303
|
+
CANON_CLIENT_HEADER,
|
|
5304
|
+
CLIENT_UPDATE_REQUIRED,
|
|
5305
|
+
isCanonClientVersion,
|
|
5306
|
+
formatCanonClientHeader,
|
|
4785
5307
|
sanitizeDownloadFileName,
|
|
5308
|
+
describeAttachment,
|
|
5309
|
+
isCanonMediaOrigin,
|
|
5310
|
+
isCanonUploadUrl,
|
|
5311
|
+
resolveAllowedUploadMimeType,
|
|
5312
|
+
isMlsEnvironmentId,
|
|
4786
5313
|
MLS_DEV_ENROLLMENT_MAX_BYTES,
|
|
4787
5314
|
parseMlsDevEnrollmentPrincipal,
|
|
4788
5315
|
parseMlsDevEnrollmentBegin,
|
|
@@ -4799,6 +5326,8 @@ export {
|
|
|
4799
5326
|
createMlsDevCredentialSigningInput,
|
|
4800
5327
|
parseMlsDevCredentialToken,
|
|
4801
5328
|
parseMlsDevCredentialReceipt,
|
|
5329
|
+
MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW,
|
|
5330
|
+
isMlsDevReceiptClockSkew,
|
|
4802
5331
|
parseMlsDevGroupFeedEntry,
|
|
4803
5332
|
parseMlsDevGroupFeedQuery,
|
|
4804
5333
|
parseMlsDevGroupFeedResult,
|
|
@@ -4808,23 +5337,36 @@ export {
|
|
|
4808
5337
|
parseMlsDevKeyPackagePublicationReceipt,
|
|
4809
5338
|
parseMlsDevKeyPackageClaim,
|
|
4810
5339
|
parseMlsDevKeyPackageClaimReceipt,
|
|
5340
|
+
MLS_DEV_INTERACTION_LIMITS,
|
|
5341
|
+
parseMlsDevInteractionRequestBody,
|
|
5342
|
+
parseMlsDevInteractionResponseBody,
|
|
5343
|
+
parseMlsDevInteractionWithdrawBody,
|
|
5344
|
+
parseMlsDevApplicationPayload,
|
|
5345
|
+
assertMlsDevInteractionPayloadBinding,
|
|
5346
|
+
isMlsDevInteractionAnswerAllowed,
|
|
4811
5347
|
parseMlsDevGroupCreateConversation,
|
|
4812
5348
|
parseMlsDevGroupHead,
|
|
4813
5349
|
parseMlsDevGroupMarker,
|
|
4814
5350
|
parseMlsDevGroupRoster,
|
|
5351
|
+
parseMlsDevRoomRemoval,
|
|
4815
5352
|
parseMlsDevGroupControl,
|
|
4816
5353
|
parseMlsDevGroupAcceptedControl,
|
|
5354
|
+
parseMlsDevGroupAdmissionsResult,
|
|
4817
5355
|
parseMlsDevGroupQuery,
|
|
4818
5356
|
parseMlsDevGroupQueryResult,
|
|
4819
5357
|
parseMlsDevGroupContext,
|
|
4820
5358
|
parseMlsDevGroupCommand,
|
|
4821
5359
|
parseMlsDevGroupPreparation,
|
|
4822
|
-
|
|
5360
|
+
MLS_DEV_TEXT_PROTOCOL_V3,
|
|
5361
|
+
isMlsDevTextProtocol,
|
|
5362
|
+
mlsDevProtocolCarriesCards,
|
|
5363
|
+
MLS_DEV_REPLY_QUOTE_MAX_CHARACTERS,
|
|
5364
|
+
MLS_DEV_TEXT_MAX_BYTES,
|
|
4823
5365
|
parseMlsDevApplicationReceipt,
|
|
4824
5366
|
parseMlsDevTextPayload,
|
|
5367
|
+
parseMlsDevItemPayload,
|
|
4825
5368
|
encodeMlsDevApplicationEnvelopeHash,
|
|
4826
5369
|
RUNTIME_CARD_FIELD_TYPES,
|
|
4827
|
-
RUNTIME_CARD_LIMITS,
|
|
4828
5370
|
RUNTIME_CARD_LINE_ITEM_COLUMN_TYPES,
|
|
4829
5371
|
readMembershipChangeMetadata,
|
|
4830
5372
|
formatMembershipChangeNotice,
|
|
@@ -4850,36 +5392,20 @@ export {
|
|
|
4850
5392
|
ENDPOINT_ID_PATTERN,
|
|
4851
5393
|
ENDPOINT_ACTION_PATTERN,
|
|
4852
5394
|
isEndpointOperationReceipt,
|
|
5395
|
+
CONNECTOR_RELAY_INSTALLATION_PREFIX,
|
|
5396
|
+
CONNECTOR_RELAY_INSTALLATION_PATTERN,
|
|
5397
|
+
isConnectorRelayInstallation,
|
|
5398
|
+
isConnectorRelayCredential,
|
|
5399
|
+
CONNECTOR_RELAY_MAX_BODY_BYTES,
|
|
5400
|
+
CONNECTOR_RELAY_MAX_RESPONSE_BYTES,
|
|
5401
|
+
CONNECTOR_RELAY_ANSWER_WITHIN_MS,
|
|
5402
|
+
connectorRelayResponseBytes,
|
|
5403
|
+
ConnectorRelayRequestError,
|
|
5404
|
+
parseConnectorRelayRequest,
|
|
4853
5405
|
mergeDeliveryObservation,
|
|
4854
5406
|
projectDeliveryStatus,
|
|
4855
5407
|
classifyTransferHttpFailure,
|
|
4856
5408
|
classifyTransferReceipt,
|
|
4857
|
-
ARTIFACT_PROTOCOL_VERSION,
|
|
4858
|
-
ARTIFACT_PROTOCOL_MIN_VERSION,
|
|
4859
|
-
artifactSchemaName,
|
|
4860
|
-
ARTIFACT_COMMAND_MAX_BYTES,
|
|
4861
|
-
ARTIFACT_STATE_MAX_BYTES,
|
|
4862
|
-
ARTIFACT_KINDS,
|
|
4863
|
-
ARTIFACT_COMMAND_TYPES,
|
|
4864
|
-
ARTIFACT_LIMITS,
|
|
4865
|
-
isArtifactId,
|
|
4866
|
-
isArtifactKey,
|
|
4867
|
-
ArtifactValidationError,
|
|
4868
|
-
quoteArtifactText,
|
|
4869
|
-
artifactSingleLine,
|
|
4870
|
-
roundFormNumber,
|
|
4871
|
-
validateFormValue,
|
|
4872
|
-
formValuesEqual,
|
|
4873
|
-
formatFormValue,
|
|
4874
|
-
normalizeFormField,
|
|
4875
|
-
normalizeFormCells,
|
|
4876
|
-
normalizeFormInput,
|
|
4877
|
-
artifactToolInputSchemas,
|
|
4878
|
-
parseArtifactCreateAction,
|
|
4879
|
-
parseArtifactUpdateAction,
|
|
4880
|
-
parseArtifactImageRegion,
|
|
4881
|
-
parseArtifactGenerateImageAction,
|
|
4882
|
-
parseArtifactAddImageAction,
|
|
4883
5409
|
MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES,
|
|
4884
5410
|
parseMlsDevCredentialReconciliationBegin,
|
|
4885
5411
|
parseMlsDevCredentialReconciliationChallenge,
|
|
@@ -4893,7 +5419,12 @@ export {
|
|
|
4893
5419
|
parseMlsDevKeyPackageCommand,
|
|
4894
5420
|
parseMlsDevKeyPackagePayload,
|
|
4895
5421
|
parseMlsDevKeyPackagePreparation,
|
|
4896
|
-
|
|
5422
|
+
MLS_PRINCIPAL_ALIAS_PREFIX,
|
|
5423
|
+
isMlsPrincipalAlias,
|
|
5424
|
+
parseMlsPrincipalOpening,
|
|
5425
|
+
encodeMlsPrincipalAliasInput,
|
|
5426
|
+
parseMlsRoomIdentities,
|
|
5427
|
+
mlsDevDeviceHttpConfiguration,
|
|
4897
5428
|
parseMlsDevClientConfiguration,
|
|
4898
5429
|
createMlsDevCredentialVerifier
|
|
4899
5430
|
};
|