@agentscope-ai/agentscope 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.mts +234 -0
- package/dist/agent/index.d.ts +234 -0
- package/dist/agent/index.js +1412 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/index.mjs +1375 -0
- package/dist/agent/index.mjs.map +1 -0
- package/dist/base-BOx3UzOl.d.mts +41 -0
- package/dist/base-BoIps2RL.d.ts +41 -0
- package/dist/base-C7jwyH4Z.d.mts +52 -0
- package/dist/base-Cwi4bjze.d.ts +127 -0
- package/dist/base-DYlBMCy_.d.mts +127 -0
- package/dist/base-NX-knWOv.d.ts +52 -0
- package/dist/block-VsnHrllL.d.mts +48 -0
- package/dist/block-VsnHrllL.d.ts +48 -0
- package/dist/event/index.d.mts +181 -0
- package/dist/event/index.d.ts +181 -0
- package/dist/event/index.js +58 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/index.mjs +33 -0
- package/dist/event/index.mjs.map +1 -0
- package/dist/formatter/index.d.mts +187 -0
- package/dist/formatter/index.d.ts +187 -0
- package/dist/formatter/index.js +647 -0
- package/dist/formatter/index.js.map +1 -0
- package/dist/formatter/index.mjs +616 -0
- package/dist/formatter/index.mjs.map +1 -0
- package/dist/index-BTJDlKvQ.d.mts +195 -0
- package/dist/index-BcatlwXQ.d.ts +195 -0
- package/dist/index-CAxQAkiP.d.mts +21 -0
- package/dist/index-CAxQAkiP.d.ts +21 -0
- package/dist/mcp/index.d.mts +9 -0
- package/dist/mcp/index.d.ts +9 -0
- package/dist/mcp/index.js +432 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/index.mjs +408 -0
- package/dist/mcp/index.mjs.map +1 -0
- package/dist/message/index.d.mts +10 -0
- package/dist/message/index.d.ts +10 -0
- package/dist/message/index.js +67 -0
- package/dist/message/index.js.map +1 -0
- package/dist/message/index.mjs +37 -0
- package/dist/message/index.mjs.map +1 -0
- package/dist/message-CkN21KaY.d.mts +99 -0
- package/dist/message-CzLeTlua.d.ts +99 -0
- package/dist/model/index.d.mts +377 -0
- package/dist/model/index.d.ts +377 -0
- package/dist/model/index.js +1880 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/index.mjs +1849 -0
- package/dist/model/index.mjs.map +1 -0
- package/dist/storage/index.d.mts +68 -0
- package/dist/storage/index.d.ts +68 -0
- package/dist/storage/index.js +250 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/index.mjs +212 -0
- package/dist/storage/index.mjs.map +1 -0
- package/dist/tool/index.d.mts +311 -0
- package/dist/tool/index.d.ts +311 -0
- package/dist/tool/index.js +1494 -0
- package/dist/tool/index.js.map +1 -0
- package/dist/tool/index.mjs +1447 -0
- package/dist/tool/index.mjs.map +1 -0
- package/dist/toolkit-CEpulFi0.d.ts +99 -0
- package/dist/toolkit-CGEZSZPa.d.mts +99 -0
- package/jest.config.js +11 -0
- package/package.json +92 -0
- package/src/_utils/common.ts +104 -0
- package/src/_utils/index.ts +1 -0
- package/src/agent/agent-base.ts +0 -0
- package/src/agent/agent.test.ts +1028 -0
- package/src/agent/agent.ts +1032 -0
- package/src/agent/index.ts +2 -0
- package/src/agent/interfaces.ts +23 -0
- package/src/agent/test-compression.ts +72 -0
- package/src/event/index.ts +250 -0
- package/src/formatter/base.ts +133 -0
- package/src/formatter/dashscope-chat-formatter.test.ts +372 -0
- package/src/formatter/dashscope-chat-formatter.ts +163 -0
- package/src/formatter/deepseek-chat-formatter.ts +130 -0
- package/src/formatter/index.ts +5 -0
- package/src/formatter/ollama-chat-formatter.ts +67 -0
- package/src/formatter/openai-chat-formatter.test.ts +263 -0
- package/src/formatter/openai-chat-formatter.ts +301 -0
- package/src/formatter/openai.md +767 -0
- package/src/mcp/base.ts +114 -0
- package/src/mcp/http.test.ts +303 -0
- package/src/mcp/http.ts +224 -0
- package/src/mcp/index.ts +2 -0
- package/src/mcp/stdio.test.ts +91 -0
- package/src/mcp/stdio.ts +119 -0
- package/src/message/block.ts +60 -0
- package/src/message/enums.ts +4 -0
- package/src/message/index.ts +12 -0
- package/src/message/message.test.ts +80 -0
- package/src/message/message.ts +131 -0
- package/src/model/base.ts +226 -0
- package/src/model/dashscope-model.test.ts +335 -0
- package/src/model/dashscope-model.ts +441 -0
- package/src/model/deepseek-model.test.ts +279 -0
- package/src/model/deepseek-model.ts +401 -0
- package/src/model/index.ts +7 -0
- package/src/model/ollama-model.test.ts +307 -0
- package/src/model/ollama-model.ts +356 -0
- package/src/model/openai-model.ts +327 -0
- package/src/model/response.ts +22 -0
- package/src/model/usage.ts +12 -0
- package/src/storage/base.ts +52 -0
- package/src/storage/file-system.test.ts +587 -0
- package/src/storage/file-system.ts +269 -0
- package/src/storage/index.ts +2 -0
- package/src/tool/base.ts +23 -0
- package/src/tool/bash.test.ts +174 -0
- package/src/tool/bash.ts +152 -0
- package/src/tool/edit.test.ts +83 -0
- package/src/tool/edit.ts +95 -0
- package/src/tool/glob.test.ts +63 -0
- package/src/tool/glob.ts +166 -0
- package/src/tool/grep.test.ts +74 -0
- package/src/tool/grep.ts +256 -0
- package/src/tool/index.ts +10 -0
- package/src/tool/read.test.ts +77 -0
- package/src/tool/read.ts +117 -0
- package/src/tool/response.ts +82 -0
- package/src/tool/task.test.ts +299 -0
- package/src/tool/task.ts +399 -0
- package/src/tool/toolkit.test.ts +636 -0
- package/src/tool/toolkit.ts +601 -0
- package/src/tool/write.test.ts +52 -0
- package/src/tool/write.ts +57 -0
- package/src/type/index.ts +52 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.cjs.json +11 -0
- package/tsconfig.esm.json +10 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +20 -0
- package/typedoc.json +52 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/storage/index.ts
|
|
31
|
+
var storage_exports = {};
|
|
32
|
+
__export(storage_exports, {
|
|
33
|
+
LocalFileStorage: () => LocalFileStorage,
|
|
34
|
+
StorageBase: () => StorageBase
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(storage_exports);
|
|
37
|
+
|
|
38
|
+
// src/storage/base.ts
|
|
39
|
+
var StorageBase = class {
|
|
40
|
+
/**
|
|
41
|
+
* Offload the compressed context to external storage for agentic search if needed.
|
|
42
|
+
* @param _options.msgs
|
|
43
|
+
* @param _options
|
|
44
|
+
* @returns The identifier or URL of the offloaded context, or undefined if offloading is not implemented or not needed
|
|
45
|
+
*/
|
|
46
|
+
async offloadContext(_options) {
|
|
47
|
+
console.log("Offloading context is not implemented for this storage. Skipping offloading.");
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/storage/file-system.ts
|
|
53
|
+
var import_fs = __toESM(require("fs"));
|
|
54
|
+
var import_path = __toESM(require("path"));
|
|
55
|
+
var mime = __toESM(require("mime-types"));
|
|
56
|
+
var LocalFileStorage = class extends StorageBase {
|
|
57
|
+
saveDir;
|
|
58
|
+
offloadDir;
|
|
59
|
+
/**
|
|
60
|
+
* Internal metadata key prefix for storage-layer fields.
|
|
61
|
+
* Fields with this prefix are managed by storage and filtered out when returning to agent layer.
|
|
62
|
+
*/
|
|
63
|
+
INTERNAL_PREFIX = "_storage_";
|
|
64
|
+
/**
|
|
65
|
+
* Initialize a LocalFileStorage instance.
|
|
66
|
+
* @param root0
|
|
67
|
+
* @param root0.pathSegments - Path segments to determine the directory for saving agent state (e.g. ['rootDir', '{sessionId}'])
|
|
68
|
+
* @param root0.offloadPathSegments - Optional path segments for offloading compressed context for agentic search (e.g. ['rootDir', 'offload'])
|
|
69
|
+
*/
|
|
70
|
+
constructor({
|
|
71
|
+
pathSegments = [],
|
|
72
|
+
offloadPathSegments = []
|
|
73
|
+
}) {
|
|
74
|
+
super();
|
|
75
|
+
this.saveDir = import_path.default.join(...pathSegments);
|
|
76
|
+
this.offloadDir = offloadPathSegments.length > 0 ? import_path.default.join(...offloadPathSegments) : void 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Load the complete agent state including context and metadata.
|
|
80
|
+
* @param options
|
|
81
|
+
* @param options.agentId - The agent identifier
|
|
82
|
+
* @returns The agent state with context and metadata (internal fields filtered out)
|
|
83
|
+
*/
|
|
84
|
+
async loadAgentState(options) {
|
|
85
|
+
const agentDir = import_path.default.join(this.saveDir, options?.agentId || "");
|
|
86
|
+
if (!import_fs.default.existsSync(agentDir)) {
|
|
87
|
+
console.log(`Agent directory ${agentDir} does not exist. Returning empty state.`);
|
|
88
|
+
return {
|
|
89
|
+
context: [],
|
|
90
|
+
metadata: {}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
console.log(`Loading agent state from directory: ${agentDir}`);
|
|
94
|
+
const contextFile = import_path.default.join(agentDir, "context.jsonl");
|
|
95
|
+
const stateFile = import_path.default.join(agentDir, "state.json");
|
|
96
|
+
let metadata = {};
|
|
97
|
+
if (import_fs.default.existsSync(stateFile)) {
|
|
98
|
+
const content = import_fs.default.readFileSync(stateFile, "utf-8");
|
|
99
|
+
metadata = JSON.parse(content);
|
|
100
|
+
}
|
|
101
|
+
const compressionBoundaryMsgId = metadata[`${this.INTERNAL_PREFIX}compressionBoundaryMsgId`];
|
|
102
|
+
let context = [];
|
|
103
|
+
if (import_fs.default.existsSync(contextFile)) {
|
|
104
|
+
const content = import_fs.default.readFileSync(contextFile, "utf-8");
|
|
105
|
+
const allMsgs = content.trim().split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
106
|
+
if (compressionBoundaryMsgId) {
|
|
107
|
+
const boundaryIndex = allMsgs.findIndex((msg) => msg.id === compressionBoundaryMsgId);
|
|
108
|
+
if (boundaryIndex !== -1) {
|
|
109
|
+
context = allMsgs.slice(boundaryIndex);
|
|
110
|
+
} else {
|
|
111
|
+
context = allMsgs;
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
context = allMsgs;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const publicMetadata = this._filterInternalFields(metadata);
|
|
118
|
+
return {
|
|
119
|
+
context,
|
|
120
|
+
metadata: publicMetadata
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Save the complete agent state including context and metadata.
|
|
125
|
+
* @param options
|
|
126
|
+
* @param options.agentId - The agent identifier
|
|
127
|
+
* @param options.context - The conversation context to save
|
|
128
|
+
* @param options.metadata - The agent metadata to save
|
|
129
|
+
*/
|
|
130
|
+
async saveAgentState(options) {
|
|
131
|
+
const agentDir = import_path.default.join(this.saveDir, options.agentId || "");
|
|
132
|
+
const contextFile = import_path.default.join(agentDir, "context.jsonl");
|
|
133
|
+
const stateFile = import_path.default.join(agentDir, "state.json");
|
|
134
|
+
if (!import_fs.default.existsSync(agentDir)) {
|
|
135
|
+
import_fs.default.mkdirSync(agentDir, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
const compressionBoundaryMsgId = options.context[0]?.id;
|
|
138
|
+
if (!import_fs.default.existsSync(contextFile)) {
|
|
139
|
+
const content = options.context.map((msg) => JSON.stringify(msg)).join("\n");
|
|
140
|
+
if (content) {
|
|
141
|
+
import_fs.default.writeFileSync(contextFile, content + "\n", "utf-8");
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
const existingContent = import_fs.default.readFileSync(contextFile, "utf-8");
|
|
145
|
+
const existingLines = existingContent.trim().split("\n").filter((line) => line.length > 0);
|
|
146
|
+
if (existingLines.length > 0) {
|
|
147
|
+
const lastLine = existingLines[existingLines.length - 1];
|
|
148
|
+
const lastMsg = JSON.parse(lastLine);
|
|
149
|
+
const lastMsgIndex = options.context.findIndex((msg) => msg.id === lastMsg.id);
|
|
150
|
+
const newMsgs = lastMsgIndex >= 0 ? options.context.slice(lastMsgIndex) : options.context;
|
|
151
|
+
if (newMsgs.length > 0) {
|
|
152
|
+
const allLines = [
|
|
153
|
+
...existingLines.slice(0, -1),
|
|
154
|
+
...newMsgs.map((msg) => JSON.stringify(msg))
|
|
155
|
+
];
|
|
156
|
+
const content = allLines.join("\n") + "\n";
|
|
157
|
+
import_fs.default.writeFileSync(contextFile, content, "utf-8");
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
const content = options.context.map((msg) => JSON.stringify(msg)).join("\n");
|
|
161
|
+
if (content) {
|
|
162
|
+
import_fs.default.writeFileSync(contextFile, content + "\n", "utf-8");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const internalMetadata = {
|
|
167
|
+
...options.metadata,
|
|
168
|
+
[`${this.INTERNAL_PREFIX}compressionBoundaryMsgId`]: compressionBoundaryMsgId
|
|
169
|
+
};
|
|
170
|
+
import_fs.default.writeFileSync(stateFile, JSON.stringify(internalMetadata, null, 2), "utf-8");
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Filter out internal storage fields from metadata.
|
|
174
|
+
* @param metadata - The metadata object
|
|
175
|
+
* @returns Metadata with internal fields removed
|
|
176
|
+
*/
|
|
177
|
+
_filterInternalFields(metadata) {
|
|
178
|
+
const filtered = {};
|
|
179
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
180
|
+
if (!key.startsWith(this.INTERNAL_PREFIX)) {
|
|
181
|
+
filtered[key] = value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return filtered;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Offload the compressed context to external storage for agentic search if needed.
|
|
188
|
+
* @param options
|
|
189
|
+
* @param options.agentId - The agent identifier
|
|
190
|
+
* @param options.msgs - The messages to offload
|
|
191
|
+
* @returns The file path of the offloaded context, or undefined if offloading is not implemented or not needed
|
|
192
|
+
*/
|
|
193
|
+
async offloadContext(options) {
|
|
194
|
+
if (!this.offloadDir) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const fileName = `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.txt`;
|
|
198
|
+
const offloadFile = import_path.default.join(this.offloadDir, options.agentId || "", fileName);
|
|
199
|
+
const offloadDataDir = import_path.default.join(this.offloadDir, options.agentId || "", "data");
|
|
200
|
+
const offloadAgentDir = import_path.default.dirname(offloadFile);
|
|
201
|
+
if (!import_fs.default.existsSync(offloadAgentDir)) {
|
|
202
|
+
import_fs.default.mkdirSync(offloadAgentDir, { recursive: true });
|
|
203
|
+
}
|
|
204
|
+
let appendContent = "";
|
|
205
|
+
for (const msg of options.msgs) {
|
|
206
|
+
const msgContent = [];
|
|
207
|
+
for (const block of msg.content) {
|
|
208
|
+
switch (block.type) {
|
|
209
|
+
case "text":
|
|
210
|
+
msgContent.push(`${msg.name}: ${block.text}`);
|
|
211
|
+
break;
|
|
212
|
+
case "data":
|
|
213
|
+
if (block.source.type === "url") {
|
|
214
|
+
msgContent.push(
|
|
215
|
+
`${msg.name}: <data src={${block.source.url}} type={${block.source.mediaType}} />`
|
|
216
|
+
);
|
|
217
|
+
} else if (block.source.type === "base64") {
|
|
218
|
+
const mainType = block.source.mediaType.split("/")[0];
|
|
219
|
+
const extension2 = mime.extension(block.source.mediaType) || "bin";
|
|
220
|
+
const filePath = import_path.default.join(
|
|
221
|
+
offloadDataDir,
|
|
222
|
+
`${mainType}-${Date.now()}.${extension2}`
|
|
223
|
+
);
|
|
224
|
+
if (!import_fs.default.existsSync(offloadDataDir)) {
|
|
225
|
+
import_fs.default.mkdirSync(offloadDataDir, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
const buffer = Buffer.from(block.source.data, "base64");
|
|
228
|
+
import_fs.default.writeFileSync(filePath, buffer);
|
|
229
|
+
msgContent.push(
|
|
230
|
+
`${msg.name}: <data src={${filePath}} type={${block.source.mediaType}} />`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
case "tool_call":
|
|
235
|
+
msgContent.push(`${msg.name}: Calling tool ${block.name} ...`);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
appendContent += msgContent.join("\n") + "\n";
|
|
240
|
+
}
|
|
241
|
+
import_fs.default.appendFileSync(offloadFile, appendContent, "utf-8");
|
|
242
|
+
return offloadFile;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
246
|
+
0 && (module.exports = {
|
|
247
|
+
LocalFileStorage,
|
|
248
|
+
StorageBase
|
|
249
|
+
});
|
|
250
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/storage/index.ts","../../src/storage/base.ts","../../src/storage/file-system.ts"],"sourcesContent":["export { AgentState, StorageBase } from './base';\nexport { LocalFileStorage } from './file-system';\n","import { Msg } from '../message';\n\n/**\n * The complete agent state including both conversation context and metadata.\n */\nexport interface AgentState {\n /**\n * The conversation context (message history).\n */\n context: Msg[];\n /**\n * Agent metadata (replyId, curIter, curSummary, etc.).\n */\n metadata: Record<string, unknown>;\n}\n\n/**\n * The base storage class that responsible for\n * - loading/saving agent context and state, and\n * - offloading compressed context to external storage for agentic search if needed\n */\nexport abstract class StorageBase {\n /**\n * Load the complete agent state including context and metadata.\n * @param options.agentId - The agent identifier\n * @returns The agent state with context and metadata\n */\n abstract loadAgentState(options?: { agentId?: string }): Promise<AgentState>;\n\n /**\n * Save the complete agent state including context and metadata.\n * @param options.agentId - The agent identifier\n * @param options.context - The conversation context to save\n * @param options.metadata - The agent metadata to save\n */\n abstract saveAgentState(options: {\n agentId?: string;\n context: Msg[];\n metadata: Record<string, unknown>;\n }): Promise<void>;\n\n /**\n * Offload the compressed context to external storage for agentic search if needed.\n * @param _options.msgs\n * @param _options\n * @returns The identifier or URL of the offloaded context, or undefined if offloading is not implemented or not needed\n */\n async offloadContext(_options: { msgs: Msg[] }): Promise<string | undefined> {\n console.log('Offloading context is not implemented for this storage. Skipping offloading.');\n return undefined;\n }\n}\n","import fs from 'fs';\nimport path from 'path';\n\nimport * as mime from 'mime-types';\n\nimport { Msg } from '../message';\nimport { AgentState, StorageBase } from './base';\n\n/**\n * Local file system storage implementation.\n * Stores agent state in JSON files with support for incremental context updates.\n */\nexport class LocalFileStorage extends StorageBase {\n saveDir: string;\n offloadDir?: string;\n\n /**\n * Internal metadata key prefix for storage-layer fields.\n * Fields with this prefix are managed by storage and filtered out when returning to agent layer.\n */\n private readonly INTERNAL_PREFIX = '_storage_';\n\n /**\n * Initialize a LocalFileStorage instance.\n * @param root0\n * @param root0.pathSegments - Path segments to determine the directory for saving agent state (e.g. ['rootDir', '{sessionId}'])\n * @param root0.offloadPathSegments - Optional path segments for offloading compressed context for agentic search (e.g. ['rootDir', 'offload'])\n */\n constructor({\n pathSegments = [],\n offloadPathSegments = [],\n }: {\n pathSegments?: string[];\n offloadPathSegments?: string[];\n }) {\n super();\n this.saveDir = path.join(...pathSegments);\n this.offloadDir =\n offloadPathSegments.length > 0 ? path.join(...offloadPathSegments) : undefined;\n }\n\n /**\n * Load the complete agent state including context and metadata.\n * @param options\n * @param options.agentId - The agent identifier\n * @returns The agent state with context and metadata (internal fields filtered out)\n */\n async loadAgentState(options?: { agentId?: string }): Promise<AgentState> {\n const agentDir = path.join(this.saveDir, options?.agentId || '');\n\n // If the agent directory doesn't exist, return empty state\n if (!fs.existsSync(agentDir)) {\n console.log(`Agent directory ${agentDir} does not exist. Returning empty state.`);\n return {\n context: [],\n metadata: {},\n };\n }\n console.log(`Loading agent state from directory: ${agentDir}`);\n\n const contextFile = path.join(agentDir, 'context.jsonl');\n const stateFile = path.join(agentDir, 'state.json');\n\n // Load metadata\n let metadata: Record<string, unknown> = {};\n if (fs.existsSync(stateFile)) {\n const content = fs.readFileSync(stateFile, 'utf-8');\n metadata = JSON.parse(content);\n }\n\n // Extract internal compression boundary ID\n const compressionBoundaryMsgId = metadata[\n `${this.INTERNAL_PREFIX}compressionBoundaryMsgId`\n ] as string | undefined;\n\n // Load context (incrementally if compression boundary exists)\n let context: Msg[] = [];\n if (fs.existsSync(contextFile)) {\n const content = fs.readFileSync(contextFile, 'utf-8');\n const allMsgs = content\n .trim()\n .split('\\n')\n .filter(line => line.length > 0)\n .map(line => JSON.parse(line));\n\n if (compressionBoundaryMsgId) {\n // Load only messages after the compression boundary\n const boundaryIndex = allMsgs.findIndex(msg => msg.id === compressionBoundaryMsgId);\n if (boundaryIndex !== -1) {\n // Include the boundary message itself\n context = allMsgs.slice(boundaryIndex);\n } else {\n // Boundary not found, load all messages\n context = allMsgs;\n }\n } else {\n // No compression, load all messages\n context = allMsgs;\n }\n }\n\n // Filter out internal fields from metadata before returning\n const publicMetadata = this._filterInternalFields(metadata);\n\n return {\n context,\n metadata: publicMetadata,\n };\n }\n\n /**\n * Save the complete agent state including context and metadata.\n * @param options\n * @param options.agentId - The agent identifier\n * @param options.context - The conversation context to save\n * @param options.metadata - The agent metadata to save\n */\n async saveAgentState(options: {\n agentId?: string;\n context: Msg[];\n metadata: Record<string, unknown>;\n }): Promise<void> {\n const agentDir = path.join(this.saveDir, options.agentId || '');\n const contextFile = path.join(agentDir, 'context.jsonl');\n const stateFile = path.join(agentDir, 'state.json');\n\n // Ensure directory exists\n if (!fs.existsSync(agentDir)) {\n fs.mkdirSync(agentDir, { recursive: true });\n }\n\n // Determine compression boundary (first message in current context)\n const compressionBoundaryMsgId = options.context[0]?.id;\n\n // Save context with incremental append optimization\n if (!fs.existsSync(contextFile)) {\n // First time: write all messages\n const content = options.context.map(msg => JSON.stringify(msg)).join('\\n');\n if (content) {\n fs.writeFileSync(contextFile, content + '\\n', 'utf-8');\n }\n } else {\n // File exists: append only new messages\n const existingContent = fs.readFileSync(contextFile, 'utf-8');\n const existingLines = existingContent\n .trim()\n .split('\\n')\n .filter(line => line.length > 0);\n\n if (existingLines.length > 0) {\n const lastLine = existingLines[existingLines.length - 1];\n const lastMsg = JSON.parse(lastLine);\n\n // Find new messages that need to be saved (including the last saved message to overwrite it)\n const lastMsgIndex = options.context.findIndex(msg => msg.id === lastMsg.id);\n const newMsgs =\n lastMsgIndex >= 0 ? options.context.slice(lastMsgIndex) : options.context;\n\n if (newMsgs.length > 0) {\n // Combine existing messages (without last line) with new messages\n const allLines = [\n ...existingLines.slice(0, -1),\n ...newMsgs.map(msg => JSON.stringify(msg)),\n ];\n const content = allLines.join('\\n') + '\\n';\n fs.writeFileSync(contextFile, content, 'utf-8');\n }\n } else {\n // File is empty, write all messages\n const content = options.context.map(msg => JSON.stringify(msg)).join('\\n');\n if (content) {\n fs.writeFileSync(contextFile, content + '\\n', 'utf-8');\n }\n }\n }\n\n // Save metadata with internal compression boundary\n const internalMetadata = {\n ...options.metadata,\n [`${this.INTERNAL_PREFIX}compressionBoundaryMsgId`]: compressionBoundaryMsgId,\n };\n fs.writeFileSync(stateFile, JSON.stringify(internalMetadata, null, 2), 'utf-8');\n }\n\n /**\n * Filter out internal storage fields from metadata.\n * @param metadata - The metadata object\n * @returns Metadata with internal fields removed\n */\n private _filterInternalFields(metadata: Record<string, unknown>): Record<string, unknown> {\n const filtered: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (!key.startsWith(this.INTERNAL_PREFIX)) {\n filtered[key] = value;\n }\n }\n return filtered;\n }\n\n /**\n * Offload the compressed context to external storage for agentic search if needed.\n * @param options\n * @param options.agentId - The agent identifier\n * @param options.msgs - The messages to offload\n * @returns The file path of the offloaded context, or undefined if offloading is not implemented or not needed\n */\n async offloadContext(options: { agentId?: string; msgs: Msg[] }): Promise<string | undefined> {\n if (!this.offloadDir) {\n return;\n }\n\n // Offload the compressed context to the text file\n // e.g. 2026-03-01.txt\n const fileName = `${new Date().toISOString().split('T')[0]}.txt`;\n const offloadFile = path.join(this.offloadDir, options.agentId || '', fileName);\n const offloadDataDir = path.join(this.offloadDir, options.agentId || '', 'data');\n\n // Create the dir if it doesn't exist\n const offloadAgentDir = path.dirname(offloadFile);\n if (!fs.existsSync(offloadAgentDir)) {\n fs.mkdirSync(offloadAgentDir, { recursive: true });\n }\n\n // Append the new context to the offload file\n let appendContent = '';\n for (const msg of options.msgs) {\n const msgContent: string[] = [];\n for (const block of msg.content) {\n switch (block.type) {\n case 'text':\n msgContent.push(`${msg.name}: ${block.text}`);\n break;\n case 'data':\n if (block.source.type === 'url') {\n msgContent.push(\n `${msg.name}: <data src={${block.source.url}} type={${block.source.mediaType}} />`\n );\n } else if (block.source.type === 'base64') {\n // Save the base64 data to a file and add a reference to the file in the offload content\n const mainType = block.source.mediaType.split('/')[0];\n const extension = mime.extension(block.source.mediaType) || 'bin';\n const filePath = path.join(\n offloadDataDir,\n `${mainType}-${Date.now()}.${extension}`\n );\n if (!fs.existsSync(offloadDataDir)) {\n fs.mkdirSync(offloadDataDir, { recursive: true });\n }\n const buffer = Buffer.from(block.source.data, 'base64');\n fs.writeFileSync(filePath, buffer);\n msgContent.push(\n `${msg.name}: <data src={${filePath}} type={${block.source.mediaType}} />`\n );\n }\n break;\n case 'tool_call':\n msgContent.push(`${msg.name}: Calling tool ${block.name} ...`);\n break;\n }\n }\n appendContent += msgContent.join('\\n') + '\\n';\n }\n\n // Append to the offload file\n fs.appendFileSync(offloadFile, appendContent, 'utf-8');\n\n return offloadFile;\n }\n}\n"],"mappings":"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","names":["path","fs","extension"]}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// src/storage/base.ts
|
|
2
|
+
var StorageBase = class {
|
|
3
|
+
/**
|
|
4
|
+
* Offload the compressed context to external storage for agentic search if needed.
|
|
5
|
+
* @param _options.msgs
|
|
6
|
+
* @param _options
|
|
7
|
+
* @returns The identifier or URL of the offloaded context, or undefined if offloading is not implemented or not needed
|
|
8
|
+
*/
|
|
9
|
+
async offloadContext(_options) {
|
|
10
|
+
console.log("Offloading context is not implemented for this storage. Skipping offloading.");
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/storage/file-system.ts
|
|
16
|
+
import fs from "fs";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import * as mime from "mime-types";
|
|
19
|
+
var LocalFileStorage = class extends StorageBase {
|
|
20
|
+
saveDir;
|
|
21
|
+
offloadDir;
|
|
22
|
+
/**
|
|
23
|
+
* Internal metadata key prefix for storage-layer fields.
|
|
24
|
+
* Fields with this prefix are managed by storage and filtered out when returning to agent layer.
|
|
25
|
+
*/
|
|
26
|
+
INTERNAL_PREFIX = "_storage_";
|
|
27
|
+
/**
|
|
28
|
+
* Initialize a LocalFileStorage instance.
|
|
29
|
+
* @param root0
|
|
30
|
+
* @param root0.pathSegments - Path segments to determine the directory for saving agent state (e.g. ['rootDir', '{sessionId}'])
|
|
31
|
+
* @param root0.offloadPathSegments - Optional path segments for offloading compressed context for agentic search (e.g. ['rootDir', 'offload'])
|
|
32
|
+
*/
|
|
33
|
+
constructor({
|
|
34
|
+
pathSegments = [],
|
|
35
|
+
offloadPathSegments = []
|
|
36
|
+
}) {
|
|
37
|
+
super();
|
|
38
|
+
this.saveDir = path.join(...pathSegments);
|
|
39
|
+
this.offloadDir = offloadPathSegments.length > 0 ? path.join(...offloadPathSegments) : void 0;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Load the complete agent state including context and metadata.
|
|
43
|
+
* @param options
|
|
44
|
+
* @param options.agentId - The agent identifier
|
|
45
|
+
* @returns The agent state with context and metadata (internal fields filtered out)
|
|
46
|
+
*/
|
|
47
|
+
async loadAgentState(options) {
|
|
48
|
+
const agentDir = path.join(this.saveDir, options?.agentId || "");
|
|
49
|
+
if (!fs.existsSync(agentDir)) {
|
|
50
|
+
console.log(`Agent directory ${agentDir} does not exist. Returning empty state.`);
|
|
51
|
+
return {
|
|
52
|
+
context: [],
|
|
53
|
+
metadata: {}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
console.log(`Loading agent state from directory: ${agentDir}`);
|
|
57
|
+
const contextFile = path.join(agentDir, "context.jsonl");
|
|
58
|
+
const stateFile = path.join(agentDir, "state.json");
|
|
59
|
+
let metadata = {};
|
|
60
|
+
if (fs.existsSync(stateFile)) {
|
|
61
|
+
const content = fs.readFileSync(stateFile, "utf-8");
|
|
62
|
+
metadata = JSON.parse(content);
|
|
63
|
+
}
|
|
64
|
+
const compressionBoundaryMsgId = metadata[`${this.INTERNAL_PREFIX}compressionBoundaryMsgId`];
|
|
65
|
+
let context = [];
|
|
66
|
+
if (fs.existsSync(contextFile)) {
|
|
67
|
+
const content = fs.readFileSync(contextFile, "utf-8");
|
|
68
|
+
const allMsgs = content.trim().split("\n").filter((line) => line.length > 0).map((line) => JSON.parse(line));
|
|
69
|
+
if (compressionBoundaryMsgId) {
|
|
70
|
+
const boundaryIndex = allMsgs.findIndex((msg) => msg.id === compressionBoundaryMsgId);
|
|
71
|
+
if (boundaryIndex !== -1) {
|
|
72
|
+
context = allMsgs.slice(boundaryIndex);
|
|
73
|
+
} else {
|
|
74
|
+
context = allMsgs;
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
context = allMsgs;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const publicMetadata = this._filterInternalFields(metadata);
|
|
81
|
+
return {
|
|
82
|
+
context,
|
|
83
|
+
metadata: publicMetadata
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Save the complete agent state including context and metadata.
|
|
88
|
+
* @param options
|
|
89
|
+
* @param options.agentId - The agent identifier
|
|
90
|
+
* @param options.context - The conversation context to save
|
|
91
|
+
* @param options.metadata - The agent metadata to save
|
|
92
|
+
*/
|
|
93
|
+
async saveAgentState(options) {
|
|
94
|
+
const agentDir = path.join(this.saveDir, options.agentId || "");
|
|
95
|
+
const contextFile = path.join(agentDir, "context.jsonl");
|
|
96
|
+
const stateFile = path.join(agentDir, "state.json");
|
|
97
|
+
if (!fs.existsSync(agentDir)) {
|
|
98
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
99
|
+
}
|
|
100
|
+
const compressionBoundaryMsgId = options.context[0]?.id;
|
|
101
|
+
if (!fs.existsSync(contextFile)) {
|
|
102
|
+
const content = options.context.map((msg) => JSON.stringify(msg)).join("\n");
|
|
103
|
+
if (content) {
|
|
104
|
+
fs.writeFileSync(contextFile, content + "\n", "utf-8");
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
const existingContent = fs.readFileSync(contextFile, "utf-8");
|
|
108
|
+
const existingLines = existingContent.trim().split("\n").filter((line) => line.length > 0);
|
|
109
|
+
if (existingLines.length > 0) {
|
|
110
|
+
const lastLine = existingLines[existingLines.length - 1];
|
|
111
|
+
const lastMsg = JSON.parse(lastLine);
|
|
112
|
+
const lastMsgIndex = options.context.findIndex((msg) => msg.id === lastMsg.id);
|
|
113
|
+
const newMsgs = lastMsgIndex >= 0 ? options.context.slice(lastMsgIndex) : options.context;
|
|
114
|
+
if (newMsgs.length > 0) {
|
|
115
|
+
const allLines = [
|
|
116
|
+
...existingLines.slice(0, -1),
|
|
117
|
+
...newMsgs.map((msg) => JSON.stringify(msg))
|
|
118
|
+
];
|
|
119
|
+
const content = allLines.join("\n") + "\n";
|
|
120
|
+
fs.writeFileSync(contextFile, content, "utf-8");
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
const content = options.context.map((msg) => JSON.stringify(msg)).join("\n");
|
|
124
|
+
if (content) {
|
|
125
|
+
fs.writeFileSync(contextFile, content + "\n", "utf-8");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const internalMetadata = {
|
|
130
|
+
...options.metadata,
|
|
131
|
+
[`${this.INTERNAL_PREFIX}compressionBoundaryMsgId`]: compressionBoundaryMsgId
|
|
132
|
+
};
|
|
133
|
+
fs.writeFileSync(stateFile, JSON.stringify(internalMetadata, null, 2), "utf-8");
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Filter out internal storage fields from metadata.
|
|
137
|
+
* @param metadata - The metadata object
|
|
138
|
+
* @returns Metadata with internal fields removed
|
|
139
|
+
*/
|
|
140
|
+
_filterInternalFields(metadata) {
|
|
141
|
+
const filtered = {};
|
|
142
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
143
|
+
if (!key.startsWith(this.INTERNAL_PREFIX)) {
|
|
144
|
+
filtered[key] = value;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return filtered;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Offload the compressed context to external storage for agentic search if needed.
|
|
151
|
+
* @param options
|
|
152
|
+
* @param options.agentId - The agent identifier
|
|
153
|
+
* @param options.msgs - The messages to offload
|
|
154
|
+
* @returns The file path of the offloaded context, or undefined if offloading is not implemented or not needed
|
|
155
|
+
*/
|
|
156
|
+
async offloadContext(options) {
|
|
157
|
+
if (!this.offloadDir) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const fileName = `${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.txt`;
|
|
161
|
+
const offloadFile = path.join(this.offloadDir, options.agentId || "", fileName);
|
|
162
|
+
const offloadDataDir = path.join(this.offloadDir, options.agentId || "", "data");
|
|
163
|
+
const offloadAgentDir = path.dirname(offloadFile);
|
|
164
|
+
if (!fs.existsSync(offloadAgentDir)) {
|
|
165
|
+
fs.mkdirSync(offloadAgentDir, { recursive: true });
|
|
166
|
+
}
|
|
167
|
+
let appendContent = "";
|
|
168
|
+
for (const msg of options.msgs) {
|
|
169
|
+
const msgContent = [];
|
|
170
|
+
for (const block of msg.content) {
|
|
171
|
+
switch (block.type) {
|
|
172
|
+
case "text":
|
|
173
|
+
msgContent.push(`${msg.name}: ${block.text}`);
|
|
174
|
+
break;
|
|
175
|
+
case "data":
|
|
176
|
+
if (block.source.type === "url") {
|
|
177
|
+
msgContent.push(
|
|
178
|
+
`${msg.name}: <data src={${block.source.url}} type={${block.source.mediaType}} />`
|
|
179
|
+
);
|
|
180
|
+
} else if (block.source.type === "base64") {
|
|
181
|
+
const mainType = block.source.mediaType.split("/")[0];
|
|
182
|
+
const extension2 = mime.extension(block.source.mediaType) || "bin";
|
|
183
|
+
const filePath = path.join(
|
|
184
|
+
offloadDataDir,
|
|
185
|
+
`${mainType}-${Date.now()}.${extension2}`
|
|
186
|
+
);
|
|
187
|
+
if (!fs.existsSync(offloadDataDir)) {
|
|
188
|
+
fs.mkdirSync(offloadDataDir, { recursive: true });
|
|
189
|
+
}
|
|
190
|
+
const buffer = Buffer.from(block.source.data, "base64");
|
|
191
|
+
fs.writeFileSync(filePath, buffer);
|
|
192
|
+
msgContent.push(
|
|
193
|
+
`${msg.name}: <data src={${filePath}} type={${block.source.mediaType}} />`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
case "tool_call":
|
|
198
|
+
msgContent.push(`${msg.name}: Calling tool ${block.name} ...`);
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
appendContent += msgContent.join("\n") + "\n";
|
|
203
|
+
}
|
|
204
|
+
fs.appendFileSync(offloadFile, appendContent, "utf-8");
|
|
205
|
+
return offloadFile;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
export {
|
|
209
|
+
LocalFileStorage,
|
|
210
|
+
StorageBase
|
|
211
|
+
};
|
|
212
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/storage/base.ts","../../src/storage/file-system.ts"],"sourcesContent":["import { Msg } from '../message';\n\n/**\n * The complete agent state including both conversation context and metadata.\n */\nexport interface AgentState {\n /**\n * The conversation context (message history).\n */\n context: Msg[];\n /**\n * Agent metadata (replyId, curIter, curSummary, etc.).\n */\n metadata: Record<string, unknown>;\n}\n\n/**\n * The base storage class that responsible for\n * - loading/saving agent context and state, and\n * - offloading compressed context to external storage for agentic search if needed\n */\nexport abstract class StorageBase {\n /**\n * Load the complete agent state including context and metadata.\n * @param options.agentId - The agent identifier\n * @returns The agent state with context and metadata\n */\n abstract loadAgentState(options?: { agentId?: string }): Promise<AgentState>;\n\n /**\n * Save the complete agent state including context and metadata.\n * @param options.agentId - The agent identifier\n * @param options.context - The conversation context to save\n * @param options.metadata - The agent metadata to save\n */\n abstract saveAgentState(options: {\n agentId?: string;\n context: Msg[];\n metadata: Record<string, unknown>;\n }): Promise<void>;\n\n /**\n * Offload the compressed context to external storage for agentic search if needed.\n * @param _options.msgs\n * @param _options\n * @returns The identifier or URL of the offloaded context, or undefined if offloading is not implemented or not needed\n */\n async offloadContext(_options: { msgs: Msg[] }): Promise<string | undefined> {\n console.log('Offloading context is not implemented for this storage. Skipping offloading.');\n return undefined;\n }\n}\n","import fs from 'fs';\nimport path from 'path';\n\nimport * as mime from 'mime-types';\n\nimport { Msg } from '../message';\nimport { AgentState, StorageBase } from './base';\n\n/**\n * Local file system storage implementation.\n * Stores agent state in JSON files with support for incremental context updates.\n */\nexport class LocalFileStorage extends StorageBase {\n saveDir: string;\n offloadDir?: string;\n\n /**\n * Internal metadata key prefix for storage-layer fields.\n * Fields with this prefix are managed by storage and filtered out when returning to agent layer.\n */\n private readonly INTERNAL_PREFIX = '_storage_';\n\n /**\n * Initialize a LocalFileStorage instance.\n * @param root0\n * @param root0.pathSegments - Path segments to determine the directory for saving agent state (e.g. ['rootDir', '{sessionId}'])\n * @param root0.offloadPathSegments - Optional path segments for offloading compressed context for agentic search (e.g. ['rootDir', 'offload'])\n */\n constructor({\n pathSegments = [],\n offloadPathSegments = [],\n }: {\n pathSegments?: string[];\n offloadPathSegments?: string[];\n }) {\n super();\n this.saveDir = path.join(...pathSegments);\n this.offloadDir =\n offloadPathSegments.length > 0 ? path.join(...offloadPathSegments) : undefined;\n }\n\n /**\n * Load the complete agent state including context and metadata.\n * @param options\n * @param options.agentId - The agent identifier\n * @returns The agent state with context and metadata (internal fields filtered out)\n */\n async loadAgentState(options?: { agentId?: string }): Promise<AgentState> {\n const agentDir = path.join(this.saveDir, options?.agentId || '');\n\n // If the agent directory doesn't exist, return empty state\n if (!fs.existsSync(agentDir)) {\n console.log(`Agent directory ${agentDir} does not exist. Returning empty state.`);\n return {\n context: [],\n metadata: {},\n };\n }\n console.log(`Loading agent state from directory: ${agentDir}`);\n\n const contextFile = path.join(agentDir, 'context.jsonl');\n const stateFile = path.join(agentDir, 'state.json');\n\n // Load metadata\n let metadata: Record<string, unknown> = {};\n if (fs.existsSync(stateFile)) {\n const content = fs.readFileSync(stateFile, 'utf-8');\n metadata = JSON.parse(content);\n }\n\n // Extract internal compression boundary ID\n const compressionBoundaryMsgId = metadata[\n `${this.INTERNAL_PREFIX}compressionBoundaryMsgId`\n ] as string | undefined;\n\n // Load context (incrementally if compression boundary exists)\n let context: Msg[] = [];\n if (fs.existsSync(contextFile)) {\n const content = fs.readFileSync(contextFile, 'utf-8');\n const allMsgs = content\n .trim()\n .split('\\n')\n .filter(line => line.length > 0)\n .map(line => JSON.parse(line));\n\n if (compressionBoundaryMsgId) {\n // Load only messages after the compression boundary\n const boundaryIndex = allMsgs.findIndex(msg => msg.id === compressionBoundaryMsgId);\n if (boundaryIndex !== -1) {\n // Include the boundary message itself\n context = allMsgs.slice(boundaryIndex);\n } else {\n // Boundary not found, load all messages\n context = allMsgs;\n }\n } else {\n // No compression, load all messages\n context = allMsgs;\n }\n }\n\n // Filter out internal fields from metadata before returning\n const publicMetadata = this._filterInternalFields(metadata);\n\n return {\n context,\n metadata: publicMetadata,\n };\n }\n\n /**\n * Save the complete agent state including context and metadata.\n * @param options\n * @param options.agentId - The agent identifier\n * @param options.context - The conversation context to save\n * @param options.metadata - The agent metadata to save\n */\n async saveAgentState(options: {\n agentId?: string;\n context: Msg[];\n metadata: Record<string, unknown>;\n }): Promise<void> {\n const agentDir = path.join(this.saveDir, options.agentId || '');\n const contextFile = path.join(agentDir, 'context.jsonl');\n const stateFile = path.join(agentDir, 'state.json');\n\n // Ensure directory exists\n if (!fs.existsSync(agentDir)) {\n fs.mkdirSync(agentDir, { recursive: true });\n }\n\n // Determine compression boundary (first message in current context)\n const compressionBoundaryMsgId = options.context[0]?.id;\n\n // Save context with incremental append optimization\n if (!fs.existsSync(contextFile)) {\n // First time: write all messages\n const content = options.context.map(msg => JSON.stringify(msg)).join('\\n');\n if (content) {\n fs.writeFileSync(contextFile, content + '\\n', 'utf-8');\n }\n } else {\n // File exists: append only new messages\n const existingContent = fs.readFileSync(contextFile, 'utf-8');\n const existingLines = existingContent\n .trim()\n .split('\\n')\n .filter(line => line.length > 0);\n\n if (existingLines.length > 0) {\n const lastLine = existingLines[existingLines.length - 1];\n const lastMsg = JSON.parse(lastLine);\n\n // Find new messages that need to be saved (including the last saved message to overwrite it)\n const lastMsgIndex = options.context.findIndex(msg => msg.id === lastMsg.id);\n const newMsgs =\n lastMsgIndex >= 0 ? options.context.slice(lastMsgIndex) : options.context;\n\n if (newMsgs.length > 0) {\n // Combine existing messages (without last line) with new messages\n const allLines = [\n ...existingLines.slice(0, -1),\n ...newMsgs.map(msg => JSON.stringify(msg)),\n ];\n const content = allLines.join('\\n') + '\\n';\n fs.writeFileSync(contextFile, content, 'utf-8');\n }\n } else {\n // File is empty, write all messages\n const content = options.context.map(msg => JSON.stringify(msg)).join('\\n');\n if (content) {\n fs.writeFileSync(contextFile, content + '\\n', 'utf-8');\n }\n }\n }\n\n // Save metadata with internal compression boundary\n const internalMetadata = {\n ...options.metadata,\n [`${this.INTERNAL_PREFIX}compressionBoundaryMsgId`]: compressionBoundaryMsgId,\n };\n fs.writeFileSync(stateFile, JSON.stringify(internalMetadata, null, 2), 'utf-8');\n }\n\n /**\n * Filter out internal storage fields from metadata.\n * @param metadata - The metadata object\n * @returns Metadata with internal fields removed\n */\n private _filterInternalFields(metadata: Record<string, unknown>): Record<string, unknown> {\n const filtered: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(metadata)) {\n if (!key.startsWith(this.INTERNAL_PREFIX)) {\n filtered[key] = value;\n }\n }\n return filtered;\n }\n\n /**\n * Offload the compressed context to external storage for agentic search if needed.\n * @param options\n * @param options.agentId - The agent identifier\n * @param options.msgs - The messages to offload\n * @returns The file path of the offloaded context, or undefined if offloading is not implemented or not needed\n */\n async offloadContext(options: { agentId?: string; msgs: Msg[] }): Promise<string | undefined> {\n if (!this.offloadDir) {\n return;\n }\n\n // Offload the compressed context to the text file\n // e.g. 2026-03-01.txt\n const fileName = `${new Date().toISOString().split('T')[0]}.txt`;\n const offloadFile = path.join(this.offloadDir, options.agentId || '', fileName);\n const offloadDataDir = path.join(this.offloadDir, options.agentId || '', 'data');\n\n // Create the dir if it doesn't exist\n const offloadAgentDir = path.dirname(offloadFile);\n if (!fs.existsSync(offloadAgentDir)) {\n fs.mkdirSync(offloadAgentDir, { recursive: true });\n }\n\n // Append the new context to the offload file\n let appendContent = '';\n for (const msg of options.msgs) {\n const msgContent: string[] = [];\n for (const block of msg.content) {\n switch (block.type) {\n case 'text':\n msgContent.push(`${msg.name}: ${block.text}`);\n break;\n case 'data':\n if (block.source.type === 'url') {\n msgContent.push(\n `${msg.name}: <data src={${block.source.url}} type={${block.source.mediaType}} />`\n );\n } else if (block.source.type === 'base64') {\n // Save the base64 data to a file and add a reference to the file in the offload content\n const mainType = block.source.mediaType.split('/')[0];\n const extension = mime.extension(block.source.mediaType) || 'bin';\n const filePath = path.join(\n offloadDataDir,\n `${mainType}-${Date.now()}.${extension}`\n );\n if (!fs.existsSync(offloadDataDir)) {\n fs.mkdirSync(offloadDataDir, { recursive: true });\n }\n const buffer = Buffer.from(block.source.data, 'base64');\n fs.writeFileSync(filePath, buffer);\n msgContent.push(\n `${msg.name}: <data src={${filePath}} type={${block.source.mediaType}} />`\n );\n }\n break;\n case 'tool_call':\n msgContent.push(`${msg.name}: Calling tool ${block.name} ...`);\n break;\n }\n }\n appendContent += msgContent.join('\\n') + '\\n';\n }\n\n // Append to the offload file\n fs.appendFileSync(offloadFile, appendContent, 'utf-8');\n\n return offloadFile;\n }\n}\n"],"mappings":"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","names":["extension"]}
|