@hasna/mementos 0.14.74 → 0.14.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsN5D"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4N5D"}
package/dist/cli/index.js CHANGED
@@ -4130,6 +4130,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
4130
4130
  `
4131
4131
  ${MEMORY_VERSION_SNAPSHOT_TRIGGER}
4132
4132
  INSERT OR IGNORE INTO _migrations (id) VALUES (36);
4133
+ `,
4134
+ `
4135
+ DROP TRIGGER IF EXISTS audit_memory_insert;
4136
+ CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
4137
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
4138
+ VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
4139
+ END;
4140
+
4141
+ DROP TRIGGER IF EXISTS audit_memory_update;
4142
+ CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
4143
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
4144
+ VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
4145
+ json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
4146
+ datetime('now'));
4147
+ END;
4148
+
4149
+ DROP TRIGGER IF EXISTS audit_memory_delete;
4150
+ CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
4151
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
4152
+ VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
4153
+ END;
4154
+
4155
+ UPDATE memory_audit_log SET old_value_hash = NULL
4156
+ WHERE old_value_hash IS NOT NULL
4157
+ AND length(old_value_hash) = 32
4158
+ AND old_value_hash NOT GLOB '*[^0-9A-F]*';
4159
+
4160
+ UPDATE memory_audit_log SET new_value_hash = NULL
4161
+ WHERE new_value_hash IS NOT NULL
4162
+ AND length(new_value_hash) = 32
4163
+ AND new_value_hash NOT GLOB '*[^0-9A-F]*';
4164
+
4165
+ INSERT OR IGNORE INTO _migrations (id) VALUES (37);
4133
4166
  `
4134
4167
  ];
4135
4168
  });
@@ -6708,13 +6741,46 @@ function getAgent(idOrName, db) {
6708
6741
  return parseAgentRow(rows[0]);
6709
6742
  return null;
6710
6743
  }
