@looopy-ai/core 1.0.1

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 (132) hide show
  1. package/LICENSE +9 -0
  2. package/dist/core/agent.d.ts +53 -0
  3. package/dist/core/agent.js +416 -0
  4. package/dist/core/cleanup.d.ts +12 -0
  5. package/dist/core/cleanup.js +45 -0
  6. package/dist/core/index.d.ts +3 -0
  7. package/dist/core/index.js +3 -0
  8. package/dist/core/iteration.d.ts +5 -0
  9. package/dist/core/iteration.js +60 -0
  10. package/dist/core/logger.d.ts +11 -0
  11. package/dist/core/logger.js +31 -0
  12. package/dist/core/loop.d.ts +5 -0
  13. package/dist/core/loop.js +125 -0
  14. package/dist/core/tools.d.ts +4 -0
  15. package/dist/core/tools.js +78 -0
  16. package/dist/core/types.d.ts +30 -0
  17. package/dist/core/types.js +1 -0
  18. package/dist/events/index.d.ts +3 -0
  19. package/dist/events/index.js +2 -0
  20. package/dist/events/utils.d.ts +250 -0
  21. package/dist/events/utils.js +263 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +8 -0
  24. package/dist/observability/index.d.ts +1 -0
  25. package/dist/observability/index.js +1 -0
  26. package/dist/observability/spans/agent-turn.d.ts +31 -0
  27. package/dist/observability/spans/agent-turn.js +94 -0
  28. package/dist/observability/spans/index.d.ts +5 -0
  29. package/dist/observability/spans/index.js +5 -0
  30. package/dist/observability/spans/iteration.d.ts +14 -0
  31. package/dist/observability/spans/iteration.js +41 -0
  32. package/dist/observability/spans/llm-call.d.ts +14 -0
  33. package/dist/observability/spans/llm-call.js +50 -0
  34. package/dist/observability/spans/loop.d.ts +14 -0
  35. package/dist/observability/spans/loop.js +40 -0
  36. package/dist/observability/spans/tool.d.ts +14 -0
  37. package/dist/observability/spans/tool.js +44 -0
  38. package/dist/observability/tracing.d.ts +58 -0
  39. package/dist/observability/tracing.js +203 -0
  40. package/dist/providers/chat-completions/aggregate.d.ts +4 -0
  41. package/dist/providers/chat-completions/aggregate.js +152 -0
  42. package/dist/providers/chat-completions/content.d.ts +25 -0
  43. package/dist/providers/chat-completions/content.js +229 -0
  44. package/dist/providers/chat-completions/index.d.ts +4 -0
  45. package/dist/providers/chat-completions/index.js +4 -0
  46. package/dist/providers/chat-completions/streaming.d.ts +12 -0
  47. package/dist/providers/chat-completions/streaming.js +3 -0
  48. package/dist/providers/chat-completions/types.d.ts +39 -0
  49. package/dist/providers/chat-completions/types.js +1 -0
  50. package/dist/providers/index.d.ts +2 -0
  51. package/dist/providers/index.js +1 -0
  52. package/dist/providers/litellm-provider.d.ts +43 -0
  53. package/dist/providers/litellm-provider.js +377 -0
  54. package/dist/server/event-buffer.d.ts +37 -0
  55. package/dist/server/event-buffer.js +116 -0
  56. package/dist/server/event-router.d.ts +31 -0
  57. package/dist/server/event-router.js +91 -0
  58. package/dist/server/index.d.ts +3 -0
  59. package/dist/server/index.js +3 -0
  60. package/dist/server/sse.d.ts +60 -0
  61. package/dist/server/sse.js +159 -0
  62. package/dist/stores/artifacts/artifact-scheduler.d.ts +50 -0
  63. package/dist/stores/artifacts/artifact-scheduler.js +86 -0
  64. package/dist/stores/artifacts/index.d.ts +3 -0
  65. package/dist/stores/artifacts/index.js +3 -0
  66. package/dist/stores/artifacts/internal-event-artifact-store.d.ts +54 -0
  67. package/dist/stores/artifacts/internal-event-artifact-store.js +126 -0
  68. package/dist/stores/artifacts/memory-artifact-store.d.ts +52 -0
  69. package/dist/stores/artifacts/memory-artifact-store.js +268 -0
  70. package/dist/stores/filesystem/filesystem-agent-store.d.ts +18 -0
  71. package/dist/stores/filesystem/filesystem-agent-store.js +61 -0
  72. package/dist/stores/filesystem/filesystem-artifact-store.d.ts +59 -0
  73. package/dist/stores/filesystem/filesystem-artifact-store.js +325 -0
  74. package/dist/stores/filesystem/filesystem-context-store.d.ts +37 -0
  75. package/dist/stores/filesystem/filesystem-context-store.js +245 -0
  76. package/dist/stores/filesystem/filesystem-message-store.d.ts +28 -0
  77. package/dist/stores/filesystem/filesystem-message-store.js +149 -0
  78. package/dist/stores/filesystem/filesystem-task-state-store.d.ts +27 -0
  79. package/dist/stores/filesystem/filesystem-task-state-store.js +220 -0
  80. package/dist/stores/filesystem/index.d.ts +10 -0
  81. package/dist/stores/filesystem/index.js +5 -0
  82. package/dist/stores/index.d.ts +5 -0
  83. package/dist/stores/index.js +5 -0
  84. package/dist/stores/memory/memory-state-store.d.ts +15 -0
  85. package/dist/stores/memory/memory-state-store.js +55 -0
  86. package/dist/stores/messages/hybrid-message-store.d.ts +29 -0
  87. package/dist/stores/messages/hybrid-message-store.js +72 -0
  88. package/dist/stores/messages/index.d.ts +4 -0
  89. package/dist/stores/messages/index.js +4 -0
  90. package/dist/stores/messages/interfaces.d.ts +42 -0
  91. package/dist/stores/messages/interfaces.js +18 -0
  92. package/dist/stores/messages/mem0-message-store.d.ts +34 -0
  93. package/dist/stores/messages/mem0-message-store.js +218 -0
  94. package/dist/stores/messages/memory-message-store.d.ts +27 -0
  95. package/dist/stores/messages/memory-message-store.js +183 -0
  96. package/dist/tools/artifact-tools.d.ts +4 -0
  97. package/dist/tools/artifact-tools.js +277 -0
  98. package/dist/tools/client-tool-provider.d.ts +25 -0
  99. package/dist/tools/client-tool-provider.js +139 -0
  100. package/dist/tools/index.d.ts +4 -0
  101. package/dist/tools/index.js +4 -0
  102. package/dist/tools/local-tools.d.ts +13 -0
  103. package/dist/tools/local-tools.js +70 -0
  104. package/dist/tools/mcp-client.d.ts +29 -0
  105. package/dist/tools/mcp-client.js +62 -0
  106. package/dist/tools/mcp-tool-provider.d.ts +22 -0
  107. package/dist/tools/mcp-tool-provider.js +86 -0
  108. package/dist/types/a2a.d.ts +36 -0
  109. package/dist/types/a2a.js +1 -0
  110. package/dist/types/agent.d.ts +14 -0
  111. package/dist/types/agent.js +1 -0
  112. package/dist/types/artifact.d.ts +126 -0
  113. package/dist/types/artifact.js +1 -0
  114. package/dist/types/context.d.ts +13 -0
  115. package/dist/types/context.js +1 -0
  116. package/dist/types/event.d.ts +360 -0
  117. package/dist/types/event.js +30 -0
  118. package/dist/types/index.d.ts +9 -0
  119. package/dist/types/index.js +9 -0
  120. package/dist/types/llm.d.ts +24 -0
  121. package/dist/types/llm.js +1 -0
  122. package/dist/types/message.d.ts +9 -0
  123. package/dist/types/message.js +1 -0
  124. package/dist/types/state.d.ts +86 -0
  125. package/dist/types/state.js +1 -0
  126. package/dist/types/tools.d.ts +57 -0
  127. package/dist/types/tools.js +53 -0
  128. package/dist/utils/error.d.ts +8 -0
  129. package/dist/utils/error.js +23 -0
  130. package/dist/utils/process-signals.d.ts +3 -0
  131. package/dist/utils/process-signals.js +67 -0
  132. package/package.json +54 -0
