@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/client/assets/index-DESjREgK.css +1 -0
- package/dist/client/assets/index-DESjREgK.css.br +0 -0
- package/dist/client/assets/{index-ofROTcnX.js → index-l7vDDpLb.js} +4419 -1080
- package/dist/client/assets/index-l7vDDpLb.js.br +0 -0
- package/dist/client/assets/{index-ofROTcnX.js.map → index-l7vDDpLb.js.map} +1 -1
- package/dist/client/assets/{vendor-BDSOQlwO.js → vendor-BWzYG1ky.js} +40 -2560
- package/dist/client/assets/vendor-BWzYG1ky.js.br +0 -0
- package/dist/client/assets/vendor-BWzYG1ky.js.map +1 -0
- package/dist/client/index.html +3 -3
- package/dist/index.d.ts +48 -1
- package/dist/index.js +793 -176
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/client/assets/index-BgxiIYf2.js +0 -7487
- package/dist/client/assets/index-BgxiIYf2.js.br +0 -0
- package/dist/client/assets/index-BgxiIYf2.js.map +0 -1
- package/dist/client/assets/index-CuhlDCnv.css +0 -1
- package/dist/client/assets/index-CuhlDCnv.css.br +0 -0
- package/dist/client/assets/index-ofROTcnX.js.br +0 -0
- package/dist/client/assets/vendor-BDSOQlwO.js.br +0 -0
- package/dist/client/assets/vendor-BDSOQlwO.js.map +0 -1
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
|
-
|
|
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
|
|
3252
|
+
return;
|
|
2869
3253
|
}
|
|
2870
3254
|
const metadataStr = JSON.stringify(metadata);
|
|
2871
3255
|
if (metadataStr.length > MAX_METADATA_SIZE) {
|
|
2872
|
-
throw new
|
|
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
|
|
3264
|
+
throw new InvalidContentError("Content must be a string", content);
|
|
2879
3265
|
}
|
|
2880
3266
|
if (content.length === 0) {
|
|
2881
|
-
throw new
|
|
3267
|
+
throw new InvalidContentError("Content cannot be empty", content);
|
|
2882
3268
|
}
|
|
2883
3269
|
if (content.length > MAX_CONTENT_LENGTH) {
|
|
2884
|
-
throw new
|
|
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
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
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
|
|
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(
|
|
2906
|
-
|
|
3311
|
+
router.post(
|
|
3312
|
+
"/sessions",
|
|
3313
|
+
asyncHandler(async (req, res) => {
|
|
2907
3314
|
const body = req.body;
|
|
2908
|
-
if (!body
|
|
2909
|
-
|
|
3315
|
+
if (!isCreateSessionRequest(body)) {
|
|
3316
|
+
throw new MissingFieldsError(["agentId", "userId"]);
|
|
2910
3317
|
}
|
|
2911
|
-
if (!validateUuid13(body.agentId)
|
|
2912
|
-
|
|
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
|
-
|
|
3326
|
+
throw new AgentNotFoundError(body.agentId);
|
|
2917
3327
|
}
|
|
2918
|
-
if (body.metadata
|
|
2919
|
-
|
|
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
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
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:
|
|
2943
|
-
lastActivity:
|
|
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
|
-
}
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
);
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
);
|
|
3024
|
-
}
|
|
3025
|
-
|
|
3026
|
-
router.get(
|
|
3027
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
3037
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
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
|
-
|
|
3113
|
-
|
|
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
|
-
|
|
3690
|
+
throw new SessionNotFoundError(sessionId);
|
|
3118
3691
|
}
|
|
3119
3692
|
sessions.delete(sessionId);
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
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
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
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
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
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.
|
|
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: "
|
|
4903
|
+
gitHead: "966e28250672a7a0c5f934defd995c51b94c4622",
|
|
4287
4904
|
dependencies: {
|
|
4288
|
-
"@elizaos/core": "
|
|
4289
|
-
"@elizaos/plugin-sql": "
|
|
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
|