@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.
Files changed (37) hide show
  1. package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts +2 -0
  2. package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts.map +1 -0
  3. package/dist/cli/commands/io-clean.d.ts.map +1 -1
  4. package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
  5. package/dist/cli/commands/storage.d.ts.map +1 -1
  6. package/dist/cli/global-options.d.ts +27 -0
  7. package/dist/cli/global-options.d.ts.map +1 -0
  8. package/dist/cli/index.js +502 -241
  9. package/dist/cli/register-all.d.ts +17 -0
  10. package/dist/cli/register-all.d.ts.map +1 -0
  11. package/dist/cli/startup-side-effects.d.ts +9 -0
  12. package/dist/cli/startup-side-effects.d.ts.map +1 -0
  13. package/dist/db/__fixtures__/fail-closed-stub-server.d.ts +2 -0
  14. package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +1 -0
  15. package/dist/db/api-mode.d.ts +70 -2
  16. package/dist/db/api-mode.d.ts.map +1 -1
  17. package/dist/db/locks.d.ts.map +1 -1
  18. package/dist/db/memories.d.ts +24 -6
  19. package/dist/db/memories.d.ts.map +1 -1
  20. package/dist/db/session-jobs.d.ts.map +1 -1
  21. package/dist/db/store-backend.d.ts +33 -0
  22. package/dist/db/store-backend.d.ts.map +1 -0
  23. package/dist/index.js +80 -20
  24. package/dist/lib/enum-validation.d.ts +20 -0
  25. package/dist/lib/enum-validation.d.ts.map +1 -0
  26. package/dist/mcp/index.js +147 -30
  27. package/dist/server/helpers.d.ts +11 -0
  28. package/dist/server/helpers.d.ts.map +1 -1
  29. package/dist/server/index.d.ts.map +1 -1
  30. package/dist/server/index.js +185 -30
  31. package/dist/test-support/preload-local-store.d.ts +2 -0
  32. package/dist/test-support/preload-local-store.d.ts.map +1 -0
  33. package/dist/test-support/store-isolation.d.ts +83 -0
  34. package/dist/test-support/store-isolation.d.ts.map +1 -0
  35. package/dist/types/index.d.ts +8 -4
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * Attach EVERY command group to `program`, in the order the CLI presents them.
4
+ *
5
+ * This is the single registration list. `index.tsx` calls it to build the real
6
+ * CLI, and `global-options.test.ts` calls it to walk the real option tables — so
7
+ * the structural short-flag guard cannot silently check a smaller tree than the
8
+ * one that ships.
9
+ *
10
+ * That drift was real: the guard originally re-listed 11 groups by hand while
11
+ * the CLI wired 15 sources, leaving `init`, `project-panel`, `brains` and the
12
+ * `@hasna/events` groups (`events`, `webhooks`) unwalked. Seven live `-j`
13
+ * collisions sat in that blind spot while the guard reported clean. Adding a
14
+ * command group here is now the only step required for it to be covered.
15
+ */
16
+ export declare function registerAllCommands(program: Command): Command;
17
+ //# sourceMappingURL=register-all.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-all.d.ts","sourceRoot":"","sources":["../../src/cli/register-all.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBzC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAiB7D"}
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * Mark a command as having no startup database access, and return it so it can
4
+ * be used inline at the end of a builder chain.
5
+ */
6
+ export declare function withoutStartupDbAccess<T extends Command>(command: T): T;
7
+ /** Whether the command about to run has opted out of the startup DB access. */
8
+ export declare function skipsStartupDbAccess(command: Command | undefined): boolean;
9
+ //# sourceMappingURL=startup-side-effects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-side-effects.d.ts","sourceRoot":"","sources":["../../src/cli/startup-side-effects.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAGvE;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAE1E"}
@@ -0,0 +1,2 @@
1
+ declare const server: Bun.Server<undefined>;
2
+ //# sourceMappingURL=fail-closed-stub-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fail-closed-stub-server.d.ts","sourceRoot":"","sources":["../../../src/db/__fixtures__/fail-closed-stub-server.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,MAAM,uBAiBV,CAAC"}
@@ -2,7 +2,56 @@ export interface ApiConfig {
2
2
  baseUrl: string;
3
3
  apiKey: string;
4
4
  }
