@elizaos/server 1.4.2 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1398,13 +1398,13 @@ var InternalMessageBus = class extends EventTarget {
1398
1398
  if (eventHandlers.has(handler)) {
1399
1399
  return this;
1400
1400
  }
1401
- const wrappedHandler = (e) => {
1401
+ const wrappedHandler = ((e) => {
1402
1402
  if (e instanceof CustomEvent) {
1403
1403
  handler(e.detail);
1404
1404
  } else {
1405
1405
  handler(void 0);
1406
1406
  }
1407
- };
1407
+ });
1408
1408
  eventHandlers.set(handler, wrappedHandler);
1409
1409
  this.addEventListener(event, wrappedHandler);
1410
1410
  return this;
@@ -2855,92 +2855,518 @@ function v4(options, buf, offset) {
2855
2855
  }
2856
2856
  var v4_default = v4;
2857
2857
 
2858
+ // src/api/messaging/errors/SessionErrors.ts
2859
+ var SessionError = class extends Error {
2860
+ code;
2861
+ statusCode;
2862
+ details;
2863
+ timestamp;
2864
+ constructor(code, message, statusCode = 500, details) {
2865
+ super(message);
2866
+ this.name = this.constructor.name;
2867
+ this.code = code;
2868
+ this.statusCode = statusCode;
2869
+ this.details = details;
2870
+ this.timestamp = /* @__PURE__ */ new Date();
2871
+ if (Error.captureStackTrace) {
2872
+ Error.captureStackTrace(this, this.constructor);
2873
+ }
2874
+ }
2875
+ /**
2876
+ * Convert error to JSON for API responses
2877
+ */
2878
+ toJSON() {
2879
+ return {
2880
+ error: {
2881
+ code: this.code,
2882
+ message: this.message,
2883
+ timestamp: this.timestamp.toISOString(),
2884
+ ...process.env.NODE_ENV === "development" && {
2885
+ details: this.details,
2886
+ stack: this.stack
2887
+ }
2888
+ }
2889
+ };
2890
+ }
2891
+ };
2892
+ var SessionNotFoundError = class extends SessionError {
2893
+ constructor(sessionId, details) {
2894
+ super("SESSION_NOT_FOUND", `Session with ID '${sessionId}' not found`, 404, details);
2895
+ }
2896
+ };
2897
+ var SessionExpiredError = class extends SessionError {
2898
+ constructor(sessionId, expiredAt, details) {
2899
+ const message = expiredAt ? `Session '${sessionId}' expired at ${expiredAt.toISOString()}` : `Session '${sessionId}' has expired`;
2900
+ super("SESSION_EXPIRED", message, 410, details);
2901
+ }
2902
+ };
2903
+ var SessionCreationError = class extends SessionError {
2904
+ constructor(reason, details) {
2905
+ super("SESSION_CREATION_FAILED", `Failed to create session: ${reason}`, 500, details);
2906
+ }
2907
+ };
2908
+ var AgentNotFoundError = class extends SessionError {
2909
+ constructor(agentId, details) {
2910
+ super("AGENT_NOT_FOUND", `Agent with ID '${agentId}' not found`, 404, details);
2911
+ }
2912
+ };
2913
+ var ValidationError = class extends SessionError {
2914
+ field;
2915
+ value;
2916
+ constructor(message, field, value, details) {
2917
+ super("VALIDATION_ERROR", message, 400, details);
2918
+ this.field = field;
2919
+ this.value = value;
2920
+ }
2921
+ };
2922
+ var InvalidUuidError = class extends ValidationError {
2923
+ constructor(field, value) {
2924
+ super(`Invalid UUID format for field '${field}'`, field, value, {
2925
+ providedValue: value,
2926
+ expectedFormat: "UUID v4"
2927
+ });
2928
+ }
2929
+ };
2930
+ var MissingFieldsError = class extends ValidationError {
2931
+ constructor(fields) {
2932
+ super(`Missing required fields: ${fields.join(", ")}`, void 0, void 0, {
2933
+ missingFields: fields
2934
+ });
2935
+ }
2936
+ };
2937
+ var InvalidContentError = class extends ValidationError {
2938
+ constructor(reason, content) {
2939
+ super(`Invalid content: ${reason}`, "content", content, { reason });
2940
+ }
2941
+ };
2942
+ var InvalidMetadataError = class extends ValidationError {
2943
+ constructor(reason, metadata) {
2944
+ super(`Invalid metadata: ${reason}`, "metadata", metadata, {
2945
+ reason,
2946
+ providedMetadata: metadata
2947
+ });
2948
+ }
2949
+ };
2950
+ var InvalidPaginationError = class extends ValidationError {
2951
+ constructor(parameter, value, reason) {
2952
+ super(`Invalid pagination parameter '${parameter}': ${reason}`, parameter, value, {
2953
+ parameter,
2954
+ value,
2955
+ reason
2956
+ });
2957
+ }
2958
+ };
2959
+ var InvalidTimeoutConfigError = class extends ValidationError {
2960
+ constructor(reason, config) {
2961
+ super(`Invalid timeout configuration: ${reason}`, "timeoutConfig", config, {
2962
+ reason,
2963
+ providedConfig: config
2964
+ });
2965
+ }
2966
+ };
2967
+ var SessionRenewalError = class extends SessionError {
2968
+ constructor(sessionId, reason, details) {
2969
+ super("SESSION_RENEWAL_FAILED", `Cannot renew session '${sessionId}': ${reason}`, 400, details);
2970
+ }
2971
+ };
2972
+ var MessageSendError = class extends SessionError {
2973
+ constructor(sessionId, reason, details) {
2974
+ super(
2975
+ "MESSAGE_SEND_FAILED",
2976
+ `Failed to send message in session '${sessionId}': ${reason}`,
2977
+ 500,
2978
+ details
2979
+ );
2980
+ }
2981
+ };
2982
+ function createErrorHandler() {
2983
+ return (err, _req, res, next) => {
2984
+ if (res.headersSent) {
2985
+ return next(err);
2986
+ }
2987
+ if (err instanceof SessionError) {
2988
+ return res.status(err.statusCode).json(err.toJSON());
2989
+ }
2990
+ if (err.name === "ValidationError") {
2991
+ return res.status(400).json({
2992
+ error: {
2993
+ code: "VALIDATION_ERROR",
2994
+ message: err.message,
2995
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2996
+ }
2997
+ });
2998
+ }
2999
+ console.error("Unexpected error:", err);
3000
+ return res.status(500).json({
3001
+ error: {
3002
+ code: "INTERNAL_SERVER_ERROR",
3003
+ message: "An unexpected error occurred",
3004
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3005
+ ...process.env.NODE_ENV === "development" && {
3006
+ details: err.message,
3007
+ stack: err.stack
3008
+ }
3009
+ }
3010
+ });
3011
+ };
3012
+ }
3013
+
2858
3014
  // src/api/messaging/sessions.ts
2859
- var SESSION_TIMEOUT_MS = parseInt(process.env.SESSION_TIMEOUT_MINUTES || "30") * 60 * 1e3;
3015
+ function safeParseInt(value, fallback, min, max) {
3016
+ if (!value) {
3017
+ return fallback;
3018
+ }
3019
+ const parsed = parseInt(value, 10);
3020
+ if (isNaN(parsed) || !isFinite(parsed)) {
3021
+ logger13.warn(`[Sessions API] Invalid integer value: "${value}", using fallback: ${fallback}`);
3022
+ return fallback;
3023
+ }
3024
+ let result = parsed;
3025
+ if (min !== void 0 && result < min) {
3026
+ logger13.warn(`[Sessions API] Value ${result} is below minimum ${min}, clamping to minimum`);
3027
+ result = min;
3028
+ }
3029
+ if (max !== void 0 && result > max) {
3030
+ logger13.warn(`[Sessions API] Value ${result} is above maximum ${max}, clamping to maximum`);
3031
+ result = max;
3032
+ }
3033
+ return result;
3034
+ }
3035
+ var DEFAULT_TIMEOUT_MINUTES = safeParseInt(
3036
+ process.env.SESSION_DEFAULT_TIMEOUT_MINUTES,
3037
+ 30,
3038
+ 1,
3039
+ 10080
3040
+ // 7 days max
3041
+ );
3042
+ var MIN_TIMEOUT_MINUTES = safeParseInt(process.env.SESSION_MIN_TIMEOUT_MINUTES, 5, 1, 60);
3043
+ var MAX_TIMEOUT_MINUTES = safeParseInt(
3044
+ process.env.SESSION_MAX_TIMEOUT_MINUTES,
3045
+ 1440,
3046
+ // 24 hours
3047
+ 60,
3048
+ 10080
3049
+ // 7 days max
3050
+ );
3051
+ var DEFAULT_MAX_DURATION_MINUTES = safeParseInt(
3052
+ process.env.SESSION_MAX_DURATION_MINUTES,
3053
+ 720,
3054
+ // 12 hours
3055
+ 60,
3056
+ 20160
3057
+ // 14 days max
3058
+ );
3059
+ var DEFAULT_WARNING_THRESHOLD_MINUTES = safeParseInt(
3060
+ process.env.SESSION_WARNING_THRESHOLD_MINUTES,
3061
+ 5,
3062
+ 1,
3063
+ 60
3064
+ );
3065
+ var CLEANUP_INTERVAL_MS = safeParseInt(process.env.SESSION_CLEANUP_INTERVAL_MINUTES, 5, 1, 60) * 60 * 1e3;
2860
3066
  var sessions = /* @__PURE__ */ new Map();
2861
3067
  var DEFAULT_SERVER_ID4 = "00000000-0000-0000-0000-000000000000";
3068
+ var agentTimeoutConfigs = /* @__PURE__ */ new Map();
3069
+ var activeCleanupIntervals = /* @__PURE__ */ new Set();
3070
+ var processHandlersRegistered = false;
3071
+ function isValidSession(obj) {
3072
+ if (!obj || typeof obj !== "object") {
3073
+ return false;
3074
+ }
3075
+ const session = obj;
3076
+ return typeof session.id === "string" && typeof session.agentId === "string" && typeof session.channelId === "string" && typeof session.userId === "string" && session.createdAt instanceof Date && session.lastActivity instanceof Date && session.expiresAt instanceof Date && typeof session.renewalCount === "number" && session.timeoutConfig !== void 0 && typeof session.timeoutConfig === "object";
3077
+ }
3078
+ function isCreateSessionRequest(obj) {
3079
+ if (!obj || typeof obj !== "object") {
3080
+ return false;
3081
+ }
3082
+ const req = obj;
3083
+ return typeof req.agentId === "string" && typeof req.userId === "string";
3084
+ }
3085
+ function isSendMessageRequest(obj) {
3086
+ if (!obj || typeof obj !== "object") {
3087
+ return false;
3088
+ }
3089
+ const req = obj;
3090
+ return typeof req.content === "string";
3091
+ }
3092
+ function isValidTimeoutConfig(obj) {
3093
+ if (!obj || typeof obj !== "object") {
3094
+ return false;
3095
+ }
3096
+ const config = obj;
3097
+ return (config.timeoutMinutes === void 0 || typeof config.timeoutMinutes === "number" || typeof config.timeoutMinutes === "string") && (config.autoRenew === void 0 || typeof config.autoRenew === "boolean") && (config.maxDurationMinutes === void 0 || typeof config.maxDurationMinutes === "number" || typeof config.maxDurationMinutes === "string") && (config.warningThresholdMinutes === void 0 || typeof config.warningThresholdMinutes === "number" || typeof config.warningThresholdMinutes === "string");
3098
+ }
2862
3099
  var MAX_CONTENT_LENGTH = 4e3;
2863
3100
  var MAX_METADATA_SIZE = 1024 * 10;
2864
3101
  var MAX_LIMIT = 100;
2865
3102
  var DEFAULT_LIMIT = 50;
3103
+ function getAgentTimeoutConfig(agent) {
3104
+ if (agentTimeoutConfigs.has(agent.agentId)) {
3105
+ return agentTimeoutConfigs.get(agent.agentId);
3106
+ }
3107
+ const timeoutSetting = agent.getSetting("SESSION_TIMEOUT_MINUTES");
3108
+ const maxDurationSetting = agent.getSetting("SESSION_MAX_DURATION_MINUTES");
3109
+ const warningThresholdSetting = agent.getSetting("SESSION_WARNING_THRESHOLD_MINUTES");
3110
+ const agentConfig = {
3111
+ timeoutMinutes: timeoutSetting ? safeParseInt(
3112
+ String(timeoutSetting),
3113
+ DEFAULT_TIMEOUT_MINUTES,
3114
+ MIN_TIMEOUT_MINUTES,
3115
+ MAX_TIMEOUT_MINUTES
3116
+ ) : DEFAULT_TIMEOUT_MINUTES,
3117
+ autoRenew: agent.getSetting("SESSION_AUTO_RENEW") ? agent.getSetting("SESSION_AUTO_RENEW") === "true" : true,
3118
+ maxDurationMinutes: maxDurationSetting ? safeParseInt(
3119
+ String(maxDurationSetting),
3120
+ DEFAULT_MAX_DURATION_MINUTES,
3121
+ MIN_TIMEOUT_MINUTES,
3122
+ MAX_TIMEOUT_MINUTES * 2
3123
+ ) : DEFAULT_MAX_DURATION_MINUTES,
3124
+ warningThresholdMinutes: warningThresholdSetting ? safeParseInt(
3125
+ String(warningThresholdSetting),
3126
+ DEFAULT_WARNING_THRESHOLD_MINUTES,
3127
+ 1,
3128
+ MAX_TIMEOUT_MINUTES
3129
+ ) : DEFAULT_WARNING_THRESHOLD_MINUTES
3130
+ };
3131
+ agentTimeoutConfigs.set(agent.agentId, agentConfig);
3132
+ return agentConfig;
3133
+ }
3134
+ function mergeTimeoutConfigs(sessionConfig, agentConfig) {
3135
+ const merged = {
3136
+ timeoutMinutes: DEFAULT_TIMEOUT_MINUTES,
3137
+ autoRenew: true,
3138
+ maxDurationMinutes: DEFAULT_MAX_DURATION_MINUTES,
3139
+ warningThresholdMinutes: DEFAULT_WARNING_THRESHOLD_MINUTES
3140
+ };
3141
+ if (agentConfig) {
3142
+ Object.assign(merged, agentConfig);
3143
+ }
3144
+ if (sessionConfig) {
3145
+ if (sessionConfig.timeoutMinutes !== void 0) {
3146
+ const timeoutValue = Number(sessionConfig.timeoutMinutes);
3147
+ if (isNaN(timeoutValue) || !isFinite(timeoutValue)) {
3148
+ logger13.warn(
3149
+ `[Sessions API] Invalid timeout minutes in session config: ${sessionConfig.timeoutMinutes}, using default`
3150
+ );
3151
+ merged.timeoutMinutes = DEFAULT_TIMEOUT_MINUTES;
3152
+ } else {
3153
+ const timeout = Math.max(MIN_TIMEOUT_MINUTES, Math.min(MAX_TIMEOUT_MINUTES, timeoutValue));
3154
+ merged.timeoutMinutes = timeout;
3155
+ }
3156
+ }
3157
+ if (sessionConfig.autoRenew !== void 0) {
3158
+ merged.autoRenew = sessionConfig.autoRenew;
3159
+ }
3160
+ if (sessionConfig.maxDurationMinutes !== void 0) {
3161
+ const maxDurationValue = Number(sessionConfig.maxDurationMinutes);
3162
+ if (isNaN(maxDurationValue) || !isFinite(maxDurationValue)) {
3163
+ logger13.warn(
3164
+ `[Sessions API] Invalid max duration minutes in session config: ${sessionConfig.maxDurationMinutes}, using default`
3165
+ );
3166
+ merged.maxDurationMinutes = DEFAULT_MAX_DURATION_MINUTES;
3167
+ } else {
3168
+ merged.maxDurationMinutes = Math.max(
3169
+ merged.timeoutMinutes,
3170
+ Math.min(MAX_TIMEOUT_MINUTES * 2, maxDurationValue)
3171
+ );
3172
+ }
3173
+ }
3174
+ if (sessionConfig.warningThresholdMinutes !== void 0) {
3175
+ const warningValue = Number(sessionConfig.warningThresholdMinutes);
3176
+ if (isNaN(warningValue) || !isFinite(warningValue)) {
3177
+ logger13.warn(
3178
+ `[Sessions API] Invalid warning threshold minutes in session config: ${sessionConfig.warningThresholdMinutes}, using default`
3179
+ );
3180
+ merged.warningThresholdMinutes = DEFAULT_WARNING_THRESHOLD_MINUTES;
3181
+ } else {
3182
+ merged.warningThresholdMinutes = Math.max(1, warningValue);
3183
+ }
3184
+ }
3185
+ }
3186
+ return merged;
3187
+ }
3188
+ function calculateExpirationDate(createdAt, lastActivity, config, _renewalCount) {
3189
+ const baseTime = config.autoRenew ? lastActivity : createdAt;
3190
+ const timeoutMs = (config.timeoutMinutes || DEFAULT_TIMEOUT_MINUTES) * 60 * 1e3;
3191
+ if (config.maxDurationMinutes) {
3192
+ const maxDurationMs = config.maxDurationMinutes * 60 * 1e3;
3193
+ const timeSinceCreation = Date.now() - createdAt.getTime();
3194
+ if (timeSinceCreation + timeoutMs > maxDurationMs) {
3195
+ return new Date(createdAt.getTime() + maxDurationMs);
3196
+ }
3197
+ }
3198
+ return new Date(baseTime.getTime() + timeoutMs);
3199
+ }
3200
+ function shouldWarnAboutExpiration(session) {
3201
+ if (session.warningState?.sent) {
3202
+ return false;
3203
+ }
3204
+ const warningThresholdMs = (session.timeoutConfig.warningThresholdMinutes || DEFAULT_WARNING_THRESHOLD_MINUTES) * 60 * 1e3;
3205
+ const timeRemaining = session.expiresAt.getTime() - Date.now();
3206
+ return timeRemaining <= warningThresholdMs && timeRemaining > 0;
3207
+ }
3208
+ function renewSession(session) {
3209
+ if (!session.timeoutConfig.autoRenew) {
3210
+ return false;
3211
+ }
3212
+ const now = /* @__PURE__ */ new Date();
3213
+ const maxDurationMs = (session.timeoutConfig.maxDurationMinutes || DEFAULT_MAX_DURATION_MINUTES) * 60 * 1e3;
3214
+ const timeSinceCreation = now.getTime() - session.createdAt.getTime();
3215
+ if (timeSinceCreation >= maxDurationMs) {
3216
+ return false;
3217
+ }
3218
+ session.lastActivity = now;
3219
+ session.renewalCount++;
3220
+ session.expiresAt = calculateExpirationDate(
3221
+ session.createdAt,
3222
+ session.lastActivity,
3223
+ session.timeoutConfig,
3224
+ session.renewalCount
3225
+ );
3226
+ session.warningState = void 0;
3227
+ logger13.info(
3228
+ `[Sessions API] Renewed session ${session.id}, renewal count: ${session.renewalCount}`
3229
+ );
3230
+ return true;
3231
+ }
3232
+ function createSessionInfoResponse(session) {
3233
+ const now = Date.now();
3234
+ const timeRemaining = Math.max(0, session.expiresAt.getTime() - now);
3235
+ const warningThresholdMs = (session.timeoutConfig.warningThresholdMinutes || DEFAULT_WARNING_THRESHOLD_MINUTES) * 60 * 1e3;
3236
+ return {
3237
+ sessionId: session.id,
3238
+ agentId: session.agentId,
3239
+ userId: session.userId,
3240
+ createdAt: session.createdAt,
3241
+ lastActivity: session.lastActivity,
3242
+ metadata: session.metadata,
3243
+ expiresAt: session.expiresAt,
3244
+ timeoutConfig: session.timeoutConfig,
3245
+ renewalCount: session.renewalCount,
3246
+ timeRemaining,
3247
+ isNearExpiration: timeRemaining <= warningThresholdMs && timeRemaining > 0
3248
+ };
3249
+ }
2866
3250
  function validateMetadata(metadata) {
2867
3251
  if (!metadata || typeof metadata !== "object") {
2868
- return true;
3252
+ return;
2869
3253
  }
2870
3254
  const metadataStr = JSON.stringify(metadata);
2871
3255
  if (metadataStr.length > MAX_METADATA_SIZE) {
2872
- throw new Error(`Metadata exceeds maximum size of ${MAX_METADATA_SIZE} bytes`);
3256
+ throw new InvalidMetadataError(
3257
+ `Metadata exceeds maximum size of ${MAX_METADATA_SIZE} bytes`,
3258
+ metadata
3259
+ );
2873
3260
  }
2874
- return true;
2875
3261
  }
2876
3262
  function validateContent(content) {
2877
3263
  if (typeof content !== "string") {
2878
- throw new Error("Content must be a string");
3264
+ throw new InvalidContentError("Content must be a string", content);
2879
3265
  }
2880
3266
  if (content.length === 0) {
2881
- throw new Error("Content cannot be empty");
3267
+ throw new InvalidContentError("Content cannot be empty", content);
2882
3268
  }
2883
3269
  if (content.length > MAX_CONTENT_LENGTH) {
2884
- throw new Error(`Content exceeds maximum length of ${MAX_CONTENT_LENGTH} characters`);
3270
+ throw new InvalidContentError(
3271
+ `Content exceeds maximum length of ${MAX_CONTENT_LENGTH} characters`,
3272
+ content
3273
+ );
2885
3274
  }
2886
3275
  return true;
2887
3276
  }
2888
- function errorResponse(res, status, message, details) {
2889
- logger13.error(`[Sessions API] Error: ${message}`, details);
2890
- return res.status(status).json({
2891
- error: message,
2892
- details: process.env.NODE_ENV === "development" ? details : void 0
2893
- });
3277
+ function asyncHandler(fn) {
3278
+ return (req, res, next) => {
3279
+ Promise.resolve(fn(req, res, next)).catch(next);
3280
+ };
2894
3281
  }
2895
3282
  function createSessionsRouter(agents, serverInstance) {
2896
3283
  const router = express12.Router();
2897
3284
  router.get("/sessions/health", (_req, res) => {
3285
+ const now = Date.now();
3286
+ let activeSessions = 0;
3287
+ let expiringSoon = 0;
3288
+ let invalidSessions = 0;
3289
+ for (const session of sessions.values()) {
3290
+ if (!isValidSession(session)) {
3291
+ invalidSessions++;
3292
+ continue;
3293
+ }
3294
+ if (session.expiresAt.getTime() > now) {
3295
+ activeSessions++;
3296
+ if (shouldWarnAboutExpiration(session)) {
3297
+ expiringSoon++;
3298
+ }
3299
+ }
3300
+ }
2898
3301
  const response = {
2899
3302
  status: "healthy",
2900
- activeSessions: sessions.size,
2901
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
3303
+ activeSessions,
3304
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3305
+ expiringSoon,
3306
+ ...invalidSessions > 0 && { invalidSessions },
3307
+ uptime: process.uptime()
2902
3308
  };
2903
3309
  res.json(response);
2904
3310
  });
2905
- router.post("/sessions", async (req, res) => {
2906
- try {
3311
+ router.post(
3312
+ "/sessions",
3313
+ asyncHandler(async (req, res) => {
2907
3314
  const body = req.body;
2908
- if (!body.agentId || !body.userId) {
2909
- return errorResponse(res, 400, "Missing required fields: agentId and userId");
3315
+ if (!isCreateSessionRequest(body)) {
3316
+ throw new MissingFieldsError(["agentId", "userId"]);
2910
3317
  }
2911
- if (!validateUuid13(body.agentId) || !validateUuid13(body.userId)) {
2912
- return errorResponse(res, 400, "Invalid UUID format for agentId or userId");
3318
+ if (!validateUuid13(body.agentId)) {
3319
+ throw new InvalidUuidError("agentId", body.agentId);
3320
+ }
3321
+ if (!validateUuid13(body.userId)) {
3322
+ throw new InvalidUuidError("userId", body.userId);
2913
3323
  }
2914
3324
  const agent = agents.get(body.agentId);
2915
3325
  if (!agent) {
2916
- return errorResponse(res, 404, "Agent not found");
3326
+ throw new AgentNotFoundError(body.agentId);
2917
3327
  }
2918
- if (body.metadata && !validateMetadata(body.metadata)) {
2919
- return errorResponse(res, 400, "Invalid metadata");
3328
+ if (body.metadata) {
3329
+ validateMetadata(body.metadata);
2920
3330
  }
3331
+ const agentTimeoutConfig = getAgentTimeoutConfig(agent);
3332
+ const finalTimeoutConfig = mergeTimeoutConfigs(body.timeoutConfig, agentTimeoutConfig);
3333
+ logger13.info(
3334
+ `[Sessions API] Creating session with timeout config: agentId=${body.agentId}, timeout=${finalTimeoutConfig.timeoutMinutes}, autoRenew=${finalTimeoutConfig.autoRenew}, maxDuration=${finalTimeoutConfig.maxDurationMinutes}`
3335
+ );
2921
3336
  const sessionId = v4_default();
2922
3337
  const channelId = v4_default();
2923
- await serverInstance.createChannel({
2924
- id: channelId,
2925
- name: `session-${sessionId}`,
2926
- type: ChannelType3.DM,
2927
- messageServerId: DEFAULT_SERVER_ID4,
2928
- metadata: {
2929
- sessionId,
2930
- agentId: body.agentId,
2931
- userId: body.userId,
2932
- ...body.metadata || {}
2933
- }
2934
- });
2935
- await serverInstance.addParticipantsToChannel(channelId, [body.agentId]);
3338
+ try {
3339
+ await serverInstance.createChannel({
3340
+ id: channelId,
3341
+ name: `session-${sessionId}`,
3342
+ type: ChannelType3.DM,
3343
+ messageServerId: DEFAULT_SERVER_ID4,
3344
+ metadata: {
3345
+ sessionId,
3346
+ agentId: body.agentId,
3347
+ userId: body.userId,
3348
+ timeoutConfig: finalTimeoutConfig,
3349
+ ...body.metadata || {}
3350
+ }
3351
+ });
3352
+ await serverInstance.addParticipantsToChannel(channelId, [body.agentId]);
3353
+ } catch (error) {
3354
+ throw new SessionCreationError("Failed to create channel or add participants", {
3355
+ originalError: error instanceof Error ? error.message : String(error)
3356
+ });
3357
+ }
3358
+ const now = /* @__PURE__ */ new Date();
2936
3359
  const session = {
2937
3360
  id: sessionId,
2938
3361
  agentId: body.agentId,
2939
3362
  channelId,
2940
3363
  userId: body.userId,
2941
3364
  metadata: body.metadata || {},
2942
- createdAt: /* @__PURE__ */ new Date(),
2943
- lastActivity: /* @__PURE__ */ new Date()
3365
+ createdAt: now,
3366
+ lastActivity: now,
3367
+ expiresAt: calculateExpirationDate(now, now, finalTimeoutConfig, 0),
3368
+ timeoutConfig: finalTimeoutConfig,
3369
+ renewalCount: 0
2944
3370
  };
2945
3371
  sessions.set(sessionId, session);
2946
3372
  const response = {
@@ -2948,123 +3374,171 @@ function createSessionsRouter(agents, serverInstance) {
2948
3374
  agentId: session.agentId,
2949
3375
  userId: session.userId,
2950
3376
  createdAt: session.createdAt,
2951
- metadata: session.metadata
3377
+ metadata: session.metadata,
3378
+ expiresAt: session.expiresAt,
3379
+ timeoutConfig: session.timeoutConfig
2952
3380
  };
2953
3381
  res.status(201).json(response);
2954
- } catch (error) {
2955
- errorResponse(
2956
- res,
2957
- 500,
2958
- "Failed to create session",
2959
- error instanceof Error ? error.message : String(error)
2960
- );
2961
- }
2962
- });
2963
- router.get("/sessions/:sessionId", async (req, res) => {
2964
- const { sessionId } = req.params;
2965
- const session = sessions.get(sessionId);
2966
- if (!session) {
2967
- return errorResponse(res, 404, "Session not found");
2968
- }
2969
- const response = {
2970
- sessionId: session.id,
2971
- agentId: session.agentId,
2972
- userId: session.userId,
2973
- createdAt: session.createdAt,
2974
- lastActivity: session.lastActivity,
2975
- metadata: session.metadata
2976
- };
2977
- res.json(response);
2978
- });
2979
- router.post("/sessions/:sessionId/messages", async (req, res) => {
2980
- try {
3382
+ })
3383
+ );
3384
+ router.get(
3385
+ "/sessions/:sessionId",
3386
+ asyncHandler(async (req, res) => {
3387
+ const { sessionId } = req.params;
3388
+ const session = sessions.get(sessionId);
3389
+ if (!session || !isValidSession(session)) {
3390
+ throw new SessionNotFoundError(sessionId);
3391
+ }
3392
+ if (session.expiresAt.getTime() <= Date.now()) {
3393
+ sessions.delete(sessionId);
3394
+ throw new SessionExpiredError(sessionId, session.expiresAt);
3395
+ }
3396
+ const response = createSessionInfoResponse(session);
3397
+ res.json(response);
3398
+ })
3399
+ );
3400
+ router.post(
3401
+ "/sessions/:sessionId/messages",
3402
+ asyncHandler(async (req, res) => {
2981
3403
  const { sessionId } = req.params;
2982
3404
  const body = req.body;
3405
+ if (!isSendMessageRequest(body)) {
3406
+ throw new InvalidContentError("Invalid message request format", body);
3407
+ }
2983
3408
  const session = sessions.get(sessionId);
2984
3409
  if (!session) {
2985
- return errorResponse(res, 404, "Session not found");
3410
+ throw new SessionNotFoundError(sessionId);
3411
+ }
3412
+ if (session.expiresAt.getTime() <= Date.now()) {
3413
+ sessions.delete(sessionId);
3414
+ throw new SessionExpiredError(sessionId, session.expiresAt);
3415
+ }
3416
+ validateContent(body.content);
3417
+ if (body.metadata) {
3418
+ validateMetadata(body.metadata);
3419
+ }
3420
+ const wasRenewed = renewSession(session);
3421
+ if (!wasRenewed && session.timeoutConfig.autoRenew) {
3422
+ const maxDurationMs = (session.timeoutConfig.maxDurationMinutes || DEFAULT_MAX_DURATION_MINUTES) * 60 * 1e3;
3423
+ const timeSinceCreation = Date.now() - session.createdAt.getTime();
3424
+ if (timeSinceCreation >= maxDurationMs) {
3425
+ logger13.warn(`[Sessions API] Session ${sessionId} has reached maximum duration`);
3426
+ }
3427
+ } else if (!session.timeoutConfig.autoRenew) {
3428
+ session.lastActivity = /* @__PURE__ */ new Date();
2986
3429
  }
3430
+ if (shouldWarnAboutExpiration(session)) {
3431
+ session.warningState = {
3432
+ sent: true,
3433
+ sentAt: /* @__PURE__ */ new Date()
3434
+ };
3435
+ logger13.info(`[Sessions API] Session ${sessionId} is near expiration, warning state set`);
3436
+ }
3437
+ let message;
2987
3438
  try {
2988
- validateContent(body.content);
3439
+ message = await serverInstance.createMessage({
3440
+ channelId: session.channelId,
3441
+ authorId: session.userId,
3442
+ content: body.content,
3443
+ rawMessage: {
3444
+ content: body.content,
3445
+ attachments: body.attachments
3446
+ },
3447
+ sourceType: "user",
3448
+ metadata: {
3449
+ sessionId,
3450
+ ...body.metadata || {}
3451
+ }
3452
+ });
2989
3453
  } catch (error) {
2990
- return errorResponse(res, 400, error instanceof Error ? error.message : String(error));
2991
- }
2992
- if (body.metadata && !validateMetadata(body.metadata)) {
2993
- return errorResponse(res, 400, "Invalid metadata");
3454
+ throw new MessageSendError(sessionId, "Failed to create message in database", {
3455
+ originalError: error instanceof Error ? error.message : String(error)
3456
+ });
2994
3457
  }
2995
- session.lastActivity = /* @__PURE__ */ new Date();
2996
- const message = await serverInstance.createMessage({
2997
- channelId: session.channelId,
2998
- authorId: session.userId,
2999
- content: body.content,
3000
- rawMessage: {
3001
- content: body.content,
3002
- attachments: body.attachments
3003
- },
3004
- sourceType: "user",
3005
- metadata: {
3006
- sessionId,
3007
- ...body.metadata || {}
3008
- }
3009
- });
3010
- res.status(201).json({
3458
+ const response = {
3011
3459
  id: message.id,
3012
3460
  content: message.content,
3013
3461
  authorId: message.authorId,
3014
3462
  createdAt: message.createdAt,
3015
- metadata: message.metadata
3016
- });
3017
- } catch (error) {
3018
- errorResponse(
3019
- res,
3020
- 500,
3021
- "Failed to send message",
3022
- error instanceof Error ? error.message : String(error)
3023
- );
3024
- }
3025
- });
3026
- router.get("/sessions/:sessionId/messages", async (req, res) => {
3027
- try {
3463
+ metadata: message.metadata,
3464
+ sessionStatus: {
3465
+ expiresAt: session.expiresAt,
3466
+ renewalCount: session.renewalCount,
3467
+ wasRenewed,
3468
+ isNearExpiration: shouldWarnAboutExpiration(session)
3469
+ }
3470
+ };
3471
+ res.status(201).json(response);
3472
+ })
3473
+ );
3474
+ router.get(
3475
+ "/sessions/:sessionId/messages",
3476
+ asyncHandler(async (req, res) => {
3028
3477
  const { sessionId } = req.params;
3029
- const query = req.query;
3478
+ const query = {
3479
+ limit: req.query.limit,
3480
+ before: req.query.before,
3481
+ after: req.query.after
3482
+ };
3030
3483
  const session = sessions.get(sessionId);
3031
3484
  if (!session) {
3032
- return errorResponse(res, 404, "Session not found");
3485
+ throw new SessionNotFoundError(sessionId);
3486
+ }
3487
+ if (session.expiresAt.getTime() <= Date.now()) {
3488
+ sessions.delete(sessionId);
3489
+ throw new SessionExpiredError(sessionId, session.expiresAt);
3033
3490
  }
3034
3491
  let messageLimit = DEFAULT_LIMIT;
3035
3492
  if (query.limit) {
3036
- const parsedLimit = parseInt(query.limit, 10);
3037
- if (isNaN(parsedLimit) || parsedLimit < 1) {
3038
- return errorResponse(res, 400, "Invalid limit parameter");
3039
- }
3040
- messageLimit = Math.min(parsedLimit, MAX_LIMIT);
3493
+ const parsedLimit = safeParseInt(query.limit, DEFAULT_LIMIT, 1, MAX_LIMIT);
3494
+ messageLimit = parsedLimit;
3041
3495
  }
3042
3496
  let beforeDate;
3043
3497
  let afterDate;
3044
3498
  if (query.before) {
3045
3499
  const beforeTimestamp = parseInt(query.before, 10);
3046
- if (isNaN(beforeTimestamp)) {
3047
- return errorResponse(res, 400, "Invalid before parameter");
3500
+ if (isNaN(beforeTimestamp) || !isFinite(beforeTimestamp)) {
3501
+ throw new InvalidPaginationError("before", query.before, "Must be a valid timestamp");
3048
3502
  }
3049
3503
  beforeDate = new Date(beforeTimestamp);
3504
+ if (isNaN(beforeDate.getTime())) {
3505
+ throw new InvalidPaginationError("before", query.before, "Invalid date from timestamp");
3506
+ }
3050
3507
  }
3051
3508
  if (query.after) {
3052
3509
  const afterTimestamp = parseInt(query.after, 10);
3053
- if (isNaN(afterTimestamp)) {
3054
- return errorResponse(res, 400, "Invalid after parameter");
3510
+ if (isNaN(afterTimestamp) || !isFinite(afterTimestamp)) {
3511
+ throw new InvalidPaginationError("after", query.after, "Must be a valid timestamp");
3055
3512
  }
3056
3513
  afterDate = new Date(afterTimestamp);
3514
+ if (isNaN(afterDate.getTime())) {
3515
+ throw new InvalidPaginationError("after", query.after, "Invalid date from timestamp");
3516
+ }
3057
3517
  }
3058
3518
  let messages;
3059
- if (afterDate) {
3060
- messages = await serverInstance.getMessagesForChannel(
3519
+ if (afterDate && beforeDate) {
3520
+ const fetchLimit = Math.min(500, messageLimit * 10);
3521
+ const allMessages = await serverInstance.getMessagesForChannel(
3522
+ session.channelId,
3523
+ fetchLimit,
3524
+ beforeDate
3525
+ );
3526
+ messages = allMessages.filter((msg) => msg.createdAt > afterDate && msg.createdAt < beforeDate).slice(0, messageLimit);
3527
+ if (allMessages.length === fetchLimit) {
3528
+ logger13.debug(`[Sessions API] Range query hit limit of ${fetchLimit} messages`);
3529
+ }
3530
+ } else if (afterDate) {
3531
+ const fetchLimit = Math.min(1e3, messageLimit * 20);
3532
+ const recentMessages = await serverInstance.getMessagesForChannel(
3061
3533
  session.channelId,
3062
- messageLimit * 2,
3063
- // Get extra to ensure we have enough after filtering
3064
- void 0
3065
- // Don't use beforeDate for initial query
3534
+ fetchLimit
3066
3535
  );
3067
- messages = messages.filter((msg) => msg.createdAt > afterDate).slice(0, messageLimit);
3536
+ const newerMessages = recentMessages.filter((msg) => msg.createdAt > afterDate);
3537
+ if (newerMessages.length > messageLimit) {
3538
+ messages = newerMessages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).slice(0, messageLimit).reverse();
3539
+ } else {
3540
+ messages = newerMessages;
3541
+ }
3068
3542
  } else {
3069
3543
  messages = await serverInstance.getMessagesForChannel(
3070
3544
  session.channelId,
@@ -3075,7 +3549,10 @@ function createSessionsRouter(agents, serverInstance) {
3075
3549
  const simplifiedMessages = messages.map((msg) => {
3076
3550
  let rawMessage = {};
3077
3551
  try {
3078
- rawMessage = typeof msg.rawMessage === "string" ? JSON.parse(msg.rawMessage) : msg.rawMessage || {};
3552
+ const parsedData = typeof msg.rawMessage === "string" ? JSON.parse(msg.rawMessage) : msg.rawMessage;
3553
+ if (parsedData && typeof parsedData === "object") {
3554
+ rawMessage = parsedData;
3555
+ }
3079
3556
  } catch (error) {
3080
3557
  logger13.warn(
3081
3558
  `[Sessions API] Failed to parse rawMessage for message ${msg.id}`,
@@ -3095,68 +3572,208 @@ function createSessionsRouter(agents, serverInstance) {
3095
3572
  }
3096
3573
  };
3097
3574
  });
3575
+ const oldestMessage = simplifiedMessages[simplifiedMessages.length - 1];
3576
+ const newestMessage = simplifiedMessages[0];
3098
3577
  const response = {
3099
3578
  messages: simplifiedMessages,
3100
3579
  hasMore: messages.length === messageLimit
3101
3580
  };
3581
+ if (simplifiedMessages.length > 0) {
3582
+ response.cursors = {
3583
+ before: oldestMessage?.createdAt.getTime(),
3584
+ after: newestMessage?.createdAt.getTime()
3585
+ };
3586
+ }
3102
3587
  res.json(response);
3103
- } catch (error) {
3104
- errorResponse(
3105
- res,
3106
- 500,
3107
- "Failed to fetch messages",
3108
- error instanceof Error ? error.message : String(error)
3588
+ })
3589
+ );
3590
+ router.post(
3591
+ "/sessions/:sessionId/renew",
3592
+ asyncHandler(async (req, res) => {
3593
+ const { sessionId } = req.params;
3594
+ const session = sessions.get(sessionId);
3595
+ if (!session) {
3596
+ throw new SessionNotFoundError(sessionId);
3597
+ }
3598
+ if (session.expiresAt.getTime() <= Date.now()) {
3599
+ sessions.delete(sessionId);
3600
+ throw new SessionExpiredError(sessionId, session.expiresAt);
3601
+ }
3602
+ const previousAutoRenew = session.timeoutConfig.autoRenew;
3603
+ session.timeoutConfig.autoRenew = true;
3604
+ const renewed = renewSession(session);
3605
+ session.timeoutConfig.autoRenew = previousAutoRenew;
3606
+ if (!renewed) {
3607
+ throw new SessionRenewalError(sessionId, "Maximum duration reached", {
3608
+ maxDuration: session.timeoutConfig.maxDurationMinutes,
3609
+ createdAt: session.createdAt,
3610
+ timeSinceCreation: Date.now() - session.createdAt.getTime()
3611
+ });
3612
+ }
3613
+ const response = createSessionInfoResponse(session);
3614
+ res.json(response);
3615
+ })
3616
+ );
3617
+ router.patch(
3618
+ "/sessions/:sessionId/timeout",
3619
+ asyncHandler(async (req, res) => {
3620
+ const { sessionId } = req.params;
3621
+ const newConfig = req.body;
3622
+ const session = sessions.get(sessionId);
3623
+ if (!session) {
3624
+ throw new SessionNotFoundError(sessionId);
3625
+ }
3626
+ if (session.expiresAt.getTime() <= Date.now()) {
3627
+ sessions.delete(sessionId);
3628
+ throw new SessionExpiredError(sessionId, session.expiresAt);
3629
+ }
3630
+ if (!isValidTimeoutConfig(newConfig)) {
3631
+ throw new InvalidTimeoutConfigError("Invalid timeout configuration format", newConfig);
3632
+ }
3633
+ if (newConfig.timeoutMinutes !== void 0) {
3634
+ const timeoutValue = Number(newConfig.timeoutMinutes);
3635
+ if (!isNaN(timeoutValue) && isFinite(timeoutValue)) {
3636
+ if (timeoutValue < MIN_TIMEOUT_MINUTES || timeoutValue > MAX_TIMEOUT_MINUTES) {
3637
+ throw new InvalidTimeoutConfigError(
3638
+ `Timeout must be between ${MIN_TIMEOUT_MINUTES} and ${MAX_TIMEOUT_MINUTES} minutes`,
3639
+ newConfig
3640
+ );
3641
+ }
3642
+ }
3643
+ }
3644
+ const agent = agents.get(session.agentId);
3645
+ const agentConfig = agent ? getAgentTimeoutConfig(agent) : void 0;
3646
+ session.timeoutConfig = mergeTimeoutConfigs(newConfig, agentConfig);
3647
+ session.expiresAt = calculateExpirationDate(
3648
+ session.createdAt,
3649
+ session.lastActivity,
3650
+ session.timeoutConfig,
3651
+ session.renewalCount
3109
3652
  );
3110
- }
3111
- });
3112
- router.delete("/sessions/:sessionId", async (req, res) => {
3113
- try {
3653
+ logger13.info(
3654
+ `[Sessions API] Updated timeout config for session ${sessionId}: timeout=${session.timeoutConfig.timeoutMinutes}, autoRenew=${session.timeoutConfig.autoRenew}, maxDuration=${session.timeoutConfig.maxDurationMinutes}`
3655
+ );
3656
+ const response = createSessionInfoResponse(session);
3657
+ res.json(response);
3658
+ })
3659
+ );
3660
+ router.post(
3661
+ "/sessions/:sessionId/heartbeat",
3662
+ asyncHandler(async (req, res) => {
3663
+ const { sessionId } = req.params;
3664
+ const session = sessions.get(sessionId);
3665
+ if (!session || !isValidSession(session)) {
3666
+ throw new SessionNotFoundError(sessionId);
3667
+ }
3668
+ if (session.expiresAt.getTime() <= Date.now()) {
3669
+ sessions.delete(sessionId);
3670
+ throw new SessionExpiredError(sessionId, session.expiresAt);
3671
+ }
3672
+ session.lastActivity = /* @__PURE__ */ new Date();
3673
+ if (session.timeoutConfig.autoRenew) {
3674
+ const renewed = renewSession(session);
3675
+ if (renewed) {
3676
+ logger13.info(`[Sessions API] Session renewed via heartbeat: ${sessionId}`);
3677
+ }
3678
+ }
3679
+ const response = createSessionInfoResponse(session);
3680
+ logger13.debug(`[Sessions API] Heartbeat received for session: ${sessionId}`);
3681
+ res.json(response);
3682
+ })
3683
+ );
3684
+ router.delete(
3685
+ "/sessions/:sessionId",
3686
+ asyncHandler(async (req, res) => {
3114
3687
  const { sessionId } = req.params;
3115
3688
  const session = sessions.get(sessionId);
3116
3689
  if (!session) {
3117
- return errorResponse(res, 404, "Session not found");
3690
+ throw new SessionNotFoundError(sessionId);
3118
3691
  }
3119
3692
  sessions.delete(sessionId);
3120
- res.json({ success: true });
3121
- } catch (error) {
3122
- errorResponse(
3123
- res,
3124
- 500,
3125
- "Failed to delete session",
3126
- error instanceof Error ? error.message : String(error)
3693
+ logger13.info(`[Sessions API] Deleted session ${sessionId}`);
3694
+ res.json({
3695
+ success: true,
3696
+ message: `Session ${sessionId} deleted successfully`
3697
+ });
3698
+ })
3699
+ );
3700
+ router.get(
3701
+ "/sessions",
3702
+ asyncHandler(async (_req, res) => {
3703
+ const now = Date.now();
3704
+ const activeSessions = Array.from(sessions.values()).filter((session) => isValidSession(session) && session.expiresAt.getTime() > now).map((session) => createSessionInfoResponse(session));
3705
+ res.json({
3706
+ sessions: activeSessions,
3707
+ total: activeSessions.length,
3708
+ stats: {
3709
+ totalSessions: sessions.size,
3710
+ activeSessions: activeSessions.length,
3711
+ expiredSessions: sessions.size - activeSessions.length
3712
+ }
3713
+ });
3714
+ })
3715
+ );
3716
+ const cleanupInterval = setInterval(() => {
3717
+ const now = /* @__PURE__ */ new Date();
3718
+ let cleanedCount = 0;
3719
+ let expiredCount = 0;
3720
+ let warningCount = 0;
3721
+ for (const [sessionId, session] of sessions.entries()) {
3722
+ if (!isValidSession(session)) {
3723
+ logger13.warn(`[Sessions API] Invalid session structure for ${sessionId}, removing`);
3724
+ sessions.delete(sessionId);
3725
+ cleanedCount++;
3726
+ continue;
3727
+ }
3728
+ if (session.expiresAt.getTime() <= now.getTime()) {
3729
+ sessions.delete(sessionId);
3730
+ cleanedCount++;
3731
+ expiredCount++;
3732
+ logger13.info(`[Sessions API] Cleaned up expired session: ${sessionId}`);
3733
+ } else if (shouldWarnAboutExpiration(session) && !session.warningState?.sent) {
3734
+ session.warningState = {
3735
+ sent: true,
3736
+ sentAt: now
3737
+ };
3738
+ warningCount++;
3739
+ logger13.info(`[Sessions API] Session ${sessionId} will expire soon`);
3740
+ }
3741
+ }
3742
+ if (cleanedCount > 0 || warningCount > 0) {
3743
+ logger13.info(
3744
+ `[Sessions API] Cleanup cycle completed: ${cleanedCount} expired sessions removed, ${warningCount} warnings issued`
3127
3745
  );
3128
3746
  }
3129
- });
3130
- router.get("/sessions", async (_req, res) => {
3131
- const activeSessions = Array.from(sessions.values()).map((session) => ({
3132
- sessionId: session.id,
3133
- agentId: session.agentId,
3134
- userId: session.userId,
3135
- createdAt: session.createdAt,
3136
- lastActivity: session.lastActivity,
3137
- metadata: session.metadata
3138
- }));
3139
- res.json({
3140
- sessions: activeSessions,
3141
- total: activeSessions.length
3142
- });
3143
- });
3144
- const cleanupInterval = setInterval(
3145
- () => {
3146
- const now = /* @__PURE__ */ new Date();
3147
- for (const [sessionId, session] of sessions.entries()) {
3148
- if (now.getTime() - session.lastActivity.getTime() > SESSION_TIMEOUT_MS) {
3149
- sessions.delete(sessionId);
3150
- logger13.info(`[Sessions API] Cleaned up inactive session: ${sessionId}`);
3151
- }
3747
+ }, CLEANUP_INTERVAL_MS);
3748
+ activeCleanupIntervals.add(cleanupInterval);
3749
+ const cleanup = () => {
3750
+ if (activeCleanupIntervals.has(cleanupInterval)) {
3751
+ clearInterval(cleanupInterval);
3752
+ activeCleanupIntervals.delete(cleanupInterval);
3753
+ logger13.info("[Sessions API] Cleanup interval cleared");
3754
+ }
3755
+ };
3756
+ if (!processHandlersRegistered) {
3757
+ processHandlersRegistered = true;
3758
+ const globalCleanup = () => {
3759
+ logger13.info("[Sessions API] Global cleanup initiated");
3760
+ for (const interval of activeCleanupIntervals) {
3761
+ clearInterval(interval);
3762
+ }
3763
+ activeCleanupIntervals.clear();
3764
+ if (process.env.CLEAR_SESSIONS_ON_SHUTDOWN === "true") {
3765
+ sessions.clear();
3766
+ agentTimeoutConfigs.clear();
3152
3767
  }
3153
- },
3154
- 5 * 60 * 1e3
3155
- );
3156
- process.on("SIGTERM", () => {
3157
- clearInterval(cleanupInterval);
3158
- });
3159
- return router;
3768
+ };
3769
+ process.once("SIGTERM", globalCleanup);
3770
+ process.once("SIGINT", globalCleanup);
3771
+ process.once("beforeExit", globalCleanup);
3772
+ }
3773
+ router.use(createErrorHandler());
3774
+ const routerWithCleanup = router;
3775
+ routerWithCleanup.cleanup = cleanup;
3776
+ return routerWithCleanup;
3160
3777
  }
3161
3778
 
3162
3779
  // src/api/messaging/index.ts
@@ -4232,7 +4849,7 @@ import express29 from "express";
4232
4849
  // package.json
4233
4850
  var package_default = {
4234
4851
  name: "@elizaos/server",
4235
- version: "1.4.2",
4852
+ version: "1.4.4",
4236
4853
  description: "ElizaOS Server - Core server infrastructure for ElizaOS agents",
4237
4854
  publishConfig: {
4238
4855
  access: "public",
@@ -4283,10 +4900,10 @@ var package_default = {
4283
4900
  which: "^4.0.0",
4284
4901
  ws: "^8.18.0"
4285
4902
  },
4286
- gitHead: "c4e30a38836ac4bed81c671239377685892ebdf5",
4903
+ gitHead: "966e28250672a7a0c5f934defd995c51b94c4622",
4287
4904
  dependencies: {
4288
- "@elizaos/core": "workspace:*",
4289
- "@elizaos/plugin-sql": "workspace:*",
4905
+ "@elizaos/core": "1.4.4",
4906
+ "@elizaos/plugin-sql": "1.4.4",
4290
4907
  "@types/express": "^5.0.2",
4291
4908
  "@types/helmet": "^4.0.0",
4292
4909
  "@types/multer": "^1.4.13",
@@ -6175,7 +6792,7 @@ var AgentServer = class {
6175
6792
  connectSrc: ["'self'", "ws:", "wss:", "https:", "http:"],
6176
6793
  mediaSrc: ["'self'", "blob:", "data:"],
6177
6794
  objectSrc: ["'none'"],
6178
- frameSrc: ["'none'"],
6795
+ frameSrc: [this.isWebUIEnabled ? "'self'" : "'none'"],
6179
6796
  baseUri: ["'self'"],
6180
6797
  formAction: ["'self'"]
6181
6798
  // upgrade-insecure-requests is added by helmet automatically