calmlens 0.20.0 → 0.22.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.
data/sig/types.rbs CHANGED
@@ -27,8 +27,10 @@ module CalmLens
27
27
 
28
28
  # ---- Assets (Asset.ts) ----------------------------------------------------------------------
29
29
 
30
- # Where the asset is in the pipeline.
31
- type asset_classification_status = "pending" | "processing" | "completed" | "failed"
30
+ # Where the asset is in the pipeline: `queued` while it waits for its content (a multipart upload
31
+ # still receiving parts) or its turn, `processing`, then `completed` or `failed`. The asset is
32
+ # settled once it is `completed` or `failed`.
33
+ type asset_classification_status = "queued" | "processing" | "completed" | "failed"
32
34
  # A moderation decision, which also decides delivery: served to anyone with access (`allowed`),
33
35
  # served like `allowed` but marked so your app reduces its reach (`limited`), only through signed
34
36
  # URLs or API keys (`age-restricted`), not served (`blocked`), or not served with the stored
@@ -41,7 +43,7 @@ module CalmLens
41
43
  type asset_content_status = "temporary" | "stored" | "removed"
42
44
  type asset_content_removal_reason = "policy" | "retention" | "requested" | "parent-removed" | "disposed"
43
45
  # Why processing failed, while the classification status is `failed`.
44
- type asset_classification_failure_reason = "file-too-large" | "duration-limit" | "resolution-limit" | "unsupported-format" | "undecodable-content" | "multiple-tracks" | "type-mismatch" | "free-upload-limit" | "child-failed" | "processing-error"
46
+ type asset_classification_failure_reason = "file-too-large" | "duration-limit" | "resolution-limit" | "unsupported-format" | "undecodable-content" | "multiple-tracks" | "type-mismatch" | "free-uploads-exhausted" | "media-limit" | "uploads-paused" | "too-many-items" | "processing-error"
45
47
  type asset_type = "image" | "video" | "audio" | "plaintext-document" | "multimedia-document" | "website" | "file"
46
48
  type asset_visibility = "public" | "private"
47
49
  # FLAGGED_CONTENT_ACTION_SCHEMA in PolicyRule.ts.
@@ -66,7 +68,11 @@ module CalmLens
66
68
  ?ocr: { enabled: bool }?,
67
69
  ?person_detection: { enabled: bool }?,
68
70
  ?face_detection: { enabled: bool, ?age_estimation: bool }?,
69
- ?link_detection: { enabled: bool }?
71
+ ?link_detection: { enabled: bool }?,
72
+ # Preview. Prepares an adaptive stream (HLS) of audio or video, served at the `hls` resource and
73
+ # played by the `embed` resource; needs `keep_content_after_processing`. Left out, the project's
74
+ # streaming setting decides.
75
+ ?streaming: { enabled: bool }?
70
76
  }
71
77
  type processing_options_input = {
72
78
  ?keep_content_after_processing: bool?,
@@ -76,20 +82,6 @@ module CalmLens
76
82
  ?features: processing_features_input?
77
83
  }
78
84
 
79
- type processing_features = {
80
- ?"ocr" => { "enabled" => bool }?,
81
- ?"personDetection" => { "enabled" => bool }?,
82
- ?"faceDetection" => { "enabled" => bool, ?"ageEstimation" => bool }?,
83
- ?"linkDetection" => { "enabled" => bool }?,
84
- ?"aiGeneratedDetection" => { "enabled" => bool }?
85
- }
86
- # The processing options an asset was uploaded with.
87
- type processing_options = {
88
- ?"keepContentAfterProcessing" => bool?,
89
- ?"storeSensitiveMetadata" => bool?,
90
- ?"features" => processing_features?
91
- }
92
-
93
85
  type checksums = {
94
86
  ?"md5" => String?,
95
87
  ?"sha1" => String?,
@@ -97,20 +89,43 @@ module CalmLens
97
89
  ?"sha384" => String?,
98
90
  ?"sha512" => String?
99
91
  }
100
- # The picture served at the rendered resource; it remains after the content is removed.
101
- type asset_image = {
92
+ # One rendition of an adaptive stream: `video` for a picture size (video renditions carry no sound;
93
+ # they play with the `audio` rendition), or `audio`. `bandwidth` is the peak bit rate in bits per
94
+ # second, counting the audio a video rendition plays with; `codecs` are RFC 6381 names.
95
+ type asset_hls_rendition = {
96
+ "kind" => "video" | "audio",
97
+ "width" => Integer?,
98
+ "height" => Integer?,
99
+ "bandwidth" => Integer,
100
+ "codecs" => String
101
+ }
102
+ # Preview. The adaptive stream of audio or video: HLS with fragmented MP4 (CMAF) segments, served
103
+ # at the CDN's `hls` resource (`hls/index.m3u8`) and played by its `embed` resource. Renditions list
104
+ # video from the largest down, then audio.
105
+ type asset_hls = { "renditions" => Array[asset_hls_rendition] }
106
+ type asset_hls_status = { "status" => String, "reason" => String? }
107
+ type asset_rendered_kind = "image" | "pdf" | "video" | "audio"
108
+ # The asset's one displayable file, as the CDN's `rendered` resource serves it: an image, a PDF, or
109
+ # browser-safe video or audio. `peaks` is an audio waveform's normalised peaks.
110
+ type asset_rendered = {
111
+ "kind" => asset_rendered_kind,
112
+ "format" => String,
113
+ "width" => Integer?,
114
+ "height" => Integer?,
115
+ ?"peaks" => Array[Float]
116
+ }
117
+ # The still image that stands for the asset, as the CDN's `preview` resource serves it; the
118
+ # `placeholder` resource is its ThumbHash as a small blurred PNG.
119
+ type asset_preview = {
102
120
  "width" => Integer?,
103
121
  "height" => Integer?,
104
122
  "thumbhash" => String?,
105
- "averageColor" => String?,
106
- "perceptualHash" => String?,
107
- # How much detail `perceptualHash` captured, from 0 to 100; hashes below 50 match unrelated pictures.
108
- "perceptualHashQuality" => Integer?
123
+ "averageColor" => String?
109
124
  }
110
125
  type safety_hold = { "placedAt" => Integer, "reason" => "suspected-illegal-content", ?"disposedAt" => Integer? }
111
126
  # `type: "string"` carries the text in `content`; `type: "document"` stores it under `id`.
112
127
  type extracted_text = { "type" => "string" | "document", ?"content" => String, ?"id" => String }
113
- # Descriptive metadata embedded in an uploaded image. `capturedAt` is ISO 8601, with a UTC offset
128
+ # Descriptive metadata embedded in an uploaded file. `capturedAt` is ISO 8601, with a UTC offset
114
129
  # only when the file records one. `digitalSourceType` is the IPTC code the file declares, such as
115
130
  # `digitalCapture`. `containsLocation` says whether the uploaded file embedded GPS coordinates;
116
131
  # `creator` and `location` are sensitive and recorded only when `storeSensitiveMetadata` is on.
@@ -129,48 +144,95 @@ module CalmLens
129
144
  }
130
145
 
131
146
  type asset_upload = { "status" => "uploading" | "completed", "updatedAt" => Integer?, "partsReceived" => Integer? }
