@canonmsg/agent-sdk 12.0.0 → 12.2.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-HITMISWV.js +4897 -0
- package/dist/chunks/{chunk-FPQFTTND.js → chunk-J63YVAKA.js} +1627 -1099
- package/dist/chunks/{chunk-OLJ7H4E2.js → chunk-O5C3ZGI4.js} +6363 -7011
- package/dist/chunks/{mls-crypto-NZDNSPAO.js → mls-crypto-ITVGUEP7.js} +3 -3
- package/dist/cli.js +403 -30
- 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 +52 -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 +6 -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 +26 -0
- package/dist/internal/backend-contracts/runtimeCardFields.d.ts +0 -4
- package/dist/internal/backend-contracts/upload-mime-types.d.ts +17 -0
- package/dist/internal/backend-contracts/verbContract.d.ts +23 -19
- package/dist/internal/backend-contracts/verbSchemas.d.ts +38 -29
- 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 +3 -152
- 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 +22 -63
- 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/turn-protocol.d.ts +0 -5
- package/dist/internal/core/turn-verbosity.d.ts +5 -23
- package/dist/internal/core/types.d.ts +0 -292
- 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/stream.d.ts +0 -77
- 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;
|
|
@@ -118,16 +366,12 @@ function installation(value) {
|
|
|
118
366
|
return value;
|
|
119
367
|
}
|
|
120
368
|
__name(installation, "installation");
|
|
369
|
+
function isMlsPrincipalId(value) {
|
|
370
|
+
return typeof value === "string" && value.length > 0 && value.length <= 512 && !/[\u0000-\u001f\u007f-\u009f]/.test(value) && !/[\ud800-\udfff]/.test(value.replace(/[\ud800-\udbff][\udc00-\udfff]/g, "")) && utf8.encode(value).length <= 512;
|
|
371
|
+
}
|
|
372
|
+
__name(isMlsPrincipalId, "isMlsPrincipalId");
|
|
121
373
|
function principalId(value) {
|
|
122
|
-
if (
|
|
123
|
-
for (let index = 0; index < value.length; index++) {
|
|
124
|
-
const unit = value.charCodeAt(index);
|
|
125
|
-
if (unit >= 55296 && unit <= 56319) {
|
|
126
|
-
const next = value.charCodeAt(++index);
|
|
127
|
-
if (!(next >= 56320 && next <= 57343)) invalid2();
|
|
128
|
-
} else if (unit >= 56320 && unit <= 57343) invalid2();
|
|
129
|
-
}
|
|
130
|
-
if (utf8.encode(value).length > 512) invalid2();
|
|
374
|
+
if (!isMlsPrincipalId(value)) invalid2();
|
|
131
375
|
return value;
|
|
132
376
|
}
|
|
133
377
|
__name(principalId, "principalId");
|
|
@@ -209,7 +453,7 @@ function parseMlsDevEnrollmentChallenge(value) {
|
|
|
209
453
|
version: requireMlsLiteral(input.version, 1),
|
|
210
454
|
challengeId: requireMlsUuid(input.challengeId),
|
|
211
455
|
deviceId: requireMlsUuid(input.deviceId),
|
|
212
|
-
environmentId:
|
|
456
|
+
environmentId: requireMlsEnvironmentId(input.environmentId),
|
|
213
457
|
principal: principal(input.principal),
|
|
214
458
|
installationId: installation(input.installationId),
|
|
215
459
|
ciphersuite: requireMlsLiteral(input.ciphersuite, 1),
|
|
@@ -242,7 +486,7 @@ function parseMlsDevEnrollmentResult(value) {
|
|
|
242
486
|
device: Object.freeze({
|
|
243
487
|
version: requireMlsLiteral(device.version, 1),
|
|
244
488
|
deviceId: requireMlsUuid(device.deviceId),
|
|
245
|
-
environmentId:
|
|
489
|
+
environmentId: requireMlsEnvironmentId(device.environmentId),
|
|
246
490
|
principal: principal(device.principal),
|
|
247
491
|
installationId: installation(device.installationId),
|
|
248
492
|
ciphersuite: requireMlsLiteral(device.ciphersuite, 1),
|
|
@@ -333,11 +577,11 @@ function encoded2(value, bytes) {
|
|
|
333
577
|
return value;
|
|
334
578
|
}
|
|
335
579
|
__name(encoded2, "encoded");
|
|
336
|
-
function parseMlsDevIssuerKeyVersion(value) {
|
|
580
|
+
function parseMlsDevIssuerKeyVersion(value, environmentId) {
|
|
337
581
|
return boundary2(() => {
|
|
338
582
|
if (typeof value !== "string" || value.length > 200) invalid3();
|
|
339
|
-
const
|
|
340
|
-
if (!
|
|
583
|
+
const owner = mlsEnvironmentOfIssuerKeyVersion(value);
|
|
584
|
+
if (!owner || environmentId !== void 0 && owner !== environmentId) invalid3();
|
|
341
585
|
return value;
|
|
342
586
|
});
|
|
343
587
|
}
|
|
@@ -348,11 +592,12 @@ function parseMlsDevCredentialClaims(value) {
|
|
|
348
592
|
const issuedAtMs = requireMlsInteger(input.issuedAtMs);
|
|
349
593
|
const expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
350
594
|
if (expiresAtMs - issuedAtMs !== MLS_DEV_CREDENTIAL_TTL_MS) invalid3();
|
|
595
|
+
const device = parseMlsDevEnrollmentResult({ version: 1, authorization: "registration_only", device: input.device }).device;
|
|
351
596
|
return bounded2({
|
|
352
597
|
version: requireMlsLiteral(input.version, 1),
|
|
353
598
|
credentialId: requireMlsUuid(input.credentialId),
|
|
354
|
-
issuer: requireMlsLiteral(input.issuer,
|
|
355
|
-
device
|
|
599
|
+
issuer: requireMlsLiteral(input.issuer, mlsEnvironmentProfile(device.environmentId).issuer),
|
|
600
|
+
device,
|
|
356
601
|
generation: requireMlsInteger(input.generation, 1),
|
|
357
602
|
issuedAtMs,
|
|
358
603
|
expiresAtMs
|
|
@@ -363,7 +608,9 @@ __name(parseMlsDevCredentialClaims, "parseMlsDevCredentialClaims");
|
|
|
363
608
|
function parseMlsDevCredentialUnsigned(value) {
|
|
364
609
|
return boundary2(() => {
|
|
365
610
|
const input = readMlsDataRecord(value, ["keyVersion", "claims"]);
|
|
366
|
-
|
|
611
|
+
const keyVersion = parseMlsDevIssuerKeyVersion(input.keyVersion), claims = parseMlsDevCredentialClaims(input.claims);
|
|
612
|
+
if (mlsEnvironmentOfIssuerKeyVersion(keyVersion) !== claims.device.environmentId) invalid3();
|
|
613
|
+
return bounded2({ keyVersion, claims });
|
|
367
614
|
});
|
|
368
615
|
}
|
|
369
616
|
__name(parseMlsDevCredentialUnsigned, "parseMlsDevCredentialUnsigned");
|
|
@@ -423,7 +670,7 @@ function parseMlsDevCredentialRevocationReceipt(value) {
|
|
|
423
670
|
}
|
|
424
671
|
__name(parseMlsDevCredentialRevocationReceipt, "parseMlsDevCredentialRevocationReceipt");
|
|
425
672
|
function signingInput(unsigned2) {
|
|
426
|
-
const header = { alg: "Ed25519", typ:
|
|
673
|
+
const header = { alg: "Ed25519", typ: mlsEnvironmentProfile(unsigned2.claims.device.environmentId).credentialType, kid: unsigned2.keyVersion };
|
|
427
674
|
const result = encodeCanonicalMlsBase64Url(utf82.encode(JSON.stringify(header))) + "." + encodeCanonicalMlsBase64Url(utf82.encode(JSON.stringify(unsigned2.claims)));
|
|
428
675
|
if (result.length > MLS_DEV_CREDENTIAL_MAX_BYTES) invalid3();
|
|
429
676
|
return result;
|
|
@@ -445,8 +692,8 @@ function parseMlsDevCredentialSigningInput(value) {
|
|
|
445
692
|
if (parts.length !== 2) invalid3();
|
|
446
693
|
const header = readMlsDataRecord(jsonSegment(parts[0]), ["alg", "typ", "kid"]);
|
|
447
694
|
requireMlsLiteral(header.alg, "Ed25519");
|
|
448
|
-
requireMlsLiteral(header.typ, "canon-mls-dev-credential+jws");
|
|
449
695
|
const unsigned2 = parseMlsDevCredentialUnsigned({ keyVersion: header.kid, claims: jsonSegment(parts[1]) });
|
|
696
|
+
requireMlsLiteral(header.typ, mlsEnvironmentProfile(unsigned2.claims.device.environmentId).credentialType);
|
|
450
697
|
if (signingInput(unsigned2) !== value) invalid3();
|
|
451
698
|
return unsigned2;
|
|
452
699
|
});
|
|
@@ -476,6 +723,54 @@ function parseMlsDevCredentialReceipt(value) {
|
|
|
476
723
|
}
|
|
477
724
|
__name(parseMlsDevCredentialReceipt, "parseMlsDevCredentialReceipt");
|
|
478
725
|
|
|
726
|
+
// ../backend-contracts/src/mlsDevFeedReceipt.ts
|
|
727
|
+
var MlsDevFeedReceiptValidationError = class extends Error {
|
|
728
|
+
static {
|
|
729
|
+
__name(this, "MlsDevFeedReceiptValidationError");
|
|
730
|
+
}
|
|
731
|
+
code = "INVALID_MLS_DEV_FEED_RECEIPT";
|
|
732
|
+
constructor() {
|
|
733
|
+
super("INVALID_MLS_DEV_FEED_RECEIPT");
|
|
734
|
+
this.name = "MlsDevFeedReceiptValidationError";
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
function invalid4() {
|
|
738
|
+
throw new MlsDevFeedReceiptValidationError();
|
|
739
|
+
}
|
|
740
|
+
__name(invalid4, "invalid");
|
|
741
|
+
function boundary3(read) {
|
|
742
|
+
try {
|
|
743
|
+
return read();
|
|
744
|
+
} catch {
|
|
745
|
+
return invalid4();
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
__name(boundary3, "boundary");
|
|
749
|
+
function parseMlsDevReceiptKeyVersion(value, environmentId) {
|
|
750
|
+
return boundary3(() => {
|
|
751
|
+
if (typeof value !== "string" || value.length > 200) invalid4();
|
|
752
|
+
const owner = mlsEnvironmentOfReceiptKeyVersion(value);
|
|
753
|
+
if (!owner || environmentId !== void 0 && owner !== environmentId) invalid4();
|
|
754
|
+
return value;
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
__name(parseMlsDevReceiptKeyVersion, "parseMlsDevReceiptKeyVersion");
|
|
758
|
+
function parseMlsDevFeedReceiptProof(value) {
|
|
759
|
+
return boundary3(() => {
|
|
760
|
+
const input = readMlsDataRecord(value, ["keyVersion", "signature"]);
|
|
761
|
+
decodeCanonicalMlsBase64Url(input.signature, 64);
|
|
762
|
+
return Object.freeze({ keyVersion: parseMlsDevReceiptKeyVersion(input.keyVersion), signature: input.signature });
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
__name(parseMlsDevFeedReceiptProof, "parseMlsDevFeedReceiptProof");
|
|
766
|
+
var MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW = "MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW";
|
|
767
|
+
function isMlsDevReceiptClockSkew(value) {
|
|
768
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
769
|
+
const keys2 = Object.keys(value).sort().join(), input = value;
|
|
770
|
+
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;
|
|
771
|
+
}
|
|
772
|
+
__name(isMlsDevReceiptClockSkew, "isMlsDevReceiptClockSkew");
|
|
773
|
+
|
|
479
774
|
// ../backend-contracts/src/mlsDevGroupFeed.ts
|
|
480
775
|
var MLS_DEV_GROUP_FEED_MAX_ENTRIES = 4;
|
|
481
776
|
var MLS_DEV_GROUP_FEED_MAX_BYTES = 4 * 1024 * 1024;
|
|
@@ -489,99 +784,104 @@ var MlsDevGroupFeedValidationError = class extends Error {
|
|
|
489
784
|
this.name = "MlsDevGroupFeedValidationError";
|
|
490
785
|
}
|
|
491
786
|
};
|
|
492
|
-
function
|
|
787
|
+
function invalid5() {
|
|
493
788
|
throw new MlsDevGroupFeedValidationError();
|
|
494
789
|
}
|
|
495
|
-
__name(
|
|
496
|
-
function
|
|
790
|
+
__name(invalid5, "invalid");
|
|
791
|
+
function boundary4(read) {
|
|
497
792
|
try {
|
|
498
793
|
return read();
|
|
499
794
|
} catch {
|
|
500
|
-
return
|
|
795
|
+
return invalid5();
|
|
501
796
|
}
|
|
502
797
|
}
|
|
503
|
-
__name(
|
|
798
|
+
__name(boundary4, "boundary");
|
|
504
799
|
function id(value) {
|
|
505
800
|
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
506
|
-
|
|
801
|
+
invalid5();
|
|
507
802
|
return value;
|
|
508
803
|
}
|
|
509
804
|
__name(id, "id");
|
|
510
805
|
function boolean(value) {
|
|
511
806
|
if (typeof value !== "boolean")
|
|
512
|
-
|
|
807
|
+
invalid5();
|
|
513
808
|
return value;
|
|
514
809
|
}
|
|
515
810
|
__name(boolean, "boolean");
|
|
516
811
|
function bounded3(value) {
|
|
517
812
|
if (new TextEncoder().encode(JSON.stringify(value)).length > MLS_DEV_GROUP_FEED_MAX_BYTES)
|
|
518
|
-
|
|
813
|
+
invalid5();
|
|
519
814
|
return Object.freeze(value);
|
|
520
815
|
}
|
|
521
816
|
__name(bounded3, "bounded");
|
|
522
817
|
function array(value) {
|
|
523
818
|
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > MLS_DEV_GROUP_FEED_MAX_ENTRIES)
|
|
524
|
-
|
|
819
|
+
invalid5();
|
|
525
820
|
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
526
821
|
if (Reflect.ownKeys(descriptors).length !== value.length + 1)
|
|
527
|
-
|
|
822
|
+
invalid5();
|
|
528
823
|
return Array.from({ length: value.length }, (_, index) => {
|
|
529
824
|
const field = descriptors[index];
|
|
530
825
|
if (!field || !field.enumerable || !Object.hasOwn(field, "value"))
|
|
531
|
-
|
|
826
|
+
invalid5();
|
|
532
827
|
return field.value;
|
|
533
828
|
});
|
|
534
829
|
}
|
|
535
830
|
__name(array, "array");
|
|
536
831
|
function parseMlsDevGroupFeedEntry(value) {
|
|
537
|
-
return
|
|
538
|
-
const
|
|
832
|
+
return boundary4(() => {
|
|
833
|
+
const signed = value !== null && typeof value === "object" && Object.hasOwn(value, "receiptProof") ? ["receiptProof"] : [];
|
|
834
|
+
const input = readMlsDataRecord(value, ["version", "ordinal", "kind", "accepted", ...signed], ["version", "ordinal", "kind", "receipt", "envelope", ...signed]);
|
|
539
835
|
const version = requireMlsLiteral(input.version, 1), ordinal = requireMlsInteger(input.ordinal, 1);
|
|
836
|
+
const proof = signed.length ? { receiptProof: parseMlsDevFeedReceiptProof(input.receiptProof) } : {};
|
|
540
837
|
if (input.kind === "control") {
|
|
541
838
|
if (!Object.hasOwn(input, "accepted"))
|
|
542
|
-
|
|
543
|
-
return bounded3({ version, ordinal, kind: "control", accepted: parseMlsDevGroupAcceptedControl(input.accepted) });
|
|
839
|
+
invalid5();
|
|
840
|
+
return bounded3({ version, ordinal, kind: "control", accepted: parseMlsDevGroupAcceptedControl(input.accepted), ...proof });
|
|
544
841
|
}
|
|
545
842
|
const kind = requireMlsLiteral(input.kind, "application"), receipt = parseMlsDevApplicationReceipt(input.receipt), envelope = parseMlsDevApplicationEnvelope(input.envelope);
|
|
546
843
|
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 });
|
|
844
|
+
invalid5();
|
|
845
|
+
return bounded3({ version, ordinal, kind, receipt, envelope, ...proof });
|
|
549
846
|
});
|
|
550
847
|
}
|
|
551
848
|
__name(parseMlsDevGroupFeedEntry, "parseMlsDevGroupFeedEntry");
|
|
552
849
|
function parseMlsDevGroupFeedQuery(value) {
|
|
553
|
-
return
|
|
850
|
+
return boundary4(() => {
|
|
554
851
|
const input = readMlsDataRecord(value, ["version", "conversationId", "credential", "afterOrdinal", "limit"]);
|
|
555
|
-
const
|
|
556
|
-
if (
|
|
557
|
-
|
|
852
|
+
const limit2 = requireMlsInteger(input.limit, 1);
|
|
853
|
+
if (limit2 > MLS_DEV_GROUP_FEED_MAX_ENTRIES)
|
|
854
|
+
invalid5();
|
|
558
855
|
parseMlsDevCredentialToken(input.credential);
|
|
559
|
-
return Object.freeze({ version: requireMlsLiteral(input.version, 1), conversationId: id(input.conversationId), credential: input.credential, afterOrdinal: requireMlsInteger(input.afterOrdinal), limit });
|
|
856
|
+
return Object.freeze({ version: requireMlsLiteral(input.version, 1), conversationId: id(input.conversationId), credential: input.credential, afterOrdinal: requireMlsInteger(input.afterOrdinal), limit: limit2 });
|
|
560
857
|
});
|
|
561
858
|
}
|
|
562
859
|
__name(parseMlsDevGroupFeedQuery, "parseMlsDevGroupFeedQuery");
|
|
563
860
|
function parseMlsDevGroupFeedResult(value) {
|
|
564
|
-
return
|
|
861
|
+
return boundary4(() => {
|
|
565
862
|
const input = readMlsDataRecord(value, ["version", "protocol", "conversationId", "marker", "membershipRevision", "admissionId", "roster", "minimumOrdinal", "latestOrdinal", "nextOrdinal", "entries", "hasMore", "terminal"]);
|
|
566
863
|
const conversationId = id(input.conversationId), marker = input.marker === null ? null : parseMlsDevGroupMarker(input.marker);
|
|
567
864
|
const minimumOrdinal = input.minimumOrdinal === null ? null : requireMlsInteger(input.minimumOrdinal, 1);
|
|
568
865
|
const latestOrdinal = requireMlsInteger(input.latestOrdinal), nextOrdinal = requireMlsInteger(input.nextOrdinal);
|
|
569
866
|
const entries = array(input.entries).map(parseMlsDevGroupFeedEntry), hasMore = boolean(input.hasMore), terminal = boolean(input.terminal);
|
|
570
867
|
if (nextOrdinal > latestOrdinal || minimumOrdinal !== null && minimumOrdinal > latestOrdinal || minimumOrdinal === null && (entries.length || nextOrdinal || hasMore || terminal) || !marker && (latestOrdinal || minimumOrdinal !== null || entries.length || terminal))
|
|
571
|
-
|
|
868
|
+
invalid5();
|
|
869
|
+
const protocol = isMlsDevTextProtocol(input.protocol) ? input.protocol : invalid5();
|
|
572
870
|
for (let i = 0; i < entries.length; i++) {
|
|
573
871
|
const entry = entries[i], subject = entry.kind === "control" ? entry.accepted.control : entry.envelope;
|
|
872
|
+
if (entry.kind === "application" && entry.envelope.interaction && !mlsDevProtocolCarriesCards(protocol))
|
|
873
|
+
invalid5();
|
|
574
874
|
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
|
-
|
|
875
|
+
invalid5();
|
|
576
876
|
const head = entry.kind === "control" ? entry.accepted.head : entry.receipt.head;
|
|
577
877
|
if (head.sequence > marker.head.sequence || head.epoch > marker.head.epoch)
|
|
578
|
-
|
|
878
|
+
invalid5();
|
|
579
879
|
}
|
|
580
880
|
if (entries.length && entries.at(-1).ordinal !== nextOrdinal || hasMore !== (minimumOrdinal !== null && nextOrdinal < latestOrdinal))
|
|
581
|
-
|
|
881
|
+
invalid5();
|
|
582
882
|
return bounded3({
|
|
583
883
|
version: requireMlsLiteral(input.version, 1),
|
|
584
|
-
protocol
|
|
884
|
+
protocol,
|
|
585
885
|
conversationId,
|
|
586
886
|
marker,
|
|
587
887
|
membershipRevision: requireMlsInteger(input.membershipRevision),
|
|
@@ -611,25 +911,25 @@ var MlsDevKeyPackagesValidationError = class extends Error {
|
|
|
611
911
|
this.name = "MlsDevKeyPackagesValidationError";
|
|
612
912
|
}
|
|
613
913
|
};
|
|
614
|
-
function
|
|
914
|
+
function invalid6() {
|
|
615
915
|
throw new MlsDevKeyPackagesValidationError();
|
|
616
916
|
}
|
|
617
|
-
__name(
|
|
618
|
-
function
|
|
917
|
+
__name(invalid6, "invalid");
|
|
918
|
+
function boundary5(read) {
|
|
619
919
|
try {
|
|
620
920
|
return read();
|
|
621
921
|
} catch {
|
|
622
|
-
return
|
|
922
|
+
return invalid6();
|
|
623
923
|
}
|
|
624
924
|
}
|
|
625
|
-
__name(
|
|
925
|
+
__name(boundary5, "boundary");
|
|
626
926
|
function bounded4(value) {
|
|
627
|
-
if (new TextEncoder().encode(JSON.stringify(value)).length > MLS_DEV_KEY_PACKAGES_MAX_BYTES)
|
|
927
|
+
if (new TextEncoder().encode(JSON.stringify(value)).length > MLS_DEV_KEY_PACKAGES_MAX_BYTES) invalid6();
|
|
628
928
|
return Object.freeze(value);
|
|
629
929
|
}
|
|
630
930
|
__name(bounded4, "bounded");
|
|
631
931
|
function id2(value) {
|
|
632
|
-
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
932
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..") invalid6();
|
|
633
933
|
return value;
|
|
634
934
|
}
|
|
635
935
|
__name(id2, "id");
|
|
@@ -639,18 +939,18 @@ function ref(value) {
|
|
|
639
939
|
}
|
|
640
940
|
__name(ref, "ref");
|
|
641
941
|
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))
|
|
942
|
+
return boundary5(() => {
|
|
943
|
+
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_KEY_PACKAGE_TLS_MAX_BYTES * 4 / 3)) invalid6();
|
|
644
944
|
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))
|
|
945
|
+
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
946
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
647
|
-
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
947
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid6();
|
|
648
948
|
return value;
|
|
649
949
|
});
|
|
650
950
|
}
|
|
651
951
|
__name(parseMlsDevKeyPackageWire, "parseMlsDevKeyPackageWire");
|
|
652
952
|
function parseMlsDevKeyPackageClaimPreconditions(value) {
|
|
653
|
-
return
|
|
953
|
+
return boundary5(() => {
|
|
654
954
|
const input = readMlsDataRecord(value, ["conversationId", "membershipRevision", "admissionId", "targetAdmissionId"]);
|
|
655
955
|
return Object.freeze({
|
|
656
956
|
conversationId: id2(input.conversationId),
|
|
@@ -662,17 +962,17 @@ function parseMlsDevKeyPackageClaimPreconditions(value) {
|
|
|
662
962
|
}
|
|
663
963
|
__name(parseMlsDevKeyPackageClaimPreconditions, "parseMlsDevKeyPackageClaimPreconditions");
|
|
664
964
|
function parseMlsDevKeyPackagePublish(value) {
|
|
665
|
-
return
|
|
965
|
+
return boundary5(() => {
|
|
666
966
|
const input = readMlsDataRecord(value, ["version", "requestId", "keyPackage"]);
|
|
667
967
|
return bounded4({ version: requireMlsLiteral(input.version, 1), requestId: requireMlsUuid(input.requestId), keyPackage: parseMlsDevKeyPackageWire(input.keyPackage) });
|
|
668
968
|
});
|
|
669
969
|
}
|
|
670
970
|
__name(parseMlsDevKeyPackagePublish, "parseMlsDevKeyPackagePublish");
|
|
671
971
|
function parseMlsDevKeyPackagePublicationReceipt(value) {
|
|
672
|
-
return
|
|
972
|
+
return boundary5(() => {
|
|
673
973
|
const input = readMlsDataRecord(value, ["version", "authorization", "requestId", "keyPackageRef", "deviceId", "publishedAtMs", "expiresAtMs"]);
|
|
674
974
|
const publishedAtMs = requireMlsInteger(input.publishedAtMs), expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
675
|
-
if (publishedAtMs >= expiresAtMs)
|
|
975
|
+
if (publishedAtMs >= expiresAtMs) invalid6();
|
|
676
976
|
return bounded4({
|
|
677
977
|
version: requireMlsLiteral(input.version, 1),
|
|
678
978
|
authorization: requireMlsLiteral(input.authorization, "key_package_publication_only"),
|
|
@@ -686,7 +986,7 @@ function parseMlsDevKeyPackagePublicationReceipt(value) {
|
|
|
686
986
|
}
|
|
687
987
|
__name(parseMlsDevKeyPackagePublicationReceipt, "parseMlsDevKeyPackagePublicationReceipt");
|
|
688
988
|
function parseMlsDevKeyPackageClaim(value) {
|
|
689
|
-
return
|
|
989
|
+
return boundary5(() => {
|
|
690
990
|
const input = readMlsDataRecord(value, ["version", "requestId", "claimantCredential", "recipientDeviceId", "preconditions"]);
|
|
691
991
|
parseMlsDevCredentialToken(input.claimantCredential);
|
|
692
992
|
return bounded4({
|
|
@@ -700,7 +1000,7 @@ function parseMlsDevKeyPackageClaim(value) {
|
|
|
700
1000
|
}
|
|
701
1001
|
__name(parseMlsDevKeyPackageClaim, "parseMlsDevKeyPackageClaim");
|
|
702
1002
|
function parseMlsDevKeyPackageClaimReceipt(value) {
|
|
703
|
-
return
|
|
1003
|
+
return boundary5(() => {
|
|
704
1004
|
const input = readMlsDataRecord(value, [
|
|
705
1005
|
"version",
|
|
706
1006
|
"authorization",
|
|
@@ -713,7 +1013,7 @@ function parseMlsDevKeyPackageClaimReceipt(value) {
|
|
|
713
1013
|
"claimedAtMs"
|
|
714
1014
|
]);
|
|
715
1015
|
const claimantDeviceId = requireMlsUuid(input.claimantDeviceId), recipientDeviceId = requireMlsUuid(input.recipientDeviceId);
|
|
716
|
-
if (claimantDeviceId === recipientDeviceId)
|
|
1016
|
+
if (claimantDeviceId === recipientDeviceId) invalid6();
|
|
717
1017
|
return bounded4({
|
|
718
1018
|
version: requireMlsLiteral(input.version, 1),
|
|
719
1019
|
authorization: requireMlsLiteral(input.authorization, "key_package_claim_only"),
|
|
@@ -729,6 +1029,228 @@ function parseMlsDevKeyPackageClaimReceipt(value) {
|
|
|
729
1029
|
}
|
|
730
1030
|
__name(parseMlsDevKeyPackageClaimReceipt, "parseMlsDevKeyPackageClaimReceipt");
|
|
731
1031
|
|
|
1032
|
+
// ../backend-contracts/src/mlsDevInteraction.ts
|
|
1033
|
+
var MLS_DEV_INTERACTION_LIMITS = Object.freeze({ title: 200, details: 8e3, options: 10, optionLabel: 120, text: 4e3 });
|
|
1034
|
+
var MLS_DEV_SECRET_ANSWER_MAX_BYTES = MLS_DEV_INTERACTION_LIMITS.text * 4 + 16;
|
|
1035
|
+
var MLS_DEV_INTERACTION_WITHDRAW_REASONS = ["stopped", "turn_ended", "expired", "restart"];
|
|
1036
|
+
var MlsDevInteractionValidationError = class extends Error {
|
|
1037
|
+
static {
|
|
1038
|
+
__name(this, "MlsDevInteractionValidationError");
|
|
1039
|
+
}
|
|
1040
|
+
code = "INVALID_MLS_DEV_INTERACTION";
|
|
1041
|
+
constructor() {
|
|
1042
|
+
super("INVALID_MLS_DEV_INTERACTION");
|
|
1043
|
+
this.name = "MlsDevInteractionValidationError";
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
var utf83 = new TextEncoder();
|
|
1047
|
+
function invalid7() {
|
|
1048
|
+
throw new MlsDevInteractionValidationError();
|
|
1049
|
+
}
|
|
1050
|
+
__name(invalid7, "invalid");
|
|
1051
|
+
function boundary6(read) {
|
|
1052
|
+
try {
|
|
1053
|
+
return read();
|
|
1054
|
+
} catch {
|
|
1055
|
+
return invalid7();
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
__name(boundary6, "boundary");
|
|
1059
|
+
function id3(value) {
|
|
1060
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..") invalid7();
|
|
1061
|
+
return value;
|
|
1062
|
+
}
|
|
1063
|
+
__name(id3, "id");
|
|
1064
|
+
function interactionId(value) {
|
|
1065
|
+
if (typeof value !== "string" || !MLS_DEV_INTERACTION_ID_PATTERN.test(value) || value === "." || value === "..") invalid7();
|
|
1066
|
+
return value;
|
|
1067
|
+
}
|
|
1068
|
+
__name(interactionId, "interactionId");
|
|
1069
|
+
function encoded3(value, bytes) {
|
|
1070
|
+
decodeCanonicalMlsBase64Url(value, bytes);
|
|
1071
|
+
return value;
|
|
1072
|
+
}
|
|
1073
|
+
__name(encoded3, "encoded");
|
|
1074
|
+
function wire(value, min, max) {
|
|
1075
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) invalid7();
|
|
1076
|
+
const bytes = Math.floor(value.length * 3 / 4);
|
|
1077
|
+
if (bytes < min || bytes > max) invalid7();
|
|
1078
|
+
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
1079
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid7();
|
|
1080
|
+
return value;
|
|
1081
|
+
}
|
|
1082
|
+
__name(wire, "wire");
|
|
1083
|
+
function text(value, max) {
|
|
1084
|
+
if (typeof value !== "string" || !value.trim() || [...value].length > max || new TextDecoder("utf-8", { fatal: true }).decode(utf83.encode(value)) !== value) invalid7();
|
|
1085
|
+
return value;
|
|
1086
|
+
}
|
|
1087
|
+
__name(text, "text");
|
|
1088
|
+
function boolean2(value) {
|
|
1089
|
+
if (typeof value !== "boolean") invalid7();
|
|
1090
|
+
return value;
|
|
1091
|
+
}
|
|
1092
|
+
__name(boolean2, "boolean");
|
|
1093
|
+
function array2(value, min, max) {
|
|
1094
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length < min || value.length > max) invalid7();
|
|
1095
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
1096
|
+
if (Reflect.ownKeys(descriptors).length !== value.length + 1) invalid7();
|
|
1097
|
+
return Array.from({ length: value.length }, (_, index) => {
|
|
1098
|
+
const field = descriptors[index];
|
|
1099
|
+
if (!field || !field.enumerable || !Object.hasOwn(field, "value")) invalid7();
|
|
1100
|
+
return field.value;
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
__name(array2, "array");
|
|
1104
|
+
function closed(value, required, optional) {
|
|
1105
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) invalid7();
|
|
1106
|
+
const present2 = Reflect.ownKeys(value);
|
|
1107
|
+
if (present2.some((key) => typeof key !== "string" || !required.includes(key) && !optional.includes(key))) invalid7();
|
|
1108
|
+
return readMlsDataRecord(value, present2);
|
|
1109
|
+
}
|
|
1110
|
+
__name(closed, "closed");
|
|
1111
|
+
function ordered(value) {
|
|
1112
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).filter(([, field]) => field !== void 0)));
|
|
1113
|
+
}
|
|
1114
|
+
__name(ordered, "ordered");
|
|
1115
|
+
function parseMlsDevInteractionRequestBody(value) {
|
|
1116
|
+
return boundary6(() => {
|
|
1117
|
+
const input = closed(
|
|
1118
|
+
value,
|
|
1119
|
+
["requestId", "kind", "title", "responderIds", "expiresAtMs"],
|
|
1120
|
+
["details", "options", "multiple", "risk", "allowSession", "scopeKey", "secretKey"]
|
|
1121
|
+
);
|
|
1122
|
+
const kind = input.kind;
|
|
1123
|
+
if (typeof kind !== "string" || !MLS_DEV_INTERACTION_KINDS.includes(kind)) invalid7();
|
|
1124
|
+
const has = /* @__PURE__ */ __name((key) => Object.hasOwn(input, key), "has");
|
|
1125
|
+
const options = has("options") ? array2(input.options, 1, MLS_DEV_INTERACTION_LIMITS.options).map((option) => {
|
|
1126
|
+
const fields2 = readMlsDataRecord(option, ["id", "label"]);
|
|
1127
|
+
if (typeof fields2.id !== "string" || !/^[A-Za-z0-9_.:-]{1,64}$/.test(fields2.id)) invalid7();
|
|
1128
|
+
return Object.freeze({ id: fields2.id, label: text(fields2.label, MLS_DEV_INTERACTION_LIMITS.optionLabel) });
|
|
1129
|
+
}) : void 0;
|
|
1130
|
+
if (options && new Set(options.map((option) => option.id)).size !== options.length) invalid7();
|
|
1131
|
+
const binding3 = parseMlsDevInteractionBinding({ role: "request", requestId: input.requestId, kind, responderIds: input.responderIds, expiresAtMs: input.expiresAtMs });
|
|
1132
|
+
if (binding3.role !== "request") invalid7();
|
|
1133
|
+
if (kind !== "approval" && ["risk", "allowSession", "scopeKey"].some(has)) invalid7();
|
|
1134
|
+
if (kind !== "question" && (has("options") || has("multiple"))) invalid7();
|
|
1135
|
+
if (has("multiple") && !options) invalid7();
|
|
1136
|
+
if (kind === "secret" !== has("secretKey")) invalid7();
|
|
1137
|
+
const allowSession = has("allowSession") ? boolean2(input.allowSession) : void 0;
|
|
1138
|
+
if (has("scopeKey") && (typeof input.scopeKey !== "string" || !/^[A-Za-z0-9_:-]{1,64}$/.test(input.scopeKey))) invalid7();
|
|
1139
|
+
if (allowSession === true && !has("scopeKey")) invalid7();
|
|
1140
|
+
if (has("risk") && input.risk !== "normal" && input.risk !== "destructive") invalid7();
|
|
1141
|
+
const secretKey = has("secretKey") ? (() => {
|
|
1142
|
+
const key = readMlsDataRecord(input.secretKey, ["kem", "publicKey"]);
|
|
1143
|
+
return Object.freeze({ kem: requireMlsLiteral(key.kem, "x25519"), publicKey: encoded3(key.publicKey, 32) });
|
|
1144
|
+
})() : void 0;
|
|
1145
|
+
return ordered({
|
|
1146
|
+
requestId: binding3.requestId,
|
|
1147
|
+
kind: binding3.kind,
|
|
1148
|
+
title: text(input.title, MLS_DEV_INTERACTION_LIMITS.title),
|
|
1149
|
+
details: has("details") ? text(input.details, MLS_DEV_INTERACTION_LIMITS.details) : void 0,
|
|
1150
|
+
options: options ? Object.freeze(options) : void 0,
|
|
1151
|
+
multiple: has("multiple") ? boolean2(input.multiple) : void 0,
|
|
1152
|
+
risk: input.risk,
|
|
1153
|
+
allowSession,
|
|
1154
|
+
scopeKey: input.scopeKey,
|
|
1155
|
+
responderIds: binding3.responderIds,
|
|
1156
|
+
expiresAtMs: binding3.expiresAtMs,
|
|
1157
|
+
secretKey
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
__name(parseMlsDevInteractionRequestBody, "parseMlsDevInteractionRequestBody");
|
|
1162
|
+
function parseMlsDevInteractionAnswer(value) {
|
|
1163
|
+
return boundary6(() => {
|
|
1164
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) invalid7();
|
|
1165
|
+
const keys2 = Reflect.ownKeys(value);
|
|
1166
|
+
if (keys2.includes("decision")) {
|
|
1167
|
+
const input = readMlsDataRecord(value, ["decision", "scope"]);
|
|
1168
|
+
if (input.decision !== "allow" && input.decision !== "deny" || input.scope !== "once" && input.scope !== "session") invalid7();
|
|
1169
|
+
return Object.freeze({ decision: input.decision, scope: input.scope });
|
|
1170
|
+
}
|
|
1171
|
+
if (keys2.includes("choices")) {
|
|
1172
|
+
const choices = array2(readMlsDataRecord(value, ["choices"]).choices, 1, MLS_DEV_INTERACTION_LIMITS.options).map((choice) => {
|
|
1173
|
+
if (typeof choice !== "string" || !/^[A-Za-z0-9_.:-]{1,64}$/.test(choice)) invalid7();
|
|
1174
|
+
return choice;
|
|
1175
|
+
});
|
|
1176
|
+
if (new Set(choices).size !== choices.length) invalid7();
|
|
1177
|
+
return Object.freeze({ choices: Object.freeze(choices) });
|
|
1178
|
+
}
|
|
1179
|
+
if (keys2.includes("text")) return Object.freeze({ text: text(readMlsDataRecord(value, ["text"]).text, MLS_DEV_INTERACTION_LIMITS.text) });
|
|
1180
|
+
if (keys2.includes("sealed")) {
|
|
1181
|
+
const sealed = readMlsDataRecord(readMlsDataRecord(value, ["sealed"]).sealed, ["enc", "ciphertext"]);
|
|
1182
|
+
return Object.freeze({ sealed: Object.freeze({ enc: encoded3(sealed.enc, 32), ciphertext: wire(sealed.ciphertext, 17, MLS_DEV_SECRET_ANSWER_MAX_BYTES) }) });
|
|
1183
|
+
}
|
|
1184
|
+
if (readMlsDataRecord(value, ["cancelled"]).cancelled !== true) invalid7();
|
|
1185
|
+
return Object.freeze({ cancelled: true });
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
__name(parseMlsDevInteractionAnswer, "parseMlsDevInteractionAnswer");
|
|
1189
|
+
function parseMlsDevInteractionResponseBody(value) {
|
|
1190
|
+
return boundary6(() => {
|
|
1191
|
+
const input = readMlsDataRecord(value, ["requestId", "agentId", "answer"]);
|
|
1192
|
+
return Object.freeze({ requestId: interactionId(input.requestId), agentId: id3(input.agentId), answer: parseMlsDevInteractionAnswer(input.answer) });
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
__name(parseMlsDevInteractionResponseBody, "parseMlsDevInteractionResponseBody");
|
|
1196
|
+
function parseMlsDevInteractionWithdrawBody(value) {
|
|
1197
|
+
return boundary6(() => {
|
|
1198
|
+
const input = readMlsDataRecord(value, ["requestId", "reason"]);
|
|
1199
|
+
if (!MLS_DEV_INTERACTION_WITHDRAW_REASONS.includes(input.reason)) invalid7();
|
|
1200
|
+
return Object.freeze({ requestId: interactionId(input.requestId), reason: input.reason });
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
__name(parseMlsDevInteractionWithdrawBody, "parseMlsDevInteractionWithdrawBody");
|
|
1204
|
+
var payloadFields = ["version", "kind", "conversationId", "groupId", "messageId", "operationId", "senderDeviceId"];
|
|
1205
|
+
function parseMlsDevApplicationPayload(value) {
|
|
1206
|
+
return boundary6(() => {
|
|
1207
|
+
const kind = value && typeof value === "object" ? Object.getOwnPropertyDescriptor(value, "kind") : void 0;
|
|
1208
|
+
if (!kind || !Object.hasOwn(kind, "value")) invalid7();
|
|
1209
|
+
if (kind.value === "text") return parseMlsDevTextPayload(value);
|
|
1210
|
+
if (kind.value === "item") return parseMlsDevItemPayload(value);
|
|
1211
|
+
const input = readMlsDataRecord(value, [...payloadFields, "interaction"]);
|
|
1212
|
+
const ids = {
|
|
1213
|
+
version: requireMlsLiteral(input.version, 1),
|
|
1214
|
+
kind: input.kind,
|
|
1215
|
+
conversationId: id3(input.conversationId),
|
|
1216
|
+
groupId: encoded3(input.groupId, 32),
|
|
1217
|
+
messageId: id3(input.messageId),
|
|
1218
|
+
operationId: id3(input.operationId),
|
|
1219
|
+
senderDeviceId: requireMlsUuid(input.senderDeviceId)
|
|
1220
|
+
};
|
|
1221
|
+
if (ids.messageId.startsWith("membership-")) invalid7();
|
|
1222
|
+
const interaction = kind.value === "interaction.request" ? parseMlsDevInteractionRequestBody(input.interaction) : kind.value === "interaction.response" ? parseMlsDevInteractionResponseBody(input.interaction) : kind.value === "interaction.withdraw" ? parseMlsDevInteractionWithdrawBody(input.interaction) : invalid7();
|
|
1223
|
+
const payload = Object.freeze({ ...ids, interaction });
|
|
1224
|
+
if (utf83.encode(JSON.stringify(payload)).length > MLS_DEV_TEXT_MAX_BYTES) invalid7();
|
|
1225
|
+
return payload;
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
__name(parseMlsDevApplicationPayload, "parseMlsDevApplicationPayload");
|
|
1229
|
+
function assertMlsDevInteractionPayloadBinding(payload, binding3) {
|
|
1230
|
+
boundary6(() => {
|
|
1231
|
+
if (payload.kind === "text" || payload.kind === "item") {
|
|
1232
|
+
if (binding3 !== void 0) invalid7();
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
if (!binding3 || payload.interaction.requestId !== binding3.requestId) invalid7();
|
|
1236
|
+
if (payload.kind === "interaction.request") {
|
|
1237
|
+
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();
|
|
1238
|
+
} else if (payload.kind === "interaction.response") {
|
|
1239
|
+
if (binding3.role !== "response" || payload.interaction.agentId !== binding3.agentId) invalid7();
|
|
1240
|
+
} else if (binding3.role !== "withdraw") invalid7();
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
__name(assertMlsDevInteractionPayloadBinding, "assertMlsDevInteractionPayloadBinding");
|
|
1244
|
+
function isMlsDevInteractionAnswerAllowed(request, answer) {
|
|
1245
|
+
if ("cancelled" in answer) return request.kind !== "approval";
|
|
1246
|
+
if (request.kind === "approval") return "decision" in answer && (answer.scope !== "session" || request.allowSession === true);
|
|
1247
|
+
if (request.kind === "secret") return "sealed" in answer;
|
|
1248
|
+
if ("text" in answer) return !request.options;
|
|
1249
|
+
if (!("choices" in answer) || !request.options) return false;
|
|
1250
|
+
return (request.multiple === true || answer.choices.length === 1) && answer.choices.every((choice) => request.options.some((option) => option.id === choice));
|
|
1251
|
+
}
|
|
1252
|
+
__name(isMlsDevInteractionAnswerAllowed, "isMlsDevInteractionAnswerAllowed");
|
|
1253
|
+
|
|
732
1254
|
// ../backend-contracts/src/mlsDevGroup.ts
|
|
733
1255
|
var MLS_DEV_GROUP_ROSTER_MAX = 32;
|
|
734
1256
|
var MLS_DEV_GROUP_TLS_MAX_BYTES = 256 * 1024;
|
|
@@ -737,6 +1259,7 @@ var MLS_DEV_GROUP_WRAPPED_MAX_BYTES = 3 * 1024 * 1024;
|
|
|
737
1259
|
var MLS_DEV_GROUP_PREPARATION_MAX_BYTES = 4 * 1024 * 1024;
|
|
738
1260
|
var MLS_DEV_GROUP_QUERY_MAX_BYTES = 4 * 1024 * 1024;
|
|
739
1261
|
var MLS_DEV_GROUP_QUERY_MAX_CONTROLS = 4;
|
|
1262
|
+
var MLS_DEV_GROUP_ADMISSION_MAX_CANDIDATES = 64;
|
|
740
1263
|
var MlsDevGroupValidationError = class extends Error {
|
|
741
1264
|
static {
|
|
742
1265
|
__name(this, "MlsDevGroupValidationError");
|
|
@@ -747,119 +1270,169 @@ var MlsDevGroupValidationError = class extends Error {
|
|
|
747
1270
|
this.name = "MlsDevGroupValidationError";
|
|
748
1271
|
}
|
|
749
1272
|
};
|
|
750
|
-
var
|
|
751
|
-
function
|
|
1273
|
+
var utf84 = new TextEncoder();
|
|
1274
|
+
function invalid8() {
|
|
752
1275
|
throw new MlsDevGroupValidationError();
|
|
753
1276
|
}
|
|
754
|
-
__name(
|
|
755
|
-
function
|
|
1277
|
+
__name(invalid8, "invalid");
|
|
1278
|
+
function boundary7(read) {
|
|
756
1279
|
try {
|
|
757
1280
|
return read();
|
|
758
1281
|
} catch {
|
|
759
|
-
return
|
|
1282
|
+
return invalid8();
|
|
760
1283
|
}
|
|
761
1284
|
}
|
|
762
|
-
__name(
|
|
1285
|
+
__name(boundary7, "boundary");
|
|
763
1286
|
function bounded5(value, maximum = MLS_DEV_GROUP_CONTROL_MAX_BYTES) {
|
|
764
|
-
if (
|
|
765
|
-
|
|
1287
|
+
if (utf84.encode(JSON.stringify(value)).length > maximum)
|
|
1288
|
+
invalid8();
|
|
766
1289
|
return Object.freeze(value);
|
|
767
1290
|
}
|
|
768
1291
|
__name(bounded5, "bounded");
|
|
769
|
-
function
|
|
1292
|
+
function id4(value) {
|
|
770
1293
|
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
771
|
-
|
|
1294
|
+
invalid8();
|
|
772
1295
|
return value;
|
|
773
1296
|
}
|
|
774
|
-
__name(
|
|
775
|
-
function
|
|
1297
|
+
__name(id4, "id");
|
|
1298
|
+
function encoded4(value, bytes) {
|
|
776
1299
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
777
1300
|
return value;
|
|
778
1301
|
}
|
|
779
|
-
__name(
|
|
780
|
-
function
|
|
1302
|
+
__name(encoded4, "encoded");
|
|
1303
|
+
function boolean3(value) {
|
|
781
1304
|
if (typeof value !== "boolean")
|
|
782
|
-
|
|
1305
|
+
invalid8();
|
|
783
1306
|
return value;
|
|
784
1307
|
}
|
|
785
|
-
__name(
|
|
1308
|
+
__name(boolean3, "boolean");
|
|
1309
|
+
function textProtocol(value) {
|
|
1310
|
+
if (!isMlsDevTextProtocol(value))
|
|
1311
|
+
invalid8();
|
|
1312
|
+
return value;
|
|
1313
|
+
}
|
|
1314
|
+
__name(textProtocol, "textProtocol");
|
|
786
1315
|
function token(value) {
|
|
787
1316
|
parseMlsDevCredentialToken(value);
|
|
788
1317
|
return value;
|
|
789
1318
|
}
|
|
790
1319
|
__name(token, "token");
|
|
791
|
-
function
|
|
1320
|
+
function wire2(value) {
|
|
792
1321
|
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_GROUP_TLS_MAX_BYTES * 4 / 3))
|
|
793
|
-
|
|
1322
|
+
invalid8();
|
|
794
1323
|
const bytes = Math.floor(value.length * 3 / 4);
|
|
795
1324
|
if (value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
796
|
-
|
|
1325
|
+
invalid8();
|
|
797
1326
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
798
1327
|
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
799
|
-
|
|
1328
|
+
invalid8();
|
|
800
1329
|
return value;
|
|
801
1330
|
}
|
|
802
|
-
__name(
|
|
803
|
-
function
|
|
1331
|
+
__name(wire2, "wire");
|
|
1332
|
+
function plaintextJson(value) {
|
|
1333
|
+
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)
|
|
1334
|
+
invalid8();
|
|
1335
|
+
return value;
|
|
1336
|
+
}
|
|
1337
|
+
__name(plaintextJson, "plaintextJson");
|
|
1338
|
+
function present(value, keys2) {
|
|
1339
|
+
return value && typeof value === "object" ? keys2.filter((key) => Object.hasOwn(value, key)) : [];
|
|
1340
|
+
}
|
|
1341
|
+
__name(present, "present");
|
|
1342
|
+
var pick = /* @__PURE__ */ __name((value, keys2) => Object.fromEntries(keys2.map((key) => [key, value[key]])), "pick");
|
|
1343
|
+
var textExtras = /* @__PURE__ */ __name((text5) => ({ ...text5.replyTo ? { replyTo: text5.replyTo } : {}, ...text5.mentions ? { mentions: text5.mentions } : {} }), "textExtras");
|
|
1344
|
+
function sealedCiphertext(value) {
|
|
1345
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1) invalid8();
|
|
1346
|
+
const bytes = Math.floor(value.length * 3 / 4);
|
|
1347
|
+
if (bytes < 17 || bytes > MLS_DEV_SECRET_ANSWER_MAX_BYTES) invalid8();
|
|
1348
|
+
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
1349
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid8();
|
|
1350
|
+
return value;
|
|
1351
|
+
}
|
|
1352
|
+
__name(sealedCiphertext, "sealedCiphertext");
|
|
1353
|
+
function array3(value, maximum) {
|
|
804
1354
|
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || value.length > maximum)
|
|
805
|
-
|
|
1355
|
+
invalid8();
|
|
806
1356
|
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
807
1357
|
if (Reflect.ownKeys(descriptors).length !== value.length + 1)
|
|
808
|
-
|
|
1358
|
+
invalid8();
|
|
809
1359
|
return Array.from({ length: value.length }, (_, index) => {
|
|
810
1360
|
const descriptor = descriptors[index];
|
|
811
1361
|
if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, "value"))
|
|
812
|
-
|
|
1362
|
+
invalid8();
|
|
813
1363
|
return descriptor.value;
|
|
814
1364
|
});
|
|
815
1365
|
}
|
|
816
|
-
__name(
|
|
1366
|
+
__name(array3, "array");
|
|
817
1367
|
function parseMlsDevGroupCreateConversation(value) {
|
|
818
|
-
return
|
|
1368
|
+
return boundary7(() => {
|
|
819
1369
|
const input = readMlsDataRecord(value, ["name", "memberIds", "mlsDev"]), config = readMlsDataRecord(input.mlsDev, ["version", "conversationId", "credential"], ["version", "conversationId", "credential", "protocol"]);
|
|
820
1370
|
if (typeof input.name !== "string" || input.name.trim().length > 100)
|
|
821
|
-
|
|
822
|
-
const memberIds =
|
|
1371
|
+
invalid8();
|
|
1372
|
+
const memberIds = array3(input.memberIds, 31).map((value2) => parseMlsDevEnrollmentPrincipal({ type: "human", id: value2 }).id);
|
|
823
1373
|
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:
|
|
1374
|
+
invalid8();
|
|
1375
|
+
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
1376
|
});
|
|
827
1377
|
}
|
|
828
1378
|
__name(parseMlsDevGroupCreateConversation, "parseMlsDevGroupCreateConversation");
|
|
829
1379
|
function parseMlsDevGroupHead(value) {
|
|
830
|
-
return
|
|
1380
|
+
return boundary7(() => {
|
|
831
1381
|
const input = readMlsDataRecord(value, ["sequence", "epoch", "hash"]);
|
|
832
|
-
return Object.freeze({ sequence: requireMlsInteger(input.sequence, 1), epoch: requireMlsInteger(input.epoch), hash:
|
|
1382
|
+
return Object.freeze({ sequence: requireMlsInteger(input.sequence, 1), epoch: requireMlsInteger(input.epoch), hash: encoded4(input.hash, 32) });
|
|
833
1383
|
});
|
|
834
1384
|
}
|
|
835
1385
|
__name(parseMlsDevGroupHead, "parseMlsDevGroupHead");
|
|
836
1386
|
function parseMlsDevGroupMarker(value) {
|
|
837
|
-
return
|
|
1387
|
+
return boundary7(() => {
|
|
838
1388
|
const input = readMlsDataRecord(value, ["version", "groupId", "head"]);
|
|
839
|
-
return Object.freeze({ version: requireMlsLiteral(input.version, 1), groupId:
|
|
1389
|
+
return Object.freeze({ version: requireMlsLiteral(input.version, 1), groupId: encoded4(input.groupId, 32), head: parseMlsDevGroupHead(input.head) });
|
|
840
1390
|
});
|
|
841
1391
|
}
|
|
842
1392
|
__name(parseMlsDevGroupMarker, "parseMlsDevGroupMarker");
|
|
843
1393
|
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:
|
|
1394
|
+
return boundary7(() => {
|
|
1395
|
+
const values = array3(value, MLS_DEV_GROUP_ROSTER_MAX).map((item2) => {
|
|
1396
|
+
const entry = readMlsDataRecord(item2, ["device", "admissionId"]);
|
|
1397
|
+
return Object.freeze({ device: parseMlsDevEnrollmentResult({ version: 1, authorization: "registration_only", device: entry.device }).device, admissionId: id4(entry.admissionId) });
|
|
848
1398
|
});
|
|
849
1399
|
for (let i = 1; i < values.length; i++)
|
|
850
1400
|
if (values[i - 1].device.deviceId >= values[i].device.deviceId)
|
|
851
|
-
|
|
1401
|
+
invalid8();
|
|
852
1402
|
return Object.freeze(values);
|
|
853
1403
|
});
|
|
854
1404
|
}
|
|
855
1405
|
__name(parseMlsDevGroupRoster, "parseMlsDevGroupRoster");
|
|
1406
|
+
var MLS_DEV_ROOM_REMOVAL_DEADLINE_MS = 72 * 60 * 60 * 1e3;
|
|
1407
|
+
function parseMlsDevRoomRemoval(value) {
|
|
1408
|
+
return boundary7(() => {
|
|
1409
|
+
const input = readMlsDataRecord(value, ["version", "status", "pending", "closedAtMs"]), devices = /* @__PURE__ */ new Set(), principals = /* @__PURE__ */ new Set();
|
|
1410
|
+
const status = ["pending", "closing", "closed"].includes(input.status) ? input.status : invalid8();
|
|
1411
|
+
const pending = array3(input.pending, MLS_DEV_GROUP_ROSTER_MAX).map((item2) => {
|
|
1412
|
+
const entry = readMlsDataRecord(item2, ["principalId", "deviceIds", "deadlineAtMs"]);
|
|
1413
|
+
const principalId2 = parseMlsDevEnrollmentPrincipal({ type: "human", id: entry.principalId }).id;
|
|
1414
|
+
const deviceIds = array3(entry.deviceIds, MLS_DEV_GROUP_ROSTER_MAX).map(requireMlsUuid);
|
|
1415
|
+
if (principals.has(principalId2)) invalid8();
|
|
1416
|
+
principals.add(principalId2);
|
|
1417
|
+
for (const deviceId of deviceIds) {
|
|
1418
|
+
if (devices.has(deviceId)) invalid8();
|
|
1419
|
+
devices.add(deviceId);
|
|
1420
|
+
}
|
|
1421
|
+
return Object.freeze({ principalId: principalId2, deviceIds: Object.freeze(deviceIds), deadlineAtMs: requireMlsInteger(entry.deadlineAtMs, 1) });
|
|
1422
|
+
});
|
|
1423
|
+
const closedAtMs = input.closedAtMs === null ? null : requireMlsInteger(input.closedAtMs, 1);
|
|
1424
|
+
if (status === "pending" ? !pending.length || closedAtMs !== null : closedAtMs === null || status === "closed" && pending.length) invalid8();
|
|
1425
|
+
return Object.freeze({ version: requireMlsLiteral(input.version, 1), status, pending: Object.freeze(pending), closedAtMs });
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
__name(parseMlsDevRoomRemoval, "parseMlsDevRoomRemoval");
|
|
856
1429
|
function parseMlsDevGroupPreconditions(value) {
|
|
857
|
-
return
|
|
1430
|
+
return boundary7(() => {
|
|
858
1431
|
const input = readMlsDataRecord(value, ["membershipRevision", "admissionId", "targetAdmissionId"]);
|
|
859
1432
|
return Object.freeze({
|
|
860
1433
|
membershipRevision: requireMlsInteger(input.membershipRevision),
|
|
861
|
-
admissionId:
|
|
862
|
-
targetAdmissionId: input.targetAdmissionId === null ? null :
|
|
1434
|
+
admissionId: id4(input.admissionId),
|
|
1435
|
+
targetAdmissionId: input.targetAdmissionId === null ? null : id4(input.targetAdmissionId)
|
|
863
1436
|
});
|
|
864
1437
|
});
|
|
865
1438
|
}
|
|
@@ -883,37 +1456,40 @@ var unsignedFields = [
|
|
|
883
1456
|
];
|
|
884
1457
|
function unsigned(input) {
|
|
885
1458
|
if (typeof input.kind !== "string" || !["create", "add", "remove"].includes(input.kind))
|
|
886
|
-
|
|
1459
|
+
invalid8();
|
|
887
1460
|
const kind = input.kind;
|
|
888
1461
|
const senderCredential = token(input.senderCredential), sender = parseMlsDevCredentialToken(senderCredential).unsigned.claims.device;
|
|
889
1462
|
const expectedHead = input.expectedHead === null ? null : parseMlsDevGroupHead(input.expectedHead);
|
|
890
1463
|
const epoch = requireMlsInteger(input.epoch), preconditions = parseMlsDevGroupPreconditions(input.preconditions), roster = parseMlsDevGroupRoster(input.roster);
|
|
891
|
-
const
|
|
892
|
-
|
|
1464
|
+
const environmentId = requireMlsEnvironmentId(input.environmentId);
|
|
1465
|
+
if (sender.environmentId !== environmentId || roster.some((entry) => entry.device.environmentId !== environmentId))
|
|
1466
|
+
invalid8();
|
|
1467
|
+
const groupInfo = input.groupInfo === null ? null : wire2(input.groupInfo), commit = input.commit === null ? null : wire2(input.commit);
|
|
1468
|
+
const welcome = input.welcome === null ? null : wire2(input.welcome);
|
|
893
1469
|
const keyPackageClaim = input.keyPackageClaim === null ? null : parseMlsDevKeyPackageClaimReceipt(input.keyPackageClaim);
|
|
894
1470
|
const removedDeviceId = input.removedDeviceId === null ? null : requireMlsUuid(input.removedDeviceId);
|
|
895
1471
|
const own = roster.find((entry) => entry.device.deviceId === sender.deviceId);
|
|
896
1472
|
if (!own || JSON.stringify(own.device) !== JSON.stringify(sender) || own.admissionId !== preconditions.admissionId)
|
|
897
|
-
|
|
1473
|
+
invalid8();
|
|
898
1474
|
if (kind === "create") {
|
|
899
1475
|
if (expectedHead !== null || epoch !== 0 || roster.length !== 1 || !groupInfo || commit !== null || welcome !== null || keyPackageClaim !== null || removedDeviceId !== null || preconditions.targetAdmissionId !== null)
|
|
900
|
-
|
|
1476
|
+
invalid8();
|
|
901
1477
|
} else {
|
|
902
1478
|
if (!expectedHead || expectedHead.sequence >= Number.MAX_SAFE_INTEGER || expectedHead.epoch >= Number.MAX_SAFE_INTEGER || epoch !== expectedHead.epoch + 1 || !commit || preconditions.targetAdmissionId === null)
|
|
903
|
-
|
|
1479
|
+
invalid8();
|
|
904
1480
|
if (kind === "add") {
|
|
905
1481
|
const target = roster.find((entry) => entry.device.deviceId === keyPackageClaim?.recipientDeviceId);
|
|
906
1482
|
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
|
-
|
|
1483
|
+
invalid8();
|
|
908
1484
|
} else if (!removedDeviceId || removedDeviceId === sender.deviceId || roster.some((entry) => entry.device.deviceId === removedDeviceId) || welcome !== null || keyPackageClaim !== null)
|
|
909
|
-
|
|
1485
|
+
invalid8();
|
|
910
1486
|
}
|
|
911
1487
|
return bounded5({
|
|
912
1488
|
version: requireMlsLiteral(input.version, 1),
|
|
913
|
-
environmentId
|
|
1489
|
+
environmentId,
|
|
914
1490
|
kind,
|
|
915
|
-
conversationId:
|
|
916
|
-
groupId:
|
|
1491
|
+
conversationId: id4(input.conversationId),
|
|
1492
|
+
groupId: encoded4(input.groupId, 32),
|
|
917
1493
|
senderCredential,
|
|
918
1494
|
expectedHead,
|
|
919
1495
|
epoch,
|
|
@@ -928,25 +1504,25 @@ function unsigned(input) {
|
|
|
928
1504
|
}
|
|
929
1505
|
__name(unsigned, "unsigned");
|
|
930
1506
|
function parseMlsDevGroupControl(value) {
|
|
931
|
-
return
|
|
1507
|
+
return boundary7(() => {
|
|
932
1508
|
const input = readMlsDataRecord(value, [...unsignedFields, "proof"]);
|
|
933
|
-
return bounded5({ ...unsigned(input), proof:
|
|
1509
|
+
return bounded5({ ...unsigned(input), proof: encoded4(input.proof, 64) });
|
|
934
1510
|
});
|
|
935
1511
|
}
|
|
936
1512
|
__name(parseMlsDevGroupControl, "parseMlsDevGroupControl");
|
|
937
1513
|
function parseMlsDevGroupAcceptedControl(value) {
|
|
938
|
-
return
|
|
1514
|
+
return boundary7(() => {
|
|
939
1515
|
const input = readMlsDataRecord(value, ["version", "authorization", "operationId", "control", "head", "acceptedAtMs", "membershipRevision"]);
|
|
940
1516
|
const control = parseMlsDevGroupControl(input.control), head = parseMlsDevGroupHead(input.head);
|
|
941
1517
|
if (head.sequence !== (control.expectedHead?.sequence ?? 0) + 1 || head.epoch !== control.epoch)
|
|
942
|
-
|
|
1518
|
+
invalid8();
|
|
943
1519
|
const membershipRevision = requireMlsInteger(input.membershipRevision);
|
|
944
1520
|
if (membershipRevision !== control.preconditions.membershipRevision && !(control.kind === "remove" && membershipRevision === control.preconditions.membershipRevision + 1))
|
|
945
|
-
|
|
1521
|
+
invalid8();
|
|
946
1522
|
return bounded5({
|
|
947
1523
|
version: requireMlsLiteral(input.version, 1),
|
|
948
1524
|
authorization: requireMlsLiteral(input.authorization, "group_control_only"),
|
|
949
|
-
operationId:
|
|
1525
|
+
operationId: id4(input.operationId),
|
|
950
1526
|
control,
|
|
951
1527
|
head,
|
|
952
1528
|
acceptedAtMs: requireMlsInteger(input.acceptedAtMs),
|
|
@@ -955,40 +1531,65 @@ function parseMlsDevGroupAcceptedControl(value) {
|
|
|
955
1531
|
});
|
|
956
1532
|
}
|
|
957
1533
|
__name(parseMlsDevGroupAcceptedControl, "parseMlsDevGroupAcceptedControl");
|
|
1534
|
+
function parseMlsDevGroupAdmissionsResult(value) {
|
|
1535
|
+
return boundary7(() => {
|
|
1536
|
+
const input = readMlsDataRecord(value, ["version", "conversationId", "membershipRevision", "candidates", "waitingPrincipalIds"]);
|
|
1537
|
+
const candidates = array3(input.candidates, MLS_DEV_GROUP_ADMISSION_MAX_CANDIDATES).map((raw) => {
|
|
1538
|
+
const entry = readMlsDataRecord(raw, ["principal", "deviceId", "targetAdmissionId"]), principal2 = readMlsDataRecord(entry.principal, ["type", "id"]);
|
|
1539
|
+
if (principal2.type !== "human" && principal2.type !== "ai_agent") invalid8();
|
|
1540
|
+
return Object.freeze({
|
|
1541
|
+
principal: Object.freeze({ type: principal2.type, id: id4(principal2.id) }),
|
|
1542
|
+
deviceId: requireMlsUuid(entry.deviceId),
|
|
1543
|
+
targetAdmissionId: id4(entry.targetAdmissionId)
|
|
1544
|
+
});
|
|
1545
|
+
});
|
|
1546
|
+
if (new Set(candidates.map((entry) => entry.deviceId)).size !== candidates.length) invalid8();
|
|
1547
|
+
const waiting = array3(input.waitingPrincipalIds, 32).map(id4);
|
|
1548
|
+
if (new Set(waiting).size !== waiting.length) invalid8();
|
|
1549
|
+
return bounded5(Object.freeze({
|
|
1550
|
+
version: requireMlsLiteral(input.version, 1),
|
|
1551
|
+
conversationId: id4(input.conversationId),
|
|
1552
|
+
membershipRevision: requireMlsInteger(input.membershipRevision, 1),
|
|
1553
|
+
candidates: Object.freeze(candidates),
|
|
1554
|
+
waitingPrincipalIds: Object.freeze(waiting)
|
|
1555
|
+
}), 64 * 1024);
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
__name(parseMlsDevGroupAdmissionsResult, "parseMlsDevGroupAdmissionsResult");
|
|
958
1559
|
function parseMlsDevGroupQuery(value) {
|
|
959
|
-
return
|
|
1560
|
+
return boundary7(() => {
|
|
960
1561
|
const input = readMlsDataRecord(value, ["version", "conversationId", "credential", "afterSequence", "limit"]);
|
|
961
|
-
const
|
|
962
|
-
if (
|
|
963
|
-
|
|
1562
|
+
const limit2 = requireMlsInteger(input.limit, 1);
|
|
1563
|
+
if (limit2 > MLS_DEV_GROUP_QUERY_MAX_CONTROLS)
|
|
1564
|
+
invalid8();
|
|
964
1565
|
return bounded5({
|
|
965
1566
|
version: requireMlsLiteral(input.version, 1),
|
|
966
|
-
conversationId:
|
|
1567
|
+
conversationId: id4(input.conversationId),
|
|
967
1568
|
credential: token(input.credential),
|
|
968
1569
|
afterSequence: requireMlsInteger(input.afterSequence),
|
|
969
|
-
limit
|
|
1570
|
+
limit: limit2
|
|
970
1571
|
}, 16 * 1024);
|
|
971
1572
|
});
|
|
972
1573
|
}
|
|
973
1574
|
__name(parseMlsDevGroupQuery, "parseMlsDevGroupQuery");
|
|
974
1575
|
function parseMlsDevGroupQueryResult(value) {
|
|
975
|
-
return
|
|
1576
|
+
return boundary7(() => {
|
|
976
1577
|
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 =
|
|
1578
|
+
const conversationId = id4(input.conversationId), marker = input.marker === null ? null : parseMlsDevGroupMarker(input.marker);
|
|
1579
|
+
const controls = array3(input.controls, MLS_DEV_GROUP_QUERY_MAX_CONTROLS).map(parseMlsDevGroupAcceptedControl);
|
|
1580
|
+
const nextSequence = requireMlsInteger(input.nextSequence), terminal = boolean3(input.terminal), hasMore = boolean3(input.hasMore);
|
|
980
1581
|
for (let i = 0; i < controls.length; i++) {
|
|
981
1582
|
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
|
-
|
|
1583
|
+
invalid8();
|
|
983
1584
|
}
|
|
984
1585
|
if (controls.length && controls.at(-1).head.sequence !== nextSequence || marker && nextSequence > marker.head.sequence || !marker && (controls.length || nextSequence || terminal || hasMore))
|
|
985
|
-
|
|
1586
|
+
invalid8();
|
|
986
1587
|
return bounded5({
|
|
987
1588
|
version: requireMlsLiteral(input.version, 1),
|
|
988
1589
|
conversationId,
|
|
989
1590
|
marker,
|
|
990
1591
|
membershipRevision: requireMlsInteger(input.membershipRevision),
|
|
991
|
-
admissionId:
|
|
1592
|
+
admissionId: id4(input.admissionId),
|
|
992
1593
|
roster: parseMlsDevGroupRoster(input.roster),
|
|
993
1594
|
controls: Object.freeze(controls),
|
|
994
1595
|
nextSequence,
|
|
@@ -999,42 +1600,43 @@ function parseMlsDevGroupQueryResult(value) {
|
|
|
999
1600
|
}
|
|
1000
1601
|
__name(parseMlsDevGroupQueryResult, "parseMlsDevGroupQueryResult");
|
|
1001
1602
|
function parseMlsDevGroupContext(value) {
|
|
1002
|
-
return
|
|
1603
|
+
return boundary7(() => {
|
|
1003
1604
|
const input = readMlsDataRecord(value, ["version", "environmentId", "conversationId", "groupId", "deviceId", "installationId", "revision"], ["version", "environmentId", "conversationId", "groupId", "deviceId", "installationId", "revision", "protocol"]);
|
|
1004
1605
|
const revision2 = requireMlsInteger(input.revision);
|
|
1005
1606
|
if (revision2 >= Number.MAX_SAFE_INTEGER)
|
|
1006
|
-
|
|
1607
|
+
invalid8();
|
|
1007
1608
|
return Object.freeze({
|
|
1008
1609
|
version: requireMlsLiteral(input.version, 1),
|
|
1009
|
-
environmentId:
|
|
1010
|
-
conversationId:
|
|
1011
|
-
groupId:
|
|
1610
|
+
environmentId: requireMlsEnvironmentId(input.environmentId),
|
|
1611
|
+
conversationId: id4(input.conversationId),
|
|
1612
|
+
groupId: encoded4(input.groupId, 32),
|
|
1012
1613
|
deviceId: requireMlsUuid(input.deviceId),
|
|
1013
1614
|
installationId: parseMlsDevInstallationId(input.installationId),
|
|
1014
1615
|
revision: revision2,
|
|
1015
|
-
...Object.hasOwn(input, "protocol") ? { protocol:
|
|
1616
|
+
...Object.hasOwn(input, "protocol") ? { protocol: textProtocol(input.protocol) } : {}
|
|
1016
1617
|
});
|
|
1017
1618
|
});
|
|
1018
1619
|
}
|
|
1019
1620
|
__name(parseMlsDevGroupContext, "parseMlsDevGroupContext");
|
|
1020
1621
|
function parseMlsDevGroupCommand(value) {
|
|
1021
|
-
return
|
|
1622
|
+
return boundary7(() => {
|
|
1022
1623
|
if (!value || typeof value !== "object")
|
|
1023
|
-
|
|
1624
|
+
invalid8();
|
|
1024
1625
|
const descriptor = Object.getOwnPropertyDescriptor(value, "op");
|
|
1025
1626
|
if (!descriptor || !Object.hasOwn(descriptor, "value"))
|
|
1026
|
-
|
|
1627
|
+
invalid8();
|
|
1027
1628
|
const op = descriptor.value;
|
|
1028
1629
|
if (op === "process_application") {
|
|
1029
1630
|
const input2 = readMlsDataRecord(value, ["op", "entry"]), entry = parseMlsDevGroupFeedEntry(input2.entry);
|
|
1030
|
-
if (entry.kind !== "application")
|
|
1031
|
-
|
|
1631
|
+
if (entry.kind !== "application" || !entry.receiptProof)
|
|
1632
|
+
invalid8();
|
|
1032
1633
|
return bounded5({ op, entry });
|
|
1033
1634
|
}
|
|
1034
1635
|
if (op === "encrypt_text") {
|
|
1035
|
-
const
|
|
1636
|
+
const extras = present(value, ["replyTo", "mentions"]);
|
|
1637
|
+
const input2 = readMlsDataRecord(value, ["op", "operationId", "messageId", "credential", "expectedHead", "preconditions", "text", ...extras]);
|
|
1036
1638
|
const credential = token(input2.credential), senderDeviceId = parseMlsDevCredentialToken(credential).unsigned.claims.device.deviceId;
|
|
1037
|
-
const
|
|
1639
|
+
const text5 = parseMlsDevTextPayload({
|
|
1038
1640
|
version: 1,
|
|
1039
1641
|
kind: "text",
|
|
1040
1642
|
conversationId: "validation",
|
|
@@ -1042,117 +1644,283 @@ function parseMlsDevGroupCommand(value) {
|
|
|
1042
1644
|
messageId: input2.messageId,
|
|
1043
1645
|
operationId: input2.operationId,
|
|
1044
1646
|
senderDeviceId,
|
|
1045
|
-
text: input2.text
|
|
1647
|
+
text: input2.text,
|
|
1648
|
+
...pick(input2, extras)
|
|
1046
1649
|
});
|
|
1047
1650
|
return bounded5({
|
|
1048
1651
|
op,
|
|
1049
|
-
operationId:
|
|
1050
|
-
messageId:
|
|
1652
|
+
operationId: text5.operationId,
|
|
1653
|
+
messageId: text5.messageId,
|
|
1051
1654
|
credential,
|
|
1052
1655
|
expectedHead: parseMlsDevGroupHead(input2.expectedHead),
|
|
1053
1656
|
preconditions: parseMlsDevApplicationPreconditions(input2.preconditions),
|
|
1054
|
-
text:
|
|
1657
|
+
text: text5.text,
|
|
1658
|
+
...textExtras(text5)
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
if (op === "encrypt_item") {
|
|
1662
|
+
const input2 = readMlsDataRecord(value, ["op", "operationId", "messageId", "credential", "expectedHead", "preconditions", "item"]);
|
|
1663
|
+
const credential = token(input2.credential), senderDeviceId = parseMlsDevCredentialToken(credential).unsigned.claims.device.deviceId;
|
|
1664
|
+
const parsed = parseMlsDevItemPayload({
|
|
1665
|
+
version: 1,
|
|
1666
|
+
kind: "item",
|
|
1667
|
+
conversationId: "validation",
|
|
1668
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1669
|
+
messageId: input2.messageId,
|
|
1670
|
+
operationId: input2.operationId,
|
|
1671
|
+
senderDeviceId,
|
|
1672
|
+
item: input2.item
|
|
1673
|
+
});
|
|
1674
|
+
return bounded5({
|
|
1675
|
+
op,
|
|
1676
|
+
operationId: parsed.operationId,
|
|
1677
|
+
messageId: parsed.messageId,
|
|
1678
|
+
credential,
|
|
1679
|
+
expectedHead: parseMlsDevGroupHead(input2.expectedHead),
|
|
1680
|
+
preconditions: parseMlsDevApplicationPreconditions(input2.preconditions),
|
|
1681
|
+
item: parsed.item
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
if (op === "encrypt_interaction") {
|
|
1685
|
+
const input2 = readMlsDataRecord(value, ["op", "operationId", "messageId", "credential", "expectedHead", "preconditions", "interaction", "body", "seal"]);
|
|
1686
|
+
const credential = token(input2.credential), interaction = parseMlsDevInteractionBinding(input2.interaction);
|
|
1687
|
+
const body = readMlsDataRecord(input2.body, Reflect.ownKeys(input2.body ?? {}).filter((key) => typeof key === "string"));
|
|
1688
|
+
let seal = null;
|
|
1689
|
+
if (interaction.role === "request") {
|
|
1690
|
+
if (Object.hasOwn(body, "secretKey") || input2.seal !== null)
|
|
1691
|
+
invalid8();
|
|
1692
|
+
const parsed = parseMlsDevInteractionRequestBody(interaction.kind === "secret" ? { ...body, secretKey: { kem: "x25519", publicKey: "A".repeat(43) } } : body);
|
|
1693
|
+
if (parsed.requestId !== interaction.requestId || parsed.kind !== interaction.kind || parsed.expiresAtMs !== interaction.expiresAtMs || parsed.responderIds[0] !== interaction.responderIds[0])
|
|
1694
|
+
invalid8();
|
|
1695
|
+
} else if (interaction.role === "response") {
|
|
1696
|
+
if (input2.seal === null) {
|
|
1697
|
+
const parsed = parseMlsDevInteractionResponseBody(body);
|
|
1698
|
+
if (parsed.requestId !== interaction.requestId || parsed.agentId !== interaction.agentId || "sealed" in parsed.answer)
|
|
1699
|
+
invalid8();
|
|
1700
|
+
} else {
|
|
1701
|
+
const fields3 = readMlsDataRecord(input2.seal, ["text", "publicKey", "requesterDeviceId"]), ids2 = readMlsDataRecord(body, ["requestId", "agentId"]);
|
|
1702
|
+
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)
|
|
1703
|
+
invalid8();
|
|
1704
|
+
seal = Object.freeze({ text: fields3.text, publicKey: encoded4(fields3.publicKey, 32), requesterDeviceId: requireMlsUuid(fields3.requesterDeviceId) });
|
|
1705
|
+
}
|
|
1706
|
+
} else if (input2.seal !== null || parseMlsDevInteractionWithdrawBody(body).requestId !== interaction.requestId)
|
|
1707
|
+
invalid8();
|
|
1708
|
+
const ids = parseMlsDevTextPayload({
|
|
1709
|
+
version: 1,
|
|
1710
|
+
kind: "text",
|
|
1711
|
+
conversationId: "validation",
|
|
1712
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1713
|
+
messageId: input2.messageId,
|
|
1714
|
+
operationId: input2.operationId,
|
|
1715
|
+
senderDeviceId: parseMlsDevCredentialToken(credential).unsigned.claims.device.deviceId,
|
|
1716
|
+
text: "x"
|
|
1717
|
+
});
|
|
1718
|
+
if (utf84.encode(JSON.stringify(body)).length > MLS_DEV_TEXT_MAX_BYTES)
|
|
1719
|
+
invalid8();
|
|
1720
|
+
return bounded5({
|
|
1721
|
+
op,
|
|
1722
|
+
operationId: ids.operationId,
|
|
1723
|
+
messageId: ids.messageId,
|
|
1724
|
+
credential,
|
|
1725
|
+
expectedHead: parseMlsDevGroupHead(input2.expectedHead),
|
|
1726
|
+
preconditions: parseMlsDevApplicationPreconditions(input2.preconditions),
|
|
1727
|
+
interaction,
|
|
1728
|
+
body: Object.freeze(body),
|
|
1729
|
+
seal
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
if (op === "open_secret_answer") {
|
|
1733
|
+
const input2 = readMlsDataRecord(value, ["op", "requestId", "responderId", "responderDeviceId", "sealed"]), sealed = readMlsDataRecord(input2.sealed, ["enc", "ciphertext"]);
|
|
1734
|
+
const binding3 = parseMlsDevInteractionBinding({ role: "response", requestId: input2.requestId, agentId: input2.responderId });
|
|
1735
|
+
return bounded5({
|
|
1736
|
+
op,
|
|
1737
|
+
requestId: binding3.requestId,
|
|
1738
|
+
responderId: id4(input2.responderId),
|
|
1739
|
+
responderDeviceId: requireMlsUuid(input2.responderDeviceId),
|
|
1740
|
+
sealed: Object.freeze({ enc: encoded4(sealed.enc, 32), ciphertext: sealedCiphertext(sealed.ciphertext) })
|
|
1055
1741
|
});
|
|
1056
1742
|
}
|
|
1057
1743
|
if (op === "discard") {
|
|
1058
1744
|
const input2 = readMlsDataRecord(value, ["op", "operationId", "control"]);
|
|
1059
|
-
return bounded5({ op, operationId:
|
|
1745
|
+
return bounded5({ op, operationId: id4(input2.operationId), control: parseMlsDevGroupControl(input2.control) });
|
|
1060
1746
|
}
|
|
1061
1747
|
if (op === "finalize") {
|
|
1062
1748
|
const input2 = readMlsDataRecord(value, ["op", "accepted"]);
|
|
1063
1749
|
return bounded5({ op, accepted: parseMlsDevGroupAcceptedControl(input2.accepted) });
|
|
1064
1750
|
}
|
|
1065
1751
|
if (op === "join" || op === "process") {
|
|
1066
|
-
const input2 = readMlsDataRecord(value, op === "join" ? ["op", "credential", "accepted", "keyPackageId"] : ["op", "credential", "accepted"]);
|
|
1067
|
-
const common2 = {
|
|
1752
|
+
const input2 = readMlsDataRecord(value, op === "join" ? ["op", "credential", "accepted", "receiptProof", "feedOrdinal", "keyPackageId"] : ["op", "credential", "accepted", "receiptProof", "feedOrdinal"]);
|
|
1753
|
+
const common2 = {
|
|
1754
|
+
credential: token(input2.credential),
|
|
1755
|
+
accepted: parseMlsDevGroupAcceptedControl(input2.accepted),
|
|
1756
|
+
receiptProof: parseMlsDevFeedReceiptProof(input2.receiptProof),
|
|
1757
|
+
feedOrdinal: requireMlsInteger(input2.feedOrdinal, 1)
|
|
1758
|
+
};
|
|
1068
1759
|
if (op === "join") {
|
|
1069
1760
|
if (common2.accepted.control.kind !== "add")
|
|
1070
|
-
|
|
1761
|
+
invalid8();
|
|
1071
1762
|
return bounded5({ op, ...common2, keyPackageId: requireMlsUuid(input2.keyPackageId) });
|
|
1072
1763
|
}
|
|
1073
1764
|
return bounded5({ op, ...common2 });
|
|
1074
1765
|
}
|
|
1075
1766
|
const common = ["op", "operationId", "credential", "preconditions"];
|
|
1076
1767
|
const input = readMlsDataRecord(value, op === "create" ? common : op === "add" ? [...common, "expectedHead", "keyPackageClaim"] : op === "remove" ? [...common, "expectedHead", "removedDeviceId"] : []);
|
|
1077
|
-
const
|
|
1768
|
+
const fields2 = { operationId: id4(input.operationId), credential: token(input.credential), preconditions: parseMlsDevGroupPreconditions(input.preconditions) };
|
|
1078
1769
|
if (op === "create") {
|
|
1079
|
-
if (
|
|
1080
|
-
|
|
1081
|
-
return bounded5({ op, ...
|
|
1770
|
+
if (fields2.preconditions.targetAdmissionId !== null)
|
|
1771
|
+
invalid8();
|
|
1772
|
+
return bounded5({ op, ...fields2 });
|
|
1082
1773
|
}
|
|
1083
|
-
if (
|
|
1084
|
-
|
|
1774
|
+
if (fields2.preconditions.targetAdmissionId === null)
|
|
1775
|
+
invalid8();
|
|
1085
1776
|
const expectedHead = parseMlsDevGroupHead(input.expectedHead);
|
|
1086
1777
|
if (op === "add")
|
|
1087
1778
|
return bounded5({
|
|
1088
1779
|
op,
|
|
1089
|
-
...
|
|
1780
|
+
...fields2,
|
|
1090
1781
|
expectedHead,
|
|
1091
1782
|
keyPackageClaim: parseMlsDevKeyPackageClaimReceipt(input.keyPackageClaim)
|
|
1092
1783
|
});
|
|
1093
1784
|
if (op === "remove")
|
|
1094
1785
|
return bounded5({
|
|
1095
1786
|
op,
|
|
1096
|
-
...
|
|
1787
|
+
...fields2,
|
|
1097
1788
|
expectedHead,
|
|
1098
1789
|
removedDeviceId: requireMlsUuid(input.removedDeviceId)
|
|
1099
1790
|
});
|
|
1100
|
-
return
|
|
1791
|
+
return invalid8();
|
|
1101
1792
|
});
|
|
1102
1793
|
}
|
|
1103
1794
|
__name(parseMlsDevGroupCommand, "parseMlsDevGroupCommand");
|
|
1104
1795
|
function parseMlsDevGroupPreparation(value) {
|
|
1105
|
-
return
|
|
1796
|
+
return boundary7(() => {
|
|
1106
1797
|
const input = readMlsDataRecord(value, ["wrappedState", "payload"]);
|
|
1107
1798
|
if (typeof input.wrappedState !== "string" || input.wrappedState.length > MLS_DEV_GROUP_WRAPPED_MAX_BYTES || !input.wrappedState.startsWith("v1:"))
|
|
1108
|
-
|
|
1799
|
+
invalid8();
|
|
1109
1800
|
const body = input.wrappedState.slice(3);
|
|
1110
1801
|
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
|
-
|
|
1802
|
+
invalid8();
|
|
1112
1803
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1113
1804
|
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0 || !body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0)
|
|
1114
|
-
|
|
1805
|
+
invalid8();
|
|
1115
1806
|
const kind = Object.getOwnPropertyDescriptor(input.payload ?? {}, "kind");
|
|
1116
1807
|
if (!kind || !Object.hasOwn(kind, "value"))
|
|
1117
|
-
|
|
1808
|
+
invalid8();
|
|
1118
1809
|
let payload;
|
|
1119
1810
|
if (kind.value === "application") {
|
|
1120
|
-
const
|
|
1121
|
-
payload = Object.freeze({
|
|
1811
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "envelope", ...present(input.payload, ["payload"])]);
|
|
1812
|
+
payload = Object.freeze({
|
|
1813
|
+
kind: "application",
|
|
1814
|
+
envelope: parseMlsDevApplicationEnvelope(fields2.envelope),
|
|
1815
|
+
...fields2.payload === void 0 ? {} : { payload: plaintextJson(fields2.payload) }
|
|
1816
|
+
});
|
|
1817
|
+
} else if (kind.value === "item") {
|
|
1818
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "payload", "feedOrdinal"]);
|
|
1819
|
+
const ids = parseMlsDevTextPayload({
|
|
1820
|
+
version: 1,
|
|
1821
|
+
kind: "text",
|
|
1822
|
+
conversationId: "validation",
|
|
1823
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1824
|
+
messageId: fields2.messageId,
|
|
1825
|
+
operationId: fields2.operationId,
|
|
1826
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1827
|
+
text: "x"
|
|
1828
|
+
});
|
|
1829
|
+
payload = Object.freeze({
|
|
1830
|
+
kind: "item",
|
|
1831
|
+
messageId: ids.messageId,
|
|
1832
|
+
operationId: ids.operationId,
|
|
1833
|
+
senderDeviceId: ids.senderDeviceId,
|
|
1834
|
+
payload: plaintextJson(fields2.payload),
|
|
1835
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1836
|
+
});
|
|
1122
1837
|
} else if (kind.value === "text") {
|
|
1123
|
-
const
|
|
1124
|
-
const
|
|
1838
|
+
const extras = present(input.payload, ["replyTo", "mentions"]);
|
|
1839
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "text", "feedOrdinal", ...extras]);
|
|
1840
|
+
const text5 = parseMlsDevTextPayload({
|
|
1125
1841
|
version: 1,
|
|
1126
1842
|
kind: "text",
|
|
1127
1843
|
conversationId: "validation",
|
|
1128
1844
|
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1129
|
-
messageId:
|
|
1130
|
-
operationId:
|
|
1131
|
-
senderDeviceId:
|
|
1132
|
-
text:
|
|
1845
|
+
messageId: fields2.messageId,
|
|
1846
|
+
operationId: fields2.operationId,
|
|
1847
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1848
|
+
text: fields2.text,
|
|
1849
|
+
...pick(fields2, extras)
|
|
1133
1850
|
});
|
|
1134
1851
|
payload = Object.freeze({
|
|
1135
1852
|
kind: "text",
|
|
1136
|
-
messageId:
|
|
1137
|
-
operationId:
|
|
1138
|
-
senderDeviceId:
|
|
1139
|
-
text:
|
|
1140
|
-
|
|
1853
|
+
messageId: text5.messageId,
|
|
1854
|
+
operationId: text5.operationId,
|
|
1855
|
+
senderDeviceId: text5.senderDeviceId,
|
|
1856
|
+
text: text5.text,
|
|
1857
|
+
...textExtras(text5),
|
|
1858
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1859
|
+
});
|
|
1860
|
+
} else if (kind.value === "interaction_application") {
|
|
1861
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "envelope", "payload"]);
|
|
1862
|
+
payload = Object.freeze({ kind: "interaction_application", envelope: parseMlsDevApplicationEnvelope(fields2.envelope), payload: plaintextJson(fields2.payload) });
|
|
1863
|
+
if (!payload.envelope.interaction)
|
|
1864
|
+
invalid8();
|
|
1865
|
+
} else if (kind.value === "interaction") {
|
|
1866
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "payload", "feedOrdinal"]);
|
|
1867
|
+
const ids = parseMlsDevTextPayload({
|
|
1868
|
+
version: 1,
|
|
1869
|
+
kind: "text",
|
|
1870
|
+
conversationId: "validation",
|
|
1871
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1872
|
+
messageId: fields2.messageId,
|
|
1873
|
+
operationId: fields2.operationId,
|
|
1874
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1875
|
+
text: "x"
|
|
1876
|
+
});
|
|
1877
|
+
payload = Object.freeze({
|
|
1878
|
+
kind: "interaction",
|
|
1879
|
+
messageId: ids.messageId,
|
|
1880
|
+
operationId: ids.operationId,
|
|
1881
|
+
senderDeviceId: ids.senderDeviceId,
|
|
1882
|
+
payload: plaintextJson(fields2.payload),
|
|
1883
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1141
1884
|
});
|
|
1885
|
+
} else if (kind.value === "invalid") {
|
|
1886
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "messageId", "operationId", "senderDeviceId", "feedOrdinal"]);
|
|
1887
|
+
const ids = parseMlsDevTextPayload({
|
|
1888
|
+
version: 1,
|
|
1889
|
+
kind: "text",
|
|
1890
|
+
conversationId: "validation",
|
|
1891
|
+
groupId: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
1892
|
+
messageId: fields2.messageId,
|
|
1893
|
+
operationId: fields2.operationId,
|
|
1894
|
+
senderDeviceId: fields2.senderDeviceId,
|
|
1895
|
+
text: "x"
|
|
1896
|
+
});
|
|
1897
|
+
payload = Object.freeze({
|
|
1898
|
+
kind: "invalid",
|
|
1899
|
+
messageId: ids.messageId,
|
|
1900
|
+
operationId: ids.operationId,
|
|
1901
|
+
senderDeviceId: ids.senderDeviceId,
|
|
1902
|
+
feedOrdinal: requireMlsInteger(fields2.feedOrdinal, 1)
|
|
1903
|
+
});
|
|
1904
|
+
} else if (kind.value === "secret") {
|
|
1905
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "requestId", "text"]);
|
|
1906
|
+
const binding3 = parseMlsDevInteractionBinding({ role: "withdraw", requestId: fields2.requestId });
|
|
1907
|
+
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)
|
|
1908
|
+
invalid8();
|
|
1909
|
+
payload = Object.freeze({ kind: "secret", requestId: binding3.requestId, text: fields2.text });
|
|
1142
1910
|
} else if (kind.value === "control") {
|
|
1143
|
-
const
|
|
1144
|
-
payload = Object.freeze({ kind: "control", control: parseMlsDevGroupControl(
|
|
1911
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "control"]);
|
|
1912
|
+
payload = Object.freeze({ kind: "control", control: parseMlsDevGroupControl(fields2.control) });
|
|
1145
1913
|
} else if (kind.value === "discarded") {
|
|
1146
|
-
const
|
|
1147
|
-
payload = Object.freeze({ kind: "discarded", operationId:
|
|
1914
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "operationId", "head"]);
|
|
1915
|
+
payload = Object.freeze({ kind: "discarded", operationId: id4(fields2.operationId), head: fields2.head === null ? null : parseMlsDevGroupHead(fields2.head) });
|
|
1148
1916
|
} else {
|
|
1149
|
-
const
|
|
1917
|
+
const fields2 = readMlsDataRecord(input.payload, ["kind", "head", "roster", "removed", "consumedKeyPackageId"]);
|
|
1150
1918
|
payload = Object.freeze({
|
|
1151
|
-
kind: requireMlsLiteral(
|
|
1152
|
-
head: parseMlsDevGroupHead(
|
|
1153
|
-
roster: parseMlsDevGroupRoster(
|
|
1154
|
-
removed:
|
|
1155
|
-
consumedKeyPackageId:
|
|
1919
|
+
kind: requireMlsLiteral(fields2.kind, "applied"),
|
|
1920
|
+
head: parseMlsDevGroupHead(fields2.head),
|
|
1921
|
+
roster: parseMlsDevGroupRoster(fields2.roster),
|
|
1922
|
+
removed: boolean3(fields2.removed),
|
|
1923
|
+
consumedKeyPackageId: fields2.consumedKeyPackageId === null ? null : requireMlsUuid(fields2.consumedKeyPackageId)
|
|
1156
1924
|
});
|
|
1157
1925
|
}
|
|
1158
1926
|
return bounded5({ wrappedState: input.wrappedState, payload }, MLS_DEV_GROUP_PREPARATION_MAX_BYTES);
|
|
@@ -1162,9 +1930,25 @@ __name(parseMlsDevGroupPreparation, "parseMlsDevGroupPreparation");
|
|
|
1162
1930
|
|
|
1163
1931
|
// ../backend-contracts/src/mlsDevApplication.ts
|
|
1164
1932
|
var MLS_DEV_TEXT_PROTOCOL = "canon.mls.dev-text.v1";
|
|
1933
|
+
var MLS_DEV_TEXT_PROTOCOL_V2 = "canon.mls.dev-text.v2";
|
|
1934
|
+
var MLS_DEV_TEXT_PROTOCOL_V3 = "canon.mls.dev-text.v3";
|
|
1935
|
+
var MLS_DEV_TEXT_PROTOCOLS = [MLS_DEV_TEXT_PROTOCOL, MLS_DEV_TEXT_PROTOCOL_V2, MLS_DEV_TEXT_PROTOCOL_V3];
|
|
1936
|
+
function isMlsDevTextProtocol(value) {
|
|
1937
|
+
return MLS_DEV_TEXT_PROTOCOLS.some((protocol) => protocol === value);
|
|
1938
|
+
}
|
|
1939
|
+
__name(isMlsDevTextProtocol, "isMlsDevTextProtocol");
|
|
1940
|
+
function mlsDevProtocolCarriesCards(value) {
|
|
1941
|
+
return value === MLS_DEV_TEXT_PROTOCOL_V2 || value === MLS_DEV_TEXT_PROTOCOL_V3;
|
|
1942
|
+
}
|
|
1943
|
+
__name(mlsDevProtocolCarriesCards, "mlsDevProtocolCarriesCards");
|
|
1944
|
+
var MLS_DEV_REPLY_QUOTE_MAX_CHARACTERS = 1024;
|
|
1945
|
+
var MLS_DEV_MENTIONS_MAX = 32;
|
|
1946
|
+
var MLS_DEV_INTERACTION_ID_PATTERN = /^[A-Za-z0-9_.:-]{1,160}$/;
|
|
1947
|
+
var MLS_DEV_INTERACTION_KINDS = ["approval", "question", "secret"];
|
|
1165
1948
|
var MLS_DEV_APPLICATION_TLS_MAX_BYTES = 256 * 1024;
|
|
1166
1949
|
var MLS_DEV_APPLICATION_MAX_BYTES = 384 * 1024;
|
|
1167
1950
|
var MLS_DEV_TEXT_MAX_BYTES = 32 * 1024;
|
|
1951
|
+
var MLS_DEV_ITEM_TYPES = ["image", "video", "audio", "file"];
|
|
1168
1952
|
var MlsDevApplicationValidationError = class extends Error {
|
|
1169
1953
|
static {
|
|
1170
1954
|
__name(this, "MlsDevApplicationValidationError");
|
|
@@ -1174,125 +1958,245 @@ var MlsDevApplicationValidationError = class extends Error {
|
|
|
1174
1958
|
super("INVALID_MLS_DEV_APPLICATION");
|
|
1175
1959
|
this.name = "MlsDevApplicationValidationError";
|
|
1176
1960
|
}
|
|
1177
|
-
};
|
|
1178
|
-
var
|
|
1179
|
-
function
|
|
1961
|
+
};
|
|
1962
|
+
var utf85 = new TextEncoder();
|
|
1963
|
+
function invalid9() {
|
|
1180
1964
|
throw new MlsDevApplicationValidationError();
|
|
1181
1965
|
}
|
|
1182
|
-
__name(
|
|
1183
|
-
function
|
|
1966
|
+
__name(invalid9, "invalid");
|
|
1967
|
+
function boundary8(read) {
|
|
1184
1968
|
try {
|
|
1185
1969
|
return read();
|
|
1186
1970
|
} catch {
|
|
1187
|
-
return
|
|
1971
|
+
return invalid9();
|
|
1188
1972
|
}
|
|
1189
1973
|
}
|
|
1190
|
-
__name(
|
|
1974
|
+
__name(boundary8, "boundary");
|
|
1191
1975
|
function bounded6(value) {
|
|
1192
|
-
if (
|
|
1193
|
-
|
|
1976
|
+
if (utf85.encode(JSON.stringify(value)).length > MLS_DEV_APPLICATION_MAX_BYTES)
|
|
1977
|
+
invalid9();
|
|
1194
1978
|
return Object.freeze(value);
|
|
1195
1979
|
}
|
|
1196
1980
|
__name(bounded6, "bounded");
|
|
1197
|
-
function
|
|
1981
|
+
function id5(value) {
|
|
1198
1982
|
if (typeof value !== "string" || !/^[A-Za-z0-9_.:~-]{1,200}$/.test(value) || value === "." || value === "..")
|
|
1199
|
-
|
|
1983
|
+
invalid9();
|
|
1200
1984
|
return value;
|
|
1201
1985
|
}
|
|
1202
|
-
__name(
|
|
1986
|
+
__name(id5, "id");
|
|
1987
|
+
function isMlsDevMessageId(value) {
|
|
1988
|
+
return typeof value === "string" && /^[A-Za-z0-9_.:~-]{1,200}$/.test(value) && value !== "." && value !== ".." && !value.startsWith("membership-");
|
|
1989
|
+
}
|
|
1990
|
+
__name(isMlsDevMessageId, "isMlsDevMessageId");
|
|
1203
1991
|
function messageId(value) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
return result;
|
|
1992
|
+
if (!isMlsDevMessageId(value))
|
|
1993
|
+
invalid9();
|
|
1994
|
+
return value;
|
|
1208
1995
|
}
|
|
1209
1996
|
__name(messageId, "messageId");
|
|
1210
|
-
function
|
|
1997
|
+
function interactionId2(value) {
|
|
1998
|
+
if (typeof value !== "string" || !MLS_DEV_INTERACTION_ID_PATTERN.test(value) || value === "." || value === "..")
|
|
1999
|
+
invalid9();
|
|
2000
|
+
return value;
|
|
2001
|
+
}
|
|
2002
|
+
__name(interactionId2, "interactionId");
|
|
2003
|
+
function parseMlsDevInteractionBinding(value) {
|
|
2004
|
+
return boundary8(() => {
|
|
2005
|
+
const role = value && typeof value === "object" ? Object.getOwnPropertyDescriptor(value, "role") : void 0;
|
|
2006
|
+
if (!role || !Object.hasOwn(role, "value"))
|
|
2007
|
+
invalid9();
|
|
2008
|
+
if (role.value === "request") {
|
|
2009
|
+
const input2 = readMlsDataRecord(value, ["role", "requestId", "kind", "responderIds", "expiresAtMs"]);
|
|
2010
|
+
if (typeof input2.kind !== "string" || !MLS_DEV_INTERACTION_KINDS.includes(input2.kind))
|
|
2011
|
+
invalid9();
|
|
2012
|
+
const responders = input2.responderIds;
|
|
2013
|
+
if (!Array.isArray(responders) || Object.getPrototypeOf(responders) !== Array.prototype || responders.length !== 1 || Reflect.ownKeys(Object.getOwnPropertyDescriptors(responders)).length !== 2)
|
|
2014
|
+
invalid9();
|
|
2015
|
+
const only = Object.getOwnPropertyDescriptor(responders, 0);
|
|
2016
|
+
if (!only || !only.enumerable || !Object.hasOwn(only, "value"))
|
|
2017
|
+
invalid9();
|
|
2018
|
+
const expiresAtMs = requireMlsInteger(input2.expiresAtMs, 1);
|
|
2019
|
+
return Object.freeze({
|
|
2020
|
+
role: "request",
|
|
2021
|
+
requestId: interactionId2(input2.requestId),
|
|
2022
|
+
kind: input2.kind,
|
|
2023
|
+
responderIds: Object.freeze([id5(only.value)]),
|
|
2024
|
+
expiresAtMs
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
if (role.value === "response") {
|
|
2028
|
+
const input2 = readMlsDataRecord(value, ["role", "requestId", "agentId"]);
|
|
2029
|
+
return Object.freeze({ role: "response", requestId: interactionId2(input2.requestId), agentId: id5(input2.agentId) });
|
|
2030
|
+
}
|
|
2031
|
+
const input = readMlsDataRecord(value, ["role", "requestId"]);
|
|
2032
|
+
return Object.freeze({ role: requireMlsLiteral(input.role, "withdraw"), requestId: interactionId2(input.requestId) });
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
__name(parseMlsDevInteractionBinding, "parseMlsDevInteractionBinding");
|
|
2036
|
+
function encoded5(value, bytes) {
|
|
1211
2037
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
1212
2038
|
return value;
|
|
1213
2039
|
}
|
|
1214
|
-
__name(
|
|
2040
|
+
__name(encoded5, "encoded");
|
|
1215
2041
|
function parseMlsDevApplicationWire(value) {
|
|
1216
|
-
return
|
|
2042
|
+
return boundary8(() => {
|
|
1217
2043
|
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_APPLICATION_TLS_MAX_BYTES * 4 / 3))
|
|
1218
|
-
|
|
2044
|
+
invalid9();
|
|
1219
2045
|
const bytes = Math.floor(value.length * 3 / 4);
|
|
1220
2046
|
if (bytes > MLS_DEV_APPLICATION_TLS_MAX_BYTES || value.length !== Math.ceil(bytes * 4 / 3) || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
1221
|
-
|
|
2047
|
+
invalid9();
|
|
1222
2048
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
1223
2049
|
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
1224
|
-
|
|
2050
|
+
invalid9();
|
|
1225
2051
|
return value;
|
|
1226
2052
|
});
|
|
1227
2053
|
}
|
|
1228
2054
|
__name(parseMlsDevApplicationWire, "parseMlsDevApplicationWire");
|
|
1229
2055
|
function parseMlsDevApplicationPreconditions(value) {
|
|
1230
|
-
return
|
|
2056
|
+
return boundary8(() => {
|
|
1231
2057
|
const input = readMlsDataRecord(value, ["membershipRevision", "admissionId"]);
|
|
1232
|
-
return Object.freeze({ membershipRevision: requireMlsInteger(input.membershipRevision), admissionId:
|
|
2058
|
+
return Object.freeze({ membershipRevision: requireMlsInteger(input.membershipRevision), admissionId: id5(input.admissionId) });
|
|
1233
2059
|
});
|
|
1234
2060
|
}
|
|
1235
2061
|
__name(parseMlsDevApplicationPreconditions, "parseMlsDevApplicationPreconditions");
|
|
1236
2062
|
var bindingFields = ["version", "environmentId", "conversationId", "groupId", "messageId", "senderCredential", "expectedHead", "preconditions"];
|
|
2063
|
+
var interactionBindingFields = [...bindingFields, "interaction"];
|
|
1237
2064
|
function binding(input) {
|
|
1238
|
-
|
|
2065
|
+
const environmentId = requireMlsEnvironmentId(input.environmentId);
|
|
2066
|
+
if (parseMlsDevCredentialToken(input.senderCredential).unsigned.claims.device.environmentId !== environmentId) invalid9();
|
|
1239
2067
|
return {
|
|
1240
2068
|
version: requireMlsLiteral(input.version, 1),
|
|
1241
|
-
environmentId
|
|
1242
|
-
conversationId:
|
|
1243
|
-
groupId:
|
|
2069
|
+
environmentId,
|
|
2070
|
+
conversationId: id5(input.conversationId),
|
|
2071
|
+
groupId: encoded5(input.groupId, 32),
|
|
1244
2072
|
messageId: messageId(input.messageId),
|
|
1245
2073
|
senderCredential: input.senderCredential,
|
|
1246
2074
|
expectedHead: parseMlsDevGroupHead(input.expectedHead),
|
|
1247
|
-
preconditions: parseMlsDevApplicationPreconditions(input.preconditions)
|
|
2075
|
+
preconditions: parseMlsDevApplicationPreconditions(input.preconditions),
|
|
2076
|
+
...Object.hasOwn(input, "interaction") ? { interaction: parseMlsDevInteractionBinding(input.interaction) } : {}
|
|
1248
2077
|
};
|
|
1249
2078
|
}
|
|
1250
2079
|
__name(binding, "binding");
|
|
2080
|
+
function fields(value, extra) {
|
|
2081
|
+
return readMlsDataRecord(value, [...bindingFields, ...extra], [...interactionBindingFields, ...extra]);
|
|
2082
|
+
}
|
|
2083
|
+
__name(fields, "fields");
|
|
1251
2084
|
function parseMlsDevApplicationEnvelope(value) {
|
|
1252
|
-
return
|
|
1253
|
-
const input =
|
|
1254
|
-
return bounded6({ ...binding(input), ciphertext: parseMlsDevApplicationWire(input.ciphertext), proof:
|
|
2085
|
+
return boundary8(() => {
|
|
2086
|
+
const input = fields(value, ["ciphertext", "proof"]);
|
|
2087
|
+
return bounded6({ ...binding(input), ciphertext: parseMlsDevApplicationWire(input.ciphertext), proof: encoded5(input.proof, 64) });
|
|
1255
2088
|
});
|
|
1256
2089
|
}
|
|
1257
2090
|
__name(parseMlsDevApplicationEnvelope, "parseMlsDevApplicationEnvelope");
|
|
1258
2091
|
function parseMlsDevApplicationReceipt(value) {
|
|
1259
|
-
return
|
|
2092
|
+
return boundary8(() => {
|
|
1260
2093
|
const input = readMlsDataRecord(value, ["version", "authorization", "operationId", "conversationId", "groupId", "messageId", "feedOrdinal", "head", "acceptedAtMs", "envelopeHash"]);
|
|
1261
2094
|
return bounded6({
|
|
1262
2095
|
version: requireMlsLiteral(input.version, 1),
|
|
1263
2096
|
authorization: requireMlsLiteral(input.authorization, "encrypted_text_only"),
|
|
1264
|
-
operationId:
|
|
1265
|
-
conversationId:
|
|
1266
|
-
groupId:
|
|
2097
|
+
operationId: id5(input.operationId),
|
|
2098
|
+
conversationId: id5(input.conversationId),
|
|
2099
|
+
groupId: encoded5(input.groupId, 32),
|
|
1267
2100
|
messageId: messageId(input.messageId),
|
|
1268
2101
|
feedOrdinal: requireMlsInteger(input.feedOrdinal, 1),
|
|
1269
2102
|
head: parseMlsDevGroupHead(input.head),
|
|
1270
2103
|
acceptedAtMs: requireMlsInteger(input.acceptedAtMs),
|
|
1271
|
-
envelopeHash:
|
|
2104
|
+
envelopeHash: encoded5(input.envelopeHash, 32)
|
|
1272
2105
|
});
|
|
1273
2106
|
});
|
|
1274
2107
|
}
|
|
1275
2108
|
__name(parseMlsDevApplicationReceipt, "parseMlsDevApplicationReceipt");
|
|
2109
|
+
function text2(value) {
|
|
2110
|
+
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)
|
|
2111
|
+
invalid9();
|
|
2112
|
+
return value;
|
|
2113
|
+
}
|
|
2114
|
+
__name(text2, "text");
|
|
2115
|
+
function textExtras2(value) {
|
|
2116
|
+
const extras = {};
|
|
2117
|
+
if (Object.hasOwn(value, "replyTo")) {
|
|
2118
|
+
const reply = readMlsDataRecord(value.replyTo, ["messageId", "quote"]);
|
|
2119
|
+
if ([...text2(reply.quote)].length > MLS_DEV_REPLY_QUOTE_MAX_CHARACTERS)
|
|
2120
|
+
invalid9();
|
|
2121
|
+
extras.replyTo = Object.freeze({ messageId: messageId(reply.messageId), quote: reply.quote });
|
|
2122
|
+
}
|
|
2123
|
+
if (Object.hasOwn(value, "mentions")) {
|
|
2124
|
+
const mentions = value.mentions;
|
|
2125
|
+
if (!Array.isArray(mentions) || mentions.length < 1 || mentions.length > MLS_DEV_MENTIONS_MAX || new Set(mentions).size !== mentions.length)
|
|
2126
|
+
invalid9();
|
|
2127
|
+
extras.mentions = Object.freeze(mentions.map(id5));
|
|
2128
|
+
}
|
|
2129
|
+
return extras;
|
|
2130
|
+
}
|
|
2131
|
+
__name(textExtras2, "textExtras");
|
|
2132
|
+
var optionalKeys = /* @__PURE__ */ __name((value, keys2) => value && typeof value === "object" ? keys2.filter((key) => Object.hasOwn(value, key)) : [], "optionalKeys");
|
|
1276
2133
|
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();
|
|
2134
|
+
return boundary8(() => {
|
|
2135
|
+
const input = readMlsDataRecord(value, ["version", "kind", "conversationId", "groupId", "messageId", "operationId", "senderDeviceId", "text", ...optionalKeys(value, ["replyTo", "mentions"])]);
|
|
1281
2136
|
return bounded6({
|
|
1282
2137
|
version: requireMlsLiteral(input.version, 1),
|
|
1283
2138
|
kind: requireMlsLiteral(input.kind, "text"),
|
|
1284
|
-
conversationId:
|
|
1285
|
-
groupId:
|
|
2139
|
+
conversationId: id5(input.conversationId),
|
|
2140
|
+
groupId: encoded5(input.groupId, 32),
|
|
1286
2141
|
messageId: messageId(input.messageId),
|
|
1287
|
-
operationId:
|
|
2142
|
+
operationId: id5(input.operationId),
|
|
1288
2143
|
senderDeviceId: requireMlsUuid(input.senderDeviceId),
|
|
1289
|
-
text: input.text
|
|
2144
|
+
text: text2(input.text),
|
|
2145
|
+
...textExtras2(input)
|
|
1290
2146
|
});
|
|
1291
2147
|
});
|
|
1292
2148
|
}
|
|
1293
2149
|
__name(parseMlsDevTextPayload, "parseMlsDevTextPayload");
|
|
1294
|
-
|
|
1295
|
-
|
|
2150
|
+
var itemFields = ["type", "uploadId", "url", "mimeType", "sizeBytes", "fileName", "width", "height", "durationMs", "thumbnailUrl", "caption", "replyTo", "mentions"];
|
|
2151
|
+
function httpsUrl(value) {
|
|
2152
|
+
if (typeof value !== "string" || value.length > 2048) invalid9();
|
|
2153
|
+
const url = boundary8(() => new URL(value));
|
|
2154
|
+
if (url.protocol !== "https:" || url.username || url.password) invalid9();
|
|
2155
|
+
return value;
|
|
2156
|
+
}
|
|
2157
|
+
__name(httpsUrl, "httpsUrl");
|
|
2158
|
+
var dimension = /* @__PURE__ */ __name((value) => requireMlsInteger(value, 0) <= 1e5 ? value : invalid9(), "dimension");
|
|
2159
|
+
function item(value) {
|
|
2160
|
+
const input = readMlsDataRecord(value, ["type", "uploadId", "url", "mimeType", "sizeBytes", ...optionalKeys(value, itemFields.slice(5))]);
|
|
2161
|
+
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))
|
|
2162
|
+
invalid9();
|
|
2163
|
+
if (input.fileName !== void 0 && (typeof input.fileName !== "string" || !input.fileName.trim() || [...input.fileName].length > 240))
|
|
2164
|
+
invalid9();
|
|
2165
|
+
return Object.freeze({
|
|
2166
|
+
type: input.type,
|
|
2167
|
+
uploadId: requireMlsUuid(input.uploadId),
|
|
2168
|
+
url: httpsUrl(input.url),
|
|
2169
|
+
mimeType: input.mimeType,
|
|
2170
|
+
sizeBytes: requireMlsInteger(input.sizeBytes, 1),
|
|
2171
|
+
...input.fileName === void 0 ? {} : { fileName: input.fileName },
|
|
2172
|
+
...Object.fromEntries(["width", "height", "durationMs"].filter((key) => input[key] !== void 0).map((key) => [key, key === "durationMs" ? requireMlsInteger(input[key], 0) : dimension(input[key])])),
|
|
2173
|
+
...input.thumbnailUrl === void 0 ? {} : { thumbnailUrl: httpsUrl(input.thumbnailUrl) },
|
|
2174
|
+
...input.caption === void 0 ? {} : { caption: text2(input.caption) },
|
|
2175
|
+
...textExtras2(input)
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
__name(item, "item");
|
|
2179
|
+
function parseMlsDevItemPayload(value) {
|
|
2180
|
+
return boundary8(() => {
|
|
2181
|
+
const input = readMlsDataRecord(value, ["version", "kind", "conversationId", "groupId", "messageId", "operationId", "senderDeviceId", "item"]);
|
|
2182
|
+
const payload = {
|
|
2183
|
+
version: requireMlsLiteral(input.version, 1),
|
|
2184
|
+
kind: requireMlsLiteral(input.kind, "item"),
|
|
2185
|
+
conversationId: id5(input.conversationId),
|
|
2186
|
+
groupId: encoded5(input.groupId, 32),
|
|
2187
|
+
messageId: messageId(input.messageId),
|
|
2188
|
+
operationId: id5(input.operationId),
|
|
2189
|
+
senderDeviceId: requireMlsUuid(input.senderDeviceId),
|
|
2190
|
+
item: item(input.item)
|
|
2191
|
+
};
|
|
2192
|
+
if (utf85.encode(JSON.stringify(payload)).length > MLS_DEV_TEXT_MAX_BYTES)
|
|
2193
|
+
invalid9();
|
|
2194
|
+
return bounded6(payload);
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
__name(parseMlsDevItemPayload, "parseMlsDevItemPayload");
|
|
2198
|
+
function framed(domain4, value, operationId) {
|
|
2199
|
+
const operation = utf85.encode(id5(operationId)), json = utf85.encode(JSON.stringify(value)), prefix = utf85.encode(domain4);
|
|
1296
2200
|
const result = new Uint8Array(prefix.length + 8 + operation.length + json.length), view = new DataView(result.buffer);
|
|
1297
2201
|
let offset = 0;
|
|
1298
2202
|
result.set(prefix, offset);
|
|
@@ -1308,7 +2212,7 @@ function framed(domain3, value, operationId) {
|
|
|
1308
2212
|
}
|
|
1309
2213
|
__name(framed, "framed");
|
|
1310
2214
|
function encodeMlsDevApplicationEnvelopeHash(value, operationId) {
|
|
1311
|
-
return
|
|
2215
|
+
return boundary8(() => framed("canon.mls.dev-application-envelope.v1\0", parseMlsDevApplicationEnvelope(value), operationId));
|
|
1312
2216
|
}
|
|
1313
2217
|
__name(encodeMlsDevApplicationEnvelopeHash, "encodeMlsDevApplicationEnvelopeHash");
|
|
1314
2218
|
|
|
@@ -1329,12 +2233,6 @@ var RUNTIME_CARD_FIELD_TYPES = [
|
|
|
1329
2233
|
"searchSelect",
|
|
1330
2234
|
"lineItems"
|
|
1331
2235
|
];
|
|
1332
|
-
var RUNTIME_CARD_LIMITS = {
|
|
1333
|
-
detailsRows: 32,
|
|
1334
|
-
lineItemRows: 50,
|
|
1335
|
-
lineItemColumns: 8,
|
|
1336
|
-
searchSelectChoices: 100
|
|
1337
|
-
};
|
|
1338
2236
|
var RUNTIME_CARD_LINE_ITEM_COLUMN_TYPES = [
|
|
1339
2237
|
"text",
|
|
1340
2238
|
"number",
|
|
@@ -1422,9 +2320,7 @@ var VERB_LIMITS = {
|
|
|
1422
2320
|
externalSourceLabelChars: 80,
|
|
1423
2321
|
/**
|
|
1424
2322
|
* Contact-request note. Values longer than this are truncated by senders
|
|
1425
|
-
* (slice(0,497)+'...')
|
|
1426
|
-
* functions/src/api/sendContextualMessage.ts, packages/core/src/reach-out.ts
|
|
1427
|
-
* and the hermes plugin; this constant is the canonical figure.
|
|
2323
|
+
* (slice(0,497)+'...'); this constant is the canonical figure.
|
|
1428
2324
|
*/
|
|
1429
2325
|
contactRequestNoteChars: 500,
|
|
1430
2326
|
/** Group membership cap incl. creator (functions/src/utils/conversations.ts MAX_GROUP_MEMBERS). */
|
|
@@ -1526,7 +2422,7 @@ var VERB_NATIVE_METADATA_KEYS = [
|
|
|
1526
2422
|
"handles"
|
|
1527
2423
|
];
|
|
1528
2424
|
var VERB_NATIVE_STRING_KEYS = VERB_NATIVE_METADATA_KEYS.filter(
|
|
1529
|
-
(
|
|
2425
|
+
(key) => key !== "handles"
|
|
1530
2426
|
);
|
|
1531
2427
|
var RUNTIME_CORRELATION_VALUE_PATTERN = new RegExp(
|
|
1532
2428
|
VERB_ID_PATTERNS.runtimeCorrelationValue
|
|
@@ -1728,7 +2624,7 @@ var sessionRuleDef = {
|
|
|
1728
2624
|
};
|
|
1729
2625
|
var runtimeCardDef = {
|
|
1730
2626
|
type: "object",
|
|
1731
|
-
description: `ENVELOPE-ONLY validation of a canon.card.v1 document: block structures are NOT checked here.
|
|
2627
|
+
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
2628
|
required: ["schema", "title", "fallbackText", "blocks"],
|
|
1733
2629
|
additionalProperties: true,
|
|
1734
2630
|
properties: {
|
|
@@ -1845,7 +2741,7 @@ var send_to_input = {
|
|
|
1845
2741
|
]
|
|
1846
2742
|
};
|
|
1847
2743
|
var send_to_result = {
|
|
1848
|
-
description: "Canonical outcome vocabulary.
|
|
2744
|
+
description: "Canonical outcome vocabulary.",
|
|
1849
2745
|
oneOf: [
|
|
1850
2746
|
{
|
|
1851
2747
|
type: "object",
|
|
@@ -1861,6 +2757,20 @@ var send_to_result = {
|
|
|
1861
2757
|
sessionSelection: { type: "string" }
|
|
1862
2758
|
}
|
|
1863
2759
|
},
|
|
2760
|
+
{
|
|
2761
|
+
type: "object",
|
|
2762
|
+
required: ["status", "conversationId", "messageId", "openingText"],
|
|
2763
|
+
additionalProperties: true,
|
|
2764
|
+
description: "The room is encrypted, so Canon stored no opening text: the sender sends it encrypted under messageId.",
|
|
2765
|
+
properties: {
|
|
2766
|
+
status: { const: "opened" },
|
|
2767
|
+
conversationId: { type: "string" },
|
|
2768
|
+
messageId: { type: "string" },
|
|
2769
|
+
openingText: { const: "deferred" },
|
|
2770
|
+
created: { type: "boolean" },
|
|
2771
|
+
reused: { type: "boolean" }
|
|
2772
|
+
}
|
|
2773
|
+
},
|
|
1864
2774
|
{
|
|
1865
2775
|
type: "object",
|
|
1866
2776
|
required: ["status", "requestId"],
|
|
@@ -1871,6 +2781,12 @@ var send_to_result = {
|
|
|
1871
2781
|
deferredIntentId: {
|
|
1872
2782
|
type: ["string", "null"],
|
|
1873
2783
|
description: "Present when the send is deferred until the contact request resolves."
|
|
2784
|
+
},
|
|
2785
|
+
conversationId: { type: "string" },
|
|
2786
|
+
messageId: { type: "string" },
|
|
2787
|
+
openingText: {
|
|
2788
|
+
const: "deferred",
|
|
2789
|
+
description: "The room is encrypted, so Canon stored no opening text: the sender sends it encrypted under messageId."
|
|
1874
2790
|
}
|
|
1875
2791
|
}
|
|
1876
2792
|
},
|
|
@@ -1910,7 +2826,7 @@ var request_input_input = {
|
|
|
1910
2826
|
responseUserId: {
|
|
1911
2827
|
type: "string",
|
|
1912
2828
|
pattern: VERB_ID_PATTERNS.runtimeId,
|
|
1913
|
-
description: "Must be a human conversation member. Trust-critical bindings
|
|
2829
|
+
description: "Must be a human conversation member. Trust-critical bindings derive this from turn provenance and ignore model-supplied values."
|
|
1914
2830
|
},
|
|
1915
2831
|
native: REF("native"),
|
|
1916
2832
|
turnId: { type: "string", maxLength: VERB_LIMITS.turnIdChars },
|
|
@@ -2755,9 +3671,9 @@ function splitTurnMetadata(metadata) {
|
|
|
2755
3671
|
if (!metadata || typeof metadata !== "object") return { turn: void 0, rest: void 0 };
|
|
2756
3672
|
const turn = {};
|
|
2757
3673
|
const rest = {};
|
|
2758
|
-
for (const [
|
|
2759
|
-
if (TURN_KEYS.includes(
|
|
2760
|
-
else rest[
|
|
3674
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
3675
|
+
if (TURN_KEYS.includes(key)) turn[key] = value;
|
|
3676
|
+
else rest[key] = value;
|
|
2761
3677
|
}
|
|
2762
3678
|
return {
|
|
2763
3679
|
turn: Object.keys(turn).length ? turn : void 0,
|
|
@@ -2765,10 +3681,10 @@ function splitTurnMetadata(metadata) {
|
|
|
2765
3681
|
};
|
|
2766
3682
|
}
|
|
2767
3683
|
__name(splitTurnMetadata, "splitTurnMetadata");
|
|
2768
|
-
function compact(
|
|
3684
|
+
function compact(record5) {
|
|
2769
3685
|
const out = {};
|
|
2770
|
-
for (const [
|
|
2771
|
-
if (value !== void 0) out[
|
|
3686
|
+
for (const [key, value] of Object.entries(record5)) {
|
|
3687
|
+
if (value !== void 0) out[key] = value;
|
|
2772
3688
|
}
|
|
2773
3689
|
return out;
|
|
2774
3690
|
}
|
|
@@ -2948,12 +3864,12 @@ function parseDirectConversationSelection(value) {
|
|
|
2948
3864
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2949
3865
|
throw new Error("selection must be an object");
|
|
2950
3866
|
}
|
|
2951
|
-
const
|
|
2952
|
-
if (
|
|
2953
|
-
return { mode:
|
|
3867
|
+
const record5 = value;
|
|
3868
|
+
if (record5.mode === "latest_or_new" || record5.mode === "new") {
|
|
3869
|
+
return { mode: record5.mode };
|
|
2954
3870
|
}
|
|
2955
|
-
if (
|
|
2956
|
-
const conversationId = typeof
|
|
3871
|
+
if (record5.mode === "specific") {
|
|
3872
|
+
const conversationId = typeof record5.conversationId === "string" ? record5.conversationId.trim() : "";
|
|
2957
3873
|
if (!conversationId) throw new Error("selection.conversationId is required");
|
|
2958
3874
|
return { mode: "specific", conversationId };
|
|
2959
3875
|
}
|
|
@@ -2978,10 +3894,10 @@ function record(value) {
|
|
|
2978
3894
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2979
3895
|
}
|
|
2980
3896
|
__name(record, "record");
|
|
2981
|
-
function
|
|
3897
|
+
function text3(value, max) {
|
|
2982
3898
|
return typeof value === "string" && value.length > 0 && value.length <= max ? value : void 0;
|
|
2983
3899
|
}
|
|
2984
|
-
__name(
|
|
3900
|
+
__name(text3, "text");
|
|
2985
3901
|
function member(value, choices) {
|
|
2986
3902
|
return choices.includes(value) ? value : void 0;
|
|
2987
3903
|
}
|
|
@@ -3016,19 +3932,19 @@ function normalizeDispatch(value) {
|
|
|
3016
3932
|
return primitive ? { kind: "primitive", primitive } : void 0;
|
|
3017
3933
|
}
|
|
3018
3934
|
case "control": {
|
|
3019
|
-
const controlId =
|
|
3020
|
-
return controlId ? defined({ kind: "control", controlId, value:
|
|
3935
|
+
const controlId = text3(dispatch.controlId, 128);
|
|
3936
|
+
return controlId ? defined({ kind: "control", controlId, value: text3(dispatch.value, 1024) }) : void 0;
|
|
3021
3937
|
}
|
|
3022
3938
|
case "text_passthrough": {
|
|
3023
|
-
const template =
|
|
3939
|
+
const template = text3(dispatch.template, 2048);
|
|
3024
3940
|
return template ? { kind: "text_passthrough", template } : void 0;
|
|
3025
3941
|
}
|
|
3026
3942
|
case "compose": {
|
|
3027
|
-
const content =
|
|
3943
|
+
const content = text3(dispatch.text, 2048);
|
|
3028
3944
|
return content ? { kind: "compose", text: content } : void 0;
|
|
3029
3945
|
}
|
|
3030
3946
|
case "open_details":
|
|
3031
|
-
return defined({ kind: "open_details", target:
|
|
3947
|
+
return defined({ kind: "open_details", target: text3(dispatch.target, 128) });
|
|
3032
3948
|
default:
|
|
3033
3949
|
return void 0;
|
|
3034
3950
|
}
|
|
@@ -3036,21 +3952,21 @@ function normalizeDispatch(value) {
|
|
|
3036
3952
|
__name(normalizeDispatch, "normalizeDispatch");
|
|
3037
3953
|
function normalizeArgument(value) {
|
|
3038
3954
|
const arg = record(value);
|
|
3039
|
-
const
|
|
3040
|
-
const label =
|
|
3955
|
+
const id7 = text3(arg.id, 128);
|
|
3956
|
+
const label = text3(arg.label, 160);
|
|
3041
3957
|
const kind = member(arg.kind, ["string", "enum", "boolean"]);
|
|
3042
|
-
if (!
|
|
3043
|
-
if (["required", "captureRemaining"].some((
|
|
3958
|
+
if (!id7 || !label || !kind) return void 0;
|
|
3959
|
+
if (["required", "captureRemaining"].some((key) => arg[key] !== void 0 && typeof arg[key] !== "boolean")) return void 0;
|
|
3044
3960
|
if (arg.choices !== void 0 && !Array.isArray(arg.choices)) return void 0;
|
|
3045
3961
|
const choices = Array.isArray(arg.choices) ? arg.choices.slice(0, 128).flatMap((value2) => {
|
|
3046
3962
|
const choice = record(value2);
|
|
3047
|
-
const entry =
|
|
3048
|
-
const label2 =
|
|
3049
|
-
return entry && label2 ? [defined({ value: entry, label: label2, description:
|
|
3963
|
+
const entry = text3(choice.value, 1024);
|
|
3964
|
+
const label2 = text3(choice.label, 160);
|
|
3965
|
+
return entry && label2 ? [defined({ value: entry, label: label2, description: text3(choice.description, 1024) })] : [];
|
|
3050
3966
|
}) : void 0;
|
|
3051
3967
|
if (kind === "enum" && !choices?.length) return void 0;
|
|
3052
3968
|
return defined({
|
|
3053
|
-
id:
|
|
3969
|
+
id: id7,
|
|
3054
3970
|
label,
|
|
3055
3971
|
kind,
|
|
3056
3972
|
choices,
|
|
@@ -3064,15 +3980,15 @@ function normalizePublicRuntimeCommands(value) {
|
|
|
3064
3980
|
const seenIds = /* @__PURE__ */ new Set();
|
|
3065
3981
|
const seenAliases = /* @__PURE__ */ new Set();
|
|
3066
3982
|
const result = [];
|
|
3067
|
-
for (const
|
|
3068
|
-
const command = record(
|
|
3069
|
-
const
|
|
3070
|
-
const label =
|
|
3983
|
+
for (const item2 of value.slice(0, MAX_PUBLIC_RUNTIME_COMMANDS)) {
|
|
3984
|
+
const command = record(item2);
|
|
3985
|
+
const id7 = text3(command.id, 128);
|
|
3986
|
+
const label = text3(command.label, 160);
|
|
3071
3987
|
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 : [
|
|
3988
|
+
if (!id7 || !label || !dispatch || seenIds.has(id7) || command.visibility === "hidden" || command.sensitive === true) continue;
|
|
3989
|
+
if (["ownerOnly", "sensitive"].some((key) => command[key] !== void 0 && typeof command[key] !== "boolean")) continue;
|
|
3990
|
+
if (["aliases", "args", "placements", "availability"].some((key) => command[key] !== void 0 && !Array.isArray(command[key]))) continue;
|
|
3991
|
+
const rawAliases = Array.isArray(command.aliases) && command.aliases.length ? command.aliases : [id7];
|
|
3076
3992
|
const aliases = [...new Set(rawAliases.slice(0, 8).flatMap((value2) => {
|
|
3077
3993
|
const alias = typeof value2 === "string" && value2.length <= 128 ? normalizeRuntimeCommandAlias(value2) : null;
|
|
3078
3994
|
return alias && /^[A-Za-z0-9:_-]+$/.test(alias) && !seenAliases.has(alias.toLowerCase()) ? [alias] : [];
|
|
@@ -3080,20 +3996,20 @@ function normalizePublicRuntimeCommands(value) {
|
|
|
3080
3996
|
if (!aliases.length) continue;
|
|
3081
3997
|
const args = Array.isArray(command.args) ? command.args.slice(0, 12).map(normalizeArgument) : void 0;
|
|
3082
3998
|
if (args?.some((arg) => !arg) || Array.isArray(command.args) && command.args.length > 12) continue;
|
|
3083
|
-
seenIds.add(
|
|
3999
|
+
seenIds.add(id7);
|
|
3084
4000
|
aliases.forEach((alias) => seenAliases.add(alias.toLowerCase()));
|
|
3085
4001
|
result.push(defined({
|
|
3086
|
-
id:
|
|
4002
|
+
id: id7,
|
|
3087
4003
|
label,
|
|
3088
4004
|
aliases,
|
|
3089
4005
|
dispatch,
|
|
3090
4006
|
args,
|
|
3091
|
-
description:
|
|
4007
|
+
description: text3(command.description, 1024),
|
|
3092
4008
|
category: member(command.category, ["plan", "turn", "session", "runtime", "details", "skill", "custom"]),
|
|
3093
4009
|
placements: members(command.placements, ["composer_slash", "command_palette", "session_strip"]),
|
|
3094
4010
|
availability: members(command.availability, ["idle", "busy", "busy_with_queue", "waiting_input", "always"]),
|
|
3095
4011
|
ownerOnly: typeof command.ownerOnly === "boolean" ? command.ownerOnly : void 0,
|
|
3096
|
-
disabledReason:
|
|
4012
|
+
disabledReason: text3(command.disabledReason, 1024),
|
|
3097
4013
|
trailingTextBehavior: member(command.trailingTextBehavior, ["ignore", "send_as_prompt"]),
|
|
3098
4014
|
primitive: member(command.primitive, PRIMITIVES),
|
|
3099
4015
|
tier: member(command.tier, ["primary", "detail", "diagnostic"])
|
|
@@ -3127,14 +4043,14 @@ function normalizePublicRuntimeFacts(value) {
|
|
|
3127
4043
|
const seen = /* @__PURE__ */ new Set();
|
|
3128
4044
|
return value.slice(0, 16).flatMap((entry) => {
|
|
3129
4045
|
const fact = record(entry);
|
|
3130
|
-
const
|
|
3131
|
-
const group =
|
|
3132
|
-
const label =
|
|
3133
|
-
const content =
|
|
3134
|
-
if (!
|
|
3135
|
-
seen.add(
|
|
4046
|
+
const id7 = text3(fact.id, 64);
|
|
4047
|
+
const group = id7 && Object.hasOwn(PUBLIC_RUNTIME_FACT_GROUPS, id7) ? PUBLIC_RUNTIME_FACT_GROUPS[id7] : void 0;
|
|
4048
|
+
const label = text3(fact.label, 80);
|
|
4049
|
+
const content = text3(fact.value, 512);
|
|
4050
|
+
if (!id7 || !group || !label || !content || seen.has(id7) || fact.sensitive === true || fact.visibility === "hidden") return [];
|
|
4051
|
+
seen.add(id7);
|
|
3136
4052
|
return [defined({
|
|
3137
|
-
id:
|
|
4053
|
+
id: id7,
|
|
3138
4054
|
group,
|
|
3139
4055
|
label,
|
|
3140
4056
|
value: content,
|
|
@@ -3147,13 +4063,8 @@ function normalizePublicRuntimeFacts(value) {
|
|
|
3147
4063
|
}
|
|
3148
4064
|
__name(normalizePublicRuntimeFacts, "normalizePublicRuntimeFacts");
|
|
3149
4065
|
|
|
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
4066
|
// ../backend-contracts/src/endpointContentPolicy.ts
|
|
3156
|
-
var MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS = ["
|
|
4067
|
+
var MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS = ["forward_message"];
|
|
3157
4068
|
var ENDPOINT_CONTENT_ACTIONS = [
|
|
3158
4069
|
"mls_application",
|
|
3159
4070
|
"mls_group_control",
|
|
@@ -3161,8 +4072,6 @@ var ENDPOINT_CONTENT_ACTIONS = [
|
|
|
3161
4072
|
"append_artifact_command",
|
|
3162
4073
|
"retain_artifact_asset",
|
|
3163
4074
|
"sync_phone_contacts",
|
|
3164
|
-
"request_work_session",
|
|
3165
|
-
"resolve_work_session_request",
|
|
3166
4075
|
"update_agent_behavior_policy",
|
|
3167
4076
|
"update_conversation_agent_behavior_policy",
|
|
3168
4077
|
"send_message",
|
|
@@ -3215,6 +4124,8 @@ var ENDPOINT_CONTENT_ACTIONS = [
|
|
|
3215
4124
|
var ENDPOINT_CONTENT_FUNCTION_QUERIES = [
|
|
3216
4125
|
"mls_group_feed",
|
|
3217
4126
|
"mls_group_controls",
|
|
4127
|
+
"mls_group_admissions",
|
|
4128
|
+
"mls_interactions",
|
|
3218
4129
|
"list_artifacts",
|
|
3219
4130
|
"get_artifact_log",
|
|
3220
4131
|
"get_artifact_asset",
|
|
@@ -3222,9 +4133,6 @@ var ENDPOINT_CONTENT_FUNCTION_QUERIES = [
|
|
|
3222
4133
|
"get_unread_summary",
|
|
3223
4134
|
"peek_runtime_signal",
|
|
3224
4135
|
"list_runtime_primitives",
|
|
3225
|
-
"get_work_session_catalog",
|
|
3226
|
-
"get_work_session_details",
|
|
3227
|
-
"get_work_session_request",
|
|
3228
4136
|
"get_agent_behavior_policy",
|
|
3229
4137
|
"get_conversation_agent_behavior_policy",
|
|
3230
4138
|
"get_ephemeral",
|
|
@@ -3246,7 +4154,8 @@ var ENDPOINT_CONTENT_FUNCTION_QUERIES = [
|
|
|
3246
4154
|
"get_media_message",
|
|
3247
4155
|
"get_voice_session",
|
|
3248
4156
|
"get_image_generation",
|
|
3249
|
-
"get_image_generation_status"
|
|
4157
|
+
"get_image_generation_status",
|
|
4158
|
+
"same_owner_agents"
|
|
3250
4159
|
];
|
|
3251
4160
|
var ENDPOINT_CONTENT_QUERIES = [...ENDPOINT_CONTENT_FUNCTION_QUERIES, "admit_messages"];
|
|
3252
4161
|
var ENDPOINT_CONTENT_EPHEMERALS = [
|
|
@@ -3360,7 +4269,8 @@ var actionRouting = {
|
|
|
3360
4269
|
send_message: ["conversationId", "messageId", "replyTo", "mentions.*"],
|
|
3361
4270
|
delete_message: ["conversationId", "messageId"],
|
|
3362
4271
|
forward_message: ["sourceConversationId", "targetConversationId", "messageId"],
|
|
3363
|
-
|
|
4272
|
+
// A reaction is an action on a message's public stub, so its emoji is server-visible in encrypted rooms too (ruling 3).
|
|
4273
|
+
react: ["conversationId", "messageId", "emoji"],
|
|
3364
4274
|
send_contextual_message: ["conversationId"],
|
|
3365
4275
|
create_interaction: ["conversationId", "requestId", "inputId", "approvalId", "cardId", "planId", "responseUserId"],
|
|
3366
4276
|
consume_interaction: ["conversationId", "inputId", "approvalId", "cardId", "planId"],
|
|
@@ -3381,13 +4291,19 @@ var actionRouting = {
|
|
|
3381
4291
|
set_block: ["targetUserId"],
|
|
3382
4292
|
save_contact: ["contactUserId"],
|
|
3383
4293
|
remove_contact: ["contactId", "contactUserId"],
|
|
3384
|
-
|
|
4294
|
+
// `signal.type` is the closed Stop family (interrupt, stop_and_drop, new_session) the server must read to deliver it;
|
|
4295
|
+
// a runtime `primitive` (commands, arguments) stays private, so it remains refused in encrypted rooms.
|
|
4296
|
+
session_control: ["conversationId", "agentId", "signal.type"],
|
|
3385
4297
|
approve_contact_request: ["requestId"],
|
|
3386
4298
|
reject_contact_request: ["requestId"],
|
|
3387
4299
|
cancel_direct_request: ["requestId"],
|
|
3388
4300
|
update_message_disposition: ["conversationId", "messageId"],
|
|
3389
|
-
|
|
4301
|
+
// An encrypted room's item uploads its bytes as in a plaintext room (they are not end-to-end encrypted, payload v3
|
|
4302
|
+
// §4): the type, size and display hints are public; its file name and caption ride only inside the item.
|
|
4303
|
+
create_upload: ["conversationId", "mimeType", "sizeBytes", "width", "height", "durationMs"],
|
|
3390
4304
|
finalize_upload: ["uploadId"],
|
|
4305
|
+
// An encrypted item's application names its uploads beside the ciphertext, for Canon to retain on acceptance.
|
|
4306
|
+
mls_application: ["mediaUploadIds.*"],
|
|
3391
4307
|
delete_upload: ["uploadId"],
|
|
3392
4308
|
retry_video_processing: ["uploadId"],
|
|
3393
4309
|
stage_media_message: ["conversationId", "messageId", "files.*.fileId", "contentVersion", ...preparedRoutingPaths.map((path) => `preparedContent.${path}`)],
|
|
@@ -3395,19 +4311,18 @@ var actionRouting = {
|
|
|
3395
4311
|
renew_media_message_upload: ["conversationId", "messageId", "fileId", "generation"],
|
|
3396
4312
|
finalize_memo_stream: ["conversationId", "streamId"],
|
|
3397
4313
|
abort_memo_stream: ["streamId"],
|
|
3398
|
-
create_voice_session: ["conversationId"],
|
|
4314
|
+
create_voice_session: ["conversationId", "media"],
|
|
3399
4315
|
join_voice_session: ["conversationId", "sessionId"],
|
|
3400
4316
|
decline_voice_session: ["conversationId", "sessionId"],
|
|
3401
4317
|
end_voice_session: ["conversationId", "sessionId"],
|
|
3402
4318
|
consume_runtime_signal: ["conversationId", "expectedSignalId"],
|
|
3403
4319
|
consume_runtime_primitive: ["conversationId", "requestId", "expectedRevision"],
|
|
3404
4320
|
generate_image: ["conversationId", "requestId"],
|
|
3405
|
-
request_work_session: ["conversationId", "agentId", "requestId"],
|
|
3406
|
-
resolve_work_session_request: ["requestId"],
|
|
3407
4321
|
update_agent_behavior_policy: ["agentId"],
|
|
3408
4322
|
update_conversation_agent_behavior_policy: ["conversationId", "agentId"]
|
|
3409
4323
|
};
|
|
3410
4324
|
var queryRouting = {
|
|
4325
|
+
mls_interactions: ["version", "conversationId"],
|
|
3411
4326
|
list_artifacts: ["conversationId", "cursor", "limit"],
|
|
3412
4327
|
get_artifact_log: ["conversationId", "artifactId", "afterSequence", "cursor", "limit"],
|
|
3413
4328
|
get_artifact_asset: ["conversationId", "artifactId", "assetId"],
|
|
@@ -3433,12 +4348,19 @@ var queryRouting = {
|
|
|
3433
4348
|
get_media_message: ["conversationId", "messageId", "installationId"],
|
|
3434
4349
|
get_voice_session: ["conversationId", "sessionId"],
|
|
3435
4350
|
get_image_generation: ["requestId"],
|
|
3436
|
-
|
|
3437
|
-
get_work_session_details: ["agentId", "conversationId"],
|
|
3438
|
-
get_work_session_request: ["requestId"],
|
|
4351
|
+
same_owner_agents: ["agentIds.*"],
|
|
3439
4352
|
get_agent_behavior_policy: ["agentId"],
|
|
3440
4353
|
get_conversation_agent_behavior_policy: ["conversationId", "agentId"]
|
|
3441
4354
|
};
|
|
4355
|
+
var ENDPOINT_TURN_PRESENCE_STATES = ["thinking", "waiting_input", "interrupted", "idle"];
|
|
4356
|
+
var timestamp = /* @__PURE__ */ __name((value) => value === null || Number.isSafeInteger(value) && value >= 0, "timestamp");
|
|
4357
|
+
var turnPresence = {
|
|
4358
|
+
state: /* @__PURE__ */ __name((value) => ENDPOINT_TURN_PRESENCE_STATES.includes(value), "state"),
|
|
4359
|
+
// `turn-` and 32 random hex digits per encrypted turn: never the runner's own turn id, which seeds its cards' request ids.
|
|
4360
|
+
turnId: /* @__PURE__ */ __name((value) => value === null || typeof value === "string" && /^turn-[0-9a-f]{32}$/.test(value), "turnId"),
|
|
4361
|
+
openedAt: timestamp,
|
|
4362
|
+
turnUpdatedAt: timestamp
|
|
4363
|
+
};
|
|
3442
4364
|
var transientRouting = {
|
|
3443
4365
|
create_memo_stream: ["conversationId", "streamId"],
|
|
3444
4366
|
append_memo_chunk: ["streamId"],
|
|
@@ -3478,7 +4400,7 @@ function endpointOperationRequestRetention(action, body, envelope) {
|
|
|
3478
4400
|
if (action !== "respond_interaction") return "journal";
|
|
3479
4401
|
if (envelope && Object.keys(envelope).length > 0) return "memory";
|
|
3480
4402
|
const routing = ["kind", "conversationId", "agentId", "requestId"];
|
|
3481
|
-
const hasOnly = /* @__PURE__ */ __name((
|
|
4403
|
+
const hasOnly = /* @__PURE__ */ __name((fields2) => Object.keys(body).every((field) => fields2.includes(field)), "hasOnly");
|
|
3482
4404
|
if (routing.some((field) => body[field] !== void 0 && typeof body[field] !== "string")) return "memory";
|
|
3483
4405
|
if ((body.kind === "input" || body.kind === "card") && hasOnly([...routing, "status", "actionId"])) {
|
|
3484
4406
|
if (!["submitted", "cancelled"].includes(String(body.status)) || body.actionId !== void 0 && typeof body.actionId !== "string") return "memory";
|
|
@@ -3501,8 +4423,8 @@ function endpointOperationRequestRetention(action, body, envelope) {
|
|
|
3501
4423
|
__name(endpointOperationRequestRetention, "endpointOperationRequestRetention");
|
|
3502
4424
|
function endpointContentBoundary(lane, name, payload) {
|
|
3503
4425
|
if (!namesForLane[lane]?.includes(name)) throw new EndpointContentPolicyError(lane, name);
|
|
3504
|
-
const
|
|
3505
|
-
if (lane === "query.request" && name === "get_ephemeral" &&
|
|
4426
|
+
const record5 = payload && typeof payload === "object" ? payload : void 0;
|
|
4427
|
+
if (lane === "query.request" && name === "get_ephemeral" && record5 && !ENDPOINT_CONTENT_EPHEMERAL_FAMILIES.includes(record5.family)) {
|
|
3506
4428
|
throw new EndpointContentPolicyError(lane, `${name}/unknown-family`);
|
|
3507
4429
|
}
|
|
3508
4430
|
const requestRoutingPaths = (actionRouting[name] ?? []).flatMap((field) => [`body.value.${field}`, `envelope.${field}`]);
|
|
@@ -3510,7 +4432,7 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3510
4432
|
if (path.some((part) => secretSegments.has(part))) return "restricted-secret";
|
|
3511
4433
|
if (lane === "operation.request" && ["respond_interaction", "sync_phone_contacts"].includes(name) && matches(path, "preparedContent.payload")) return "restricted-secret";
|
|
3512
4434
|
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) ||
|
|
4435
|
+
if (lane === "operation.receipt" && path[0] === "result" && (["consume_interaction", "respond_interaction"].includes(name) || record5?.resultReference?.kind === "interaction")) return "restricted-secret";
|
|
3514
4436
|
let selected = payload;
|
|
3515
4437
|
for (const part of path) selected = selected && typeof selected === "object" && Object.hasOwn(selected, part) ? selected[part] : void 0;
|
|
3516
4438
|
if (selected !== null && typeof selected === "object") return "private-content";
|
|
@@ -3519,8 +4441,8 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3519
4441
|
if (name === "respond_interaction" && path[0] === "body" && path[1] === "value") return "restricted-secret";
|
|
3520
4442
|
if (name === "sync_phone_contacts" && path[0] === "body" && path[1] === "value") return "restricted-secret";
|
|
3521
4443
|
if (name === "verb_execute") {
|
|
3522
|
-
const
|
|
3523
|
-
const spec = VERB_WIRE_ENVELOPE_FIELDS[
|
|
4444
|
+
const wire4 = record5?.body?.value;
|
|
4445
|
+
const spec = VERB_WIRE_ENVELOPE_FIELDS[wire4?.verb];
|
|
3524
4446
|
if (path[0] === "body" && path[1] === "value") {
|
|
3525
4447
|
if (path.length === 3 && ["wire", "verb"].includes(path[2])) return "public-metadata";
|
|
3526
4448
|
if (path[2] === "envelope" && spec) {
|
|
@@ -3532,7 +4454,7 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3532
4454
|
}
|
|
3533
4455
|
} else if (lane === "operation.receipt") {
|
|
3534
4456
|
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" ||
|
|
4457
|
+
if (path[0] === "result" && (name === "consume_interaction" || name === "respond_interaction" || record5?.resultReference?.kind === "interaction")) return "restricted-secret";
|
|
3536
4458
|
} else if (lane === "query.request") {
|
|
3537
4459
|
if (publicMatch(path, queryRouting[name] ?? [])) return "public-metadata";
|
|
3538
4460
|
} else if (lane === "query.result" || lane === "ephemeral.result") {
|
|
@@ -3540,6 +4462,7 @@ function endpointContentBoundary(lane, name, payload) {
|
|
|
3540
4462
|
if (name === "get_ephemeral" && path[0] === "value") return "private-content";
|
|
3541
4463
|
} else if (lane === "ephemeral.request") {
|
|
3542
4464
|
if (publicMatch(path, transientRouting[name] ?? [])) return "public-metadata";
|
|
4465
|
+
if (name === "publish_turn" && path.length === 1 && Object.hasOwn(turnPresence, path[0]) && turnPresence[path[0]](selected)) return "public-metadata";
|
|
3543
4466
|
} else if (lane === "event") {
|
|
3544
4467
|
if (publicMatch(path, eventRouting(name))) return "public-metadata";
|
|
3545
4468
|
}
|
|
@@ -3563,12 +4486,12 @@ var PreparedContentError = class extends Error {
|
|
|
3563
4486
|
__name(this, "PreparedContentError");
|
|
3564
4487
|
}
|
|
3565
4488
|
};
|
|
3566
|
-
function
|
|
4489
|
+
function invalid10() {
|
|
3567
4490
|
throw new PreparedContentError("INVALID_PREPARED_CONTENT");
|
|
3568
4491
|
}
|
|
3569
|
-
__name(
|
|
4492
|
+
__name(invalid10, "invalid");
|
|
3570
4493
|
var record2 = /* @__PURE__ */ __name((value) => !!value && typeof value === "object" && !Array.isArray(value), "record");
|
|
3571
|
-
var keys = /* @__PURE__ */ __name((value, allowed) => Object.keys(value).every((
|
|
4494
|
+
var keys = /* @__PURE__ */ __name((value, allowed) => Object.keys(value).every((key) => allowed.includes(key)), "keys");
|
|
3572
4495
|
var revision = /* @__PURE__ */ __name((value) => Number.isSafeInteger(value) && value >= 0, "revision");
|
|
3573
4496
|
var blobIdentity = /* @__PURE__ */ __name((value) => typeof value === "string" && value.length > 0 && value.length <= 1024 && !/[\u0000-\u001f]/.test(value), "blobIdentity");
|
|
3574
4497
|
var identity = /* @__PURE__ */ __name((value) => typeof value === "string" && ENDPOINT_ID_PATTERN.test(value), "identity");
|
|
@@ -3579,33 +4502,33 @@ function canonicalEndpointJson(value) {
|
|
|
3579
4502
|
function encode(input) {
|
|
3580
4503
|
if (input === null || typeof input !== "object") return JSON.stringify(input);
|
|
3581
4504
|
if (Array.isArray(input)) return `[${input.map(encode).join(",")}]`;
|
|
3582
|
-
const
|
|
3583
|
-
return `{${Object.keys(
|
|
4505
|
+
const object = input;
|
|
4506
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${encode(object[key])}`).join(",")}}`;
|
|
3584
4507
|
}
|
|
3585
4508
|
__name(encode, "encode");
|
|
3586
4509
|
return encode(normalized);
|
|
3587
4510
|
}
|
|
3588
4511
|
__name(canonicalEndpointJson, "canonicalEndpointJson");
|
|
3589
4512
|
function validateMetadata(value) {
|
|
3590
|
-
if (!record2(value))
|
|
4513
|
+
if (!record2(value)) invalid10();
|
|
3591
4514
|
if (value.version !== PREPARED_CONTENT_VERSION) throw new PreparedContentError("UNSUPPORTED_PREPARED_CONTENT_VERSION");
|
|
3592
4515
|
if (value.codec !== PREPARED_JSON_CODEC) throw new PreparedContentError("UNSUPPORTED_PREPARED_CONTENT_CODEC");
|
|
3593
|
-
if (!keys(value, ["version", "context", "codec", "payload", "blobs", "expected"]))
|
|
4516
|
+
if (!keys(value, ["version", "context", "codec", "payload", "blobs", "expected"])) invalid10();
|
|
3594
4517
|
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))
|
|
4518
|
+
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
4519
|
const expected = value.expected;
|
|
3597
|
-
if (!record2(expected) || !keys(expected, ["installationGeneration", "protocolRevision", "admission"]) || !revision(expected.installationGeneration) || !revision(expected.protocolRevision))
|
|
4520
|
+
if (!record2(expected) || !keys(expected, ["installationGeneration", "protocolRevision", "admission"]) || !revision(expected.installationGeneration) || !revision(expected.protocolRevision)) invalid10();
|
|
3598
4521
|
if (expected.admission !== void 0) {
|
|
3599
4522
|
const admission = expected.admission;
|
|
3600
|
-
if (!record2(admission) || !keys(admission, ["conversationId", "membershipRevision", "admissionId", "targetAdmissionId"]) || ["conversationId", "admissionId", "targetAdmissionId"].some((
|
|
4523
|
+
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
4524
|
}
|
|
3602
|
-
if (!Array.isArray(value.blobs))
|
|
4525
|
+
if (!Array.isArray(value.blobs)) invalid10();
|
|
3603
4526
|
const ids = /* @__PURE__ */ new Set();
|
|
3604
4527
|
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))
|
|
4528
|
+
if (!record2(blob) || !keys(blob, ["id", "index"]) || !blobIdentity(blob.id) || blob.index !== index || ids.has(blob.id)) invalid10();
|
|
3606
4529
|
ids.add(blob.id);
|
|
3607
4530
|
}
|
|
3608
|
-
if (typeof value.payload !== "string")
|
|
4531
|
+
if (typeof value.payload !== "string") invalid10();
|
|
3609
4532
|
}
|
|
3610
4533
|
__name(validateMetadata, "validateMetadata");
|
|
3611
4534
|
function decodePreparedJsonContent(value) {
|
|
@@ -3614,18 +4537,18 @@ function decodePreparedJsonContent(value) {
|
|
|
3614
4537
|
try {
|
|
3615
4538
|
decoded = JSON.parse(value.payload);
|
|
3616
4539
|
} catch {
|
|
3617
|
-
|
|
4540
|
+
invalid10();
|
|
3618
4541
|
}
|
|
3619
|
-
if (!record2(decoded) || !keys(decoded, ["body", "envelope"]) || !record2(decoded.body) || decoded.envelope !== void 0 && !record2(decoded.envelope))
|
|
3620
|
-
if (canonicalEndpointJson(decoded) !== value.payload)
|
|
4542
|
+
if (!record2(decoded) || !keys(decoded, ["body", "envelope"]) || !record2(decoded.body) || decoded.envelope !== void 0 && !record2(decoded.envelope)) invalid10();
|
|
4543
|
+
if (canonicalEndpointJson(decoded) !== value.payload) invalid10();
|
|
3621
4544
|
const { body, envelope } = decoded;
|
|
3622
4545
|
if (endpointOperationRequestRetention(value.context.action, body, envelope) === "memory") {
|
|
3623
4546
|
throw new PreparedContentError("PREPARED_CONTENT_MEMORY_ONLY");
|
|
3624
4547
|
}
|
|
3625
4548
|
const effective = { ...body, ...envelope };
|
|
3626
|
-
for (const
|
|
3627
|
-
const bound = value.context[
|
|
3628
|
-
if (bound !== void 0 && effective[
|
|
4549
|
+
for (const key of ["conversationId", "messageId"]) {
|
|
4550
|
+
const bound = value.context[key];
|
|
4551
|
+
if (bound !== void 0 && effective[key] !== void 0 && effective[key] !== bound) invalid10();
|
|
3629
4552
|
}
|
|
3630
4553
|
return envelope === void 0 ? { body } : { body, envelope };
|
|
3631
4554
|
}
|
|
@@ -3655,7 +4578,7 @@ function prepareJsonContent(input) {
|
|
|
3655
4578
|
}
|
|
3656
4579
|
__name(prepareJsonContent, "prepareJsonContent");
|
|
3657
4580
|
function logicalMediaBlobId(conversationId, messageId2, index) {
|
|
3658
|
-
if (!identity(conversationId) || !identity(messageId2) || !revision(index))
|
|
4581
|
+
if (!identity(conversationId) || !identity(messageId2) || !revision(index)) invalid10();
|
|
3659
4582
|
return JSON.stringify([conversationId, messageId2, index]);
|
|
3660
4583
|
}
|
|
3661
4584
|
__name(logicalMediaBlobId, "logicalMediaBlobId");
|
|
@@ -3671,10 +4594,130 @@ function isEndpointOperationReceipt(value) {
|
|
|
3671
4594
|
}
|
|
3672
4595
|
__name(isEndpointOperationReceipt, "isEndpointOperationReceipt");
|
|
3673
4596
|
|
|
4597
|
+
// ../backend-contracts/src/connectorRelay.ts
|
|
4598
|
+
var CONNECTOR_RELAY_INSTALLATION_PREFIX = "canon-relay:";
|
|
4599
|
+
var CONNECTOR_RELAY_INSTALLATION_PATTERN = /^canon-relay:[0-9a-f]{40}$/;
|
|
4600
|
+
function isConnectorRelayInstallation(installationId) {
|
|
4601
|
+
return typeof installationId === "string" && installationId.startsWith(CONNECTOR_RELAY_INSTALLATION_PREFIX);
|
|
4602
|
+
}
|
|
4603
|
+
__name(isConnectorRelayInstallation, "isConnectorRelayInstallation");
|
|
4604
|
+
var CONNECTOR_RELAY_CREDENTIAL_PATTERN = /^canon_relay_[A-Za-z0-9_-]{43}$/;
|
|
4605
|
+
function isConnectorRelayCredential(value) {
|
|
4606
|
+
return typeof value === "string" && CONNECTOR_RELAY_CREDENTIAL_PATTERN.test(value);
|
|
4607
|
+
}
|
|
4608
|
+
__name(isConnectorRelayCredential, "isConnectorRelayCredential");
|
|
4609
|
+
var CONNECTOR_RELAY_INPUT_ID_PATTERN = /^relay:[0-9a-f]{40}$/;
|
|
4610
|
+
var CONNECTOR_RELAY_MAX_BODY_BYTES = 256 * 1024;
|
|
4611
|
+
var CONNECTOR_RELAY_MAX_TEXT_BYTES = MLS_DEV_TEXT_MAX_BYTES;
|
|
4612
|
+
var CONNECTOR_RELAY_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
4613
|
+
var CONNECTOR_RELAY_ANSWER_WITHIN_MS = 7e3;
|
|
4614
|
+
function connectorRelayResponseBytes(value) {
|
|
4615
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
4616
|
+
}
|
|
4617
|
+
__name(connectorRelayResponseBytes, "connectorRelayResponseBytes");
|
|
4618
|
+
var ConnectorRelayRequestError = class extends Error {
|
|
4619
|
+
static {
|
|
4620
|
+
__name(this, "ConnectorRelayRequestError");
|
|
4621
|
+
}
|
|
4622
|
+
name = "ConnectorRelayRequestError";
|
|
4623
|
+
};
|
|
4624
|
+
function invalid11(what) {
|
|
4625
|
+
throw new ConnectorRelayRequestError(`Invalid relay request: ${what}`);
|
|
4626
|
+
}
|
|
4627
|
+
__name(invalid11, "invalid");
|
|
4628
|
+
function record3(value, required, optional = []) {
|
|
4629
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) invalid11("expected an object");
|
|
4630
|
+
const result = value;
|
|
4631
|
+
for (const key of Object.keys(result)) if (!required.includes(key) && !optional.includes(key)) invalid11(`unexpected ${key}`);
|
|
4632
|
+
for (const key of required) if (!Object.hasOwn(result, key)) invalid11(`missing ${key}`);
|
|
4633
|
+
return result;
|
|
4634
|
+
}
|
|
4635
|
+
__name(record3, "record");
|
|
4636
|
+
function id6(value, what, pattern = ENDPOINT_ID_PATTERN) {
|
|
4637
|
+
if (typeof value !== "string" || !pattern.test(value)) invalid11(what);
|
|
4638
|
+
return value;
|
|
4639
|
+
}
|
|
4640
|
+
__name(id6, "id");
|
|
4641
|
+
function doc(value, what) {
|
|
4642
|
+
if (typeof value !== "string" || !value || value.length > 200 || value.includes("/")) invalid11(what);
|
|
4643
|
+
return value;
|
|
4644
|
+
}
|
|
4645
|
+
__name(doc, "doc");
|
|
4646
|
+
var room = /* @__PURE__ */ __name((value) => doc(value, "conversationId"), "room");
|
|
4647
|
+
function text4(value) {
|
|
4648
|
+
if (typeof value !== "string" || !value.trim() || new TextEncoder().encode(value).length > CONNECTOR_RELAY_MAX_TEXT_BYTES) invalid11("text");
|
|
4649
|
+
return value;
|
|
4650
|
+
}
|
|
4651
|
+
__name(text4, "text");
|
|
4652
|
+
function limit(value) {
|
|
4653
|
+
if (value === void 0) return void 0;
|
|
4654
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > 100) invalid11("limit");
|
|
4655
|
+
return value;
|
|
4656
|
+
}
|
|
4657
|
+
__name(limit, "limit");
|
|
4658
|
+
function parseConnectorRelayRequest(value) {
|
|
4659
|
+
const body = record3(value, ["version", "agentId", "grantId", "op", "args"]);
|
|
4660
|
+
if (body.version !== 1) invalid11("version");
|
|
4661
|
+
const head = { version: 1, agentId: doc(body.agentId, "agentId"), grantId: id6(body.grantId, "grantId") };
|
|
4662
|
+
switch (body.op) {
|
|
4663
|
+
case "pending": {
|
|
4664
|
+
const args = record3(body.args, ["conversationId"], ["limit"]);
|
|
4665
|
+
return { ...head, op: "pending", args: { conversationId: room(args.conversationId), ...args.limit === void 0 ? {} : { limit: limit(args.limit) } } };
|
|
4666
|
+
}
|
|
4667
|
+
case "claim": {
|
|
4668
|
+
const args = record3(body.args, ["inputId", "requestId"]);
|
|
4669
|
+
return { ...head, op: "claim", args: { inputId: id6(args.inputId, "inputId", CONNECTOR_RELAY_INPUT_ID_PATTERN), requestId: id6(args.requestId, "requestId") } };
|
|
4670
|
+
}
|
|
4671
|
+
case "reply": {
|
|
4672
|
+
const args = record3(body.args, ["inputId", "requestId", "text"]);
|
|
4673
|
+
return { ...head, op: "reply", args: {
|
|
4674
|
+
inputId: id6(args.inputId, "inputId", CONNECTOR_RELAY_INPUT_ID_PATTERN),
|
|
4675
|
+
requestId: id6(args.requestId, "requestId"),
|
|
4676
|
+
text: text4(args.text)
|
|
4677
|
+
} };
|
|
4678
|
+
}
|
|
4679
|
+
case "settle": {
|
|
4680
|
+
const args = record3(body.args, ["inputId", "requestId", "outcome"]);
|
|
4681
|
+
if (!["completed", "no_reply", "failed"].includes(args.outcome)) invalid11("outcome");
|
|
4682
|
+
return { ...head, op: "settle", args: {
|
|
4683
|
+
inputId: id6(args.inputId, "inputId", CONNECTOR_RELAY_INPUT_ID_PATTERN),
|
|
4684
|
+
requestId: id6(args.requestId, "requestId"),
|
|
4685
|
+
outcome: args.outcome
|
|
4686
|
+
} };
|
|
4687
|
+
}
|
|
4688
|
+
case "read": {
|
|
4689
|
+
const args = record3(body.args, ["conversationId"], ["limit", "before"]);
|
|
4690
|
+
if (args.before !== void 0 && (typeof args.before !== "string" || !/^[1-9][0-9]{0,15}$/.test(args.before))) invalid11("before");
|
|
4691
|
+
return { ...head, op: "read", args: {
|
|
4692
|
+
conversationId: room(args.conversationId),
|
|
4693
|
+
...args.limit === void 0 ? {} : { limit: limit(args.limit) },
|
|
4694
|
+
...args.before === void 0 ? {} : { before: args.before }
|
|
4695
|
+
} };
|
|
4696
|
+
}
|
|
4697
|
+
case "send": {
|
|
4698
|
+
const args = record3(body.args, ["conversationId", "messageId", "text"]);
|
|
4699
|
+
return { ...head, op: "send", args: { conversationId: room(args.conversationId), messageId: id6(args.messageId, "messageId"), text: text4(args.text) } };
|
|
4700
|
+
}
|
|
4701
|
+
case "credential": {
|
|
4702
|
+
const args = record3(body.args, ["installationId", "generation", "credential"]);
|
|
4703
|
+
if (!Number.isSafeInteger(args.generation) || args.generation < 1) invalid11("generation");
|
|
4704
|
+
if (!isConnectorRelayCredential(args.credential)) invalid11("credential");
|
|
4705
|
+
return { ...head, op: "credential", args: {
|
|
4706
|
+
installationId: id6(args.installationId, "installationId", CONNECTOR_RELAY_INSTALLATION_PATTERN),
|
|
4707
|
+
generation: args.generation,
|
|
4708
|
+
credential: args.credential
|
|
4709
|
+
} };
|
|
4710
|
+
}
|
|
4711
|
+
default:
|
|
4712
|
+
return invalid11("op");
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
__name(parseConnectorRelayRequest, "parseConnectorRelayRequest");
|
|
4716
|
+
|
|
3674
4717
|
// ../backend-contracts/src/deliveryObservation.ts
|
|
3675
4718
|
var isTerminalDeliveryOutcome = /* @__PURE__ */ __name((outcome) => !["unknown", "pending"].includes(outcome), "isTerminalDeliveryOutcome");
|
|
3676
4719
|
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(([
|
|
4720
|
+
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
4721
|
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
4722
|
return next;
|
|
3680
4723
|
}
|
|
@@ -3695,7 +4738,7 @@ __name(classifyTransferHttpFailure, "classifyTransferHttpFailure");
|
|
|
3695
4738
|
function classifyTransferReceipt(value, expected) {
|
|
3696
4739
|
if (!value || typeof value !== "object" || Array.isArray(value)) return "invalid";
|
|
3697
4740
|
const receipt = value;
|
|
3698
|
-
if (Object.entries(expected).some(([
|
|
4741
|
+
if (Object.entries(expected).some(([key, field]) => receipt[key] !== field)) return "invalid";
|
|
3699
4742
|
switch (receipt.status) {
|
|
3700
4743
|
case "succeeded":
|
|
3701
4744
|
return "resultError" in receipt || !receipt.result || typeof receipt.result !== "object" || Array.isArray(receipt.result) ? "uncertain" : "result";
|
|
@@ -3712,17 +4755,8 @@ function classifyTransferReceipt(value, expected) {
|
|
|
3712
4755
|
__name(classifyTransferReceipt, "classifyTransferReceipt");
|
|
3713
4756
|
|
|
3714
4757
|
// ../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
4758
|
var ARTIFACT_COMMAND_MAX_BYTES = 64 * 1024;
|
|
3719
4759
|
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
4760
|
var ARTIFACT_LIMITS = Object.freeze({
|
|
3727
4761
|
titleChars: 120,
|
|
3728
4762
|
chessMaxPlies: 600,
|
|
@@ -3747,604 +4781,7 @@ var ARTIFACT_LIMITS = Object.freeze({
|
|
|
3747
4781
|
outcomes: 200,
|
|
3748
4782
|
outcomeMessageChars: 500
|
|
3749
4783
|
});
|
|
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
4784
|
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
4785
|
|
|
4349
4786
|
// ../backend-contracts/src/mlsDevCredentialReconciliation.ts
|
|
4350
4787
|
var MLS_DEV_CREDENTIAL_RECONCILIATION_CHALLENGE_TTL_MS = 3e5;
|
|
@@ -4359,32 +4796,32 @@ var MlsDevCredentialReconciliationValidationError = class extends Error {
|
|
|
4359
4796
|
this.name = "MlsDevCredentialReconciliationValidationError";
|
|
4360
4797
|
}
|
|
4361
4798
|
};
|
|
4362
|
-
var
|
|
4363
|
-
var domain2 =
|
|
4364
|
-
function
|
|
4799
|
+
var utf86 = new TextEncoder();
|
|
4800
|
+
var domain2 = utf86.encode("canon.mls.credential-reconciliation-proof.v1\0");
|
|
4801
|
+
function invalid12() {
|
|
4365
4802
|
throw new MlsDevCredentialReconciliationValidationError();
|
|
4366
4803
|
}
|
|
4367
|
-
__name(
|
|
4368
|
-
function
|
|
4804
|
+
__name(invalid12, "invalid");
|
|
4805
|
+
function boundary9(read) {
|
|
4369
4806
|
try {
|
|
4370
4807
|
return read();
|
|
4371
4808
|
} catch {
|
|
4372
|
-
return
|
|
4809
|
+
return invalid12();
|
|
4373
4810
|
}
|
|
4374
4811
|
}
|
|
4375
|
-
__name(
|
|
4812
|
+
__name(boundary9, "boundary");
|
|
4376
4813
|
function bounded7(value) {
|
|
4377
|
-
if (
|
|
4814
|
+
if (utf86.encode(JSON.stringify(value)).length > MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES) invalid12();
|
|
4378
4815
|
return Object.freeze(value);
|
|
4379
4816
|
}
|
|
4380
4817
|
__name(bounded7, "bounded");
|
|
4381
|
-
function
|
|
4818
|
+
function encoded6(value, bytes) {
|
|
4382
4819
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
4383
4820
|
return value;
|
|
4384
4821
|
}
|
|
4385
|
-
__name(
|
|
4822
|
+
__name(encoded6, "encoded");
|
|
4386
4823
|
function parseMlsDevCredentialReconciliationBegin(value) {
|
|
4387
|
-
return
|
|
4824
|
+
return boundary9(() => {
|
|
4388
4825
|
const input = readMlsDataRecord(value, ["version", "deviceId", "requestId", "expectedCredentialGeneration"]);
|
|
4389
4826
|
return bounded7({
|
|
4390
4827
|
version: requireMlsLiteral(input.version, 1),
|
|
@@ -4396,7 +4833,7 @@ function parseMlsDevCredentialReconciliationBegin(value) {
|
|
|
4396
4833
|
}
|
|
4397
4834
|
__name(parseMlsDevCredentialReconciliationBegin, "parseMlsDevCredentialReconciliationBegin");
|
|
4398
4835
|
function parseMlsDevCredentialReconciliationChallenge(value) {
|
|
4399
|
-
return
|
|
4836
|
+
return boundary9(() => {
|
|
4400
4837
|
const input = readMlsDataRecord(value, [
|
|
4401
4838
|
"version",
|
|
4402
4839
|
"challengeId",
|
|
@@ -4409,12 +4846,12 @@ function parseMlsDevCredentialReconciliationChallenge(value) {
|
|
|
4409
4846
|
]);
|
|
4410
4847
|
const device = parseMlsDevEnrollmentResult({ version: 1, authorization: "registration_only", device: input.device }).device;
|
|
4411
4848
|
const issuedAtMs = requireMlsInteger(input.issuedAtMs), expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
4412
|
-
if (issuedAtMs < device.registeredAtMs || expiresAtMs - issuedAtMs !== MLS_DEV_CREDENTIAL_RECONCILIATION_CHALLENGE_TTL_MS)
|
|
4849
|
+
if (issuedAtMs < device.registeredAtMs || expiresAtMs - issuedAtMs !== MLS_DEV_CREDENTIAL_RECONCILIATION_CHALLENGE_TTL_MS) invalid12();
|
|
4413
4850
|
return bounded7({
|
|
4414
4851
|
version: requireMlsLiteral(input.version, 1),
|
|
4415
4852
|
challengeId: requireMlsUuid(input.challengeId),
|
|
4416
4853
|
requestId: requireMlsUuid(input.requestId),
|
|
4417
|
-
nonce:
|
|
4854
|
+
nonce: encoded6(input.nonce, 32),
|
|
4418
4855
|
device,
|
|
4419
4856
|
expectedCredentialGeneration: requireMlsInteger(input.expectedCredentialGeneration),
|
|
4420
4857
|
issuedAtMs,
|
|
@@ -4424,18 +4861,18 @@ function parseMlsDevCredentialReconciliationChallenge(value) {
|
|
|
4424
4861
|
}
|
|
4425
4862
|
__name(parseMlsDevCredentialReconciliationChallenge, "parseMlsDevCredentialReconciliationChallenge");
|
|
4426
4863
|
function parseMlsDevCredentialReconciliationComplete(value) {
|
|
4427
|
-
return
|
|
4864
|
+
return boundary9(() => {
|
|
4428
4865
|
const input = readMlsDataRecord(value, ["version", "challengeId", "proof"]);
|
|
4429
|
-
return bounded7({ version: requireMlsLiteral(input.version, 1), challengeId: requireMlsUuid(input.challengeId), proof:
|
|
4866
|
+
return bounded7({ version: requireMlsLiteral(input.version, 1), challengeId: requireMlsUuid(input.challengeId), proof: encoded6(input.proof, 64) });
|
|
4430
4867
|
});
|
|
4431
4868
|
}
|
|
4432
4869
|
__name(parseMlsDevCredentialReconciliationComplete, "parseMlsDevCredentialReconciliationComplete");
|
|
4433
4870
|
function parseMlsDevCredentialReconciliationReceipt(value) {
|
|
4434
|
-
return
|
|
4871
|
+
return boundary9(() => {
|
|
4435
4872
|
const input = readMlsDataRecord(value, ["version", "authorization", "challenge", "observedCredentialGeneration", "observedAtMs"]);
|
|
4436
4873
|
const challenge = parseMlsDevCredentialReconciliationChallenge(input.challenge);
|
|
4437
4874
|
const observedCredentialGeneration = requireMlsInteger(input.observedCredentialGeneration), observedAtMs = requireMlsInteger(input.observedAtMs);
|
|
4438
|
-
if (observedCredentialGeneration < challenge.expectedCredentialGeneration || observedAtMs < challenge.issuedAtMs || observedAtMs >= challenge.expiresAtMs)
|
|
4875
|
+
if (observedCredentialGeneration < challenge.expectedCredentialGeneration || observedAtMs < challenge.issuedAtMs || observedAtMs >= challenge.expiresAtMs) invalid12();
|
|
4439
4876
|
return bounded7({
|
|
4440
4877
|
version: requireMlsLiteral(input.version, 1),
|
|
4441
4878
|
authorization: requireMlsLiteral(input.authorization, "credential_generation_observation_only"),
|
|
@@ -4461,51 +4898,51 @@ var MlsDevDeviceCustodyValidationError = class extends Error {
|
|
|
4461
4898
|
this.name = "MlsDevDeviceCustodyValidationError";
|
|
4462
4899
|
}
|
|
4463
4900
|
};
|
|
4464
|
-
var
|
|
4465
|
-
function
|
|
4901
|
+
var utf87 = new TextEncoder();
|
|
4902
|
+
function invalid13() {
|
|
4466
4903
|
throw new MlsDevDeviceCustodyValidationError();
|
|
4467
4904
|
}
|
|
4468
|
-
__name(
|
|
4469
|
-
function
|
|
4905
|
+
__name(invalid13, "invalid");
|
|
4906
|
+
function boundary10(read) {
|
|
4470
4907
|
try {
|
|
4471
4908
|
return read();
|
|
4472
4909
|
} catch {
|
|
4473
|
-
return
|
|
4910
|
+
return invalid13();
|
|
4474
4911
|
}
|
|
4475
4912
|
}
|
|
4476
|
-
__name(
|
|
4477
|
-
function bounded8(value,
|
|
4478
|
-
if (
|
|
4913
|
+
__name(boundary10, "boundary");
|
|
4914
|
+
function bounded8(value, limit2) {
|
|
4915
|
+
if (utf87.encode(JSON.stringify(value)).length > limit2) invalid13();
|
|
4479
4916
|
return Object.freeze(value);
|
|
4480
4917
|
}
|
|
4481
4918
|
__name(bounded8, "bounded");
|
|
4482
4919
|
function binding2(input) {
|
|
4483
4920
|
return {
|
|
4484
4921
|
version: requireMlsLiteral(input.version, 1),
|
|
4485
|
-
environmentId:
|
|
4922
|
+
environmentId: requireMlsEnvironmentId(input.environmentId),
|
|
4486
4923
|
principal: parseMlsDevEnrollmentPrincipal(input.principal),
|
|
4487
4924
|
installationId: parseMlsDevInstallationId(input.installationId)
|
|
4488
4925
|
};
|
|
4489
4926
|
}
|
|
4490
4927
|
__name(binding2, "binding");
|
|
4491
4928
|
function parseMlsDevDeviceBinding(value) {
|
|
4492
|
-
return
|
|
4929
|
+
return boundary10(() => bounded8(binding2(readMlsDataRecord(value, ["version", "environmentId", "principal", "installationId"])), MLS_DEV_DEVICE_CONTEXT_MAX_BYTES));
|
|
4493
4930
|
}
|
|
4494
4931
|
__name(parseMlsDevDeviceBinding, "parseMlsDevDeviceBinding");
|
|
4495
4932
|
function parseMlsDevDeviceContext(value) {
|
|
4496
|
-
return
|
|
4933
|
+
return boundary10(() => {
|
|
4497
4934
|
const input = readMlsDataRecord(value, ["version", "environmentId", "principal", "installationId", "revision"]);
|
|
4498
4935
|
const revision2 = requireMlsInteger(input.revision);
|
|
4499
|
-
if (revision2 >= Number.MAX_SAFE_INTEGER)
|
|
4936
|
+
if (revision2 >= Number.MAX_SAFE_INTEGER) invalid13();
|
|
4500
4937
|
return bounded8({ ...binding2(input), revision: revision2 }, MLS_DEV_DEVICE_CONTEXT_MAX_BYTES);
|
|
4501
4938
|
});
|
|
4502
4939
|
}
|
|
4503
4940
|
__name(parseMlsDevDeviceContext, "parseMlsDevDeviceContext");
|
|
4504
4941
|
function parseMlsDevDeviceCommand(value) {
|
|
4505
|
-
return
|
|
4506
|
-
if (value === null || typeof value !== "object")
|
|
4942
|
+
return boundary10(() => {
|
|
4943
|
+
if (value === null || typeof value !== "object") invalid13();
|
|
4507
4944
|
const descriptor = Object.getOwnPropertyDescriptor(value, "op");
|
|
4508
|
-
if (!descriptor || !Object.hasOwn(descriptor, "value"))
|
|
4945
|
+
if (!descriptor || !Object.hasOwn(descriptor, "value")) invalid13();
|
|
4509
4946
|
if (descriptor.value === "create") {
|
|
4510
4947
|
const input2 = readMlsDataRecord(value, ["op"]);
|
|
4511
4948
|
return Object.freeze({ op: requireMlsLiteral(input2.op, "create") });
|
|
@@ -4521,19 +4958,19 @@ function parseMlsDevDeviceCommand(value) {
|
|
|
4521
4958
|
const input2 = readMlsDataRecord(value, ["op", "challenge", "expectedCredentialGeneration"]);
|
|
4522
4959
|
const challenge2 = parseMlsDevCredentialReconciliationChallenge(input2.challenge);
|
|
4523
4960
|
const expectedCredentialGeneration2 = requireMlsInteger(input2.expectedCredentialGeneration);
|
|
4524
|
-
if (challenge2.expectedCredentialGeneration !== expectedCredentialGeneration2)
|
|
4961
|
+
if (challenge2.expectedCredentialGeneration !== expectedCredentialGeneration2) invalid13();
|
|
4525
4962
|
return bounded8({
|
|
4526
4963
|
op: requireMlsLiteral(input2.op, "credential_reconciliation_proof"),
|
|
4527
4964
|
challenge: challenge2,
|
|
4528
4965
|
expectedCredentialGeneration: expectedCredentialGeneration2
|
|
4529
4966
|
}, MLS_DEV_DEVICE_COMMAND_MAX_BYTES);
|
|
4530
4967
|
}
|
|
4531
|
-
if (descriptor.value !== "credential_proof")
|
|
4968
|
+
if (descriptor.value !== "credential_proof") invalid13();
|
|
4532
4969
|
const input = readMlsDataRecord(value, ["op", "challenge", "registrationResult", "expectedCredentialGeneration"]);
|
|
4533
4970
|
const challenge = parseMlsDevCredentialChallenge(input.challenge);
|
|
4534
4971
|
const registrationResult = parseMlsDevEnrollmentResult(input.registrationResult);
|
|
4535
4972
|
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))
|
|
4973
|
+
if (expectedCredentialGeneration >= Number.MAX_SAFE_INTEGER || challenge.credential.claims.generation !== expectedCredentialGeneration + 1 || JSON.stringify(challenge.credential.claims.device) !== JSON.stringify(registrationResult.device)) invalid13();
|
|
4537
4974
|
return bounded8({
|
|
4538
4975
|
op: requireMlsLiteral(input.op, "credential_proof"),
|
|
4539
4976
|
challenge,
|
|
@@ -4543,29 +4980,29 @@ function parseMlsDevDeviceCommand(value) {
|
|
|
4543
4980
|
});
|
|
4544
4981
|
}
|
|
4545
4982
|
__name(parseMlsDevDeviceCommand, "parseMlsDevDeviceCommand");
|
|
4546
|
-
function
|
|
4983
|
+
function encoded7(value, bytes) {
|
|
4547
4984
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
4548
4985
|
return value;
|
|
4549
4986
|
}
|
|
4550
|
-
__name(
|
|
4987
|
+
__name(encoded7, "encoded");
|
|
4551
4988
|
function wrapped(value) {
|
|
4552
|
-
if (typeof value !== "string" || value.length > MLS_DEV_DEVICE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:"))
|
|
4989
|
+
if (typeof value !== "string" || value.length > MLS_DEV_DEVICE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:")) invalid13();
|
|
4553
4990
|
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))
|
|
4991
|
+
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
4992
|
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)
|
|
4993
|
+
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0) invalid13();
|
|
4994
|
+
if (!body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0) invalid13();
|
|
4558
4995
|
return value;
|
|
4559
4996
|
}
|
|
4560
4997
|
__name(wrapped, "wrapped");
|
|
4561
4998
|
function parseMlsDevDevicePreparation(value, op) {
|
|
4562
|
-
return
|
|
4563
|
-
if (!["create", "enrollment_proof", "credential_proof", "credential_reconciliation_proof"].includes(op))
|
|
4999
|
+
return boundary10(() => {
|
|
5000
|
+
if (!["create", "enrollment_proof", "credential_proof", "credential_reconciliation_proof"].includes(op)) invalid13();
|
|
4564
5001
|
const input = readMlsDataRecord(value, ["wrappedState", "payload"]);
|
|
4565
5002
|
const payload = readMlsDataRecord(input.payload, op === "create" ? ["signaturePublicKey"] : ["signaturePublicKey", "proof"]);
|
|
4566
5003
|
return bounded8({ wrappedState: wrapped(input.wrappedState), payload: Object.freeze({
|
|
4567
|
-
signaturePublicKey:
|
|
4568
|
-
...op === "create" ? {} : { proof:
|
|
5004
|
+
signaturePublicKey: encoded7(payload.signaturePublicKey, 32),
|
|
5005
|
+
...op === "create" ? {} : { proof: encoded7(payload.proof, 64) }
|
|
4569
5006
|
}) }, MLS_DEV_DEVICE_COMMAND_MAX_BYTES);
|
|
4570
5007
|
});
|
|
4571
5008
|
}
|
|
@@ -4586,56 +5023,56 @@ var MlsDevKeyPackageValidationError = class extends Error {
|
|
|
4586
5023
|
this.name = "MlsDevKeyPackageValidationError";
|
|
4587
5024
|
}
|
|
4588
5025
|
};
|
|
4589
|
-
var
|
|
4590
|
-
function
|
|
5026
|
+
var utf88 = new TextEncoder();
|
|
5027
|
+
function invalid14() {
|
|
4591
5028
|
throw new MlsDevKeyPackageValidationError();
|
|
4592
5029
|
}
|
|
4593
|
-
__name(
|
|
4594
|
-
function
|
|
5030
|
+
__name(invalid14, "invalid");
|
|
5031
|
+
function boundary11(read) {
|
|
4595
5032
|
try {
|
|
4596
5033
|
return read();
|
|
4597
5034
|
} catch {
|
|
4598
|
-
return
|
|
5035
|
+
return invalid14();
|
|
4599
5036
|
}
|
|
4600
5037
|
}
|
|
4601
|
-
__name(
|
|
5038
|
+
__name(boundary11, "boundary");
|
|
4602
5039
|
function bounded9(value, maximum) {
|
|
4603
|
-
if (
|
|
5040
|
+
if (utf88.encode(JSON.stringify(value)).length > maximum) invalid14();
|
|
4604
5041
|
return Object.freeze(value);
|
|
4605
5042
|
}
|
|
4606
5043
|
__name(bounded9, "bounded");
|
|
4607
|
-
function
|
|
5044
|
+
function encoded8(value, bytes) {
|
|
4608
5045
|
decodeCanonicalMlsBase64Url(value, bytes);
|
|
4609
5046
|
return value;
|
|
4610
5047
|
}
|
|
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))
|
|
5048
|
+
__name(encoded8, "encoded");
|
|
5049
|
+
function wire3(value) {
|
|
5050
|
+
if (typeof value !== "string" || value.length < 2 || value.length > Math.ceil(MLS_DEV_KEY_PACKAGE_WIRE_MAX_BYTES * 4 / 3)) invalid14();
|
|
4614
5051
|
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))
|
|
5052
|
+
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
5053
|
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".indexOf(value.at(-1));
|
|
4617
|
-
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0)
|
|
5054
|
+
if (value.length % 4 === 2 && (last & 15) !== 0 || value.length % 4 === 3 && (last & 3) !== 0) invalid14();
|
|
4618
5055
|
return value;
|
|
4619
5056
|
}
|
|
4620
|
-
__name(
|
|
5057
|
+
__name(wire3, "wire");
|
|
4621
5058
|
function wrapped2(value) {
|
|
4622
|
-
if (typeof value !== "string" || value.length > MLS_DEV_KEY_PACKAGE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:"))
|
|
5059
|
+
if (typeof value !== "string" || value.length > MLS_DEV_KEY_PACKAGE_WRAPPED_STATE_MAX_BYTES || !value.startsWith("v1:")) invalid14();
|
|
4623
5060
|
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))
|
|
5061
|
+
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
5062
|
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)
|
|
5063
|
+
if (body.endsWith("==") && (alphabet.indexOf(body.at(-3)) & 15) !== 0) invalid14();
|
|
5064
|
+
if (!body.endsWith("==") && body.endsWith("=") && (alphabet.indexOf(body.at(-2)) & 3) !== 0) invalid14();
|
|
4628
5065
|
const bytes = body.length / 4 * 3 - (body.endsWith("==") ? 2 : body.endsWith("=") ? 1 : 0);
|
|
4629
|
-
if (bytes > 128 * 1024 + 28)
|
|
5066
|
+
if (bytes > 128 * 1024 + 28) invalid14();
|
|
4630
5067
|
return value;
|
|
4631
5068
|
}
|
|
4632
5069
|
__name(wrapped2, "wrapped");
|
|
4633
5070
|
function parseMlsDevKeyPackageId(value) {
|
|
4634
|
-
return
|
|
5071
|
+
return boundary11(() => requireMlsUuid(value));
|
|
4635
5072
|
}
|
|
4636
5073
|
__name(parseMlsDevKeyPackageId, "parseMlsDevKeyPackageId");
|
|
4637
5074
|
function parseMlsDevKeyPackageCommand(value) {
|
|
4638
|
-
return
|
|
5075
|
+
return boundary11(() => {
|
|
4639
5076
|
const input = readMlsDataRecord(value, ["version", "op", "keyPackageId", "credential"]);
|
|
4640
5077
|
const token2 = parseMlsDevCredentialToken(input.credential);
|
|
4641
5078
|
parseMlsDevEnrollmentPrincipal(token2.unsigned.claims.device.principal);
|
|
@@ -4649,7 +5086,7 @@ function parseMlsDevKeyPackageCommand(value) {
|
|
|
4649
5086
|
}
|
|
4650
5087
|
__name(parseMlsDevKeyPackageCommand, "parseMlsDevKeyPackageCommand");
|
|
4651
5088
|
function parseMlsDevKeyPackagePayload(value) {
|
|
4652
|
-
return
|
|
5089
|
+
return boundary11(() => {
|
|
4653
5090
|
const input = readMlsDataRecord(value, [
|
|
4654
5091
|
"version",
|
|
4655
5092
|
"keyPackageId",
|
|
@@ -4664,7 +5101,7 @@ function parseMlsDevKeyPackagePayload(value) {
|
|
|
4664
5101
|
"expiresAtMs"
|
|
4665
5102
|
]);
|
|
4666
5103
|
const notBeforeMs = requireMlsInteger(input.notBeforeMs), expiresAtMs = requireMlsInteger(input.expiresAtMs);
|
|
4667
|
-
if (notBeforeMs % 1e3 !== 0 || expiresAtMs % 1e3 !== 0 || expiresAtMs <= notBeforeMs || expiresAtMs - notBeforeMs > 864e5)
|
|
5104
|
+
if (notBeforeMs % 1e3 !== 0 || expiresAtMs % 1e3 !== 0 || expiresAtMs <= notBeforeMs || expiresAtMs - notBeforeMs > 864e5) invalid14();
|
|
4668
5105
|
return bounded9({
|
|
4669
5106
|
version: requireMlsLiteral(input.version, 1),
|
|
4670
5107
|
keyPackageId: requireMlsUuid(input.keyPackageId),
|
|
@@ -4672,9 +5109,9 @@ function parseMlsDevKeyPackagePayload(value) {
|
|
|
4672
5109
|
credentialId: requireMlsUuid(input.credentialId),
|
|
4673
5110
|
credentialGeneration: requireMlsInteger(input.credentialGeneration, 1),
|
|
4674
5111
|
ciphersuite: requireMlsLiteral(input.ciphersuite, 1),
|
|
4675
|
-
signaturePublicKey:
|
|
4676
|
-
keyPackage:
|
|
4677
|
-
keyPackageRef:
|
|
5112
|
+
signaturePublicKey: encoded8(input.signaturePublicKey, 32),
|
|
5113
|
+
keyPackage: wire3(input.keyPackage),
|
|
5114
|
+
keyPackageRef: encoded8(input.keyPackageRef, 32),
|
|
4678
5115
|
notBeforeMs,
|
|
4679
5116
|
expiresAtMs
|
|
4680
5117
|
}, MLS_DEV_KEY_PACKAGE_PREPARATION_MAX_BYTES);
|
|
@@ -4682,7 +5119,7 @@ function parseMlsDevKeyPackagePayload(value) {
|
|
|
4682
5119
|
}
|
|
4683
5120
|
__name(parseMlsDevKeyPackagePayload, "parseMlsDevKeyPackagePayload");
|
|
4684
5121
|
function parseMlsDevKeyPackagePreparation(value) {
|
|
4685
|
-
return
|
|
5122
|
+
return boundary11(() => {
|
|
4686
5123
|
const input = readMlsDataRecord(value, ["wrappedState", "payload"]);
|
|
4687
5124
|
return bounded9(
|
|
4688
5125
|
{ wrappedState: wrapped2(input.wrappedState), payload: parseMlsDevKeyPackagePayload(input.payload) },
|
|
@@ -4692,6 +5129,74 @@ function parseMlsDevKeyPackagePreparation(value) {
|
|
|
4692
5129
|
}
|
|
4693
5130
|
__name(parseMlsDevKeyPackagePreparation, "parseMlsDevKeyPackagePreparation");
|
|
4694
5131
|
|
|
5132
|
+
// ../backend-contracts/src/mlsPrincipalAlias.ts
|
|
5133
|
+
var MLS_PRINCIPAL_ALIAS_PREFIX = "mlsa_";
|
|
5134
|
+
var ALIAS_PATTERN = /^mlsa_[A-Za-z0-9_-]{43}$/;
|
|
5135
|
+
var utf89 = new TextEncoder();
|
|
5136
|
+
var domain3 = utf89.encode("canon.mls.principal-alias.v1\0");
|
|
5137
|
+
function invalid15() {
|
|
5138
|
+
throw new Error("INVALID_MLS_PRINCIPAL_ALIAS");
|
|
5139
|
+
}
|
|
5140
|
+
__name(invalid15, "invalid");
|
|
5141
|
+
function isMlsPrincipalAlias(value) {
|
|
5142
|
+
return typeof value === "string" && ALIAS_PATTERN.test(value);
|
|
5143
|
+
}
|
|
5144
|
+
__name(isMlsPrincipalAlias, "isMlsPrincipalAlias");
|
|
5145
|
+
function isMlsAccountId(value) {
|
|
5146
|
+
return isMlsPrincipalId(value) && !value.includes("/") && value !== "." && value !== "..";
|
|
5147
|
+
}
|
|
5148
|
+
__name(isMlsAccountId, "isMlsAccountId");
|
|
5149
|
+
function accountId(value) {
|
|
5150
|
+
if (!isMlsAccountId(value) || ALIAS_PATTERN.test(value)) invalid15();
|
|
5151
|
+
return value;
|
|
5152
|
+
}
|
|
5153
|
+
__name(accountId, "accountId");
|
|
5154
|
+
function parseMlsPrincipalOpening(value) {
|
|
5155
|
+
try {
|
|
5156
|
+
const input = readMlsDataRecord(value, ["type", "accountId", "salt"]);
|
|
5157
|
+
if (input.type !== "human" && input.type !== "ai_agent") invalid15();
|
|
5158
|
+
decodeCanonicalMlsBase64Url(input.salt, 32);
|
|
5159
|
+
return Object.freeze({ type: input.type, accountId: accountId(input.accountId), salt: input.salt });
|
|
5160
|
+
} catch {
|
|
5161
|
+
return invalid15();
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
__name(parseMlsPrincipalOpening, "parseMlsPrincipalOpening");
|
|
5165
|
+
function encodeMlsPrincipalAliasInput(environmentId, opening) {
|
|
5166
|
+
const value = parseMlsPrincipalOpening(opening);
|
|
5167
|
+
const environment = utf89.encode(requireMlsEnvironmentId(environmentId));
|
|
5168
|
+
const id7 = utf89.encode(value.accountId);
|
|
5169
|
+
const output = new Uint8Array(domain3.length + 4 + environment.length + 1 + 4 + id7.length + 32);
|
|
5170
|
+
const view = new DataView(output.buffer);
|
|
5171
|
+
let offset = 0;
|
|
5172
|
+
const raw = /* @__PURE__ */ __name((bytes) => {
|
|
5173
|
+
output.set(bytes, offset);
|
|
5174
|
+
offset += bytes.length;
|
|
5175
|
+
}, "raw");
|
|
5176
|
+
const lp = /* @__PURE__ */ __name((bytes) => {
|
|
5177
|
+
view.setUint32(offset, bytes.length);
|
|
5178
|
+
offset += 4;
|
|
5179
|
+
raw(bytes);
|
|
5180
|
+
}, "lp");
|
|
5181
|
+
raw(domain3);
|
|
5182
|
+
lp(environment);
|
|
5183
|
+
output[offset++] = value.type === "human" ? 0 : 1;
|
|
5184
|
+
lp(id7);
|
|
5185
|
+
raw(decodeCanonicalMlsBase64Url(value.salt, 32));
|
|
5186
|
+
return output;
|
|
5187
|
+
}
|
|
5188
|
+
__name(encodeMlsPrincipalAliasInput, "encodeMlsPrincipalAliasInput");
|
|
5189
|
+
function parseMlsRoomIdentities(value) {
|
|
5190
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) invalid15();
|
|
5191
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
5192
|
+
for (const [alias, opening] of Object.entries(readMlsDataRecord(value, Object.keys(value)))) {
|
|
5193
|
+
if (!isMlsPrincipalAlias(alias)) invalid15();
|
|
5194
|
+
output[alias] = parseMlsPrincipalOpening(opening);
|
|
5195
|
+
}
|
|
5196
|
+
return Object.freeze(output);
|
|
5197
|
+
}
|
|
5198
|
+
__name(parseMlsRoomIdentities, "parseMlsRoomIdentities");
|
|
5199
|
+
|
|
4695
5200
|
// ../endpoint/src/runtime-api-origins.generated.ts
|
|
4696
5201
|
var RUNTIME_API_ORIGINS = {
|
|
4697
5202
|
"canon-dev-v1": "https://dev.api.canonmail.com",
|
|
@@ -4699,76 +5204,86 @@ var RUNTIME_API_ORIGINS = {
|
|
|
4699
5204
|
};
|
|
4700
5205
|
|
|
4701
5206
|
// ../endpoint/src/mls-crypto.ts
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
}
|
|
4709
|
-
|
|
5207
|
+
function mlsDevDeviceHttpConfiguration(environmentId) {
|
|
5208
|
+
if (!isMlsEnvironmentId(environmentId)) invalid16();
|
|
5209
|
+
const origin = RUNTIME_API_ORIGINS[environmentId];
|
|
5210
|
+
return Object.freeze({
|
|
5211
|
+
enrollmentUrl: `${origin}/mls/enrollment`,
|
|
5212
|
+
credentialUrl: `${origin}/mls/credential`,
|
|
5213
|
+
reconciliationUrl: `${origin}/mls/reconciliation`,
|
|
5214
|
+
keyPackagesUrl: `${origin}/mls/key-packages`,
|
|
5215
|
+
trustedOrigins: Object.freeze([origin])
|
|
5216
|
+
});
|
|
5217
|
+
}
|
|
5218
|
+
__name(mlsDevDeviceHttpConfiguration, "mlsDevDeviceHttpConfiguration");
|
|
5219
|
+
function invalid16() {
|
|
4710
5220
|
throw new Error("MLS_DEV_CLIENT_CONFIGURATION_INVALID");
|
|
4711
5221
|
}
|
|
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(
|
|
5222
|
+
__name(invalid16, "invalid");
|
|
5223
|
+
function record4(value, keys2) {
|
|
5224
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) invalid16();
|
|
5225
|
+
const fields2 = Object.getOwnPropertyDescriptors(value);
|
|
5226
|
+
if (Reflect.ownKeys(fields2).length !== keys2.length || keys2.some((key) => !Object.hasOwn(fields2, key) || !Object.hasOwn(fields2[key], "value"))) invalid16();
|
|
4717
5227
|
return value;
|
|
4718
5228
|
}
|
|
4719
|
-
__name(
|
|
5229
|
+
__name(record4, "record");
|
|
4720
5230
|
function primeOrderPoint(value) {
|
|
4721
5231
|
const point = ed25519.Point.fromBytes(value, false);
|
|
4722
5232
|
return !point.isSmallOrder() && point.isTorsionFree();
|
|
4723
5233
|
}
|
|
4724
5234
|
__name(primeOrderPoint, "primeOrderPoint");
|
|
4725
|
-
function issuerKeys(value) {
|
|
4726
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > 8)
|
|
5235
|
+
function issuerKeys(value, environmentId, receipt = false) {
|
|
5236
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 8) invalid16();
|
|
4727
5237
|
const seen = /* @__PURE__ */ new Set();
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
const
|
|
4731
|
-
|
|
5238
|
+
let environment = environmentId;
|
|
5239
|
+
return Object.freeze(Array.from(value, (item2) => {
|
|
5240
|
+
const key = record4(item2, ["keyVersion", "signaturePublicKey"]);
|
|
5241
|
+
const keyVersion = receipt ? parseMlsDevReceiptKeyVersion(key.keyVersion, environment) : parseMlsDevIssuerKeyVersion(key.keyVersion, environment);
|
|
5242
|
+
environment ??= receipt ? mlsEnvironmentOfReceiptKeyVersion(keyVersion) : mlsEnvironmentOfIssuerKeyVersion(keyVersion);
|
|
5243
|
+
if (seen.has(keyVersion) || typeof key.signaturePublicKey !== "string" || !primeOrderPoint(decodeCanonicalMlsBase64Url(key.signaturePublicKey, 32))) invalid16();
|
|
4732
5244
|
seen.add(keyVersion);
|
|
4733
|
-
return Object.freeze({ keyVersion, signaturePublicKey:
|
|
5245
|
+
return Object.freeze({ keyVersion, signaturePublicKey: key.signaturePublicKey });
|
|
4734
5246
|
}));
|
|
4735
5247
|
}
|
|
4736
5248
|
__name(issuerKeys, "issuerKeys");
|
|
4737
5249
|
function parseMlsDevClientConfiguration(value) {
|
|
4738
5250
|
try {
|
|
4739
|
-
const
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
5251
|
+
const listed = !!value && typeof value === "object" && Object.hasOwn(value, "principalIds");
|
|
5252
|
+
const input = record4(value, ["version", "environmentId", "enabled", ...listed ? ["principalIds"] : [], "issuerKeys", "receiptKeys"]);
|
|
5253
|
+
if (input.version !== 1 || !isMlsEnvironmentId(input.environmentId) || input.enabled !== true || listed && (!Array.isArray(input.principalIds) || input.principalIds.length < 1 || input.principalIds.length > 100)) invalid16();
|
|
5254
|
+
const environmentId = input.environmentId;
|
|
5255
|
+
const principalIds = listed ? Array.from(input.principalIds, (id7) => parseMlsDevEnrollmentPrincipal({ type: "human", id: id7 }).id) : void 0;
|
|
5256
|
+
if (principalIds && new Set(principalIds).size !== principalIds.length) invalid16();
|
|
4743
5257
|
return Object.freeze({
|
|
4744
5258
|
version: 1,
|
|
4745
|
-
environmentId
|
|
5259
|
+
environmentId,
|
|
4746
5260
|
enabled: true,
|
|
4747
|
-
principalIds: Object.freeze(principalIds),
|
|
4748
|
-
issuerKeys: issuerKeys(input.issuerKeys)
|
|
5261
|
+
...principalIds ? { principalIds: Object.freeze(principalIds) } : {},
|
|
5262
|
+
issuerKeys: issuerKeys(input.issuerKeys, environmentId),
|
|
5263
|
+
receiptKeys: issuerKeys(input.receiptKeys, environmentId, true)
|
|
4749
5264
|
});
|
|
4750
5265
|
} catch {
|
|
4751
|
-
return
|
|
5266
|
+
return invalid16();
|
|
4752
5267
|
}
|
|
4753
5268
|
}
|
|
4754
5269
|
__name(parseMlsDevClientConfiguration, "parseMlsDevClientConfiguration");
|
|
4755
5270
|
function createMlsDevCredentialVerifier(options) {
|
|
4756
5271
|
let trusted;
|
|
4757
5272
|
try {
|
|
4758
|
-
trusted = new Map(issuerKeys(options.issuerKeys).map((
|
|
5273
|
+
trusted = new Map(issuerKeys(options.issuerKeys).map((key) => [key.keyVersion, key.signaturePublicKey]));
|
|
4759
5274
|
} catch {
|
|
4760
|
-
return
|
|
5275
|
+
return invalid16();
|
|
4761
5276
|
}
|
|
4762
5277
|
const clock = options.clock ?? Date.now;
|
|
4763
5278
|
return async (value) => {
|
|
4764
5279
|
try {
|
|
4765
5280
|
const receipt = parseMlsDevCredentialReceipt(value);
|
|
4766
5281
|
const parsed = parseMlsDevCredentialToken(receipt.credential);
|
|
4767
|
-
const
|
|
5282
|
+
const key = trusted.get(parsed.unsigned.keyVersion);
|
|
4768
5283
|
const now = clock();
|
|
4769
5284
|
const { issuedAtMs, expiresAtMs } = parsed.unsigned.claims;
|
|
4770
|
-
if (!
|
|
4771
|
-
const publicKey = decodeCanonicalMlsBase64Url(
|
|
5285
|
+
if (!key || !Number.isSafeInteger(now) || now < issuedAtMs || now >= expiresAtMs) return false;
|
|
5286
|
+
const publicKey = decodeCanonicalMlsBase64Url(key, 32);
|
|
4772
5287
|
const signature = decodeCanonicalMlsBase64Url(parsed.signature, 64);
|
|
4773
5288
|
if (!primeOrderPoint(publicKey) || !primeOrderPoint(signature.subarray(0, 32))) return false;
|
|
4774
5289
|
return ed25519.verify(signature, new TextEncoder().encode(parsed.signingInput), publicKey, { zip215: false });
|
|
@@ -4782,7 +5297,16 @@ __name(createMlsDevCredentialVerifier, "createMlsDevCredentialVerifier");
|
|
|
4782
5297
|
export {
|
|
4783
5298
|
__name,
|
|
4784
5299
|
getCanonEnvironmentContract,
|
|
5300
|
+
CANON_CLIENT_HEADER,
|
|
5301
|
+
CLIENT_UPDATE_REQUIRED,
|
|
5302
|
+
isCanonClientVersion,
|
|
5303
|
+
formatCanonClientHeader,
|
|
4785
5304
|
sanitizeDownloadFileName,
|
|
5305
|
+
describeAttachment,
|
|
5306
|
+
isCanonMediaOrigin,
|
|
5307
|
+
isCanonUploadUrl,
|
|
5308
|
+
resolveAllowedUploadMimeType,
|
|
5309
|
+
isMlsEnvironmentId,
|
|
4786
5310
|
MLS_DEV_ENROLLMENT_MAX_BYTES,
|
|
4787
5311
|
parseMlsDevEnrollmentPrincipal,
|
|
4788
5312
|
parseMlsDevEnrollmentBegin,
|
|
@@ -4799,6 +5323,8 @@ export {
|
|
|
4799
5323
|
createMlsDevCredentialSigningInput,
|
|
4800
5324
|
parseMlsDevCredentialToken,
|
|
4801
5325
|
parseMlsDevCredentialReceipt,
|
|
5326
|
+
MLS_DEV_GROUP_RECEIPT_CLOCK_SKEW,
|
|
5327
|
+
isMlsDevReceiptClockSkew,
|
|
4802
5328
|
parseMlsDevGroupFeedEntry,
|
|
4803
5329
|
parseMlsDevGroupFeedQuery,
|
|
4804
5330
|
parseMlsDevGroupFeedResult,
|
|
@@ -4808,23 +5334,36 @@ export {
|
|
|
4808
5334
|
parseMlsDevKeyPackagePublicationReceipt,
|
|
4809
5335
|
parseMlsDevKeyPackageClaim,
|
|
4810
5336
|
parseMlsDevKeyPackageClaimReceipt,
|
|
5337
|
+
MLS_DEV_INTERACTION_LIMITS,
|
|
5338
|
+
parseMlsDevInteractionRequestBody,
|
|
5339
|
+
parseMlsDevInteractionResponseBody,
|
|
5340
|
+
parseMlsDevInteractionWithdrawBody,
|
|
5341
|
+
parseMlsDevApplicationPayload,
|
|
5342
|
+
assertMlsDevInteractionPayloadBinding,
|
|
5343
|
+
isMlsDevInteractionAnswerAllowed,
|
|
4811
5344
|
parseMlsDevGroupCreateConversation,
|
|
4812
5345
|
parseMlsDevGroupHead,
|
|
4813
5346
|
parseMlsDevGroupMarker,
|
|
4814
5347
|
parseMlsDevGroupRoster,
|
|
5348
|
+
parseMlsDevRoomRemoval,
|
|
4815
5349
|
parseMlsDevGroupControl,
|
|
4816
5350
|
parseMlsDevGroupAcceptedControl,
|
|
5351
|
+
parseMlsDevGroupAdmissionsResult,
|
|
4817
5352
|
parseMlsDevGroupQuery,
|
|
4818
5353
|
parseMlsDevGroupQueryResult,
|
|
4819
5354
|
parseMlsDevGroupContext,
|
|
4820
5355
|
parseMlsDevGroupCommand,
|
|
4821
5356
|
parseMlsDevGroupPreparation,
|
|
4822
|
-
|
|
5357
|
+
MLS_DEV_TEXT_PROTOCOL_V3,
|
|
5358
|
+
isMlsDevTextProtocol,
|
|
5359
|
+
mlsDevProtocolCarriesCards,
|
|
5360
|
+
MLS_DEV_REPLY_QUOTE_MAX_CHARACTERS,
|
|
5361
|
+
MLS_DEV_TEXT_MAX_BYTES,
|
|
4823
5362
|
parseMlsDevApplicationReceipt,
|
|
4824
5363
|
parseMlsDevTextPayload,
|
|
5364
|
+
parseMlsDevItemPayload,
|
|
4825
5365
|
encodeMlsDevApplicationEnvelopeHash,
|
|
4826
5366
|
RUNTIME_CARD_FIELD_TYPES,
|
|
4827
|
-
RUNTIME_CARD_LIMITS,
|
|
4828
5367
|
RUNTIME_CARD_LINE_ITEM_COLUMN_TYPES,
|
|
4829
5368
|
readMembershipChangeMetadata,
|
|
4830
5369
|
formatMembershipChangeNotice,
|
|
@@ -4850,36 +5389,20 @@ export {
|
|
|
4850
5389
|
ENDPOINT_ID_PATTERN,
|
|
4851
5390
|
ENDPOINT_ACTION_PATTERN,
|
|
4852
5391
|
isEndpointOperationReceipt,
|
|
5392
|
+
CONNECTOR_RELAY_INSTALLATION_PREFIX,
|
|
5393
|
+
CONNECTOR_RELAY_INSTALLATION_PATTERN,
|
|
5394
|
+
isConnectorRelayInstallation,
|
|
5395
|
+
isConnectorRelayCredential,
|
|
5396
|
+
CONNECTOR_RELAY_MAX_BODY_BYTES,
|
|
5397
|
+
CONNECTOR_RELAY_MAX_RESPONSE_BYTES,
|
|
5398
|
+
CONNECTOR_RELAY_ANSWER_WITHIN_MS,
|
|
5399
|
+
connectorRelayResponseBytes,
|
|
5400
|
+
ConnectorRelayRequestError,
|
|
5401
|
+
parseConnectorRelayRequest,
|
|
4853
5402
|
mergeDeliveryObservation,
|
|
4854
5403
|
projectDeliveryStatus,
|
|
4855
5404
|
classifyTransferHttpFailure,
|
|
4856
5405
|
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
5406
|
MLS_DEV_CREDENTIAL_RECONCILIATION_MAX_BYTES,
|
|
4884
5407
|
parseMlsDevCredentialReconciliationBegin,
|
|
4885
5408
|
parseMlsDevCredentialReconciliationChallenge,
|
|
@@ -4893,7 +5416,12 @@ export {
|
|
|
4893
5416
|
parseMlsDevKeyPackageCommand,
|
|
4894
5417
|
parseMlsDevKeyPackagePayload,
|
|
4895
5418
|
parseMlsDevKeyPackagePreparation,
|
|
4896
|
-
|
|
5419
|
+
MLS_PRINCIPAL_ALIAS_PREFIX,
|
|
5420
|
+
isMlsPrincipalAlias,
|
|
5421
|
+
parseMlsPrincipalOpening,
|
|
5422
|
+
encodeMlsPrincipalAliasInput,
|
|
5423
|
+
parseMlsRoomIdentities,
|
|
5424
|
+
mlsDevDeviceHttpConfiguration,
|
|
4897
5425
|
parseMlsDevClientConfiguration,
|
|
4898
5426
|
createMlsDevCredentialVerifier
|
|
4899
5427
|
};
|