@hasna/mementos 0.14.73 → 0.14.75
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/cli/commands/agent.d.ts.map +1 -1
- package/dist/cli/commands/storage.d.ts.map +1 -1
- package/dist/cli/exit-codes.d.ts +13 -0
- package/dist/cli/exit-codes.d.ts.map +1 -0
- package/dist/cli/helpers.d.ts +1 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +180 -25
- package/dist/db/agents.d.ts +6 -0
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/api-mode.d.ts +33 -3
- package/dist/db/api-mode.d.ts.map +1 -1
- package/dist/db/audit.d.ts +14 -0
- package/dist/db/audit.d.ts.map +1 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/pg-migrations.d.ts.map +1 -1
- package/dist/db/store-backend.d.ts +11 -2
- package/dist/db/store-backend.d.ts.map +1 -1
- package/dist/index.js +93 -13
- package/dist/mcp/index.js +122 -13
- package/dist/server/index.js +106 -14
- package/dist/test-support/store-isolation.d.ts +22 -2
- package/dist/test-support/store-isolation.d.ts.map +1 -1
- package/dist/test-support/strip-ansi.d.ts +3 -0
- package/dist/test-support/strip-ansi.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4N5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8UzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK9D"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const EXIT_USAGE = 64;
|
|
2
|
+
/**
|
|
3
|
+
* Map a CommanderError onto this CLI's exit-status contract.
|
|
4
|
+
*
|
|
5
|
+
* Pure and total: given the `code`/`exitCode` pair Commander produces, returns
|
|
6
|
+
* the status the process should exit with. Success paths (`--help`, `--version`,
|
|
7
|
+
* which arrive with exitCode 0) are passed through untouched.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveExitCode(err: {
|
|
10
|
+
code?: string;
|
|
11
|
+
exitCode?: number;
|
|
12
|
+
}): number;
|
|
13
|
+
//# sourceMappingURL=exit-codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit-codes.d.ts","sourceRoot":"","sources":["../../src/cli/exit-codes.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,UAAU,KAAK,CAAC;AA2B7B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAOT"}
|
package/dist/cli/helpers.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare function importanceColor(importance: number): (s: string) => stri
|
|
|
6
6
|
export declare function colorScope(scope: MemoryScope): string;
|
|
7
7
|
export declare function colorCategory(category: MemoryCategory): string;
|
|
8
8
|
export declare function colorImportance(importance: number): string;
|
|
9
|
-
export declare function colorEntityType(type: string): string;
|
|
9
|
+
export declare function colorEntityType(type: string, suffix?: string): string;
|
|
10
10
|
export declare function resolveEntityArg(nameOrId: string, type?: EntityType): Entity;
|
|
11
11
|
export declare function outputJson(data: unknown): void;
|
|
12
12
|
export declare const DEFAULT_COMPACT_LIMIT = 20;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EACV,MAAM,EACN,cAAc,EACd,WAAW,EAEZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAM5D,wBAAgB,iBAAiB,IAAI,MAAM,CAe1C;AAsBD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAKzE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAErD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EACV,MAAM,EACN,cAAc,EACd,WAAW,EAEZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAM5D,wBAAgB,iBAAiB,IAAI,MAAM,CAe1C;AAsBD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAKzE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAErD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AAoBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAGjE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAqB5E;AAMD,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,SAAyB,GAAG,MAAM,CAKnG;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,IAAI,CAYP;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAuE9C;AAED,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,MAAM,EACT,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3D,MAAM,CASR;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BpD;AAMD,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,CAAC,EAAE,OAAO,KAAK,KAAK,CAYvE;AAMD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWzD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1D,UAAU,EAAE,UAAU,GACrB,MAAM,GAAG,IAAI,CA2Bf;AAMD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBjD;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAchD;AAMD,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAMD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAyBhE;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1B,UAAU,EAAE,UAAU,GACrB,IAAI,CAuIN;AAMD,eAAO,MAAM,YAAY,UAA6C,CAAC;AACvE,eAAO,MAAM,gBAAgB,UAAiD,CAAC;AAE/E,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQlF;AAED,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAW/F;AAED,sDAAsD;AACtD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAShF;AAED,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMrD;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CA0C1G;AAED,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAcxD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAMnE"}
|
package/dist/cli/index.js
CHANGED
|
@@ -3010,6 +3010,14 @@ function getApiModeEnvSources() {
|
|
|
3010
3010
|
databaseUrlKey: firstEnvKey(DATABASE_URL_ENV_KEYS)
|
|
3011
3011
|
};
|
|
3012
3012
|
}
|
|
3013
|
+
function getConfiguredApiEnv() {
|
|
3014
|
+
const rawBase = firstEnv(API_URL_ENV_KEYS);
|
|
3015
|
+
return {
|
|
3016
|
+
baseUrl: rawBase ? normalizeBase(rawBase) : null,
|
|
3017
|
+
apiKeyPresent: Boolean(firstEnvKey(API_KEY_ENV_KEYS)),
|
|
3018
|
+
dbPathKey: firstEnvKey(DB_PATH_ENV_KEYS)
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3013
3021
|
function isLoopbackHost(rawHost) {
|
|
3014
3022
|
const host = rawHost.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
|
|
3015
3023
|
return host === "localhost" || host === "::1" || /^127\./.test(host);
|
|
@@ -3055,6 +3063,8 @@ function assertUnambiguousStoreEnv() {
|
|
|
3055
3063
|
}
|
|
3056
3064
|
function getApiConfig() {
|
|
3057
3065
|
assertUnambiguousStoreEnv();
|
|
3066
|
+
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
3067
|
+
return null;
|
|
3058
3068
|
const rawBase = firstEnv(API_URL_ENV_KEYS);
|
|
3059
3069
|
const apiKey = firstEnv(API_KEY_ENV_KEYS);
|
|
3060
3070
|
if (!rawBase || !apiKey)
|
|
@@ -4120,6 +4130,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
|
|
|
4120
4130
|
`
|
|
4121
4131
|
${MEMORY_VERSION_SNAPSHOT_TRIGGER}
|
|
4122
4132
|
INSERT OR IGNORE INTO _migrations (id) VALUES (36);
|
|
4133
|
+
`,
|
|
4134
|
+
`
|
|
4135
|
+
DROP TRIGGER IF EXISTS audit_memory_insert;
|
|
4136
|
+
CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
|
|
4137
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
4138
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
|
|
4139
|
+
END;
|
|
4140
|
+
|
|
4141
|
+
DROP TRIGGER IF EXISTS audit_memory_update;
|
|
4142
|
+
CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
|
|
4143
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
|
|
4144
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
|
|
4145
|
+
json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
|
|
4146
|
+
datetime('now'));
|
|
4147
|
+
END;
|
|
4148
|
+
|
|
4149
|
+
DROP TRIGGER IF EXISTS audit_memory_delete;
|
|
4150
|
+
CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
|
|
4151
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
4152
|
+
VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
|
|
4153
|
+
END;
|
|
4154
|
+
|
|
4155
|
+
UPDATE memory_audit_log SET old_value_hash = NULL
|
|
4156
|
+
WHERE old_value_hash IS NOT NULL
|
|
4157
|
+
AND length(old_value_hash) = 32
|
|
4158
|
+
AND old_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
4159
|
+
|
|
4160
|
+
UPDATE memory_audit_log SET new_value_hash = NULL
|
|
4161
|
+
WHERE new_value_hash IS NOT NULL
|
|
4162
|
+
AND length(new_value_hash) = 32
|
|
4163
|
+
AND new_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
4164
|
+
|
|
4165
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (37);
|
|
4123
4166
|
`
|
|
4124
4167
|
];
|
|
4125
4168
|
});
|
|
@@ -4179,9 +4222,10 @@ function migrateGlobalDir() {
|
|
|
4179
4222
|
}
|
|
4180
4223
|
}
|
|
4181
4224
|
function getDbPath() {
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4225
|
+
for (const key of DB_PATH_ENV_KEYS) {
|
|
4226
|
+
const envPath = process.env[key]?.trim();
|
|
4227
|
+
if (envPath)
|
|
4228
|
+
return envPath;
|
|
4185
4229
|
}
|
|
4186
4230
|
const cwd = process.cwd();
|
|
4187
4231
|
const nearest = findNearestMementosDb(cwd);
|
|
@@ -4225,6 +4269,12 @@ function getDatabase(dbPath) {
|
|
|
4225
4269
|
if (getStorageMode() === "cloud") {
|
|
4226
4270
|
return getCloudDatabase();
|
|
4227
4271
|
}
|
|
4272
|
+
if (process.env["NODE_ENV"] === "test") {
|
|
4273
|
+
const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
|
|
4274
|
+
if (!hasExplicitDbPath) {
|
|
4275
|
+
throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4228
4278
|
}
|
|
4229
4279
|
const path = dbPath || getDbPath();
|
|
4230
4280
|
if (_db) {
|
|
@@ -6036,9 +6086,9 @@ function colorCategory(category) {
|
|
|
6036
6086
|
function colorImportance(importance) {
|
|
6037
6087
|
return importanceColor(importance)(String(importance));
|
|
6038
6088
|
}
|
|
6039
|
-
function colorEntityType(type) {
|
|
6089
|
+
function colorEntityType(type, suffix = "") {
|
|
6040
6090
|
const colorFn = entityTypeColor[type] || chalk.white;
|
|
6041
|
-
return colorFn(type);
|
|
6091
|
+
return colorFn(`${type}${suffix}`);
|
|
6042
6092
|
}
|
|
6043
6093
|
function resolveEntityArg(nameOrId, type) {
|
|
6044
6094
|
const byName = getEntityByName(nameOrId, type);
|
|
@@ -6691,13 +6741,46 @@ function getAgent(idOrName, db) {
|
|
|
6691
6741
|
return parseAgentRow(rows[0]);
|
|
6692
6742
|
return null;
|
|
6693
6743
|
}
|
|
6694
|
-
function
|
|
6695
|
-
|
|
6696
|
-
|
|
6744
|
+
function normalizedAgentListFilter(filter) {
|
|
6745
|
+
const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
|
|
6746
|
+
const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
|
|
6747
|
+
return { limit, offset };
|
|
6748
|
+
}
|
|
6749
|
+
function isDatabaseAdapter(value) {
|
|
6750
|
+
return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
|
|
6751
|
+
}
|
|
6752
|
+
function paginatedAgentQuery(baseSql, baseParams, filter) {
|
|
6753
|
+
const { limit, offset } = normalizedAgentListFilter(filter);
|
|
6754
|
+
let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
|
|
6755
|
+
const params = [...baseParams];
|
|
6756
|
+
if (limit !== undefined) {
|
|
6757
|
+
sql += " LIMIT ?";
|
|
6758
|
+
params.push(limit);
|
|
6759
|
+
} else if ((offset ?? 0) > 0) {
|
|
6760
|
+
sql += " LIMIT ?";
|
|
6761
|
+
params.push(UNBOUNDED_AGENT_LIST_LIMIT);
|
|
6762
|
+
}
|
|
6763
|
+
if ((offset ?? 0) > 0) {
|
|
6764
|
+
sql += " OFFSET ?";
|
|
6765
|
+
params.push(offset);
|
|
6766
|
+
}
|
|
6767
|
+
return { sql, params };
|
|
6768
|
+
}
|
|
6769
|
+
function listAgents(filterOrDb = {}, db) {
|
|
6770
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
6771
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
6772
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
6773
|
+
if (!explicitDb && isApiMode()) {
|
|
6774
|
+
const q = toQuery({
|
|
6775
|
+
limit: normalized.limit,
|
|
6776
|
+
offset: normalized.offset
|
|
6777
|
+
});
|
|
6778
|
+
const { data } = apiJson("GET", `/agents${q}`);
|
|
6697
6779
|
return data?.agents ?? [];
|
|
6698
6780
|
}
|
|
6699
|
-
const d =
|
|
6700
|
-
const
|
|
6781
|
+
const d = explicitDb || getDatabase();
|
|
6782
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
|
|
6783
|
+
const rows = d.query(sql).all(...params);
|
|
6701
6784
|
return rows.map(parseAgentRow);
|
|
6702
6785
|
}
|
|
6703
6786
|
function touchAgent(idOrName, db) {
|
|
@@ -6714,15 +6797,19 @@ function touchAgent(idOrName, db) {
|
|
|
6714
6797
|
return;
|
|
6715
6798
|
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
|
|
6716
6799
|
}
|
|
6717
|
-
function listAgentsByProject(projectId, db) {
|
|
6718
|
-
|
|
6719
|
-
|
|
6800
|
+
function listAgentsByProject(projectId, filterOrDb = {}, db) {
|
|
6801
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
6802
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
6803
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
6804
|
+
if (!explicitDb && isApiMode()) {
|
|
6805
|
+
const q = toQuery({ project_id: projectId, ...normalized });
|
|
6720
6806
|
const { data } = apiJson("GET", `/agents${q}`);
|
|
6721
6807
|
return data?.agents ?? [];
|
|
6722
6808
|
}
|
|
6723
|
-
const d =
|
|
6809
|
+
const d = explicitDb || getDatabase();
|
|
6724
6810
|
const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
|
|
6725
|
-
const
|
|
6811
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
|
|
6812
|
+
const rows = d.query(sql).all(...params);
|
|
6726
6813
|
return rows.map(parseAgentRow);
|
|
6727
6814
|
}
|
|
6728
6815
|
function updateAgent(id, updates, db) {
|
|
@@ -6770,12 +6857,13 @@ function updateAgent(id, updates, db) {
|
|
|
6770
6857
|
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
|
|
6771
6858
|
return getAgent(agent.id, d);
|
|
6772
6859
|
}
|
|
6773
|
-
var CONFLICT_WINDOW_MS;
|
|
6860
|
+
var CONFLICT_WINDOW_MS, UNBOUNDED_AGENT_LIST_LIMIT;
|
|
6774
6861
|
var init_agents = __esm(() => {
|
|
6775
6862
|
init_types();
|
|
6776
6863
|
init_database();
|
|
6777
6864
|
init_api_mode();
|
|
6778
6865
|
CONFLICT_WINDOW_MS = 30 * 60 * 1000;
|
|
6866
|
+
UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
6779
6867
|
});
|
|
6780
6868
|
|
|
6781
6869
|
// src/lib/poll.ts
|
|
@@ -12205,6 +12293,35 @@ var init_pg_migrations = __esm(() => {
|
|
|
12205
12293
|
EXECUTE FUNCTION snapshot_memory_version();
|
|
12206
12294
|
|
|
12207
12295
|
INSERT INTO _migrations (id) VALUES (36) ON CONFLICT DO NOTHING;
|
|
12296
|
+
`,
|
|
12297
|
+
`
|
|
12298
|
+
CREATE OR REPLACE FUNCTION audit_memory_insert() RETURNS trigger AS $$
|
|
12299
|
+
BEGIN
|
|
12300
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, new_value_hash, created_at)
|
|
12301
|
+
VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'create', NEW.agent_id, md5(COALESCE(NEW.value, '')), NOW());
|
|
12302
|
+
RETURN NEW;
|
|
12303
|
+
END;
|
|
12304
|
+
$$ LANGUAGE plpgsql;
|
|
12305
|
+
|
|
12306
|
+
CREATE OR REPLACE FUNCTION audit_memory_update() RETURNS trigger AS $$
|
|
12307
|
+
BEGIN
|
|
12308
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, new_value_hash, changes, created_at)
|
|
12309
|
+
VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'update', NEW.agent_id, md5(COALESCE(OLD.value, '')), md5(COALESCE(NEW.value, '')),
|
|
12310
|
+
json_build_object('version_from', OLD.version, 'version_to', NEW.version, 'importance_from', OLD.importance, 'importance_to', NEW.importance)::text,
|
|
12311
|
+
NOW());
|
|
12312
|
+
RETURN NEW;
|
|
12313
|
+
END;
|
|
12314
|
+
$$ LANGUAGE plpgsql;
|
|
12315
|
+
|
|
12316
|
+
CREATE OR REPLACE FUNCTION audit_memory_delete() RETURNS trigger AS $$
|
|
12317
|
+
BEGIN
|
|
12318
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, created_at)
|
|
12319
|
+
VALUES (gen_random_uuid()::text, OLD.id, OLD.key, 'delete', OLD.agent_id, md5(COALESCE(OLD.value, '')), NOW());
|
|
12320
|
+
RETURN OLD;
|
|
12321
|
+
END;
|
|
12322
|
+
$$ LANGUAGE plpgsql;
|
|
12323
|
+
|
|
12324
|
+
INSERT INTO _migrations (id) VALUES (37) ON CONFLICT DO NOTHING;
|
|
12208
12325
|
`
|
|
12209
12326
|
];
|
|
12210
12327
|
});
|
|
@@ -59025,6 +59142,26 @@ function applyGlobalOptions(program2) {
|
|
|
59025
59142
|
return program2;
|
|
59026
59143
|
}
|
|
59027
59144
|
|
|
59145
|
+
// src/cli/exit-codes.ts
|
|
59146
|
+
var EXIT_USAGE = 64;
|
|
59147
|
+
var USAGE_ERROR_CODES = new Set([
|
|
59148
|
+
"commander.unknownCommand",
|
|
59149
|
+
"commander.unknownOption",
|
|
59150
|
+
"commander.missingArgument",
|
|
59151
|
+
"commander.optionMissingArgument",
|
|
59152
|
+
"commander.missingMandatoryOptionValue",
|
|
59153
|
+
"commander.excessArguments",
|
|
59154
|
+
"commander.conflictingOption",
|
|
59155
|
+
"commander.invalidArgument",
|
|
59156
|
+
"commander.help"
|
|
59157
|
+
]);
|
|
59158
|
+
function resolveExitCode(err) {
|
|
59159
|
+
const exitCode = typeof err.exitCode === "number" ? err.exitCode : 1;
|
|
59160
|
+
if (exitCode === 0)
|
|
59161
|
+
return 0;
|
|
59162
|
+
return USAGE_ERROR_CODES.has(err.code ?? "") ? EXIT_USAGE : exitCode;
|
|
59163
|
+
}
|
|
59164
|
+
|
|
59028
59165
|
// node_modules/@hasna/events/dist/commander.js
|
|
59029
59166
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
59030
59167
|
import { existsSync as existsSync3 } from "fs";
|
|
@@ -60452,7 +60589,7 @@ function registerChainCommand(program2) {
|
|
|
60452
60589
|
const m = memories[i];
|
|
60453
60590
|
const order = m.sequence_order !== null && m.sequence_order !== undefined ? m.sequence_order : i + 1;
|
|
60454
60591
|
const value = m.value.length > 120 ? m.value.slice(0, 120) + "..." : m.value;
|
|
60455
|
-
console.log(` ${chalk8.cyan(String(order) + ".")} ${chalk8.bold(m.key)}
|
|
60592
|
+
console.log(` ${chalk8.cyan(String(order) + ".")} ${chalk8.bold(`${m.key}:`)} ${value}`);
|
|
60456
60593
|
}
|
|
60457
60594
|
} catch (e) {
|
|
60458
60595
|
handleError(e);
|
|
@@ -62013,14 +62150,17 @@ function registerAgentCommands(program2) {
|
|
|
62013
62150
|
program2.command("agents").description("List all registered agents").option("--limit <n>", "Max results (compact default: 20)", parseInt).option("--cursor <n>", "Cursor offset for the next page", parseInt).option("--offset <n>", "Offset for pagination", parseInt).action((opts) => {
|
|
62014
62151
|
try {
|
|
62015
62152
|
const globalOpts = program2.opts();
|
|
62016
|
-
const allAgents = listAgents();
|
|
62017
62153
|
const limit = positiveIntOrDefault(opts.limit, DEFAULT_COMPACT_LIMIT);
|
|
62018
62154
|
const offset = cursorOrOffset(opts.cursor, opts.offset) ?? 0;
|
|
62019
|
-
const
|
|
62155
|
+
const explicitPagination = opts.limit !== undefined || opts.cursor !== undefined || opts.offset !== undefined;
|
|
62156
|
+
const agents = listAgents({
|
|
62157
|
+
limit: globalOpts.json ? explicitPagination ? limit : undefined : limit + 1,
|
|
62158
|
+
offset
|
|
62159
|
+
});
|
|
62020
62160
|
const hasMore = !globalOpts.json && agents.length > limit;
|
|
62021
62161
|
const displayAgents = hasMore ? agents.slice(0, limit) : agents;
|
|
62022
62162
|
if (globalOpts.json) {
|
|
62023
|
-
outputJson(
|
|
62163
|
+
outputJson(agents);
|
|
62024
62164
|
return;
|
|
62025
62165
|
}
|
|
62026
62166
|
if (displayAgents.length === 0) {
|
|
@@ -62992,12 +63132,12 @@ function registerGraphCommands(program2) {
|
|
|
62992
63132
|
console.log();
|
|
62993
63133
|
console.log(chalk26.bold(`Entities: ${stats.entities.total}`));
|
|
62994
63134
|
for (const r of entityRows) {
|
|
62995
|
-
console.log(` ${colorEntityType(r.type)}
|
|
63135
|
+
console.log(` ${colorEntityType(r.type, ":")} ${r.count}`);
|
|
62996
63136
|
}
|
|
62997
63137
|
console.log();
|
|
62998
63138
|
console.log(chalk26.bold(`Relations: ${stats.relations.total}`));
|
|
62999
63139
|
for (const r of relationRows) {
|
|
63000
|
-
console.log(` ${chalk26.cyan(r.relation_type)}
|
|
63140
|
+
console.log(` ${chalk26.cyan(`${r.relation_type}:`)} ${r.count}`);
|
|
63001
63141
|
}
|
|
63002
63142
|
console.log();
|
|
63003
63143
|
console.log(chalk26.bold(`Memory links: ${stats.memory_links}`));
|
|
@@ -64038,7 +64178,7 @@ function registerToolsCommand(program2) {
|
|
|
64038
64178
|
console.log(chalk34.bold(`
|
|
64039
64179
|
Common errors:`));
|
|
64040
64180
|
for (const err of stats.common_errors) {
|
|
64041
|
-
console.log(` ${chalk34.red(err.error_type)}
|
|
64181
|
+
console.log(` ${chalk34.red(`${err.error_type}:`)} ${err.count} times`);
|
|
64042
64182
|
}
|
|
64043
64183
|
}
|
|
64044
64184
|
if (lessons.length === 0) {
|
|
@@ -65045,10 +65185,13 @@ function resolveStoreBackend() {
|
|
|
65045
65185
|
const apiConfig = getApiConfig();
|
|
65046
65186
|
const storageMode = getStorageMode();
|
|
65047
65187
|
const sources = getApiModeEnvSources();
|
|
65188
|
+
const configured = getConfiguredApiEnv();
|
|
65048
65189
|
const backend = apiMode ? "cloud-api" : storageMode === "cloud" ? "cloud-postgres" : "local-sqlite";
|
|
65049
65190
|
let selectedBy = "default";
|
|
65050
65191
|
if (apiMode) {
|
|
65051
65192
|
selectedBy = `${sources.urlKey} + ${sources.keyKey} (presence)`;
|
|
65193
|
+
} else if (backend === "local-sqlite" && configured.dbPathKey) {
|
|
65194
|
+
selectedBy = configured.apiKeyPresent && configured.baseUrl ? `${configured.dbPathKey} (explicit local path, outranks the API selectors)` : `${configured.dbPathKey} (explicit local path)`;
|
|
65052
65195
|
} else if (backend === "cloud-postgres") {
|
|
65053
65196
|
const modeKey = [MEMENTOS_STORAGE_ENV.mode, MEMENTOS_STORAGE_FALLBACK_ENV.mode].find((key) => process.env[key]?.trim());
|
|
65054
65197
|
selectedBy = modeKey ?? sources.databaseUrlKey ?? "storage config file";
|
|
@@ -65059,8 +65202,8 @@ function resolveStoreBackend() {
|
|
|
65059
65202
|
api_mode: apiMode,
|
|
65060
65203
|
storage_mode: storageMode,
|
|
65061
65204
|
db_path: getDbPath(),
|
|
65062
|
-
api_endpoint: apiConfig?.baseUrl ??
|
|
65063
|
-
api_key_present: Boolean(apiConfig?.apiKey),
|
|
65205
|
+
api_endpoint: apiConfig?.baseUrl ?? configured.baseUrl,
|
|
65206
|
+
api_key_present: Boolean(apiConfig?.apiKey) || configured.apiKeyPresent,
|
|
65064
65207
|
selected_by: selectedBy
|
|
65065
65208
|
};
|
|
65066
65209
|
}
|
|
@@ -65114,6 +65257,9 @@ function installStorageSubcommands(storage, program2) {
|
|
|
65114
65257
|
console.log(`Storage mode: ${report.storage_mode}`);
|
|
65115
65258
|
if (report.backend === "local-sqlite") {
|
|
65116
65259
|
console.log(`Database: ${report.db_path}`);
|
|
65260
|
+
if (report.api_key_present && report.api_endpoint) {
|
|
65261
|
+
console.log(`API endpoint (configured, OUTRANKED): ${report.api_endpoint}`);
|
|
65262
|
+
}
|
|
65117
65263
|
} else {
|
|
65118
65264
|
console.log(`API endpoint: ${report.api_endpoint ?? "(none)"}`);
|
|
65119
65265
|
console.log(`API key: ${report.api_key_present ? "configured" : "not configured"}`);
|
|
@@ -66962,6 +67108,14 @@ function getPackageVersion2() {
|
|
|
66962
67108
|
var program2 = new Command;
|
|
66963
67109
|
program2.name("mementos").description("Universal memory system for AI agents").version(getPackageVersion2());
|
|
66964
67110
|
applyGlobalOptions(program2);
|
|
67111
|
+
function applyExitCodeContract(command) {
|
|
67112
|
+
command.exitOverride((err) => {
|
|
67113
|
+
process.exit(resolveExitCode(err));
|
|
67114
|
+
});
|
|
67115
|
+
for (const subcommand of command.commands) {
|
|
67116
|
+
applyExitCodeContract(subcommand);
|
|
67117
|
+
}
|
|
67118
|
+
}
|
|
66965
67119
|
var startupWarningShown = false;
|
|
66966
67120
|
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
66967
67121
|
if (skipsStartupDbAccess(actionCommand))
|
|
@@ -66977,4 +67131,5 @@ program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
|
66977
67131
|
} catch {}
|
|
66978
67132
|
});
|
|
66979
67133
|
registerAllCommands(program2);
|
|
67134
|
+
applyExitCodeContract(program2);
|
|
66980
67135
|
program2.parse(process.argv);
|
package/dist/db/agents.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { SqliteAdapter as Database } from "../storage.js";
|
|
2
2
|
import type { Agent } from "../types/index.js";
|
|
3
|
+
export interface AgentListFilter {
|
|
4
|
+
limit?: number;
|
|
5
|
+
offset?: number;
|
|
6
|
+
}
|
|
3
7
|
export declare function registerAgent(name: string, sessionId?: string, description?: string, role?: string, projectId?: string, db?: Database): Agent;
|
|
4
8
|
export declare function getAgent(idOrName: string, db?: Database): Agent | null;
|
|
5
9
|
export declare function listAgents(db?: Database): Agent[];
|
|
10
|
+
export declare function listAgents(filter: AgentListFilter, db?: Database): Agent[];
|
|
6
11
|
export declare function touchAgent(idOrName: string, db?: Database): void;
|
|
7
12
|
export declare function listAgentsByProject(projectId: string, db?: Database): Agent[];
|
|
13
|
+
export declare function listAgentsByProject(projectId: string, filter: AgentListFilter, db?: Database): Agent[];
|
|
8
14
|
export declare function updateAgent(id: string, updates: {
|
|
9
15
|
name?: string;
|
|
10
16
|
description?: string;
|
package/dist/db/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAQ/C,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CA0EP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CA6Bd;AA+CD,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AACnD,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AAsB5E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAahE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AAC/E,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EACvB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,EAAE,CAAC;AA0BX,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACtI,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsDd"}
|
package/dist/db/api-mode.d.ts
CHANGED
|
@@ -15,6 +15,23 @@ export declare function getApiModeEnvSources(): {
|
|
|
15
15
|
keyKey: string | null;
|
|
16
16
|
databaseUrlKey: string | null;
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* What the environment CONFIGURES, regardless of which store finally wins.
|
|
20
|
+
*
|
|
21
|
+
* Distinct from {@link getApiConfig}, which answers "may I build a cloud
|
|
22
|
+
* request?" and returns null once an explicit DB_PATH outranks the credentials
|
|
23
|
+
* (precedence 1). An operator report needs both halves separately: conflating
|
|
24
|
+
* them prints "no API key configured" at someone whose key is configured and
|
|
25
|
+
* merely outranked, which points them at a credential problem they do not have.
|
|
26
|
+
*
|
|
27
|
+
* Never returns a credential value — the endpoint is not secret, the key is
|
|
28
|
+
* reported only as a boolean.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getConfiguredApiEnv(): {
|
|
31
|
+
baseUrl: string | null;
|
|
32
|
+
apiKeyPresent: boolean;
|
|
33
|
+
dbPathKey: string | null;
|
|
34
|
+
};
|
|
18
35
|
/**
|
|
19
36
|
* Escape hatch for a test that genuinely must reach a non-loopback endpoint.
|
|
20
37
|
* Opt-in by design: the default has to be safe, because the failure it prevents
|
|
@@ -38,13 +55,26 @@ export declare class MementosStoreConfigError extends Error {
|
|
|
38
55
|
* dataset with no error and no flag. From the caller's side that is
|
|
39
56
|
* indistinguishable from a store that is working.
|
|
40
57
|
*
|
|
58
|
+
* THIS FUNCTION ONLY ASSERTS. It returns `void`, so it cannot select anything —
|
|
59
|
+
* the precedence table below describes the RESOLVER, which is `getApiConfig()`
|
|
60
|
+
* immediately after it, and each early `return` here only suppresses the
|
|
61
|
+
* ambiguity error for a case that is already unambiguous. Steps 1 and 2 are
|
|
62
|
+
* therefore implemented in `getApiConfig()` and `isApiMode()` respectively, and
|
|
63
|
+
* the table is documented here because this is where the error text lives.
|
|
64
|
+
*
|
|
65
|
+
* Keeping the table honest about that is not cosmetic. Until 2026-08-03 step 1
|
|
66
|
+
* was implemented NOWHERE: the `return` below reads as a selection, and an
|
|
67
|
+
* operator who read it concluded that a scratch DB_PATH made them safe while a
|
|
68
|
+
* fully-configured environment still routed every read and write to the shared
|
|
69
|
+
* production store.
|
|
70
|
+
*
|
|
41
71
|
* Precedence, highest first:
|
|
42
72
|
* 1. An explicit SQLite path (`HASNA_MEMENTOS_DB_PATH` / `MEMENTOS_DB_PATH`) is
|
|
43
73
|
* the narrowest, most specific signal and selects LOCAL, so local dev,
|
|
44
74
|
* tooling and import/export keep working when a stray credential is exported
|
|
45
|
-
* globally.
|
|
46
|
-
* 2. A client DSN present disables API mode.
|
|
47
|
-
*
|
|
75
|
+
* globally. ENFORCED in `getApiConfig()`, which returns null for it.
|
|
76
|
+
* 2. A client DSN present disables API mode. ENFORCED in `isApiMode()`. A DSN
|
|
77
|
+
* on a client is forbidden and is tracked separately in database.ts.
|
|
48
78
|
* 3. Both API URL and API key present -> API mode.
|
|
49
79
|
* 4. Exactly ONE of them present -> ERROR naming the missing variable.
|
|
50
80
|
* 5. Neither present -> LOCAL. The documented single-operator default.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"AA0CA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,qBAAqB,mEAAoE,CAAC;AAsBvG;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAMtH;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uCAAuC,CAAC;AAyDlF,6EAA6E;AAC7E,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,2BAA2B;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED
|
|
1
|
+
{"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"AA0CA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,qBAAqB,mEAAoE,CAAC;AAsBvG;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAMtH;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,IAAI;IACrC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAOA;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uCAAuC,CAAC;AAyDlF,6EAA6E;AAC7E,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,2BAA2B;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CA0BhD;AAED,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAyB/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AA0GD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAiB7B;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAe/D"}
|
package/dist/db/audit.d.ts
CHANGED
|
@@ -9,7 +9,21 @@ export interface AuditEntry {
|
|
|
9
9
|
memory_key: string | null;
|
|
10
10
|
operation: "create" | "update" | "delete" | "archive" | "restore" | "read";
|
|
11
11
|
agent_id: string | null;
|
|
12
|
+
/**
|
|
13
|
+
* md5 hex digest of the value the row held BEFORE the operation, or `null`.
|
|
14
|
+
*
|
|
15
|
+
* `null` means the digest is UNAVAILABLE, never that the value was empty. It is
|
|
16
|
+
* the honest answer on SQLite, which has no `md5()` and whose driver
|
|
17
|
+
* (`bun:sqlite`) exposes no user-defined-function API, so a trigger cannot
|
|
18
|
+
* compute one. Postgres computes `md5(COALESCE(...))` natively and populates it.
|
|
19
|
+
*
|
|
20
|
+
* So: compare `md5(candidate)` against a NON-NULL value to prove what a row
|
|
21
|
+
* held. A `null` is a statement about the BACKEND and must never be read as
|
|
22
|
+
* evidence about the content — treating it as a failed match is the confident
|
|
23
|
+
* wrong answer migration 37 exists to prevent.
|
|
24
|
+
*/
|
|
12
25
|
old_value_hash: string | null;
|
|
26
|
+
/** md5 hex digest of the value AFTER the operation, or `null`. See `old_value_hash`. */
|
|
13
27
|
new_value_hash: string | null;
|
|
14
28
|
changes: Record<string, unknown>;
|
|
15
29
|
created_at: string;
|
package/dist/db/audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/db/audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG1D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAW,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CAMd;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,EACN,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CA6Bd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB,CAkBA"}
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/db/audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG1D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wFAAwF;IACxF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAW,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CAMd;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,EACN,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CA6Bd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB,CAkBA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,IAAI,QAAQ,EAK1B,MAAM,eAAe,CAAC;AAkDvB,wBAAgB,SAAS,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,IAAI,QAAQ,EAK1B,MAAM,eAAe,CAAC;AAkDvB,wBAAgB,SAAS,IAAI,MAAM,CAwBlC;AAoDD,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAoGrD;AAgDD,wBAAgB,aAAa,IAAI,IAAI,CASpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAuBf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,+BAA+B,wqBAiB3C,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,+BAA+B,wqBAiB3C,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,MAAM,EAkhC9B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EAy0BjC,CAAC"}
|
|
@@ -16,9 +16,18 @@ export interface StoreBackendReport {
|
|
|
16
16
|
storage_mode: string;
|
|
17
17
|
/** The SQLite path that WOULD be used. Meaningful only for `local-sqlite`. */
|
|
18
18
|
db_path: string;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Cloud endpoint origin + prefix as CONFIGURED, not necessarily as used: it
|
|
21
|
+
* is still reported when an explicit DB_PATH has outranked it, because the
|
|
22
|
+
* operator needs to see what was set as well as what won. Read `backend` /
|
|
23
|
+
* `api_mode` for what is actually in force, and `selected_by` for why.
|
|
24
|
+
* Not a secret; the key is never included.
|
|
25
|
+
*/
|
|
20
26
|
api_endpoint: string | null;
|
|
21
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Whether an API key is CONFIGURED in the environment — again independent of
|
|
29
|
+
* whether it won. The value is never read or reported.
|
|
30
|
+
*/
|
|
22
31
|
api_key_present: boolean;
|
|
23
32
|
/** Env key NAMES that produced this backend, or `"default"`. Names only. */
|
|
24
33
|
selected_by: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-backend.d.ts","sourceRoot":"","sources":["../../src/db/store-backend.ts"],"names":[],"mappings":"AA4BA;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB
|
|
1
|
+
{"version":3,"file":"store-backend.d.ts","sourceRoot":"","sources":["../../src/db/store-backend.ts"],"names":[],"mappings":"AA4BA;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,CAoDxD"}
|
package/dist/index.js
CHANGED
|
@@ -1031,6 +1031,8 @@ function assertUnambiguousStoreEnv() {
|
|
|
1031
1031
|
}
|
|
1032
1032
|
function getApiConfig() {
|
|
1033
1033
|
assertUnambiguousStoreEnv();
|
|
1034
|
+
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
1035
|
+
return null;
|
|
1034
1036
|
const rawBase = firstEnv(API_URL_ENV_KEYS);
|
|
1035
1037
|
const apiKey = firstEnv(API_KEY_ENV_KEYS);
|
|
1036
1038
|
if (!rawBase || !apiKey)
|
|
@@ -2096,6 +2098,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
|
|
|
2096
2098
|
`
|
|
2097
2099
|
${MEMORY_VERSION_SNAPSHOT_TRIGGER}
|
|
2098
2100
|
INSERT OR IGNORE INTO _migrations (id) VALUES (36);
|
|
2101
|
+
`,
|
|
2102
|
+
`
|
|
2103
|
+
DROP TRIGGER IF EXISTS audit_memory_insert;
|
|
2104
|
+
CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
|
|
2105
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
2106
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
|
|
2107
|
+
END;
|
|
2108
|
+
|
|
2109
|
+
DROP TRIGGER IF EXISTS audit_memory_update;
|
|
2110
|
+
CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
|
|
2111
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
|
|
2112
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
|
|
2113
|
+
json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
|
|
2114
|
+
datetime('now'));
|
|
2115
|
+
END;
|
|
2116
|
+
|
|
2117
|
+
DROP TRIGGER IF EXISTS audit_memory_delete;
|
|
2118
|
+
CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
|
|
2119
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
2120
|
+
VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
|
|
2121
|
+
END;
|
|
2122
|
+
|
|
2123
|
+
UPDATE memory_audit_log SET old_value_hash = NULL
|
|
2124
|
+
WHERE old_value_hash IS NOT NULL
|
|
2125
|
+
AND length(old_value_hash) = 32
|
|
2126
|
+
AND old_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
2127
|
+
|
|
2128
|
+
UPDATE memory_audit_log SET new_value_hash = NULL
|
|
2129
|
+
WHERE new_value_hash IS NOT NULL
|
|
2130
|
+
AND length(new_value_hash) = 32
|
|
2131
|
+
AND new_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
2132
|
+
|
|
2133
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (37);
|
|
2099
2134
|
`
|
|
2100
2135
|
];
|
|
2101
2136
|
});
|
|
@@ -2155,9 +2190,10 @@ function migrateGlobalDir() {
|
|
|
2155
2190
|
}
|
|
2156
2191
|
}
|
|
2157
2192
|
function getDbPath() {
|
|
2158
|
-
const
|
|
2159
|
-
|
|
2160
|
-
|
|
2193
|
+
for (const key of DB_PATH_ENV_KEYS) {
|
|
2194
|
+
const envPath = process.env[key]?.trim();
|
|
2195
|
+
if (envPath)
|
|
2196
|
+
return envPath;
|
|
2161
2197
|
}
|
|
2162
2198
|
const cwd = process.cwd();
|
|
2163
2199
|
const nearest = findNearestMementosDb(cwd);
|
|
@@ -2201,6 +2237,12 @@ function getDatabase(dbPath) {
|
|
|
2201
2237
|
if (getStorageMode() === "cloud") {
|
|
2202
2238
|
return getCloudDatabase();
|
|
2203
2239
|
}
|
|
2240
|
+
if (process.env["NODE_ENV"] === "test") {
|
|
2241
|
+
const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
|
|
2242
|
+
if (!hasExplicitDbPath) {
|
|
2243
|
+
throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2204
2246
|
}
|
|
2205
2247
|
const path = dbPath || getDbPath();
|
|
2206
2248
|
if (_db) {
|
|
@@ -49986,6 +50028,7 @@ function getMemoryVersions(memoryId, db) {
|
|
|
49986
50028
|
init_database();
|
|
49987
50029
|
init_api_mode();
|
|
49988
50030
|
var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
|
|
50031
|
+
var UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
49989
50032
|
function parseAgentRow(row) {
|
|
49990
50033
|
return {
|
|
49991
50034
|
id: row["id"],
|
|
@@ -50077,13 +50120,46 @@ function getAgent(idOrName, db) {
|
|
|
50077
50120
|
return parseAgentRow(rows[0]);
|
|
50078
50121
|
return null;
|
|
50079
50122
|
}
|
|
50080
|
-
function
|
|
50081
|
-
|
|
50082
|
-
|
|
50123
|
+
function normalizedAgentListFilter(filter) {
|
|
50124
|
+
const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
|
|
50125
|
+
const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
|
|
50126
|
+
return { limit, offset };
|
|
50127
|
+
}
|
|
50128
|
+
function isDatabaseAdapter(value) {
|
|
50129
|
+
return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
|
|
50130
|
+
}
|
|
50131
|
+
function paginatedAgentQuery(baseSql, baseParams, filter) {
|
|
50132
|
+
const { limit, offset } = normalizedAgentListFilter(filter);
|
|
50133
|
+
let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
|
|
50134
|
+
const params = [...baseParams];
|
|
50135
|
+
if (limit !== undefined) {
|
|
50136
|
+
sql += " LIMIT ?";
|
|
50137
|
+
params.push(limit);
|
|
50138
|
+
} else if ((offset ?? 0) > 0) {
|
|
50139
|
+
sql += " LIMIT ?";
|
|
50140
|
+
params.push(UNBOUNDED_AGENT_LIST_LIMIT);
|
|
50141
|
+
}
|
|
50142
|
+
if ((offset ?? 0) > 0) {
|
|
50143
|
+
sql += " OFFSET ?";
|
|
50144
|
+
params.push(offset);
|
|
50145
|
+
}
|
|
50146
|
+
return { sql, params };
|
|
50147
|
+
}
|
|
50148
|
+
function listAgents(filterOrDb = {}, db) {
|
|
50149
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
50150
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
50151
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
50152
|
+
if (!explicitDb && isApiMode()) {
|
|
50153
|
+
const q = toQuery({
|
|
50154
|
+
limit: normalized.limit,
|
|
50155
|
+
offset: normalized.offset
|
|
50156
|
+
});
|
|
50157
|
+
const { data } = apiJson("GET", `/agents${q}`);
|
|
50083
50158
|
return data?.agents ?? [];
|
|
50084
50159
|
}
|
|
50085
|
-
const d =
|
|
50086
|
-
const
|
|
50160
|
+
const d = explicitDb || getDatabase();
|
|
50161
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
|
|
50162
|
+
const rows = d.query(sql).all(...params);
|
|
50087
50163
|
return rows.map(parseAgentRow);
|
|
50088
50164
|
}
|
|
50089
50165
|
function touchAgent(idOrName, db) {
|
|
@@ -50100,15 +50176,19 @@ function touchAgent(idOrName, db) {
|
|
|
50100
50176
|
return;
|
|
50101
50177
|
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
|
|
50102
50178
|
}
|
|
50103
|
-
function listAgentsByProject(projectId, db) {
|
|
50104
|
-
|
|
50105
|
-
|
|
50179
|
+
function listAgentsByProject(projectId, filterOrDb = {}, db) {
|
|
50180
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
50181
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
50182
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
50183
|
+
if (!explicitDb && isApiMode()) {
|
|
50184
|
+
const q = toQuery({ project_id: projectId, ...normalized });
|
|
50106
50185
|
const { data } = apiJson("GET", `/agents${q}`);
|
|
50107
50186
|
return data?.agents ?? [];
|
|
50108
50187
|
}
|
|
50109
|
-
const d =
|
|
50188
|
+
const d = explicitDb || getDatabase();
|
|
50110
50189
|
const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
|
|
50111
|
-
const
|
|
50190
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
|
|
50191
|
+
const rows = d.query(sql).all(...params);
|
|
50112
50192
|
return rows.map(parseAgentRow);
|
|
50113
50193
|
}
|
|
50114
50194
|
function updateAgent(id, updates, db) {
|
package/dist/mcp/index.js
CHANGED
|
@@ -1094,6 +1094,8 @@ function assertUnambiguousStoreEnv() {
|
|
|
1094
1094
|
}
|
|
1095
1095
|
function getApiConfig() {
|
|
1096
1096
|
assertUnambiguousStoreEnv();
|
|
1097
|
+
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
1098
|
+
return null;
|
|
1097
1099
|
const rawBase = firstEnv(API_URL_ENV_KEYS);
|
|
1098
1100
|
const apiKey = firstEnv(API_KEY_ENV_KEYS);
|
|
1099
1101
|
if (!rawBase || !apiKey)
|
|
@@ -2159,6 +2161,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
|
|
|
2159
2161
|
`
|
|
2160
2162
|
${MEMORY_VERSION_SNAPSHOT_TRIGGER}
|
|
2161
2163
|
INSERT OR IGNORE INTO _migrations (id) VALUES (36);
|
|
2164
|
+
`,
|
|
2165
|
+
`
|
|
2166
|
+
DROP TRIGGER IF EXISTS audit_memory_insert;
|
|
2167
|
+
CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
|
|
2168
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
2169
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
|
|
2170
|
+
END;
|
|
2171
|
+
|
|
2172
|
+
DROP TRIGGER IF EXISTS audit_memory_update;
|
|
2173
|
+
CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
|
|
2174
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
|
|
2175
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
|
|
2176
|
+
json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
|
|
2177
|
+
datetime('now'));
|
|
2178
|
+
END;
|
|
2179
|
+
|
|
2180
|
+
DROP TRIGGER IF EXISTS audit_memory_delete;
|
|
2181
|
+
CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
|
|
2182
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
2183
|
+
VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
|
|
2184
|
+
END;
|
|
2185
|
+
|
|
2186
|
+
UPDATE memory_audit_log SET old_value_hash = NULL
|
|
2187
|
+
WHERE old_value_hash IS NOT NULL
|
|
2188
|
+
AND length(old_value_hash) = 32
|
|
2189
|
+
AND old_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
2190
|
+
|
|
2191
|
+
UPDATE memory_audit_log SET new_value_hash = NULL
|
|
2192
|
+
WHERE new_value_hash IS NOT NULL
|
|
2193
|
+
AND length(new_value_hash) = 32
|
|
2194
|
+
AND new_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
2195
|
+
|
|
2196
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (37);
|
|
2162
2197
|
`
|
|
2163
2198
|
];
|
|
2164
2199
|
});
|
|
@@ -2218,9 +2253,10 @@ function migrateGlobalDir() {
|
|
|
2218
2253
|
}
|
|
2219
2254
|
}
|
|
2220
2255
|
function getDbPath() {
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2223
|
-
|
|
2256
|
+
for (const key of DB_PATH_ENV_KEYS) {
|
|
2257
|
+
const envPath = process.env[key]?.trim();
|
|
2258
|
+
if (envPath)
|
|
2259
|
+
return envPath;
|
|
2224
2260
|
}
|
|
2225
2261
|
const cwd = process.cwd();
|
|
2226
2262
|
const nearest = findNearestMementosDb(cwd);
|
|
@@ -2264,6 +2300,12 @@ function getDatabase(dbPath) {
|
|
|
2264
2300
|
if (getStorageMode() === "cloud") {
|
|
2265
2301
|
return getCloudDatabase();
|
|
2266
2302
|
}
|
|
2303
|
+
if (process.env["NODE_ENV"] === "test") {
|
|
2304
|
+
const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
|
|
2305
|
+
if (!hasExplicitDbPath) {
|
|
2306
|
+
throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2267
2309
|
}
|
|
2268
2310
|
const path = dbPath || getDbPath();
|
|
2269
2311
|
if (_db) {
|
|
@@ -13007,6 +13049,35 @@ var init_pg_migrations = __esm(() => {
|
|
|
13007
13049
|
EXECUTE FUNCTION snapshot_memory_version();
|
|
13008
13050
|
|
|
13009
13051
|
INSERT INTO _migrations (id) VALUES (36) ON CONFLICT DO NOTHING;
|
|
13052
|
+
`,
|
|
13053
|
+
`
|
|
13054
|
+
CREATE OR REPLACE FUNCTION audit_memory_insert() RETURNS trigger AS $$
|
|
13055
|
+
BEGIN
|
|
13056
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, new_value_hash, created_at)
|
|
13057
|
+
VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'create', NEW.agent_id, md5(COALESCE(NEW.value, '')), NOW());
|
|
13058
|
+
RETURN NEW;
|
|
13059
|
+
END;
|
|
13060
|
+
$$ LANGUAGE plpgsql;
|
|
13061
|
+
|
|
13062
|
+
CREATE OR REPLACE FUNCTION audit_memory_update() RETURNS trigger AS $$
|
|
13063
|
+
BEGIN
|
|
13064
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, new_value_hash, changes, created_at)
|
|
13065
|
+
VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'update', NEW.agent_id, md5(COALESCE(OLD.value, '')), md5(COALESCE(NEW.value, '')),
|
|
13066
|
+
json_build_object('version_from', OLD.version, 'version_to', NEW.version, 'importance_from', OLD.importance, 'importance_to', NEW.importance)::text,
|
|
13067
|
+
NOW());
|
|
13068
|
+
RETURN NEW;
|
|
13069
|
+
END;
|
|
13070
|
+
$$ LANGUAGE plpgsql;
|
|
13071
|
+
|
|
13072
|
+
CREATE OR REPLACE FUNCTION audit_memory_delete() RETURNS trigger AS $$
|
|
13073
|
+
BEGIN
|
|
13074
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, created_at)
|
|
13075
|
+
VALUES (gen_random_uuid()::text, OLD.id, OLD.key, 'delete', OLD.agent_id, md5(COALESCE(OLD.value, '')), NOW());
|
|
13076
|
+
RETURN OLD;
|
|
13077
|
+
END;
|
|
13078
|
+
$$ LANGUAGE plpgsql;
|
|
13079
|
+
|
|
13080
|
+
INSERT INTO _migrations (id) VALUES (37) ON CONFLICT DO NOTHING;
|
|
13010
13081
|
`
|
|
13011
13082
|
];
|
|
13012
13083
|
});
|
|
@@ -55734,6 +55805,7 @@ init_types();
|
|
|
55734
55805
|
init_database();
|
|
55735
55806
|
init_api_mode();
|
|
55736
55807
|
var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
|
|
55808
|
+
var UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
55737
55809
|
function parseAgentRow(row) {
|
|
55738
55810
|
return {
|
|
55739
55811
|
id: row["id"],
|
|
@@ -55825,13 +55897,46 @@ function getAgent(idOrName, db) {
|
|
|
55825
55897
|
return parseAgentRow(rows[0]);
|
|
55826
55898
|
return null;
|
|
55827
55899
|
}
|
|
55828
|
-
function
|
|
55829
|
-
|
|
55830
|
-
|
|
55900
|
+
function normalizedAgentListFilter(filter) {
|
|
55901
|
+
const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
|
|
55902
|
+
const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
|
|
55903
|
+
return { limit, offset };
|
|
55904
|
+
}
|
|
55905
|
+
function isDatabaseAdapter(value) {
|
|
55906
|
+
return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
|
|
55907
|
+
}
|
|
55908
|
+
function paginatedAgentQuery(baseSql, baseParams, filter) {
|
|
55909
|
+
const { limit, offset } = normalizedAgentListFilter(filter);
|
|
55910
|
+
let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
|
|
55911
|
+
const params = [...baseParams];
|
|
55912
|
+
if (limit !== undefined) {
|
|
55913
|
+
sql += " LIMIT ?";
|
|
55914
|
+
params.push(limit);
|
|
55915
|
+
} else if ((offset ?? 0) > 0) {
|
|
55916
|
+
sql += " LIMIT ?";
|
|
55917
|
+
params.push(UNBOUNDED_AGENT_LIST_LIMIT);
|
|
55918
|
+
}
|
|
55919
|
+
if ((offset ?? 0) > 0) {
|
|
55920
|
+
sql += " OFFSET ?";
|
|
55921
|
+
params.push(offset);
|
|
55922
|
+
}
|
|
55923
|
+
return { sql, params };
|
|
55924
|
+
}
|
|
55925
|
+
function listAgents(filterOrDb = {}, db) {
|
|
55926
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
55927
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
55928
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
55929
|
+
if (!explicitDb && isApiMode()) {
|
|
55930
|
+
const q = toQuery({
|
|
55931
|
+
limit: normalized.limit,
|
|
55932
|
+
offset: normalized.offset
|
|
55933
|
+
});
|
|
55934
|
+
const { data } = apiJson("GET", `/agents${q}`);
|
|
55831
55935
|
return data?.agents ?? [];
|
|
55832
55936
|
}
|
|
55833
|
-
const d =
|
|
55834
|
-
const
|
|
55937
|
+
const d = explicitDb || getDatabase();
|
|
55938
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
|
|
55939
|
+
const rows = d.query(sql).all(...params);
|
|
55835
55940
|
return rows.map(parseAgentRow);
|
|
55836
55941
|
}
|
|
55837
55942
|
function touchAgent(idOrName, db) {
|
|
@@ -55848,15 +55953,19 @@ function touchAgent(idOrName, db) {
|
|
|
55848
55953
|
return;
|
|
55849
55954
|
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
|
|
55850
55955
|
}
|
|
55851
|
-
function listAgentsByProject(projectId, db) {
|
|
55852
|
-
|
|
55853
|
-
|
|
55956
|
+
function listAgentsByProject(projectId, filterOrDb = {}, db) {
|
|
55957
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
55958
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
55959
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
55960
|
+
if (!explicitDb && isApiMode()) {
|
|
55961
|
+
const q = toQuery({ project_id: projectId, ...normalized });
|
|
55854
55962
|
const { data } = apiJson("GET", `/agents${q}`);
|
|
55855
55963
|
return data?.agents ?? [];
|
|
55856
55964
|
}
|
|
55857
|
-
const d =
|
|
55965
|
+
const d = explicitDb || getDatabase();
|
|
55858
55966
|
const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
|
|
55859
|
-
const
|
|
55967
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
|
|
55968
|
+
const rows = d.query(sql).all(...params);
|
|
55860
55969
|
return rows.map(parseAgentRow);
|
|
55861
55970
|
}
|
|
55862
55971
|
function updateAgent(id, updates, db) {
|
package/dist/server/index.js
CHANGED
|
@@ -655,6 +655,8 @@ function assertUnambiguousStoreEnv() {
|
|
|
655
655
|
}
|
|
656
656
|
function getApiConfig() {
|
|
657
657
|
assertUnambiguousStoreEnv();
|
|
658
|
+
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
659
|
+
return null;
|
|
658
660
|
const rawBase = firstEnv(API_URL_ENV_KEYS);
|
|
659
661
|
const apiKey = firstEnv(API_KEY_ENV_KEYS);
|
|
660
662
|
if (!rawBase || !apiKey)
|
|
@@ -1720,6 +1722,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
|
|
|
1720
1722
|
`
|
|
1721
1723
|
${MEMORY_VERSION_SNAPSHOT_TRIGGER}
|
|
1722
1724
|
INSERT OR IGNORE INTO _migrations (id) VALUES (36);
|
|
1725
|
+
`,
|
|
1726
|
+
`
|
|
1727
|
+
DROP TRIGGER IF EXISTS audit_memory_insert;
|
|
1728
|
+
CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
|
|
1729
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
1730
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
|
|
1731
|
+
END;
|
|
1732
|
+
|
|
1733
|
+
DROP TRIGGER IF EXISTS audit_memory_update;
|
|
1734
|
+
CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
|
|
1735
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
|
|
1736
|
+
VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
|
|
1737
|
+
json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
|
|
1738
|
+
datetime('now'));
|
|
1739
|
+
END;
|
|
1740
|
+
|
|
1741
|
+
DROP TRIGGER IF EXISTS audit_memory_delete;
|
|
1742
|
+
CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
|
|
1743
|
+
INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
|
|
1744
|
+
VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
|
|
1745
|
+
END;
|
|
1746
|
+
|
|
1747
|
+
UPDATE memory_audit_log SET old_value_hash = NULL
|
|
1748
|
+
WHERE old_value_hash IS NOT NULL
|
|
1749
|
+
AND length(old_value_hash) = 32
|
|
1750
|
+
AND old_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
1751
|
+
|
|
1752
|
+
UPDATE memory_audit_log SET new_value_hash = NULL
|
|
1753
|
+
WHERE new_value_hash IS NOT NULL
|
|
1754
|
+
AND length(new_value_hash) = 32
|
|
1755
|
+
AND new_value_hash NOT GLOB '*[^0-9A-F]*';
|
|
1756
|
+
|
|
1757
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (37);
|
|
1723
1758
|
`
|
|
1724
1759
|
];
|
|
1725
1760
|
});
|
|
@@ -1779,9 +1814,10 @@ function migrateGlobalDir() {
|
|
|
1779
1814
|
}
|
|
1780
1815
|
}
|
|
1781
1816
|
function getDbPath2() {
|
|
1782
|
-
const
|
|
1783
|
-
|
|
1784
|
-
|
|
1817
|
+
for (const key of DB_PATH_ENV_KEYS) {
|
|
1818
|
+
const envPath = process.env[key]?.trim();
|
|
1819
|
+
if (envPath)
|
|
1820
|
+
return envPath;
|
|
1785
1821
|
}
|
|
1786
1822
|
const cwd = process.cwd();
|
|
1787
1823
|
const nearest = findNearestMementosDb(cwd);
|
|
@@ -1825,6 +1861,12 @@ function getDatabase(dbPath) {
|
|
|
1825
1861
|
if (getStorageMode() === "cloud") {
|
|
1826
1862
|
return getCloudDatabase();
|
|
1827
1863
|
}
|
|
1864
|
+
if (process.env["NODE_ENV"] === "test") {
|
|
1865
|
+
const hasExplicitDbPath = DB_PATH_ENV_KEYS.some((key) => process.env[key]?.trim());
|
|
1866
|
+
if (!hasExplicitDbPath) {
|
|
1867
|
+
throw new Error("REFUSING-UNPINNED-TEST-OPEN: a test process tried to open the default local " + `SQLite store (${getDbPath2()}) with no explicit dbPath argument and no ` + `${DB_PATH_ENV_KEYS.join("/")} set. Pass an explicit dbPath (e.g. ":memory:" ` + "or a scratch file), or set one of those env keys to a scratch path, before " + "calling getDatabase(). This guard exists because an unpinned test process " + "would otherwise silently read and write the real, shared, on-disk memory " + "store (todos 57b8b8c5).");
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1828
1870
|
}
|
|
1829
1871
|
const path = dbPath || getDbPath2();
|
|
1830
1872
|
if (_db) {
|
|
@@ -56138,6 +56180,7 @@ init_types();
|
|
|
56138
56180
|
init_database();
|
|
56139
56181
|
init_api_mode();
|
|
56140
56182
|
var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
|
|
56183
|
+
var UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
56141
56184
|
function parseAgentRow(row) {
|
|
56142
56185
|
return {
|
|
56143
56186
|
id: row["id"],
|
|
@@ -56229,24 +56272,61 @@ function getAgent(idOrName, db) {
|
|
|
56229
56272
|
return parseAgentRow(rows[0]);
|
|
56230
56273
|
return null;
|
|
56231
56274
|
}
|
|
56232
|
-
function
|
|
56233
|
-
|
|
56234
|
-
|
|
56275
|
+
function normalizedAgentListFilter(filter) {
|
|
56276
|
+
const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
|
|
56277
|
+
const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
|
|
56278
|
+
return { limit, offset };
|
|
56279
|
+
}
|
|
56280
|
+
function isDatabaseAdapter(value) {
|
|
56281
|
+
return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
|
|
56282
|
+
}
|
|
56283
|
+
function paginatedAgentQuery(baseSql, baseParams, filter) {
|
|
56284
|
+
const { limit, offset } = normalizedAgentListFilter(filter);
|
|
56285
|
+
let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
|
|
56286
|
+
const params = [...baseParams];
|
|
56287
|
+
if (limit !== undefined) {
|
|
56288
|
+
sql += " LIMIT ?";
|
|
56289
|
+
params.push(limit);
|
|
56290
|
+
} else if ((offset ?? 0) > 0) {
|
|
56291
|
+
sql += " LIMIT ?";
|
|
56292
|
+
params.push(UNBOUNDED_AGENT_LIST_LIMIT);
|
|
56293
|
+
}
|
|
56294
|
+
if ((offset ?? 0) > 0) {
|
|
56295
|
+
sql += " OFFSET ?";
|
|
56296
|
+
params.push(offset);
|
|
56297
|
+
}
|
|
56298
|
+
return { sql, params };
|
|
56299
|
+
}
|
|
56300
|
+
function listAgents(filterOrDb = {}, db) {
|
|
56301
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
56302
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
56303
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
56304
|
+
if (!explicitDb && isApiMode()) {
|
|
56305
|
+
const q = toQuery({
|
|
56306
|
+
limit: normalized.limit,
|
|
56307
|
+
offset: normalized.offset
|
|
56308
|
+
});
|
|
56309
|
+
const { data } = apiJson("GET", `/agents${q}`);
|
|
56235
56310
|
return data?.agents ?? [];
|
|
56236
56311
|
}
|
|
56237
|
-
const d =
|
|
56238
|
-
const
|
|
56312
|
+
const d = explicitDb || getDatabase();
|
|
56313
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
|
|
56314
|
+
const rows = d.query(sql).all(...params);
|
|
56239
56315
|
return rows.map(parseAgentRow);
|
|
56240
56316
|
}
|
|
56241
|
-
function listAgentsByProject(projectId, db) {
|
|
56242
|
-
|
|
56243
|
-
|
|
56317
|
+
function listAgentsByProject(projectId, filterOrDb = {}, db) {
|
|
56318
|
+
const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
|
|
56319
|
+
const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
|
|
56320
|
+
const normalized = normalizedAgentListFilter(filter);
|
|
56321
|
+
if (!explicitDb && isApiMode()) {
|
|
56322
|
+
const q = toQuery({ project_id: projectId, ...normalized });
|
|
56244
56323
|
const { data } = apiJson("GET", `/agents${q}`);
|
|
56245
56324
|
return data?.agents ?? [];
|
|
56246
56325
|
}
|
|
56247
|
-
const d =
|
|
56326
|
+
const d = explicitDb || getDatabase();
|
|
56248
56327
|
const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
|
|
56249
|
-
const
|
|
56328
|
+
const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
|
|
56329
|
+
const rows = d.query(sql).all(...params);
|
|
56250
56330
|
return rows.map(parseAgentRow);
|
|
56251
56331
|
}
|
|
56252
56332
|
function updateAgent(id, updates, db) {
|
|
@@ -56409,9 +56489,21 @@ function cleanExpiredLocks(db) {
|
|
|
56409
56489
|
|
|
56410
56490
|
// src/server/routes/agents.ts
|
|
56411
56491
|
init_router();
|
|
56492
|
+
function paginationInt(value) {
|
|
56493
|
+
if (value === undefined)
|
|
56494
|
+
return;
|
|
56495
|
+
const parsed = Number(value);
|
|
56496
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
56497
|
+
return;
|
|
56498
|
+
return Math.floor(parsed);
|
|
56499
|
+
}
|
|
56412
56500
|
addRoute("GET", "/api/agents", (_req, url) => {
|
|
56413
56501
|
const q = getSearchParams(url);
|
|
56414
|
-
const
|
|
56502
|
+
const pagination = {
|
|
56503
|
+
limit: paginationInt(q["limit"]),
|
|
56504
|
+
offset: paginationInt(q["cursor"] ?? q["offset"])
|
|
56505
|
+
};
|
|
56506
|
+
const agents = q["project_id"] ? listAgentsByProject(q["project_id"], pagination) : listAgents(pagination);
|
|
56415
56507
|
if (q["fields"]) {
|
|
56416
56508
|
const fields = q["fields"].split(",").map((f) => f.trim());
|
|
56417
56509
|
const filtered = agents.map((a) => Object.fromEntries(fields.map((f) => [f, a[f]]).filter(([, v]) => v !== undefined)));
|
|
@@ -31,8 +31,6 @@ export interface IsolatedStoreEnvOptions {
|
|
|
31
31
|
*/
|
|
32
32
|
export declare function isolatedStoreEnv(dbPath: string, options?: IsolatedStoreEnvOptions): Record<string, string>;
|
|
33
33
|
export interface StubApiEnvOptions {
|
|
34
|
-
/** Pin the (non-authoritative) local SQLite path too, for belt and braces. */
|
|
35
|
-
dbPath?: string;
|
|
36
34
|
/** Stub bearer token. Never a real credential — the stub does not check it. */
|
|
37
35
|
apiKey?: string;
|
|
38
36
|
}
|
|
@@ -48,6 +46,28 @@ export interface StubApiEnvOptions {
|
|
|
48
46
|
* @param baseUrl the stub's origin, e.g. `http://127.0.0.1:<port>/<mode>`
|
|
49
47
|
*/
|
|
50
48
|
export declare function stubApiEnv(baseUrl: string, options?: StubApiEnvOptions): Record<string, string>;
|
|
49
|
+
/**
|
|
50
|
+
* Build a child env that DOES resolve to the cloud API — the negative case the
|
|
51
|
+
* isolation guards are proved against.
|
|
52
|
+
*
|
|
53
|
+
* This exists because of the 2026-08-03 precedence-1 fix. Before it, a cloud
|
|
54
|
+
* case could be built by taking any env and adding an API url+key, and several
|
|
55
|
+
* tests did exactly that on top of {@link isolatedStoreEnv}. An explicit
|
|
56
|
+
* `MEMENTOS_DB_PATH` now DEFEATS the API selectors (see getApiConfig in
|
|
57
|
+
* src/db/api-mode.ts), so that recipe silently produces a LOCAL backend — which
|
|
58
|
+
* would leave the isolation guards' positive controls unable to observe the bad
|
|
59
|
+
* state they exist to detect, and `assertLocalStoreBackend`'s own self-test
|
|
60
|
+
* unable to fail. Both would still be green.
|
|
61
|
+
*
|
|
62
|
+
* So the DB_PATH keys are removed here explicitly, and that removal is the
|
|
63
|
+
* load-bearing line rather than an afterthought: it is the only way to reach the
|
|
64
|
+
* cloud backend now. They are NOT in STORE_SELECTOR_ENV_KEYS because that set
|
|
65
|
+
* means "moves the store OFF local SQLite", and DB_PATH does the opposite.
|
|
66
|
+
*
|
|
67
|
+
* @param baseUrl point this at a closed loopback port, never a real endpoint
|
|
68
|
+
* @param apiKey never a real credential
|
|
69
|
+
*/
|
|
70
|
+
export declare function cloudSelectingEnv(baseUrl: string, apiKey?: string): Record<string, string>;
|
|
51
71
|
/** LLM provider keys most harnesses blank so no test can make a billed call. */
|
|
52
72
|
export declare const LLM_PROVIDER_ENV_KEYS: readonly ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "CEREBRAS_API_KEY", "XAI_API_KEY"];
|
|
53
73
|
/** `{ ANTHROPIC_API_KEY: "", ... }` — pass as `extra` to blank LLM providers. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-isolation.d.ts","sourceRoot":"","sources":["../../src/test-support/store-isolation.ts"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAWpD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,uBAA4B,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxB;AASD,MAAM,WAAW,iBAAiB;IAChC
|
|
1
|
+
{"version":3,"file":"store-isolation.d.ts","sourceRoot":"","sources":["../../src/test-support/store-isolation.ts"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAWpD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,uBAA4B,GACpC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxB;AASD,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;CAUjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQnG;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,SAAmB,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,qFAKxB,CAAC;AAEX,iFAAiF;AACjF,wBAAgB,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE5D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAsD7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,SAA0B,GAAG,IAAI,CAQ9F"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-ansi.d.ts","sourceRoot":"","sources":["../../src/test-support/strip-ansi.ts"],"names":[],"mappings":"AAyBA,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C"}
|