147
+ # How many sections of long audio or video are analysed so far.
148
+ type classification_progress = { "completedSections" => Integer, "totalSections" => Integer }
132
149
  type asset_classification = {
133
150
  "status" => asset_classification_status,
134
151
  # `null` unless `status` is `failed`.
135
152
  "failureReason" => asset_classification_failure_reason?,
136
153
  "updatedAt" => Integer?,
137
154
  "completedAt" => Integer?,
155
+ # While long audio or video is analysed in sections and `status` is `processing`: how many of its
156
+ # sections are done. `nil` otherwise.
157
+ "progress" => classification_progress?,
158
+ # Whether the results came from one of the project's test inputs rather than analysing the
159
+ # content. Never use test results, such as age ranges, for real decisions.
160
+ "testInput" => bool,
138
161
  "moderation" => asset_moderation,
139
- ?"personDetection" => person_detection_result,
140
- ?"faceDetection" => face_detection_result,
141
- ?"linkDetection" => link_detection_result,
142
- # Preview. Where the asset's links lead; present when the project's policy acts on links.
143
- ?"linkSafety" => link_safety_result,
144
- ?"aiGeneratedDetection" => ai_generated_detection_result,
145
- # Where OCR found text, and personal information within it; redaction transforms blur these regions.
146
- ?"textRegions" => Array[text_region],
147
- ?"labels" => Array[classification_label]?
148
- }
162
+ # OCR's result for an image processed with OCR turned on; `nil` when OCR didn't run.
163
+ "ocr" => ocr_result?,
164
+ # Link detection's result for an image; `nil` when it didn't run.
165
+ "linkDetection" => link_detection_result?,
166
+ # Preview. Person and face detection's results; `nil` when they didn't run.
167
+ ?"personDetection" => person_detection_result?,
168
+ ?"faceDetection" => face_detection_result?,
169
+ # Preview. Where the asset's links lead when the project's policy acts on links; otherwise `nil`.
170
+ ?"linkSafety" => link_safety_result?,
171
+ ?"aiGeneratedDetection" => ai_generated_detection_result?
172
+ }
173
+ # Who made a review: a person (`user`, also when an assistant's key acts for one) or an API key
174
+ # (`api-key`), with the user's or API key's ID, `nil` only when it was not recorded.
175
+ type review_actor = { "type" => "user" | "api-key", "id" => String? }
149
176
  type asset_review = {
150
177
  "id" => String,
151
178
  "decision" => asset_moderation_decision,
152
179
  "note" => String?,
153
- ?"reviewerName" => String?,
154
- ?"reviewerEmail" => String?,
180
+ "decidedBy" => review_actor,
155
181
  "reviewedAt" => Integer,
156
- "appliedToAsset" => bool
182
+ # Whether the decision was applied to the asset; `false` for a review queue decision recorded
183
+ # without changing the asset's status.
184
+ "applied" => bool
185
+ }
186
+ # A stricter delivery decision in one region the project serves: the status in force there, the
187
+ # mandatory rule that sets it and the category whose findings it acted on, when it acts on one.
188
+ type regional_decision = {
189
+ "region" => String,
190
+ "status" => "limited" | "age-restricted" | "blocked" | "purged",
191
+ "rule" => String,
192
+ ?"category" => String
193
+ }
194
+ # What decided the automated status: the `policy`'s categories, the asset's `children`, a
195
+ # `hash-list` match, a `mandatory-rule` or `link-safety`.
196
+ type moderation_source = "policy" | "children" | "hash-list" | "mandatory-rule" | "link-safety"
197
+ # The policy that made a decision: its ID, its revision then and, for a policy from a preset, the
198
+ # preset release it used.
199
+ type decision_policy = {
200
+ "id" => String,
201
+ "revision" => Integer,
202
+ "preset" => { "id" => String, "version" => Integer }?
203
+ }
204
+ # A review the human review queue is waiting for: when the asset joined it (Unix milliseconds),
205
+ # why, and the categories that made the decision uncertain.
206
+ type asset_pending_review = {
207
+ "requestedAt" => Integer,
208
+ "reason" => "sampled" | "uncertain",
209
+ "uncertainCategories" => Array[String]
157
210
  }
158
- type regional_decision = { "region" => String, "action" => String, "rule" => String, ?"category" => String }
159
- type test_regional_action = { "region" => String, "action" => String }
211
+ # Every field but `status`, `manualReviews`, `flaggedCategories`, `regions` and `timeline` is `nil`
212
+ # until moderation decides; `pendingReview` is `nil` when no review is waiting.
160
213
  type asset_moderation = {
161
- ?"testCategoryActions" => Hash[String, String],
162
- ?"testRegionalActions" => Array[test_regional_action],
163
214
  "manualReviews" => Array[asset_review],
164
215
  "status" => asset_moderation_status,
165
- ?"automatedAction" => flagged_content_action,
166
- ?"source" => String, ?"rule" => String,
216
+ # The policy's original decision; a reviewer's decision can change `status` without changing it.
217
+ "automatedStatus" => asset_moderation_decision?,
218
+ # The policy that made the automated decision, as it was then.
219
+ "policy" => decision_policy?,
220
+ # The review the human review queue is waiting for, set with the automated decision that
221
+ # joined the queue; `nil` when no review is waiting.
222
+ "pendingReview" => asset_pending_review?,
223
+ "source" => moderation_source?,
224
+ # The mandatory rule that decided, when `source` is `mandatory-rule`; otherwise `nil`.
225
+ "rule" => String?,
167
226
  "flaggedCategories" => Array[String], "regions" => Array[regional_decision],
168
- ?"explanationComplete" => bool,
169
- ?"categories" => Hash[String, category_policy_score],
170
- # Preview. What the policy's link-safety section made of the asset's links; absent when the
227
+ "explanationComplete" => bool?,
228
+ "categories" => Hash[String, category_policy_score]?,
229
+ # Preview. What the policy's link-safety section made of the asset's links; `nil` when the
171
230
  # policy doesn't act on links.
172
- ?"linkSafety" => link_safety_decision,
173
- ?"raw" => moderation_raw
231
+ ?"linkSafety" => link_safety_decision?,
232
+ # An audio or video asset's findings timeline: how many segments `assets.list_timeline` lists.
233
+ # `null` for other assets and media without findings.
234
+ "timeline" => moderation_timeline?,
235
+ "raw" => moderation_raw?
174
236
  }
175
237
  # ASSET_CONTENT_SCHEMA. `format` is the MIME type.
176
238
  type asset_content = {
@@ -182,17 +244,34 @@ module CalmLens
182
244
  "extension" => String?,
183
245
  "sizeInBytes" => Integer?,
184
246
  "checksums" => checksums?,
185
- "image" => asset_image?,
247
+ # The file's own size, length and page count, where they apply; `null` when unknown.
248
+ "width" => Integer?,
249
+ "height" => Integer?,
250
+ "durationSeconds" => Float?,
251
+ "pageCount" => Integer?,
252
+ "perceptualHash" => String?,
253
+ # How much detail `perceptualHash` captured, from 0 to 100; hashes below 50 match unrelated pictures.
254
+ "perceptualHashQuality" => Integer?,
255
+ # Metadata embedded in the uploaded file; `null` when it declares none, once the content is
256
+ # removed and while the asset is held.
257
+ "metadata" => file_metadata?,
186
258
  "extractedText" => extracted_text?,
187
- # Metadata embedded in an uploaded image; `null` when it declares none, for other asset types,
188
- # once the content is removed and while the asset is held.
189
- "fileMetadata" => file_metadata?
259
+ # `null` until processing makes it, for text, spreadsheets and other files, once the content is
260
+ # removed and while the asset is held.
261
+ "rendered" => asset_rendered?,
262
+ # Preview. The adaptive stream once streaming has prepared one for audio or video; `nil` for
263
+ # other assets, until then, once the content is removed and while the asset is held.
264
+ "hls" => asset_hls?,
265
+ "hlsStatus" => asset_hls_status?,
266
+ "preview" => asset_preview?
190
267
  }
191
268
 
