@elizaos/autonomous 2.0.0-alpha.10

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 (241) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +270 -0
  3. package/src/actions/emote.ts +101 -0
  4. package/src/actions/restart.ts +101 -0
  5. package/src/actions/send-message.ts +168 -0
  6. package/src/actions/stream-control.ts +439 -0
  7. package/src/actions/switch-stream-source.ts +126 -0
  8. package/src/actions/terminal.ts +186 -0
  9. package/src/api/agent-admin-routes.ts +178 -0
  10. package/src/api/agent-lifecycle-routes.ts +129 -0
  11. package/src/api/agent-model.ts +143 -0
  12. package/src/api/agent-transfer-routes.ts +211 -0
  13. package/src/api/apps-routes.ts +210 -0
  14. package/src/api/auth-routes.ts +90 -0
  15. package/src/api/bsc-trade.ts +736 -0
  16. package/src/api/bug-report-routes.ts +161 -0
  17. package/src/api/character-routes.ts +421 -0
  18. package/src/api/cloud-billing-routes.ts +598 -0
  19. package/src/api/cloud-compat-routes.ts +192 -0
  20. package/src/api/cloud-routes.ts +529 -0
  21. package/src/api/cloud-status-routes.ts +234 -0
  22. package/src/api/compat-utils.ts +154 -0
  23. package/src/api/connector-health.ts +135 -0
  24. package/src/api/coordinator-wiring.ts +179 -0
  25. package/src/api/credit-detection.ts +47 -0
  26. package/src/api/database.ts +1357 -0
  27. package/src/api/diagnostics-routes.ts +389 -0
  28. package/src/api/drop-service.ts +205 -0
  29. package/src/api/early-logs.ts +111 -0
  30. package/src/api/http-helpers.ts +252 -0
  31. package/src/api/index.ts +85 -0
  32. package/src/api/knowledge-routes.ts +1189 -0
  33. package/src/api/knowledge-service-loader.ts +92 -0
  34. package/src/api/memory-bounds.ts +121 -0
  35. package/src/api/memory-routes.ts +349 -0
  36. package/src/api/merkle-tree.ts +239 -0
  37. package/src/api/models-routes.ts +72 -0
  38. package/src/api/nfa-routes.ts +169 -0
  39. package/src/api/nft-verify.ts +188 -0
  40. package/src/api/og-tracker.ts +72 -0
  41. package/src/api/parse-action-block.ts +145 -0
  42. package/src/api/permissions-routes.ts +222 -0
  43. package/src/api/plugin-validation.ts +355 -0
  44. package/src/api/provider-switch-config.ts +455 -0
  45. package/src/api/registry-routes.ts +165 -0
  46. package/src/api/registry-service.ts +292 -0
  47. package/src/api/route-helpers.ts +21 -0
  48. package/src/api/sandbox-routes.ts +1480 -0
  49. package/src/api/server.ts +17674 -0
  50. package/src/api/signal-routes.ts +265 -0
  51. package/src/api/stream-persistence.ts +297 -0
  52. package/src/api/stream-route-state.ts +48 -0
  53. package/src/api/stream-routes.ts +1046 -0
  54. package/src/api/stream-voice-routes.ts +208 -0
  55. package/src/api/streaming-text.ts +129 -0
  56. package/src/api/streaming-types.ts +23 -0
  57. package/src/api/subscription-routes.ts +283 -0
  58. package/src/api/terminal-run-limits.ts +31 -0
  59. package/src/api/training-backend-check.ts +40 -0
  60. package/src/api/training-routes.ts +314 -0
  61. package/src/api/training-service-like.ts +46 -0
  62. package/src/api/trajectory-routes.ts +714 -0
  63. package/src/api/trigger-routes.ts +438 -0
  64. package/src/api/twitter-verify.ts +226 -0
  65. package/src/api/tx-service.ts +193 -0
  66. package/src/api/wallet-dex-prices.ts +206 -0
  67. package/src/api/wallet-evm-balance.ts +989 -0
  68. package/src/api/wallet-routes.ts +505 -0
  69. package/src/api/wallet-rpc.ts +523 -0
  70. package/src/api/wallet-trading-profile.ts +694 -0
  71. package/src/api/wallet.ts +745 -0
  72. package/src/api/whatsapp-routes.ts +282 -0
  73. package/src/api/zip-utils.ts +130 -0
  74. package/src/auth/anthropic.ts +63 -0
  75. package/src/auth/apply-stealth.ts +38 -0
  76. package/src/auth/claude-code-stealth.ts +141 -0
  77. package/src/auth/credentials.ts +226 -0
  78. package/src/auth/index.ts +18 -0
  79. package/src/auth/openai-codex.ts +94 -0
  80. package/src/auth/types.ts +24 -0
  81. package/src/awareness/registry.ts +220 -0
  82. package/src/bin.ts +10 -0
  83. package/src/cli/index.ts +36 -0
  84. package/src/cli/parse-duration.ts +43 -0
  85. package/src/cloud/auth.test.ts +370 -0
  86. package/src/cloud/auth.ts +176 -0
  87. package/src/cloud/backup.test.ts +150 -0
  88. package/src/cloud/backup.ts +50 -0
  89. package/src/cloud/base-url.ts +45 -0
  90. package/src/cloud/bridge-client.test.ts +481 -0
  91. package/src/cloud/bridge-client.ts +307 -0
  92. package/src/cloud/cloud-manager.test.ts +223 -0
  93. package/src/cloud/cloud-manager.ts +151 -0
  94. package/src/cloud/cloud-proxy.test.ts +122 -0
  95. package/src/cloud/cloud-proxy.ts +52 -0
  96. package/src/cloud/index.ts +23 -0
  97. package/src/cloud/reconnect.test.ts +178 -0
  98. package/src/cloud/reconnect.ts +108 -0
  99. package/src/cloud/validate-url.test.ts +147 -0
  100. package/src/cloud/validate-url.ts +176 -0
  101. package/src/config/character-schema.ts +44 -0
  102. package/src/config/config.ts +149 -0
  103. package/src/config/env-vars.ts +86 -0
  104. package/src/config/includes.ts +196 -0
  105. package/src/config/index.ts +15 -0
  106. package/src/config/object-utils.ts +10 -0
  107. package/src/config/paths.ts +92 -0
  108. package/src/config/plugin-auto-enable.ts +520 -0
  109. package/src/config/schema.ts +1342 -0
  110. package/src/config/telegram-custom-commands.ts +99 -0
  111. package/src/config/types.agent-defaults.ts +342 -0
  112. package/src/config/types.agents.ts +112 -0
  113. package/src/config/types.gateway.ts +243 -0
  114. package/src/config/types.hooks.ts +124 -0
  115. package/src/config/types.messages.ts +201 -0
  116. package/src/config/types.milady.ts +791 -0
  117. package/src/config/types.tools.ts +416 -0
  118. package/src/config/types.ts +7 -0
  119. package/src/config/zod-schema.agent-runtime.ts +777 -0
  120. package/src/config/zod-schema.core.ts +778 -0
  121. package/src/config/zod-schema.hooks.ts +139 -0
  122. package/src/config/zod-schema.providers-core.ts +1126 -0
  123. package/src/config/zod-schema.session.ts +98 -0
  124. package/src/config/zod-schema.ts +865 -0
  125. package/src/contracts/apps.ts +46 -0
  126. package/src/contracts/awareness.ts +56 -0
  127. package/src/contracts/config.ts +172 -0
  128. package/src/contracts/drop.ts +21 -0
  129. package/src/contracts/index.ts +8 -0
  130. package/src/contracts/onboarding.ts +592 -0
  131. package/src/contracts/permissions.ts +52 -0
  132. package/src/contracts/verification.ts +9 -0
  133. package/src/contracts/wallet.ts +503 -0
  134. package/src/diagnostics/integration-observability.ts +132 -0
  135. package/src/emotes/catalog.ts +655 -0
  136. package/src/external-modules.d.ts +7 -0
  137. package/src/hooks/discovery.test.ts +357 -0
  138. package/src/hooks/discovery.ts +231 -0
  139. package/src/hooks/eligibility.ts +146 -0
  140. package/src/hooks/hooks.test.ts +320 -0
  141. package/src/hooks/index.ts +8 -0
  142. package/src/hooks/loader.test.ts +418 -0
  143. package/src/hooks/loader.ts +256 -0
  144. package/src/hooks/registry.test.ts +168 -0
  145. package/src/hooks/registry.ts +74 -0
  146. package/src/hooks/types.ts +121 -0
  147. package/src/index.ts +19 -0
  148. package/src/onboarding-presets.ts +828 -0
  149. package/src/plugins/custom-rtmp/index.ts +40 -0
  150. package/src/providers/admin-trust.ts +76 -0
  151. package/src/providers/session-bridge.ts +143 -0
  152. package/src/providers/session-utils.ts +42 -0
  153. package/src/providers/simple-mode.ts +113 -0
  154. package/src/providers/ui-catalog.ts +135 -0
  155. package/src/providers/workspace-provider.ts +213 -0
  156. package/src/providers/workspace.ts +497 -0
  157. package/src/runtime/agent-event-service.ts +57 -0
  158. package/src/runtime/cloud-onboarding.test.ts +489 -0
  159. package/src/runtime/cloud-onboarding.ts +408 -0
  160. package/src/runtime/core-plugins.ts +53 -0
  161. package/src/runtime/custom-actions.ts +605 -0
  162. package/src/runtime/eliza.ts +4941 -0
  163. package/src/runtime/embedding-presets.ts +73 -0
  164. package/src/runtime/index.ts +8 -0
  165. package/src/runtime/milady-plugin.ts +180 -0
  166. package/src/runtime/onboarding-names.ts +76 -0
  167. package/src/runtime/release-plugin-policy.ts +119 -0
  168. package/src/runtime/restart.ts +59 -0
  169. package/src/runtime/trajectory-persistence.ts +2584 -0
  170. package/src/runtime/version.ts +6 -0
  171. package/src/security/audit-log.ts +222 -0
  172. package/src/security/network-policy.ts +91 -0
  173. package/src/server/index.ts +6 -0
  174. package/src/services/agent-export.ts +976 -0
  175. package/src/services/app-manager.ts +755 -0
  176. package/src/services/browser-capture.ts +215 -0
  177. package/src/services/coding-agent-context.ts +355 -0
  178. package/src/services/fallback-training-service.ts +196 -0
  179. package/src/services/index.ts +17 -0
  180. package/src/services/mcp-marketplace.ts +327 -0
  181. package/src/services/plugin-manager-types.ts +185 -0
  182. package/src/services/privy-wallets.ts +352 -0
  183. package/src/services/registry-client-app-meta.ts +201 -0
  184. package/src/services/registry-client-endpoints.ts +253 -0
  185. package/src/services/registry-client-local.ts +485 -0
  186. package/src/services/registry-client-network.ts +173 -0
  187. package/src/services/registry-client-queries.ts +176 -0
  188. package/src/services/registry-client-types.ts +104 -0
  189. package/src/services/registry-client.ts +366 -0
  190. package/src/services/remote-signing-service.ts +261 -0
  191. package/src/services/sandbox-engine.ts +753 -0
  192. package/src/services/sandbox-manager.ts +503 -0
  193. package/src/services/self-updater.ts +213 -0
  194. package/src/services/signal-pairing.ts +189 -0
  195. package/src/services/signing-policy.ts +230 -0
  196. package/src/services/skill-catalog-client.ts +195 -0
  197. package/src/services/skill-marketplace.ts +909 -0
  198. package/src/services/stream-manager.ts +707 -0
  199. package/src/services/tts-stream-bridge.ts +465 -0
  200. package/src/services/update-checker.ts +163 -0
  201. package/src/services/version-compat.ts +367 -0
  202. package/src/services/whatsapp-pairing.ts +279 -0
  203. package/src/shared/ui-catalog-prompt.ts +1158 -0
  204. package/src/test-support/process-helpers.ts +35 -0
  205. package/src/test-support/route-test-helpers.ts +113 -0
  206. package/src/test-support/test-helpers.ts +304 -0
  207. package/src/testing/index.ts +3 -0
  208. package/src/triggers/action.ts +342 -0
  209. package/src/triggers/runtime.ts +432 -0
  210. package/src/triggers/scheduling.ts +472 -0
  211. package/src/triggers/types.ts +133 -0
  212. package/src/types/app-hyperscape-routes-shim.d.ts +29 -0
  213. package/src/types/external-modules.d.ts +7 -0
  214. package/src/utils/exec-safety.ts +23 -0
  215. package/src/utils/number-parsing.ts +112 -0
  216. package/src/utils/spoken-text.ts +65 -0
  217. package/src/version-resolver.ts +60 -0
  218. package/test/api/agent-admin-routes.test.ts +160 -0
  219. package/test/api/agent-lifecycle-routes.test.ts +164 -0
  220. package/test/api/agent-transfer-routes.test.ts +136 -0
  221. package/test/api/apps-routes.test.ts +140 -0
  222. package/test/api/auth-routes.test.ts +160 -0
  223. package/test/api/bug-report-routes.test.ts +88 -0
  224. package/test/api/knowledge-routes.test.ts +73 -0
  225. package/test/api/lifecycle.test.ts +342 -0
  226. package/test/api/memory-routes.test.ts +74 -0
  227. package/test/api/models-routes.test.ts +112 -0
  228. package/test/api/nfa-routes.test.ts +78 -0
  229. package/test/api/permissions-routes.test.ts +185 -0
  230. package/test/api/registry-routes.test.ts +157 -0
  231. package/test/api/signal-routes.test.ts +113 -0
  232. package/test/api/subscription-routes.test.ts +90 -0
  233. package/test/api/trigger-routes.test.ts +87 -0
  234. package/test/api/wallet-routes.observability.test.ts +191 -0
  235. package/test/api/wallet-routes.test.ts +502 -0
  236. package/test/diagnostics/integration-observability.test.ts +135 -0
  237. package/test/security/audit-log.test.ts +229 -0
  238. package/test/security/network-policy.test.ts +143 -0
  239. package/test/services/version-compat.test.ts +127 -0
  240. package/tsconfig.build.json +21 -0
  241. package/tsconfig.json +19 -0