6711
- function listAgents(db) {
6712
- if (!db && isApiMode()) {
6713
- const { data } = apiJson("GET", "/agents");
6744
+ function normalizedAgentListFilter(filter) {
6745
+ const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
6746
+ const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
6747
+ return { limit, offset };
6748
+ }
6749
+ function isDatabaseAdapter(value) {
6750
+ return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
6751
+ }
6752
+ function paginatedAgentQuery(baseSql, baseParams, filter) {
6753
+ const { limit, offset } = normalizedAgentListFilter(filter);
6754
+ let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
6755
+ const params = [...baseParams];
6756
+ if (limit !== undefined) {
6757
+ sql += " LIMIT ?";
6758
+ params.push(limit);
6759
+ } else if ((offset ?? 0) > 0) {
6760
+ sql += " LIMIT ?";
6761
+ params.push(UNBOUNDED_AGENT_LIST_LIMIT);
6762
+ }
6763
+ if ((offset ?? 0) > 0) {
6764
+ sql += " OFFSET ?";
6765
+ params.push(offset);
6766
+ }
6767
+ return { sql, params };
6768
+ }
6769
+ function listAgents(filterOrDb = {}, db) {
6770
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
6771
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
6772
+ const normalized = normalizedAgentListFilter(filter);
6773
+ if (!explicitDb && isApiMode()) {
6774
+ const q = toQuery({
6775
+ limit: normalized.limit,
6776
+ offset: normalized.offset
6777
+ });
6778
+ const { data } = apiJson("GET", `/agents${q}`);
6714
6779
  return data?.agents ?? [];
6715
6780
  }
6716
- const d = db || getDatabase();
6717
- const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
6781
+ const d = explicitDb || getDatabase();
6782
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
6783
+ const rows = d.query(sql).all(...params);
6718
6784
  return rows.map(parseAgentRow);
6719
6785
  }
6720
6786
  function touchAgent(idOrName, db) {
@@ -6731,15 +6797,19 @@ function touchAgent(idOrName, db) {
6731
6797
  return;
6732
6798
  d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
6733
6799
  }
6734
- function listAgentsByProject(projectId, db) {
6735
- if (!db && isApiMode()) {
6736
- const q = toQuery({ project_id: projectId });
6800
+ function listAgentsByProject(projectId, filterOrDb = {}, db) {
6801
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
6802
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
6803
+ const normalized = normalizedAgentListFilter(filter);
6804
+ if (!explicitDb && isApiMode()) {
6805
+ const q = toQuery({ project_id: projectId, ...normalized });
6737
6806
  const { data } = apiJson("GET", `/agents${q}`);
6738
6807
  return data?.agents ?? [];
6739
6808
  }
6740
- const d = db || getDatabase();
6809
+ const d = explicitDb || getDatabase();
6741
6810
  const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
6742
- const rows = d.query("SELECT * FROM agents WHERE active_project_id = ? ORDER BY last_seen_at DESC").all(resolvedId);
6811
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
6812
+ const rows = d.query(sql).all(...params);
6743
6813
  return rows.map(parseAgentRow);
6744
6814
  }
6745
6815
  function updateAgent(id, updates, db) {
@@ -6787,12 +6857,13 @@ function updateAgent(id, updates, db) {
6787
6857
  d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
6788
6858
  return getAgent(agent.id, d);
6789
6859
  }
6790
- var CONFLICT_WINDOW_MS;
6860
+ var CONFLICT_WINDOW_MS, UNBOUNDED_AGENT_LIST_LIMIT;
6791
6861
  var init_agents = __esm(() => {
6792
6862
  init_types();
6793
6863
  init_database();
6794
6864
  init_api_mode();
6795
6865
  CONFLICT_WINDOW_MS = 30 * 60 * 1000;
6866
+ UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
6796
6867
  });
6797
6868
 
6798
6869
  // src/lib/poll.ts
@@ -12222,6 +12293,35 @@ var init_pg_migrations = __esm(() => {
12222
12293
  EXECUTE FUNCTION snapshot_memory_version();
12223
12294
 
12224
12295
  INSERT INTO _migrations (id) VALUES (36) ON CONFLICT DO NOTHING;
12296
+ `,
12297
+ `
12298
+ CREATE OR REPLACE FUNCTION audit_memory_insert() RETURNS trigger AS $$
12299
+ BEGIN
12300
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, new_value_hash, created_at)
12301
+ VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'create', NEW.agent_id, md5(COALESCE(NEW.value, '')), NOW());
12302
+ RETURN NEW;
12303
+ END;
12304
+ $$ LANGUAGE plpgsql;
12305
+
12306
+ CREATE OR REPLACE FUNCTION audit_memory_update() RETURNS trigger AS $$
12307
+ BEGIN
12308
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, new_value_hash, changes, created_at)
12309
+ VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'update', NEW.agent_id, md5(COALESCE(OLD.value, '')), md5(COALESCE(NEW.value, '')),
12310
+ json_build_object('version_from', OLD.version, 'version_to', NEW.version, 'importance_from', OLD.importance, 'importance_to', NEW.importance)::text,
12311
+ NOW());
12312
+ RETURN NEW;
12313
+ END;
12314
+ $$ LANGUAGE plpgsql;
12315
+
12316
+ CREATE OR REPLACE FUNCTION audit_memory_delete() RETURNS trigger AS $$
12317
+ BEGIN
12318
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, created_at)
12319
+ VALUES (gen_random_uuid()::text, OLD.id, OLD.key, 'delete', OLD.agent_id, md5(COALESCE(OLD.value, '')), NOW());
12320
+ RETURN OLD;
12321
+ END;
12322
+ $$ LANGUAGE plpgsql;
12323
+
12324
+ INSERT INTO _migrations (id) VALUES (37) ON CONFLICT DO NOTHING;
12225
12325
  `
12226
12326
  ];
12227
12327
  });
@@ -62050,14 +62150,17 @@ function registerAgentCommands(program2) {
62050
62150
  program2.command("agents").description("List all registered agents").option("--limit <n>", "Max results (compact default: 20)", parseInt).option("--cursor <n>", "Cursor offset for the next page", parseInt).option("--offset <n>", "Offset for pagination", parseInt).action((opts) => {
62051
62151
  try {
62052
62152
  const globalOpts = program2.opts();
62053
- const allAgents = listAgents();
62054
62153
  const limit = positiveIntOrDefault(opts.limit, DEFAULT_COMPACT_LIMIT);
62055
62154
  const offset = cursorOrOffset(opts.cursor, opts.offset) ?? 0;
62056
- const agents = globalOpts.json ? allAgents : allAgents.slice(offset, offset + limit + 1);
62155
+ const explicitPagination = opts.limit !== undefined || opts.cursor !== undefined || opts.offset !== undefined;
62156
+ const agents = listAgents({
62157
+ limit: globalOpts.json ? explicitPagination ? limit : undefined : limit + 1,
62158
+ offset
62159
+ });
62057
62160
  const hasMore = !globalOpts.json && agents.length > limit;
62058
62161
  const displayAgents = hasMore ? agents.slice(0, limit) : agents;
62059
62162
  if (globalOpts.json) {
62060
- outputJson(allAgents);
62163
+ outputJson(agents);
62061
62164
  return;
62062
62165
  }
62063
62166
  if (displayAgents.length === 0) {
@@ -1,10 +1,16 @@
1
1
  import { SqliteAdapter as Database } from "../storage.js";
2
2
  import type { Agent } from "../types/index.js";
3
+ export interface AgentListFilter {
4
+ limit?: number;
5
+ offset?: number;
6
+ }
3
7
  export declare function registerAgent(name: string, sessionId?: string, description?: string, role?: string, projectId?: string, db?: Database): Agent;
4
8
  export declare function getAgent(idOrName: string, db?: Database): Agent | null;
5
9
  export declare function listAgents(db?: Database): Agent[];
10
+ export declare function listAgents(filter: AgentListFilter, db?: Database): Agent[];
6
11
  export declare function touchAgent(idOrName: string, db?: Database): void;
7
12
  export declare function listAgentsByProject(projectId: string, db?: Database): Agent[];
13
+ export declare function listAgentsByProject(projectId: string, filter: AgentListFilter, db?: Database): Agent[];
8
14
  export declare function updateAgent(id: string, updates: {
9
15
  name?: string;
10
16
  description?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAqB/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CA0EP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CA6Bd;AAED,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAUjD;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAahE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAa7E;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACtI,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsDd"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAQ/C,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CA0EP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CA6Bd;AA+CD,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AACnD,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AAsB5E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAahE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AAC/E,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EACvB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,EAAE,CAAC;AA0BX,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACtI,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsDd"}
@@ -9,7 +9,21 @@ export interface AuditEntry {
9
9
  memory_key: string | null;
10
10
  operation: "create" | "update" | "delete" | "archive" | "restore" | "read";
11
11
  agent_id: string | null;
12
+ /**
13
+ * md5 hex digest of the value the row held BEFORE the operation, or `null`.
14
+ *
15
+ * `null` means the digest is UNAVAILABLE, never that the value was empty. It is
16
+ * the honest answer on SQLite, which has no `md5()` and whose driver
17
+ * (`bun:sqlite`) exposes no user-defined-function API, so a trigger cannot
18
+ * compute one. Postgres computes `md5(COALESCE(...))` natively and populates it.
19
+ *
20
+ * So: compare `md5(candidate)` against a NON-NULL value to prove what a row
21
+ * held. A `null` is a statement about the BACKEND and must never be read as
22
+ * evidence about the content — treating it as a failed match is the confident
23
+ * wrong answer migration 37 exists to prevent.
24
+ */
12
25
  old_value_hash: string | null;
26
+ /** md5 hex digest of the value AFTER the operation, or `null`. See `old_value_hash`. */
13
27
  new_value_hash: string | null;
14
28
  changes: Record<string, unknown>;
15
29
  created_at: string;
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/db/audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG1D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAW,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CAMd;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,EACN,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CA6Bd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB,CAkBA"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/db/audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG1D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wFAAwF;IACxF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAW,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CAMd;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,EACN,EAAE,CAAC,EAAE,QAAQ,GACZ,UAAU,EAAE,CA6Bd;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB,CAkBA"}
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,+BAA+B,wqBAiB3C,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,MAAM,EAm9B9B,CAAC"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,+BAA+B,wqBAiB3C,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,MAAM,EAkhC9B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EA+xBjC,CAAC"}
1
+ {"version":3,"file":"pg-migrations.d.ts","sourceRoot":"","sources":["../../src/db/pg-migrations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,EAy0BjC,CAAC"}
package/dist/index.js CHANGED
@@ -2098,6 +2098,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
2098
2098
  `
2099
2099
  ${MEMORY_VERSION_SNAPSHOT_TRIGGER}
2100
2100
  INSERT OR IGNORE INTO _migrations (id) VALUES (36);
2101
+ `,
2102
+ `
2103
+ DROP TRIGGER IF EXISTS audit_memory_insert;
2104
+ CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
2105
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
2106
+ VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
2107
+ END;
2108
+
2109
+ DROP TRIGGER IF EXISTS audit_memory_update;
2110
+ CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
2111
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
2112
+ VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
2113
+ json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
2114
+ datetime('now'));
2115
+ END;
2116
+
2117
+ DROP TRIGGER IF EXISTS audit_memory_delete;
2118
+ CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
2119
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
2120
+ VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
2121
+ END;
2122
+
2123
+ UPDATE memory_audit_log SET old_value_hash = NULL
2124
+ WHERE old_value_hash IS NOT NULL
2125
+ AND length(old_value_hash) = 32
2126
+ AND old_value_hash NOT GLOB '*[^0-9A-F]*';
2127
+
2128
+ UPDATE memory_audit_log SET new_value_hash = NULL
2129
+ WHERE new_value_hash IS NOT NULL
2130
+ AND length(new_value_hash) = 32
2131
+ AND new_value_hash NOT GLOB '*[^0-9A-F]*';
2132
+
2133
+ INSERT OR IGNORE INTO _migrations (id) VALUES (37);
2101
2134
  `
2102
2135
  ];
2103
2136
  });
@@ -49995,6 +50028,7 @@ function getMemoryVersions(memoryId, db) {
49995
50028
  init_database();
49996
50029
  init_api_mode();
49997
50030
  var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
50031
+ var UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
49998
50032
  function parseAgentRow(row) {
49999
50033
  return {
50000
50034
  id: row["id"],
@@ -50086,13 +50120,46 @@ function getAgent(idOrName, db) {
50086
50120
  return parseAgentRow(rows[0]);
50087
50121
  return null;
50088
50122
  }
50089
- function listAgents(db) {
50090
- if (!db && isApiMode()) {
50091
- const { data } = apiJson("GET", "/agents");
50123
+ function normalizedAgentListFilter(filter) {
50124
+ const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
50125
+ const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
50126
+ return { limit, offset };
50127
+ }
50128
+ function isDatabaseAdapter(value) {
50129
+ return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
50130
+ }
50131
+ function paginatedAgentQuery(baseSql, baseParams, filter) {
50132
+ const { limit, offset } = normalizedAgentListFilter(filter);
50133
+ let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
50134
+ const params = [...baseParams];
50135
+ if (limit !== undefined) {
50136
+ sql += " LIMIT ?";
50137
+ params.push(limit);
50138
+ } else if ((offset ?? 0) > 0) {
50139
+ sql += " LIMIT ?";
50140
+ params.push(UNBOUNDED_AGENT_LIST_LIMIT);
50141
+ }
50142
+ if ((offset ?? 0) > 0) {
50143
+ sql += " OFFSET ?";
50144
+ params.push(offset);
50145
+ }
50146
+ return { sql, params };
50147
+ }
50148
+ function listAgents(filterOrDb = {}, db) {
50149
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
50150
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
50151
+ const normalized = normalizedAgentListFilter(filter);
50152
+ if (!explicitDb && isApiMode()) {
50153
+ const q = toQuery({
50154
+ limit: normalized.limit,
50155
+ offset: normalized.offset
50156
+ });
50157
+ const { data } = apiJson("GET", `/agents${q}`);
50092
50158
  return data?.agents ?? [];
50093
50159
  }
50094
- const d = db || getDatabase();
50095
- const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
50160
+ const d = explicitDb || getDatabase();
50161
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
50162
+ const rows = d.query(sql).all(...params);
50096
50163
  return rows.map(parseAgentRow);
50097
50164
  }
50098
50165
  function touchAgent(idOrName, db) {
@@ -50109,15 +50176,19 @@ function touchAgent(idOrName, db) {
50109
50176
  return;
50110
50177
  d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
50111
50178
  }
50112
- function listAgentsByProject(projectId, db) {
50113
- if (!db && isApiMode()) {
50114
- const q = toQuery({ project_id: projectId });
50179
+ function listAgentsByProject(projectId, filterOrDb = {}, db) {
50180
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
50181
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
50182
+ const normalized = normalizedAgentListFilter(filter);
50183
+ if (!explicitDb && isApiMode()) {
50184
+ const q = toQuery({ project_id: projectId, ...normalized });
50115
50185
  const { data } = apiJson("GET", `/agents${q}`);
50116
50186
  return data?.agents ?? [];
50117
50187
  }
50118
- const d = db || getDatabase();
50188
+ const d = explicitDb || getDatabase();
50119
50189
  const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
50120
- const rows = d.query("SELECT * FROM agents WHERE active_project_id = ? ORDER BY last_seen_at DESC").all(resolvedId);
50190
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
50191
+ const rows = d.query(sql).all(...params);
50121
50192
  return rows.map(parseAgentRow);
50122
50193
  }
50123
50194
  function updateAgent(id, updates, db) {
package/dist/mcp/index.js CHANGED
@@ -2161,6 +2161,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
2161
2161
  `
2162
2162
  ${MEMORY_VERSION_SNAPSHOT_TRIGGER}
2163
2163
  INSERT OR IGNORE INTO _migrations (id) VALUES (36);
2164
+ `,
2165
+ `
2166
+ DROP TRIGGER IF EXISTS audit_memory_insert;
2167
+ CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
2168
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
2169
+ VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
2170
+ END;
2171
+
2172
+ DROP TRIGGER IF EXISTS audit_memory_update;
2173
+ CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
2174
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
2175
+ VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
2176
+ json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
2177
+ datetime('now'));
2178
+ END;
2179
+
2180
+ DROP TRIGGER IF EXISTS audit_memory_delete;
2181
+ CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
2182
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
2183
+ VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
2184
+ END;
2185
+
2186
+ UPDATE memory_audit_log SET old_value_hash = NULL
2187
+ WHERE old_value_hash IS NOT NULL
2188
+ AND length(old_value_hash) = 32
2189
+ AND old_value_hash NOT GLOB '*[^0-9A-F]*';
2190
+
2191
+ UPDATE memory_audit_log SET new_value_hash = NULL
2192
+ WHERE new_value_hash IS NOT NULL
2193
+ AND length(new_value_hash) = 32
2194
+ AND new_value_hash NOT GLOB '*[^0-9A-F]*';
2195
+
2196
+ INSERT OR IGNORE INTO _migrations (id) VALUES (37);
2164
2197
  `
2165
2198
  ];
2166
2199
  });
@@ -13016,6 +13049,35 @@ var init_pg_migrations = __esm(() => {
13016
13049
  EXECUTE FUNCTION snapshot_memory_version();
13017
13050
 
13018
13051
  INSERT INTO _migrations (id) VALUES (36) ON CONFLICT DO NOTHING;
13052
+ `,
13053
+ `
13054
+ CREATE OR REPLACE FUNCTION audit_memory_insert() RETURNS trigger AS $$
13055
+ BEGIN
13056
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, new_value_hash, created_at)
13057
+ VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'create', NEW.agent_id, md5(COALESCE(NEW.value, '')), NOW());
13058
+ RETURN NEW;
13059
+ END;
13060
+ $$ LANGUAGE plpgsql;
13061
+
13062
+ CREATE OR REPLACE FUNCTION audit_memory_update() RETURNS trigger AS $$
13063
+ BEGIN
13064
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, new_value_hash, changes, created_at)
13065
+ VALUES (gen_random_uuid()::text, NEW.id, NEW.key, 'update', NEW.agent_id, md5(COALESCE(OLD.value, '')), md5(COALESCE(NEW.value, '')),
13066
+ json_build_object('version_from', OLD.version, 'version_to', NEW.version, 'importance_from', OLD.importance, 'importance_to', NEW.importance)::text,
13067
+ NOW());
13068
+ RETURN NEW;
13069
+ END;
13070
+ $$ LANGUAGE plpgsql;
13071
+
13072
+ CREATE OR REPLACE FUNCTION audit_memory_delete() RETURNS trigger AS $$
13073
+ BEGIN
13074
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, old_value_hash, created_at)
13075
+ VALUES (gen_random_uuid()::text, OLD.id, OLD.key, 'delete', OLD.agent_id, md5(COALESCE(OLD.value, '')), NOW());
13076
+ RETURN OLD;
13077
+ END;
13078
+ $$ LANGUAGE plpgsql;
13079
+
13080
+ INSERT INTO _migrations (id) VALUES (37) ON CONFLICT DO NOTHING;
13019
13081
  `
13020
13082
  ];