192
269
  type asset = {
193
270
  "id" => String,
194
271
  "projectId" => String,
195
272
  ?"parentId" => String?,
273
+ # The pages of the parent document a child shows, from 1; empty when unknown or not a child.
274
+ ?"parentPages" => Array[Integer],
196
275
  ?"folderId" => String?,
197
276
  ?"uploadPresetId" => String?,
198
277
  "createdAt" => Integer,
@@ -202,13 +281,14 @@ module CalmLens
202
281
  ?"externalId" => String?,
203
282
  ?"externalUrl" => String?,
204
283
  "type" => asset_type,
205
- ?"metadata" => Array[metadata_item]?,
284
+ # Empty when the asset has none.
285
+ "metadata" => Array[metadata_item],
206
286
  ?"authorId" => String?,
207
287
  # The earlier posts CalmLens used; null once the content is removed.
208
288
  ?"context" => moderation_context?,
209
- ?"tags" => Array[String]?,
210
- ?"processingOptions" => processing_options?,
211
- ?"visibility" => asset_visibility?,
289
+ # Empty when the asset has none.
290
+ "tags" => Array[String],
291
+ "visibility" => asset_visibility,
212
292
  "upload" => asset_upload,
213
293
  "classification" => asset_classification,
214
294
  "content" => asset_content
@@ -219,6 +299,9 @@ module CalmLens
219
299
  type likelihood = "very-unlikely" | "unlikely" | "possible" | "likely" | "very-likely"
220
300
  # not-supported: this processing path cannot assess the signal; unavailable: assessment failed.
221
301
  type not_evaluated_reason = "not-supported" | "unavailable"
302
+ # A processing feature's reason, which adds in-children: a document's or website's images are each
303
+ # a child asset with its own result (`assets.list_children`).
304
+ type feature_not_evaluated_reason = not_evaluated_reason | "in-children"
222
305
  type classification_category = "sexual" | "violence" | "self-harm" | "harassment" | "hate" | "criminal-activity"
223
306
 
224
307
  # `status: "evaluated"` carries `someScore`, `likelihood` and optionally `allScore`;
@@ -234,14 +317,12 @@ module CalmLens
234
317
 
235
318
  # `status: "evaluated"` or `"partial"` carries `likelihood` and `score`; `"not-evaluated"`
236
319
  # carries `reason`.
237
- type policy_condition_result = { "status" => "evaluated" | "not-evaluated", ?"score" => number, ?"reason" => not_evaluated_reason }
238
320
  type category_report = {
239
321
  "status" => "evaluated" | "partial" | "not-evaluated",
240
322
  ?"likelihood" => likelihood,
241
323
  ?"score" => number,
242
324
  ?"reason" => not_evaluated_reason,
243
- ?"qualifiers" => qualifier_results,
244
- ?"policyConditions" => Hash[String, policy_condition_result]
325
+ ?"qualifiers" => qualifier_results
245
326
  }
246
327
  type classification_categories = {
247
328
  "sexual" => category_report,
@@ -253,46 +334,29 @@ module CalmLens
253
334
  "personal-information" => category_report,
254
335
  "profanity" => category_report
255
336
  }
256
- type classification_label = {
257
- "description" => String,
258
- "score" => number,
259
- ?"topicalityScore" => number?,
260
- "likelihood" => likelihood,
261
- ?"knowledgeBaseId" => String?
262
- }
263
337
  type media_segment = {
264
338
  "source" => "frame" | "speech" | "frame-text" | "clip",
265
339
  "startSeconds" => number,
266
340
  "endSeconds" => number,
267
341
  "categories" => classification_categories
268
342
  }
269
- type media_report = {
270
- "durationSeconds" => number,
271
- ?"frameIntervalSeconds" => number,
272
- "sampledFrames" => Integer,
273
- "audioTranscribed" => bool,
274
- "segments" => Array[media_segment]
275
- }
276
343
 
277
344
  # Position and size as fractions of the image width and height, from 0 to 1.
278
345
  type bounding_box = { "left" => number, "top" => number, "width" => number, "height" => number }
279
346
  type age_range = { "low" => Integer, "high" => Integer }
280
347
  type detected_person = { "box" => bounding_box, "confidence" => number }
281
348
  type detected_face = { "box" => bounding_box, "confidence" => number, ?"ageRange" => age_range }
282
- type processing_feature_stage = "beta" | "ga"
283
349
  # `status: "evaluated"` carries `people`; `"not-evaluated"` carries `reason`.
284
350
  type person_detection_result = {
285
351
  "status" => "evaluated" | "not-evaluated",
286
- "stage" => processing_feature_stage,
287
352
  ?"people" => Array[detected_person],
288
- ?"reason" => not_evaluated_reason
353
+ ?"reason" => feature_not_evaluated_reason
289
354
  }
290
355
  # `status: "evaluated"` carries `faces`; `"not-evaluated"` carries `reason`.
291
356
  type face_detection_result = {
292
357
  "status" => "evaluated" | "not-evaluated",
293
- "stage" => processing_feature_stage,
294
358
  ?"faces" => Array[detected_face],
295
- ?"reason" => not_evaluated_reason
359
+ ?"reason" => feature_not_evaluated_reason
296
360
  }
297
361
  type detected_qr_code = { "value" => String, "box" => bounding_box }
298
362
  # A web link found in a QR code or in the text OCR extracted.
@@ -300,10 +364,9 @@ module CalmLens
300
364
  # `status: "evaluated"` carries `qrCodes` and `links`; `"not-evaluated"` carries `reason`.
301
365
  type link_detection_result = {
302
366
  "status" => "evaluated" | "not-evaluated",
303
- "stage" => processing_feature_stage,
304
367
  ?"qrCodes" => Array[detected_qr_code],
305
368
  ?"links" => Array[detected_link],
306
- ?"reason" => not_evaluated_reason
369
+ ?"reason" => feature_not_evaluated_reason
307
370
  }
308
371
  # Link safety (preview, LinkSafety.ts).
309
372
  # What a link's destination is: `safe` when every reputation check passed, `adult` or `gambling`
@@ -338,7 +401,6 @@ module CalmLens
338
401
  # Preview. `status: "evaluated"` carries `links`; `"not-evaluated"` carries `reason`.
339
402
  type link_safety_result = {
340
403
  "status" => "evaluated" | "not-evaluated",
341
- "stage" => processing_feature_stage,
342
404
  ?"links" => Array[checked_link],
343
405
  ?"reason" => not_evaluated_reason
344
406
  }
@@ -355,66 +417,71 @@ module CalmLens
355
417
  },
356
418
  ?"offPlatformLinks" => { "action" => "limit" | "age-restrict" | "block", "allowedDomains" => Array[String] }
357
419
  }
358
- # Preview. The strongest action the asset's links take, the indexes into
359
- # `classification.linkSafety.links` of the links that take it, and the settings in force.
420
+ # Preview. The strongest action the asset's links take, the checked links that take it, and the
421
+ # settings in force.
360
422
  type link_safety_decision = {
361
423
  "action" => link_safety_action,
362
- "links" => Array[Integer],
424
+ "links" => Array[checked_link],
363
425
  "settings" => link_safety_settings
364
426
  }
365
427
  type provenance_signal = { "kind" => "c2pa" | "iptc-digital-source-type" | "generator-metadata", "value" => String }
366
428
  # `status: "evaluated"` carries `provenance` and `signals`; `"not-evaluated"` carries `reason`.
367
429
  type ai_generated_detection_result = {
368
430
  "status" => "evaluated" | "not-evaluated",
369
- "stage" => processing_feature_stage,
370
431
  ?"provenance" => "ai-generated" | "ai-edited" | "unknown",
371
432
  ?"signals" => Array[provenance_signal],
372
- ?"reason" => not_evaluated_reason
433
+ ?"reason" => feature_not_evaluated_reason
373
434
  }
374
435
  # Where OCR found text in an image, and whether it is personal information.