5
+ export declare const API_URL_ENV_KEYS: readonly ["HASNA_MEMENTOS_API_URL", "MEMENTOS_API_URL"];
6
+ export declare const API_KEY_ENV_KEYS: readonly ["HASNA_MEMENTOS_API_KEY", "MEMENTOS_API_KEY"];
7
+ export declare const DATABASE_URL_ENV_KEYS: readonly ["HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"];
8
+ /**
9
+ * Which env keys currently select API mode, by name only — never the values.
10
+ * Used by the operator-facing mode report so a human can see *why* the client
11
+ * is pointed at the cloud without reading source or echoing a credential.
12
+ */
13
+ export declare function getApiModeEnvSources(): {
14
+ urlKey: string | null;
15
+ keyKey: string | null;
16
+ databaseUrlKey: string | null;
17
+ };
18
+ /**
19
+ * Escape hatch for a test that genuinely must reach a non-loopback endpoint.
20
+ * Opt-in by design: the default has to be safe, because the failure it prevents
21
+ * is silent and lands in a shared store.
22
+ */
23
+ export declare const ALLOW_REMOTE_API_IN_TESTS_ENV = "MEMENTOS_ALLOW_REMOTE_API_IN_TESTS";
5
24
  /** Resolve the API client config from env, or `null` when not configured. */
25
+ /** The env keys that explicitly select the local SQLite store. */
26
+ export declare const DB_PATH_ENV_KEYS: readonly ["HASNA_MEMENTOS_DB_PATH", "MEMENTOS_DB_PATH"];
27
+ /** Raised when the environment does not unambiguously select one store. */
28
+ export declare class MementosStoreConfigError extends Error {
29
+ readonly code = "MEMENTOS_STORE_CONFIG";
30
+ constructor(message: string);
31
+ }
32
+ /**
33
+ * Throw unless the environment unambiguously selects one store.
34
+ *
35
+ * AMBIGUOUS CONFIGURATION IS AN ERROR, NOT A DEFAULT. Half an API configuration
36
+ * used to resolve to `null`, which every caller read as "local SQLite" — so an
37
+ * operator whose API key failed to load silently got a different, usually stale,
38
+ * dataset with no error and no flag. From the caller's side that is
39
+ * indistinguishable from a store that is working.
40
+ *
41
+ * Precedence, highest first:
42
+ * 1. An explicit SQLite path (`HASNA_MEMENTOS_DB_PATH` / `MEMENTOS_DB_PATH`) is
43
+ * the narrowest, most specific signal and selects LOCAL, so local dev,
44
+ * tooling and import/export keep working when a stray credential is exported
45
+ * globally.
46
+ * 2. A client DSN present disables API mode. Unchanged here, and tracked
47
+ * separately — a DSN on a client is forbidden and handled in database.ts.
48
+ * 3. Both API URL and API key present -> API mode.
49
+ * 4. Exactly ONE of them present -> ERROR naming the missing variable.
50
+ * 5. Neither present -> LOCAL. The documented single-operator default.
51
+ *
52
+ * Never reads, logs, or embeds a credential value — only variable NAMES.
53
+ */
54
+ export declare function assertUnambiguousStoreEnv(): void;
6
55
  export declare function getApiConfig(): ApiConfig | null;
