@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.
- package/dist/cjs/accountAccess.js +72 -0
- package/dist/cjs/agency.js +112 -1
- package/dist/cjs/applicationRoles.js +211 -0
- package/dist/cjs/client/check.js +9 -0
- package/dist/cjs/client/contentClaims.js +139 -0
- package/dist/cjs/client/index.js +37 -1
- package/dist/cjs/client/safety.js +183 -2
- package/dist/cjs/client/signIn.js +2 -0
- package/dist/cjs/contentClaims.js +246 -0
- package/dist/cjs/index.js +14 -3
- package/dist/cjs/safety.js +430 -1
- package/dist/cjs/serviceToken.js +14 -0
- package/dist/esm/accountAccess.js +69 -0
- package/dist/esm/agency.js +111 -0
- package/dist/esm/applicationRoles.js +207 -0
- package/dist/esm/client/check.js +8 -0
- package/dist/esm/client/contentClaims.js +134 -0
- package/dist/esm/client/index.js +2 -1
- package/dist/esm/client/safety.js +183 -2
- package/dist/esm/client/signIn.js +2 -0
- package/dist/esm/contentClaims.js +229 -0
- package/dist/esm/index.js +5 -1
- package/dist/esm/safety.js +423 -0
- package/dist/esm/serviceToken.js +11 -0
- package/dist/types/accountAccess.d.ts +128 -0
- package/dist/types/accountEmail.d.ts +2 -2
- package/dist/types/agency.d.ts +327 -2
- package/dist/types/agentKeys.d.ts +6 -6
- package/dist/types/applicationRoles.d.ts +438 -0
- package/dist/types/client/agentKeys.d.ts +2 -2
- package/dist/types/client/check.d.ts +3 -0
- package/dist/types/client/contentClaims.d.ts +106 -0
- package/dist/types/client/deviceSession.d.ts +1 -1
- package/dist/types/client/index.d.ts +2 -1
- package/dist/types/client/safety.d.ts +204 -4
- package/dist/types/client/signIn.d.ts +1 -1
- package/dist/types/contentClaims.d.ts +383 -0
- package/dist/types/deviceSession.d.ts +4 -4
- package/dist/types/email/messages.d.ts +4 -4
- package/dist/types/externalIdentity.d.ts +16 -16
- package/dist/types/index.d.ts +6 -2
- package/dist/types/inference/accountBilling.d.ts +2 -2
- package/dist/types/inference/aliaModelRelease.d.ts +8 -8
- package/dist/types/inference/catalogue.d.ts +26 -26
- package/dist/types/inference/inbox.d.ts +4 -4
- package/dist/types/inference/modelDocumentation.d.ts +16 -16
- package/dist/types/inference/providerConnection.d.ts +4 -4
- package/dist/types/inference/realtime.d.ts +74 -74
- package/dist/types/inference/request.d.ts +60 -60
- package/dist/types/inference/usage.d.ts +4 -4
- package/dist/types/products/access.d.ts +4 -4
- package/dist/types/safety.d.ts +2374 -2
- package/dist/types/serviceToken.d.ts +11 -0
- package/dist/types/sessionStatus.d.ts +4 -4
- package/dist/types/signIn.d.ts +5 -5
- package/dist/types/updates.d.ts +6 -6
- package/dist/types/userResponse.d.ts +8 -8
- package/package.json +5 -20
- package/dist/cjs/.tsbuildinfo +0 -1
- package/dist/esm/.tsbuildinfo +0 -1
- package/dist/types/.tsbuildinfo +0 -1
package/dist/cjs/safety.js
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.profileAccessDecisionSchema = exports.profileAccessInputSchema = void 0;
|
|
3
|
+
exports.supervisionInviteInputSchema = exports.safetyErrorSchema = exports.SAFETY_ERROR_CODES = exports.cancelPurchaseSchema = exports.confirmPurchaseSchema = exports.reservePurchaseSchema = exports.purchaseApprovalListSchema = exports.purchaseApprovalSummarySchema = exports.purchaseApprovalSchema = exports.purchaseEffectsSchema = exports.purchaseApprovalLineSchema = exports.supervisionContactsResponseSchema = exports.supervisionStateSchema = exports.supervisionContactSchema = exports.supervisionInvitationSchema = exports.supervisionSchema = exports.safetyRequesterSchema = exports.messagingSessionsResponseSchema = exports.messagingSessionsInputSchema = exports.messageAccessBatchResponseSchema = exports.deliveryAccessBatchInputSchema = exports.messageAccessBatchInputSchema = exports.deliveryAccessInputSchema = exports.contentAccessBatchResponseSchema = exports.contentAccessBatchInputSchema = exports.publicationPoliciesResponseSchema = exports.publicationPoliciesInputSchema = exports.publicationPolicySchema = exports.contentAccessInputSchema = exports.CONTENT_CLASSIFICATIONS = exports.contactAccessResponseSchema = exports.contactAccessInputSchema = exports.messageAccessInputSchema = exports.MESSAGE_ACCESS_MAX_INPUT_BYTES = exports.MESSAGE_ACCESS_MAX_PARTICIPANT_SLOTS = exports.MESSAGE_ACCESS_MAX_SUPERVISED_PARTICIPANTS = exports.MESSAGE_ACCESS_MAX_PARTICIPANTS = exports.MESSAGE_ACCESS_MAX_RESOURCES = exports.profileAccessBatchResponseSchema = exports.profileAccessBatchInputSchema = exports.safetyDecisionSchema = exports.SAFETY_DECISION_REASONS = exports.profileAccessDecisionSchema = exports.profileAccessInputSchema = exports.SUPERVISION_STATUSES = exports.SUPERVISION_INVITATION_STATUSES = exports.SUPERVISION_CONSENT_VERSION = exports.SUPERVISED_ACTIONS = exports.SAFETY_POLICY_VERSION = exports.PURCHASE_APPROVAL_STATUSES = void 0;
|
|
4
|
+
exports.safetyOrganizationsResponseSchema = exports.safetyOrganizationsInputSchema = exports.safetyOrganizationIdentitySchema = exports.safetyPeopleResponseSchema = exports.safetyPeopleInputSchema = exports.safetyPersonSchema = exports.safetySessionValidationSchema = exports.safetySessionInputSchema = exports.purchaseReviewInputSchema = exports.supervisionReviewInputSchema = exports.supervisionContactInputSchema = exports.supervisionAcceptInputSchema = void 0;
|
|
4
5
|
const zod_1 = require("zod");
|
|
6
|
+
const accountSubject_1 = require("./accountSubject");
|
|
7
|
+
// The Zod-free half of this contract lives in `./client/` (`@oxy.so/contracts/client`);
|
|
8
|
+
// its vocabularies are re-exported here so the root entry is unchanged.
|
|
9
|
+
const safety_1 = require("./client/safety");
|
|
10
|
+
Object.defineProperty(exports, "PURCHASE_APPROVAL_STATUSES", { enumerable: true, get: function () { return safety_1.PURCHASE_APPROVAL_STATUSES; } });
|
|
11
|
+
Object.defineProperty(exports, "SAFETY_POLICY_VERSION", { enumerable: true, get: function () { return safety_1.SAFETY_POLICY_VERSION; } });
|
|
12
|
+
Object.defineProperty(exports, "SUPERVISED_ACTIONS", { enumerable: true, get: function () { return safety_1.SUPERVISED_ACTIONS; } });
|
|
13
|
+
Object.defineProperty(exports, "SUPERVISION_CONSENT_VERSION", { enumerable: true, get: function () { return safety_1.SUPERVISION_CONSENT_VERSION; } });
|
|
14
|
+
Object.defineProperty(exports, "SUPERVISION_INVITATION_STATUSES", { enumerable: true, get: function () { return safety_1.SUPERVISION_INVITATION_STATUSES; } });
|
|
15
|
+
Object.defineProperty(exports, "SUPERVISION_STATUSES", { enumerable: true, get: function () { return safety_1.SUPERVISION_STATUSES; } });
|
|
16
|
+
const organizations_1 = require("./organizations");
|
|
17
|
+
const signIn_1 = require("./signIn");
|
|
18
|
+
const userResponse_1 = require("./userResponse");
|
|
5
19
|
/** The requester is authenticated by the bearer; applications cannot submit viewer IDs. */
|
|
6
20
|
exports.profileAccessInputSchema = zod_1.z
|
|
7
21
|
.object({ subjectAccountId: zod_1.z.string().min(1).max(128) })
|
|
@@ -13,3 +27,418 @@ exports.profileAccessDecisionSchema = zod_1.z
|
|
|
13
27
|
policyVersion: zod_1.z.literal('1'),
|
|
14
28
|
})
|
|
15
29
|
.strict();
|
|
30
|
+
exports.SAFETY_DECISION_REASONS = [
|
|
31
|
+
'allowed',
|
|
32
|
+
'owner',
|
|
33
|
+
'public',
|
|
34
|
+
'private',
|
|
35
|
+
'blocked',
|
|
36
|
+
'unavailable',
|
|
37
|
+
'unsupported_consumer',
|
|
38
|
+
'contact_not_approved',
|
|
39
|
+
'mature_content',
|
|
40
|
+
'unrated_content',
|
|
41
|
+
'approval_required',
|
|
42
|
+
];
|
|
43
|
+
const accountId = zod_1.z.string().min(1).max(128);
|
|
44
|
+
const resourceId = zod_1.z.string().trim().min(1).max(256);
|
|
45
|
+
const uniqueIds = (max, min = 1) => zod_1.z
|
|
46
|
+
.array(accountId)
|
|
47
|
+
.min(min)
|
|
48
|
+
.max(max)
|
|
49
|
+
.refine((ids) => new Set(ids).size === ids.length, 'Account IDs must be unique');
|
|
50
|
+
exports.safetyDecisionSchema = zod_1.z
|
|
51
|
+
.object({
|
|
52
|
+
allowed: zod_1.z.boolean(),
|
|
53
|
+
reason: zod_1.z.enum(exports.SAFETY_DECISION_REASONS),
|
|
54
|
+
policyVersion: zod_1.z.literal(safety_1.SAFETY_POLICY_VERSION),
|
|
55
|
+
})
|
|
56
|
+
.strict();
|
|
57
|
+
exports.profileAccessBatchInputSchema = zod_1.z
|
|
58
|
+
.object({ subjectAccountIds: uniqueIds(100) })
|
|
59
|
+
.strict();
|
|
60
|
+
exports.profileAccessBatchResponseSchema = zod_1.z
|
|
61
|
+
.object({
|
|
62
|
+
items: zod_1.z
|
|
63
|
+
.array(zod_1.z
|
|
64
|
+
.object({
|
|
65
|
+
subjectAccountId: accountId,
|
|
66
|
+
decision: exports.safetyDecisionSchema,
|
|
67
|
+
})
|
|
68
|
+
.strict())
|
|
69
|
+
.max(100),
|
|
70
|
+
})
|
|
71
|
+
.strict();
|
|
72
|
+
exports.MESSAGE_ACCESS_MAX_RESOURCES = 100;
|
|
73
|
+
exports.MESSAGE_ACCESS_MAX_PARTICIPANTS = 1024;
|
|
74
|
+
exports.MESSAGE_ACCESS_MAX_SUPERVISED_PARTICIPANTS = 256;
|
|
75
|
+
exports.MESSAGE_ACCESS_MAX_PARTICIPANT_SLOTS = 4096;
|
|
76
|
+
/** Leaves room for the original requester proof in a 512 KiB request. */
|
|
77
|
+
exports.MESSAGE_ACCESS_MAX_INPUT_BYTES = 448 * 1024;
|
|
78
|
+
exports.messageAccessInputSchema = zod_1.z
|
|
79
|
+
.object({ participantAccountIds: uniqueIds(exports.MESSAGE_ACCESS_MAX_PARTICIPANTS), resourceId })
|
|
80
|
+
.strict();
|
|
81
|
+
exports.contactAccessInputSchema = zod_1.z.object({ targetAccountIds: uniqueIds(1000) }).strict();
|
|
82
|
+
exports.contactAccessResponseSchema = zod_1.z
|
|
83
|
+
.object({
|
|
84
|
+
items: zod_1.z
|
|
85
|
+
.array(zod_1.z
|
|
86
|
+
.object({
|
|
87
|
+
targetAccountId: accountId,
|
|
88
|
+
decision: exports.safetyDecisionSchema,
|
|
89
|
+
})
|
|
90
|
+
.strict())
|
|
91
|
+
.max(1000),
|
|
92
|
+
})
|
|
93
|
+
.strict();
|
|
94
|
+
exports.CONTENT_CLASSIFICATIONS = ['general', 'mature', 'unrated'];
|
|
95
|
+
exports.contentAccessInputSchema = zod_1.z
|
|
96
|
+
.object({
|
|
97
|
+
resourceId,
|
|
98
|
+
classification: zod_1.z.enum(exports.CONTENT_CLASSIFICATIONS),
|
|
99
|
+
authorAccountId: accountId.optional(),
|
|
100
|
+
audience: zod_1.z.enum(['public', 'private', 'followers']).optional(),
|
|
101
|
+
applicationAudienceAllowed: zod_1.z.boolean().optional(),
|
|
102
|
+
applicationResourceGrant: zod_1.z.boolean().optional(),
|
|
103
|
+
operation: zod_1.z.enum(['read', 'manage_own']).optional(),
|
|
104
|
+
})
|
|
105
|
+
.strict()
|
|
106
|
+
.refine((item) => !item.audience ||
|
|
107
|
+
item.audience === 'public' ||
|
|
108
|
+
(!!item.authorAccountId && item.applicationAudienceAllowed !== undefined), 'Nonpublic content requires its authoritative author and application audience decision')
|
|
109
|
+
.refine((item) => item.operation !== 'manage_own' || !!item.authorAccountId, 'Own management requires its authoritative author');
|
|
110
|
+
exports.publicationPolicySchema = zod_1.z
|
|
111
|
+
.object({
|
|
112
|
+
publicationAllowed: zod_1.z.boolean(),
|
|
113
|
+
requiredAudience: zod_1.z.literal('private').nullable(),
|
|
114
|
+
collaborationAllowed: zod_1.z.boolean(),
|
|
115
|
+
federationAllowed: zod_1.z.boolean(),
|
|
116
|
+
policyVersion: zod_1.z.literal(safety_1.SAFETY_POLICY_VERSION),
|
|
117
|
+
})
|
|
118
|
+
.strict();
|
|
119
|
+
exports.publicationPoliciesInputSchema = zod_1.z
|
|
120
|
+
.object({
|
|
121
|
+
authorAccountIds: zod_1.z
|
|
122
|
+
.array(accountId)
|
|
123
|
+
.min(1)
|
|
124
|
+
.max(100)
|
|
125
|
+
.refine((ids) => new Set(ids).size === ids.length, 'Author IDs must be unique'),
|
|
126
|
+
})
|
|
127
|
+
.strict();
|
|
128
|
+
exports.publicationPoliciesResponseSchema = zod_1.z
|
|
129
|
+
.object({
|
|
130
|
+
items: zod_1.z
|
|
131
|
+
.array(zod_1.z.object({ authorAccountId: accountId, policy: exports.publicationPolicySchema }).strict())
|
|
132
|
+
.max(100),
|
|
133
|
+
})
|
|
134
|
+
.strict();
|
|
135
|
+
exports.contentAccessBatchInputSchema = zod_1.z
|
|
136
|
+
.object({
|
|
137
|
+
items: zod_1.z
|
|
138
|
+
.array(exports.contentAccessInputSchema)
|
|
139
|
+
.min(1)
|
|
140
|
+
.max(100)
|
|
141
|
+
.refine((items) => new Set(items.map((item) => item.resourceId)).size === items.length, 'Resource IDs must be unique'),
|
|
142
|
+
})
|
|
143
|
+
.strict();
|
|
144
|
+
exports.contentAccessBatchResponseSchema = zod_1.z
|
|
145
|
+
.object({
|
|
146
|
+
items: zod_1.z
|
|
147
|
+
.array(zod_1.z
|
|
148
|
+
.object({
|
|
149
|
+
resourceId,
|
|
150
|
+
decision: exports.safetyDecisionSchema,
|
|
151
|
+
})
|
|
152
|
+
.strict())
|
|
153
|
+
.max(100),
|
|
154
|
+
})
|
|
155
|
+
.strict();
|
|
156
|
+
/** A reviewed background consumer may decide delivery, but never authenticate as the sender. */
|
|
157
|
+
exports.deliveryAccessInputSchema = exports.messageAccessInputSchema
|
|
158
|
+
.extend({ senderAccountId: accountId })
|
|
159
|
+
.strict();
|
|
160
|
+
/** Count repeated participants across resources too: this bounds both graph and pair evaluations. */
|
|
161
|
+
const messageBatch = (schema) => zod_1.z
|
|
162
|
+
.object({ items: zod_1.z.array(schema).min(1).max(exports.MESSAGE_ACCESS_MAX_RESOURCES) })
|
|
163
|
+
.strict()
|
|
164
|
+
.refine((value) => new Set(value.items.map((item) => item.resourceId)).size === value.items.length, 'Resource IDs must be unique')
|
|
165
|
+
.refine((value) => value.items.reduce((total, item) => total + item.participantAccountIds.length, 0) <=
|
|
166
|
+
exports.MESSAGE_ACCESS_MAX_PARTICIPANT_SLOTS, 'Participant budget exceeded')
|
|
167
|
+
.refine((value) => new TextEncoder().encode(JSON.stringify(value)).byteLength <=
|
|
168
|
+
exports.MESSAGE_ACCESS_MAX_INPUT_BYTES, 'Message batch byte budget exceeded');
|
|
169
|
+
exports.messageAccessBatchInputSchema = messageBatch(exports.messageAccessInputSchema);
|
|
170
|
+
exports.deliveryAccessBatchInputSchema = messageBatch(exports.deliveryAccessInputSchema);
|
|
171
|
+
exports.messageAccessBatchResponseSchema = exports.contentAccessBatchResponseSchema;
|
|
172
|
+
/** Live socket-recipient proof; a request ID correlates the result and never identifies an account. */
|
|
173
|
+
exports.messagingSessionsInputSchema = zod_1.z
|
|
174
|
+
.object({
|
|
175
|
+
items: zod_1.z
|
|
176
|
+
.array(zod_1.z
|
|
177
|
+
.object({
|
|
178
|
+
requestId: resourceId,
|
|
179
|
+
accessToken: zod_1.z.string().min(1).max(16384),
|
|
180
|
+
})
|
|
181
|
+
.strict())
|
|
182
|
+
.min(1)
|
|
183
|
+
.max(100),
|
|
184
|
+
})
|
|
185
|
+
.strict()
|
|
186
|
+
.refine((value) => new Set(value.items.map((item) => item.requestId)).size === value.items.length, 'Request IDs must be unique')
|
|
187
|
+
.refine((value) => new TextEncoder().encode(JSON.stringify(value)).byteLength <= exports.MESSAGE_ACCESS_MAX_INPUT_BYTES, 'Session proof byte budget exceeded');
|
|
188
|
+
exports.messagingSessionsResponseSchema = zod_1.z
|
|
189
|
+
.object({
|
|
190
|
+
items: zod_1.z
|
|
191
|
+
.array(zod_1.z.discriminatedUnion('allowed', [
|
|
192
|
+
zod_1.z.object({ requestId: resourceId, allowed: zod_1.z.literal(true), accountId }).strict(),
|
|
193
|
+
zod_1.z.object({ requestId: resourceId, allowed: zod_1.z.literal(false) }).strict(),
|
|
194
|
+
]))
|
|
195
|
+
.max(100),
|
|
196
|
+
})
|
|
197
|
+
.strict();
|
|
198
|
+
exports.safetyRequesterSchema = zod_1.z.union([
|
|
199
|
+
zod_1.z.object({ accessToken: zod_1.z.string().min(1).max(16384).nullable() }).strict(),
|
|
200
|
+
zod_1.z.object({ connectionToken: zod_1.z.string().min(1).max(16384) }).strict(),
|
|
201
|
+
]);
|
|
202
|
+
exports.supervisionSchema = zod_1.z
|
|
203
|
+
.object({
|
|
204
|
+
childAccountId: accountId,
|
|
205
|
+
guardianAccountId: accountId.nullable(),
|
|
206
|
+
status: zod_1.z.enum(safety_1.SUPERVISION_STATUSES),
|
|
207
|
+
policyVersion: zod_1.z.literal(safety_1.SAFETY_POLICY_VERSION),
|
|
208
|
+
consentVersion: zod_1.z.literal(safety_1.SUPERVISION_CONSENT_VERSION),
|
|
209
|
+
createdAt: zod_1.z.string().datetime(),
|
|
210
|
+
})
|
|
211
|
+
.strict();
|
|
212
|
+
exports.supervisionInvitationSchema = zod_1.z
|
|
213
|
+
.object({
|
|
214
|
+
id: accountId,
|
|
215
|
+
childAccountId: accountId,
|
|
216
|
+
guardianAccountId: accountId,
|
|
217
|
+
status: zod_1.z.enum(safety_1.SUPERVISION_INVITATION_STATUSES),
|
|
218
|
+
expiresAt: zod_1.z.string().datetime(),
|
|
219
|
+
consentVersion: zod_1.z.literal(safety_1.SUPERVISION_CONSENT_VERSION),
|
|
220
|
+
createdAt: zod_1.z.string().datetime(),
|
|
221
|
+
})
|
|
222
|
+
.strict();
|
|
223
|
+
exports.supervisionContactSchema = zod_1.z
|
|
224
|
+
.object({
|
|
225
|
+
childAccountId: accountId,
|
|
226
|
+
contactAccountId: accountId,
|
|
227
|
+
approvedAt: zod_1.z.string().datetime(),
|
|
228
|
+
})
|
|
229
|
+
.strict();
|
|
230
|
+
exports.supervisionStateSchema = zod_1.z
|
|
231
|
+
.object({
|
|
232
|
+
self: exports.supervisionSchema.nullable(),
|
|
233
|
+
children: zod_1.z.array(exports.supervisionSchema).max(100),
|
|
234
|
+
invitations: zod_1.z.array(exports.supervisionInvitationSchema).max(100),
|
|
235
|
+
})
|
|
236
|
+
.strict();
|
|
237
|
+
exports.supervisionContactsResponseSchema = zod_1.z
|
|
238
|
+
.object({ items: zod_1.z.array(exports.supervisionContactSchema).max(1000) })
|
|
239
|
+
.strict();
|
|
240
|
+
const fingerprint = zod_1.z.string().regex(/^[a-f0-9]{64}$/);
|
|
241
|
+
const minorUnits = zod_1.z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
242
|
+
exports.purchaseApprovalLineSchema = zod_1.z
|
|
243
|
+
.object({
|
|
244
|
+
itemReference: resourceId,
|
|
245
|
+
title: zod_1.z.string().trim().min(1).max(300),
|
|
246
|
+
optionsText: zod_1.z.string().trim().max(300),
|
|
247
|
+
merchantAccountId: accountId,
|
|
248
|
+
quantity: zod_1.z.number().int().min(1).max(1000000),
|
|
249
|
+
unitAmountMinor: minorUnits,
|
|
250
|
+
lineAmountMinor: minorUnits,
|
|
251
|
+
})
|
|
252
|
+
.strict();
|
|
253
|
+
const purchaseEffectsObject = zod_1.z
|
|
254
|
+
.object({
|
|
255
|
+
operationId: resourceId,
|
|
256
|
+
resourceId,
|
|
257
|
+
cartFingerprint: fingerprint,
|
|
258
|
+
summary: zod_1.z.string().trim().min(1).max(300),
|
|
259
|
+
merchantAccountIds: zod_1.z
|
|
260
|
+
.array(accountId)
|
|
261
|
+
.min(1)
|
|
262
|
+
.max(100)
|
|
263
|
+
.refine((ids) => new Set(ids).size === ids.length, 'Merchant accounts must be unique')
|
|
264
|
+
.transform((ids) => [...ids].sort()),
|
|
265
|
+
lineItems: zod_1.z.array(exports.purchaseApprovalLineSchema).min(1).max(200),
|
|
266
|
+
shippingMinor: minorUnits,
|
|
267
|
+
taxMinor: minorUnits,
|
|
268
|
+
discountMinor: minorUnits,
|
|
269
|
+
roundingAdjustmentMinor: zod_1.z
|
|
270
|
+
.number()
|
|
271
|
+
.int()
|
|
272
|
+
.min(-Number.MAX_SAFE_INTEGER)
|
|
273
|
+
.max(Number.MAX_SAFE_INTEGER),
|
|
274
|
+
paymentFingerprint: fingerprint,
|
|
275
|
+
amountMinor: minorUnits,
|
|
276
|
+
currency: zod_1.z.string().regex(/^[A-Z]{3}$/),
|
|
277
|
+
expiresAt: zod_1.z.string().datetime(),
|
|
278
|
+
})
|
|
279
|
+
.strict();
|
|
280
|
+
function validatePurchaseEvidence(value, ctx) {
|
|
281
|
+
const merchants = new Set(value.merchantAccountIds);
|
|
282
|
+
const references = new Set();
|
|
283
|
+
for (const line of value.lineItems) {
|
|
284
|
+
const key = `${line.merchantAccountId}:${line.itemReference}`;
|
|
285
|
+
if (!merchants.has(line.merchantAccountId) || references.has(key))
|
|
286
|
+
ctx.addIssue({
|
|
287
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
288
|
+
message: 'Purchase lines must name an included merchant and unique item reference',
|
|
289
|
+
});
|
|
290
|
+
references.add(key);
|
|
291
|
+
}
|
|
292
|
+
if (merchants.size !== new Set(value.lineItems.map((line) => line.merchantAccountId)).size)
|
|
293
|
+
ctx.addIssue({
|
|
294
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
295
|
+
message: 'Every merchant must have an approval line',
|
|
296
|
+
});
|
|
297
|
+
const total = value.lineItems.reduce((sum, line) => sum + BigInt(line.lineAmountMinor), 0n) +
|
|
298
|
+
BigInt(value.shippingMinor) +
|
|
299
|
+
BigInt(value.taxMinor) -
|
|
300
|
+
BigInt(value.discountMinor) +
|
|
301
|
+
BigInt(value.roundingAdjustmentMinor);
|
|
302
|
+
if (total < 0n || total > BigInt(Number.MAX_SAFE_INTEGER) || total !== BigInt(value.amountMinor))
|
|
303
|
+
ctx.addIssue({
|
|
304
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
305
|
+
message: 'Approval evidence must equal the exact payment total',
|
|
306
|
+
});
|
|
307
|
+
if (Math.abs(value.roundingAdjustmentMinor) >
|
|
308
|
+
value.lineItems.length + 3 * value.merchantAccountIds.length)
|
|
309
|
+
ctx.addIssue({
|
|
310
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
311
|
+
message: 'Rounding adjustment exceeds the bounded conversion stages',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
exports.purchaseEffectsSchema = purchaseEffectsObject.superRefine(validatePurchaseEvidence);
|
|
315
|
+
const purchaseApprovalFields = {
|
|
316
|
+
id: accountId,
|
|
317
|
+
childAccountId: accountId,
|
|
318
|
+
applicationId: accountId,
|
|
319
|
+
status: zod_1.z.enum(safety_1.PURCHASE_APPROVAL_STATUSES),
|
|
320
|
+
createdAt: zod_1.z.string().datetime(),
|
|
321
|
+
receiptReference: zod_1.z.string().max(512).nullable(),
|
|
322
|
+
};
|
|
323
|
+
exports.purchaseApprovalSchema = purchaseEffectsObject
|
|
324
|
+
.extend(purchaseApprovalFields)
|
|
325
|
+
.strict()
|
|
326
|
+
.superRefine(validatePurchaseEvidence);
|
|
327
|
+
exports.purchaseApprovalSummarySchema = purchaseEffectsObject
|
|
328
|
+
.omit({ lineItems: true })
|
|
329
|
+
.extend(purchaseApprovalFields)
|
|
330
|
+
.strict();
|
|
331
|
+
exports.purchaseApprovalListSchema = zod_1.z
|
|
332
|
+
.object({
|
|
333
|
+
items: zod_1.z.array(exports.purchaseApprovalSummarySchema).max(100),
|
|
334
|
+
nextCursor: zod_1.z.string().nullable(),
|
|
335
|
+
})
|
|
336
|
+
.strict();
|
|
337
|
+
exports.reservePurchaseSchema = purchaseEffectsObject
|
|
338
|
+
.extend({ approvalId: accountId })
|
|
339
|
+
.strict()
|
|
340
|
+
.superRefine(validatePurchaseEvidence);
|
|
341
|
+
exports.confirmPurchaseSchema = zod_1.z
|
|
342
|
+
.object({
|
|
343
|
+
approvalId: accountId,
|
|
344
|
+
operationId: resourceId,
|
|
345
|
+
receiptReference: zod_1.z.string().trim().min(1).max(512),
|
|
346
|
+
})
|
|
347
|
+
.strict();
|
|
348
|
+
exports.cancelPurchaseSchema = exports.confirmPurchaseSchema.omit({ receiptReference: true });
|
|
349
|
+
exports.SAFETY_ERROR_CODES = [
|
|
350
|
+
'bad_request',
|
|
351
|
+
'validation_failed',
|
|
352
|
+
'unauthorized',
|
|
353
|
+
'forbidden',
|
|
354
|
+
'not_found',
|
|
355
|
+
'conflict',
|
|
356
|
+
'reauth_required',
|
|
357
|
+
'limit_reached',
|
|
358
|
+
'service_unavailable',
|
|
359
|
+
'rate_limited',
|
|
360
|
+
];
|
|
361
|
+
exports.safetyErrorSchema = zod_1.z
|
|
362
|
+
.object({ error: zod_1.z.object({ code: zod_1.z.enum(exports.SAFETY_ERROR_CODES), message: zod_1.z.string() }).strict() })
|
|
363
|
+
.strict();
|
|
364
|
+
exports.supervisionInviteInputSchema = zod_1.z
|
|
365
|
+
.object({
|
|
366
|
+
guardianAccountId: accountId,
|
|
367
|
+
consentVersion: zod_1.z.literal(safety_1.SUPERVISION_CONSENT_VERSION),
|
|
368
|
+
reauth: signIn_1.reauthProofSchema,
|
|
369
|
+
})
|
|
370
|
+
.strict();
|
|
371
|
+
exports.supervisionAcceptInputSchema = zod_1.z
|
|
372
|
+
.object({ consentVersion: zod_1.z.literal(safety_1.SUPERVISION_CONSENT_VERSION), reauth: signIn_1.reauthProofSchema })
|
|
373
|
+
.strict();
|
|
374
|
+
exports.supervisionContactInputSchema = zod_1.z
|
|
375
|
+
.object({ childAccountId: accountId, contactAccountId: accountId, reauth: signIn_1.reauthProofSchema })
|
|
376
|
+
.strict();
|
|
377
|
+
exports.supervisionReviewInputSchema = zod_1.z
|
|
378
|
+
.object({
|
|
379
|
+
childAccountId: accountId,
|
|
380
|
+
action: zod_1.z.enum(['end', 'transfer']),
|
|
381
|
+
guardianAccountId: accountId.optional(),
|
|
382
|
+
evidenceReference: zod_1.z.string().trim().min(1).max(2048),
|
|
383
|
+
reauth: signIn_1.reauthProofSchema,
|
|
384
|
+
})
|
|
385
|
+
.strict();
|
|
386
|
+
exports.purchaseReviewInputSchema = zod_1.z
|
|
387
|
+
.object({ decision: zod_1.z.enum(['approved', 'rejected']), reauth: signIn_1.reauthProofSchema })
|
|
388
|
+
.strict();
|
|
389
|
+
exports.safetySessionInputSchema = zod_1.z
|
|
390
|
+
.object({
|
|
391
|
+
sessionId: accountId,
|
|
392
|
+
accessToken: zod_1.z.string().min(1).max(16384),
|
|
393
|
+
action: zod_1.z.enum(safety_1.SUPERVISED_ACTIONS),
|
|
394
|
+
})
|
|
395
|
+
.strict();
|
|
396
|
+
exports.safetySessionValidationSchema = zod_1.z
|
|
397
|
+
.object({
|
|
398
|
+
valid: zod_1.z.literal(true),
|
|
399
|
+
sessionId: accountId,
|
|
400
|
+
expiresAt: zod_1.z.string().datetime(),
|
|
401
|
+
lastActivity: zod_1.z.string().datetime(),
|
|
402
|
+
user: userResponse_1.userResponseSchema,
|
|
403
|
+
actor: accountSubject_1.accountActorChainSchema,
|
|
404
|
+
})
|
|
405
|
+
.strict();
|
|
406
|
+
/** Minimal identity for the authenticated account's supervision management, never profile details. */
|
|
407
|
+
exports.safetyPersonSchema = zod_1.z
|
|
408
|
+
.object({ accountId, username: zod_1.z.string().nullable(), displayName: zod_1.z.string().nullable() })
|
|
409
|
+
.strict();
|
|
410
|
+
exports.safetyPeopleInputSchema = zod_1.z.union([
|
|
411
|
+
zod_1.z.object({ query: zod_1.z.string().trim().min(2).max(100) }).strict(),
|
|
412
|
+
zod_1.z.object({ accountIds: uniqueIds(100) }).strict(),
|
|
413
|
+
]);
|
|
414
|
+
exports.safetyPeopleResponseSchema = zod_1.z
|
|
415
|
+
.object({ items: zod_1.z.array(exports.safetyPersonSchema).max(100) })
|
|
416
|
+
.strict();
|
|
417
|
+
/** Existing public employer identity only; never directory content or management authority. */
|
|
418
|
+
exports.safetyOrganizationIdentitySchema = zod_1.z
|
|
419
|
+
.object({
|
|
420
|
+
accountId,
|
|
421
|
+
username: zod_1.z.string().nullable(),
|
|
422
|
+
name: zod_1.z.string(),
|
|
423
|
+
organizationType: zod_1.z.enum(organizations_1.ORGANIZATION_TYPES),
|
|
424
|
+
})
|
|
425
|
+
.strict();
|
|
426
|
+
exports.safetyOrganizationsInputSchema = zod_1.z.union([
|
|
427
|
+
zod_1.z
|
|
428
|
+
.object({
|
|
429
|
+
query: zod_1.z.string().trim().min(1).max(100),
|
|
430
|
+
limit: zod_1.z.number().int().min(1).max(20).optional(),
|
|
431
|
+
})
|
|
432
|
+
.strict(),
|
|
433
|
+
zod_1.z.object({ ids: uniqueIds(100) }).strict(),
|
|
434
|
+
]);
|
|
435
|
+
exports.safetyOrganizationsResponseSchema = zod_1.z
|
|
436
|
+
.object({
|
|
437
|
+
items: zod_1.z
|
|
438
|
+
.array(zod_1.z
|
|
439
|
+
.object({ requestedAccountId: accountId, organization: exports.safetyOrganizationIdentitySchema })
|
|
440
|
+
.strict())
|
|
441
|
+
.max(100),
|
|
442
|
+
missingIds: zod_1.z.array(accountId).max(100),
|
|
443
|
+
})
|
|
444
|
+
.strict();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serviceTokenOptionsSchema = exports.serviceTokenScopesSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/** Omitted means the existing grant; an explicit set only narrows that authority. */
|
|
6
|
+
exports.serviceTokenScopesSchema = zod_1.z
|
|
7
|
+
.array(zod_1.z.string().trim().min(1).max(128))
|
|
8
|
+
.min(1)
|
|
9
|
+
.max(100)
|
|
10
|
+
.refine((scopes) => new Set(scopes).size === scopes.length, 'Requested scopes must be unique')
|
|
11
|
+
.transform((scopes) => [...scopes].sort());
|
|
12
|
+
exports.serviceTokenOptionsSchema = zod_1.z
|
|
13
|
+
.object({ scopes: exports.serviceTokenScopesSchema.optional() })
|
|
14
|
+
.strict();
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contract for `GET /internal/accounts/:accountId/access/:userId`: Oxy
|
|
3
|
+
* answers a trusted first-party service's question "may this person act FOR
|
|
4
|
+
* that account, and with what?" with no session of that person in the request.
|
|
5
|
+
*
|
|
6
|
+
* It exists for the account Oxy never lets anybody act AS: a `channel`
|
|
7
|
+
* ({@link isDelegatedActAsEligibleKind} refuses it). An app publishes to a
|
|
8
|
+
* channel with ITS OWN credential, on behalf of a member Oxy vouches for here,
|
|
9
|
+
* so nobody holds a bearer that speaks as the channel. A long-running import
|
|
10
|
+
* (Oxy Move) has only a service token and `X-Oxy-User-Id`, which the
|
|
11
|
+
* user-facing `GET /accounts/:id/members` refuses; this is its answer.
|
|
12
|
+
*
|
|
13
|
+
* Needs a service token whose application is platform-trusted AND holds the
|
|
14
|
+
* privileged {@link ACCOUNTS_MEMBERSHIP_READ_SCOPE}. The answer is always 200:
|
|
15
|
+
* `access` is `null` for no active membership, an archived account, an unknown
|
|
16
|
+
* account and an unclaimed directory account alike. Callers must treat every
|
|
17
|
+
* other status as a refusal (fail closed), and must not cache the answer longer
|
|
18
|
+
* than the action it authorizes.
|
|
19
|
+
*
|
|
20
|
+
* Platform-agnostic — zod only.
|
|
21
|
+
*/
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
import { accountKindSchema } from './accountGraph.js';
|
|
24
|
+
/**
|
|
25
|
+
* The scope the endpoint requires.
|
|
26
|
+
*
|
|
27
|
+
* Privileged (staff-granted). Channel membership is sensitive because a channel
|
|
28
|
+
* is anonymous: its members are the people behind a voice that is published
|
|
29
|
+
* without their names, and "is this person a member of that channel" is
|
|
30
|
+
* exactly the fact the channel exists to withhold.
|
|
31
|
+
*/
|
|
32
|
+
export const ACCOUNTS_MEMBERSHIP_READ_SCOPE = 'accounts:membership:read';
|
|
33
|
+
/**
|
|
34
|
+
* Where the access comes from.
|
|
35
|
+
*
|
|
36
|
+
* - `self` — the person IS the account (a personal account asked about itself).
|
|
37
|
+
* - `direct` — an active membership row on the account.
|
|
38
|
+
* - `inherited` — an active, cascading membership on an ancestor account.
|
|
39
|
+
*/
|
|
40
|
+
export const ACCOUNT_ACCESS_SOURCES = ['self', 'direct', 'inherited'];
|
|
41
|
+
export const accountAccessSourceSchema = z.enum(ACCOUNT_ACCESS_SOURCES);
|
|
42
|
+
/**
|
|
43
|
+
* One person's EFFECTIVE access over one account.
|
|
44
|
+
*
|
|
45
|
+
* `role` and `permissions` are strings rather than closed enums on purpose: the
|
|
46
|
+
* role and permission vocabularies are the API's, they grow, and a consumer
|
|
47
|
+
* pinned to an older contracts release must keep reading new ones. Gate on
|
|
48
|
+
* `permissions` (the role's baseline adjusted by per-member grants and
|
|
49
|
+
* revokes), never on `role`.
|
|
50
|
+
*/
|
|
51
|
+
export const accountAccessGrantSchema = z.object({
|
|
52
|
+
role: z.string().min(1),
|
|
53
|
+
permissions: z.array(z.string().min(1)),
|
|
54
|
+
/** Only an active membership grants access, so this is the one value. */
|
|
55
|
+
status: z.literal('active'),
|
|
56
|
+
source: accountAccessSourceSchema,
|
|
57
|
+
});
|
|
58
|
+
export const accountAccessVerdictSchema = z
|
|
59
|
+
.object({
|
|
60
|
+
accountId: z.string().min(1),
|
|
61
|
+
/** `null` when no such account exists. */
|
|
62
|
+
kind: accountKindSchema.nullable(),
|
|
63
|
+
/** `null` means: this person may not act for this account. */
|
|
64
|
+
access: accountAccessGrantSchema.nullable(),
|
|
65
|
+
})
|
|
66
|
+
.refine((verdict) => verdict.access === null || verdict.kind !== null, {
|
|
67
|
+
message: 'An account that does not exist grants no access',
|
|
68
|
+
path: ['access'],
|
|
69
|
+
});
|
package/dist/esm/agency.js
CHANGED
|
@@ -140,6 +140,35 @@ export const delegationGrantSchema = z
|
|
|
140
140
|
updatedAt: z.string().datetime(),
|
|
141
141
|
})
|
|
142
142
|
.strict();
|
|
143
|
+
/**
|
|
144
|
+
* The two levels an owner publishes for whoever uses their agent (ADR 0042):
|
|
145
|
+
* "Ver" and "Ver y actuar". A ceiling never says `draft` or `execute_on_request`:
|
|
146
|
+
* an agent is either allowed to act on its own or not at all, and nothing ever
|
|
147
|
+
* asks the person for approval.
|
|
148
|
+
*/
|
|
149
|
+
export const AGENT_REQUESTER_CEILING_AUTONOMY = ['read_only', 'autonomous'];
|
|
150
|
+
/** What an agent may do in one app for whoever uses it; written by an operator of the bot. */
|
|
151
|
+
export const agentRequesterCeilingWriteSchema = z
|
|
152
|
+
.object({
|
|
153
|
+
capabilityPackages: z.array(capabilityPackageSchema),
|
|
154
|
+
capabilities: z.array(identifierSchema).default([]),
|
|
155
|
+
toolOverrides: z.array(toolGrantOverrideSchema).default([]),
|
|
156
|
+
limits: z.array(grantLimitSchema).default([]),
|
|
157
|
+
maximumAutonomy: z.enum(AGENT_REQUESTER_CEILING_AUTONOMY),
|
|
158
|
+
})
|
|
159
|
+
.strict();
|
|
160
|
+
export const agentRequesterCeilingSchema = agentRequesterCeilingWriteSchema
|
|
161
|
+
.extend({
|
|
162
|
+
id: identifierSchema,
|
|
163
|
+
agentAccountId: identifierSchema,
|
|
164
|
+
appId: identifierSchema,
|
|
165
|
+
catalog: capabilityCatalogBindingSchema,
|
|
166
|
+
version: z.number().int().positive(),
|
|
167
|
+
revokedAt: z.string().datetime().nullable(),
|
|
168
|
+
createdAt: z.string().datetime(),
|
|
169
|
+
updatedAt: z.string().datetime(),
|
|
170
|
+
})
|
|
171
|
+
.strict();
|
|
143
172
|
export const automationTriggerSchema = z.discriminatedUnion('type', [
|
|
144
173
|
z.object({ type: z.literal('manual') }).strict(),
|
|
145
174
|
z
|
|
@@ -201,6 +230,8 @@ export const capabilityTicketClaimsSchema = z
|
|
|
201
230
|
/** Signed only for callers that explicitly requested a pinned catalogue. */
|
|
202
231
|
catalog: capabilityCatalogBindingSchema.optional(),
|
|
203
232
|
grantId: identifierSchema.optional(),
|
|
233
|
+
/** The owner's published ceiling that bounds an agent acting for a non-owner (ADR 0042). */
|
|
234
|
+
ceilingId: identifierSchema.optional(),
|
|
204
235
|
requesterAccountId: identifierSchema,
|
|
205
236
|
ownerAccountId: identifierSchema,
|
|
206
237
|
actor: executionActorRefSchema,
|
|
@@ -239,6 +270,24 @@ export const capabilityTicketClaimsSchema = z
|
|
|
239
270
|
message: 'requester actors require their exact principal and direct read-only authority without standing grants',
|
|
240
271
|
});
|
|
241
272
|
}
|
|
273
|
+
if (claims.ceilingId !== undefined) {
|
|
274
|
+
if (claims.grantId !== undefined) {
|
|
275
|
+
context.addIssue({
|
|
276
|
+
code: z.ZodIssueCode.custom,
|
|
277
|
+
path: ['ceilingId'],
|
|
278
|
+
message: 'a ticket is bounded by either a grant or a ceiling, never both',
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (claims.actor.type !== 'agent' ||
|
|
282
|
+
claims.ownerAccountId !== claims.requesterAccountId ||
|
|
283
|
+
claims.resource.resourceId !== claims.resource.effectiveAccountId) {
|
|
284
|
+
context.addIssue({
|
|
285
|
+
code: z.ZodIssueCode.custom,
|
|
286
|
+
path: ['ceilingId'],
|
|
287
|
+
message: "a ceiling bounds an agent acting on the requester's own authority at an account root",
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
242
291
|
if (claims.exp <= claims.iat) {
|
|
243
292
|
context.addIssue({
|
|
244
293
|
code: z.ZodIssueCode.custom,
|
|
@@ -300,6 +349,7 @@ export const policyDecisionSchema = z
|
|
|
300
349
|
reason: identifierSchema,
|
|
301
350
|
effectiveAutonomy: autonomyLevelSchema.optional(),
|
|
302
351
|
grantId: identifierSchema.optional(),
|
|
352
|
+
ceilingId: identifierSchema.optional(),
|
|
303
353
|
})
|
|
304
354
|
.strict();
|
|
305
355
|
export const auditResultSchema = z
|
|
@@ -509,6 +559,24 @@ export const catalogEventSchema = z
|
|
|
509
559
|
resourceTypes: z.array(identifierSchema).min(1),
|
|
510
560
|
})
|
|
511
561
|
.strict();
|
|
562
|
+
/**
|
|
563
|
+
* A feature an agent may gate on (Alia ADR 0020 §6): the app answers whether the
|
|
564
|
+
* person may use it (`checkTool`) and, when using it costs something, spends it
|
|
565
|
+
* (`consumeTool`). Both are internal tools of the same catalogue.
|
|
566
|
+
*/
|
|
567
|
+
export const catalogFeatureSchema = z
|
|
568
|
+
.object({
|
|
569
|
+
key: z
|
|
570
|
+
.string()
|
|
571
|
+
.regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)*$/, 'feature keys are dot-separated slugs')
|
|
572
|
+
.max(128),
|
|
573
|
+
description: nonEmptyStringSchema,
|
|
574
|
+
checkTool: identifierSchema,
|
|
575
|
+
consumeTool: identifierSchema.optional(),
|
|
576
|
+
})
|
|
577
|
+
.strict();
|
|
578
|
+
/** How a coordinator reaches the catalogue's internal tools. */
|
|
579
|
+
export const CATALOG_INTERNAL_TRANSPORTS = ['http', 'mcp'];
|
|
512
580
|
export const appCapabilityCatalogSchema = z
|
|
513
581
|
.object({
|
|
514
582
|
schemaVersion: z.literal('1'),
|
|
@@ -564,11 +632,54 @@ export const appCapabilityCatalogSchema = z
|
|
|
564
632
|
})
|
|
565
633
|
.strict()
|
|
566
634
|
.optional(),
|
|
635
|
+
/**
|
|
636
|
+
* `mcp`: internal tools are served at `<internalBaseUrl>/_oxy/mcp`. Absent or
|
|
637
|
+
* `http`: each tool's `invocation` path on `internalBaseUrl`.
|
|
638
|
+
*/
|
|
639
|
+
internalTransport: z.enum(CATALOG_INTERNAL_TRANSPORTS).optional(),
|
|
640
|
+
features: z.array(catalogFeatureSchema).optional(),
|
|
567
641
|
tools: z.array(catalogToolSchema),
|
|
568
642
|
events: z.array(catalogEventSchema),
|
|
569
643
|
})
|
|
570
644
|
.strict()
|
|
571
645
|
.superRefine((catalog, context) => {
|
|
646
|
+
const toolsByName = new Map(catalog.tools.map((tool) => [tool.name, tool]));
|
|
647
|
+
const featureKeys = new Set();
|
|
648
|
+
for (const [index, feature] of (catalog.features ?? []).entries()) {
|
|
649
|
+
if (featureKeys.has(feature.key)) {
|
|
650
|
+
context.addIssue({
|
|
651
|
+
code: z.ZodIssueCode.custom,
|
|
652
|
+
message: `Duplicate feature key: ${feature.key}`,
|
|
653
|
+
path: ['features', index, 'key'],
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
featureKeys.add(feature.key);
|
|
657
|
+
const check = toolsByName.get(feature.checkTool);
|
|
658
|
+
if (!check ||
|
|
659
|
+
check.effect !== 'read' ||
|
|
660
|
+
check.exposure.length !== 1 ||
|
|
661
|
+
check.exposure[0] !== 'internal') {
|
|
662
|
+
context.addIssue({
|
|
663
|
+
code: z.ZodIssueCode.custom,
|
|
664
|
+
message: 'checkTool must name an internal-only read tool of this catalogue',
|
|
665
|
+
path: ['features', index, 'checkTool'],
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
if (feature.consumeTool !== undefined) {
|
|
669
|
+
const consume = toolsByName.get(feature.consumeTool);
|
|
670
|
+
if (!consume ||
|
|
671
|
+
consume.effect === 'read' ||
|
|
672
|
+
consume.idempotency !== 'required' ||
|
|
673
|
+
consume.exposure.length !== 1 ||
|
|
674
|
+
consume.exposure[0] !== 'internal') {
|
|
675
|
+
context.addIssue({
|
|
676
|
+
code: z.ZodIssueCode.custom,
|
|
677
|
+
message: 'consumeTool must name an internal-only effectful tool that requires idempotency keys',
|
|
678
|
+
path: ['features', index, 'consumeTool'],
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
572
683
|
const names = new Set();
|
|
573
684
|
for (const [index, tool] of catalog.tools.entries()) {
|
|
574
685
|
if (names.has(tool.name)) {
|