@cortask/core 0.2.36 → 0.2.38
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/index.d.ts +65 -1
- package/dist/index.js +684 -108
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -934,6 +934,8 @@ function createProvider(id, apiKey) {
|
|
|
934
934
|
|
|
935
935
|
// src/agent/runner.ts
|
|
936
936
|
import crypto2 from "crypto";
|
|
937
|
+
import fs2 from "fs/promises";
|
|
938
|
+
import nodePath from "path";
|
|
937
939
|
|
|
938
940
|
// src/agent/system-prompt.ts
|
|
939
941
|
function buildSystemPrompt(ctx) {
|
|
@@ -960,7 +962,12 @@ Current date and time: ${(/* @__PURE__ */ new Date()).toLocaleString("en-US", {
|
|
|
960
962
|
- If a task requires multiple steps, break it down and work through each step
|
|
961
963
|
- After creating or generating a file the user would want to download (documents, images, exports, etc.), always call show_file so it appears as a downloadable card in the chat
|
|
962
964
|
- When the user wants to view file contents inline (e.g. "show me the file here"), use the artifact tool with the path parameter to read from disk. NEVER re-output file contents as the content parameter \u2014 use path instead so it loads instantly
|
|
963
|
-
-
|
|
965
|
+
- **Memory system**: You have two memory mechanisms:
|
|
966
|
+
- **Pinned notes** (memory.md): A concise, curated summary of the most important context. Read via memory_read, rewritten via memory_save. Keep this short \u2014 it's loaded into every conversation. Use memory_save to consolidate when it grows too long.
|
|
967
|
+
- **Searchable memory** (database): Individual facts indexed for search. Use memory_append to save a note (appends to pinned notes AND indexes in the database). Use memory_search to recall previously saved context.
|
|
968
|
+
- When the user shares personal info, preferences, or important context, use memory_append to save it. Use global scope for user info (name, preferences), project scope for project-specific context. Don't ask \u2014 just save and briefly acknowledge.
|
|
969
|
+
- When a topic might relate to prior conversations, use memory_search to check for relevant memories before answering.
|
|
970
|
+
- If memory.md gets long, use memory_read then memory_save to consolidate it into a concise summary. Don't let it become a log \u2014 keep it to key facts only.
|
|
964
971
|
|
|
965
972
|
## File Organization
|
|
966
973
|
When creating or saving files, organize them into logical subdirectories rather than dumping everything in the workspace root. Choose a clear, descriptive directory structure based on the content. For example:
|
|
@@ -1188,12 +1195,13 @@ var AgentRunner = class {
|
|
|
1188
1195
|
};
|
|
1189
1196
|
return buildSystemPrompt(ctx);
|
|
1190
1197
|
}
|
|
1191
|
-
createToolContext(sessionId, runId) {
|
|
1198
|
+
createToolContext(sessionId, runId, workspaceId) {
|
|
1192
1199
|
return {
|
|
1193
1200
|
workspacePath: this.deps.getWorkspacePath(),
|
|
1194
1201
|
dataDir: this.deps.getDataDir(),
|
|
1195
1202
|
sessionId,
|
|
1196
1203
|
runId,
|
|
1204
|
+
workspaceId,
|
|
1197
1205
|
requestPermission: async (req) => {
|
|
1198
1206
|
if (this.deps.onPermissionRequest) {
|
|
1199
1207
|
return this.deps.onPermissionRequest(req);
|
|
@@ -1205,22 +1213,53 @@ var AgentRunner = class {
|
|
|
1205
1213
|
return this.deps.onQuestionnaireRequest(req);
|
|
1206
1214
|
}
|
|
1207
1215
|
return {};
|
|
1208
|
-
}
|
|
1216
|
+
},
|
|
1217
|
+
memoryManager: this.deps.memoryManager
|
|
1209
1218
|
};
|
|
1210
1219
|
}
|
|
1220
|
+
async readFileReferences(refs) {
|
|
1221
|
+
if (!refs?.length) return [];
|
|
1222
|
+
const workspacePath = this.deps.getWorkspacePath();
|
|
1223
|
+
const parts = [];
|
|
1224
|
+
const MAX_FILE_SIZE = 50 * 1024;
|
|
1225
|
+
for (const ref of refs) {
|
|
1226
|
+
const fullPath = nodePath.resolve(workspacePath, ref);
|
|
1227
|
+
if (!fullPath.startsWith(nodePath.resolve(workspacePath))) continue;
|
|
1228
|
+
try {
|
|
1229
|
+
const stat = await fs2.stat(fullPath);
|
|
1230
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
1231
|
+
parts.push({ type: "text", text: `[File: ${ref}]
|
|
1232
|
+
(Skipped \u2014 file exceeds 50KB limit)` });
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
const content = await fs2.readFile(fullPath, "utf-8");
|
|
1236
|
+
parts.push({ type: "text", text: `[File: ${ref}]
|
|
1237
|
+
\`\`\`
|
|
1238
|
+
${content}
|
|
1239
|
+
\`\`\`` });
|
|
1240
|
+
} catch {
|
|
1241
|
+
parts.push({ type: "text", text: `[File: ${ref}]
|
|
1242
|
+
(File not found or unreadable)` });
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return parts;
|
|
1246
|
+
}
|
|
1211
1247
|
async run(params) {
|
|
1212
1248
|
const runId = crypto2.randomUUID();
|
|
1213
1249
|
const sessionId = params.sessionId ?? crypto2.randomUUID();
|
|
1214
1250
|
const { config } = this.deps;
|
|
1215
1251
|
const messages = await this.deps.getSessionMessages(sessionId);
|
|
1216
|
-
|
|
1252
|
+
const fileRefParts = await this.readFileReferences(params.fileReferences);
|
|
1253
|
+
const hasMultipart = (params.attachments?.length ?? 0) > 0 || fileRefParts.length > 0;
|
|
1254
|
+
if (hasMultipart) {
|
|
1217
1255
|
const parts = [
|
|
1218
1256
|
{ type: "text", text: params.prompt },
|
|
1219
|
-
...
|
|
1257
|
+
...fileRefParts,
|
|
1258
|
+
...params.attachments?.map((a) => ({
|
|
1220
1259
|
type: "image",
|
|
1221
1260
|
imageBase64: a.base64,
|
|
1222
1261
|
mimeType: a.mimeType
|
|
1223
|
-
}))
|
|
1262
|
+
})) ?? []
|
|
1224
1263
|
];
|
|
1225
1264
|
messages.push({ role: "user", content: parts });
|
|
1226
1265
|
} else {
|
|
@@ -1228,7 +1267,7 @@ var AgentRunner = class {
|
|
|
1228
1267
|
}
|
|
1229
1268
|
const systemPrompt = await this.buildSystemPromptText();
|
|
1230
1269
|
const toolDefs = this.buildToolDefinitions();
|
|
1231
|
-
const toolContext = this.createToolContext(sessionId, runId);
|
|
1270
|
+
const toolContext = this.createToolContext(sessionId, runId, params.workspaceId);
|
|
1232
1271
|
let totalInputTokens = 0;
|
|
1233
1272
|
let totalOutputTokens = 0;
|
|
1234
1273
|
const allToolCalls = [];
|
|
@@ -1335,14 +1374,17 @@ var AgentRunner = class {
|
|
|
1335
1374
|
const { config } = this.deps;
|
|
1336
1375
|
const signal = params.signal;
|
|
1337
1376
|
const messages = await this.deps.getSessionMessages(sessionId);
|
|
1338
|
-
|
|
1377
|
+
const streamFileRefParts = await this.readFileReferences(params.fileReferences);
|
|
1378
|
+
const streamHasMultipart = (params.attachments?.length ?? 0) > 0 || streamFileRefParts.length > 0;
|
|
1379
|
+
if (streamHasMultipart) {
|
|
1339
1380
|
const parts = [
|
|
1340
1381
|
{ type: "text", text: params.prompt },
|
|
1341
|
-
...
|
|
1382
|
+
...streamFileRefParts,
|
|
1383
|
+
...params.attachments?.map((a) => ({
|
|
1342
1384
|
type: "image",
|
|
1343
1385
|
imageBase64: a.base64,
|
|
1344
1386
|
mimeType: a.mimeType
|
|
1345
|
-
}))
|
|
1387
|
+
})) ?? []
|
|
1346
1388
|
];
|
|
1347
1389
|
messages.push({ role: "user", content: parts });
|
|
1348
1390
|
} else {
|
|
@@ -1350,7 +1392,7 @@ var AgentRunner = class {
|
|
|
1350
1392
|
}
|
|
1351
1393
|
const systemPrompt = await this.buildSystemPromptText();
|
|
1352
1394
|
const toolDefs = this.buildToolDefinitions();
|
|
1353
|
-
const toolContext = this.createToolContext(sessionId, runId);
|
|
1395
|
+
const toolContext = this.createToolContext(sessionId, runId, params.workspaceId);
|
|
1354
1396
|
let totalInputTokens = 0;
|
|
1355
1397
|
let totalOutputTokens = 0;
|
|
1356
1398
|
let finalContent = "";
|
|
@@ -1595,7 +1637,7 @@ var AgentRunner = class {
|
|
|
1595
1637
|
};
|
|
1596
1638
|
|
|
1597
1639
|
// src/agent/tools/read-file.ts
|
|
1598
|
-
import
|
|
1640
|
+
import fs3 from "fs/promises";
|
|
1599
1641
|
import path2 from "path";
|
|
1600
1642
|
var readFileTool = {
|
|
1601
1643
|
definition: {
|
|
@@ -1622,7 +1664,7 @@ var readFileTool = {
|
|
|
1622
1664
|
};
|
|
1623
1665
|
}
|
|
1624
1666
|
try {
|
|
1625
|
-
const content = await
|
|
1667
|
+
const content = await fs3.readFile(filePath, "utf-8");
|
|
1626
1668
|
return { toolCallId: "", content };
|
|
1627
1669
|
} catch (err) {
|
|
1628
1670
|
return {
|
|
@@ -1635,7 +1677,7 @@ var readFileTool = {
|
|
|
1635
1677
|
};
|
|
1636
1678
|
|
|
1637
1679
|
// src/agent/tools/write-file.ts
|
|
1638
|
-
import
|
|
1680
|
+
import fs4 from "fs/promises";
|
|
1639
1681
|
import path3 from "path";
|
|
1640
1682
|
var writeFileTool = {
|
|
1641
1683
|
definition: {
|
|
@@ -1679,8 +1721,8 @@ var writeFileTool = {
|
|
|
1679
1721
|
};
|
|
1680
1722
|
}
|
|
1681
1723
|
try {
|
|
1682
|
-
await
|
|
1683
|
-
await
|
|
1724
|
+
await fs4.mkdir(path3.dirname(filePath), { recursive: true });
|
|
1725
|
+
await fs4.writeFile(filePath, args.content, "utf-8");
|
|
1684
1726
|
return {
|
|
1685
1727
|
toolCallId: "",
|
|
1686
1728
|
content: `File written: ${args.path}`
|
|
@@ -1696,7 +1738,7 @@ var writeFileTool = {
|
|
|
1696
1738
|
};
|
|
1697
1739
|
|
|
1698
1740
|
// src/agent/tools/list-files.ts
|
|
1699
|
-
import
|
|
1741
|
+
import fs5 from "fs/promises";
|
|
1700
1742
|
import path4 from "path";
|
|
1701
1743
|
var listFilesTool = {
|
|
1702
1744
|
definition: {
|
|
@@ -1725,7 +1767,7 @@ var listFilesTool = {
|
|
|
1725
1767
|
};
|
|
1726
1768
|
}
|
|
1727
1769
|
try {
|
|
1728
|
-
const entries = await
|
|
1770
|
+
const entries = await fs5.readdir(dirPath, { withFileTypes: true });
|
|
1729
1771
|
const lines = entries.map((e) => {
|
|
1730
1772
|
const suffix = e.isDirectory() ? "/" : "";
|
|
1731
1773
|
return `${e.name}${suffix}`;
|
|
@@ -2073,9 +2115,11 @@ var questionnaireTool = {
|
|
|
2073
2115
|
};
|
|
2074
2116
|
|
|
2075
2117
|
// src/agent/tools/memory.ts
|
|
2076
|
-
import
|
|
2118
|
+
import crypto3 from "crypto";
|
|
2119
|
+
import fs6 from "fs/promises";
|
|
2077
2120
|
import path5 from "path";
|
|
2078
2121
|
var CORTASK_DIR = ".cortask";
|
|
2122
|
+
var MEMORY_FILE_WARNING_LINES = 200;
|
|
2079
2123
|
function getMemoryPath(scope, context) {
|
|
2080
2124
|
if (scope === "global") {
|
|
2081
2125
|
return path5.join(context.dataDir, "memory.md");
|
|
@@ -2101,7 +2145,7 @@ var memoryReadTool = {
|
|
|
2101
2145
|
const scope = args.scope || "project";
|
|
2102
2146
|
const memoryPath = getMemoryPath(scope, context);
|
|
2103
2147
|
try {
|
|
2104
|
-
const content = await
|
|
2148
|
+
const content = await fs6.readFile(memoryPath, "utf-8");
|
|
2105
2149
|
return { toolCallId: "", content };
|
|
2106
2150
|
} catch {
|
|
2107
2151
|
return {
|
|
@@ -2142,8 +2186,8 @@ var memorySaveTool = {
|
|
|
2142
2186
|
}
|
|
2143
2187
|
const scope = args.scope || "project";
|
|
2144
2188
|
const memoryPath = getMemoryPath(scope, context);
|
|
2145
|
-
await
|
|
2146
|
-
await
|
|
2189
|
+
await fs6.mkdir(path5.dirname(memoryPath), { recursive: true });
|
|
2190
|
+
await fs6.writeFile(memoryPath, content, "utf-8");
|
|
2147
2191
|
return {
|
|
2148
2192
|
toolCallId: "",
|
|
2149
2193
|
content: `${scope === "global" ? "Global" : "Project"} memory saved (${content.length} chars)`
|
|
@@ -2153,7 +2197,7 @@ var memorySaveTool = {
|
|
|
2153
2197
|
var memoryAppendTool = {
|
|
2154
2198
|
definition: {
|
|
2155
2199
|
name: "memory_append",
|
|
2156
|
-
description: "Append a note to long-term memory. Use scope 'global' for user preferences that apply across all projects, or 'project' (default) for project-specific context.",
|
|
2200
|
+
description: "Append a note to long-term memory. Use scope 'global' for user preferences that apply across all projects, or 'project' (default) for project-specific context. The note is also indexed in the searchable memory database.",
|
|
2157
2201
|
inputSchema: {
|
|
2158
2202
|
type: "object",
|
|
2159
2203
|
properties: {
|
|
@@ -2181,24 +2225,104 @@ var memoryAppendTool = {
|
|
|
2181
2225
|
}
|
|
2182
2226
|
const scope = args.scope || "project";
|
|
2183
2227
|
const memoryPath = getMemoryPath(scope, context);
|
|
2184
|
-
await
|
|
2228
|
+
await fs6.mkdir(path5.dirname(memoryPath), { recursive: true });
|
|
2185
2229
|
let existing = "";
|
|
2186
2230
|
try {
|
|
2187
|
-
existing = await
|
|
2231
|
+
existing = await fs6.readFile(memoryPath, "utf-8");
|
|
2188
2232
|
} catch {
|
|
2189
2233
|
existing = scope === "global" ? "# Global Memory\n\nUser preferences and context that apply across all projects.\n" : "# Project Memory\n\nThis file is used by Cortask to remember important context about this project.\n";
|
|
2190
2234
|
}
|
|
2191
2235
|
const updated = existing.trimEnd() + "\n\n" + note + "\n";
|
|
2192
|
-
await
|
|
2236
|
+
await fs6.writeFile(memoryPath, updated, "utf-8");
|
|
2237
|
+
if (context.memoryManager && scope === "project") {
|
|
2238
|
+
try {
|
|
2239
|
+
await context.memoryManager.index([
|
|
2240
|
+
{
|
|
2241
|
+
id: crypto3.randomUUID(),
|
|
2242
|
+
content: note,
|
|
2243
|
+
source: "agent",
|
|
2244
|
+
sessionId: context.sessionId,
|
|
2245
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2246
|
+
metadata: { tool: "memory_append" }
|
|
2247
|
+
}
|
|
2248
|
+
]);
|
|
2249
|
+
} catch {
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
const lineCount = updated.split("\n").length;
|
|
2253
|
+
const warning = lineCount > MEMORY_FILE_WARNING_LINES ? ` Warning: memory.md is now ${lineCount} lines. Consider using memory_save to consolidate and rewrite it as a concise summary.` : "";
|
|
2193
2254
|
return {
|
|
2194
2255
|
toolCallId: "",
|
|
2195
|
-
content: `Note appended to ${scope} memory
|
|
2256
|
+
content: `Note appended to ${scope} memory.${warning}`
|
|
2196
2257
|
};
|
|
2197
2258
|
}
|
|
2198
2259
|
};
|
|
2260
|
+
var memorySearchTool = {
|
|
2261
|
+
definition: {
|
|
2262
|
+
name: "memory_search",
|
|
2263
|
+
description: "Search long-term memory using semantic and keyword search. Returns the most relevant memories matching the query. Use this to recall previously saved context, decisions, or notes.",
|
|
2264
|
+
inputSchema: {
|
|
2265
|
+
type: "object",
|
|
2266
|
+
properties: {
|
|
2267
|
+
query: {
|
|
2268
|
+
type: "string",
|
|
2269
|
+
description: "The search query to find relevant memories."
|
|
2270
|
+
},
|
|
2271
|
+
limit: {
|
|
2272
|
+
type: "number",
|
|
2273
|
+
description: "Maximum number of results to return (default: 5)."
|
|
2274
|
+
}
|
|
2275
|
+
},
|
|
2276
|
+
required: ["query"]
|
|
2277
|
+
}
|
|
2278
|
+
},
|
|
2279
|
+
async execute(args, context) {
|
|
2280
|
+
const query = args.query;
|
|
2281
|
+
if (!query) {
|
|
2282
|
+
return {
|
|
2283
|
+
toolCallId: "",
|
|
2284
|
+
content: "Error: query is required",
|
|
2285
|
+
isError: true
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
if (!context.memoryManager) {
|
|
2289
|
+
return {
|
|
2290
|
+
toolCallId: "",
|
|
2291
|
+
content: "Memory search is not available for this workspace."
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
const limit = typeof args.limit === "number" ? args.limit : 5;
|
|
2295
|
+
try {
|
|
2296
|
+
const results = await context.memoryManager.search(query, limit);
|
|
2297
|
+
if (results.length === 0) {
|
|
2298
|
+
return {
|
|
2299
|
+
toolCallId: "",
|
|
2300
|
+
content: "No memories found matching the query."
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
const formatted = results.map((r, i) => {
|
|
2304
|
+
const date = r.entry.createdAt ? new Date(r.entry.createdAt).toLocaleDateString() : "unknown";
|
|
2305
|
+
return `${i + 1}. [${r.matchType}] (score: ${r.score.toFixed(3)}, ${date})
|
|
2306
|
+
${r.entry.content}`;
|
|
2307
|
+
}).join("\n\n");
|
|
2308
|
+
return {
|
|
2309
|
+
toolCallId: "",
|
|
2310
|
+
content: `Found ${results.length} memories:
|
|
2311
|
+
|
|
2312
|
+
${formatted}`
|
|
2313
|
+
};
|
|
2314
|
+
} catch (err) {
|
|
2315
|
+
return {
|
|
2316
|
+
toolCallId: "",
|
|
2317
|
+
content: `Memory search error: ${err instanceof Error ? err.message : String(err)}`,
|
|
2318
|
+
isError: true
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
};
|
|
2199
2323
|
|
|
2200
2324
|
// src/agent/tools/show-file.ts
|
|
2201
|
-
import
|
|
2325
|
+
import fs7 from "fs/promises";
|
|
2202
2326
|
import path6 from "path";
|
|
2203
2327
|
var showFileTool = {
|
|
2204
2328
|
definition: {
|
|
@@ -2229,7 +2353,7 @@ var showFileTool = {
|
|
|
2229
2353
|
};
|
|
2230
2354
|
}
|
|
2231
2355
|
try {
|
|
2232
|
-
const stat = await
|
|
2356
|
+
const stat = await fs7.stat(filePath);
|
|
2233
2357
|
return {
|
|
2234
2358
|
toolCallId: "",
|
|
2235
2359
|
content: JSON.stringify({
|
|
@@ -2251,11 +2375,11 @@ var showFileTool = {
|
|
|
2251
2375
|
|
|
2252
2376
|
// src/agent/tools/data-file.ts
|
|
2253
2377
|
import path8 from "path";
|
|
2254
|
-
import
|
|
2378
|
+
import fs9 from "fs";
|
|
2255
2379
|
import Database from "better-sqlite3";
|
|
2256
2380
|
|
|
2257
2381
|
// src/data/parser.ts
|
|
2258
|
-
import
|
|
2382
|
+
import fs8 from "fs";
|
|
2259
2383
|
import path7 from "path";
|
|
2260
2384
|
var MAX_RAW_SIZE = 5e5;
|
|
2261
2385
|
var MAX_ROWS = 5e4;
|
|
@@ -2286,7 +2410,7 @@ function detectColumnTypes(data) {
|
|
|
2286
2410
|
});
|
|
2287
2411
|
}
|
|
2288
2412
|
function parseCsv(filePath, forceDelimiter) {
|
|
2289
|
-
let raw =
|
|
2413
|
+
let raw = fs8.readFileSync(filePath, "utf-8");
|
|
2290
2414
|
if (raw.length > MAX_RAW_SIZE) {
|
|
2291
2415
|
raw = raw.slice(0, MAX_RAW_SIZE);
|
|
2292
2416
|
const lastNewline = raw.lastIndexOf("\n");
|
|
@@ -2352,7 +2476,7 @@ function splitCsvLines(text, delimiter) {
|
|
|
2352
2476
|
return result;
|
|
2353
2477
|
}
|
|
2354
2478
|
function parseJson(filePath) {
|
|
2355
|
-
let raw =
|
|
2479
|
+
let raw = fs8.readFileSync(filePath, "utf-8");
|
|
2356
2480
|
if (raw.length > MAX_RAW_SIZE) {
|
|
2357
2481
|
throw new Error(
|
|
2358
2482
|
`JSON file too large (${(raw.length / 1024).toFixed(0)}KB, max ${MAX_RAW_SIZE / 1024}KB). Consider converting to CSV first.`
|
|
@@ -2472,7 +2596,7 @@ var dataFileTool = {
|
|
|
2472
2596
|
isError: true
|
|
2473
2597
|
};
|
|
2474
2598
|
}
|
|
2475
|
-
if (!
|
|
2599
|
+
if (!fs9.existsSync(filePath)) {
|
|
2476
2600
|
return {
|
|
2477
2601
|
toolCallId: "",
|
|
2478
2602
|
content: `File not found: ${rawPath}`,
|
|
@@ -2717,12 +2841,12 @@ function createCronTool(cronService) {
|
|
|
2717
2841
|
required: ["action"]
|
|
2718
2842
|
}
|
|
2719
2843
|
},
|
|
2720
|
-
execute: async (args,
|
|
2844
|
+
execute: async (args, context) => {
|
|
2721
2845
|
const action = args.action;
|
|
2722
2846
|
try {
|
|
2723
2847
|
switch (action) {
|
|
2724
2848
|
case "list": {
|
|
2725
|
-
const jobs = cronService.list();
|
|
2849
|
+
const jobs = cronService.list(context.workspaceId);
|
|
2726
2850
|
if (jobs.length === 0) {
|
|
2727
2851
|
return { toolCallId: "", content: "No cron jobs found." };
|
|
2728
2852
|
}
|
|
@@ -2756,7 +2880,8 @@ function createCronTool(cronService) {
|
|
|
2756
2880
|
schedule,
|
|
2757
2881
|
prompt,
|
|
2758
2882
|
enabled: args.enabled ?? true,
|
|
2759
|
-
delivery
|
|
2883
|
+
delivery,
|
|
2884
|
+
workspaceId: context.workspaceId
|
|
2760
2885
|
});
|
|
2761
2886
|
return {
|
|
2762
2887
|
toolCallId: "",
|
|
@@ -2879,7 +3004,7 @@ function buildSchedule(args) {
|
|
|
2879
3004
|
}
|
|
2880
3005
|
|
|
2881
3006
|
// src/agent/tools/artifact.ts
|
|
2882
|
-
import
|
|
3007
|
+
import fs10 from "fs/promises";
|
|
2883
3008
|
import path9 from "path";
|
|
2884
3009
|
function createArtifactTool(artifactStore) {
|
|
2885
3010
|
return {
|
|
@@ -2932,7 +3057,7 @@ function createArtifactTool(artifactStore) {
|
|
|
2932
3057
|
};
|
|
2933
3058
|
}
|
|
2934
3059
|
try {
|
|
2935
|
-
content = await
|
|
3060
|
+
content = await fs10.readFile(resolved, "utf-8");
|
|
2936
3061
|
} catch {
|
|
2937
3062
|
return {
|
|
2938
3063
|
toolCallId: "",
|
|
@@ -3077,9 +3202,9 @@ async function ensureBrowser() {
|
|
|
3077
3202
|
async screenshot() {
|
|
3078
3203
|
const tmpPath = `${process.env.TEMP || "/tmp"}/ab-screenshot-${Date.now()}.png`;
|
|
3079
3204
|
await exec2(["screenshot", tmpPath]);
|
|
3080
|
-
const
|
|
3081
|
-
const buf = await
|
|
3082
|
-
await
|
|
3205
|
+
const fs22 = await import("fs/promises");
|
|
3206
|
+
const buf = await fs22.readFile(tmpPath);
|
|
3207
|
+
await fs22.unlink(tmpPath).catch(() => {
|
|
3083
3208
|
});
|
|
3084
3209
|
return buf;
|
|
3085
3210
|
},
|
|
@@ -3408,7 +3533,7 @@ ${result.content}`,
|
|
|
3408
3533
|
}
|
|
3409
3534
|
|
|
3410
3535
|
// src/agent/tools/subagent.ts
|
|
3411
|
-
import
|
|
3536
|
+
import crypto4 from "crypto";
|
|
3412
3537
|
|
|
3413
3538
|
// src/agent/subagent/types.ts
|
|
3414
3539
|
var SUBAGENT_DEFAULTS = {
|
|
@@ -3545,7 +3670,7 @@ async function executeSpawn(args, context) {
|
|
|
3545
3670
|
isError: true
|
|
3546
3671
|
};
|
|
3547
3672
|
}
|
|
3548
|
-
const childRunId =
|
|
3673
|
+
const childRunId = crypto4.randomUUID();
|
|
3549
3674
|
const childSessionId = `subagent:${childRunId}`;
|
|
3550
3675
|
const abortController = new AbortController();
|
|
3551
3676
|
const record = {
|
|
@@ -3675,24 +3800,24 @@ ${lines.join("\n")}` };
|
|
|
3675
3800
|
}
|
|
3676
3801
|
|
|
3677
3802
|
// src/agent/tools/skill.ts
|
|
3678
|
-
import
|
|
3803
|
+
import fs15 from "fs/promises";
|
|
3679
3804
|
|
|
3680
3805
|
// src/skills/writer.ts
|
|
3681
|
-
import
|
|
3806
|
+
import fs13 from "fs/promises";
|
|
3682
3807
|
import path12 from "path";
|
|
3683
3808
|
|
|
3684
3809
|
// src/skills/loader.ts
|
|
3685
|
-
import
|
|
3810
|
+
import fs12 from "fs/promises";
|
|
3686
3811
|
import path11 from "path";
|
|
3687
3812
|
import matter from "gray-matter";
|
|
3688
3813
|
|
|
3689
3814
|
// src/skills/credential-schema.ts
|
|
3690
|
-
import
|
|
3815
|
+
import fs11 from "fs/promises";
|
|
3691
3816
|
import path10 from "path";
|
|
3692
3817
|
async function loadCredentialSchema(skillDir) {
|
|
3693
3818
|
const schemaPath = path10.join(skillDir, "credentials.json");
|
|
3694
3819
|
try {
|
|
3695
|
-
const raw = await
|
|
3820
|
+
const raw = await fs11.readFile(schemaPath, "utf-8");
|
|
3696
3821
|
const parsed = JSON.parse(raw);
|
|
3697
3822
|
if (!parsed.credentials || !Array.isArray(parsed.credentials)) {
|
|
3698
3823
|
return void 0;
|
|
@@ -3775,13 +3900,13 @@ function getEligibleSkills(skills) {
|
|
|
3775
3900
|
async function scanSkillDir(dir, source) {
|
|
3776
3901
|
const skills = [];
|
|
3777
3902
|
try {
|
|
3778
|
-
const entries = await
|
|
3903
|
+
const entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
3779
3904
|
for (const entry of entries) {
|
|
3780
3905
|
if (!entry.isDirectory()) continue;
|
|
3781
3906
|
const skillDir = path11.join(dir, entry.name);
|
|
3782
3907
|
const skillMdPath = path11.join(skillDir, "SKILL.md");
|
|
3783
3908
|
try {
|
|
3784
|
-
const raw = await
|
|
3909
|
+
const raw = await fs12.readFile(skillMdPath, "utf-8");
|
|
3785
3910
|
const parsed = matter(raw);
|
|
3786
3911
|
const manifest = parsed.data;
|
|
3787
3912
|
if (!manifest.name) {
|
|
@@ -3792,13 +3917,13 @@ async function scanSkillDir(dir, source) {
|
|
|
3792
3917
|
}
|
|
3793
3918
|
let skillSource = source;
|
|
3794
3919
|
try {
|
|
3795
|
-
await
|
|
3920
|
+
await fs12.access(path11.join(skillDir, ".origin"));
|
|
3796
3921
|
skillSource = "git";
|
|
3797
3922
|
} catch {
|
|
3798
3923
|
}
|
|
3799
3924
|
let hasCodeTools = false;
|
|
3800
3925
|
try {
|
|
3801
|
-
await
|
|
3926
|
+
await fs12.access(path11.join(skillDir, "index.js"));
|
|
3802
3927
|
hasCodeTools = true;
|
|
3803
3928
|
} catch {
|
|
3804
3929
|
}
|
|
@@ -3945,13 +4070,13 @@ async function createSkill(skillsDir, name, rawContent) {
|
|
|
3945
4070
|
if (nameErr) throw new Error(nameErr);
|
|
3946
4071
|
const skillDir = path12.join(skillsDir, name);
|
|
3947
4072
|
try {
|
|
3948
|
-
await
|
|
4073
|
+
await fs13.access(skillDir);
|
|
3949
4074
|
throw new Error(`Skill "${name}" already exists`);
|
|
3950
4075
|
} catch (err) {
|
|
3951
4076
|
if (err.message.includes("already exists")) throw err;
|
|
3952
4077
|
}
|
|
3953
|
-
await
|
|
3954
|
-
await
|
|
4078
|
+
await fs13.mkdir(skillDir, { recursive: true });
|
|
4079
|
+
await fs13.writeFile(path12.join(skillDir, "SKILL.md"), rawContent, "utf-8");
|
|
3955
4080
|
clearSkillCache();
|
|
3956
4081
|
return { name, path: skillDir };
|
|
3957
4082
|
}
|
|
@@ -3959,17 +4084,17 @@ async function updateSkill(skillsDir, name, rawContent) {
|
|
|
3959
4084
|
const skillDir = path12.join(skillsDir, name);
|
|
3960
4085
|
const skillMdPath = path12.join(skillDir, "SKILL.md");
|
|
3961
4086
|
try {
|
|
3962
|
-
await
|
|
4087
|
+
await fs13.access(skillMdPath);
|
|
3963
4088
|
} catch {
|
|
3964
4089
|
throw new Error(`Skill "${name}" not found`);
|
|
3965
4090
|
}
|
|
3966
|
-
await
|
|
4091
|
+
await fs13.writeFile(skillMdPath, rawContent, "utf-8");
|
|
3967
4092
|
clearSkillCache();
|
|
3968
4093
|
}
|
|
3969
4094
|
async function readSkillFile(skillsDir, name) {
|
|
3970
4095
|
const skillMdPath = path12.join(skillsDir, name, "SKILL.md");
|
|
3971
4096
|
try {
|
|
3972
|
-
return await
|
|
4097
|
+
return await fs13.readFile(skillMdPath, "utf-8");
|
|
3973
4098
|
} catch {
|
|
3974
4099
|
throw new Error(`Skill "${name}" not found`);
|
|
3975
4100
|
}
|
|
@@ -3977,10 +4102,10 @@ async function readSkillFile(skillsDir, name) {
|
|
|
3977
4102
|
|
|
3978
4103
|
// src/skills/installer.ts
|
|
3979
4104
|
import { execFile as execFile2 } from "child_process";
|
|
3980
|
-
import
|
|
4105
|
+
import fs14 from "fs/promises";
|
|
3981
4106
|
import path13 from "path";
|
|
3982
4107
|
async function installSkillFromGit(gitUrl, skillsDir) {
|
|
3983
|
-
await
|
|
4108
|
+
await fs14.mkdir(skillsDir, { recursive: true });
|
|
3984
4109
|
const repoName = gitUrl.split("/").pop()?.replace(/\.git$/, "");
|
|
3985
4110
|
if (!repoName) {
|
|
3986
4111
|
throw new Error(`Invalid git URL: ${gitUrl}`);
|
|
@@ -3990,27 +4115,27 @@ async function installSkillFromGit(gitUrl, skillsDir) {
|
|
|
3990
4115
|
}
|
|
3991
4116
|
const targetDir = path13.join(skillsDir, repoName);
|
|
3992
4117
|
try {
|
|
3993
|
-
await
|
|
4118
|
+
await fs14.access(targetDir);
|
|
3994
4119
|
throw new Error(`Skill "${repoName}" is already installed`);
|
|
3995
4120
|
} catch (err) {
|
|
3996
4121
|
if (err.message.includes("already installed")) throw err;
|
|
3997
4122
|
}
|
|
3998
4123
|
await execFileAsync("git", ["clone", "--depth", "1", gitUrl, targetDir]);
|
|
3999
4124
|
try {
|
|
4000
|
-
await
|
|
4125
|
+
await fs14.access(path13.join(targetDir, "SKILL.md"));
|
|
4001
4126
|
} catch {
|
|
4002
|
-
await
|
|
4127
|
+
await fs14.rm(targetDir, { recursive: true, force: true });
|
|
4003
4128
|
throw new Error(
|
|
4004
4129
|
`Invalid skill: no SKILL.md found in ${repoName}`
|
|
4005
4130
|
);
|
|
4006
4131
|
}
|
|
4007
|
-
await
|
|
4132
|
+
await fs14.writeFile(
|
|
4008
4133
|
path13.join(targetDir, ".origin"),
|
|
4009
4134
|
gitUrl,
|
|
4010
4135
|
"utf-8"
|
|
4011
4136
|
);
|
|
4012
4137
|
try {
|
|
4013
|
-
await
|
|
4138
|
+
await fs14.rm(path13.join(targetDir, ".git"), {
|
|
4014
4139
|
recursive: true,
|
|
4015
4140
|
force: true
|
|
4016
4141
|
});
|
|
@@ -4023,11 +4148,11 @@ async function installSkillFromGit(gitUrl, skillsDir) {
|
|
|
4023
4148
|
async function removeSkill(skillName, skillsDir) {
|
|
4024
4149
|
const targetDir = path13.join(skillsDir, skillName);
|
|
4025
4150
|
try {
|
|
4026
|
-
await
|
|
4151
|
+
await fs14.access(targetDir);
|
|
4027
4152
|
} catch {
|
|
4028
4153
|
throw new Error(`Skill "${skillName}" not found`);
|
|
4029
4154
|
}
|
|
4030
|
-
await
|
|
4155
|
+
await fs14.rm(targetDir, { recursive: true, force: true });
|
|
4031
4156
|
clearSkillCache();
|
|
4032
4157
|
logger.info(`Removed skill "${skillName}"`, "skills");
|
|
4033
4158
|
}
|
|
@@ -4119,7 +4244,7 @@ function createSkillTool(userSkillsDir, bundledSkillNames) {
|
|
|
4119
4244
|
}
|
|
4120
4245
|
case "list": {
|
|
4121
4246
|
try {
|
|
4122
|
-
const entries = await
|
|
4247
|
+
const entries = await fs15.readdir(userSkillsDir, {
|
|
4123
4248
|
withFileTypes: true
|
|
4124
4249
|
});
|
|
4125
4250
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -4186,22 +4311,23 @@ var builtinTools = [
|
|
|
4186
4311
|
memoryReadTool,
|
|
4187
4312
|
memorySaveTool,
|
|
4188
4313
|
memoryAppendTool,
|
|
4314
|
+
memorySearchTool,
|
|
4189
4315
|
showFileTool,
|
|
4190
4316
|
dataFileTool
|
|
4191
4317
|
];
|
|
4192
4318
|
|
|
4193
4319
|
// src/credentials/store.ts
|
|
4194
|
-
import
|
|
4195
|
-
import
|
|
4320
|
+
import crypto5 from "crypto";
|
|
4321
|
+
import fs16 from "fs/promises";
|
|
4196
4322
|
import path14 from "path";
|
|
4197
4323
|
function deriveKey(secret, salt) {
|
|
4198
|
-
return
|
|
4324
|
+
return crypto5.scryptSync(secret, salt, 32);
|
|
4199
4325
|
}
|
|
4200
4326
|
function encrypt(text, secret) {
|
|
4201
|
-
const salt =
|
|
4327
|
+
const salt = crypto5.randomBytes(16);
|
|
4202
4328
|
const key = deriveKey(secret, salt);
|
|
4203
|
-
const iv =
|
|
4204
|
-
const cipher =
|
|
4329
|
+
const iv = crypto5.randomBytes(12);
|
|
4330
|
+
const cipher = crypto5.createCipheriv("aes-256-gcm", key, iv);
|
|
4205
4331
|
const encrypted = Buffer.concat([
|
|
4206
4332
|
cipher.update(text, "utf-8"),
|
|
4207
4333
|
cipher.final()
|
|
@@ -4219,7 +4345,7 @@ function decrypt(enc, secret) {
|
|
|
4219
4345
|
const key = deriveKey(secret, salt);
|
|
4220
4346
|
const iv = Buffer.from(enc.iv, "hex");
|
|
4221
4347
|
const tag = Buffer.from(enc.tag, "hex");
|
|
4222
|
-
const decipher =
|
|
4348
|
+
const decipher = crypto5.createDecipheriv("aes-256-gcm", key, iv);
|
|
4223
4349
|
decipher.setAuthTag(tag);
|
|
4224
4350
|
const decrypted = Buffer.concat([
|
|
4225
4351
|
decipher.update(Buffer.from(enc.data, "hex")),
|
|
@@ -4238,7 +4364,7 @@ var EncryptedCredentialStore = class {
|
|
|
4238
4364
|
async load() {
|
|
4239
4365
|
if (this.store) return this.store;
|
|
4240
4366
|
try {
|
|
4241
|
-
const raw = await
|
|
4367
|
+
const raw = await fs16.readFile(this.filePath, "utf-8");
|
|
4242
4368
|
this.store = JSON.parse(raw);
|
|
4243
4369
|
} catch {
|
|
4244
4370
|
this.store = { version: 1, entries: {} };
|
|
@@ -4247,13 +4373,13 @@ var EncryptedCredentialStore = class {
|
|
|
4247
4373
|
}
|
|
4248
4374
|
async save() {
|
|
4249
4375
|
if (!this.store) return;
|
|
4250
|
-
await
|
|
4376
|
+
await fs16.mkdir(path14.dirname(this.filePath), { recursive: true });
|
|
4251
4377
|
const tmp = `${this.filePath}.${process.pid}.tmp`;
|
|
4252
|
-
await
|
|
4378
|
+
await fs16.writeFile(tmp, JSON.stringify(this.store, null, 2), {
|
|
4253
4379
|
encoding: "utf-8",
|
|
4254
4380
|
mode: 384
|
|
4255
4381
|
});
|
|
4256
|
-
await
|
|
4382
|
+
await fs16.rename(tmp, this.filePath);
|
|
4257
4383
|
}
|
|
4258
4384
|
async get(key) {
|
|
4259
4385
|
const store = await this.load();
|
|
@@ -4289,11 +4415,11 @@ async function getOrCreateSecret(dataDir) {
|
|
|
4289
4415
|
if (secretFromEnv) return secretFromEnv;
|
|
4290
4416
|
const keyPath = path14.join(dataDir, "master.key");
|
|
4291
4417
|
try {
|
|
4292
|
-
return await
|
|
4418
|
+
return await fs16.readFile(keyPath, "utf-8");
|
|
4293
4419
|
} catch {
|
|
4294
|
-
const key =
|
|
4295
|
-
await
|
|
4296
|
-
await
|
|
4420
|
+
const key = crypto5.randomBytes(32).toString("hex");
|
|
4421
|
+
await fs16.mkdir(path14.dirname(keyPath), { recursive: true });
|
|
4422
|
+
await fs16.writeFile(keyPath, key, { mode: 384 });
|
|
4297
4423
|
return key;
|
|
4298
4424
|
}
|
|
4299
4425
|
}
|
|
@@ -4303,7 +4429,7 @@ function credentialKey(category, ...parts) {
|
|
|
4303
4429
|
|
|
4304
4430
|
// src/config/schema.ts
|
|
4305
4431
|
import { z } from "zod";
|
|
4306
|
-
import
|
|
4432
|
+
import fs17 from "fs/promises";
|
|
4307
4433
|
import path15 from "path";
|
|
4308
4434
|
var providerConfigSchema = z.object({
|
|
4309
4435
|
model: z.string().optional()
|
|
@@ -4329,6 +4455,10 @@ var spendingLimitSchema = z.object({
|
|
|
4329
4455
|
maxCostUsd: z.number().nonnegative().optional(),
|
|
4330
4456
|
period: z.enum(["daily", "weekly", "monthly"]).default("monthly")
|
|
4331
4457
|
});
|
|
4458
|
+
var memoryConfigSchema = z.object({
|
|
4459
|
+
embeddingProvider: z.enum(["local", "openai", "google", "ollama"]).default("local"),
|
|
4460
|
+
embeddingModel: z.string().optional()
|
|
4461
|
+
});
|
|
4332
4462
|
var serverConfigSchema = z.object({
|
|
4333
4463
|
port: z.number().int().min(1).max(65535).default(3777),
|
|
4334
4464
|
host: z.string().default("127.0.0.1")
|
|
@@ -4353,11 +4483,12 @@ var cortaskConfigSchema = z.object({
|
|
|
4353
4483
|
spending: spendingLimitSchema.default({}),
|
|
4354
4484
|
channels: channelsConfigSchema.default({}),
|
|
4355
4485
|
skills: skillsConfigSchema.default({}),
|
|
4486
|
+
memory: memoryConfigSchema.default({}),
|
|
4356
4487
|
server: serverConfigSchema.default({})
|
|
4357
4488
|
});
|
|
4358
4489
|
async function loadConfig(configPath) {
|
|
4359
4490
|
try {
|
|
4360
|
-
const raw = await
|
|
4491
|
+
const raw = await fs17.readFile(configPath, "utf-8");
|
|
4361
4492
|
const resolved = raw.replace(/\$\{(\w+)\}/g, (_, name) => {
|
|
4362
4493
|
return process.env[name] ?? "";
|
|
4363
4494
|
});
|
|
@@ -4377,14 +4508,14 @@ async function loadConfig(configPath) {
|
|
|
4377
4508
|
}
|
|
4378
4509
|
async function saveConfig(configPath, config) {
|
|
4379
4510
|
const dir = path15.dirname(configPath);
|
|
4380
|
-
await
|
|
4511
|
+
await fs17.mkdir(dir, { recursive: true });
|
|
4381
4512
|
if (configPath.endsWith(".yaml") || configPath.endsWith(".yml")) {
|
|
4382
4513
|
const { stringify } = await import("yaml").catch(() => ({
|
|
4383
4514
|
stringify: (obj) => JSON.stringify(obj, null, 2)
|
|
4384
4515
|
}));
|
|
4385
|
-
await
|
|
4516
|
+
await fs17.writeFile(configPath, stringify(config), "utf-8");
|
|
4386
4517
|
} else {
|
|
4387
|
-
await
|
|
4518
|
+
await fs17.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
4388
4519
|
}
|
|
4389
4520
|
}
|
|
4390
4521
|
function getDataDir() {
|
|
@@ -4437,7 +4568,7 @@ function getDefaultModel(type) {
|
|
|
4437
4568
|
}
|
|
4438
4569
|
|
|
4439
4570
|
// src/workspace/manager.ts
|
|
4440
|
-
import
|
|
4571
|
+
import fs18 from "fs/promises";
|
|
4441
4572
|
import path16 from "path";
|
|
4442
4573
|
import Database2 from "better-sqlite3";
|
|
4443
4574
|
var CORTASK_DIR2 = ".cortask";
|
|
@@ -4506,12 +4637,12 @@ var WorkspaceManager = class {
|
|
|
4506
4637
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4507
4638
|
const absPath = rootPath ? path16.resolve(rootPath) : path16.join(this.dataDir, "projects", id);
|
|
4508
4639
|
const cortaskDir = path16.join(absPath, CORTASK_DIR2);
|
|
4509
|
-
await
|
|
4640
|
+
await fs18.mkdir(cortaskDir, { recursive: true });
|
|
4510
4641
|
const memoryPath = path16.join(cortaskDir, "memory.md");
|
|
4511
4642
|
try {
|
|
4512
|
-
await
|
|
4643
|
+
await fs18.access(memoryPath);
|
|
4513
4644
|
} catch {
|
|
4514
|
-
await
|
|
4645
|
+
await fs18.writeFile(
|
|
4515
4646
|
memoryPath,
|
|
4516
4647
|
"# Project Memory\n\nThis file is used by Cortask to remember important context about this project.\n",
|
|
4517
4648
|
"utf-8"
|
|
@@ -4576,27 +4707,27 @@ var WorkspaceManager = class {
|
|
|
4576
4707
|
async readMemory(workspacePath) {
|
|
4577
4708
|
const memoryPath = path16.join(workspacePath, CORTASK_DIR2, "memory.md");
|
|
4578
4709
|
try {
|
|
4579
|
-
return await
|
|
4710
|
+
return await fs18.readFile(memoryPath, "utf-8");
|
|
4580
4711
|
} catch {
|
|
4581
4712
|
return void 0;
|
|
4582
4713
|
}
|
|
4583
4714
|
}
|
|
4584
4715
|
async writeMemory(workspacePath, content) {
|
|
4585
4716
|
const memoryPath = path16.join(workspacePath, CORTASK_DIR2, "memory.md");
|
|
4586
|
-
await
|
|
4587
|
-
await
|
|
4717
|
+
await fs18.mkdir(path16.dirname(memoryPath), { recursive: true });
|
|
4718
|
+
await fs18.writeFile(memoryPath, content, "utf-8");
|
|
4588
4719
|
}
|
|
4589
4720
|
async readGlobalMemory(dataDir) {
|
|
4590
4721
|
const memoryPath = path16.join(dataDir, "memory.md");
|
|
4591
4722
|
try {
|
|
4592
|
-
return await
|
|
4723
|
+
return await fs18.readFile(memoryPath, "utf-8");
|
|
4593
4724
|
} catch {
|
|
4594
4725
|
return void 0;
|
|
4595
4726
|
}
|
|
4596
4727
|
}
|
|
4597
4728
|
async writeGlobalMemory(dataDir, content) {
|
|
4598
|
-
await
|
|
4599
|
-
await
|
|
4729
|
+
await fs18.mkdir(dataDir, { recursive: true });
|
|
4730
|
+
await fs18.writeFile(path16.join(dataDir, "memory.md"), content, "utf-8");
|
|
4600
4731
|
}
|
|
4601
4732
|
getSessionDbPath(workspacePath) {
|
|
4602
4733
|
return path16.join(workspacePath, CORTASK_DIR2, "sessions.db");
|
|
@@ -4715,9 +4846,9 @@ var SessionStore = class {
|
|
|
4715
4846
|
|
|
4716
4847
|
// src/session/migrate.ts
|
|
4717
4848
|
import Database4 from "better-sqlite3";
|
|
4718
|
-
import
|
|
4849
|
+
import fs19 from "fs";
|
|
4719
4850
|
function migrateSessionDatabase(dbPath) {
|
|
4720
|
-
if (!
|
|
4851
|
+
if (!fs19.existsSync(dbPath)) {
|
|
4721
4852
|
logger.debug(`Session database does not exist yet: ${dbPath}`, "migration");
|
|
4722
4853
|
return;
|
|
4723
4854
|
}
|
|
@@ -4799,7 +4930,7 @@ function migrateAllWorkspaces(workspaces) {
|
|
|
4799
4930
|
}
|
|
4800
4931
|
|
|
4801
4932
|
// src/skills/tools.ts
|
|
4802
|
-
import
|
|
4933
|
+
import fs20 from "fs/promises";
|
|
4803
4934
|
import path17 from "path";
|
|
4804
4935
|
async function buildSkillTools(skills, credentialStore) {
|
|
4805
4936
|
const toolDefs = [];
|
|
@@ -4930,12 +5061,12 @@ async function executeHttpTool(template, args, skill, credentialStore, workspace
|
|
|
4930
5061
|
};
|
|
4931
5062
|
}
|
|
4932
5063
|
const tempDir = path17.join(workspacePath, "_temp");
|
|
4933
|
-
await
|
|
5064
|
+
await fs20.mkdir(tempDir, { recursive: true });
|
|
4934
5065
|
const isJson = looksLikeJson(content);
|
|
4935
5066
|
const ext = isJson ? "json" : "txt";
|
|
4936
5067
|
const filename = `${template.name}_${Date.now()}.${ext}`;
|
|
4937
5068
|
const filePath = path17.join(tempDir, filename);
|
|
4938
|
-
await
|
|
5069
|
+
await fs20.writeFile(filePath, content, "utf-8");
|
|
4939
5070
|
const summary = buildResponseSummary(content, isJson);
|
|
4940
5071
|
return {
|
|
4941
5072
|
toolCallId: "",
|
|
@@ -5120,7 +5251,7 @@ async function revokeSkillOAuth2(skillName, credentialId, credentialStore) {
|
|
|
5120
5251
|
}
|
|
5121
5252
|
|
|
5122
5253
|
// src/cron/service.ts
|
|
5123
|
-
import
|
|
5254
|
+
import crypto6 from "crypto";
|
|
5124
5255
|
import Database5 from "better-sqlite3";
|
|
5125
5256
|
var MAX_TIMER_DELAY_MS = 6e4;
|
|
5126
5257
|
var MIN_REFIRE_GAP_MS = 2e3;
|
|
@@ -5288,7 +5419,7 @@ var CronService = class {
|
|
|
5288
5419
|
}
|
|
5289
5420
|
// CRUD
|
|
5290
5421
|
add(input) {
|
|
5291
|
-
const id =
|
|
5422
|
+
const id = crypto6.randomUUID();
|
|
5292
5423
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5293
5424
|
const job = {
|
|
5294
5425
|
id,
|
|
@@ -5460,13 +5591,13 @@ function rowToJob(row) {
|
|
|
5460
5591
|
}
|
|
5461
5592
|
|
|
5462
5593
|
// src/artifacts/store.ts
|
|
5463
|
-
import
|
|
5594
|
+
import crypto7 from "crypto";
|
|
5464
5595
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5465
5596
|
var ArtifactStore = class {
|
|
5466
5597
|
store = /* @__PURE__ */ new Map();
|
|
5467
5598
|
create(type, title, content) {
|
|
5468
5599
|
this.pruneExpired();
|
|
5469
|
-
const id =
|
|
5600
|
+
const id = crypto7.randomUUID();
|
|
5470
5601
|
const mimeType = getMimeType(type);
|
|
5471
5602
|
const artifact = {
|
|
5472
5603
|
id,
|
|
@@ -5765,6 +5896,449 @@ var TemplateStore = class {
|
|
|
5765
5896
|
this.db.prepare("DELETE FROM prompt_templates WHERE id = ?").run(id);
|
|
5766
5897
|
}
|
|
5767
5898
|
};
|
|
5899
|
+
|
|
5900
|
+
// src/memory/manager.ts
|
|
5901
|
+
import crypto9 from "crypto";
|
|
5902
|
+
|
|
5903
|
+
// src/memory/store.ts
|
|
5904
|
+
import fs21 from "fs";
|
|
5905
|
+
import path18 from "path";
|
|
5906
|
+
import Database9 from "better-sqlite3";
|
|
5907
|
+
var MemoryStore = class {
|
|
5908
|
+
db;
|
|
5909
|
+
ftsAvailable = false;
|
|
5910
|
+
constructor(dbPath) {
|
|
5911
|
+
const dir = path18.dirname(dbPath);
|
|
5912
|
+
if (!fs21.existsSync(dir)) {
|
|
5913
|
+
fs21.mkdirSync(dir, { recursive: true });
|
|
5914
|
+
}
|
|
5915
|
+
this.db = new Database9(dbPath);
|
|
5916
|
+
this.db.pragma("journal_mode = WAL");
|
|
5917
|
+
this.init();
|
|
5918
|
+
}
|
|
5919
|
+
init() {
|
|
5920
|
+
this.db.exec(`
|
|
5921
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
5922
|
+
id TEXT PRIMARY KEY,
|
|
5923
|
+
content TEXT NOT NULL,
|
|
5924
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
5925
|
+
session_id TEXT,
|
|
5926
|
+
created_at TEXT NOT NULL,
|
|
5927
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
5928
|
+
provider_model TEXT
|
|
5929
|
+
);
|
|
5930
|
+
`);
|
|
5931
|
+
this.db.exec(`
|
|
5932
|
+
CREATE TABLE IF NOT EXISTS embedding_cache (
|
|
5933
|
+
content_hash TEXT PRIMARY KEY,
|
|
5934
|
+
provider_model TEXT NOT NULL,
|
|
5935
|
+
embedding BLOB NOT NULL,
|
|
5936
|
+
created_at TEXT NOT NULL
|
|
5937
|
+
);
|
|
5938
|
+
`);
|
|
5939
|
+
try {
|
|
5940
|
+
this.db.exec(`
|
|
5941
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
|
|
5942
|
+
id UNINDEXED,
|
|
5943
|
+
content,
|
|
5944
|
+
source UNINDEXED,
|
|
5945
|
+
content='chunks',
|
|
5946
|
+
content_rowid='rowid'
|
|
5947
|
+
);
|
|
5948
|
+
`);
|
|
5949
|
+
this.ftsAvailable = true;
|
|
5950
|
+
} catch {
|
|
5951
|
+
this.ftsAvailable = false;
|
|
5952
|
+
}
|
|
5953
|
+
if (this.ftsAvailable) {
|
|
5954
|
+
this.db.exec(`
|
|
5955
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ai AFTER INSERT ON chunks BEGIN
|
|
5956
|
+
INSERT INTO chunks_fts(rowid, id, content, source)
|
|
5957
|
+
VALUES (new.rowid, new.id, new.content, new.source);
|
|
5958
|
+
END;
|
|
5959
|
+
CREATE TRIGGER IF NOT EXISTS chunks_ad AFTER DELETE ON chunks BEGIN
|
|
5960
|
+
INSERT INTO chunks_fts(chunks_fts, rowid, id, content, source)
|
|
5961
|
+
VALUES ('delete', old.rowid, old.id, old.content, old.source);
|
|
5962
|
+
END;
|
|
5963
|
+
CREATE TRIGGER IF NOT EXISTS chunks_au AFTER UPDATE ON chunks BEGIN
|
|
5964
|
+
INSERT INTO chunks_fts(chunks_fts, rowid, id, content, source)
|
|
5965
|
+
VALUES ('delete', old.rowid, old.id, old.content, old.source);
|
|
5966
|
+
INSERT INTO chunks_fts(rowid, id, content, source)
|
|
5967
|
+
VALUES (new.rowid, new.id, new.content, new.source);
|
|
5968
|
+
END;
|
|
5969
|
+
`);
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5972
|
+
insertChunk(chunk) {
|
|
5973
|
+
this.db.prepare(
|
|
5974
|
+
`INSERT OR REPLACE INTO chunks (id, content, source, session_id, created_at, metadata, provider_model)
|
|
5975
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
5976
|
+
).run(
|
|
5977
|
+
chunk.id,
|
|
5978
|
+
chunk.content,
|
|
5979
|
+
chunk.source,
|
|
5980
|
+
chunk.session_id,
|
|
5981
|
+
chunk.created_at,
|
|
5982
|
+
chunk.metadata,
|
|
5983
|
+
chunk.provider_model
|
|
5984
|
+
);
|
|
5985
|
+
}
|
|
5986
|
+
insertChunks(chunks) {
|
|
5987
|
+
const insert = this.db.prepare(
|
|
5988
|
+
`INSERT OR REPLACE INTO chunks (id, content, source, session_id, created_at, metadata, provider_model)
|
|
5989
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
5990
|
+
);
|
|
5991
|
+
const tx = this.db.transaction((items) => {
|
|
5992
|
+
for (const chunk of items) {
|
|
5993
|
+
insert.run(
|
|
5994
|
+
chunk.id,
|
|
5995
|
+
chunk.content,
|
|
5996
|
+
chunk.source,
|
|
5997
|
+
chunk.session_id,
|
|
5998
|
+
chunk.created_at,
|
|
5999
|
+
chunk.metadata,
|
|
6000
|
+
chunk.provider_model
|
|
6001
|
+
);
|
|
6002
|
+
}
|
|
6003
|
+
});
|
|
6004
|
+
tx(chunks);
|
|
6005
|
+
}
|
|
6006
|
+
searchFTS(query, limit) {
|
|
6007
|
+
if (!this.ftsAvailable) return [];
|
|
6008
|
+
const sanitized = query.replace(/[^\w\s]/g, " ").trim();
|
|
6009
|
+
if (!sanitized) return [];
|
|
6010
|
+
const ftsQuery = sanitized.split(/\s+/).filter(Boolean).map((w) => `"${w}"`).join(" OR ");
|
|
6011
|
+
try {
|
|
6012
|
+
return this.db.prepare(
|
|
6013
|
+
`SELECT id, content, source, rank
|
|
6014
|
+
FROM chunks_fts
|
|
6015
|
+
WHERE chunks_fts MATCH ?
|
|
6016
|
+
ORDER BY rank
|
|
6017
|
+
LIMIT ?`
|
|
6018
|
+
).all(ftsQuery, limit);
|
|
6019
|
+
} catch {
|
|
6020
|
+
return [];
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
6023
|
+
getChunk(id) {
|
|
6024
|
+
return this.db.prepare("SELECT * FROM chunks WHERE id = ?").get(id) ?? null;
|
|
6025
|
+
}
|
|
6026
|
+
listRecent(limit) {
|
|
6027
|
+
return this.db.prepare("SELECT * FROM chunks ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
6028
|
+
}
|
|
6029
|
+
getAllChunks() {
|
|
6030
|
+
return this.db.prepare("SELECT * FROM chunks").all();
|
|
6031
|
+
}
|
|
6032
|
+
deleteChunk(id) {
|
|
6033
|
+
this.db.prepare("DELETE FROM chunks WHERE id = ?").run(id);
|
|
6034
|
+
}
|
|
6035
|
+
clearAll() {
|
|
6036
|
+
this.db.exec("DELETE FROM chunks");
|
|
6037
|
+
if (this.ftsAvailable) {
|
|
6038
|
+
this.db.exec("DELETE FROM chunks_fts");
|
|
6039
|
+
}
|
|
6040
|
+
this.db.exec("DELETE FROM embedding_cache");
|
|
6041
|
+
}
|
|
6042
|
+
clearEmbeddings() {
|
|
6043
|
+
this.db.exec("DELETE FROM embedding_cache");
|
|
6044
|
+
}
|
|
6045
|
+
getCachedEmbedding(contentHash, providerModel) {
|
|
6046
|
+
const row = this.db.prepare(
|
|
6047
|
+
"SELECT embedding FROM embedding_cache WHERE content_hash = ? AND provider_model = ?"
|
|
6048
|
+
).get(contentHash, providerModel);
|
|
6049
|
+
if (!row) return null;
|
|
6050
|
+
return Array.from(new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4));
|
|
6051
|
+
}
|
|
6052
|
+
setCachedEmbedding(contentHash, providerModel, embedding) {
|
|
6053
|
+
const buffer = Buffer.from(new Float32Array(embedding).buffer);
|
|
6054
|
+
this.db.prepare(
|
|
6055
|
+
`INSERT OR REPLACE INTO embedding_cache (content_hash, provider_model, embedding, created_at)
|
|
6056
|
+
VALUES (?, ?, ?, ?)`
|
|
6057
|
+
).run(contentHash, providerModel, buffer, (/* @__PURE__ */ new Date()).toISOString());
|
|
6058
|
+
}
|
|
6059
|
+
get isFTSAvailable() {
|
|
6060
|
+
return this.ftsAvailable;
|
|
6061
|
+
}
|
|
6062
|
+
close() {
|
|
6063
|
+
this.db.close();
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
|
|
6067
|
+
// src/memory/embeddings.ts
|
|
6068
|
+
import crypto8 from "crypto";
|
|
6069
|
+
var DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
|
|
6070
|
+
var EmbeddingService = class {
|
|
6071
|
+
apiProvider;
|
|
6072
|
+
localProvider;
|
|
6073
|
+
model;
|
|
6074
|
+
store;
|
|
6075
|
+
constructor(opts) {
|
|
6076
|
+
this.apiProvider = opts.apiProvider ?? null;
|
|
6077
|
+
this.localProvider = opts.localProvider ?? null;
|
|
6078
|
+
this.model = opts.model ?? DEFAULT_EMBEDDING_MODEL;
|
|
6079
|
+
this.store = opts.store;
|
|
6080
|
+
}
|
|
6081
|
+
get providerId() {
|
|
6082
|
+
if (this.localProvider) return "local";
|
|
6083
|
+
return this.apiProvider?.id ?? "unknown";
|
|
6084
|
+
}
|
|
6085
|
+
async embed(texts) {
|
|
6086
|
+
const results = new Array(texts.length).fill(null);
|
|
6087
|
+
const toEmbed = [];
|
|
6088
|
+
for (let i = 0; i < texts.length; i++) {
|
|
6089
|
+
const hash = this.hashContent(texts[i]);
|
|
6090
|
+
const cached = this.store.getCachedEmbedding(hash, this.providerModel);
|
|
6091
|
+
if (cached) {
|
|
6092
|
+
results[i] = cached;
|
|
6093
|
+
} else {
|
|
6094
|
+
toEmbed.push({ index: i, text: texts[i] });
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
if (toEmbed.length > 0) {
|
|
6098
|
+
const embeddings = await this.generateEmbeddings(
|
|
6099
|
+
toEmbed.map((t) => t.text)
|
|
6100
|
+
);
|
|
6101
|
+
for (let i = 0; i < toEmbed.length; i++) {
|
|
6102
|
+
const embedding = embeddings[i];
|
|
6103
|
+
const entry = toEmbed[i];
|
|
6104
|
+
results[entry.index] = embedding;
|
|
6105
|
+
const hash = this.hashContent(entry.text);
|
|
6106
|
+
this.store.setCachedEmbedding(hash, this.providerModel, embedding);
|
|
6107
|
+
}
|
|
6108
|
+
}
|
|
6109
|
+
return results;
|
|
6110
|
+
}
|
|
6111
|
+
async embedSingle(text) {
|
|
6112
|
+
const [result] = await this.embed([text]);
|
|
6113
|
+
return result;
|
|
6114
|
+
}
|
|
6115
|
+
async generateEmbeddings(texts) {
|
|
6116
|
+
if (this.localProvider) {
|
|
6117
|
+
return this.localProvider.embedBatch(texts);
|
|
6118
|
+
}
|
|
6119
|
+
if (this.apiProvider) {
|
|
6120
|
+
const result = await this.apiProvider.embed({
|
|
6121
|
+
model: this.model,
|
|
6122
|
+
inputs: texts
|
|
6123
|
+
});
|
|
6124
|
+
return result.embeddings;
|
|
6125
|
+
}
|
|
6126
|
+
throw new Error("No embedding provider available");
|
|
6127
|
+
}
|
|
6128
|
+
get providerModel() {
|
|
6129
|
+
if (this.localProvider) {
|
|
6130
|
+
return `local:${this.localProvider.model}`;
|
|
6131
|
+
}
|
|
6132
|
+
return `${this.apiProvider?.id ?? "unknown"}:${this.model}`;
|
|
6133
|
+
}
|
|
6134
|
+
hashContent(content) {
|
|
6135
|
+
return crypto8.createHash("sha256").update(content).digest("hex");
|
|
6136
|
+
}
|
|
6137
|
+
};
|
|
6138
|
+
|
|
6139
|
+
// src/memory/search.ts
|
|
6140
|
+
function cosineSimilarity(a, b) {
|
|
6141
|
+
if (a.length !== b.length) return 0;
|
|
6142
|
+
let dot = 0;
|
|
6143
|
+
let normA = 0;
|
|
6144
|
+
let normB = 0;
|
|
6145
|
+
for (let i = 0; i < a.length; i++) {
|
|
6146
|
+
dot += a[i] * b[i];
|
|
6147
|
+
normA += a[i] * a[i];
|
|
6148
|
+
normB += b[i] * b[i];
|
|
6149
|
+
}
|
|
6150
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
6151
|
+
return denom === 0 ? 0 : dot / denom;
|
|
6152
|
+
}
|
|
6153
|
+
function bm25RankToScore(rank) {
|
|
6154
|
+
return 1 / (1 + Math.abs(rank));
|
|
6155
|
+
}
|
|
6156
|
+
async function hybridSearch(store, embeddingService, query, limit) {
|
|
6157
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
6158
|
+
const ftsResults = store.searchFTS(query, limit * 2);
|
|
6159
|
+
for (const fts of ftsResults) {
|
|
6160
|
+
const entry = chunkToEntry(store.getChunk(fts.id));
|
|
6161
|
+
if (!entry) continue;
|
|
6162
|
+
resultMap.set(fts.id, {
|
|
6163
|
+
entry,
|
|
6164
|
+
score: bm25RankToScore(fts.rank),
|
|
6165
|
+
matchType: "fts"
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6168
|
+
if (embeddingService) {
|
|
6169
|
+
try {
|
|
6170
|
+
const queryEmbedding = await embeddingService.embedSingle(query);
|
|
6171
|
+
const allChunks = store.getAllChunks();
|
|
6172
|
+
if (allChunks.length > 0) {
|
|
6173
|
+
const chunkTexts = allChunks.map((c) => c.content);
|
|
6174
|
+
const embeddings = await embeddingService.embed(chunkTexts);
|
|
6175
|
+
const scored = [];
|
|
6176
|
+
for (let i = 0; i < allChunks.length; i++) {
|
|
6177
|
+
scored.push({
|
|
6178
|
+
id: allChunks[i].id,
|
|
6179
|
+
score: cosineSimilarity(queryEmbedding, embeddings[i])
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
scored.sort((a, b) => b.score - a.score);
|
|
6183
|
+
for (const item of scored.slice(0, limit * 2)) {
|
|
6184
|
+
const existing = resultMap.get(item.id);
|
|
6185
|
+
if (existing) {
|
|
6186
|
+
existing.score = Math.max(existing.score, item.score);
|
|
6187
|
+
existing.matchType = "hybrid";
|
|
6188
|
+
} else {
|
|
6189
|
+
const entry = chunkToEntry(store.getChunk(item.id));
|
|
6190
|
+
if (entry) {
|
|
6191
|
+
resultMap.set(item.id, {
|
|
6192
|
+
entry,
|
|
6193
|
+
score: item.score,
|
|
6194
|
+
matchType: "vector"
|
|
6195
|
+
});
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
}
|
|
6200
|
+
} catch {
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
return Array.from(resultMap.values()).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
6204
|
+
}
|
|
6205
|
+
function chunkToEntry(chunk) {
|
|
6206
|
+
if (!chunk) return null;
|
|
6207
|
+
return {
|
|
6208
|
+
id: chunk.id,
|
|
6209
|
+
content: chunk.content,
|
|
6210
|
+
source: chunk.source,
|
|
6211
|
+
sessionId: chunk.session_id ?? void 0,
|
|
6212
|
+
createdAt: chunk.created_at,
|
|
6213
|
+
metadata: JSON.parse(chunk.metadata)
|
|
6214
|
+
};
|
|
6215
|
+
}
|
|
6216
|
+
|
|
6217
|
+
// src/memory/manager.ts
|
|
6218
|
+
var MemoryManager = class {
|
|
6219
|
+
store;
|
|
6220
|
+
embeddingService = null;
|
|
6221
|
+
constructor(opts) {
|
|
6222
|
+
this.store = new MemoryStore(opts.dbPath);
|
|
6223
|
+
if (opts.localProvider) {
|
|
6224
|
+
this.embeddingService = new EmbeddingService({
|
|
6225
|
+
localProvider: opts.localProvider,
|
|
6226
|
+
store: this.store
|
|
6227
|
+
});
|
|
6228
|
+
} else if (opts.apiProvider) {
|
|
6229
|
+
this.embeddingService = new EmbeddingService({
|
|
6230
|
+
apiProvider: opts.apiProvider,
|
|
6231
|
+
model: opts.embeddingModel,
|
|
6232
|
+
store: this.store
|
|
6233
|
+
});
|
|
6234
|
+
}
|
|
6235
|
+
}
|
|
6236
|
+
async index(entries) {
|
|
6237
|
+
const chunks = entries.map((e) => ({
|
|
6238
|
+
id: e.id || crypto9.randomUUID(),
|
|
6239
|
+
content: e.content,
|
|
6240
|
+
source: e.source,
|
|
6241
|
+
session_id: e.sessionId ?? null,
|
|
6242
|
+
created_at: e.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
6243
|
+
metadata: JSON.stringify(e.metadata || {}),
|
|
6244
|
+
provider_model: null
|
|
6245
|
+
}));
|
|
6246
|
+
this.store.insertChunks(chunks);
|
|
6247
|
+
if (this.embeddingService && chunks.length > 0) {
|
|
6248
|
+
try {
|
|
6249
|
+
await this.embeddingService.embed(chunks.map((c) => c.content));
|
|
6250
|
+
} catch {
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6254
|
+
async search(query, limit = 5) {
|
|
6255
|
+
return hybridSearch(this.store, this.embeddingService, query, limit);
|
|
6256
|
+
}
|
|
6257
|
+
async list(limit = 20) {
|
|
6258
|
+
const rows = this.store.listRecent(limit);
|
|
6259
|
+
return rows.map((row) => ({
|
|
6260
|
+
id: row.id,
|
|
6261
|
+
content: row.content,
|
|
6262
|
+
source: row.source,
|
|
6263
|
+
sessionId: row.session_id ?? void 0,
|
|
6264
|
+
createdAt: row.created_at,
|
|
6265
|
+
metadata: JSON.parse(row.metadata)
|
|
6266
|
+
}));
|
|
6267
|
+
}
|
|
6268
|
+
async delete(id) {
|
|
6269
|
+
this.store.deleteChunk(id);
|
|
6270
|
+
}
|
|
6271
|
+
async clearEmbeddings() {
|
|
6272
|
+
this.store.clearEmbeddings();
|
|
6273
|
+
}
|
|
6274
|
+
async clear() {
|
|
6275
|
+
this.store.clearAll();
|
|
6276
|
+
}
|
|
6277
|
+
close() {
|
|
6278
|
+
this.store.close();
|
|
6279
|
+
}
|
|
6280
|
+
};
|
|
6281
|
+
|
|
6282
|
+
// src/memory/embeddings-local.ts
|
|
6283
|
+
var DEFAULT_LOCAL_MODEL = "hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf";
|
|
6284
|
+
function normalizeEmbedding(vec) {
|
|
6285
|
+
const sanitized = vec.map((v) => Number.isFinite(v) ? v : 0);
|
|
6286
|
+
const magnitude = Math.sqrt(
|
|
6287
|
+
sanitized.reduce((sum, v) => sum + v * v, 0)
|
|
6288
|
+
);
|
|
6289
|
+
if (magnitude < 1e-10) return sanitized;
|
|
6290
|
+
return sanitized.map((v) => v / magnitude);
|
|
6291
|
+
}
|
|
6292
|
+
async function createLocalEmbeddingProvider(modelPath) {
|
|
6293
|
+
const resolvedPath = modelPath?.trim() || DEFAULT_LOCAL_MODEL;
|
|
6294
|
+
const { getLlama, resolveModelFile, LlamaLogLevel } = await import("node-llama-cpp");
|
|
6295
|
+
let llama = null;
|
|
6296
|
+
let model = null;
|
|
6297
|
+
let context = null;
|
|
6298
|
+
const ensureContext = async () => {
|
|
6299
|
+
if (!llama) {
|
|
6300
|
+
llama = await getLlama({ logLevel: LlamaLogLevel.error });
|
|
6301
|
+
}
|
|
6302
|
+
if (!model) {
|
|
6303
|
+
const resolved = await resolveModelFile(resolvedPath);
|
|
6304
|
+
model = await llama.loadModel({ modelPath: resolved });
|
|
6305
|
+
}
|
|
6306
|
+
if (!context) {
|
|
6307
|
+
context = await model.createEmbeddingContext();
|
|
6308
|
+
}
|
|
6309
|
+
return context;
|
|
6310
|
+
};
|
|
6311
|
+
return {
|
|
6312
|
+
id: "local",
|
|
6313
|
+
model: resolvedPath,
|
|
6314
|
+
async embedQuery(text) {
|
|
6315
|
+
const ctx = await ensureContext();
|
|
6316
|
+
const embedding = await ctx.getEmbeddingFor(text);
|
|
6317
|
+
return normalizeEmbedding(Array.from(embedding.vector));
|
|
6318
|
+
},
|
|
6319
|
+
async embedBatch(texts) {
|
|
6320
|
+
const ctx = await ensureContext();
|
|
6321
|
+
const results = await Promise.all(
|
|
6322
|
+
texts.map(async (text) => {
|
|
6323
|
+
const embedding = await ctx.getEmbeddingFor(text);
|
|
6324
|
+
return normalizeEmbedding(Array.from(embedding.vector));
|
|
6325
|
+
})
|
|
6326
|
+
);
|
|
6327
|
+
return results;
|
|
6328
|
+
},
|
|
6329
|
+
dispose() {
|
|
6330
|
+
if (context) {
|
|
6331
|
+
context.dispose?.();
|
|
6332
|
+
context = null;
|
|
6333
|
+
}
|
|
6334
|
+
if (model) {
|
|
6335
|
+
model.dispose?.();
|
|
6336
|
+
model = null;
|
|
6337
|
+
}
|
|
6338
|
+
llama = null;
|
|
6339
|
+
}
|
|
6340
|
+
};
|
|
6341
|
+
}
|
|
5768
6342
|
export {
|
|
5769
6343
|
AVAILABLE_PROVIDERS,
|
|
5770
6344
|
AgentRunner,
|
|
@@ -5775,6 +6349,7 @@ export {
|
|
|
5775
6349
|
GoogleProvider,
|
|
5776
6350
|
GrokProvider,
|
|
5777
6351
|
MODEL_DEFINITIONS,
|
|
6352
|
+
MemoryManager,
|
|
5778
6353
|
MiniMaxProvider,
|
|
5779
6354
|
ModelStore,
|
|
5780
6355
|
MoonshotProvider,
|
|
@@ -5802,6 +6377,7 @@ export {
|
|
|
5802
6377
|
createArtifactTool,
|
|
5803
6378
|
createBrowserTool,
|
|
5804
6379
|
createCronTool,
|
|
6380
|
+
createLocalEmbeddingProvider,
|
|
5805
6381
|
createProvider,
|
|
5806
6382
|
createSkill,
|
|
5807
6383
|
createSkillTool,
|