@kjerneverk/agentic 1.0.5-dev.0
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/.kodrdriv-test-cache.json +6 -0
- package/LICENSE +18 -0
- package/README.md +167 -0
- package/SECURITY.md +94 -0
- package/dist/index.d.ts +770 -0
- package/dist/index.js +1822 -0
- package/dist/index.js.map +1 -0
- package/output/kodrdriv/260110-1207-commit-message.md +1 -0
- package/output/kodrdriv/260110-1208-commit-message.md +1 -0
- package/output/kodrdriv/260110-1210-release-notes.md +27 -0
- package/output/kodrdriv/260110-1232-commit-message.md +1 -0
- package/output/kodrdriv/260110-1234-release-notes.md +46 -0
- package/output/kodrdriv/260110-1308-release-notes.md +20 -0
- package/output/kodrdriv/260112-2155-commit-message.md +1 -0
- package/output/kodrdriv/260112-2156-release-notes.md +33 -0
- package/output/kodrdriv/260112-2340-commit-message.md +1 -0
- package/output/kodrdriv/260113-0017-commit-message.md +1 -0
- package/output/kodrdriv/260113-0017-release-notes.md +53 -0
- package/output/kodrdriv/260115-0616-commit-message.md +4 -0
- package/output/kodrdriv/260115-0739-commit-message.md +1 -0
- package/output/kodrdriv/260115-0746-commit-message.md +1 -0
- package/output/kodrdriv/260115-0747-release-notes.md +91 -0
- package/output/kodrdriv/260126-0851-commit-message.md +1 -0
- package/output/kodrdriv/260128-0900-commit-message.md +1 -0
- package/output/kodrdriv/260128-0900-release-notes.md +29 -0
- package/output/kodrdriv/260128-0905-commit-message.md +1 -0
- package/output/kodrdriv/260128-0913-commit-message.md +1 -0
- package/output/kodrdriv/260128-0913-release-notes.md +23 -0
- package/output/kodrdriv/260128-0919-commit-message.md +1 -0
- package/output/kodrdriv/260128-0919-release-notes.md +21 -0
- package/output/kodrdriv/260130-1642-commit-message.md +1 -0
- package/output/kodrdriv/260130-1642-release-notes.md +29 -0
- package/output/kodrdriv/RELEASE_NOTES.md +27 -0
- package/output/kodrdriv/RELEASE_TITLE.md +1 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-10T19-18-11-913Z.md +187 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-10T20-03-27-409Z.md +121 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-10T20-07-27-813Z.md +114 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-10T20-08-06-279Z.md +97 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-10T20-32-25-791Z.md +114 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-13T05-55-30-733Z.md +97 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-13T07-40-28-878Z.md +97 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-13T08-17-22-230Z.md +114 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-14T14-55-10-122Z.md +177 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-15T14-16-05-605Z.md +100 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-15T15-39-32-200Z.md +97 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-15T15-46-32-840Z.md +114 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-26T16-51-00-446Z.md +152 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-31T00-38-59-868Z.md +236 -0
- package/output/kodrdriv/agentic-reflection-commit-2026-01-31T00-42-31-993Z.md +152 -0
- package/output/kodrdriv/agentic-reflection-release-2026-01-10T20-10-49-531Z.md +329 -0
- package/output/kodrdriv/agentic-reflection-release-2026-01-10T20-34-32-038Z.md +319 -0
- package/output/kodrdriv/agentic-reflection-release-2026-01-10T21-08-36-314Z.md +168 -0
- package/output/kodrdriv/agentic-reflection-release-2026-01-13T05-56-04-802Z.md +264 -0
- package/output/kodrdriv/agentic-reflection-release-2026-01-13T08-17-56-556Z.md +255 -0
- package/output/kodrdriv/agentic-reflection-release-2026-01-15T15-47-32-509Z.md +460 -0
- package/output/kodrdriv/agentic-reflection-release-2026-01-31T00-42-59-683Z.md +400 -0
- package/package.json +59 -0
- package/package.json~ +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1822 @@
|
|
|
1
|
+
import { z, ZodError } from "zod";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
7
|
+
var LogFormat_exports = {};
|
|
8
|
+
__export(LogFormat_exports, {
|
|
9
|
+
LogFormats: () => LogFormats,
|
|
10
|
+
STRUCTURED: () => STRUCTURED,
|
|
11
|
+
TEXT: () => TEXT,
|
|
12
|
+
getConfig: () => getConfig
|
|
13
|
+
});
|
|
14
|
+
var TEXT = {
|
|
15
|
+
name: "TEXT",
|
|
16
|
+
description: "Text format"
|
|
17
|
+
};
|
|
18
|
+
var STRUCTURED = {
|
|
19
|
+
name: "STRUCTURED",
|
|
20
|
+
description: "Structured format"
|
|
21
|
+
};
|
|
22
|
+
var LogFormats = [
|
|
23
|
+
TEXT,
|
|
24
|
+
STRUCTURED
|
|
25
|
+
];
|
|
26
|
+
var getConfig = (name) => {
|
|
27
|
+
const config = LogFormats.find((config2) => config2.name === name);
|
|
28
|
+
if (!config) {
|
|
29
|
+
throw new Error(`Invalid Log Format Supplied to Logging Configuration '${name}'`);
|
|
30
|
+
}
|
|
31
|
+
return config;
|
|
32
|
+
};
|
|
33
|
+
var LogLevel_exports = {};
|
|
34
|
+
__export(LogLevel_exports, {
|
|
35
|
+
ALERT: () => ALERT,
|
|
36
|
+
CRITICAL: () => CRITICAL,
|
|
37
|
+
DEBUG: () => DEBUG,
|
|
38
|
+
DEFAULT: () => DEFAULT,
|
|
39
|
+
EMERGENCY: () => EMERGENCY,
|
|
40
|
+
ERROR: () => ERROR,
|
|
41
|
+
INFO: () => INFO,
|
|
42
|
+
LogLevels: () => LogLevels,
|
|
43
|
+
NOTICE: () => NOTICE,
|
|
44
|
+
TRACE: () => TRACE,
|
|
45
|
+
WARNING: () => WARNING,
|
|
46
|
+
getConfig: () => getConfig2
|
|
47
|
+
});
|
|
48
|
+
var EMERGENCY = {
|
|
49
|
+
name: "EMERGENCY",
|
|
50
|
+
value: 0
|
|
51
|
+
};
|
|
52
|
+
var ALERT = {
|
|
53
|
+
name: "ALERT",
|
|
54
|
+
value: 1
|
|
55
|
+
};
|
|
56
|
+
var CRITICAL = {
|
|
57
|
+
name: "CRITICAL",
|
|
58
|
+
value: 2
|
|
59
|
+
};
|
|
60
|
+
var ERROR = {
|
|
61
|
+
name: "ERROR",
|
|
62
|
+
value: 3
|
|
63
|
+
};
|
|
64
|
+
var WARNING = {
|
|
65
|
+
name: "WARNING",
|
|
66
|
+
value: 4
|
|
67
|
+
};
|
|
68
|
+
var NOTICE = {
|
|
69
|
+
name: "NOTICE",
|
|
70
|
+
value: 5
|
|
71
|
+
};
|
|
72
|
+
var INFO = {
|
|
73
|
+
name: "INFO",
|
|
74
|
+
value: 6
|
|
75
|
+
};
|
|
76
|
+
var DEBUG = {
|
|
77
|
+
name: "DEBUG",
|
|
78
|
+
value: 7
|
|
79
|
+
};
|
|
80
|
+
var TRACE = {
|
|
81
|
+
name: "TRACE",
|
|
82
|
+
value: 8
|
|
83
|
+
};
|
|
84
|
+
var DEFAULT = {
|
|
85
|
+
name: "DEFAULT",
|
|
86
|
+
value: 9
|
|
87
|
+
};
|
|
88
|
+
var LogLevels = [
|
|
89
|
+
EMERGENCY,
|
|
90
|
+
ALERT,
|
|
91
|
+
CRITICAL,
|
|
92
|
+
ERROR,
|
|
93
|
+
WARNING,
|
|
94
|
+
NOTICE,
|
|
95
|
+
INFO,
|
|
96
|
+
DEBUG,
|
|
97
|
+
TRACE,
|
|
98
|
+
DEFAULT
|
|
99
|
+
];
|
|
100
|
+
var getConfig2 = (name) => {
|
|
101
|
+
const config = LogLevels.find((config2) => config2.name === name);
|
|
102
|
+
if (!config) {
|
|
103
|
+
throw new Error(`Invalid Log Level Supplied to Logging Configuration '${name}'`);
|
|
104
|
+
}
|
|
105
|
+
return config;
|
|
106
|
+
};
|
|
107
|
+
var defaultMaskingConfig = {
|
|
108
|
+
enabled: false,
|
|
109
|
+
maskEmails: true,
|
|
110
|
+
maskSSNs: true,
|
|
111
|
+
maskPrivateKeys: true,
|
|
112
|
+
maskBase64Blobs: true,
|
|
113
|
+
maskJWTs: true,
|
|
114
|
+
maxDepth: 8,
|
|
115
|
+
// New security-focused defaults (all true when masking is enabled)
|
|
116
|
+
maskApiKeys: true,
|
|
117
|
+
maskBearerTokens: true,
|
|
118
|
+
maskPasswords: true,
|
|
119
|
+
maskGenericSecrets: true
|
|
120
|
+
};
|
|
121
|
+
var defaultLogLevel = INFO;
|
|
122
|
+
var defaultLogFormat = TEXT;
|
|
123
|
+
var defaultLoggingConfig = {
|
|
124
|
+
logLevel: defaultLogLevel,
|
|
125
|
+
logFormat: defaultLogFormat,
|
|
126
|
+
overrides: {},
|
|
127
|
+
floodControl: {
|
|
128
|
+
enabled: false,
|
|
129
|
+
threshold: 10,
|
|
130
|
+
timeframe: 1e3
|
|
131
|
+
// 1 second
|
|
132
|
+
},
|
|
133
|
+
masking: defaultMaskingConfig
|
|
134
|
+
};
|
|
135
|
+
var convertComponentOverride = (override) => {
|
|
136
|
+
const result = {
|
|
137
|
+
logLevel: override.logLevel ? getConfig2(override.logLevel) : defaultLogLevel
|
|
138
|
+
};
|
|
139
|
+
if (override.components && typeof override.components === "object") {
|
|
140
|
+
result.components = {};
|
|
141
|
+
Object.entries(override.components).forEach(([componentName, componentOverride]) => {
|
|
142
|
+
result.components[componentName] = convertComponentOverride(componentOverride);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
};
|
|
147
|
+
var convertOverrides = (overrides) => {
|
|
148
|
+
const convertedOverrides = {};
|
|
149
|
+
if (overrides) {
|
|
150
|
+
Object.entries(overrides).forEach(([key, value]) => {
|
|
151
|
+
convertedOverrides[key] = convertComponentOverride(value);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return convertedOverrides;
|
|
155
|
+
};
|
|
156
|
+
var convertConfig = (config) => {
|
|
157
|
+
return {
|
|
158
|
+
logLevel: config.logLevel ? getConfig2(config.logLevel) : defaultLogLevel,
|
|
159
|
+
logFormat: config.logFormat ? getConfig(config.logFormat) : defaultLogFormat,
|
|
160
|
+
overrides: convertOverrides(config.overrides),
|
|
161
|
+
floodControl: {
|
|
162
|
+
...defaultLoggingConfig.floodControl,
|
|
163
|
+
...config.floodControl || {}
|
|
164
|
+
},
|
|
165
|
+
masking: {
|
|
166
|
+
...defaultLoggingConfig.masking,
|
|
167
|
+
...config.masking || {}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
var resolveLogLevel = (config, category, components) => {
|
|
172
|
+
let logLevel = config.logLevel;
|
|
173
|
+
const overrides = config.overrides;
|
|
174
|
+
if (!overrides || !overrides[category]) {
|
|
175
|
+
return logLevel;
|
|
176
|
+
}
|
|
177
|
+
let currentOverride = overrides[category];
|
|
178
|
+
logLevel = currentOverride.logLevel;
|
|
179
|
+
for (const component of components) {
|
|
180
|
+
if (!currentOverride.components || !currentOverride.components[component]) {
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
currentOverride = currentOverride.components[component];
|
|
184
|
+
logLevel = currentOverride.logLevel;
|
|
185
|
+
}
|
|
186
|
+
return logLevel;
|
|
187
|
+
};
|
|
188
|
+
var configureLogging = () => {
|
|
189
|
+
let config = {};
|
|
190
|
+
const loggingConfigEnv = process.env.LOGGING_CONFIG;
|
|
191
|
+
const expoLoggingConfigEnv = process.env.EXPO_PUBLIC_LOGGING_CONFIG;
|
|
192
|
+
const nextLoggingConfigEnv = process.env.NEXT_PUBLIC_LOGGING_CONFIG;
|
|
193
|
+
let logLevelEnv = process.env.LOG_LEVEL;
|
|
194
|
+
let logFormatEnv = process.env.LOG_FORMAT;
|
|
195
|
+
if (loggingConfigEnv) {
|
|
196
|
+
try {
|
|
197
|
+
config = JSON.parse(loggingConfigEnv);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error("Invalid JSON in LOGGING_CONFIG environment variable:", error);
|
|
200
|
+
config = {};
|
|
201
|
+
}
|
|
202
|
+
} else if (expoLoggingConfigEnv) {
|
|
203
|
+
try {
|
|
204
|
+
config = JSON.parse(expoLoggingConfigEnv);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error("Invalid JSON in EXPO_PUBLIC_LOGGING_CONFIG environment variable:", error);
|
|
207
|
+
config = {};
|
|
208
|
+
}
|
|
209
|
+
} else if (nextLoggingConfigEnv) {
|
|
210
|
+
try {
|
|
211
|
+
config = JSON.parse(nextLoggingConfigEnv);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error("Invalid JSON in NEXT_PUBLIC_LOGGING_CONFIG environment variable:", error);
|
|
214
|
+
config = {};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const convertedConfig = convertConfig(config);
|
|
218
|
+
if (logLevelEnv) {
|
|
219
|
+
logLevelEnv = logLevelEnv?.toUpperCase();
|
|
220
|
+
const logLevelConfig = getConfig2(logLevelEnv);
|
|
221
|
+
convertedConfig.logLevel = logLevelConfig;
|
|
222
|
+
}
|
|
223
|
+
if (logFormatEnv) {
|
|
224
|
+
logFormatEnv = logFormatEnv.toUpperCase();
|
|
225
|
+
const logFormatConfig = getConfig(logFormatEnv);
|
|
226
|
+
convertedConfig.logFormat = logFormatConfig;
|
|
227
|
+
}
|
|
228
|
+
const finalConfig = { ...defaultLoggingConfig, ...convertedConfig };
|
|
229
|
+
return finalConfig;
|
|
230
|
+
};
|
|
231
|
+
var createWriter = (formatter, logMethod, options = {}) => {
|
|
232
|
+
const {
|
|
233
|
+
respectInjectedMethod = false,
|
|
234
|
+
errorMethod = console.error,
|
|
235
|
+
warningMethod = console.warn,
|
|
236
|
+
infoMethod = console.log
|
|
237
|
+
} = options;
|
|
238
|
+
return {
|
|
239
|
+
write: (level, coordinates, payload) => {
|
|
240
|
+
let finalLogMethod = logMethod;
|
|
241
|
+
if (!respectInjectedMethod) {
|
|
242
|
+
if (level.name === ERROR.name || level.name === CRITICAL.name || level.name === ALERT.name || level.name === EMERGENCY.name) {
|
|
243
|
+
finalLogMethod = errorMethod;
|
|
244
|
+
} else if (level.name === WARNING.name) {
|
|
245
|
+
finalLogMethod = warningMethod;
|
|
246
|
+
} else {
|
|
247
|
+
finalLogMethod = infoMethod;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
finalLogMethod(formatter.formatLog(level, coordinates, payload));
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
var stringifyJSON = function(obj, visited = /* @__PURE__ */ new Set()) {
|
|
255
|
+
try {
|
|
256
|
+
return stringifyJSONCustom(obj, visited);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error("[Fjell Logging] Critical error in stringifyJSON, using ultimate fallback:", error);
|
|
259
|
+
try {
|
|
260
|
+
return `"[Object: ${typeof obj}]"`;
|
|
261
|
+
} catch {
|
|
262
|
+
return '"[Object: unknown]"';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var STRINGIFY_CONFIG = {
|
|
267
|
+
MAX_ARRAY_ELEMENTS: 100,
|
|
268
|
+
MAX_OBJECT_PROPERTIES: 100,
|
|
269
|
+
TRUNCATION_MESSAGE: "...[truncated]"
|
|
270
|
+
};
|
|
271
|
+
var stringifyJSONCustom = function(obj, visited = /* @__PURE__ */ new Set()) {
|
|
272
|
+
try {
|
|
273
|
+
const arrOfKeyVals = [];
|
|
274
|
+
const arrVals = [];
|
|
275
|
+
let objKeys = [];
|
|
276
|
+
if (typeof obj === "number" || typeof obj === "boolean" || obj === null)
|
|
277
|
+
return "" + obj;
|
|
278
|
+
else if (typeof obj === "string")
|
|
279
|
+
return '"' + obj + '"';
|
|
280
|
+
else if (typeof obj === "symbol")
|
|
281
|
+
return "";
|
|
282
|
+
else if (typeof obj === "function")
|
|
283
|
+
return "";
|
|
284
|
+
else if (obj instanceof Date)
|
|
285
|
+
return "{}";
|
|
286
|
+
else if (obj instanceof RegExp)
|
|
287
|
+
return "{}";
|
|
288
|
+
else if (obj instanceof Error)
|
|
289
|
+
return "{}";
|
|
290
|
+
else if (typeof obj === "object" && obj.constructor && obj.constructor.name === "Buffer") {
|
|
291
|
+
const result = {};
|
|
292
|
+
for (let i = 0; i < obj.length; i++) {
|
|
293
|
+
result[i] = obj[i];
|
|
294
|
+
}
|
|
295
|
+
return stringifyJSONCustom(result, visited);
|
|
296
|
+
}
|
|
297
|
+
if (obj instanceof Object && visited.has(obj)) {
|
|
298
|
+
return '"(circular)"';
|
|
299
|
+
} else if (Array.isArray(obj)) {
|
|
300
|
+
if (obj.length === 0)
|
|
301
|
+
return "[]";
|
|
302
|
+
else {
|
|
303
|
+
visited.add(obj);
|
|
304
|
+
try {
|
|
305
|
+
const maxElements = STRINGIFY_CONFIG.MAX_ARRAY_ELEMENTS;
|
|
306
|
+
const shouldTruncate = obj.length > maxElements;
|
|
307
|
+
const elementsToProcess = shouldTruncate ? maxElements : obj.length;
|
|
308
|
+
for (let i = 0; i < elementsToProcess; i++) {
|
|
309
|
+
try {
|
|
310
|
+
arrVals.push(stringifyJSONCustom(obj[i], visited));
|
|
311
|
+
} catch {
|
|
312
|
+
arrVals.push('"[Error serializing array element]"');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (shouldTruncate) {
|
|
316
|
+
arrVals.push(`"${STRINGIFY_CONFIG.TRUNCATION_MESSAGE} (${obj.length - maxElements} more items)"`);
|
|
317
|
+
}
|
|
318
|
+
} finally {
|
|
319
|
+
visited.delete(obj);
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
return "[" + arrVals.join(",") + "]";
|
|
323
|
+
} catch {
|
|
324
|
+
console.warn("[Fjell Logging] Array too large to serialize completely, using truncated representation");
|
|
325
|
+
return `[${arrVals.slice(0, 10).join(",")},${STRINGIFY_CONFIG.TRUNCATION_MESSAGE}]`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
} else if (obj instanceof Object) {
|
|
329
|
+
visited.add(obj);
|
|
330
|
+
try {
|
|
331
|
+
objKeys = Object.keys(obj);
|
|
332
|
+
const maxProperties = STRINGIFY_CONFIG.MAX_OBJECT_PROPERTIES;
|
|
333
|
+
const shouldTruncate = objKeys.length > maxProperties;
|
|
334
|
+
const propertiesToProcess = shouldTruncate ? maxProperties : objKeys.length;
|
|
335
|
+
for (let i = 0; i < propertiesToProcess; i++) {
|
|
336
|
+
const key = objKeys[i];
|
|
337
|
+
try {
|
|
338
|
+
const keyOut = '"' + key + '":';
|
|
339
|
+
const keyValOut = obj[key];
|
|
340
|
+
if (keyValOut instanceof Function || typeof keyValOut === "undefined")
|
|
341
|
+
continue;
|
|
342
|
+
else if (typeof keyValOut === "string")
|
|
343
|
+
arrOfKeyVals.push(keyOut + '"' + keyValOut + '"');
|
|
344
|
+
else if (typeof keyValOut === "boolean" || typeof keyValOut === "number" || keyValOut === null)
|
|
345
|
+
arrOfKeyVals.push(keyOut + keyValOut);
|
|
346
|
+
else if (keyValOut instanceof Object) {
|
|
347
|
+
arrOfKeyVals.push(keyOut + stringifyJSONCustom(keyValOut, visited));
|
|
348
|
+
}
|
|
349
|
+
} catch {
|
|
350
|
+
arrOfKeyVals.push('"' + key + '":"[Error serializing property]"');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (shouldTruncate) {
|
|
354
|
+
arrOfKeyVals.push(`"${STRINGIFY_CONFIG.TRUNCATION_MESSAGE}":"(${objKeys.length - maxProperties} more properties)"`);
|
|
355
|
+
}
|
|
356
|
+
} finally {
|
|
357
|
+
visited.delete(obj);
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
return "{" + arrOfKeyVals.join(",") + "}";
|
|
361
|
+
} catch {
|
|
362
|
+
console.warn("[Fjell Logging] Object too large to serialize completely, using truncated representation");
|
|
363
|
+
return `{${arrOfKeyVals.slice(0, 10).join(",")},${STRINGIFY_CONFIG.TRUNCATION_MESSAGE}}`;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return "";
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error("[Fjell Logging] Error in stringifyJSONCustom, using fallback:", error);
|
|
369
|
+
return "[Object: serialization failed]";
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var safeFormat = (message, ...args) => {
|
|
373
|
+
let result = message;
|
|
374
|
+
let argIndex = 0;
|
|
375
|
+
result = result.replace(/%([sdjifoO%])/g, (match, specifier) => {
|
|
376
|
+
if (specifier === "%") {
|
|
377
|
+
return "%";
|
|
378
|
+
}
|
|
379
|
+
if (argIndex >= args.length) {
|
|
380
|
+
return match;
|
|
381
|
+
}
|
|
382
|
+
const arg = args[argIndex++];
|
|
383
|
+
switch (specifier) {
|
|
384
|
+
case "s":
|
|
385
|
+
return String(arg);
|
|
386
|
+
case "d":
|
|
387
|
+
return String(parseInt(arg, 10));
|
|
388
|
+
case "i":
|
|
389
|
+
return String(parseInt(arg, 10));
|
|
390
|
+
case "f":
|
|
391
|
+
return String(parseFloat(arg));
|
|
392
|
+
case "j":
|
|
393
|
+
try {
|
|
394
|
+
return stringifyJSON(arg);
|
|
395
|
+
} catch {
|
|
396
|
+
return String(arg);
|
|
397
|
+
}
|
|
398
|
+
case "o":
|
|
399
|
+
return stringifyJSON(arg);
|
|
400
|
+
case "O":
|
|
401
|
+
return stringifyJSON(arg);
|
|
402
|
+
default:
|
|
403
|
+
return String(arg);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
return result;
|
|
407
|
+
};
|
|
408
|
+
var safeInspect = (obj) => {
|
|
409
|
+
try {
|
|
410
|
+
if (obj && typeof obj === "object" && obj.problematic && typeof obj.problematic === "object") {
|
|
411
|
+
return "[Object: object]";
|
|
412
|
+
}
|
|
413
|
+
return stringifyJSON(obj);
|
|
414
|
+
} catch {
|
|
415
|
+
return `[Object: ${typeof obj}]`;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
var safeJSONStringify = (obj) => {
|
|
419
|
+
try {
|
|
420
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
421
|
+
return JSON.stringify(obj, (key, value) => {
|
|
422
|
+
try {
|
|
423
|
+
if (typeof value === "symbol") {
|
|
424
|
+
return String(value);
|
|
425
|
+
}
|
|
426
|
+
if (typeof value === "function") {
|
|
427
|
+
return "[Function]";
|
|
428
|
+
}
|
|
429
|
+
if (typeof value === "object" && value !== null) {
|
|
430
|
+
if (seen.has(value)) {
|
|
431
|
+
return "[Circular Reference]";
|
|
432
|
+
}
|
|
433
|
+
seen.add(value);
|
|
434
|
+
}
|
|
435
|
+
if (value instanceof Error) {
|
|
436
|
+
return {
|
|
437
|
+
name: value.name,
|
|
438
|
+
message: value.message,
|
|
439
|
+
stack: value.stack
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
if (value instanceof RegExp) {
|
|
443
|
+
return value.toString();
|
|
444
|
+
}
|
|
445
|
+
if (value instanceof Date) {
|
|
446
|
+
return value.toISOString();
|
|
447
|
+
}
|
|
448
|
+
return value;
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error("[Fjell Logging] Error processing value in replacer:", error);
|
|
451
|
+
return "[Error: unable to serialize value]";
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
} catch (error) {
|
|
455
|
+
console.error("[Fjell Logging] CRITICAL: safeJSONStringify failed, returning fallback:", error);
|
|
456
|
+
try {
|
|
457
|
+
const message = obj?.message || obj?.severity || "Unknown";
|
|
458
|
+
return JSON.stringify({
|
|
459
|
+
severity: "ERROR",
|
|
460
|
+
message: "[Fjell Logging] Failed to serialize log entry",
|
|
461
|
+
originalMessage: String(message),
|
|
462
|
+
error: "Circular reference or non-serializable object detected"
|
|
463
|
+
});
|
|
464
|
+
} catch {
|
|
465
|
+
return '{"severity":"ERROR","message":"[Fjell Logging] Critical serialization failure"}';
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
var createFormatter = (logFormat) => {
|
|
470
|
+
if (logFormat.name === "TEXT") {
|
|
471
|
+
return getTextFormatter();
|
|
472
|
+
} else if (logFormat.name === "STRUCTURED") {
|
|
473
|
+
return getStructuredFormatter();
|
|
474
|
+
}
|
|
475
|
+
throw new Error(`Unknown log format: ${logFormat.name}`);
|
|
476
|
+
};
|
|
477
|
+
var getTextFormatter = () => {
|
|
478
|
+
const formatLog = (level, coordinates, payload) => {
|
|
479
|
+
const hasSpecifiers = /%[sdjifoO%]/.test(payload.message);
|
|
480
|
+
let logMessage;
|
|
481
|
+
if (payload.data.length === 0) {
|
|
482
|
+
logMessage = payload.message;
|
|
483
|
+
} else if (hasSpecifiers) {
|
|
484
|
+
logMessage = safeFormat(payload.message, ...payload.data);
|
|
485
|
+
} else {
|
|
486
|
+
logMessage = `${payload.message} ${safeInspect(payload.data)}`;
|
|
487
|
+
}
|
|
488
|
+
return `(${(/* @__PURE__ */ new Date()).valueOf()}) [${level.name}] - [${coordinates.category}] ${coordinates.components.map((c) => `[${c}]`)} ${logMessage}`;
|
|
489
|
+
};
|
|
490
|
+
const timerMessage = (level, coordinates, payload) => {
|
|
491
|
+
const randomInt = Math.floor(Math.random() * 1e6);
|
|
492
|
+
const timerMessage2 = `(${(/* @__PURE__ */ new Date()).valueOf()}) [${level.name}] - [${coordinates.category}] ${coordinates.components.map((c) => `[${c}]`)} ${safeFormat(payload.message, ...payload.data)} ${safeInspect(payload.data)} ${randomInt}`;
|
|
493
|
+
return timerMessage2;
|
|
494
|
+
};
|
|
495
|
+
return { formatLog, timerMessage, getLogFormat: () => TEXT };
|
|
496
|
+
};
|
|
497
|
+
var getStructuredFormatter = () => {
|
|
498
|
+
const formatLog = (level, coordinates, payload) => {
|
|
499
|
+
const severity = level.name;
|
|
500
|
+
const hasSpecifiers = /%[sdjifoO%]/.test(payload.message);
|
|
501
|
+
return safeJSONStringify({
|
|
502
|
+
severity,
|
|
503
|
+
message: hasSpecifiers ? safeFormat(payload.message, ...payload.data) : payload.message,
|
|
504
|
+
"logging.googleapis.com/labels": {
|
|
505
|
+
category: coordinates.category,
|
|
506
|
+
components: `${coordinates.components.map((c) => `[${c}]`)}`
|
|
507
|
+
},
|
|
508
|
+
...!hasSpecifiers && payload.data.length > 0 && { data: safeInspect(payload.data) }
|
|
509
|
+
});
|
|
510
|
+
};
|
|
511
|
+
const timerMessage = (level, coordinates, payload) => {
|
|
512
|
+
const severity = level.name;
|
|
513
|
+
const randomInt = Math.floor(Math.random() * 1e6);
|
|
514
|
+
return safeJSONStringify({
|
|
515
|
+
severity,
|
|
516
|
+
message: safeFormat(payload.message, ...payload.data),
|
|
517
|
+
"logging.googleapis.com/labels": {
|
|
518
|
+
category: coordinates.category,
|
|
519
|
+
components: `${coordinates.components.map((c) => `[${c}]`)}`
|
|
520
|
+
},
|
|
521
|
+
data: safeInspect(payload.data),
|
|
522
|
+
"logging.googleapis.com/spanId": String(randomInt)
|
|
523
|
+
});
|
|
524
|
+
};
|
|
525
|
+
return { formatLog, timerMessage, getLogFormat: () => STRUCTURED };
|
|
526
|
+
};
|
|
527
|
+
var hash = (message, data) => {
|
|
528
|
+
const dataString = data.map((item) => {
|
|
529
|
+
try {
|
|
530
|
+
return JSON.stringify(item);
|
|
531
|
+
} catch {
|
|
532
|
+
return stringifyJSON(item);
|
|
533
|
+
}
|
|
534
|
+
}).join("");
|
|
535
|
+
return `${message}${dataString}`;
|
|
536
|
+
};
|
|
537
|
+
var FloodControl = class {
|
|
538
|
+
config;
|
|
539
|
+
history = /* @__PURE__ */ new Map();
|
|
540
|
+
suppressed = /* @__PURE__ */ new Map();
|
|
541
|
+
cleanupTimer = null;
|
|
542
|
+
constructor(config) {
|
|
543
|
+
this.config = config;
|
|
544
|
+
if (this.config.enabled) {
|
|
545
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), this.config.timeframe * 2);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
destroy() {
|
|
549
|
+
if (this.cleanupTimer) {
|
|
550
|
+
clearInterval(this.cleanupTimer);
|
|
551
|
+
this.cleanupTimer = null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
cleanup() {
|
|
555
|
+
const now = Date.now();
|
|
556
|
+
for (const [hash2, timestamps] of this.history.entries()) {
|
|
557
|
+
const recentTimestamps = timestamps.filter(
|
|
558
|
+
(timestamp) => now - timestamp < this.config.timeframe
|
|
559
|
+
);
|
|
560
|
+
if (recentTimestamps.length > 0) {
|
|
561
|
+
this.history.set(hash2, recentTimestamps);
|
|
562
|
+
} else {
|
|
563
|
+
this.history.delete(hash2);
|
|
564
|
+
this.suppressed.delete(hash2);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
check(message, data) {
|
|
569
|
+
if (!this.config.enabled) {
|
|
570
|
+
return "log";
|
|
571
|
+
}
|
|
572
|
+
const messageHash = hash(message, data);
|
|
573
|
+
const now = Date.now();
|
|
574
|
+
const timestamps = (this.history.get(messageHash) || []).filter(
|
|
575
|
+
(timestamp) => now - timestamp < this.config.timeframe
|
|
576
|
+
);
|
|
577
|
+
timestamps.push(now);
|
|
578
|
+
this.history.set(messageHash, timestamps);
|
|
579
|
+
if (timestamps.length > this.config.threshold) {
|
|
580
|
+
const suppressedInfo = this.suppressed.get(messageHash);
|
|
581
|
+
if (suppressedInfo) {
|
|
582
|
+
suppressedInfo.count++;
|
|
583
|
+
return "suppress";
|
|
584
|
+
} else {
|
|
585
|
+
this.suppressed.set(messageHash, { count: 1, firstTimestamp: timestamps[0], summaryLogged: false });
|
|
586
|
+
return "suppress";
|
|
587
|
+
}
|
|
588
|
+
} else {
|
|
589
|
+
if (this.suppressed.has(messageHash)) {
|
|
590
|
+
this.suppressed.delete(messageHash);
|
|
591
|
+
return "resume";
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return "log";
|
|
595
|
+
}
|
|
596
|
+
getSuppressedCount(message, data) {
|
|
597
|
+
const messageHash = hash(message, data);
|
|
598
|
+
return this.suppressed.get(messageHash)?.count || 0;
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
var createLogger = (logFormat, logLevel, coordinates, floodControlConfig, loggingConfig, writerOptions, options) => {
|
|
602
|
+
const formatter = createFormatter(logFormat);
|
|
603
|
+
const floodControl = floodControlConfig.enabled ? new FloodControl(floodControlConfig) : null;
|
|
604
|
+
const logFunction = console.log;
|
|
605
|
+
const writer = createWriter(formatter, logFunction, writerOptions);
|
|
606
|
+
const asyncLogging = options?.asyncLogging !== false;
|
|
607
|
+
const enableDebugBuffering = asyncLogging;
|
|
608
|
+
const debugBuffer = [];
|
|
609
|
+
const DEBUG_BUFFER_SIZE = 100;
|
|
610
|
+
const DEBUG_FLUSH_INTERVAL = 100;
|
|
611
|
+
let debugFlushTimer = null;
|
|
612
|
+
const flushDebugBuffer = () => {
|
|
613
|
+
if (debugBuffer.length === 0) return;
|
|
614
|
+
try {
|
|
615
|
+
const messagesToFlush = [...debugBuffer];
|
|
616
|
+
debugBuffer.length = 0;
|
|
617
|
+
messagesToFlush.forEach(({ level, coordinates: coordinates2, payload }) => {
|
|
618
|
+
try {
|
|
619
|
+
writer.write(level, coordinates2, payload);
|
|
620
|
+
} catch (error) {
|
|
621
|
+
console.error("[Fjell Logging] Error writing buffered log message:", error);
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
} catch (error) {
|
|
625
|
+
console.error("[Fjell Logging] Error flushing debug buffer:", error);
|
|
626
|
+
} finally {
|
|
627
|
+
if (debugFlushTimer) {
|
|
628
|
+
clearTimeout(debugFlushTimer);
|
|
629
|
+
debugFlushTimer = null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
const scheduleDebugFlush = () => {
|
|
634
|
+
if (debugFlushTimer) return;
|
|
635
|
+
try {
|
|
636
|
+
debugFlushTimer = setTimeout(() => {
|
|
637
|
+
try {
|
|
638
|
+
flushDebugBuffer();
|
|
639
|
+
} catch (error) {
|
|
640
|
+
console.error("[Fjell Logging] Error in scheduled debug flush:", error);
|
|
641
|
+
}
|
|
642
|
+
}, DEBUG_FLUSH_INTERVAL);
|
|
643
|
+
} catch (error) {
|
|
644
|
+
console.error("[Fjell Logging] Error scheduling debug flush:", error);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
const writeImmediate = (level, coordinates2, payload) => {
|
|
648
|
+
try {
|
|
649
|
+
writer.write(level, coordinates2, payload);
|
|
650
|
+
} catch (error) {
|
|
651
|
+
console.error("[Fjell Logging] Error writing log message:", error);
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
const handleDebugBuffering = (level, coordinates2, payload) => {
|
|
655
|
+
try {
|
|
656
|
+
debugBuffer.push({ level, coordinates: coordinates2, payload });
|
|
657
|
+
if (debugBuffer.length >= DEBUG_BUFFER_SIZE) {
|
|
658
|
+
flushDebugBuffer();
|
|
659
|
+
} else {
|
|
660
|
+
scheduleDebugFlush();
|
|
661
|
+
}
|
|
662
|
+
} catch (error) {
|
|
663
|
+
console.error("[Fjell Logging] Error buffering debug message, falling back to immediate write:", error);
|
|
664
|
+
writeImmediate(level, coordinates2, payload);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
const write = (level, message, data) => {
|
|
668
|
+
if (logLevel.value < level.value) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const check = floodControl ? floodControl.check(message, data) : "log";
|
|
672
|
+
const payload = { message, data };
|
|
673
|
+
const asyncWrite = () => {
|
|
674
|
+
try {
|
|
675
|
+
switch (check) {
|
|
676
|
+
case "log":
|
|
677
|
+
if (enableDebugBuffering && (level.name === "TRACE" || level.name === "DEFAULT" || level.name === "DEBUG")) {
|
|
678
|
+
handleDebugBuffering(level, coordinates, payload);
|
|
679
|
+
} else {
|
|
680
|
+
writeImmediate(level, coordinates, payload);
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
683
|
+
case "suppress":
|
|
684
|
+
if (floodControl && floodControl.getSuppressedCount(message, data) === 1) {
|
|
685
|
+
try {
|
|
686
|
+
const originalLevel = level;
|
|
687
|
+
const newPayload = { message: `Started suppressing repeated log message`, data: [] };
|
|
688
|
+
writer.write(originalLevel, coordinates, newPayload);
|
|
689
|
+
} catch (error) {
|
|
690
|
+
console.error("[Fjell Logging] Error writing suppress message:", error);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
break;
|
|
694
|
+
case "resume": {
|
|
695
|
+
try {
|
|
696
|
+
const count = floodControl ? floodControl.getSuppressedCount(message, data) : 0;
|
|
697
|
+
const resumePayload = {
|
|
698
|
+
message: `Stopped suppressing repeated log message. Suppressed ${count} times.`,
|
|
699
|
+
data: []
|
|
700
|
+
};
|
|
701
|
+
writer.write(level, coordinates, resumePayload);
|
|
702
|
+
writer.write(level, coordinates, payload);
|
|
703
|
+
} catch (error) {
|
|
704
|
+
console.error("[Fjell Logging] Error writing resume messages:", error);
|
|
705
|
+
}
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
} catch (error) {
|
|
710
|
+
console.error("[Fjell Logging] Error in async write operation:", error);
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
if (asyncLogging) {
|
|
714
|
+
try {
|
|
715
|
+
if (typeof setImmediate !== "undefined") {
|
|
716
|
+
setImmediate(asyncWrite);
|
|
717
|
+
} else {
|
|
718
|
+
setTimeout(asyncWrite, 0);
|
|
719
|
+
}
|
|
720
|
+
} catch (error) {
|
|
721
|
+
console.error("[Fjell Logging] Error scheduling async write, falling back to sync:", error);
|
|
722
|
+
try {
|
|
723
|
+
asyncWrite();
|
|
724
|
+
} catch (syncError) {
|
|
725
|
+
console.error("[Fjell Logging] Error in synchronous fallback write:", syncError);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
} else {
|
|
729
|
+
asyncWrite();
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
const startTimeLogger = (logLevel2, coordinates2, payload) => {
|
|
733
|
+
const timerMessage = formatter.timerMessage(logLevel2, coordinates2, payload);
|
|
734
|
+
logLevel2.value >= DEBUG.value && console.time(timerMessage);
|
|
735
|
+
return {
|
|
736
|
+
end: () => {
|
|
737
|
+
logLevel2.value >= DEBUG.value && console.timeEnd(timerMessage);
|
|
738
|
+
},
|
|
739
|
+
log: (...data) => {
|
|
740
|
+
logLevel2.value >= DEBUG.value && console.timeLog(timerMessage, ...data);
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
};
|
|
744
|
+
return {
|
|
745
|
+
emergency: (message, ...data) => {
|
|
746
|
+
write(EMERGENCY, message, data);
|
|
747
|
+
},
|
|
748
|
+
alert: (message, ...data) => {
|
|
749
|
+
write(ALERT, message, data);
|
|
750
|
+
},
|
|
751
|
+
critical: (message, ...data) => {
|
|
752
|
+
write(CRITICAL, message, data);
|
|
753
|
+
},
|
|
754
|
+
error: (message, ...data) => {
|
|
755
|
+
write(ERROR, message, data);
|
|
756
|
+
},
|
|
757
|
+
warning: (message, ...data) => {
|
|
758
|
+
write(WARNING, message, data);
|
|
759
|
+
},
|
|
760
|
+
notice: (message, ...data) => {
|
|
761
|
+
write(NOTICE, message, data);
|
|
762
|
+
},
|
|
763
|
+
info: (message, ...data) => {
|
|
764
|
+
write(INFO, message, data);
|
|
765
|
+
},
|
|
766
|
+
debug: (message, ...data) => {
|
|
767
|
+
write(DEBUG, message, data);
|
|
768
|
+
},
|
|
769
|
+
trace: (message, ...data) => {
|
|
770
|
+
write(TRACE, message, data);
|
|
771
|
+
},
|
|
772
|
+
default: (message, ...data) => {
|
|
773
|
+
write(DEFAULT, message, data);
|
|
774
|
+
},
|
|
775
|
+
time: (message, ...data) => {
|
|
776
|
+
const payload = { message, data };
|
|
777
|
+
return startTimeLogger(logLevel, coordinates, payload);
|
|
778
|
+
},
|
|
779
|
+
get: (...additionalComponents) => {
|
|
780
|
+
const newComponents = [...coordinates.components, ...additionalComponents];
|
|
781
|
+
let childLogLevel = logLevel;
|
|
782
|
+
if (loggingConfig) {
|
|
783
|
+
childLogLevel = resolveLogLevel(loggingConfig, coordinates.category, newComponents);
|
|
784
|
+
}
|
|
785
|
+
return createLogger(logFormat, childLogLevel, {
|
|
786
|
+
category: coordinates.category,
|
|
787
|
+
components: newComponents
|
|
788
|
+
}, floodControlConfig, loggingConfig, writerOptions, options);
|
|
789
|
+
},
|
|
790
|
+
destroy: () => {
|
|
791
|
+
try {
|
|
792
|
+
flushDebugBuffer();
|
|
793
|
+
} catch (error) {
|
|
794
|
+
console.error("[Fjell Logging] Error flushing debug buffer during destroy:", error);
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
if (debugFlushTimer) {
|
|
798
|
+
clearTimeout(debugFlushTimer);
|
|
799
|
+
debugFlushTimer = null;
|
|
800
|
+
}
|
|
801
|
+
} catch (error) {
|
|
802
|
+
console.error("[Fjell Logging] Error clearing debug flush timer during destroy:", error);
|
|
803
|
+
}
|
|
804
|
+
try {
|
|
805
|
+
if (floodControl) {
|
|
806
|
+
floodControl.destroy();
|
|
807
|
+
}
|
|
808
|
+
} catch (error) {
|
|
809
|
+
console.error("[Fjell Logging] Error destroying flood control during destroy:", error);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
};
|
|
814
|
+
var getLogger = (name) => {
|
|
815
|
+
const config = configureLogging();
|
|
816
|
+
const logger = createBaseLogger(name, config);
|
|
817
|
+
return logger;
|
|
818
|
+
};
|
|
819
|
+
var createBaseLogger = (name, config) => {
|
|
820
|
+
const { logFormat, floodControl } = config;
|
|
821
|
+
const coordinates = { category: name, components: [] };
|
|
822
|
+
const logLevel = resolveLogLevel(config, name, []);
|
|
823
|
+
return createLogger(logFormat, logLevel, coordinates, floodControl, config, void 0, {
|
|
824
|
+
asyncLogging: false
|
|
825
|
+
});
|
|
826
|
+
};
|
|
827
|
+
var index_default = { getLogger };
|
|
828
|
+
const LIBRARY_NAME = "agentic";
|
|
829
|
+
const LibLogger = index_default.getLogger("@riotprompt/agentic");
|
|
830
|
+
function createSilentLogger(name) {
|
|
831
|
+
return {
|
|
832
|
+
name,
|
|
833
|
+
debug: () => {
|
|
834
|
+
},
|
|
835
|
+
info: () => {
|
|
836
|
+
},
|
|
837
|
+
warn: () => {
|
|
838
|
+
},
|
|
839
|
+
error: () => {
|
|
840
|
+
},
|
|
841
|
+
verbose: () => {
|
|
842
|
+
},
|
|
843
|
+
silly: () => {
|
|
844
|
+
},
|
|
845
|
+
get: (...components) => createSilentLogger(`${name}:${components.join(":")}`)
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
const SILENT_LOGGER = createSilentLogger("silent");
|
|
849
|
+
const isLoggingEnabled = () => {
|
|
850
|
+
return process.env.AGENTIC_LOGGING === "true" || process.env.DEBUG?.includes("agentic") || process.env.NODE_ENV === "development";
|
|
851
|
+
};
|
|
852
|
+
function createLoggerFromFjell(fjellLogger, name) {
|
|
853
|
+
return {
|
|
854
|
+
name,
|
|
855
|
+
debug: (message, ...args) => fjellLogger.debug(message, ...args),
|
|
856
|
+
info: (message, ...args) => fjellLogger.info(message, ...args),
|
|
857
|
+
warn: (message, ...args) => fjellLogger.warning(message, ...args),
|
|
858
|
+
error: (message, ...args) => fjellLogger.error(message, ...args),
|
|
859
|
+
verbose: (message, ...args) => fjellLogger.debug(message, ...args),
|
|
860
|
+
silly: (message, ...args) => fjellLogger.debug(message, ...args),
|
|
861
|
+
get: (...components) => {
|
|
862
|
+
const childLogger = fjellLogger.get(...components);
|
|
863
|
+
return createLoggerFromFjell(childLogger, `${name}:${components.join(":")}`);
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
const FJELL_LOGGER = {
|
|
868
|
+
name: "fjell",
|
|
869
|
+
debug: (message, ...args) => LibLogger.debug(message, ...args),
|
|
870
|
+
info: (message, ...args) => LibLogger.info(message, ...args),
|
|
871
|
+
warn: (message, ...args) => LibLogger.warning(message, ...args),
|
|
872
|
+
error: (message, ...args) => LibLogger.error(message, ...args),
|
|
873
|
+
verbose: (message, ...args) => LibLogger.debug(message, ...args),
|
|
874
|
+
silly: (message, ...args) => LibLogger.debug(message, ...args),
|
|
875
|
+
get: (...components) => {
|
|
876
|
+
const childLogger = LibLogger.get(...components);
|
|
877
|
+
return createLoggerFromFjell(childLogger, components.join(":"));
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
const DEFAULT_LOGGER = isLoggingEnabled() ? FJELL_LOGGER : SILENT_LOGGER;
|
|
881
|
+
function wrapLogger(toWrap, name) {
|
|
882
|
+
const requiredMethods = [
|
|
883
|
+
"debug",
|
|
884
|
+
"info",
|
|
885
|
+
"warn",
|
|
886
|
+
"error",
|
|
887
|
+
"verbose",
|
|
888
|
+
"silly"
|
|
889
|
+
];
|
|
890
|
+
const missingMethods = requiredMethods.filter(
|
|
891
|
+
(method) => typeof toWrap[method] !== "function"
|
|
892
|
+
);
|
|
893
|
+
if (missingMethods.length > 0) {
|
|
894
|
+
throw new Error(
|
|
895
|
+
`Logger is missing required methods: ${missingMethods.join(", ")}`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
const log = (level, message, ...args) => {
|
|
899
|
+
message = `[${LIBRARY_NAME}] ${name ? `[${name}]` : ""}: ${message}`;
|
|
900
|
+
if (level === "debug") toWrap.debug(message, ...args);
|
|
901
|
+
else if (level === "info") toWrap.info(message, ...args);
|
|
902
|
+
else if (level === "warn") toWrap.warn(message, ...args);
|
|
903
|
+
else if (level === "error") toWrap.error(message, ...args);
|
|
904
|
+
else if (level === "verbose") toWrap.verbose(message, ...args);
|
|
905
|
+
else if (level === "silly") toWrap.silly(message, ...args);
|
|
906
|
+
};
|
|
907
|
+
return {
|
|
908
|
+
name: name || "wrapped",
|
|
909
|
+
debug: (message, ...args) => log("debug", message, ...args),
|
|
910
|
+
info: (message, ...args) => log("info", message, ...args),
|
|
911
|
+
warn: (message, ...args) => log("warn", message, ...args),
|
|
912
|
+
error: (message, ...args) => log("error", message, ...args),
|
|
913
|
+
verbose: (message, ...args) => log("verbose", message, ...args),
|
|
914
|
+
silly: (message, ...args) => log("silly", message, ...args),
|
|
915
|
+
get: (...components) => wrapLogger(toWrap, name ? `${name}:${components.join(":")}` : components.join(":"))
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
const ToolSchema = z.object({
|
|
919
|
+
name: z.string().min(1),
|
|
920
|
+
description: z.string().min(1),
|
|
921
|
+
parameters: z.object({
|
|
922
|
+
type: z.literal("object"),
|
|
923
|
+
properties: z.record(z.string(), z.any()).default({}),
|
|
924
|
+
required: z.array(z.string()).optional()
|
|
925
|
+
}).passthrough(),
|
|
926
|
+
execute: z.custom(
|
|
927
|
+
(val) => typeof val === "function",
|
|
928
|
+
{ message: "execute must be a function" }
|
|
929
|
+
),
|
|
930
|
+
category: z.string().optional(),
|
|
931
|
+
cost: z.enum(["cheap", "moderate", "expensive"]).optional(),
|
|
932
|
+
examples: z.array(
|
|
933
|
+
z.object({
|
|
934
|
+
scenario: z.string(),
|
|
935
|
+
params: z.any(),
|
|
936
|
+
expectedResult: z.string()
|
|
937
|
+
})
|
|
938
|
+
).optional()
|
|
939
|
+
}).passthrough();
|
|
940
|
+
class ToolRegistry {
|
|
941
|
+
tools;
|
|
942
|
+
context;
|
|
943
|
+
logger;
|
|
944
|
+
usageStats;
|
|
945
|
+
toolGuard;
|
|
946
|
+
toolSandbox;
|
|
947
|
+
constructor(context = {}, logger) {
|
|
948
|
+
this.tools = /* @__PURE__ */ new Map();
|
|
949
|
+
this.context = context;
|
|
950
|
+
this.logger = wrapLogger(logger || DEFAULT_LOGGER, "ToolRegistry");
|
|
951
|
+
this.usageStats = /* @__PURE__ */ new Map();
|
|
952
|
+
this.logger.debug("Created ToolRegistry");
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Create a new ToolRegistry instance
|
|
956
|
+
*/
|
|
957
|
+
static create(context, logger) {
|
|
958
|
+
return new ToolRegistry(context, logger);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Configure security guard for tool execution
|
|
962
|
+
*/
|
|
963
|
+
withSecurity(guard) {
|
|
964
|
+
this.toolGuard = guard;
|
|
965
|
+
this.logger.debug("Security guard configured");
|
|
966
|
+
return this;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Get the configured security guard
|
|
970
|
+
*/
|
|
971
|
+
getSecurityGuard() {
|
|
972
|
+
return this.toolGuard;
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Configure sandbox for tool execution
|
|
976
|
+
*/
|
|
977
|
+
withSandbox(sandbox) {
|
|
978
|
+
this.toolSandbox = sandbox;
|
|
979
|
+
this.logger.debug("Sandbox configured");
|
|
980
|
+
return this;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Get the configured sandbox
|
|
984
|
+
*/
|
|
985
|
+
getSandbox() {
|
|
986
|
+
return this.toolSandbox;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Register a single tool
|
|
990
|
+
*/
|
|
991
|
+
register(tool) {
|
|
992
|
+
try {
|
|
993
|
+
ToolSchema.parse(tool);
|
|
994
|
+
} catch (error) {
|
|
995
|
+
throw new Error(`Invalid tool definition for "${tool.name}": ${error}`);
|
|
996
|
+
}
|
|
997
|
+
if (this.tools.has(tool.name)) {
|
|
998
|
+
this.logger.warn(`Tool "${tool.name}" already registered, overwriting`);
|
|
999
|
+
}
|
|
1000
|
+
this.tools.set(tool.name, tool);
|
|
1001
|
+
this.usageStats.set(tool.name, { calls: 0, failures: 0, totalDuration: 0 });
|
|
1002
|
+
this.logger.debug("Registered tool", {
|
|
1003
|
+
name: tool.name,
|
|
1004
|
+
category: tool.category
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Register multiple tools at once
|
|
1009
|
+
*/
|
|
1010
|
+
registerAll(tools) {
|
|
1011
|
+
this.logger.debug("Registering multiple tools", { count: tools.length });
|
|
1012
|
+
tools.forEach((tool) => this.register(tool));
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Get a tool by name
|
|
1016
|
+
*/
|
|
1017
|
+
get(name) {
|
|
1018
|
+
return this.tools.get(name);
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Get all registered tools
|
|
1022
|
+
*/
|
|
1023
|
+
getAll() {
|
|
1024
|
+
return Array.from(this.tools.values());
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Get tools by category
|
|
1028
|
+
*/
|
|
1029
|
+
getByCategory(category) {
|
|
1030
|
+
return this.getAll().filter((tool) => tool.category === category);
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Check if a tool is registered
|
|
1034
|
+
*/
|
|
1035
|
+
has(name) {
|
|
1036
|
+
return this.tools.has(name);
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Get number of registered tools
|
|
1040
|
+
*/
|
|
1041
|
+
count() {
|
|
1042
|
+
return this.tools.size;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Execute a tool by name
|
|
1046
|
+
*/
|
|
1047
|
+
async execute(name, params) {
|
|
1048
|
+
const tool = this.tools.get(name);
|
|
1049
|
+
if (!tool) {
|
|
1050
|
+
throw new Error(`Tool "${name}" not found`);
|
|
1051
|
+
}
|
|
1052
|
+
if (this.toolGuard && !this.toolGuard.isToolAllowed(name)) {
|
|
1053
|
+
throw new Error(`Tool "${name}" is not allowed`);
|
|
1054
|
+
}
|
|
1055
|
+
if (this.toolGuard && tool.schema) {
|
|
1056
|
+
const validation = this.toolGuard.validateParams(
|
|
1057
|
+
name,
|
|
1058
|
+
params,
|
|
1059
|
+
tool.schema
|
|
1060
|
+
);
|
|
1061
|
+
if (!validation.success) {
|
|
1062
|
+
throw new Error(`Tool "${name}": ${validation.error}`);
|
|
1063
|
+
}
|
|
1064
|
+
params = validation.data;
|
|
1065
|
+
}
|
|
1066
|
+
this.logger.debug("Executing tool", { name, params });
|
|
1067
|
+
const startTime = Date.now();
|
|
1068
|
+
const stats = this.usageStats.get(name);
|
|
1069
|
+
stats.calls++;
|
|
1070
|
+
try {
|
|
1071
|
+
let result;
|
|
1072
|
+
if (this.toolSandbox) {
|
|
1073
|
+
result = await this.toolSandbox.execute(
|
|
1074
|
+
tool,
|
|
1075
|
+
params,
|
|
1076
|
+
this.context
|
|
1077
|
+
);
|
|
1078
|
+
} else {
|
|
1079
|
+
result = await tool.execute(params, this.context);
|
|
1080
|
+
}
|
|
1081
|
+
const duration = Date.now() - startTime;
|
|
1082
|
+
stats.totalDuration += duration;
|
|
1083
|
+
this.logger.debug("Tool execution succeeded", { name, duration });
|
|
1084
|
+
return result;
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
stats.failures++;
|
|
1087
|
+
this.logger.error("Tool execution failed", { name, error });
|
|
1088
|
+
throw error;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Safely parse and execute a tool from JSON arguments
|
|
1093
|
+
*/
|
|
1094
|
+
async executeFromJSON(name, jsonArgs) {
|
|
1095
|
+
if (this.toolGuard) {
|
|
1096
|
+
const parseResult = this.toolGuard.parseToolArguments(name, jsonArgs);
|
|
1097
|
+
if (!parseResult.success) {
|
|
1098
|
+
throw new Error(`Tool "${name}": ${parseResult.error}`);
|
|
1099
|
+
}
|
|
1100
|
+
return this.execute(name, parseResult.data);
|
|
1101
|
+
}
|
|
1102
|
+
const params = JSON.parse(jsonArgs);
|
|
1103
|
+
return this.execute(name, params);
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Execute multiple tools in sequence
|
|
1107
|
+
*/
|
|
1108
|
+
async executeBatch(calls) {
|
|
1109
|
+
this.logger.debug("Executing batch", { count: calls.length });
|
|
1110
|
+
const results = [];
|
|
1111
|
+
for (const call of calls) {
|
|
1112
|
+
try {
|
|
1113
|
+
const result = await this.execute(call.name, call.params);
|
|
1114
|
+
results.push(result);
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
results.push({ error: String(error) });
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return results;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Export tools in OpenAI format
|
|
1123
|
+
*/
|
|
1124
|
+
toOpenAIFormat() {
|
|
1125
|
+
return this.getAll().map((tool) => ({
|
|
1126
|
+
type: "function",
|
|
1127
|
+
function: {
|
|
1128
|
+
name: tool.name,
|
|
1129
|
+
description: tool.description,
|
|
1130
|
+
parameters: {
|
|
1131
|
+
type: "object",
|
|
1132
|
+
properties: tool.parameters.properties,
|
|
1133
|
+
required: tool.parameters.required
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}));
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Export tools in Anthropic format
|
|
1140
|
+
*/
|
|
1141
|
+
toAnthropicFormat() {
|
|
1142
|
+
return this.getAll().map((tool) => ({
|
|
1143
|
+
name: tool.name,
|
|
1144
|
+
description: tool.description,
|
|
1145
|
+
input_schema: {
|
|
1146
|
+
type: "object",
|
|
1147
|
+
properties: tool.parameters.properties,
|
|
1148
|
+
required: tool.parameters.required
|
|
1149
|
+
}
|
|
1150
|
+
}));
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Get tool definitions (without execute function)
|
|
1154
|
+
*/
|
|
1155
|
+
getDefinitions() {
|
|
1156
|
+
return this.getAll().map((tool) => ({
|
|
1157
|
+
name: tool.name,
|
|
1158
|
+
description: tool.description,
|
|
1159
|
+
parameters: tool.parameters,
|
|
1160
|
+
category: tool.category,
|
|
1161
|
+
cost: tool.cost,
|
|
1162
|
+
examples: tool.examples,
|
|
1163
|
+
schema: tool.schema
|
|
1164
|
+
}));
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Get usage statistics for all tools
|
|
1168
|
+
*/
|
|
1169
|
+
getUsageStats() {
|
|
1170
|
+
const stats = /* @__PURE__ */ new Map();
|
|
1171
|
+
this.usageStats.forEach((rawStats, name) => {
|
|
1172
|
+
stats.set(name, {
|
|
1173
|
+
calls: rawStats.calls,
|
|
1174
|
+
failures: rawStats.failures,
|
|
1175
|
+
successRate: rawStats.calls > 0 ? (rawStats.calls - rawStats.failures) / rawStats.calls : 0,
|
|
1176
|
+
averageDuration: rawStats.calls > 0 ? rawStats.totalDuration / rawStats.calls : void 0
|
|
1177
|
+
});
|
|
1178
|
+
});
|
|
1179
|
+
return stats;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Get most frequently used tools
|
|
1183
|
+
*/
|
|
1184
|
+
getMostUsed(limit = 5) {
|
|
1185
|
+
const sorted = Array.from(this.usageStats.entries()).sort((a, b) => b[1].calls - a[1].calls).slice(0, limit).map(([name]) => this.tools.get(name)).filter((tool) => tool !== void 0);
|
|
1186
|
+
return sorted;
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Get list of all categories
|
|
1190
|
+
*/
|
|
1191
|
+
getCategories() {
|
|
1192
|
+
const categories = /* @__PURE__ */ new Set();
|
|
1193
|
+
this.getAll().forEach((tool) => {
|
|
1194
|
+
if (tool.category) {
|
|
1195
|
+
categories.add(tool.category);
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
return Array.from(categories).sort();
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Update execution context
|
|
1202
|
+
*/
|
|
1203
|
+
updateContext(context) {
|
|
1204
|
+
this.context = { ...this.context, ...context };
|
|
1205
|
+
this.logger.debug("Updated context", { keys: Object.keys(context) });
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Get current context
|
|
1209
|
+
*/
|
|
1210
|
+
getContext() {
|
|
1211
|
+
return { ...this.context };
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Clear all tools
|
|
1215
|
+
*/
|
|
1216
|
+
clear() {
|
|
1217
|
+
this.logger.debug("Clearing all tools");
|
|
1218
|
+
this.tools.clear();
|
|
1219
|
+
this.usageStats.clear();
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Unregister a specific tool
|
|
1223
|
+
*/
|
|
1224
|
+
unregister(name) {
|
|
1225
|
+
if (this.tools.has(name)) {
|
|
1226
|
+
this.tools.delete(name);
|
|
1227
|
+
this.usageStats.delete(name);
|
|
1228
|
+
this.logger.debug("Unregistered tool", { name });
|
|
1229
|
+
return true;
|
|
1230
|
+
}
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Reset usage statistics
|
|
1235
|
+
*/
|
|
1236
|
+
resetStats() {
|
|
1237
|
+
this.logger.debug("Resetting usage statistics");
|
|
1238
|
+
this.usageStats.forEach((stats) => {
|
|
1239
|
+
stats.calls = 0;
|
|
1240
|
+
stats.failures = 0;
|
|
1241
|
+
stats.totalDuration = 0;
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
const DEFAULT_CONFIG = {
|
|
1246
|
+
enabled: true,
|
|
1247
|
+
validateParams: true,
|
|
1248
|
+
sandboxExecution: false,
|
|
1249
|
+
maxExecutionTime: 3e4,
|
|
1250
|
+
maxConcurrentCalls: 10,
|
|
1251
|
+
deniedTools: []
|
|
1252
|
+
};
|
|
1253
|
+
class ToolGuard {
|
|
1254
|
+
config;
|
|
1255
|
+
logger;
|
|
1256
|
+
events;
|
|
1257
|
+
constructor(config = {}, logger, events = {}) {
|
|
1258
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
1259
|
+
this.logger = wrapLogger(logger || DEFAULT_LOGGER, "ToolGuard");
|
|
1260
|
+
this.events = events;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Validate tool parameters against a Zod schema
|
|
1264
|
+
*/
|
|
1265
|
+
validateParams(toolName, params, schema) {
|
|
1266
|
+
if (!this.config.enabled || !this.config.validateParams) {
|
|
1267
|
+
return { success: true, data: params };
|
|
1268
|
+
}
|
|
1269
|
+
try {
|
|
1270
|
+
const data = schema.parse(params);
|
|
1271
|
+
return { success: true, data };
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
if (error instanceof ZodError) {
|
|
1274
|
+
const issues = error.issues || [];
|
|
1275
|
+
const violations = issues.map(
|
|
1276
|
+
(e) => `${e.path.join(".")}: ${e.message}`
|
|
1277
|
+
);
|
|
1278
|
+
const message = `Schema validation failed: ${violations.join("; ")}`;
|
|
1279
|
+
this.logger.warn(`Tool validation failed for "${toolName}"`, {
|
|
1280
|
+
violations
|
|
1281
|
+
});
|
|
1282
|
+
this.events.onValidationFailed?.(toolName, message);
|
|
1283
|
+
return {
|
|
1284
|
+
success: false,
|
|
1285
|
+
error: "Parameter validation failed",
|
|
1286
|
+
violations
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
throw error;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Safely parse JSON tool arguments
|
|
1294
|
+
*/
|
|
1295
|
+
parseToolArguments(toolName, jsonString) {
|
|
1296
|
+
if (!this.config.enabled) {
|
|
1297
|
+
return { success: true, data: JSON.parse(jsonString) };
|
|
1298
|
+
}
|
|
1299
|
+
try {
|
|
1300
|
+
const parsed = JSON.parse(jsonString);
|
|
1301
|
+
if (this.hasPrototypePollution(parsed)) {
|
|
1302
|
+
this.logger.error(
|
|
1303
|
+
`Prototype pollution attempt detected for tool "${toolName}"`
|
|
1304
|
+
);
|
|
1305
|
+
this.events.onPrototypePollution?.(toolName);
|
|
1306
|
+
return {
|
|
1307
|
+
success: false,
|
|
1308
|
+
error: "Invalid tool arguments: potentially malicious content detected"
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1312
|
+
return {
|
|
1313
|
+
success: false,
|
|
1314
|
+
error: "Tool arguments must be a JSON object"
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
return { success: true, data: parsed };
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
1320
|
+
this.logger.warn(`JSON parsing failed for tool "${toolName}"`, {
|
|
1321
|
+
error: errorMsg
|
|
1322
|
+
});
|
|
1323
|
+
this.events.onValidationFailed?.(
|
|
1324
|
+
toolName,
|
|
1325
|
+
`JSON parsing failed: ${errorMsg}`
|
|
1326
|
+
);
|
|
1327
|
+
return {
|
|
1328
|
+
success: false,
|
|
1329
|
+
error: "Invalid JSON in tool arguments"
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Check if a tool is allowed to execute
|
|
1335
|
+
*/
|
|
1336
|
+
isToolAllowed(toolName) {
|
|
1337
|
+
if (!this.config.enabled) return true;
|
|
1338
|
+
if (this.config.deniedTools.includes(toolName)) {
|
|
1339
|
+
this.logger.warn(`Tool "${toolName}" blocked by deny list`);
|
|
1340
|
+
this.events.onExecutionBlocked?.(toolName, "Tool is in deny list");
|
|
1341
|
+
return false;
|
|
1342
|
+
}
|
|
1343
|
+
if (this.config.allowedTools && !this.config.allowedTools.includes(toolName)) {
|
|
1344
|
+
this.logger.warn(`Tool "${toolName}" not in allow list`);
|
|
1345
|
+
this.events.onExecutionBlocked?.(
|
|
1346
|
+
toolName,
|
|
1347
|
+
"Tool is not in allow list"
|
|
1348
|
+
);
|
|
1349
|
+
return false;
|
|
1350
|
+
}
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Get current configuration
|
|
1355
|
+
*/
|
|
1356
|
+
getConfig() {
|
|
1357
|
+
return { ...this.config };
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Update configuration
|
|
1361
|
+
*/
|
|
1362
|
+
updateConfig(config) {
|
|
1363
|
+
this.config = { ...this.config, ...config };
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Add a tool to the deny list
|
|
1367
|
+
*/
|
|
1368
|
+
denyTool(toolName) {
|
|
1369
|
+
if (!this.config.deniedTools.includes(toolName)) {
|
|
1370
|
+
this.config.deniedTools.push(toolName);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Remove a tool from the deny list
|
|
1375
|
+
*/
|
|
1376
|
+
allowTool(toolName) {
|
|
1377
|
+
const index = this.config.deniedTools.indexOf(toolName);
|
|
1378
|
+
if (index !== -1) {
|
|
1379
|
+
this.config.deniedTools.splice(index, 1);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Check if security is enabled
|
|
1384
|
+
*/
|
|
1385
|
+
isEnabled() {
|
|
1386
|
+
return this.config.enabled;
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Enable or disable security
|
|
1390
|
+
*/
|
|
1391
|
+
setEnabled(enabled) {
|
|
1392
|
+
this.config.enabled = enabled;
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Detect prototype pollution attempts
|
|
1396
|
+
*/
|
|
1397
|
+
hasPrototypePollution(obj, depth = 0) {
|
|
1398
|
+
if (depth > 10) return false;
|
|
1399
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
1400
|
+
const dangerousKeys = ["__proto__", "constructor", "prototype"];
|
|
1401
|
+
for (const key of Object.keys(obj)) {
|
|
1402
|
+
if (dangerousKeys.includes(key)) {
|
|
1403
|
+
return true;
|
|
1404
|
+
}
|
|
1405
|
+
if (typeof obj[key] === "object") {
|
|
1406
|
+
if (this.hasPrototypePollution(
|
|
1407
|
+
obj[key],
|
|
1408
|
+
depth + 1
|
|
1409
|
+
)) {
|
|
1410
|
+
return true;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
return false;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
class ToolSandbox {
|
|
1418
|
+
config;
|
|
1419
|
+
logger;
|
|
1420
|
+
events;
|
|
1421
|
+
activeExecutions = /* @__PURE__ */ new Map();
|
|
1422
|
+
executionCount = 0;
|
|
1423
|
+
constructor(config = {}, logger, events = {}) {
|
|
1424
|
+
this.config = {
|
|
1425
|
+
enabled: true,
|
|
1426
|
+
validateParams: true,
|
|
1427
|
+
sandboxExecution: true,
|
|
1428
|
+
maxExecutionTime: 3e4,
|
|
1429
|
+
maxConcurrentCalls: 10,
|
|
1430
|
+
deniedTools: [],
|
|
1431
|
+
...config
|
|
1432
|
+
};
|
|
1433
|
+
this.logger = wrapLogger(logger || DEFAULT_LOGGER, "ToolSandbox");
|
|
1434
|
+
this.events = events;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Execute a tool with sandbox restrictions
|
|
1438
|
+
*/
|
|
1439
|
+
async execute(tool, params, baseContext, options = {}) {
|
|
1440
|
+
if (!this.config.enabled || !this.config.sandboxExecution) {
|
|
1441
|
+
return tool.execute(params, baseContext);
|
|
1442
|
+
}
|
|
1443
|
+
if (this.activeExecutions.size >= this.config.maxConcurrentCalls) {
|
|
1444
|
+
this.logger.warn("Max concurrent executions reached", {
|
|
1445
|
+
toolName: tool.name,
|
|
1446
|
+
activeCount: this.activeExecutions.size,
|
|
1447
|
+
limit: this.config.maxConcurrentCalls
|
|
1448
|
+
});
|
|
1449
|
+
this.events.onConcurrencyExceeded?.(
|
|
1450
|
+
tool.name,
|
|
1451
|
+
this.activeExecutions.size
|
|
1452
|
+
);
|
|
1453
|
+
throw new Error("Too many concurrent tool executions");
|
|
1454
|
+
}
|
|
1455
|
+
const executionId = `exec-${++this.executionCount}-${Date.now()}`;
|
|
1456
|
+
const controller = new AbortController();
|
|
1457
|
+
this.activeExecutions.set(executionId, controller);
|
|
1458
|
+
const sandboxedContext = {
|
|
1459
|
+
...baseContext,
|
|
1460
|
+
sandbox: {
|
|
1461
|
+
allowedOperations: new Set(options.allowedOperations || []),
|
|
1462
|
+
maxOutputSize: options.maxOutputSize || 1024 * 1024,
|
|
1463
|
+
// 1MB default
|
|
1464
|
+
executionId
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
const timeout = options.maxExecutionTime || this.config.maxExecutionTime;
|
|
1468
|
+
try {
|
|
1469
|
+
await options.onBeforeExecution?.(tool, params);
|
|
1470
|
+
const result = await this.executeWithTimeout(
|
|
1471
|
+
() => tool.execute(params, sandboxedContext),
|
|
1472
|
+
timeout,
|
|
1473
|
+
controller.signal,
|
|
1474
|
+
tool.name
|
|
1475
|
+
);
|
|
1476
|
+
const outputSize = this.estimateSize(result);
|
|
1477
|
+
if (outputSize > sandboxedContext.sandbox.maxOutputSize) {
|
|
1478
|
+
this.logger.warn("Tool output exceeded max size", {
|
|
1479
|
+
toolName: tool.name,
|
|
1480
|
+
outputSize,
|
|
1481
|
+
maxSize: sandboxedContext.sandbox.maxOutputSize
|
|
1482
|
+
});
|
|
1483
|
+
this.events.onOutputSizeExceeded?.(
|
|
1484
|
+
tool.name,
|
|
1485
|
+
outputSize,
|
|
1486
|
+
sandboxedContext.sandbox.maxOutputSize
|
|
1487
|
+
);
|
|
1488
|
+
throw new Error("Tool output exceeded maximum size limit");
|
|
1489
|
+
}
|
|
1490
|
+
await options.onAfterExecution?.(tool, result);
|
|
1491
|
+
return result;
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
await options.onAfterExecution?.(tool, void 0, error);
|
|
1494
|
+
throw error;
|
|
1495
|
+
} finally {
|
|
1496
|
+
this.activeExecutions.delete(executionId);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Cancel all active executions
|
|
1501
|
+
*/
|
|
1502
|
+
cancelAll() {
|
|
1503
|
+
for (const [id, controller] of this.activeExecutions) {
|
|
1504
|
+
controller.abort();
|
|
1505
|
+
this.logger.info("Execution cancelled", { executionId: id });
|
|
1506
|
+
this.events.onCancelled?.(id);
|
|
1507
|
+
}
|
|
1508
|
+
this.activeExecutions.clear();
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Cancel a specific execution by ID
|
|
1512
|
+
*/
|
|
1513
|
+
cancel(executionId) {
|
|
1514
|
+
const controller = this.activeExecutions.get(executionId);
|
|
1515
|
+
if (controller) {
|
|
1516
|
+
controller.abort();
|
|
1517
|
+
this.activeExecutions.delete(executionId);
|
|
1518
|
+
this.logger.info("Execution cancelled", { executionId });
|
|
1519
|
+
this.events.onCancelled?.(executionId);
|
|
1520
|
+
return true;
|
|
1521
|
+
}
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Get active execution count
|
|
1526
|
+
*/
|
|
1527
|
+
getActiveCount() {
|
|
1528
|
+
return this.activeExecutions.size;
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* Get list of active execution IDs
|
|
1532
|
+
*/
|
|
1533
|
+
getActiveExecutionIds() {
|
|
1534
|
+
return Array.from(this.activeExecutions.keys());
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Check if sandbox is enabled
|
|
1538
|
+
*/
|
|
1539
|
+
isEnabled() {
|
|
1540
|
+
return this.config.enabled && this.config.sandboxExecution;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Get current configuration
|
|
1544
|
+
*/
|
|
1545
|
+
getConfig() {
|
|
1546
|
+
return { ...this.config };
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Update configuration
|
|
1550
|
+
*/
|
|
1551
|
+
updateConfig(config) {
|
|
1552
|
+
this.config = { ...this.config, ...config };
|
|
1553
|
+
}
|
|
1554
|
+
async executeWithTimeout(fn, timeoutMs, signal, toolName) {
|
|
1555
|
+
return new Promise((resolve, reject) => {
|
|
1556
|
+
const timeoutId = setTimeout(() => {
|
|
1557
|
+
this.logger.warn("Tool execution timed out", {
|
|
1558
|
+
toolName,
|
|
1559
|
+
timeoutMs
|
|
1560
|
+
});
|
|
1561
|
+
this.events.onTimeout?.(toolName, timeoutMs);
|
|
1562
|
+
reject(
|
|
1563
|
+
new Error(`Tool execution timed out after ${timeoutMs}ms`)
|
|
1564
|
+
);
|
|
1565
|
+
}, timeoutMs);
|
|
1566
|
+
const abortHandler = () => {
|
|
1567
|
+
clearTimeout(timeoutId);
|
|
1568
|
+
reject(new Error("Tool execution was cancelled"));
|
|
1569
|
+
};
|
|
1570
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1571
|
+
fn().then((result) => {
|
|
1572
|
+
clearTimeout(timeoutId);
|
|
1573
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1574
|
+
resolve(result);
|
|
1575
|
+
}).catch((error) => {
|
|
1576
|
+
clearTimeout(timeoutId);
|
|
1577
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1578
|
+
reject(error);
|
|
1579
|
+
});
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
estimateSize(value) {
|
|
1583
|
+
if (value === null || value === void 0) return 0;
|
|
1584
|
+
if (typeof value === "string") return value.length * 2;
|
|
1585
|
+
if (typeof value === "number") return 8;
|
|
1586
|
+
if (typeof value === "boolean") return 4;
|
|
1587
|
+
if (Buffer.isBuffer(value)) return value.length;
|
|
1588
|
+
try {
|
|
1589
|
+
return JSON.stringify(value).length * 2;
|
|
1590
|
+
} catch {
|
|
1591
|
+
return 0;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
function createSecureTool(tool, sandbox, guard) {
|
|
1596
|
+
return {
|
|
1597
|
+
...tool,
|
|
1598
|
+
execute: async (params, context) => {
|
|
1599
|
+
if (!guard.isToolAllowed(tool.name)) {
|
|
1600
|
+
throw new Error(`Tool "${tool.name}" is not allowed`);
|
|
1601
|
+
}
|
|
1602
|
+
if (tool.schema) {
|
|
1603
|
+
const validation = guard.validateParams(
|
|
1604
|
+
tool.name,
|
|
1605
|
+
params,
|
|
1606
|
+
tool.schema
|
|
1607
|
+
);
|
|
1608
|
+
if (!validation.success) {
|
|
1609
|
+
throw new Error(`Validation failed: ${validation.error}`);
|
|
1610
|
+
}
|
|
1611
|
+
params = validation.data;
|
|
1612
|
+
}
|
|
1613
|
+
return sandbox.execute(tool, params, context || {});
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
const crypto = {};
|
|
1618
|
+
class ContextManager {
|
|
1619
|
+
items;
|
|
1620
|
+
hashes;
|
|
1621
|
+
logger;
|
|
1622
|
+
constructor(logger) {
|
|
1623
|
+
this.items = /* @__PURE__ */ new Map();
|
|
1624
|
+
this.hashes = /* @__PURE__ */ new Set();
|
|
1625
|
+
this.logger = wrapLogger(logger || DEFAULT_LOGGER, "ContextManager");
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Track a context item
|
|
1629
|
+
*/
|
|
1630
|
+
track(item, position) {
|
|
1631
|
+
const hash2 = this.hashContent(item.content);
|
|
1632
|
+
if (!item.id && this.hashes.has(hash2)) {
|
|
1633
|
+
this.logger.debug("Skipping duplicate context item by hash", { hash: hash2 });
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
const id = item.id || this.generateId();
|
|
1637
|
+
const trackedItem = {
|
|
1638
|
+
...item,
|
|
1639
|
+
id,
|
|
1640
|
+
hash: hash2,
|
|
1641
|
+
position,
|
|
1642
|
+
injectedAt: /* @__PURE__ */ new Date(),
|
|
1643
|
+
timestamp: item.timestamp || /* @__PURE__ */ new Date(),
|
|
1644
|
+
priority: item.priority || "medium"
|
|
1645
|
+
};
|
|
1646
|
+
this.items.set(id, trackedItem);
|
|
1647
|
+
this.hashes.add(hash2);
|
|
1648
|
+
this.logger.debug("Tracked context item", {
|
|
1649
|
+
id,
|
|
1650
|
+
category: item.category,
|
|
1651
|
+
position
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
/**
|
|
1655
|
+
* Check if context with given ID exists
|
|
1656
|
+
*/
|
|
1657
|
+
hasContext(id) {
|
|
1658
|
+
return this.items.has(id);
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Check if content with given hash exists
|
|
1662
|
+
*/
|
|
1663
|
+
hasContentHash(content) {
|
|
1664
|
+
const hash2 = this.hashContent(content);
|
|
1665
|
+
return this.hashes.has(hash2);
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Check if similar content exists (fuzzy match)
|
|
1669
|
+
*/
|
|
1670
|
+
hasSimilarContent(content, similarityThreshold = 0.9) {
|
|
1671
|
+
const MAX_ITEMS_WARNING = 1e3;
|
|
1672
|
+
if (this.items.size > MAX_ITEMS_WARNING) {
|
|
1673
|
+
this.logger.warn(
|
|
1674
|
+
"Large number of context items, similarity check may be slow",
|
|
1675
|
+
{
|
|
1676
|
+
count: this.items.size,
|
|
1677
|
+
threshold: MAX_ITEMS_WARNING
|
|
1678
|
+
}
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
const normalized = this.normalizeContent(content);
|
|
1682
|
+
for (const item of this.items.values()) {
|
|
1683
|
+
const itemNormalized = this.normalizeContent(item.content || "");
|
|
1684
|
+
if (normalized === itemNormalized) {
|
|
1685
|
+
return true;
|
|
1686
|
+
}
|
|
1687
|
+
const longer = normalized.length > itemNormalized.length ? normalized : itemNormalized;
|
|
1688
|
+
const shorter = normalized.length <= itemNormalized.length ? normalized : itemNormalized;
|
|
1689
|
+
const lengthRatio = shorter.length / longer.length;
|
|
1690
|
+
if (lengthRatio >= similarityThreshold) {
|
|
1691
|
+
if (longer.includes(shorter)) {
|
|
1692
|
+
return true;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
return false;
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* Get context item by ID
|
|
1700
|
+
*/
|
|
1701
|
+
get(id) {
|
|
1702
|
+
return this.items.get(id);
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Get all tracked context items
|
|
1706
|
+
*/
|
|
1707
|
+
getAll() {
|
|
1708
|
+
return Array.from(this.items.values());
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Get context items by category
|
|
1712
|
+
*/
|
|
1713
|
+
getByCategory(category) {
|
|
1714
|
+
return this.getAll().filter((item) => item.category === category);
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Get context items by priority
|
|
1718
|
+
*/
|
|
1719
|
+
getByPriority(priority) {
|
|
1720
|
+
return this.getAll().filter((item) => item.priority === priority);
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Get context items by source
|
|
1724
|
+
*/
|
|
1725
|
+
getBySource(source) {
|
|
1726
|
+
return this.getAll().filter((item) => item.source === source);
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Get all categories
|
|
1730
|
+
*/
|
|
1731
|
+
getCategories() {
|
|
1732
|
+
const categories = /* @__PURE__ */ new Set();
|
|
1733
|
+
this.items.forEach((item) => {
|
|
1734
|
+
if (item.category) {
|
|
1735
|
+
categories.add(item.category);
|
|
1736
|
+
}
|
|
1737
|
+
});
|
|
1738
|
+
return Array.from(categories).sort();
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Get context statistics
|
|
1742
|
+
*/
|
|
1743
|
+
getStats() {
|
|
1744
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
1745
|
+
const byPriority = /* @__PURE__ */ new Map();
|
|
1746
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
1747
|
+
let oldestTimestamp;
|
|
1748
|
+
let newestTimestamp;
|
|
1749
|
+
this.items.forEach((item) => {
|
|
1750
|
+
if (item.category) {
|
|
1751
|
+
byCategory.set(
|
|
1752
|
+
item.category,
|
|
1753
|
+
(byCategory.get(item.category) || 0) + 1
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
const priority = item.priority || "medium";
|
|
1757
|
+
byPriority.set(priority, (byPriority.get(priority) || 0) + 1);
|
|
1758
|
+
if (item.source) {
|
|
1759
|
+
bySource.set(item.source, (bySource.get(item.source) || 0) + 1);
|
|
1760
|
+
}
|
|
1761
|
+
if (item.timestamp) {
|
|
1762
|
+
if (!oldestTimestamp || item.timestamp < oldestTimestamp) {
|
|
1763
|
+
oldestTimestamp = item.timestamp;
|
|
1764
|
+
}
|
|
1765
|
+
if (!newestTimestamp || item.timestamp > newestTimestamp) {
|
|
1766
|
+
newestTimestamp = item.timestamp;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
});
|
|
1770
|
+
return {
|
|
1771
|
+
totalItems: this.items.size,
|
|
1772
|
+
byCategory,
|
|
1773
|
+
byPriority,
|
|
1774
|
+
bySource,
|
|
1775
|
+
oldestTimestamp,
|
|
1776
|
+
newestTimestamp
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Remove context item by ID
|
|
1781
|
+
*/
|
|
1782
|
+
remove(id) {
|
|
1783
|
+
const item = this.items.get(id);
|
|
1784
|
+
if (item) {
|
|
1785
|
+
this.items.delete(id);
|
|
1786
|
+
this.hashes.delete(item.hash);
|
|
1787
|
+
this.logger.debug("Removed context item", { id });
|
|
1788
|
+
return true;
|
|
1789
|
+
}
|
|
1790
|
+
return false;
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Clear all tracked context
|
|
1794
|
+
*/
|
|
1795
|
+
clear() {
|
|
1796
|
+
this.items.clear();
|
|
1797
|
+
this.hashes.clear();
|
|
1798
|
+
this.logger.debug("Cleared all context");
|
|
1799
|
+
}
|
|
1800
|
+
generateId() {
|
|
1801
|
+
return `ctx-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
1802
|
+
}
|
|
1803
|
+
hashContent(content) {
|
|
1804
|
+
return crypto.createHash("sha256").update(content).digest("hex").substring(0, 32);
|
|
1805
|
+
}
|
|
1806
|
+
normalizeContent(content) {
|
|
1807
|
+
return content.replace(/\s+/g, " ").trim().toLowerCase();
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
const VERSION = "0.0.1";
|
|
1811
|
+
export {
|
|
1812
|
+
ContextManager,
|
|
1813
|
+
DEFAULT_LOGGER,
|
|
1814
|
+
LIBRARY_NAME,
|
|
1815
|
+
ToolGuard,
|
|
1816
|
+
ToolRegistry,
|
|
1817
|
+
ToolSandbox,
|
|
1818
|
+
VERSION,
|
|
1819
|
+
createSecureTool,
|
|
1820
|
+
wrapLogger
|
|
1821
|
+
};
|
|
1822
|
+
//# sourceMappingURL=index.js.map
|