js-agent-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 (168) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +29 -0
  3. package/dist/core/AgentDashboard.d.ts +33 -0
  4. package/dist/core/AgentDashboard.d.ts.map +1 -0
  5. package/dist/core/AgentDashboard.js +477 -0
  6. package/dist/core/AgentDashboard.js.map +1 -0
  7. package/dist/core/AutoVectorStore.d.ts +23 -0
  8. package/dist/core/AutoVectorStore.d.ts.map +1 -0
  9. package/dist/core/AutoVectorStore.js +55 -0
  10. package/dist/core/AutoVectorStore.js.map +1 -0
  11. package/dist/core/BaseAgent.d.ts +70 -0
  12. package/dist/core/BaseAgent.d.ts.map +1 -0
  13. package/dist/core/BaseAgent.js +583 -0
  14. package/dist/core/BaseAgent.js.map +1 -0
  15. package/dist/core/EventEmitter.d.ts +8 -0
  16. package/dist/core/EventEmitter.d.ts.map +1 -0
  17. package/dist/core/EventEmitter.js +32 -0
  18. package/dist/core/EventEmitter.js.map +1 -0
  19. package/dist/core/LocalEmbedder.d.ts +25 -0
  20. package/dist/core/LocalEmbedder.d.ts.map +1 -0
  21. package/dist/core/LocalEmbedder.js +62 -0
  22. package/dist/core/LocalEmbedder.js.map +1 -0
  23. package/dist/core/LongTermMemory.d.ts +30 -0
  24. package/dist/core/LongTermMemory.d.ts.map +1 -0
  25. package/dist/core/LongTermMemory.js +123 -0
  26. package/dist/core/LongTermMemory.js.map +1 -0
  27. package/dist/core/MemoryVectorStore.d.ts +17 -0
  28. package/dist/core/MemoryVectorStore.d.ts.map +1 -0
  29. package/dist/core/MemoryVectorStore.js +44 -0
  30. package/dist/core/MemoryVectorStore.js.map +1 -0
  31. package/dist/core/OpenAIProvider.d.ts +21 -0
  32. package/dist/core/OpenAIProvider.d.ts.map +1 -0
  33. package/dist/core/OpenAIProvider.js +254 -0
  34. package/dist/core/OpenAIProvider.js.map +1 -0
  35. package/dist/core/SimpleMemory.d.ts +12 -0
  36. package/dist/core/SimpleMemory.d.ts.map +1 -0
  37. package/dist/core/SimpleMemory.js +27 -0
  38. package/dist/core/SimpleMemory.js.map +1 -0
  39. package/dist/core/StructuredPlanner.d.ts +13 -0
  40. package/dist/core/StructuredPlanner.d.ts.map +1 -0
  41. package/dist/core/StructuredPlanner.js +156 -0
  42. package/dist/core/StructuredPlanner.js.map +1 -0
  43. package/dist/core/ToolRegistry.d.ts +18 -0
  44. package/dist/core/ToolRegistry.d.ts.map +1 -0
  45. package/dist/core/ToolRegistry.js +74 -0
  46. package/dist/core/ToolRegistry.js.map +1 -0
  47. package/dist/core/index.d.ts +14 -0
  48. package/dist/core/index.d.ts.map +1 -0
  49. package/dist/core/index.js +14 -0
  50. package/dist/core/index.js.map +1 -0
  51. package/dist/core/logging/CompositeLogger.d.ts +8 -0
  52. package/dist/core/logging/CompositeLogger.d.ts.map +1 -0
  53. package/dist/core/logging/CompositeLogger.js +23 -0
  54. package/dist/core/logging/CompositeLogger.js.map +1 -0
  55. package/dist/core/logging/NodeFsLogger.d.ts +12 -0
  56. package/dist/core/logging/NodeFsLogger.d.ts.map +1 -0
  57. package/dist/core/logging/NodeFsLogger.js +46 -0
  58. package/dist/core/logging/NodeFsLogger.js.map +1 -0
  59. package/dist/core/logging/WebIndexedDbLogger.d.ts +15 -0
  60. package/dist/core/logging/WebIndexedDbLogger.d.ts.map +1 -0
  61. package/dist/core/logging/WebIndexedDbLogger.js +65 -0
  62. package/dist/core/logging/WebIndexedDbLogger.js.map +1 -0
  63. package/dist/core/logging/index.d.ts +11 -0
  64. package/dist/core/logging/index.d.ts.map +1 -0
  65. package/dist/core/logging/index.js +33 -0
  66. package/dist/core/logging/index.js.map +1 -0
  67. package/dist/core/persistence/NodeFsSkillStore.d.ts +17 -0
  68. package/dist/core/persistence/NodeFsSkillStore.d.ts.map +1 -0
  69. package/dist/core/persistence/NodeFsSkillStore.js +124 -0
  70. package/dist/core/persistence/NodeFsSkillStore.js.map +1 -0
  71. package/dist/core/persistence/NodeFsVectorStore.d.ts +25 -0
  72. package/dist/core/persistence/NodeFsVectorStore.d.ts.map +1 -0
  73. package/dist/core/persistence/NodeFsVectorStore.js +74 -0
  74. package/dist/core/persistence/NodeFsVectorStore.js.map +1 -0
  75. package/dist/core/persistence/SkillLoader.d.ts +26 -0
  76. package/dist/core/persistence/SkillLoader.d.ts.map +1 -0
  77. package/dist/core/persistence/SkillLoader.js +144 -0
  78. package/dist/core/persistence/SkillLoader.js.map +1 -0
  79. package/dist/core/persistence/WebIndexedDbSkillStore.d.ts +21 -0
  80. package/dist/core/persistence/WebIndexedDbSkillStore.d.ts.map +1 -0
  81. package/dist/core/persistence/WebIndexedDbSkillStore.js +119 -0
  82. package/dist/core/persistence/WebIndexedDbSkillStore.js.map +1 -0
  83. package/dist/core/persistence/WebIndexedDbVectorStore.d.ts +30 -0
  84. package/dist/core/persistence/WebIndexedDbVectorStore.d.ts.map +1 -0
  85. package/dist/core/persistence/WebIndexedDbVectorStore.js +87 -0
  86. package/dist/core/persistence/WebIndexedDbVectorStore.js.map +1 -0
  87. package/dist/core/persistence/index.d.ts +6 -0
  88. package/dist/core/persistence/index.d.ts.map +1 -0
  89. package/dist/core/persistence/index.js +21 -0
  90. package/dist/core/persistence/index.js.map +1 -0
  91. package/dist/index.d.ts +3 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +3 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/interfaces/IAgentEvent.d.ts +27 -0
  96. package/dist/interfaces/IAgentEvent.d.ts.map +1 -0
  97. package/dist/interfaces/IAgentEvent.js +2 -0
  98. package/dist/interfaces/IAgentEvent.js.map +1 -0
  99. package/dist/interfaces/IAgentState.d.ts +22 -0
  100. package/dist/interfaces/IAgentState.d.ts.map +1 -0
  101. package/dist/interfaces/IAgentState.js +2 -0
  102. package/dist/interfaces/IAgentState.js.map +1 -0
  103. package/dist/interfaces/ILLMProvider.d.ts +43 -0
  104. package/dist/interfaces/ILLMProvider.d.ts.map +1 -0
  105. package/dist/interfaces/ILLMProvider.js +2 -0
  106. package/dist/interfaces/ILLMProvider.js.map +1 -0
  107. package/dist/interfaces/ILogger.d.ts +15 -0
  108. package/dist/interfaces/ILogger.d.ts.map +1 -0
  109. package/dist/interfaces/ILogger.js +2 -0
  110. package/dist/interfaces/ILogger.js.map +1 -0
  111. package/dist/interfaces/IMemory.d.ts +32 -0
  112. package/dist/interfaces/IMemory.d.ts.map +1 -0
  113. package/dist/interfaces/IMemory.js +2 -0
  114. package/dist/interfaces/IMemory.js.map +1 -0
  115. package/dist/interfaces/IPlanner.d.ts +20 -0
  116. package/dist/interfaces/IPlanner.d.ts.map +1 -0
  117. package/dist/interfaces/IPlanner.js +2 -0
  118. package/dist/interfaces/IPlanner.js.map +1 -0
  119. package/dist/interfaces/ISkill.d.ts +9 -0
  120. package/dist/interfaces/ISkill.d.ts.map +1 -0
  121. package/dist/interfaces/ISkill.js +2 -0
  122. package/dist/interfaces/ISkill.js.map +1 -0
  123. package/dist/interfaces/ISkillStore.d.ts +53 -0
  124. package/dist/interfaces/ISkillStore.d.ts.map +1 -0
  125. package/dist/interfaces/ISkillStore.js +2 -0
  126. package/dist/interfaces/ISkillStore.js.map +1 -0
  127. package/dist/interfaces/ITool.d.ts +32 -0
  128. package/dist/interfaces/ITool.d.ts.map +1 -0
  129. package/dist/interfaces/ITool.js +2 -0
  130. package/dist/interfaces/ITool.js.map +1 -0
  131. package/dist/interfaces/index.d.ts +10 -0
  132. package/dist/interfaces/index.d.ts.map +1 -0
  133. package/dist/interfaces/index.js +10 -0
  134. package/dist/interfaces/index.js.map +1 -0
  135. package/package.json +47 -0
  136. package/src/core/AgentDashboard.ts +533 -0
  137. package/src/core/AutoVectorStore.ts +60 -0
  138. package/src/core/BaseAgent.ts +676 -0
  139. package/src/core/EventEmitter.ts +35 -0
  140. package/src/core/LocalEmbedder.ts +68 -0
  141. package/src/core/LongTermMemory.ts +146 -0
  142. package/src/core/MemoryVectorStore.ts +54 -0
  143. package/src/core/OpenAIProvider.ts +274 -0
  144. package/src/core/SimpleMemory.ts +31 -0
  145. package/src/core/StructuredPlanner.ts +165 -0
  146. package/src/core/ToolRegistry.ts +89 -0
  147. package/src/core/index.ts +16 -0
  148. package/src/core/logging/CompositeLogger.ts +26 -0
  149. package/src/core/logging/NodeFsLogger.ts +53 -0
  150. package/src/core/logging/WebIndexedDbLogger.ts +76 -0
  151. package/src/core/logging/index.ts +35 -0
  152. package/src/core/persistence/NodeFsSkillStore.ts +138 -0
  153. package/src/core/persistence/NodeFsVectorStore.ts +86 -0
  154. package/src/core/persistence/SkillLoader.ts +153 -0
  155. package/src/core/persistence/WebIndexedDbSkillStore.ts +139 -0
  156. package/src/core/persistence/WebIndexedDbVectorStore.ts +106 -0
  157. package/src/core/persistence/index.ts +22 -0
  158. package/src/index.ts +2 -0
  159. package/src/interfaces/IAgentEvent.ts +46 -0
  160. package/src/interfaces/IAgentState.ts +22 -0
  161. package/src/interfaces/ILLMProvider.ts +47 -0
  162. package/src/interfaces/ILogger.ts +16 -0
  163. package/src/interfaces/IMemory.ts +29 -0
  164. package/src/interfaces/IPlanner.ts +22 -0
  165. package/src/interfaces/ISkill.ts +9 -0
  166. package/src/interfaces/ISkillStore.ts +60 -0
  167. package/src/interfaces/ITool.ts +38 -0
  168. package/src/interfaces/index.ts +10 -0
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "js-agent-core",
3
+ "version": "1.0.1",
4
+ "description": "A powerful, isomorphic JS Agent core library for building AI agents with TypeScript",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE",
12
+ "src"
13
+ ],
14
+ "scripts": {
15
+ "build": "npm run test:all && tsc",
16
+ "test": "vitest run tests/unit",
17
+ "test:unit": "vitest run tests/unit",
18
+ "test:integration": "vitest run tests/integration",
19
+ "test:all": "npm run test:unit && npm run test:integration",
20
+ "test:watch": "vitest",
21
+ "test:coverage": "vitest run --coverage",
22
+ "bundle": "node scripts/bundle.js"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git@gitee.com:lin_zhi_xiong/js-agent.git"
27
+ },
28
+ "keywords": [
29
+ "agent",
30
+ "llm",
31
+ "ai",
32
+ "typescript"
33
+ ],
34
+ "author": "",
35
+ "license": "ISC",
36
+ "dependencies": {
37
+ "zod": "^3.25.76",
38
+ "zod-to-json-schema": "^3.25.1"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^18.19.1",
42
+ "@vitest/coverage-v8": "^3.0.5",
43
+ "jsdom": "^24.1.1",
44
+ "typescript": "^5.9.3",
45
+ "vitest": "^3.0.5"
46
+ }
47
+ }
@@ -0,0 +1,533 @@
1
+ import { BaseAgent } from './BaseAgent.js';
2
+
3
+ export interface DashboardOptions {
4
+ container?: HTMLElement;
5
+ autoOpen?: boolean;
6
+ }
7
+
8
+ export class AgentDashboard {
9
+ private agent: BaseAgent;
10
+ private container: HTMLElement;
11
+ private isVisible: boolean = false;
12
+
13
+ constructor(agent: BaseAgent, options: DashboardOptions = {}) {
14
+ this.agent = agent;
15
+ this.container = options.container || this.createDefaultContainer();
16
+ this.isVisible = options.autoOpen || false;
17
+
18
+ if (this.isVisible) {
19
+ this.show();
20
+ }
21
+
22
+ this.initUI();
23
+ this.bindEvents();
24
+ }
25
+
26
+ private createDefaultContainer(): HTMLElement {
27
+ const div = document.createElement('div');
28
+ div.id = 'agent-dashboard';
29
+ div.style.cssText = `
30
+ width: 350px;
31
+ height: 100vh;
32
+ background-color: #1e1e1e;
33
+ color: #d4d4d4;
34
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
35
+ box-shadow: -2px 0 10px rgba(0,0,0,0.5);
36
+ display: none;
37
+ flex-direction: column;
38
+ z-index: 9999;
39
+ overflow: hidden;
40
+ `;
41
+
42
+ // Try to find a specific container for the dashboard, otherwise append to body
43
+ const wrapper = document.getElementById('dashboard-wrapper');
44
+ if (wrapper) {
45
+ div.style.width = '100%';
46
+ div.style.height = '100%';
47
+ div.style.boxShadow = 'none';
48
+ div.style.display = 'flex'; // Always show when embedded
49
+ wrapper.appendChild(div);
50
+ } else if (document.body) {
51
+ div.style.position = 'fixed';
52
+ div.style.bottom = '0';
53
+ div.style.right = '0';
54
+ document.body.appendChild(div);
55
+ } else if (document.readyState === 'loading') {
56
+ window.addEventListener('DOMContentLoaded', () => {
57
+ div.style.position = 'fixed';
58
+ div.style.bottom = '0';
59
+ div.style.right = '0';
60
+ document.body.appendChild(div);
61
+ });
62
+ } else {
63
+ // Should not happen in most browser environments but just in case
64
+ setTimeout(() => {
65
+ div.style.position = 'fixed';
66
+ div.style.bottom = '0';
67
+ div.style.right = '0';
68
+ document.body.appendChild(div);
69
+ }, 0);
70
+ }
71
+ return div;
72
+ }
73
+
74
+ private initUI(): void {
75
+ this.container.innerHTML = `
76
+ <div style="padding: 12px; background: #333; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #444;">
77
+ <span style="font-weight: bold; font-size: 14px;">JS-Agent Dashboard</span>
78
+ <button id="close-dashboard" style="background: transparent; border: none; color: #999; cursor: pointer; font-size: 18px;">&times;</button>
79
+ </div>
80
+ <div id="dashboard-content" style="flex: 1; overflow-y: auto; padding: 15px; font-size: 13px;">
81
+ <div id="status-indicator" style="margin-bottom: 15px; padding: 8px; border-radius: 4px; background: #252526; border-left: 4px solid #007acc;">
82
+ Status: <span id="agent-status">Ready</span>
83
+ </div>
84
+ <div id="log-container" style="margin-bottom: 15px;">
85
+ <div id="log-history" style="max-height: 150px; overflow-y: auto; margin-bottom: 10px; border-top: 1px solid #333; padding-top: 5px; display: none;"></div>
86
+ </div>
87
+ <div id="thought-log" style="margin-bottom: 15px;">
88
+ <div style="color: #569cd6; margin-bottom: 5px; font-weight: bold;">Thinking...</div>
89
+ <div id="thought-content" style="background: #252526; padding: 10px; border-radius: 4px; line-height: 1.5; min-height: 40px; border: 1px solid #333;"></div>
90
+ </div>
91
+ <div id="task-container" style="margin-bottom: 15px; position: relative;">
92
+ <div style="color: #ce9178; margin-bottom: 5px; font-weight: bold;">Task Graph</div>
93
+ <div id="task-graph-container" style="background: #252526; border-radius: 4px; border: 1px solid #333; height: 200px; position: relative; overflow: auto; margin-bottom: 10px;">
94
+ <svg id="task-graph-svg" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"></svg>
95
+ <div id="task-list" style="position: relative; width: 100%; height: 100%;"></div>
96
+ </div>
97
+ </div>
98
+ <div id="memory-container" style="margin-bottom: 15px;">
99
+ <div style="color: #b5cea8; margin-bottom: 5px; font-weight: bold;">Memory Status</div>
100
+ <div id="memory-usage" style="background: #252526; padding: 10px; border-radius: 4px; border: 1px solid #333; font-size: 11px;">
101
+ <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
102
+ <span>Context Window (Tokens)</span>
103
+ <span id="token-usage">0 / 128k</span>
104
+ </div>
105
+ <div style="background: #1a1a1a; height: 6px; border-radius: 3px; overflow: hidden; margin-bottom: 10px;">
106
+ <div id="token-bar" style="width: 0%; height: 100%; background: #4caf50; transition: width 0.3s ease;"></div>
107
+ </div>
108
+ <div id="memory-logs" style="max-height: 100px; overflow-y: auto; color: #888;">
109
+ <div style="font-style: italic;">No memory entries yet...</div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ `;
115
+
116
+ this.container.querySelector('#close-dashboard')?.addEventListener('click', () => this.hide());
117
+ }
118
+
119
+ private bindEvents(): void {
120
+ this.agent.on('start', async (event) => {
121
+ const input = event.payload;
122
+ this.show();
123
+ this.updateStatus('Running');
124
+ this.clearThoughts();
125
+ this.clearTasks();
126
+ this.addLog('System', `Agent started with input: "${input}"`);
127
+
128
+ // Sync memory state
129
+ const usage = await this.agent.getMemoryUsage();
130
+ this.updateMemoryUsage(usage.used, usage.total);
131
+
132
+ const history = await this.agent.getHistory();
133
+ const logs = this.container.querySelector('#memory-logs') as HTMLElement;
134
+ if (logs) logs.innerHTML = '';
135
+ history.forEach(m => this.addMemoryEntry(m));
136
+ });
137
+
138
+ this.agent.on('think', (event) => {
139
+ const content = event.payload;
140
+ console.log('Dashboard: Think event received', content);
141
+ this.updateThought(content);
142
+ });
143
+
144
+ this.agent.on('task.plan', (event) => {
145
+ const plan = event.payload;
146
+ this.renderTasks(plan.tasks);
147
+ this.addLog('Planner', `New plan generated with ${plan.tasks.length} tasks.`);
148
+ });
149
+
150
+ this.agent.on('task.update', (event) => {
151
+ const task = event.payload;
152
+ this.updateTaskUI(task);
153
+ this.addLog('Task', `Task "${task.goal}" updated to ${task.status}`);
154
+ });
155
+
156
+ this.agent.on('tool.call', (event) => {
157
+ const payload = event.payload;
158
+ this.addLog('Tool', `Calling ${payload.name}...`);
159
+ this.highlightActiveTool(payload.name);
160
+ });
161
+
162
+ this.agent.on('tool.progress', (event) => {
163
+ const payload = event.payload;
164
+ this.updateToolProgress(payload.name, payload.percent, payload.message);
165
+ });
166
+
167
+ this.agent.on('tool.result', (event) => {
168
+ const result = event.payload;
169
+ const status = result.error ? 'failed' : 'success';
170
+ this.addLog('Tool', `Tool result: ${status}`);
171
+ // Find the tool name from the result if possible, or we might need to track current active tool
172
+ this.clearActiveToolHighlights();
173
+ });
174
+
175
+ this.agent.on('agent.done', (result) => {
176
+ console.log('Dashboard: Done event received', result);
177
+ this.updateStatus('Ready');
178
+ this.addLog('System', `Agent finished: ${result}`);
179
+ });
180
+
181
+ this.agent.on('agent.error', (error) => {
182
+ console.error('Dashboard: Error event received', error);
183
+ this.updateStatus('Error');
184
+ this.addLog('Error', error);
185
+ });
186
+
187
+ this.agent.on('memory.add', (payload) => {
188
+ this.addMemoryEntry(payload);
189
+ });
190
+
191
+ this.agent.on('memory.usage', (usage) => {
192
+ this.updateMemoryUsage(usage.used, usage.total);
193
+ });
194
+ }
195
+
196
+ private addMemoryEntry(payload: any): void {
197
+ const logs = this.container.querySelector('#memory-logs') as HTMLElement;
198
+ // Check if the first child is the "No memory entries yet" message
199
+ if (logs) {
200
+ if (logs.querySelector('div')?.style.fontStyle === 'italic') {
201
+ logs.innerHTML = '';
202
+ }
203
+
204
+ const entry = document.createElement('div');
205
+ entry.style.cssText = 'border-bottom: 1px solid #333; padding: 6px; margin-bottom: 4px; background: #2d2d2d; border-radius: 4px;';
206
+
207
+ const header = document.createElement('div');
208
+ header.style.cssText = 'display: flex; justify-content: space-between; margin-bottom: 4px; font-size: 11px;';
209
+
210
+ const roleBadge = document.createElement('span');
211
+ roleBadge.className = 'role-badge';
212
+ const roleColors: Record<string, string> = {
213
+ 'user': '#2196f3',
214
+ 'assistant': '#4caf50',
215
+ 'system': '#ff9800',
216
+ 'tool': '#9c27b0'
217
+ };
218
+ const bg = roleColors[payload.role] || '#757575';
219
+ roleBadge.style.cssText = `background: ${bg}; color: white; padding: 2px 6px; border-radius: 10px; font-weight: bold;`;
220
+ roleBadge.textContent = payload.role.toUpperCase();
221
+
222
+ const metaInfo = document.createElement('span');
223
+ metaInfo.style.color = '#888';
224
+ if (payload.name) {
225
+ metaInfo.textContent = payload.name;
226
+ }
227
+
228
+ header.appendChild(roleBadge);
229
+ header.appendChild(metaInfo);
230
+
231
+ const content = document.createElement('div');
232
+ content.style.cssText = 'font-size: 12px; color: #ccc; white-space: pre-wrap; word-break: break-word; max-height: 100px; overflow-y: auto;';
233
+
234
+ // Simple truncation for very long content
235
+ const text = payload.content || '';
236
+ content.textContent = text.length > 500 ? text.substring(0, 500) + '...' : text;
237
+
238
+ entry.appendChild(header);
239
+ entry.appendChild(content);
240
+
241
+ logs.prepend(entry);
242
+ }
243
+ }
244
+
245
+ private updateMemoryUsage(used: number, total: number): void {
246
+ const usageText = this.container.querySelector('#token-usage') as HTMLElement;
247
+ const usageBar = this.container.querySelector('#token-bar') as HTMLElement;
248
+ if (usageText) {
249
+ const usedText = used < 1000 ? `${used}` : `${(used / 1000).toFixed(1)}k`;
250
+ const totalText = total < 1000 ? `${total}` : `${(total / 1000).toFixed(0)}k`;
251
+ usageText.textContent = `${usedText} / ${totalText}`;
252
+ }
253
+ if (usageBar) {
254
+ const percent = Math.min((used / total) * 100, 100);
255
+ usageBar.style.width = `${percent}%`;
256
+ usageBar.style.background = percent > 80 ? '#f44336' : (percent > 50 ? '#ff9800' : '#4caf50');
257
+ }
258
+ }
259
+
260
+ private updateTaskUI(task: any): void {
261
+ const taskEl = this.container.querySelector(`[data-task-id="${task.id}"]`) as HTMLElement;
262
+ if (taskEl) {
263
+ taskEl.style.borderLeftColor = this.getTaskStatusColor(task.status);
264
+ }
265
+ }
266
+
267
+ private addLog(label: string, message: string, color: string = '#888'): void {
268
+ const historyEl = this.container.querySelector('#log-history') as HTMLElement;
269
+ if (historyEl) {
270
+ historyEl.style.display = 'block';
271
+ const logEntry = document.createElement('div');
272
+ logEntry.className = 'log-entry';
273
+ logEntry.style.cssText = `margin-top: 5px; border-bottom: 1px solid #333; padding-bottom: 2px;`;
274
+ logEntry.innerHTML = `
275
+ <span style="color: ${this.getLabelColor(label)}; font-weight: bold; font-size: 11px;">[${label}]</span>
276
+ <span style="color: #ccc; font-size: 11px;">${message}</span>
277
+ `;
278
+ historyEl.appendChild(logEntry);
279
+ historyEl.scrollTop = historyEl.scrollHeight;
280
+ }
281
+ }
282
+
283
+ private getLabelColor(label: string): string {
284
+ switch (label) {
285
+ case 'System': return '#569cd6';
286
+ case 'Planner': return '#ce9178';
287
+ case 'Task': return '#b5cea8';
288
+ case 'Tool': return '#dcdcdc';
289
+ case 'Error': return '#f44336';
290
+ default: return '#888';
291
+ }
292
+ }
293
+
294
+ private updateStatus(status: string, color: string = '#007acc'): void {
295
+ const el = this.container.querySelector('#agent-status');
296
+ const indicator = this.container.querySelector('#status-indicator') as HTMLElement;
297
+ if (el) el.textContent = status;
298
+ if (indicator) indicator.style.borderLeftColor = color;
299
+ }
300
+
301
+ private highlightActiveTool(toolName: string): void {
302
+ const taskNodes = this.container.querySelectorAll('.task-node');
303
+ taskNodes.forEach(node => {
304
+ const toolText = (node as HTMLElement).querySelector('div:nth-child(2)')?.textContent;
305
+ if (toolText === toolName) {
306
+ (node as HTMLElement).style.boxShadow = '0 0 10px #2196f3';
307
+ (node as HTMLElement).style.borderColor = '#2196f3';
308
+ const progressContainer = (node as HTMLElement).querySelector('.progress-container') as HTMLElement;
309
+ if (progressContainer) progressContainer.style.display = 'block';
310
+ }
311
+ });
312
+ }
313
+
314
+ private updateToolProgress(toolName: string, percent: number, message?: string): void {
315
+ const taskNodes = this.container.querySelectorAll('.task-node');
316
+ taskNodes.forEach(node => {
317
+ const toolText = (node as HTMLElement).querySelector('div:nth-child(2)')?.textContent;
318
+ if (toolText === toolName) {
319
+ const progressBar = (node as HTMLElement).querySelector('.progress-bar') as HTMLElement;
320
+ if (progressBar) {
321
+ progressBar.style.width = `${percent}%`;
322
+ }
323
+ if (message) {
324
+ this.addLog('Tool', `${toolName}: ${message}`);
325
+ }
326
+ }
327
+ });
328
+ }
329
+
330
+ private clearActiveToolHighlights(): void {
331
+ const taskNodes = this.container.querySelectorAll('.task-node');
332
+ taskNodes.forEach(node => {
333
+ (node as HTMLElement).style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)';
334
+ const taskId = (node as HTMLElement).getAttribute('data-task-id');
335
+ // Restore border color based on task status
336
+ const task = (this.agent as any).taskList?.tasks?.find((t: any) => t.id === taskId);
337
+ if (task) {
338
+ (node as HTMLElement).style.borderLeftColor = this.getTaskStatusColor(task.status);
339
+ }
340
+ const progressContainer = (node as HTMLElement).querySelector('.progress-container') as HTMLElement;
341
+ if (progressContainer) progressContainer.style.display = 'none';
342
+ });
343
+ }
344
+
345
+ private updateThought(content: string): void {
346
+ const el = this.container.querySelector('#thought-content');
347
+ if (el) {
348
+ // Append content to support history and multiple steps
349
+ const entry = document.createElement('div');
350
+ entry.style.cssText = 'border-bottom: 1px solid #333; padding: 5px 0;';
351
+ entry.textContent = content;
352
+ el.appendChild(entry);
353
+
354
+ // Auto-scroll to bottom
355
+ const contentArea = this.container.querySelector('#dashboard-content');
356
+ if (contentArea) {
357
+ contentArea.scrollTop = contentArea.scrollHeight;
358
+ }
359
+ }
360
+ }
361
+
362
+ private clearThoughts(): void {
363
+ const el = this.container.querySelector('#thought-content');
364
+ if (el) el.textContent = '';
365
+ }
366
+
367
+ private renderTasks(tasks: any[]): void {
368
+ const list = this.container.querySelector('#task-list') as HTMLElement;
369
+ const svg = this.container.querySelector('#task-graph-svg') as unknown as SVGSVGElement;
370
+ if (!list || !svg) return;
371
+
372
+ list.innerHTML = '';
373
+ svg.innerHTML = '';
374
+
375
+ // Simple layout: arrange tasks in columns based on dependency level
376
+ const levels = this.calculateTaskLevels(tasks);
377
+ const maxLevel = Math.max(...Object.values(levels), 0);
378
+ const columnWidth = 150;
379
+ const rowHeight = 60;
380
+ const padding = 20;
381
+
382
+ // Set container size based on graph
383
+ const graphContainer = this.container.querySelector('#task-graph-container') as HTMLElement;
384
+ if (graphContainer) {
385
+ const width = (maxLevel + 1) * columnWidth + padding * 2;
386
+ const tasksPerLevel = Object.values(levels).reduce((acc: any, level) => {
387
+ acc[level] = (acc[level] || 0) + 1;
388
+ return acc;
389
+ }, {});
390
+ const maxTasksInLevel = Math.max(...Object.values(tasksPerLevel) as number[], 0);
391
+ const height = maxTasksInLevel * rowHeight + padding * 2;
392
+
393
+ list.style.width = `${Math.max(width, graphContainer.clientWidth)}px`;
394
+ list.style.height = `${Math.max(height, 200)}px`;
395
+ svg.style.width = list.style.width;
396
+ svg.style.height = list.style.height;
397
+ }
398
+
399
+ const positions = new Map<string, { x: number, y: number }>();
400
+ const levelCounts = new Map<number, number>();
401
+
402
+ tasks.forEach(task => {
403
+ const level = levels[task.id] || 0;
404
+ const count = levelCounts.get(level) || 0;
405
+
406
+ const x = padding + level * columnWidth;
407
+ const y = padding + count * rowHeight;
408
+ positions.set(task.id, { x, y });
409
+ levelCounts.set(level, count + 1);
410
+
411
+ const taskEl = document.createElement('div');
412
+ taskEl.className = 'task-node';
413
+ taskEl.setAttribute('data-task-id', task.id);
414
+ taskEl.style.cssText = `
415
+ position: absolute;
416
+ left: ${x}px;
417
+ top: ${y}px;
418
+ width: 120px;
419
+ padding: 8px;
420
+ background: #2d2d2d;
421
+ border-radius: 4px;
422
+ border-left: 3px solid ${this.getTaskStatusColor(task.status)};
423
+ font-size: 11px;
424
+ transition: all 0.3s ease;
425
+ z-index: 2;
426
+ box-shadow: 0 2px 4px rgba(0,0,0,0.3);
427
+ `;
428
+ taskEl.innerHTML = `
429
+ <div style="font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${task.goal}">${task.goal}</div>
430
+ <div style="color: #888; font-size: 10px;">${task.tool || 'no tool'}</div>
431
+ <div class="progress-container" style="margin-top: 5px; background: #1a1a1a; height: 4px; border-radius: 2px; overflow: hidden; display: none;">
432
+ <div class="progress-bar" style="width: 0%; height: 100%; background: #2196f3; transition: width 0.3s ease;"></div>
433
+ </div>
434
+ `;
435
+ list.appendChild(taskEl);
436
+ });
437
+
438
+ // Draw dependency lines
439
+ tasks.forEach(task => {
440
+ if (task.dependsOn && Array.isArray(task.dependsOn)) {
441
+ task.dependsOn.forEach((depId: string) => {
442
+ const startPos = positions.get(depId);
443
+ const endPos = positions.get(task.id);
444
+ if (startPos && endPos) {
445
+ this.drawArrow(svg, startPos.x + 120, startPos.y + 20, endPos.x, endPos.y + 20);
446
+ }
447
+ });
448
+ }
449
+ });
450
+ }
451
+
452
+ private calculateTaskLevels(tasks: any[]): Record<string, number> {
453
+ const levels: Record<string, number> = {};
454
+ const taskMap = new Map(tasks.map(t => [t.id, t]));
455
+
456
+ const getLevel = (id: string, visited = new Set<string>()): number => {
457
+ if (levels[id] !== undefined) return levels[id];
458
+ if (visited.has(id)) return 0; // Circular dependency fallback
459
+
460
+ visited.add(id);
461
+ const task = taskMap.get(id);
462
+ if (!task || !task.dependsOn || task.dependsOn.length === 0) {
463
+ levels[id] = 0;
464
+ return 0;
465
+ }
466
+
467
+ const depLevels = task.dependsOn.map((depId: string) => getLevel(depId, visited));
468
+ levels[id] = Math.max(...depLevels) + 1;
469
+ return levels[id];
470
+ };
471
+
472
+ tasks.forEach(t => getLevel(t.id));
473
+ return levels;
474
+ }
475
+
476
+ private drawArrow(svg: SVGSVGElement, x1: number, y1: number, x2: number, y2: number): void {
477
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
478
+ const midX = (x1 + x2) / 2;
479
+ // Cubic bezier for smooth curves
480
+ const d = `M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}`;
481
+ path.setAttribute('d', d);
482
+ path.setAttribute('stroke', '#555');
483
+ path.setAttribute('stroke-width', '1.5');
484
+ path.setAttribute('fill', 'none');
485
+
486
+ // Arrow head
487
+ const markerId = 'arrowhead';
488
+ if (!svg.querySelector(`#${markerId}`)) {
489
+ const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
490
+ const marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
491
+ marker.setAttribute('id', markerId);
492
+ marker.setAttribute('viewBox', '0 0 10 10');
493
+ marker.setAttribute('refX', '8');
494
+ marker.setAttribute('refY', '5');
495
+ marker.setAttribute('markerWidth', '6');
496
+ marker.setAttribute('markerHeight', '6');
497
+ marker.setAttribute('orient', 'auto-start-reverse');
498
+ const arrowPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
499
+ arrowPath.setAttribute('d', 'M 0 0 L 10 5 L 0 10 z');
500
+ arrowPath.setAttribute('fill', '#555');
501
+ marker.appendChild(arrowPath);
502
+ defs.appendChild(marker);
503
+ svg.appendChild(defs);
504
+ }
505
+
506
+ path.setAttribute('marker-end', `url(#${markerId})`);
507
+ svg.appendChild(path);
508
+ }
509
+
510
+ private getTaskStatusColor(status: string): string {
511
+ switch (status) {
512
+ case 'completed': return '#4caf50';
513
+ case 'failed': return '#f44336';
514
+ case 'in_progress': return '#2196f3';
515
+ default: return '#666';
516
+ }
517
+ }
518
+
519
+ private clearTasks(): void {
520
+ const list = this.container.querySelector('#task-list');
521
+ if (list) list.innerHTML = '';
522
+ }
523
+
524
+ public show(): void {
525
+ this.container.style.display = 'flex';
526
+ this.isVisible = true;
527
+ }
528
+
529
+ public hide(): void {
530
+ this.container.style.display = 'none';
531
+ this.isVisible = false;
532
+ }
533
+ }
@@ -0,0 +1,60 @@
1
+ import { IVectorStore } from '../interfaces/index.js';
2
+ import { MemoryVectorStore } from './MemoryVectorStore.js';
3
+
4
+ /**
5
+ * A proxy that dynamically loads the appropriate VectorStore implementation
6
+ * based on the runtime environment (Node.js vs Browser).
7
+ */
8
+ export class AutoVectorStore implements IVectorStore {
9
+ private instance: IVectorStore | null = null;
10
+ private options: any;
11
+
12
+ constructor(options: any = {}) {
13
+ this.options = options;
14
+ }
15
+
16
+ private async getInstance(): Promise<IVectorStore> {
17
+ if (this.instance) return this.instance;
18
+
19
+ const isNode = typeof process !== 'undefined' && process.versions?.node;
20
+ const isBrowser = typeof window !== 'undefined' && typeof indexedDB !== 'undefined';
21
+
22
+ try {
23
+ if (isNode) {
24
+ // Node.js: Dynamic import NodeFsVectorStore
25
+ const { NodeFsVectorStore } = await import('./persistence/NodeFsVectorStore.js');
26
+ this.instance = new NodeFsVectorStore(this.options);
27
+ } else if (isBrowser) {
28
+ // Browser: Use WebIndexedDbVectorStore (already exported from persistence/index.js)
29
+ const { WebIndexedDbVectorStore } = await import('./persistence/WebIndexedDbVectorStore.js');
30
+ this.instance = new WebIndexedDbVectorStore(this.options);
31
+ } else {
32
+ // Fallback: In-memory store
33
+ this.instance = new MemoryVectorStore();
34
+ }
35
+ } catch (error) {
36
+ console.warn('Failed to load environment-specific VectorStore, falling back to MemoryVectorStore', error);
37
+ this.instance = new MemoryVectorStore();
38
+ }
39
+
40
+ return this.instance!;
41
+ }
42
+
43
+ async add(text: string, embedding: number[], metadata?: Record<string, any>): Promise<void> {
44
+ const instance = await this.getInstance();
45
+ return instance.add(text, embedding, metadata);
46
+ }
47
+
48
+ async search(queryEmbedding: number[], topK?: number): Promise<Array<{ text: string; score: number; metadata?: Record<string, any> }>> {
49
+ const instance = await this.getInstance();
50
+ return instance.search(queryEmbedding, topK);
51
+ }
52
+
53
+ async searchByKeyword(keyword: string, topK?: number): Promise<Array<{ text: string; score: number; metadata?: Record<string, any> }>> {
54
+ const instance = await this.getInstance();
55
+ if (instance.searchByKeyword) {
56
+ return instance.searchByKeyword(keyword, topK);
57
+ }
58
+ return [];
59
+ }
60
+ }