@@ -49,10 +49,21 @@ type ServerNotification = {
4949
5050const CLAUDE_PERMISSION_REPLY_RE = / ^ ( y e s | n o ) \s + ( [ a - k m - z ] { 5 } ) $ / i;
5151const QUEUE_LIMIT = 1_000 ;
52+ const CONVERSATIONS_LIST_LIMIT = 500 ;
53+ const MESSAGES_READ_LIMIT = 200 ;
54+ const EVENTS_POLL_LIMIT = 200 ;
55+ const EVENTS_WAIT_TIMEOUT_LIMIT_MS = 300_000 ;
5256const PENDING_CLAUDE_PERMISSION_TTL_MS = 60 * 60 * 1_000 ;
5357const PENDING_APPROVAL_DEFAULT_TTL_MS = 30 * 60 * 1_000 ;
5458const PENDING_SWEEP_INTERVAL_MS = 5 * 60 * 1_000 ;
5559
60+ function clampPositiveInteger ( value : number | undefined , fallback : number , max : number ) : number {
61+ if ( typeof value !== "number" || ! Number . isFinite ( value ) ) {
62+ return fallback ;
63+ }
64+ return Math . min ( max , Math . max ( 1 , Math . floor ( value ) ) ) ;
65+ }
66+
5667/** Connects the MCP server surface to a Gateway client and queues channel events for polling. */
5768export class OpenClawChannelBridge {
5869 private gateway : GatewayClient | null = null ;
@@ -212,8 +223,9 @@ export class OpenClawChannelBridge {
212223 includeLastMessage ?: boolean ;
213224 } ) : Promise < ConversationDescriptor [ ] > {
214225 await this . waitUntilReady ( ) ;
226+ const limit = clampPositiveInteger ( params ?. limit , 50 , CONVERSATIONS_LIST_LIMIT ) ;
215227 const response : SessionListResult = await this . requestGateway ( "sessions.list" , {
216- limit : params ?. limit ?? 50 ,
228+ limit,
217229 search : params ?. search ,
218230 includeDerivedTitles : params ?. includeDerivedTitles ?? true ,
219231 includeLastMessage : params ?. includeLastMessage ?? true ,
@@ -250,9 +262,10 @@ export class OpenClawChannelBridge {
250262 limit = 20 ,
251263 ) : Promise < NonNullable < ChatHistoryResult [ "messages" ] > > {
252264 await this . waitUntilReady ( ) ;
265+ const requestLimit = clampPositiveInteger ( limit , 20 , MESSAGES_READ_LIMIT ) ;
253266 const response : ChatHistoryResult = await this . requestGateway ( "sessions.get" , {
254267 key : sessionKey ,
255- limit,
268+ limit : requestLimit ,
256269 } ) ;
257270 return response . messages ?? [ ] ;
258271 }
@@ -307,7 +320,10 @@ export class OpenClawChannelBridge {
307320
308321 /** Poll queued events after a cursor without consuming them. */
309322 pollEvents ( filter : WaitFilter , limit = 20 ) : { events : QueueEvent [ ] ; nextCursor : number } {
310- const events = this . queue . filter ( ( event ) => matchEventFilter ( event , filter ) ) . slice ( 0 , limit ) ;
323+ const eventLimit = clampPositiveInteger ( limit , 20 , EVENTS_POLL_LIMIT ) ;
324+ const events = this . queue
325+ . filter ( ( event ) => matchEventFilter ( event , filter ) )
326+ . slice ( 0 , eventLimit ) ;
311327 const nextCursor = events . at ( - 1 ) ?. cursor ?? filter . afterCursor ;
312328 return { events, nextCursor } ;
313329 }
@@ -318,6 +334,7 @@ export class OpenClawChannelBridge {
318334 if ( existing ) {
319335 return existing ;
320336 }
337+ const waitTimeoutMs = clampPositiveInteger ( timeoutMs , 30_000 , EVENTS_WAIT_TIMEOUT_LIMIT_MS ) ;
321338 return await new Promise < QueueEvent | null > ( ( resolve ) => {
322339 const waiter : PendingWaiter = {
323340 filter,
@@ -327,11 +344,9 @@ export class OpenClawChannelBridge {
327344 } ,
328345 timeout : null ,
329346 } ;
330- if ( timeoutMs > 0 ) {
331- waiter . timeout = setTimeout ( ( ) => {
332- waiter . resolve ( null ) ;
333- } , timeoutMs ) ;
334- }
347+ waiter . timeout = setTimeout ( ( ) => {
348+ waiter . resolve ( null ) ;
349+ } , waitTimeoutMs ) ;
335350 this . pendingWaiters . add ( waiter ) ;
336351 } ) ;
337352 }
0 commit comments