@jcyamacho/agent-memory 0.0.20 → 0.2.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/README.md +31 -63
- package/dist/index.js +334 -577
- package/package.json +8 -9
package/dist/index.js
CHANGED
|
@@ -2431,9 +2431,9 @@ var require_validate = __commonJS((exports) => {
|
|
|
2431
2431
|
}
|
|
2432
2432
|
}
|
|
2433
2433
|
function returnResults(it) {
|
|
2434
|
-
const { gen, schemaEnv, validateName, ValidationError
|
|
2434
|
+
const { gen, schemaEnv, validateName, ValidationError, opts } = it;
|
|
2435
2435
|
if (schemaEnv.$async) {
|
|
2436
|
-
gen.if((0, codegen_1._)`${names_1.default.errors} === 0`, () => gen.return(names_1.default.data), () => gen.throw((0, codegen_1._)`new ${
|
|
2436
|
+
gen.if((0, codegen_1._)`${names_1.default.errors} === 0`, () => gen.return(names_1.default.data), () => gen.throw((0, codegen_1._)`new ${ValidationError}(${names_1.default.vErrors})`));
|
|
2437
2437
|
} else {
|
|
2438
2438
|
gen.assign((0, codegen_1._)`${validateName}.errors`, names_1.default.vErrors);
|
|
2439
2439
|
if (opts.unevaluated)
|
|
@@ -2783,14 +2783,14 @@ var require_validate = __commonJS((exports) => {
|
|
|
2783
2783
|
var require_validation_error = __commonJS((exports) => {
|
|
2784
2784
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2785
2785
|
|
|
2786
|
-
class
|
|
2786
|
+
class ValidationError extends Error {
|
|
2787
2787
|
constructor(errors3) {
|
|
2788
2788
|
super("validation failed");
|
|
2789
2789
|
this.errors = errors3;
|
|
2790
2790
|
this.ajv = this.validation = true;
|
|
2791
2791
|
}
|
|
2792
2792
|
}
|
|
2793
|
-
exports.default =
|
|
2793
|
+
exports.default = ValidationError;
|
|
2794
2794
|
});
|
|
2795
2795
|
|
|
2796
2796
|
// node_modules/ajv/dist/compile/ref_error.js
|
|
@@ -11543,7 +11543,7 @@ var AssertObjectSchema = custom((v) => v !== null && (typeof v === "object" || t
|
|
|
11543
11543
|
var ProgressTokenSchema = union([string2(), number2().int()]);
|
|
11544
11544
|
var CursorSchema = string2();
|
|
11545
11545
|
var TaskCreationParamsSchema = looseObject({
|
|
11546
|
-
ttl:
|
|
11546
|
+
ttl: number2().optional(),
|
|
11547
11547
|
pollInterval: number2().optional()
|
|
11548
11548
|
});
|
|
11549
11549
|
var TaskMetadataSchema = object({
|
|
@@ -11697,7 +11697,8 @@ var ClientCapabilitiesSchema = object({
|
|
|
11697
11697
|
roots: object({
|
|
11698
11698
|
listChanged: boolean2().optional()
|
|
11699
11699
|
}).optional(),
|
|
11700
|
-
tasks: ClientTasksCapabilitySchema.optional()
|
|
11700
|
+
tasks: ClientTasksCapabilitySchema.optional(),
|
|
11701
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
11701
11702
|
});
|
|
11702
11703
|
var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
|
|
11703
11704
|
protocolVersion: string2(),
|
|
@@ -11722,7 +11723,8 @@ var ServerCapabilitiesSchema = object({
|
|
|
11722
11723
|
tools: object({
|
|
11723
11724
|
listChanged: boolean2().optional()
|
|
11724
11725
|
}).optional(),
|
|
11725
|
-
tasks: ServerTasksCapabilitySchema.optional()
|
|
11726
|
+
tasks: ServerTasksCapabilitySchema.optional(),
|
|
11727
|
+
extensions: record(string2(), AssertObjectSchema).optional()
|
|
11726
11728
|
});
|
|
11727
11729
|
var InitializeResultSchema = ResultSchema.extend({
|
|
11728
11730
|
protocolVersion: string2(),
|
|
@@ -11837,6 +11839,7 @@ var ResourceSchema = object({
|
|
|
11837
11839
|
uri: string2(),
|
|
11838
11840
|
description: optional(string2()),
|
|
11839
11841
|
mimeType: optional(string2()),
|
|
11842
|
+
size: optional(number2()),
|
|
11840
11843
|
annotations: AnnotationsSchema.optional(),
|
|
11841
11844
|
_meta: optional(looseObject({}))
|
|
11842
11845
|
});
|
|
@@ -12464,14 +12467,13 @@ class StdioServerTransport {
|
|
|
12464
12467
|
}
|
|
12465
12468
|
}
|
|
12466
12469
|
// package.json
|
|
12467
|
-
var version2 = "0.0
|
|
12470
|
+
var version2 = "0.2.0";
|
|
12468
12471
|
|
|
12469
12472
|
// src/config.ts
|
|
12470
12473
|
import { homedir } from "node:os";
|
|
12471
12474
|
import { join } from "node:path";
|
|
12472
12475
|
import { parseArgs } from "node:util";
|
|
12473
12476
|
var AGENT_MEMORY_DB_PATH_ENV = "AGENT_MEMORY_DB_PATH";
|
|
12474
|
-
var AGENT_MEMORY_MODELS_CACHE_PATH_ENV = "AGENT_MEMORY_MODELS_CACHE_PATH";
|
|
12475
12477
|
var DEFAULT_UI_PORT = 6580;
|
|
12476
12478
|
function resolveConfig(environment = process.env, argv = process.argv.slice(2)) {
|
|
12477
12479
|
const { values } = parseArgs({
|
|
@@ -12484,7 +12486,6 @@ function resolveConfig(environment = process.env, argv = process.argv.slice(2))
|
|
|
12484
12486
|
});
|
|
12485
12487
|
return {
|
|
12486
12488
|
databasePath: resolveDatabasePath(environment),
|
|
12487
|
-
modelsCachePath: resolveModelsCachePath(environment),
|
|
12488
12489
|
uiMode: Boolean(values.ui),
|
|
12489
12490
|
uiPort: Number(values.port) || DEFAULT_UI_PORT
|
|
12490
12491
|
};
|
|
@@ -12492,137 +12493,7 @@ function resolveConfig(environment = process.env, argv = process.argv.slice(2))
|
|
|
12492
12493
|
function resolveDatabasePath(environment = process.env) {
|
|
12493
12494
|
return environment[AGENT_MEMORY_DB_PATH_ENV] || join(homedir(), ".config", "agent-memory", "memory.db");
|
|
12494
12495
|
}
|
|
12495
|
-
function resolveModelsCachePath(environment = process.env) {
|
|
12496
|
-
return environment[AGENT_MEMORY_MODELS_CACHE_PATH_ENV] || join(homedir(), ".config", "agent-memory", "models");
|
|
12497
|
-
}
|
|
12498
|
-
|
|
12499
|
-
// src/embedding/service.ts
|
|
12500
|
-
import { mkdirSync } from "node:fs";
|
|
12501
|
-
import { pipeline, env as transformersEnv } from "@huggingface/transformers";
|
|
12502
|
-
|
|
12503
|
-
// src/errors.ts
|
|
12504
|
-
class MemoryError extends Error {
|
|
12505
|
-
code;
|
|
12506
|
-
constructor(code, message, options) {
|
|
12507
|
-
super(message, options);
|
|
12508
|
-
this.name = "MemoryError";
|
|
12509
|
-
this.code = code;
|
|
12510
|
-
}
|
|
12511
|
-
}
|
|
12512
|
-
|
|
12513
|
-
class ValidationError extends MemoryError {
|
|
12514
|
-
constructor(message) {
|
|
12515
|
-
super("VALIDATION_ERROR", message);
|
|
12516
|
-
this.name = "ValidationError";
|
|
12517
|
-
}
|
|
12518
|
-
}
|
|
12519
|
-
|
|
12520
|
-
class NotFoundError extends MemoryError {
|
|
12521
|
-
constructor(message) {
|
|
12522
|
-
super("NOT_FOUND", message);
|
|
12523
|
-
this.name = "NotFoundError";
|
|
12524
|
-
}
|
|
12525
|
-
}
|
|
12526
|
-
|
|
12527
|
-
class PersistenceError extends MemoryError {
|
|
12528
|
-
constructor(message, options) {
|
|
12529
|
-
super("PERSISTENCE_ERROR", message, options);
|
|
12530
|
-
this.name = "PersistenceError";
|
|
12531
|
-
}
|
|
12532
|
-
}
|
|
12533
|
-
|
|
12534
|
-
// src/embedding/service.ts
|
|
12535
|
-
var DEFAULT_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
12536
|
-
function configureModelsCache(modelsCachePath) {
|
|
12537
|
-
mkdirSync(modelsCachePath, { recursive: true });
|
|
12538
|
-
transformersEnv.useFSCache = true;
|
|
12539
|
-
transformersEnv.cacheDir = modelsCachePath;
|
|
12540
|
-
}
|
|
12541
12496
|
|
|
12542
|
-
class EmbeddingService {
|
|
12543
|
-
options;
|
|
12544
|
-
extractorPromise;
|
|
12545
|
-
constructor(options = {}) {
|
|
12546
|
-
this.options = options;
|
|
12547
|
-
}
|
|
12548
|
-
async warmup() {
|
|
12549
|
-
await this.getExtractor();
|
|
12550
|
-
}
|
|
12551
|
-
async createVector(text) {
|
|
12552
|
-
const normalizedText = text.trim();
|
|
12553
|
-
if (!normalizedText) {
|
|
12554
|
-
throw new ValidationError("Text is required.");
|
|
12555
|
-
}
|
|
12556
|
-
const extractor = await this.getExtractor();
|
|
12557
|
-
const embedding = await extractor(normalizedText);
|
|
12558
|
-
return normalizeVector(embedding.tolist());
|
|
12559
|
-
}
|
|
12560
|
-
getExtractor() {
|
|
12561
|
-
if (!this.extractorPromise) {
|
|
12562
|
-
this.extractorPromise = (this.options.createExtractor ?? createDefaultExtractor)();
|
|
12563
|
-
}
|
|
12564
|
-
return this.extractorPromise;
|
|
12565
|
-
}
|
|
12566
|
-
}
|
|
12567
|
-
async function createDefaultExtractor() {
|
|
12568
|
-
const extractor = await pipeline("feature-extraction", DEFAULT_EMBEDDING_MODEL);
|
|
12569
|
-
return (text) => extractor(text, {
|
|
12570
|
-
pooling: "mean",
|
|
12571
|
-
normalize: true
|
|
12572
|
-
});
|
|
12573
|
-
}
|
|
12574
|
-
function normalizeVector(value) {
|
|
12575
|
-
if (value.length === 0) {
|
|
12576
|
-
throw new ValidationError("Embedding model returned an empty vector.");
|
|
12577
|
-
}
|
|
12578
|
-
const [firstItem] = value;
|
|
12579
|
-
if (typeof firstItem === "number") {
|
|
12580
|
-
return value.map((item) => {
|
|
12581
|
-
if (typeof item !== "number" || !Number.isFinite(item)) {
|
|
12582
|
-
throw new ValidationError("Embedding model returned a non-numeric vector.");
|
|
12583
|
-
}
|
|
12584
|
-
return item;
|
|
12585
|
-
});
|
|
12586
|
-
}
|
|
12587
|
-
if (Array.isArray(firstItem)) {
|
|
12588
|
-
return normalizeVector(firstItem);
|
|
12589
|
-
}
|
|
12590
|
-
throw new ValidationError("Embedding model returned an unexpected vector shape.");
|
|
12591
|
-
}
|
|
12592
|
-
// src/embedding/similarity.ts
|
|
12593
|
-
function compareVectors(left, right) {
|
|
12594
|
-
validateVector(left, "Left vector");
|
|
12595
|
-
validateVector(right, "Right vector");
|
|
12596
|
-
if (left.length !== right.length) {
|
|
12597
|
-
throw new ValidationError("Vectors must have the same length.");
|
|
12598
|
-
}
|
|
12599
|
-
let dotProduct = 0;
|
|
12600
|
-
let leftMagnitude = 0;
|
|
12601
|
-
let rightMagnitude = 0;
|
|
12602
|
-
for (const [index, leftValue] of left.entries()) {
|
|
12603
|
-
const rightValue = right[index];
|
|
12604
|
-
if (rightValue === undefined) {
|
|
12605
|
-
throw new ValidationError("Vectors must have the same length.");
|
|
12606
|
-
}
|
|
12607
|
-
dotProduct += leftValue * rightValue;
|
|
12608
|
-
leftMagnitude += leftValue * leftValue;
|
|
12609
|
-
rightMagnitude += rightValue * rightValue;
|
|
12610
|
-
}
|
|
12611
|
-
if (leftMagnitude === 0 || rightMagnitude === 0) {
|
|
12612
|
-
throw new ValidationError("Vectors must not have zero magnitude.");
|
|
12613
|
-
}
|
|
12614
|
-
return dotProduct / (Math.sqrt(leftMagnitude) * Math.sqrt(rightMagnitude));
|
|
12615
|
-
}
|
|
12616
|
-
function validateVector(vector, label) {
|
|
12617
|
-
if (vector.length === 0) {
|
|
12618
|
-
throw new ValidationError(`${label} must not be empty.`);
|
|
12619
|
-
}
|
|
12620
|
-
for (const value of vector) {
|
|
12621
|
-
if (!Number.isFinite(value)) {
|
|
12622
|
-
throw new ValidationError(`${label} must contain only finite numbers.`);
|
|
12623
|
-
}
|
|
12624
|
-
}
|
|
12625
|
-
}
|
|
12626
12497
|
// node_modules/zod/v3/helpers/util.js
|
|
12627
12498
|
var util;
|
|
12628
12499
|
(function(util2) {
|
|
@@ -18106,6 +17977,10 @@ class Protocol {
|
|
|
18106
17977
|
this._progressHandlers.clear();
|
|
18107
17978
|
this._taskProgressTokens.clear();
|
|
18108
17979
|
this._pendingDebouncedNotifications.clear();
|
|
17980
|
+
for (const info of this._timeoutInfo.values()) {
|
|
17981
|
+
clearTimeout(info.timeoutId);
|
|
17982
|
+
}
|
|
17983
|
+
this._timeoutInfo.clear();
|
|
18109
17984
|
for (const controller of this._requestHandlerAbortControllers.values()) {
|
|
18110
17985
|
controller.abort();
|
|
18111
17986
|
}
|
|
@@ -18236,7 +18111,9 @@ class Protocol {
|
|
|
18236
18111
|
await capturedTransport?.send(errorResponse);
|
|
18237
18112
|
}
|
|
18238
18113
|
}).catch((error2) => this._onerror(new Error(`Failed to send response: ${error2}`))).finally(() => {
|
|
18239
|
-
this._requestHandlerAbortControllers.
|
|
18114
|
+
if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
|
|
18115
|
+
this._requestHandlerAbortControllers.delete(request.id);
|
|
18116
|
+
}
|
|
18240
18117
|
});
|
|
18241
18118
|
}
|
|
18242
18119
|
_onprogress(notification) {
|
|
@@ -19894,6 +19771,9 @@ class McpServer {
|
|
|
19894
19771
|
annotations = rest.shift();
|
|
19895
19772
|
}
|
|
19896
19773
|
} else if (typeof firstArg === "object" && firstArg !== null) {
|
|
19774
|
+
if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) {
|
|
19775
|
+
throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
|
|
19776
|
+
}
|
|
19897
19777
|
annotations = rest.shift();
|
|
19898
19778
|
}
|
|
19899
19779
|
}
|
|
@@ -19986,6 +19866,9 @@ function getZodSchemaObject(schema) {
|
|
|
19986
19866
|
if (isZodRawShapeCompat(schema)) {
|
|
19987
19867
|
return objectFromShape(schema);
|
|
19988
19868
|
}
|
|
19869
|
+
if (!isZodSchemaInstance(schema)) {
|
|
19870
|
+
throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
|
|
19871
|
+
}
|
|
19989
19872
|
return schema;
|
|
19990
19873
|
}
|
|
19991
19874
|
function promptArgumentsFromSchema(schema) {
|
|
@@ -20030,6 +19913,37 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
20030
19913
|
}
|
|
20031
19914
|
};
|
|
20032
19915
|
|
|
19916
|
+
// src/errors.ts
|
|
19917
|
+
class MemoryError extends Error {
|
|
19918
|
+
code;
|
|
19919
|
+
constructor(code, message, options) {
|
|
19920
|
+
super(message, options);
|
|
19921
|
+
this.name = "MemoryError";
|
|
19922
|
+
this.code = code;
|
|
19923
|
+
}
|
|
19924
|
+
}
|
|
19925
|
+
|
|
19926
|
+
class ValidationError extends MemoryError {
|
|
19927
|
+
constructor(message) {
|
|
19928
|
+
super("VALIDATION_ERROR", message);
|
|
19929
|
+
this.name = "ValidationError";
|
|
19930
|
+
}
|
|
19931
|
+
}
|
|
19932
|
+
|
|
19933
|
+
class NotFoundError extends MemoryError {
|
|
19934
|
+
constructor(message) {
|
|
19935
|
+
super("NOT_FOUND", message);
|
|
19936
|
+
this.name = "NotFoundError";
|
|
19937
|
+
}
|
|
19938
|
+
}
|
|
19939
|
+
|
|
19940
|
+
class PersistenceError extends MemoryError {
|
|
19941
|
+
constructor(message, options) {
|
|
19942
|
+
super("PERSISTENCE_ERROR", message, options);
|
|
19943
|
+
this.name = "PersistenceError";
|
|
19944
|
+
}
|
|
19945
|
+
}
|
|
19946
|
+
|
|
20033
19947
|
// src/mcp/tools/shared.ts
|
|
20034
19948
|
function toMcpError(error2) {
|
|
20035
19949
|
if (error2 instanceof McpError) {
|
|
@@ -20045,20 +19959,31 @@ function toMcpError(error2) {
|
|
|
20045
19959
|
return new McpError(ErrorCode.InternalError, message);
|
|
20046
19960
|
}
|
|
20047
19961
|
var escapeXml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
20048
|
-
function
|
|
20049
|
-
|
|
20050
|
-
|
|
19962
|
+
function toMemoryXml(record3, options) {
|
|
19963
|
+
const scopeAttribute = getMemoryScopeAttribute(record3, options?.skipWorkspaceIfEquals);
|
|
19964
|
+
const attributes = [
|
|
19965
|
+
`id="${escapeXml(record3.id)}"`,
|
|
19966
|
+
`updated_at="${record3.updatedAt.toISOString()}"`,
|
|
19967
|
+
scopeAttribute,
|
|
19968
|
+
options?.deleted ? 'deleted="true"' : undefined
|
|
19969
|
+
].filter((value) => value).join(" ");
|
|
19970
|
+
return `<memory ${attributes}>
|
|
19971
|
+
${escapeXml(record3.content)}
|
|
19972
|
+
</memory>`;
|
|
19973
|
+
}
|
|
19974
|
+
function getMemoryScopeAttribute(record3, skipWorkspaceIfEquals) {
|
|
19975
|
+
if (record3.workspace === undefined) {
|
|
19976
|
+
return 'global="true"';
|
|
20051
19977
|
}
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
throw new MemoryError("VALIDATION_ERROR", `${fieldName} must be a valid ISO 8601 datetime.`);
|
|
19978
|
+
if (record3.workspace === skipWorkspaceIfEquals) {
|
|
19979
|
+
return;
|
|
20055
19980
|
}
|
|
20056
|
-
return
|
|
19981
|
+
return `workspace="${escapeXml(record3.workspace)}"`;
|
|
20057
19982
|
}
|
|
20058
19983
|
|
|
20059
19984
|
// src/mcp/tools/forget.ts
|
|
20060
19985
|
var forgetInputSchema = {
|
|
20061
|
-
id: string2().describe("Memory id to delete. Use an id returned by `
|
|
19986
|
+
id: string2().describe("Memory id to delete. Use an id returned by `review`.")
|
|
20062
19987
|
};
|
|
20063
19988
|
function registerForgetTool(server, memory) {
|
|
20064
19989
|
server.registerTool("forget", {
|
|
@@ -20068,252 +19993,13 @@ function registerForgetTool(server, memory) {
|
|
|
20068
19993
|
idempotentHint: true,
|
|
20069
19994
|
openWorldHint: false
|
|
20070
19995
|
},
|
|
20071
|
-
description: 'Delete a memory that is wrong or obsolete. Use after `
|
|
19996
|
+
description: 'Delete a memory that is wrong or obsolete. Use after `review` when you have the memory id. Use `revise` instead if the fact should remain with corrected wording. Returns the deleted memory as `<memory ... deleted="true">...</memory>`.',
|
|
20072
19997
|
inputSchema: forgetInputSchema
|
|
20073
19998
|
}, async ({ id }) => {
|
|
20074
19999
|
try {
|
|
20075
|
-
await memory.delete({ id });
|
|
20000
|
+
const deletedMemory = await memory.delete({ id });
|
|
20076
20001
|
return {
|
|
20077
|
-
content: [{ type: "text", text:
|
|
20078
|
-
};
|
|
20079
|
-
} catch (error2) {
|
|
20080
|
-
throw toMcpError(error2);
|
|
20081
|
-
}
|
|
20082
|
-
});
|
|
20083
|
-
}
|
|
20084
|
-
|
|
20085
|
-
// src/memory.ts
|
|
20086
|
-
var toNormalizedScore = (value) => value;
|
|
20087
|
-
|
|
20088
|
-
// src/ranking.ts
|
|
20089
|
-
var RETRIEVAL_SCORE_WEIGHT = 9;
|
|
20090
|
-
var EMBEDDING_SIMILARITY_WEIGHT = 4;
|
|
20091
|
-
var WORKSPACE_MATCH_WEIGHT = 5;
|
|
20092
|
-
var RECENCY_WEIGHT = 1;
|
|
20093
|
-
var MAX_COMPOSITE_SCORE = RETRIEVAL_SCORE_WEIGHT + EMBEDDING_SIMILARITY_WEIGHT + WORKSPACE_MATCH_WEIGHT + RECENCY_WEIGHT;
|
|
20094
|
-
var GLOBAL_WORKSPACE_SCORE = 0.5;
|
|
20095
|
-
function rerankSearchResults(results, workspace, queryEmbedding) {
|
|
20096
|
-
if (results.length === 0) {
|
|
20097
|
-
return results;
|
|
20098
|
-
}
|
|
20099
|
-
const normalizedQueryWs = workspace ? normalizeWorkspacePath(workspace) : undefined;
|
|
20100
|
-
const updatedAtTimes = results.map((result) => result.updatedAt.getTime());
|
|
20101
|
-
const minUpdatedAt = Math.min(...updatedAtTimes);
|
|
20102
|
-
const maxUpdatedAt = Math.max(...updatedAtTimes);
|
|
20103
|
-
return results.map((result) => {
|
|
20104
|
-
const embeddingSimilarityScore = computeEmbeddingSimilarityScore(result, queryEmbedding);
|
|
20105
|
-
const workspaceScore = computeWorkspaceScore(result.workspace, normalizedQueryWs);
|
|
20106
|
-
const recencyScore = maxUpdatedAt === minUpdatedAt ? 0 : (result.updatedAt.getTime() - minUpdatedAt) / (maxUpdatedAt - minUpdatedAt);
|
|
20107
|
-
const combinedScore = (result.score * RETRIEVAL_SCORE_WEIGHT + embeddingSimilarityScore * EMBEDDING_SIMILARITY_WEIGHT + workspaceScore * WORKSPACE_MATCH_WEIGHT + recencyScore * RECENCY_WEIGHT) / MAX_COMPOSITE_SCORE;
|
|
20108
|
-
return {
|
|
20109
|
-
...result,
|
|
20110
|
-
score: toNormalizedScore(combinedScore)
|
|
20111
|
-
};
|
|
20112
|
-
}).sort((a, b) => b.score - a.score);
|
|
20113
|
-
}
|
|
20114
|
-
function computeEmbeddingSimilarityScore(result, queryEmbedding) {
|
|
20115
|
-
return normalizeCosineSimilarity(compareVectors(result.embedding, queryEmbedding));
|
|
20116
|
-
}
|
|
20117
|
-
function normalizeWorkspacePath(value) {
|
|
20118
|
-
return value.trim().replaceAll("\\", "/").replace(/\/+/g, "/").split("/").filter(Boolean).join("/");
|
|
20119
|
-
}
|
|
20120
|
-
function normalizeCosineSimilarity(value) {
|
|
20121
|
-
return (value + 1) / 2;
|
|
20122
|
-
}
|
|
20123
|
-
function computeWorkspaceScore(memoryWs, queryWs) {
|
|
20124
|
-
if (!queryWs) {
|
|
20125
|
-
return 0;
|
|
20126
|
-
}
|
|
20127
|
-
if (!memoryWs) {
|
|
20128
|
-
return GLOBAL_WORKSPACE_SCORE;
|
|
20129
|
-
}
|
|
20130
|
-
const normalizedMemoryWs = normalizeWorkspacePath(memoryWs);
|
|
20131
|
-
if (normalizedMemoryWs === queryWs) {
|
|
20132
|
-
return 1;
|
|
20133
|
-
}
|
|
20134
|
-
return 0;
|
|
20135
|
-
}
|
|
20136
|
-
|
|
20137
|
-
// src/memory-service.ts
|
|
20138
|
-
var DEFAULT_RECALL_LIMIT = 15;
|
|
20139
|
-
var MAX_RECALL_LIMIT = 50;
|
|
20140
|
-
var RECALL_CANDIDATE_LIMIT_MULTIPLIER = 3;
|
|
20141
|
-
var DEFAULT_LIST_LIMIT = 15;
|
|
20142
|
-
var MAX_LIST_LIMIT = 100;
|
|
20143
|
-
|
|
20144
|
-
class MemoryService {
|
|
20145
|
-
repository;
|
|
20146
|
-
embeddingService;
|
|
20147
|
-
workspaceResolver;
|
|
20148
|
-
constructor(repository, embeddingService, workspaceResolver) {
|
|
20149
|
-
this.repository = repository;
|
|
20150
|
-
this.embeddingService = embeddingService;
|
|
20151
|
-
this.workspaceResolver = workspaceResolver;
|
|
20152
|
-
}
|
|
20153
|
-
async create(input) {
|
|
20154
|
-
const content = input.content.trim();
|
|
20155
|
-
if (!content) {
|
|
20156
|
-
throw new ValidationError("Memory content is required.");
|
|
20157
|
-
}
|
|
20158
|
-
const workspace = await this.workspaceResolver.resolve(input.workspace);
|
|
20159
|
-
const memory = await this.repository.create({
|
|
20160
|
-
content,
|
|
20161
|
-
embedding: await this.embeddingService.createVector(content),
|
|
20162
|
-
workspace
|
|
20163
|
-
});
|
|
20164
|
-
return toPublicMemoryRecord(memory);
|
|
20165
|
-
}
|
|
20166
|
-
async update(input) {
|
|
20167
|
-
const content = input.content.trim();
|
|
20168
|
-
if (!content)
|
|
20169
|
-
throw new ValidationError("Memory content is required.");
|
|
20170
|
-
const memory = await this.repository.update({
|
|
20171
|
-
id: input.id,
|
|
20172
|
-
content,
|
|
20173
|
-
embedding: await this.embeddingService.createVector(content)
|
|
20174
|
-
});
|
|
20175
|
-
return toPublicMemoryRecord(memory);
|
|
20176
|
-
}
|
|
20177
|
-
async delete(input) {
|
|
20178
|
-
const id = input.id.trim();
|
|
20179
|
-
if (!id)
|
|
20180
|
-
throw new ValidationError("Memory id is required.");
|
|
20181
|
-
return this.repository.delete({ id });
|
|
20182
|
-
}
|
|
20183
|
-
async get(id) {
|
|
20184
|
-
const memory = await this.repository.get(id);
|
|
20185
|
-
return memory ? toPublicMemoryRecord(memory) : undefined;
|
|
20186
|
-
}
|
|
20187
|
-
async list(input) {
|
|
20188
|
-
const queryWorkspace = normalizeOptionalString(input.workspace);
|
|
20189
|
-
const workspace = await this.workspaceResolver.resolve(input.workspace);
|
|
20190
|
-
const page = await this.repository.list({
|
|
20191
|
-
workspace,
|
|
20192
|
-
workspaceIsNull: workspace ? false : Boolean(input.workspaceIsNull),
|
|
20193
|
-
offset: normalizeOffset(input.offset),
|
|
20194
|
-
limit: normalizeListLimit(input.limit)
|
|
20195
|
-
});
|
|
20196
|
-
return {
|
|
20197
|
-
items: page.items.map((item) => toPublicMemoryRecord(remapWorkspace(item, workspace, queryWorkspace))),
|
|
20198
|
-
hasMore: page.hasMore
|
|
20199
|
-
};
|
|
20200
|
-
}
|
|
20201
|
-
async listWorkspaces() {
|
|
20202
|
-
return this.repository.listWorkspaces();
|
|
20203
|
-
}
|
|
20204
|
-
async search(input) {
|
|
20205
|
-
const terms = normalizeTerms(input.terms);
|
|
20206
|
-
if (terms.length === 0) {
|
|
20207
|
-
throw new ValidationError("At least one search term is required.");
|
|
20208
|
-
}
|
|
20209
|
-
const requestedLimit = normalizeLimit(input.limit);
|
|
20210
|
-
const queryWorkspace = normalizeOptionalString(input.workspace);
|
|
20211
|
-
const workspace = await this.workspaceResolver.resolve(input.workspace);
|
|
20212
|
-
const normalizedQuery = {
|
|
20213
|
-
terms,
|
|
20214
|
-
limit: requestedLimit * RECALL_CANDIDATE_LIMIT_MULTIPLIER,
|
|
20215
|
-
updatedAfter: input.updatedAfter,
|
|
20216
|
-
updatedBefore: input.updatedBefore
|
|
20217
|
-
};
|
|
20218
|
-
const [results, queryEmbedding] = await Promise.all([
|
|
20219
|
-
this.repository.search(normalizedQuery),
|
|
20220
|
-
this.embeddingService.createVector(terms.join(" "))
|
|
20221
|
-
]);
|
|
20222
|
-
return rerankSearchResults(results, workspace, queryEmbedding).slice(0, requestedLimit).map((result) => toPublicSearchResult(remapWorkspace(result, workspace, queryWorkspace)));
|
|
20223
|
-
}
|
|
20224
|
-
}
|
|
20225
|
-
function toPublicMemoryRecord(memory) {
|
|
20226
|
-
return {
|
|
20227
|
-
id: memory.id,
|
|
20228
|
-
content: memory.content,
|
|
20229
|
-
workspace: memory.workspace,
|
|
20230
|
-
createdAt: memory.createdAt,
|
|
20231
|
-
updatedAt: memory.updatedAt
|
|
20232
|
-
};
|
|
20233
|
-
}
|
|
20234
|
-
function toPublicSearchResult(result) {
|
|
20235
|
-
return {
|
|
20236
|
-
id: result.id,
|
|
20237
|
-
content: result.content,
|
|
20238
|
-
score: result.score,
|
|
20239
|
-
workspace: result.workspace,
|
|
20240
|
-
createdAt: result.createdAt,
|
|
20241
|
-
updatedAt: result.updatedAt
|
|
20242
|
-
};
|
|
20243
|
-
}
|
|
20244
|
-
function normalizeLimit(value) {
|
|
20245
|
-
if (value === undefined) {
|
|
20246
|
-
return DEFAULT_RECALL_LIMIT;
|
|
20247
|
-
}
|
|
20248
|
-
if (!Number.isInteger(value) || value < 1 || value > MAX_RECALL_LIMIT) {
|
|
20249
|
-
throw new ValidationError(`Limit must be an integer between 1 and ${MAX_RECALL_LIMIT}.`);
|
|
20250
|
-
}
|
|
20251
|
-
return value;
|
|
20252
|
-
}
|
|
20253
|
-
function normalizeOffset(value) {
|
|
20254
|
-
return Number.isInteger(value) && value && value > 0 ? value : 0;
|
|
20255
|
-
}
|
|
20256
|
-
function normalizeListLimit(value) {
|
|
20257
|
-
if (!Number.isInteger(value) || value === undefined) {
|
|
20258
|
-
return DEFAULT_LIST_LIMIT;
|
|
20259
|
-
}
|
|
20260
|
-
return Math.min(Math.max(value, 1), MAX_LIST_LIMIT);
|
|
20261
|
-
}
|
|
20262
|
-
function normalizeTerms(terms) {
|
|
20263
|
-
const normalizedTerms = terms.map((term) => term.trim()).filter(Boolean);
|
|
20264
|
-
return [...new Set(normalizedTerms)];
|
|
20265
|
-
}
|
|
20266
|
-
function normalizeOptionalString(value) {
|
|
20267
|
-
const trimmed = value?.trim();
|
|
20268
|
-
return trimmed ? trimmed : undefined;
|
|
20269
|
-
}
|
|
20270
|
-
function remapWorkspace(record3, canonicalWorkspace, queryWorkspace) {
|
|
20271
|
-
if (record3.workspace !== canonicalWorkspace) {
|
|
20272
|
-
return record3;
|
|
20273
|
-
}
|
|
20274
|
-
return { ...record3, workspace: queryWorkspace };
|
|
20275
|
-
}
|
|
20276
|
-
|
|
20277
|
-
// src/mcp/tools/recall.ts
|
|
20278
|
-
var recallInputSchema = {
|
|
20279
|
-
terms: array(string2()).min(1).describe("2-5 short anchor-heavy terms or exact phrases. Prefer identifiers, commands, file paths, and exact wording likely to appear in the memory."),
|
|
20280
|
-
limit: number2().int().min(1).max(MAX_RECALL_LIMIT).optional().describe("Maximum matches to return. Keep this small when you only need the strongest hits."),
|
|
20281
|
-
workspace: string2().optional().describe("Current working directory for project-scoped recall. Omit for cross-project recall."),
|
|
20282
|
-
updated_after: string2().optional().describe("Only return memories updated on or after this ISO 8601 timestamp."),
|
|
20283
|
-
updated_before: string2().optional().describe("Only return memories updated on or before this ISO 8601 timestamp.")
|
|
20284
|
-
};
|
|
20285
|
-
function toMemoryXml(r) {
|
|
20286
|
-
const workspace = r.workspace ? ` workspace="${escapeXml(r.workspace)}"` : "";
|
|
20287
|
-
const content = escapeXml(r.content);
|
|
20288
|
-
const score = Number(r.score.toFixed(3)).toString();
|
|
20289
|
-
return `<memory id="${r.id}" score="${score}"${workspace} updated_at="${r.updatedAt.toISOString()}">
|
|
20290
|
-
${content}
|
|
20291
|
-
</memory>`;
|
|
20292
|
-
}
|
|
20293
|
-
function registerRecallTool(server, memory) {
|
|
20294
|
-
server.registerTool("recall", {
|
|
20295
|
-
annotations: {
|
|
20296
|
-
title: "Recall",
|
|
20297
|
-
readOnlyHint: true,
|
|
20298
|
-
openWorldHint: false
|
|
20299
|
-
},
|
|
20300
|
-
description: "Retrieve memories relevant to the current task or check whether a fact already exists before saving. Use at conversation start and before design choices. Pass short anchor-heavy `terms` and `workspace` when available. Results reflect the queried workspace context when applicable. Returns `<memories>...</memories>` or a no-match hint.",
|
|
20301
|
-
inputSchema: recallInputSchema
|
|
20302
|
-
}, async ({ terms, limit, workspace, updated_after, updated_before }) => {
|
|
20303
|
-
try {
|
|
20304
|
-
const results = await memory.search({
|
|
20305
|
-
terms,
|
|
20306
|
-
limit,
|
|
20307
|
-
workspace,
|
|
20308
|
-
updatedAfter: parseOptionalDate(updated_after, "updated_after"),
|
|
20309
|
-
updatedBefore: parseOptionalDate(updated_before, "updated_before")
|
|
20310
|
-
});
|
|
20311
|
-
const text = results.length === 0 ? "No matching memories found. Retry once with 1-3 overlapping alternate terms or an exact identifier, command, file path, or phrase likely to appear in the memory." : `<memories>
|
|
20312
|
-
${results.map(toMemoryXml).join(`
|
|
20313
|
-
`)}
|
|
20314
|
-
</memories>`;
|
|
20315
|
-
return {
|
|
20316
|
-
content: [{ type: "text", text }]
|
|
20002
|
+
content: [{ type: "text", text: toMemoryXml(deletedMemory, { deleted: true }) }]
|
|
20317
20003
|
};
|
|
20318
20004
|
} catch (error2) {
|
|
20319
20005
|
throw toMcpError(error2);
|
|
@@ -20334,7 +20020,7 @@ function registerRememberTool(server, memory) {
|
|
|
20334
20020
|
idempotentHint: false,
|
|
20335
20021
|
openWorldHint: false
|
|
20336
20022
|
},
|
|
20337
|
-
description:
|
|
20023
|
+
description: "Save one new durable fact. Use for stable preferences, reusable decisions, and project context not obvious from code or git history. If the fact already exists, use `revise` instead. Returns the saved memory as `<memory ...>...</memory>`.",
|
|
20338
20024
|
inputSchema: rememberInputSchema
|
|
20339
20025
|
}, async ({ content, workspace }) => {
|
|
20340
20026
|
try {
|
|
@@ -20343,7 +20029,7 @@ function registerRememberTool(server, memory) {
|
|
|
20343
20029
|
workspace
|
|
20344
20030
|
});
|
|
20345
20031
|
return {
|
|
20346
|
-
content: [{ type: "text", text:
|
|
20032
|
+
content: [{ type: "text", text: toMemoryXml(savedMemory) }]
|
|
20347
20033
|
};
|
|
20348
20034
|
} catch (error2) {
|
|
20349
20035
|
throw toMcpError(error2);
|
|
@@ -20352,17 +20038,11 @@ function registerRememberTool(server, memory) {
|
|
|
20352
20038
|
}
|
|
20353
20039
|
|
|
20354
20040
|
// src/mcp/tools/review.ts
|
|
20355
|
-
var REVIEW_PAGE_SIZE =
|
|
20041
|
+
var REVIEW_PAGE_SIZE = 50;
|
|
20356
20042
|
var reviewInputSchema = {
|
|
20357
20043
|
workspace: string2().describe("Current working directory for project-scoped listing."),
|
|
20358
20044
|
page: number2().int().min(0).optional().describe("Zero-based page number. Defaults to 0.")
|
|
20359
20045
|
};
|
|
20360
|
-
function toMemoryXml2(record3) {
|
|
20361
|
-
const content = escapeXml(record3.content);
|
|
20362
|
-
return `<memory id="${record3.id}" updated_at="${record3.updatedAt.toISOString()}">
|
|
20363
|
-
${content}
|
|
20364
|
-
</memory>`;
|
|
20365
|
-
}
|
|
20366
20046
|
function registerReviewTool(server, memory) {
|
|
20367
20047
|
server.registerTool("review", {
|
|
20368
20048
|
annotations: {
|
|
@@ -20370,13 +20050,14 @@ function registerReviewTool(server, memory) {
|
|
|
20370
20050
|
readOnlyHint: true,
|
|
20371
20051
|
openWorldHint: false
|
|
20372
20052
|
},
|
|
20373
|
-
description: '
|
|
20053
|
+
description: 'Load workspace and global memories sorted by most recently updated. Use at the start of a task and before saving or revising memory. Returns `<memories workspace="..." has_more="true|false">...</memories>` with pagination support. Global memories are marked with `global="true"`.',
|
|
20374
20054
|
inputSchema: reviewInputSchema
|
|
20375
20055
|
}, async ({ workspace, page }) => {
|
|
20376
20056
|
try {
|
|
20377
20057
|
const pageIndex = page ?? 0;
|
|
20378
20058
|
const result = await memory.list({
|
|
20379
20059
|
workspace,
|
|
20060
|
+
global: true,
|
|
20380
20061
|
offset: pageIndex * REVIEW_PAGE_SIZE,
|
|
20381
20062
|
limit: REVIEW_PAGE_SIZE
|
|
20382
20063
|
});
|
|
@@ -20385,9 +20066,11 @@ function registerReviewTool(server, memory) {
|
|
|
20385
20066
|
content: [{ type: "text", text: "No memories found for this workspace." }]
|
|
20386
20067
|
};
|
|
20387
20068
|
}
|
|
20388
|
-
const
|
|
20389
|
-
|
|
20390
|
-
`)
|
|
20069
|
+
const escapedWorkspace = escapeXml(workspace);
|
|
20070
|
+
const memories = result.items.map((item) => toMemoryXml(item, { skipWorkspaceIfEquals: workspace })).join(`
|
|
20071
|
+
`);
|
|
20072
|
+
const text = `<memories workspace="${escapedWorkspace}" has_more="${result.hasMore}">
|
|
20073
|
+
${memories}
|
|
20391
20074
|
</memories>`;
|
|
20392
20075
|
return {
|
|
20393
20076
|
content: [{ type: "text", text }]
|
|
@@ -20400,8 +20083,9 @@ ${result.items.map(toMemoryXml2).join(`
|
|
|
20400
20083
|
|
|
20401
20084
|
// src/mcp/tools/revise.ts
|
|
20402
20085
|
var reviseInputSchema = {
|
|
20403
|
-
id: string2().describe("Memory id to update. Use an id returned by `
|
|
20404
|
-
content: string2().describe("Corrected replacement text for that memory.")
|
|
20086
|
+
id: string2().describe("Memory id to update. Use an id returned by `review`."),
|
|
20087
|
+
content: string2().optional().describe("Corrected replacement text for that memory. Omit to keep the current content."),
|
|
20088
|
+
global: boolean2().optional().describe("Set to true to move a project-scoped memory to global scope.")
|
|
20405
20089
|
};
|
|
20406
20090
|
function registerReviseTool(server, memory) {
|
|
20407
20091
|
server.registerTool("revise", {
|
|
@@ -20411,16 +20095,23 @@ function registerReviseTool(server, memory) {
|
|
|
20411
20095
|
idempotentHint: false,
|
|
20412
20096
|
openWorldHint: false
|
|
20413
20097
|
},
|
|
20414
|
-
description:
|
|
20098
|
+
description: "Update one existing memory when the same fact still applies but its wording changed, or when a project-scoped memory should become global. Use after `review` when you already have the memory id. Omit fields you do not want to change. Returns the revised memory as `<memory ...>...</memory>`.",
|
|
20415
20099
|
inputSchema: reviseInputSchema
|
|
20416
|
-
}, async ({ id, content }) => {
|
|
20100
|
+
}, async ({ id, content, global: global2 }) => {
|
|
20417
20101
|
try {
|
|
20418
|
-
|
|
20102
|
+
if (content === undefined && global2 !== true) {
|
|
20103
|
+
throw new ValidationError("Provide at least one field to revise.");
|
|
20104
|
+
}
|
|
20105
|
+
const revisedMemory = await memory.update({
|
|
20106
|
+
id,
|
|
20107
|
+
content,
|
|
20108
|
+
workspace: global2 === true ? null : undefined
|
|
20109
|
+
});
|
|
20419
20110
|
return {
|
|
20420
20111
|
content: [
|
|
20421
20112
|
{
|
|
20422
20113
|
type: "text",
|
|
20423
|
-
text:
|
|
20114
|
+
text: toMemoryXml(revisedMemory)
|
|
20424
20115
|
}
|
|
20425
20116
|
]
|
|
20426
20117
|
};
|
|
@@ -20432,13 +20123,12 @@ function registerReviseTool(server, memory) {
|
|
|
20432
20123
|
|
|
20433
20124
|
// src/mcp/server.ts
|
|
20434
20125
|
var SERVER_INSTRUCTIONS = [
|
|
20435
|
-
"
|
|
20436
|
-
"
|
|
20437
|
-
"
|
|
20438
|
-
"
|
|
20439
|
-
"
|
|
20440
|
-
"
|
|
20441
|
-
"Do not store secrets, temporary task state, or facts obvious from current code or git history."
|
|
20126
|
+
"Durable memory for stable preferences, corrections, reusable decisions, and project context not obvious from code or git history.",
|
|
20127
|
+
"Workflow: (1) Call `review` with the current workspace at conversation start -- this loads workspace and global memories into context.",
|
|
20128
|
+
"(2) During the session, call `remember` to save a new fact, `revise` to correct content or promote a project-scoped memory to global scope, and call `forget` to remove one that is wrong or obsolete.",
|
|
20129
|
+
"Always check loaded memories before calling `remember` to avoid duplicates -- use `revise` instead when the fact already exists.",
|
|
20130
|
+
"Pass workspace on `remember` for project-scoped memory. Omit it only for facts that apply across all projects.",
|
|
20131
|
+
"Never store secrets, temporary task state, or facts obvious from current code or git history."
|
|
20442
20132
|
].join(" ");
|
|
20443
20133
|
function createMcpServer(memory, version3) {
|
|
20444
20134
|
const server = new McpServer({
|
|
@@ -20448,27 +20138,122 @@ function createMcpServer(memory, version3) {
|
|
|
20448
20138
|
instructions: SERVER_INSTRUCTIONS
|
|
20449
20139
|
});
|
|
20450
20140
|
registerRememberTool(server, memory);
|
|
20451
|
-
registerRecallTool(server, memory);
|
|
20452
20141
|
registerReviseTool(server, memory);
|
|
20453
20142
|
registerForgetTool(server, memory);
|
|
20454
20143
|
registerReviewTool(server, memory);
|
|
20455
20144
|
return server;
|
|
20456
20145
|
}
|
|
20457
20146
|
|
|
20147
|
+
// src/memory-service.ts
|
|
20148
|
+
var DEFAULT_LIST_LIMIT = 15;
|
|
20149
|
+
var MAX_LIST_LIMIT = 100;
|
|
20150
|
+
|
|
20151
|
+
class MemoryService {
|
|
20152
|
+
repository;
|
|
20153
|
+
workspaceResolver;
|
|
20154
|
+
constructor(repository, workspaceResolver) {
|
|
20155
|
+
this.repository = repository;
|
|
20156
|
+
this.workspaceResolver = workspaceResolver;
|
|
20157
|
+
}
|
|
20158
|
+
async create(input) {
|
|
20159
|
+
const content = input.content.trim();
|
|
20160
|
+
if (!content) {
|
|
20161
|
+
throw new ValidationError("Memory content is required.");
|
|
20162
|
+
}
|
|
20163
|
+
const workspace = await this.normalizeWorkspaceInput(input.workspace);
|
|
20164
|
+
return this.repository.create({ content, workspace });
|
|
20165
|
+
}
|
|
20166
|
+
async update(input) {
|
|
20167
|
+
const content = this.normalizeUpdateContent(input.content);
|
|
20168
|
+
const workspace = await this.normalizeUpdateWorkspace(input.workspace);
|
|
20169
|
+
return this.repository.update({ id: input.id, content, workspace });
|
|
20170
|
+
}
|
|
20171
|
+
async delete(input) {
|
|
20172
|
+
const id = input.id.trim();
|
|
20173
|
+
if (!id)
|
|
20174
|
+
throw new ValidationError("Memory id is required.");
|
|
20175
|
+
const existingMemory = await this.repository.get(id);
|
|
20176
|
+
if (!existingMemory) {
|
|
20177
|
+
throw new NotFoundError(`Memory not found: ${id}`);
|
|
20178
|
+
}
|
|
20179
|
+
await this.repository.delete({ id });
|
|
20180
|
+
return existingMemory;
|
|
20181
|
+
}
|
|
20182
|
+
async get(id) {
|
|
20183
|
+
return this.repository.get(id);
|
|
20184
|
+
}
|
|
20185
|
+
async list(input) {
|
|
20186
|
+
const queryWorkspace = input.workspace?.trim() || undefined;
|
|
20187
|
+
const workspace = queryWorkspace ? await this.workspaceResolver.resolve(queryWorkspace) : undefined;
|
|
20188
|
+
const page = await this.repository.list({
|
|
20189
|
+
workspace,
|
|
20190
|
+
global: input.global,
|
|
20191
|
+
offset: normalizeOffset(input.offset),
|
|
20192
|
+
limit: normalizeListLimit(input.limit)
|
|
20193
|
+
});
|
|
20194
|
+
return {
|
|
20195
|
+
items: page.items.map((item) => remapWorkspace(item, workspace, queryWorkspace)),
|
|
20196
|
+
hasMore: page.hasMore
|
|
20197
|
+
};
|
|
20198
|
+
}
|
|
20199
|
+
async listWorkspaces() {
|
|
20200
|
+
return this.repository.listWorkspaces();
|
|
20201
|
+
}
|
|
20202
|
+
normalizeUpdateContent(value) {
|
|
20203
|
+
if (value === undefined) {
|
|
20204
|
+
return;
|
|
20205
|
+
}
|
|
20206
|
+
const trimmed = value.trim();
|
|
20207
|
+
if (!trimmed) {
|
|
20208
|
+
throw new ValidationError("Memory content is required.");
|
|
20209
|
+
}
|
|
20210
|
+
return trimmed;
|
|
20211
|
+
}
|
|
20212
|
+
async normalizeUpdateWorkspace(value) {
|
|
20213
|
+
if (value === undefined || value === null) {
|
|
20214
|
+
return value;
|
|
20215
|
+
}
|
|
20216
|
+
return this.normalizeWorkspaceInput(value);
|
|
20217
|
+
}
|
|
20218
|
+
async normalizeWorkspaceInput(value) {
|
|
20219
|
+
if (value === undefined) {
|
|
20220
|
+
return;
|
|
20221
|
+
}
|
|
20222
|
+
const trimmed = value.trim();
|
|
20223
|
+
if (!trimmed) {
|
|
20224
|
+
throw new ValidationError("Workspace is required.");
|
|
20225
|
+
}
|
|
20226
|
+
return this.workspaceResolver.resolve(trimmed);
|
|
20227
|
+
}
|
|
20228
|
+
}
|
|
20229
|
+
function normalizeOffset(value) {
|
|
20230
|
+
return Number.isInteger(value) && value && value > 0 ? value : 0;
|
|
20231
|
+
}
|
|
20232
|
+
function normalizeListLimit(value) {
|
|
20233
|
+
if (!Number.isInteger(value) || value === undefined) {
|
|
20234
|
+
return DEFAULT_LIST_LIMIT;
|
|
20235
|
+
}
|
|
20236
|
+
return Math.min(Math.max(value, 1), MAX_LIST_LIMIT);
|
|
20237
|
+
}
|
|
20238
|
+
function remapWorkspace(record3, canonicalWorkspace, queryWorkspace) {
|
|
20239
|
+
if (record3.workspace !== canonicalWorkspace) {
|
|
20240
|
+
return record3;
|
|
20241
|
+
}
|
|
20242
|
+
return { ...record3, workspace: queryWorkspace };
|
|
20243
|
+
}
|
|
20244
|
+
|
|
20458
20245
|
// src/sqlite/db.ts
|
|
20459
|
-
import { mkdirSync
|
|
20246
|
+
import { mkdirSync } from "node:fs";
|
|
20460
20247
|
import { dirname } from "node:path";
|
|
20461
20248
|
import Database from "better-sqlite3";
|
|
20462
20249
|
|
|
20463
20250
|
// src/sqlite/memory-schema.ts
|
|
20464
|
-
function createMemoriesTable(database
|
|
20465
|
-
const embeddingColumn = getEmbeddingColumnSql(options.embeddingColumn);
|
|
20251
|
+
function createMemoriesTable(database) {
|
|
20466
20252
|
database.exec(`
|
|
20467
20253
|
CREATE TABLE IF NOT EXISTS memories (
|
|
20468
20254
|
id TEXT PRIMARY KEY,
|
|
20469
20255
|
content TEXT NOT NULL,
|
|
20470
20256
|
workspace TEXT,
|
|
20471
|
-
${embeddingColumn}
|
|
20472
20257
|
created_at INTEGER NOT NULL,
|
|
20473
20258
|
updated_at INTEGER NOT NULL
|
|
20474
20259
|
);
|
|
@@ -20476,7 +20261,7 @@ function createMemoriesTable(database, options) {
|
|
|
20476
20261
|
}
|
|
20477
20262
|
function createMemoryIndexes(database) {
|
|
20478
20263
|
database.exec(`
|
|
20479
|
-
CREATE INDEX IF NOT EXISTS
|
|
20264
|
+
CREATE INDEX IF NOT EXISTS idx_memories_updated_at ON memories(updated_at);
|
|
20480
20265
|
CREATE INDEX IF NOT EXISTS idx_memories_workspace ON memories(workspace);
|
|
20481
20266
|
`);
|
|
20482
20267
|
}
|
|
@@ -20516,82 +20301,38 @@ function dropMemorySearchArtifacts(database) {
|
|
|
20516
20301
|
DROP TABLE IF EXISTS memories_fts;
|
|
20517
20302
|
`);
|
|
20518
20303
|
}
|
|
20519
|
-
function getEmbeddingColumnSql(mode) {
|
|
20520
|
-
switch (mode) {
|
|
20521
|
-
case "omit":
|
|
20522
|
-
return "";
|
|
20523
|
-
case "nullable":
|
|
20524
|
-
return `embedding BLOB,
|
|
20525
|
-
`;
|
|
20526
|
-
case "required":
|
|
20527
|
-
return `embedding BLOB NOT NULL,
|
|
20528
|
-
`;
|
|
20529
|
-
}
|
|
20530
|
-
}
|
|
20531
20304
|
|
|
20532
20305
|
// src/sqlite/migrations/001-create-memory-schema.ts
|
|
20533
20306
|
var createMemorySchemaMigration = {
|
|
20534
20307
|
version: 1,
|
|
20535
20308
|
async up(database) {
|
|
20536
|
-
createMemoriesTable(database
|
|
20309
|
+
createMemoriesTable(database);
|
|
20537
20310
|
createMemoryIndexes(database);
|
|
20538
20311
|
createMemorySearchArtifacts(database);
|
|
20539
20312
|
}
|
|
20540
20313
|
};
|
|
20541
20314
|
|
|
20542
|
-
// src/sqlite/embedding-codec.ts
|
|
20543
|
-
var FLOAT32_BYTE_WIDTH = 4;
|
|
20544
|
-
function encodeEmbedding(vector) {
|
|
20545
|
-
const typedArray = Float32Array.from(vector);
|
|
20546
|
-
return new Uint8Array(typedArray.buffer.slice(0));
|
|
20547
|
-
}
|
|
20548
|
-
function decodeEmbedding(value) {
|
|
20549
|
-
const bytes = toUint8Array(value);
|
|
20550
|
-
if (bytes.byteLength === 0) {
|
|
20551
|
-
throw new Error("Embedding blob is empty.");
|
|
20552
|
-
}
|
|
20553
|
-
if (bytes.byteLength % FLOAT32_BYTE_WIDTH !== 0) {
|
|
20554
|
-
throw new Error("Embedding blob length is not a multiple of 4.");
|
|
20555
|
-
}
|
|
20556
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
20557
|
-
const vector = [];
|
|
20558
|
-
for (let offset = 0;offset < bytes.byteLength; offset += FLOAT32_BYTE_WIDTH) {
|
|
20559
|
-
vector.push(view.getFloat32(offset, true));
|
|
20560
|
-
}
|
|
20561
|
-
return vector;
|
|
20562
|
-
}
|
|
20563
|
-
function toUint8Array(value) {
|
|
20564
|
-
if (value instanceof Uint8Array) {
|
|
20565
|
-
return value;
|
|
20566
|
-
}
|
|
20567
|
-
if (value instanceof ArrayBuffer) {
|
|
20568
|
-
return new Uint8Array(value);
|
|
20569
|
-
}
|
|
20570
|
-
throw new Error("Expected embedding blob as Uint8Array or ArrayBuffer.");
|
|
20571
|
-
}
|
|
20572
|
-
|
|
20573
20315
|
// src/sqlite/migrations/002-add-memory-embedding.ts
|
|
20574
|
-
function createAddMemoryEmbeddingMigration(
|
|
20316
|
+
function createAddMemoryEmbeddingMigration() {
|
|
20575
20317
|
return {
|
|
20576
20318
|
version: 2,
|
|
20577
20319
|
async up(database) {
|
|
20578
20320
|
database.exec("ALTER TABLE memories ADD COLUMN embedding BLOB");
|
|
20579
|
-
const rows = database.prepare("SELECT id, content FROM memories ORDER BY created_at ASC").all();
|
|
20580
|
-
const updateStatement = database.prepare("UPDATE memories SET embedding = ? WHERE id = ?");
|
|
20581
|
-
for (const row of rows) {
|
|
20582
|
-
const embedding = await embeddingService.createVector(row.content);
|
|
20583
|
-
updateStatement.run(encodeEmbedding(embedding), row.id);
|
|
20584
|
-
}
|
|
20585
|
-
const nullRows = database.prepare("SELECT COUNT(*) AS count FROM memories WHERE embedding IS NULL").all();
|
|
20586
|
-
if ((nullRows[0]?.count ?? 0) > 0) {
|
|
20587
|
-
throw new Error("Failed to backfill embeddings for all memories.");
|
|
20588
|
-
}
|
|
20589
20321
|
dropMemorySearchArtifacts(database);
|
|
20590
20322
|
database.exec("ALTER TABLE memories RENAME TO memories_old");
|
|
20591
|
-
|
|
20323
|
+
database.exec(`
|
|
20324
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
20325
|
+
id TEXT PRIMARY KEY,
|
|
20326
|
+
content TEXT NOT NULL,
|
|
20327
|
+
workspace TEXT,
|
|
20328
|
+
embedding BLOB NOT NULL,
|
|
20329
|
+
created_at INTEGER NOT NULL,
|
|
20330
|
+
updated_at INTEGER NOT NULL
|
|
20331
|
+
);
|
|
20332
|
+
`);
|
|
20592
20333
|
database.exec(`
|
|
20593
20334
|
INSERT INTO memories (id, content, workspace, embedding, created_at, updated_at)
|
|
20594
|
-
SELECT id, content, workspace, embedding, created_at, updated_at
|
|
20335
|
+
SELECT id, content, workspace, COALESCE(embedding, X'00000000'), created_at, updated_at
|
|
20595
20336
|
FROM memories_old
|
|
20596
20337
|
`);
|
|
20597
20338
|
database.exec("DROP TABLE memories_old");
|
|
@@ -20619,12 +20360,31 @@ function createNormalizeWorkspaceMigration(workspaceResolver) {
|
|
|
20619
20360
|
};
|
|
20620
20361
|
}
|
|
20621
20362
|
|
|
20363
|
+
// src/sqlite/migrations/004-remove-memory-embedding.ts
|
|
20364
|
+
var removeMemoryEmbeddingMigration = {
|
|
20365
|
+
version: 4,
|
|
20366
|
+
async up(database) {
|
|
20367
|
+
dropMemorySearchArtifacts(database);
|
|
20368
|
+
database.exec("ALTER TABLE memories RENAME TO memories_old");
|
|
20369
|
+
createMemoriesTable(database);
|
|
20370
|
+
database.exec(`
|
|
20371
|
+
INSERT INTO memories (id, content, workspace, created_at, updated_at)
|
|
20372
|
+
SELECT id, content, workspace, created_at, updated_at
|
|
20373
|
+
FROM memories_old
|
|
20374
|
+
`);
|
|
20375
|
+
database.exec("DROP TABLE memories_old");
|
|
20376
|
+
createMemoryIndexes(database);
|
|
20377
|
+
createMemorySearchArtifacts(database, true);
|
|
20378
|
+
}
|
|
20379
|
+
};
|
|
20380
|
+
|
|
20622
20381
|
// src/sqlite/migrations/index.ts
|
|
20623
20382
|
function createMemoryMigrations(options) {
|
|
20624
20383
|
return [
|
|
20625
20384
|
createMemorySchemaMigration,
|
|
20626
|
-
createAddMemoryEmbeddingMigration(
|
|
20627
|
-
createNormalizeWorkspaceMigration(options.workspaceResolver)
|
|
20385
|
+
createAddMemoryEmbeddingMigration(),
|
|
20386
|
+
createNormalizeWorkspaceMigration(options.workspaceResolver),
|
|
20387
|
+
removeMemoryEmbeddingMigration
|
|
20628
20388
|
];
|
|
20629
20389
|
}
|
|
20630
20390
|
|
|
@@ -20638,7 +20398,7 @@ var PRAGMA_STATEMENTS = [
|
|
|
20638
20398
|
async function openMemoryDatabase(databasePath, options) {
|
|
20639
20399
|
let database;
|
|
20640
20400
|
try {
|
|
20641
|
-
|
|
20401
|
+
mkdirSync(dirname(databasePath), { recursive: true });
|
|
20642
20402
|
database = new Database(databasePath);
|
|
20643
20403
|
await initializeMemoryDatabase(database, createMemoryMigrations(options));
|
|
20644
20404
|
return database;
|
|
@@ -20712,7 +20472,6 @@ function validateMigrations(migrations) {
|
|
|
20712
20472
|
}
|
|
20713
20473
|
// src/sqlite/repository.ts
|
|
20714
20474
|
import { randomUUID } from "node:crypto";
|
|
20715
|
-
var DEFAULT_SEARCH_LIMIT = 15;
|
|
20716
20475
|
var DEFAULT_LIST_LIMIT2 = 15;
|
|
20717
20476
|
|
|
20718
20477
|
class SqliteMemoryRepository {
|
|
@@ -20725,24 +20484,11 @@ class SqliteMemoryRepository {
|
|
|
20725
20484
|
constructor(database) {
|
|
20726
20485
|
this.database = database;
|
|
20727
20486
|
this.insertStatement = database.prepare(`
|
|
20728
|
-
INSERT INTO memories (
|
|
20729
|
-
|
|
20730
|
-
content,
|
|
20731
|
-
workspace,
|
|
20732
|
-
embedding,
|
|
20733
|
-
created_at,
|
|
20734
|
-
updated_at
|
|
20735
|
-
) VALUES (
|
|
20736
|
-
?,
|
|
20737
|
-
?,
|
|
20738
|
-
?,
|
|
20739
|
-
?,
|
|
20740
|
-
?,
|
|
20741
|
-
?
|
|
20742
|
-
)
|
|
20487
|
+
INSERT INTO memories (id, content, workspace, created_at, updated_at)
|
|
20488
|
+
VALUES (?, ?, ?, ?, ?)
|
|
20743
20489
|
`);
|
|
20744
|
-
this.getStatement = database.prepare("SELECT id, content, workspace,
|
|
20745
|
-
this.updateStatement = database.prepare("UPDATE memories SET content = ?,
|
|
20490
|
+
this.getStatement = database.prepare("SELECT id, content, workspace, created_at, updated_at FROM memories WHERE id = ?");
|
|
20491
|
+
this.updateStatement = database.prepare("UPDATE memories SET content = ?, workspace = ?, updated_at = ? WHERE id = ?");
|
|
20746
20492
|
this.deleteStatement = database.prepare("DELETE FROM memories WHERE id = ?");
|
|
20747
20493
|
this.listWorkspacesStatement = database.prepare("SELECT DISTINCT workspace FROM memories WHERE workspace IS NOT NULL ORDER BY workspace");
|
|
20748
20494
|
}
|
|
@@ -20752,63 +20498,21 @@ class SqliteMemoryRepository {
|
|
|
20752
20498
|
const memory = {
|
|
20753
20499
|
id: randomUUID(),
|
|
20754
20500
|
content: input.content,
|
|
20755
|
-
embedding: input.embedding,
|
|
20756
20501
|
workspace: input.workspace,
|
|
20757
20502
|
createdAt: now,
|
|
20758
20503
|
updatedAt: now
|
|
20759
20504
|
};
|
|
20760
|
-
this.insertStatement.run(memory.id, memory.content, memory.workspace,
|
|
20505
|
+
this.insertStatement.run(memory.id, memory.content, memory.workspace, memory.createdAt.getTime(), memory.updatedAt.getTime());
|
|
20761
20506
|
return memory;
|
|
20762
20507
|
} catch (error2) {
|
|
20763
20508
|
throw new PersistenceError("Failed to save memory.", { cause: error2 });
|
|
20764
20509
|
}
|
|
20765
20510
|
}
|
|
20766
|
-
async search(query) {
|
|
20767
|
-
try {
|
|
20768
|
-
const whereParams = [toFtsQuery(query.terms)];
|
|
20769
|
-
const limit = query.limit ?? DEFAULT_SEARCH_LIMIT;
|
|
20770
|
-
const whereClauses = ["memories_fts MATCH ?"];
|
|
20771
|
-
if (query.updatedAfter) {
|
|
20772
|
-
whereClauses.push("m.updated_at >= ?");
|
|
20773
|
-
whereParams.push(query.updatedAfter.getTime());
|
|
20774
|
-
}
|
|
20775
|
-
if (query.updatedBefore) {
|
|
20776
|
-
whereClauses.push("m.updated_at <= ?");
|
|
20777
|
-
whereParams.push(query.updatedBefore.getTime());
|
|
20778
|
-
}
|
|
20779
|
-
const params = [...whereParams, limit];
|
|
20780
|
-
const statement = this.database.prepare(`
|
|
20781
|
-
SELECT
|
|
20782
|
-
m.id,
|
|
20783
|
-
m.content,
|
|
20784
|
-
m.workspace,
|
|
20785
|
-
m.embedding,
|
|
20786
|
-
m.created_at,
|
|
20787
|
-
m.updated_at,
|
|
20788
|
-
MAX(0, -bm25(memories_fts)) AS score
|
|
20789
|
-
FROM memories_fts
|
|
20790
|
-
INNER JOIN memories AS m ON m.rowid = memories_fts.rowid
|
|
20791
|
-
WHERE ${whereClauses.join(" AND ")}
|
|
20792
|
-
ORDER BY score DESC
|
|
20793
|
-
LIMIT ?
|
|
20794
|
-
`);
|
|
20795
|
-
const rows = statement.all(...params);
|
|
20796
|
-
const maxScore = Math.max(...rows.map((row) => row.score), 0);
|
|
20797
|
-
return rows.map((row) => ({
|
|
20798
|
-
...toMemoryEntity(row),
|
|
20799
|
-
score: toNormalizedScore(maxScore > 0 ? row.score / maxScore : 0)
|
|
20800
|
-
}));
|
|
20801
|
-
} catch (error2) {
|
|
20802
|
-
throw new PersistenceError("Failed to search memories.", {
|
|
20803
|
-
cause: error2
|
|
20804
|
-
});
|
|
20805
|
-
}
|
|
20806
|
-
}
|
|
20807
20511
|
async get(id) {
|
|
20808
20512
|
try {
|
|
20809
20513
|
const rows = this.getStatement.all(id);
|
|
20810
20514
|
const row = rows[0];
|
|
20811
|
-
return row ?
|
|
20515
|
+
return row ? toMemoryRecord(row) : undefined;
|
|
20812
20516
|
} catch (error2) {
|
|
20813
20517
|
throw new PersistenceError("Failed to find memory.", { cause: error2 });
|
|
20814
20518
|
}
|
|
@@ -20819,35 +20523,46 @@ class SqliteMemoryRepository {
|
|
|
20819
20523
|
const params = [];
|
|
20820
20524
|
const offset = options.offset ?? 0;
|
|
20821
20525
|
const limit = options.limit ?? DEFAULT_LIST_LIMIT2;
|
|
20822
|
-
if (options.workspace) {
|
|
20526
|
+
if (options.workspace && options.global) {
|
|
20527
|
+
whereClauses.push("(workspace = ? OR workspace IS NULL)");
|
|
20528
|
+
params.push(options.workspace);
|
|
20529
|
+
} else if (options.workspace) {
|
|
20823
20530
|
whereClauses.push("workspace = ?");
|
|
20824
20531
|
params.push(options.workspace);
|
|
20825
|
-
} else if (options.
|
|
20532
|
+
} else if (options.global) {
|
|
20826
20533
|
whereClauses.push("workspace IS NULL");
|
|
20827
20534
|
}
|
|
20828
20535
|
const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
20829
20536
|
const queryLimit = limit + 1;
|
|
20830
20537
|
params.push(queryLimit, offset);
|
|
20831
20538
|
const statement = this.database.prepare(`
|
|
20832
|
-
SELECT id, content, workspace,
|
|
20539
|
+
SELECT id, content, workspace, created_at, updated_at
|
|
20833
20540
|
FROM memories
|
|
20834
20541
|
${whereClause}
|
|
20835
|
-
ORDER BY
|
|
20542
|
+
ORDER BY updated_at DESC
|
|
20836
20543
|
LIMIT ? OFFSET ?
|
|
20837
20544
|
`);
|
|
20838
20545
|
const rows = statement.all(...params);
|
|
20839
20546
|
const hasMore = rows.length > limit;
|
|
20840
|
-
const items = (hasMore ? rows.slice(0, limit) : rows).map(
|
|
20547
|
+
const items = (hasMore ? rows.slice(0, limit) : rows).map(toMemoryRecord);
|
|
20841
20548
|
return { items, hasMore };
|
|
20842
20549
|
} catch (error2) {
|
|
20843
20550
|
throw new PersistenceError("Failed to list memories.", { cause: error2 });
|
|
20844
20551
|
}
|
|
20845
20552
|
}
|
|
20846
20553
|
async update(input) {
|
|
20554
|
+
const existingMemory = await this.get(input.id);
|
|
20555
|
+
if (!existingMemory) {
|
|
20556
|
+
throw new NotFoundError(`Memory not found: ${input.id}`);
|
|
20557
|
+
}
|
|
20558
|
+
const patch = applyMemoryPatch(existingMemory, input);
|
|
20559
|
+
if (!hasMemoryChanges(existingMemory, patch)) {
|
|
20560
|
+
return existingMemory;
|
|
20561
|
+
}
|
|
20847
20562
|
let result;
|
|
20848
20563
|
try {
|
|
20849
20564
|
const now = Date.now();
|
|
20850
|
-
result = this.updateStatement.run(
|
|
20565
|
+
result = this.updateStatement.run(patch.content, patch.workspace, now, input.id);
|
|
20851
20566
|
} catch (error2) {
|
|
20852
20567
|
throw new PersistenceError("Failed to update memory.", { cause: error2 });
|
|
20853
20568
|
}
|
|
@@ -20880,25 +20595,28 @@ class SqliteMemoryRepository {
|
|
|
20880
20595
|
}
|
|
20881
20596
|
}
|
|
20882
20597
|
}
|
|
20883
|
-
var
|
|
20598
|
+
var toMemoryRecord = (row) => ({
|
|
20884
20599
|
id: row.id,
|
|
20885
20600
|
content: row.content,
|
|
20886
|
-
embedding: decodeEmbedding(row.embedding),
|
|
20887
20601
|
workspace: row.workspace ?? undefined,
|
|
20888
20602
|
createdAt: new Date(row.created_at),
|
|
20889
20603
|
updatedAt: new Date(row.updated_at)
|
|
20890
20604
|
});
|
|
20891
|
-
|
|
20892
|
-
|
|
20893
|
-
|
|
20894
|
-
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
20605
|
+
function applyMemoryPatch(memory, input) {
|
|
20606
|
+
return {
|
|
20607
|
+
content: input.content ?? memory.content,
|
|
20608
|
+
workspace: input.workspace === undefined ? toStoredWorkspace(memory.workspace) : input.workspace
|
|
20609
|
+
};
|
|
20610
|
+
}
|
|
20611
|
+
function hasMemoryChanges(memory, patch) {
|
|
20612
|
+
return patch.content !== memory.content || patch.workspace !== toStoredWorkspace(memory.workspace);
|
|
20613
|
+
}
|
|
20614
|
+
function toStoredWorkspace(workspace) {
|
|
20615
|
+
return workspace ?? null;
|
|
20898
20616
|
}
|
|
20899
20617
|
// node_modules/@hono/node-server/dist/index.mjs
|
|
20900
20618
|
import { createServer as createServerHTTP } from "http";
|
|
20901
|
-
import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
|
|
20619
|
+
import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
|
|
20902
20620
|
import { Http2ServerRequest } from "http2";
|
|
20903
20621
|
import { Readable } from "stream";
|
|
20904
20622
|
import crypto from "crypto";
|
|
@@ -21208,6 +20926,48 @@ if (typeof global.crypto === "undefined") {
|
|
|
21208
20926
|
global.crypto = crypto;
|
|
21209
20927
|
}
|
|
21210
20928
|
var outgoingEnded = Symbol("outgoingEnded");
|
|
20929
|
+
var incomingDraining = Symbol("incomingDraining");
|
|
20930
|
+
var DRAIN_TIMEOUT_MS = 500;
|
|
20931
|
+
var MAX_DRAIN_BYTES = 64 * 1024 * 1024;
|
|
20932
|
+
var drainIncoming = (incoming) => {
|
|
20933
|
+
const incomingWithDrainState = incoming;
|
|
20934
|
+
if (incoming.destroyed || incomingWithDrainState[incomingDraining]) {
|
|
20935
|
+
return;
|
|
20936
|
+
}
|
|
20937
|
+
incomingWithDrainState[incomingDraining] = true;
|
|
20938
|
+
if (incoming instanceof Http2ServerRequest2) {
|
|
20939
|
+
try {
|
|
20940
|
+
incoming.stream?.close?.(h2constants.NGHTTP2_NO_ERROR);
|
|
20941
|
+
} catch {}
|
|
20942
|
+
return;
|
|
20943
|
+
}
|
|
20944
|
+
let bytesRead = 0;
|
|
20945
|
+
const cleanup = () => {
|
|
20946
|
+
clearTimeout(timer);
|
|
20947
|
+
incoming.off("data", onData);
|
|
20948
|
+
incoming.off("end", cleanup);
|
|
20949
|
+
incoming.off("error", cleanup);
|
|
20950
|
+
};
|
|
20951
|
+
const forceClose = () => {
|
|
20952
|
+
cleanup();
|
|
20953
|
+
const socket = incoming.socket;
|
|
20954
|
+
if (socket && !socket.destroyed) {
|
|
20955
|
+
socket.destroySoon();
|
|
20956
|
+
}
|
|
20957
|
+
};
|
|
20958
|
+
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
20959
|
+
timer.unref?.();
|
|
20960
|
+
const onData = (chunk) => {
|
|
20961
|
+
bytesRead += chunk.length;
|
|
20962
|
+
if (bytesRead > MAX_DRAIN_BYTES) {
|
|
20963
|
+
forceClose();
|
|
20964
|
+
}
|
|
20965
|
+
};
|
|
20966
|
+
incoming.on("data", onData);
|
|
20967
|
+
incoming.on("end", cleanup);
|
|
20968
|
+
incoming.on("error", cleanup);
|
|
20969
|
+
incoming.resume();
|
|
20970
|
+
};
|
|
21211
20971
|
var handleRequestError = () => new Response(null, {
|
|
21212
20972
|
status: 400
|
|
21213
20973
|
});
|
|
@@ -21371,14 +21131,18 @@ var getRequestListener = (fetchCallback, options = {}) => {
|
|
|
21371
21131
|
setTimeout(() => {
|
|
21372
21132
|
if (!incomingEnded) {
|
|
21373
21133
|
setTimeout(() => {
|
|
21374
|
-
incoming
|
|
21375
|
-
outgoing.destroy();
|
|
21134
|
+
drainIncoming(incoming);
|
|
21376
21135
|
});
|
|
21377
21136
|
}
|
|
21378
21137
|
});
|
|
21379
21138
|
}
|
|
21380
21139
|
};
|
|
21381
21140
|
}
|
|
21141
|
+
outgoing.on("finish", () => {
|
|
21142
|
+
if (!incomingEnded) {
|
|
21143
|
+
drainIncoming(incoming);
|
|
21144
|
+
}
|
|
21145
|
+
});
|
|
21382
21146
|
}
|
|
21383
21147
|
outgoing.on("close", () => {
|
|
21384
21148
|
const abortController = req[abortControllerKey];
|
|
@@ -21393,7 +21157,7 @@ var getRequestListener = (fetchCallback, options = {}) => {
|
|
|
21393
21157
|
setTimeout(() => {
|
|
21394
21158
|
if (!incomingEnded) {
|
|
21395
21159
|
setTimeout(() => {
|
|
21396
|
-
incoming
|
|
21160
|
+
drainIncoming(incoming);
|
|
21397
21161
|
});
|
|
21398
21162
|
}
|
|
21399
21163
|
});
|
|
@@ -21818,7 +21582,7 @@ var HonoRequest = class {
|
|
|
21818
21582
|
return headerData;
|
|
21819
21583
|
}
|
|
21820
21584
|
async parseBody(options) {
|
|
21821
|
-
return
|
|
21585
|
+
return parseBody(this, options);
|
|
21822
21586
|
}
|
|
21823
21587
|
#cachedBody = (key) => {
|
|
21824
21588
|
const { bodyCache, raw } = this;
|
|
@@ -24211,7 +23975,7 @@ function createPageRoutes(memory) {
|
|
|
24211
23975
|
const wsFilter = workspace && !isNoWorkspace ? workspace : undefined;
|
|
24212
23976
|
const page = await memory.list({
|
|
24213
23977
|
workspace: wsFilter,
|
|
24214
|
-
|
|
23978
|
+
global: isNoWorkspace,
|
|
24215
23979
|
offset: (pageNum - 1) * DEFAULT_LIST_LIMIT3,
|
|
24216
23980
|
limit: DEFAULT_LIST_LIMIT3
|
|
24217
23981
|
});
|
|
@@ -24294,9 +24058,9 @@ function createGitWorkspaceResolver(options = {}) {
|
|
|
24294
24058
|
const cache = new Map;
|
|
24295
24059
|
return {
|
|
24296
24060
|
async resolve(workspace) {
|
|
24297
|
-
const trimmed =
|
|
24061
|
+
const trimmed = workspace.trim();
|
|
24298
24062
|
if (!trimmed) {
|
|
24299
|
-
|
|
24063
|
+
throw new ValidationError("Workspace is required.");
|
|
24300
24064
|
}
|
|
24301
24065
|
const cached2 = cache.get(trimmed);
|
|
24302
24066
|
if (cached2) {
|
|
@@ -24325,20 +24089,13 @@ async function defaultGetGitCommonDir(cwd) {
|
|
|
24325
24089
|
});
|
|
24326
24090
|
return stdout.trim();
|
|
24327
24091
|
}
|
|
24328
|
-
function normalizeOptionalString2(value) {
|
|
24329
|
-
const trimmed = value?.trim();
|
|
24330
|
-
return trimmed ? trimmed : undefined;
|
|
24331
|
-
}
|
|
24332
24092
|
|
|
24333
24093
|
// src/index.ts
|
|
24334
24094
|
var config2 = resolveConfig();
|
|
24335
|
-
configureModelsCache(config2.modelsCachePath);
|
|
24336
|
-
var embeddingService = new EmbeddingService;
|
|
24337
24095
|
var workspaceResolver = createGitWorkspaceResolver();
|
|
24338
|
-
var database = await openMemoryDatabase(config2.databasePath, {
|
|
24096
|
+
var database = await openMemoryDatabase(config2.databasePath, { workspaceResolver });
|
|
24339
24097
|
var repository2 = new SqliteMemoryRepository(database);
|
|
24340
|
-
var memoryService = new MemoryService(repository2,
|
|
24341
|
-
embeddingService.warmup();
|
|
24098
|
+
var memoryService = new MemoryService(repository2, workspaceResolver);
|
|
24342
24099
|
if (config2.uiMode) {
|
|
24343
24100
|
let shutdown = function() {
|
|
24344
24101
|
server.close();
|