375
436
  type text_region = { "box" => bounding_box, "personalInformation" => bool }
376
- # Results of the features requested in `processingOptions.features`. OCR stores extracted text on the image
377
- # asset instead of a result here.
378
- type processing_feature_results = {
379
- ?"personDetection" => person_detection_result,
380
- ?"faceDetection" => face_detection_result,
381
- ?"linkDetection" => link_detection_result,
382
- ?"linkSafety" => link_safety_result,
383
- ?"aiGeneratedDetection" => ai_generated_detection_result
437
+ # `status: "evaluated"` carries `textRegions`, where OCR found text (redaction transforms blur
438
+ # these regions); `"not-evaluated"` carries `reason`. The text itself is `content.extractedText`.
439
+ type ocr_result = {
440
+ "status" => "evaluated" | "not-evaluated",
441
+ ?"textRegions" => Array[text_region],
442
+ ?"reason" => feature_not_evaluated_reason
384
443
  }
385
444
 
386
445
  # How strongly each assessed category meets the policy and what the policy does with it.
387
446
  type policy_rule_snapshot = {
388
447
  "when" => Array[String], "action" => flagged_content_action,
389
- ?"name" => String, ?"match" => "all", ?"unless" => Array[String]
448
+ ?"name" => String, ?"unless" => Array[String]
390
449
  }
391
- type category_policy_source = { "kind" => String, ?"index" => Integer, ?"indexes" => Array[Integer] }
450
+ # What decided a category's action: its `default` action, a policy score `below-threshold`, or the
451
+ # `allowance` or `restriction` rules listed by `indexes` (at least one, the decisive rule first).
452
+ type category_policy_source = { "kind" => "default" | "below-threshold" } |
453
+ { "kind" => "allowance" | "restriction", "indexes" => Array[Integer], "conditionScore" => Float }
392
454
  type category_policy_settings_snapshot = {
393
- "categoryAction" => flagged_content_action,
455
+ "action" => flagged_content_action,
394
456
  "allowances" => Array[policy_rule_snapshot], "restrictions" => Array[policy_rule_snapshot]
395
457
  }
396
458
  type category_policy_score = {
397
459
  "score" => number, "action" => flagged_content_action,
398
460
  ?"source" => category_policy_source, ?"settings" => category_policy_settings_snapshot
399
461
  }
462
+ type moderation_timeline = { "segmentCount" => Integer }
400
463
  type moderation_raw = {
401
464
  "categories" => classification_categories,
402
- ?"timeline" => media_report,
403
- ?"hashMatches" => Array[hash_match]
465
+ # Empty when the image matched no hash list entry.
466
+ "hashMatches" => Array[hash_match]
404
467
  }
405
468
 
406
469
  # ---- Pages and search (Page.ts, AssetSearch.ts) --------------------------------------------
407
470
 
408
- type order_direction = "asc" | "desc"
409
- type order_input = { field: String, direction: order_direction }
410
- type order = { "field" => String, "direction" => order_direction }
471
+ # A list's sort: comma-separated fields, each ascending unless prefixed with `-`, such as
472
+ # `-createdAt,id`.
473
+ type sort = String
411
474
 
412
475
  type asset_page = {
413
476
  "items" => Array[asset],
414
477
  "hasMore" => bool,
415
- "nextCursor" => String?,
416
- "pageSize" => Integer,
417
- "ordering" => Array[order]
478
+ "nextCursor" => String?
479
+ }
480
+ # One page of an audio or video asset's findings timeline, in time order.
481
+ type asset_timeline_page = {
482
+ "items" => Array[media_segment],
483
+ "hasMore" => bool,
484
+ "nextCursor" => String?
418
485
  }
419
486
 
420
487
  type asset_search_mode = "keyword" | "semantic" | "hybrid"
@@ -428,8 +495,32 @@ module CalmLens
428
495
  }
429
496
 
430
497
  type asset_batch_operation = "delete" | "remove-content"
431
- type batch_assets_response = { "queued" => bool }
432
- type signed_url = { "url" => String, "expiresAt" => Integer }
498
+ # `batchId` is named by the `batch-id` fact of the events the batch causes.
499
+ type batch_assets_response = { "batchId" => String, "status" => "queued", "assetCount" => Integer }
500
+ # A signed asset URL, with any resource, selector and viewer claims appended, and when it expires
501
+ # (milliseconds since the Unix epoch).
502
+ type signed_asset_url = { "url" => String, "expiresAt" => Integer }
503
+ # Whether the viewer an asset URL's claims describe can see the content now.
504
+ type asset_availability = "available" | "age-restricted" | "unavailable"
505
+ # ASSET_INFO_SCHEMA: what the CDN's `info` resource returns, an allowlist of the asset's public
506
+ # fields plus the viewer's `availability`.
507
+ type asset_info = {
508
+ "id" => String,
509
+ "projectId" => String,
510
+ "type" => asset_type,
511
+ "externalUrl" => String?,
512
+ "content" => {
513
+ "format" => String?,
514
+ "width" => Integer?,
515
+ "height" => Integer?,
516
+ "durationSeconds" => Float?,
517
+ "pageCount" => Integer?,
518
+ "rendered" => asset_rendered?,
519
+ "preview" => asset_preview?,
520
+ "hls" => asset_hls?
521
+ },
522
+ "availability" => asset_availability
523
+ }
433
524
 
434
525
  # ---- Transforms and lenses (AssetTransform.ts, Lens.ts) -------------------------------------
435
526
 
@@ -544,20 +635,18 @@ module CalmLens
544
635
  type transform_input = String | image_transform_options_input
545
636
 
546
637
  type lens_type = "image" | "video" | "audio" | "document" | "website"
547
- # ENSURE_LENS_BODY_SCHEMA. Changing a saved lens needs a greater `version` (default 1).
638
+ # ENSURE_LENS_BODY_SCHEMA: a lens definition, which the CDN serves once it is saved.
548
639
  type lens_definition = {
549
640
  identifier: String,
550
641
  name: String,
551
642
  type: lens_type,
552
643
  active: bool,
553
644
  ?description: String?,
554
- ?version: Integer,
555
645
  ?transform: image_transform_options_input?
556
646
  }
557
647
  type lens = {
558
648
  "id" => String,
559
649
  ?"projectId" => String?,
560
- "version" => Integer,
561
650
  "identifier" => String,
562
651
  "name" => String,
563
652
  ?"description" => String?,
@@ -567,6 +656,86 @@ module CalmLens
567
656
  "createdAt" => Integer,
568
657
  ?"updatedAt" => Integer
569
658
  }
