@helyx/bot 1.1.0 → 1.2.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +16 -1
  3. package/dist/auto-moderation-scanner-registry.d.ts +27 -0
  4. package/dist/auto-moderation-scanner-registry.js +172 -0
  5. package/dist/cloud-connector.js +3 -0
  6. package/dist/cloud-control-dispatcher.js +32 -0
  7. package/dist/command-path-contract.js +1 -0
  8. package/dist/control-permissions.d.ts +2 -2
  9. package/dist/control-permissions.js +4 -2
  10. package/dist/control-server.d.ts +72 -16
  11. package/dist/control-server.js +150 -250
  12. package/dist/dashboard-action-control.d.ts +25 -0
  13. package/dist/dashboard-action-control.js +73 -0
  14. package/dist/dashboard-action-routes.d.ts +9 -0
  15. package/dist/dashboard-action-routes.js +36 -0
  16. package/dist/dashboard-feature-control.d.ts +37 -0
  17. package/dist/dashboard-feature-control.js +206 -0
  18. package/dist/dashboard-module-projection.d.ts +9 -0
  19. package/dist/dashboard-module-projection.js +15 -0
  20. package/dist/dashboard-preview-import-routes.d.ts +6 -0
  21. package/dist/dashboard-preview-import-routes.js +121 -0
  22. package/dist/discord-analytics.d.ts +3 -0
  23. package/dist/discord-analytics.js +49 -5
  24. package/dist/discord-auto-moderation-service.d.ts +21 -0
  25. package/dist/discord-auto-moderation-service.js +425 -0
  26. package/dist/discord-guild-health.d.ts +2 -2
  27. package/dist/discord-member-enforcement-service.d.ts +26 -0
  28. package/dist/discord-member-enforcement-service.js +590 -0
  29. package/dist/discord-message-enforcement-service.d.ts +9 -0
  30. package/dist/discord-message-enforcement-service.js +102 -0
  31. package/dist/discord-violation-thread-service.d.ts +33 -0
  32. package/dist/discord-violation-thread-service.js +789 -0
  33. package/dist/ecko-evidence-receiver.d.ts +32 -0
  34. package/dist/ecko-evidence-receiver.js +173 -0
  35. package/dist/ecko-transport-auth.d.ts +19 -0
  36. package/dist/ecko-transport-auth.js +80 -0
  37. package/dist/ecko-transport-routes.d.ts +19 -0
  38. package/dist/ecko-transport-routes.js +130 -0
  39. package/dist/embedded-auto-moderation-pool.d.ts +46 -0
  40. package/dist/embedded-auto-moderation-pool.js +324 -0
  41. package/dist/embedded-auto-moderation-scanner.d.ts +34 -0
  42. package/dist/embedded-auto-moderation-scanner.js +263 -0
  43. package/dist/embedded-auto-moderation-worker-runtime.d.ts +37 -0
  44. package/dist/embedded-auto-moderation-worker-runtime.js +37 -0
  45. package/dist/embedded-auto-moderation-worker.d.ts +2 -0
  46. package/dist/embedded-auto-moderation-worker.js +13 -0
  47. package/dist/events.d.ts +5 -1
  48. package/dist/events.js +243 -4
  49. package/dist/guilds.d.ts +2 -2
  50. package/dist/guilds.js +14 -8
  51. package/dist/hosted-server-moderation.d.ts +16 -5
  52. package/dist/hosted-server-moderation.js +32 -4
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +84 -14
  55. package/dist/moderation-action-registry.d.ts +11 -0
  56. package/dist/moderation-action-registry.js +375 -0
  57. package/dist/moderation-case-disassociation.d.ts +13 -0
  58. package/dist/moderation-case-disassociation.js +83 -0
  59. package/dist/moderation-platform-services.d.ts +27 -0
  60. package/dist/moderation-platform-services.js +50 -0
  61. package/dist/modules.d.ts +6 -0
  62. package/dist/modules.js +65 -3
  63. package/dist/privileged-message-metadata-cache.d.ts +35 -0
  64. package/dist/privileged-message-metadata-cache.js +70 -0
  65. package/dist/scheduled-task-runner.js +24 -1
  66. package/dist/shard-contracts.d.ts +151 -1
  67. package/dist/shard-contracts.js +142 -1
  68. package/dist/shard-coordinator.d.ts +1 -1
  69. package/dist/shard-ecko-scanner-registry.d.ts +38 -0
  70. package/dist/shard-ecko-scanner-registry.js +96 -0
  71. package/dist/shard-router.d.ts +23 -0
  72. package/dist/shard-router.js +69 -5
  73. package/dist/shard-supervisor.d.ts +11 -0
  74. package/dist/shard-supervisor.js +67 -4
  75. package/dist/shard-worker.d.ts +15 -1
  76. package/dist/shard-worker.js +95 -17
  77. package/dist/transient-violation-evidence.d.ts +39 -0
  78. package/dist/transient-violation-evidence.js +132 -0
  79. package/documentation/README.md +2 -0
  80. package/documentation/examples/.env.example +12 -0
  81. package/documentation/guides/self-hosting.md +17 -1
  82. package/documentation/modules/auto-moderation.md +51 -0
  83. package/documentation/modules/moderation.md +42 -0
  84. package/documentation/reference/environment.md +11 -0
  85. package/package.json +17 -5
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import type { ManagedResourceValue } from "@helyx/sdk";
2
+ import { type ManagedResourceValue } from "@helyx/sdk";
3
3
  import type { BotControlHandler } from "./control-server.js";
