lemura 1.0.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE +21 -0
  3. package/README.md +143 -0
  4. package/dist/adapters/index.d.mts +45 -0
  5. package/dist/adapters/index.d.ts +45 -0
  6. package/dist/adapters/index.js +371 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/adapters/index.mjs +369 -0
  9. package/dist/adapters/index.mjs.map +1 -0
  10. package/dist/adapters-BSkhv5ac.d.ts +208 -0
  11. package/dist/adapters-BnG0LEYD.d.mts +208 -0
  12. package/dist/context/index.d.mts +143 -0
  13. package/dist/context/index.d.ts +143 -0
  14. package/dist/context/index.js +321 -0
  15. package/dist/context/index.js.map +1 -0
  16. package/dist/context/index.mjs +314 -0
  17. package/dist/context/index.mjs.map +1 -0
  18. package/dist/index.d.mts +91 -0
  19. package/dist/index.d.ts +91 -0
  20. package/dist/index.js +1375 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/index.mjs +1348 -0
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/logger/index.d.mts +19 -0
  25. package/dist/logger/index.d.ts +19 -0
  26. package/dist/logger/index.js +67 -0
  27. package/dist/logger/index.js.map +1 -0
  28. package/dist/logger/index.mjs +65 -0
  29. package/dist/logger/index.mjs.map +1 -0
  30. package/dist/logger-DxvKliuk.d.mts +37 -0
  31. package/dist/logger-DxvKliuk.d.ts +37 -0
  32. package/dist/rag/index.d.mts +10 -0
  33. package/dist/rag/index.d.ts +10 -0
  34. package/dist/rag/index.js +43 -0
  35. package/dist/rag/index.js.map +1 -0
  36. package/dist/rag/index.mjs +41 -0
  37. package/dist/rag/index.mjs.map +1 -0
  38. package/dist/rag-La_Bo-J8.d.mts +45 -0
  39. package/dist/rag-La_Bo-J8.d.ts +45 -0
  40. package/dist/skills/index.d.mts +15 -0
  41. package/dist/skills/index.d.ts +15 -0
  42. package/dist/skills/index.js +40 -0
  43. package/dist/skills/index.js.map +1 -0
  44. package/dist/skills/index.mjs +38 -0
  45. package/dist/skills/index.mjs.map +1 -0
  46. package/dist/skills-wc8S-OvC.d.mts +14 -0
  47. package/dist/skills-wc8S-OvC.d.ts +14 -0
  48. package/dist/storage-BGu4Loao.d.ts +121 -0
  49. package/dist/storage-DMcliVVj.d.mts +121 -0
  50. package/dist/tools/index.d.mts +17 -0
  51. package/dist/tools/index.d.ts +17 -0
  52. package/dist/tools/index.js +72 -0
  53. package/dist/tools/index.js.map +1 -0
  54. package/dist/tools/index.mjs +70 -0
  55. package/dist/tools/index.mjs.map +1 -0
  56. package/dist/types/index.d.mts +118 -0
  57. package/dist/types/index.d.ts +118 -0
  58. package/dist/types/index.js +84 -0
  59. package/dist/types/index.js.map +1 -0
  60. package/dist/types/index.mjs +74 -0
  61. package/dist/types/index.mjs.map +1 -0
  62. package/package.json +79 -0