659
+ type lens_page = {
660
+ "items" => Array[lens],
661
+ "hasMore" => bool,
662
+ "nextCursor" => String?
663
+ }
664
+
665
+ # ---- Configuration as data (UploadPreset.ts, ProjectConfiguration.ts) --------------------------
666
+
667
+ type upload_preset_access = "token" | "anyone"
668
+ # An upload preset's processing options, as the API returns them.
669
+ type upload_preset_processing_options = {
670
+ ?"keepContentAfterProcessing" => bool?,
671
+ ?"storeSensitiveMetadata" => bool?,
672
+ ?"features" => {
673
+ ?"ocr" => { "enabled" => bool }?,
674
+ ?"personDetection" => { "enabled" => bool }?,
675
+ ?"faceDetection" => { "enabled" => bool, ?"ageEstimation" => bool? }?,
676
+ ?"linkDetection" => { "enabled" => bool }?,
677
+ ?"streaming" => { "enabled" => bool }?
678
+ }?
679
+ }
680
+ # The rules for uploads made from browsers and apps. `key` is unique in its project; `revision`
681
+ # counts changes, starting at 1. Send it back with a change.
682
+ type upload_preset = {
683
+ "id" => String,
684
+ "projectId" => String,
685
+ ?"createdByUserId" => String?,
686
+ "key" => String,
687
+ "revision" => Integer,
688
+ "name" => String,
689
+ "access" => upload_preset_access,
690
+ "enabled" => bool,
691
+ ?"allowedContentTypes" => Array[String]?,
692
+ ?"maxSizeInBytes" => Integer?,
693
+ ?"folderId" => String?,
694
+ ?"policyId" => String?,
695
+ "visibility" => asset_visibility,
696
+ ?"processingOptions" => upload_preset_processing_options?,
697
+ "revealDecision" => bool,
698
+ ?"dailyUploadLimit" => Integer?,
699
+ ?"allowedOrigins" => Array[String]?,
700
+ "createdAt" => Integer,
701
+ "updatedAt" => Integer
702
+ }
703
+ type upload_preset_page = { "items" => Array[upload_preset], "hasMore" => bool, "nextCursor" => String? }
704
+ type ensure_upload_presets_response = { "uploadPresets" => Array[upload_preset] }
705
+
706
+ type remote_url_rule = { "mode" => "allow" | "deny", "domains" => Array[String] }
707
+ type human_review_settings = {
708
+ "allowedSamplePercent" => number,
709
+ "restrictedSamplePercent" => number,
710
+ "uncertaintyMarginPercent" => number,
711
+ "reviewMissingEvidence" => bool
712
+ }
713
+ type public_project_settings = {
714
+ "optOutJurisdictions" => Array[String],
715
+ "blockUnmoderatedAssets" => bool,
716
+ "storeSensitiveMetadata" => bool,
717
+ "streamingEnabled" => bool,
718
+ "aiMediaSearchEnabled" => bool,
719
+ "remoteUrlRule" => remote_url_rule?,
720
+ "websiteSubmissionsPerMinute" => Integer?,
721
+ "humanReview" => human_review_settings
722
+ }
723
+ # The project's settings, with defaults filled in. `revision` counts changes to the settings and
724
+ # metadata fields, starting at 1. Send it back with a change.
725
+ type project_settings_resource = { "projectId" => String, "revision" => Integer, "settings" => public_project_settings }
726
+
727
+ type metadata_field_type = "string" | "number" | "boolean" | "date" | "url"
728
+ type metadata_field_icon = "tag" | "hash" | "user" | "folder" | "layers" | "photo" | "link"
729
+ # A metadata field the portal shows and filters by. `color` is `#rrggbb`.
730
+ type metadata_field = {
731
+ "id" => String,
732
+ "title" => String,
733
+ ?"description" => String,
734
+ "type" => metadata_field_type,
735
+ "icon" => metadata_field_icon,
736
+ "color" => String
737
+ }
738
+ type metadata_fields_resource = { "projectId" => String, "revision" => Integer, "fields" => Array[metadata_field] }
570
739
 
571
740
  # ---- Folders (Folder.ts) --------------------------------------------------------------------
572
741
 
@@ -597,8 +766,8 @@ module CalmLens
597
766
  # picture); `action` is `block` when the match was close enough for a block list to block.
598
767
  type hash_match = {
599
768
  "source" => "hash-list",
600
- "listId" => String,
601
- "entryId" => String,
769
+ "hashListId" => String,
770
+ "hashListEntryId" => String,
602
771
  "distance" => Integer,
603
772
  "action" => hash_list_action
604
773
  }
@@ -610,73 +779,85 @@ module CalmLens
610
779
  "hash" => String,
611
780
  "quality" => number?,
612
781
  ?"label" => String?,
613
- ?"sourceAssetId" => String?,
782
+ # The asset the hash was taken from, when the entry was added from an asset.
783
+ ?"assetId" => String?,
614
784
  "createdAt" => Integer
615
785
  }
616
786
 
617
787
  type hash_list_entry_page = {
618
788
  "items" => Array[hash_list_entry],
619
789
  "hasMore" => bool,
620
- "nextCursor" => String?,
621
- "pageSize" => Integer,
622
- "ordering" => Array[order]
790
+ "nextCursor" => String?
623
791
  }
624
792
 
625
793
  # ---- Policies (Policy.ts) ---------------------------------------------------------------------
626
794
 
627
795
  type policy_preset_id = "general-community" | "children-platform" | "education-support" | "wholesome-community"
628
- # What a policy does with one category: `action` when its policy score reaches 0.5 (default
629
- # `block`), allowances that let covered content through or act on it more softly, and
630
- # restrictions, which outweigh allowances.
796
+ # What a policy does with one category: `action` when its policy score reaches 0.5, allowances
797
+ # that let covered content through or act on it more softly, and restrictions, which outweigh
798
+ # allowances.
631
799
  type category_policy_settings = {
632
- ?"action" => flagged_content_action,
633
- ?"allowances" => Array[policy_rule_snapshot],
634
- ?"restrictions" => Array[policy_rule_snapshot]
800
+ "action" => flagged_content_action,
801
+ "allowances" => Array[policy_rule_snapshot],
802
+ "restrictions" => Array[policy_rule_snapshot]
635
803
  }
636
- # Settings by category (`sexual`, `violence`, ...), and optionally link safety.
804
+ # Settings for every category, and optionally link safety.
637
805
  type policy_settings = {
638
- "categorySettings" => Hash[String, category_policy_settings],
806
+ "categorySettings" => {
807
+ "sexual" => category_policy_settings,
808
+ "violence" => category_policy_settings,
809
+ "self-harm" => category_policy_settings,
810
+ "harassment" => category_policy_settings,
811
+ "hate" => category_policy_settings,
812
+ "criminal-activity" => category_policy_settings,
813
+ "personal-information" => category_policy_settings,
814
+ "profanity" => category_policy_settings
815
+ },
639
816
  ?"linkSafety" => link_safety_settings
640
817
  }
818
+ # How a policy relates to its preset: `live` follows the latest release (the organisation's own
819
+ # preset policy with automatic updates on), `pinned` is the organisation's own preset policy held
820
+ # at one release, and `copy` is a policy made from a preset.
821
+ type policy_preset_mode = "live" | "pinned" | "copy"
641
822
  # Where a policy started from a built-in preset. `version` is the release it is pinned to,
642
- # `latestVersion` the newest; `own` is whether this is the organisation's own policy for the preset
643
- # rather than a copy made from it. An organisation has at most one per preset, and only it can
644
- # follow automatic updates.
823
+ # `latestVersion` the newest.
645
824
  type policy_preset_origin = {
646
825
  "id" => policy_preset_id,
647
826
  "version" => Integer,
648
827
  "latestVersion" => Integer,
649
- "own" => bool
828
+ "mode" => policy_preset_mode
650
829
  }
651
- # A moderation policy. Policies belong to the project's organisation and its projects share them.
652
- # Send `revision` with each change; a policy changed since is refused with `policy/revision-conflict`.
830
+ # A moderation policy. Policies belong to the organisation and its projects share them. Send
831
+ # `revision` with each change; a policy changed since is refused with `policy/revision-conflict`.
653
832
  type policy = {
654
833
  "id" => String,
655
834
  "name" => String,
656
835
  "description" => String,
657
836
  "settings" => policy_settings,
658
837
  "revision" => Integer,
659
- "automaticUpdates" => bool,
660
838
  "preset" => policy_preset_origin?,
661
839
  "projectIds" => Array[String],
662
840
  "createdAt" => Integer,
663
841
  "updatedAt" => Integer
664
842
  }
665
- # Every policy of the project's organisation, oldest first, and the one the project moderates with.
666
- type policy_list = { "policies" => Array[policy], "projectPolicyId" => String }
843
+ # A page of the organisation's policies, oldest first unless `sort` says otherwise.
844
+ type policy_page = { "items" => Array[policy], "hasMore" => bool, "nextCursor" => String? }
667
845
 
