@poolzin/pool-bot 2026.3.11 → 2026.3.14

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 (195) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/dist/.buildstamp +1 -1
  3. package/dist/agents/checkpoint-manager.js +291 -0
  4. package/dist/agents/poolbot-tools.js +5 -0
  5. package/dist/agents/subagent-announce-reliability.js +160 -0
  6. package/dist/agents/tool-result-truncation.js +299 -0
  7. package/dist/agents/tools/nodes-file-tool.js +197 -0
  8. package/dist/build-info.json +3 -3
  9. package/dist/cli/config-cli.js +60 -0
  10. package/dist/cron/cron-improvements.js +195 -0
  11. package/dist/discord/discord-improvements.js +167 -0
  12. package/dist/gateway/auth-rate-limit.js +19 -0
  13. package/dist/gateway/auth.js +41 -0
  14. package/dist/gateway/gateway-improvements.js +294 -0
  15. package/dist/gateway/node-command-policy.js +7 -2
  16. package/dist/infra/net/ssrf.js +15 -2
  17. package/dist/infra/shell-security.js +201 -0
  18. package/dist/memory/memory-improvements.js +239 -0
  19. package/dist/node-host/runner.js +146 -79
  20. package/dist/security/prototype-pollution.js +141 -0
  21. package/dist/security/webhook-security.js +253 -0
  22. package/dist/shared/net/ip.js +52 -1
  23. package/dist/slack/slack-improvements.js +225 -0
  24. package/dist/telegram/telegram-improvements.js +220 -0
  25. package/dist/ui-plugins/ui-plugins-improvements.js +191 -0
  26. package/docs/ANALISE_OPENCLAW_PROFISSIONAL.md +520 -0
  27. package/docs/competitive-analysis.md +421 -0
  28. package/docs/implementation-analysis.md +393 -0
  29. package/docs/plans/2026-03-11-file-operations-security-hardening.md +307 -0
  30. package/docs/plans/2026-03-11-integracao-projetos-poolbot.md +666 -0
  31. package/docs/refactor/plugin-development-guide.md +281 -0
  32. package/extensions/agency-agents/README.md +301 -0
  33. package/extensions/agency-agents/agents/CONTRIBUTING.md +353 -0
  34. package/extensions/agency-agents/agents/README.md +602 -0
  35. package/extensions/agency-agents/agents/design/design-brand-guardian.md +320 -0
  36. package/extensions/agency-agents/agents/design/design-image-prompt-engineer.md +234 -0
  37. package/extensions/agency-agents/agents/design/design-ui-designer.md +381 -0
  38. package/extensions/agency-agents/agents/design/design-ux-architect.md +467 -0
  39. package/extensions/agency-agents/agents/design/design-ux-researcher.md +327 -0
  40. package/extensions/agency-agents/agents/design/design-visual-storyteller.md +147 -0
  41. package/extensions/agency-agents/agents/design/design-whimsy-injector.md +436 -0
  42. package/extensions/agency-agents/agents/engineering/engineering-ai-engineer.md +144 -0
  43. package/extensions/agency-agents/agents/engineering/engineering-backend-architect.md +233 -0
  44. package/extensions/agency-agents/agents/engineering/engineering-devops-automator.md +374 -0
  45. package/extensions/agency-agents/agents/engineering/engineering-frontend-developer.md +223 -0
  46. package/extensions/agency-agents/agents/engineering/engineering-mobile-app-builder.md +491 -0
  47. package/extensions/agency-agents/agents/engineering/engineering-rapid-prototyper.md +460 -0
  48. package/extensions/agency-agents/agents/engineering/engineering-security-engineer.md +275 -0
  49. package/extensions/agency-agents/agents/engineering/engineering-senior-developer.md +174 -0
  50. package/extensions/agency-agents/agents/examples/README.md +48 -0
  51. package/extensions/agency-agents/agents/examples/nexus-spatial-discovery.md +852 -0
  52. package/extensions/agency-agents/agents/examples/workflow-landing-page.md +119 -0
  53. package/extensions/agency-agents/agents/examples/workflow-startup-mvp.md +155 -0
  54. package/extensions/agency-agents/agents/integrations/README.md +117 -0
  55. package/extensions/agency-agents/agents/integrations/aider/README.md +38 -0
  56. package/extensions/agency-agents/agents/integrations/antigravity/README.md +49 -0
  57. package/extensions/agency-agents/agents/integrations/claude-code/README.md +31 -0
  58. package/extensions/agency-agents/agents/integrations/cursor/README.md +38 -0
  59. package/extensions/agency-agents/agents/integrations/gemini-cli/README.md +36 -0
  60. package/extensions/agency-agents/agents/integrations/opencode/README.md +58 -0
  61. package/extensions/agency-agents/agents/integrations/windsurf/README.md +26 -0
  62. package/extensions/agency-agents/agents/marketing/marketing-app-store-optimizer.md +319 -0
  63. package/extensions/agency-agents/agents/marketing/marketing-content-creator.md +52 -0
  64. package/extensions/agency-agents/agents/marketing/marketing-growth-hacker.md +52 -0
  65. package/extensions/agency-agents/agents/marketing/marketing-instagram-curator.md +111 -0
  66. package/extensions/agency-agents/agents/marketing/marketing-reddit-community-builder.md +121 -0
  67. package/extensions/agency-agents/agents/marketing/marketing-social-media-strategist.md +123 -0
  68. package/extensions/agency-agents/agents/marketing/marketing-tiktok-strategist.md +123 -0
  69. package/extensions/agency-agents/agents/marketing/marketing-twitter-engager.md +124 -0
  70. package/extensions/agency-agents/agents/marketing/marketing-wechat-official-account.md +143 -0
  71. package/extensions/agency-agents/agents/marketing/marketing-xiaohongshu-specialist.md +136 -0
  72. package/extensions/agency-agents/agents/marketing/marketing-zhihu-strategist.md +160 -0
  73. package/extensions/agency-agents/agents/product/product-feedback-synthesizer.md +117 -0
  74. package/extensions/agency-agents/agents/product/product-sprint-prioritizer.md +152 -0
  75. package/extensions/agency-agents/agents/product/product-trend-researcher.md +157 -0
  76. package/extensions/agency-agents/agents/project-management/project-management-experiment-tracker.md +196 -0
  77. package/extensions/agency-agents/agents/project-management/project-management-project-shepherd.md +192 -0
  78. package/extensions/agency-agents/agents/project-management/project-management-studio-operations.md +198 -0
  79. package/extensions/agency-agents/agents/project-management/project-management-studio-producer.md +201 -0
  80. package/extensions/agency-agents/agents/project-management/project-manager-senior.md +133 -0
  81. package/extensions/agency-agents/agents/scripts/convert.sh +362 -0
  82. package/extensions/agency-agents/agents/scripts/install.sh +465 -0
  83. package/extensions/agency-agents/agents/scripts/lint-agents.sh +115 -0
  84. package/extensions/agency-agents/agents/spatial-computing/macos-spatial-metal-engineer.md +335 -0
  85. package/extensions/agency-agents/agents/spatial-computing/terminal-integration-specialist.md +68 -0
  86. package/extensions/agency-agents/agents/spatial-computing/visionos-spatial-engineer.md +52 -0
  87. package/extensions/agency-agents/agents/spatial-computing/xr-cockpit-interaction-specialist.md +30 -0
  88. package/extensions/agency-agents/agents/spatial-computing/xr-immersive-developer.md +30 -0
  89. package/extensions/agency-agents/agents/spatial-computing/xr-interface-architect.md +30 -0
  90. package/extensions/agency-agents/agents/specialized/agentic-identity-trust.md +367 -0
  91. package/extensions/agency-agents/agents/specialized/agents-orchestrator.md +365 -0
  92. package/extensions/agency-agents/agents/specialized/data-analytics-reporter.md +52 -0
  93. package/extensions/agency-agents/agents/specialized/data-consolidation-agent.md +58 -0
  94. package/extensions/agency-agents/agents/specialized/lsp-index-engineer.md +312 -0
  95. package/extensions/agency-agents/agents/specialized/report-distribution-agent.md +63 -0
  96. package/extensions/agency-agents/agents/specialized/sales-data-extraction-agent.md +65 -0
  97. package/extensions/agency-agents/agents/strategy/EXECUTIVE-BRIEF.md +95 -0
  98. package/extensions/agency-agents/agents/strategy/QUICKSTART.md +194 -0
  99. package/extensions/agency-agents/agents/strategy/coordination/agent-activation-prompts.md +401 -0
  100. package/extensions/agency-agents/agents/strategy/coordination/handoff-templates.md +357 -0
  101. package/extensions/agency-agents/agents/strategy/nexus-strategy.md +1110 -0
  102. package/extensions/agency-agents/agents/strategy/playbooks/phase-0-discovery.md +178 -0
  103. package/extensions/agency-agents/agents/strategy/playbooks/phase-1-strategy.md +238 -0
  104. package/extensions/agency-agents/agents/strategy/playbooks/phase-2-foundation.md +278 -0
  105. package/extensions/agency-agents/agents/strategy/playbooks/phase-3-build.md +286 -0
  106. package/extensions/agency-agents/agents/strategy/playbooks/phase-4-hardening.md +332 -0
  107. package/extensions/agency-agents/agents/strategy/playbooks/phase-5-launch.md +277 -0
  108. package/extensions/agency-agents/agents/strategy/playbooks/phase-6-operate.md +318 -0
  109. package/extensions/agency-agents/agents/strategy/runbooks/scenario-enterprise-feature.md +157 -0
  110. package/extensions/agency-agents/agents/strategy/runbooks/scenario-incident-response.md +217 -0
  111. package/extensions/agency-agents/agents/strategy/runbooks/scenario-marketing-campaign.md +187 -0
  112. package/extensions/agency-agents/agents/strategy/runbooks/scenario-startup-mvp.md +154 -0
  113. package/extensions/agency-agents/agents/support/support-analytics-reporter.md +363 -0
  114. package/extensions/agency-agents/agents/support/support-executive-summary-generator.md +210 -0
  115. package/extensions/agency-agents/agents/support/support-finance-tracker.md +440 -0
  116. package/extensions/agency-agents/agents/support/support-infrastructure-maintainer.md +616 -0
  117. package/extensions/agency-agents/agents/support/support-legal-compliance-checker.md +586 -0
  118. package/extensions/agency-agents/agents/support/support-support-responder.md +583 -0
  119. package/extensions/agency-agents/agents/testing/testing-accessibility-auditor.md +313 -0
  120. package/extensions/agency-agents/agents/testing/testing-api-tester.md +304 -0
  121. package/extensions/agency-agents/agents/testing/testing-evidence-collector.md +208 -0
  122. package/extensions/agency-agents/agents/testing/testing-performance-benchmarker.md +266 -0
  123. package/extensions/agency-agents/agents/testing/testing-reality-checker.md +236 -0
  124. package/extensions/agency-agents/agents/testing/testing-test-results-analyzer.md +303 -0
  125. package/extensions/agency-agents/agents/testing/testing-tool-evaluator.md +392 -0
  126. package/extensions/agency-agents/agents/testing/testing-workflow-optimizer.md +448 -0
  127. package/extensions/agency-agents/index.ts +733 -0
  128. package/extensions/agency-agents/node_modules/.bin/jiti +21 -0
  129. package/extensions/agency-agents/node_modules/.bin/tsc +21 -0
  130. package/extensions/agency-agents/node_modules/.bin/tsserver +21 -0
  131. package/extensions/agency-agents/node_modules/.bin/tsx +21 -0
  132. package/extensions/agency-agents/node_modules/.bin/vite +21 -0
  133. package/extensions/agency-agents/node_modules/.bin/vitest +21 -0
  134. package/extensions/agency-agents/node_modules/.bin/yaml +21 -0
  135. package/extensions/agency-agents/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  136. package/extensions/agency-agents/package.json +25 -0
  137. package/extensions/agency-agents/src/AgencyAgentsService.test.ts +443 -0
  138. package/extensions/agency-agents/src/AgencyAgentsService.ts +288 -0
  139. package/extensions/agency-agents/src/types.ts +147 -0
  140. package/extensions/agency-agents/vitest.config.ts +8 -0
  141. package/extensions/hexstrike-ai/README.md +98 -0
  142. package/extensions/hexstrike-ai/node_modules/.bin/tsc +21 -0
  143. package/extensions/hexstrike-ai/node_modules/.bin/tsserver +21 -0
  144. package/extensions/hexstrike-ai/package.json +29 -0
  145. package/extensions/hexstrike-ai/poolbot.plugin.json +31 -0
  146. package/extensions/hexstrike-ai/src/client.ts +91 -0
  147. package/extensions/hexstrike-ai/src/index.ts +170 -0
  148. package/extensions/hexstrike-ai/src/server/hexstrike_mcp.py +5470 -0
  149. package/extensions/hexstrike-ai/src/server/hexstrike_server.py +17289 -0
  150. package/extensions/hexstrike-ai/src/server/requirements.txt +84 -0
  151. package/extensions/hexstrike-ai/src/server-manager.ts +83 -0
  152. package/extensions/hexstrike-ai/tsconfig.json +20 -0
  153. package/extensions/hexstrike-bridge/package.json +1 -1
  154. package/extensions/hexstrike-bridge/poolbot.plugin.json +23 -0
  155. package/extensions/mcp-server/poolbot.plugin.json +10 -0
  156. package/extensions/page-agent/README.md +159 -0
  157. package/extensions/page-agent/index.ts +595 -0
  158. package/extensions/page-agent/node_modules/.bin/jiti +21 -0
  159. package/extensions/page-agent/node_modules/.bin/playwright +21 -0
  160. package/extensions/page-agent/node_modules/.bin/tsc +21 -0
  161. package/extensions/page-agent/node_modules/.bin/tsserver +21 -0
  162. package/extensions/page-agent/node_modules/.bin/tsx +21 -0
  163. package/extensions/page-agent/node_modules/.bin/vitest +21 -0
  164. package/extensions/page-agent/node_modules/.bin/yaml +21 -0
  165. package/extensions/page-agent/package.json +43 -0
  166. package/extensions/page-agent/src/PageAgentService.test.ts +517 -0
  167. package/extensions/page-agent/src/PageAgentService.ts +636 -0
  168. package/extensions/page-agent/src/PoolBotPageController.test.ts +358 -0
  169. package/extensions/page-agent/src/PoolBotPageController.ts +245 -0
  170. package/extensions/page-agent/src/index.ts +20 -0
  171. package/extensions/page-agent/src/tools.test.ts +231 -0
  172. package/extensions/page-agent/src/tools.ts +167 -0
  173. package/extensions/page-agent/src/types.ts +198 -0
  174. package/extensions/template/README.md +101 -0
  175. package/extensions/template/index.ts +38 -0
  176. package/extensions/template/package.json +15 -0
  177. package/extensions/template/poolbot.plugin.json +10 -0
  178. package/extensions/xyops/README.md +227 -0
  179. package/extensions/xyops/index.ts +342 -0
  180. package/extensions/xyops/node_modules/.bin/jiti +21 -0
  181. package/extensions/xyops/node_modules/.bin/tsc +21 -0
  182. package/extensions/xyops/node_modules/.bin/tsserver +21 -0
  183. package/extensions/xyops/node_modules/.bin/tsx +21 -0
  184. package/extensions/xyops/node_modules/.bin/vitest +21 -0
  185. package/extensions/xyops/node_modules/.bin/yaml +21 -0
  186. package/extensions/xyops/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  187. package/extensions/xyops/package.json +39 -0
  188. package/extensions/xyops/src/client.test.ts +467 -0
  189. package/extensions/xyops/src/client.ts +157 -0
  190. package/extensions/xyops/src/types.ts +147 -0
  191. package/extensions/xyops/vitest.config.ts +8 -0
  192. package/package.json +1 -1
  193. package/extensions/mavalie/README.md +0 -97
  194. package/extensions/mavalie/package.json +0 -15
  195. package/extensions/mavalie/src/index.ts +0 -62
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Cron Improvements
3
+ *
4
+ * Implements OpenClaw improvements:
5
+ * - Isolated delivery with delivery.mode: "none" | "webhook" | "announce"
6
+ * - Failure alerts configurable (thresholds, cooldowns)
7
+ * - Session target guardrail (reject sessionTarget: "main" for non-default agents)
8
+ * - Schedule errors with auto-disable and notification
9
+ * - One-shot retry with backoff configurable
10
+ *
11
+ * OpenClaw #32384, #30951
12
+ */
13
+ export const DEFAULT_CRON_DELIVERY_CONFIG = {
14
+ mode: "none", // Default to no delivery (silent)
15
+ timeoutMs: 30_000, // 30 seconds
16
+ retryOnFailure: true,
17
+ maxRetries: 3,
18
+ retryDelayMs: 5000,
19
+ };
20
+ export const DEFAULT_CRON_FAILURE_ALERT_CONFIG = {
21
+ enabled: true,
22
+ failureThreshold: 3, // Alert after 3 consecutive failures
23
+ cooldownMs: 60 * 60 * 1000, // 1 hour cooldown
24
+ channels: ["log"], // Default to log only
25
+ includeErrorDetails: true,
26
+ includeJobMetadata: true,
27
+ };
28
+ const cronJobFailureStates = new Map();
29
+ /**
30
+ * Get or create cron job failure state
31
+ */
32
+ export function getOrCreateCronJobFailureState(jobId) {
33
+ const existing = cronJobFailureStates.get(jobId);
34
+ if (existing) {
35
+ return existing;
36
+ }
37
+ const newState = {
38
+ consecutiveFailures: 0,
39
+ alertSuppressed: false,
40
+ };
41
+ cronJobFailureStates.set(jobId, newState);
42
+ return newState;
43
+ }
44
+ /**
45
+ * Record cron job failure and check if alert should be triggered
46
+ */
47
+ export function recordCronJobFailure(params) {
48
+ const { jobId, error, config } = params;
49
+ const state = getOrCreateCronJobFailureState(jobId);
50
+ state.consecutiveFailures += 1;
51
+ state.lastFailureAt = Date.now();
52
+ state.lastError = error;
53
+ if (!config.enabled) {
54
+ return { shouldAlert: false, suppressed: false };
55
+ }
56
+ // Check cooldown
57
+ const now = Date.now();
58
+ if (state.lastAlertAt && now - state.lastAlertAt < config.cooldownMs) {
59
+ state.alertSuppressed = true;
60
+ return { shouldAlert: false, suppressed: true };
61
+ }
62
+ // Check threshold
63
+ if (state.consecutiveFailures < config.failureThreshold) {
64
+ return { shouldAlert: false, suppressed: false };
65
+ }
66
+ state.alertSuppressed = false;
67
+ state.lastAlertAt = now;
68
+ return { shouldAlert: true, suppressed: false };
69
+ }
70
+ /**
71
+ * Record cron job success (resets failure count)
72
+ */
73
+ export function recordCronJobSuccess(jobId) {
74
+ const state = cronJobFailureStates.get(jobId);
75
+ if (state) {
76
+ state.consecutiveFailures = 0;
77
+ state.alertSuppressed = false;
78
+ }
79
+ }
80
+ export const DEFAULT_SESSION_TARGET_GUARDRAIL_CONFIG = {
81
+ enabled: true,
82
+ rejectMainForNonDefault: true,
83
+ allowExplicitOverride: true,
84
+ warnOnly: false,
85
+ };
86
+ /**
87
+ * Validate session target for cron job
88
+ */
89
+ export function validateSessionTarget(params) {
90
+ const { agentId, sessionTarget, defaultAgentId, config } = params;
91
+ if (!config.enabled) {
92
+ return { valid: true };
93
+ }
94
+ // Check if targeting "main" for non-default agent
95
+ if (config.rejectMainForNonDefault && sessionTarget === "main" && agentId !== defaultAgentId) {
96
+ const reason = `Cannot use sessionTarget "main" for non-default agent "${agentId}"`;
97
+ const suggestion = `Use explicit session key or target default agent "${defaultAgentId}"`;
98
+ if (config.warnOnly) {
99
+ // Log warning but allow
100
+ return { valid: true };
101
+ }
102
+ return { valid: false, reason, suggestion };
103
+ }
104
+ return { valid: true };
105
+ }
106
+ export const DEFAULT_SCHEDULE_ERROR_CONFIG = {
107
+ enabled: true,
108
+ autoDisableAfterErrors: 5,
109
+ errorWindowMs: 60 * 60 * 1000, // 1 hour window
110
+ notifyOnDisable: true,
111
+ notificationChannels: ["log"],
112
+ };
113
+ const scheduleErrorStates = new Map();
114
+ /**
115
+ * Get or create schedule error state
116
+ */
117
+ export function getOrCreateScheduleErrorState(scheduleId) {
118
+ const existing = scheduleErrorStates.get(scheduleId);
119
+ if (existing) {
120
+ return existing;
121
+ }
122
+ const newState = {
123
+ errorTimestamps: [],
124
+ disabled: false,
125
+ };
126
+ scheduleErrorStates.set(scheduleId, newState);
127
+ return newState;
128
+ }
129
+ /**
130
+ * Record schedule error and check if auto-disable should trigger
131
+ */
132
+ export function recordScheduleError(params) {
133
+ const { scheduleId, error: _error, config } = params;
134
+ const state = getOrCreateScheduleErrorState(scheduleId);
135
+ if (state.disabled) {
136
+ return { shouldDisable: false, shouldNotify: false };
137
+ }
138
+ const now = Date.now();
139
+ // Clean old errors outside window
140
+ state.errorTimestamps = state.errorTimestamps.filter((ts) => now - ts < config.errorWindowMs);
141
+ // Add new error
142
+ state.errorTimestamps.push(now);
143
+ if (!config.enabled) {
144
+ return { shouldDisable: false, shouldNotify: false };
145
+ }
146
+ // Check if should auto-disable
147
+ if (state.errorTimestamps.length >= config.autoDisableAfterErrors) {
148
+ state.disabled = true;
149
+ state.disabledAt = now;
150
+ state.disableReason = `Auto-disabled after ${state.errorTimestamps.length} errors in ${config.errorWindowMs / 1000}s`;
151
+ return { shouldDisable: true, shouldNotify: config.notifyOnDisable };
152
+ }
153
+ return { shouldDisable: false, shouldNotify: false };
154
+ }
155
+ /**
156
+ * Reset schedule error state (manual re-enable)
157
+ */
158
+ export function resetScheduleErrorState(scheduleId) {
159
+ const state = scheduleErrorStates.get(scheduleId);
160
+ if (state) {
161
+ state.errorTimestamps = [];
162
+ state.disabled = false;
163
+ state.disabledAt = undefined;
164
+ state.disableReason = undefined;
165
+ }
166
+ }
167
+ export const DEFAULT_ONESHOT_RETRY_CONFIG = {
168
+ enabled: true,
169
+ maxRetries: 3,
170
+ baseDelayMs: 1000,
171
+ maxDelayMs: 60_000, // 1 minute
172
+ backoffMultiplier: 2,
173
+ jitterFactor: 0.1, // 10% jitter
174
+ };
175
+ /**
176
+ * Calculate retry delay with exponential backoff and jitter
177
+ */
178
+ export function calculateRetryDelay(attempt, config = DEFAULT_ONESHOT_RETRY_CONFIG) {
179
+ if (!config.enabled || attempt >= config.maxRetries) {
180
+ return 0;
181
+ }
182
+ // Exponential backoff
183
+ const exponentialDelay = config.baseDelayMs * Math.pow(config.backoffMultiplier, attempt);
184
+ // Cap at max delay
185
+ const cappedDelay = Math.min(exponentialDelay, config.maxDelayMs);
186
+ // Add jitter
187
+ const jitter = cappedDelay * config.jitterFactor * (Math.random() * 2 - 1);
188
+ return Math.max(0, Math.floor(cappedDelay + jitter));
189
+ }
190
+ /**
191
+ * Check if retry is allowed
192
+ */
193
+ export function canRetry(attempt, config = DEFAULT_ONESHOT_RETRY_CONFIG) {
194
+ return config.enabled && attempt < config.maxRetries;
195
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Discord Channel Improvements
3
+ *
4
+ * Implements OpenClaw improvements:
5
+ * - Thread-bound subagent sessions (enhanced)
6
+ * - Status reactions configuráveis
7
+ * - Voice channel join/leave via /vc
8
+ * - Native slash command auth
9
+ * - Component interactions with wildcard handlers
10
+ *
11
+ * OpenClaw #32384, #30951
12
+ */
13
+ export const DEFAULT_VOICE_CHANNEL_CONFIG = {
14
+ enabled: true,
15
+ autoJoin: false,
16
+ autoLeaveAfterSilenceMs: 5 * 60 * 1000, // 5 minutes
17
+ maxChannelsPerGuild: 5,
18
+ enableTts: false,
19
+ };
20
+ const voiceChannelSessions = new Map();
21
+ /**
22
+ * Get or create voice channel session
23
+ */
24
+ export function getOrCreateVoiceChannelSession(params) {
25
+ const { channelId, guildId, ttsEnabled = false } = params;
26
+ const key = `${guildId}:${channelId}`;
27
+ const existing = voiceChannelSessions.get(key);
28
+ if (existing) {
29
+ return existing;
30
+ }
31
+ const session = {
32
+ channelId,
33
+ guildId,
34
+ joinedAt: Date.now(),
35
+ lastActivityAt: Date.now(),
36
+ ttsEnabled,
37
+ };
38
+ voiceChannelSessions.set(key, session);
39
+ return session;
40
+ }
41
+ /**
42
+ * Update voice channel session activity
43
+ */
44
+ export function updateVoiceChannelActivity(channelId, guildId) {
45
+ const key = `${guildId}:${channelId}`;
46
+ const session = voiceChannelSessions.get(key);
47
+ if (session) {
48
+ session.lastActivityAt = Date.now();
49
+ }
50
+ }
51
+ /**
52
+ * Remove voice channel session
53
+ */
54
+ export function removeVoiceChannelSession(channelId, guildId) {
55
+ const key = `${guildId}:${channelId}`;
56
+ return voiceChannelSessions.delete(key);
57
+ }
58
+ /**
59
+ * Check for stale voice channel sessions
60
+ */
61
+ export function getStaleVoiceChannelSessions(config = DEFAULT_VOICE_CHANNEL_CONFIG) {
62
+ const now = Date.now();
63
+ const stale = [];
64
+ for (const session of voiceChannelSessions.values()) {
65
+ if (now - session.lastActivityAt > config.autoLeaveAfterSilenceMs) {
66
+ stale.push(session);
67
+ }
68
+ }
69
+ return stale;
70
+ }
71
+ export const DEFAULT_SLASH_COMMAND_AUTH_CONFIG = {
72
+ enabled: true,
73
+ requireAuthForAll: false,
74
+ protectedCommands: ["admin", "config", "pair", "auth"],
75
+ allowLoopback: true,
76
+ authTimeoutMs: 5 * 60 * 1000, // 5 minutes
77
+ };
78
+ /**
79
+ * Check if a slash command requires authentication
80
+ */
81
+ export function isSlashCommandProtected(commandName, config = DEFAULT_SLASH_COMMAND_AUTH_CONFIG) {
82
+ if (!config.enabled) {
83
+ return false;
84
+ }
85
+ if (config.requireAuthForAll) {
86
+ return true;
87
+ }
88
+ return config.protectedCommands.includes(commandName.toLowerCase());
89
+ }
90
+ export const DEFAULT_COMPONENT_INTERACTION_CONFIG = {
91
+ enabled: true,
92
+ wildcardPatterns: ["action:*", "button:*", "select:*", "modal:*"],
93
+ rateLimitPerMinute: 60,
94
+ responseTimeoutMs: 3000, // Discord requires response within 3 seconds
95
+ };
96
+ /**
97
+ * Match custom ID against wildcard patterns
98
+ */
99
+ export function matchWildcardPattern(customId, config = DEFAULT_COMPONENT_INTERACTION_CONFIG) {
100
+ if (!config.enabled) {
101
+ return null;
102
+ }
103
+ for (const pattern of config.wildcardPatterns) {
104
+ // Convert wildcard pattern to regex
105
+ // * matches any characters except :
106
+ const regexPattern = pattern.replace(/\*/g, "[^:]*");
107
+ const regex = new RegExp(`^${regexPattern}$`);
108
+ if (regex.test(customId)) {
109
+ return pattern;
110
+ }
111
+ }
112
+ return null;
113
+ }
114
+ /**
115
+ * Component interaction rate limiting
116
+ */
117
+ const interactionCounts = new Map();
118
+ /**
119
+ * Check if component interaction is rate limited
120
+ */
121
+ export function checkComponentInteractionRateLimit(userId, config = DEFAULT_COMPONENT_INTERACTION_CONFIG) {
122
+ const now = Date.now();
123
+ const windowMs = 60_000; // 1 minute
124
+ const timestamps = interactionCounts.get(userId) ?? [];
125
+ const recentTimestamps = timestamps.filter((ts) => now - ts < windowMs);
126
+ if (recentTimestamps.length >= config.rateLimitPerMinute) {
127
+ const oldestTimestamp = recentTimestamps[0];
128
+ const retryAfterMs = windowMs - (now - oldestTimestamp);
129
+ return { allowed: false, retryAfterMs };
130
+ }
131
+ recentTimestamps.push(now);
132
+ interactionCounts.set(userId, recentTimestamps);
133
+ return { allowed: true };
134
+ }
135
+ export const DEFAULT_THREAD_BINDING_ENHANCEMENT = {
136
+ autoBindSubagents: true,
137
+ threadTtlMs: 60 * 60 * 1000, // 1 hour
138
+ autoUnbindOnSessionEnd: true,
139
+ maxThreadsPerSession: 10,
140
+ };
141
+ export const DEFAULT_DISCORD_STATUS_REACTION_CONFIG = {
142
+ enabled: true,
143
+ showInThreads: true,
144
+ showInChannels: false, // Don't spam channels by default
145
+ useEmojiReactions: true,
146
+ useEmbedStatus: false,
147
+ emojis: {
148
+ queued: "👀",
149
+ thinking: "🤔",
150
+ tool: "⚡",
151
+ coding: "👨‍💻",
152
+ web: "🌐",
153
+ done: "✅",
154
+ error: "❌",
155
+ stallSoft: "⏳",
156
+ stallHard: "⚠️",
157
+ },
158
+ };
159
+ /**
160
+ * Get status reaction emoji for a given status
161
+ */
162
+ export function getStatusReactionEmoji(status, config = DEFAULT_DISCORD_STATUS_REACTION_CONFIG) {
163
+ if (!config.enabled) {
164
+ return null;
165
+ }
166
+ return config.emojis[status.toLowerCase()] ?? null;
167
+ }
@@ -26,6 +26,12 @@ const DEFAULT_MAX_ATTEMPTS = 10;
26
26
  const DEFAULT_WINDOW_MS = 60_000; // 1 minute
27
27
  const DEFAULT_LOCKOUT_MS = 300_000; // 5 minutes
28
28
  const PRUNE_INTERVAL_MS = 60_000; // prune stale entries every minute
29
+ // Control-plane rate limiting: 3 requests per minute per device+IP
30
+ // OpenClaw #32384 - prevents abuse of control-plane endpoints
31
+ export const CONTROL_PLANE_MAX_ATTEMPTS = 3;
32
+ export const CONTROL_PLANE_WINDOW_MS = 60_000; // 1 minute
33
+ export const CONTROL_PLANE_LOCKOUT_MS = 300_000; // 5 minutes
34
+ export const AUTH_RATE_LIMIT_SCOPE_CONTROL_PLANE = "control-plane";
29
35
  // ---------------------------------------------------------------------------
30
36
  // Implementation
31
37
  // ---------------------------------------------------------------------------
@@ -134,3 +140,16 @@ export function createAuthRateLimiter(config) {
134
140
  }
135
141
  return { check, recordFailure, reset, size, prune, dispose };
136
142
  }
