@oxy.so/contracts 4.17.0 → 4.21.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 (50) 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/index.js +22 -1
  6. package/dist/cjs/client/safety.js +183 -2
  7. package/dist/cjs/client/signIn.js +2 -0
  8. package/dist/cjs/index.js +13 -3
  9. package/dist/cjs/safety.js +430 -1
  10. package/dist/cjs/serviceToken.js +14 -0
  11. package/dist/esm/accountAccess.js +69 -0
  12. package/dist/esm/agency.js +111 -0
  13. package/dist/esm/applicationRoles.js +207 -0
  14. package/dist/esm/client/check.js +8 -0
  15. package/dist/esm/client/index.js +1 -1
  16. package/dist/esm/client/safety.js +183 -2
  17. package/dist/esm/client/signIn.js +2 -0
  18. package/dist/esm/index.js +4 -1
  19. package/dist/esm/safety.js +423 -0
  20. package/dist/esm/serviceToken.js +11 -0
  21. package/dist/types/accountAccess.d.ts +128 -0
  22. package/dist/types/accountEmail.d.ts +2 -2
  23. package/dist/types/agency.d.ts +327 -2
  24. package/dist/types/agentKeys.d.ts +6 -6
  25. package/dist/types/applicationRoles.d.ts +438 -0
  26. package/dist/types/client/agentKeys.d.ts +2 -2
  27. package/dist/types/client/check.d.ts +3 -0
  28. package/dist/types/client/deviceSession.d.ts +1 -1
  29. package/dist/types/client/index.d.ts +1 -1
  30. package/dist/types/client/safety.d.ts +204 -4
  31. package/dist/types/client/signIn.d.ts +1 -1
  32. package/dist/types/deviceSession.d.ts +4 -4
  33. package/dist/types/email/messages.d.ts +2 -2
  34. package/dist/types/externalIdentity.d.ts +4 -4
  35. package/dist/types/index.d.ts +5 -2
  36. package/dist/types/inference/accountBilling.d.ts +2 -2
  37. package/dist/types/inference/catalogue.d.ts +22 -22
  38. package/dist/types/inference/providerConnection.d.ts +4 -4
  39. package/dist/types/inference/realtime.d.ts +74 -74
  40. package/dist/types/inference/request.d.ts +60 -60
  41. package/dist/types/inference/usage.d.ts +4 -4
  42. package/dist/types/products/access.d.ts +4 -4
  43. package/dist/types/safety.d.ts +2374 -2
  44. package/dist/types/serviceToken.d.ts +11 -0
  45. package/dist/types/signIn.d.ts +5 -5
  46. package/dist/types/userResponse.d.ts +2 -2
  47. package/package.json +5 -20
  48. package/dist/cjs/.tsbuildinfo +0 -1
  49. package/dist/esm/.tsbuildinfo +0 -1
  50. package/dist/types/.tsbuildinfo +0 -1
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ /**
3
+ * Wire contract for `GET /internal/accounts/:accountId/access/:userId`: Oxy
4
+ * answers a trusted first-party service's question "may this person act FOR
5
+ * that account, and with what?" with no session of that person in the request.
6
+ *
7
+ * It exists for the account Oxy never lets anybody act AS: a `channel`
8
+ * ({@link isDelegatedActAsEligibleKind} refuses it). An app publishes to a
9
+ * channel with ITS OWN credential, on behalf of a member Oxy vouches for here,
10
+ * so nobody holds a bearer that speaks as the channel. A long-running import
11
+ * (Oxy Move) has only a service token and `X-Oxy-User-Id`, which the
12
+ * user-facing `GET /accounts/:id/members` refuses; this is its answer.
13
+ *
14
+ * Needs a service token whose application is platform-trusted AND holds the
15
+ * privileged {@link ACCOUNTS_MEMBERSHIP_READ_SCOPE}. The answer is always 200:
16
+ * `access` is `null` for no active membership, an archived account, an unknown
17
+ * account and an unclaimed directory account alike. Callers must treat every
18
+ * other status as a refusal (fail closed), and must not cache the answer longer
19
+ * than the action it authorizes.
20
+ *
21
+ * Platform-agnostic — zod only.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.accountAccessVerdictSchema = exports.accountAccessGrantSchema = exports.accountAccessSourceSchema = exports.ACCOUNT_ACCESS_SOURCES = exports.ACCOUNTS_MEMBERSHIP_READ_SCOPE = void 0;
25
+ const zod_1 = require("zod");
26
+ const accountGraph_1 = require("./accountGraph");
27
+ /**
28
+ * The scope the endpoint requires.
29
+ *
30
+ * Privileged (staff-granted). Channel membership is sensitive because a channel
31
+ * is anonymous: its members are the people behind a voice that is published
32
+ * without their names, and "is this person a member of that channel" is
33
+ * exactly the fact the channel exists to withhold.
34
+ */
35
+ exports.ACCOUNTS_MEMBERSHIP_READ_SCOPE = 'accounts:membership:read';
36
+ /**
37
+ * Where the access comes from.
38
+ *
39
+ * - `self` — the person IS the account (a personal account asked about itself).
40
+ * - `direct` — an active membership row on the account.
41
+ * - `inherited` — an active, cascading membership on an ancestor account.
42
+ */
43
+ exports.ACCOUNT_ACCESS_SOURCES = ['self', 'direct', 'inherited'];
44
+ exports.accountAccessSourceSchema = zod_1.z.enum(exports.ACCOUNT_ACCESS_SOURCES);
45
+ /**
46
+ * One person's EFFECTIVE access over one account.
47
+ *
48
+ * `role` and `permissions` are strings rather than closed enums on purpose: the
49
+ * role and permission vocabularies are the API's, they grow, and a consumer
50
+ * pinned to an older contracts release must keep reading new ones. Gate on
51
+ * `permissions` (the role's baseline adjusted by per-member grants and
52
+ * revokes), never on `role`.
53
+ */
54
+ exports.accountAccessGrantSchema = zod_1.z.object({
55
+ role: zod_1.z.string().min(1),
56
+ permissions: zod_1.z.array(zod_1.z.string().min(1)),
57
+ /** Only an active membership grants access, so this is the one value. */
58
+ status: zod_1.z.literal('active'),
59
+ source: exports.accountAccessSourceSchema,
60
+ });
61
+ exports.accountAccessVerdictSchema = zod_1.z
62
+ .object({
63
+ accountId: zod_1.z.string().min(1),
64
+ /** `null` when no such account exists. */
65
+ kind: accountGraph_1.accountKindSchema.nullable(),
66
+ /** `null` means: this person may not act for this account. */
67
+ access: exports.accountAccessGrantSchema.nullable(),
68
+ })
69
+ .refine((verdict) => verdict.access === null || verdict.kind !== null, {
70
+ message: 'An account that does not exist grants no access',
71
+ path: ['access'],
72
+ });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizedAppEventSchema = exports.catalogRegistrationSchema = exports.appCapabilityCatalogSchema = exports.catalogEventSchema = exports.catalogToolSchema = exports.auditEventSchema = exports.auditResultSchema = exports.policyDecisionSchema = exports.invocationPrincipalSchema = exports.capabilityTicketClaimsSchema = exports.automationDefinitionSchema = exports.automationDataFlowSchema = exports.automationActorSelectionSchema = exports.automationTriggerSchema = exports.delegationGrantSchema = exports.capabilityTicketRequestSchema = exports.capabilityCoordinatorSchema = exports.executionAuthorizationRefSchema = exports.capabilityCatalogBindingSchema = exports.grantLimitSchema = exports.toolGrantOverrideSchema = exports.resourceRefSchema = exports.foregroundExecutionAuthorizationInputSchema = exports.executionActorRefSchema = exports.actorRefSchema = exports.capabilityPackageSchema = exports.CAPABILITY_PACKAGES = exports.autonomyLevelSchema = exports.AUTONOMY_LEVELS = void 0;
3
+ exports.normalizedAppEventSchema = exports.catalogRegistrationSchema = exports.appCapabilityCatalogSchema = exports.CATALOG_INTERNAL_TRANSPORTS = exports.catalogFeatureSchema = exports.catalogEventSchema = exports.catalogToolSchema = exports.auditEventSchema = exports.auditResultSchema = exports.policyDecisionSchema = exports.invocationPrincipalSchema = exports.capabilityTicketClaimsSchema = exports.automationDefinitionSchema = exports.automationDataFlowSchema = exports.automationActorSelectionSchema = exports.automationTriggerSchema = exports.agentRequesterCeilingSchema = exports.agentRequesterCeilingWriteSchema = exports.AGENT_REQUESTER_CEILING_AUTONOMY = exports.delegationGrantSchema = exports.capabilityTicketRequestSchema = exports.capabilityCoordinatorSchema = exports.executionAuthorizationRefSchema = exports.capabilityCatalogBindingSchema = exports.grantLimitSchema = exports.toolGrantOverrideSchema = exports.resourceRefSchema = exports.foregroundExecutionAuthorizationInputSchema = exports.executionActorRefSchema = exports.actorRefSchema = exports.capabilityPackageSchema = exports.CAPABILITY_PACKAGES = exports.autonomyLevelSchema = exports.AUTONOMY_LEVELS = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const capabilityInvocation_1 = require("./capabilityInvocation");
6
6
  const nonEmptyStringSchema = zod_1.z.string().trim().min(1);