package/dist/index.js ADDED
@@ -0,0 +1,1375 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+
5
+ // src/types/errors.ts
6
+ var LemuraError = class extends Error {
7
+ /**
8
+ * @param message - The error message
9
+ * @param code - The error code for programmatic handling
10
+ * @param problem - A clear description of the problem for the end user
11
+ * @param hints - A list of suggestions to resolve the issue
12
+ */
13
+ constructor(message, code, problem, hints = []) {
14
+ super(message);
15
+ this.code = code;
16
+ this.problem = problem;
17
+ this.hints = hints;
18
+ this.name = "LemuraError";
19
+ Object.setPrototypeOf(this, new.target.prototype);
20
+ }
21
+ };
22
+ var LemuraContextOverflowError = class extends LemuraError {
23
+ constructor(message) {
24
+ super(message, "CONTEXT_OVERFLOW");
25
+ this.name = "LemuraContextOverflowError";
26
+ }
27
+ };
28
+ var LemuraToolNotFoundError = class extends LemuraError {
29
+ constructor(message) {
30
+ super(message, "TOOL_NOT_FOUND");
31
+ this.name = "LemuraToolNotFoundError";
32
+ }
33
+ };
34
+ var LemuraAdapterError = class extends LemuraError {
35
+ constructor(message, code = "ADAPTER_ERROR", cause, problem, hints = []) {
36
+ super(message, code, problem, hints);
37
+ this.cause = cause;
38
+ this.name = "LemuraAdapterError";
39
+ }
40
+ };
41
+ var LemuraSkillInjectionError = class extends LemuraError {
42
+ constructor(message) {
43
+ super(message, "SKILL_INJECTION_FAILED");
44
+ this.name = "LemuraSkillInjectionError";
45
+ }
46
+ };
47
+ var LemuraMaxIterationsError = class extends LemuraError {
48
+ constructor(message) {
49
+ super(message, "MAX_ITERATIONS_EXCEEDED");
50
+ this.name = "LemuraMaxIterationsError";
51
+ }
52
+ };
53
+ var LemuraToolValidationError = class extends LemuraError {
54
+ constructor(message) {
55
+ super(message, "TOOL_VALIDATION_FAILED");
56
+ this.name = "LemuraToolValidationError";
57
+ }
58
+ };
59
+ var LemuraToolTimeoutError = class extends LemuraError {
60
+ constructor(message) {
61
+ super(message, "TOOL_TIMEOUT");
62
+ this.name = "LemuraToolTimeoutError";
63
+ }
64
+ };
65
+
66
+ // src/types/logger.ts
67
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
68
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
69
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
70
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
71
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
72
+ LogLevel2[LogLevel2["FATAL"] = 4] = "FATAL";
73
+ return LogLevel2;
74
+ })(LogLevel || {});
75
+
76
+ // src/adapters/OpenAICompatibleAdapter.ts
77
+ var OpenAICompatibleAdapter = class {
78
+ name = "openai_compatible";
79
+ version = "1.0.0";
80
+ baseUrl;
81
+ apiKey;
82
+ defaultModel;
83
+ defaultHeaders;
84
+ timeoutMs;
85
+ retryConfig;
86
+ constructor(config = {}) {
87
+ this.baseUrl = (config.baseUrl ?? process.env.LEMURA_BASE_URL ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(/\/$/, "");
88
+ this.apiKey = config.apiKey ?? process.env.LEMURA_API_KEY ?? process.env.OPENAI_API_KEY ?? "";
89
+ this.defaultModel = config.defaultModel ?? process.env.LEMURA_MODEL ?? process.env.OPENAI_MODEL ?? "gpt-3.5-turbo";
90
+ this.defaultHeaders = config.defaultHeaders || {};
91
+ this.timeoutMs = config.timeout || 3e4;
92
+ this.retryConfig = config.retry || { maxRetries: 2, baseDelayMs: 1e3 };
93
+ }
94
+ async fetchWithRetry(url, init) {
95
+ let attempts = 0;
96
+ while (attempts <= this.retryConfig.maxRetries) {
97
+ try {
98
+ const controller = new AbortController();
99
+ const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
100
+ const headers = {
101
+ "Authorization": `Bearer ${this.apiKey}`,
102
+ ...this.defaultHeaders
103
+ };
104
+ if (init.headers) {
105
+ Object.assign(headers, init.headers);
106
+ }
107
+ if (headers["Content-Type"] === "unset") {
108
+ delete headers["Content-Type"];
109
+ } else if (!headers["Content-Type"]) {
110
+ headers["Content-Type"] = "application/json";
111
+ }
112
+ const response = await fetch(url, {
113
+ ...init,
114
+ signal: controller.signal,
115
+ headers
116
+ });
117
+ clearTimeout(timeoutId);
118
+ if (response.ok) return response;
119
+ if ((response.status === 429 || response.status === 503) && attempts < this.retryConfig.maxRetries) {
120
+ attempts++;
121
+ const delay = this.retryConfig.baseDelayMs * Math.pow(2, attempts - 1);
122
+ await new Promise((resolve) => setTimeout(resolve, delay));
123
+ continue;
124
+ }
125
+ const errorText = await response.text().catch(() => "");
126
+ let problem = "The server replied with an error during the API call.";
127
+ let hints = ["Check the API documentation for the provider you are using."];
128
+ if (response.status === 401) {
129
+ problem = "Authentication failed. The API key is invalid or missing.";
130
+ hints = [
131
+ "Ensure your API key is correctly configured in the adapter or environment variables.",
132
+ "Check if the API key has expired or been revoked."
133
+ ];
134
+ } else if (response.status === 404) {
135
+ problem = "The requested resource or model was not found.";
136
+ hints = [
137
+ "Verify that the baseUrl is correct (e.g., https://api.openai.com/v1).",
138
+ "Check if the model name is correct and available for your account.",
139
+ "Ensure you are not appending extra paths to the baseUrl."
140
+ ];
141
+ } else if (response.status === 429) {
142
+ problem = "Rate limit exceeded.";
143
+ hints = [
144
+ "Wait a few seconds before retrying.",
145
+ "Check your usage limits and billing status on the provider dashboard."
146
+ ];
147
+ }
148
+ throw new LemuraAdapterError(
149
+ `HTTP ${response.status}: ${errorText}`,
150
+ "HTTP_ERROR",
151
+ { status: response.status, body: errorText },
152
+ problem,
153
+ hints
154
+ );
155
+ } catch (err) {
156
+ if (err instanceof LemuraAdapterError) throw err;
157
+ if (attempts < this.retryConfig.maxRetries) {
158
+ attempts++;
159
+ const delay = this.retryConfig.baseDelayMs * Math.pow(2, attempts - 1);
160
+ await new Promise((resolve) => setTimeout(resolve, delay));
161
+ continue;
162
+ }
163
+ throw new LemuraAdapterError(
164
+ `Network request failed: ${err instanceof Error ? err.message : String(err)}`,
165
+ "NETWORK_ERROR",
166
+ err,
167
+ "A network error occurred while connecting to the provider.",
168
+ [
169
+ "Check your internet connection.",
170
+ "Verify that the baseUrl is reachable from your network.",
171
+ "Check for proxy or firewall settings that might block the request."
172
+ ]
173
+ );
174
+ }
175
+ }
176
+ throw new LemuraAdapterError(
177
+ "Max retries exceeded",
178
+ "MAX_RETRIES",
179
+ void 0,
180
+ "The request failed after multiple retry attempts.",
181
+ ["Check if the provider service is down or experiencing high load."]
182
+ );
183
+ }
184
+ mapFinishReason(reason) {
185
+ if (!reason) return "stop";
186
+ const r = reason.toLowerCase();
187
+ if (r === "tool_calls" || r === "tool_call") return "tool_call";
188
+ if (r === "length" || r === "max_tokens") return "max_tokens";
189
+ if (r === "content_filter" || r === "error") return "error";
190
+ return "stop";
191
+ }
192
+ buildPayload(request) {
193
+ const payload = {
194
+ model: request.model || this.defaultModel,
195
+ messages: request.messages
196
+ };
197
+ if (request.maxTokens !== void 0) payload.max_tokens = request.maxTokens;
198
+ if (request.temperature !== void 0) payload.temperature = request.temperature;
199
+ if (request.stopSequences?.length) payload.stop = request.stopSequences;
200
+ if (request.stream) payload.stream = true;
201
+ if (request.tools && request.tools.length > 0) {
202
+ payload.tools = request.tools.map((t) => ({
203
+ type: "function",
204
+ function: {
205
+ name: t.name,
206
+ description: t.description,
207
+ parameters: t.parameters
208
+ }
209
+ }));
210
+ }
211
+ return payload;
212
+ }
213
+ async complete(request) {
214
+ const payload = this.buildPayload(request);
215
+ const response = await this.fetchWithRetry(`${this.baseUrl}/chat/completions`, {
216
+ method: "POST",
217
+ body: JSON.stringify(payload)
218
+ });
219
+ const data = await response.json();
220
+ const choice = data.choices?.[0];
221
+ if (!choice) {
222
+ throw new LemuraAdapterError("Invalid response format: missing choices", "INVALID_RESPONSE", data);
223
+ }
224
+ const message = choice.message;
225
+ let toolCalls;
226
+ if (message.tool_calls && message.tool_calls.length > 0) {
227
+ toolCalls = message.tool_calls.map((tc) => ({
228
+ id: tc.id,
229
+ name: tc.function.name,
230
+ arguments: tc.function.arguments
231
+ }));
232
+ }
233
+ return {
234
+ content: message.content || "",
235
+ toolCalls,
236
+ finishReason: this.mapFinishReason(choice.finish_reason),
237
+ usage: {
238
+ promptTokens: data.usage?.prompt_tokens || 0,
239
+ completionTokens: data.usage?.completion_tokens || 0,
240
+ totalTokens: data.usage?.total_tokens || 0
241
+ },
242
+ rawResponse: data
243
+ };
244
+ }
245
+ async *stream(request) {
246
+ const payload = this.buildPayload({ ...request, stream: true });
247
+ const response = await this.fetchWithRetry(`${this.baseUrl}/chat/completions`, {
248
+ method: "POST",
249
+ body: JSON.stringify(payload)
250
+ });
251
+ if (!response.body) {
252
+ throw new LemuraAdapterError("Response body is null", "STREAM_ERROR");
253
+ }
254
+ const reader = response.body.getReader();
255
+ const decoder = new TextDecoder("utf-8");
256
+ let buffer = "";
257
+ try {
258
+ while (true) {
259
+ const { value, done } = await reader.read();
260
+ if (done) break;
261
+ buffer += decoder.decode(value, { stream: true });
262
+ const lines = buffer.split("\n");
263
+ buffer = lines.pop() || "";
264
+ for (const line of lines) {
265
+ const trimmed = line.trim();
266
+ if (!trimmed || trimmed === "data: [DONE]") continue;
267
+ if (trimmed.startsWith("data: ")) {
268
+ const jsonStr = trimmed.slice(6);
269
+ let data;
270
+ try {
271
+ data = JSON.parse(jsonStr);
272
+ } catch (err) {
273
+ continue;
274
+ }
275
+ const choice = data.choices?.[0];
276
+ if (!choice) continue;
277
+ const delta = choice.delta?.content || "";
278
+ const toolCallBlock = choice.delta?.tool_calls?.[0];
279
+ let toolCallDelta;
280
+ if (toolCallBlock) {
281
+ toolCallDelta = {
282
+ id: toolCallBlock.id,
283
+ name: toolCallBlock.function?.name,
284
+ arguments: toolCallBlock.function?.arguments
285
+ };
286
+ }
287
+ const isFinished = choice.finish_reason !== null && choice.finish_reason !== void 0;
288
+ yield {
289
+ delta,
290
+ finished: isFinished,
291
+ ...toolCallDelta && { toolCallDelta },
292
+ ...isFinished && { finishReason: this.mapFinishReason(choice.finish_reason) }
293
+ };
294
+ }
295
+ }
296
+ }
297
+ } finally {
298
+ reader.releaseLock();
299
+ }
300
+ }
301
+ estimateTokens(text) {
302
+ return Math.ceil(text.length / 4);
303
+ }
304
+ getModelInfo() {
305
+ return {
306
+ supportsVision: true,
307
+ supportsTools: true,
308
+ contextWindow: 128e3
309
+ };
310
+ }
311
+ async healthCheck() {
312
+ try {
313
+ const resp = await this.fetchWithRetry(`${this.baseUrl}/models`, { method: "GET" });
314
+ return resp.ok;
315
+ } catch {
316
+ return false;
317
+ }
318
+ }
319
+ async transcribe(request) {
320
+ const binaryString = atob(request.audioBase64);
321
+ const bytes = new Uint8Array(binaryString.length);
322
+ for (let i = 0; i < binaryString.length; i++) {
323
+ bytes[i] = binaryString.charCodeAt(i);
324
+ }
325
+ const blob = new Blob([bytes], { type: request.mimeType });
326
+ const formData = new FormData();
327
+ formData.append("file", blob, "audio.webm");
328
+ formData.append("model", "whisper-1");
329
+ if (request.language) formData.append("language", request.language);
330
+ const response = await this.fetchWithRetry(`${this.baseUrl}/audio/transcriptions`, {
331
+ method: "POST",
332
+ body: formData,
333
+ headers: {
334
+ "Content-Type": "unset"
335
+ }
336
+ });
337
+ const data = await response.json();
338
+ return {
339
+ transcript: data.text,
340
+ confidence: 1,
341
+ // OpenAI doesn't return confidence in standard response
342
+ language: data.language || request.language || "en"
343
+ };
344
+ }
345
+ async *synthesize(request) {
346
+ const response = await this.fetchWithRetry(`${this.baseUrl}/audio/speech`, {
347
+ method: "POST",
348
+ body: JSON.stringify({
349
+ model: "tts-1",
350
+ input: request.text,
351
+ voice: request.voiceId || "alloy",
352
+ response_format: request.format || "mp3"
353
+ })
354
+ });
355
+ if (!response.body) throw new LemuraAdapterError("No response body for TTS", "STREAM_ERROR");
356
+ const reader = response.body.getReader();
357
+ try {
358
+ while (true) {
359
+ const { done, value } = await reader.read();
360
+ if (done) break;
361
+ if (value) {
362
+ const binary = new TextDecoder("latin1").decode(value);
363
+ yield { audioBase64: btoa(binary) };
364
+ }
365
+ }
366
+ } finally {
367
+ reader.releaseLock();
368
+ }
369
+ }
370
+ async describeImage(request) {
371
+ const payload = {
372
+ model: this.defaultModel,
373
+ messages: [
374
+ {
375
+ role: "user",
376
+ content: [
377
+ { type: "text", text: request.prompt || "Describe this image" },
378
+ {
379
+ type: "image_url",
380
+ image_url: {
381
+ url: `data:image/jpeg;base64,${request.imageBase64}`
382
+ }
383
+ }
384
+ ]
385
+ }
386
+ ]
387
+ };
388
+ const response = await this.fetchWithRetry(`${this.baseUrl}/chat/completions`, {
389
+ method: "POST",
390
+ body: JSON.stringify(payload)
391
+ });
392
+ const data = await response.json();
393
+ return {
394
+ description: data.choices[0].message.content,
395
+ objects: []
396
+ // OpenAI doesn't return structured objects in standard vision call
397
+ };
398
+ }
399
+ async generateImage(request) {
400
+ const response = await this.fetchWithRetry(`${this.baseUrl}/images/generations`, {
401
+ method: "POST",
402
+ body: JSON.stringify({
403
+ prompt: request.prompt,
404
+ model: "dall-e-3",
405
+ n: 1,
406
+ size: request.dimensions || "1024x1024"
407
+ })
408
+ });
409
+ const data = await response.json();
410
+ return {
411
+ imageUrl: data.data[0].url,
412
+ revisedPrompt: data.data[0].revised_prompt
413
+ };
414
+ }
415
+ };
416
+
417
+ // src/context/ContextManager.ts
418
+ var ContextManager = class {
419
+ strategies = [];
420
+ /**
421
+ * Registers a new compression or pre-turn strategy and sorts the stack by priority.
422
+ *
423
+ * @param strategy - The strategy implementation to register
424
+ */
425
+ registerStrategy(strategy) {
426
+ this.strategies.push(strategy);
427
+ this.strategies.sort((a, b) => a.priority - b.priority);
428
+ }
429
+ /**
430
+ * Applies all registered strategies that return true for `shouldApply()`
431
+ * until the context token count is safely below the maximum budget.
432
+ *
433
+ * @param context - The context window to prepare
434
+ * @param safetyMargin - Modifier applied to maxTokens (default: 0.95 -> 95%)
435
+ * @returns A new ContextWindow object potentially compressed
436
+ * @throws {LemuraContextOverflowError} If the context is still over maxTokens after all strategies
437
+ */
438
+ async prepare(context, safetyMargin = 0.95) {
439
+ let currentCtx = { ...context, turns: [...context.turns] };
440
+ currentCtx.maxTokens * safetyMargin;
441
+ for (const strategy of this.strategies) {
442
+ if (strategy.shouldApply(currentCtx)) {
443
+ currentCtx = await strategy.apply(currentCtx);
444
+ }
445
+ }
446
+ if (currentCtx.tokenCount > currentCtx.maxTokens) {
447
+ throw new LemuraContextOverflowError(
448
+ `Context overflowed: ${currentCtx.tokenCount} tokens > ${currentCtx.maxTokens}`
449
+ );
450
+ }
451
+ return currentCtx;
452
+ }
453
+ };
454
+
455
+ // src/context/SandwichCompressionStrategy.ts
456
+ var SandwichCompressionStrategy = class {
457
+ constructor(adapter, config) {
458
+ this.adapter = adapter;
459
+ this.config = config;
460
+ }
461
+ name = "sandwich_compression";
462
+ priority = 20;
463
+ shouldApply(ctx) {
464
+ return ctx.tokenCount >= ctx.maxTokens * this.config.triggerThreshold && ctx.turns.length > this.config.preserveFirst + this.config.preserveLast;
465
+ }
466
+ async apply(ctx) {
467
+ const { preserveFirst, preserveLast } = this.config;
468
+ const head = ctx.turns.slice(0, preserveFirst);
469
+ const tail = ctx.turns.slice(ctx.turns.length - preserveLast);
470
+ const middle = ctx.turns.slice(preserveFirst, ctx.turns.length - preserveLast);
471
+ const middleText = middle.map((t) => `${t.role}: ${JSON.stringify(t.content)}`).join("\n");
472
+ const summaryResponse = await this.adapter.complete({
473
+ model: "",
474
+ messages: [{
475
+ role: "user",
476
+ content: `Summarize the following conversation history briefly:
477
+ ${middleText}`
478
+ }]
479
+ });
480
+ const summaryStr = summaryResponse.content;
481
+ const newCompressionSummary = ctx.compressionSummary ? `${ctx.compressionSummary}
482
+ ${summaryStr}` : summaryStr;
483
+ const summaryTurn = {
484
+ role: "system",
485
+ content: `[COMPRESSED HISTORY SUMMARY]
486
+ ${newCompressionSummary}`,
487
+ tokenCount: this.adapter.estimateTokens(newCompressionSummary),
488
+ turnIndex: -1,
489
+ compressed: true
490
+ };
491
+ const newTurns = [...head, summaryTurn, ...tail];
492
+ const newTokenCount = newTurns.reduce((sum, t) => sum + t.tokenCount, 0) + this.adapter.estimateTokens(ctx.systemPrompt) + this.adapter.estimateTokens(ctx.scratchpad);
493
+ return {
494
+ ...ctx,
495
+ turns: newTurns,
496
+ tokenCount: newTokenCount,
497
+ compressionSummary: newCompressionSummary
498
+ };
499
+ }
500
+ /**
501
+ * Applies sandwich compression specifically to a Short Term Memory item's content.
502
+ * Implements a 3-layer pipeline: Pre-Layer (encoding), Core Layer (dense summary), Post-Layer (refinement cues).
503
+ *
504
+ * @param content - The heavy text content to compress
505
+ * @param instructions - Guiding instructions for the core layer summary
506
+ * @returns The three-layer sandwich result
507
+ */
508
+ async compressMemoryItem(content, instructions = "Extract the key information") {
509
+ const estimatedChunks = Math.max(1, Math.ceil(this.adapter.estimateTokens(content) / 2e3));
510
+ const preLayer = `[PRE-LAYER ENCODED: ${estimatedChunks} internal chunks]`;
511
+ const summaryResponse = await this.adapter.complete({
512
+ model: "",
513
+ messages: [{
514
+ role: "user",
515
+ content: `### INSTRUCTIONS ###
516
+ ${instructions}
517
+
518
+ ### CONTENT ###
519
+ ${content}
520
+
521
+ ### INSTRUCTIONS ###
522
+ ${instructions}`
523
+ }]
524
+ });
525
+ const coreLayer = summaryResponse.content;
526
+ const postLayer = `[POST-LAYER DECODING: Use \`refine_layer\` or \`read_chunk\` tools to expand specific sections]`;
527
+ return { preLayer, coreLayer, postLayer };
528
+ }
529
+ };
530
+
531
+ // src/context/HistoryCompressionStrategy.ts
532
+ var ScratchpadStrategy = class {
533
+ name = "scratchpad_strategy";
534
+ priority = 10;
535
+ shouldApply(ctx) {
536
+ return ctx.scratchpad.length > 0;
537
+ }
538
+ async apply(ctx) {
539
+ return ctx;
540
+ }
541
+ };
542
+ var HistoryCompressionStrategy = class {
543
+ constructor(adapter, config) {
544
+ this.adapter = adapter;
545
+ this.config = config;
546
+ }
547
+ name = "history_compression";
548
+ priority = 30;
549
+ shouldApply(ctx) {
550
+ const triggerTokens = ctx.maxTokens * this.config.triggerAtPercent;
551
+ const uncompressedTurns = ctx.turns.filter((t) => t.role !== "system" && !t.compressed);
552
+ return ctx.tokenCount >= triggerTokens && uncompressedTurns.length > this.config.windowSize;
553
+ }
554
+ async apply(ctx) {
555
+ const uncompressedIndices = ctx.turns.map((t, i) => ({ t, i })).filter(({ t }) => t.role !== "system" && !t.compressed).slice(0, this.config.windowSize);
556
+ const targetTurns = uncompressedIndices.map((u) => u.t);
557
+ const middleText = targetTurns.map((t) => `${t.role}: ${JSON.stringify(t.content)}`).join("\n");
558
+ const summaryResponse = await this.adapter.complete({
559
+ model: "",
560
+ messages: [{
561
+ role: "user",
562
+ content: `Summarize the oldest part of this conversation:
563
+ ${middleText}`
564
+ }]
565
+ });
566
+ const summaryStr = summaryResponse.content;
567
+ const newCompressionSummary = ctx.compressionSummary ? `${ctx.compressionSummary}
568
+ ${summaryStr}` : summaryStr;
569
+ const indicesToRemove = new Set(uncompressedIndices.map((u) => u.i));
570
+ const newTurns = ctx.turns.filter((_, i) => !indicesToRemove.has(i));
571
+ const TokenCount = newTurns.reduce((sum, t) => sum + t.tokenCount, 0) + this.adapter.estimateTokens(ctx.systemPrompt) + this.adapter.estimateTokens(ctx.scratchpad);
572
+ return {
573
+ ...ctx,
574
+ turns: newTurns,
575
+ tokenCount: TokenCount,
576
+ compressionSummary: newCompressionSummary
577
+ };
578
+ }
579
+ };
580
+ var ShortTermMemoryRegistry = class {
581
+ storage;
582
+ maxTextTokens;
583
+ constructor(config) {
584
+ this.storage = config.storage;
585
+ this.maxTextTokens = config.maxTextTokens ?? 1e5;
586
+ }
587
+ /**
588
+ * Registers a new memory item and returns its reference string.
589
+ *
590
+ * @param content - The raw content to store
591
+ * @param type - The type of content ('text' or 'blob')
592
+ * @param metadata - Optional metadata (e.g. sandwich layers, original filename)
593
+ * @param estimateTokens - Optional function to estimate token count for 'text' type
594
+ * @returns A reference string formatted as '[STM:uuid]'
595
+ * @throws {Error} if a text item exceeds the maxTextTokens limit
596
+ */
597
+ async register(content, type, metadata, estimateTokens) {
598
+ if (type === "text") {
599
+ const tokenCount = estimateTokens ? estimateTokens(content) : Math.ceil(String(content).length / 4);
600
+ if (tokenCount > this.maxTextTokens) {
601
+ throw new Error(`Text content exceeds max tokens limit of ${this.maxTextTokens} (estimated ${tokenCount})`);
602
+ }
603
+ }
604
+ const id = crypto.randomUUID();
605
+ const item = {
606
+ id,
607
+ content,
608
+ type,
609
+ ...metadata !== void 0 ? { metadata } : {}
610
+ };
611
+ await this.storage.set(id, item);
612
+ return `[STM:${id}]`;
613
+ }
614
+ /**
615
+ * Updates an existing STM item's content or metadata.
616
+ *
617
+ * @param id - The UUID of the item to update
618
+ * @param updates - Partial updates to apply (content or metadata)
619
+ */
620
+ async update(id, updates) {
621
+ const item = await this.storage.get(id);
622
+ if (!item) throw new Error(`STM item not found for update: ${id}`);
623
+ const updatedItem = {
624
+ ...item,
625
+ ...updates.content !== void 0 ? { content: updates.content } : {},
626
+ ...updates.metadata !== void 0 ? { metadata: { ...item.metadata, ...updates.metadata } } : {}
627
+ };
628
+ await this.storage.set(id, updatedItem);
629
+ }
630
+ /**
631
+ * Retrieves an STM item by its full reference string (e.g., '[STM:uuid]').
632
+ *
633
+ * @param ref - The full reference string
634
+ * @returns The STMItem or undefined if not found
635
+ */
636
+ async getByRef(ref) {
637
+ const match = ref.match(/^\[STM:(.+)\]$/);
638
+ if (!match || !match[1]) return void 0;
639
+ return this.storage.get(match[1]);
640
+ }
641
+ /**
642
+ * Deletes an STM item by its ID.
643
+ *
644
+ * @param id - The UUID of the item to delete
645
+ */
646
+ async delete(id) {
647
+ await this.storage.delete(id);
648
+ }
649
+ };
650
+ var InMemoryStorageAdapter = class {
651
+ store = /* @__PURE__ */ new Map();
652
+ /**
653
+ * Retrieves stored content by ID.
654
+ *
655
+ * @param id - The identifier of the stored item
656
+ * @returns The stored content or undefined if not found
657
+ */
658
+ async get(id) {
659
+ return this.store.get(id)?.content;
660
+ }
661
+ /**
662
+ * Returns the full item including metadata.
663
+ *
664
+ * @param id - The identifier of the stored item
665
+ * @returns The complete item with content and metadata
666
+ * @internal
667
+ */
668
+ async getFull(id) {
669
+ return this.store.get(id);
670
+ }
671
+ /**
672
+ * Stores content, generating an ID if none is provided.
673
+ *
674
+ * @param id - Optional provided ID. If omitted, a UUID is generated.
675
+ * @param content - The content to store
676
+ * @param metadata - Optional metadata
677
+ * @returns The ID under which the content is stored
678
+ */
679
+ async set(id, content, metadata) {
680
+ const resolvedId = id ?? crypto.randomUUID();
681
+ this.store.set(resolvedId, metadata !== void 0 ? { content, metadata } : { content });
682
+ return resolvedId;
683
+ }
684
+ /**
685
+ * Deletes the content for the given ID.
686
+ *
687
+ * @param id - The identifier of the item to delete
688
+ */
689
+ async delete(id) {
690
+ this.store.delete(id);
691
+ }
692
+ /**
693
+ * Synchronous health check, always true for in-memory.
694
+ *
695
+ * @returns true
696
+ */
697
+ async healthCheck() {
698
+ return true;
699
+ }
700
+ };
701
+
702
+ // src/tools/ToolRegistry.ts
703
+ var ToolRegistry = class {
704
+ tools = /* @__PURE__ */ new Map();
705
+ constructor(initialTools = []) {
706
+ for (const tool of initialTools) {
707
+ this.register(tool);
708
+ }
709
+ }
710
+ register(tool) {
711
+ if (this.tools.has(tool.name)) {
712
+ throw new Error(`Tool ${tool.name} is already registered.`);
713
+ }
714
+ this.tools.set(tool.name, tool);
715
+ }
716
+ get(name) {
717
+ return this.tools.get(name);
718
+ }
719
+ getAll() {
720
+ return Array.from(this.tools.values());
721
+ }
722
+ async execute(name, params, context) {
723
+ const tool = this.tools.get(name);
724
+ if (!tool) {
725
+ throw new LemuraToolNotFoundError(`Tool '${name}' not found.`);
726
+ }
727
+ try {
728
+ const result = await tool.execute(params, context);
729
+ return result;
730
+ } catch (err) {
731
+ if (err instanceof LemuraToolValidationError) {
732
+ throw err;
733
+ }
734
+ throw new Error(`Tool execution failed for '${name}': ${err.message}`);
735
+ }
736
+ }
737
+ };
738
+
739
+ // src/skills/SkillInjector.ts
740
+ var SkillInjector = class {
741
+ skills = [];
742
+ constructor(skills = []) {
743
+ this.skills = [...skills];
744
+ this.sortSkills();
745
+ }
746
+ register(skill) {
747
+ this.skills.push(skill);
748
+ this.sortSkills();
749
+ }
750
+ sortSkills() {
751
+ this.skills.sort((a, b) => a.priority - b.priority);
752
+ }
753
+ getSkillsForInjection(position) {
754
+ return this.skills.filter((s) => s.inject === position);
755
+ }
756
+ /**
757
+ * Generates a combined prompt block for all skills targeting a specific injection position.
758
+ */
759
+ buildInjectionBlock(position) {
760
+ const relevantSkills = this.getSkillsForInjection(position);
761
+ if (relevantSkills.length === 0) return "";
762
+ let block = "";
763
+ for (const skill of relevantSkills) {
764
+ const content = skill.standard || skill.micro || skill.nano || skill.description;
765
+ block += `
766
+ [Skill: ${skill.name} (Tier: ${skill.tier})]
767
+ ${content}
768
+ `;
769
+ }
770
+ return block.trim();
771
+ }
772
+ };
773
+
774
+ // src/rag/InMemoryRAGAdapter.ts
775
+ var InMemoryRAGAdapter = class {
776
+ documents = /* @__PURE__ */ new Map();
777
+ async ingest(request) {
778
+ for (const doc of request.documents) {
779
+ this.documents.set(doc.id, doc);
780
+ }
781
+ return {
782
+ ingestedCount: request.documents.length,
783
+ failedCount: 0
784
+ };
785
+ }
786
+ async query(request) {
787
+ const queryStr = request.query.toLowerCase();
788
+ const results = [];
789
+ for (const doc of this.documents.values()) {
790
+ const content = doc.content.toLowerCase();
791
+ let score = 0;
792
+ if (content.includes(queryStr)) score += 0.8;
793
+ const words = queryStr.split(" ");
794
+ for (const word of words) {
795
+ if (content.includes(word)) score += 0.1;
796
+ }
797
+ if (score > 0 && score >= (request.minScore || 0)) {
798
+ results.push({ document: doc, score: Math.min(score, 1) });
799
+ }
800
+ }
801
+ results.sort((a, b) => b.score - a.score);
802
+ const topK = request.topK || 5;
803
+ return {
804
+ results: results.slice(0, topK)
805
+ };
806
+ }
807
+ async healthCheck() {
808
+ return true;
809
+ }
810
+ };
811
+
812
+ // src/logger/DefaultLogger.ts
813
+ var DefaultLogger = class {
814
+ level = 1 /* INFO */;
815
+ COLORS = {
816
+ DEBUG: "\x1B[36m",
817
+ // Cyan
818
+ INFO: "\x1B[32m",
819
+ // Green
820
+ WARN: "\x1B[33m",
821
+ // Yellow
822
+ ERROR: "\x1B[31m",
823
+ // Red
824
+ FATAL: "\x1B[41m\x1B[37m",
825
+ // White on Red background
826
+ RESET: "\x1B[0m"
827
+ };
828
+ setLevel(level) {
829
+ this.level = level;
830
+ }
831
+ debug(message, metadata) {
832
+ if (this.level <= 0 /* DEBUG */) this.log("DEBUG", message, metadata);
833
+ }
834
+ info(message, metadata) {
835
+ if (this.level <= 1 /* INFO */) this.log("INFO", message, metadata);
836
+ }
837
+ warn(message, metadata) {
838
+ if (this.level <= 2 /* WARN */) this.log("WARN", message, metadata);
839
+ }
840
+ error(message, metadata) {
841
+ if (this.level <= 3 /* ERROR */) this.log("ERROR", message, metadata);
842
+ }
843
+ fatal(message, metadata) {
844
+ if (this.level <= 4 /* FATAL */) this.log("FATAL", message, metadata);
845
+ }
846
+ log(severity, message, metadata) {
847
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
848
+ const color = this.COLORS[severity] || this.COLORS.RESET;
849
+ const reset = this.COLORS.RESET;
850
+ console.log(`${timestamp} [${color}${severity}${reset}] ${message}`);
851
+ if (metadata) {
852
+ if (metadata.problem) {
853
+ console.log(` ${color}PROBLEM:${reset} ${metadata.problem}`);
854
+ }
855
+ if (metadata.hints && metadata.hints.length > 0) {
856
+ console.log(` ${color}HINTS:${reset}`);
857
+ metadata.hints.forEach((hint) => console.log(` - ${hint}`));
858
+ }
859
+ const otherKeys = Object.keys(metadata).filter((k) => !["problem", "hints", "severity"].includes(k));
860
+ if (otherKeys.length > 0) {
861
+ otherKeys.forEach((key) => {
862
+ const value = metadata[key];
863
+ if (typeof value === "object") {
864
+ console.log(` ${key}: ${JSON.stringify(value, null, 2).replace(/\n/g, "\n ")}`);
865
+ } else {
866
+ console.log(` ${key}: ${value}`);
867
+ }
868
+ });
869
+ }
870
+ }
871
+ }
872
+ };
873
+
874
+ // src/tools/builtin/short_term_memory.ts
875
+ var readChunkTool = {
876
+ name: "read_chunk",
877
+ description: "Reads a specific portion or chunk of memory from a Short Term Memory reference.",
878
+ parameters: {
879
+ type: "object",
880
+ properties: {
881
+ ref: { type: "string", description: "The STM reference (e.g., [STM:uuid])" },
882
+ start: { type: "number", description: "Starting index or page" },
883
+ end: { type: "number", description: "Ending index or page" }
884
+ },
885
+ required: ["ref"]
886
+ },
887
+ async execute(params, context) {
888
+ if (!context.stmRegistry) throw new Error("STM Registry not available in context");
889
+ const item = await context.stmRegistry.getByRef(params.ref);
890
+ if (!item) throw new Error(`STM item not found: ${params.ref}`);
891
+ const content = String(item.content);
892
+ if (params.start !== void 0 || params.end !== void 0) {
893
+ return content.slice(params.start ?? 0, params.end ?? content.length);
894
+ }
895
+ return content;
896
+ }
897
+ };
898
+ var searchChunkTool = {
899
+ name: "search_chunk",
900
+ description: "Searches for specific content or patterns within Short Term Memory chunks.",
901
+ parameters: {
902
+ type: "object",
903
+ properties: {
904
+ ref: { type: "string", description: "The STM reference" },
905
+ query: { type: "string", description: "The search query or keyword" }
906
+ },
907
+ required: ["ref", "query"]
908
+ },
909
+ async execute(params, context) {
910
+ if (!context.stmRegistry) throw new Error("STM Registry not available in context");
911
+ const item = await context.stmRegistry.getByRef(params.ref);
912
+ if (!item) throw new Error(`STM item not found: ${params.ref}`);
913
+ const content = String(item.content);
914
+ const index = content.toLowerCase().indexOf(params.query.toLowerCase());
915
+ if (index === -1) return "No matches found.";
916
+ const start = Math.max(0, index - 100);
917
+ const end = Math.min(content.length, index + 100);
918
+ return `Match found at index ${index}: ...${content.slice(start, end)}...`;
919
+ }
920
+ };
921
+ var listChunksTool = {
922
+ name: "list_chunks",
923
+ description: "Lists available chunks or structural breakdown of a Short Term Memory reference.",
924
+ parameters: {
925
+ type: "object",
926
+ properties: {
927
+ ref: { type: "string", description: "The STM reference" }
928
+ },
929
+ required: ["ref"]
930
+ },
931
+ async execute(params, context) {
932
+ if (!context.stmRegistry) throw new Error("STM Registry not available in context");
933
+ const item = await context.stmRegistry.getByRef(params.ref);
934
+ if (!item) throw new Error(`STM item not found: ${params.ref}`);
935
+ const size = String(item.content).length;
936
+ const chunkSize = 2e3;
937
+ const totalChunks = Math.ceil(size / chunkSize);
938
+ return {
939
+ totalSize: size,
940
+ chunkSize,
941
+ totalChunks,
942
+ chunks: Array.from({ length: totalChunks }, (_, i) => ({
943
+ index: i,
944
+ start: i * chunkSize,
945
+ end: Math.min((i + 1) * chunkSize, size)
946
+ }))
947
+ };
948
+ }
949
+ };
950
+ var updateChunkTool = {
951
+ name: "update_chunk",
952
+ description: "Updates or appends content to a specific Short Term Memory reference.",
953
+ parameters: {
954
+ type: "object",
955
+ properties: {
956
+ ref: { type: "string", description: "The STM reference" },
957
+ content: { type: "string", description: "The new content or update" },
958
+ mode: { type: "string", enum: ["append", "replace"], default: "append" }
959
+ },
960
+ required: ["ref", "content"]
961
+ },
962
+ async execute(params, context) {
963
+ if (!context.stmRegistry) throw new Error("STM Registry not available in context");
964
+ const item = await context.stmRegistry.getByRef(params.ref);
965
+ if (!item) throw new Error(`STM item not found: ${params.ref}`);
966
+ let newContent = String(item.content);
967
+ if (params.mode === "replace") {
968
+ newContent = params.content;
969
+ } else {
970
+ newContent += params.content;
971
+ }
972
+ const uuid = params.ref.match(/\[STM:(.+)\]/)?.[1];
973
+ if (!uuid) throw new Error("Invalid STM reference");
974
+ await context.stmRegistry.update(uuid, { content: newContent });
975
+ return { status: "success", ref: params.ref };
976
+ }
977
+ };
978
+ var readScratchpadTool = {
979
+ name: "read_scratchpad",
980
+ description: "Reads the content from the current agent scratchpad.",
981
+ parameters: { type: "object", properties: {} },
982
+ async execute(_params, context) {
983
+ return context.scratchpad ?? "";
984
+ }
985
+ };
986
+ var writeScratchpadTool = {
987
+ name: "write_scratchpad",
988
+ description: "Writes content to the agent scratchpad for long-term reasoning steps.",
989
+ parameters: {
990
+ type: "object",
991
+ properties: {
992
+ content: { type: "string", description: "Content to write" },
993
+ append: { type: "boolean", default: true }
994
+ },
995
+ required: ["content"]
996
+ },
997
+ async execute(params, context) {
998
+ let newScratchpad = context.scratchpad ?? "";
999
+ if (params.append) {
1000
+ newScratchpad += (newScratchpad ? "\n" : "") + params.content;
1001
+ } else {
1002
+ newScratchpad = params.content;
1003
+ }
1004
+ return { status: "success", newScratchpad, note: "Scratchpad updated" };
1005
+ }
1006
+ };
1007
+ var removeScratchpadTool = {
1008
+ name: "remove_scratchpad",
1009
+ description: "Clears or removes content from the scratchpad.",
1010
+ parameters: { type: "object", properties: {} },
1011
+ async execute(_params, _context) {
1012
+ return { status: "success", newScratchpad: "", note: "Scratchpad cleared" };
1013
+ }
1014
+ };
1015
+ var summarizeSandwichTool = {
1016
+ name: "summarize_sandwich",
1017
+ description: "Generated a layered sandwich summary of a large STM reference.",
1018
+ parameters: {
1019
+ type: "object",
1020
+ properties: {
1021
+ ref: { type: "string", description: "The STM reference" },
1022
+ instructions: { type: "string", description: "Summarization instructions" }
1023
+ },
1024
+ required: ["ref"]
1025
+ },
1026
+ async execute(params, context) {
1027
+ if (!context.stmRegistry) throw new Error("STM Registry not available in context");
1028
+ const item = await context.stmRegistry.getByRef(params.ref);
1029
+ if (!item) throw new Error(`STM item not found: ${params.ref}`);
1030
+ return { status: "pending", note: "Summarization requires AI provider" };
1031
+ }
1032
+ };
1033
+
1034
+ // src/agent/SessionManager.ts
1035
+ var SessionManager = class {
1036
+ contextManager;
1037
+ toolRegistry;
1038
+ skillInjector;
1039
+ context;
1040
+ adapter;
1041
+ config;
1042
+ iterations = 0;
1043
+ logger;
1044
+ constructor(config) {
1045
+ this.config = config;
1046
+ this.adapter = config.adapter;
1047
+ this.logger = config.logger || new DefaultLogger();
1048
+ this.contextManager = new ContextManager();
1049
+ this.toolRegistry = new ToolRegistry(config.tools || []);
1050
+ this.skillInjector = new SkillInjector(config.skills || []);
1051
+ for (const strategy of config.compressionStrategies || []) {
1052
+ this.contextManager.registerStrategy(strategy);
1053
+ }
1054
+ if (config.stmRegistry) {
1055
+ this.toolRegistry.register(readChunkTool);
1056
+ this.toolRegistry.register(searchChunkTool);
1057
+ this.toolRegistry.register(listChunksTool);
1058
+ this.toolRegistry.register(updateChunkTool);
1059
+ this.toolRegistry.register(readScratchpadTool);
1060
+ this.toolRegistry.register(writeScratchpadTool);
1061
+ this.toolRegistry.register(removeScratchpadTool);
1062
+ this.toolRegistry.register(summarizeSandwichTool);
1063
+ }
1064
+ this.context = {
1065
+ systemPrompt: config.systemPrompt || "",
1066
+ scratchpad: "",
1067
+ turns: [],
1068
+ tokenCount: 0,
1069
+ maxTokens: config.maxTokens,
1070
+ metadata: {}
1071
+ };
1072
+ }
1073
+ getContext() {
1074
+ return { ...this.context };
1075
+ }
1076
+ getHistory() {
1077
+ return [...this.context.turns];
1078
+ }
1079
+ async run(userMessage) {
1080
+ this.logger.info(`Starting new session run`, {
1081
+ model: this.config.model,
1082
+ message: Array.isArray(userMessage) ? "[Multimodal Content]" : userMessage
1083
+ });
1084
+ this.context.turns.push({
1085
+ role: "user",
1086
+ content: userMessage,
1087
+ tokenCount: Array.isArray(userMessage) ? userMessage.length * 50 : this.adapter.estimateTokens(userMessage),
1088
+ turnIndex: this.context.turns.length,
1089
+ compressed: false
1090
+ });
1091
+ const maxIts = this.config.maxIterations || 10;
1092
+ this.iterations = 0;
1093
+ while (this.iterations < maxIts) {
1094
+ this.iterations++;
1095
+ this.logger.debug(`ReAct Iteration ${this.iterations}/${maxIts}`);
1096
+ this.context = await this.contextManager.prepare(this.context);
1097
+ const messages = this.context.turns.map((t) => ({
1098
+ role: t.role,
1099
+ content: t.content,
1100
+ ...t.role === "tool" && t.toolResults?.[0] ? { name: t.toolResults[0].toolCallId } : {},
1101
+ ...t.role === "assistant" && t.toolCalls ? { toolCalls: t.toolCalls } : {}
1102
+ }));
1103
+ let fullSystemPrompt = this.context.systemPrompt || "";
1104
+ const injectedSkills = this.skillInjector.buildInjectionBlock("system_prompt");
1105
+ if (injectedSkills) {
1106
+ fullSystemPrompt += "\n\n" + injectedSkills;
1107
+ }
1108
+ if (fullSystemPrompt.trim()) {
1109
+ messages.unshift({ role: "system", content: fullSystemPrompt.trim() });
1110
+ }
1111
+ this.logger.debug(`Calling provider adapter (${this.adapter.name})...`);
1112
+ let response;
1113
+ try {
1114
+ response = await this.adapter.complete({
1115
+ model: this.config.model,
1116
+ messages,
1117
+ tools: this.toolRegistry.getAll(),
1118
+ maxTokens: 1e3
1119
+ // default gen tokens
1120
+ });
1121
+ } catch (err) {
1122
+ const metadata = err.problem ? { problem: err.problem, hints: err.hints } : {};
1123
+ this.logger.fatal(`Provider call failed: ${err.message}`, metadata);
1124
+ throw err;
1125
+ }
1126
+ if (response.finishReason === "tool_call" && response.toolCalls) {
1127
+ this.logger.info(`Assistant requested ${response.toolCalls.length} tool calls`, {
1128
+ tools: response.toolCalls.map((tc) => tc.name)
1129
+ });
1130
+ const toolResults = [];
1131
+ for (const tc of response.toolCalls) {
1132
+ try {
1133
+ const args = JSON.parse(tc.arguments);
1134
+ const executeContext = {
1135
+ sessionId: "default",
1136
+ turnIndex: this.context.turns.length,
1137
+ logger: this.logger,
1138
+ stmRegistry: this.config.stmRegistry,
1139
+ scratchpad: this.context.scratchpad
1140
+ };
1141
+ if (this.config.ragAdapter) {
1142
+ executeContext.ragAdapter = this.config.ragAdapter;
1143
+ }
1144
+ this.logger.debug(`Executing tool: ${tc.name}`, { args: tc.arguments });
1145
+ const result = await this.toolRegistry.execute(tc.name, args, executeContext);
1146
+ this.logger.debug(`Tool ${tc.name} returned successfully`);
1147
+ let finalResult = result;
1148
+ if (typeof result === "object" && result !== null) {
1149
+ const resObj = result;
1150
+ if (resObj.status === "success" && resObj.newScratchpad !== void 0) {
1151
+ this.context.scratchpad = resObj.newScratchpad;
1152
+ finalResult = resObj.note || "Scratchpad updated";
1153
+ }
1154
+ }
1155
+ let content = JSON.stringify(finalResult);
1156
+ const tokenCount = this.adapter.estimateTokens(content);
1157
+ if (this.config.maxTokensPerTool && tokenCount > this.config.maxTokensPerTool) {
1158
+ content = content.slice(0, this.config.maxTokensPerTool * 4) + "... [TRUNCATED DUE TO TOOL TOKEN LIMIT]";
1159
+ }
1160
+ toolResults.push({ toolCallId: tc.id, content });
1161
+ } catch (e) {
1162
+ this.logger.error(`Tool ${tc.name} execution failed: ${e.message}`, {
1163
+ problem: e.problem || `Tool ${tc.name} failed to execute properly.`,
1164
+ hints: e.hints || ["Check the tool parameters and ensure the required services are running."]
1165
+ });
1166
+ toolResults.push({ toolCallId: tc.id, content: `Error: ${e.message}` });
1167
+ }
1168
+ }
1169
+ const assistantTurn = {
1170
+ role: "assistant",
1171
+ content: response.content || "",
1172
+ tokenCount: this.adapter.estimateTokens(response.content || "") + 50,
1173
+ // rough tool estimate
1174
+ turnIndex: this.context.turns.length,
1175
+ compressed: false,
1176
+ toolCalls: response.toolCalls
1177
+ };
1178
+ this.context.turns.push(assistantTurn);
1179
+ if (this.config.onTurn) this.config.onTurn(assistantTurn);
1180
+ for (const res of toolResults) {
1181
+ const toolTurn = {
1182
+ role: "tool",
1183
+ content: res.content,
1184
+ tokenCount: this.adapter.estimateTokens(res.content),
1185
+ turnIndex: this.context.turns.length,
1186
+ compressed: false,
1187
+ toolResults: [res]
1188
+ };
1189
+ this.context.turns.push(toolTurn);
1190
+ if (this.config.onTurn) this.config.onTurn(toolTurn);
1191
+ }
1192
+ continue;
1193
+ }
1194
+ if (response.finishReason === "stop" || response.finishReason === "max_tokens" || response.finishReason === "error") {
1195
+ const finalTurn = {
1196
+ role: "assistant",
1197
+ content: response.content,
1198
+ tokenCount: response.usage.completionTokens,
1199
+ turnIndex: this.context.turns.length,
1200
+ compressed: false
1201
+ };
1202
+ this.context.turns.push(finalTurn);
1203
+ if (this.config.onTurn) this.config.onTurn(finalTurn);
1204
+ this.logger.info(`Run completed successfully`);
1205
+ return response.content;
1206
+ }
1207
+ }
1208
+ const maxItsErr = new LemuraMaxIterationsError(`Exceeded max iterations of ${maxIts}`);
1209
+ this.logger.fatal(maxItsErr.message, {
1210
+ problem: "The agent entered an infinite loop or took too many steps to resolve the task.",
1211
+ hints: ["Increase maxIterations if the task is complex.", "Check if tools are returning consistent results."]
1212
+ });
1213
+ throw maxItsErr;
1214
+ }
1215
+ };
1216
+
1217
+ // src/agent/execution/ToolResponseProcessor.ts
1218
+ var ToolResponseProcessor = class {
1219
+ evaluate(response, tool, context) {
1220
+ const length = response.length;
1221
+ const estimatedTokens = length / 4;
1222
+ let sizeClass = "small";
1223
+ if (estimatedTokens > 2e3) sizeClass = "oversized";
1224
+ else if (estimatedTokens > 800) sizeClass = "large";
1225
+ else if (estimatedTokens > 200) sizeClass = "medium";
1226
+ return {
1227
+ relevanceScore: 1,
1228
+ sizeClass,
1229
+ shouldCompress: sizeClass === "large" || sizeClass === "oversized",
1230
+ suggestedMaxTokens: 500,
1231
+ answered: true,
1232
+ answeredPartially: false,
1233
+ errorDetected: response.toLowerCase().includes("error"),
1234
+ suggestedAction: "continue"
1235
+ };
1236
+ }
1237
+ compress(response, evaluation) {
1238
+ if (!evaluation.shouldCompress || evaluation.errorDetected) {
1239
+ return response;
1240
+ }
1241
+ if (response.length > 1e3) {
1242
+ return response.substring(0, 1e3) + "\n...[COMPRESSED TO SAVE TOKENS]...";
1243
+ }
1244
+ return response;
1245
+ }
1246
+ };
1247
+
1248
+ // src/agent/execution/GoalInjector.ts
1249
+ var GoalInjector = class {
1250
+ constructor(goal) {
1251
+ this.goal = goal;
1252
+ }
1253
+ injectInto(prompt) {
1254
+ const formatting = `[CURRENT GOAL]
1255
+ ${this.goal.statement}
1256
+
1257
+ Success criteria:
1258
+ ${this.goal.successCriteria.map((c) => `- ${c}`).join("\n")}
1259
+ [/CURRENT GOAL]`;
1260
+ if (this.goal.injectionPosition === "system_prompt") {
1261
+ return `${prompt}
1262
+
1263
+ ${formatting}`;
1264
+ }
1265
+ return prompt;
1266
+ }
1267
+ };
1268
+
1269
+ // src/agent/execution/ContinuationPlanner.ts
1270
+ var ContinuationPlanner = class {
1271
+ constructor(plan) {
1272
+ this.plan = plan;
1273
+ }
1274
+ getPlanStatusString() {
1275
+ let result = `[CONTINUATION PLAN \u2014 Step ${this.plan.currentStepIndex + 1}/${this.plan.steps.length}]
1276
+ `;
1277
+ for (const step of this.plan.steps) {
1278
+ const getIcon = (status) => {
1279
+ switch (status) {
1280
+ case "done":
1281
+ return "\u2705";
1282
+ case "running":
1283
+ return "\u25B6";
1284
+ case "failed":
1285
+ return "\u274C";
1286
+ case "skipped":
1287
+ return "\u23ED";
1288
+ default:
1289
+ return "\u23F3";
1290
+ }
1291
+ };
1292
+ const statusText = step.status === "pending" && step.dependsOn.length > 0 ? `Waiting on Step ${step.dependsOn.join(", ")}` : step.status.charAt(0).toUpperCase() + step.status.slice(1);
1293
+ result += `${getIcon(step.status)} Step ${step.stepId} (${step.toolName}): ${statusText}
1294
+ `;
1295
+ }
1296
+ return result;
1297
+ }
1298
+ };
1299
+
1300
+ // src/agent/execution/StepCounter.ts
1301
+ var StepCounter = class {
1302
+ constructor(maxSteps = 20) {
1303
+ this.maxSteps = maxSteps;
1304
+ }
1305
+ toolCallCount = 0;
1306
+ increment(count = 1) {
1307
+ this.toolCallCount += count;
1308
+ }
1309
+ get count() {
1310
+ return this.toolCallCount;
1311
+ }
1312
+ isMaxReached() {
1313
+ return this.toolCallCount >= this.maxSteps;
1314
+ }
1315
+ getForcedConclusionPrompt() {
1316
+ return `You have used ${this.toolCallCount}/${this.maxSteps} steps. Provide your final response now.
1317
+ Do not call any more tools. Use the required structure below.`;
1318
+ }
1319
+ };
1320
+
1321
+ // src/agent/execution/FinalResponseFormatter.ts
1322
+ var FinalResponseFormatter = class {
1323
+ static getRequiredStructure() {
1324
+ return `## Goal Status: [ACHIEVED | PARTIALLY_ACHIEVED | FAILED]
1325
+
1326
+ ### What was accomplished
1327
+ [Summary of completed work]
1328
+
1329
+ ### Remaining tasks
1330
+ [Bulleted list, or "None"]
1331
+
1332
+ ### Failed steps
1333
+ [Tool/step name + error context, or "None"]
1334
+
1335
+ ### Result
1336
+ [The actual answer or deliverable]`;
1337
+ }
1338
+ static validateStructure(response) {
1339
+ if (!response.includes("## Goal Status:")) return false;
1340
+ if (!response.includes("### What was accomplished")) return false;
1341
+ if (!response.includes("### Remaining tasks")) return false;
1342
+ if (!response.includes("### Failed steps")) return false;
1343
+ if (!response.includes("### Result")) return false;
1344
+ return true;
1345
+ }
1346
+ };
1347
+
1348
+ exports.ContextManager = ContextManager;
1349
+ exports.ContinuationPlanner = ContinuationPlanner;
1350
+ exports.DefaultLogger = DefaultLogger;
1351
+ exports.FinalResponseFormatter = FinalResponseFormatter;
1352
+ exports.GoalInjector = GoalInjector;
1353
+ exports.HistoryCompressionStrategy = HistoryCompressionStrategy;
1354
+ exports.InMemoryRAGAdapter = InMemoryRAGAdapter;
1355
+ exports.InMemoryStorageAdapter = InMemoryStorageAdapter;
1356
+ exports.LemuraAdapterError = LemuraAdapterError;
1357
+ exports.LemuraContextOverflowError = LemuraContextOverflowError;
1358
+ exports.LemuraError = LemuraError;
1359
+ exports.LemuraMaxIterationsError = LemuraMaxIterationsError;
1360
+ exports.LemuraSkillInjectionError = LemuraSkillInjectionError;
1361
+ exports.LemuraToolNotFoundError = LemuraToolNotFoundError;
1362
+ exports.LemuraToolTimeoutError = LemuraToolTimeoutError;
1363
+ exports.LemuraToolValidationError = LemuraToolValidationError;
1364
+ exports.LogLevel = LogLevel;
1365
+ exports.OpenAICompatibleAdapter = OpenAICompatibleAdapter;
1366
+ exports.SandwichCompressionStrategy = SandwichCompressionStrategy;
1367
+ exports.ScratchpadStrategy = ScratchpadStrategy;
1368
+ exports.SessionManager = SessionManager;
1369
+ exports.ShortTermMemoryRegistry = ShortTermMemoryRegistry;
1370
+ exports.SkillInjector = SkillInjector;
1371
+ exports.StepCounter = StepCounter;
1372
+ exports.ToolRegistry = ToolRegistry;
1373
+ exports.ToolResponseProcessor = ToolResponseProcessor;
1374
+ //# sourceMappingURL=index.js.map
1375
+ //# sourceMappingURL=index.js.map