@aigne/core 1.71.0 → 1.72.0-beta.10

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 (192) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/lib/cjs/agents/agent.d.ts +42 -29
  3. package/lib/cjs/agents/agent.js +32 -11
  4. package/lib/cjs/agents/ai-agent.d.ts +63 -4
  5. package/lib/cjs/agents/ai-agent.js +148 -20
  6. package/lib/cjs/agents/chat-model.d.ts +162 -0
  7. package/lib/cjs/agents/chat-model.js +56 -5
  8. package/lib/cjs/agents/image-agent.d.ts +17 -1
  9. package/lib/cjs/agents/image-agent.js +16 -0
  10. package/lib/cjs/agents/image-model.d.ts +17 -2
  11. package/lib/cjs/agents/image-model.js +2 -0
  12. package/lib/cjs/agents/mcp-agent.d.ts +17 -0
  13. package/lib/cjs/agents/mcp-agent.js +18 -0
  14. package/lib/cjs/agents/team-agent.d.ts +55 -0
  15. package/lib/cjs/agents/team-agent.js +31 -0
  16. package/lib/cjs/agents/transform-agent.d.ts +12 -0
  17. package/lib/cjs/agents/transform-agent.js +13 -0
  18. package/lib/cjs/agents/video-model.d.ts +15 -0
  19. package/lib/cjs/agents/video-model.js +2 -0
  20. package/lib/cjs/aigne/usage.d.ts +5 -0
  21. package/lib/cjs/aigne/usage.js +6 -0
  22. package/lib/cjs/index.d.ts +1 -0
  23. package/lib/cjs/index.js +1 -0
  24. package/lib/cjs/loader/agent-yaml.d.ts +27 -64
  25. package/lib/cjs/loader/agent-yaml.js +22 -129
  26. package/lib/cjs/loader/agents.d.ts +4 -0
  27. package/lib/cjs/loader/agents.js +17 -0
  28. package/lib/cjs/loader/index.d.ts +16 -12
  29. package/lib/cjs/loader/index.js +46 -82
  30. package/lib/cjs/loader/schema.d.ts +21 -6
  31. package/lib/cjs/loader/schema.js +60 -1
  32. package/lib/cjs/memory/recorder.d.ts +4 -4
  33. package/lib/cjs/memory/retriever.d.ts +4 -4
  34. package/lib/cjs/prompt/agent-session.d.ts +135 -0
  35. package/lib/cjs/prompt/agent-session.js +889 -0
  36. package/lib/cjs/prompt/compact/compactor.d.ts +7 -0
  37. package/lib/cjs/prompt/compact/compactor.js +48 -0
  38. package/lib/cjs/prompt/compact/session-memory-extractor.d.ts +7 -0
  39. package/lib/cjs/prompt/compact/session-memory-extractor.js +139 -0
  40. package/lib/cjs/prompt/compact/types.d.ts +329 -0
  41. package/lib/cjs/prompt/compact/types.js +53 -0
  42. package/lib/cjs/prompt/compact/user-memory-extractor.d.ts +7 -0
  43. package/lib/cjs/prompt/compact/user-memory-extractor.js +120 -0
  44. package/lib/cjs/prompt/context/afs/history.d.ts +9 -0
  45. package/lib/cjs/prompt/context/afs/history.js +33 -0
  46. package/lib/cjs/prompt/context/afs/index.d.ts +20 -0
  47. package/lib/cjs/prompt/context/afs/index.js +54 -0
  48. package/lib/cjs/prompt/context/index.d.ts +31 -0
  49. package/lib/cjs/prompt/context/index.js +18 -0
  50. package/lib/cjs/prompt/prompt-builder.d.ts +11 -9
  51. package/lib/cjs/prompt/prompt-builder.js +81 -151
  52. package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.d.ts +18 -0
  53. package/lib/cjs/prompt/skills/afs/agent-skill/agent-skill.js +69 -0
  54. package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.d.ts +13 -0
  55. package/lib/cjs/prompt/skills/afs/agent-skill/skill-loader.js +62 -0
  56. package/lib/cjs/prompt/skills/afs/base.d.ts +4 -0
  57. package/lib/cjs/prompt/skills/afs/base.js +8 -0
  58. package/lib/cjs/prompt/skills/afs/delete.d.ts +3 -2
  59. package/lib/cjs/prompt/skills/afs/delete.js +17 -5
  60. package/lib/cjs/prompt/skills/afs/edit.d.ts +9 -11
  61. package/lib/cjs/prompt/skills/afs/edit.js +89 -63
  62. package/lib/cjs/prompt/skills/afs/exec.d.ts +4 -3
  63. package/lib/cjs/prompt/skills/afs/exec.js +23 -7
  64. package/lib/cjs/prompt/skills/afs/index.js +4 -1
  65. package/lib/cjs/prompt/skills/afs/list.d.ts +4 -4
  66. package/lib/cjs/prompt/skills/afs/list.js +38 -55
  67. package/lib/cjs/prompt/skills/afs/read.d.ts +10 -5
  68. package/lib/cjs/prompt/skills/afs/read.js +66 -19
  69. package/lib/cjs/prompt/skills/afs/rename.d.ts +3 -2
  70. package/lib/cjs/prompt/skills/afs/rename.js +20 -6
  71. package/lib/cjs/prompt/skills/afs/search.d.ts +4 -3
  72. package/lib/cjs/prompt/skills/afs/search.js +24 -8
  73. package/lib/cjs/prompt/skills/afs/write.d.ts +3 -2
  74. package/lib/cjs/prompt/skills/afs/write.js +22 -8
  75. package/lib/cjs/prompt/template.d.ts +84 -9
  76. package/lib/cjs/prompt/template.js +46 -17
  77. package/lib/dts/agents/agent.d.ts +42 -29
  78. package/lib/dts/agents/ai-agent.d.ts +63 -4
  79. package/lib/dts/agents/chat-model.d.ts +162 -0
  80. package/lib/dts/agents/image-agent.d.ts +17 -1
  81. package/lib/dts/agents/image-model.d.ts +17 -2
  82. package/lib/dts/agents/mcp-agent.d.ts +17 -0
  83. package/lib/dts/agents/team-agent.d.ts +55 -0
  84. package/lib/dts/agents/transform-agent.d.ts +12 -0
  85. package/lib/dts/agents/video-model.d.ts +15 -0
  86. package/lib/dts/aigne/context.d.ts +2 -2
  87. package/lib/dts/aigne/usage.d.ts +5 -0
  88. package/lib/dts/index.d.ts +1 -0
  89. package/lib/dts/loader/agent-yaml.d.ts +27 -64
  90. package/lib/dts/loader/agents.d.ts +4 -0
  91. package/lib/dts/loader/index.d.ts +16 -12
  92. package/lib/dts/loader/schema.d.ts +21 -6
  93. package/lib/dts/memory/recorder.d.ts +4 -4
  94. package/lib/dts/memory/retriever.d.ts +4 -4
  95. package/lib/dts/prompt/agent-session.d.ts +135 -0
  96. package/lib/dts/prompt/compact/compactor.d.ts +7 -0
  97. package/lib/dts/prompt/compact/session-memory-extractor.d.ts +7 -0
  98. package/lib/dts/prompt/compact/types.d.ts +329 -0
  99. package/lib/dts/prompt/compact/user-memory-extractor.d.ts +7 -0
  100. package/lib/dts/prompt/context/afs/history.d.ts +9 -0
  101. package/lib/dts/prompt/context/afs/index.d.ts +20 -0
  102. package/lib/dts/prompt/context/index.d.ts +31 -0
  103. package/lib/dts/prompt/prompt-builder.d.ts +11 -9
  104. package/lib/dts/prompt/skills/afs/agent-skill/agent-skill.d.ts +18 -0
  105. package/lib/dts/prompt/skills/afs/agent-skill/skill-loader.d.ts +13 -0
  106. package/lib/dts/prompt/skills/afs/base.d.ts +4 -0
  107. package/lib/dts/prompt/skills/afs/delete.d.ts +3 -2
  108. package/lib/dts/prompt/skills/afs/edit.d.ts +9 -11
  109. package/lib/dts/prompt/skills/afs/exec.d.ts +4 -3
  110. package/lib/dts/prompt/skills/afs/list.d.ts +4 -4
  111. package/lib/dts/prompt/skills/afs/read.d.ts +10 -5
  112. package/lib/dts/prompt/skills/afs/rename.d.ts +3 -2
  113. package/lib/dts/prompt/skills/afs/search.d.ts +4 -3
  114. package/lib/dts/prompt/skills/afs/write.d.ts +3 -2
  115. package/lib/dts/prompt/template.d.ts +84 -9
  116. package/lib/esm/agents/agent.d.ts +42 -29
  117. package/lib/esm/agents/agent.js +32 -11
  118. package/lib/esm/agents/ai-agent.d.ts +63 -4
  119. package/lib/esm/agents/ai-agent.js +148 -20
  120. package/lib/esm/agents/chat-model.d.ts +162 -0
  121. package/lib/esm/agents/chat-model.js +55 -4
  122. package/lib/esm/agents/image-agent.d.ts +17 -1
  123. package/lib/esm/agents/image-agent.js +16 -0
  124. package/lib/esm/agents/image-model.d.ts +17 -2
  125. package/lib/esm/agents/image-model.js +2 -0
  126. package/lib/esm/agents/mcp-agent.d.ts +17 -0
  127. package/lib/esm/agents/mcp-agent.js +18 -0
  128. package/lib/esm/agents/team-agent.d.ts +55 -0
  129. package/lib/esm/agents/team-agent.js +31 -0
  130. package/lib/esm/agents/transform-agent.d.ts +12 -0
  131. package/lib/esm/agents/transform-agent.js +13 -0
  132. package/lib/esm/agents/video-model.d.ts +15 -0
  133. package/lib/esm/agents/video-model.js +2 -0
  134. package/lib/esm/aigne/context.d.ts +2 -2
  135. package/lib/esm/aigne/usage.d.ts +5 -0
  136. package/lib/esm/aigne/usage.js +6 -0
  137. package/lib/esm/index.d.ts +1 -0
  138. package/lib/esm/index.js +1 -0
  139. package/lib/esm/loader/agent-yaml.d.ts +27 -64
  140. package/lib/esm/loader/agent-yaml.js +22 -128
  141. package/lib/esm/loader/agents.d.ts +4 -0
  142. package/lib/esm/loader/agents.js +14 -0
  143. package/lib/esm/loader/index.d.ts +16 -12
  144. package/lib/esm/loader/index.js +47 -82
  145. package/lib/esm/loader/schema.d.ts +21 -6
  146. package/lib/esm/loader/schema.js +57 -0
  147. package/lib/esm/memory/recorder.d.ts +4 -4
  148. package/lib/esm/memory/retriever.d.ts +4 -4
  149. package/lib/esm/prompt/agent-session.d.ts +135 -0
  150. package/lib/esm/prompt/agent-session.js +849 -0
  151. package/lib/esm/prompt/compact/compactor.d.ts +7 -0
  152. package/lib/esm/prompt/compact/compactor.js +44 -0
  153. package/lib/esm/prompt/compact/session-memory-extractor.d.ts +7 -0
  154. package/lib/esm/prompt/compact/session-memory-extractor.js +135 -0
  155. package/lib/esm/prompt/compact/types.d.ts +329 -0
  156. package/lib/esm/prompt/compact/types.js +50 -0
  157. package/lib/esm/prompt/compact/user-memory-extractor.d.ts +7 -0
  158. package/lib/esm/prompt/compact/user-memory-extractor.js +116 -0
  159. package/lib/esm/prompt/context/afs/history.d.ts +9 -0
  160. package/lib/esm/prompt/context/afs/history.js +30 -0
  161. package/lib/esm/prompt/context/afs/index.d.ts +20 -0
  162. package/lib/esm/prompt/context/afs/index.js +51 -0
  163. package/lib/esm/prompt/context/index.d.ts +31 -0
  164. package/lib/esm/prompt/context/index.js +15 -0
  165. package/lib/esm/prompt/prompt-builder.d.ts +11 -9
  166. package/lib/esm/prompt/prompt-builder.js +80 -150
  167. package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.d.ts +18 -0
  168. package/lib/esm/prompt/skills/afs/agent-skill/agent-skill.js +65 -0
  169. package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.d.ts +13 -0
  170. package/lib/esm/prompt/skills/afs/agent-skill/skill-loader.js +54 -0
  171. package/lib/esm/prompt/skills/afs/base.d.ts +4 -0
  172. package/lib/esm/prompt/skills/afs/base.js +4 -0
  173. package/lib/esm/prompt/skills/afs/delete.d.ts +3 -2
  174. package/lib/esm/prompt/skills/afs/delete.js +17 -5
  175. package/lib/esm/prompt/skills/afs/edit.d.ts +9 -11
  176. package/lib/esm/prompt/skills/afs/edit.js +89 -63
  177. package/lib/esm/prompt/skills/afs/exec.d.ts +4 -3
  178. package/lib/esm/prompt/skills/afs/exec.js +23 -7
  179. package/lib/esm/prompt/skills/afs/index.js +4 -1
  180. package/lib/esm/prompt/skills/afs/list.d.ts +4 -4
  181. package/lib/esm/prompt/skills/afs/list.js +38 -55
  182. package/lib/esm/prompt/skills/afs/read.d.ts +10 -5
  183. package/lib/esm/prompt/skills/afs/read.js +66 -19
  184. package/lib/esm/prompt/skills/afs/rename.d.ts +3 -2
  185. package/lib/esm/prompt/skills/afs/rename.js +20 -6
  186. package/lib/esm/prompt/skills/afs/search.d.ts +4 -3
  187. package/lib/esm/prompt/skills/afs/search.js +24 -8
  188. package/lib/esm/prompt/skills/afs/write.d.ts +3 -2
  189. package/lib/esm/prompt/skills/afs/write.js +22 -8
  190. package/lib/esm/prompt/template.d.ts +84 -9
  191. package/lib/esm/prompt/template.js +46 -17
  192. package/package.json +6 -5