@@ -0,0 +1,149 @@
1
+ import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ export class FileSystemMessageStore {
4
+ basePath;
5
+ agentId;
6
+ messageIndex = 0;
7
+ constructor(config) {
8
+ this.basePath = config.basePath || './_agent_store';
9
+ this.agentId = config.agentId;
10
+ }
11
+ async append(contextId, messages) {
12
+ const messagesDir = this.getMessagesDir(contextId);
13
+ await mkdir(messagesDir, { recursive: true });
14
+ const currentCount = await this.getCount(contextId);
15
+ for (let i = 0; i < messages.length; i++) {
16
+ const message = messages[i];
17
+ const index = currentCount + i;
18
+ const timestamp = new Date().toISOString();
19
+ const storedMessage = {
20
+ ...message,
21
+ id: `${contextId}-${index}`,
22
+ contextId,
23
+ index,
24
+ timestamp,
25
+ };
26
+ const filename = this.getMessageFilename(timestamp, index);
27
+ const filePath = join(messagesDir, filename);
28
+ await writeFile(filePath, JSON.stringify(storedMessage, null, 2), 'utf-8');
29
+ }
30
+ }
31
+ async getRecent(contextId, options) {
32
+ const allMessages = await this.loadMessages(contextId);
33
+ if (!options?.maxMessages && !options?.maxTokens) {
34
+ return allMessages.map(this.toMessage);
35
+ }
36
+ let messages = allMessages;
37
+ if (options.maxMessages !== undefined) {
38
+ messages = messages.slice(-options.maxMessages);
39
+ }
40
+ if (options.maxTokens !== undefined) {
41
+ let totalTokens = 0;
42
+ const result = [];
43
+ for (let i = messages.length - 1; i >= 0; i--) {
44
+ const message = messages[i];
45
+ const estimatedTokens = message.tokens || this.estimateTokens(message);
46
+ if (totalTokens + estimatedTokens > options.maxTokens && result.length > 0) {
47
+ break;
48
+ }
49
+ result.unshift(message);
50
+ totalTokens += estimatedTokens;
51
+ }
52
+ messages = result;
53
+ }
54
+ return messages.map(this.toMessage);
55
+ }
56
+ async getAll(contextId) {
57
+ const messages = await this.loadMessages(contextId);
58
+ return messages.map(this.toMessage);
59
+ }
60
+ async getCount(contextId) {
61
+ try {
62
+ const messagesDir = this.getMessagesDir(contextId);
63
+ const files = await readdir(messagesDir);
64
+ return files.filter((f) => f.endsWith('.json')).length;
65
+ }
66
+ catch {
67
+ return 0;
68
+ }
69
+ }
70
+ async getRange(contextId, startIndex, endIndex) {
71
+ const allMessages = await this.loadMessages(contextId);
72
+ const messages = allMessages.filter((m) => m.index >= startIndex && m.index <= endIndex);
73
+ return messages.map(this.toMessage);
74
+ }
75
+ async compact(contextId, options) {
76
+ const allMessages = await this.loadMessages(contextId);
77
+ const keepRecent = options?.keepRecent || 20;
78
+ if (allMessages.length <= keepRecent) {
79
+ return {
80
+ summaryMessages: [],
81
+ compactedRange: { start: 0, end: 0 },
82
+ tokensSaved: 0,
83
+ };
84
+ }
85
+ const toDelete = allMessages.slice(0, -keepRecent);
86
+ const messagesDir = this.getMessagesDir(contextId);
87
+ for (const message of toDelete) {
88
+ const filename = this.getMessageFilename(message.timestamp, message.index);
89
+ const filePath = join(messagesDir, filename);
90
+ await rm(filePath, { force: true });
91
+ }
92
+ const tokensFreed = toDelete.reduce((sum, m) => sum + (m.tokens || this.estimateTokens(m)), 0);
93
+ return {
94
+ summaryMessages: [],
95
+ compactedRange: {
96
+ start: toDelete[0]?.index || 0,
97
+ end: toDelete[toDelete.length - 1]?.index || 0,
98
+ },
99
+ tokensSaved: tokensFreed,
100
+ };
101
+ }
102
+ async clear(contextId) {
103
+ try {
104
+ const messagesDir = this.getMessagesDir(contextId);
105
+ await rm(messagesDir, { recursive: true, force: true });
106
+ }
107
+ catch {
108
+ }
109
+ }
110
+ getMessagesDir(contextId) {
111
+ const safeAgentId = this.sanitizeName(this.agentId);
112
+ const safeContextId = this.sanitizeName(contextId);
113
+ return join(this.basePath, `agent=${safeAgentId}`, `context=${safeContextId}`, 'messages');
114
+ }
115
+ getMessageFilename(timestamp, index) {
116
+ const safeTimestamp = timestamp.replace(/:/g, '-').replace(/\./g, '_');
117
+ return `${safeTimestamp}-${String(index).padStart(3, '0')}-${String(this.messageIndex++).padStart(6, '0')}.json`;
118
+ }
119
+ async loadMessages(contextId) {
120
+ try {
121
+ const messagesDir = this.getMessagesDir(contextId);
122
+ const files = await readdir(messagesDir);
123
+ const messages = [];
124
+ for (const file of files) {
125
+ if (!file.endsWith('.json'))
126
+ continue;
127
+ const filePath = join(messagesDir, file);
128
+ const content = await readFile(filePath, 'utf-8');
129
+ const message = JSON.parse(content);
130
+ messages.push(message);
131
+ }
132
+ return messages.sort((a, b) => a.index - b.index);
133
+ }
134
+ catch {
135
+ return [];
136
+ }
137
+ }
138
+ toMessage(stored) {
139
+ const { id: _id, contextId: _contextId, index: _index, timestamp: _timestamp, tokens: _tokens, tags: _tags, compacted: _compacted, summarizedRange: _summarizedRange, ...message } = stored;
140
+ return message;
141
+ }
142
+ estimateTokens(message) {
143
+ const content = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
144
+ return Math.ceil(content.length / 4);
145
+ }
146
+ sanitizeName(name) {
147
+ return name.replace(/[^a-zA-Z0-9_-]/g, '_');
148
+ }
149
+ }
@@ -0,0 +1,27 @@
1
+ import type { PersistedLoopState, TaskStateStore } from '../../types/state';
2
+ export interface FileSystemStateStoreConfig {
3
+ basePath?: string;
4
+ defaultTTL?: number;
5
+ }
6
+ export declare class FileSystemStateStore implements TaskStateStore {
7
+ private basePath;
8
+ private defaultTTL;
9
+ constructor(config?: FileSystemStateStoreConfig);
10
+ save(taskId: string, state: PersistedLoopState): Promise<void>;
11
+ load(taskId: string): Promise<PersistedLoopState | null>;
12
+ exists(taskId: string): Promise<boolean>;
13
+ delete(taskId: string): Promise<void>;
14
+ listTasks(filter?: {
15
+ agentId?: string;
16
+ contextId?: string;
17
+ completedAfter?: Date;
18
+ }): Promise<string[]>;
19
+ private getTaskIdsFromDir;
20
+ private processStateFile;
21
+ setTTL(taskId: string, ttlSeconds: number): Promise<void>;
22
+ private getStateFilePath;
23
+ private findStateFile;
24
+ private getAgentDirectories;
25
+ private getContextDirectories;
26
+ private sanitizeName;
27
+ }
@@ -0,0 +1,220 @@
1
+ import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ export class FileSystemStateStore {
4
+ basePath;
5
+ defaultTTL;
6
+ constructor(config = {}) {
7
+ this.basePath = config.basePath || './_agent_store';
8
+ this.defaultTTL = config.defaultTTL || 24 * 60 * 60;
9
+ }
10
+ async save(taskId, state) {
11
+ const filePath = this.getStateFilePath(state.agentId, state.contextId, taskId);
12
+ await mkdir(join(filePath, '..'), { recursive: true });
13
+ const stateWithMeta = {
14
+ ...state,
15
+ _metadata: {
16
+ savedAt: new Date().toISOString(),
17
+ expiresAt: new Date(Date.now() + this.defaultTTL * 1000).toISOString(),
18
+ },
19
+ };
20
+ await writeFile(filePath, JSON.stringify(stateWithMeta, null, 2), 'utf-8');
21
+ }
22
+ async load(taskId) {
23
+ try {
24
+ const filePath = await this.findStateFile(taskId);
25
+ if (!filePath) {
26
+ return null;
27
+ }
28
+ const content = await readFile(filePath, 'utf-8');
29
+ const stateWithMeta = JSON.parse(content);
30
+ if (stateWithMeta._metadata?.expiresAt) {
31
+ const expiresAt = new Date(stateWithMeta._metadata.expiresAt);
32
+ if (expiresAt < new Date()) {
33
+ await rm(filePath);
34
+ return null;
35
+ }
36
+ }
37
+ const { _metadata, ...state } = stateWithMeta;
38
+ return state;
39
+ }
40
+ catch (error) {
41
+ if (error.code === 'ENOENT') {
42
+ return null;
43
+ }
44
+ throw error;
45
+ }
46
+ }
47
+ async exists(taskId) {
48
+ const filePath = await this.findStateFile(taskId);
49
+ if (!filePath) {
50
+ return false;
51
+ }
52
+ try {
53
+ const content = await readFile(filePath, 'utf-8');
54
+ const stateWithMeta = JSON.parse(content);
55
+ if (stateWithMeta._metadata?.expiresAt) {
56
+ const expiresAt = new Date(stateWithMeta._metadata.expiresAt);
57
+ if (expiresAt < new Date()) {
58
+ await rm(filePath);
59
+ return false;
60
+ }
61
+ }
62
+ return true;
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ }
68
+ async delete(taskId) {
69
+ const filePath = await this.findStateFile(taskId);
70
+ if (filePath) {
71
+ await rm(filePath);
72
+ }
73
+ }
74
+ async listTasks(filter) {
75
+ const taskIds = [];
76
+ try {
77
+ const agentDirs = await this.getAgentDirectories(filter?.agentId);
78
+ for (const agentDir of agentDirs) {
79
+ const contextDirs = await this.getContextDirectories(agentDir, filter?.contextId);
80
+ for (const contextDir of contextDirs) {
81
+ const dirTaskIds = await this.getTaskIdsFromDir(contextDir, filter);
82
+ taskIds.push(...dirTaskIds);
83
+ }
84
+ }
85
+ }
86
+ catch {
87
+ }
88
+ return taskIds;
89
+ }
90
+ async getTaskIdsFromDir(contextDir, filter) {
91
+ const taskIds = [];
92
+ const taskDir = join(contextDir, 'task');
93
+ try {
94
+ const files = await readdir(taskDir);
95
+ for (const file of files) {
96
+ if (!file.endsWith('.json'))
97
+ continue;
98
+ const taskId = await this.processStateFile(join(taskDir, file), filter);
99
+ if (taskId) {
100
+ taskIds.push(taskId);
101
+ }
102
+ }
103
+ }
104
+ catch {
105
+ }
106
+ return taskIds;
107
+ }
108
+ async processStateFile(filePath, filter) {
109
+ try {
110
+ const content = await readFile(filePath, 'utf-8');
111
+ const stateWithMeta = JSON.parse(content);
112
+ if (stateWithMeta._metadata?.expiresAt) {
113
+ const expiresAt = new Date(stateWithMeta._metadata.expiresAt);
114
+ if (expiresAt < new Date()) {
115
+ await rm(filePath);
116
+ return null;
117
+ }
118
+ }
119
+ if (filter?.completedAfter && stateWithMeta.completed) {
120
+ const lastActivity = new Date(stateWithMeta.lastActivity);
121
+ if (lastActivity < filter.completedAfter) {
122
+ return null;
123
+ }
124
+ }
125
+ return stateWithMeta.taskId;
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ }
131
+ async setTTL(taskId, ttlSeconds) {
132
+ const filePath = await this.findStateFile(taskId);
133
+ if (!filePath) {
134
+ return;
135
+ }
136
+ const content = await readFile(filePath, 'utf-8');
137
+ const stateWithMeta = JSON.parse(content);
138
+ stateWithMeta._metadata = {
139
+ ...stateWithMeta._metadata,
140
+ expiresAt: new Date(Date.now() + ttlSeconds * 1000).toISOString(),
141
+ };
142
+ await writeFile(filePath, JSON.stringify(stateWithMeta, null, 2), 'utf-8');
143
+ }
144
+ getStateFilePath(agentId, contextId, taskId) {
145
+ const safeAgentId = this.sanitizeName(agentId);
146
+ const safeContextId = this.sanitizeName(contextId);
147
+ const safeTaskId = this.sanitizeName(taskId);
148
+ return join(this.basePath, `agent=${safeAgentId}`, `context=${safeContextId}`, 'task', `${safeTaskId}.json`);
149
+ }
150
+ async findStateFile(taskId) {
151
+ try {
152
+ const agentDirs = await this.getAgentDirectories();
153
+ for (const agentDir of agentDirs) {
154
+ const contextDirs = await this.getContextDirectories(agentDir);
155
+ for (const contextDir of contextDirs) {
156
+ const safeTaskId = this.sanitizeName(taskId);
157
+ const filePath = join(contextDir, 'task', `${safeTaskId}.json`);
158
+ try {
159
+ await stat(filePath);
160
+ return filePath;
161
+ }
162
+ catch {
163
+ }
164
+ }
165
+ }
166
+ }
167
+ catch {
168
+ }
169
+ return null;
170
+ }
171
+ async getAgentDirectories(agentId) {
172
+ try {
173
+ const entries = await readdir(this.basePath);
174
+ const agentDirs = [];
175
+ for (const entry of entries) {
176
+ if (entry.startsWith('agent=')) {
177
+ if (agentId) {
178
+ const safeAgentId = this.sanitizeName(agentId);
179
+ if (entry === `agent=${safeAgentId}`) {
180
+ agentDirs.push(join(this.basePath, entry));
181
+ }
182
+ }
183
+ else {
184
+ agentDirs.push(join(this.basePath, entry));
185
+ }
186
+ }
187
+ }
188
+ return agentDirs;
189
+ }
190
+ catch {
191
+ return [];
192
+ }
193
+ }
194
+ async getContextDirectories(agentDir, contextId) {
195
+ try {
196
+ const entries = await readdir(agentDir);
197
+ const contextDirs = [];
198
+ for (const entry of entries) {
199
+ if (entry.startsWith('context=')) {
200
+ if (contextId) {
201
+ const safeContextId = this.sanitizeName(contextId);
202
+ if (entry === `context=${safeContextId}`) {
203
+ contextDirs.push(join(agentDir, entry));
204
+ }
205
+ }
206
+ else {
207
+ contextDirs.push(join(agentDir, entry));
208
+ }
209
+ }
210
+ }
211
+ return contextDirs;
212
+ }
213
+ catch {
214
+ return [];
215
+ }
216
+ }
217
+ sanitizeName(name) {
218
+ return name.replace(/[^a-zA-Z0-9_-]/g, '_');
219
+ }
220
+ }
@@ -0,0 +1,10 @@
1
+ export type { FileSystemAgentStoreConfig } from './filesystem-agent-store';
2
+ export { FileSystemAgentStore } from './filesystem-agent-store';
3
+ export type { FileSystemArtifactStoreConfig } from './filesystem-artifact-store';
4
+ export { FileSystemArtifactStore } from './filesystem-artifact-store';
5
+ export type { FileSystemContextStoreConfig } from './filesystem-context-store';
6
+ export { FileSystemContextStore } from './filesystem-context-store';
7
+ export type { FileSystemMessageStoreConfig } from './filesystem-message-store';
8
+ export { FileSystemMessageStore } from './filesystem-message-store';
9
+ export type { FileSystemStateStoreConfig } from './filesystem-task-state-store';
10
+ export { FileSystemStateStore } from './filesystem-task-state-store';
@@ -0,0 +1,5 @@
1
+ export { FileSystemAgentStore } from './filesystem-agent-store';
2
+ export { FileSystemArtifactStore } from './filesystem-artifact-store';
3
+ export { FileSystemContextStore } from './filesystem-context-store';
4
+ export { FileSystemMessageStore } from './filesystem-message-store';
5
+ export { FileSystemStateStore } from './filesystem-task-state-store';
@@ -0,0 +1,5 @@
1
+ export * from './artifacts';
2
+ export * from './filesystem';
3
+ export * from './filesystem';
4
+ export * from './memory/memory-state-store';
5
+ export * from './messages';
@@ -0,0 +1,5 @@
1
+ export * from './artifacts';
2
+ export * from './filesystem';
3
+ export * from './filesystem';
4
+ export * from './memory/memory-state-store';
5
+ export * from './messages';
@@ -0,0 +1,15 @@
1
+ import type { PersistedLoopState, TaskStateStore } from '../../types/state';
2
+ export declare class InMemoryStateStore implements TaskStateStore {
3
+ private states;
4
+ save(taskId: string, state: PersistedLoopState): Promise<void>;
5
+ load(taskId: string): Promise<PersistedLoopState | null>;
6
+ exists(taskId: string): Promise<boolean>;
7
+ delete(taskId: string): Promise<void>;
8
+ listTasks(filter?: {
9
+ agentId?: string;
10
+ contextId?: string;
11
+ completedAfter?: Date;
12
+ }): Promise<string[]>;
13
+ setTTL(taskId: string, ttlSeconds: number): Promise<void>;
14
+ private cleanup;
15
+ }
@@ -0,0 +1,55 @@
1
+ export class InMemoryStateStore {
2
+ states = new Map();
3
+ async save(taskId, state) {
4
+ this.states.set(taskId, {
5
+ state: JSON.parse(JSON.stringify(state)),
6
+ expiresAt: Date.now() + 24 * 60 * 60 * 1000,
7
+ });
8
+ }
9
+ async load(taskId) {
10
+ this.cleanup();
11
+ const entry = this.states.get(taskId);
12
+ return entry ? JSON.parse(JSON.stringify(entry.state)) : null;
13
+ }
14
+ async exists(taskId) {
15
+ this.cleanup();
16
+ return this.states.has(taskId);
17
+ }
18
+ async delete(taskId) {
19
+ this.states.delete(taskId);
20
+ }
21
+ async listTasks(filter) {
22
+ this.cleanup();
23
+ const taskIds = [];
24
+ for (const [taskId, entry] of this.states.entries()) {
25
+ const state = entry.state;
26
+ if (filter) {
27
+ if (filter.agentId && state.agentId !== filter.agentId)
28
+ continue;
29
+ if (filter.contextId && state.contextId !== filter.contextId)
30
+ continue;
31
+ if (filter.completedAfter) {
32
+ const lastActivity = new Date(state.lastActivity);
33
+ if (lastActivity <= filter.completedAfter)
34
+ continue;
35
+ }
36
+ }
37
+ taskIds.push(taskId);
38
+ }
39
+ return taskIds;
40
+ }
41
+ async setTTL(taskId, ttlSeconds) {
42
+ const entry = this.states.get(taskId);
43
+ if (entry) {
44
+ entry.expiresAt = Date.now() + ttlSeconds * 1000;
45
+ }
46
+ }
47
+ cleanup() {
48
+ const now = Date.now();
49
+ for (const [taskId, entry] of this.states.entries()) {
50
+ if (entry.expiresAt < now) {
51
+ this.states.delete(taskId);
52
+ }
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,29 @@
1
+ import type { Message } from '../../types/message';
2
+ import type { CompactionOptions, CompactionResult, MessageStore } from './interfaces';
3
+ export type SyncStrategy = 'immediate' | 'batch' | 'end-of-session';
4
+ export interface HybridConfig {
5
+ messageStore: MessageStore;
6
+ memoryStore: MessageStore;
7
+ syncStrategy?: SyncStrategy;
8
+ includeMemoryContext?: boolean;
9
+ }
10
+ export declare class HybridMessageStore implements MessageStore {
11
+ private config;
12
+ constructor(config: HybridConfig);
13
+ append(contextId: string, messages: Message[]): Promise<void>;
14
+ getRecent(contextId: string, options?: {
15
+ maxMessages?: number;
16
+ maxTokens?: number;
17
+ }): Promise<Message[]>;
18
+ getAll(contextId: string): Promise<Message[]>;
19
+ getCount(contextId: string): Promise<number>;
20
+ getRange(contextId: string, startIndex: number, endIndex: number): Promise<Message[]>;
21
+ compact(contextId: string, options?: CompactionOptions): Promise<CompactionResult>;
22
+ clear(contextId: string): Promise<void>;
23
+ syncToMemory(contextId: string): Promise<void>;
24
+ getMemoryContext(contextId: string): Promise<Message[]>;
25
+ getRawMessages(contextId: string, options?: {
26
+ maxMessages?: number;
27
+ maxTokens?: number;
28
+ }): Promise<Message[]>;
29
+ }
@@ -0,0 +1,72 @@
1
+ export class HybridMessageStore {
2
+ config;
3
+ constructor(config) {
4
+ this.config = {
5
+ messageStore: config.messageStore,
6
+ memoryStore: config.memoryStore,
7
+ syncStrategy: config.syncStrategy || 'immediate',
8
+ includeMemoryContext: config.includeMemoryContext ?? true,
9
+ };
10
+ }
11
+ async append(contextId, messages) {
12
+ await this.config.messageStore.append(contextId, messages);
13
+ if (this.config.syncStrategy === 'immediate') {
14
+ await this.config.memoryStore.append(contextId, messages);
15
+ }
16
+ }
17
+ async getRecent(contextId, options) {
18
+ const messages = await this.config.messageStore.getRecent(contextId, options);
19
+ if (!this.config.includeMemoryContext) {
20
+ return messages;
21
+ }
22
+ try {
23
+ const memories = await this.config.memoryStore.getRecent(contextId, {
24
+ maxMessages: 5,
25
+ });
26
+ return [...memories, ...messages];
27
+ }
28
+ catch (error) {
29
+ console.error('Failed to get memory context:', error);
30
+ return messages;
31
+ }
32
+ }
33
+ async getAll(contextId) {
34
+ return this.config.messageStore.getAll(contextId);
35
+ }
36
+ async getCount(contextId) {
37
+ return this.config.messageStore.getCount(contextId);
38
+ }
39
+ async getRange(contextId, startIndex, endIndex) {
40
+ return this.config.messageStore.getRange(contextId, startIndex, endIndex);
41
+ }
42
+ async compact(contextId, options) {
43
+ const localResult = await this.config.messageStore.compact(contextId, options);
44
+ try {
45
+ await this.config.memoryStore.compact(contextId, options);
46
+ }
47
+ catch (error) {
48
+ console.error('Failed to compact memory store:', error);
49
+ }
50
+ return localResult;
51
+ }
52
+ async clear(contextId) {
53
+ await Promise.all([
54
+ this.config.messageStore.clear(contextId),
55
+ this.config.memoryStore.clear(contextId),
56
+ ]);
57
+ }
58
+ async syncToMemory(contextId) {
59
+ const messages = await this.config.messageStore.getAll(contextId);
60
+ const memoryCount = await this.config.memoryStore.getCount(contextId);
61
+ if (messages.length > memoryCount) {
62
+ const newMessages = messages.slice(memoryCount);
63
+ await this.config.memoryStore.append(contextId, newMessages);
64
+ }
65
+ }
66
+ async getMemoryContext(contextId) {
67
+ return this.config.memoryStore.getRecent(contextId, { maxMessages: 10 });
68
+ }
69
+ async getRawMessages(contextId, options) {
70
+ return this.config.messageStore.getRecent(contextId, options);
71
+ }
72
+ }
@@ -0,0 +1,4 @@
1
+ export * from './hybrid-message-store';
2
+ export * from './interfaces';
3
+ export * from './mem0-message-store';
4
+ export * from './memory-message-store';
@@ -0,0 +1,4 @@
1
+ export * from './hybrid-message-store';
2
+ export * from './interfaces';
3
+ export * from './mem0-message-store';
4
+ export * from './memory-message-store';
@@ -0,0 +1,42 @@
1
+ import type { Message } from '../../types/message';
2
+ export interface StoredMessage extends Message {
3
+ id: string;
4
+ contextId: string;
5
+ index: number;
6
+ timestamp: string;
7
+ tokens?: number;
8
+ tags?: string[];
9
+ compacted?: boolean;
10
+ summarizedRange?: {
11
+ start: number;
12
+ end: number;
13
+ };
14
+ }
15
+ export interface MessageStore {
16
+ append(contextId: string, messages: Message[]): Promise<void>;
17
+ getRecent(contextId: string, options?: {
18
+ maxMessages?: number;
19
+ maxTokens?: number;
20
+ }): Promise<Message[]>;
21
+ getAll(contextId: string): Promise<Message[]>;
22
+ getCount(contextId: string): Promise<number>;
23
+ getRange(contextId: string, startIndex: number, endIndex: number): Promise<Message[]>;
24
+ compact(contextId: string, options?: CompactionOptions): Promise<CompactionResult>;
25
+ clear(contextId: string): Promise<void>;
26
+ }
27
+ export interface CompactionOptions {
28
+ keepRecent?: number;
29
+ targetTokens?: number;
30
+ strategy?: 'sliding-window' | 'summarization' | 'hierarchical';
31
+ summaryPrompt?: string;
32
+ }
33
+ export interface CompactionResult {
34
+ summaryMessages: Message[];
35
+ compactedRange: {
36
+ start: number;
37
+ end: number;
38
+ };
39
+ tokensSaved: number;
40
+ }
41
+ export declare function estimateTokens(content: string | unknown): number;
42
+ export declare function trimToTokenBudget<T extends Message>(messages: T[], maxTokens: number): T[];
@@ -0,0 +1,18 @@
1
+ export function estimateTokens(content) {
2
+ const text = typeof content === 'string' ? content : JSON.stringify(content);
3
+ return Math.ceil(text.length / 4);
4
+ }
5
+ export function trimToTokenBudget(messages, maxTokens) {
6
+ let total = 0;
7
+ const result = [];
8
+ for (let i = messages.length - 1; i >= 0; i--) {
9
+ const msg = messages[i];
10
+ const tokens = estimateTokens(msg.content);
11
+ if (total + tokens > maxTokens) {
12
+ break;
13
+ }
14
+ result.unshift(msg);
15
+ total += tokens;
16
+ }
17
+ return result;
18
+ }