@hasna/mementos 0.14.47 → 0.14.49

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 (38) hide show
  1. package/Dockerfile.package +32 -0
  2. package/bun.lock +405 -0
  3. package/dist/cli/index.js +141 -70
  4. package/dist/db/pg-migrations.d.ts.map +1 -1
  5. package/dist/generated/storage-kit/health.d.ts +20 -0
  6. package/dist/generated/storage-kit/health.d.ts.map +1 -0
  7. package/dist/generated/storage-kit/index.d.ts +8 -0
  8. package/dist/generated/storage-kit/index.d.ts.map +1 -0
  9. package/dist/generated/storage-kit/migrations.d.ts +48 -0
  10. package/dist/generated/storage-kit/migrations.d.ts.map +1 -0
  11. package/dist/generated/storage-kit/mode.d.ts +48 -0
  12. package/dist/generated/storage-kit/mode.d.ts.map +1 -0
  13. package/dist/generated/storage-kit/pool.d.ts +34 -0
  14. package/dist/generated/storage-kit/pool.d.ts.map +1 -0
  15. package/dist/generated/storage-kit/query.d.ts +36 -0
  16. package/dist/generated/storage-kit/query.d.ts.map +1 -0
  17. package/dist/generated/storage-kit/tls.d.ts +26 -0
  18. package/dist/generated/storage-kit/tls.d.ts.map +1 -0
  19. package/dist/index.js +96 -64
  20. package/dist/mcp/index.js +135 -64
  21. package/dist/pg-sync-worker.d.ts +2 -0
  22. package/dist/pg-sync-worker.d.ts.map +1 -0
  23. package/dist/pg-sync-worker.js +47 -0
  24. package/dist/sdk/index.d.ts +27 -0
  25. package/dist/sdk/index.d.ts.map +1 -1
  26. package/dist/sdk/index.js +25 -6
  27. package/dist/server/auth.d.ts +11 -0
  28. package/dist/server/auth.d.ts.map +1 -0
  29. package/dist/server/index.d.ts.map +1 -1
  30. package/dist/server/index.js +362 -129
  31. package/dist/server/openapi.d.ts +2 -0
  32. package/dist/server/openapi.d.ts.map +1 -0
  33. package/dist/storage.d.ts +36 -3
  34. package/dist/storage.d.ts.map +1 -1
  35. package/dist/storage.js +99 -64
  36. package/docker-entrypoint.sh +46 -0
  37. package/hasna.contract.json +16 -0
  38. package/package.json +7 -3
package/dist/index.js CHANGED
@@ -51,6 +51,8 @@ import { Database } from "bun:sqlite";
51
51
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
52
52
  import { homedir } from "os";
53
53
  import { join } from "path";
54
+ import { fileURLToPath } from "url";
55
+ import { Worker } from "worker_threads";
54
56
  import pg from "pg";