@@ -0,0 +1,889 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.AgentSession = void 0;
40
+ const afs_history_1 = require("@aigne/afs-history");
41
+ const uuid_1 = require("@aigne/uuid");
42
+ const ufo_1 = require("ufo");
43
+ const yaml_1 = require("yaml");
44
+ const token_estimator_js_1 = require("../utils/token-estimator.js");
45
+ const type_utils_js_1 = require("../utils/type-utils.js");
46
+ const types_js_1 = require("./compact/types.js");
47
+ __exportStar(require("./compact/types.js"), exports);
48
+ class AgentSession {
49
+ sessionId;
50
+ userId;
51
+ agentId;
52
+ afs;
53
+ historyModulePath;
54
+ mode;
55
+ compactConfig;
56
+ sessionMemoryConfig;
57
+ userMemoryConfig;
58
+ runtimeState;
59
+ initialized;
60
+ compactionPromise;
61
+ sessionMemoryUpdatePromise;
62
+ userMemoryUpdatePromise;
63
+ constructor(options) {
64
+ this.sessionId = options.sessionId;
65
+ this.userId = options.userId;
66
+ this.agentId = options.agentId;
67
+ this.afs = options.afs;
68
+ this.mode = options.mode ?? types_js_1.DEFAULT_SESSION_MODE;
69
+ this.compactConfig = options.compact ?? {};
70
+ this.sessionMemoryConfig = options.sessionMemory ?? {};
71
+ this.userMemoryConfig = options.userMemory ?? {};
72
+ this.runtimeState = {
73
+ historyEntries: [],
74
+ currentEntry: null,
75
+ };
76
+ }
77
+ /**
78
+ * Check if memory extraction is enabled
79
+ * Memory extraction requires mode to be "auto" AND AFS history module to be available
80
+ */
81
+ get isMemoryEnabled() {
82
+ return this.mode === "auto" && !!this.afs && !!this.historyModulePath;
83
+ }
84
+ async setSystemMessages(...messages) {
85
+ await this.ensureInitialized();
86
+ this.runtimeState.systemMessages = messages;
87
+ }
88
+ async getMessages() {
89
+ await this.ensureInitialized();
90
+ const { systemMessages, userMemory, sessionMemory, compactSummary, historyEntries, currentEntry, currentEntryCompression, } = this.runtimeState;
91
+ let currentMessages = [];
92
+ if (currentEntry?.messages?.length) {
93
+ if (currentEntryCompression) {
94
+ const { compressedCount, summary } = currentEntryCompression;
95
+ const firstMsg = currentEntry.messages[0];
96
+ const hasSkill = firstMsg?.role === "user" &&
97
+ Array.isArray(firstMsg.content) &&
98
+ firstMsg.content.some((block) => block.type === "text" && block.isAgentSkill === true);
99
+ const skillMessage = hasSkill ? [firstMsg] : [];
100
+ const summaryMessage = {
101
+ role: "user",
102
+ content: `[Earlier messages in this conversation (${compressedCount} messages compressed)]\n${summary}`,
103
+ };
104
+ const remainingMessages = currentEntry.messages.slice(compressedCount);
105
+ currentMessages = [...skillMessage, summaryMessage, ...remainingMessages];
106
+ }
107
+ else {
108
+ currentMessages = currentEntry.messages;
109
+ }
110
+ }
111
+ const messages = [
112
+ ...(systemMessages ?? []),
113
+ ...(userMemory && userMemory.length > 0 ? [this.formatUserMemory(userMemory)] : []),
114
+ ...(sessionMemory && sessionMemory.length > 0
115
+ ? [this.formatSessionMemory(sessionMemory)]
116
+ : []),
117
+ ...(compactSummary
118
+ ? [
119
+ {
120
+ role: "system",
121
+ content: `Previous conversation summary:\n${compactSummary}`,
122
+ },
123
+ ]
124
+ : []),
125
+ ...historyEntries.flatMap((entry) => entry.content?.messages ?? []),
126
+ ...currentMessages,
127
+ ];
128
+ // Filter out thinking messages and truncate large messages
129
+ return messages
130
+ .map((msg) => {
131
+ if (!msg.content || typeof msg.content === "string") {
132
+ return msg;
133
+ }
134
+ // Filter out thinking from UnionContent[]
135
+ const filteredContent = msg.content.filter((c) => !(c.type === "text" && c.isThinking));
136
+ if (filteredContent.length === 0)
137
+ return null;
138
+ return { ...msg, content: filteredContent };
139
+ })
140
+ .filter(type_utils_js_1.isNonNullable)
141
+ .map((msg) => this.truncateLargeMessage(msg));
142
+ }
143
+ /**
144
+ * Format user memory facts into a system message
145
+ * Applies token budget limit to ensure memory injection fits within constraints
146
+ */
147
+ formatUserMemory(memoryEntries) {
148
+ const memoryRatio = this.userMemoryConfig.memoryRatio ?? types_js_1.DEFAULT_MEMORY_RATIO;
149
+ const maxTokens = Math.floor((this.compactConfig.maxTokens ?? types_js_1.DEFAULT_MAX_TOKENS) * memoryRatio);
150
+ const header = "[User Memory Facts]";
151
+ let currentTokens = (0, token_estimator_js_1.estimateTokens)(header);
152
+ const facts = [];
153
+ for (const entry of memoryEntries) {
154
+ const fact = entry.content?.fact;
155
+ if (!fact)
156
+ continue;
157
+ const factTokens = (0, token_estimator_js_1.estimateTokens)(fact);
158
+ // Check if adding this fact would exceed token budget
159
+ if (currentTokens + factTokens > maxTokens) {
160
+ break; // Stop adding facts
161
+ }
162
+ facts.push(fact);
163
+ currentTokens += factTokens;
164
+ }
165
+ return {
166
+ role: "system",
167
+ content: this.formatMemoryTemplate({ header, data: facts }),
168
+ };
169
+ }
170
+ /**
171
+ * Format session memory facts into a system message
172
+ * Applies token budget limit to ensure memory injection fits within constraints
173
+ */
174
+ formatSessionMemory(memoryEntries) {
175
+ const memoryRatio = this.sessionMemoryConfig.memoryRatio ?? types_js_1.DEFAULT_MEMORY_RATIO;
176
+ const maxTokens = Math.floor((this.compactConfig.maxTokens ?? types_js_1.DEFAULT_MAX_TOKENS) * memoryRatio);
177
+ const header = "[Session Memory Facts]";
178
+ let currentTokens = (0, token_estimator_js_1.estimateTokens)(header);
179
+ const facts = [];
180
+ for (const entry of memoryEntries) {
181
+ const fact = entry.content?.fact;
182
+ if (!fact)
183
+ continue;
184
+ const factTokens = (0, token_estimator_js_1.estimateTokens)(fact);
185
+ // Check if adding this fact would exceed token budget
186
+ if (currentTokens + factTokens > maxTokens) {
187
+ break; // Stop adding facts
188
+ }
189
+ facts.push(fact);
190
+ currentTokens += factTokens;
191
+ }
192
+ return {
193
+ role: "system",
194
+ content: this.formatMemoryTemplate({ header, data: facts }),
195
+ };
196
+ }
197
+ formatMemoryTemplate({ header, data }) {
198
+ return `\
199
+ ${header}
200
+
201
+ ${"```yaml"}
202
+ ${(0, yaml_1.stringify)(data)}
203
+ ${"```"}
204
+ `;
205
+ }
206
+ async startMessage(input, message, options) {
207
+ await this.ensureInitialized();
208
+ // Only run compact if mode is not disabled
209
+ if (this.mode !== "disabled") {
210
+ await this.maybeAutoCompact(options);
211
+ // Always wait for compaction to complete before starting a new message
212
+ // This ensures data consistency even in async compact mode
213
+ if (this.compactionPromise)
214
+ await this.compactionPromise;
215
+ }
216
+ this.runtimeState.currentEntryCompression = undefined;
217
+ this.runtimeState.currentEntry = { input, messages: [message] };
218
+ }
219
+ async endMessage(output, message, options) {
220
+ await this.ensureInitialized();
221
+ if (!this.runtimeState.currentEntry?.input ||
222
+ !this.runtimeState.currentEntry.messages?.length) {
223
+ throw new Error("No current entry to end. Call startMessage() first.");
224
+ }
225
+ if (message)
226
+ this.runtimeState.currentEntry.messages.push(message);
227
+ this.runtimeState.currentEntry.output = output;
228
+ let newEntry;
229
+ // Only persist to AFS if mode is not disabled
230
+ if (this.mode !== "disabled" && this.afs && this.historyModulePath) {
231
+ newEntry = (await this.afs.write((0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "new"), {
232
+ userId: this.userId,
233
+ sessionId: this.sessionId,
234
+ agentId: this.agentId,
235
+ content: this.runtimeState.currentEntry,
236
+ })).data;
237
+ }
238
+ else {
239
+ // Create in-memory entry for runtime state
240
+ const id = (0, uuid_1.v7)();
241
+ newEntry = {
242
+ id,
243
+ path: `/history/${id}`,
244
+ userId: this.userId,
245
+ sessionId: this.sessionId,
246
+ agentId: this.agentId,
247
+ content: this.runtimeState.currentEntry,
248
+ };
249
+ }
250
+ this.runtimeState.historyEntries.push(newEntry);
251
+ this.runtimeState.currentEntry = null;
252
+ this.runtimeState.currentEntryCompression = undefined;
253
+ // Only run compact and memory extraction if mode is not disabled
254
+ if (this.mode !== "disabled") {
255
+ await Promise.all([
256
+ // Check if auto-compact should be triggered
257
+ this.maybeAutoCompact(options),
258
+ // Check if auto-update session memory should be triggered
259
+ this.maybeAutoUpdateSessionMemory(options),
260
+ ]);
261
+ }
262
+ }
263
+ /**
264
+ * Manually trigger compaction
265
+ */
266
+ async compact(options) {
267
+ await this.ensureInitialized();
268
+ // If compaction is already in progress, wait for it to complete
269
+ if (this.compactionPromise) {
270
+ return this.compactionPromise;
271
+ }
272
+ // Start new compaction task
273
+ this.compactionPromise = this.doCompact(options).finally(() => {
274
+ this.compactionPromise = undefined;
275
+ });
276
+ return this.compactionPromise;
277
+ }
278
+ /**
279
+ * Internal method that performs the actual compaction
280
+ */
281
+ async doCompact(options) {
282
+ const { compactor } = this.compactConfig ?? {};
283
+ if (!compactor) {
284
+ throw new Error("Cannot compact without a compactor agent configured.");
285
+ }
286
+ const historyEntries = this.runtimeState.historyEntries;
287
+ if (historyEntries.length === 0)
288
+ return;
289
+ const maxTokens = this.maxTokens;
290
+ let keepTokenBudget = this.keepTokenBudget;
291
+ // Calculate tokens for system messages
292
+ const systemTokens = (this.runtimeState.systemMessages ?? []).reduce((sum, msg) => {
293
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
294
+ return sum + (0, token_estimator_js_1.estimateTokens)(content);
295
+ }, 0);
296
+ // Calculate tokens for current entry messages
297
+ const currentTokens = (this.runtimeState.currentEntry?.messages ?? []).reduce((sum, msg) => {
298
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
299
+ return sum + (0, token_estimator_js_1.estimateTokens)(content);
300
+ }, 0);
301
+ // Subtract system and current tokens from budget
302
+ // This ensures total tokens (system + current + kept history) stays within ratio budget
303
+ keepTokenBudget = Math.max(0, keepTokenBudget - systemTokens - currentTokens);
304
+ // Find split point by iterating backwards from most recent entry
305
+ // The split point divides history into: [compact] | [keep]
306
+ let splitIndex = historyEntries.length; // Default: keep all (no compaction)
307
+ let accumulatedTokens = 0;
308
+ for (let i = historyEntries.length - 1; i >= 0; i--) {
309
+ const entry = historyEntries[i];
310
+ if (!entry)
311
+ continue;
312
+ const entryTokens = this.estimateMessagesTokens(entry.content?.messages ?? []);
313
+ // Check if adding this entry would exceed token budget
314
+ if (accumulatedTokens + entryTokens > keepTokenBudget) {
315
+ // Would exceed budget, split here (this entry and earlier ones will be compacted)
316
+ splitIndex = i + 1;
317
+ break;
318
+ }
319
+ // Can keep this entry, accumulate and continue
320
+ accumulatedTokens += entryTokens;
321
+ splitIndex = i;
322
+ }
323
+ // Split history at the found point
324
+ const entriesToCompact = historyEntries.slice(0, splitIndex);
325
+ const entriesToKeep = historyEntries.slice(splitIndex);
326
+ // If nothing to compact, return
327
+ if (entriesToCompact.length === 0) {
328
+ return;
329
+ }
330
+ const latestCompactedEntry = entriesToCompact.at(-1);
331
+ if (!latestCompactedEntry)
332
+ return;
333
+ // Split into batches to avoid context overflow
334
+ const batches = this.splitIntoBatches(entriesToCompact, maxTokens);
335
+ // Process batches incrementally, each summary becomes input for the next
336
+ let currentSummary = this.runtimeState.compactSummary;
337
+ for (const batch of batches) {
338
+ const messages = batch
339
+ .flatMap((e) => e.content?.messages ?? [])
340
+ .filter(type_utils_js_1.isNonNullable)
341
+ .map((msg) => this.truncateLargeMessage(msg));
342
+ const result = await options.context.invoke(compactor, {
343
+ previousSummary: [currentSummary].filter(type_utils_js_1.isNonNullable),
344
+ messages,
345
+ });
346
+ currentSummary = result.summary;
347
+ }
348
+ // Write compact entry to AFS
349
+ if (this.afs && this.historyModulePath) {
350
+ await this.afs.write((0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "@metadata/compact/new"), {
351
+ userId: this.userId,
352
+ agentId: this.agentId,
353
+ content: { summary: currentSummary },
354
+ metadata: {
355
+ latestEntryId: latestCompactedEntry.id,
356
+ },
357
+ });
358
+ }
359
+ // Update runtime state: keep the summary and recent entries
360
+ this.runtimeState.compactSummary = currentSummary;
361
+ this.runtimeState.historyEntries = entriesToKeep;
362
+ }
363
+ async compactCurrentEntry(options) {
364
+ const { compactor } = this.compactConfig ?? {};
365
+ if (!compactor)
366
+ return;
367
+ const currentEntry = this.runtimeState.currentEntry;
368
+ if (!currentEntry?.messages?.length)
369
+ return;
370
+ const alreadyCompressedCount = this.runtimeState.currentEntryCompression?.compressedCount ?? 0;
371
+ const uncompressedMessages = currentEntry.messages.slice(alreadyCompressedCount);
372
+ if (uncompressedMessages.length === 0)
373
+ return;
374
+ const keepTokenBudget = this.keepTokenBudget;
375
+ const singleMessageLimit = this.singleMessageLimit;
376
+ let splitIndex = uncompressedMessages.length;
377
+ let accumulatedTokens = 0;
378
+ for (let i = uncompressedMessages.length - 1; i >= 0; i--) {
379
+ const msg = uncompressedMessages[i];
380
+ if (!msg)
381
+ continue;
382
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
383
+ const msgTokens = (0, token_estimator_js_1.estimateTokens)(content);
384
+ const effectiveTokens = msgTokens > singleMessageLimit ? singleMessageLimit : msgTokens;
385
+ if (accumulatedTokens + effectiveTokens > keepTokenBudget) {
386
+ splitIndex = i + 1;
387
+ break;
388
+ }
389
+ accumulatedTokens += effectiveTokens;
390
+ splitIndex = i;
391
+ }
392
+ const keptMessages = uncompressedMessages.slice(splitIndex);
393
+ const requiredToolCallIds = new Set();
394
+ for (const msg of keptMessages) {
395
+ if (msg.role === "tool" && msg.toolCallId) {
396
+ requiredToolCallIds.add(msg.toolCallId);
397
+ }
398
+ }
399
+ if (requiredToolCallIds.size > 0) {
400
+ for (let i = splitIndex - 1; i >= 0; i--) {
401
+ const msg = uncompressedMessages[i];
402
+ if (!msg?.toolCalls)
403
+ continue;
404
+ for (const toolCall of msg.toolCalls) {
405
+ if (requiredToolCallIds.has(toolCall.id)) {
406
+ splitIndex = i;
407
+ break;
408
+ }
409
+ }
410
+ }
411
+ }
412
+ const messagesToCompact = uncompressedMessages
413
+ .slice(0, splitIndex)
414
+ .map((msg) => this.truncateLargeMessage(msg));
415
+ if (messagesToCompact.length === 0)
416
+ return;
417
+ const result = await options.context.invoke(compactor, {
418
+ previousSummary: this.runtimeState.currentEntryCompression?.summary
419
+ ? [this.runtimeState.currentEntryCompression.summary]
420
+ : undefined,
421
+ messages: messagesToCompact,
422
+ });
423
+ this.runtimeState.currentEntryCompression = {
424
+ summary: result.summary,
425
+ compressedCount: alreadyCompressedCount + messagesToCompact.length,
426
+ };
427
+ }
428
+ async maybeCompactCurrentEntry(options) {
429
+ const currentEntry = this.runtimeState.currentEntry;
430
+ if (!currentEntry?.messages?.length)
431
+ return;
432
+ const compressedCount = this.runtimeState.currentEntryCompression?.compressedCount ?? 0;
433
+ const uncompressedMessages = currentEntry.messages.slice(compressedCount);
434
+ const threshold = this.keepTokenBudget;
435
+ const currentTokens = this.estimateMessagesTokens(uncompressedMessages, this.singleMessageLimit);
436
+ if (currentTokens > threshold) {
437
+ await this.compactCurrentEntry(options);
438
+ }
439
+ }
440
+ async maybeAutoCompact(options) {
441
+ if (this.compactionPromise)
442
+ await this.compactionPromise;
443
+ if (!this.compactConfig)
444
+ return;
445
+ const mode = this.compactConfig.mode ?? types_js_1.DEFAULT_COMPACT_MODE;
446
+ if (mode === "disabled")
447
+ return;
448
+ const { compactor } = this.compactConfig;
449
+ if (!compactor)
450
+ return;
451
+ const maxTokens = this.maxTokens;
452
+ const messages = await this.getMessages();
453
+ const currentTokens = this.estimateMessagesTokens(messages);
454
+ if (currentTokens >= maxTokens) {
455
+ this.compact(options);
456
+ const isAsync = this.compactConfig.async ?? types_js_1.DEFAULT_COMPACT_ASYNC;
457
+ if (!isAsync)
458
+ await this.compactionPromise;
459
+ }
460
+ }
461
+ /**
462
+ * Estimate token count for an array of messages
463
+ */
464
+ estimateMessagesTokens(messages, singleMessageLimit) {
465
+ return messages.reduce((sum, msg) => {
466
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "");
467
+ const tokens = (0, token_estimator_js_1.estimateTokens)(content);
468
+ if (singleMessageLimit && tokens > singleMessageLimit) {
469
+ return sum + singleMessageLimit;
470
+ }
471
+ return sum + tokens;
472
+ }, 0);
473
+ }
474
+ /**
475
+ * Split entries into batches based on token limit
476
+ * Each batch will not exceed the specified maxTokens
477
+ */
478
+ splitIntoBatches(entries, maxTokens) {
479
+ const batches = [];
480
+ let currentBatch = [];
481
+ let currentTokens = 0;
482
+ for (const entry of entries) {
483
+ const entryTokens = this.estimateMessagesTokens(entry.content?.messages ?? []);
484
+ // If adding this entry exceeds limit and we have entries in current batch, start new batch
485
+ if (currentTokens + entryTokens > maxTokens && currentBatch.length > 0) {
486
+ batches.push(currentBatch);
487
+ currentBatch = [entry];
488
+ currentTokens = entryTokens;
489
+ }
490
+ else {
491
+ currentBatch.push(entry);
492
+ currentTokens += entryTokens;
493
+ }
494
+ }
495
+ // Add remaining entries
496
+ if (currentBatch.length > 0) {
497
+ batches.push(currentBatch);
498
+ }
499
+ return batches;
500
+ }
501
+ async appendCurrentMessages(messages, options) {
502
+ await this.ensureInitialized();
503
+ if (!this.runtimeState.currentEntry || !this.runtimeState.currentEntry.messages?.length) {
504
+ throw new Error("No current entry to append messages. Call startMessage() first.");
505
+ }
506
+ this.runtimeState.currentEntry.messages.push(...[messages].flat());
507
+ await this.maybeCompactCurrentEntry(options);
508
+ }
509
+ truncateLargeMessage(msg) {
510
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
511
+ const tokens = (0, token_estimator_js_1.estimateTokens)(content);
512
+ const singleMessageLimit = this.singleMessageLimit;
513
+ if (tokens <= singleMessageLimit)
514
+ return msg;
515
+ const keepRatio = (singleMessageLimit / tokens) * 0.9;
516
+ const keepLength = Math.floor(content.length * keepRatio);
517
+ const headLength = Math.floor(keepLength * 0.7);
518
+ const tailLength = Math.floor(keepLength * 0.3);
519
+ const truncated = content.slice(0, headLength) +
520
+ `\n\n[... Content too large, truncated ${tokens - singleMessageLimit} tokens ...]\n\n` +
521
+ content.slice(-tailLength);
522
+ if (typeof msg.content === "string") {
523
+ return { ...msg, content: truncated };
524
+ }
525
+ return msg;
526
+ }
527
+ async ensureInitialized() {
528
+ this.initialized ??= this.initialize();
529
+ await this.initialized;
530
+ }
531
+ async initialize() {
532
+ if (this.initialized)
533
+ return;
534
+ await this.initializeDefaultCompactor();
535
+ await this.initializeDefaultSessionMemoryExtractor();
536
+ await this.initializeDefaultUserMemoryExtractor();
537
+ const historyModule = (await this.afs?.listModules())?.find((m) => m.module instanceof afs_history_1.AFSHistory);
538
+ this.historyModulePath = historyModule?.path;
539
+ if (this.afs && this.historyModulePath) {
540
+ // Load user memory, session memory, and session history in parallel
541
+ const [userMemory, sessionMemory, sessionHistory] = await Promise.all([
542
+ this.loadUserMemory(),
543
+ this.loadSessionMemory(),
544
+ this.loadSessionHistory(),
545
+ ]);
546
+ // Update runtime state with loaded data
547
+ this.runtimeState.userMemory = userMemory;
548
+ this.runtimeState.sessionMemory = sessionMemory;
549
+ this.runtimeState.compactSummary = sessionHistory.compactSummary;
550
+ this.runtimeState.historyEntries = sessionHistory.historyEntries;
551
+ }
552
+ }
553
+ /**
554
+ * Load session memory facts
555
+ * @returns Array of memory fact entries for the current session
556
+ */
557
+ async loadSessionMemory() {
558
+ if (!this.afs || !this.historyModulePath)
559
+ return [];
560
+ // Check if session memory is disabled
561
+ const mode = this.sessionMemoryConfig.mode ?? types_js_1.DEFAULT_SESSION_MEMORY_MODE;
562
+ if (mode === "disabled")
563
+ return [];
564
+ const sessionMemoryPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "@metadata/memory");
565
+ const queryLimit = this.sessionMemoryConfig.queryLimit ?? types_js_1.DEFAULT_MEMORY_QUERY_LIMIT;
566
+ const memoryResult = await this.afs.list(sessionMemoryPath, {
567
+ filter: { userId: this.userId, agentId: this.agentId },
568
+ orderBy: [["updatedAt", "desc"]],
569
+ limit: queryLimit,
570
+ });
571
+ // Filter out entries without content
572
+ const facts = memoryResult.data
573
+ .reverse()
574
+ .filter((entry) => (0, type_utils_js_1.isNonNullable)(entry.content));
575
+ return facts;
576
+ }
577
+ /**
578
+ * Load user memory facts
579
+ * @returns Array of memory fact entries for the current user
580
+ */
581
+ async loadUserMemory() {
582
+ if (!this.afs || !this.historyModulePath || !this.userId)
583
+ return [];
584
+ // Check if user memory is disabled
585
+ const mode = this.userMemoryConfig.mode ?? types_js_1.DEFAULT_USER_MEMORY_MODE;
586
+ if (mode === "disabled")
587
+ return [];
588
+ const userMemoryPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-user", this.userId, "@metadata/memory");
589
+ const queryLimit = this.userMemoryConfig.queryLimit ?? types_js_1.DEFAULT_MEMORY_QUERY_LIMIT;
590
+ const memoryResult = await this.afs.list(userMemoryPath, {
591
+ filter: { userId: this.userId, agentId: this.agentId },
592
+ orderBy: [["updatedAt", "desc"]],
593
+ limit: queryLimit,
594
+ });
595
+ // Filter out entries without content
596
+ const facts = memoryResult.data
597
+ .reverse()
598
+ .filter((entry) => (0, type_utils_js_1.isNonNullable)(entry.content));
599
+ return facts;
600
+ }
601
+ /**
602
+ * Load session history including compact summary and history entries
603
+ * @returns Object containing compact summary and history entries
604
+ */
605
+ async loadSessionHistory() {
606
+ if (!this.afs || !this.historyModulePath) {
607
+ return { historyEntries: [] };
608
+ }
609
+ // Load latest compact entry if exists
610
+ const compactPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "@metadata/compact");
611
+ const compactResult = await this.afs.list(compactPath, {
612
+ filter: { userId: this.userId, agentId: this.agentId },
613
+ orderBy: [["createdAt", "desc"]],
614
+ limit: 1,
615
+ });
616
+ const latestCompact = compactResult.data[0];
617
+ const compactSummary = latestCompact?.content?.summary;
618
+ // Load history entries (after compact point if exists)
619
+ const afsEntries = (await this.afs.list((0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId), {
620
+ filter: {
621
+ userId: this.userId,
622
+ agentId: this.agentId,
623
+ // Only load entries after the latest compact
624
+ after: latestCompact?.createdAt?.toISOString(),
625
+ },
626
+ orderBy: [["createdAt", "desc"]],
627
+ // Set a very large limit to load all history entries
628
+ // The default limit is 10 which would cause history truncation
629
+ limit: 10000,
630
+ })).data;
631
+ const historyEntries = afsEntries.reverse().filter((entry) => (0, type_utils_js_1.isNonNullable)(entry.content));
632
+ return {
633
+ compactSummary,
634
+ historyEntries,
635
+ };
636
+ }
637
+ /**
638
+ * Manually trigger session memory update
639
+ */
640
+ async updateSessionMemory(options) {
641
+ await this.ensureInitialized();
642
+ // If session memory update is already in progress, wait for it to complete
643
+ if (this.sessionMemoryUpdatePromise) {
644
+ return this.sessionMemoryUpdatePromise;
645
+ }
646
+ // Start new session memory update task
647
+ this.sessionMemoryUpdatePromise = this.doUpdateSessionMemory(options).finally(() => {
648
+ this.sessionMemoryUpdatePromise = undefined;
649
+ // After session memory update completes, potentially trigger user memory consolidation
650
+ this.maybeAutoUpdateUserMemory(options);
651
+ });
652
+ return this.sessionMemoryUpdatePromise;
653
+ }
654
+ async maybeAutoUpdateSessionMemory(options) {
655
+ if (this.sessionMemoryUpdatePromise)
656
+ await this.sessionMemoryUpdatePromise;
657
+ // Check if memory extraction is enabled (requires AFS history module)
658
+ if (!this.isMemoryEnabled)
659
+ return;
660
+ if (!this.sessionMemoryConfig)
661
+ return;
662
+ // Check if mode is disabled
663
+ const mode = this.sessionMemoryConfig.mode ?? types_js_1.DEFAULT_SESSION_MEMORY_MODE;
664
+ if (mode === "disabled")
665
+ return;
666
+ // Trigger session memory update
667
+ this.updateSessionMemory(options);
668
+ const isAsync = this.sessionMemoryConfig.async ?? types_js_1.DEFAULT_SESSION_MEMORY_ASYNC;
669
+ if (!isAsync)
670
+ await this.sessionMemoryUpdatePromise;
671
+ }
672
+ async maybeAutoUpdateUserMemory(options) {
673
+ if (this.userMemoryUpdatePromise)
674
+ await this.userMemoryUpdatePromise;
675
+ // Check if memory extraction is enabled (requires AFS history module)
676
+ if (!this.isMemoryEnabled)
677
+ return;
678
+ if (!this.userMemoryConfig || !this.userId)
679
+ return;
680
+ // Check if mode is disabled
681
+ const mode = this.userMemoryConfig.mode ?? types_js_1.DEFAULT_USER_MEMORY_MODE;
682
+ if (mode === "disabled")
683
+ return;
684
+ // Wait for session memory update to complete first
685
+ if (this.sessionMemoryUpdatePromise)
686
+ await this.sessionMemoryUpdatePromise;
687
+ // Trigger user memory consolidation
688
+ this.updateUserMemory(options);
689
+ const isAsync = this.userMemoryConfig.async ?? types_js_1.DEFAULT_USER_MEMORY_ASYNC;
690
+ if (!isAsync)
691
+ await this.userMemoryUpdatePromise;
692
+ }
693
+ /**
694
+ * Internal method that performs the actual session memory update
695
+ */
696
+ async doUpdateSessionMemory(options) {
697
+ const { extractor } = this.sessionMemoryConfig ?? {};
698
+ if (!extractor) {
699
+ throw new Error("Cannot update session memory without an extractor agent configured.");
700
+ }
701
+ // Get latestEntryId from the most recent memory entry's metadata
702
+ // This tells us which history entries have already been processed
703
+ const latestEntryId = this.runtimeState.sessionMemory?.at(-1)?.metadata?.latestEntryId;
704
+ // Filter unextracted entries based on latestEntryId
705
+ // Similar to compact mechanism, we find the position of the last extracted entry
706
+ // and only process entries after that point
707
+ const lastExtractedIndex = latestEntryId
708
+ ? this.runtimeState.historyEntries.findIndex((e) => e.id === latestEntryId)
709
+ : -1;
710
+ const unextractedEntries = lastExtractedIndex >= 0
711
+ ? this.runtimeState.historyEntries.slice(lastExtractedIndex + 1)
712
+ : this.runtimeState.historyEntries;
713
+ if (unextractedEntries.length === 0)
714
+ return;
715
+ // Get recent conversation messages for extraction
716
+ const recentMessages = unextractedEntries
717
+ .flatMap((entry) => entry.content?.messages ?? [])
718
+ .filter(type_utils_js_1.isNonNullable);
719
+ if (recentMessages.length === 0)
720
+ return;
721
+ // Get existing session memory facts for context
722
+ const existingFacts = this.runtimeState.sessionMemory?.map((entry) => entry.content).filter(type_utils_js_1.isNonNullable) ?? [];
723
+ // Get user memory facts to avoid duplication
724
+ const existingUserFacts = this.runtimeState.userMemory?.map((entry) => entry.content).filter(type_utils_js_1.isNonNullable) ?? [];
725
+ // Extract new facts from conversation
726
+ const result = await options.context.invoke(extractor, {
727
+ existingUserFacts,
728
+ existingFacts,
729
+ messages: recentMessages,
730
+ });
731
+ // If no changes, nothing to do
732
+ if (!result.newFacts.length && !result.removeFacts?.length) {
733
+ return;
734
+ }
735
+ // Get the last entry to record its ID for metadata
736
+ const latestExtractedEntry = unextractedEntries.at(-1);
737
+ if (this.afs && this.historyModulePath) {
738
+ // Handle fact removal
739
+ if (result.removeFacts?.length && this.runtimeState.sessionMemory) {
740
+ const entriesToRemove = [];
741
+ for (const label of result.removeFacts) {
742
+ const entry = this.runtimeState.sessionMemory.find((e) => e.content?.label === label);
743
+ if (entry)
744
+ entriesToRemove.push(entry);
745
+ }
746
+ // Remove from AFS storage and runtime state
747
+ for (const entryToRemove of entriesToRemove) {
748
+ // Delete from AFS storage
749
+ const memoryEntryPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "@metadata/memory", entryToRemove.id);
750
+ await this.afs.delete(memoryEntryPath);
751
+ // Remove from runtime state
752
+ const index = this.runtimeState.sessionMemory.indexOf(entryToRemove);
753
+ if (index !== -1) {
754
+ this.runtimeState.sessionMemory.splice(index, 1);
755
+ }
756
+ }
757
+ }
758
+ // Handle new facts
759
+ if (result.newFacts.length) {
760
+ const sessionMemoryPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-session", this.sessionId, "@metadata/memory/new");
761
+ for (const fact of result.newFacts) {
762
+ const newEntry = await this.afs.write(sessionMemoryPath, {
763
+ userId: this.userId,
764
+ sessionId: this.sessionId,
765
+ agentId: this.agentId,
766
+ content: fact,
767
+ metadata: {
768
+ latestEntryId: latestExtractedEntry?.id,
769
+ },
770
+ });
771
+ // Add to runtime state
772
+ this.runtimeState.sessionMemory ??= [];
773
+ this.runtimeState.sessionMemory.push(newEntry.data);
774
+ }
775
+ }
776
+ }
777
+ }
778
+ /**
779
+ * Manually trigger user memory update
780
+ */
781
+ async updateUserMemory(options) {
782
+ await this.ensureInitialized();
783
+ // If user memory update is already in progress, wait for it to complete
784
+ if (this.userMemoryUpdatePromise) {
785
+ return this.userMemoryUpdatePromise;
786
+ }
787
+ // Start new user memory update task
788
+ this.userMemoryUpdatePromise = this.doUpdateUserMemory(options).finally(() => {
789
+ this.userMemoryUpdatePromise = undefined;
790
+ });
791
+ return this.userMemoryUpdatePromise;
792
+ }
793
+ /**
794
+ * Internal method that performs the actual user memory extraction
795
+ */
796
+ async doUpdateUserMemory(options) {
797
+ const { extractor } = this.userMemoryConfig ?? {};
798
+ if (!extractor) {
799
+ throw new Error("Cannot update user memory without an extractor agent configured.");
800
+ }
801
+ // Get session memory facts as the source for consolidation
802
+ const sessionFacts = this.runtimeState.sessionMemory?.map((entry) => entry.content).filter(type_utils_js_1.isNonNullable) ?? [];
803
+ if (sessionFacts.length === 0)
804
+ return;
805
+ // Get existing user memory facts for context and deduplication
806
+ const existingUserFacts = this.runtimeState.userMemory?.map((entry) => entry.content).filter(type_utils_js_1.isNonNullable) ?? [];
807
+ // Extract user memory facts from session memory
808
+ const result = await options.context.invoke(extractor, {
809
+ sessionFacts,
810
+ existingUserFacts,
811
+ });
812
+ // If no changes, nothing to do
813
+ if (!result.newFacts.length && !result.removeFacts?.length) {
814
+ return;
815
+ }
816
+ if (this.afs && this.historyModulePath && this.userId) {
817
+ // Handle fact removal
818
+ if (result.removeFacts?.length && this.runtimeState.userMemory) {
819
+ const entriesToRemove = [];
820
+ for (const label of result.removeFacts) {
821
+ const entry = this.runtimeState.userMemory.find((e) => e.content?.label === label);
822
+ if (entry)
823
+ entriesToRemove.push(entry);
824
+ }
825
+ // Remove from AFS storage and runtime state
826
+ for (const entryToRemove of entriesToRemove) {
827
+ const memoryEntryPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-user", this.userId, "@metadata/memory", entryToRemove.id);
828
+ await this.afs.delete(memoryEntryPath);
829
+ const index = this.runtimeState.userMemory.indexOf(entryToRemove);
830
+ if (index !== -1) {
831
+ this.runtimeState.userMemory.splice(index, 1);
832
+ }
833
+ }
834
+ }
835
+ // Handle new/updated facts
836
+ // For user memory, labels are unique - replace existing facts with same label
837
+ if (result.newFacts.length) {
838
+ const userMemoryPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-user", this.userId, "@metadata/memory/new");
839
+ for (const fact of result.newFacts) {
840
+ // Check if fact with same label already exists
841
+ const existingEntry = this.runtimeState.userMemory?.find((e) => e.content?.label === fact.label);
842
+ if (existingEntry) {
843
+ // Delete old entry
844
+ const oldEntryPath = (0, ufo_1.joinURL)(this.historyModulePath, "by-user", this.userId, "@metadata/memory", existingEntry.id);
845
+ await this.afs.delete(oldEntryPath);
846
+ // Remove from runtime state
847
+ if (this.runtimeState.userMemory) {
848
+ const index = this.runtimeState.userMemory.indexOf(existingEntry);
849
+ if (index !== -1) {
850
+ this.runtimeState.userMemory.splice(index, 1);
851
+ }
852
+ }
853
+ }
854
+ // Create new entry
855
+ const newEntry = await this.afs.write(userMemoryPath, {
856
+ userId: this.userId,
857
+ agentId: this.agentId,
858
+ content: fact,
859
+ });
860
+ // Add to runtime state
861
+ this.runtimeState.userMemory ??= [];
862
+ this.runtimeState.userMemory.push(newEntry.data);
863
+ }
864
+ }
865
+ }
866
+ }
867
+ async initializeDefaultCompactor() {
868
+ this.compactConfig.compactor ??= await Promise.resolve().then(() => __importStar(require("./compact/compactor.js"))).then((m) => new m.AISessionCompactor());
869
+ }
870
+ async initializeDefaultSessionMemoryExtractor() {
871
+ this.sessionMemoryConfig.extractor ??= await Promise.resolve().then(() => __importStar(require("./compact/session-memory-extractor.js"))).then((m) => new m.AISessionMemoryExtractor());
872
+ }
873
+ async initializeDefaultUserMemoryExtractor() {
874
+ this.userMemoryConfig.extractor ??= await Promise.resolve().then(() => __importStar(require("./compact/user-memory-extractor.js"))).then((m) => new m.AIUserMemoryExtractor());
875
+ }
876
+ get maxTokens() {
877
+ return this.compactConfig?.maxTokens ?? types_js_1.DEFAULT_MAX_TOKENS;
878
+ }
879
+ get keepRecentRatio() {
880
+ return this.compactConfig?.keepRecentRatio ?? types_js_1.DEFAULT_KEEP_RECENT_RATIO;
881
+ }
882
+ get keepTokenBudget() {
883
+ return Math.floor(this.maxTokens * this.keepRecentRatio);
884
+ }
885
+ get singleMessageLimit() {
886
+ return this.keepTokenBudget * 0.5;
887
+ }
888
+ }
889
+ exports.AgentSession = AgentSession;