@oxy.so/contracts 4.17.0 → 4.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.
Files changed (61) hide show
  1. package/dist/cjs/accountAccess.js +72 -0
  2. package/dist/cjs/agency.js +112 -1
  3. package/dist/cjs/applicationRoles.js +211 -0
  4. package/dist/cjs/client/check.js +9 -0
  5. package/dist/cjs/client/contentClaims.js +139 -0
  6. package/dist/cjs/client/index.js +37 -1
  7. package/dist/cjs/client/safety.js +183 -2
  8. package/dist/cjs/client/signIn.js +2 -0
  9. package/dist/cjs/contentClaims.js +246 -0
  10. package/dist/cjs/index.js +14 -3
  11. package/dist/cjs/safety.js +430 -1
  12. package/dist/cjs/serviceToken.js +14 -0
  13. package/dist/esm/accountAccess.js +69 -0
  14. package/dist/esm/agency.js +111 -0
  15. package/dist/esm/applicationRoles.js +207 -0
  16. package/dist/esm/client/check.js +8 -0
  17. package/dist/esm/client/contentClaims.js +134 -0
  18. package/dist/esm/client/index.js +2 -1
  19. package/dist/esm/client/safety.js +183 -2
  20. package/dist/esm/client/signIn.js +2 -0
  21. package/dist/esm/contentClaims.js +229 -0
  22. package/dist/esm/index.js +5 -1
  23. package/dist/esm/safety.js +423 -0
  24. package/dist/esm/serviceToken.js +11 -0
  25. package/dist/types/accountAccess.d.ts +128 -0
  26. package/dist/types/accountEmail.d.ts +2 -2
  27. package/dist/types/agency.d.ts +327 -2
  28. package/dist/types/agentKeys.d.ts +6 -6
  29. package/dist/types/applicationRoles.d.ts +438 -0
  30. package/dist/types/client/agentKeys.d.ts +2 -2
  31. package/dist/types/client/check.d.ts +3 -0
  32. package/dist/types/client/contentClaims.d.ts +106 -0
  33. package/dist/types/client/deviceSession.d.ts +1 -1
  34. package/dist/types/client/index.d.ts +2 -1
  35. package/dist/types/client/safety.d.ts +204 -4
  36. package/dist/types/client/signIn.d.ts +1 -1
  37. package/dist/types/contentClaims.d.ts +383 -0
  38. package/dist/types/deviceSession.d.ts +4 -4
  39. package/dist/types/email/messages.d.ts +4 -4
  40. package/dist/types/externalIdentity.d.ts +16 -16
  41. package/dist/types/index.d.ts +6 -2
  42. package/dist/types/inference/accountBilling.d.ts +2 -2
  43. package/dist/types/inference/aliaModelRelease.d.ts +8 -8
  44. package/dist/types/inference/catalogue.d.ts +26 -26
  45. package/dist/types/inference/inbox.d.ts +4 -4
  46. package/dist/types/inference/modelDocumentation.d.ts +16 -16
  47. package/dist/types/inference/providerConnection.d.ts +4 -4
  48. package/dist/types/inference/realtime.d.ts +74 -74
  49. package/dist/types/inference/request.d.ts +60 -60
  50. package/dist/types/inference/usage.d.ts +4 -4
  51. package/dist/types/products/access.d.ts +4 -4
  52. package/dist/types/safety.d.ts +2374 -2
  53. package/dist/types/serviceToken.d.ts +11 -0
  54. package/dist/types/sessionStatus.d.ts +4 -4
  55. package/dist/types/signIn.d.ts +5 -5
  56. package/dist/types/updates.d.ts +6 -6
  57. package/dist/types/userResponse.d.ts +8 -8
  58. package/package.json +5 -20
  59. package/dist/cjs/.tsbuildinfo +0 -1
  60. package/dist/esm/.tsbuildinfo +0 -1
  61. package/dist/types/.tsbuildinfo +0 -1
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Content claims (ADR 0043 D10): the public "Report content / rights claim"
3
+ * intake, `POST /content-claims`, and the staff review of a claim.
4
+ *
5
+ * {@link contentClaimSubmissionSchema} is the request body the API parses and
6
+ * the Oxy website validates its form with before posting, so the form cannot
7
+ * accept what the API would answer 400 to. Every validation message is a
8
+ * stable KEY under `contentClaim.errors.*` (filled with
9
+ * {@link CONTENT_CLAIM_LIMITS} by whoever shows it), never English: the website
10
+ * translates the key, and the API answers its own English sentence for it in
11
+ * `fields`.
12
+ */
13
+ import { z } from 'zod';
14
+ // The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
15
+ // it is re-exported here so the root entry carries the whole contract.
16
+ import { CLAIMANT_RELATIONSHIPS, CONTENT_CLAIM_COUNTER_NOTICE_LIMITS, CONTENT_CLAIM_KINDS, CONTENT_CLAIM_LIMITS, CONTENT_CLAIM_REFERENCE_PATTERN, } from './client/contentClaims.js';
17
+ export { CLAIMANT_RELATIONSHIPS, CLOSED_CONTENT_CLAIM_STATES, CONTENT_CLAIM_COUNTER_NOTICE_LIMITS, CONTENT_CLAIM_ERROR_CODES, CONTENT_CLAIM_KINDS, CONTENT_CLAIM_LIMITS, CONTENT_CLAIM_REFERENCE_PATTERN, CONTENT_CLAIM_STATES, COUNTER_NOTICE_STATEMENT_VERSION, COUNTER_NOTICE_STATEMENTS, ERASURE_REQUEST_DEADLINE_DAYS, OPEN_CONTENT_CLAIM_STATES, isErasureRequest, isRightsClaim, } from './client/contentClaims.js';
18
+ /** Every key a submission's validation can produce, under `contentClaim.errors.*`. */
19
+ export const CONTENT_CLAIM_VALIDATION_KEYS = [
20
+ 'contentClaim.errors.kind',
21
+ 'contentClaim.errors.contentUrlsRequired',
22
+ 'contentClaim.errors.contentUrlsTooMany',
23
+ 'contentClaim.errors.contentUrlInvalid',
24
+ 'contentClaim.errors.descriptionShort',
25
+ 'contentClaim.errors.descriptionLong',
26
+ 'contentClaim.errors.originalWorkUrl',
27
+ 'contentClaim.errors.name',
28
+ 'contentClaim.errors.email',
29
+ 'contentClaim.errors.organization',
30
+ 'contentClaim.errors.relationship',
31
+ 'contentClaim.errors.goodFaith',
32
+ 'contentClaim.errors.accurate',
33
+ 'contentClaim.errors.signature',
34
+ ];
35
+ function isHttpsUrl(value) {
36
+ try {
37
+ const url = new URL(value);
38
+ return url.protocol === 'https:' && url.hostname.length > 0;
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ const httpsUrl = (message) => z.string().trim().max(CONTENT_CLAIM_LIMITS.maxUrlLength, message).refine(isHttpsUrl, message);
45
+ /**
46
+ * `POST /content-claims` — a claim, as the claimant sends it. Strict: an
47
+ * unknown key is refused rather than dropped.
48
+ */
49
+ export const contentClaimSubmissionSchema = z
50
+ .object({
51
+ kind: z.enum(CONTENT_CLAIM_KINDS, { message: 'contentClaim.errors.kind' }),
52
+ contentUrls: z
53
+ .array(httpsUrl('contentClaim.errors.contentUrlInvalid'), {
54
+ message: 'contentClaim.errors.contentUrlsRequired',
55
+ })
56
+ .min(CONTENT_CLAIM_LIMITS.minUrls, 'contentClaim.errors.contentUrlsRequired')
57
+ .max(CONTENT_CLAIM_LIMITS.maxUrls, 'contentClaim.errors.contentUrlsTooMany'),
58
+ description: z
59
+ .string({ message: 'contentClaim.errors.descriptionShort' })
60
+ .trim()
61
+ .min(CONTENT_CLAIM_LIMITS.minDescription, 'contentClaim.errors.descriptionShort')
62
+ .max(CONTENT_CLAIM_LIMITS.maxDescription, 'contentClaim.errors.descriptionLong'),
63
+ originalWorkUrl: httpsUrl('contentClaim.errors.originalWorkUrl').optional(),
64
+ claimant: z
65
+ .object({
66
+ name: z
67
+ .string({ message: 'contentClaim.errors.name' })
68
+ .trim()
69
+ .min(1, 'contentClaim.errors.name')
70
+ .max(CONTENT_CLAIM_LIMITS.maxName, 'contentClaim.errors.name'),
71
+ email: z
72
+ .string({ message: 'contentClaim.errors.email' })
73
+ .trim()
74
+ .email('contentClaim.errors.email')
75
+ .max(CONTENT_CLAIM_LIMITS.maxEmail, 'contentClaim.errors.email'),
76
+ organization: z
77
+ .string({ message: 'contentClaim.errors.organization' })
78
+ .trim()
79
+ .max(CONTENT_CLAIM_LIMITS.maxOrganization, 'contentClaim.errors.organization')
80
+ .optional(),
81
+ relationship: z.enum(CLAIMANT_RELATIONSHIPS, {
82
+ message: 'contentClaim.errors.relationship',
83
+ }),
84
+ }, { message: 'contentClaim.errors.name' })
85
+ .strict(),
86
+ statements: z
87
+ .object({
88
+ goodFaith: z.literal(true, {
89
+ errorMap: () => ({ message: 'contentClaim.errors.goodFaith' }),
90
+ }),
91
+ accurate: z.literal(true, {
92
+ errorMap: () => ({ message: 'contentClaim.errors.accurate' }),
93
+ }),
94
+ }, { message: 'contentClaim.errors.goodFaith' })
95
+ .strict(),
96
+ signature: z
97
+ .string({ message: 'contentClaim.errors.signature' })
98
+ .trim()
99
+ .min(1, 'contentClaim.errors.signature')
100
+ .max(CONTENT_CLAIM_LIMITS.maxName, 'contentClaim.errors.signature'),
101
+ })
102
+ .strict();
103
+ // ---------------------------------------------------------------------------
104
+ // The staff review (`/content-claims` behind the `content:claims` capability)
105
+ // ---------------------------------------------------------------------------
106
+ /** How a target's URL was understood. */
107
+ export const CONTENT_CLAIM_TARGET_RESOLUTIONS = [
108
+ /** An Oxy file: `cloud.oxy.so/<id>`, `/cdn/<id>`, `/assets/<id>…`, a stored key. */
109
+ 'file',
110
+ /** A page of an Oxy app or anything else: a person attaches the files it shows. */
111
+ 'unresolved',
112
+ ];
113
+ /** Who added a target: the claimant's URL, or staff attaching a file by id. */
114
+ export const CONTENT_CLAIM_TARGET_SOURCES = ['claimant', 'staff'];
115
+ /** What the audit trail records. */
116
+ export const CONTENT_CLAIM_EVENT_ACTIONS = [
117
+ 'submitted',
118
+ 'review_started',
119
+ 'targets_attached',
120
+ 'targets_blocked',
121
+ 'rejected',
122
+ 'counter_notice_recorded',
123
+ /** The owner of the blocked content sent a counter-notice themselves (`POST /content-claims/counter-notices`). */
124
+ 'counter_notice_submitted',
125
+ 'reinstated',
126
+ 'upheld',
127
+ 'note_added',
128
+ ];
129
+ // ---------------------------------------------------------------------------
130
+ // The owner's counter-notice (`POST /content-claims/counter-notices`)
131
+ // ---------------------------------------------------------------------------
132
+ /** Every key a counter-notice's validation can produce, under `contentClaim.counterNotice.errors.*`. */
133
+ export const CONTENT_CLAIM_COUNTER_NOTICE_VALIDATION_KEYS = [
134
+ 'contentClaim.counterNotice.errors.claim',
135
+ 'contentClaim.counterNotice.errors.reference',
136
+ 'contentClaim.counterNotice.errors.claimId',
137
+ 'contentClaim.counterNotice.errors.fileIds',
138
+ 'contentClaim.counterNotice.errors.name',
139
+ 'contentClaim.counterNotice.errors.address',
140
+ 'contentClaim.counterNotice.errors.phone',
141
+ 'contentClaim.counterNotice.errors.email',
142
+ 'contentClaim.counterNotice.errors.materialDescription',
143
+ 'contentClaim.counterNotice.errors.materialLocation',
144
+ 'contentClaim.counterNotice.errors.explanation',
145
+ 'contentClaim.counterNotice.errors.perjury',
146
+ 'contentClaim.counterNotice.errors.jurisdiction',
147
+ 'contentClaim.counterNotice.errors.serviceOfProcess',
148
+ 'contentClaim.counterNotice.errors.disclosure',
149
+ 'contentClaim.counterNotice.errors.signature',
150
+ ];
151
+ const N = CONTENT_CLAIM_COUNTER_NOTICE_LIMITS;
152
+ const boundedText = (min, max, message) => z.string({ message }).trim().min(min, message).max(max, message);
153
+ const affirmed = (message) => z.literal(true, { errorMap: () => ({ message }) });
154
+ /**
155
+ * `POST /content-claims/counter-notices` — the owner of content a claim took
156
+ * down disputes it (17 U.S.C. § 512(g)(3)). Signed in: the API answers only
157
+ * for files the caller owns.
158
+ *
159
+ * Names the claim by its `reference` (the `OXY-XXXX-XXXX` the owner was
160
+ * emailed), its `claimId`, or the blocked `fileIds` — at least one. With
161
+ * `fileIds` the notice covers exactly those files; without, every one of the
162
+ * caller's files the claim blocked. Strict: an unknown key is refused.
163
+ */
164
+ export const contentClaimCounterNoticeSchema = z
165
+ .object({
166
+ reference: z
167
+ .string({ message: 'contentClaim.counterNotice.errors.reference' })
168
+ .trim()
169
+ .toUpperCase()
170
+ .regex(CONTENT_CLAIM_REFERENCE_PATTERN, 'contentClaim.counterNotice.errors.reference')
171
+ .optional(),
172
+ claimId: z
173
+ .string({ message: 'contentClaim.counterNotice.errors.claimId' })
174
+ .trim()
175
+ .uuid('contentClaim.counterNotice.errors.claimId')
176
+ .optional(),
177
+ fileIds: z
178
+ .array(boundedText(1, N.maxFileId, 'contentClaim.counterNotice.errors.fileIds'), {
179
+ message: 'contentClaim.counterNotice.errors.fileIds',
180
+ })
181
+ .min(1, 'contentClaim.counterNotice.errors.fileIds')
182
+ .max(N.maxFileIds, 'contentClaim.counterNotice.errors.fileIds')
183
+ .optional(),
184
+ subscriber: z
185
+ .object({
186
+ name: boundedText(1, N.maxName, 'contentClaim.counterNotice.errors.name'),
187
+ address: boundedText(1, N.maxAddress, 'contentClaim.counterNotice.errors.address'),
188
+ phone: boundedText(N.minPhone, N.maxPhone, 'contentClaim.counterNotice.errors.phone').regex(/^\+?[0-9 ().-]+$/, 'contentClaim.counterNotice.errors.phone'),
189
+ email: z
190
+ .string({ message: 'contentClaim.counterNotice.errors.email' })
191
+ .trim()
192
+ .email('contentClaim.counterNotice.errors.email')
193
+ .max(N.maxEmail, 'contentClaim.counterNotice.errors.email'),
194
+ }, { message: 'contentClaim.counterNotice.errors.name' })
195
+ .strict(),
196
+ material: z
197
+ .object({
198
+ /** What was removed. */
199
+ description: boundedText(N.minMaterial, N.maxMaterial, 'contentClaim.counterNotice.errors.materialDescription'),
200
+ /** Where it appeared before it was removed (a URL, or where in an app). */
201
+ location: boundedText(1, N.maxLocation, 'contentClaim.counterNotice.errors.materialLocation'),
202
+ }, { message: 'contentClaim.counterNotice.errors.materialDescription' })
203
+ .strict(),
204
+ /** Why it was a mistake or a misidentification, in the owner's words. */
205
+ explanation: boundedText(N.minExplanation, N.maxExplanation, 'contentClaim.counterNotice.errors.explanation'),
206
+ /** Each of `COUNTER_NOTICE_STATEMENTS`, affirmed. */
207
+ statements: z
208
+ .object({
209
+ perjury: affirmed('contentClaim.counterNotice.errors.perjury'),
210
+ jurisdiction: affirmed('contentClaim.counterNotice.errors.jurisdiction'),
211
+ serviceOfProcess: affirmed('contentClaim.counterNotice.errors.serviceOfProcess'),
212
+ disclosure: affirmed('contentClaim.counterNotice.errors.disclosure'),
213
+ }, { message: 'contentClaim.counterNotice.errors.perjury' })
214
+ .strict(),
215
+ /** The owner's full legal name, typed as their signature. */
216
+ signature: boundedText(1, N.maxSignature, 'contentClaim.counterNotice.errors.signature'),
217
+ })
218
+ .strict()
219
+ .superRefine((value, ctx) => {
220
+ if (value.reference === undefined &&
221
+ value.claimId === undefined &&
222
+ value.fileIds === undefined) {
223
+ ctx.addIssue({
224
+ code: z.ZodIssueCode.custom,
225
+ path: ['reference'],
226
+ message: 'contentClaim.counterNotice.errors.claim',
227
+ });
228
+ }
229
+ });
package/dist/esm/index.js CHANGED
@@ -180,13 +180,14 @@ PRODUCT_PLAN_STATUSES, productPlanStatusSchema, LIVE_PRODUCT_PLAN_STATUSES, plan
180
180
  export {
181
181
  // Economic treatment (I09): derived by the server, never sent by a caller.
182
182
  INFERENCE_ECONOMIC_TREATMENTS, inferenceEconomicTreatmentSchema, PROVIDER_COST_SOURCES, providerCostSourceSchema, costCenterUsageSchema, } from './inference/economics.js';
183
- export { AUTONOMY_LEVELS, CAPABILITY_PACKAGES, autonomyLevelSchema, capabilityPackageSchema, actorRefSchema, executionActorRefSchema, foregroundExecutionAuthorizationInputSchema, resourceRefSchema, toolGrantOverrideSchema, grantLimitSchema, capabilityCatalogBindingSchema, executionAuthorizationRefSchema, capabilityCoordinatorSchema, capabilityTicketRequestSchema, delegationGrantSchema, automationTriggerSchema, automationActorSelectionSchema, automationDataFlowSchema, automationDefinitionSchema, capabilityTicketClaimsSchema, invocationPrincipalSchema, policyDecisionSchema, auditResultSchema, auditEventSchema, catalogToolSchema, catalogEventSchema, appCapabilityCatalogSchema, catalogRegistrationSchema, normalizedAppEventSchema, } from './agency.js';
183
+ export { AUTONOMY_LEVELS, CAPABILITY_PACKAGES, autonomyLevelSchema, capabilityPackageSchema, actorRefSchema, executionActorRefSchema, foregroundExecutionAuthorizationInputSchema, resourceRefSchema, toolGrantOverrideSchema, grantLimitSchema, capabilityCatalogBindingSchema, executionAuthorizationRefSchema, capabilityCoordinatorSchema, capabilityTicketRequestSchema, delegationGrantSchema, AGENT_REQUESTER_CEILING_AUTONOMY, agentRequesterCeilingWriteSchema, agentRequesterCeilingSchema, automationTriggerSchema, automationActorSelectionSchema, automationDataFlowSchema, automationDefinitionSchema, capabilityTicketClaimsSchema, invocationPrincipalSchema, policyDecisionSchema, auditResultSchema, auditEventSchema, catalogToolSchema, catalogEventSchema, catalogFeatureSchema, CATALOG_INTERNAL_TRANSPORTS, appCapabilityCatalogSchema, catalogRegistrationSchema, normalizedAppEventSchema, } from './agency.js';
184
184
  export { catalogInvocationPathParameters, catalogInvocationsOverlap, catalogInvocationTemplatesEquivalent, findOverlappingCatalogInvocations, matchCatalogInvocation, resolveCatalogInvocation, } from './capabilityInvocation.js';
185
185
  export { emailContextAddressSchema, emailContextMailboxSchema, emailContextMessageSchema, emailAgentContextSchema, } from './emailAgentContext.js';
186
186
  export { inboxComposeRequestSchema, inboxDailyBriefRequestSchema, inboxDailyBriefResponseSchema, INBOX_DAILY_BRIEF_SECTIONS, inboxNaturalSearchRequestSchema, inboxMessageInferenceParamsSchema, inboxInferenceTextResponseSchema, inboxNaturalSearchResponseSchema, inboxSmartRepliesResponseSchema, inboxThreadSummaryResponseSchema, inboxInferenceStreamEventSchema, } from './inference/inbox.js';
187
187
  export * from './externalIdentity.js';
188
188
  export * from './linkedAccounts.js';
189
189
  export * from './federationInstanceFetch.js';
190
+ export * from './accountAccess.js';
190
191
  export * from './notifications.js';
191
192
  export * from './email/messages.js';
192
193
  export * from './stickers.js';
@@ -204,3 +205,6 @@ export { personalPlanDisplayPriceSchema, personalPlanCatalogueSchema, } from './
204
205
  export * from './products/checkout.js';
205
206
  export * from './organizations.js';
206
207
  export * from './safety.js';
208
+ export { serviceTokenScopesSchema, serviceTokenOptionsSchema, } from './serviceToken.js';
209
+ export * from './applicationRoles.js';
210
+ export * from './contentClaims.js';
@@ -1,4 +1,12 @@
1
1
  import { z } from 'zod';
2
+ import { accountActorChainSchema } from './accountSubject.js';
3
+ // The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
4
+ // its vocabularies are re-exported here so the root entry is unchanged.
5
+ import { PURCHASE_APPROVAL_STATUSES, SAFETY_POLICY_VERSION, SUPERVISED_ACTIONS, SUPERVISION_CONSENT_VERSION, SUPERVISION_INVITATION_STATUSES, SUPERVISION_STATUSES, } from './client/safety.js';
6
+ import { ORGANIZATION_TYPES } from './organizations.js';
7
+ import { reauthProofSchema } from './signIn.js';
8
+ import { userResponseSchema } from './userResponse.js';
9
+ export { PURCHASE_APPROVAL_STATUSES, SAFETY_POLICY_VERSION, SUPERVISED_ACTIONS, SUPERVISION_CONSENT_VERSION, SUPERVISION_INVITATION_STATUSES, SUPERVISION_STATUSES, };
2
10
  /** The requester is authenticated by the bearer; applications cannot submit viewer IDs. */
3
11
  export const profileAccessInputSchema = z
4
12
  .object({ subjectAccountId: z.string().min(1).max(128) })
@@ -10,3 +18,418 @@ export const profileAccessDecisionSchema = z
10
18
  policyVersion: z.literal('1'),
11
19
  })