55
57
  function normalizeParams(params) {
56
58
  const flat = params.length === 1 && Array.isArray(params[0]) ? params[0] : params;
@@ -113,13 +115,15 @@ class SqliteAdapter {
113
115
  function translateSql(sql) {
114
116
  let parameterIndex = 0;
115
117
  let translated = sql.replace(/\?/g, () => `$${++parameterIndex}`);
116
- translated = translated.replace(/datetime\s*\(\s*'now'\s*\)/gi, "NOW()");
118
+ const ISO_FMT = `'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'`;
119
+ translated = translated.replace(/datetime\s*\(\s*'now'\s*\)/gi, `to_char(now() AT TIME ZONE 'UTC', ${ISO_FMT})`);
117
120
  translated = translated.replace(/datetime\s*\(\s*'now'\s*,\s*'(-?\d+)\s+(minutes?|hours?|days?|seconds?)'\s*\)/gi, (_match, amount, unit) => {
118
121
  const parsed = parseInt(String(amount), 10);
119
122
  const absolute = Math.abs(parsed);
120
123
  const normalizedUnit = String(unit).toLowerCase().replace(/s$/, "");
121
124
  const pluralUnit = absolute === 1 ? normalizedUnit : `${normalizedUnit}s`;
122
- return parsed < 0 ? `NOW() - INTERVAL '${absolute} ${pluralUnit}'` : `NOW() + INTERVAL '${absolute} ${pluralUnit}'`;
125
+ const op = parsed < 0 ? "-" : "+";
126
+ return `to_char((now() ${op} INTERVAL '${absolute} ${pluralUnit}') AT TIME ZONE 'UTC', ${ISO_FMT})`;
123
127
  });
124
128
  translated = translated.replace(/lower\s*\(\s*hex\s*\(\s*randomblob\s*\(\s*\d+\s*\)\s*\)\s*\)/gi, "gen_random_uuid()::text");
125
129
  translated = translated.replace(/\bIFNULL\s*\(/gi, "COALESCE(");
@@ -176,56 +180,24 @@ function makePool(connectionString) {
176
180
  class PgAdapter {
177
181
  pool;
178
182
  constructor(input) {
179
- this.pool = typeof input === "string" ? makePool(input) : input;
180
- }
181
- runSync(fn) {
182
- let result;
183
- let error;
184
- let done = false;
185
- fn().then((value) => {
186
- result = value;
187
- done = true;
188
- }).catch((caught) => {
189
- error = caught;
190
- done = true;
191
- });
192
- const deadline = Date.now() + 30000;
193
- while (!done && Date.now() < deadline) {
194
- Bun.sleepSync(1);
195
- }
196
- if (error) {
197
- throw error;
198
- }
199
- if (!done) {
200
- throw new Error("PostgreSQL query timed out after 30s");
201
- }
202
- return result;
183
+ this.pool = typeof input === "string" ? new PgSyncPool(input) : input;
203
184
  }
204
185
  run(sql, ...params) {
205
- return this.runSync(async () => {
206
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
207
- return {
208
- changes: result.rowCount ?? 0,
209
- lastInsertRowid: result.rows?.[0]?.id ?? 0
210
- };
211
- });
186
+ const result = this.pool.query(translateSql(sql), normalizeParams(params));
187
+ return {
188
+ changes: result.rowCount ?? 0,
189
+ lastInsertRowid: result.rows?.[0]?.id ?? 0
190
+ };
212
191
  }
213
192
  get(sql, ...params) {
214
- return this.runSync(async () => {
215
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
216
- return result.rows[0] ?? null;
217
- });
193
+ const result = this.pool.query(translateSql(sql), normalizeParams(params));
194
+ return result.rows[0] ?? null;
218
195
  }
219
196
  all(sql, ...params) {
220
- return this.runSync(async () => {
221
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
222
- return result.rows;
223
- });
197
+ return this.pool.query(translateSql(sql), normalizeParams(params)).rows;
224
198
  }
225
199
  exec(sql) {
226
- this.runSync(async () => {
227
- await this.pool.query(sql);
228
- });
200
+ this.pool.query(sql, []);
229
201
  }
230
202
  prepare(sql) {
231
203
  return {
@@ -239,28 +211,20 @@ class PgAdapter {
239
211
  return this.prepare(sql);
240
212
  }
241
213
  close() {
242
- this.runSync(async () => {
243
- await this.pool.end();
244
- });
214
+ this.pool.end();
245
215
  }
246
216
  transaction(fn) {
247
- return this.runSync(async () => {
248
- const client = await this.pool.connect();
249
- const originalQuery = this.pool.query.bind(this.pool);
217
+ this.pool.query("BEGIN", []);
218
+ try {
219
+ const value = fn();
220
+ this.pool.query("COMMIT", []);
221
+ return value;
222
+ } catch (error) {
250
223
  try {
251
- await client.query("BEGIN");
252
- this.pool.query = client.query.bind(client);
253
- const value = fn();
254
- await client.query("COMMIT");
255
- return value;
256
- } catch (error) {
257
- await client.query("ROLLBACK");
258
- throw error;
259
- } finally {
260
- this.pool.query = originalQuery;
261
- client.release();
262
- }
263
- });
224
+ this.pool.query("ROLLBACK", []);
225
+ } catch {}
226
+ throw error;
227
+ }
264
228
  }
265
229
  get raw() {
266
230
  return this.pool;
@@ -610,7 +574,7 @@ function resetAllSyncMeta(db) {
610
574
  ensureSyncMetaTable(db);
611
575
  db.run("DELETE FROM _sync_meta");
612
576
  }
613
- var MEMENTOS_STORAGE_TABLES, STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES, MODE_ENV_NAMES, warnedDeprecatedModes, getMementosStorageStatus, SYNC_EXCLUDED_TABLE_PATTERNS, SYNC_META_TABLE_SQL = `
577
+ var PgSyncPool, MEMENTOS_STORAGE_TABLES, STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES, MODE_ENV_NAMES, warnedDeprecatedModes, getMementosStorageStatus, SYNC_EXCLUDED_TABLE_PATTERNS, SYNC_META_TABLE_SQL = `
614
578
  CREATE TABLE IF NOT EXISTS _sync_meta (
615
579
  table_name TEXT PRIMARY KEY,
616
580
  last_synced_at TEXT,
@@ -618,6 +582,74 @@ CREATE TABLE IF NOT EXISTS _sync_meta (
618
582
  direction TEXT DEFAULT 'push'
619
583
  )`;
620
584
  var init_storage = __esm(() => {
585
+ PgSyncPool = class PgSyncPool {
586
+ worker;
587
+ status;
588
+ data;
589
+ closed = false;
590
+ lastError = null;
591
+ static DATA_BYTES = 128 * 1024 * 1024;
592
+ static QUERY_TIMEOUT_MS = 60000;
593
+ static resolveWorkerPath() {
594
+ const ext = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
595
+ const here = fileURLToPath(new URL(".", import.meta.url));
596
+ const candidates = [
597
+ join(here, `pg-sync-worker${ext}`),
598
+ join(here, "..", `pg-sync-worker${ext}`),
599
+ join(here, "..", "..", `pg-sync-worker${ext}`)
600
+ ];
601
+ for (const candidate of candidates) {
602
+ if (existsSync(candidate))
603
+ return candidate;
604
+ }
605
+ return candidates[0];
606
+ }
607
+ constructor(connectionString) {
608
+ const control = new SharedArrayBuffer(8);
609
+ const dataSab = new SharedArrayBuffer(PgSyncPool.DATA_BYTES);
610
+ this.status = new Int32Array(control);
611
+ this.data = new Uint8Array(dataSab);
612
+ this.worker = new Worker(PgSyncPool.resolveWorkerPath(), {
613
+ workerData: {
614
+ dsn: stripSslParams(connectionString),
615
+ ssl: sslConfigFor(connectionString),
616
+ control,
617
+ data: dataSab
618
+ }
619
+ });
620
+ this.worker.unref();
621
+ this.worker.on("error", (err) => {
622
+ this.lastError = err;
623
+ });
624
+ }
625
+ query(sql, params) {
626
+ if (this.closed)
627
+ throw new Error("PgSyncPool is closed");
628
+ if (this.lastError)
629
+ throw this.lastError;
630
+ Atomics.store(this.status, 0, 0);
631
+ this.worker.postMessage({ sql, params });
632
+ const waitResult = Atomics.wait(this.status, 0, 0, PgSyncPool.QUERY_TIMEOUT_MS);
633
+ const code = Atomics.load(this.status, 0);
634
+ if (code === 0 || waitResult === "timed-out") {
635
+ if (this.lastError)
636
+ throw this.lastError;
637
+ throw new Error("PostgreSQL query timed out after 60s");
638
+ }
639
+ const len = Atomics.load(this.status, 1);
640
+ const payload = JSON.parse(new TextDecoder().decode(this.data.subarray(0, len)));
641
+ if (code === 2) {
642
+ throw new Error(payload.message ?? "PostgreSQL error");
643
+ }
644
+ return payload;
645
+ }
646
+ end() {
647
+ if (this.closed)
648
+ return;
649
+ this.closed = true;
650
+ this.worker.terminate();
651
+ }
652
+ };
621
653
  MEMENTOS_STORAGE_TABLES = [
622
654
  "projects",
623
655
  "agents",
package/dist/mcp/index.js CHANGED
@@ -139,6 +139,8 @@ import { Database } from "bun:sqlite";
139
139
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
140
140
  import { homedir } from "os";
141
141
  import { join } from "path";
142
+ import { fileURLToPath } from "url";
143
+ import { Worker } from "worker_threads";
142
144
  import pg from "pg";
143
145
  function normalizeParams(params) {
144
146
  const flat = params.length === 1 && Array.isArray(params[0]) ? params[0] : params;
@@ -201,13 +203,15 @@ class SqliteAdapter {
201
203
  function translateSql(sql) {
202
204
  let parameterIndex = 0;
203
205
  let translated = sql.replace(/\?/g, () => `$${++parameterIndex}`);
204
- translated = translated.replace(/datetime\s*\(\s*'now'\s*\)/gi, "NOW()");
206
+ const ISO_FMT = `'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'`;
207
+ translated = translated.replace(/datetime\s*\(\s*'now'\s*\)/gi, `to_char(now() AT TIME ZONE 'UTC', ${ISO_FMT})`);
205
208
  translated = translated.replace(/datetime\s*\(\s*'now'\s*,\s*'(-?\d+)\s+(minutes?|hours?|days?|seconds?)'\s*\)/gi, (_match, amount, unit) => {
206
209
  const parsed = parseInt(String(amount), 10);
207
210
  const absolute = Math.abs(parsed);
208
211
  const normalizedUnit = String(unit).toLowerCase().replace(/s$/, "");
209
212
  const pluralUnit = absolute === 1 ? normalizedUnit : `${normalizedUnit}s`;
210
- return parsed < 0 ? `NOW() - INTERVAL '${absolute} ${pluralUnit}'` : `NOW() + INTERVAL '${absolute} ${pluralUnit}'`;
213
+ const op = parsed < 0 ? "-" : "+";
214
+ return `to_char((now() ${op} INTERVAL '${absolute} ${pluralUnit}') AT TIME ZONE 'UTC', ${ISO_FMT})`;
211
215
  });
212
216
  translated = translated.replace(/lower\s*\(\s*hex\s*\(\s*randomblob\s*\(\s*\d+\s*\)\s*\)\s*\)/gi, "gen_random_uuid()::text");
213
217
  translated = translated.replace(/\bIFNULL\s*\(/gi, "COALESCE(");
@@ -264,56 +268,24 @@ function makePool(connectionString) {
264
268
  class PgAdapter {
265
269
  pool;
266
270
  constructor(input) {
267
- this.pool = typeof input === "string" ? makePool(input) : input;
268
- }
269
- runSync(fn) {
270
- let result;
271
- let error;
272
- let done = false;
273
- fn().then((value) => {
274
- result = value;
275
- done = true;
276
- }).catch((caught) => {
277
- error = caught;
278
- done = true;
279
- });
280
- const deadline = Date.now() + 30000;
281
- while (!done && Date.now() < deadline) {
282
- Bun.sleepSync(1);
283
- }
284
- if (error) {
285
- throw error;
286
- }
287
- if (!done) {
288
- throw new Error("PostgreSQL query timed out after 30s");
289
- }
290
- return result;
271
+ this.pool = typeof input === "string" ? new PgSyncPool(input) : input;
291
272
  }
292
273
  run(sql, ...params) {
293
- return this.runSync(async () => {
294
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
295
- return {
296
- changes: result.rowCount ?? 0,
297
- lastInsertRowid: result.rows?.[0]?.id ?? 0
298
- };
299
- });
274
+ const result = this.pool.query(translateSql(sql), normalizeParams(params));
275
+ return {
276
+ changes: result.rowCount ?? 0,
277
+ lastInsertRowid: result.rows?.[0]?.id ?? 0
278
+ };
300
279
  }
301
280
  get(sql, ...params) {
302
- return this.runSync(async () => {
303
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
304
- return result.rows[0] ?? null;
305
- });
281
+ const result = this.pool.query(translateSql(sql), normalizeParams(params));
282
+ return result.rows[0] ?? null;
306
283
  }
307
284
  all(sql, ...params) {
308
- return this.runSync(async () => {
309
- const result = await this.pool.query(translateSql(sql), normalizeParams(params));
310
- return result.rows;
311
- });
285
+ return this.pool.query(translateSql(sql), normalizeParams(params)).rows;
312
286
  }
313
287
  exec(sql) {
314
- this.runSync(async () => {
315
- await this.pool.query(sql);
316
- });
288
+ this.pool.query(sql, []);
317
289
  }
318
290
  prepare(sql) {
319
291
  return {
@@ -327,28 +299,20 @@ class PgAdapter {
327
299
  return this.prepare(sql);
328
300
  }
329
301
  close() {
330
- this.runSync(async () => {
331
- await this.pool.end();
332
- });
302
+ this.pool.end();
333
303
  }
334
304
  transaction(fn) {
335
- return this.runSync(async () => {
336
- const client = await this.pool.connect();
337
- const originalQuery = this.pool.query.bind(this.pool);
305
+ this.pool.query("BEGIN", []);
306
+ try {
307
+ const value = fn();
308
+ this.pool.query("COMMIT", []);
309
+ return value;
310
+ } catch (error) {
338
311
  try {
339
- await client.query("BEGIN");
340
- this.pool.query = client.query.bind(client);
341
- const value = fn();
342
- await client.query("COMMIT");
343
- return value;
344
- } catch (error) {
345
- await client.query("ROLLBACK");
346
- throw error;
347
- } finally {
348
- this.pool.query = originalQuery;
349
- client.release();
350
- }
351
- });
312
+ this.pool.query("ROLLBACK", []);
313
+ } catch {}
314
+ throw error;
315
+ }
352
316
  }
353
317
  get raw() {
354
318
  return this.pool;
@@ -630,7 +594,7 @@ function getSyncMetaAll(db) {
630
594
  ensureSyncMetaTable(db);
631
595
  return db.all("SELECT table_name, last_synced_at, last_synced_row_count, direction FROM _sync_meta ORDER BY table_name");
632
596
  }
633
- var MEMENTOS_STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES, MODE_ENV_NAMES, warnedDeprecatedModes, SYNC_EXCLUDED_TABLE_PATTERNS, SYNC_META_TABLE_SQL = `
597
+ var PgSyncPool, MEMENTOS_STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES, MODE_ENV_NAMES, warnedDeprecatedModes, SYNC_EXCLUDED_TABLE_PATTERNS, SYNC_META_TABLE_SQL = `
634
598
  CREATE TABLE IF NOT EXISTS _sync_meta (
635
599
  table_name TEXT PRIMARY KEY,
636
600
  last_synced_at TEXT,
@@ -638,6 +602,74 @@ CREATE TABLE IF NOT EXISTS _sync_meta (
638
602
  direction TEXT DEFAULT 'push'
639
603
  )`;
640
604
  var init_storage = __esm(() => {
605
+ PgSyncPool = class PgSyncPool {
606
+ worker;
607
+ status;
608
+ data;
609
+ closed = false;
610
+ lastError = null;
611
+ static DATA_BYTES = 128 * 1024 * 1024;
612
+ static QUERY_TIMEOUT_MS = 60000;
613
+ static resolveWorkerPath() {
614
+ const ext = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
615
+ const here = fileURLToPath(new URL(".", import.meta.url));
616
+ const candidates = [
617
+ join(here, `pg-sync-worker${ext}`),
618
+ join(here, "..", `pg-sync-worker${ext}`),
619
+ join(here, "..", "..", `pg-sync-worker${ext}`)
620
+ ];
621
+ for (const candidate of candidates) {
622
+ if (existsSync(candidate))
623
+ return candidate;
624
+ }
625
+ return candidates[0];
626
+ }
627
+ constructor(connectionString) {
628
+ const control = new SharedArrayBuffer(8);
629
+ const dataSab = new SharedArrayBuffer(PgSyncPool.DATA_BYTES);
630
+ this.status = new Int32Array(control);
631
+ this.data = new Uint8Array(dataSab);
632
+ this.worker = new Worker(PgSyncPool.resolveWorkerPath(), {
633
+ workerData: {
634
+ dsn: stripSslParams(connectionString),
635
+ ssl: sslConfigFor(connectionString),
636
+ control,
637
+ data: dataSab
638
+ }
639
+ });
640
+ this.worker.unref();
641
+ this.worker.on("error", (err) => {
642
+ this.lastError = err;
643
+ });
644
+ }
645
+ query(sql, params) {
646
+ if (this.closed)
647
+ throw new Error("PgSyncPool is closed");
648
+ if (this.lastError)
649
+ throw this.lastError;
650
+ Atomics.store(this.status, 0, 0);
651
+ this.worker.postMessage({ sql, params });
652
+ const waitResult = Atomics.wait(this.status, 0, 0, PgSyncPool.QUERY_TIMEOUT_MS);
653
+ const code = Atomics.load(this.status, 0);
654
+ if (code === 0 || waitResult === "timed-out") {
655
+ if (this.lastError)
656
+ throw this.lastError;
657
+ throw new Error("PostgreSQL query timed out after 60s");
658
+ }
659
+ const len = Atomics.load(this.status, 1);
660
+ const payload = JSON.parse(new TextDecoder().decode(this.data.subarray(0, len)));
661
+ if (code === 2) {
662
+ throw new Error(payload.message ?? "PostgreSQL error");
663
+ }
664
+ return payload;
665
+ }
666
+ end() {
667
+ if (this.closed)
668
+ return;
669
+ this.closed = true;
670
+ this.worker.terminate();
671
+ }
672
+ };
641
673
  MEMENTOS_STORAGE_TABLES = [
642
674
  "projects",
643
675
  "agents",
@@ -11798,6 +11830,45 @@ var init_pg_migrations = __esm(() => {
11798
11830
  machine_id TEXT,
11799
11831
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
11800
11832
  );
11833
+ `,
11834
+ `
11835
+ CREATE TABLE IF NOT EXISTS tasks (
11836
+ id TEXT PRIMARY KEY,
11837
+ subject TEXT NOT NULL,
11838
+ description TEXT DEFAULT '',
11839
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','in_progress','completed','failed','cancelled')),
11840
+ priority TEXT NOT NULL DEFAULT 'medium' CHECK(priority IN ('critical','high','medium','low')),
11841
+ tags TEXT NOT NULL DEFAULT '[]',
11842
+ assigned_agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
11843
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
11844
+ session_id TEXT,
11845
+ parent_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
11846
+ metadata TEXT NOT NULL DEFAULT '{}',
11847
+ progress REAL NOT NULL DEFAULT 0 CHECK(progress >= 0 AND progress <= 1),
11848
+ due_at TEXT,
11849
+ started_at TEXT,
11850
+ completed_at TEXT,
11851
+ failed_at TEXT,
11852
+ error TEXT,
11853
+ created_at TEXT NOT NULL DEFAULT to_char((now() AT TIME ZONE 'UTC'), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
11854
+ updated_at TEXT NOT NULL DEFAULT to_char((now() AT TIME ZONE 'UTC'), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
11855
+ );
11856
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
11857
+ CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
11858
+ CREATE INDEX IF NOT EXISTS idx_tasks_agent ON tasks(assigned_agent_id);
11859
+ CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
11860
+ CREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_id);
11861
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id);
11862
+
11863
+ CREATE TABLE IF NOT EXISTS task_comments (
11864
+ id TEXT PRIMARY KEY,
11865
+ task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
11866
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
11867
+ body TEXT NOT NULL,
11868
+ created_at TEXT NOT NULL DEFAULT to_char((now() AT TIME ZONE 'UTC'), 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
11869
+ );
11870
+ CREATE INDEX IF NOT EXISTS idx_task_comments_task ON task_comments(task_id);
11871
+ CREATE INDEX IF NOT EXISTS idx_task_comments_agent ON task_comments(agent_id);
11801
11872
  `
11802
11873
  ];
11803
11874
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pg-sync-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg-sync-worker.d.ts","sourceRoot":"","sources":["../src/pg-sync-worker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ // @bun
2
+ // src/pg-sync-worker.ts
3
+ import { parentPort, workerData } from "worker_threads";
4
+ import pg from "pg";
5
+ var { dsn, ssl, control, data } = workerData;
6
+ var status = new Int32Array(control);
7
+ var dataView = new Uint8Array(data);
8
+ var encoder = new TextEncoder;
9
+ var client = new pg.Client({ connectionString: dsn, ssl });
10
+ var connected = false;
11
+ var connecting = null;
12
+ async function ensureConnected() {
13
+ if (connected)
14
+ return;
15
+ if (!connecting) {
16
+ connecting = client.connect().then(() => {
17
+ connected = true;
18
+ });
19
+ }
20
+ await connecting;
21
+ }
22
+ function respond(statusCode, payload) {
23
+ const bytes = encoder.encode(JSON.stringify(payload));
24
+ if (bytes.length > dataView.length) {
25
+ const errBytes = encoder.encode(JSON.stringify({
26
+ message: `PgSyncWorker: response of ${bytes.length} bytes exceeds shared buffer (${dataView.length})`
27
+ }));
28
+ dataView.set(errBytes, 0);
29
+ Atomics.store(status, 1, errBytes.length);
30
+ Atomics.store(status, 0, 2);
31
+ Atomics.notify(status, 0);
32
+ return;
33
+ }
34
+ dataView.set(bytes, 0);
35
+ Atomics.store(status, 1, bytes.length);
36
+ Atomics.store(status, 0, statusCode);
37
+ Atomics.notify(status, 0);
38
+ }
39
+ parentPort?.on("message", async (msg) => {
40
+ try {
41
+ await ensureConnected();
42
+ const result = await client.query(msg.sql, msg.params);
43
+ respond(1, { rows: result.rows, rowCount: result.rowCount });
44
+ } catch (error) {
45
+ respond(2, { message: error instanceof Error ? error.message : String(error) });
46
+ }
47
+ });
@@ -272,6 +272,18 @@ export interface ListTasksFilter {
272
272
  export interface MementosClientConfig {
273
273
  baseUrl?: string;
274
274
  fetch?: typeof globalThis.fetch;
275
+ /**
276
+ * API key issued by `contracts issue-key --app mementos`. Sent as both
277
+ * `Authorization: Bearer <key>` and `x-api-key`. Required against a
278
+ * self_hosted deployment with API-key auth enabled.
279
+ */
280
+ apiKey?: string;
281
+ /**
282
+ * Versioned route prefix. Defaults to the canonical `/v1`. The deployed
283
+ * service also serves the legacy `/api` prefix; set `prefix: "/api"` to target
284
+ * it explicitly.
285
+ */
286
+ prefix?: string;
275
287
  }
276
288
  export declare class MementosError extends Error {
277
289
  readonly status: number;
@@ -368,6 +380,8 @@ export interface SessionMemoryJob {
368
380
  export declare class MementosClient {
369
381
  private baseUrl;
370
382
  private _fetch;
383
+ private apiKey?;
384
+ private prefix;
371
385
  constructor(config?: MementosClientConfig);
372
386
  static fromEnv(overrides?: Partial<MementosClientConfig>): MementosClient;
373
387
  private request;
@@ -383,6 +397,7 @@ export declare class MementosClient {
383
397
  getHealth(): Promise<{
384
398
  status: "ok" | "warn";
385
399
  version: string;
400
+ mode: "local" | "cloud";
386
401
  profile: string;
387
402
  db_path: string;
388
403
  hostname: string;
@@ -394,6 +409,18 @@ export declare class MementosClient {
394
409
  agents: number;
395
410
  projects: number;
396
411
  }>;
412
+ /** Liveness/readiness probe — verifies backing-store connectivity. */
413
+ getReady(): Promise<{
414
+ status: "ready" | "not_ready";
415
+ version: string;
416
+ mode: "local" | "cloud";
417
+ }>;
418
+ /** Service version + storage mode. */
419
+ getVersion(): Promise<{
420
+ status: "ok";
421
+ version: string;
422
+ mode: "local" | "cloud";
423
+ }>;
397
424
  getReport(options?: {
398
425
  days?: number;
399
426
  project_id?: string;