@meshagent/meshagent-agents 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/LICENSE +1 -0
- package/README.md +206 -0
- package/dist/browser/agent-messages.d.ts +343 -0
- package/dist/browser/agent-messages.js +1015 -0
- package/dist/browser/change-emitter.d.ts +8 -0
- package/dist/browser/change-emitter.js +20 -0
- package/dist/browser/chat-client.d.ts +225 -0
- package/dist/browser/chat-client.js +642 -0
- package/dist/browser/images-dataset.d.ts +83 -0
- package/dist/browser/images-dataset.js +282 -0
- package/dist/browser/index.d.ts +6 -0
- package/dist/browser/index.js +22 -0
- package/dist/browser/package.json +1 -0
- package/dist/browser/room-shell-toolkit.d.ts +14 -0
- package/dist/browser/room-shell-toolkit.js +274 -0
- package/dist/browser/room-storage-toolkit.d.ts +11 -0
- package/dist/browser/room-storage-toolkit.js +513 -0
- package/dist/browser/thread-storage.d.ts +102 -0
- package/dist/browser/thread-storage.js +476 -0
- package/dist/esm/agent-messages.d.ts +343 -0
- package/dist/esm/agent-messages.js +989 -0
- package/dist/esm/change-emitter.d.ts +8 -0
- package/dist/esm/change-emitter.js +16 -0
- package/dist/esm/chat-client.d.ts +225 -0
- package/dist/esm/chat-client.js +632 -0
- package/dist/esm/images-dataset.d.ts +83 -0
- package/dist/esm/images-dataset.js +274 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/room-shell-toolkit.d.ts +14 -0
- package/dist/esm/room-shell-toolkit.js +270 -0
- package/dist/esm/room-storage-toolkit.d.ts +11 -0
- package/dist/esm/room-storage-toolkit.js +509 -0
- package/dist/esm/thread-storage.d.ts +102 -0
- package/dist/esm/thread-storage.js +465 -0
- package/dist/node/agent-messages.d.ts +343 -0
- package/dist/node/agent-messages.js +1015 -0
- package/dist/node/change-emitter.d.ts +8 -0
- package/dist/node/change-emitter.js +20 -0
- package/dist/node/chat-client.d.ts +225 -0
- package/dist/node/chat-client.js +642 -0
- package/dist/node/images-dataset.d.ts +83 -0
- package/dist/node/images-dataset.js +282 -0
- package/dist/node/index.d.ts +6 -0
- package/dist/node/index.js +22 -0
- package/dist/node/package.json +1 -0
- package/dist/node/room-shell-toolkit.d.ts +14 -0
- package/dist/node/room-shell-toolkit.js +274 -0
- package/dist/node/room-storage-toolkit.d.ts +11 -0
- package/dist/node/room-storage-toolkit.js +513 -0
- package/dist/node/thread-storage.d.ts +102 -0
- package/dist/node/thread-storage.js +476 -0
- package/package.json +57 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import { EmptyContent, JsonContent, LinkContent, RoomClient, RoomServerException, TextContent, Tool, Toolkit, ToolContentSpec, WebSocketClientProtocol, } from "@meshagent/meshagent";
|
|
2
|
+
export const defaultRoomStorageToolkitMaxLength = 20000;
|
|
3
|
+
export class RoomStorageToolkit extends Toolkit {
|
|
4
|
+
constructor({ client, projectId, rooms, readOnly = false, maxLength = defaultRoomStorageToolkitMaxLength, }) {
|
|
5
|
+
const config = new RoomStorageToolkitConfig({
|
|
6
|
+
client,
|
|
7
|
+
projectId,
|
|
8
|
+
rooms: normalizeRooms(rooms),
|
|
9
|
+
maxLength,
|
|
10
|
+
});
|
|
11
|
+
super({
|
|
12
|
+
name: "room_storage",
|
|
13
|
+
title: "room storage",
|
|
14
|
+
description: "tools for interacting with explicitly shared room storage",
|
|
15
|
+
tools: roomStorageTools({ config, readOnly }),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function roomStorageTools({ config, readOnly, }) {
|
|
20
|
+
const tools = [
|
|
21
|
+
new ListRoomFilesTool(config),
|
|
22
|
+
new ReadRoomFileTool(config),
|
|
23
|
+
new GrepRoomFileTool(config),
|
|
24
|
+
new GetRoomFileDownloadUrlTool(config),
|
|
25
|
+
];
|
|
26
|
+
if (!readOnly) {
|
|
27
|
+
tools.push(new WriteRoomFileTool(config), new CopyRoomFileTool(config), new DeleteRoomFileTool(config));
|
|
28
|
+
}
|
|
29
|
+
return tools;
|
|
30
|
+
}
|
|
31
|
+
class RoomStorageToolkitConfig {
|
|
32
|
+
constructor({ client, projectId, rooms, maxLength, }) {
|
|
33
|
+
if (rooms.length === 0) {
|
|
34
|
+
throw new TypeError("RoomStorageToolkit requires at least one allowed room.");
|
|
35
|
+
}
|
|
36
|
+
if (maxLength <= 0) {
|
|
37
|
+
throw new TypeError("maxLength must be positive.");
|
|
38
|
+
}
|
|
39
|
+
this.client = client;
|
|
40
|
+
this.projectId = projectId;
|
|
41
|
+
this.rooms = rooms;
|
|
42
|
+
this.allowedRooms = new Set(rooms);
|
|
43
|
+
this.maxLength = maxLength;
|
|
44
|
+
}
|
|
45
|
+
requireRoom(value) {
|
|
46
|
+
return this.requireRoomField(value, "room_name");
|
|
47
|
+
}
|
|
48
|
+
requireRoomField(value, field) {
|
|
49
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
50
|
+
throw new RoomServerException(`${field} is required.`);
|
|
51
|
+
}
|
|
52
|
+
const roomName = value.trim();
|
|
53
|
+
if (!this.allowedRooms.has(roomName)) {
|
|
54
|
+
throw new RoomServerException(`room '${roomName}' is not shared with this toolkit.`);
|
|
55
|
+
}
|
|
56
|
+
return roomName;
|
|
57
|
+
}
|
|
58
|
+
requirePath(value) {
|
|
59
|
+
return this.requirePathField(value, "path");
|
|
60
|
+
}
|
|
61
|
+
requirePathField(value, field) {
|
|
62
|
+
if (typeof value !== "string") {
|
|
63
|
+
throw new RoomServerException(`${field} is required.`);
|
|
64
|
+
}
|
|
65
|
+
const path = value.trim();
|
|
66
|
+
const parts = path.split("/").filter((part) => part !== "");
|
|
67
|
+
if (parts.some((part) => part === "." || part === "..")) {
|
|
68
|
+
throw new RoomServerException(`${field} may not contain . or .. segments.`);
|
|
69
|
+
}
|
|
70
|
+
return path;
|
|
71
|
+
}
|
|
72
|
+
async withRoom(roomName, callback) {
|
|
73
|
+
const connection = await this.client.connectRoom(this.projectId, roomName);
|
|
74
|
+
const room = new RoomClient({
|
|
75
|
+
protocolFactory: WebSocketClientProtocol.createFactory({
|
|
76
|
+
url: connection.roomUrl,
|
|
77
|
+
token: connection.jwt,
|
|
78
|
+
}),
|
|
79
|
+
reconnectTimeout: 0,
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
await room.start();
|
|
83
|
+
await room.ready;
|
|
84
|
+
return await callback(room);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
room.dispose();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
class RoomStorageTool extends Tool {
|
|
92
|
+
constructor(config, params) {
|
|
93
|
+
super({
|
|
94
|
+
name: params.name,
|
|
95
|
+
title: params.title,
|
|
96
|
+
description: params.description,
|
|
97
|
+
inputSchema: schema(config.rooms, params.properties, params.requiredFields),
|
|
98
|
+
outputSpec: params.outputSpec,
|
|
99
|
+
});
|
|
100
|
+
this.config = config;
|
|
101
|
+
}
|
|
102
|
+
roomName(args) {
|
|
103
|
+
return this.config.requireRoom(args.room_name);
|
|
104
|
+
}
|
|
105
|
+
path(args) {
|
|
106
|
+
return this.config.requirePath(args.path);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
class ListRoomFilesTool extends RoomStorageTool {
|
|
110
|
+
constructor(config) {
|
|
111
|
+
super(config, {
|
|
112
|
+
name: "list_files_in_room",
|
|
113
|
+
title: "list files in room",
|
|
114
|
+
description: "list files in shared room storage",
|
|
115
|
+
properties: {
|
|
116
|
+
path: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "The room storage directory path to list.",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
requiredFields: ["path"],
|
|
122
|
+
outputSpec: new ToolContentSpec({ types: ["json"], stream: false }),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async execute(args) {
|
|
126
|
+
const selectedRoom = this.roomName(args);
|
|
127
|
+
const selectedPath = this.path(args);
|
|
128
|
+
return await this.config.withRoom(selectedRoom, async (room) => {
|
|
129
|
+
const entries = await room.storage.list(selectedPath);
|
|
130
|
+
return new JsonContent({
|
|
131
|
+
json: { files: entries.map(storageEntryJson) },
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
class ReadRoomFileTool extends RoomStorageTool {
|
|
137
|
+
constructor(config) {
|
|
138
|
+
super(config, {
|
|
139
|
+
name: "read_file",
|
|
140
|
+
title: "read file",
|
|
141
|
+
description: "read a file from shared room storage",
|
|
142
|
+
properties: {
|
|
143
|
+
path: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "The room storage file path to read.",
|
|
146
|
+
},
|
|
147
|
+
offset: {
|
|
148
|
+
type: ["integer", "null"],
|
|
149
|
+
description: "Optional character offset for text files.",
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
requiredFields: ["path", "offset"],
|
|
153
|
+
outputSpec: new ToolContentSpec({
|
|
154
|
+
types: ["text", "file"],
|
|
155
|
+
stream: false,
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async execute(args) {
|
|
160
|
+
const selectedRoom = this.roomName(args);
|
|
161
|
+
const selectedPath = this.path(args);
|
|
162
|
+
const offset = optionalNonNegativeInt(args.offset, "offset") ?? 0;
|
|
163
|
+
return await this.config.withRoom(selectedRoom, async (room) => {
|
|
164
|
+
const content = await room.storage.download(selectedPath);
|
|
165
|
+
if (!isTextLike(content)) {
|
|
166
|
+
return content;
|
|
167
|
+
}
|
|
168
|
+
const text = new TextDecoder().decode(content.data);
|
|
169
|
+
return new TextContent({
|
|
170
|
+
text: sliceText(text, offset, this.config.maxLength),
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
class GrepRoomFileTool extends RoomStorageTool {
|
|
176
|
+
constructor(config) {
|
|
177
|
+
super(config, {
|
|
178
|
+
name: "grep_file",
|
|
179
|
+
title: "grep file",
|
|
180
|
+
description: "search a text file in shared room storage with a regular expression",
|
|
181
|
+
properties: {
|
|
182
|
+
path: {
|
|
183
|
+
type: "string",
|
|
184
|
+
description: "The room storage file path to search.",
|
|
185
|
+
},
|
|
186
|
+
pattern: {
|
|
187
|
+
type: "string",
|
|
188
|
+
description: "A JavaScript regular expression pattern.",
|
|
189
|
+
},
|
|
190
|
+
offset: {
|
|
191
|
+
type: ["integer", "null"],
|
|
192
|
+
description: "Optional match offset.",
|
|
193
|
+
},
|
|
194
|
+
before: {
|
|
195
|
+
type: ["integer", "null"],
|
|
196
|
+
description: "Optional number of context lines before each match.",
|
|
197
|
+
},
|
|
198
|
+
after: {
|
|
199
|
+
type: ["integer", "null"],
|
|
200
|
+
description: "Optional number of context lines after each match.",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
requiredFields: ["path", "pattern", "offset", "before", "after"],
|
|
204
|
+
outputSpec: new ToolContentSpec({ types: ["text"], stream: false }),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
async execute(args) {
|
|
208
|
+
const selectedRoom = this.roomName(args);
|
|
209
|
+
const selectedPath = this.path(args);
|
|
210
|
+
if (typeof args.pattern !== "string" || args.pattern === "") {
|
|
211
|
+
throw new RoomServerException("pattern is required.");
|
|
212
|
+
}
|
|
213
|
+
const offset = optionalNonNegativeInt(args.offset, "offset") ?? 0;
|
|
214
|
+
const before = optionalNonNegativeInt(args.before, "before") ?? 0;
|
|
215
|
+
const after = optionalNonNegativeInt(args.after, "after") ?? 0;
|
|
216
|
+
const pattern = new RegExp(args.pattern);
|
|
217
|
+
return await this.config.withRoom(selectedRoom, async (room) => {
|
|
218
|
+
const content = await room.storage.download(selectedPath);
|
|
219
|
+
if (!isTextLike(content)) {
|
|
220
|
+
return new TextContent({
|
|
221
|
+
text: "grep_file only supports text-like files. Use read_file for binary files.",
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
const text = new TextDecoder().decode(content.data);
|
|
225
|
+
return new TextContent({
|
|
226
|
+
text: grepText(text, pattern, offset, before, after, this.config.maxLength),
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
class GetRoomFileDownloadUrlTool extends RoomStorageTool {
|
|
232
|
+
constructor(config) {
|
|
233
|
+
super(config, {
|
|
234
|
+
name: "get_file_download_url",
|
|
235
|
+
title: "get file download url",
|
|
236
|
+
description: "get a temporary download URL for a file in shared room storage",
|
|
237
|
+
properties: {
|
|
238
|
+
path: {
|
|
239
|
+
type: "string",
|
|
240
|
+
description: "The room storage file path.",
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
requiredFields: ["path"],
|
|
244
|
+
outputSpec: new ToolContentSpec({ types: ["link"], stream: false }),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
async execute(args) {
|
|
248
|
+
const selectedRoom = this.roomName(args);
|
|
249
|
+
const selectedPath = this.path(args);
|
|
250
|
+
return await this.config.withRoom(selectedRoom, async (room) => {
|
|
251
|
+
const url = await room.storage.downloadUrl(selectedPath);
|
|
252
|
+
return new LinkContent({ url, name: basename(selectedPath) });
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
class WriteRoomFileTool extends RoomStorageTool {
|
|
257
|
+
constructor(config) {
|
|
258
|
+
super(config, {
|
|
259
|
+
name: "write_file",
|
|
260
|
+
title: "write file",
|
|
261
|
+
description: "write a text file to shared room storage",
|
|
262
|
+
properties: {
|
|
263
|
+
path: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "The room storage file path to write.",
|
|
266
|
+
},
|
|
267
|
+
text: { type: "string", description: "The text content to write." },
|
|
268
|
+
overwrite: {
|
|
269
|
+
type: "boolean",
|
|
270
|
+
description: "Whether to overwrite an existing file.",
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
requiredFields: ["path", "text", "overwrite"],
|
|
274
|
+
outputSpec: new ToolContentSpec({ types: ["text"], stream: false }),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
async execute(args) {
|
|
278
|
+
const selectedRoom = this.roomName(args);
|
|
279
|
+
const selectedPath = this.path(args);
|
|
280
|
+
if (typeof args.text !== "string") {
|
|
281
|
+
throw new RoomServerException("text is required.");
|
|
282
|
+
}
|
|
283
|
+
return await this.config.withRoom(selectedRoom, async (room) => {
|
|
284
|
+
await room.storage.upload(selectedPath, new TextEncoder().encode(args.text), {
|
|
285
|
+
overwrite: args.overwrite === true,
|
|
286
|
+
name: basename(selectedPath),
|
|
287
|
+
mimeType: "text/plain",
|
|
288
|
+
});
|
|
289
|
+
return new TextContent({ text: "the file was saved" });
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
class DeleteRoomFileTool extends RoomStorageTool {
|
|
294
|
+
constructor(config) {
|
|
295
|
+
super(config, {
|
|
296
|
+
name: "delete_file",
|
|
297
|
+
title: "delete file",
|
|
298
|
+
description: "delete a file or directory from shared room storage",
|
|
299
|
+
properties: {
|
|
300
|
+
path: {
|
|
301
|
+
type: "string",
|
|
302
|
+
description: "The room storage path to delete.",
|
|
303
|
+
},
|
|
304
|
+
recursive: {
|
|
305
|
+
type: "boolean",
|
|
306
|
+
description: "Whether to recursively delete directories.",
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
requiredFields: ["path", "recursive"],
|
|
310
|
+
outputSpec: new ToolContentSpec({ types: ["empty"], stream: false }),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async execute(args) {
|
|
314
|
+
const selectedRoom = this.roomName(args);
|
|
315
|
+
const selectedPath = this.path(args);
|
|
316
|
+
return await this.config.withRoom(selectedRoom, async (room) => {
|
|
317
|
+
await room.storage.delete(selectedPath, {
|
|
318
|
+
recursive: args.recursive === true,
|
|
319
|
+
});
|
|
320
|
+
return new EmptyContent();
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
class CopyRoomFileTool extends Tool {
|
|
325
|
+
constructor(config) {
|
|
326
|
+
super({
|
|
327
|
+
name: "copy_file",
|
|
328
|
+
title: "copy file",
|
|
329
|
+
description: "copy a file between explicitly shared room storage",
|
|
330
|
+
inputSchema: {
|
|
331
|
+
type: "object",
|
|
332
|
+
additionalProperties: false,
|
|
333
|
+
required: [
|
|
334
|
+
"source_room_name",
|
|
335
|
+
"source_path",
|
|
336
|
+
"destination_room_name",
|
|
337
|
+
"destination_path",
|
|
338
|
+
"overwrite",
|
|
339
|
+
],
|
|
340
|
+
properties: {
|
|
341
|
+
source_room_name: {
|
|
342
|
+
type: "string",
|
|
343
|
+
enum: config.rooms,
|
|
344
|
+
description: "The shared room to copy the file from.",
|
|
345
|
+
},
|
|
346
|
+
source_path: {
|
|
347
|
+
type: "string",
|
|
348
|
+
description: "The room storage file path to copy from.",
|
|
349
|
+
},
|
|
350
|
+
destination_room_name: {
|
|
351
|
+
type: "string",
|
|
352
|
+
enum: config.rooms,
|
|
353
|
+
description: "The shared room to copy the file to.",
|
|
354
|
+
},
|
|
355
|
+
destination_path: {
|
|
356
|
+
type: "string",
|
|
357
|
+
description: "The room storage file path to copy to.",
|
|
358
|
+
},
|
|
359
|
+
overwrite: {
|
|
360
|
+
type: "boolean",
|
|
361
|
+
description: "Whether to overwrite an existing destination file.",
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
outputSpec: new ToolContentSpec({ types: ["text"], stream: false }),
|
|
366
|
+
});
|
|
367
|
+
this.config = config;
|
|
368
|
+
}
|
|
369
|
+
async execute(args) {
|
|
370
|
+
const sourceRoom = this.config.requireRoomField(args.source_room_name, "source_room_name");
|
|
371
|
+
const sourcePath = this.config.requirePathField(args.source_path, "source_path");
|
|
372
|
+
const destinationRoom = this.config.requireRoomField(args.destination_room_name, "destination_room_name");
|
|
373
|
+
const destinationPath = this.config.requirePathField(args.destination_path, "destination_path");
|
|
374
|
+
const overwrite = args.overwrite === true;
|
|
375
|
+
if (sourceRoom === destinationRoom) {
|
|
376
|
+
return await this.config.withRoom(sourceRoom, async (room) => {
|
|
377
|
+
const content = await room.storage.download(sourcePath);
|
|
378
|
+
await room.storage.upload(destinationPath, content.data, {
|
|
379
|
+
overwrite,
|
|
380
|
+
name: basename(destinationPath),
|
|
381
|
+
mimeType: content.mimeType,
|
|
382
|
+
});
|
|
383
|
+
return new TextContent({ text: "the file was copied" });
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
const content = await this.config.withRoom(sourceRoom, (room) => room.storage.download(sourcePath));
|
|
387
|
+
return await this.config.withRoom(destinationRoom, async (room) => {
|
|
388
|
+
await room.storage.upload(destinationPath, content.data, {
|
|
389
|
+
overwrite,
|
|
390
|
+
name: basename(destinationPath),
|
|
391
|
+
mimeType: content.mimeType,
|
|
392
|
+
});
|
|
393
|
+
return new TextContent({ text: "the file was copied" });
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function schema(rooms, properties, requiredFields) {
|
|
398
|
+
return {
|
|
399
|
+
type: "object",
|
|
400
|
+
additionalProperties: false,
|
|
401
|
+
required: ["room_name", ...requiredFields],
|
|
402
|
+
properties: {
|
|
403
|
+
room_name: {
|
|
404
|
+
type: "string",
|
|
405
|
+
enum: rooms,
|
|
406
|
+
description: "The shared room whose storage should be used.",
|
|
407
|
+
},
|
|
408
|
+
...properties,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function normalizeRooms(rooms) {
|
|
413
|
+
return [
|
|
414
|
+
...new Set(rooms.map((room) => room.trim()).filter((room) => room !== "")),
|
|
415
|
+
];
|
|
416
|
+
}
|
|
417
|
+
function storageEntryJson(entry) {
|
|
418
|
+
return {
|
|
419
|
+
name: entry.name,
|
|
420
|
+
is_folder: entry.isFolder,
|
|
421
|
+
...(entry.size != null ? { size: entry.size } : {}),
|
|
422
|
+
...(entry.createdAt != null
|
|
423
|
+
? { created_at: entry.createdAt.toISOString() }
|
|
424
|
+
: {}),
|
|
425
|
+
...(entry.updatedAt != null
|
|
426
|
+
? { updated_at: entry.updatedAt.toISOString() }
|
|
427
|
+
: {}),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function optionalNonNegativeInt(value, field) {
|
|
431
|
+
if (value == null)
|
|
432
|
+
return undefined;
|
|
433
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
434
|
+
throw new RoomServerException(`${field} must be a non-negative integer.`);
|
|
435
|
+
}
|
|
436
|
+
return value;
|
|
437
|
+
}
|
|
438
|
+
function isTextLike(content) {
|
|
439
|
+
const mimeType = content.mimeType.toLowerCase();
|
|
440
|
+
if (mimeType.startsWith("text/") ||
|
|
441
|
+
mimeType === "application/json" ||
|
|
442
|
+
mimeType === "application/xml" ||
|
|
443
|
+
mimeType === "image/svg+xml") {
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
const name = content.name.toLowerCase();
|
|
447
|
+
return [
|
|
448
|
+
".csv",
|
|
449
|
+
".htm",
|
|
450
|
+
".html",
|
|
451
|
+
".js",
|
|
452
|
+
".json",
|
|
453
|
+
".md",
|
|
454
|
+
".txt",
|
|
455
|
+
".ts",
|
|
456
|
+
".tsx",
|
|
457
|
+
".xml",
|
|
458
|
+
".yaml",
|
|
459
|
+
".yml",
|
|
460
|
+
].some((suffix) => name.endsWith(suffix));
|
|
461
|
+
}
|
|
462
|
+
function sliceText(text, offset, maxLength) {
|
|
463
|
+
const start = Math.min(offset, text.length);
|
|
464
|
+
const end = Math.min(start + maxLength, text.length);
|
|
465
|
+
const sliced = text.slice(start, end);
|
|
466
|
+
return end >= text.length
|
|
467
|
+
? sliced
|
|
468
|
+
: `${sliced}\n... truncated at ${end} of ${text.length} characters ...`;
|
|
469
|
+
}
|
|
470
|
+
function grepText(text, pattern, offset, before, after, maxLength) {
|
|
471
|
+
const lines = text.split("\n");
|
|
472
|
+
const selected = new Set();
|
|
473
|
+
let matchCount = 0;
|
|
474
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
475
|
+
if (!pattern.test(lines[index]))
|
|
476
|
+
continue;
|
|
477
|
+
pattern.lastIndex = 0;
|
|
478
|
+
if (matchCount < offset) {
|
|
479
|
+
matchCount += 1;
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
matchCount += 1;
|
|
483
|
+
for (let line = Math.max(0, index - before); line <= Math.min(lines.length - 1, index + after); line += 1) {
|
|
484
|
+
selected.add(line);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (selected.size === 0)
|
|
488
|
+
return "No matches found.";
|
|
489
|
+
const sorted = [...selected].sort((a, b) => a - b);
|
|
490
|
+
let previous = -2;
|
|
491
|
+
let output = "";
|
|
492
|
+
for (const index of sorted) {
|
|
493
|
+
if (previous >= 0 && index !== previous + 1)
|
|
494
|
+
output += "--\n";
|
|
495
|
+
output += `${index + 1}: ${lines[index]}\n`;
|
|
496
|
+
previous = index;
|
|
497
|
+
if (output.length > maxLength) {
|
|
498
|
+
return `${output.slice(0, maxLength)}\n... truncated ...`;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return output;
|
|
502
|
+
}
|
|
503
|
+
function basename(path) {
|
|
504
|
+
const parts = path
|
|
505
|
+
.replace(/\\/g, "/")
|
|
506
|
+
.split("/")
|
|
507
|
+
.filter((part) => part !== "");
|
|
508
|
+
return parts.length === 0 ? "download" : parts[parts.length - 1];
|
|
509
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Schema } from "apache-arrow";
|
|
2
|
+
import type { Participant, RoomClient } from "@meshagent/meshagent";
|
|
3
|
+
import { ChangeEmitter } from "./change-emitter.js";
|
|
4
|
+
import { BaseChatClient } from "./chat-client.js";
|
|
5
|
+
import { type AgentThreadMessage } from "./agent-messages.js";
|
|
6
|
+
export declare const defaultUntitledThreadName = "New Chat";
|
|
7
|
+
export declare class ThreadListEntry {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly createdAt: string;
|
|
11
|
+
readonly modifiedAt: string;
|
|
12
|
+
constructor({ path, name, createdAt, modifiedAt }: {
|
|
13
|
+
path: string;
|
|
14
|
+
name: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
modifiedAt: string;
|
|
17
|
+
});
|
|
18
|
+
renamed(name: string, modifiedAt: string): ThreadListEntry;
|
|
19
|
+
static fromDatasetRow(row: Record<string, unknown>): ThreadListEntry | null;
|
|
20
|
+
}
|
|
21
|
+
export declare abstract class ThreadStorage {
|
|
22
|
+
abstract get path(): string;
|
|
23
|
+
abstract start(): Promise<void>;
|
|
24
|
+
abstract stop(): Promise<void>;
|
|
25
|
+
abstract waitUntilReady(): Promise<void>;
|
|
26
|
+
abstract agentMessages(): AgentThreadMessage[];
|
|
27
|
+
abstract pushMessage({ message, sender }: {
|
|
28
|
+
message: AgentThreadMessage;
|
|
29
|
+
sender?: Participant;
|
|
30
|
+
}): void;
|
|
31
|
+
}
|
|
32
|
+
export declare abstract class ThreadStorageRepository extends ChangeEmitter {
|
|
33
|
+
abstract open(): Promise<void>;
|
|
34
|
+
abstract close(): Promise<void>;
|
|
35
|
+
abstract entries(): ThreadListEntry[];
|
|
36
|
+
abstract addOrUpdateThread(entry: ThreadListEntry): Promise<void>;
|
|
37
|
+
abstract deleteThread(threadPath: string): Promise<void>;
|
|
38
|
+
abstract renameThread(threadPath: string, name: string): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export declare class DatasetThreadListRef {
|
|
41
|
+
readonly namespace?: string[];
|
|
42
|
+
readonly table: string;
|
|
43
|
+
constructor({ namespace, table }: {
|
|
44
|
+
namespace?: string[];
|
|
45
|
+
table: string;
|
|
46
|
+
});
|
|
47
|
+
static parse(url: string): DatasetThreadListRef;
|
|
48
|
+
}
|
|
49
|
+
export declare class DatasetThreadStorageRepository extends ThreadStorageRepository {
|
|
50
|
+
static readonly schema: Schema<any>;
|
|
51
|
+
readonly room: RoomClient;
|
|
52
|
+
readonly path: string;
|
|
53
|
+
readonly ref: DatasetThreadListRef;
|
|
54
|
+
private readonly entriesByPath;
|
|
55
|
+
private abortController?;
|
|
56
|
+
private refreshEntriesFuture?;
|
|
57
|
+
private watchTask?;
|
|
58
|
+
private initialSnapshotReady;
|
|
59
|
+
private closed;
|
|
60
|
+
constructor({ room, path }: {
|
|
61
|
+
room: RoomClient;
|
|
62
|
+
path: string;
|
|
63
|
+
});
|
|
64
|
+
open(): Promise<void>;
|
|
65
|
+
private watchLoop;
|
|
66
|
+
close(): Promise<void>;
|
|
67
|
+
entries(): ThreadListEntry[];
|
|
68
|
+
addOrUpdateThread(entry: ThreadListEntry): Promise<void>;
|
|
69
|
+
deleteThread(threadPath: string): Promise<void>;
|
|
70
|
+
renameThread(threadPath: string, name: string): Promise<void>;
|
|
71
|
+
private ensureTable;
|
|
72
|
+
private handleWatchEvent;
|
|
73
|
+
private refreshEntries;
|
|
74
|
+
private batchFor;
|
|
75
|
+
}
|
|
76
|
+
export declare class AgentThreadStorageRepository extends ThreadStorageRepository {
|
|
77
|
+
readonly chatClient: BaseChatClient;
|
|
78
|
+
readonly limit: number;
|
|
79
|
+
private readonly entriesByPath;
|
|
80
|
+
private subscription?;
|
|
81
|
+
private pendingOpen?;
|
|
82
|
+
private pendingListMessageId?;
|
|
83
|
+
private closed;
|
|
84
|
+
constructor({ chatClient, limit }: {
|
|
85
|
+
chatClient: BaseChatClient;
|
|
86
|
+
limit?: number;
|
|
87
|
+
});
|
|
88
|
+
open(): Promise<void>;
|
|
89
|
+
close(): Promise<void>;
|
|
90
|
+
entries(): ThreadListEntry[];
|
|
91
|
+
addOrUpdateThread(_entry: ThreadListEntry): Promise<void>;
|
|
92
|
+
deleteThread(threadPath: string): Promise<void>;
|
|
93
|
+
renameThread(threadPath: string, name: string): Promise<void>;
|
|
94
|
+
private requestList;
|
|
95
|
+
private handleEvent;
|
|
96
|
+
private handleThreadsListed;
|
|
97
|
+
private upsertThread;
|
|
98
|
+
private entryFromAgentThread;
|
|
99
|
+
}
|
|
100
|
+
export declare class DatasetThreadStorage extends DatasetThreadStorageRepository {
|
|
101
|
+
}
|
|
102
|
+
export declare function defaultThreadDisplayNameFromPath(path: string): string;
|