@@ -143,6 +143,35 @@ exports.delegationGrantSchema = zod_1.z
143
143
  updatedAt: zod_1.z.string().datetime(),
144
144
  })
145
145
  .strict();
146
+ /**
147
+ * The two levels an owner publishes for whoever uses their agent (ADR 0042):
148
+ * "Ver" and "Ver y actuar". A ceiling never says `draft` or `execute_on_request`:
149
+ * an agent is either allowed to act on its own or not at all, and nothing ever
150
+ * asks the person for approval.
151
+ */
152
+ exports.AGENT_REQUESTER_CEILING_AUTONOMY = ['read_only', 'autonomous'];
153
+ /** What an agent may do in one app for whoever uses it; written by an operator of the bot. */
154
+ exports.agentRequesterCeilingWriteSchema = zod_1.z
155
+ .object({
156
+ capabilityPackages: zod_1.z.array(exports.capabilityPackageSchema),
157
+ capabilities: zod_1.z.array(identifierSchema).default([]),
158
+ toolOverrides: zod_1.z.array(exports.toolGrantOverrideSchema).default([]),
159
+ limits: zod_1.z.array(exports.grantLimitSchema).default([]),
160
+ maximumAutonomy: zod_1.z.enum(exports.AGENT_REQUESTER_CEILING_AUTONOMY),
161
+ })
162
+ .strict();
163
+ exports.agentRequesterCeilingSchema = exports.agentRequesterCeilingWriteSchema
164
+ .extend({
165
+ id: identifierSchema,
166
+ agentAccountId: identifierSchema,
167
+ appId: identifierSchema,
168
+ catalog: exports.capabilityCatalogBindingSchema,
169
+ version: zod_1.z.number().int().positive(),
170
+ revokedAt: zod_1.z.string().datetime().nullable(),
171
+ createdAt: zod_1.z.string().datetime(),
172
+ updatedAt: zod_1.z.string().datetime(),
173
+ })
174
+ .strict();
146
175
  exports.automationTriggerSchema = zod_1.z.discriminatedUnion('type', [
147
176
  zod_1.z.object({ type: zod_1.z.literal('manual') }).strict(),
148
177
  zod_1.z
@@ -204,6 +233,8 @@ exports.capabilityTicketClaimsSchema = zod_1.z
204
233
  /** Signed only for callers that explicitly requested a pinned catalogue. */
205
234
  catalog: exports.capabilityCatalogBindingSchema.optional(),
206
235
  grantId: identifierSchema.optional(),
236
+ /** The owner's published ceiling that bounds an agent acting for a non-owner (ADR 0042). */
237
+ ceilingId: identifierSchema.optional(),
207
238
  requesterAccountId: identifierSchema,
208
239
  ownerAccountId: identifierSchema,
209
240
  actor: exports.executionActorRefSchema,
@@ -242,6 +273,24 @@ exports.capabilityTicketClaimsSchema = zod_1.z
242
273
  message: 'requester actors require their exact principal and direct read-only authority without standing grants',
243
274
  });