12
20
  .strict();
21
+ export const SAFETY_DECISION_REASONS = [
22
+ 'allowed',
23
+ 'owner',
24
+ 'public',
25
+ 'private',
26
+ 'blocked',
27
+ 'unavailable',
28
+ 'unsupported_consumer',
29
+ 'contact_not_approved',
30
+ 'mature_content',
31
+ 'unrated_content',
32
+ 'approval_required',
33
+ ];
34
+ const accountId = z.string().min(1).max(128);
35
+ const resourceId = z.string().trim().min(1).max(256);
36
+ const uniqueIds = (max, min = 1) => z
37
+ .array(accountId)
38
+ .min(min)
39
+ .max(max)
40
+ .refine((ids) => new Set(ids).size === ids.length, 'Account IDs must be unique');
41
+ export const safetyDecisionSchema = z
42
+ .object({
43
+ allowed: z.boolean(),
44
+ reason: z.enum(SAFETY_DECISION_REASONS),
45
+ policyVersion: z.literal(SAFETY_POLICY_VERSION),
46
+ })
47
+ .strict();
48
+ export const profileAccessBatchInputSchema = z
49
+ .object({ subjectAccountIds: uniqueIds(100) })
50
+ .strict();
51
+ export const profileAccessBatchResponseSchema = z
52
+ .object({
53
+ items: z
54
+ .array(z
55
+ .object({
56
+ subjectAccountId: accountId,
57
+ decision: safetyDecisionSchema,
58
+ })
59
+ .strict())
60
+ .max(100),
61
+ })
62
+ .strict();
63
+ export const MESSAGE_ACCESS_MAX_RESOURCES = 100;
64
+ export const MESSAGE_ACCESS_MAX_PARTICIPANTS = 1024;
65
+ export const MESSAGE_ACCESS_MAX_SUPERVISED_PARTICIPANTS = 256;
66
+ export const MESSAGE_ACCESS_MAX_PARTICIPANT_SLOTS = 4096;
67
+ /** Leaves room for the original requester proof in a 512 KiB request. */
68
+ export const MESSAGE_ACCESS_MAX_INPUT_BYTES = 448 * 1024;
69
+ export const messageAccessInputSchema = z
70
+ .object({ participantAccountIds: uniqueIds(MESSAGE_ACCESS_MAX_PARTICIPANTS), resourceId })
71
+ .strict();
72
+ export const contactAccessInputSchema = z.object({ targetAccountIds: uniqueIds(1000) }).strict();
73
+ export const contactAccessResponseSchema = z
74
+ .object({
75
+ items: z
76
+ .array(z
77
+ .object({
78
+ targetAccountId: accountId,
79
+ decision: safetyDecisionSchema,
80
+ })
81
+ .strict())
82
+ .max(1000),
83
+ })
84
+ .strict();
85
+ export const CONTENT_CLASSIFICATIONS = ['general', 'mature', 'unrated'];
86
+ export const contentAccessInputSchema = z
87
+ .object({
88
+ resourceId,
89
+ classification: z.enum(CONTENT_CLASSIFICATIONS),
90
+ authorAccountId: accountId.optional(),
91
+ audience: z.enum(['public', 'private', 'followers']).optional(),
92
+ applicationAudienceAllowed: z.boolean().optional(),
93
+ applicationResourceGrant: z.boolean().optional(),
94
+ operation: z.enum(['read', 'manage_own']).optional(),
95
+ })
96
+ .strict()
97
+ .refine((item) => !item.audience ||
98
+ item.audience === 'public' ||
99
+ (!!item.authorAccountId && item.applicationAudienceAllowed !== undefined), 'Nonpublic content requires its authoritative author and application audience decision')
100
+ .refine((item) => item.operation !== 'manage_own' || !!item.authorAccountId, 'Own management requires its authoritative author');
101
+ export const publicationPolicySchema = z
102
+ .object({
103
+ publicationAllowed: z.boolean(),
104
+ requiredAudience: z.literal('private').nullable(),
105
+ collaborationAllowed: z.boolean(),
106
+ federationAllowed: z.boolean(),
107
+ policyVersion: z.literal(SAFETY_POLICY_VERSION),
108
+ })
109
+ .strict();
110
+ export const publicationPoliciesInputSchema = z
111
+ .object({
112
+ authorAccountIds: z
113
+ .array(accountId)
114
+ .min(1)
115
+ .max(100)
116
+ .refine((ids) => new Set(ids).size === ids.length, 'Author IDs must be unique'),
117
+ })
118
+ .strict();
119
+ export const publicationPoliciesResponseSchema = z
120
+ .object({
121
+ items: z
122
+ .array(z.object({ authorAccountId: accountId, policy: publicationPolicySchema }).strict())
123
+ .max(100),
124
+ })
125
+ .strict();
126
+ export const contentAccessBatchInputSchema = z
127
+ .object({
128
+ items: z
129
+ .array(contentAccessInputSchema)
130
+ .min(1)
131
+ .max(100)
132
+ .refine((items) => new Set(items.map((item) => item.resourceId)).size === items.length, 'Resource IDs must be unique'),
133
+ })
134
+ .strict();
135
+ export const contentAccessBatchResponseSchema = z
136
+ .object({
137
+ items: z
138
+ .array(z
139
+ .object({
140
+ resourceId,
141
+ decision: safetyDecisionSchema,
142
+ })
143
+ .strict())
144
+ .max(100),
145
+ })
146
+ .strict();
147
+ /** A reviewed background consumer may decide delivery, but never authenticate as the sender. */
148
+ export const deliveryAccessInputSchema = messageAccessInputSchema
149
+ .extend({ senderAccountId: accountId })
150
+ .strict();
151
+ /** Count repeated participants across resources too: this bounds both graph and pair evaluations. */
152
+ const messageBatch = (schema) => z
153
+ .object({ items: z.array(schema).min(1).max(MESSAGE_ACCESS_MAX_RESOURCES) })
154
+ .strict()
155
+ .refine((value) => new Set(value.items.map((item) => item.resourceId)).size === value.items.length, 'Resource IDs must be unique')
156
+ .refine((value) => value.items.reduce((total, item) => total + item.participantAccountIds.length, 0) <=
157
+ MESSAGE_ACCESS_MAX_PARTICIPANT_SLOTS, 'Participant budget exceeded')
158
+ .refine((value) => new TextEncoder().encode(JSON.stringify(value)).byteLength <=
159
+ MESSAGE_ACCESS_MAX_INPUT_BYTES, 'Message batch byte budget exceeded');
160
+ export const messageAccessBatchInputSchema = messageBatch(messageAccessInputSchema);
161
+ export const deliveryAccessBatchInputSchema = messageBatch(deliveryAccessInputSchema);
162
+ export const messageAccessBatchResponseSchema = contentAccessBatchResponseSchema;
163
+ /** Live socket-recipient proof; a request ID correlates the result and never identifies an account. */
164
+ export const messagingSessionsInputSchema = z
165
+ .object({
166
+ items: z
167
+ .array(z
168
+ .object({
169
+ requestId: resourceId,
170
+ accessToken: z.string().min(1).max(16384),
171
+ })
172
+ .strict())
173
+ .min(1)
174
+ .max(100),
175
+ })
176
+ .strict()
177
+ .refine((value) => new Set(value.items.map((item) => item.requestId)).size === value.items.length, 'Request IDs must be unique')
178
+ .refine((value) => new TextEncoder().encode(JSON.stringify(value)).byteLength <= MESSAGE_ACCESS_MAX_INPUT_BYTES, 'Session proof byte budget exceeded');
179
+ export const messagingSessionsResponseSchema = z
180
+ .object({
181
+ items: z
182
+ .array(z.discriminatedUnion('allowed', [
183
+ z.object({ requestId: resourceId, allowed: z.literal(true), accountId }).strict(),
184
+ z.object({ requestId: resourceId, allowed: z.literal(false) }).strict(),
185
+ ]))
186
+ .max(100),
187
+ })
188
+ .strict();
189
+ export const safetyRequesterSchema = z.union([
190
+ z.object({ accessToken: z.string().min(1).max(16384).nullable() }).strict(),
191
+ z.object({ connectionToken: z.string().min(1).max(16384) }).strict(),
192
+ ]);
193
+ export const supervisionSchema = z
194
+ .object({
195
+ childAccountId: accountId,
196
+ guardianAccountId: accountId.nullable(),
197
+ status: z.enum(SUPERVISION_STATUSES),
198
+ policyVersion: z.literal(SAFETY_POLICY_VERSION),
199
+ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION),
200
+ createdAt: z.string().datetime(),
201
+ })
202
+ .strict();
203
+ export const supervisionInvitationSchema = z
204
+ .object({
205
+ id: accountId,
206
+ childAccountId: accountId,
207
+ guardianAccountId: accountId,
208
+ status: z.enum(SUPERVISION_INVITATION_STATUSES),
209
+ expiresAt: z.string().datetime(),
210
+ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION),
211
+ createdAt: z.string().datetime(),
212
+ })
213
+ .strict();
214
+ export const supervisionContactSchema = z
215
+ .object({
216
+ childAccountId: accountId,
217
+ contactAccountId: accountId,
218
+ approvedAt: z.string().datetime(),
219
+ })
220
+ .strict();
221
+ export const supervisionStateSchema = z
222
+ .object({
223
+ self: supervisionSchema.nullable(),
224
+ children: z.array(supervisionSchema).max(100),
225
+ invitations: z.array(supervisionInvitationSchema).max(100),
226
+ })
227
+ .strict();
228
+ export const supervisionContactsResponseSchema = z
229
+ .object({ items: z.array(supervisionContactSchema).max(1000) })
230
+ .strict();
231
+ const fingerprint = z.string().regex(/^[a-f0-9]{64}$/);
232
+ const minorUnits = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
233
+ export const purchaseApprovalLineSchema = z
234
+ .object({
235
+ itemReference: resourceId,
236
+ title: z.string().trim().min(1).max(300),
237
+ optionsText: z.string().trim().max(300),
238
+ merchantAccountId: accountId,
239
+ quantity: z.number().int().min(1).max(1000000),
240
+ unitAmountMinor: minorUnits,
241
+ lineAmountMinor: minorUnits,
242
+ })
243
+ .strict();
244
+ const purchaseEffectsObject = z
245
+ .object({
246
+ operationId: resourceId,
247
+ resourceId,
248
+ cartFingerprint: fingerprint,
249
+ summary: z.string().trim().min(1).max(300),
250
+ merchantAccountIds: z
251
+ .array(accountId)
252
+ .min(1)
253
+ .max(100)
254
+ .refine((ids) => new Set(ids).size === ids.length, 'Merchant accounts must be unique')
255
+ .transform((ids) => [...ids].sort()),
256
+ lineItems: z.array(purchaseApprovalLineSchema).min(1).max(200),
257
+ shippingMinor: minorUnits,
258
+ taxMinor: minorUnits,
259
+ discountMinor: minorUnits,
260
+ roundingAdjustmentMinor: z
261
+ .number()
262
+ .int()
263
+ .min(-Number.MAX_SAFE_INTEGER)
264
+ .max(Number.MAX_SAFE_INTEGER),
265
+ paymentFingerprint: fingerprint,
266
+ amountMinor: minorUnits,
267
+ currency: z.string().regex(/^[A-Z]{3}$/),
268
+ expiresAt: z.string().datetime(),
269
+ })
270
+ .strict();
271
+ function validatePurchaseEvidence(value, ctx) {
272
+ const merchants = new Set(value.merchantAccountIds);
273
+ const references = new Set();
274
+ for (const line of value.lineItems) {
275
+ const key = `${line.merchantAccountId}:${line.itemReference}`;
276
+ if (!merchants.has(line.merchantAccountId) || references.has(key))
277
+ ctx.addIssue({
278
+ code: z.ZodIssueCode.custom,
279
+ message: 'Purchase lines must name an included merchant and unique item reference',
280
+ });
281
+ references.add(key);
282
+ }
283
+ if (merchants.size !== new Set(value.lineItems.map((line) => line.merchantAccountId)).size)
284
+ ctx.addIssue({
285
+ code: z.ZodIssueCode.custom,
286
+ message: 'Every merchant must have an approval line',
287
+ });
288
+ const total = value.lineItems.reduce((sum, line) => sum + BigInt(line.lineAmountMinor), 0n) +
289
+ BigInt(value.shippingMinor) +
290
+ BigInt(value.taxMinor) -
291
+ BigInt(value.discountMinor) +
292
+ BigInt(value.roundingAdjustmentMinor);
293
+ if (total < 0n || total > BigInt(Number.MAX_SAFE_INTEGER) || total !== BigInt(value.amountMinor))
294
+ ctx.addIssue({
295
+ code: z.ZodIssueCode.custom,
296
+ message: 'Approval evidence must equal the exact payment total',
297
+ });
298
+ if (Math.abs(value.roundingAdjustmentMinor) >
299
+ value.lineItems.length + 3 * value.merchantAccountIds.length)
300
+ ctx.addIssue({
301
+ code: z.ZodIssueCode.custom,
302
+ message: 'Rounding adjustment exceeds the bounded conversion stages',
303
+ });
304
+ }
305
+ export const purchaseEffectsSchema = purchaseEffectsObject.superRefine(validatePurchaseEvidence);
306
+ const purchaseApprovalFields = {
307
+ id: accountId,
308
+ childAccountId: accountId,
309
+ applicationId: accountId,
310
+ status: z.enum(PURCHASE_APPROVAL_STATUSES),
311
+ createdAt: z.string().datetime(),
312
+ receiptReference: z.string().max(512).nullable(),
313
+ };
314
+ export const purchaseApprovalSchema = purchaseEffectsObject
315
+ .extend(purchaseApprovalFields)
316
+ .strict()
317
+ .superRefine(validatePurchaseEvidence);
318
+ export const purchaseApprovalSummarySchema = purchaseEffectsObject
319
+ .omit({ lineItems: true })
320
+ .extend(purchaseApprovalFields)
321
+ .strict();
322
+ export const purchaseApprovalListSchema = z
323
+ .object({
324
+ items: z.array(purchaseApprovalSummarySchema).max(100),
325
+ nextCursor: z.string().nullable(),
326
+ })
327
+ .strict();
328
+ export const reservePurchaseSchema = purchaseEffectsObject
329
+ .extend({ approvalId: accountId })
330
+ .strict()
331
+ .superRefine(validatePurchaseEvidence);
332
+ export const confirmPurchaseSchema = z
333
+ .object({
334
+ approvalId: accountId,
335
+ operationId: resourceId,
336
+ receiptReference: z.string().trim().min(1).max(512),
337
+ })
338
+ .strict();
339
+ export const cancelPurchaseSchema = confirmPurchaseSchema.omit({ receiptReference: true });
340
+ export const SAFETY_ERROR_CODES = [
341
+ 'bad_request',
342
+ 'validation_failed',
343
+ 'unauthorized',
344
+ 'forbidden',
345
+ 'not_found',
346
+ 'conflict',
347
+ 'reauth_required',
348
+ 'limit_reached',
349
+ 'service_unavailable',
350
+ 'rate_limited',
351
+ ];
352
+ export const safetyErrorSchema = z
353
+ .object({ error: z.object({ code: z.enum(SAFETY_ERROR_CODES), message: z.string() }).strict() })
354
+ .strict();
355
+ export const supervisionInviteInputSchema = z
356
+ .object({
357
+ guardianAccountId: accountId,
358
+ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION),
359
+ reauth: reauthProofSchema,
360
+ })
361
+ .strict();
362
+ export const supervisionAcceptInputSchema = z
363
+ .object({ consentVersion: z.literal(SUPERVISION_CONSENT_VERSION), reauth: reauthProofSchema })
364
+ .strict();
365
+ export const supervisionContactInputSchema = z
366
+ .object({ childAccountId: accountId, contactAccountId: accountId, reauth: reauthProofSchema })
367
+ .strict();
368
+ export const supervisionReviewInputSchema = z
369
+ .object({
370
+ childAccountId: accountId,
371
+ action: z.enum(['end', 'transfer']),
372
+ guardianAccountId: accountId.optional(),
373
+ evidenceReference: z.string().trim().min(1).max(2048),
374
+ reauth: reauthProofSchema,
375
+ })
376
+ .strict();
377
+ export const purchaseReviewInputSchema = z
378
+ .object({ decision: z.enum(['approved', 'rejected']), reauth: reauthProofSchema })
379
+ .strict();
380
+ export const safetySessionInputSchema = z
381
+ .object({
382
+ sessionId: accountId,
383
+ accessToken: z.string().min(1).max(16384),
384
+ action: z.enum(SUPERVISED_ACTIONS),
385
+ })
386
+ .strict();
387
+ export const safetySessionValidationSchema = z
388
+ .object({
389
+ valid: z.literal(true),
390
+ sessionId: accountId,
391
+ expiresAt: z.string().datetime(),
392
+ lastActivity: z.string().datetime(),
393
+ user: userResponseSchema,
394
+ actor: accountActorChainSchema,
395
+ })
396
+ .strict();
397
+ /** Minimal identity for the authenticated account's supervision management, never profile details. */
398
+ export const safetyPersonSchema = z
399
+ .object({ accountId, username: z.string().nullable(), displayName: z.string().nullable() })
400
+ .strict();
401
+ export const safetyPeopleInputSchema = z.union([
402
+ z.object({ query: z.string().trim().min(2).max(100) }).strict(),
403
+ z.object({ accountIds: uniqueIds(100) }).strict(),
404
+ ]);
405
+ export const safetyPeopleResponseSchema = z
406
+ .object({ items: z.array(safetyPersonSchema).max(100) })
407
+ .strict();
408
+ /** Existing public employer identity only; never directory content or management authority. */
409
+ export const safetyOrganizationIdentitySchema = z
410
+ .object({
411
+ accountId,
412
+ username: z.string().nullable(),
413
+ name: z.string(),
414
+ organizationType: z.enum(ORGANIZATION_TYPES),
415
+ })
416
+ .strict();
417
+ export const safetyOrganizationsInputSchema = z.union([
418
+ z
419
+ .object({
420
+ query: z.string().trim().min(1).max(100),
421
+ limit: z.number().int().min(1).max(20).optional(),
422
+ })
423
+ .strict(),
424
+ z.object({ ids: uniqueIds(100) }).strict(),
425
+ ]);
426
+ export const safetyOrganizationsResponseSchema = z
427
+ .object({
428
+ items: z
429
+ .array(z
430
+ .object({ requestedAccountId: accountId, organization: safetyOrganizationIdentitySchema })
431
+ .strict())
432
+ .max(100),
433
+ missingIds: z.array(accountId).max(100),
434
+ })
435
+ .strict();