@@ -0,0 +1,1126 @@
1
+ import { z } from "zod";
2
+ import {
3
+ normalizeTelegramCommandDescription,
4
+ normalizeTelegramCommandName,
5
+ resolveTelegramCustomCommands,
6
+ } from "./telegram-custom-commands";
7
+ import { ToolPolicySchema } from "./zod-schema.agent-runtime";
8
+ import {
9
+ BlockStreamingChunkSchema,
10
+ BlockStreamingCoalesceSchema,
11
+ ChannelHeartbeatVisibilitySchema,
12
+ DmConfigSchema,
13
+ DmPolicySchema,
14
+ ExecutableTokenSchema,
15
+ GroupPolicySchema,
16
+ MarkdownConfigSchema,
17
+ MSTeamsReplyStyleSchema,
18
+ ProviderCommandsSchema,
19
+ ReplyToModeSchema,
20
+ RetryConfigSchema,
21
+ requireOpenAllowFrom,
22
+ } from "./zod-schema.core";
23
+
24
+ const ToolPolicyBySenderSchema = z
25
+ .record(z.string(), ToolPolicySchema)
26
+ .optional();
27
+
28
+ const TelegramInlineButtonsScopeSchema = z.enum([
29
+ "off",
30
+ "dm",
31
+ "group",
32
+ "all",
33
+ "allowlist",
34
+ ]);
35
+
36
+ const TelegramCapabilitiesSchema = z.union([
37
+ z.array(z.string()),
38
+ z
39
+ .object({
40
+ inlineButtons: TelegramInlineButtonsScopeSchema.optional(),
41
+ })
42
+ .strict(),
43
+ ]);
44
+
45
+ export const TelegramTopicSchema = z
46
+ .object({
47
+ requireMention: z.boolean().optional(),
48
+ skills: z.array(z.string()).optional(),
49
+ enabled: z.boolean().optional(),
50
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
51
+ systemPrompt: z.string().optional(),
52
+ })
53
+ .strict();
54
+
55
+ export const TelegramGroupSchema = z
56
+ .object({
57
+ requireMention: z.boolean().optional(),
58
+ tools: ToolPolicySchema,
59
+ toolsBySender: ToolPolicyBySenderSchema,
60
+ skills: z.array(z.string()).optional(),
61
+ enabled: z.boolean().optional(),
62
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
63
+ systemPrompt: z.string().optional(),
64
+ topics: z.record(z.string(), TelegramTopicSchema.optional()).optional(),
65
+ })
66
+ .strict();
67
+
68
+ const TelegramCustomCommandSchema = z
69
+ .object({
70
+ command: z.string().transform(normalizeTelegramCommandName),
71
+ description: z.string().transform(normalizeTelegramCommandDescription),
72
+ })
73
+ .strict();
74
+
75
+ const validateTelegramCustomCommands = (
76
+ value: { customCommands?: Array<{ command?: string; description?: string }> },
77
+ ctx: z.RefinementCtx,
78
+ ) => {
79
+ if (!value.customCommands || value.customCommands.length === 0) {
80
+ return;
81
+ }
82
+ const { issues } = resolveTelegramCustomCommands({
83
+ commands: value.customCommands,
84
+ checkReserved: false,
85
+ checkDuplicates: false,
86
+ });
87
+ for (const issue of issues) {
88
+ ctx.addIssue({
89
+ code: z.ZodIssueCode.custom,
90
+ path: ["customCommands", issue.index, issue.field],
91
+ message: issue.message,
92
+ });
93
+ }
94
+ };
95
+
96
+ export const TelegramAccountSchemaBase = z
97
+ .object({
98
+ name: z.string().optional(),
99
+ capabilities: TelegramCapabilitiesSchema.optional(),
100
+ markdown: MarkdownConfigSchema,
101
+ enabled: z.boolean().optional(),
102
+ commands: ProviderCommandsSchema,
103
+ customCommands: z.array(TelegramCustomCommandSchema).optional(),
104
+ configWrites: z.boolean().optional(),
105
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
106
+ botToken: z.string().optional(),
107
+ tokenFile: z.string().optional(),
108
+ replyToMode: ReplyToModeSchema.optional(),
109
+ groups: z.record(z.string(), TelegramGroupSchema.optional()).optional(),
110
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
111
+ groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
112
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
113
+ historyLimit: z.number().int().min(0).optional(),
114
+ dmHistoryLimit: z.number().int().min(0).optional(),
115
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
116
+ textChunkLimit: z.number().int().positive().optional(),
117
+ chunkMode: z.enum(["length", "newline"]).optional(),
118
+ blockStreaming: z.boolean().optional(),
119
+ draftChunk: BlockStreamingChunkSchema.optional(),
120
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
121
+ streamMode: z
122
+ .enum(["off", "partial", "block"])
123
+ .optional()
124
+ .default("partial"),
125
+ mediaMaxMb: z.number().positive().optional(),
126
+ timeoutSeconds: z.number().int().positive().optional(),
127
+ retry: RetryConfigSchema,
128
+ network: z
129
+ .object({
130
+ autoSelectFamily: z.boolean().optional(),
131
+ })
132
+ .strict()
133
+ .optional(),
134
+ proxy: z.string().optional(),
135
+ webhookUrl: z.string().optional(),
136
+ webhookSecret: z.string().optional(),
137
+ webhookPath: z.string().optional(),
138
+ actions: z
139
+ .object({
140
+ reactions: z.boolean().optional(),
141
+ sendMessage: z.boolean().optional(),
142
+ deleteMessage: z.boolean().optional(),
143
+ sticker: z.boolean().optional(),
144
+ })
145
+ .strict()
146
+ .optional(),
147
+ reactionNotifications: z.enum(["off", "own", "all"]).optional(),
148
+ reactionLevel: z.enum(["off", "ack", "minimal", "extensive"]).optional(),
149
+ heartbeat: ChannelHeartbeatVisibilitySchema,
150
+ linkPreview: z.boolean().optional(),
151
+ })
152
+ .strict();
153
+
154
+ export const TelegramAccountSchema = TelegramAccountSchemaBase.superRefine(
155
+ (value, ctx) => {
156
+ requireOpenAllowFrom({
157
+ policy: value.dmPolicy,
158
+ allowFrom: value.allowFrom,
159
+ ctx,
160
+ path: ["allowFrom"],
161
+ message:
162
+ 'channels.telegram.dmPolicy="open" requires channels.telegram.allowFrom to include "*"',
163
+ });
164
+ validateTelegramCustomCommands(value, ctx);
165
+ },
166
+ );
167
+
168
+ export const TelegramConfigSchema = TelegramAccountSchemaBase.extend({
169
+ accounts: z.record(z.string(), TelegramAccountSchema.optional()).optional(),
170
+ }).superRefine((value, ctx) => {
171
+ requireOpenAllowFrom({
172
+ policy: value.dmPolicy,
173
+ allowFrom: value.allowFrom,
174
+ ctx,
175
+ path: ["allowFrom"],
176
+ message:
177
+ 'channels.telegram.dmPolicy="open" requires channels.telegram.allowFrom to include "*"',
178
+ });
179
+ validateTelegramCustomCommands(value, ctx);
180
+
181
+ const baseWebhookUrl =
182
+ typeof value.webhookUrl === "string" ? value.webhookUrl.trim() : "";
183
+ const baseWebhookSecret =
184
+ typeof value.webhookSecret === "string" ? value.webhookSecret.trim() : "";
185
+ if (baseWebhookUrl && !baseWebhookSecret) {
186
+ ctx.addIssue({
187
+ code: z.ZodIssueCode.custom,
188
+ message:
189
+ "channels.telegram.webhookUrl requires channels.telegram.webhookSecret",
190
+ path: ["webhookSecret"],
191
+ });
192
+ }
193
+ if (!value.accounts) {
194
+ return;
195
+ }
196
+ for (const [accountId, account] of Object.entries(value.accounts)) {
197
+ if (!account) {
198
+ continue;
199
+ }
200
+ if (account.enabled === false) {
201
+ continue;
202
+ }
203
+ const accountWebhookUrl =
204
+ typeof account.webhookUrl === "string" ? account.webhookUrl.trim() : "";
205
+ if (!accountWebhookUrl) {
206
+ continue;
207
+ }
208
+ const accountSecret =
209
+ typeof account.webhookSecret === "string"
210
+ ? account.webhookSecret.trim()
211
+ : "";
212
+ if (!accountSecret && !baseWebhookSecret) {
213
+ ctx.addIssue({
214
+ code: z.ZodIssueCode.custom,
215
+ message:
216
+ "channels.telegram.accounts.*.webhookUrl requires channels.telegram.webhookSecret or channels.telegram.accounts.*.webhookSecret",
217
+ path: ["accounts", accountId, "webhookSecret"],
218
+ });
219
+ }
220
+ }
221
+ });
222
+
223
+ export const DiscordDmSchema = z
224
+ .object({
225
+ enabled: z.boolean().optional(),
226
+ policy: DmPolicySchema.optional().default("pairing"),
227
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
228
+ groupEnabled: z.boolean().optional(),
229
+ groupChannels: z.array(z.union([z.string(), z.number()])).optional(),
230
+ })
231
+ .strict()
232
+ .superRefine((value, ctx) => {
233
+ requireOpenAllowFrom({
234
+ policy: value.policy,
235
+ allowFrom: value.allowFrom,
236
+ ctx,
237
+ path: ["allowFrom"],
238
+ message:
239
+ 'channels.discord.dm.policy="open" requires channels.discord.dm.allowFrom to include "*"',
240
+ });
241
+ });
242
+
243
+ export const DiscordGuildChannelSchema = z
244
+ .object({
245
+ allow: z.boolean().optional(),
246
+ requireMention: z.boolean().optional(),
247
+ tools: ToolPolicySchema,
248
+ toolsBySender: ToolPolicyBySenderSchema,
249
+ skills: z.array(z.string()).optional(),
250
+ enabled: z.boolean().optional(),
251
+ users: z.array(z.union([z.string(), z.number()])).optional(),
252
+ systemPrompt: z.string().optional(),
253
+ autoThread: z.boolean().optional(),
254
+ })
255
+ .strict();
256
+
257
+ export const DiscordGuildSchema = z
258
+ .object({
259
+ slug: z.string().optional(),
260
+ requireMention: z.boolean().optional(),
261
+ tools: ToolPolicySchema,
262
+ toolsBySender: ToolPolicyBySenderSchema,
263
+ reactionNotifications: z
264
+ .enum(["off", "own", "all", "allowlist"])
265
+ .optional(),
266
+ users: z.array(z.union([z.string(), z.number()])).optional(),
267
+ channels: z
268
+ .record(z.string(), DiscordGuildChannelSchema.optional())
269
+ .optional(),
270
+ })
271
+ .strict();
272
+
273
+ export const DiscordAccountSchema = z
274
+ .object({
275
+ name: z.string().optional(),
276
+ capabilities: z.array(z.string()).optional(),
277
+ markdown: MarkdownConfigSchema,
278
+ enabled: z.boolean().optional(),
279
+ commands: ProviderCommandsSchema,
280
+ configWrites: z.boolean().optional(),
281
+ token: z.string().optional(),
282
+ allowBots: z.boolean().optional(),
283
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
284
+ historyLimit: z.number().int().min(0).optional(),
285
+ dmHistoryLimit: z.number().int().min(0).optional(),
286
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
287
+ textChunkLimit: z.number().int().positive().optional(),
288
+ chunkMode: z.enum(["length", "newline"]).optional(),
289
+ blockStreaming: z.boolean().optional(),
290
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
291
+ maxLinesPerMessage: z.number().int().positive().optional(),
292
+ mediaMaxMb: z.number().positive().optional(),
293
+ retry: RetryConfigSchema,
294
+ actions: z
295
+ .object({
296
+ reactions: z.boolean().optional(),
297
+ stickers: z.boolean().optional(),
298
+ emojiUploads: z.boolean().optional(),
299
+ stickerUploads: z.boolean().optional(),
300
+ polls: z.boolean().optional(),
301
+ permissions: z.boolean().optional(),
302
+ messages: z.boolean().optional(),
303
+ threads: z.boolean().optional(),
304
+ pins: z.boolean().optional(),
305
+ search: z.boolean().optional(),
306
+ memberInfo: z.boolean().optional(),
307
+ roleInfo: z.boolean().optional(),
308
+ roles: z.boolean().optional(),
309
+ channelInfo: z.boolean().optional(),
310
+ voiceStatus: z.boolean().optional(),
311
+ events: z.boolean().optional(),
312
+ moderation: z.boolean().optional(),
313
+ channels: z.boolean().optional(),
314
+ presence: z.boolean().optional(),
315
+ })
316
+ .strict()
317
+ .optional(),
318
+ replyToMode: ReplyToModeSchema.optional(),
319
+ dm: DiscordDmSchema.optional(),
320
+ guilds: z.record(z.string(), DiscordGuildSchema.optional()).optional(),
321
+ heartbeat: ChannelHeartbeatVisibilitySchema,
322
+ execApprovals: z
323
+ .object({
324
+ enabled: z.boolean().optional(),
325
+ approvers: z.array(z.union([z.string(), z.number()])).optional(),
326
+ agentFilter: z.array(z.string()).optional(),
327
+ sessionFilter: z.array(z.string()).optional(),
328
+ })
329
+ .strict()
330
+ .optional(),
331
+ intents: z
332
+ .object({
333
+ presence: z.boolean().optional(),
334
+ guildMembers: z.boolean().optional(),
335
+ })
336
+ .strict()
337
+ .optional(),
338
+ pluralkit: z
339
+ .object({
340
+ enabled: z.boolean().optional(),
341
+ token: z.string().optional(),
342
+ })
343
+ .strict()
344
+ .optional(),
345
+ })
346
+ .strict();
347
+
348
+ export const DiscordConfigSchema = DiscordAccountSchema.extend({
349
+ accounts: z.record(z.string(), DiscordAccountSchema.optional()).optional(),
350
+ });
351
+
352
+ export const GoogleChatDmSchema = z
353
+ .object({
354
+ enabled: z.boolean().optional(),
355
+ policy: DmPolicySchema.optional().default("pairing"),
356
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
357
+ })
358
+ .strict()
359
+ .superRefine((value, ctx) => {
360
+ requireOpenAllowFrom({
361
+ policy: value.policy,
362
+ allowFrom: value.allowFrom,
363
+ ctx,
364
+ path: ["allowFrom"],
365
+ message:
366
+ 'channels.googlechat.dm.policy="open" requires channels.googlechat.dm.allowFrom to include "*"',
367
+ });
368
+ });
369
+
370
+ export const GoogleChatGroupSchema = z
371
+ .object({
372
+ enabled: z.boolean().optional(),
373
+ allow: z.boolean().optional(),
374
+ requireMention: z.boolean().optional(),
375
+ users: z.array(z.union([z.string(), z.number()])).optional(),
376
+ systemPrompt: z.string().optional(),
377
+ })
378
+ .strict();
379
+
380
+ export const GoogleChatAccountSchema = z
381
+ .object({
382
+ name: z.string().optional(),
383
+ capabilities: z.array(z.string()).optional(),
384
+ enabled: z.boolean().optional(),
385
+ configWrites: z.boolean().optional(),
386
+ allowBots: z.boolean().optional(),
387
+ requireMention: z.boolean().optional(),
388
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
389
+ groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
390
+ groups: z.record(z.string(), GoogleChatGroupSchema.optional()).optional(),
391
+ serviceAccount: z
392
+ .union([z.string(), z.record(z.string(), z.unknown())])
393
+ .optional(),
394
+ serviceAccountFile: z.string().optional(),
395
+ audienceType: z.enum(["app-url", "project-number"]).optional(),
396
+ audience: z.string().optional(),
397
+ webhookPath: z.string().optional(),
398
+ webhookUrl: z.string().optional(),
399
+ botUser: z.string().optional(),
400
+ historyLimit: z.number().int().min(0).optional(),
401
+ dmHistoryLimit: z.number().int().min(0).optional(),
402
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
403
+ textChunkLimit: z.number().int().positive().optional(),
404
+ chunkMode: z.enum(["length", "newline"]).optional(),
405
+ blockStreaming: z.boolean().optional(),
406
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
407
+ mediaMaxMb: z.number().positive().optional(),
408
+ replyToMode: ReplyToModeSchema.optional(),
409
+ actions: z
410
+ .object({
411
+ reactions: z.boolean().optional(),
412
+ })
413
+ .strict()
414
+ .optional(),
415
+ dm: GoogleChatDmSchema.optional(),
416
+ typingIndicator: z.enum(["none", "message", "reaction"]).optional(),
417
+ })
418
+ .strict();
419
+
420
+ export const GoogleChatConfigSchema = GoogleChatAccountSchema.extend({
421
+ accounts: z.record(z.string(), GoogleChatAccountSchema.optional()).optional(),
422
+ defaultAccount: z.string().optional(),
423
+ });
424
+
425
+ export const SlackDmSchema = z
426
+ .object({
427
+ enabled: z.boolean().optional(),
428
+ policy: DmPolicySchema.optional().default("pairing"),
429
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
430
+ groupEnabled: z.boolean().optional(),
431
+ groupChannels: z.array(z.union([z.string(), z.number()])).optional(),
432
+ replyToMode: ReplyToModeSchema.optional(),
433
+ })
434
+ .strict()
435
+ .superRefine((value, ctx) => {
436
+ requireOpenAllowFrom({
437
+ policy: value.policy,
438
+ allowFrom: value.allowFrom,
439
+ ctx,
440
+ path: ["allowFrom"],
441
+ message:
442
+ 'channels.slack.dm.policy="open" requires channels.slack.dm.allowFrom to include "*"',
443
+ });
444
+ });
445
+
446
+ export const SlackChannelSchema = z
447
+ .object({
448
+ enabled: z.boolean().optional(),
449
+ allow: z.boolean().optional(),
450
+ requireMention: z.boolean().optional(),
451
+ tools: ToolPolicySchema,
452
+ toolsBySender: ToolPolicyBySenderSchema,
453
+ allowBots: z.boolean().optional(),
454
+ users: z.array(z.union([z.string(), z.number()])).optional(),
455
+ skills: z.array(z.string()).optional(),
456
+ systemPrompt: z.string().optional(),
457
+ })
458
+ .strict();
459
+
460
+ export const SlackThreadSchema = z
461
+ .object({
462
+ historyScope: z.enum(["thread", "channel"]).optional(),
463
+ inheritParent: z.boolean().optional(),
464
+ })
465
+ .strict();
466
+
467
+ const SlackReplyToModeByChatTypeSchema = z
468
+ .object({
469
+ direct: ReplyToModeSchema.optional(),
470
+ group: ReplyToModeSchema.optional(),
471
+ channel: ReplyToModeSchema.optional(),
472
+ })
473
+ .strict();
474
+
475
+ export const SlackAccountSchema = z
476
+ .object({
477
+ name: z.string().optional(),
478
+ mode: z.enum(["socket", "http"]).optional(),
479
+ signingSecret: z.string().optional(),
480
+ webhookPath: z.string().optional(),
481
+ capabilities: z.array(z.string()).optional(),
482
+ markdown: MarkdownConfigSchema,
483
+ enabled: z.boolean().optional(),
484
+ commands: ProviderCommandsSchema,
485
+ configWrites: z.boolean().optional(),
486
+ botToken: z.string().optional(),
487
+ appToken: z.string().optional(),
488
+ userToken: z.string().optional(),
489
+ userTokenReadOnly: z.boolean().optional().default(true),
490
+ allowBots: z.boolean().optional(),
491
+ requireMention: z.boolean().optional(),
492
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
493
+ historyLimit: z.number().int().min(0).optional(),
494
+ dmHistoryLimit: z.number().int().min(0).optional(),
495
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
496
+ textChunkLimit: z.number().int().positive().optional(),
497
+ chunkMode: z.enum(["length", "newline"]).optional(),
498
+ blockStreaming: z.boolean().optional(),
499
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
500
+ mediaMaxMb: z.number().positive().optional(),
501
+ reactionNotifications: z
502
+ .enum(["off", "own", "all", "allowlist"])
503
+ .optional(),
504
+ reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(),
505
+ replyToMode: ReplyToModeSchema.optional(),
506
+ replyToModeByChatType: SlackReplyToModeByChatTypeSchema.optional(),
507
+ thread: SlackThreadSchema.optional(),
508
+ actions: z
509
+ .object({
510
+ reactions: z.boolean().optional(),
511
+ messages: z.boolean().optional(),
512
+ pins: z.boolean().optional(),
513
+ search: z.boolean().optional(),
514
+ permissions: z.boolean().optional(),
515
+ memberInfo: z.boolean().optional(),
516
+ channelInfo: z.boolean().optional(),
517
+ emojiList: z.boolean().optional(),
518
+ })
519
+ .strict()
520
+ .optional(),
521
+ slashCommand: z
522
+ .object({
523
+ enabled: z.boolean().optional(),
524
+ name: z.string().optional(),
525
+ sessionPrefix: z.string().optional(),
526
+ ephemeral: z.boolean().optional(),
527
+ })
528
+ .strict()
529
+ .optional(),
530
+ dm: SlackDmSchema.optional(),
531
+ channels: z.record(z.string(), SlackChannelSchema.optional()).optional(),
532
+ heartbeat: ChannelHeartbeatVisibilitySchema,
533
+ })
534
+ .strict();
535
+
536
+ export const SlackConfigSchema = SlackAccountSchema.extend({
537
+ mode: z.enum(["socket", "http"]).optional().default("socket"),
538
+ signingSecret: z.string().optional(),
539
+ webhookPath: z.string().optional().default("/slack/events"),
540
+ accounts: z.record(z.string(), SlackAccountSchema.optional()).optional(),
541
+ }).superRefine((value, ctx) => {
542
+ const baseMode = value.mode ?? "socket";
543
+ if (baseMode === "http" && !value.signingSecret) {
544
+ ctx.addIssue({
545
+ code: z.ZodIssueCode.custom,
546
+ message:
547
+ 'channels.slack.mode="http" requires channels.slack.signingSecret',
548
+ path: ["signingSecret"],
549
+ });
550
+ }
551
+ if (!value.accounts) {
552
+ return;
553
+ }
554
+ for (const [accountId, account] of Object.entries(value.accounts)) {
555
+ if (!account) {
556
+ continue;
557
+ }
558
+ if (account.enabled === false) {
559
+ continue;
560
+ }
561
+ const accountMode = account.mode ?? baseMode;
562
+ if (accountMode !== "http") {
563
+ continue;
564
+ }
565
+ const accountSecret = account.signingSecret ?? value.signingSecret;
566
+ if (!accountSecret) {
567
+ ctx.addIssue({
568
+ code: z.ZodIssueCode.custom,
569
+ message:
570
+ 'channels.slack.accounts.*.mode="http" requires channels.slack.signingSecret or channels.slack.accounts.*.signingSecret',
571
+ path: ["accounts", accountId, "signingSecret"],
572
+ });
573
+ }
574
+ }
575
+ });
576
+
577
+ export const SignalAccountSchemaBase = z
578
+ .object({
579
+ name: z.string().optional(),
580
+ capabilities: z.array(z.string()).optional(),
581
+ markdown: MarkdownConfigSchema,
582
+ enabled: z.boolean().optional(),
583
+ configWrites: z.boolean().optional(),
584
+ account: z.string().optional(),
585
+ httpUrl: z.string().optional(),
586
+ httpHost: z.string().optional(),
587
+ httpPort: z.number().int().positive().optional(),
588
+ cliPath: ExecutableTokenSchema.optional(),
589
+ autoStart: z.boolean().optional(),
590
+ startupTimeoutMs: z.number().int().min(1000).max(120000).optional(),
591
+ receiveMode: z
592
+ .union([z.literal("on-start"), z.literal("manual")])
593
+ .optional(),
594
+ ignoreAttachments: z.boolean().optional(),
595
+ ignoreStories: z.boolean().optional(),
596
+ sendReadReceipts: z.boolean().optional(),
597
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
598
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
599
+ groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
600
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
601
+ historyLimit: z.number().int().min(0).optional(),
602
+ dmHistoryLimit: z.number().int().min(0).optional(),
603
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
604
+ textChunkLimit: z.number().int().positive().optional(),
605
+ chunkMode: z.enum(["length", "newline"]).optional(),
606
+ blockStreaming: z.boolean().optional(),
607
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
608
+ mediaMaxMb: z.number().int().positive().optional(),
609
+ reactionNotifications: z
610
+ .enum(["off", "own", "all", "allowlist"])
611
+ .optional(),
612
+ reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(),
613
+ actions: z
614
+ .object({
615
+ reactions: z.boolean().optional(),
616
+ })
617
+ .strict()
618
+ .optional(),
619
+ reactionLevel: z.enum(["off", "ack", "minimal", "extensive"]).optional(),
620
+ heartbeat: ChannelHeartbeatVisibilitySchema,
621
+ })
622
+ .strict();
623
+
624
+ export const SignalAccountSchema = SignalAccountSchemaBase.superRefine(
625
+ (value, ctx) => {
626
+ requireOpenAllowFrom({
627
+ policy: value.dmPolicy,
628
+ allowFrom: value.allowFrom,
629
+ ctx,
630
+ path: ["allowFrom"],
631
+ message:
632
+ 'channels.signal.dmPolicy="open" requires channels.signal.allowFrom to include "*"',
633
+ });
634
+ },
635
+ );
636
+
637
+ export const SignalConfigSchema = SignalAccountSchemaBase.extend({
638
+ accounts: z.record(z.string(), SignalAccountSchema.optional()).optional(),
639
+ }).superRefine((value, ctx) => {
640
+ requireOpenAllowFrom({
641
+ policy: value.dmPolicy,
642
+ allowFrom: value.allowFrom,
643
+ ctx,
644
+ path: ["allowFrom"],
645
+ message:
646
+ 'channels.signal.dmPolicy="open" requires channels.signal.allowFrom to include "*"',
647
+ });
648
+ });
649
+
650
+ export const IMessageAccountSchemaBase = z
651
+ .object({
652
+ name: z.string().optional(),
653
+ capabilities: z.array(z.string()).optional(),
654
+ markdown: MarkdownConfigSchema,
655
+ enabled: z.boolean().optional(),
656
+ configWrites: z.boolean().optional(),
657
+ cliPath: ExecutableTokenSchema.optional(),
658
+ dbPath: z.string().optional(),
659
+ remoteHost: z.string().optional(),
660
+ service: z
661
+ .union([z.literal("imessage"), z.literal("sms"), z.literal("auto")])
662
+ .optional(),
663
+ region: z.string().optional(),
664
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
665
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
666
+ groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
667
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
668
+ historyLimit: z.number().int().min(0).optional(),
669
+ dmHistoryLimit: z.number().int().min(0).optional(),
670
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
671
+ includeAttachments: z.boolean().optional(),
672
+ mediaMaxMb: z.number().int().positive().optional(),
673
+ textChunkLimit: z.number().int().positive().optional(),
674
+ chunkMode: z.enum(["length", "newline"]).optional(),
675
+ blockStreaming: z.boolean().optional(),
676
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
677
+ groups: z
678
+ .record(
679
+ z.string(),
680
+ z
681
+ .object({
682
+ requireMention: z.boolean().optional(),
683
+ tools: ToolPolicySchema,
684
+ toolsBySender: ToolPolicyBySenderSchema,
685
+ })
686
+ .strict()
687
+ .optional(),
688
+ )
689
+ .optional(),
690
+ heartbeat: ChannelHeartbeatVisibilitySchema,
691
+ })
692
+ .strict();
693
+
694
+ export const IMessageAccountSchema = IMessageAccountSchemaBase.superRefine(
695
+ (value, ctx) => {
696
+ requireOpenAllowFrom({
697
+ policy: value.dmPolicy,
698
+ allowFrom: value.allowFrom,
699
+ ctx,
700
+ path: ["allowFrom"],
701
+ message:
702
+ 'channels.imessage.dmPolicy="open" requires channels.imessage.allowFrom to include "*"',
703
+ });
704
+ },
705
+ );
706
+
707
+ export const IMessageConfigSchema = IMessageAccountSchemaBase.extend({
708
+ accounts: z.record(z.string(), IMessageAccountSchema.optional()).optional(),
709
+ }).superRefine((value, ctx) => {
710
+ requireOpenAllowFrom({
711
+ policy: value.dmPolicy,
712
+ allowFrom: value.allowFrom,
713
+ ctx,
714
+ path: ["allowFrom"],
715
+ message:
716
+ 'channels.imessage.dmPolicy="open" requires channels.imessage.allowFrom to include "*"',
717
+ });
718
+ });
719
+
720
+ const BlueBubblesAllowFromEntry = z.union([z.string(), z.number()]);
721
+
722
+ const BlueBubblesActionSchema = z
723
+ .object({
724
+ reactions: z.boolean().optional(),
725
+ edit: z.boolean().optional(),
726
+ unsend: z.boolean().optional(),
727
+ reply: z.boolean().optional(),
728
+ sendWithEffect: z.boolean().optional(),
729
+ renameGroup: z.boolean().optional(),
730
+ setGroupIcon: z.boolean().optional(),
731
+ addParticipant: z.boolean().optional(),
732
+ removeParticipant: z.boolean().optional(),
733
+ leaveGroup: z.boolean().optional(),
734
+ sendAttachment: z.boolean().optional(),
735
+ })
736
+ .strict()
737
+ .optional();
738
+
739
+ const BlueBubblesGroupConfigSchema = z
740
+ .object({
741
+ requireMention: z.boolean().optional(),
742
+ tools: ToolPolicySchema,
743
+ toolsBySender: ToolPolicyBySenderSchema,
744
+ })
745
+ .strict();
746
+
747
+ export const BlueBubblesAccountSchemaBase = z
748
+ .object({
749
+ name: z.string().optional(),
750
+ capabilities: z.array(z.string()).optional(),
751
+ markdown: MarkdownConfigSchema,
752
+ configWrites: z.boolean().optional(),
753
+ enabled: z.boolean().optional(),
754
+ serverUrl: z.string().optional(),
755
+ password: z.string().optional(),
756
+ webhookPath: z.string().optional(),
757
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
758
+ allowFrom: z.array(BlueBubblesAllowFromEntry).optional(),
759
+ groupAllowFrom: z.array(BlueBubblesAllowFromEntry).optional(),
760
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
761
+ historyLimit: z.number().int().min(0).optional(),
762
+ dmHistoryLimit: z.number().int().min(0).optional(),
763
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
764
+ textChunkLimit: z.number().int().positive().optional(),
765
+ chunkMode: z.enum(["length", "newline"]).optional(),
766
+ mediaMaxMb: z.number().int().positive().optional(),
767
+ sendReadReceipts: z.boolean().optional(),
768
+ blockStreaming: z.boolean().optional(),
769
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
770
+ groups: z
771
+ .record(z.string(), BlueBubblesGroupConfigSchema.optional())
772
+ .optional(),
773
+ heartbeat: ChannelHeartbeatVisibilitySchema,
774
+ })
775
+ .strict();
776
+
777
+ export const BlueBubblesAccountSchema =
778
+ BlueBubblesAccountSchemaBase.superRefine((value, ctx) => {
779
+ requireOpenAllowFrom({
780
+ policy: value.dmPolicy,
781
+ allowFrom: value.allowFrom,
782
+ ctx,
783
+ path: ["allowFrom"],
784
+ message:
785
+ 'channels.bluebubbles.accounts.*.dmPolicy="open" requires allowFrom to include "*"',
786
+ });
787
+ });
788
+
789
+ export const BlueBubblesConfigSchema = BlueBubblesAccountSchemaBase.extend({
790
+ accounts: z
791
+ .record(z.string(), BlueBubblesAccountSchema.optional())
792
+ .optional(),
793
+ actions: BlueBubblesActionSchema,
794
+ }).superRefine((value, ctx) => {
795
+ requireOpenAllowFrom({
796
+ policy: value.dmPolicy,
797
+ allowFrom: value.allowFrom,
798
+ ctx,
799
+ path: ["allowFrom"],
800
+ message:
801
+ 'channels.bluebubbles.dmPolicy="open" requires channels.bluebubbles.allowFrom to include "*"',
802
+ });
803
+ });
804
+
805
+ // ── Retake.tv streaming connector ──────────────────────────────────────────
806
+
807
+ export const RetakeConfigSchema = z
808
+ .object({
809
+ enabled: z.boolean().optional(),
810
+ accessToken: z.string().optional(),
811
+ apiUrl: z.string().url().optional(),
812
+ captureUrl: z.string().optional(),
813
+ })
814
+ .strict();
815
+
816
+ // ── Twitch chat connector schema ──────────────────────────────────────────
817
+
818
+ export const TwitchConnectorConfigSchema = z
819
+ .object({
820
+ enabled: z.boolean().optional(),
821
+ username: z.string().optional(),
822
+ clientId: z.string().optional(),
823
+ accessToken: z.string().optional(),
824
+ clientSecret: z.string().optional(),
825
+ refreshToken: z.string().optional(),
826
+ channel: z.string().optional(),
827
+ channels: z.string().optional(),
828
+ requireMention: z.boolean().optional(),
829
+ allowedRoles: z.string().optional(),
830
+ })
831
+ .strict();
832
+
833
+ // ── Streaming destination schemas ──────────────────────────────────────────
834
+
835
+ export const TwitchStreamConfigSchema = z
836
+ .object({
837
+ enabled: z.boolean().optional(),
838
+ streamKey: z.string().optional(),
839
+ })
840
+ .strict();
841
+
842
+ export const YoutubeStreamConfigSchema = z
843
+ .object({
844
+ enabled: z.boolean().optional(),
845
+ streamKey: z.string().optional(),
846
+ rtmpUrl: z.string().url().optional(),
847
+ })
848
+ .strict();
849
+
850
+ export const CustomRtmpConfigSchema = z
851
+ .object({
852
+ enabled: z.boolean().optional(),
853
+ rtmpUrl: z.string().optional(),
854
+ rtmpKey: z.string().optional(),
855
+ })
856
+ .strict();
857
+
858
+ export const PumpfunStreamConfigSchema = z
859
+ .object({
860
+ enabled: z.boolean().optional(),
861
+ streamKey: z.string().optional(),
862
+ rtmpUrl: z.string().optional(),
863
+ })
864
+ .strict();
865
+
866
+ export const XStreamConfigSchema = z
867
+ .object({
868
+ enabled: z.boolean().optional(),
869
+ streamKey: z.string().optional(),
870
+ rtmpUrl: z.string().optional(),
871
+ })
872
+ .strict();
873
+
874
+ export const MSTeamsChannelSchema = z
875
+ .object({
876
+ requireMention: z.boolean().optional(),
877
+ tools: ToolPolicySchema,
878
+ toolsBySender: ToolPolicyBySenderSchema,
879
+ replyStyle: MSTeamsReplyStyleSchema.optional(),
880
+ })
881
+ .strict();
882
+
883
+ export const MSTeamsTeamSchema = z
884
+ .object({
885
+ requireMention: z.boolean().optional(),
886
+ tools: ToolPolicySchema,
887
+ toolsBySender: ToolPolicyBySenderSchema,
888
+ replyStyle: MSTeamsReplyStyleSchema.optional(),
889
+ channels: z.record(z.string(), MSTeamsChannelSchema.optional()).optional(),
890
+ })
891
+ .strict();
892
+
893
+ export const MSTeamsConfigSchema = z
894
+ .object({
895
+ enabled: z.boolean().optional(),
896
+ capabilities: z.array(z.string()).optional(),
897
+ markdown: MarkdownConfigSchema,
898
+ configWrites: z.boolean().optional(),
899
+ appId: z.string().optional(),
900
+ appPassword: z.string().optional(),
901
+ tenantId: z.string().optional(),
902
+ webhook: z
903
+ .object({
904
+ port: z.number().int().positive().optional(),
905
+ path: z.string().optional(),
906
+ })
907
+ .strict()
908
+ .optional(),
909
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
910
+ allowFrom: z.array(z.string()).optional(),
911
+ groupAllowFrom: z.array(z.string()).optional(),
912
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
913
+ textChunkLimit: z.number().int().positive().optional(),
914
+ chunkMode: z.enum(["length", "newline"]).optional(),
915
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
916
+ mediaAllowHosts: z.array(z.string()).optional(),
917
+ mediaAuthAllowHosts: z.array(z.string()).optional(),
918
+ requireMention: z.boolean().optional(),
919
+ historyLimit: z.number().int().min(0).optional(),
920
+ dmHistoryLimit: z.number().int().min(0).optional(),
921
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
922
+ replyStyle: MSTeamsReplyStyleSchema.optional(),
923
+ teams: z.record(z.string(), MSTeamsTeamSchema.optional()).optional(),
924
+ /** Max media size in MB (default: 100MB for OneDrive upload support). */
925
+ mediaMaxMb: z.number().positive().optional(),
926
+ /** SharePoint site ID for file uploads in group chats/channels (e.g., "contoso.sharepoint.com,guid1,guid2") */
927
+ sharePointSiteId: z.string().optional(),
928
+ heartbeat: ChannelHeartbeatVisibilitySchema,
929
+ })
930
+ .strict()
931
+ .superRefine((value, ctx) => {
932
+ requireOpenAllowFrom({
933
+ policy: value.dmPolicy,
934
+ allowFrom: value.allowFrom,
935
+ ctx,
936
+ path: ["allowFrom"],
937
+ message:
938
+ 'channels.msteams.dmPolicy="open" requires channels.msteams.allowFrom to include "*"',
939
+ });
940
+ });
941
+
942
+ export const WhatsAppAccountSchema = z
943
+ .object({
944
+ name: z.string().optional(),
945
+ capabilities: z.array(z.string()).optional(),
946
+ markdown: MarkdownConfigSchema,
947
+ configWrites: z.boolean().optional(),
948
+ enabled: z.boolean().optional(),
949
+ sendReadReceipts: z.boolean().optional(),
950
+ messagePrefix: z.string().optional(),
951
+ /** Override auth directory for this WhatsApp account (Baileys multi-file auth state). */
952
+ authDir: z.string().optional(),
953
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
954
+ selfChatMode: z.boolean().optional(),
955
+ allowFrom: z.array(z.string()).optional(),
956
+ groupAllowFrom: z.array(z.string()).optional(),
957
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
958
+ historyLimit: z.number().int().min(0).optional(),
959
+ dmHistoryLimit: z.number().int().min(0).optional(),
960
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
961
+ textChunkLimit: z.number().int().positive().optional(),
962
+ chunkMode: z.enum(["length", "newline"]).optional(),
963
+ mediaMaxMb: z.number().int().positive().optional(),
964
+ blockStreaming: z.boolean().optional(),
965
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
966
+ groups: z
967
+ .record(
968
+ z.string(),
969
+ z
970
+ .object({
971
+ requireMention: z.boolean().optional(),
972
+ tools: ToolPolicySchema,
973
+ toolsBySender: ToolPolicyBySenderSchema,
974
+ })
975
+ .strict()
976
+ .optional(),
977
+ )
978
+ .optional(),
979
+ ackReaction: z
980
+ .object({
981
+ emoji: z.string().optional(),
982
+ direct: z.boolean().optional().default(true),
983
+ group: z
984
+ .enum(["always", "mentions", "never"])
985
+ .optional()
986
+ .default("mentions"),
987
+ })
988
+ .strict()
989
+ .optional(),
990
+ debounceMs: z.number().int().nonnegative().optional().default(0),
991
+ heartbeat: ChannelHeartbeatVisibilitySchema,
992
+ })
993
+ .strict()
994
+ .superRefine((value, ctx) => {
995
+ if (value.dmPolicy !== "open") {
996
+ return;
997
+ }
998
+ const allow = (value.allowFrom ?? [])
999
+ .map((v) => String(v).trim())
1000
+ .filter(Boolean);
1001
+ if (allow.includes("*")) {
1002
+ return;
1003
+ }
1004
+ ctx.addIssue({
1005
+ code: z.ZodIssueCode.custom,
1006
+ path: ["allowFrom"],
1007
+ message:
1008
+ 'channels.whatsapp.accounts.*.dmPolicy="open" requires allowFrom to include "*"',
1009
+ });
1010
+ });
1011
+
1012
+ export const WhatsAppConfigSchema = z
1013
+ .object({
1014
+ accounts: z.record(z.string(), WhatsAppAccountSchema.optional()).optional(),
1015
+ capabilities: z.array(z.string()).optional(),
1016
+ markdown: MarkdownConfigSchema,
1017
+ configWrites: z.boolean().optional(),
1018
+ sendReadReceipts: z.boolean().optional(),
1019
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
1020
+ messagePrefix: z.string().optional(),
1021
+ selfChatMode: z.boolean().optional(),
1022
+ allowFrom: z.array(z.string()).optional(),
1023
+ groupAllowFrom: z.array(z.string()).optional(),
1024
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
1025
+ historyLimit: z.number().int().min(0).optional(),
1026
+ dmHistoryLimit: z.number().int().min(0).optional(),
1027
+ dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
1028
+ textChunkLimit: z.number().int().positive().optional(),
1029
+ chunkMode: z.enum(["length", "newline"]).optional(),
1030
+ mediaMaxMb: z.number().int().positive().optional().default(50),
1031
+ blockStreaming: z.boolean().optional(),
1032
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
1033
+ actions: z
1034
+ .object({
1035
+ reactions: z.boolean().optional(),
1036
+ sendMessage: z.boolean().optional(),
1037
+ polls: z.boolean().optional(),
1038
+ })
1039
+ .strict()
1040
+ .optional(),
1041
+ groups: z
1042
+ .record(
1043
+ z.string(),
1044
+ z
1045
+ .object({
1046
+ requireMention: z.boolean().optional(),
1047
+ tools: ToolPolicySchema,
1048
+ toolsBySender: ToolPolicyBySenderSchema,
1049
+ })
1050
+ .strict()
1051
+ .optional(),
1052
+ )
1053
+ .optional(),
1054
+ ackReaction: z
1055
+ .object({
1056
+ emoji: z.string().optional(),
1057
+ direct: z.boolean().optional().default(true),
1058
+ group: z
1059
+ .enum(["always", "mentions", "never"])
1060
+ .optional()
1061
+ .default("mentions"),
1062
+ })
1063
+ .strict()
1064
+ .optional(),
1065
+ debounceMs: z.number().int().nonnegative().optional().default(0),
1066
+ heartbeat: ChannelHeartbeatVisibilitySchema,
1067
+ })
1068
+ .strict()
1069
+ .superRefine((value, ctx) => {
1070
+ if (value.dmPolicy !== "open") {
1071
+ return;
1072
+ }
1073
+ const allow = (value.allowFrom ?? [])
1074
+ .map((v) => String(v).trim())
1075
+ .filter(Boolean);
1076
+ if (allow.includes("*")) {
1077
+ return;
1078
+ }
1079
+ ctx.addIssue({
1080
+ code: z.ZodIssueCode.custom,
1081
+ path: ["allowFrom"],
1082
+ message:
1083
+ 'channels.whatsapp.dmPolicy="open" requires channels.whatsapp.allowFrom to include "*"',
1084
+ });
1085
+ });
1086
+
1087
+ export const TwitterConfigSchema = z
1088
+ .object({
1089
+ enabled: z.boolean().optional(),
1090
+ // Authentication
1091
+ apiKey: z.string().optional(),
1092
+ apiSecretKey: z.string().optional(),
1093
+ accessToken: z.string().optional(),
1094
+ accessTokenSecret: z.string().optional(),
1095
+
1096
+ // Posting configuration
1097
+ postEnable: z.boolean().optional().default(true),
1098
+ postImmediately: z.boolean().optional().default(false),
1099
+ postIntervalMin: z.number().int().positive().optional().default(90),
1100
+ postIntervalMax: z.number().int().positive().optional().default(180),
1101
+ postIntervalVariance: z.number().min(0).max(1).optional().default(0.1),
1102
+
1103
+ // Interaction settings
1104
+ searchEnable: z.boolean().optional().default(false),
1105
+ autoRespondMentions: z.boolean().optional().default(true),
1106
+ enableActionProcessing: z.boolean().optional().default(true),
1107
+ timelineAlgorithm: z
1108
+ .enum(["weighted", "latest"])
1109
+ .optional()
1110
+ .default("weighted"),
1111
+
1112
+ // DM settings
1113
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
1114
+
1115
+ // Safety and testing
1116
+ dryRun: z.boolean().optional().default(false),
1117
+ retryLimit: z.number().int().positive().optional().default(3),
1118
+ pollInterval: z.number().int().positive().optional().default(120),
1119
+
1120
+ // Advanced
1121
+ maxTweetLength: z.number().int().positive().optional().default(4000),
1122
+ configWrites: z.boolean().optional(),
1123
+ })
1124
+ .strict();
1125
+
1126
+ export type TwitterConfig = z.infer<typeof TwitterConfigSchema>;