@hasna/mementos 0.14.68 → 0.14.69
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/__fixtures__/clean-fallback-stub-server.d.ts +2 -0
- package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts.map +1 -0
- package/dist/cli/commands/io-clean.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
- package/dist/cli/commands/storage.d.ts.map +1 -1
- package/dist/cli/global-options.d.ts +27 -0
- package/dist/cli/global-options.d.ts.map +1 -0
- package/dist/cli/index.js +502 -241
- package/dist/cli/register-all.d.ts +17 -0
- package/dist/cli/register-all.d.ts.map +1 -0
- package/dist/cli/startup-side-effects.d.ts +9 -0
- package/dist/cli/startup-side-effects.d.ts.map +1 -0
- package/dist/db/__fixtures__/fail-closed-stub-server.d.ts +2 -0
- package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +1 -0
- package/dist/db/api-mode.d.ts +70 -2
- package/dist/db/api-mode.d.ts.map +1 -1
- package/dist/db/locks.d.ts.map +1 -1
- package/dist/db/memories.d.ts +24 -6
- package/dist/db/memories.d.ts.map +1 -1
- package/dist/db/session-jobs.d.ts.map +1 -1
- package/dist/db/store-backend.d.ts +33 -0
- package/dist/db/store-backend.d.ts.map +1 -0
- package/dist/index.js +80 -20
- package/dist/lib/enum-validation.d.ts +20 -0
- package/dist/lib/enum-validation.d.ts.map +1 -0
- package/dist/mcp/index.js +147 -30
- package/dist/server/helpers.d.ts +11 -0
- package/dist/server/helpers.d.ts.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +185 -30
- package/dist/test-support/preload-local-store.d.ts +2 -0
- package/dist/test-support/preload-local-store.d.ts.map +1 -0
- package/dist/test-support/store-isolation.d.ts +83 -0
- package/dist/test-support/store-isolation.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -4
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2101,9 +2101,9 @@ var require_commander = __commonJS((exports) => {
|
|
|
2101
2101
|
|
|
2102
2102
|
// src/storage.ts
|
|
2103
2103
|
import { Database } from "bun:sqlite";
|
|
2104
|
-
import { existsSync
|
|
2105
|
-
import { homedir
|
|
2106
|
-
import { join
|
|
2104
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2105
|
+
import { homedir } from "os";
|
|
2106
|
+
import { join } from "path";
|
|
2107
2107
|
import { fileURLToPath } from "url";
|
|
2108
2108
|
import { Worker } from "worker_threads";
|
|
2109
2109
|
import pg from "pg";
|
|
@@ -2371,7 +2371,7 @@ function normalizeStorageMode(value) {
|
|
|
2371
2371
|
return null;
|
|
2372
2372
|
}
|
|
2373
2373
|
function readConfigFile() {
|
|
2374
|
-
if (!
|
|
2374
|
+
if (!existsSync(STORAGE_CONFIG_PATH)) {
|
|
2375
2375
|
return {};
|
|
2376
2376
|
}
|
|
2377
2377
|
try {
|
|
@@ -2831,12 +2831,12 @@ var init_storage = __esm(() => {
|
|
|
2831
2831
|
const ext = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
|
|
2832
2832
|
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
2833
2833
|
const candidates = [
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2834
|
+
join(here, `pg-sync-worker${ext}`),
|
|
2835
|
+
join(here, "..", `pg-sync-worker${ext}`),
|
|
2836
|
+
join(here, "..", "..", `pg-sync-worker${ext}`)
|
|
2837
2837
|
];
|
|
2838
2838
|
for (const candidate of candidates) {
|
|
2839
|
-
if (
|
|
2839
|
+
if (existsSync(candidate))
|
|
2840
2840
|
return candidate;
|
|
2841
2841
|
}
|
|
2842
2842
|
return candidates[0];
|
|
@@ -2911,7 +2911,7 @@ var init_storage = __esm(() => {
|
|
|
2911
2911
|
databaseUrl: "MEMENTOS_DATABASE_URL",
|
|
2912
2912
|
mode: "MEMENTOS_STORAGE_MODE"
|
|
2913
2913
|
};
|
|
2914
|
-
LOCAL_DATA_DIR =
|
|
2914
|
+
LOCAL_DATA_DIR = join(homedir(), ".hasna", "mementos");
|
|
2915
2915
|
DEFAULT_STORAGE_CONFIG = {
|
|
2916
2916
|
rds: {
|
|
2917
2917
|
host: "",
|
|
@@ -2927,8 +2927,8 @@ var init_storage = __esm(() => {
|
|
|
2927
2927
|
schedule_minutes: 0
|
|
2928
2928
|
}
|
|
2929
2929
|
};
|
|
2930
|
-
STORAGE_CONFIG_DIR =
|
|
2931
|
-
STORAGE_CONFIG_PATH =
|
|
2930
|
+
STORAGE_CONFIG_DIR = join(LOCAL_DATA_DIR, "storage");
|
|
2931
|
+
STORAGE_CONFIG_PATH = join(STORAGE_CONFIG_DIR, "config.json");
|
|
2932
2932
|
DATABASE_ENV_NAMES = [
|
|
2933
2933
|
{ name: MEMENTOS_STORAGE_ENV.databaseUrl, deprecated: false },
|
|
2934
2934
|
{ name: MEMENTOS_STORAGE_FALLBACK_ENV.databaseUrl, deprecated: false }
|
|
@@ -2958,10 +2958,10 @@ var init_storage = __esm(() => {
|
|
|
2958
2958
|
|
|
2959
2959
|
// src/db/api-mode.ts
|
|
2960
2960
|
import { tmpdir } from "os";
|
|
2961
|
-
import { join as
|
|
2961
|
+
import { join as join2 } from "path";
|
|
2962
2962
|
import { writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
2963
|
-
import { randomUUID
|
|
2964
|
-
function firstEnv(
|
|
2963
|
+
import { randomUUID } from "crypto";
|
|
2964
|
+
function firstEnv(keys) {
|
|
2965
2965
|
for (const k of keys) {
|
|
2966
2966
|
const v = process.env[k]?.trim();
|
|
2967
2967
|
if (v)
|
|
@@ -2969,8 +2969,45 @@ function firstEnv(...keys) {
|
|
|
2969
2969
|
}
|
|
2970
2970
|
return;
|
|
2971
2971
|
}
|
|
2972
|
+
function firstEnvKey(keys) {
|
|
2973
|
+
for (const k of keys) {
|
|
2974
|
+
if (process.env[k]?.trim())
|
|
2975
|
+
return k;
|
|
2976
|
+
}
|
|
2977
|
+
return null;
|
|
2978
|
+
}
|
|
2972
2979
|
function hasDatabaseUrl() {
|
|
2973
|
-
return Boolean(firstEnv(
|
|
2980
|
+
return Boolean(firstEnv(DATABASE_URL_ENV_KEYS));
|
|
2981
|
+
}
|
|
2982
|
+
function getApiModeEnvSources() {
|
|
2983
|
+
return {
|
|
2984
|
+
urlKey: firstEnvKey(API_URL_ENV_KEYS),
|
|
2985
|
+
keyKey: firstEnvKey(API_KEY_ENV_KEYS),
|
|
2986
|
+
databaseUrlKey: firstEnvKey(DATABASE_URL_ENV_KEYS)
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function isLoopbackHost(rawHost) {
|
|
2990
|
+
const host = rawHost.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
|
|
2991
|
+
return host === "localhost" || host === "::1" || /^127\./.test(host);
|
|
2992
|
+
}
|
|
2993
|
+
function assertRequestAllowedUnderTest(baseUrl) {
|
|
2994
|
+
if (process.env["NODE_ENV"] !== "test")
|
|
2995
|
+
return;
|
|
2996
|
+
if (process.env[ALLOW_REMOTE_API_IN_TESTS_ENV]?.trim())
|
|
2997
|
+
return;
|
|
2998
|
+
let host;
|
|
2999
|
+
try {
|
|
3000
|
+
host = new URL(baseUrl).hostname;
|
|
3001
|
+
} catch {
|
|
3002
|
+
host = "";
|
|
3003
|
+
}
|
|
3004
|
+
if (host && isLoopbackHost(host))
|
|
3005
|
+
return;
|
|
3006
|
+
throw new Error("api-mode: REFUSING to make a cloud request from a test process \u2014 this would write to or read " + "from the SHARED PRODUCTION memory store, where test fixtures are indistinguishable from real " + `memories.
|
|
3007
|
+
` + ` host : ${host || "(unparseable base URL)"}
|
|
3008
|
+
` + ` how this happens: a selector set at module scope (after the bun test preload ran), or \`bun test\` ` + `invoked from a directory with no bunfig.toml so the preload never loaded.
|
|
3009
|
+
` + " fix : build the child/process env via src/test-support/store-isolation.ts, or point the " + `suite at a loopback stub.
|
|
3010
|
+
` + ` override : set ${ALLOW_REMOTE_API_IN_TESTS_ENV}=1 only for a test that must reach a remote endpoint.`);
|
|
2974
3011
|
}
|
|
2975
3012
|
function normalizeBase(raw) {
|
|
2976
3013
|
let base = raw.trim().replace(/\/+$/, "");
|
|
@@ -2978,9 +3015,24 @@ function normalizeBase(raw) {
|
|
|
2978
3015
|
return base;
|
|
2979
3016
|
return `${base}/v1`;
|
|
2980
3017
|
}
|
|
3018
|
+
function assertUnambiguousStoreEnv() {
|
|
3019
|
+
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
3020
|
+
return;
|
|
3021
|
+
if (hasDatabaseUrl())
|
|
3022
|
+
return;
|
|
3023
|
+
const urlKey = firstEnvKey(API_URL_ENV_KEYS);
|
|
3024
|
+
const keyKey = firstEnvKey(API_KEY_ENV_KEYS);
|
|
3025
|
+
if (urlKey && !keyKey) {
|
|
3026
|
+
throw new MementosStoreConfigError(`${urlKey} points at the cloud memory store but ${API_KEY_ENV_KEYS[0]} is not set. ` + `Refusing to serve the on-box SQLite store in its place, because it holds a different ` + `dataset. Set ${API_KEY_ENV_KEYS[0]} to reach the cloud store. If you meant to use the ` + `on-box SQLite store, unset ${urlKey} or set ${DB_PATH_ENV_KEYS[0]} explicitly.`);
|
|
3027
|
+
}
|
|
3028
|
+
if (keyKey && !urlKey) {
|
|
3029
|
+
throw new MementosStoreConfigError(`${keyKey} is set but ${API_URL_ENV_KEYS[0]} is not, so the cloud memory store cannot be ` + `reached. Refusing to serve the on-box SQLite store in its place, because it holds a ` + `different dataset. Set ${API_URL_ENV_KEYS[0]} to reach the cloud store. If you meant to ` + `use the on-box SQLite store, unset ${keyKey} or set ${DB_PATH_ENV_KEYS[0]} explicitly.`);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
2981
3032
|
function getApiConfig() {
|
|
2982
|
-
|
|
2983
|
-
const
|
|
3033
|
+
assertUnambiguousStoreEnv();
|
|
3034
|
+
const rawBase = firstEnv(API_URL_ENV_KEYS);
|
|
3035
|
+
const apiKey = firstEnv(API_KEY_ENV_KEYS);
|
|
2984
3036
|
if (!rawBase || !apiKey)
|
|
2985
3037
|
return null;
|
|
2986
3038
|
return { baseUrl: normalizeBase(rawBase), apiKey };
|
|
@@ -2994,6 +3046,7 @@ function apiRequestRaw(method, path, body) {
|
|
|
2994
3046
|
const cfg = getApiConfig();
|
|
2995
3047
|
if (!cfg)
|
|
2996
3048
|
throw new Error("api-mode: not configured (HASNA_MEMENTOS_API_URL / HASNA_MEMENTOS_API_KEY)");
|
|
3049
|
+
assertRequestAllowedUnderTest(cfg.baseUrl);
|
|
2997
3050
|
const url = `${cfg.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
2998
3051
|
const hasBody = body !== undefined && body !== null;
|
|
2999
3052
|
const timeout = process.env["HASNA_MEMENTOS_API_TIMEOUT"] || DEFAULT_TIMEOUT_S;
|
|
@@ -3019,7 +3072,7 @@ x-api-key: ${cfg.apiKey}
|
|
|
3019
3072
|
];
|
|
3020
3073
|
let bodyFile;
|
|
3021
3074
|
if (hasBody) {
|
|
3022
|
-
bodyFile =
|
|
3075
|
+
bodyFile = join2(tmpdir(), `mem-req-${process.pid}-${randomUUID()}.json`);
|
|
3023
3076
|
writeFileSync2(bodyFile, JSON.stringify(body), { mode: 384 });
|
|
3024
3077
|
args.push("--data-binary", `@${bodyFile}`);
|
|
3025
3078
|
}
|
|
@@ -3060,13 +3113,13 @@ x-api-key: ${cfg.apiKey}
|
|
|
3060
3113
|
}
|
|
3061
3114
|
return { status, body: respBody };
|
|
3062
3115
|
}
|
|
3063
|
-
function apiJson(method, path, body) {
|
|
3116
|
+
function apiJson(method, path, body, options) {
|
|
3064
3117
|
const raw = apiRequestRaw(method, path, body);
|
|
3065
3118
|
if (raw.status >= 200 && raw.status < 300) {
|
|
3066
3119
|
const data = raw.body.trim() ? JSON.parse(raw.body) : undefined;
|
|
3067
3120
|
return { status: raw.status, data };
|
|
3068
3121
|
}
|
|
3069
|
-
if (raw.status === 404) {
|
|
3122
|
+
if (raw.status === 404 && options?.allow404) {
|
|
3070
3123
|
return { status: 404, data: undefined };
|
|
3071
3124
|
}
|
|
3072
3125
|
let msg = `mementos cloud ${method} ${path} \u2192 ${raw.status}`;
|
|
@@ -3098,8 +3151,19 @@ function toQuery(params) {
|
|
|
3098
3151
|
const s = sp.toString();
|
|
3099
3152
|
return s ? `?${s}` : "";
|
|
3100
3153
|
}
|
|
3101
|
-
var ApiRequestError, DEFAULT_TIMEOUT_S = "45";
|
|
3154
|
+
var API_URL_ENV_KEYS, API_KEY_ENV_KEYS, DATABASE_URL_ENV_KEYS, ALLOW_REMOTE_API_IN_TESTS_ENV = "MEMENTOS_ALLOW_REMOTE_API_IN_TESTS", DB_PATH_ENV_KEYS, MementosStoreConfigError, ApiRequestError, DEFAULT_TIMEOUT_S = "45";
|
|
3102
3155
|
var init_api_mode = __esm(() => {
|
|
3156
|
+
API_URL_ENV_KEYS = ["HASNA_MEMENTOS_API_URL", "MEMENTOS_API_URL"];
|
|
3157
|
+
API_KEY_ENV_KEYS = ["HASNA_MEMENTOS_API_KEY", "MEMENTOS_API_KEY"];
|
|
3158
|
+
DATABASE_URL_ENV_KEYS = ["HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"];
|
|
3159
|
+
DB_PATH_ENV_KEYS = ["HASNA_MEMENTOS_DB_PATH", "MEMENTOS_DB_PATH"];
|
|
3160
|
+
MementosStoreConfigError = class MementosStoreConfigError extends Error {
|
|
3161
|
+
code = "MEMENTOS_STORE_CONFIG";
|
|
3162
|
+
constructor(message) {
|
|
3163
|
+
super(message);
|
|
3164
|
+
this.name = "MementosStoreConfigError";
|
|
3165
|
+
}
|
|
3166
|
+
};
|
|
3103
3167
|
ApiRequestError = class ApiRequestError extends Error {
|
|
3104
3168
|
status;
|
|
3105
3169
|
body;
|
|
@@ -4022,13 +4086,13 @@ __export(exports_database, {
|
|
|
4022
4086
|
shortUuid: () => shortUuid,
|
|
4023
4087
|
resolvePartialId: () => resolvePartialId,
|
|
4024
4088
|
resetDatabase: () => resetDatabase,
|
|
4025
|
-
now: () =>
|
|
4089
|
+
now: () => now,
|
|
4026
4090
|
getDbPath: () => getDbPath,
|
|
4027
4091
|
getDatabase: () => getDatabase,
|
|
4028
4092
|
closeDatabase: () => closeDatabase
|
|
4029
4093
|
});
|
|
4030
|
-
import { existsSync as
|
|
4031
|
-
import { dirname, join as
|
|
4094
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, cpSync } from "fs";
|
|
4095
|
+
import { dirname, join as join3, resolve } from "path";
|
|
4032
4096
|
function isInMemoryDb(path) {
|
|
4033
4097
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
4034
4098
|
}
|
|
@@ -4037,8 +4101,8 @@ function findNearestMementosDb(startDir) {
|
|
|
4037
4101
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
4038
4102
|
const legacyHomeDb = resolve(home, ".mementos", "mementos.db");
|
|
4039
4103
|
while (true) {
|
|
4040
|
-
const candidate =
|
|
4041
|
-
if (
|
|
4104
|
+
const candidate = join3(dir, ".mementos", "mementos.db");
|
|
4105
|
+
if (existsSync2(candidate) && resolve(candidate) !== legacyHomeDb)
|
|
4042
4106
|
return candidate;
|
|
4043
4107
|
const parent = dirname(dir);
|
|
4044
4108
|
if (parent === dir)
|
|
@@ -4050,7 +4114,7 @@ function findNearestMementosDb(startDir) {
|
|
|
4050
4114
|
function findGitRoot(startDir) {
|
|
4051
4115
|
let dir = resolve(startDir);
|
|
4052
4116
|
while (true) {
|
|
4053
|
-
if (
|
|
4117
|
+
if (existsSync2(join3(dir, ".git")))
|
|
4054
4118
|
return dir;
|
|
4055
4119
|
const parent = dirname(dir);
|
|
4056
4120
|
if (parent === dir)
|
|
@@ -4061,10 +4125,10 @@ function findGitRoot(startDir) {
|
|
|
4061
4125
|
}
|
|
4062
4126
|
function migrateGlobalDir() {
|
|
4063
4127
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
4064
|
-
const newDir =
|
|
4065
|
-
const oldDir =
|
|
4066
|
-
if (!
|
|
4067
|
-
mkdirSync2(
|
|
4128
|
+
const newDir = join3(home, ".hasna", "mementos");
|
|
4129
|
+
const oldDir = join3(home, ".mementos");
|
|
4130
|
+
if (!existsSync2(newDir) && existsSync2(oldDir)) {
|
|
4131
|
+
mkdirSync2(join3(home, ".hasna"), { recursive: true });
|
|
4068
4132
|
cpSync(oldDir, newDir, { recursive: true });
|
|
4069
4133
|
}
|
|
4070
4134
|
}
|
|
@@ -4080,18 +4144,18 @@ function getDbPath() {
|
|
|
4080
4144
|
if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
|
|
4081
4145
|
const gitRoot = findGitRoot(cwd);
|
|
4082
4146
|
if (gitRoot) {
|
|
4083
|
-
return
|
|
4147
|
+
return join3(gitRoot, ".mementos", "mementos.db");
|
|
4084
4148
|
}
|
|
4085
4149
|
}
|
|
4086
4150
|
migrateGlobalDir();
|
|
4087
4151
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
4088
|
-
return
|
|
4152
|
+
return join3(home, ".hasna", "mementos", "mementos.db");
|
|
4089
4153
|
}
|
|
4090
4154
|
function ensureDir(filePath) {
|
|
4091
4155
|
if (isInMemoryDb(filePath))
|
|
4092
4156
|
return;
|
|
4093
4157
|
const dir = dirname(resolve(filePath));
|
|
4094
|
-
if (!
|
|
4158
|
+
if (!existsSync2(dir)) {
|
|
4095
4159
|
mkdirSync2(dir, { recursive: true });
|
|
4096
4160
|
}
|
|
4097
4161
|
}
|
|
@@ -4190,7 +4254,7 @@ function resetDatabase() {
|
|
|
4190
4254
|
_db = null;
|
|
4191
4255
|
_pg = null;
|
|
4192
4256
|
}
|
|
4193
|
-
function
|
|
4257
|
+
function now() {
|
|
4194
4258
|
return new Date().toISOString();
|
|
4195
4259
|
}
|
|
4196
4260
|
function uuid() {
|
|
@@ -4236,8 +4300,19 @@ var init_database = __esm(() => {
|
|
|
4236
4300
|
});
|
|
4237
4301
|
|
|
4238
4302
|
// src/types/index.ts
|
|
4239
|
-
var AgentConflictError, EntityNotFoundError, MemoryNotFoundError, VersionConflictError, MemoryConflictError;
|
|
4303
|
+
var MEMORY_SCOPES, MEMORY_CATEGORIES, MEMORY_SOURCES, MEMORY_STATUSES, AgentConflictError, EntityNotFoundError, MemoryNotFoundError, VersionConflictError, MemoryConflictError;
|
|
4240
4304
|
var init_types = __esm(() => {
|
|
4305
|
+
MEMORY_SCOPES = ["global", "shared", "private", "working"];
|
|
4306
|
+
MEMORY_CATEGORIES = [
|
|
4307
|
+
"preference",
|
|
4308
|
+
"fact",
|
|
4309
|
+
"knowledge",
|
|
4310
|
+
"history",
|
|
4311
|
+
"procedural",
|
|
4312
|
+
"resource"
|
|
4313
|
+
];
|
|
4314
|
+
MEMORY_SOURCES = ["user", "agent", "system", "auto", "imported"];
|
|
4315
|
+
MEMORY_STATUSES = ["active", "archived", "expired"];
|
|
4241
4316
|
AgentConflictError = class AgentConflictError extends Error {
|
|
4242
4317
|
conflict = true;
|
|
4243
4318
|
existing_id;
|
|
@@ -4394,6 +4469,41 @@ var init_redact = __esm(() => {
|
|
|
4394
4469
|
];
|
|
4395
4470
|
});
|
|
4396
4471
|
|
|
4472
|
+
// src/lib/enum-validation.ts
|
|
4473
|
+
function formatEnumViolation(v) {
|
|
4474
|
+
return `Invalid ${v.field}: "${v.value}". Allowed values: ${v.allowed.join(", ")}.`;
|
|
4475
|
+
}
|
|
4476
|
+
function validateEnumField(field, value) {
|
|
4477
|
+
const allowed = ENUM_FIELDS[field];
|
|
4478
|
+
if (!allowed)
|
|
4479
|
+
return null;
|
|
4480
|
+
if (value === undefined || value === null || value === "")
|
|
4481
|
+
return null;
|
|
4482
|
+
if (typeof value === "string" && allowed.includes(value))
|
|
4483
|
+
return null;
|
|
4484
|
+
return { field, value: String(value), allowed };
|
|
4485
|
+
}
|
|
4486
|
+
function validateMemoryEnums(input) {
|
|
4487
|
+
for (const field of Object.keys(ENUM_FIELDS)) {
|
|
4488
|
+
if (!(field in input))
|
|
4489
|
+
continue;
|
|
4490
|
+
const violation = validateEnumField(field, input[field]);
|
|
4491
|
+
if (violation)
|
|
4492
|
+
return violation;
|
|
4493
|
+
}
|
|
4494
|
+
return null;
|
|
4495
|
+
}
|
|
4496
|
+
var ENUM_FIELDS;
|
|
4497
|
+
var init_enum_validation = __esm(() => {
|
|
4498
|
+
init_types();
|
|
4499
|
+
ENUM_FIELDS = {
|
|
4500
|
+
category: MEMORY_CATEGORIES,
|
|
4501
|
+
scope: MEMORY_SCOPES,
|
|
4502
|
+
source: MEMORY_SOURCES,
|
|
4503
|
+
status: MEMORY_STATUSES
|
|
4504
|
+
};
|
|
4505
|
+
});
|
|
4506
|
+
|
|
4397
4507
|
// src/lib/hooks.ts
|
|
4398
4508
|
var exports_hooks = {};
|
|
4399
4509
|
__export(exports_hooks, {
|
|
@@ -4565,7 +4675,7 @@ function linkEntityToMemory(entityId, memoryId, role = "context", db) {
|
|
|
4565
4675
|
return data;
|
|
4566
4676
|
}
|
|
4567
4677
|
const d = db || getDatabase();
|
|
4568
|
-
const timestamp =
|
|
4678
|
+
const timestamp = now();
|
|
4569
4679
|
d.run(`INSERT OR IGNORE INTO entity_memories (entity_id, memory_id, role, created_at)
|
|
4570
4680
|
VALUES (?, ?, ?, ?)`, [entityId, memoryId, role, timestamp]);
|
|
4571
4681
|
const row = d.query("SELECT * FROM entity_memories WHERE entity_id = ? AND memory_id = ?").get(entityId, memoryId);
|
|
@@ -4691,11 +4801,14 @@ function parseMemoryRow(row) {
|
|
|
4691
4801
|
}
|
|
4692
4802
|
function createMemory(input, dedupeMode = "merge", db) {
|
|
4693
4803
|
if (!db && isApiMode()) {
|
|
4694
|
-
const { data } = apiJson("POST", "/memories", { ...input, dedupe: dedupeMode });
|
|
4804
|
+
const { status, data } = apiJson("POST", "/memories", { ...input, dedupe: dedupeMode });
|
|
4805
|
+
if (!data || !data.id) {
|
|
4806
|
+
throw new ApiRequestError(`mementos cloud POST /memories \u2192 ${status} but no memory was returned; the write did not persist (key: ${input.key})`, status, "");
|
|
4807
|
+
}
|
|
4695
4808
|
return data;
|
|
4696
4809
|
}
|
|
4697
4810
|
const d = db || getDatabase();
|
|
4698
|
-
const timestamp =
|
|
4811
|
+
const timestamp = now();
|
|
4699
4812
|
if (input.project_id) {
|
|
4700
4813
|
const resolved = resolvePartialId(d, "projects", input.project_id);
|
|
4701
4814
|
if (resolved) {
|
|
@@ -4830,19 +4943,28 @@ function bulkUpsertMemories(memories, db) {
|
|
|
4830
4943
|
const d = db || getDatabase();
|
|
4831
4944
|
let inserted = 0;
|
|
4832
4945
|
let skipped = 0;
|
|
4946
|
+
let rejected = 0;
|
|
4833
4947
|
const errors = [];
|
|
4834
|
-
const insert = d.prepare(`INSERT
|
|
4835
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4948
|
+
const insert = d.prepare(`INSERT INTO memories (id, key, value, category, scope, summary, tags, importance, source, status, pinned, agent_id, project_id, session_id, machine_id, namespace, created_by_agent, when_to_use, sequence_group, sequence_order, metadata, access_count, version, expires_at, valid_from, valid_until, ingested_at, created_at, updated_at)
|
|
4949
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4950
|
+
ON CONFLICT DO NOTHING`);
|
|
4836
4951
|
const insertTag = d.prepare("INSERT OR IGNORE INTO memory_tags (memory_id, tag) VALUES (?, ?)");
|
|
4837
4952
|
for (const mem of memories) {
|
|
4838
4953
|
const key = mem["key"];
|
|
4839
4954
|
const id = mem["id"] || uuid();
|
|
4840
4955
|
try {
|
|
4841
4956
|
if (!key) {
|
|
4842
|
-
|
|
4957
|
+
rejected++;
|
|
4958
|
+
errors.push(`rejected row without key (id=${id})`);
|
|
4959
|
+
continue;
|
|
4960
|
+
}
|
|
4961
|
+
const violation = validateMemoryEnums(mem);
|
|
4962
|
+
if (violation) {
|
|
4963
|
+
rejected++;
|
|
4964
|
+
errors.push(`Rejected "${key}": ${formatEnumViolation(violation)}`);
|
|
4843
4965
|
continue;
|
|
4844
4966
|
}
|
|
4845
|
-
const timestamp =
|
|
4967
|
+
const timestamp = now();
|
|
4846
4968
|
let tags = [];
|
|
4847
4969
|
const rawTags = mem["tags"];
|
|
4848
4970
|
if (Array.isArray(rawTags)) {
|
|
@@ -4888,13 +5010,14 @@ function bulkUpsertMemories(memories, db) {
|
|
|
4888
5010
|
skipped++;
|
|
4889
5011
|
}
|
|
4890
5012
|
} catch (e) {
|
|
5013
|
+
rejected++;
|
|
4891
5014
|
errors.push(`Failed "${String(key)}": ${e instanceof Error ? e.message : String(e)}`);
|
|
4892
5015
|
}
|
|
4893
5016
|
}
|
|
4894
|
-
return { inserted, skipped, errors, total: memories.length };
|
|
5017
|
+
return { inserted, skipped, rejected, errors, total: memories.length };
|
|
4895
5018
|
}
|
|
4896
5019
|
function ensureMemoryReferences(d, input) {
|
|
4897
|
-
const t =
|
|
5020
|
+
const t = now();
|
|
4898
5021
|
const tryRun = (sql, params) => {
|
|
4899
5022
|
try {
|
|
4900
5023
|
d.run(sql, params);
|
|
@@ -4919,7 +5042,7 @@ function listMemoriesByKey(key, db) {
|
|
|
4919
5042
|
}
|
|
4920
5043
|
function getMemory(id, db) {
|
|
4921
5044
|
if (!db && isApiMode()) {
|
|
4922
|
-
const { status, data } = apiJson("GET", `/memories/${encodeURIComponent(id)}
|
|
5045
|
+
const { status, data } = apiJson("GET", `/memories/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
4923
5046
|
return status === 404 ? null : data ?? null;
|
|
4924
5047
|
}
|
|
4925
5048
|
const d = db || getDatabase();
|
|
@@ -5252,7 +5375,7 @@ function getMemoryEmbeddings(ids, db) {
|
|
|
5252
5375
|
}
|
|
5253
5376
|
function updateMemory(id, input, db) {
|
|
5254
5377
|
if (!db && isApiMode()) {
|
|
5255
|
-
const { status, data } = apiJson("PATCH", `/memories/${encodeURIComponent(id)}`, input);
|
|
5378
|
+
const { status, data } = apiJson("PATCH", `/memories/${encodeURIComponent(id)}`, input, { allow404: true });
|
|
5256
5379
|
if (status === 404)
|
|
5257
5380
|
throw new MemoryNotFoundError(id);
|
|
5258
5381
|
return data;
|
|
@@ -5283,7 +5406,7 @@ function updateMemory(id, input, db) {
|
|
|
5283
5406
|
]);
|
|
5284
5407
|
} catch {}
|
|
5285
5408
|
const sets = ["version = version + 1", "updated_at = ?"];
|
|
5286
|
-
const params = [
|
|
5409
|
+
const params = [now()];
|
|
5287
5410
|
if (input.value !== undefined) {
|
|
5288
5411
|
sets.push("value = ?");
|
|
5289
5412
|
params.push(redactSecrets(input.value));
|
|
@@ -5360,7 +5483,7 @@ function updateMemory(id, input, db) {
|
|
|
5360
5483
|
}
|
|
5361
5484
|
function deleteMemory(id, db) {
|
|
5362
5485
|
if (!db && isApiMode()) {
|
|
5363
|
-
const { status } = apiJson("DELETE", `/memories/${encodeURIComponent(id)}
|
|
5486
|
+
const { status } = apiJson("DELETE", `/memories/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
5364
5487
|
return status !== 404;
|
|
5365
5488
|
}
|
|
5366
5489
|
const d = db || getDatabase();
|
|
@@ -5393,14 +5516,14 @@ function touchMemory(id, db) {
|
|
|
5393
5516
|
if (!db && isApiMode())
|
|
5394
5517
|
return;
|
|
5395
5518
|
const d = db || getDatabase();
|
|
5396
|
-
d.run("UPDATE memories SET access_count = access_count + 1, accessed_at = ? WHERE id = ?", [
|
|
5519
|
+
d.run("UPDATE memories SET access_count = access_count + 1, accessed_at = ? WHERE id = ?", [now(), id]);
|
|
5397
5520
|
}
|
|
5398
5521
|
function incrementRecallCount(id, db) {
|
|
5399
5522
|
if (!db && isApiMode())
|
|
5400
5523
|
return;
|
|
5401
5524
|
const d = db || getDatabase();
|
|
5402
5525
|
try {
|
|
5403
|
-
d.run("UPDATE memories SET recall_count = recall_count + 1, access_count = access_count + 1, accessed_at = ? WHERE id = ?", [
|
|
5526
|
+
d.run("UPDATE memories SET recall_count = recall_count + 1, access_count = access_count + 1, accessed_at = ? WHERE id = ?", [now(), id]);
|
|
5404
5527
|
const row = d.query("SELECT recall_count, importance FROM memories WHERE id = ?").get(id);
|
|
5405
5528
|
if (!row)
|
|
5406
5529
|
return;
|
|
@@ -5417,7 +5540,7 @@ function cleanExpiredMemories(db) {
|
|
|
5417
5540
|
return data?.cleaned ?? 0;
|
|
5418
5541
|
}
|
|
5419
5542
|
const d = db || getDatabase();
|
|
5420
|
-
const timestamp =
|
|
5543
|
+
const timestamp = now();
|
|
5421
5544
|
const countRow = d.query("SELECT COUNT(*) as c FROM memories WHERE expires_at IS NOT NULL AND expires_at < ?").get(timestamp);
|
|
5422
5545
|
const count = countRow.c;
|
|
5423
5546
|
if (count > 0) {
|
|
@@ -5512,6 +5635,7 @@ var init_memories = __esm(() => {
|
|
|
5512
5635
|
init_types();
|
|
5513
5636
|
init_database();
|
|
5514
5637
|
init_redact();
|
|
5638
|
+
init_enum_validation();
|
|
5515
5639
|
init_hooks();
|
|
5516
5640
|
init_poisoning();
|
|
5517
5641
|
init_entity_memories();
|
|
@@ -5547,7 +5671,7 @@ function registerProject(name, path, description, memoryPrefix, db) {
|
|
|
5547
5671
|
return data;
|
|
5548
5672
|
}
|
|
5549
5673
|
const d = db || getDatabase();
|
|
5550
|
-
const timestamp =
|
|
5674
|
+
const timestamp = now();
|
|
5551
5675
|
const existing = d.query("SELECT * FROM projects WHERE path = ?").get(path);
|
|
5552
5676
|
if (existing) {
|
|
5553
5677
|
const existingId = existing["id"];
|
|
@@ -5563,7 +5687,7 @@ function registerProject(name, path, description, memoryPrefix, db) {
|
|
|
5563
5687
|
}
|
|
5564
5688
|
function getProject(idOrPath, db) {
|
|
5565
5689
|
if (!db && isApiMode()) {
|
|
5566
|
-
const { status, data } = apiJson("GET", `/projects/${encodeURIComponent(idOrPath)}
|
|
5690
|
+
const { status, data } = apiJson("GET", `/projects/${encodeURIComponent(idOrPath)}`, undefined, { allow404: true });
|
|
5567
5691
|
if (status === 404 || !data)
|
|
5568
5692
|
return null;
|
|
5569
5693
|
return data;
|
|
@@ -5613,7 +5737,7 @@ function createEntity(input, db) {
|
|
|
5613
5737
|
return data;
|
|
5614
5738
|
}
|
|
5615
5739
|
const d = db || getDatabase();
|
|
5616
|
-
const timestamp =
|
|
5740
|
+
const timestamp = now();
|
|
5617
5741
|
const metadataJson = JSON.stringify(input.metadata || {});
|
|
5618
5742
|
const existing = d.query(`SELECT * FROM entities
|
|
5619
5743
|
WHERE name = ? AND type = ? AND COALESCE(project_id, '') = ?`).get(input.name, input.type, input.project_id || "");
|
|
@@ -5656,7 +5780,7 @@ function createEntity(input, db) {
|
|
|
5656
5780
|
}
|
|
5657
5781
|
function getEntity(id, db) {
|
|
5658
5782
|
if (!db && isApiMode()) {
|
|
5659
|
-
const { status, data } = apiJson("GET", `/entities/${encodeURIComponent(id)}
|
|
5783
|
+
const { status, data } = apiJson("GET", `/entities/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
5660
5784
|
if (status === 404 || !data)
|
|
5661
5785
|
throw new EntityNotFoundError(id);
|
|
5662
5786
|
return data;
|
|
@@ -5739,7 +5863,7 @@ function listEntities(filter = {}, db) {
|
|
|
5739
5863
|
}
|
|
5740
5864
|
function deleteEntity(id, db) {
|
|
5741
5865
|
if (!db && isApiMode()) {
|
|
5742
|
-
const { status } = apiJson("DELETE", `/entities/${encodeURIComponent(id)}
|
|
5866
|
+
const { status } = apiJson("DELETE", `/entities/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
5743
5867
|
if (status === 404)
|
|
5744
5868
|
throw new EntityNotFoundError(id);
|
|
5745
5869
|
return;
|
|
@@ -5789,7 +5913,7 @@ function mergeEntities(sourceId, targetId, db) {
|
|
|
5789
5913
|
d.run(`UPDATE entity_memories SET entity_id = ? WHERE entity_id = ?`, [tgt, src]);
|
|
5790
5914
|
d.run("DELETE FROM entity_memories WHERE entity_id = ?", [src]);
|
|
5791
5915
|
d.run("DELETE FROM entities WHERE id = ?", [src]);
|
|
5792
|
-
d.run("UPDATE entities SET updated_at = ? WHERE id = ?", [
|
|
5916
|
+
d.run("UPDATE entities SET updated_at = ? WHERE id = ?", [now(), tgt]);
|
|
5793
5917
|
return getEntity(tgt, d);
|
|
5794
5918
|
}
|
|
5795
5919
|
var init_entities = __esm(() => {
|
|
@@ -6454,7 +6578,7 @@ function registerAgent(name, sessionId, description, role, projectId, db) {
|
|
|
6454
6578
|
return data;
|
|
6455
6579
|
}
|
|
6456
6580
|
const d = db || getDatabase();
|
|
6457
|
-
const timestamp =
|
|
6581
|
+
const timestamp = now();
|
|
6458
6582
|
const normalizedName = name.trim().toLowerCase();
|
|
6459
6583
|
if (projectId) {
|
|
6460
6584
|
const resolvedProjectId = resolvePartialId(d, "projects", projectId);
|
|
@@ -6503,7 +6627,7 @@ function registerAgent(name, sessionId, description, role, projectId, db) {
|
|
|
6503
6627
|
}
|
|
6504
6628
|
function getAgent(idOrName, db) {
|
|
6505
6629
|
if (!db && isApiMode()) {
|
|
6506
|
-
const { status, data } = apiJson("GET", `/agents/${encodeURIComponent(idOrName)}
|
|
6630
|
+
const { status, data } = apiJson("GET", `/agents/${encodeURIComponent(idOrName)}`, undefined, { allow404: true });
|
|
6507
6631
|
if (status === 404 || !data)
|
|
6508
6632
|
return null;
|
|
6509
6633
|
return data;
|
|
@@ -6541,7 +6665,7 @@ function touchAgent(idOrName, db) {
|
|
|
6541
6665
|
const agent = getAgent(idOrName, d);
|
|
6542
6666
|
if (!agent)
|
|
6543
6667
|
return;
|
|
6544
|
-
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [
|
|
6668
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
|
|
6545
6669
|
}
|
|
6546
6670
|
function listAgentsByProject(projectId, db) {
|
|
6547
6671
|
if (!db && isApiMode()) {
|
|
@@ -6556,7 +6680,7 @@ function listAgentsByProject(projectId, db) {
|
|
|
6556
6680
|
}
|
|
6557
6681
|
function updateAgent(id, updates, db) {
|
|
6558
6682
|
if (!db && isApiMode()) {
|
|
6559
|
-
const { status, data } = apiJson("PATCH", `/agents/${encodeURIComponent(id)}`, updates);
|
|
6683
|
+
const { status, data } = apiJson("PATCH", `/agents/${encodeURIComponent(id)}`, updates, { allow404: true });
|
|
6560
6684
|
if (status === 404 || !data)
|
|
6561
6685
|
return null;
|
|
6562
6686
|
return data;
|
|
@@ -6565,7 +6689,7 @@ function updateAgent(id, updates, db) {
|
|
|
6565
6689
|
const agent = getAgent(id, d);
|
|
6566
6690
|
if (!agent)
|
|
6567
6691
|
return null;
|
|
6568
|
-
const timestamp =
|
|
6692
|
+
const timestamp = now();
|
|
6569
6693
|
if (updates.name) {
|
|
6570
6694
|
const normalizedNewName = updates.name.trim().toLowerCase();
|
|
6571
6695
|
if (normalizedNewName !== agent.name) {
|
|
@@ -7402,7 +7526,7 @@ function createRelation(input, db) {
|
|
|
7402
7526
|
}
|
|
7403
7527
|
const d = db || getDatabase();
|
|
7404
7528
|
const id = shortUuid();
|
|
7405
|
-
const timestamp =
|
|
7529
|
+
const timestamp = now();
|
|
7406
7530
|
const weight = input.weight ?? 1;
|
|
7407
7531
|
const metadata = JSON.stringify(input.metadata ?? {});
|
|
7408
7532
|
d.run(`INSERT INTO relations (id, source_entity_id, target_entity_id, relation_type, weight, metadata, created_at)
|
|
@@ -7455,7 +7579,7 @@ function listRelations(filter, db) {
|
|
|
7455
7579
|
}
|
|
7456
7580
|
function deleteRelation(id, db) {
|
|
7457
7581
|
if (!db && isApiMode()) {
|
|
7458
|
-
const { status } = apiJson("DELETE", `/relations/${encodeURIComponent(id)}
|
|
7582
|
+
const { status } = apiJson("DELETE", `/relations/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
7459
7583
|
if (status === 404)
|
|
7460
7584
|
throw new Error(`Relation not found: ${id}`);
|
|
7461
7585
|
return;
|
|
@@ -8367,7 +8491,7 @@ function createWebhookHook(input, db) {
|
|
|
8367
8491
|
}
|
|
8368
8492
|
const d = db || getDatabase();
|
|
8369
8493
|
const id = shortUuid();
|
|
8370
|
-
const timestamp =
|
|
8494
|
+
const timestamp = now();
|
|
8371
8495
|
d.run(`INSERT INTO webhook_hooks
|
|
8372
8496
|
(id, type, handler_url, priority, blocking, agent_id, project_id, description, enabled, created_at, invocation_count, failure_count)
|
|
8373
8497
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, 0, 0)`, [
|
|
@@ -8385,7 +8509,7 @@ function createWebhookHook(input, db) {
|
|
|
8385
8509
|
}
|
|
8386
8510
|
function getWebhookHook(id, db) {
|
|
8387
8511
|
if (!db && isApiMode()) {
|
|
8388
|
-
const { status, data } = apiJson("GET", `/webhooks/${encodeURIComponent(id)}
|
|
8512
|
+
const { status, data } = apiJson("GET", `/webhooks/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
8389
8513
|
if (status === 404 || !data)
|
|
8390
8514
|
return null;
|
|
8391
8515
|
return data;
|
|
@@ -8421,7 +8545,7 @@ function updateWebhookHook(id, updates, db) {
|
|
|
8421
8545
|
enabled: updates.enabled,
|
|
8422
8546
|
priority: updates.priority,
|
|
8423
8547
|
description: updates.description
|
|
8424
|
-
});
|
|
8548
|
+
}, { allow404: true });
|
|
8425
8549
|
if (status === 404 || !data)
|
|
8426
8550
|
return null;
|
|
8427
8551
|
return data;
|
|
@@ -8452,7 +8576,7 @@ function updateWebhookHook(id, updates, db) {
|
|
|
8452
8576
|
}
|
|
8453
8577
|
function deleteWebhookHook(id, db) {
|
|
8454
8578
|
if (!db && isApiMode()) {
|
|
8455
|
-
const { status } = apiJson("DELETE", `/webhooks/${encodeURIComponent(id)}
|
|
8579
|
+
const { status } = apiJson("DELETE", `/webhooks/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
8456
8580
|
return status === 204 || status === 200;
|
|
8457
8581
|
}
|
|
8458
8582
|
const d = db || getDatabase();
|
|
@@ -8550,7 +8674,7 @@ function parseEventRow(row) {
|
|
|
8550
8674
|
function createSynthesisRun(input, db) {
|
|
8551
8675
|
const d = db || getDatabase();
|
|
8552
8676
|
const id = shortUuid();
|
|
8553
|
-
const timestamp =
|
|
8677
|
+
const timestamp = now();
|
|
8554
8678
|
d.run(`INSERT INTO synthesis_runs (id, triggered_by, project_id, agent_id, corpus_size, proposals_generated, proposals_accepted, proposals_rejected, status, started_at)
|
|
8555
8679
|
VALUES (?, ?, ?, ?, ?, 0, 0, 0, 'pending', ?)`, [
|
|
8556
8680
|
id,
|
|
@@ -8651,7 +8775,7 @@ function updateSynthesisRun(id, updates, db) {
|
|
|
8651
8775
|
function createProposal(input, db) {
|
|
8652
8776
|
const d = db || getDatabase();
|
|
8653
8777
|
const id = shortUuid();
|
|
8654
|
-
const timestamp =
|
|
8778
|
+
const timestamp = now();
|
|
8655
8779
|
d.run(`INSERT INTO synthesis_proposals (id, run_id, proposal_type, memory_ids, target_memory_id, proposed_changes, reasoning, confidence, status, created_at)
|
|
8656
8780
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?)`, [
|
|
8657
8781
|
id,
|
|
@@ -8710,7 +8834,7 @@ function updateProposal(id, updates, db) {
|
|
|
8710
8834
|
function createMetric(input, db) {
|
|
8711
8835
|
const d = db || getDatabase();
|
|
8712
8836
|
const id = shortUuid();
|
|
8713
|
-
const timestamp =
|
|
8837
|
+
const timestamp = now();
|
|
8714
8838
|
d.run(`INSERT INTO synthesis_metrics (id, run_id, metric_type, value, baseline, created_at)
|
|
8715
8839
|
VALUES (?, ?, ?, ?, ?, ?)`, [id, input.run_id, input.metric_type, input.value, input.baseline ?? null, timestamp]);
|
|
8716
8840
|
return { id, run_id: input.run_id, metric_type: input.metric_type, value: input.value, baseline: input.baseline ?? null, created_at: timestamp };
|
|
@@ -8724,7 +8848,7 @@ function recordSynthesisEvent(input, db) {
|
|
|
8724
8848
|
try {
|
|
8725
8849
|
const d = db || getDatabase();
|
|
8726
8850
|
const id = shortUuid();
|
|
8727
|
-
const timestamp =
|
|
8851
|
+
const timestamp = now();
|
|
8728
8852
|
d.run(`INSERT INTO synthesis_events (id, event_type, memory_id, agent_id, project_id, session_id, query, importance_at_time, metadata, created_at)
|
|
8729
8853
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
8730
8854
|
id,
|
|
@@ -8978,7 +9102,7 @@ __export(exports_contradiction, {
|
|
|
8978
9102
|
});
|
|
8979
9103
|
function invalidateFact(oldMemoryId, newMemoryId, db) {
|
|
8980
9104
|
const d = db || getDatabase();
|
|
8981
|
-
const timestamp =
|
|
9105
|
+
const timestamp = now();
|
|
8982
9106
|
d.run("UPDATE memories SET valid_until = ?, updated_at = ? WHERE id = ?", [timestamp, timestamp, oldMemoryId]);
|
|
8983
9107
|
if (newMemoryId) {
|
|
8984
9108
|
const row = d.query("SELECT metadata FROM memories WHERE id = ?").get(newMemoryId);
|
|
@@ -9456,7 +9580,7 @@ async function buildCorpus(options) {
|
|
|
9456
9580
|
duplicateCandidates,
|
|
9457
9581
|
lowImportanceHighRecall,
|
|
9458
9582
|
highImportanceLowRecall,
|
|
9459
|
-
generatedAt:
|
|
9583
|
+
generatedAt: now()
|
|
9460
9584
|
};
|
|
9461
9585
|
}
|
|
9462
9586
|
var init_corpus_builder = __esm(() => {
|
|
@@ -9789,7 +9913,7 @@ async function executeProposals(runId, proposals, db) {
|
|
|
9789
9913
|
const rollback = executeProposal(proposal, d);
|
|
9790
9914
|
updateProposal(proposal.id, {
|
|
9791
9915
|
status: "accepted",
|
|
9792
|
-
executed_at:
|
|
9916
|
+
executed_at: now(),
|
|
9793
9917
|
rollback_data: rollback
|
|
9794
9918
|
}, d);
|
|
9795
9919
|
rollbackData[proposal.id] = rollback;
|
|
@@ -9838,7 +9962,7 @@ function executeArchive(proposal, d) {
|
|
|
9838
9962
|
if (!mem)
|
|
9839
9963
|
continue;
|
|
9840
9964
|
rollback[memId] = mem.status;
|
|
9841
|
-
d.run("UPDATE memories SET status = 'archived', updated_at = ? WHERE id = ?", [
|
|
9965
|
+
d.run("UPDATE memories SET status = 'archived', updated_at = ? WHERE id = ?", [now(), memId]);
|
|
9842
9966
|
}
|
|
9843
9967
|
return { old_status: rollback };
|
|
9844
9968
|
}
|
|
@@ -9853,7 +9977,7 @@ function executePromote(proposal, d) {
|
|
|
9853
9977
|
if (!mem)
|
|
9854
9978
|
continue;
|
|
9855
9979
|
rollback[memId] = mem.importance;
|
|
9856
|
-
d.run("UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?", [Math.max(1, Math.min(10, Math.round(newImportance))),
|
|
9980
|
+
d.run("UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?", [Math.max(1, Math.min(10, Math.round(newImportance))), now(), memId]);
|
|
9857
9981
|
}
|
|
9858
9982
|
return { old_importance: rollback };
|
|
9859
9983
|
}
|
|
@@ -9870,7 +9994,7 @@ function executeUpdateValue(proposal, d) {
|
|
|
9870
9994
|
if (!mem)
|
|
9871
9995
|
throw new Error(`Memory ${memId} not found`);
|
|
9872
9996
|
rollback[memId] = { value: mem.value, version: mem.version };
|
|
9873
|
-
d.run("UPDATE memories SET value = ?, version = version + 1, updated_at = ? WHERE id = ?", [newValue,
|
|
9997
|
+
d.run("UPDATE memories SET value = ?, version = version + 1, updated_at = ? WHERE id = ?", [newValue, now(), memId]);
|
|
9874
9998
|
return { old_state: rollback };
|
|
9875
9999
|
}
|
|
9876
10000
|
function executeAddTag(proposal, d) {
|
|
@@ -9885,7 +10009,7 @@ function executeAddTag(proposal, d) {
|
|
|
9885
10009
|
continue;
|
|
9886
10010
|
rollback[memId] = [...mem.tags];
|
|
9887
10011
|
const newTags = Array.from(new Set([...mem.tags, ...tagsToAdd]));
|
|
9888
|
-
d.run("UPDATE memories SET tags = ?, updated_at = ? WHERE id = ?", [JSON.stringify(newTags),
|
|
10012
|
+
d.run("UPDATE memories SET tags = ?, updated_at = ? WHERE id = ?", [JSON.stringify(newTags), now(), memId]);
|
|
9889
10013
|
const insertTag = d.prepare("INSERT OR IGNORE INTO memory_tags (memory_id, tag) VALUES (?, ?)");
|
|
9890
10014
|
for (const tag of tagsToAdd) {
|
|
9891
10015
|
insertTag.run(memId, tag);
|
|
@@ -9918,11 +10042,11 @@ function executeMerge(proposal, d) {
|
|
|
9918
10042
|
const mergedValue = proposal.proposed_changes["merged_value"] ?? [target.value, ...sourceValues].join(`
|
|
9919
10043
|
---
|
|
9920
10044
|
`);
|
|
9921
|
-
d.run("UPDATE memories SET value = ?, version = version + 1, updated_at = ? WHERE id = ?", [mergedValue,
|
|
10045
|
+
d.run("UPDATE memories SET value = ?, version = version + 1, updated_at = ? WHERE id = ?", [mergedValue, now(), targetId]);
|
|
9922
10046
|
for (const memId of proposal.memory_ids) {
|
|
9923
10047
|
if (memId === targetId)
|
|
9924
10048
|
continue;
|
|
9925
|
-
d.run("UPDATE memories SET status = 'archived', updated_at = ? WHERE id = ?", [
|
|
10049
|
+
d.run("UPDATE memories SET status = 'archived', updated_at = ? WHERE id = ?", [now(), memId]);
|
|
9926
10050
|
}
|
|
9927
10051
|
return rollback;
|
|
9928
10052
|
}
|
|
@@ -9938,7 +10062,7 @@ function executeRemoveDuplicate(proposal, d) {
|
|
|
9938
10062
|
if (mem.id === keepId)
|
|
9939
10063
|
continue;
|
|
9940
10064
|
rollback[mem.id] = mem.status;
|
|
9941
|
-
d.run("UPDATE memories SET status = 'archived', updated_at = ? WHERE id = ?", [
|
|
10065
|
+
d.run("UPDATE memories SET status = 'archived', updated_at = ? WHERE id = ?", [now(), mem.id]);
|
|
9942
10066
|
}
|
|
9943
10067
|
return { old_status: rollback, kept_id: keepId };
|
|
9944
10068
|
}
|
|
@@ -9970,7 +10094,7 @@ function rollbackProposal(proposal, d) {
|
|
|
9970
10094
|
if (!oldStatus)
|
|
9971
10095
|
break;
|
|
9972
10096
|
for (const [memId, status] of Object.entries(oldStatus)) {
|
|
9973
|
-
d.run("UPDATE memories SET status = ?, updated_at = ? WHERE id = ?", [status,
|
|
10097
|
+
d.run("UPDATE memories SET status = ?, updated_at = ? WHERE id = ?", [status, now(), memId]);
|
|
9974
10098
|
}
|
|
9975
10099
|
break;
|
|
9976
10100
|
}
|
|
@@ -9979,7 +10103,7 @@ function rollbackProposal(proposal, d) {
|
|
|
9979
10103
|
if (!oldImportance)
|
|
9980
10104
|
break;
|
|
9981
10105
|
for (const [memId, importance] of Object.entries(oldImportance)) {
|
|
9982
|
-
d.run("UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?", [importance,
|
|
10106
|
+
d.run("UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?", [importance, now(), memId]);
|
|
9983
10107
|
}
|
|
9984
10108
|
break;
|
|
9985
10109
|
}
|
|
@@ -9988,7 +10112,7 @@ function rollbackProposal(proposal, d) {
|
|
|
9988
10112
|
if (!oldState)
|
|
9989
10113
|
break;
|
|
9990
10114
|
for (const [memId, state] of Object.entries(oldState)) {
|
|
9991
|
-
d.run("UPDATE memories SET value = ?, version = ?, updated_at = ? WHERE id = ?", [state.value, state.version,
|
|
10115
|
+
d.run("UPDATE memories SET value = ?, version = ?, updated_at = ? WHERE id = ?", [state.value, state.version, now(), memId]);
|
|
9992
10116
|
}
|
|
9993
10117
|
break;
|
|
9994
10118
|
}
|
|
@@ -9997,7 +10121,7 @@ function rollbackProposal(proposal, d) {
|
|
|
9997
10121
|
if (!oldTags)
|
|
9998
10122
|
break;
|
|
9999
10123
|
for (const [memId, tags] of Object.entries(oldTags)) {
|
|
10000
|
-
d.run("UPDATE memories SET tags = ?, updated_at = ? WHERE id = ?", [JSON.stringify(tags),
|
|
10124
|
+
d.run("UPDATE memories SET tags = ?, updated_at = ? WHERE id = ?", [JSON.stringify(tags), now(), memId]);
|
|
10001
10125
|
d.run("DELETE FROM memory_tags WHERE memory_id = ?", [memId]);
|
|
10002
10126
|
const insertTag = d.prepare("INSERT OR IGNORE INTO memory_tags (memory_id, tag) VALUES (?, ?)");
|
|
10003
10127
|
for (const tag of tags) {
|
|
@@ -10012,11 +10136,11 @@ function rollbackProposal(proposal, d) {
|
|
|
10012
10136
|
const archivedMemories = rb["archived_memories"];
|
|
10013
10137
|
const targetId = proposal.target_memory_id;
|
|
10014
10138
|
if (targetId && targetOldValue !== undefined && targetOldVersion !== undefined) {
|
|
10015
|
-
d.run("UPDATE memories SET value = ?, version = ?, updated_at = ? WHERE id = ?", [targetOldValue, targetOldVersion,
|
|
10139
|
+
d.run("UPDATE memories SET value = ?, version = ?, updated_at = ? WHERE id = ?", [targetOldValue, targetOldVersion, now(), targetId]);
|
|
10016
10140
|
}
|
|
10017
10141
|
if (archivedMemories) {
|
|
10018
10142
|
for (const [memId, status] of Object.entries(archivedMemories)) {
|
|
10019
|
-
d.run("UPDATE memories SET status = ?, updated_at = ? WHERE id = ?", [status,
|
|
10143
|
+
d.run("UPDATE memories SET status = ?, updated_at = ? WHERE id = ?", [status, now(), memId]);
|
|
10020
10144
|
}
|
|
10021
10145
|
}
|
|
10022
10146
|
break;
|
|
@@ -10150,12 +10274,12 @@ async function runSynthesis(options = {}) {
|
|
|
10150
10274
|
proposals_accepted: execResult.executed,
|
|
10151
10275
|
proposals_rejected: validation.rejectedProposals.length + execResult.failed,
|
|
10152
10276
|
status: "completed",
|
|
10153
|
-
completed_at:
|
|
10277
|
+
completed_at: now()
|
|
10154
10278
|
}, d);
|
|
10155
10279
|
} else {
|
|
10156
10280
|
updateSynthesisRun(run.id, {
|
|
10157
10281
|
status: "completed",
|
|
10158
|
-
completed_at:
|
|
10282
|
+
completed_at: now()
|
|
10159
10283
|
}, d);
|
|
10160
10284
|
}
|
|
10161
10285
|
let effectivenessReport = null;
|
|
@@ -10177,7 +10301,7 @@ async function runSynthesis(options = {}) {
|
|
|
10177
10301
|
updateSynthesisRun(run.id, {
|
|
10178
10302
|
status: "failed",
|
|
10179
10303
|
error: err instanceof Error ? err.message : String(err),
|
|
10180
|
-
completed_at:
|
|
10304
|
+
completed_at: now()
|
|
10181
10305
|
}, d);
|
|
10182
10306
|
const failedRun = listSynthesisRuns({ project_id: projectId, limit: 1 }, d)[0] ?? run;
|
|
10183
10307
|
return {
|
|
@@ -10193,7 +10317,7 @@ async function rollbackSynthesis(runId, db) {
|
|
|
10193
10317
|
const d = db || getDatabase();
|
|
10194
10318
|
const result = await rollbackRun(runId, d);
|
|
10195
10319
|
if (result.errors.length === 0) {
|
|
10196
|
-
updateSynthesisRun(runId, { status: "rolled_back", completed_at:
|
|
10320
|
+
updateSynthesisRun(runId, { status: "rolled_back", completed_at: now() }, d);
|
|
10197
10321
|
}
|
|
10198
10322
|
return result;
|
|
10199
10323
|
}
|
|
@@ -10259,7 +10383,7 @@ function parseJobRow(row) {
|
|
|
10259
10383
|
function createSessionJob(input, db) {
|
|
10260
10384
|
const d = db || getDatabase();
|
|
10261
10385
|
const id = uuid();
|
|
10262
|
-
const timestamp =
|
|
10386
|
+
const timestamp = now();
|
|
10263
10387
|
const source = input.source ?? "manual";
|
|
10264
10388
|
const metadata = JSON.stringify(input.metadata ?? {});
|
|
10265
10389
|
d.run(`INSERT INTO session_memory_jobs
|
|
@@ -10278,7 +10402,7 @@ function createSessionJob(input, db) {
|
|
|
10278
10402
|
}
|
|
10279
10403
|
function getSessionJob(id, db) {
|
|
10280
10404
|
if (!db && isApiMode()) {
|
|
10281
|
-
const { status, data } = apiJson("GET", `/sessions/jobs/${encodeURIComponent(id)}
|
|
10405
|
+
const { status, data } = apiJson("GET", `/sessions/jobs/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
10282
10406
|
if (status === 404 || !data)
|
|
10283
10407
|
return null;
|
|
10284
10408
|
return data;
|
|
@@ -10408,7 +10532,7 @@ function saveToolEvent(input, db) {
|
|
|
10408
10532
|
}
|
|
10409
10533
|
const d = db || getDatabase();
|
|
10410
10534
|
const id = uuid();
|
|
10411
|
-
const timestamp =
|
|
10535
|
+
const timestamp = now();
|
|
10412
10536
|
const metadataJson = JSON.stringify(input.metadata || {});
|
|
10413
10537
|
d.run(`INSERT INTO tool_events (id, tool_name, action, success, error_type, error_message, tokens_used, latency_ms, context, lesson, when_to_use, agent_id, project_id, session_id, metadata, created_at)
|
|
10414
10538
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
@@ -58701,15 +58825,108 @@ var {
|
|
|
58701
58825
|
Help
|
|
58702
58826
|
} = import__.default;
|
|
58703
58827
|
|
|
58828
|
+
// src/cli/index.tsx
|
|
58829
|
+
init_database();
|
|
58830
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
58831
|
+
import { dirname as dirname7, join as join12 } from "path";
|
|
58832
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
58833
|
+
|
|
58834
|
+
// src/db/machines.ts
|
|
58835
|
+
init_database();
|
|
58836
|
+
import { hostname, platform } from "os";
|
|
58837
|
+
function parseMachine(row) {
|
|
58838
|
+
if (!row)
|
|
58839
|
+
return null;
|
|
58840
|
+
return {
|
|
58841
|
+
...row,
|
|
58842
|
+
is_primary: Boolean(row.is_primary)
|
|
58843
|
+
};
|
|
58844
|
+
}
|
|
58845
|
+
function normalizeHostname(host) {
|
|
58846
|
+
return host.replace(/\.(local|lan|home|internal)$/i, "");
|
|
58847
|
+
}
|
|
58848
|
+
function registerMachine(name, db = getDatabase()) {
|
|
58849
|
+
const rawHost = hostname();
|
|
58850
|
+
const host = normalizeHostname(rawHost);
|
|
58851
|
+
const plat = platform();
|
|
58852
|
+
const machineName = name?.trim() || host;
|
|
58853
|
+
const existing = parseMachine(db.query("SELECT * FROM machines WHERE hostname = ?").get(host));
|
|
58854
|
+
if (existing) {
|
|
58855
|
+
db.run("UPDATE machines SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
|
|
58856
|
+
return parseMachine(db.query("SELECT * FROM machines WHERE id = ?").get(existing.id));
|
|
58857
|
+
}
|
|
58858
|
+
let finalName = machineName;
|
|
58859
|
+
let suffix = 2;
|
|
58860
|
+
while (db.query("SELECT id FROM machines WHERE name = ?").get(finalName)) {
|
|
58861
|
+
finalName = `${machineName}-${suffix++}`;
|
|
58862
|
+
}
|
|
58863
|
+
const id = uuid();
|
|
58864
|
+
db.run("INSERT INTO machines (id, name, hostname, platform) VALUES (?, ?, ?, ?)", [id, finalName, host, plat]);
|
|
58865
|
+
return parseMachine(db.query("SELECT * FROM machines WHERE id = ?").get(id));
|
|
58866
|
+
}
|
|
58867
|
+
function getPrimaryMachine(db = getDatabase()) {
|
|
58868
|
+
return parseMachine(db.query("SELECT * FROM machines WHERE is_primary = 1 LIMIT 1").get());
|
|
58869
|
+
}
|
|
58870
|
+
function getPrimaryMachineCandidate(db = getDatabase()) {
|
|
58871
|
+
if (getPrimaryMachine(db))
|
|
58872
|
+
return null;
|
|
58873
|
+
return parseMachine(db.query("SELECT * FROM machines ORDER BY created_at ASC, id ASC LIMIT 1").get());
|
|
58874
|
+
}
|
|
58875
|
+
function getPrimaryMachineStartupWarning(db = getDatabase()) {
|
|
58876
|
+
if (getPrimaryMachine(db))
|
|
58877
|
+
return null;
|
|
58878
|
+
const candidate = getPrimaryMachineCandidate(db);
|
|
58879
|
+
if (!candidate) {
|
|
58880
|
+
return "No primary machine configured. Fallback sync target is unset because no machines are registered yet.";
|
|
58881
|
+
}
|
|
58882
|
+
return `No primary machine configured. Fallback sync target is unset. Candidate: ${candidate.name} (${candidate.id.slice(0, 8)} / ${candidate.hostname}). Confirm it with set_primary_machine.`;
|
|
58883
|
+
}
|
|
58884
|
+
function touchMachine(id, db = getDatabase()) {
|
|
58885
|
+
db.run("UPDATE machines SET last_seen_at = ? WHERE id = ?", [now(), id]);
|
|
58886
|
+
}
|
|
58887
|
+
function getCurrentMachineId(db = getDatabase()) {
|
|
58888
|
+
const host = normalizeHostname(hostname());
|
|
58889
|
+
const m = db.query("SELECT id FROM machines WHERE hostname = ?").get(host);
|
|
58890
|
+
if (m) {
|
|
58891
|
+
touchMachine(m.id, db);
|
|
58892
|
+
return m.id;
|
|
58893
|
+
}
|
|
58894
|
+
return registerMachine(undefined, db).id;
|
|
58895
|
+
}
|
|
58896
|
+
|
|
58897
|
+
// src/cli/startup-side-effects.ts
|
|
58898
|
+
var NO_STARTUP_DB_ACCESS = new WeakSet;
|
|
58899
|
+
function withoutStartupDbAccess(command) {
|
|
58900
|
+
NO_STARTUP_DB_ACCESS.add(command);
|
|
58901
|
+
return command;
|
|
58902
|
+
}
|
|
58903
|
+
function skipsStartupDbAccess(command) {
|
|
58904
|
+
return command !== undefined && NO_STARTUP_DB_ACCESS.has(command);
|
|
58905
|
+
}
|
|
58906
|
+
|
|
58907
|
+
// src/cli/global-options.ts
|
|
58908
|
+
var GLOBAL_OPTIONS = [
|
|
58909
|
+
["-p, --project <path>", "Project path for scoping"],
|
|
58910
|
+
["-j, --json", "Output as JSON"],
|
|
58911
|
+
["-f, --format <fmt>", "Output format: compact, json, csv, yaml"],
|
|
58912
|
+
["-a, --agent <name>", "Agent name or ID"],
|
|
58913
|
+
["-s, --session <id>", "Session ID"]
|
|
58914
|
+
];
|
|
58915
|
+
function applyGlobalOptions(program2) {
|
|
58916
|
+
for (const [flags, description] of GLOBAL_OPTIONS)
|
|
58917
|
+
program2.option(flags, description);
|
|
58918
|
+
return program2;
|
|
58919
|
+
}
|
|
58920
|
+
|
|
58704
58921
|
// node_modules/@hasna/events/dist/commander.js
|
|
58705
58922
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
58706
|
-
import { existsSync } from "fs";
|
|
58707
|
-
import { homedir } from "os";
|
|
58708
|
-
import { join } from "path";
|
|
58923
|
+
import { existsSync as existsSync3 } from "fs";
|
|
58924
|
+
import { homedir as homedir2 } from "os";
|
|
58925
|
+
import { join as join4 } from "path";
|
|
58709
58926
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
58710
|
-
import { randomUUID } from "crypto";
|
|
58711
|
-
import { spawn } from "child_process";
|
|
58712
58927
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
58928
|
+
import { spawn } from "child_process";
|
|
58929
|
+
import { randomUUID as randomUUID22 } from "crypto";
|
|
58713
58930
|
function getPathValue(input, path) {
|
|
58714
58931
|
return path.split(".").reduce((value, part) => {
|
|
58715
58932
|
if (value && typeof value === "object" && part in value) {
|
|
@@ -58754,7 +58971,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
58754
58971
|
var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR";
|
|
58755
58972
|
var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
|
|
58756
58973
|
function getEventsDataDir(override) {
|
|
58757
|
-
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] ||
|
|
58974
|
+
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(homedir2(), ".hasna", "events");
|
|
58758
58975
|
}
|
|
58759
58976
|
|
|
58760
58977
|
class JsonEventsStore {
|
|
@@ -58764,9 +58981,9 @@ class JsonEventsStore {
|
|
|
58764
58981
|
deliveriesPath;
|
|
58765
58982
|
constructor(dataDir = getEventsDataDir()) {
|
|
58766
58983
|
this.dataDir = dataDir;
|
|
58767
|
-
this.channelsPath =
|
|
58768
|
-
this.eventsPath =
|
|
58769
|
-
this.deliveriesPath =
|
|
58984
|
+
this.channelsPath = join4(dataDir, "channels.json");
|
|
58985
|
+
this.eventsPath = join4(dataDir, "events.json");
|
|
58986
|
+
this.deliveriesPath = join4(dataDir, "deliveries.json");
|
|
58770
58987
|
}
|
|
58771
58988
|
async init() {
|
|
58772
58989
|
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
@@ -58838,7 +59055,7 @@ class JsonEventsStore {
|
|
|
58838
59055
|
};
|
|
58839
59056
|
}
|
|
58840
59057
|
async ensureArrayFile(path) {
|
|
58841
|
-
if (!
|
|
59058
|
+
if (!existsSync3(path)) {
|
|
58842
59059
|
await writeFile(path, `[]
|
|
58843
59060
|
`, { encoding: "utf-8", mode: 384 });
|
|
58844
59061
|
}
|
|
@@ -58876,7 +59093,7 @@ function signPayload(secret, timestamp, body) {
|
|
|
58876
59093
|
const digest = createHmac("sha256", secret).update(buildSignatureBase(timestamp, body)).digest("hex");
|
|
58877
59094
|
return `sha256=${digest}`;
|
|
58878
59095
|
}
|
|
58879
|
-
function
|
|
59096
|
+
function now2() {
|
|
58880
59097
|
return new Date().toISOString();
|
|
58881
59098
|
}
|
|
58882
59099
|
function truncate(value, max = 4096) {
|
|
@@ -58903,7 +59120,7 @@ function buildWebhookRequest(event, channel) {
|
|
|
58903
59120
|
async function dispatchWebhook(event, channel, options = {}) {
|
|
58904
59121
|
if (!channel.webhook)
|
|
58905
59122
|
throw new Error(`Channel ${channel.id} has no webhook config`);
|
|
58906
|
-
const startedAt =
|
|
59123
|
+
const startedAt = now2();
|
|
58907
59124
|
const { body, headers } = buildWebhookRequest(event, channel);
|
|
58908
59125
|
const controller = new AbortController;
|
|
58909
59126
|
const timeout = setTimeout(() => controller.abort(), channel.webhook.timeoutMs ?? 15000);
|
|
@@ -58919,7 +59136,7 @@ async function dispatchWebhook(event, channel, options = {}) {
|
|
|
58919
59136
|
attempt: 1,
|
|
58920
59137
|
status: response.ok ? "success" : "failed",
|
|
58921
59138
|
startedAt,
|
|
58922
|
-
completedAt:
|
|
59139
|
+
completedAt: now2(),
|
|
58923
59140
|
responseStatus: response.status,
|
|
58924
59141
|
responseBody,
|
|
58925
59142
|
error: response.ok ? undefined : `Webhook returned HTTP ${response.status}`
|
|
@@ -58929,7 +59146,7 @@ async function dispatchWebhook(event, channel, options = {}) {
|
|
|
58929
59146
|
attempt: 1,
|
|
58930
59147
|
status: "failed",
|
|
58931
59148
|
startedAt,
|
|
58932
|
-
completedAt:
|
|
59149
|
+
completedAt: now2(),
|
|
58933
59150
|
error: error instanceof Error ? error.message : String(error)
|
|
58934
59151
|
};
|
|
58935
59152
|
} finally {
|
|
@@ -58939,7 +59156,7 @@ async function dispatchWebhook(event, channel, options = {}) {
|
|
|
58939
59156
|
async function dispatchCommand(event, channel) {
|
|
58940
59157
|
if (!channel.command)
|
|
58941
59158
|
throw new Error(`Channel ${channel.id} has no command config`);
|
|
58942
|
-
const startedAt =
|
|
59159
|
+
const startedAt = now2();
|
|
58943
59160
|
const eventJson = JSON.stringify(event);
|
|
58944
59161
|
const env = {
|
|
58945
59162
|
...process.env,
|
|
@@ -58955,7 +59172,7 @@ async function dispatchCommand(event, channel) {
|
|
|
58955
59172
|
HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
|
|
58956
59173
|
HASNA_EVENT_JSON: eventJson
|
|
58957
59174
|
};
|
|
58958
|
-
return new Promise((
|
|
59175
|
+
return new Promise((resolve2) => {
|
|
58959
59176
|
const child = spawn(channel.command.command, channel.command.args ?? [], {
|
|
58960
59177
|
cwd: channel.command.cwd,
|
|
58961
59178
|
env,
|
|
@@ -58973,11 +59190,11 @@ async function dispatchCommand(event, channel) {
|
|
|
58973
59190
|
});
|
|
58974
59191
|
child.on("error", (error) => {
|
|
58975
59192
|
clearTimeout(timeout);
|
|
58976
|
-
|
|
59193
|
+
resolve2({
|
|
58977
59194
|
attempt: 1,
|
|
58978
59195
|
status: "failed",
|
|
58979
59196
|
startedAt,
|
|
58980
|
-
completedAt:
|
|
59197
|
+
completedAt: now2(),
|
|
58981
59198
|
stdout: truncate(stdout),
|
|
58982
59199
|
stderr: truncate(stderr),
|
|
58983
59200
|
error: error.message
|
|
@@ -58986,11 +59203,11 @@ async function dispatchCommand(event, channel) {
|
|
|
58986
59203
|
child.on("close", (code, signal) => {
|
|
58987
59204
|
clearTimeout(timeout);
|
|
58988
59205
|
const success = code === 0;
|
|
58989
|
-
|
|
59206
|
+
resolve2({
|
|
58990
59207
|
attempt: 1,
|
|
58991
59208
|
status: success ? "success" : "failed",
|
|
58992
59209
|
startedAt,
|
|
58993
|
-
completedAt:
|
|
59210
|
+
completedAt: now2(),
|
|
58994
59211
|
stdout: truncate(stdout),
|
|
58995
59212
|
stderr: truncate(stderr),
|
|
58996
59213
|
error: success ? undefined : `Command exited with ${signal ? `signal ${signal}` : `code ${code}`}`
|
|
@@ -59006,27 +59223,27 @@ async function dispatchChannel(event, channel, options = {}) {
|
|
|
59006
59223
|
return {
|
|
59007
59224
|
attempt: 1,
|
|
59008
59225
|
status: "skipped",
|
|
59009
|
-
startedAt:
|
|
59010
|
-
completedAt:
|
|
59226
|
+
startedAt: now2(),
|
|
59227
|
+
completedAt: now2(),
|
|
59011
59228
|
error: `Unsupported transport: ${channel.transport}`
|
|
59012
59229
|
};
|
|
59013
59230
|
}
|
|
59014
59231
|
function createDeliveryResult(event, channel, attempts) {
|
|
59015
59232
|
const status = attempts.some((attempt) => attempt.status === "success") ? "success" : attempts.every((attempt) => attempt.status === "skipped") ? "skipped" : "failed";
|
|
59016
59233
|
return {
|
|
59017
|
-
id:
|
|
59234
|
+
id: randomUUID2(),
|
|
59018
59235
|
eventId: event.id,
|
|
59019
59236
|
channelId: channel.id,
|
|
59020
59237
|
transport: channel.transport,
|
|
59021
59238
|
status,
|
|
59022
59239
|
attempts,
|
|
59023
|
-
createdAt: attempts[0]?.startedAt ??
|
|
59024
|
-
completedAt: attempts.at(-1)?.completedAt ??
|
|
59240
|
+
createdAt: attempts[0]?.startedAt ?? now2(),
|
|
59241
|
+
completedAt: attempts.at(-1)?.completedAt ?? now2()
|
|
59025
59242
|
};
|
|
59026
59243
|
}
|
|
59027
59244
|
function createEvent(input) {
|
|
59028
59245
|
return {
|
|
59029
|
-
id: input.id ??
|
|
59246
|
+
id: input.id ?? randomUUID22(),
|
|
59030
59247
|
source: input.source,
|
|
59031
59248
|
type: input.type,
|
|
59032
59249
|
time: normalizeTime(input.time),
|
|
@@ -59385,75 +59602,6 @@ function collectValues(value, previous) {
|
|
|
59385
59602
|
return previous;
|
|
59386
59603
|
}
|
|
59387
59604
|
|
|
59388
|
-
// src/cli/index.tsx
|
|
59389
|
-
init_database();
|
|
59390
|
-
import { readFileSync as readFileSync8 } from "fs";
|
|
59391
|
-
import { dirname as dirname7, join as join12 } from "path";
|
|
59392
|
-
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
59393
|
-
|
|
59394
|
-
// src/db/machines.ts
|
|
59395
|
-
init_database();
|
|
59396
|
-
import { hostname, platform } from "os";
|
|
59397
|
-
function parseMachine(row) {
|
|
59398
|
-
if (!row)
|
|
59399
|
-
return null;
|
|
59400
|
-
return {
|
|
59401
|
-
...row,
|
|
59402
|
-
is_primary: Boolean(row.is_primary)
|
|
59403
|
-
};
|
|
59404
|
-
}
|
|
59405
|
-
function normalizeHostname(host) {
|
|
59406
|
-
return host.replace(/\.(local|lan|home|internal)$/i, "");
|
|
59407
|
-
}
|
|
59408
|
-
function registerMachine(name, db = getDatabase()) {
|
|
59409
|
-
const rawHost = hostname();
|
|
59410
|
-
const host = normalizeHostname(rawHost);
|
|
59411
|
-
const plat = platform();
|
|
59412
|
-
const machineName = name?.trim() || host;
|
|
59413
|
-
const existing = parseMachine(db.query("SELECT * FROM machines WHERE hostname = ?").get(host));
|
|
59414
|
-
if (existing) {
|
|
59415
|
-
db.run("UPDATE machines SET last_seen_at = ? WHERE id = ?", [now2(), existing.id]);
|
|
59416
|
-
return parseMachine(db.query("SELECT * FROM machines WHERE id = ?").get(existing.id));
|
|
59417
|
-
}
|
|
59418
|
-
let finalName = machineName;
|
|
59419
|
-
let suffix = 2;
|
|
59420
|
-
while (db.query("SELECT id FROM machines WHERE name = ?").get(finalName)) {
|
|
59421
|
-
finalName = `${machineName}-${suffix++}`;
|
|
59422
|
-
}
|
|
59423
|
-
const id = uuid();
|
|
59424
|
-
db.run("INSERT INTO machines (id, name, hostname, platform) VALUES (?, ?, ?, ?)", [id, finalName, host, plat]);
|
|
59425
|
-
return parseMachine(db.query("SELECT * FROM machines WHERE id = ?").get(id));
|
|
59426
|
-
}
|
|
59427
|
-
function getPrimaryMachine(db = getDatabase()) {
|
|
59428
|
-
return parseMachine(db.query("SELECT * FROM machines WHERE is_primary = 1 LIMIT 1").get());
|
|
59429
|
-
}
|
|
59430
|
-
function getPrimaryMachineCandidate(db = getDatabase()) {
|
|
59431
|
-
if (getPrimaryMachine(db))
|
|
59432
|
-
return null;
|
|
59433
|
-
return parseMachine(db.query("SELECT * FROM machines ORDER BY created_at ASC, id ASC LIMIT 1").get());
|
|
59434
|
-
}
|
|
59435
|
-
function getPrimaryMachineStartupWarning(db = getDatabase()) {
|
|
59436
|
-
if (getPrimaryMachine(db))
|
|
59437
|
-
return null;
|
|
59438
|
-
const candidate = getPrimaryMachineCandidate(db);
|
|
59439
|
-
if (!candidate) {
|
|
59440
|
-
return "No primary machine configured. Fallback sync target is unset because no machines are registered yet.";
|
|
59441
|
-
}
|
|
59442
|
-
return `No primary machine configured. Fallback sync target is unset. Candidate: ${candidate.name} (${candidate.id.slice(0, 8)} / ${candidate.hostname}). Confirm it with set_primary_machine.`;
|
|
59443
|
-
}
|
|
59444
|
-
function touchMachine(id, db = getDatabase()) {
|
|
59445
|
-
db.run("UPDATE machines SET last_seen_at = ? WHERE id = ?", [now2(), id]);
|
|
59446
|
-
}
|
|
59447
|
-
function getCurrentMachineId(db = getDatabase()) {
|
|
59448
|
-
const host = normalizeHostname(hostname());
|
|
59449
|
-
const m = db.query("SELECT id FROM machines WHERE hostname = ?").get(host);
|
|
59450
|
-
if (m) {
|
|
59451
|
-
touchMachine(m.id, db);
|
|
59452
|
-
return m.id;
|
|
59453
|
-
}
|
|
59454
|
-
return registerMachine(undefined, db).id;
|
|
59455
|
-
}
|
|
59456
|
-
|
|
59457
59605
|
// src/cli/commands/memory.ts
|
|
59458
59606
|
init_helpers();
|
|
59459
59607
|
|
|
@@ -59510,10 +59658,12 @@ var FORMAT_UNITS = [
|
|
|
59510
59658
|
];
|
|
59511
59659
|
|
|
59512
59660
|
// src/cli/commands/memory-cmd-crud.ts
|
|
59661
|
+
init_enum_validation();
|
|
59662
|
+
init_types();
|
|
59513
59663
|
init_helpers();
|
|
59514
59664
|
function registerCrudCommands(program2) {
|
|
59515
59665
|
const handleError = makeHandleError(program2);
|
|
59516
|
-
program2.command("save <key> <value>").description("Save a memory (create or upsert)").option("-c, --category <cat>",
|
|
59666
|
+
program2.command("save <key> <value>").description("Save a memory (create or upsert)").option("-c, --category <cat>", `Category: ${MEMORY_CATEGORIES.join(", ")}`).option("--scope <scope>", `Scope: ${MEMORY_SCOPES.join(", ")}`).option("--importance <n>", "Importance 1-10", parseInt).option("--tags <tags>", "Comma-separated tags").option("--summary <text>", "Brief summary").option("--ttl <duration>", "Time-to-live: 30s, 5m, 2h, 1d, 1w, or milliseconds").option("--source <src>", "Source: user, agent, system, auto, imported").option("--template <name>", "Apply a template: correction, preference, decision, learning").option("--dedupe <mode>", "Conflict handling: merge (default, upsert the matching row), create (fork a new row under the same key), error").action((key, value, opts) => {
|
|
59517
59667
|
try {
|
|
59518
59668
|
const globalOpts = program2.opts();
|
|
59519
59669
|
const templates = {
|
|
@@ -59551,6 +59701,20 @@ function registerCrudCommands(program2) {
|
|
|
59551
59701
|
}
|
|
59552
59702
|
templateDefaults = tpl;
|
|
59553
59703
|
}
|
|
59704
|
+
for (const [flag, value2] of [
|
|
59705
|
+
["category", opts.category],
|
|
59706
|
+
["scope", opts.scope],
|
|
59707
|
+
["source", opts.source]
|
|
59708
|
+
]) {
|
|
59709
|
+
const violation = validateEnumField(flag, value2);
|
|
59710
|
+
if (!violation)
|
|
59711
|
+
continue;
|
|
59712
|
+
let msg = formatEnumViolation(violation);
|
|
59713
|
+
if (flag === "category" && templates[violation.value]) {
|
|
59714
|
+
msg += ` Did you mean --template ${violation.value}?`;
|
|
59715
|
+
}
|
|
59716
|
+
throw new Error(msg);
|
|
59717
|
+
}
|
|
59554
59718
|
const explicitTags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined;
|
|
59555
59719
|
const mergedTags = explicitTags ? explicitTags : templateDefaults?.tags && templateDefaults.tags.length > 0 ? templateDefaults.tags : undefined;
|
|
59556
59720
|
let resolvedAgentId;
|
|
@@ -59576,17 +59740,40 @@ function registerCrudCommands(program2) {
|
|
|
59576
59740
|
if (project)
|
|
59577
59741
|
input.project_id = project.id;
|
|
59578
59742
|
}
|
|
59579
|
-
const
|
|
59743
|
+
const bucket = (m) => [m.scope ?? "private", m.agent_id ?? "", m.project_id ?? "", m.session_id ?? ""].join("\x1F");
|
|
59744
|
+
const targetBucket = bucket({
|
|
59745
|
+
scope: input.scope ?? "private",
|
|
59746
|
+
agent_id: input.agent_id,
|
|
59747
|
+
project_id: input.project_id,
|
|
59748
|
+
session_id: input.session_id
|
|
59749
|
+
});
|
|
59750
|
+
const dedupe = opts.dedupe;
|
|
59751
|
+
const forkRequested = dedupe === "create" || dedupe === "version-fork";
|
|
59752
|
+
let willUpdateExisting = false;
|
|
59753
|
+
if (!forkRequested) {
|
|
59754
|
+
const sameKey = getMemoriesByKey(key);
|
|
59755
|
+
const match = sameKey.find((m) => bucket(m) === targetBucket);
|
|
59756
|
+
willUpdateExisting = Boolean(match);
|
|
59757
|
+
if (!match && sameKey.length > 0) {
|
|
59758
|
+
const rows = sameKey.map((m) => ` ${m.id.slice(0, 8)} scope=${m.scope} project=${m.project_id ?? "none"} session=${m.session_id ?? "none"} agent=${m.agent_id ?? "none"}`).join(`
|
|
59759
|
+
`);
|
|
59760
|
+
throw new Error(`Refusing to fork key "${key}": ${sameKey.length} active memor${sameKey.length === 1 ? "y" : "ies"} ` + `already ${sameKey.length === 1 ? "uses" : "use"} it, ` + `but none matches the scope/project/session this save targets ` + `(scope=${input.scope ?? "private"}, project=${input.project_id ?? "none"}, session=${input.session_id ?? "none"}).
|
|
59761
|
+
` + `${rows}
|
|
59762
|
+
` + `Saving would create a second active row under the same key. Either target the existing row ` + `(match its scope/project/session flags, or use \`mementos update <id>\`), or pass \`--dedupe create\` ` + `to fork deliberately.`);
|
|
59763
|
+
}
|
|
59764
|
+
}
|
|
59765
|
+
const memory = forkRequested ? createMemory(input, dedupe) : createMemory(input);
|
|
59766
|
+
const outcome = willUpdateExisting ? "Updated" : "Created";
|
|
59580
59767
|
if (globalOpts.json) {
|
|
59581
|
-
outputJson(memory);
|
|
59768
|
+
outputJson({ ...memory, outcome: outcome.toLowerCase() });
|
|
59582
59769
|
} else {
|
|
59583
|
-
console.log(chalk2.green(
|
|
59770
|
+
console.log(chalk2.green(`${outcome}: ${memory.key} (${memory.id.slice(0, 8)})`));
|
|
59584
59771
|
}
|
|
59585
59772
|
} catch (e) {
|
|
59586
59773
|
handleError(e);
|
|
59587
59774
|
}
|
|
59588
59775
|
});
|
|
59589
|
-
program2.command("update <id>").description("Update a memory by ID").option("--value <text>", "New value").option("--importance <n>", "New importance 1-10", parseInt).option("--tags <tags>", "New comma-separated tags").option("--summary <text>", "New summary").option("--pin", "Pin the memory").option("--unpin", "Unpin the memory").option("-c, --category <cat>", "New category").option("
|
|
59776
|
+
program2.command("update <id>").description("Update a memory by ID").option("--value <text>", "New value").option("--importance <n>", "New importance 1-10", parseInt).option("--tags <tags>", "New comma-separated tags").option("--summary <text>", "New summary").option("--pin", "Pin the memory").option("--unpin", "Unpin the memory").option("-c, --category <cat>", "New category").option("--scope <scope>", "New scope").option("--status <status>", "New status: active, archived, expired").action((id, opts) => {
|
|
59590
59777
|
try {
|
|
59591
59778
|
const globalOpts = program2.opts();
|
|
59592
59779
|
const resolvedId = resolveMemoryId(id);
|
|
@@ -59620,17 +59807,31 @@ function registerCrudCommands(program2) {
|
|
|
59620
59807
|
updateInput.scope = opts.scope;
|
|
59621
59808
|
if (opts.status !== undefined)
|
|
59622
59809
|
updateInput.status = opts.status;
|
|
59810
|
+
for (const [flag, value] of [
|
|
59811
|
+
["category", opts.category],
|
|
59812
|
+
["scope", opts.scope],
|
|
59813
|
+
["status", opts.status]
|
|
59814
|
+
]) {
|
|
59815
|
+
const violation = validateEnumField(flag, value);
|
|
59816
|
+
if (violation)
|
|
59817
|
+
throw new Error(formatEnumViolation(violation));
|
|
59818
|
+
}
|
|
59819
|
+
const changedFields = Object.keys(updateInput).filter((k) => k !== "version");
|
|
59820
|
+
if (changedFields.length === 0) {
|
|
59821
|
+
throw new Error(`Nothing to update: no fields were given for ${existing.key} (${existing.id.slice(0, 8)}). ` + `Pass at least one of --value, --scope, --category, --status, --importance, --tags, --summary, --pin/--unpin. ` + `Note that -s is the global --session, not --scope.`);
|
|
59822
|
+
}
|
|
59623
59823
|
const updated = updateMemory(resolvedId, updateInput);
|
|
59624
59824
|
if (globalOpts.json) {
|
|
59625
|
-
outputJson(updated);
|
|
59825
|
+
outputJson({ ...updated, updated_fields: changedFields });
|
|
59626
59826
|
} else {
|
|
59627
|
-
|
|
59827
|
+
const n = changedFields.length;
|
|
59828
|
+
console.log(chalk2.green(`Updated ${n} field${n === 1 ? "" : "s"}: ${updated.key} (${updated.id.slice(0, 8)})`) + chalk2.dim(` [${changedFields.join(", ")}]`));
|
|
59628
59829
|
}
|
|
59629
59830
|
} catch (e) {
|
|
59630
59831
|
handleError(e);
|
|
59631
59832
|
}
|
|
59632
59833
|
});
|
|
59633
|
-
program2.command("forget <keyOrId>").description("Delete a memory by key or ID").option("
|
|
59834
|
+
program2.command("forget <keyOrId>").description("Delete a memory by key or ID").option("--scope <scope>", "Filter by scope (global, shared, private)").option("--agent <agent>", "Filter by agent ID").option("--project <project>", "Filter by project ID").option("--all", "Delete ALL matching memories (no disambiguation needed)").action((keyOrId, opts) => {
|
|
59634
59835
|
try {
|
|
59635
59836
|
const globalOpts = program2.opts();
|
|
59636
59837
|
const idMatch = isApiMode() ? null : resolvePartialId(getDatabase(), "memories", keyOrId);
|
|
@@ -59730,7 +59931,7 @@ function registerViewCommands(program2) {
|
|
|
59730
59931
|
handleError(e);
|
|
59731
59932
|
}
|
|
59732
59933
|
});
|
|
59733
|
-
program2.command("pin <keyOrId>").description("Pin a memory by key or partial ID").option("
|
|
59934
|
+
program2.command("pin <keyOrId>").description("Pin a memory by key or partial ID").option("--scope <scope>", "Scope filter for key lookup").option("--agent <name>", "Agent filter for key lookup").option("--project <path>", "Project filter for key lookup").action((keyOrId, opts) => {
|
|
59734
59935
|
try {
|
|
59735
59936
|
const globalOpts = program2.opts();
|
|
59736
59937
|
const memory = resolveKeyOrId(keyOrId, opts, globalOpts);
|
|
@@ -59755,7 +59956,7 @@ function registerViewCommands(program2) {
|
|
|
59755
59956
|
handleError(e);
|
|
59756
59957
|
}
|
|
59757
59958
|
});
|
|
59758
|
-
program2.command("unpin <keyOrId>").description("Unpin a memory by key or partial ID").option("
|
|
59959
|
+
program2.command("unpin <keyOrId>").description("Unpin a memory by key or partial ID").option("--scope <scope>", "Scope filter for key lookup").option("--agent <name>", "Agent filter for key lookup").option("--project <path>", "Project filter for key lookup").action((keyOrId, opts) => {
|
|
59759
59960
|
try {
|
|
59760
59961
|
const globalOpts = program2.opts();
|
|
59761
59962
|
const memory = resolveKeyOrId(keyOrId, opts, globalOpts);
|
|
@@ -59780,7 +59981,7 @@ function registerViewCommands(program2) {
|
|
|
59780
59981
|
handleError(e);
|
|
59781
59982
|
}
|
|
59782
59983
|
});
|
|
59783
|
-
program2.command("archive <keyOrId>").description("Archive a memory by key or ID (hides from lists, keeps history)").option("
|
|
59984
|
+
program2.command("archive <keyOrId>").description("Archive a memory by key or ID (hides from lists, keeps history)").option("--scope <scope>", "Scope filter for key lookup").action((keyOrId, opts) => {
|
|
59784
59985
|
try {
|
|
59785
59986
|
const globalOpts = program2.opts();
|
|
59786
59987
|
const memory = resolveKeyOrId(keyOrId, opts, globalOpts);
|
|
@@ -59799,7 +60000,7 @@ function registerViewCommands(program2) {
|
|
|
59799
60000
|
process.exit(1);
|
|
59800
60001
|
}
|
|
59801
60002
|
});
|
|
59802
|
-
program2.command("versions <keyOrId>").description("Show version history for a memory").option("
|
|
60003
|
+
program2.command("versions <keyOrId>").description("Show version history for a memory").option("--scope <scope>", "Scope filter for key lookup").action((keyOrId, opts) => {
|
|
59803
60004
|
try {
|
|
59804
60005
|
const globalOpts = program2.opts();
|
|
59805
60006
|
const memory = resolveKeyOrId(keyOrId, opts, globalOpts);
|
|
@@ -59841,7 +60042,7 @@ import chalk4 from "chalk";
|
|
|
59841
60042
|
import { resolve as resolve4 } from "path";
|
|
59842
60043
|
function registerTailCommand(program2) {
|
|
59843
60044
|
const handleError = makeHandleError(program2);
|
|
59844
|
-
program2.command("tail").description("Watch for new/updated memories in real-time (like tail -f)").option("
|
|
60045
|
+
program2.command("tail").description("Watch for new/updated memories in real-time (like tail -f)").option("--scope <scope>", "Scope filter: global, shared, private").option("-c, --category <cat>", "Category filter: preference, fact, knowledge, history").option("--agent <name>", "Agent filter").option("--project <path>", "Project filter").option("--interval <ms>", "Poll interval in milliseconds (default: 2000)", parseInt).option("--notify", "Send macOS notifications for each change").action((opts) => {
|
|
59845
60046
|
try {
|
|
59846
60047
|
const globalOpts = program2.opts();
|
|
59847
60048
|
const jsonMode = !!globalOpts.json;
|
|
@@ -59955,7 +60156,7 @@ import chalk6 from "chalk";
|
|
|
59955
60156
|
import { resolve as resolve5 } from "path";
|
|
59956
60157
|
function registerSearchCommand(program2) {
|
|
59957
60158
|
const handleError = makeHandleError(program2);
|
|
59958
|
-
program2.command("search <query>").description("Full-text search across memories").option("
|
|
60159
|
+
program2.command("search <query>").description("Full-text search across memories").option("--scope <scope>", "Scope filter").option("-c, --category <cat>", "Category filter").option("--tags <tags>", "Comma-separated tags filter").option("--project <path>", "Project filter (path or name)").option("--agent <name>", "Agent filter").option("--session <id>", "Session ID filter").option("--limit <n>", "Max results", parseInt).option("--offset <n>", "Offset for pagination", parseInt).option("--cursor <n>", "Cursor offset for the next page", parseInt).option("--format <fmt>", "Output format: compact (default), json, csv, yaml").option("--verbose", "Show match highlights and wider snippets").option("--history", "Show recent search queries instead of searching").option("--popular", "Show most popular search queries").action((query, opts) => {
|
|
59959
60160
|
try {
|
|
59960
60161
|
const fmt = getOutputFormat(program2, opts.format);
|
|
59961
60162
|
const isStructured = fmt === "json" || fmt === "csv" || fmt === "yaml";
|
|
@@ -60216,7 +60417,7 @@ init_memories();
|
|
|
60216
60417
|
init_helpers();
|
|
60217
60418
|
import chalk10 from "chalk";
|
|
60218
60419
|
function registerRemoveCommand(program2) {
|
|
60219
|
-
program2.command("remove <nameOrId>").description("Remove/delete a memory by name or ID (alias for memory forget)").option("--agent <id>", "Agent ID").option("
|
|
60420
|
+
program2.command("remove <nameOrId>").description("Remove/delete a memory by name or ID (alias for memory forget)").option("--agent <id>", "Agent ID").option("--scope <scope>", "Filter by scope (when looking up by key)").action((nameOrId, opts) => {
|
|
60220
60421
|
const globalOpts = program2.opts();
|
|
60221
60422
|
const agentId = opts.agent || globalOpts.agent;
|
|
60222
60423
|
let id = isApiMode() ? null : resolvePartialId(getDatabase(), "memories", nameOrId);
|
|
@@ -60273,7 +60474,7 @@ import chalk11 from "chalk";
|
|
|
60273
60474
|
import { resolve as resolve6 } from "path";
|
|
60274
60475
|
function registerRecallCommand(program2) {
|
|
60275
60476
|
const handleError = makeHandleError(program2);
|
|
60276
|
-
program2.command("recall <key>").description("Recall a memory by key").option("
|
|
60477
|
+
program2.command("recall <key>").description("Recall a memory by key").option("--scope <scope>", "Scope filter").option("--agent <name>", "Agent filter").option("--project <path>", "Project filter").action((key, opts) => {
|
|
60277
60478
|
try {
|
|
60278
60479
|
const globalOpts = program2.opts();
|
|
60279
60480
|
const agentId = opts.agent || globalOpts.agent;
|
|
@@ -60331,7 +60532,7 @@ import chalk12 from "chalk";
|
|
|
60331
60532
|
import { resolve as resolve7 } from "path";
|
|
60332
60533
|
function registerListCommand(program2) {
|
|
60333
60534
|
const handleError = makeHandleError(program2);
|
|
60334
|
-
program2.command("list").description("List memories with optional filters").option("
|
|
60535
|
+
program2.command("list").description("List memories with optional filters").option("--scope <scope>", "Scope filter").option("-c, --category <cat>", "Category filter").option("--tags <tags>", "Comma-separated tags filter").option("--importance-min <n>", "Minimum importance", parseInt).option("--pinned", "Show only pinned").option("--agent <name>", "Agent filter").option("--project <path>", "Project filter").option("--session <id>", "Session ID filter").option("--limit <n>", "Max results", parseInt).option("--offset <n>", "Offset for pagination", parseInt).option("--cursor <n>", "Cursor offset for the next page", parseInt).option("--status <status>", "Status filter: active, archived, expired").option("--format <fmt>", "Output format: compact (default), json, csv, yaml").option("--verbose", "Show wider memory snippets in human output").action((opts) => {
|
|
60335
60536
|
try {
|
|
60336
60537
|
const globalOpts = program2.opts();
|
|
60337
60538
|
const fmt = getOutputFormat(program2, opts.format);
|
|
@@ -60980,7 +61181,7 @@ init_helpers();
|
|
|
60980
61181
|
import { resolve as resolve11 } from "path";
|
|
60981
61182
|
function registerExportCommand(program2) {
|
|
60982
61183
|
const handleError = makeHandleError(program2);
|
|
60983
|
-
program2.command("export").description("Export memories as JSON").option("
|
|
61184
|
+
program2.command("export").description("Export memories as JSON").option("--scope <scope>", "Scope filter").option("-c, --category <cat>", "Category filter").option("--agent <name>", "Agent filter").option("--project <path>", "Project filter").action((opts) => {
|
|
60984
61185
|
try {
|
|
60985
61186
|
const globalOpts = program2.opts();
|
|
60986
61187
|
const agentId = opts.agent || globalOpts.agent;
|
|
@@ -61296,7 +61497,7 @@ function enforceQuotas(config, db) {
|
|
|
61296
61497
|
}
|
|
61297
61498
|
function archiveStale(staleDays, db) {
|
|
61298
61499
|
const d = db || getDatabase();
|
|
61299
|
-
const timestamp =
|
|
61500
|
+
const timestamp = now();
|
|
61300
61501
|
const cutoff = new Date(Date.now() - staleDays * 24 * 60 * 60 * 1000).toISOString();
|
|
61301
61502
|
const archiveWhere = `status = 'active' AND pinned = 0 AND COALESCE(accessed_at, created_at) < ?`;
|
|
61302
61503
|
const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${archiveWhere}`).get(cutoff).c;
|
|
@@ -61307,7 +61508,7 @@ function archiveStale(staleDays, db) {
|
|
|
61307
61508
|
}
|
|
61308
61509
|
function archiveUnused(days, db) {
|
|
61309
61510
|
const d = db || getDatabase();
|
|
61310
|
-
const timestamp =
|
|
61511
|
+
const timestamp = now();
|
|
61311
61512
|
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
61312
61513
|
const unusedWhere = `status = 'active' AND pinned = 0 AND access_count = 0 AND created_at < ?`;
|
|
61313
61514
|
const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${unusedWhere}`).get(cutoff).c;
|
|
@@ -61318,7 +61519,7 @@ function archiveUnused(days, db) {
|
|
|
61318
61519
|
}
|
|
61319
61520
|
function deprioritizeStale(days, db) {
|
|
61320
61521
|
const d = db || getDatabase();
|
|
61321
|
-
const timestamp =
|
|
61522
|
+
const timestamp = now();
|
|
61322
61523
|
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
61323
61524
|
const deprioWhere = `status = 'active' AND pinned = 0 AND importance > 1 AND COALESCE(accessed_at, updated_at) < ?`;
|
|
61324
61525
|
const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${deprioWhere}`).get(cutoff).c;
|
|
@@ -61346,7 +61547,9 @@ init_api_mode();
|
|
|
61346
61547
|
init_helpers();
|
|
61347
61548
|
function runCleanupViaApi() {
|
|
61348
61549
|
const empty = { expired: 0, evicted: 0, archived: 0, unused_archived: 0, deprioritized: 0 };
|
|
61349
|
-
const { status, data } = apiJson("POST", "/maintenance/cleanup"
|
|
61550
|
+
const { status, data } = apiJson("POST", "/maintenance/cleanup", undefined, {
|
|
61551
|
+
allow404: true
|
|
61552
|
+
});
|
|
61350
61553
|
if (status !== 404 && data)
|
|
61351
61554
|
return { ...empty, ...data };
|
|
61352
61555
|
const legacy = apiJson("POST", "/memories/clean");
|
|
@@ -61637,7 +61840,7 @@ function getFocus(agentId) {
|
|
|
61637
61840
|
init_helpers();
|
|
61638
61841
|
function registerAgentCommands(program2) {
|
|
61639
61842
|
const handleError = makeHandleError(program2);
|
|
61640
|
-
program2.command("register-agent <name>").alias("init-agent").description("Register an agent (returns ID)").option("-d, --description <text>", "Agent description").option("-r, --role <role>", "Agent role").option("
|
|
61843
|
+
program2.command("register-agent <name>").alias("init-agent").description("Register an agent (returns ID)").option("-d, --description <text>", "Agent description").option("-r, --role <role>", "Agent role").option("--project <id>", "Lock agent to a project (sets active_project_id)").action((name, opts) => {
|
|
61641
61844
|
try {
|
|
61642
61845
|
const globalOpts = program2.opts();
|
|
61643
61846
|
const agent = registerAgent(name, undefined, opts.description, opts.role, opts.project);
|
|
@@ -64060,7 +64263,7 @@ import chalk39 from "chalk";
|
|
|
64060
64263
|
import { resolve as resolve19 } from "path";
|
|
64061
64264
|
function registerWatchCommand(program2) {
|
|
64062
64265
|
const handleError = makeHandleError(program2);
|
|
64063
|
-
program2.command("watch").description("Watch for new and changed memories in real-time").option("
|
|
64266
|
+
program2.command("watch").description("Watch for new and changed memories in real-time").option("--scope <scope>", "Scope filter: global, shared, private").option("-c, --category <cat>", "Category filter: preference, fact, knowledge, history").option("--agent <name>", "Agent filter").option("--project <path>", "Project filter").option("--interval <ms>", "Poll interval in milliseconds", parseInt).action((opts) => {
|
|
64064
64267
|
try {
|
|
64065
64268
|
const globalOpts = program2.opts();
|
|
64066
64269
|
const agentId = opts.agent || globalOpts.agent;
|
|
@@ -64656,6 +64859,35 @@ function getStorageSyncStatus(options = {}) {
|
|
|
64656
64859
|
}
|
|
64657
64860
|
}
|
|
64658
64861
|
|
|
64862
|
+
// src/db/store-backend.ts
|
|
64863
|
+
init_database();
|
|
64864
|
+
init_api_mode();
|
|
64865
|
+
init_storage();
|
|
64866
|
+
function resolveStoreBackend() {
|
|
64867
|
+
const apiMode = isApiMode();
|
|
64868
|
+
const apiConfig = getApiConfig();
|
|
64869
|
+
const storageMode = getStorageMode();
|
|
64870
|
+
const sources = getApiModeEnvSources();
|
|
64871
|
+
const backend = apiMode ? "cloud-api" : storageMode === "cloud" ? "cloud-postgres" : "local-sqlite";
|
|
64872
|
+
let selectedBy = "default";
|
|
64873
|
+
if (apiMode) {
|
|
64874
|
+
selectedBy = `${sources.urlKey} + ${sources.keyKey} (presence)`;
|
|
64875
|
+
} else if (backend === "cloud-postgres") {
|
|
64876
|
+
const modeKey = [MEMENTOS_STORAGE_ENV.mode, MEMENTOS_STORAGE_FALLBACK_ENV.mode].find((key) => process.env[key]?.trim());
|
|
64877
|
+
selectedBy = modeKey ?? sources.databaseUrlKey ?? "storage config file";
|
|
64878
|
+
}
|
|
64879
|
+
return {
|
|
64880
|
+
schema: "mementos.store_backend.v1",
|
|
64881
|
+
backend,
|
|
64882
|
+
api_mode: apiMode,
|
|
64883
|
+
storage_mode: storageMode,
|
|
64884
|
+
db_path: getDbPath(),
|
|
64885
|
+
api_endpoint: apiConfig?.baseUrl ?? null,
|
|
64886
|
+
api_key_present: Boolean(apiConfig?.apiKey),
|
|
64887
|
+
selected_by: selectedBy
|
|
64888
|
+
};
|
|
64889
|
+
}
|
|
64890
|
+
|
|
64659
64891
|
// src/cli/commands/storage.ts
|
|
64660
64892
|
function parseTables(raw) {
|
|
64661
64893
|
if (!raw) {
|
|
@@ -64680,6 +64912,26 @@ function printSyncResult(result) {
|
|
|
64680
64912
|
}
|
|
64681
64913
|
}
|
|
64682
64914
|
function installStorageSubcommands(storage, program2) {
|
|
64915
|
+
withoutStartupDbAccess(storage.command("mode").description("Show which store this process will actually read and write (no DB or network access)").option("--json", "Output JSON").action((opts) => {
|
|
64916
|
+
const useJson = Boolean(opts.json || program2.opts().json);
|
|
64917
|
+
const report = resolveStoreBackend();
|
|
64918
|
+
if (useJson) {
|
|
64919
|
+
outputJson2(true, report);
|
|
64920
|
+
return;
|
|
64921
|
+
}
|
|
64922
|
+
const label = report.backend === "local-sqlite" ? chalk40.green(report.backend) : chalk40.yellow(report.backend);
|
|
64923
|
+
console.log(`Backend: ${label}`);
|
|
64924
|
+
console.log(`Selected by: ${report.selected_by}`);
|
|
64925
|
+
console.log(`API mode: ${report.api_mode ? "yes" : "no"}`);
|
|
64926
|
+
console.log(`Storage mode: ${report.storage_mode}`);
|
|
64927
|
+
if (report.backend === "local-sqlite") {
|
|
64928
|
+
console.log(`Database: ${report.db_path}`);
|
|
64929
|
+
} else {
|
|
64930
|
+
console.log(`API endpoint: ${report.api_endpoint ?? "(none)"}`);
|
|
64931
|
+
console.log(`API key: ${report.api_key_present ? "configured" : "not configured"}`);
|
|
64932
|
+
console.log(`Local SQLite (not authoritative): ${report.db_path}`);
|
|
64933
|
+
}
|
|
64934
|
+
}));
|
|
64683
64935
|
storage.command("status").description("Show local database and remote storage sync status").option("--json", "Output JSON").action((opts) => {
|
|
64684
64936
|
const useJson = Boolean(opts.json || program2.opts().json);
|
|
64685
64937
|
const status = getStorageSyncStatus();
|
|
@@ -65140,7 +65392,7 @@ function parseMemoryLink(row) {
|
|
|
65140
65392
|
function createMemoryLink(input, db) {
|
|
65141
65393
|
const d = db || getDatabase();
|
|
65142
65394
|
const id = shortUuid();
|
|
65143
|
-
const timestamp =
|
|
65395
|
+
const timestamp = now();
|
|
65144
65396
|
d.run(`INSERT OR IGNORE INTO memory_links (id, source_memory_id, target_memory_id, relation_type, run_id, metadata, created_at)
|
|
65145
65397
|
VALUES (?, ?, ?, ?, ?, ?, ?)`, [
|
|
65146
65398
|
id,
|
|
@@ -65226,7 +65478,7 @@ function createRun(options, db) {
|
|
|
65226
65478
|
options.projectId ?? null,
|
|
65227
65479
|
options.agentId ?? null,
|
|
65228
65480
|
options.dryRun ?? true ? 1 : 0,
|
|
65229
|
-
|
|
65481
|
+
now()
|
|
65230
65482
|
]);
|
|
65231
65483
|
return getRun(id, db);
|
|
65232
65484
|
}
|
|
@@ -65271,7 +65523,7 @@ function persistAction(action, db) {
|
|
|
65271
65523
|
action.reason,
|
|
65272
65524
|
JSON.stringify(action.plannedChanges),
|
|
65273
65525
|
action.applied ? 1 : 0,
|
|
65274
|
-
|
|
65526
|
+
now()
|
|
65275
65527
|
]);
|
|
65276
65528
|
}
|
|
65277
65529
|
function markActionApplied(action, db) {
|
|
@@ -65646,11 +65898,11 @@ async function runConsolidation(options = {}) {
|
|
|
65646
65898
|
actions = applied;
|
|
65647
65899
|
}
|
|
65648
65900
|
const summary = buildSummary2(actions);
|
|
65649
|
-
run = updateRun(run.id, { status: "completed", summary, completed_at:
|
|
65901
|
+
run = updateRun(run.id, { status: "completed", summary, completed_at: now() }, db);
|
|
65650
65902
|
return { run, actions, dryRun, summary };
|
|
65651
65903
|
} catch (error) {
|
|
65652
65904
|
const message = error instanceof Error ? error.message : String(error);
|
|
65653
|
-
run = updateRun(run.id, { status: "failed", error: message, completed_at:
|
|
65905
|
+
run = updateRun(run.id, { status: "failed", error: message, completed_at: now() }, db);
|
|
65654
65906
|
return {
|
|
65655
65907
|
run,
|
|
65656
65908
|
actions: [],
|
|
@@ -65723,7 +65975,7 @@ function createRun2(options, memoryIds, db) {
|
|
|
65723
65975
|
options.provider ?? null,
|
|
65724
65976
|
options.model ?? null,
|
|
65725
65977
|
JSON.stringify(memoryIds),
|
|
65726
|
-
|
|
65978
|
+
now()
|
|
65727
65979
|
]);
|
|
65728
65980
|
return getRun2(id, db);
|
|
65729
65981
|
}
|
|
@@ -65766,7 +66018,7 @@ function insertLessonRow(runId, lesson, db) {
|
|
|
65766
66018
|
lesson.lesson,
|
|
65767
66019
|
JSON.stringify(lesson.evidence),
|
|
65768
66020
|
lesson.importance,
|
|
65769
|
-
|
|
66021
|
+
now()
|
|
65770
66022
|
]);
|
|
65771
66023
|
}
|
|
65772
66024
|
function listToolEventsForTrajectory(options, db) {
|
|
@@ -66059,7 +66311,7 @@ async function reflectOnTrajectory(options) {
|
|
|
66059
66311
|
}
|
|
66060
66312
|
});
|
|
66061
66313
|
}
|
|
66062
|
-
run = updateRun2(run.id, { status: "completed", summary: criticResult.summary, completed_at:
|
|
66314
|
+
run = updateRun2(run.id, { status: "completed", summary: criticResult.summary, completed_at: now() }, db);
|
|
66063
66315
|
return {
|
|
66064
66316
|
run,
|
|
66065
66317
|
dryRun,
|
|
@@ -66073,7 +66325,7 @@ async function reflectOnTrajectory(options) {
|
|
|
66073
66325
|
};
|
|
66074
66326
|
} catch (error40) {
|
|
66075
66327
|
const message = error40 instanceof Error ? error40.message : String(error40);
|
|
66076
|
-
run = updateRun2(run.id, { status: "failed", error: message, completed_at:
|
|
66328
|
+
run = updateRun2(run.id, { status: "failed", error: message, completed_at: now() }, db);
|
|
66077
66329
|
return {
|
|
66078
66330
|
run,
|
|
66079
66331
|
dryRun,
|
|
@@ -66489,6 +66741,26 @@ function makeBrainsCommand() {
|
|
|
66489
66741
|
return brains;
|
|
66490
66742
|
}
|
|
66491
66743
|
|
|
66744
|
+
// src/cli/register-all.ts
|
|
66745
|
+
function registerAllCommands(program2) {
|
|
66746
|
+
registerInitCommand(program2);
|
|
66747
|
+
registerMemoryCommands(program2);
|
|
66748
|
+
registerInfoCommands(program2);
|
|
66749
|
+
registerIoCommands(program2);
|
|
66750
|
+
registerAgentCommands(program2);
|
|
66751
|
+
registerProjectCommands(program2);
|
|
66752
|
+
registerProjectPanelCommand(program2);
|
|
66753
|
+
registerEntityCommands(program2);
|
|
66754
|
+
registerRelationCommands(program2);
|
|
66755
|
+
registerGraphCommands(program2);
|
|
66756
|
+
registerSystemCommands(program2);
|
|
66757
|
+
registerStorageCommands(program2);
|
|
66758
|
+
registerConsolidationCommands(program2);
|
|
66759
|
+
program2.addCommand(makeBrainsCommand());
|
|
66760
|
+
registerEventsCommands(program2, { source: "mementos" });
|
|
66761
|
+
return program2;
|
|
66762
|
+
}
|
|
66763
|
+
|
|
66492
66764
|
// src/cli/index.tsx
|
|
66493
66765
|
function getPackageVersion2() {
|
|
66494
66766
|
try {
|
|
@@ -66500,9 +66772,12 @@ function getPackageVersion2() {
|
|
|
66500
66772
|
}
|
|
66501
66773
|
}
|
|
66502
66774
|
var program2 = new Command;
|
|
66503
|
-
program2.name("mementos").description("Universal memory system for AI agents").version(getPackageVersion2())
|
|
66775
|
+
program2.name("mementos").description("Universal memory system for AI agents").version(getPackageVersion2());
|
|
66776
|
+
applyGlobalOptions(program2);
|
|
66504
66777
|
var startupWarningShown = false;
|
|
66505
|
-
program2.hook("preAction", () => {
|
|
66778
|
+
program2.hook("preAction", (_thisCommand, actionCommand) => {
|
|
66779
|
+
if (skipsStartupDbAccess(actionCommand))
|
|
66780
|
+
return;
|
|
66506
66781
|
if (startupWarningShown)
|
|
66507
66782
|
return;
|
|
66508
66783
|
startupWarningShown = true;
|
|
@@ -66513,19 +66788,5 @@ program2.hook("preAction", () => {
|
|
|
66513
66788
|
}
|
|
66514
66789
|
} catch {}
|
|
66515
66790
|
});
|
|
66516
|
-
|
|
66517
|
-
registerMemoryCommands(program2);
|
|
66518
|
-
registerInfoCommands(program2);
|
|
66519
|
-
registerIoCommands(program2);
|
|
66520
|
-
registerAgentCommands(program2);
|
|
66521
|
-
registerProjectCommands(program2);
|
|
66522
|
-
registerProjectPanelCommand(program2);
|
|
66523
|
-
registerEntityCommands(program2);
|
|
66524
|
-
registerRelationCommands(program2);
|
|
66525
|
-
registerGraphCommands(program2);
|
|
66526
|
-
registerSystemCommands(program2);
|
|
66527
|
-
registerStorageCommands(program2);
|
|
66528
|
-
registerConsolidationCommands(program2);
|
|
66529
|
-
program2.addCommand(makeBrainsCommand());
|
|
66530
|
-
registerEventsCommands(program2, { source: "mementos" });
|
|
66791
|
+
registerAllCommands(program2);
|
|
66531
66792
|
program2.parse(process.argv);
|