244
275
  }
276
+ if (claims.ceilingId !== undefined) {
277
+ if (claims.grantId !== undefined) {
278
+ context.addIssue({
279
+ code: zod_1.z.ZodIssueCode.custom,
280
+ path: ['ceilingId'],
281
+ message: 'a ticket is bounded by either a grant or a ceiling, never both',
282
+ });
283
+ }
284
+ if (claims.actor.type !== 'agent' ||
285
+ claims.ownerAccountId !== claims.requesterAccountId ||
286
+ claims.resource.resourceId !== claims.resource.effectiveAccountId) {
287
+ context.addIssue({
288
+ code: zod_1.z.ZodIssueCode.custom,
289
+ path: ['ceilingId'],
290
+ message: "a ceiling bounds an agent acting on the requester's own authority at an account root",
291
+ });
292
+ }
293
+ }
245
294
  if (claims.exp <= claims.iat) {
246
295
  context.addIssue({
247
296
  code: zod_1.z.ZodIssueCode.custom,
@@ -303,6 +352,7 @@ exports.policyDecisionSchema = zod_1.z
303
352
  reason: identifierSchema,
304
353
  effectiveAutonomy: exports.autonomyLevelSchema.optional(),
305
354
  grantId: identifierSchema.optional(),
355
+ ceilingId: identifierSchema.optional(),
306
356
  })
307
357
  .strict();
308
358
  exports.auditResultSchema = zod_1.z
@@ -512,6 +562,24 @@ exports.catalogEventSchema = zod_1.z
512
562
  resourceTypes: zod_1.z.array(identifierSchema).min(1),
513
563
  })
514
564
  .strict();
565
+ /**
566
+ * A feature an agent may gate on (Alia ADR 0020 §6): the app answers whether the
567
+ * person may use it (`checkTool`) and, when using it costs something, spends it
568
+ * (`consumeTool`). Both are internal tools of the same catalogue.
569
+ */
570
+ exports.catalogFeatureSchema = zod_1.z
571
+ .object({
572
+ key: zod_1.z
573
+ .string()
574
+ .regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)*$/, 'feature keys are dot-separated slugs')
575
+ .max(128),
576
+ description: nonEmptyStringSchema,
577
+ checkTool: identifierSchema,
578
+ consumeTool: identifierSchema.optional(),
579
+ })
580
+ .strict();
581
+ /** How a coordinator reaches the catalogue's internal tools. */
582
+ exports.CATALOG_INTERNAL_TRANSPORTS = ['http', 'mcp'];
515
583
  exports.appCapabilityCatalogSchema = zod_1.z
516
584
  .object({
517
585
  schemaVersion: zod_1.z.literal('1'),
@@ -567,11 +635,54 @@ exports.appCapabilityCatalogSchema = zod_1.z
567
635
  })
568
636
  .strict()
569
637
  .optional(),
638
+ /**
639
+ * `mcp`: internal tools are served at `<internalBaseUrl>/_oxy/mcp`. Absent or
640
+ * `http`: each tool's `invocation` path on `internalBaseUrl`.
641
+ */
642
+ internalTransport: zod_1.z.enum(exports.CATALOG_INTERNAL_TRANSPORTS).optional(),
643
+ features: zod_1.z.array(exports.catalogFeatureSchema).optional(),
570
644
  tools: zod_1.z.array(exports.catalogToolSchema),
571
645
  events: zod_1.z.array(exports.catalogEventSchema),
572
646
  })
573
647
  .strict()