668
846
  # ---- Events and webhooks (Event.ts, EventCatalog.ts, WebhookPayload.ts) ----------------------
669
847
 
670
848
  # EVENT_TYPES in EventCatalog.ts. Ignore types your code does not handle. Webhook, Slack and email
671
849
  # deliveries and the public events feed leave out facts that hold personal or account data.
672
- type event_type = "asset.uploaded" | "asset.classified" | "asset.allowed" | "asset.limited" |
673
- "asset.age-restricted" | "asset.blocked" | "asset.purged" | "asset.failed" | "asset.deleted" | "asset.content-removed" | "asset.retried" |
674
- "asset.review-requested" | "asset.hash-matched" | "usage.limit-reached" | "usage.spend-limit-reached" |
850
+ type event_type = "asset.uploaded" | "asset.classified" | "asset.region-restricted" | "asset.allowed" | "asset.limited" |
851
+ "asset.age-restricted" | "asset.blocked" | "asset.purged" | "asset.failed" | "asset.progress-updated" |
852
+ "asset.deleted" | "asset.content-removed" | "asset.retried" | "asset.rerun-requested" | "asset.review-requested" |
853
+ "asset.hash-matched" | "asset.link-flagged" | "usage.free-uploads-exhausted" | "usage.uploads-paused" |
675
854
  "upload-preset.daily-limit-reached" | "project.member-joined" | "project.member-removed" |
676
- "project.member-role-changed" | "project.invitation-sent" | "api-key.created" |
677
- "api-key.revoked" | "connector.failing" | "appeal.submitted" | "appeal.resolved"
855
+ "project.member-left" | "project.member-role-changed" | "project.invitation-sent" |
856
+ "project.transferred" | "api-key.created" | "api-key.revoked" | "api-key.deleted" |
857
+ "connector.disabled" | "appeal.submitted" | "appeal.resolved"
678
858
  type event_severity = "low" | "high"
679
- # A registered fact's value: text, an enum value, a count, a list of enum values, or null.
859
+ # A registered fact's value: text, an enum value, a count or a time in milliseconds (such as
860
+ # `resumes-at`), a list of enum values (such as `flagged-categories`), or null.
680
861
  # Test sends carry `{ "key" => "test", "value" => true }`.
681
862
  type event_fact_value = String | Integer | bool | Array[String] | nil
682
863
  # A registered fact. `key` is a kebab-case key registered for the event's type, such as
@@ -690,18 +871,31 @@ module CalmLens
690
871
  ?"uploadPresetId" => String?,
691
872
  ?"externalId" => String?,
692
873
  "upload" => { "status" => "uploading" | "completed" },
693
- "classification" => { "status" => asset_classification_status, "moderation" => { "status" => asset_moderation_status } },
874
+ # `testInput`: whether the results came from one of the project's test inputs rather than
875
+ # analysing the content. Never use test results, such as age ranges, for real decisions.
876
+ "classification" => { "status" => asset_classification_status, "testInput" => bool,
877
+ "moderation" => { "status" => asset_moderation_status } },
694
878
  "content" => { "status" => asset_content_status }
695
879
  }
880
+ # Who caused an event: a `user` (a person in the portal, or an assistant acting for one), an
881
+ # `api-key`, `automated` moderation or CalmLens itself (`system`). `id` is the user's or API key's
882
+ # ID, nil for `automated` and `system` and when it was not recorded.
883
+ type actor_type = "user" | "api-key" | "automated" | "system"
884
+ type actor = { "type" => actor_type, "id" => String? }
885
+ type actor_input = { type: actor_type, id: String? }
886
+
696
887
  # An `events.list` item, and the body of its webhook delivery.