143
+ /**
144
+ * Create a rate limiter specifically for control-plane operations.
145
+ * Uses stricter limits (3 req/min) to prevent abuse of sensitive endpoints.
146
+ * OpenClaw #32384
147
+ */
148
+ export function createControlPlaneRateLimiter() {
149
+ return createAuthRateLimiter({
150
+ maxAttempts: CONTROL_PLANE_MAX_ATTEMPTS,
151
+ windowMs: CONTROL_PLANE_WINDOW_MS,
152
+ lockoutMs: CONTROL_PLANE_LOCKOUT_MS,
153
+ exemptLoopback: true, // Always exempt loopback for local development
154
+ });
155
+ }
@@ -222,6 +222,47 @@ function authorizeTrustedProxy(params) {
222
222
  function shouldAllowTailscaleHeaderAuth(authSurface) {
223
223
  return authSurface === "ws-control-ui";
224
224
  }
225
+ /**
226
+ * Check if a request is from loopback (localhost) for auto-approve scenarios.
227
+ * OpenClaw #32384 - loopback requests get auto-approved for pairing scope upgrades.
228
+ */
229
+ export function isLoopbackRequest(req) {
230
+ if (!req?.socket?.remoteAddress) {
231
+ return false;
232
+ }
233
+ return isLoopbackAddress(req.socket.remoteAddress);
234
+ }
235
+ /**
236
+ * Auto-approve scope upgrades for loopback requests.
237
+ * This allows tokenless local development while maintaining security for remote requests.
238
+ * OpenClaw #32384
239
+ */
240
+ export function shouldAutoApproveScopeUpgrade(params) {
241
+ const { req, requestedScopes, currentScopes } = params;
242
+ // Only auto-approve for loopback requests
243
+ if (!isLoopbackRequest(req)) {
244
+ return false;
245
+ }
246
+ // Check if requested scopes are strictly greater than current scopes
247
+ const currentSet = new Set(currentScopes.map((s) => s.trim().toLowerCase()));
248
+ const newScopes = requestedScopes.filter((s) => !currentSet.has(s.trim().toLowerCase()));
249
+ // Auto-approve if all new scopes are "safe" (non-destructive operations)
250
+ const safeScopes = new Set([
251
+ "read",
252
+ "read:config",
253
+ "read:status",
254
+ "write",
255
+ "write:config",
256
+ "execute",
257
+ "execute:tool",
258
+ ]);
259
+ return newScopes.every((scope) => {
260
+ const normalized = scope.trim().toLowerCase();
261
+ return (safeScopes.has(normalized) ||
262
+ normalized.startsWith("read:") ||
263
+ normalized.startsWith("execute:"));
264
+ });
265
+ }
225
266
  export async function authorizeGatewayConnect(params) {
226
267
  const { auth, connectAuth, req, trustedProxies } = params;
227
268
  const tailscaleWhois = params.tailscaleWhois ?? readTailscaleWhoisIdentity;