7
56
  /**
8
57
  * True when the client should route memory operations to the cloud API.
@@ -15,8 +64,27 @@ export declare class ApiRequestError extends Error {
15
64
  readonly body: string;
16
65
  constructor(message: string, status: number, body: string);
17
66
  }
18
- /** Authed JSON request. Throws {@link ApiRequestError} on non-2xx (except 404 handled by callers). */
19
- export declare function apiJson<T = unknown>(method: string, path: string, body?: unknown): {
67
+ export interface ApiJsonOptions {
68
+ /**
69
+ * Treat `404` as a normal outcome and return `{status: 404, data: undefined}`
70
+ * instead of throwing. ONLY for callers where "absent" is a real answer —
71
+ * a GET of one record by id, a DELETE that tolerates an already-gone row, or
72
+ * a call that has an explicit fallback for a route an older server image does
73
+ * not serve yet (see `runCleanupViaApi`).
74
+ *
75
+ * It must never be set on a create/upsert path. A 404 there means the route
76
+ * did not exist on the server (client/server version skew, wrong base URL),
77
+ * so nothing was written; returning success-shaped data made the CLI print
78
+ * "Saved:" and exit 0 having persisted nothing. Default is fail-closed.
79
+ */
80
+ allow404?: boolean;
81
+ }
82
+ /**
83
+ * Authed JSON request. Throws {@link ApiRequestError} on every non-2xx,
84
+ * including 404, unless the caller opts into 404 pass-through via
85
+ * {@link ApiJsonOptions.allow404}.
86
+ */
87
+ export declare function apiJson<T = unknown>(method: string, path: string, body?: unknown, options?: ApiJsonOptions): {
20
88
  status: number;
21
89
  data: T;
22
90
  };
@@ -1 +1 @@
1
- {"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAqBD,6EAA6E;AAC7E,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAK/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AAsGD,sGAAsG;AACtG,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAiB9G;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAe/D"}
1
+ {"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"AA0CA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,qBAAqB,mEAAoE,CAAC;AAsBvG;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAMtH;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uCAAuC,CAAC;AAyDlF,6EAA6E;AAC7E,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,2BAA2B;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAuBhD;AAED,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAM/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AA0GD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAiB7B;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAe/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/db/locks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI1D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAC5F,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,QAAsB,EAChC,UAAU,SAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAuErB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAenF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAW3E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,EAAE,CAqBhB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAcrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,YAAY,EAAE,CAa7E;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,eAAe,EAAE,CAS1E;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAQvD"}
1
+ {"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/db/locks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI1D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAC5F,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,QAAsB,EAChC,UAAU,SAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAuErB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAgBnF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAW3E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,EAAE,CAqBhB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAcrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,YAAY,EAAE,CAa7E;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,eAAe,EAAE,CAS1E;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAQvD"}
@@ -4,7 +4,15 @@ export declare function parseMemoryRow(row: Record<string, unknown>): Memory;
4
4
  export declare function createMemory(input: CreateMemoryInput, dedupeMode?: DedupeMode, db?: Database): Memory;
5
5
  export interface BulkUpsertResult {
6
6
  inserted: number;
7
+ /** Rows the store already had (id or unique-key conflict) — a benign no-op. */
7
8
  skipped: number;
9
+ /**
10
+ * Rows the store REFUSED (bad enum, failed CHECK/FK, missing key). These did
11
+ * NOT persist. Kept separate from `skipped` so a caller can never read a
12
+ * dropped row as an idempotent no-op; every rejection also has a line in
13
+ * `errors`. `inserted + skipped + rejected === total`.
14
+ */
15
+ rejected: number;
8
16
  errors: string[];
9
17
  total: number;
10
18
  }
@@ -18,12 +26,22 @@ export interface BulkUpsertResult {
18
26
  * - does NOT dedupe by key/scope/agent and does NOT fire hooks / entity
19
27
  * extraction / embedding work (those run asynchronously elsewhere).
20
28
  *
21
- * Idempotency is by primary-key `id`: `INSERT OR IGNORE` maps to
22
- * `ON CONFLICT DO NOTHING` on Postgres, so a row whose `id` already exists is
23
- * left untouched and re-runs never create duplicate rows. This is the
24
- * cross-machine cloud backfill path for the fleet self-host cutover: a
25
- * machine's local memories are shipped to the cloud store verbatim without
26
- * mutating rows that are already present.
29
+ * Idempotency is by conflict, not by suppression: the statement carries an
30
+ * explicit bare `ON CONFLICT DO NOTHING`, which SQLite and Postgres both read
31
+ * as "ignore a uniqueness conflict (primary-key `id` or the unique key index)".
32
+ * A row already present is therefore left untouched and re-runs never create
33
+ * duplicates. This is the cross-machine cloud backfill path for the fleet
34
+ * self-host cutover: a machine's local memories are shipped to the cloud store
35
+ * verbatim without mutating rows that are already present.
36
+ *
37
+ * It deliberately does NOT use `INSERT OR IGNORE`: on SQLite that swallows
38
+ * *every* constraint failure, so a row refused by the `category`/`scope`/
39
+ * `source`/`status` CHECK vanished with `changes === 0` and was counted as an
40
+ * idempotent `skipped` — a cloud write that did not persist reporting success.
41
+ * With a bare `ON CONFLICT DO NOTHING` a CHECK/FK/NOT NULL violation still
42
+ * throws into the catch below and lands in `rejected` + `errors`. Enum values
43
+ * are additionally pre-validated per row so the reason names the field and the
44
+ * accepted set instead of quoting raw constraint SQL.
27
45
  *
28
46
  * FK-referenced agent/project/session/machine rows are stubbed via
29
47
  * {@link ensureMemoryReferences} so cross-machine authorship ids never fail the
@@ -1 +1 @@
1
- {"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-jobs.d.ts","sourceRoot":"","sources":["../../src/db/session-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAK1D,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;AACpF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA6BD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAC5B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CAwBlB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CAehF;AAED,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,gBAAgB,EACzB,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,EAAE,CA2CpB;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,EAC9B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,GAAG,IAAI,CAwCzB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CASxE"}
1
+ {"version":3,"file":"session-jobs.d.ts","sourceRoot":"","sources":["../../src/db/session-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAK1D,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;AACpF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA6BD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAC5B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CAwBlB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CAiBhF;AAED,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,gBAAgB,EACzB,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,EAAE,CA2CpB;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,EAC9B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,GAAG,IAAI,CAwCzB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CASxE"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The transport that reads and writes will actually use.
3
+ *
4
+ * - `local-sqlite` — the on-disk SQLite file at `db_path` is authoritative.
5
+ * - `cloud-api` — authed HTTPS to the self-hosted server (the shared store).
6
+ * - `cloud-postgres` — a direct Postgres DSN; server-only, never a client.
7
+ */
8
+ export type StoreBackend = "local-sqlite" | "cloud-api" | "cloud-postgres";
9
+ export interface StoreBackendReport {
10
+ /** Machine-readable contract for scripts and test guards. */
11
+ schema: "mementos.store_backend.v1";
12
+ backend: StoreBackend;
13
+ /** True when the HTTPS client transport is engaged (client reads AND writes). */
14
+ api_mode: boolean;
15
+ /** The `getStorageMode()` value — reported for diagnosis, NOT the answer. */
16
+ storage_mode: string;
17
+ /** The SQLite path that WOULD be used. Meaningful only for `local-sqlite`. */
18
+ db_path: string;
19
+ /** Cloud endpoint origin + prefix. Not a secret; the key is never included. */
20
+ api_endpoint: string | null;
21
+ /** Whether an API key is configured. The value is never read or reported. */
22
+ api_key_present: boolean;
23
+ /** Env key NAMES that produced this backend, or `"default"`. Names only. */
24
+ selected_by: string;
25
+ }
26
+ /**
27
+ * Resolve the effective store backend from the environment.
28
+ *
29
+ * Pure with respect to storage: touches no database, makes no network call, and
30
+ * never places a credential value in the returned report.
31
+ */
32
+ export declare function resolveStoreBackend(): StoreBackendReport;
33
+ //# sourceMappingURL=store-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-backend.d.ts","sourceRoot":"","sources":["../../src/db/store-backend.ts"],"names":[],"mappings":"AA4BA;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,CAkCxD"}
package/dist/index.js CHANGED
@@ -938,7 +938,7 @@ import { tmpdir } from "os";
938
938
  import { join as join2 } from "path";
939
939
  import { writeFileSync as writeFileSync2, unlinkSync } from "fs";
940
940
  import { randomUUID } from "crypto";
941
- function firstEnv(...keys) {
941
+ function firstEnv(keys) {
942
942
  for (const k of keys) {
943
943
  const v = process.env[k]?.trim();
944
944
  if (v)
@@ -946,8 +946,38 @@ function firstEnv(...keys) {
946
946
  }
947
947
  return;
948
948
  }
949
+ function firstEnvKey(keys) {
950
+ for (const k of keys) {
951
+ if (process.env[k]?.trim())
952
+ return k;
953
+ }
954
+ return null;
955
+ }
949
956
  function hasDatabaseUrl() {
950
- return Boolean(firstEnv("HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"));
957
+ return Boolean(firstEnv(DATABASE_URL_ENV_KEYS));
958
+ }
959
+ function isLoopbackHost(rawHost) {
960
+ const host = rawHost.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
961
+ return host === "localhost" || host === "::1" || /^127\./.test(host);
962
+ }
963
+ function assertRequestAllowedUnderTest(baseUrl) {
964
+ if (process.env["NODE_ENV"] !== "test")
965
+ return;
966
+ if (process.env[ALLOW_REMOTE_API_IN_TESTS_ENV]?.trim())
967
+ return;
968
+ let host;
969
+ try {
970
+ host = new URL(baseUrl).hostname;
971
+ } catch {
972
+ host = "";
973
+ }
974
+ if (host && isLoopbackHost(host))
975
+ return;
976
+ 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.
977
+ ` + ` host : ${host || "(unparseable base URL)"}
978
+ ` + ` 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.
979
+ ` + " fix : build the child/process env via src/test-support/store-isolation.ts, or point the " + `suite at a loopback stub.
980
+ ` + ` override : set ${ALLOW_REMOTE_API_IN_TESTS_ENV}=1 only for a test that must reach a remote endpoint.`);
951
981
  }
952
982
  function normalizeBase(raw) {
953
983
  let base = raw.trim().replace(/\/+$/, "");
@@ -955,9 +985,24 @@ function normalizeBase(raw) {
955
985
  return base;
956
986
  return `${base}/v1`;
957
987
  }
988
+ function assertUnambiguousStoreEnv() {
989
+ if (firstEnvKey(DB_PATH_ENV_KEYS))
990
+ return;
991
+ if (hasDatabaseUrl())
992
+ return;
993
+ const urlKey = firstEnvKey(API_URL_ENV_KEYS);
994
+ const keyKey = firstEnvKey(API_KEY_ENV_KEYS);
995
+ if (urlKey && !keyKey) {
996
+ 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.`);
997
+ }
998
+ if (keyKey && !urlKey) {
999
+ 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.`);
1000
+ }
1001
+ }
958
1002
  function getApiConfig() {
959
- const rawBase = firstEnv("HASNA_MEMENTOS_API_URL", "MEMENTOS_API_URL");
960
- const apiKey = firstEnv("HASNA_MEMENTOS_API_KEY", "MEMENTOS_API_KEY");
1003
+ assertUnambiguousStoreEnv();
1004
+ const rawBase = firstEnv(API_URL_ENV_KEYS);
1005
+ const apiKey = firstEnv(API_KEY_ENV_KEYS);
961
1006
  if (!rawBase || !apiKey)
962
1007
  return null;
963
1008
  return { baseUrl: normalizeBase(rawBase), apiKey };
@@ -971,6 +1016,7 @@ function apiRequestRaw(method, path, body) {
971
1016
  const cfg = getApiConfig();
972
1017
  if (!cfg)
973
1018
  throw new Error("api-mode: not configured (HASNA_MEMENTOS_API_URL / HASNA_MEMENTOS_API_KEY)");
1019
+ assertRequestAllowedUnderTest(cfg.baseUrl);
974
1020
  const url = `${cfg.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
975
1021
  const hasBody = body !== undefined && body !== null;
976
1022
  const timeout = process.env["HASNA_MEMENTOS_API_TIMEOUT"] || DEFAULT_TIMEOUT_S;
@@ -1037,13 +1083,13 @@ x-api-key: ${cfg.apiKey}
1037
1083
  }
1038
1084
  return { status, body: respBody };
1039
1085
  }
1040
- function apiJson(method, path, body) {
1086
+ function apiJson(method, path, body, options) {
1041
1087
  const raw = apiRequestRaw(method, path, body);
1042
1088
  if (raw.status >= 200 && raw.status < 300) {
1043
1089
  const data = raw.body.trim() ? JSON.parse(raw.body) : undefined;
1044
1090
  return { status: raw.status, data };
1045
1091
  }
1046
- if (raw.status === 404) {
1092
+ if (raw.status === 404 && options?.allow404) {
1047
1093
  return { status: 404, data: undefined };
1048
1094
  }
1049
1095
  let msg = `mementos cloud ${method} ${path} \u2192 ${raw.status}`;
@@ -1075,8 +1121,19 @@ function toQuery(params) {
1075
1121
  const s = sp.toString();
1076
1122
  return s ? `?${s}` : "";
1077
1123
  }
1078
- var ApiRequestError, DEFAULT_TIMEOUT_S = "45";
1124
+ 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";
1079
1125
  var init_api_mode = __esm(() => {
1126
+ API_URL_ENV_KEYS = ["HASNA_MEMENTOS_API_URL", "MEMENTOS_API_URL"];
1127
+ API_KEY_ENV_KEYS = ["HASNA_MEMENTOS_API_KEY", "MEMENTOS_API_KEY"];
1128
+ DATABASE_URL_ENV_KEYS = ["HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"];
1129
+ DB_PATH_ENV_KEYS = ["HASNA_MEMENTOS_DB_PATH", "MEMENTOS_DB_PATH"];
1130
+ MementosStoreConfigError = class MementosStoreConfigError extends Error {
1131
+ code = "MEMENTOS_STORE_CONFIG";
1132
+ constructor(message) {
1133
+ super(message);
1134
+ this.name = "MementosStoreConfigError";
1135
+ }
1136
+ };
1080
1137
  ApiRequestError = class ApiRequestError extends Error {
1081
1138
  status;
1082
1139
  body;
@@ -49293,7 +49350,10 @@ function parseMemoryRow(row) {
49293
49350
  }
49294
49351
  function createMemory(input, dedupeMode = "merge", db) {
49295
49352
  if (!db && isApiMode()) {
49296
- const { data } = apiJson("POST", "/memories", { ...input, dedupe: dedupeMode });
49353
+ const { status, data } = apiJson("POST", "/memories", { ...input, dedupe: dedupeMode });
49354
+ if (!data || !data.id) {
49355
+ throw new ApiRequestError(`mementos cloud POST /memories \u2192 ${status} but no memory was returned; the write did not persist (key: ${input.key})`, status, "");
49356
+ }
49297
49357
  return data;
49298
49358
  }
49299
49359
  const d = db || getDatabase();
@@ -49450,7 +49510,7 @@ function listMemoriesByKey(key, db) {
49450
49510
  }
49451
49511
  function getMemory(id, db) {
49452
49512
  if (!db && isApiMode()) {
49453
- const { status, data } = apiJson("GET", `/memories/${encodeURIComponent(id)}`);
49513
+ const { status, data } = apiJson("GET", `/memories/${encodeURIComponent(id)}`, undefined, { allow404: true });
49454
49514
  return status === 404 ? null : data ?? null;
49455
49515
  }
49456
49516
  const d = db || getDatabase();
@@ -49674,7 +49734,7 @@ function listMemories(filter, db) {
49674
49734
  }
49675
49735
  function updateMemory(id, input, db) {
49676
49736
  if (!db && isApiMode()) {
49677
- const { status, data } = apiJson("PATCH", `/memories/${encodeURIComponent(id)}`, input);
49737
+ const { status, data } = apiJson("PATCH", `/memories/${encodeURIComponent(id)}`, input, { allow404: true });
49678
49738
  if (status === 404)
49679
49739
  throw new MemoryNotFoundError(id);
49680
49740
  return data;
@@ -49782,7 +49842,7 @@ function updateMemory(id, input, db) {
49782
49842
  }
49783
49843
  function deleteMemory(id, db) {
49784
49844
  if (!db && isApiMode()) {
49785
- const { status } = apiJson("DELETE", `/memories/${encodeURIComponent(id)}`);
49845
+ const { status } = apiJson("DELETE", `/memories/${encodeURIComponent(id)}`, undefined, { allow404: true });
49786
49846
  return status !== 404;
49787
49847
  }
49788
49848
  const d = db || getDatabase();
@@ -49952,7 +50012,7 @@ function registerAgent(name, sessionId, description, role, projectId, db) {
49952
50012
  }
49953
50013
  function getAgent(idOrName, db) {
49954
50014
  if (!db && isApiMode()) {
49955
- const { status, data } = apiJson("GET", `/agents/${encodeURIComponent(idOrName)}`);
50015
+ const { status, data } = apiJson("GET", `/agents/${encodeURIComponent(idOrName)}`, undefined, { allow404: true });
49956
50016
  if (status === 404 || !data)
49957
50017
  return null;
49958
50018
  return data;
@@ -50005,7 +50065,7 @@ function listAgentsByProject(projectId, db) {
50005
50065
  }
50006
50066
  function updateAgent(id, updates, db) {
50007
50067
  if (!db && isApiMode()) {
50008
- const { status, data } = apiJson("PATCH", `/agents/${encodeURIComponent(id)}`, updates);
50068
+ const { status, data } = apiJson("PATCH", `/agents/${encodeURIComponent(id)}`, updates, { allow404: true });
50009
50069
  if (status === 404 || !data)
50010
50070
  return null;
50011
50071
  return data;
@@ -50112,7 +50172,7 @@ function acquireLock(agentId, resourceType, resourceId, lockType = "exclusive",
50112
50172
  }
50113
50173
  function releaseLock(lockId, agentId, db) {
50114
50174
  if (!db && isApiMode()) {
50115
- const { status } = apiJson("DELETE", `/locks/${encodeURIComponent(lockId)}`, { agent_id: agentId });
50175
+ const { status } = apiJson("DELETE", `/locks/${encodeURIComponent(lockId)}`, { agent_id: agentId }, { allow404: true });
50116
50176
  return status !== 404;
50117
50177
  }
50118
50178
  const d = db || getDatabase();
@@ -50321,7 +50381,7 @@ function registerProject(name, path, description, memoryPrefix, db) {
50321
50381
  }
50322
50382
  function getProject(idOrPath, db) {
50323
50383
  if (!db && isApiMode()) {
50324
- const { status, data } = apiJson("GET", `/projects/${encodeURIComponent(idOrPath)}`);
50384
+ const { status, data } = apiJson("GET", `/projects/${encodeURIComponent(idOrPath)}`, undefined, { allow404: true });
50325
50385
  if (status === 404 || !data)
50326
50386
  return null;
50327
50387
  return data;
@@ -50514,7 +50574,7 @@ function createEntity(input, db) {
50514
50574
  }
50515
50575
  function getEntity(id, db) {
50516
50576
  if (!db && isApiMode()) {
50517
- const { status, data } = apiJson("GET", `/entities/${encodeURIComponent(id)}`);
50577
+ const { status, data } = apiJson("GET", `/entities/${encodeURIComponent(id)}`, undefined, { allow404: true });
50518
50578
  if (status === 404 || !data)
50519
50579
  throw new EntityNotFoundError(id);
50520
50580
  return data;
@@ -50597,7 +50657,7 @@ function listEntities(filter = {}, db) {
50597
50657
  }
50598
50658
  function updateEntity(id, input, db) {
50599
50659
  if (!db && isApiMode()) {
50600
- const { status, data } = apiJson("PATCH", `/entities/${encodeURIComponent(id)}`, input);
50660
+ const { status, data } = apiJson("PATCH", `/entities/${encodeURIComponent(id)}`, input, { allow404: true });
50601
50661
  if (status === 404 || !data)
50602
50662
  throw new EntityNotFoundError(id);
50603
50663
  return data;
@@ -50630,7 +50690,7 @@ function updateEntity(id, input, db) {
50630
50690
  }
50631
50691
  function deleteEntity(id, db) {
50632
50692
  if (!db && isApiMode()) {
50633
- const { status } = apiJson("DELETE", `/entities/${encodeURIComponent(id)}`);
50693
+ const { status } = apiJson("DELETE", `/entities/${encodeURIComponent(id)}`, undefined, { allow404: true });
50634
50694
  if (status === 404)
50635
50695
  throw new EntityNotFoundError(id);
50636
50696
  return;
@@ -52691,7 +52751,7 @@ function createRelation(input, db) {
52691
52751
  }
52692
52752
  function getRelation(id, db) {
52693
52753
  if (!db && isApiMode()) {
52694
- const { status, data } = apiJson("GET", `/relations/${encodeURIComponent(id)}`);
52754
+ const { status, data } = apiJson("GET", `/relations/${encodeURIComponent(id)}`, undefined, { allow404: true });
52695
52755
  if (status === 404 || !data)
52696
52756
  throw new Error(`Relation not found: ${id}`);
52697
52757
  return data;
@@ -52736,7 +52796,7 @@ function listRelations(filter, db) {
52736
52796
  }
52737
52797
  function deleteRelation(id, db) {
52738
52798
  if (!db && isApiMode()) {
52739
- const { status } = apiJson("DELETE", `/relations/${encodeURIComponent(id)}`);
52799
+ const { status } = apiJson("DELETE", `/relations/${encodeURIComponent(id)}`, undefined, { allow404: true });
52740
52800
  if (status === 404)
52741
52801
  throw new Error(`Relation not found: ${id}`);
52742
52802
  return;
@@ -0,0 +1,20 @@
1
+ export interface EnumViolation {
2
+ field: string;
3
+ value: string;
4
+ allowed: readonly string[];
5
+ }
6
+ /** Human-readable, actionable rejection message naming the field and the accepted set. */
7
+ export declare function formatEnumViolation(v: EnumViolation): string;
8
+ /**
9
+ * Validate one enum-constrained field. Returns the violation, or `null` when
10
+ * the value is acceptable. `undefined`/`null` are acceptable — they mean "leave
11
+ * the column at its default", which is a separate concern from a bad value.
12
+ */
13
+ export declare function validateEnumField(field: string, value: unknown): EnumViolation | null;
14
+ /**
15
+ * Validate every enum-constrained field present on a create/update payload.
16
+ * Returns the first violation so the caller can fail fast with one clear
17
+ * message, or `null` when the payload is clean.
18
+ */
19
+ export declare function validateMemoryEnums(input: Record<string, unknown>): EnumViolation | null;
20
+ //# sourceMappingURL=enum-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enum-validation.d.ts","sourceRoot":"","sources":["../../src/lib/enum-validation.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAMrF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,GAAG,IAAI,CAOxF"}