697
888
  type event = {
698
889
  "id" => String,
699
890
  "projectId" => String,
891
+ "actor" => actor,
700
892
  "type" => event_type,
701
893
  "severity" => event_severity,
702
894
  "title" => String,
703
895
  "description" => String?,
704
896
  "assetId" => String?,
897
+ # Increases with each event about the same asset; nil for events not about an asset.
898
+ "assetSequence" => Integer?,
705
899
  "asset" => event_asset?,
706
900
  "facts" => Array[event_fact],
707
901
  "createdAt" => Integer
@@ -726,23 +920,31 @@ module CalmLens
726
920
  type asset_search_options = {
727
921
  query: String,
728
922
  ?mode: asset_search_mode,
729
- ?limit: Integer,
923
+ # How many results, 1 to 20 (default 10).
924
+ ?page_size: Integer,
730
925
  ?request_options: request_options
731
926
  }
732
927
  type list_assets_options = {
733
928
  ?cursor: String,
734
929
  ?page_size: Integer,
735
- ?ordering: Array[order_input],
736
- # An RSQL filter such as `classification.moderation.status==allowed`.
930
+ # `createdAt` and `id`; `-createdAt` by default.
931
+ ?sort: sort,
932
+ # An RSQL filter such as `classification.moderation.status==allowed`; `folderId==fldr_…` lists
933
+ # one folder's assets.
737
934
  ?filter: String?,
738
- ?folder_id: String?,
739
935
  ?request_options: request_options
740
936
  }
741
937
  type list_asset_children_options = {
742
938
  asset_id: String,
743
939
  ?cursor: String,
744
940
  ?page_size: Integer,
745
- ?ordering: Array[order_input],
941
+ ?sort: sort,
942
+ ?request_options: request_options
943
+ }
944
+ type list_asset_timeline_options = {
945
+ asset_id: String,
946
+ ?cursor: String,
947
+ ?page_size: Integer,
746
948
  ?request_options: request_options
747
949
  }
748
950
  type update_asset_options = {
@@ -767,15 +969,21 @@ module CalmLens
767
969
  ?confirm_purge: bool,
768
970
  ?request_options: request_options
769
971
  }
770
- type create_signed_url_options = {
771
- ?viewer_claims: viewer_claims,
772
- asset_id: String,
773
- # Defaults to `rendered` on the server; `original` takes no lens or transform.
774
- ?resource: "original" | "rendered",
775
- ?expires_in_seconds: Integer,
776
- # Transform options replace the lens's options of the same name.
972
+ # The resources beneath an asset URL ({CalmLens::CDN::RESOURCES}).
973
+ type cdn_resource = "original" | "rendered" | "preview" | "hls" | "embed" | "placeholder" | "info"
974
+ type sign_asset_url_options = {
975
+ # Exactly one of `asset_id` and `external_id`.
976
+ ?asset_id: String,
977
+ ?external_id: String,
978
+ ?project_id: String,
979
+ # Whole seconds from 1 to 86400; 900 by default.
980
+ ?expires_in: Integer,
981
+ # Whether the URL also serves `original`; false by default.
982
+ ?original: bool,
983
+ ?resource: cdn_resource,
777
984
  ?lens: String,
778
985
  ?transform: transform_input,
986
+ ?viewer_claims: viewer_claims,
779
987
  ?request_options: request_options
780
988
  }
781
989
 
@@ -802,6 +1010,8 @@ module CalmLens
802
1010
  ?description: String?,
803
1011
  ?visibility: asset_visibility?,
804
1012
  ?metadata: Array[metadata_item_input]?,
1013
+ # Tags for the asset, such as `['catalogue', 'backpacks']`.
1014
+ ?tags: Array[String]?,
805
1015
  ?author_id: String,
806
1016
  ?context: moderation_context_input,
807
1017
  ?external_id: String?,
@@ -818,6 +1028,8 @@ module CalmLens
818
1028
  ?description: String?,
819
1029
  ?visibility: asset_visibility?,
820
1030
  ?metadata: Array[metadata_item_input]?,
1031
+ # Tags for the asset, such as `['catalogue', 'backpacks']`.
1032
+ ?tags: Array[String]?,
821
1033
  ?author_id: String,
822
1034
  ?context: moderation_context_input,
823
1035
  ?external_id: String?,
@@ -833,6 +1045,8 @@ module CalmLens
833
1045
  ?description: String?,
834
1046
  ?visibility: asset_visibility?,
835
1047
  ?metadata: Array[metadata_item_input]?,
1048
+ # Tags for the asset, such as `['catalogue', 'backpacks']`.
1049
+ ?tags: Array[String]?,
836
1050
  ?author_id: String,
837
1051
  ?context: moderation_context_input,
838
1052
  ?external_id: String?,
@@ -842,7 +1056,8 @@ module CalmLens
842
1056
  ?processing_options: processing_options_input?,
843
1057
  ?request_options: request_options
844
1058
  }
845
- type upload_part_options = { asset_id: String, part: upload_part, ?queue: bool, ?request_options: request_options }
1059
+ # `wait` overrides whether the final part waits for the decision, chosen when the upload started.
1060
+ type upload_part_options = { asset_id: String, part: upload_part, ?wait: bool, ?request_options: request_options }
846
1061
 
847
1062
  # Options of {Text#classify}: the text, with the shared asset fields. `name` defaults to `text`.
848
1063
  type moderate_text_options = {
@@ -851,6 +1066,7 @@ module CalmLens
851
1066
  ?description: String?,
852
1067
  ?visibility: asset_visibility?,
853
1068
  ?metadata: Array[metadata_item_input]?,
1069
+ ?tags: Array[String]?,
854
1070
  ?author_id: String,
855
1071
  ?context: moderation_context_input,
856
1072
  ?external_id: String?,
@@ -865,19 +1081,19 @@ module CalmLens
865
1081
  type sign_upload_token_options = {
866
1082
  upload_preset_id: String,
867
1083
  ?external_id: String,
868
- ?expires_in_seconds: Integer,
1084
+ # Seconds the token can start an upload, 1 to 3600; 600 by default.
1085
+ ?expires_in: Integer,
869
1086
  ?request_options: request_options
870
1087
  }
871
1088
  type signed_upload_token = { "token" => String, "expiresAt" => Integer }
872
1089
 
873
- type folder_order_input = { field: "name" | "createdAt", direction: order_direction }
874
1090
  type list_folders_options = {
875
1091
  ?parent_id: String,
876
1092
  ?cursor: String,
877
1093
  # 1 to 100, default 50.
878
1094
  ?page_size: Integer,
879
- # At most one entry; the default is `name` ascending.
880
- ?ordering: Array[folder_order_input],
1095
+ # `name` (the default) or `createdAt`, descending when prefixed with `-`.
1096
+ ?sort: sort,
881
1097
  ?search: String,
882
1098
  ?request_options: request_options
883
1099
  }
@@ -885,39 +1101,36 @@ module CalmLens
885
1101
  "items" => Array[folder],
886
1102
  "ancestors" => Array[folder],
887
1103
  "hasMore" => bool,
888
- "nextCursor" => String?,
889
- "pageSize" => Integer,
890
- "ordering" => Array[order]
1104
+ "nextCursor" => String?
891
1105
  }
892
1106
  type folder_id_options = { folder_id: String, ?request_options: request_options }
893
1107
  type create_folder_options = { name: String, ?parent_id: String?, ?request_options: request_options }
894
1108
  type update_folder_options = { folder_id: String, ?name: String, ?request_options: request_options }
895
1109
  type move_folder_options = { folder_id: String, parent_id: String?, ?request_options: request_options }
896
- # 1 to 100 lists per page, default 50.
897
- type list_hash_lists_options = { ?cursor: String, ?page_size: Integer, ?request_options: request_options }
1110
+ # 1 to 100 lists per page, default 50; `sort` is `createdAt` and `id`, `-createdAt` by default.
1111
+ type list_hash_lists_options = { ?cursor: String, ?page_size: Integer, ?sort: sort, ?request_options: request_options }
898
1112
  type hash_list_page = {
899
1113
  "items" => Array[hash_list],
900
1114
  "hasMore" => bool,
901
- "nextCursor" => String?,
902
- "pageSize" => Integer,
903
- "ordering" => Array[order]
1115
+ "nextCursor" => String?
904
1116
  }
905
1117
  type hash_list_id_options = { hash_list_id: String, ?request_options: request_options }
906
1118
  type create_hash_list_options = { name: String, action: hash_list_action, ?description: String?, ?request_options: request_options }
907
1119
  type update_hash_list_options = { hash_list_id: String, ?name: String, ?description: String?, ?action: hash_list_action,
908
1120
  ?request_options: request_options }
909
- type list_hash_list_entries_options = { hash_list_id: String, ?cursor: String, ?page_size: Integer,
1121
+ type list_hash_list_entries_options = { hash_list_id: String, ?cursor: String, ?page_size: Integer, ?sort: sort,
910
1122
  ?request_options: request_options }
911
1123
  type add_hash_list_entry_options = { hash_list_id: String, ?hash: String, ?quality: number, ?asset_id: String, ?label: String,
912
1124
  ?request_options: request_options }
913
1125
  type hash_list_entry_id_options = { entry_id: String, ?request_options: request_options }
914
1126
 
915
1127
  # Policy settings as options: snake_case Symbol keys, or a policy's `settings` as the API returned it.
916
- type policy_rule_input = { when: Array[String], action: flagged_content_action, ?name: String, ?match: "all", ?unless: Array[String] }
1128
+ type policy_rule_input = { when: Array[String], action: flagged_content_action, ?name: String, ?unless: Array[String] }
1129
+ # Every category needs its `action`, `allowances` and `restrictions`.
917
1130
  type category_policy_settings_input = {
918
- ?action: flagged_content_action,
919
- ?allowances: Array[policy_rule_input],
920
- ?restrictions: Array[policy_rule_input]
1131
+ action: flagged_content_action,
1132
+ allowances: Array[policy_rule_input],
1133
+ restrictions: Array[policy_rule_input]
921
1134
  }
922
1135
  type link_safety_settings_input = {
923
1136
  verdict_actions: Hash[String | Symbol, link_safety_action],
@@ -927,7 +1140,9 @@ module CalmLens
927
1140
  category_settings: Hash[String | Symbol, category_policy_settings_input],
928
1141
  ?link_safety: link_safety_settings_input
929
1142
  }
930
- type list_policies_options = { ?request_options: request_options }
1143
+ # `sort` is `createdAt` and `id`, `createdAt` (oldest first) by default.
1144
+ type list_policies_options = { ?cursor: String, ?page_size: Integer, ?sort: sort, ?request_options: request_options }
1145
+ type request_only_options = { ?request_options: request_options }
931
1146
  type policy_id_options = { policy_id: String, ?request_options: request_options }
932
1147
  # Send `settings` (defining every category) with a `name`, or `preset_id` to copy the latest
933
1148
  # release of a built-in preset, named after it unless you give a `name`.
@@ -935,16 +1150,18 @@ module CalmLens
935
1150
  ?preset_id: policy_preset_id, ?request_options: request_options }
936
1151
  type update_policy_options = { policy_id: String, revision: Integer, ?name: String, ?description: String,
937
1152
  ?settings: policy_settings_input, ?request_options: request_options }
938
- # `reset` restores the pinned preset version (send `preset.version`); `update` moves an unchanged
939
- # copy to the latest release (send `preset.latestVersion`).
940
- type apply_policy_preset_options = { policy_id: String, operation: "reset" | "update", revision: Integer,
941
- preset_version: Integer, ?request_options: request_options }
1153
+ # `reset` restores the pinned release (send `preset.version`); `upgrade` moves an unchanged
1154
+ # `pinned` or `copy` policy to the latest release (send `preset.latestVersion`).
1155
+ type restore_policy_options = { policy_id: String, revision: Integer, preset_version: Integer,
1156
+ ?request_options: request_options }
942
1157
  type set_policy_automatic_updates_options = { policy_id: String, enabled: bool, revision: Integer,
943
1158
  preset_version: Integer, ?request_options: request_options }
944
1159
  type assign_policy_options = { policy_id: String, ?request_options: request_options }
945
1160
 
946
- # `page_size` is 1 to 100, default 50.
947
- type list_events_options = { ?cursor: String, ?page_size: Integer, ?request_options: request_options }
1161
+ # `page_size` is 1 to 100, default 50. `types` and `asset_id` narrow the feed; a cursor belongs to
1162
+ # the filters it was issued with.
1163
+ type list_events_options = { ?cursor: String, ?page_size: Integer, ?types: Array[event_type], ?asset_id: String,
1164
+ ?request_options: request_options }
948
1165
 
949
1166
  type ensure_lenses_options = { lenses: Array[lens_definition], ?request_options: request_options }
950
1167
  type ensure_lens_options = {
@@ -953,28 +1170,107 @@ module CalmLens
953
1170
  type: lens_type,
954
1171
  active: bool,
955
1172
  ?description: String?,
956
- ?version: Integer,
1173
+ ?transform: image_transform_options_input?,
1174
+ ?request_options: request_options
1175
+ }
1176
+ # `sort` is `createdAt` and `id`, `-createdAt` (newest first) by default.
1177
+ type list_lenses_options = { ?cursor: String, ?page_size: Integer, ?sort: sort, ?request_options: request_options }
1178
+ type lens_id_options = { lens_id: String, ?request_options: request_options }
1179
+ # The identifier must be new to the project.
1180
+ type create_lens_options = ensure_lens_options
1181
+ type update_lens_options = {
1182
+ lens_id: String,
1183
+ ?name: String,
1184
+ ?description: String?,
1185
+ ?type: lens_type,
1186
+ ?active: bool,
957
1187
  ?transform: image_transform_options_input?,
958
1188
  ?request_options: request_options
959
1189
  }
960
1190
 
1191
+ # `sort` is `createdAt` and `id`, `createdAt` (oldest first) by default.
1192
+ type list_upload_presets_options = { ?cursor: String, ?page_size: Integer, ?sort: sort, ?request_options: request_options }
1193
+ # One preset's whole definition, by `key`; fields left out take their defaults. Send the `revision`
1194
+ # you read (0 for a preset that must not exist yet) to refuse a change made since.
1195
+ type upload_preset_definition = {
1196
+ key: String,
1197
+ name: String,
1198
+ ?revision: Integer,
1199
+ ?access: upload_preset_access,
1200
+ ?enabled: bool,
1201
+ ?allowed_content_types: Array[String]?,
1202
+ ?max_size_in_bytes: Integer?,
1203
+ ?folder_id: String?,
1204
+ ?policy_id: String?,
1205
+ ?visibility: asset_visibility,
1206
+ ?processing_options: processing_options_input?,
1207
+ ?reveal_decision: bool,
1208
+ ?daily_upload_limit: Integer?,
1209
+ ?allowed_origins: Array[String]?
1210
+ }
1211
+ type ensure_upload_presets_options = { upload_presets: Array[upload_preset_definition], ?request_options: request_options }
1212
+ type upload_preset_id_options = { upload_preset_id: String, ?request_options: request_options }
1213
+ # The complete settings: those left out return to their defaults.
1214
+ type project_settings_input = {
1215
+ ?opt_out_jurisdictions: Array[String],
1216
+ ?block_unmoderated_assets: bool,
1217
+ ?store_sensitive_metadata: bool,
1218
+ ?streaming_enabled: bool,
1219
+ ?ai_media_search_enabled: bool,
1220
+ ?remote_url_rule: { mode: "allow" | "deny", domains: Array[String] }?,
1221
+ ?website_submissions_per_minute: Integer?,
1222
+ ?human_review: {
1223
+ ?allowed_sample_percent: number,
1224
+ ?restricted_sample_percent: number,
1225
+ ?uncertainty_margin_percent: number,
1226
+ ?review_missing_evidence: bool
1227
+ }
1228
+ }
1229
+ # Send the `revision` you read to refuse settings changed since. `search_indexing_budget_units` is
1230
+ # needed when turning on `ai_media_search_enabled`.
1231
+ type update_project_settings_options = {
1232
+ ?revision: Integer,
1233
+ settings: project_settings_input,
1234
+ ?search_indexing_budget_units: number,
1235
+ ?request_options: request_options
1236
+ }
1237
+ type metadata_field_input = {
1238
+ id: String,
1239
+ title: String,
1240
+ ?description: String,
1241
+ type: metadata_field_type,
1242
+ icon: metadata_field_icon,
1243
+ color: String
1244
+ }
1245
+ # Fields to create or replace by ID; other fields are kept.
1246
+ type ensure_metadata_fields_options = { ?revision: Integer, fields: Array[metadata_field_input], ?request_options: request_options }
1247
+ type delete_metadata_field_options = { field_id: String, ?revision: Integer, ?request_options: request_options }
1248
+
961
1249
  type asset_url_options = {
962
- ?viewer_claims: viewer_claims,
963
- # The asset ID or external ID.
964
- asset_identifier: String,
965
- # `rendered` is the default. `original` takes no lens or transform; `placeholder` is a small blurred PNG
966
- # that takes no lens or transform.
967
- ?resource: "original" | "rendered" | "placeholder",
968
- # Transform options replace the lens's options of the same name.
1250
+ # Exactly one of `asset_id` and `external_id`.
1251
+ ?asset_id: String,
1252
+ ?external_id: String,
1253
+ # The client's project by default.
1254
+ ?project_id: String,
1255
+ # Without one, the URL is the asset URL itself. `original`, `hls` (the HLS master playlist),
1256
+ # `embed` (the page that shows the asset), `placeholder` (a small blurred PNG) and `info` take no
1257
+ # lens or transform; `preview` takes them, and `rendered` does when it is an image.
1258
+ ?resource: cdn_resource,
1259
+ # Transform options replace the lens's options of the same name; both need a resource.
969
1260
  ?lens: String,
970
1261
  ?transform: transform_input,
971
- # Appends `?ageConfirmed=true`, so a public `age-restricted` asset is served from this URL. Set it
972
- # only where your app has already decided the viewer may see age-restricted content.
1262
+ # Unsigned viewer assertions, sent as the `viewerClaims` query without the defaults. Set
1263
+ # `age_confirmed` only where your app has already decided the viewer may see age-restricted content.
1264
+ ?viewer_claims: viewer_claims,
1265
+ ?request_options: request_options
973
1266
  }
974
1267
  type serve_options = {
1268
+ ?asset_id: String,
1269
+ ?external_id: String,
1270
+ ?project_id: String,
975
1271
  ?viewer_claims: viewer_claims,
976
- asset_identifier: String,
977
- ?resource: "original" | "rendered" | "placeholder",
1272
+ # `rendered` by default.
1273
+ ?resource: cdn_resource,
978
1274
  ?lens: String,
979
1275
  ?transform: transform_input,
980
1276
  ?init: { ?method: "GET" | "HEAD", ?headers: Hash[String, String] },
@@ -986,6 +1282,8 @@ module CalmLens
986
1282
  type: event_type,
987
1283
  ?id: String,
988
1284
  ?project_id: String,
1285
+ # The mock API key (`{ 'type' => 'api-key', 'id' => nil }`) when omitted.
1286
+ ?actor: actor_input,
989
1287
  ?severity: event_severity,
990
1288
  ?title: String,
991
1289
  ?description: String?,