4
4
  export declare const SHARD_CONTRACT_VERSION: 1;
5
5
  export declare const SHARD_CONTROL_MAX_BYTES: number;
@@ -167,6 +167,31 @@ export declare const shardHealthSnapshotSchema: z.ZodObject<{
167
167
  }, z.core.$strict>;
168
168
  export type ShardHealthSnapshot = z.infer<typeof shardHealthSnapshotSchema>;
169
169
  export declare const shardControlOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
170
+ kind: z.ZodLiteral<"automod.enhanced.configuration.page">;
171
+ cursor: z.ZodOptional<z.ZodString>;
172
+ limit: z.ZodNumber;
173
+ knownFingerprint: z.ZodOptional<z.ZodString>;
174
+ }, z.core.$strict>, z.ZodObject<{
175
+ kind: z.ZodLiteral<"automod.enhanced.result.accept">;
176
+ guildId: z.ZodString;
177
+ result: z.ZodUnknown;
178
+ evidence: z.ZodOptional<z.ZodObject<{
179
+ sourceEventId: z.ZodString;
180
+ configurationRevision: z.ZodString;
181
+ guildId: z.ZodString;
182
+ channelId: z.ZodString;
183
+ messageId: z.ZodString;
184
+ authorUserId: z.ZodString;
185
+ content: z.ZodString;
186
+ }, z.core.$strict>>;
187
+ }, z.core.$strict>, z.ZodObject<{
188
+ kind: z.ZodLiteral<"hosted-server.leave.deliver">;
189
+ guildId: z.ZodString;
190
+ operationId: z.ZodUUID;
191
+ }, z.core.$strict>, z.ZodObject<{
192
+ kind: z.ZodLiteral<"hosted-server.owner.refresh">;
193
+ guildId: z.ZodString;
194
+ }, z.core.$strict>, z.ZodObject<{
170
195
  kind: z.ZodLiteral<"guild.state.get">;
171
196
  guildId: z.ZodString;
172
197
  }, z.core.$strict>, z.ZodObject<{
@@ -224,6 +249,14 @@ export declare const shardControlOperationSchema: z.ZodDiscriminatedUnion<[z.Zod
224
249
  moduleId: z.ZodString;
225
250
  value: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNull, z.ZodString, z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodString>]>>;
226
251
  expectedVersion: z.ZodNullable<z.ZodNumber>;
252
+ }, z.core.$strict>, z.ZodObject<{
253
+ actorUserId: z.ZodString;
254
+ correlationId: z.ZodString;
255
+ kind: z.ZodLiteral<"guild.module.configuration.preview">;
256
+ guildId: z.ZodString;
257
+ moduleId: z.ZodString;
258
+ value: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNull, z.ZodString, z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodString>]>>;
259
+ input: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
227
260
  }, z.core.$strict>, z.ZodObject<{
228
261
  actorUserId: z.ZodString;
229
262
  correlationId: z.ZodString;
@@ -274,6 +307,14 @@ export declare const shardControlOperationSchema: z.ZodDiscriminatedUnion<[z.Zod
274
307
  guildId: z.ZodString;
275
308
  moduleId: z.ZodString;
276
309
  actionId: z.ZodString;
310
+ inspectionRevision: z.ZodOptional<z.ZodString>;
311
+ }, z.core.$strict>, z.ZodObject<{
312
+ actorUserId: z.ZodString;
313
+ correlationId: z.ZodString;
314
+ kind: z.ZodLiteral<"guild.module.action.preview">;
315
+ guildId: z.ZodString;
316
+ moduleId: z.ZodString;
317
+ actionId: z.ZodString;
277
318
  }, z.core.$strict>, z.ZodObject<{
278
319
  kind: z.ZodLiteral<"guild.module.resource.access">;
279
320
  guildId: z.ZodString;
@@ -324,6 +365,15 @@ export declare const shardControlOperationSchema: z.ZodDiscriminatedUnion<[z.Zod
324
365
  moduleId: z.ZodString;
325
366
  resourceType: z.ZodString;
326
367
  value: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
368
+ }, z.core.$strict>, z.ZodObject<{
369
+ actorUserId: z.ZodString;
370
+ correlationId: z.ZodString;
371
+ kind: z.ZodLiteral<"guild.module.resource.import">;
372
+ guildId: z.ZodString;
373
+ moduleId: z.ZodString;
374
+ resourceType: z.ZodString;
375
+ format: z.ZodLiteral<"utf8_text">;
376
+ content: z.ZodString;
327
377
  }, z.core.$strict>, z.ZodObject<{
328
378
  actorUserId: z.ZodString;
329
379
  correlationId: z.ZodString;
@@ -528,6 +578,31 @@ export declare const shardControlRequestSchema: z.ZodObject<{
528
578
  fencingToken: z.ZodNumber;
529
579
  deadlineAt: z.ZodISODateTime;
530
580
  operation: z.ZodDiscriminatedUnion<[z.ZodObject<{
581
+ kind: z.ZodLiteral<"automod.enhanced.configuration.page">;
582
+ cursor: z.ZodOptional<z.ZodString>;
583
+ limit: z.ZodNumber;
584
+ knownFingerprint: z.ZodOptional<z.ZodString>;
585
+ }, z.core.$strict>, z.ZodObject<{
586
+ kind: z.ZodLiteral<"automod.enhanced.result.accept">;
587
+ guildId: z.ZodString;
588
+ result: z.ZodUnknown;
589
+ evidence: z.ZodOptional<z.ZodObject<{
590
+ sourceEventId: z.ZodString;
591
+ configurationRevision: z.ZodString;
592
+ guildId: z.ZodString;
593
+ channelId: z.ZodString;
594
+ messageId: z.ZodString;
595
+ authorUserId: z.ZodString;
596
+ content: z.ZodString;
597
+ }, z.core.$strict>>;
598
+ }, z.core.$strict>, z.ZodObject<{
599
+ kind: z.ZodLiteral<"hosted-server.leave.deliver">;
600
+ guildId: z.ZodString;
601
+ operationId: z.ZodUUID;
602
+ }, z.core.$strict>, z.ZodObject<{
603
+ kind: z.ZodLiteral<"hosted-server.owner.refresh">;
604
+ guildId: z.ZodString;
605
+ }, z.core.$strict>, z.ZodObject<{
531
606
  kind: z.ZodLiteral<"guild.state.get">;
532
607
  guildId: z.ZodString;
533
608
  }, z.core.$strict>, z.ZodObject<{
@@ -585,6 +660,14 @@ export declare const shardControlRequestSchema: z.ZodObject<{
585
660
  moduleId: z.ZodString;
586
661
  value: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNull, z.ZodString, z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodString>]>>;
587
662
  expectedVersion: z.ZodNullable<z.ZodNumber>;
663
+ }, z.core.$strict>, z.ZodObject<{
664
+ actorUserId: z.ZodString;
665
+ correlationId: z.ZodString;
666
+ kind: z.ZodLiteral<"guild.module.configuration.preview">;
667
+ guildId: z.ZodString;
668
+ moduleId: z.ZodString;
669
+ value: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNull, z.ZodString, z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodString>]>>;
670
+ input: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
588
671
  }, z.core.$strict>, z.ZodObject<{
589
672
  actorUserId: z.ZodString;
590
673
  correlationId: z.ZodString;
@@ -635,6 +718,14 @@ export declare const shardControlRequestSchema: z.ZodObject<{
635
718
  guildId: z.ZodString;
636
719
  moduleId: z.ZodString;
637
720
  actionId: z.ZodString;
721
+ inspectionRevision: z.ZodOptional<z.ZodString>;
722
+ }, z.core.$strict>, z.ZodObject<{
723
+ actorUserId: z.ZodString;
724
+ correlationId: z.ZodString;
725
+ kind: z.ZodLiteral<"guild.module.action.preview">;
726
+ guildId: z.ZodString;
727
+ moduleId: z.ZodString;
728
+ actionId: z.ZodString;
638
729
  }, z.core.$strict>, z.ZodObject<{
639
730
  kind: z.ZodLiteral<"guild.module.resource.access">;
640
731
  guildId: z.ZodString;
@@ -685,6 +776,15 @@ export declare const shardControlRequestSchema: z.ZodObject<{
685
776
  moduleId: z.ZodString;
686
777
  resourceType: z.ZodString;
687
778
  value: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
779
+ }, z.core.$strict>, z.ZodObject<{
780
+ actorUserId: z.ZodString;
781
+ correlationId: z.ZodString;
782
+ kind: z.ZodLiteral<"guild.module.resource.import">;
783
+ guildId: z.ZodString;
784
+ moduleId: z.ZodString;
785
+ resourceType: z.ZodString;
786
+ format: z.ZodLiteral<"utf8_text">;
787
+ content: z.ZodString;
688
788
  }, z.core.$strict>, z.ZodObject<{
689
789
  actorUserId: z.ZodString;
690
790
  correlationId: z.ZodString;
@@ -1394,6 +1494,31 @@ export declare const supervisorMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObje
1394
1494
  fencingToken: z.ZodNumber;
1395
1495
  deadlineAt: z.ZodISODateTime;
1396
1496
  operation: z.ZodDiscriminatedUnion<[z.ZodObject<{
1497
+ kind: z.ZodLiteral<"automod.enhanced.configuration.page">;
1498
+ cursor: z.ZodOptional<z.ZodString>;
1499
+ limit: z.ZodNumber;
1500
+ knownFingerprint: z.ZodOptional<z.ZodString>;
1501
+ }, z.core.$strict>, z.ZodObject<{
1502
+ kind: z.ZodLiteral<"automod.enhanced.result.accept">;
1503
+ guildId: z.ZodString;
1504
+ result: z.ZodUnknown;
1505
+ evidence: z.ZodOptional<z.ZodObject<{
1506
+ sourceEventId: z.ZodString;
1507
+ configurationRevision: z.ZodString;
1508
+ guildId: z.ZodString;
1509
+ channelId: z.ZodString;
1510
+ messageId: z.ZodString;
1511
+ authorUserId: z.ZodString;
1512
+ content: z.ZodString;
1513
+ }, z.core.$strict>>;
1514
+ }, z.core.$strict>, z.ZodObject<{
1515
+ kind: z.ZodLiteral<"hosted-server.leave.deliver">;
1516
+ guildId: z.ZodString;
1517
+ operationId: z.ZodUUID;
1518
+ }, z.core.$strict>, z.ZodObject<{
1519
+ kind: z.ZodLiteral<"hosted-server.owner.refresh">;
1520
+ guildId: z.ZodString;
1521
+ }, z.core.$strict>, z.ZodObject<{
1397
1522
  kind: z.ZodLiteral<"guild.state.get">;
1398
1523
  guildId: z.ZodString;
1399
1524
  }, z.core.$strict>, z.ZodObject<{
@@ -1451,6 +1576,14 @@ export declare const supervisorMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObje
1451
1576
  moduleId: z.ZodString;
1452
1577
  value: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNull, z.ZodString, z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodString>]>>;
1453
1578
  expectedVersion: z.ZodNullable<z.ZodNumber>;
1579
+ }, z.core.$strict>, z.ZodObject<{
1580
+ actorUserId: z.ZodString;
1581
+ correlationId: z.ZodString;
1582
+ kind: z.ZodLiteral<"guild.module.configuration.preview">;
1583
+ guildId: z.ZodString;
1584
+ moduleId: z.ZodString;
1585
+ value: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNull, z.ZodString, z.ZodBoolean, z.ZodNumber, z.ZodArray<z.ZodString>]>>;
1586
+ input: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
1454
1587
  }, z.core.$strict>, z.ZodObject<{
1455
1588
  actorUserId: z.ZodString;
1456
1589
  correlationId: z.ZodString;
@@ -1501,6 +1634,14 @@ export declare const supervisorMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObje
1501
1634
  guildId: z.ZodString;
1502
1635
  moduleId: z.ZodString;
1503
1636
  actionId: z.ZodString;
1637
+ inspectionRevision: z.ZodOptional<z.ZodString>;
1638
+ }, z.core.$strict>, z.ZodObject<{
1639
+ actorUserId: z.ZodString;
1640
+ correlationId: z.ZodString;
1641
+ kind: z.ZodLiteral<"guild.module.action.preview">;
1642
+ guildId: z.ZodString;
1643
+ moduleId: z.ZodString;
1644
+ actionId: z.ZodString;
1504
1645
  }, z.core.$strict>, z.ZodObject<{
1505
1646
  kind: z.ZodLiteral<"guild.module.resource.access">;
1506
1647
  guildId: z.ZodString;
@@ -1551,6 +1692,15 @@ export declare const supervisorMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObje
1551
1692
  moduleId: z.ZodString;
1552
1693
  resourceType: z.ZodString;
1553
1694
  value: z.ZodRecord<z.ZodString, z.ZodCustom<ManagedResourceValue, ManagedResourceValue>>;
1695
+ }, z.core.$strict>, z.ZodObject<{
1696
+ actorUserId: z.ZodString;
1697
+ correlationId: z.ZodString;
1698
+ kind: z.ZodLiteral<"guild.module.resource.import">;
1699
+ guildId: z.ZodString;
1700
+ moduleId: z.ZodString;
1701
+ resourceType: z.ZodString;
1702
+ format: z.ZodLiteral<"utf8_text">;
1703
+ content: z.ZodString;
1554
1704
  }, z.core.$strict>, z.ZodObject<{
1555
1705
  actorUserId: z.ZodString;
1556
1706
  correlationId: z.ZodString;
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ import { CLOUD_MAX_RESOURCE_IMPORT_BYTES } from "@helyx/control-protocol";
3
+ import { AUTOMOD_SCANNER_LIMITS } from "@helyx/sdk";
2
4
  import { signedAssessmentEnvelopeSchema } from "@helyx/verification-contracts";
3
5
  export const SHARD_CONTRACT_VERSION = 1;
4
6
  // Guild dashboard state includes the bounded module catalogue as well as the
@@ -9,8 +11,30 @@ export const SHARD_CONTROL_TIMEOUT_MS = 30_000;
9
11
  export const SHARD_HEALTH_STALE_MS = 30_000;
10
12
  export const SHARD_LEASE_HEARTBEAT_TIMEOUT_MS = 20_000;
11
13
  const snowflake = z.string().regex(/^\d{17,20}$/u);
14
+ const operationId = z.uuid();
12
15
  const safeIdentifier = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u);
13
16
  const dateTime = z.iso.datetime({ offset: true });
17
+ const boundedEnhancedResult = z
18
+ .unknown()
19
+ .refine((value) => value !== null &&
20
+ typeof value === "object" &&
21
+ boundedJsonBytes(value, AUTOMOD_SCANNER_LIMITS.resultBytes), "Enhanced Auto Moderation result exceeds the transport limit");
22
+ const enhancedEvidence = z
23
+ .object({
24
+ sourceEventId: z.string().regex(/^[a-f0-9]{64}$/u),
25
+ configurationRevision: z.string().min(1).max(160),
26
+ guildId: snowflake,
27
+ channelId: snowflake,
28
+ messageId: snowflake,
29
+ authorUserId: snowflake,
30
+ content: z
31
+ .string()
32
+ .min(1)
33
+ .max(AUTOMOD_SCANNER_LIMITS.messageCharacters)
34
+ .refine((value) => Buffer.byteLength(value, "utf8") <=
35
+ AUTOMOD_SCANNER_LIMITS.messageUtf8Bytes, "Enhanced Auto Moderation evidence exceeds the transport limit"),
36
+ })
37
+ .strict();
14
38
  const moduleId = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/u);
15
39
  const mutationContext = z
16
40
  .object({
@@ -230,6 +254,39 @@ export const shardHealthSnapshotSchema = z
230
254
  });
231
255
  });
232
256
  export const shardControlOperationSchema = z.discriminatedUnion("kind", [
257
+ z
258
+ .object({
259
+ kind: z.literal("automod.enhanced.configuration.page"),
260
+ cursor: z.string().min(1).max(512).optional(),
261
+ limit: z
262
+ .number()
263
+ .int()
264
+ .min(1)
265
+ .max(AUTOMOD_SCANNER_LIMITS.configurationPageServers),
266
+ knownFingerprint: z.string().min(1).max(128).optional(),
267
+ })
268
+ .strict(),
269
+ z
270
+ .object({
271
+ kind: z.literal("automod.enhanced.result.accept"),
272
+ guildId: snowflake,
273
+ result: boundedEnhancedResult,
274
+ evidence: enhancedEvidence.optional(),
275
+ })
276
+ .strict(),
277
+ z
278
+ .object({
279
+ kind: z.literal("hosted-server.leave.deliver"),
280
+ guildId: snowflake,
281
+ operationId,
282
+ })
283
+ .strict(),
284
+ z
285
+ .object({
286
+ kind: z.literal("hosted-server.owner.refresh"),
287
+ guildId: snowflake,
288
+ })
289
+ .strict(),
233
290
  z.object({ kind: z.literal("guild.state.get"), guildId: snowflake }).strict(),
234
291
  z
235
292
  .object({
@@ -300,6 +357,16 @@ export const shardControlOperationSchema = z.discriminatedUnion("kind", [
300
357
  ...mutationContext.shape,
301
358
  })
302
359
  .strict(),
360
+ z
361
+ .object({
362
+ kind: z.literal("guild.module.configuration.preview"),
363
+ guildId: snowflake,
364
+ moduleId,
365
+ value: configurationValue,
366
+ input: managedResourceObject,
367
+ ...mutationContext.shape,
368
+ })
369
+ .strict(),
303
370
  z
304
371
  .object({
305
372
  kind: z.literal("guild.module.permissions.update"),
@@ -326,6 +393,16 @@ export const shardControlOperationSchema = z.discriminatedUnion("kind", [
326
393
  guildId: snowflake,
327
394
  moduleId,
328
395
  actionId: z.string().regex(/^[a-z][a-z0-9_-]*$/u),
396
+ inspectionRevision: z.string().min(1).max(200).optional(),
397
+ ...mutationContext.shape,
398
+ })
399
+ .strict(),
400
+ z
401
+ .object({
402
+ kind: z.literal("guild.module.action.preview"),
403
+ guildId: snowflake,
404
+ moduleId,
405
+ actionId: z.string().regex(/^[a-z][a-z0-9_-]*$/u),
329
406
  ...mutationContext.shape,
330
407
  })
331
408
  .strict(),
@@ -387,6 +464,19 @@ export const shardControlOperationSchema = z.discriminatedUnion("kind", [
387
464
  ...mutationContext.shape,
388
465
  })
389
466
  .strict(),
467
+ z
468
+ .object({
469
+ kind: z.literal("guild.module.resource.import"),
470
+ guildId: snowflake,
471
+ moduleId,
472
+ resourceType,
473
+ format: z.literal("utf8_text"),
474
+ content: z
475
+ .string()
476
+ .refine((value) => Buffer.byteLength(value, "utf8") <= CLOUD_MAX_RESOURCE_IMPORT_BYTES, "Managed resource import exceeds the transport limit"),
477
+ ...mutationContext.shape,
478
+ })
479
+ .strict(),
390
480
  z
391
481
  .object({
392
482
  kind: z.literal("guild.module.resource.create"),
@@ -962,6 +1052,13 @@ export function assertShardMessageSize(value) {
962
1052
  }
963
1053
  export async function executeShardControlOperation(handler, operation) {
964
1054
  switch (operation.kind) {
1055
+ case "automod.enhanced.configuration.page":
1056
+ throw new Error("Enhanced Auto Moderation configuration requires the shard leader");
1057
+ case "automod.enhanced.result.accept":
1058
+ throw new Error("Enhanced Auto Moderation result delivery requires the shard worker");
1059
+ case "hosted-server.leave.deliver":
1060
+ case "hosted-server.owner.refresh":
1061
+ throw new Error("Hosted leave delivery requires the shard worker");
965
1062
  case "guild.presence.list":
966
1063
  return handler.listGuildPresence({
967
1064
  guildIds: operation.guildIds,
@@ -1022,6 +1119,15 @@ export async function executeShardControlOperation(handler, operation) {
1022
1119
  correlationId: operation.correlationId,
1023
1120
  });
1024
1121
  }
1122
+ case "guild.module.configuration.preview":
1123
+ return handler.previewConfiguration({
1124
+ guildId: operation.guildId,
1125
+ moduleId: operation.moduleId,
1126
+ value: operation.value,
1127
+ input: operation.input,
1128
+ actorUserId: operation.actorUserId,
1129
+ correlationId: operation.correlationId,
1130
+ });
1025
1131
  case "guild.module.permissions.update": {
1026
1132
  return handler.updatePermissions({
1027
1133
  guildId: operation.guildId,
@@ -1044,6 +1150,18 @@ export async function executeShardControlOperation(handler, operation) {
1044
1150
  }
1045
1151
  case "guild.module.action.execute": {
1046
1152
  return handler.executeDashboardAction({
1153
+ guildId: operation.guildId,
1154
+ moduleId: operation.moduleId,
1155
+ actionId: operation.actionId,
1156
+ ...(operation.inspectionRevision === undefined
1157
+ ? {}
1158
+ : { inspectionRevision: operation.inspectionRevision }),
1159
+ actorUserId: operation.actorUserId,
1160
+ correlationId: operation.correlationId,
1161
+ });
1162
+ }
1163
+ case "guild.module.action.preview": {
1164
+ return handler.previewDashboardAction({
1047
1165
  guildId: operation.guildId,
1048
1166
  moduleId: operation.moduleId,
1049
1167
  actionId: operation.actionId,
@@ -1099,6 +1217,16 @@ export async function executeShardControlOperation(handler, operation) {
1099
1217
  actorUserId: operation.actorUserId,
1100
1218
  correlationId: operation.correlationId,
1101
1219
  });
1220
+ case "guild.module.resource.import":
1221
+ return handler.importManagedResource({
1222
+ guildId: operation.guildId,
1223
+ moduleId: operation.moduleId,
1224
+ resourceType: operation.resourceType,
1225
+ format: operation.format,
1226
+ content: operation.content,
1227
+ actorUserId: operation.actorUserId,
1228
+ correlationId: operation.correlationId,
1229
+ });
1102
1230
  case "guild.module.resource.create":
1103
1231
  return handler.createManagedResource({
1104
1232
  guildId: operation.guildId,
@@ -1158,7 +1286,7 @@ export async function executeShardControlOperation(handler, operation) {
1158
1286
  correlationId: operation.correlationId,
1159
1287
  });
1160
1288
  case "support.notification.send": {
1161
- return handler.sendSupportNotification({
1289
+ await handler.sendSupportNotification({
1162
1290
  notificationId: operation.notificationId,
1163
1291
  reference: operation.reference,
1164
1292
  caseType: operation.caseType,
@@ -1166,6 +1294,11 @@ export async function executeShardControlOperation(handler, operation) {
1166
1294
  reportedSeverity: operation.reportedSeverity,
1167
1295
  source: operation.source,
1168
1296
  });
1297
+ // Child-process IPC uses JSON serialization. Returning the handler's void
1298
+ // result would remove the required `result` property from the success
1299
+ // envelope, causing the supervisor to discard a successful Discord send
1300
+ // and retry it after a timeout.
1301
+ return null;
1169
1302
  }
1170
1303
  case "verification.result.accept":
1171
1304
  return handler.acceptVerificationResult(operation.envelope);
@@ -1221,4 +1354,12 @@ function isBoundedManagedResourceValue(value, depth = 0, budget = { keys: 0 }) {
1221
1354
  function hasBoundedManagedResourceBytes(value) {
1222
1355
  return (new TextEncoder().encode(JSON.stringify(value)).byteLength <= 48 * 1_024);
1223
1356
  }
1357
+ function boundedJsonBytes(value, maximumBytes) {
1358
+ try {
1359
+ return Buffer.byteLength(JSON.stringify(value), "utf8") <= maximumBytes;
1360
+ }
1361
+ catch {
1362
+ return false;
1363
+ }
1364
+ }
1224
1365
  //# sourceMappingURL=shard-contracts.js.map
@@ -5,9 +5,9 @@ declare const generationDescriptorSchema: z.ZodObject<{
5
5
  version: z.ZodLiteral<1>;
6
6
  applicationId: z.ZodString;
7
7
  environment: z.ZodEnum<{
8
+ production: "production";
8
9
  development: "development";
9
10
  test: "test";
10
- production: "production";
11
11
  }>;
12
12
  generation: z.ZodString;
13
13
  shardCount: z.ZodNumber;
@@ -0,0 +1,38 @@
1
+ import { type AutoModerationEnhancedResultOutcome, type AutoModerationScannerAdapterSelection, type AutoModerationScannerProviderRegistryService } from "@helyx/sdk";
2
+ import type { TransientViolationEvidenceStore } from "./transient-violation-evidence.js";
3
+ export interface ShardEckoResultRoute {
4
+ guildId: string;
5
+ result: unknown;
6
+ evidence?: {
7
+ sourceEventId: string;
8
+ configurationRevision: string;
9
+ guildId: string;
10
+ channelId: string;
11
+ messageId: string;
12
+ authorUserId: string;
13
+ content: string;
14
+ };
15
+ }
16
+ /**
17
+ * Supervisor-only Ecko facade. Configuration is read from the fenced leader's
18
+ * allowlisted provider. Results and one-use evidence are routed to the guild
19
+ * owner, where the worker registry performs all validation and actions.
20
+ */
21
+ export declare class ShardEckoScannerRegistry implements AutoModerationScannerProviderRegistryService {
22
+ #private;
23
+ private readonly input;
24
+ constructor(input: {
25
+ configurationRegistry(): AutoModerationScannerProviderRegistryService | undefined;
26
+ routeConfiguration?(input: {
27
+ cursor?: string;
28
+ limit: number;
29
+ knownFingerprint?: string;
30
+ }): ReturnType<AutoModerationScannerProviderRegistryService["getConfigurationPage"]>;
31
+ evidenceStore: TransientViolationEvidenceStore;
32
+ routeResult(input: ShardEckoResultRoute): Promise<AutoModerationEnhancedResultOutcome>;
33
+ });
34
+ select(): Promise<AutoModerationScannerAdapterSelection>;
35
+ getConfigurationPage(input: Parameters<AutoModerationScannerProviderRegistryService["getConfigurationPage"]>[0]): Promise<import("@helyx/sdk").AutoModerationScannerConfigurationPageV1>;
36
+ acceptResult(input: Parameters<AutoModerationScannerProviderRegistryService["acceptResult"]>[0]): Promise<AutoModerationEnhancedResultOutcome>;
37
+ }
38
+ //# sourceMappingURL=shard-ecko-scanner-registry.d.ts.map
@@ -0,0 +1,96 @@
1
+ import { AUTOMOD_ENHANCED_CONTRACT_VERSION, AUTOMOD_ENHANCED_PROVIDER_ID, } from "@helyx/sdk";
2
+ /**
3
+ * Supervisor-only Ecko facade. Configuration is read from the fenced leader's
4
+ * allowlisted provider. Results and one-use evidence are routed to the guild
5
+ * owner, where the worker registry performs all validation and actions.
6
+ */
7
+ export class ShardEckoScannerRegistry {
8
+ input;
9
+ constructor(input) {
10
+ this.input = input;
11
+ }
12
+ select() {
13
+ return Promise.resolve({
14
+ adapter: "unavailable",
15
+ available: false,
16
+ reason: "dependency_unhealthy",
17
+ });
18
+ }
19
+ async getConfigurationPage(input) {
20
+ const registry = this.input.configurationRegistry();
21
+ if (registry)
22
+ return registry.getConfigurationPage(input);
23
+ if (!this.input.routeConfiguration)
24
+ throw new Error("Ecko configuration leader is unavailable");
25
+ return this.input.routeConfiguration({
26
+ limit: input.limit,
27
+ ...(input.cursor ? { cursor: input.cursor } : {}),
28
+ ...(input.knownFingerprint
29
+ ? { knownFingerprint: input.knownFingerprint }
30
+ : {}),
31
+ });
32
+ }
33
+ async acceptResult(input) {
34
+ if (input.adapter !== "hosted_ecko" ||
35
+ input.providerId !== AUTOMOD_ENHANCED_PROVIDER_ID ||
36
+ input.contractVersion !== AUTOMOD_ENHANCED_CONTRACT_VERSION) {
37
+ this.#discard(input.evidenceReference);
38
+ return rejected("wrong_owner");
39
+ }
40
+ const guildId = resultGuildId(input.result);
41
+ if (!guildId) {
42
+ this.#discard(input.evidenceReference);
43
+ return rejected("invalid_envelope");
44
+ }
45
+ const evidence = input.evidenceReference
46
+ ? this.#takeEvidence(input.evidenceReference)
47
+ : undefined;
48
+ try {
49
+ return await this.input.routeResult({
50
+ guildId,
51
+ result: input.result,
52
+ ...(evidence ? { evidence } : {}),
53
+ });
54
+ }
55
+ catch (error) {
56
+ return rejected(isDeadlineError(error) ? "stale" : "wrong_owner");
57
+ }
58
+ }
59
+ #takeEvidence(reference) {
60
+ const read = this.input.evidenceStore.consume(reference);
61
+ if (read.outcome !== "available" ||
62
+ reference.source !== "enhanced_scanner" ||
63
+ reference.messageId === null)
64
+ return undefined;
65
+ return {
66
+ sourceEventId: reference.sourceEventId,
67
+ configurationRevision: reference.configurationRevision,
68
+ guildId: reference.guildId,
69
+ channelId: reference.channelId,
70
+ messageId: reference.messageId,
71
+ authorUserId: reference.authorUserId,
72
+ content: read.content,
73
+ };
74
+ }
75
+ #discard(reference) {
76
+ if (reference)
77
+ this.input.evidenceStore.discard(reference);
78
+ }
79
+ }
80
+ function resultGuildId(value) {
81
+ if (!value || typeof value !== "object")
82
+ return undefined;
83
+ const guildId = value["guildId"];
84
+ return typeof guildId === "string" && /^\d{17,20}$/u.test(guildId)
85
+ ? guildId
86
+ : undefined;
87
+ }
88
+ function isDeadlineError(error) {
89
+ return (error instanceof Error &&
90
+ (error.message.toLowerCase().includes("time") ||
91
+ error.message.toLowerCase().includes("expired")));
92
+ }
93
+ function rejected(code) {
94
+ return { outcome: "rejected", code };
95
+ }
96
+ //# sourceMappingURL=shard-ecko-scanner-registry.js.map
@@ -1,6 +1,9 @@
1
+ import { type AutoModerationEnhancedResultOutcome } from "@helyx/sdk";
1
2
  import { type BotControlHandler } from "./control-server.js";
2
3
  import { type ShardControlRequest, type ShardControlResponse } from "./shard-contracts.js";
3
4
  import type { ShardCoordinator, ShardLeaderLease, ShardLease } from "./shard-coordinator.js";
5
+ import type { ShardEckoResultRoute } from "./shard-ecko-scanner-registry.js";
6
+ import type { AutoModerationScannerProviderRegistryService } from "@helyx/sdk";
4
7
  type LocalDispatcher = (shardId: number, request: ShardControlRequest) => Promise<ShardControlResponse>;
5
8
  export declare class ShardControlRouter implements BotControlHandler {
6
9
  #private;
@@ -11,13 +14,30 @@ export declare class ShardControlRouter implements BotControlHandler {
11
14
  localLeases: ReadonlyMap<number, ShardLease>;
12
15
  dispatchLocal: LocalDispatcher;
13
16
  privacy?: Pick<BotControlHandler, "listPrivacyProviders" | "capturePrivacy" | "erasePrivacy">;
17
+ leaderScannerRegistry?: AutoModerationScannerProviderRegistryService;
14
18
  availability?: Pick<BotControlHandler, "listAvailabilityModules">;
15
19
  localLeaderLease?: ShardLeaderLease;
16
20
  coordinator?: ShardCoordinator;
17
21
  notificationGuildId?: string;
18
22
  });
19
23
  cleanupOAuthConsumer(input: Parameters<BotControlHandler["cleanupOAuthConsumer"]>[0]): ReturnType<BotControlHandler["cleanupOAuthConsumer"]>;
24
+ deliverHostedServerLeave(input: {
25
+ operationId: string;
26
+ guildId: string;
27
+ }): Promise<"queued" | "completed" | "not_currently_installed" | "accepted_for_retry" | "requires_review">;
28
+ refreshHostedServerOwner(guildId: string): Promise<{
29
+ username: string;
30
+ observedAt: string;
31
+ }>;
20
32
  listAvailabilityModules(): ReturnType<BotControlHandler["listAvailabilityModules"]>;
33
+ /** Routes an authenticated Ecko result to the current guild owner. */
34
+ acceptHostedEckoResult(input: ShardEckoResultRoute): Promise<AutoModerationEnhancedResultOutcome>;
35
+ /** Reads the fenced leader's current Enhanced configuration snapshot. */
36
+ getHostedEckoConfigurationPage(input: {
37
+ cursor?: string;
38
+ limit: number;
39
+ knownFingerprint?: string;
40
+ }): ReturnType<AutoModerationScannerProviderRegistryService["getConfigurationPage"]>;
21
41
  getGuildState(guildId: string): ReturnType<BotControlHandler["getGuildState"]>;
22
42
  canAccessDashboard(input: Parameters<BotControlHandler["canAccessDashboard"]>[0]): ReturnType<BotControlHandler["canAccessDashboard"]>;
23
43
  getGuildAccessPolicy(input: Parameters<BotControlHandler["getGuildAccessPolicy"]>[0]): ReturnType<BotControlHandler["getGuildAccessPolicy"]>;
@@ -28,13 +48,16 @@ export declare class ShardControlRouter implements BotControlHandler {
28
48
  setModuleStatus(input: Parameters<BotControlHandler["setModuleStatus"]>[0]): ReturnType<BotControlHandler["setModuleStatus"]>;
29
49
  completeFirstTimeCommandSetup(input: Parameters<BotControlHandler["completeFirstTimeCommandSetup"]>[0]): ReturnType<BotControlHandler["completeFirstTimeCommandSetup"]>;
30
50
  updateConfiguration(input: Parameters<BotControlHandler["updateConfiguration"]>[0]): ReturnType<BotControlHandler["updateConfiguration"]>;
51
+ previewConfiguration(input: Parameters<BotControlHandler["previewConfiguration"]>[0]): ReturnType<BotControlHandler["previewConfiguration"]>;
31
52
  updatePermissions(input: Parameters<BotControlHandler["updatePermissions"]>[0]): ReturnType<BotControlHandler["updatePermissions"]>;
32
53
  updateLoggingPolicy(input: Parameters<BotControlHandler["updateLoggingPolicy"]>[0]): ReturnType<BotControlHandler["updateLoggingPolicy"]>;
33
54
  executeDashboardAction(input: Parameters<BotControlHandler["executeDashboardAction"]>[0]): ReturnType<BotControlHandler["executeDashboardAction"]>;
55
+ previewDashboardAction(input: Parameters<BotControlHandler["previewDashboardAction"]>[0]): ReturnType<BotControlHandler["previewDashboardAction"]>;
34
56
  listManagedResources(input: Parameters<BotControlHandler["listManagedResources"]>[0]): ReturnType<BotControlHandler["listManagedResources"]>;
35
57
  readManagedResource(input: Parameters<BotControlHandler["readManagedResource"]>[0]): ReturnType<BotControlHandler["readManagedResource"]>;
36
58
  listManagedResourceRelation(input: Parameters<BotControlHandler["listManagedResourceRelation"]>[0]): ReturnType<BotControlHandler["listManagedResourceRelation"]>;
37
59
  previewManagedResource(input: Parameters<BotControlHandler["previewManagedResource"]>[0]): ReturnType<BotControlHandler["previewManagedResource"]>;
60
+ importManagedResource(input: Parameters<BotControlHandler["importManagedResource"]>[0]): ReturnType<BotControlHandler["importManagedResource"]>;
38
61
  createManagedResource(input: Parameters<BotControlHandler["createManagedResource"]>[0]): ReturnType<BotControlHandler["createManagedResource"]>;
39
62
  updateManagedResource(input: Parameters<BotControlHandler["updateManagedResource"]>[0]): ReturnType<BotControlHandler["updateManagedResource"]>;
40
63
  deleteManagedResource(input: Parameters<BotControlHandler["deleteManagedResource"]>[0]): ReturnType<BotControlHandler["deleteManagedResource"]>;