574
648
  .superRefine((catalog, context) => {
649
+ const toolsByName = new Map(catalog.tools.map((tool) => [tool.name, tool]));
650
+ const featureKeys = new Set();
651
+ for (const [index, feature] of (catalog.features ?? []).entries()) {
652
+ if (featureKeys.has(feature.key)) {
653
+ context.addIssue({
654
+ code: zod_1.z.ZodIssueCode.custom,
655
+ message: `Duplicate feature key: ${feature.key}`,
656
+ path: ['features', index, 'key'],
657
+ });
658
+ }
659
+ featureKeys.add(feature.key);
660
+ const check = toolsByName.get(feature.checkTool);
661
+ if (!check ||
662
+ check.effect !== 'read' ||
663
+ check.exposure.length !== 1 ||
664
+ check.exposure[0] !== 'internal') {
665
+ context.addIssue({
666
+ code: zod_1.z.ZodIssueCode.custom,
667
+ message: 'checkTool must name an internal-only read tool of this catalogue',
668
+ path: ['features', index, 'checkTool'],
669
+ });
670
+ }
671
+ if (feature.consumeTool !== undefined) {
672
+ const consume = toolsByName.get(feature.consumeTool);
673
+ if (!consume ||
674
+ consume.effect === 'read' ||
675
+ consume.idempotency !== 'required' ||
676
+ consume.exposure.length !== 1 ||
677
+ consume.exposure[0] !== 'internal') {
678
+ context.addIssue({
679
+ code: zod_1.z.ZodIssueCode.custom,
680
+ message: 'consumeTool must name an internal-only effectful tool that requires idempotency keys',
681
+ path: ['features', index, 'consumeTool'],
682
+ });
683
+ }
684
+ }
685
+ }
575
686
  const names = new Set();
576
687
  for (const [index, tool] of catalog.tools.entries()) {
577
688
  if (names.has(tool.name)) {
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * Application roles — the wire contract for permissions an APPLICATION declares
4
+ * about its own users, and for the grants that give them to Oxy users.
5
+ *
6
+ * Oxy decides who holds an app's operator powers (who may run Mercaria's
7
+ * payments desk, moderate GoWay, administer Homiio), so no app keeps an
8
+ * allow-list of Oxy user ids in its environment. The app declares a closed
9
+ * vocabulary of keys, its owners grant them to people, and the app's backend
10
+ * asks Oxy which of them the human behind a request holds
11
+ * (`GET /applications/:appId/roles/me`, read through `@oxy.so/core/server`).
12
+ *
13
+ * ## Why an application key can never collide with an Oxy permission
14
+ *
15
+ * Every permission Oxy itself checks (`app:update`, `apps:create`,
16
+ * `credentials:rotate`, every scope) is spelled with a COLON. An application key
17
+ * is dot-separated lowercase segments and cannot contain one, so the two
18
+ * vocabularies are disjoint by syntax, not by convention. They are also stored
19
+ * apart (scoped to one application id) and no Oxy gate ever reads an
20
+ * application key. A key means something only to the application that declared
21
+ * it.
22
+ *
23
+ * The pattern is exported as a SOURCE string because the API renders the same
24
+ * text into a Postgres CHECK; JavaScript's `RegExp` and Postgres' ARE agree on
25
+ * everything it uses (anchors, classes, a capturing group, a bounded repeat).
26
+ *
27
+ * Platform-agnostic — zod only, no react/react-native/expo.
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.applicationRolesMeSchema = exports.listApplicationRoleGrantsQuerySchema = exports.revokeApplicationRoleGrantRequestSchema = exports.createApplicationRoleGrantRequestSchema = exports.applicationRoleGrantListSchema = exports.applicationRoleGrantSchema = exports.declareApplicationPermissionsResponseSchema = exports.applicationPermissionListSchema = exports.applicationPermissionSchema = exports.declareApplicationPermissionsRequestSchema = exports.applicationPermissionDeclarationSchema = exports.applicationPermissionKeySchema = exports.APPLICATION_ROLE_ERROR_CODES = exports.APPLICATION_ROLE_GRANTS_PAGE_MAX = exports.APPLICATION_ROLE_GRANTS_PAGE_DEFAULT = exports.APPLICATION_ROLE_REASON_MAX_LENGTH = exports.APPLICATION_PERMISSIONS_MAX_DECLARED = exports.APPLICATION_PERMISSION_DESCRIPTION_MAX_LENGTH = exports.APPLICATION_PERMISSION_KEY_MAX_LENGTH = exports.APPLICATION_PERMISSION_KEY_PATTERN = exports.APPLICATION_PERMISSION_KEY_PATTERN_SOURCE = void 0;
31
+ exports.isApplicationPermissionKey = isApplicationPermissionKey;
32
+ const zod_1 = require("zod");
33
+ /**
34
+ * Two to four dot-separated segments, each a lowercase letter followed by
35
+ * lowercase letters, digits or underscores: `payments.operate`,
36
+ * `catalog.review.publish`. At least two segments so a key always names an
37
+ * area and an action; never a colon (see the header).
38
+ */
39
+ exports.APPLICATION_PERMISSION_KEY_PATTERN_SOURCE = '^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*){1,3}$';
40
+ exports.APPLICATION_PERMISSION_KEY_PATTERN = new RegExp(exports.APPLICATION_PERMISSION_KEY_PATTERN_SOURCE);
41
+ exports.APPLICATION_PERMISSION_KEY_MAX_LENGTH = 64;
42
+ exports.APPLICATION_PERMISSION_DESCRIPTION_MAX_LENGTH = 280;
43
+ /** How many live keys one application may declare. Operator powers are few. */
44
+ exports.APPLICATION_PERMISSIONS_MAX_DECLARED = 64;
45
+ exports.APPLICATION_ROLE_REASON_MAX_LENGTH = 500;
46
+ exports.APPLICATION_ROLE_GRANTS_PAGE_DEFAULT = 50;
47
+ exports.APPLICATION_ROLE_GRANTS_PAGE_MAX = 200;
48
+ /**
49
+ * The `error` codes the application-roles routes answer beyond the generic
50
+ * ones (`BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`). Renaming one
51
+ * is a breaking change.
52
+ */
53
+ exports.APPLICATION_ROLE_ERROR_CODES = {
54
+ /** 403: a grant naming yourself on an application you do not own outright. */
55
+ selfGrantForbidden: 'SELF_GRANT_FORBIDDEN',
56
+ /** 422: the key is not a live declared permission of this application. */
57
+ unknownPermission: 'UNKNOWN_PERMISSION',
58
+ /** 422: the grantee is not a person (an organization, project, bot or channel). */
59
+ granteeNotAPerson: 'GRANTEE_NOT_A_PERSON',
60
+ /** 409: the grantee already holds the key; `details.grantId` names the live grant. */
61
+ alreadyGranted: 'ALREADY_GRANTED',
62
+ /** 409: the grant was already revoked. */
63
+ alreadyRevoked: 'ALREADY_REVOKED',
64
+ /** 403: the bearer's session belongs to a DIFFERENT application. */
65
+ sessionApplicationMismatch: 'SESSION_APPLICATION_MISMATCH',
66
+ };
67
+ /** Whether `value` is a well-formed application permission key. */
68
+ function isApplicationPermissionKey(value) {
69
+ return (typeof value === 'string' &&
70
+ value.length <= exports.APPLICATION_PERMISSION_KEY_MAX_LENGTH &&
71
+ exports.APPLICATION_PERMISSION_KEY_PATTERN.test(value));
72
+ }
73
+ exports.applicationPermissionKeySchema = zod_1.z
74
+ .string()
75
+ .max(exports.APPLICATION_PERMISSION_KEY_MAX_LENGTH)
76
+ .regex(exports.APPLICATION_PERMISSION_KEY_PATTERN, 'A permission key is 2-4 dot-separated lowercase segments, e.g. payments.operate');
77
+ /** Plain text: no control characters other than a newline or a tab. */
78
+ function plainText(max) {
79
+ return zod_1.z
80
+ .string()
81
+ .trim()
82
+ .min(1)
83
+ .max(max)
84
+ .refine((value) => Array.from(value).every((character) => {
85
+ const code = character.charCodeAt(0);
86
+ return (code >= 32 && code !== 127) || character === '\n' || character === '\t';
87
+ }), 'Must be plain text');
88
+ }
89
+ const id = zod_1.z.string().min(1).max(128);
90
+ const isoTimestamp = zod_1.z.string().min(1);
91
+ const reasonSchema = plainText(exports.APPLICATION_ROLE_REASON_MAX_LENGTH);
92
+ /** One key as the application's owners declare it. */
93
+ exports.applicationPermissionDeclarationSchema = zod_1.z
94
+ .object({
95
+ key: exports.applicationPermissionKeySchema,
96
+ description: plainText(exports.APPLICATION_PERMISSION_DESCRIPTION_MAX_LENGTH),
97
+ })
98
+ .strict();
99
+ /**
100
+ * `PUT /applications/:appId/roles/permissions` — the WHOLE vocabulary.
101
+ *
102
+ * Declarative: a key present here and not live is added, a live key absent
103
+ * here is retired (and every grant of it revoked), a live key present here
104
+ * keeps its identity and takes the new description. Sending the same body twice
105
+ * changes nothing.
106
+ */
107
+ exports.declareApplicationPermissionsRequestSchema = zod_1.z
108
+ .object({
109
+ permissions: zod_1.z
110
+ .array(exports.applicationPermissionDeclarationSchema)
111
+ .max(exports.APPLICATION_PERMISSIONS_MAX_DECLARED),
112
+ })
113
+ .strict()
114
+ .superRefine((value, ctx) => {
115
+ const seen = new Set();
116
+ value.permissions.forEach((permission, index) => {
117
+ if (seen.has(permission.key)) {
118
+ ctx.addIssue({
119
+ code: zod_1.z.ZodIssueCode.custom,
120
+ path: ['permissions', index, 'key'],
121
+ message: `Duplicate permission key ${permission.key}`,
122
+ });
123
+ }
124
+ seen.add(permission.key);
125
+ });
126
+ });
127
+ /** A declared key. Retired keys are history: they confer nothing. */
128
+ exports.applicationPermissionSchema = zod_1.z.object({
129
+ key: exports.applicationPermissionKeySchema,
130
+ description: zod_1.z.string(),
131
+ declaredAt: isoTimestamp,
132
+ declaredByUserId: id.nullable(),
133
+ retiredAt: isoTimestamp.nullable(),
134
+ retiredByUserId: id.nullable(),
135
+ });
136
+ exports.applicationPermissionListSchema = zod_1.z.object({
137
+ items: zod_1.z.array(exports.applicationPermissionSchema),
138
+ });
139
+ exports.declareApplicationPermissionsResponseSchema = exports.applicationPermissionListSchema.extend({
140
+ /** Keys that became live with this declaration. */
141
+ added: zod_1.z.array(exports.applicationPermissionKeySchema),
142
+ /** Keys this declaration retired. */
143
+ retired: zod_1.z.array(exports.applicationPermissionKeySchema),
144
+ /** Live keys whose description changed. */
145
+ described: zod_1.z.array(exports.applicationPermissionKeySchema),
146
+ /** Live grants revoked because their key was retired. */
147
+ revokedGrants: zod_1.z.number().int().nonnegative(),
148
+ });
149
+ /** One grant of one key to one Oxy user. Revocation is a stamp, never a delete. */
150
+ exports.applicationRoleGrantSchema = zod_1.z.object({
151
+ id,
152
+ applicationId: id,
153
+ permission: exports.applicationPermissionKeySchema,
154
+ userId: id,
155
+ /** The grantee's handle, for display. */
156
+ username: zod_1.z.string().nullable(),
157
+ /** The HUMAN who granted it; null once that account was erased. */
158
+ grantedByUserId: id.nullable(),
159
+ grantedAt: isoTimestamp,
160
+ grantReason: zod_1.z.string().nullable(),
161
+ revokedAt: isoTimestamp.nullable(),
162
+ revokedByUserId: id.nullable(),
163
+ revokeReason: zod_1.z.string().nullable(),
164
+ });
165
+ exports.applicationRoleGrantListSchema = zod_1.z.object({
166
+ items: zod_1.z.array(exports.applicationRoleGrantSchema),
167
+ nextCursor: zod_1.z.string().nullable(),
168
+ });
169
+ /** `POST /applications/:appId/roles/grants` — name the grantee by id OR handle. */
170
+ exports.createApplicationRoleGrantRequestSchema = zod_1.z
171
+ .object({
172
+ userId: id.optional(),
173
+ username: zod_1.z.string().trim().min(1).max(64).optional(),
174
+ permission: exports.applicationPermissionKeySchema,
175
+ reason: reasonSchema.optional(),
176
+ })
177
+ .strict()
178
+ .refine((value) => (value.userId === undefined) !== (value.username === undefined), {
179
+ message: 'Name the grantee with exactly one of userId or username',
180
+ path: ['userId'],
181
+ });
182
+ /** `POST /applications/:appId/roles/grants/:grantId/revoke`. */
183
+ exports.revokeApplicationRoleGrantRequestSchema = zod_1.z
184
+ .object({
185
+ reason: reasonSchema.optional(),
186
+ })
187
+ .strict();
188
+ /** `GET /applications/:appId/roles/grants` query. */
189
+ exports.listApplicationRoleGrantsQuerySchema = zod_1.z
190
+ .object({
191
+ permission: exports.applicationPermissionKeySchema.optional(),
192
+ userId: id.optional(),
193
+ includeRevoked: zod_1.z
194
+ .enum(['true', 'false'])
195
+ .transform((value) => value === 'true')
196
+ .optional(),
197
+ limit: zod_1.z.coerce.number().int().min(1).max(exports.APPLICATION_ROLE_GRANTS_PAGE_MAX).optional(),
198
+ cursor: zod_1.z.string().min(1).max(512).optional(),
199
+ })
200
+ .strict();
201
+ /**
202
+ * `GET /applications/:appId/roles/me` — the keys the HUMAN behind the bearer
203
+ * holds for this application, in no particular order. `userId` is that human
204
+ * (the operator), which is NOT the account the session speaks as when a person
205
+ * is acting as an organization.
206
+ */
207
+ exports.applicationRolesMeSchema = zod_1.z.object({
208
+ applicationId: id,
209
+ userId: id,
210
+ permissions: zod_1.z.array(exports.applicationPermissionKeySchema),
211
+ });
@@ -25,6 +25,7 @@
25
25
  * strictObject(shape) unknown keys FAIL (`.strict()`)
26
26
  * looseObject(shape) unknown keys are KEPT (`.passthrough()`)
27
27
  * refine(c, test) a further condition, run once `c` passes
28
+ * transform(c, f) `f` rewrites the value once `c` passes
28
29
  * union / discriminatedUnion as Zod 3 reports them
29
30
  *
30
31
  * A failure carries the first issue Zod would report and whether Zod would have
@@ -52,6 +53,7 @@ exports.nullable = nullable;
52
53
  exports.withDefault = withDefault;
53
54
  exports.refine = refine;
54
55
  exports.refineAt = refineAt;
56
+ exports.transform = transform;
55
57
  exports.branded = branded;
56
58
  exports.minItems = minItems;
57
59
  exports.maxItems = maxItems;
@@ -265,6 +267,13 @@ function refineAt(inner, test) {
265
267
  : checked;
266
268
  };
267
269
  }
270
+ /** Zod's `.transform()`: rewrites a value `inner` accepted. */
271
+ function transform(inner, rewrite) {
272
+ return (input, path) => {
273
+ const checked = inner(input, path);
274
+ return checked.ok ? ok(rewrite(checked.value)) : checked;
275
+ };
276
+ }
268
277
  /** Narrows a checked value to a branded contract type (Zod's `.brand()`). */
269
278
  function branded(inner) {
270
279
  return inner;
@@ -18,7 +18,8 @@
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.deviceJoinCodeResponseCheck = exports.deviceBackgroundCredentialResponseCheck = exports.activeTokenCheck = exports.SESSION_ACCOUNTS_CHANGED_EVENT = exports.deviceDirectorySyncCheck = exports.deviceDirectoryCheck = exports.deviceActivateResponseCheck = exports.signedRecordEnvelopeCheck = exports.externalIdentityReferenceCheck = exports.loginResultCheck = exports.isLoopbackOrigin = exports.executeAgentKeyOperationCheck = exports.agentVerifyRequestCheck = exports.agentProofClaimsCheck = exports.agentKeyOperationResultCheck = exports.agentKeyOperationCheck = exports.agentKeyOperationChallengeCheck = exports.agentKeyListResponseCheck = exports.agentChallengeRequestCheck = exports.AGENT_PROOF_TTL_MS = exports.AGENT_PROOF_AUDIENCE = exports.AGENT_PROOF_ACTIONS = exports.ACCOUNT_AUTH_METHODS = exports.emailVerificationStartResponseCheck = exports.emailVerificationConfirmResponseCheck = exports.emailTicketCheck = exports.emailAddressCheck = exports.EMAIL_VERIFICATION_PURPOSES = exports.EMAIL_VERIFICATION_ERROR_CODES = exports.EMAIL_TICKET_TTL_MS = exports.EMAIL_CODE_TTL_MS = exports.EMAIL_CODE_MAX_ATTEMPTS = exports.EMAIL_CODE_LENGTH = exports.newlyAddedRetiredCategories = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.isOperatorSwitchTargetKind = exports.isDelegatedActAsEligibleKind = exports.isAccountKind = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.RETIRED_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.CHILD_ACCOUNT_KINDS = exports.ACCOUNT_KINDS = exports.ACCOUNT_CATEGORY_KINDS = exports.ACCOUNT_CATEGORY_IDS = exports.parseContract = exports.formatCheckPath = exports.checkContract = exports.ContractCheckError = void 0;
20
20
  exports.subjectProductAccessCheck = exports.productSubscriptionsResponseCheck = exports.productSubscriptionCancellationResultCheck = exports.personalPlanCheckoutResultCheck = exports.personalPlanCheckoutRequestCheck = exports.personalPlanCatalogueCheck = exports.cancelProductSubscriptionCheck = exports.updateOrganizationCheck = exports.organizationNameCheck = exports.organizationListCheck = exports.organizationImportBatchResultCheck = exports.organizationExternalResolutionCheck = exports.organizationExternalCheck = exports.organizationCurationResultCheck = exports.organizationClaimReviewCheck = exports.organizationClaimListCheck = exports.organizationClaimInputCheck = exports.organizationClaimDetailCheck = exports.organizationClaimCheck = exports.organizationCheck = exports.organizationBatchCheck = exports.createOrganizationCheck = exports.ORGANIZATION_TYPES = exports.ORGANIZATION_STATUSES = exports.ORGANIZATION_NAME_MAX_LENGTH = exports.ORGANIZATION_ERROR_CODES = exports.ORGANIZATION_CLAIM_STATUSES = exports.ORGANIZATION_CLAIM_DECISIONS = exports.ORGANIZATION_ALIAS_KINDS = exports.canonicalJson = exports.buildIdentityProofMessage = exports.IDENTITY_PROOF_VERSION = exports.IDENTITY_PROOF_DOMAIN = exports.IDENTITY_PROOF_CHALLENGE_TTL_MS = exports.IDENTITY_PROOF_AUDIENCE = exports.IDENTITY_PROOF_ACTION_VALUES = exports.IDENTITY_PROOF_ACTIONS = exports.IDENTITY_ERROR_CODES = exports.parseIdentityLinkQrPayload = exports.identityLinkStateCheck = exports.identityLinkCreateResponseCheck = exports.buildIdentityLinkQrPayload = exports.IDENTITY_LINK_STATUSES = exports.IDENTITY_LINK_QR_PREFIX = exports.sessionAccountsChangedEventCheck = exports.deviceTokenMintResponseCheck = exports.deviceSessionSyncCheck = exports.deviceSessionStateCheck = exports.deviceRegisterResponseCheck = exports.deviceJoinResponseCheck = void 0;
21
- exports.userNameCheck = exports.usernameCheckForAccountKind = exports.usernameCheck = exports.stripDisallowedUsernameCharacters = exports.botUsernameCheck = exports.applyBotUsernameSuffix = exports.USERNAME_PATTERN_SOURCE = exports.USERNAME_MIN_LENGTH = exports.USERNAME_MAX_LENGTH = exports.USERNAME_INVALID_MESSAGE = exports.RESERVED_USERNAME_MESSAGE = exports.RESERVED_USERNAMES = exports.NUMERIC_USERNAME_MESSAGE = exports.BOT_USERNAME_SUFFIX = exports.BOT_USERNAME_INVALID_MESSAGE = exports.totpEnrollResponseCheck = exports.totpBackupCodesResponseCheck = exports.signInMethodsCheck = exports.secondFactorRequiredCheck = exports.reauthProofCheck = exports.normalizeEmailSignInCode = exports.isSecondFactorRequired = exports.isEmailSignInChooseAccount = exports.emailSignInStartResponseCheck = exports.emailSignInPendingCheck = exports.emailSignInLinkResponseCheck = exports.emailSignInChooseAccountCheck = exports.TOTP_PERIOD_SECONDS = exports.TOTP_DIGITS = exports.TOTP_BACKUP_CODE_COUNT = exports.SIGN_IN_ERROR_CODES = exports.SIGNIN_SECOND_FACTOR_TTL_MS = exports.SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS = exports.REAUTH_ACTIONS = exports.PASSWORD_MIN_LENGTH = exports.PASSWORD_MAX_LENGTH = exports.EMAIL_SIGNIN_LONG_CODE_LENGTH = exports.EMAIL_SIGNIN_LONG_CODE_ALPHABET = exports.EMAIL_SIGNIN_LINK_TTL_MS = exports.EMAIL_SIGNIN_CHOICE_TTL_MS = exports.EMAIL_ACCOUNTS_MAX = exports.publicApplicationCheck = exports.profileAccessInputCheck = exports.profileAccessDecisionCheck = exports.isFullReputationBalance = exports.recommendationRequestCheck = exports.subscriptionCreditGrantsResponseCheck = exports.subjectProductAccessQueryCheck = void 0;
21
+ exports.secondFactorRequiredCheck = exports.reauthProofCheck = exports.normalizeEmailSignInCode = exports.isSecondFactorRequired = exports.isEmailSignInChooseAccount = exports.emailSignInStartResponseCheck = exports.emailSignInPendingCheck = exports.emailSignInLinkResponseCheck = exports.emailSignInChooseAccountCheck = exports.TOTP_PERIOD_SECONDS = exports.TOTP_DIGITS = exports.TOTP_BACKUP_CODE_COUNT = exports.SIGN_IN_ERROR_CODES = exports.SIGNIN_SECOND_FACTOR_TTL_MS = exports.SIGNIN_SECOND_FACTOR_MAX_ATTEMPTS = exports.REAUTH_ACTIONS = exports.PASSWORD_MIN_LENGTH = exports.PASSWORD_MAX_LENGTH = exports.EMAIL_SIGNIN_LONG_CODE_LENGTH = exports.EMAIL_SIGNIN_LONG_CODE_ALPHABET = exports.EMAIL_SIGNIN_LINK_TTL_MS = exports.EMAIL_SIGNIN_CHOICE_TTL_MS = exports.EMAIL_ACCOUNTS_MAX = exports.publicApplicationCheck = exports.supervisionStateCheck = exports.supervisionReviewInputCheck = exports.supervisionInviteInputCheck = exports.supervisionInvitationCheck = exports.supervisionContactsResponseCheck = exports.supervisionContactInputCheck = exports.supervisionAcceptInputCheck = exports.safetyPeopleResponseCheck = exports.safetyPeopleInputCheck = exports.safetyOrganizationsResponseCheck = exports.safetyOrganizationsInputCheck = exports.purchaseReviewInputCheck = exports.purchaseApprovalListCheck = exports.purchaseApprovalCheck = exports.profileAccessInputCheck = exports.profileAccessDecisionCheck = exports.SUPERVISION_STATUSES = exports.SUPERVISION_INVITATION_STATUSES = exports.SUPERVISION_CONSENT_VERSION = exports.SUPERVISED_ACTIONS = exports.SAFETY_POLICY_VERSION = exports.PURCHASE_APPROVAL_STATUSES = exports.isFullReputationBalance = exports.recommendationRequestCheck = exports.subscriptionCreditGrantsResponseCheck = exports.subjectProductAccessQueryCheck = void 0;
22
+ exports.userNameCheck = exports.usernameCheckForAccountKind = exports.usernameCheck = exports.stripDisallowedUsernameCharacters = exports.botUsernameCheck = exports.applyBotUsernameSuffix = exports.USERNAME_PATTERN_SOURCE = exports.USERNAME_MIN_LENGTH = exports.USERNAME_MAX_LENGTH = exports.USERNAME_INVALID_MESSAGE = exports.RESERVED_USERNAME_MESSAGE = exports.RESERVED_USERNAMES = exports.NUMERIC_USERNAME_MESSAGE = exports.BOT_USERNAME_SUFFIX = exports.BOT_USERNAME_INVALID_MESSAGE = exports.totpEnrollResponseCheck = exports.totpBackupCodesResponseCheck = exports.signInMethodsCheck = void 0;
22
23
  var check_1 = require("./check");
23
24
  Object.defineProperty(exports, "ContractCheckError", { enumerable: true, get: function () { return check_1.ContractCheckError; } });
24
25
  Object.defineProperty(exports, "checkContract", { enumerable: true, get: function () { return check_1.checkContract; } });
@@ -140,8 +141,28 @@ Object.defineProperty(exports, "recommendationRequestCheck", { enumerable: true,
140
141
  var reputation_1 = require("./reputation");
141
142
  Object.defineProperty(exports, "isFullReputationBalance", { enumerable: true, get: function () { return reputation_1.isFullReputationBalance; } });
142
143
  var safety_1 = require("./safety");
144
+ Object.defineProperty(exports, "PURCHASE_APPROVAL_STATUSES", { enumerable: true, get: function () { return safety_1.PURCHASE_APPROVAL_STATUSES; } });
145
+ Object.defineProperty(exports, "SAFETY_POLICY_VERSION", { enumerable: true, get: function () { return safety_1.SAFETY_POLICY_VERSION; } });
146
+ Object.defineProperty(exports, "SUPERVISED_ACTIONS", { enumerable: true, get: function () { return safety_1.SUPERVISED_ACTIONS; } });
147
+ Object.defineProperty(exports, "SUPERVISION_CONSENT_VERSION", { enumerable: true, get: function () { return safety_1.SUPERVISION_CONSENT_VERSION; } });
148
+ Object.defineProperty(exports, "SUPERVISION_INVITATION_STATUSES", { enumerable: true, get: function () { return safety_1.SUPERVISION_INVITATION_STATUSES; } });
149
+ Object.defineProperty(exports, "SUPERVISION_STATUSES", { enumerable: true, get: function () { return safety_1.SUPERVISION_STATUSES; } });
143
150
  Object.defineProperty(exports, "profileAccessDecisionCheck", { enumerable: true, get: function () { return safety_1.profileAccessDecisionCheck; } });
144
151
  Object.defineProperty(exports, "profileAccessInputCheck", { enumerable: true, get: function () { return safety_1.profileAccessInputCheck; } });
152
+ Object.defineProperty(exports, "purchaseApprovalCheck", { enumerable: true, get: function () { return safety_1.purchaseApprovalCheck; } });
153
+ Object.defineProperty(exports, "purchaseApprovalListCheck", { enumerable: true, get: function () { return safety_1.purchaseApprovalListCheck; } });
154
+ Object.defineProperty(exports, "purchaseReviewInputCheck", { enumerable: true, get: function () { return safety_1.purchaseReviewInputCheck; } });
155
+ Object.defineProperty(exports, "safetyOrganizationsInputCheck", { enumerable: true, get: function () { return safety_1.safetyOrganizationsInputCheck; } });
156
+ Object.defineProperty(exports, "safetyOrganizationsResponseCheck", { enumerable: true, get: function () { return safety_1.safetyOrganizationsResponseCheck; } });
157
+ Object.defineProperty(exports, "safetyPeopleInputCheck", { enumerable: true, get: function () { return safety_1.safetyPeopleInputCheck; } });
158
+ Object.defineProperty(exports, "safetyPeopleResponseCheck", { enumerable: true, get: function () { return safety_1.safetyPeopleResponseCheck; } });
159
+ Object.defineProperty(exports, "supervisionAcceptInputCheck", { enumerable: true, get: function () { return safety_1.supervisionAcceptInputCheck; } });
160
+ Object.defineProperty(exports, "supervisionContactInputCheck", { enumerable: true, get: function () { return safety_1.supervisionContactInputCheck; } });
161
+ Object.defineProperty(exports, "supervisionContactsResponseCheck", { enumerable: true, get: function () { return safety_1.supervisionContactsResponseCheck; } });
162
+ Object.defineProperty(exports, "supervisionInvitationCheck", { enumerable: true, get: function () { return safety_1.supervisionInvitationCheck; } });
163
+ Object.defineProperty(exports, "supervisionInviteInputCheck", { enumerable: true, get: function () { return safety_1.supervisionInviteInputCheck; } });
164
+ Object.defineProperty(exports, "supervisionReviewInputCheck", { enumerable: true, get: function () { return safety_1.supervisionReviewInputCheck; } });
165
+ Object.defineProperty(exports, "supervisionStateCheck", { enumerable: true, get: function () { return safety_1.supervisionStateCheck; } });
145
166
  var sessionStatus_1 = require("./sessionStatus");
146
167
  Object.defineProperty(exports, "publicApplicationCheck", { enumerable: true, get: function () { return sessionStatus_1.publicApplicationCheck; } });
147
168
  var signIn_1 = require("./signIn");