13021
13083
  });
@@ -55743,6 +55805,7 @@ init_types();
55743
55805
  init_database();
55744
55806
  init_api_mode();
55745
55807
  var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
55808
+ var UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
55746
55809
  function parseAgentRow(row) {
55747
55810
  return {
55748
55811
  id: row["id"],
@@ -55834,13 +55897,46 @@ function getAgent(idOrName, db) {
55834
55897
  return parseAgentRow(rows[0]);
55835
55898
  return null;
55836
55899
  }
55837
- function listAgents(db) {
55838
- if (!db && isApiMode()) {
55839
- const { data } = apiJson("GET", "/agents");
55900
+ function normalizedAgentListFilter(filter) {
55901
+ const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
55902
+ const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
55903
+ return { limit, offset };
55904
+ }
55905
+ function isDatabaseAdapter(value) {
55906
+ return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
55907
+ }
55908
+ function paginatedAgentQuery(baseSql, baseParams, filter) {
55909
+ const { limit, offset } = normalizedAgentListFilter(filter);
55910
+ let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
55911
+ const params = [...baseParams];
55912
+ if (limit !== undefined) {
55913
+ sql += " LIMIT ?";
55914
+ params.push(limit);
55915
+ } else if ((offset ?? 0) > 0) {
55916
+ sql += " LIMIT ?";
55917
+ params.push(UNBOUNDED_AGENT_LIST_LIMIT);
55918
+ }
55919
+ if ((offset ?? 0) > 0) {
55920
+ sql += " OFFSET ?";
55921
+ params.push(offset);
55922
+ }
55923
+ return { sql, params };
55924
+ }
55925
+ function listAgents(filterOrDb = {}, db) {
55926
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
55927
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
55928
+ const normalized = normalizedAgentListFilter(filter);
55929
+ if (!explicitDb && isApiMode()) {
55930
+ const q = toQuery({
55931
+ limit: normalized.limit,
55932
+ offset: normalized.offset
55933
+ });
55934
+ const { data } = apiJson("GET", `/agents${q}`);
55840
55935
  return data?.agents ?? [];
55841
55936
  }
55842
- const d = db || getDatabase();
55843
- const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
55937
+ const d = explicitDb || getDatabase();
55938
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
55939
+ const rows = d.query(sql).all(...params);
55844
55940
  return rows.map(parseAgentRow);
55845
55941
  }
55846
55942
  function touchAgent(idOrName, db) {
@@ -55857,15 +55953,19 @@ function touchAgent(idOrName, db) {
55857
55953
  return;
55858
55954
  d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
55859
55955
  }
55860
- function listAgentsByProject(projectId, db) {
55861
- if (!db && isApiMode()) {
55862
- const q = toQuery({ project_id: projectId });
55956
+ function listAgentsByProject(projectId, filterOrDb = {}, db) {
55957
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
55958
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
55959
+ const normalized = normalizedAgentListFilter(filter);
55960
+ if (!explicitDb && isApiMode()) {
55961
+ const q = toQuery({ project_id: projectId, ...normalized });
55863
55962
  const { data } = apiJson("GET", `/agents${q}`);
55864
55963
  return data?.agents ?? [];
55865
55964
  }
55866
- const d = db || getDatabase();
55965
+ const d = explicitDb || getDatabase();
55867
55966
  const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
55868
- const rows = d.query("SELECT * FROM agents WHERE active_project_id = ? ORDER BY last_seen_at DESC").all(resolvedId);
55967
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
55968
+ const rows = d.query(sql).all(...params);
55869
55969
  return rows.map(parseAgentRow);
55870
55970
  }
55871
55971
  function updateAgent(id, updates, db) {
@@ -1722,6 +1722,39 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
1722
1722
  `
1723
1723
  ${MEMORY_VERSION_SNAPSHOT_TRIGGER}
1724
1724
  INSERT OR IGNORE INTO _migrations (id) VALUES (36);
1725
+ `,
1726
+ `
1727
+ DROP TRIGGER IF EXISTS audit_memory_insert;
1728
+ CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
1729
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
1730
+ VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
1731
+ END;
1732
+
1733
+ DROP TRIGGER IF EXISTS audit_memory_update;
1734
+ CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
1735
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
1736
+ VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
1737
+ json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
1738
+ datetime('now'));
1739
+ END;
1740
+
1741
+ DROP TRIGGER IF EXISTS audit_memory_delete;
1742
+ CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
1743
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
1744
+ VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
1745
+ END;
1746
+
1747
+ UPDATE memory_audit_log SET old_value_hash = NULL
1748
+ WHERE old_value_hash IS NOT NULL
1749
+ AND length(old_value_hash) = 32
1750
+ AND old_value_hash NOT GLOB '*[^0-9A-F]*';
1751
+
1752
+ UPDATE memory_audit_log SET new_value_hash = NULL
1753
+ WHERE new_value_hash IS NOT NULL
1754
+ AND length(new_value_hash) = 32
1755
+ AND new_value_hash NOT GLOB '*[^0-9A-F]*';
1756
+
1757
+ INSERT OR IGNORE INTO _migrations (id) VALUES (37);
1725
1758
  `
1726
1759
  ];
1727
1760
  });
@@ -56147,6 +56180,7 @@ init_types();
56147
56180
  init_database();
56148
56181
  init_api_mode();
56149
56182
  var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
56183
+ var UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
56150
56184
  function parseAgentRow(row) {
56151
56185
  return {
56152
56186
  id: row["id"],
@@ -56238,24 +56272,61 @@ function getAgent(idOrName, db) {
56238
56272
  return parseAgentRow(rows[0]);
56239
56273
  return null;
56240
56274
  }
56241
- function listAgents(db) {
56242
- if (!db && isApiMode()) {
56243
- const { data } = apiJson("GET", "/agents");
56275
+ function normalizedAgentListFilter(filter) {
56276
+ const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
56277
+ const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
56278
+ return { limit, offset };
56279
+ }
56280
+ function isDatabaseAdapter(value) {
56281
+ return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
56282
+ }
56283
+ function paginatedAgentQuery(baseSql, baseParams, filter) {
56284
+ const { limit, offset } = normalizedAgentListFilter(filter);
56285
+ let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
56286
+ const params = [...baseParams];
56287
+ if (limit !== undefined) {
56288
+ sql += " LIMIT ?";
56289
+ params.push(limit);
56290
+ } else if ((offset ?? 0) > 0) {
56291
+ sql += " LIMIT ?";
56292
+ params.push(UNBOUNDED_AGENT_LIST_LIMIT);
56293
+ }
56294
+ if ((offset ?? 0) > 0) {
56295
+ sql += " OFFSET ?";
56296
+ params.push(offset);
56297
+ }
56298
+ return { sql, params };
56299
+ }
56300
+ function listAgents(filterOrDb = {}, db) {
56301
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
56302
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
56303
+ const normalized = normalizedAgentListFilter(filter);
56304
+ if (!explicitDb && isApiMode()) {
56305
+ const q = toQuery({
56306
+ limit: normalized.limit,
56307
+ offset: normalized.offset
56308
+ });
56309
+ const { data } = apiJson("GET", `/agents${q}`);
56244
56310
  return data?.agents ?? [];
56245
56311
  }
56246
- const d = db || getDatabase();
56247
- const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
56312
+ const d = explicitDb || getDatabase();
56313
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
56314
+ const rows = d.query(sql).all(...params);
56248
56315
  return rows.map(parseAgentRow);
56249
56316
  }
56250
- function listAgentsByProject(projectId, db) {
56251
- if (!db && isApiMode()) {
56252
- const q = toQuery({ project_id: projectId });
56317
+ function listAgentsByProject(projectId, filterOrDb = {}, db) {
56318
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
56319
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
56320
+ const normalized = normalizedAgentListFilter(filter);
56321
+ if (!explicitDb && isApiMode()) {
56322
+ const q = toQuery({ project_id: projectId, ...normalized });
56253
56323
  const { data } = apiJson("GET", `/agents${q}`);
56254
56324
  return data?.agents ?? [];
56255
56325
  }
56256
- const d = db || getDatabase();
56326
+ const d = explicitDb || getDatabase();
56257
56327
  const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
56258
- const rows = d.query("SELECT * FROM agents WHERE active_project_id = ? ORDER BY last_seen_at DESC").all(resolvedId);
56328
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
56329
+ const rows = d.query(sql).all(...params);
56259
56330
  return rows.map(parseAgentRow);
56260
56331
  }
56261
56332
  function updateAgent(id, updates, db) {
@@ -56418,9 +56489,21 @@ function cleanExpiredLocks(db) {
56418
56489
 
56419
56490
  // src/server/routes/agents.ts
56420
56491
  init_router();
56492
+ function paginationInt(value) {
56493
+ if (value === undefined)
56494
+ return;
56495
+ const parsed = Number(value);
56496
+ if (!Number.isFinite(parsed) || parsed < 0)
56497
+ return;
56498
+ return Math.floor(parsed);
56499
+ }
56421
56500
  addRoute("GET", "/api/agents", (_req, url) => {
56422
56501
  const q = getSearchParams(url);
56423
- const agents = q["project_id"] ? listAgentsByProject(q["project_id"]) : listAgents();
56502
+ const pagination = {
56503
+ limit: paginationInt(q["limit"]),
56504
+ offset: paginationInt(q["cursor"] ?? q["offset"])
56505
+ };
56506
+ const agents = q["project_id"] ? listAgentsByProject(q["project_id"], pagination) : listAgents(pagination);
56424
56507
  if (q["fields"]) {
56425
56508
  const fields = q["fields"].split(",").map((f) => f.trim());
56426
56509
  const filtered = agents.map((a) => Object.fromEntries(fields.map((f) => [f, a[f]]).filter(([, v]) => v !== undefined)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.14.74",
3
+ "version": "0.14.75",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",