@theelena/sdk 0.0.0-stage → 0.1.1

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/agent.js ADDED
@@ -0,0 +1,1786 @@
1
+ // src/agent/index.ts
2
+ import { createHash } from "crypto";
3
+
4
+ // ../shared/src/agent-contracts/common.ts
5
+ import { z } from "zod";
6
+ var ELENA_AGENT_CONTRACT_VERSION = "1";
7
+ var elenaAgentVersionSchema = z.literal(ELENA_AGENT_CONTRACT_VERSION);
8
+ var elenaAgentIdSchema = z.uuid();
9
+ var elenaAgentRevisionSchema = z.number().int().min(1).max(Number.MAX_SAFE_INTEGER);
10
+ var elenaAgentTimestampSchema = z.iso.datetime();
11
+ var elenaAgentHashSchema = z.string().regex(/^[a-f0-9]{64}$/);
12
+ var elenaAgentRefSchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,99}$/);
13
+ var elenaAgentNameSchema = z.string().trim().min(1).max(160);
14
+ var elenaAgentTextSchema = z.string().trim().min(1).max(2e3);
15
+ var elenaAgentCurrencySchema = z.literal("PEN");
16
+ var elenaAgentNonnegativeMoneySchema = z.string().regex(/^(0|[1-9]\d{0,7})\.\d{2}$/);
17
+ var elenaAgentMoneySchema = elenaAgentNonnegativeMoneySchema.refine(
18
+ (value) => value !== "0.00",
19
+ "Product price must be greater than zero"
20
+ );
21
+ var ELENA_AGENT_LIMITS = Object.freeze({
22
+ categoriesPerImport: 50,
23
+ productsPerImport: 200,
24
+ changesPerOperation: 200,
25
+ publicationProducts: 200,
26
+ galleryImages: 8,
27
+ extraGroups: 10,
28
+ optionsPerGroup: 30,
29
+ jsonBytes: 1024 * 1024,
30
+ mediaBytes: 10 * 1024 * 1024,
31
+ stagedBytesPerRestaurant: 100 * 1024 * 1024,
32
+ stagedFilesPerRestaurant: 20,
33
+ openOperationsPerRestaurant: 20,
34
+ openPreviewsPerRestaurant: 20,
35
+ activeKeysPerFamily: 10,
36
+ keyDefaultTtlSeconds: 30 * 24 * 60 * 60,
37
+ keyMaxTtlSeconds: 90 * 24 * 60 * 60,
38
+ pairingTtlSeconds: 600,
39
+ pairingPollSeconds: 5,
40
+ proposalTtlSeconds: 24 * 60 * 60,
41
+ previewTtlSeconds: 24 * 60 * 60,
42
+ stagingTtlSeconds: 48 * 60 * 60,
43
+ applyWindowSeconds: 60 * 60,
44
+ workerLeaseSeconds: 30,
45
+ workerHeartbeatSeconds: 10,
46
+ maxWorkerAttempts: 5,
47
+ resultRetentionDays: 90,
48
+ pageSize: 100
49
+ });
50
+ var elenaAgentErrorCodeSchema = z.enum([
51
+ "INVALID_REQUEST",
52
+ "UNSUPPORTED_VERSION",
53
+ "CREDENTIAL_REQUIRED",
54
+ "CREDENTIAL_INVALID",
55
+ "CREDENTIAL_EXPIRED",
56
+ "CREDENTIAL_REVOKED",
57
+ "CREDENTIAL_MIXED",
58
+ "CREDENTIAL_WRONG_FAMILY",
59
+ "OWNER_REQUIRED",
60
+ "ORIGIN_REJECTED",
61
+ "APPROVAL_REQUIRED",
62
+ "APPROVAL_REJECTED",
63
+ "APPROVAL_EXPIRED",
64
+ "APPROVAL_MISMATCH",
65
+ "SCOPE_REQUIRED",
66
+ "MODE_READ_ONLY",
67
+ "API_ACCESS_DISABLED",
68
+ "RESTAURANT_NOT_APPROVED",
69
+ "NOT_FOUND",
70
+ "VERSION_CONFLICT",
71
+ "IDEMPOTENCY_CONFLICT",
72
+ "INVALID_PRICE",
73
+ "UNCONFIRMED_DATA",
74
+ "REFERENCE_CONFLICT",
75
+ "MEDIA_INVALID",
76
+ "MEDIA_EXPIRED",
77
+ "QUOTA_EXCEEDED",
78
+ "RATE_LIMITED",
79
+ "PAIRING_PENDING",
80
+ "PAIRING_DENIED",
81
+ "PAIRING_EXPIRED",
82
+ "PAIRING_CONSUMED",
83
+ "OPERATION_TERMINAL",
84
+ "APPLY_WINDOW_EXPIRED",
85
+ "DEPENDENCY_FAILED",
86
+ "TEMPORARY_UNAVAILABLE",
87
+ "INTERNAL_ERROR"
88
+ ]);
89
+ var elenaAgentErrorSchema = z.strictObject({
90
+ version: elenaAgentVersionSchema,
91
+ error: z.strictObject({
92
+ code: elenaAgentErrorCodeSchema,
93
+ message: z.string().max(500),
94
+ retryable: z.boolean(),
95
+ requestId: elenaAgentRefSchema,
96
+ retryAfterSeconds: z.number().int().min(1).max(3600).optional(),
97
+ issues: z.array(
98
+ z.strictObject({
99
+ path: z.string().max(200),
100
+ code: elenaAgentErrorCodeSchema,
101
+ message: z.string().max(200)
102
+ })
103
+ ).max(250).optional()
104
+ })
105
+ });
106
+ var elenaAgentPageQuerySchema = z.strictObject({
107
+ version: elenaAgentVersionSchema,
108
+ cursor: z.string().regex(/^[a-zA-Z0-9_-]{1,512}$/).optional(),
109
+ limit: z.number().int().min(1).max(ELENA_AGENT_LIMITS.pageSize).default(50)
110
+ });
111
+ var elenaAgentEmptyRequestSchema = z.strictObject({ version: elenaAgentVersionSchema });
112
+ var elenaAgentAckSchema = z.strictObject({
113
+ version: elenaAgentVersionSchema,
114
+ ok: z.literal(true)
115
+ });
116
+
117
+ // ../shared/src/agent-contracts/access.ts
118
+ import { z as z2 } from "zod";
119
+ var ELENA_AGENT_TRANSPORT = Object.freeze({
120
+ agentPrefix: "elena_sk_",
121
+ agentHeader: "x-elena-agent-key",
122
+ agentPath: "/api/v1/agent",
123
+ clientPrefix: "elena_pk_",
124
+ clientHeader: "x-elena-client-key",
125
+ clientPath: "/api/v1/public/sdk",
126
+ ownerPath: "/api/v1/agent-access"
127
+ });
128
+ var elenaAgentSecretSchema = z2.string().regex(/^elena_sk_[A-Za-z0-9_-]{43}$/);
129
+ var elenaManualClientKeySchema = z2.string().regex(/^elena_pk_[A-Za-z0-9_-]{43}$/);
130
+ var elenaManagedClientKeySchema = z2.string().max(160).regex(
131
+ /^elena_pk_m_[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.[1-9]\d{0,15}\.[A-Za-z0-9_-]{43}$/
132
+ );
133
+ var elenaClientKeySchema = z2.union([
134
+ elenaManualClientKeySchema,
135
+ elenaManagedClientKeySchema
136
+ ]);
137
+ var elenaAgentDeviceCodeSchema = z2.string().regex(/^[A-Za-z0-9_-]{43}$/);
138
+ var elenaAgentUserCodeSchema = z2.string().regex(/^[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}$/);
139
+ var elenaAgentModeSchema = z2.enum(["read", "mutate"]);
140
+ var elenaAgentScopeSchema = z2.enum([
141
+ "catalog:read",
142
+ "catalog:write",
143
+ "catalog:publish",
144
+ "assets:read",
145
+ "assets:write"
146
+ ]);
147
+ var elenaAgentScopesSchema = z2.array(elenaAgentScopeSchema).min(1).max(5).refine((scopes) => new Set(scopes).size === scopes.length, "Duplicate scope").refine((scopes) => scopes.includes("catalog:read"), "catalog:read is mandatory");
148
+ var elenaAgentActionSchema = z2.enum([
149
+ "context.read",
150
+ "catalog.read",
151
+ "assets.read",
152
+ "assets.stage",
153
+ "operations.read",
154
+ "operations.propose",
155
+ "operations.apply",
156
+ "catalog.publish"
157
+ ]);
158
+ var elenaAgentRestaurantSchema = z2.strictObject({
159
+ id: elenaAgentIdSchema,
160
+ name: elenaAgentNameSchema,
161
+ slug: z2.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(100),
162
+ currency: elenaAgentCurrencySchema
163
+ });
164
+ var keyTtl = z2.number().int().min(3600).max(ELENA_AGENT_LIMITS.keyMaxTtlSeconds).default(ELENA_AGENT_LIMITS.keyDefaultTtlSeconds);
165
+ var elenaAgentKeyCreateRequestSchema = z2.discriminatedUnion("kind", [
166
+ z2.strictObject({
167
+ version: elenaAgentVersionSchema,
168
+ kind: z2.literal("agent"),
169
+ label: elenaAgentNameSchema,
170
+ mode: elenaAgentModeSchema.default("read"),
171
+ scopes: elenaAgentScopesSchema.default(["catalog:read", "assets:read"]),
172
+ ttlSeconds: keyTtl
173
+ }),
174
+ z2.strictObject({
175
+ version: elenaAgentVersionSchema,
176
+ kind: z2.literal("client"),
177
+ label: elenaAgentNameSchema,
178
+ ttlSeconds: keyTtl
179
+ })
180
+ ]);
181
+ var elenaAgentKeyPolicyRequestSchema = z2.strictObject({
182
+ version: elenaAgentVersionSchema,
183
+ expectedRevision: elenaAgentRevisionSchema,
184
+ mode: elenaAgentModeSchema,
185
+ scopes: elenaAgentScopesSchema
186
+ });
187
+ var elenaAgentKeyRevokeRequestSchema = z2.strictObject({
188
+ version: elenaAgentVersionSchema,
189
+ expectedRevision: elenaAgentRevisionSchema
190
+ });
191
+ var elenaAgentKeyReplaceRequestSchema = z2.strictObject({
192
+ version: elenaAgentVersionSchema,
193
+ expectedRevision: elenaAgentRevisionSchema,
194
+ ttlSeconds: keyTtl
195
+ });
196
+ var keyMetadata = {
197
+ id: elenaAgentIdSchema,
198
+ label: elenaAgentNameSchema,
199
+ displaySuffix: z2.string().regex(/^[A-Za-z0-9_-]{6}$/),
200
+ revision: elenaAgentRevisionSchema,
201
+ status: z2.enum(["active", "revoked", "expired"]),
202
+ createdAt: elenaAgentTimestampSchema,
203
+ expiresAt: elenaAgentTimestampSchema,
204
+ revokedAt: elenaAgentTimestampSchema.nullable(),
205
+ lastUsedAt: elenaAgentTimestampSchema.nullable()
206
+ };
207
+ var elenaAgentKeyMetadataSchema = z2.discriminatedUnion("kind", [
208
+ z2.strictObject({
209
+ ...keyMetadata,
210
+ kind: z2.literal("agent"),
211
+ mode: elenaAgentModeSchema,
212
+ scopes: elenaAgentScopesSchema
213
+ }),
214
+ z2.strictObject({ ...keyMetadata, kind: z2.literal("client") })
215
+ ]);
216
+ var elenaAgentKeyListSchema = z2.strictObject({
217
+ version: elenaAgentVersionSchema,
218
+ keys: z2.array(elenaAgentKeyMetadataSchema).max(20)
219
+ });
220
+ var elenaAgentKeyReceiptSchema = z2.discriminatedUnion("kind", [
221
+ z2.strictObject({
222
+ version: elenaAgentVersionSchema,
223
+ kind: z2.literal("agent"),
224
+ key: elenaAgentKeyMetadataSchema.options[0],
225
+ secret: elenaAgentSecretSchema
226
+ }),
227
+ z2.strictObject({
228
+ version: elenaAgentVersionSchema,
229
+ kind: z2.literal("client"),
230
+ key: elenaAgentKeyMetadataSchema.options[1],
231
+ clientKey: elenaManualClientKeySchema
232
+ })
233
+ ]);
234
+ var elenaAgentKeyResponseSchema = z2.strictObject({
235
+ version: elenaAgentVersionSchema,
236
+ key: elenaAgentKeyMetadataSchema
237
+ });
238
+ var elenaAgentHttpsUrlSchema = z2.url({ protocol: /^https$/ }).max(2048).refine(
239
+ (value) => /^https:\/\/[^/?#@\\\s]+(?:[/?#]|$)/.test(value),
240
+ "URL must not contain userinfo"
241
+ );
242
+ var elenaAgentConnectionUrlSchema = z2.url({ protocol: /^https?$/ }).max(2048).refine(
243
+ (value) => /^https?:\/\/[^/?#@\\\s]+(?:[/?#]|$)/.test(value),
244
+ "URL must not contain userinfo"
245
+ );
246
+ var elenaAgentBaseUrlSchema = elenaAgentHttpsUrlSchema.refine(
247
+ (value) => /^https:\/\/[^/?#@\\\s]+\/?$/.test(value),
248
+ "Base URL must be an explicit origin"
249
+ );
250
+ var elenaAgentConnectionOriginSchema = elenaAgentConnectionUrlSchema.refine(
251
+ (value) => /^https?:\/\/[^/?#@\\\s]+\/?$/.test(value),
252
+ "Base URL must be an explicit origin"
253
+ );
254
+ var elenaAgentPairingStartRequestSchema = z2.strictObject({
255
+ version: elenaAgentVersionSchema,
256
+ label: elenaAgentNameSchema
257
+ });
258
+ var elenaAgentPairingStartResponseSchema = z2.strictObject({
259
+ version: elenaAgentVersionSchema,
260
+ pairingId: elenaAgentIdSchema,
261
+ deviceCode: elenaAgentDeviceCodeSchema,
262
+ userCode: elenaAgentUserCodeSchema,
263
+ verificationUrl: elenaAgentConnectionUrlSchema,
264
+ expiresAt: elenaAgentTimestampSchema,
265
+ intervalSeconds: z2.literal(ELENA_AGENT_LIMITS.pairingPollSeconds)
266
+ });
267
+ var elenaAgentPairingPollRequestSchema = z2.strictObject({
268
+ version: elenaAgentVersionSchema,
269
+ deviceCode: elenaAgentDeviceCodeSchema
270
+ });
271
+ var elenaAgentPairingReviewSchema = z2.strictObject({
272
+ version: elenaAgentVersionSchema,
273
+ label: elenaAgentNameSchema,
274
+ userCode: elenaAgentUserCodeSchema,
275
+ state: z2.enum(["pending", "approved", "rejected", "expired", "consumed"]),
276
+ expiresAt: elenaAgentTimestampSchema
277
+ });
278
+ var elenaAgentPairingDecisionRequestSchema = z2.discriminatedUnion("decision", [
279
+ z2.strictObject({
280
+ version: elenaAgentVersionSchema,
281
+ userCode: elenaAgentUserCodeSchema,
282
+ decision: z2.literal("approve"),
283
+ mode: elenaAgentModeSchema.default("read"),
284
+ scopes: elenaAgentScopesSchema.default(["catalog:read", "assets:read"]),
285
+ ttlSeconds: keyTtl
286
+ }),
287
+ z2.strictObject({
288
+ version: elenaAgentVersionSchema,
289
+ userCode: elenaAgentUserCodeSchema,
290
+ decision: z2.literal("reject")
291
+ })
292
+ ]);
293
+ var elenaAgentPairingPollResponseSchema = z2.discriminatedUnion("status", [
294
+ z2.strictObject({
295
+ version: elenaAgentVersionSchema,
296
+ status: z2.literal("pending"),
297
+ expiresAt: elenaAgentTimestampSchema,
298
+ intervalSeconds: z2.literal(ELENA_AGENT_LIMITS.pairingPollSeconds)
299
+ }),
300
+ z2.strictObject({
301
+ version: elenaAgentVersionSchema,
302
+ status: z2.literal("delivered"),
303
+ restaurant: elenaAgentRestaurantSchema,
304
+ credential: elenaAgentKeyReceiptSchema.options[0]
305
+ })
306
+ ]);
307
+ var elenaAgentContextSchema = z2.strictObject({
308
+ version: elenaAgentVersionSchema,
309
+ restaurant: elenaAgentRestaurantSchema,
310
+ principal: z2.strictObject({
311
+ kind: z2.literal("agent"),
312
+ credentialId: elenaAgentIdSchema,
313
+ credentialRevision: elenaAgentRevisionSchema,
314
+ mode: elenaAgentModeSchema,
315
+ scopes: elenaAgentScopesSchema,
316
+ expiresAt: elenaAgentTimestampSchema
317
+ }),
318
+ capabilities: z2.strictObject({
319
+ apiAccess: z2.literal(true),
320
+ menu: z2.literal(true),
321
+ imports: z2.boolean(),
322
+ media: z2.boolean(),
323
+ publication: z2.boolean()
324
+ }),
325
+ actions: z2.array(elenaAgentActionSchema).min(1).max(8).refine((actions) => new Set(actions).size === actions.length, "Duplicate action")
326
+ }).superRefine((value, ctx) => {
327
+ const requiredScopes = {
328
+ "catalog.read": "catalog:read",
329
+ "assets.read": "assets:read",
330
+ "assets.stage": "assets:write",
331
+ "operations.propose": "catalog:write",
332
+ "operations.apply": "catalog:write",
333
+ "catalog.publish": "catalog:publish"
334
+ };
335
+ const writes = {
336
+ "assets.stage": true,
337
+ "operations.propose": true,
338
+ "operations.apply": true,
339
+ "catalog.publish": true
340
+ };
341
+ for (const action of value.actions) {
342
+ if (value.principal.mode === "read" && writes[action]) {
343
+ ctx.addIssue({
344
+ code: "custom",
345
+ path: ["actions"],
346
+ message: "Read mode cannot expose mutation actions"
347
+ });
348
+ }
349
+ const scope = requiredScopes[action];
350
+ if (scope && !value.principal.scopes.includes(scope)) {
351
+ ctx.addIssue({
352
+ code: "custom",
353
+ path: ["actions"],
354
+ message: "Action exceeds granted scopes"
355
+ });
356
+ }
357
+ }
358
+ if (value.actions.includes("catalog.publish") && !value.principal.scopes.includes("catalog:write")) {
359
+ ctx.addIssue({
360
+ code: "custom",
361
+ path: ["actions"],
362
+ message: "Publication also requires catalog:write"
363
+ });
364
+ }
365
+ const expectedCapabilities = {
366
+ imports: value.actions.includes("operations.propose") && value.actions.includes("operations.apply"),
367
+ media: value.actions.includes("assets.stage"),
368
+ publication: value.actions.includes("catalog.publish")
369
+ };
370
+ for (const capability of ["imports", "media", "publication"]) {
371
+ if (value.capabilities[capability] !== expectedCapabilities[capability]) {
372
+ ctx.addIssue({
373
+ code: "custom",
374
+ path: ["capabilities", capability],
375
+ message: "Capability must match effective actions"
376
+ });
377
+ }
378
+ }
379
+ });
380
+ var elenaClientContextSchema = z2.strictObject({
381
+ version: elenaAgentVersionSchema,
382
+ restaurant: elenaAgentRestaurantSchema,
383
+ capabilities: z2.strictObject({ menu: z2.literal(true), assets: z2.literal(true) })
384
+ });
385
+ var elenaAgentCliAuthStatusSchema = z2.strictObject({
386
+ version: elenaAgentVersionSchema,
387
+ authenticated: z2.boolean(),
388
+ source: z2.enum(["store", "environment", "none"]),
389
+ baseUrl: elenaAgentBaseUrlSchema.nullable(),
390
+ credentialId: elenaAgentIdSchema.nullable(),
391
+ restaurantId: elenaAgentIdSchema.nullable(),
392
+ expiresAt: elenaAgentTimestampSchema.nullable()
393
+ });
394
+ var elenaAgentCliLogoutSchema = z2.strictObject({
395
+ version: elenaAgentVersionSchema,
396
+ loggedOut: z2.literal(true),
397
+ remoteRevoked: z2.literal(false),
398
+ environmentCredentialPresent: z2.boolean()
399
+ });
400
+
401
+ // ../shared/src/agent-contracts/catalog.ts
402
+ import { z as z3 } from "zod";
403
+ var elenaAgentExistingTargetSchema = z3.strictObject({
404
+ kind: z3.literal("id"),
405
+ id: elenaAgentIdSchema,
406
+ expectedVersion: elenaAgentRevisionSchema
407
+ });
408
+ var elenaAgentExternalTargetSchema = z3.strictObject({
409
+ kind: z3.literal("external"),
410
+ namespace: elenaAgentRefSchema,
411
+ key: elenaAgentRefSchema,
412
+ expectedVersion: elenaAgentRevisionSchema.nullable()
413
+ });
414
+ var elenaAgentTargetSchema = z3.discriminatedUnion("kind", [
415
+ elenaAgentExistingTargetSchema,
416
+ elenaAgentExternalTargetSchema
417
+ ]);
418
+ var elenaAgentCategoryReferenceSchema = z3.discriminatedUnion("kind", [
419
+ elenaAgentExistingTargetSchema,
420
+ z3.strictObject({ kind: z3.literal("ref"), ref: elenaAgentRefSchema })
421
+ ]);
422
+ var elenaAgentDescriptionSchema = z3.discriminatedUnion("status", [
423
+ z3.strictObject({ status: z3.literal("transcribed"), text: elenaAgentTextSchema }),
424
+ z3.strictObject({ status: z3.literal("proposed"), text: elenaAgentTextSchema })
425
+ ]);
426
+ var elenaAgentConfirmedPriceSchema = z3.strictObject({
427
+ status: z3.literal("confirmed"),
428
+ amount: elenaAgentMoneySchema,
429
+ basis: z3.literal("operator")
430
+ });
431
+ var elenaAgentDraftPriceSchema = z3.discriminatedUnion("status", [
432
+ elenaAgentConfirmedPriceSchema,
433
+ z3.strictObject({
434
+ status: z3.literal("unconfirmed"),
435
+ reason: z3.enum(["missing", "illegible"]),
436
+ rawText: z3.string().max(100).nullable()
437
+ })
438
+ ]);
439
+ var elenaAgentMediaReferenceSchema = z3.discriminatedUnion("kind", [
440
+ z3.strictObject({
441
+ kind: z3.literal("existing"),
442
+ assetId: elenaAgentIdSchema,
443
+ sha256: elenaAgentHashSchema
444
+ }),
445
+ z3.strictObject({
446
+ kind: z3.literal("staged"),
447
+ stagingId: elenaAgentIdSchema,
448
+ sha256: elenaAgentHashSchema
449
+ })
450
+ ]);
451
+ var elenaAgentGallerySchema = z3.array(elenaAgentMediaReferenceSchema).max(ELENA_AGENT_LIMITS.galleryImages).refine(
452
+ (images) => new Set(images.map((image) => image.kind === "existing" ? image.assetId : image.stagingId)).size === images.length,
453
+ "Duplicate media reference"
454
+ );
455
+ var elenaAgentExtraOptionSchema = z3.strictObject({
456
+ name: elenaAgentNameSchema,
457
+ additionalPrice: elenaAgentNonnegativeMoneySchema,
458
+ order: z3.number().int().min(0).max(1e3)
459
+ });
460
+ var elenaAgentExtraGroupSchema = z3.strictObject({
461
+ name: elenaAgentNameSchema,
462
+ required: z3.boolean(),
463
+ minSelections: z3.number().int().min(0).max(ELENA_AGENT_LIMITS.optionsPerGroup),
464
+ maxSelections: z3.number().int().min(1).max(ELENA_AGENT_LIMITS.optionsPerGroup),
465
+ order: z3.number().int().min(0).max(1e3),
466
+ options: z3.array(elenaAgentExtraOptionSchema).min(1).max(ELENA_AGENT_LIMITS.optionsPerGroup)
467
+ }).superRefine((group, ctx) => {
468
+ if (group.minSelections > group.maxSelections || group.maxSelections > group.options.length) {
469
+ ctx.addIssue({
470
+ code: "custom",
471
+ path: ["maxSelections"],
472
+ message: "Invalid selection bounds"
473
+ });
474
+ }
475
+ if (group.required && group.minSelections < 1 || !group.required && group.minSelections !== 0) {
476
+ ctx.addIssue({
477
+ code: "custom",
478
+ path: ["minSelections"],
479
+ message: "Required must match minimum selection count"
480
+ });
481
+ }
482
+ if (new Set(
483
+ group.options.map((option) => option.name.normalize("NFKC").trim().toLocaleLowerCase("es"))
484
+ ).size !== group.options.length) {
485
+ ctx.addIssue({ code: "custom", path: ["options"], message: "Duplicate option name" });
486
+ }
487
+ });
488
+ var elenaAgentExtrasSchema = z3.strictObject({
489
+ confirmed: z3.literal(true),
490
+ groups: z3.array(elenaAgentExtraGroupSchema).max(ELENA_AGENT_LIMITS.extraGroups)
491
+ }).refine(
492
+ (extras) => new Set(
493
+ extras.groups.map((group) => group.name.normalize("NFKC").trim().toLocaleLowerCase("es"))
494
+ ).size === extras.groups.length,
495
+ "Duplicate group name"
496
+ );
497
+ var elenaAgentCategoryDataSchema = z3.strictObject({
498
+ name: elenaAgentNameSchema,
499
+ description: elenaAgentDescriptionSchema.nullable(),
500
+ order: z3.number().int().min(0).max(1e4)
501
+ });
502
+ var productFields = {
503
+ type: z3.literal("menu"),
504
+ name: elenaAgentNameSchema,
505
+ description: elenaAgentDescriptionSchema.nullable(),
506
+ category: elenaAgentCategoryReferenceSchema,
507
+ available: z3.boolean(),
508
+ images: elenaAgentGallerySchema,
509
+ extras: elenaAgentExtrasSchema
510
+ };
511
+ var elenaAgentProductDataSchema = z3.strictObject({
512
+ ...productFields,
513
+ price: elenaAgentConfirmedPriceSchema
514
+ });
515
+ var elenaAgentDraftProductDataSchema = z3.strictObject({
516
+ ...productFields,
517
+ price: elenaAgentDraftPriceSchema
518
+ });
519
+ var elenaAgentCategoryChangeSchema = z3.strictObject({
520
+ kind: z3.literal("category.upsert"),
521
+ ref: elenaAgentRefSchema,
522
+ target: elenaAgentTargetSchema,
523
+ data: elenaAgentCategoryDataSchema
524
+ });
525
+ var elenaAgentProductChangeSchema = z3.strictObject({
526
+ kind: z3.literal("product.upsert"),
527
+ ref: elenaAgentRefSchema,
528
+ target: elenaAgentTargetSchema,
529
+ data: elenaAgentProductDataSchema
530
+ });
531
+ var elenaAgentChangeSchema = z3.discriminatedUnion("kind", [
532
+ elenaAgentCategoryChangeSchema,
533
+ elenaAgentProductChangeSchema
534
+ ]);
535
+ var elenaAgentCategorySchema = z3.strictObject({
536
+ id: elenaAgentIdSchema,
537
+ revision: elenaAgentRevisionSchema,
538
+ name: elenaAgentNameSchema,
539
+ description: elenaAgentTextSchema.nullable(),
540
+ order: z3.number().int().min(0).max(1e4)
541
+ });
542
+ var elenaAgentProductSchema = z3.strictObject({
543
+ id: elenaAgentIdSchema,
544
+ revision: elenaAgentRevisionSchema,
545
+ type: z3.literal("menu"),
546
+ name: elenaAgentNameSchema,
547
+ basePrice: elenaAgentMoneySchema,
548
+ description: elenaAgentTextSchema.nullable(),
549
+ categoryId: elenaAgentIdSchema,
550
+ available: z3.boolean(),
551
+ publishedToMenu: z3.boolean(),
552
+ isDraft: z3.boolean(),
553
+ images: z3.array(z3.strictObject({ assetId: elenaAgentIdSchema, sha256: elenaAgentHashSchema })).max(ELENA_AGENT_LIMITS.galleryImages),
554
+ /** Present on current native reads; optional only for retained v1 snapshots. */
555
+ primaryImageId: elenaAgentIdSchema.nullable().optional(),
556
+ extraGroups: z3.array(elenaAgentExtraGroupSchema).max(ELENA_AGENT_LIMITS.extraGroups)
557
+ });
558
+ var elenaAgentCategoryListSchema = z3.strictObject({
559
+ version: elenaAgentVersionSchema,
560
+ categories: z3.array(elenaAgentCategorySchema).max(ELENA_AGENT_LIMITS.pageSize),
561
+ nextCursor: z3.string().regex(/^[a-zA-Z0-9_-]{1,512}$/).nullable()
562
+ });
563
+ var elenaAgentProductListSchema = z3.strictObject({
564
+ version: elenaAgentVersionSchema,
565
+ products: z3.array(elenaAgentProductSchema).max(ELENA_AGENT_LIMITS.pageSize),
566
+ nextCursor: z3.string().regex(/^[a-zA-Z0-9_-]{1,512}$/).nullable()
567
+ });
568
+ var elenaAgentProductResponseSchema = z3.strictObject({
569
+ version: elenaAgentVersionSchema,
570
+ product: elenaAgentProductSchema,
571
+ /** Authoritative editing metadata; never infer primary image from gallery order. */
572
+ editable: z3.strictObject({
573
+ restaurantId: elenaAgentIdSchema,
574
+ productId: elenaAgentIdSchema,
575
+ imageId: elenaAgentIdSchema.nullable(),
576
+ categoryRevision: elenaAgentRevisionSchema,
577
+ primaryImage: z3.strictObject({ assetId: elenaAgentIdSchema, sha256: elenaAgentHashSchema }).nullable().optional(),
578
+ imageEditorUrl: elenaAgentHttpsUrlSchema.optional()
579
+ }).optional()
580
+ });
581
+ var elenaAgentCategoryResponseSchema = z3.strictObject({
582
+ version: elenaAgentVersionSchema,
583
+ category: elenaAgentCategorySchema
584
+ });
585
+ var elenaAgentMediaStageRequestSchema = z3.strictObject({
586
+ version: elenaAgentVersionSchema,
587
+ idempotencyKey: elenaAgentRefSchema,
588
+ fileName: z3.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,119}\.(?:jpg|jpeg|png|webp)$/),
589
+ mimeType: z3.enum(["image/jpeg", "image/png", "image/webp"]),
590
+ bytes: z3.number().int().min(1).max(ELENA_AGENT_LIMITS.mediaBytes),
591
+ sha256: elenaAgentHashSchema
592
+ });
593
+ var elenaAgentMediaStagedSchema = z3.strictObject({
594
+ version: elenaAgentVersionSchema,
595
+ stagingId: elenaAgentIdSchema,
596
+ sha256: elenaAgentHashSchema,
597
+ mimeType: z3.enum(["image/jpeg", "image/png", "image/webp"]),
598
+ bytes: z3.number().int().min(1).max(ELENA_AGENT_LIMITS.mediaBytes),
599
+ expiresAt: elenaAgentTimestampSchema
600
+ });
601
+ var elenaAgentAssetSchema = z3.strictObject({
602
+ version: elenaAgentVersionSchema,
603
+ assetId: elenaAgentIdSchema,
604
+ sha256: elenaAgentHashSchema,
605
+ mimeType: z3.enum(["image/jpeg", "image/png", "image/webp"]),
606
+ bytes: z3.number().int().min(1).max(ELENA_AGENT_LIMITS.mediaBytes)
607
+ });
608
+ var elenaClientAssetSchema = z3.strictObject({
609
+ assetId: elenaAgentIdSchema,
610
+ url: elenaAgentHttpsUrlSchema
611
+ });
612
+ var elenaClientCategorySchema = z3.strictObject({
613
+ id: elenaAgentIdSchema,
614
+ name: elenaAgentNameSchema,
615
+ description: elenaAgentTextSchema.nullable(),
616
+ order: z3.number().int().min(0).max(1e4)
617
+ });
618
+ var elenaClientProductSchema = z3.strictObject({
619
+ id: elenaAgentIdSchema,
620
+ type: z3.literal("menu"),
621
+ name: elenaAgentNameSchema,
622
+ basePrice: elenaAgentMoneySchema,
623
+ description: elenaAgentTextSchema.nullable(),
624
+ categoryId: elenaAgentIdSchema,
625
+ available: z3.boolean(),
626
+ images: z3.array(elenaClientAssetSchema).max(ELENA_AGENT_LIMITS.galleryImages),
627
+ extraGroups: z3.array(elenaAgentExtraGroupSchema).max(ELENA_AGENT_LIMITS.extraGroups)
628
+ });
629
+ var elenaClientCatalogSchema = z3.strictObject({
630
+ version: elenaAgentVersionSchema,
631
+ categories: z3.array(elenaClientCategorySchema).max(ELENA_AGENT_LIMITS.pageSize),
632
+ products: z3.array(elenaClientProductSchema).max(ELENA_AGENT_LIMITS.pageSize),
633
+ nextCursor: z3.string().regex(/^[a-zA-Z0-9_-]{1,512}$/).nullable()
634
+ });
635
+ var elenaClientProductResponseSchema = z3.strictObject({
636
+ version: elenaAgentVersionSchema,
637
+ product: elenaClientProductSchema
638
+ });
639
+ var elenaClientAssetResponseSchema = z3.strictObject({
640
+ version: elenaAgentVersionSchema,
641
+ asset: elenaClientAssetSchema
642
+ });
643
+
644
+ // ../shared/src/agent-contracts/operations.ts
645
+ import { z as z4 } from "zod";
646
+ function normalizedValuesEqual(left, right) {
647
+ if (left === right) return true;
648
+ if (Array.isArray(left) || Array.isArray(right)) {
649
+ return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => normalizedValuesEqual(value, right[index]));
650
+ }
651
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") {
652
+ return false;
653
+ }
654
+ const leftRecord = left;
655
+ const rightRecord = right;
656
+ const keys = Object.keys(leftRecord);
657
+ return keys.length === Object.keys(rightRecord).length && keys.every(
658
+ (key) => Object.prototype.hasOwnProperty.call(rightRecord, key) && normalizedValuesEqual(leftRecord[key], rightRecord[key])
659
+ );
660
+ }
661
+ var elenaAgentChangesSchema = z4.array(elenaAgentChangeSchema).min(1).max(ELENA_AGENT_LIMITS.changesPerOperation).superRefine((changes, ctx) => {
662
+ const refs = /* @__PURE__ */ new Set();
663
+ const targets = /* @__PURE__ */ new Set();
664
+ const categoryRefs = new Set(
665
+ changes.filter((change) => change.kind === "category.upsert").map((change) => change.ref)
666
+ );
667
+ changes.forEach((change, index) => {
668
+ const target = change.target.kind === "id" ? `${change.kind}:id:${change.target.id}` : JSON.stringify([change.kind, "external", change.target.namespace, change.target.key]);
669
+ if (refs.has(change.ref) || targets.has(target)) {
670
+ ctx.addIssue({ code: "custom", path: [index], message: "Duplicate ref or target" });
671
+ }
672
+ refs.add(change.ref);
673
+ targets.add(target);
674
+ if (change.kind === "product.upsert" && change.data.category.kind === "ref" && !categoryRefs.has(change.data.category.ref)) {
675
+ ctx.addIssue({
676
+ code: "custom",
677
+ path: [index, "data", "category"],
678
+ message: "Unknown category ref"
679
+ });
680
+ }
681
+ });
682
+ });
683
+ var elenaAgentPublicationPayloadSchema = z4.strictObject({
684
+ kind: z4.literal("publication"),
685
+ published: z4.boolean(),
686
+ products: z4.array(elenaAgentExistingTargetSchema).min(1).max(ELENA_AGENT_LIMITS.publicationProducts).refine(
687
+ (products) => new Set(products.map((product) => product.id)).size === products.length,
688
+ "Duplicate product"
689
+ )
690
+ });
691
+ var elenaAgentOperationPayloadSchema = z4.discriminatedUnion("kind", [
692
+ z4.strictObject({ kind: z4.literal("catalog"), changes: elenaAgentChangesSchema }),
693
+ elenaAgentPublicationPayloadSchema,
694
+ z4.strictObject({
695
+ kind: z4.literal("import"),
696
+ importId: elenaAgentIdSchema,
697
+ planHash: elenaAgentHashSchema,
698
+ selectedRefs: z4.array(elenaAgentRefSchema).min(1).max(ELENA_AGENT_LIMITS.productsPerImport + ELENA_AGENT_LIMITS.categoriesPerImport).refine((refs) => new Set(refs).size === refs.length, "Duplicate selected ref")
699
+ })
700
+ ]);
701
+ var elenaAgentProposalRequestSchema = z4.strictObject({
702
+ version: elenaAgentVersionSchema,
703
+ idempotencyKey: elenaAgentRefSchema,
704
+ payload: elenaAgentOperationPayloadSchema
705
+ });
706
+ var elenaAgentApplyRequestSchema = z4.strictObject({
707
+ version: elenaAgentVersionSchema,
708
+ expectedRevision: elenaAgentRevisionSchema,
709
+ payloadHash: elenaAgentHashSchema
710
+ });
711
+ var elenaAgentApprovalRequestSchema = z4.strictObject({
712
+ version: elenaAgentVersionSchema,
713
+ expectedRevision: elenaAgentRevisionSchema,
714
+ payloadHash: elenaAgentHashSchema,
715
+ decision: z4.enum(["approve", "reject"])
716
+ });
717
+ var elenaAgentBaseVersionSchema = z4.strictObject({
718
+ kind: z4.enum(["category", "product", "credential"]),
719
+ id: elenaAgentIdSchema,
720
+ revision: elenaAgentRevisionSchema
721
+ });
722
+ var elenaAgentDiffSchema = z4.discriminatedUnion("kind", [
723
+ z4.strictObject({
724
+ kind: z4.literal("category"),
725
+ ref: elenaAgentRefSchema,
726
+ before: elenaAgentCategorySchema.nullable(),
727
+ after: elenaAgentCategoryChangeSchema,
728
+ publicEffect: z4.enum(["none", "immediate_on_apply"])
729
+ }),
730
+ z4.strictObject({
731
+ kind: z4.literal("product"),
732
+ ref: elenaAgentRefSchema,
733
+ before: elenaAgentProductSchema.nullable(),
734
+ after: elenaAgentChangeSchema.options[1],
735
+ publicEffect: z4.enum(["none", "immediate_on_apply"])
736
+ }),
737
+ z4.strictObject({
738
+ kind: z4.literal("publication"),
739
+ ref: elenaAgentRefSchema,
740
+ productId: elenaAgentIdSchema,
741
+ before: z4.boolean(),
742
+ after: z4.boolean(),
743
+ publicEffect: z4.literal("immediate_on_apply")
744
+ })
745
+ ]).superRefine((diff, ctx) => {
746
+ if (diff.kind === "publication") {
747
+ if (diff.ref !== `product:${diff.productId}`) {
748
+ ctx.addIssue({
749
+ code: "custom",
750
+ path: ["ref"],
751
+ message: "Publication ref must identify the exact product"
752
+ });
753
+ }
754
+ return;
755
+ }
756
+ if (diff.ref !== diff.after.ref) {
757
+ ctx.addIssue({ code: "custom", path: ["ref"], message: "Diff must identify its change" });
758
+ }
759
+ if (diff.kind === "product") {
760
+ const effect = diff.before?.publishedToMenu ? "immediate_on_apply" : "none";
761
+ if (diff.publicEffect !== effect) {
762
+ ctx.addIssue({
763
+ code: "custom",
764
+ path: ["publicEffect"],
765
+ message: "Diff must disclose current public visibility"
766
+ });
767
+ }
768
+ }
769
+ });
770
+ var elenaAgentItemErrorSchema = z4.strictObject({
771
+ code: elenaAgentErrorCodeSchema,
772
+ message: z4.string().max(500),
773
+ retryable: z4.boolean()
774
+ });
775
+ var elenaAgentResultItemSchema = z4.discriminatedUnion("status", [
776
+ z4.strictObject({
777
+ ref: elenaAgentRefSchema,
778
+ status: z4.literal("applied"),
779
+ resource: z4.strictObject({
780
+ kind: z4.enum(["category", "product"]),
781
+ id: elenaAgentIdSchema,
782
+ revision: elenaAgentRevisionSchema
783
+ })
784
+ }),
785
+ z4.strictObject({
786
+ ref: elenaAgentRefSchema,
787
+ status: z4.literal("skipped"),
788
+ resource: z4.strictObject({
789
+ kind: z4.enum(["category", "product"]),
790
+ id: elenaAgentIdSchema,
791
+ revision: elenaAgentRevisionSchema
792
+ })
793
+ }),
794
+ z4.strictObject({
795
+ ref: elenaAgentRefSchema,
796
+ status: z4.literal("failed"),
797
+ error: elenaAgentItemErrorSchema
798
+ }),
799
+ z4.strictObject({
800
+ ref: elenaAgentRefSchema,
801
+ status: z4.literal("blocked"),
802
+ error: elenaAgentItemErrorSchema
803
+ })
804
+ ]);
805
+ var elenaAgentOperationResultSchema = z4.strictObject({
806
+ completedAt: elenaAgentTimestampSchema,
807
+ items: z4.array(elenaAgentResultItemSchema).min(1).max(250)
808
+ }).refine(
809
+ (result) => new Set(result.items.map((item) => item.ref)).size === result.items.length,
810
+ "Duplicate result ref"
811
+ );
812
+ var elenaAgentOperationStateSchema = z4.enum([
813
+ "pending",
814
+ "approved",
815
+ "rejected",
816
+ "expired",
817
+ "applying",
818
+ "applied",
819
+ "partial",
820
+ "failed",
821
+ "cancelled"
822
+ ]);
823
+ var elenaAgentOperationSchema = z4.strictObject({
824
+ version: elenaAgentVersionSchema,
825
+ operationId: elenaAgentIdSchema,
826
+ revision: elenaAgentRevisionSchema,
827
+ state: elenaAgentOperationStateSchema,
828
+ restaurantId: elenaAgentIdSchema,
829
+ credentialId: elenaAgentIdSchema,
830
+ credentialRevision: elenaAgentRevisionSchema,
831
+ payload: elenaAgentOperationPayloadSchema,
832
+ payloadHash: elenaAgentHashSchema,
833
+ baseVersions: z4.array(elenaAgentBaseVersionSchema).max(501),
834
+ diff: z4.array(elenaAgentDiffSchema).max(250),
835
+ approvalUrl: elenaAgentConnectionUrlSchema,
836
+ createdAt: elenaAgentTimestampSchema,
837
+ expiresAt: elenaAgentTimestampSchema,
838
+ decision: z4.strictObject({
839
+ outcome: z4.enum(["approved", "rejected"]),
840
+ decidedAt: elenaAgentTimestampSchema,
841
+ approverId: elenaAgentIdSchema,
842
+ payloadHash: elenaAgentHashSchema
843
+ }).nullable(),
844
+ applyDeadline: elenaAgentTimestampSchema.nullable(),
845
+ result: elenaAgentOperationResultSchema.nullable()
846
+ }).superRefine((operation, ctx) => {
847
+ const approvedRefs = operation.payload.kind === "catalog" ? operation.payload.changes.map((change) => change.ref) : operation.payload.kind === "import" ? operation.payload.selectedRefs : operation.payload.products.map((product) => `product:${product.id}`);
848
+ if (operation.diff.some((diff) => !approvedRefs.includes(diff.ref)) || new Set(operation.diff.map((diff) => diff.ref)).size !== operation.diff.length) {
849
+ ctx.addIssue({
850
+ code: "custom",
851
+ path: ["diff"],
852
+ message: "Diff must be a unique subset of the exact approved selection"
853
+ });
854
+ }
855
+ const payload = operation.payload;
856
+ if (payload.kind === "catalog") {
857
+ const changes = new Map(payload.changes.map((change) => [change.ref, change]));
858
+ operation.diff.forEach((diff, index) => {
859
+ const change = changes.get(diff.ref);
860
+ if (!change || `${diff.kind}.upsert` !== change.kind || !normalizedValuesEqual(diff.after, change)) {
861
+ ctx.addIssue({
862
+ code: "custom",
863
+ path: ["diff", index],
864
+ message: "Catalog diff must match the exact normalized payload change"
865
+ });
866
+ }
867
+ });
868
+ } else if (payload.kind === "publication") {
869
+ const productIds = new Set(payload.products.map((product) => product.id));
870
+ operation.diff.forEach((diff, index) => {
871
+ if (diff.kind !== "publication" || !productIds.has(diff.productId) || diff.after !== payload.published) {
872
+ ctx.addIssue({
873
+ code: "custom",
874
+ path: ["diff", index],
875
+ message: "Publication diff must match the exact selected product and publication value"
876
+ });
877
+ }
878
+ });
879
+ }
880
+ const finished = ["applied", "partial", "failed", "cancelled"].includes(operation.state);
881
+ if (finished !== (operation.result !== null)) {
882
+ ctx.addIssue({
883
+ code: "custom",
884
+ path: ["result"],
885
+ message: "Only executed terminal states carry a complete result"
886
+ });
887
+ }
888
+ if (["approved", "applying", "applied", "partial", "failed"].includes(operation.state) && operation.decision?.outcome !== "approved") {
889
+ ctx.addIssue({
890
+ code: "custom",
891
+ path: ["decision"],
892
+ message: "State requires owner approval"
893
+ });
894
+ }
895
+ if (operation.state === "rejected" && operation.decision?.outcome !== "rejected") {
896
+ ctx.addIssue({
897
+ code: "custom",
898
+ path: ["decision"],
899
+ message: "Rejected state requires owner rejection"
900
+ });
901
+ }
902
+ if (operation.state === "pending" && operation.decision !== null) {
903
+ ctx.addIssue({
904
+ code: "custom",
905
+ path: ["decision"],
906
+ message: "Pending operation cannot have a decision"
907
+ });
908
+ }
909
+ if (operation.decision && operation.decision.payloadHash !== operation.payloadHash) {
910
+ ctx.addIssue({
911
+ code: "custom",
912
+ path: ["decision", "payloadHash"],
913
+ message: "Decision does not bind payload"
914
+ });
915
+ }
916
+ if (operation.state === "applying" && operation.applyDeadline === null) {
917
+ ctx.addIssue({
918
+ code: "custom",
919
+ path: ["applyDeadline"],
920
+ message: "Applying requires a deadline"
921
+ });
922
+ }
923
+ if (operation.result) {
924
+ if (operation.result.items.length !== approvedRefs.length || operation.result.items.some((item) => !approvedRefs.includes(item.ref))) {
925
+ ctx.addIssue({
926
+ code: "custom",
927
+ path: ["result"],
928
+ message: "Result must account for the exact approved selection"
929
+ });
930
+ }
931
+ const success = operation.result.items.some(
932
+ (item) => item.status === "applied" || item.status === "skipped"
933
+ );
934
+ const failure = operation.result.items.some(
935
+ (item) => item.status === "failed" || item.status === "blocked"
936
+ );
937
+ if (operation.state === "applied" && failure || operation.state === "partial" && (!success || !failure) || ["failed", "cancelled"].includes(operation.state) && success) {
938
+ ctx.addIssue({
939
+ code: "custom",
940
+ path: ["result"],
941
+ message: "Terminal state must describe actual effects"
942
+ });
943
+ }
944
+ }
945
+ });
946
+ var elenaAgentOperationListSchema = z4.strictObject({
947
+ version: elenaAgentVersionSchema,
948
+ operations: z4.array(elenaAgentOperationSchema).max(ELENA_AGENT_LIMITS.pageSize),
949
+ nextCursor: z4.string().regex(/^[a-zA-Z0-9_-]{1,512}$/).nullable()
950
+ });
951
+ var elenaAgentImportSourceSchema = z4.strictObject({
952
+ kind: z4.enum(["image", "pdf", "table", "manual"]),
953
+ label: elenaAgentNameSchema,
954
+ sha256: elenaAgentHashSchema.optional()
955
+ });
956
+ var elenaAgentImportItemSchema = z4.strictObject({
957
+ ref: elenaAgentRefSchema,
958
+ target: elenaAgentTargetSchema,
959
+ data: elenaAgentDraftProductDataSchema
960
+ });
961
+ var elenaAgentImportPreviewRequestSchema = z4.strictObject({
962
+ version: elenaAgentVersionSchema,
963
+ idempotencyKey: elenaAgentRefSchema,
964
+ currency: elenaAgentCurrencySchema,
965
+ source: elenaAgentImportSourceSchema,
966
+ categories: z4.array(elenaAgentCategoryChangeSchema).max(ELENA_AGENT_LIMITS.categoriesPerImport),
967
+ items: z4.array(elenaAgentImportItemSchema).min(1).max(ELENA_AGENT_LIMITS.productsPerImport)
968
+ }).superRefine((request, ctx) => {
969
+ const refs = /* @__PURE__ */ new Set();
970
+ const targets = /* @__PURE__ */ new Set();
971
+ const categoryRefs = new Set(request.categories.map((category) => category.ref));
972
+ const entries = [
973
+ ...request.categories.map((entry) => ({ entry, kind: "category" })),
974
+ ...request.items.map((entry) => ({ entry, kind: "product" }))
975
+ ];
976
+ for (const { entry, kind } of entries) {
977
+ const identity = entry.target.kind === "id" ? `${kind}:id:${entry.target.id}` : JSON.stringify([kind, "external", entry.target.namespace, entry.target.key]);
978
+ if (refs.has(entry.ref) || targets.has(identity)) {
979
+ ctx.addIssue({ code: "custom", message: "Duplicate import ref or target" });
980
+ }
981
+ refs.add(entry.ref);
982
+ targets.add(identity);
983
+ }
984
+ request.items.forEach((item, index) => {
985
+ if (item.data.category.kind === "ref" && !categoryRefs.has(item.data.category.ref)) {
986
+ ctx.addIssue({
987
+ code: "custom",
988
+ path: ["items", index, "data", "category"],
989
+ message: "Unknown category ref"
990
+ });
991
+ }
992
+ });
993
+ });
994
+ var elenaAgentPreviewItemSchema = z4.discriminatedUnion("classification", [
995
+ z4.strictObject({
996
+ ref: elenaAgentRefSchema,
997
+ classification: z4.literal("create"),
998
+ change: elenaAgentChangeSchema,
999
+ diff: elenaAgentDiffSchema
1000
+ }),
1001
+ z4.strictObject({
1002
+ ref: elenaAgentRefSchema,
1003
+ classification: z4.literal("update"),
1004
+ change: elenaAgentChangeSchema,
1005
+ diff: elenaAgentDiffSchema
1006
+ }),
1007
+ z4.strictObject({
1008
+ ref: elenaAgentRefSchema,
1009
+ classification: z4.literal("skip"),
1010
+ resource: z4.strictObject({
1011
+ kind: z4.enum(["category", "product"]),
1012
+ id: elenaAgentIdSchema,
1013
+ revision: elenaAgentRevisionSchema
1014
+ })
1015
+ }),
1016
+ z4.strictObject({
1017
+ ref: elenaAgentRefSchema,
1018
+ classification: z4.literal("conflict"),
1019
+ issues: z4.array(elenaAgentItemErrorSchema).min(1).max(20)
1020
+ }),
1021
+ z4.strictObject({
1022
+ ref: elenaAgentRefSchema,
1023
+ classification: z4.literal("invalid"),
1024
+ issues: z4.array(elenaAgentItemErrorSchema).min(1).max(20)
1025
+ })
1026
+ ]).superRefine((item, ctx) => {
1027
+ if (item.classification !== "create" && item.classification !== "update") return;
1028
+ const { change, diff } = item;
1029
+ if (item.ref !== change.ref || item.ref !== diff.ref) {
1030
+ ctx.addIssue({
1031
+ code: "custom",
1032
+ path: ["ref"],
1033
+ message: "Preview ref does not match change/diff"
1034
+ });
1035
+ }
1036
+ if (diff.kind === "publication" || `${diff.kind}.upsert` !== change.kind || !normalizedValuesEqual(change, diff.after)) {
1037
+ ctx.addIssue({
1038
+ code: "custom",
1039
+ path: ["diff"],
1040
+ message: "Preview diff must match the exact normalized change and resource kind"
1041
+ });
1042
+ }
1043
+ if (diff.kind === "publication") return;
1044
+ if (item.classification === "create") {
1045
+ if (change.target.kind !== "external" || change.target.expectedVersion !== null || diff.before !== null) {
1046
+ ctx.addIssue({
1047
+ code: "custom",
1048
+ path: ["diff"],
1049
+ message: "Create requires an absent external target and no previous resource"
1050
+ });
1051
+ }
1052
+ } else if (change.target.expectedVersion === null || diff.before === null || diff.before.revision !== change.target.expectedVersion || change.target.kind === "id" && diff.before.id !== change.target.id) {
1053
+ ctx.addIssue({
1054
+ code: "custom",
1055
+ path: ["diff"],
1056
+ message: "Update requires the existing target resource with its exact expected revision"
1057
+ });
1058
+ }
1059
+ });
1060
+ var elenaAgentImportPreviewSchema = z4.strictObject({
1061
+ version: elenaAgentVersionSchema,
1062
+ importId: elenaAgentIdSchema,
1063
+ planHash: elenaAgentHashSchema,
1064
+ createdAt: elenaAgentTimestampSchema,
1065
+ expiresAt: elenaAgentTimestampSchema,
1066
+ state: z4.enum(["ready", "expired"]),
1067
+ currency: elenaAgentCurrencySchema,
1068
+ items: z4.array(elenaAgentPreviewItemSchema).min(1).max(250),
1069
+ summary: z4.strictObject({
1070
+ create: z4.number().int().min(0).max(250),
1071
+ update: z4.number().int().min(0).max(250),
1072
+ skip: z4.number().int().min(0).max(250),
1073
+ conflict: z4.number().int().min(0).max(250),
1074
+ invalid: z4.number().int().min(0).max(250)
1075
+ })
1076
+ }).superRefine((preview, ctx) => {
1077
+ if (new Set(preview.items.map((item) => item.ref)).size !== preview.items.length) {
1078
+ ctx.addIssue({ code: "custom", path: ["items"], message: "Duplicate preview ref" });
1079
+ }
1080
+ for (const classification of ["create", "update", "skip", "conflict", "invalid"]) {
1081
+ if (preview.summary[classification] !== preview.items.filter((item) => item.classification === classification).length) {
1082
+ ctx.addIssue({
1083
+ code: "custom",
1084
+ path: ["summary", classification],
1085
+ message: "Summary does not match items"
1086
+ });
1087
+ }
1088
+ }
1089
+ });
1090
+ var elenaAgentCatalogSampleSchema = z4.strictObject({
1091
+ version: elenaAgentVersionSchema,
1092
+ sample: z4.literal(true),
1093
+ disclaimer: z4.string().min(1).max(2e3),
1094
+ source: z4.strictObject({
1095
+ kind: z4.literal("image"),
1096
+ label: z4.string().min(1).max(2e3),
1097
+ originalAvailable: z4.literal(false),
1098
+ ocrAccuracyVerified: z4.literal(false)
1099
+ }),
1100
+ confirmation: z4.strictObject({
1101
+ status: z4.literal("sample_only"),
1102
+ requiresOperatorReview: z4.literal(true),
1103
+ currency: elenaAgentCurrencySchema
1104
+ }),
1105
+ categories: z4.array(z4.strictObject({ ref: elenaAgentRefSchema, name: elenaAgentNameSchema })).min(1).max(50),
1106
+ items: z4.array(
1107
+ z4.strictObject({
1108
+ ref: elenaAgentRefSchema,
1109
+ categoryRef: elenaAgentRefSchema,
1110
+ name: elenaAgentNameSchema,
1111
+ price: z4.discriminatedUnion("status", [
1112
+ elenaAgentDraftPriceSchema.options[0].extend({ basis: z4.literal("sample") }),
1113
+ elenaAgentDraftPriceSchema.options[1]
1114
+ ]),
1115
+ description: elenaAgentDescriptionSchema.nullable()
1116
+ })
1117
+ ).min(1).max(200),
1118
+ notes: z4.array(z4.string().min(1).max(2e3)).min(1).max(30)
1119
+ }).superRefine((sample, ctx) => {
1120
+ const categoryRefs = new Set(sample.categories.map((category) => category.ref));
1121
+ const refs = [
1122
+ ...sample.categories.map((category) => category.ref),
1123
+ ...sample.items.map((item) => item.ref)
1124
+ ];
1125
+ if (new Set(refs).size !== refs.length)
1126
+ ctx.addIssue({ code: "custom", message: "Duplicate sample ref" });
1127
+ if (sample.items.some((item) => !categoryRefs.has(item.categoryRef))) {
1128
+ ctx.addIssue({ code: "custom", path: ["items"], message: "Unknown sample category" });
1129
+ }
1130
+ });
1131
+
1132
+ // ../shared/src/agent-contracts/public-connection.ts
1133
+ import { z as z5 } from "zod";
1134
+ var ELENA_PUBLIC_CONNECTION_DISCOVERY_ROOT = "/api/v1/public/catalog-connections";
1135
+ var elenaPublicConnectionSlugSchema = z5.string().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
1136
+ var ttl = z5.number().int().min(3600).max(ELENA_AGENT_LIMITS.keyMaxTtlSeconds).default(ELENA_AGENT_LIMITS.keyDefaultTtlSeconds);
1137
+ var mutation = {
1138
+ version: elenaAgentVersionSchema,
1139
+ // Zero asserts that no connection has ever been created for this restaurant.
1140
+ expectedRevision: z5.number().int().min(0).max(Number.MAX_SAFE_INTEGER - 1)
1141
+ };
1142
+ var elenaPublicConnectionMutationSchema = z5.discriminatedUnion("action", [
1143
+ z5.strictObject({ ...mutation, action: z5.literal("enable"), ttlSeconds: ttl }),
1144
+ z5.strictObject({ ...mutation, action: z5.literal("renew"), ttlSeconds: ttl }),
1145
+ z5.strictObject({ ...mutation, action: z5.literal("disable") }),
1146
+ z5.strictObject({ ...mutation, action: z5.literal("revoke") })
1147
+ ]);
1148
+ var elenaPublicConnectionMetadataSchema = z5.strictObject({
1149
+ revision: elenaAgentRevisionSchema,
1150
+ status: z5.enum(["published", "disabled", "revoked"]),
1151
+ expiresAt: elenaAgentTimestampSchema
1152
+ });
1153
+ var elenaPublicConnectionManagementSchema = z5.strictObject({
1154
+ version: elenaAgentVersionSchema,
1155
+ publication: elenaPublicConnectionMetadataSchema.nullable()
1156
+ });
1157
+ var elenaPublicConnectionSchema = z5.strictObject({
1158
+ version: elenaAgentVersionSchema,
1159
+ apiOrigin: elenaAgentBaseUrlSchema,
1160
+ clientKey: elenaManagedClientKeySchema,
1161
+ publication: z5.strictObject({
1162
+ revision: elenaAgentRevisionSchema,
1163
+ expiresAt: elenaAgentTimestampSchema
1164
+ }),
1165
+ context: elenaClientContextSchema
1166
+ });
1167
+
1168
+ // src/key-transport.ts
1169
+ var ElenaAgentTransportError = class extends Error {
1170
+ constructor(code, status = 0, retryable = false, requestId = null, retryAfterSeconds) {
1171
+ super(`Elena request failed (${code}).`);
1172
+ this.code = code;
1173
+ this.status = status;
1174
+ this.retryable = retryable;
1175
+ this.requestId = requestId;
1176
+ this.retryAfterSeconds = retryAfterSeconds;
1177
+ }
1178
+ code;
1179
+ status;
1180
+ retryable;
1181
+ requestId;
1182
+ retryAfterSeconds;
1183
+ name = "ElenaAgentTransportError";
1184
+ };
1185
+ function parseWire(schema, value, response = false) {
1186
+ let unsupportedVersion = false;
1187
+ try {
1188
+ if (value !== null && typeof value === "object" && "version" in value) {
1189
+ unsupportedVersion = value.version !== void 0 && value.version !== "1";
1190
+ }
1191
+ const parsed = schema.safeParse(value);
1192
+ if (parsed.success) return parsed.data;
1193
+ } catch {
1194
+ }
1195
+ throw new ElenaAgentTransportError(
1196
+ unsupportedVersion ? "UNSUPPORTED_VERSION" : response ? "INVALID_RESPONSE" : "INVALID_REQUEST"
1197
+ );
1198
+ }
1199
+ function assertOptionKeys(value, keys) {
1200
+ if (!value || typeof value !== "object" || Object.keys(value).some((key) => !keys.includes(key))) {
1201
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1202
+ }
1203
+ }
1204
+ function explicitOrigin(value, allowTestHttp = false) {
1205
+ try {
1206
+ if (typeof value !== "string" || !/^https?:\/\/[^/?#@\\\s]+\/?$/.test(value)) throw 0;
1207
+ if (/elena_(?:sk|pk)_/i.test(value)) throw 0;
1208
+ const url = new URL(value);
1209
+ const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
1210
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && (typeof process !== "undefined" && process.env.NODE_ENV === "development" || allowTestHttp && loopback)))
1211
+ throw 0;
1212
+ if (url.username || url.password || url.search || url.hash || url.pathname !== "/") throw 0;
1213
+ return url.origin;
1214
+ } catch {
1215
+ throw new ElenaAgentTransportError("ORIGIN_REJECTED");
1216
+ }
1217
+ }
1218
+ function resourceId(value) {
1219
+ return parseWire(elenaAgentIdSchema, value);
1220
+ }
1221
+ function pageQuery(value = { version: "1" }) {
1222
+ const query = parseWire(elenaAgentPageQuerySchema, value);
1223
+ const params = new URLSearchParams({ version: query.version, limit: String(query.limit) });
1224
+ if (query.cursor !== void 0) params.set("cursor", query.cursor);
1225
+ return `?${params}`;
1226
+ }
1227
+ function jsonBody(schema, value) {
1228
+ const text = JSON.stringify(parseWire(schema, value));
1229
+ if (new TextEncoder().encode(text).byteLength > ELENA_AGENT_LIMITS.jsonBytes) {
1230
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1231
+ }
1232
+ return text;
1233
+ }
1234
+ function timeoutValue(value) {
1235
+ if (!Number.isInteger(value) || value < 1 || value > 3e5) {
1236
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1237
+ }
1238
+ return value;
1239
+ }
1240
+ var privateCredential = /elena_sk_[A-Za-z0-9_-]{43}/;
1241
+ var retryCodes = {
1242
+ RATE_LIMITED: true,
1243
+ TEMPORARY_UNAVAILABLE: true
1244
+ };
1245
+ function createKeyTransport(options, header, credential) {
1246
+ if (options.testing !== void 0) {
1247
+ assertOptionKeys(options.testing, ["allowHttp"]);
1248
+ if (options.testing.allowHttp !== true || !options.fetch) {
1249
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1250
+ }
1251
+ }
1252
+ const baseUrl = explicitOrigin(options.baseUrl, options.testing?.allowHttp === true);
1253
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1254
+ if (typeof fetchImpl !== "function") throw new ElenaAgentTransportError("INVALID_REQUEST");
1255
+ const defaultTimeout = timeoutValue(options.timeoutMs ?? 3e4);
1256
+ const retries = options.maxRetries ?? 2;
1257
+ if (!Number.isInteger(retries) || retries < 0 || retries > 2) {
1258
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1259
+ }
1260
+ async function request(path, schema, requestOptions = {}, body, stage = false, retryAmbiguous = true) {
1261
+ assertOptionKeys(requestOptions, ["signal", "timeoutMs"]);
1262
+ if (privateCredential.test(path) || credential && path.includes(credential) || typeof body === "string" && (privateCredential.test(body) || credential && body.includes(credential))) {
1263
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1264
+ }
1265
+ if (requestOptions.signal !== void 0 && !(requestOptions.signal instanceof AbortSignal)) {
1266
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1267
+ }
1268
+ const timeoutMs = timeoutValue(requestOptions.timeoutMs ?? (stage ? 12e4 : defaultTimeout));
1269
+ if (requestOptions.signal?.aborted) throw new ElenaAgentTransportError("ABORTED");
1270
+ const controller = new AbortController();
1271
+ let timedOut = false;
1272
+ const timer = setTimeout(() => {
1273
+ timedOut = true;
1274
+ controller.abort();
1275
+ }, timeoutMs);
1276
+ const onAbort = () => controller.abort();
1277
+ requestOptions.signal?.addEventListener("abort", onAbort, { once: true });
1278
+ const abortError = () => new ElenaAgentTransportError(timedOut ? "TIMEOUT" : "ABORTED");
1279
+ const deadline = Date.now() + timeoutMs;
1280
+ const bounded = (promise) => new Promise((resolve, reject) => {
1281
+ const abort = () => {
1282
+ cleanup();
1283
+ reject(abortError());
1284
+ };
1285
+ const cleanup = () => controller.signal.removeEventListener("abort", abort);
1286
+ if (controller.signal.aborted) {
1287
+ reject(abortError());
1288
+ return;
1289
+ }
1290
+ controller.signal.addEventListener("abort", abort, { once: true });
1291
+ promise.then(
1292
+ (value) => {
1293
+ cleanup();
1294
+ resolve(value);
1295
+ },
1296
+ (error) => {
1297
+ cleanup();
1298
+ reject(error);
1299
+ }
1300
+ );
1301
+ });
1302
+ const delay = (ms) => new Promise((resolve, reject) => {
1303
+ const abort = () => {
1304
+ clearTimeout(wait);
1305
+ controller.signal.removeEventListener("abort", abort);
1306
+ reject(abortError());
1307
+ };
1308
+ const wait = setTimeout(() => {
1309
+ controller.signal.removeEventListener("abort", abort);
1310
+ resolve();
1311
+ }, ms);
1312
+ controller.signal.addEventListener("abort", abort, { once: true });
1313
+ if (controller.signal.aborted) abort();
1314
+ });
1315
+ try {
1316
+ for (let attempt = 0; ; attempt++) {
1317
+ let failure;
1318
+ let retryDelay = 100 * 2 ** attempt;
1319
+ try {
1320
+ const headers = new Headers({ Accept: "application/json" });
1321
+ if (header) headers.set(header, credential);
1322
+ if (typeof body === "string") headers.set("Content-Type", "application/json");
1323
+ const response = await bounded(
1324
+ Promise.resolve().then(() => {
1325
+ if (controller.signal.aborted) throw abortError();
1326
+ return fetchImpl(`${baseUrl}${path}`, {
1327
+ method: body === void 0 ? "GET" : "POST",
1328
+ headers,
1329
+ body: typeof body === "function" ? body() : body,
1330
+ credentials: "omit",
1331
+ redirect: "error",
1332
+ referrerPolicy: "no-referrer",
1333
+ cache: "no-store",
1334
+ signal: controller.signal
1335
+ });
1336
+ }).catch(() => {
1337
+ throw new ElenaAgentTransportError("NETWORK_ERROR", 0, retryAmbiguous);
1338
+ })
1339
+ );
1340
+ if (response.redirected || response.status >= 300 && response.status < 400) {
1341
+ throw new ElenaAgentTransportError("REDIRECT_REJECTED", response.status);
1342
+ }
1343
+ if (response.url && new URL(response.url).origin !== baseUrl) {
1344
+ throw new ElenaAgentTransportError("ORIGIN_REJECTED");
1345
+ }
1346
+ const text = await bounded(
1347
+ Promise.resolve().then(() => response.text()).catch(() => {
1348
+ throw new ElenaAgentTransportError("NETWORK_ERROR", 0, retryAmbiguous);
1349
+ })
1350
+ );
1351
+ if (privateCredential.test(text) || credential && text.includes(credential)) {
1352
+ throw new ElenaAgentTransportError("INVALID_RESPONSE", response.status);
1353
+ }
1354
+ let payload;
1355
+ try {
1356
+ payload = JSON.parse(text);
1357
+ } catch {
1358
+ throw new ElenaAgentTransportError("INVALID_RESPONSE", response.status);
1359
+ }
1360
+ const decodedText = JSON.stringify(payload);
1361
+ if (privateCredential.test(decodedText) || credential && decodedText.includes(credential)) {
1362
+ throw new ElenaAgentTransportError("INVALID_RESPONSE", response.status);
1363
+ }
1364
+ if (response.ok) return parseWire(schema, payload, true);
1365
+ const wire = parseWire(elenaAgentErrorSchema, payload, true).error;
1366
+ const retryable = retryCodes[wire.code] === true && wire.retryable && (wire.code === "RATE_LIMITED" && response.status === 429 || wire.code === "TEMPORARY_UNAVAILABLE" && response.status === 503);
1367
+ failure = new ElenaAgentTransportError(
1368
+ wire.code,
1369
+ response.status,
1370
+ retryable,
1371
+ wire.requestId,
1372
+ wire.retryAfterSeconds
1373
+ );
1374
+ const retryAfter = response.headers.get("retry-after");
1375
+ let headerDelay = 0;
1376
+ if (retryAfter !== null) {
1377
+ const seconds = /^\d+$/.test(retryAfter) ? Number(retryAfter) : (Date.parse(retryAfter) - Date.now()) / 1e3;
1378
+ if (!Number.isFinite(seconds) || seconds < 0 || seconds > 3600) {
1379
+ throw new ElenaAgentTransportError(
1380
+ wire.code,
1381
+ response.status,
1382
+ false,
1383
+ wire.requestId,
1384
+ wire.retryAfterSeconds
1385
+ );
1386
+ }
1387
+ headerDelay = Math.ceil(seconds * 1e3);
1388
+ }
1389
+ retryDelay = Math.max(retryDelay, headerDelay, (wire.retryAfterSeconds ?? 0) * 1e3);
1390
+ } catch (error) {
1391
+ if (controller.signal.aborted) throw abortError();
1392
+ failure = error instanceof ElenaAgentTransportError ? error : new ElenaAgentTransportError("NETWORK_ERROR", 0, retryAmbiguous);
1393
+ }
1394
+ if (!failure.retryable || attempt >= retries || Date.now() + retryDelay >= deadline)
1395
+ throw failure;
1396
+ await delay(retryDelay);
1397
+ }
1398
+ } finally {
1399
+ clearTimeout(timer);
1400
+ requestOptions.signal?.removeEventListener("abort", onAbort);
1401
+ }
1402
+ }
1403
+ return { request };
1404
+ }
1405
+
1406
+ // src/agent/index.ts
1407
+ function assertServerRuntime() {
1408
+ if (typeof window !== "undefined" || typeof document !== "undefined" || typeof process === "undefined" || typeof process.versions?.node !== "string") {
1409
+ throw new Error("Elena agent client is server-only.");
1410
+ }
1411
+ }
1412
+ assertServerRuntime();
1413
+ function createElenaAgentClient(options) {
1414
+ assertServerRuntime();
1415
+ assertOptionKeys(options, [
1416
+ "baseUrl",
1417
+ "agentKey",
1418
+ "staffOrigin",
1419
+ "fetch",
1420
+ "timeoutMs",
1421
+ "maxRetries",
1422
+ "testing"
1423
+ ]);
1424
+ if (!elenaAgentSecretSchema.safeParse(options.agentKey).success) {
1425
+ throw new ElenaAgentTransportError("CREDENTIAL_WRONG_FAMILY");
1426
+ }
1427
+ const { request } = createKeyTransport(options, "x-elena-agent-key", options.agentKey);
1428
+ const staffOrigin = options.staffOrigin === void 0 ? void 0 : explicitOrigin(options.staffOrigin);
1429
+ const root = "/api/v1/agent";
1430
+ function requireStaffOrigin() {
1431
+ if (!staffOrigin) throw new ElenaAgentTransportError("ORIGIN_REJECTED");
1432
+ }
1433
+ function operationResponse(operation, id, hash) {
1434
+ requireStaffOrigin();
1435
+ const approval = new URL(operation.approvalUrl);
1436
+ if (approval.origin !== staffOrigin || approval.pathname !== `/agent-operations/${operation.operationId}` || approval.search || approval.hash || id !== void 0 && operation.operationId !== id || hash !== void 0 && operation.payloadHash !== hash) {
1437
+ throw new ElenaAgentTransportError("INVALID_RESPONSE");
1438
+ }
1439
+ return operation;
1440
+ }
1441
+ async function getOperation(id, requestOptions) {
1442
+ requireStaffOrigin();
1443
+ const expectedId = resourceId(id);
1444
+ return operationResponse(
1445
+ await request(
1446
+ `${root}/operations/${expectedId}`,
1447
+ elenaAgentOperationSchema,
1448
+ requestOptions
1449
+ ),
1450
+ expectedId
1451
+ );
1452
+ }
1453
+ return Object.freeze({
1454
+ context: Object.freeze({
1455
+ get: (options2) => request(`${root}/context`, elenaAgentContextSchema, options2)
1456
+ }),
1457
+ catalog: Object.freeze({
1458
+ categories: Object.freeze({
1459
+ list: (query, options2) => request(
1460
+ `${root}/catalog/categories${pageQuery(query)}`,
1461
+ elenaAgentCategoryListSchema,
1462
+ options2
1463
+ ),
1464
+ get: async (id, options2) => {
1465
+ const expectedId = resourceId(id);
1466
+ const result = await request(
1467
+ `${root}/catalog/categories/${expectedId}`,
1468
+ elenaAgentCategoryResponseSchema,
1469
+ options2
1470
+ );
1471
+ if (result.category.id !== expectedId)
1472
+ throw new ElenaAgentTransportError("INVALID_RESPONSE");
1473
+ return result;
1474
+ }
1475
+ }),
1476
+ products: Object.freeze({
1477
+ list: (query, options2) => request(
1478
+ `${root}/catalog/products${pageQuery(query)}`,
1479
+ elenaAgentProductListSchema,
1480
+ options2
1481
+ ),
1482
+ get: async (id, options2) => {
1483
+ const expectedId = resourceId(id);
1484
+ const result = await request(
1485
+ `${root}/catalog/products/${expectedId}`,
1486
+ elenaAgentProductResponseSchema,
1487
+ options2
1488
+ );
1489
+ if (result.product.id !== expectedId)
1490
+ throw new ElenaAgentTransportError("INVALID_RESPONSE");
1491
+ return result;
1492
+ }
1493
+ })
1494
+ }),
1495
+ assets: Object.freeze({
1496
+ get: async (id, options2) => {
1497
+ const expectedId = resourceId(id);
1498
+ const result = await request(
1499
+ `${root}/assets/${expectedId}`,
1500
+ elenaAgentAssetSchema,
1501
+ options2
1502
+ );
1503
+ if (result.assetId !== expectedId) throw new ElenaAgentTransportError("INVALID_RESPONSE");
1504
+ return result;
1505
+ }
1506
+ }),
1507
+ media: Object.freeze({
1508
+ stage: async (input, file, options2) => {
1509
+ const metadata = parseWire(elenaAgentMediaStageRequestSchema, input);
1510
+ const requestOptions = options2 ?? {};
1511
+ assertOptionKeys(requestOptions, ["signal", "timeoutMs"]);
1512
+ const timeoutMs = requestOptions.timeoutMs ?? 12e4;
1513
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 3e5 || requestOptions.signal !== void 0 && !(requestOptions.signal instanceof AbortSignal)) {
1514
+ throw new ElenaAgentTransportError("INVALID_REQUEST");
1515
+ }
1516
+ if (requestOptions.signal?.aborted) throw new ElenaAgentTransportError("ABORTED");
1517
+ const deadline = Date.now() + timeoutMs;
1518
+ let readTimer;
1519
+ let readTimedOut = false;
1520
+ let onAbort;
1521
+ let snapshot;
1522
+ let hash;
1523
+ try {
1524
+ if (!(file instanceof Blob) || file.size !== metadata.bytes || file.type !== metadata.mimeType) {
1525
+ throw new Error("Invalid media");
1526
+ }
1527
+ const boundedFile = Blob.prototype.slice.call(file, 0, metadata.bytes, metadata.mimeType);
1528
+ const reading = Blob.prototype.arrayBuffer.call(boundedFile);
1529
+ const interrupted = new Promise((_, reject) => {
1530
+ readTimer = setTimeout(() => {
1531
+ readTimedOut = true;
1532
+ reject(new Error("Read timeout"));
1533
+ }, timeoutMs);
1534
+ onAbort = () => reject(new Error("Read aborted"));
1535
+ requestOptions.signal?.addEventListener("abort", onAbort, { once: true });
1536
+ if (requestOptions.signal?.aborted) onAbort();
1537
+ });
1538
+ const bytes = await Promise.race([reading, interrupted]);
1539
+ if (bytes.byteLength !== metadata.bytes) throw new Error("Invalid media");
1540
+ hash = createHash("sha256").update(new Uint8Array(bytes)).digest("hex");
1541
+ if (hash !== metadata.sha256) throw new Error("Invalid media");
1542
+ snapshot = new Blob([bytes], { type: metadata.mimeType });
1543
+ } catch {
1544
+ throw new ElenaAgentTransportError(
1545
+ requestOptions.signal?.aborted ? "ABORTED" : readTimedOut || Date.now() >= deadline ? "TIMEOUT" : "MEDIA_INVALID"
1546
+ );
1547
+ } finally {
1548
+ clearTimeout(readTimer);
1549
+ if (onAbort) requestOptions.signal?.removeEventListener("abort", onAbort);
1550
+ }
1551
+ const remainingMs = deadline - Date.now();
1552
+ if (remainingMs <= 0) throw new ElenaAgentTransportError("TIMEOUT");
1553
+ const result = await request(
1554
+ `${root}/media/stage`,
1555
+ elenaAgentMediaStagedSchema,
1556
+ { ...requestOptions, timeoutMs: remainingMs },
1557
+ () => {
1558
+ const form = new FormData();
1559
+ form.set("metadata", JSON.stringify(metadata));
1560
+ form.set("file", snapshot, metadata.fileName);
1561
+ return form;
1562
+ },
1563
+ true
1564
+ );
1565
+ if (result.sha256 !== hash || result.bytes !== metadata.bytes || result.mimeType !== metadata.mimeType) {
1566
+ throw new ElenaAgentTransportError("INVALID_RESPONSE");
1567
+ }
1568
+ return result;
1569
+ },
1570
+ get: async (id, options2) => {
1571
+ const expectedId = resourceId(id);
1572
+ const result = await request(
1573
+ `${root}/media/staged/${expectedId}`,
1574
+ elenaAgentMediaStagedSchema,
1575
+ options2
1576
+ );
1577
+ if (result.stagingId !== expectedId) throw new ElenaAgentTransportError("INVALID_RESPONSE");
1578
+ return result;
1579
+ }
1580
+ }),
1581
+ imports: Object.freeze({
1582
+ preview: async (input, options2) => {
1583
+ const normalized = parseWire(elenaAgentImportPreviewRequestSchema, input);
1584
+ const refs = new Set(
1585
+ [...normalized.categories, ...normalized.items].map((item) => item.ref)
1586
+ );
1587
+ const result = await request(
1588
+ `${root}/imports/preview`,
1589
+ elenaAgentImportPreviewSchema,
1590
+ options2,
1591
+ jsonBody(elenaAgentImportPreviewRequestSchema, normalized)
1592
+ );
1593
+ if (result.items.length !== refs.size || result.items.some((item) => !refs.has(item.ref))) {
1594
+ throw new ElenaAgentTransportError("INVALID_RESPONSE");
1595
+ }
1596
+ return result;
1597
+ },
1598
+ get: async (id, options2) => {
1599
+ const expectedId = resourceId(id);
1600
+ const result = await request(
1601
+ `${root}/imports/${expectedId}`,
1602
+ elenaAgentImportPreviewSchema,
1603
+ options2
1604
+ );
1605
+ if (result.importId !== expectedId) throw new ElenaAgentTransportError("INVALID_RESPONSE");
1606
+ return result;
1607
+ }
1608
+ }),
1609
+ operations: Object.freeze({
1610
+ propose: async (input, options2) => {
1611
+ requireStaffOrigin();
1612
+ return operationResponse(
1613
+ await request(
1614
+ `${root}/operations`,
1615
+ elenaAgentOperationSchema,
1616
+ options2,
1617
+ jsonBody(elenaAgentProposalRequestSchema, input)
1618
+ )
1619
+ );
1620
+ },
1621
+ list: async (query, options2) => {
1622
+ requireStaffOrigin();
1623
+ const result = await request(
1624
+ `${root}/operations${pageQuery(query)}`,
1625
+ elenaAgentOperationListSchema,
1626
+ options2
1627
+ );
1628
+ result.operations.forEach((operation) => operationResponse(operation));
1629
+ return result;
1630
+ },
1631
+ get: getOperation,
1632
+ apply: async (id, input, options2) => {
1633
+ requireStaffOrigin();
1634
+ const expectedId = resourceId(id);
1635
+ const normalized = parseWire(elenaAgentApplyRequestSchema, input);
1636
+ const body = jsonBody(elenaAgentApplyRequestSchema, normalized);
1637
+ const { payloadHash } = normalized;
1638
+ if (options2?.signal?.aborted) throw new ElenaAgentTransportError("ABORTED");
1639
+ try {
1640
+ return operationResponse(
1641
+ await request(
1642
+ `${root}/operations/${expectedId}/apply`,
1643
+ elenaAgentOperationSchema,
1644
+ options2,
1645
+ body,
1646
+ false,
1647
+ false
1648
+ ),
1649
+ expectedId,
1650
+ payloadHash
1651
+ );
1652
+ } catch (error) {
1653
+ if (!(error instanceof ElenaAgentTransportError) || !["NETWORK_ERROR", "TIMEOUT", "ABORTED"].includes(error.code))
1654
+ throw error;
1655
+ try {
1656
+ const current = operationResponse(
1657
+ await getOperation(expectedId, { timeoutMs: 3e4 }),
1658
+ expectedId,
1659
+ payloadHash
1660
+ );
1661
+ if (["applying", "applied", "partial", "failed", "cancelled"].includes(current.state))
1662
+ return current;
1663
+ } catch {
1664
+ }
1665
+ throw error;
1666
+ }
1667
+ }
1668
+ })
1669
+ });
1670
+ }
1671
+ export {
1672
+ ELENA_AGENT_CONTRACT_VERSION,
1673
+ ELENA_AGENT_LIMITS,
1674
+ ELENA_AGENT_TRANSPORT,
1675
+ ELENA_PUBLIC_CONNECTION_DISCOVERY_ROOT,
1676
+ ElenaAgentTransportError,
1677
+ createElenaAgentClient,
1678
+ elenaAgentAckSchema,
1679
+ elenaAgentActionSchema,
1680
+ elenaAgentApplyRequestSchema,
1681
+ elenaAgentApprovalRequestSchema,
1682
+ elenaAgentAssetSchema,
1683
+ elenaAgentBaseUrlSchema,
1684
+ elenaAgentBaseVersionSchema,
1685
+ elenaAgentCatalogSampleSchema,
1686
+ elenaAgentCategoryChangeSchema,
1687
+ elenaAgentCategoryDataSchema,
1688
+ elenaAgentCategoryListSchema,
1689
+ elenaAgentCategoryReferenceSchema,
1690
+ elenaAgentCategoryResponseSchema,
1691
+ elenaAgentCategorySchema,
1692
+ elenaAgentChangeSchema,
1693
+ elenaAgentChangesSchema,
1694
+ elenaAgentCliAuthStatusSchema,
1695
+ elenaAgentCliLogoutSchema,
1696
+ elenaAgentConfirmedPriceSchema,
1697
+ elenaAgentConnectionOriginSchema,
1698
+ elenaAgentConnectionUrlSchema,
1699
+ elenaAgentContextSchema,
1700
+ elenaAgentCurrencySchema,
1701
+ elenaAgentDescriptionSchema,
1702
+ elenaAgentDeviceCodeSchema,
1703
+ elenaAgentDiffSchema,
1704
+ elenaAgentDraftPriceSchema,
1705
+ elenaAgentDraftProductDataSchema,
1706
+ elenaAgentEmptyRequestSchema,
1707
+ elenaAgentErrorCodeSchema,
1708
+ elenaAgentErrorSchema,
1709
+ elenaAgentExistingTargetSchema,
1710
+ elenaAgentExternalTargetSchema,
1711
+ elenaAgentExtraGroupSchema,
1712
+ elenaAgentExtraOptionSchema,
1713
+ elenaAgentExtrasSchema,
1714
+ elenaAgentGallerySchema,
1715
+ elenaAgentHashSchema,
1716
+ elenaAgentHttpsUrlSchema,
1717
+ elenaAgentIdSchema,
1718
+ elenaAgentImportItemSchema,
1719
+ elenaAgentImportPreviewRequestSchema,
1720
+ elenaAgentImportPreviewSchema,
1721
+ elenaAgentImportSourceSchema,
1722
+ elenaAgentItemErrorSchema,
1723
+ elenaAgentKeyCreateRequestSchema,
1724
+ elenaAgentKeyListSchema,
1725
+ elenaAgentKeyMetadataSchema,
1726
+ elenaAgentKeyPolicyRequestSchema,
1727
+ elenaAgentKeyReceiptSchema,
1728
+ elenaAgentKeyReplaceRequestSchema,
1729
+ elenaAgentKeyResponseSchema,
1730
+ elenaAgentKeyRevokeRequestSchema,
1731
+ elenaAgentMediaReferenceSchema,
1732
+ elenaAgentMediaStageRequestSchema,
1733
+ elenaAgentMediaStagedSchema,
1734
+ elenaAgentModeSchema,
1735
+ elenaAgentMoneySchema,
1736
+ elenaAgentNameSchema,
1737
+ elenaAgentNonnegativeMoneySchema,
1738
+ elenaAgentOperationListSchema,
1739
+ elenaAgentOperationPayloadSchema,
1740
+ elenaAgentOperationResultSchema,
1741
+ elenaAgentOperationSchema,
1742
+ elenaAgentOperationStateSchema,
1743
+ elenaAgentPageQuerySchema,
1744
+ elenaAgentPairingDecisionRequestSchema,
1745
+ elenaAgentPairingPollRequestSchema,
1746
+ elenaAgentPairingPollResponseSchema,
1747
+ elenaAgentPairingReviewSchema,
1748
+ elenaAgentPairingStartRequestSchema,
1749
+ elenaAgentPairingStartResponseSchema,
1750
+ elenaAgentPreviewItemSchema,
1751
+ elenaAgentProductChangeSchema,
1752
+ elenaAgentProductDataSchema,
1753
+ elenaAgentProductListSchema,
1754
+ elenaAgentProductResponseSchema,
1755
+ elenaAgentProductSchema,
1756
+ elenaAgentProposalRequestSchema,
1757
+ elenaAgentPublicationPayloadSchema,
1758
+ elenaAgentRefSchema,
1759
+ elenaAgentRestaurantSchema,
1760
+ elenaAgentResultItemSchema,
1761
+ elenaAgentRevisionSchema,
1762
+ elenaAgentScopeSchema,
1763
+ elenaAgentScopesSchema,
1764
+ elenaAgentSecretSchema,
1765
+ elenaAgentTargetSchema,
1766
+ elenaAgentTextSchema,
1767
+ elenaAgentTimestampSchema,
1768
+ elenaAgentUserCodeSchema,
1769
+ elenaAgentVersionSchema,
1770
+ elenaClientAssetResponseSchema,
1771
+ elenaClientAssetSchema,
1772
+ elenaClientCatalogSchema,
1773
+ elenaClientCategorySchema,
1774
+ elenaClientContextSchema,
1775
+ elenaClientKeySchema,
1776
+ elenaClientProductResponseSchema,
1777
+ elenaClientProductSchema,
1778
+ elenaManagedClientKeySchema,
1779
+ elenaManualClientKeySchema,
1780
+ elenaPublicConnectionManagementSchema,
1781
+ elenaPublicConnectionMetadataSchema,
1782
+ elenaPublicConnectionMutationSchema,
1783
+ elenaPublicConnectionSchema,
1784
+ elenaPublicConnectionSlugSchema
1785
+ };
1786
+ //# sourceMappingURL=agent.js.map