@hasna/mementos 0.14.74 → 0.14.78

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 (68) hide show
  1. package/bun.lock +93 -0
  2. package/dist/cli/commands/agent.d.ts.map +1 -1
  3. package/dist/cli/commands/info-context.d.ts.map +1 -1
  4. package/dist/cli/commands/info-stale.d.ts.map +1 -1
  5. package/dist/cli/commands/io-export.d.ts.map +1 -1
  6. package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
  7. package/dist/cli/commands/memory-cmd-list.d.ts.map +1 -1
  8. package/dist/cli/commands/memory-cmd-recall.d.ts.map +1 -1
  9. package/dist/cli/commands/memory-cmd-search.d.ts.map +1 -1
  10. package/dist/cli/commands/memory-cmd-tail.d.ts.map +1 -1
  11. package/dist/cli/commands/project.d.ts.map +1 -1
  12. package/dist/cli/commands/system-watch.d.ts.map +1 -1
  13. package/dist/cli/helpers.d.ts +38 -0
  14. package/dist/cli/helpers.d.ts.map +1 -1
  15. package/dist/cli/index.js +2077 -299
  16. package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +1 -1
  17. package/dist/db/agents.d.ts +6 -0
  18. package/dist/db/agents.d.ts.map +1 -1
  19. package/dist/db/audit.d.ts +14 -0
  20. package/dist/db/audit.d.ts.map +1 -1
  21. package/dist/db/memory-project-link.d.ts +13 -0
  22. package/dist/db/memory-project-link.d.ts.map +1 -0
  23. package/dist/db/migrations.d.ts.map +1 -1
  24. package/dist/db/pg-migrations.d.ts +0 -6
  25. package/dist/db/pg-migrations.d.ts.map +1 -1
  26. package/dist/db/projects.d.ts +22 -1
  27. package/dist/db/projects.d.ts.map +1 -1
  28. package/dist/index.d.ts +5 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +2909 -173
  31. package/dist/lib/package-version.d.ts +3 -0
  32. package/dist/lib/package-version.d.ts.map +1 -0
  33. package/dist/mcp/index.js +524 -10
  34. package/dist/memory-project-link/index.d.ts +2 -0
  35. package/dist/memory-project-link/index.d.ts.map +1 -0
  36. package/dist/memory-project-link/schema.d.ts +5 -0
  37. package/dist/memory-project-link/schema.d.ts.map +1 -0
  38. package/dist/project-registration/authority.d.ts +39 -0
  39. package/dist/project-registration/authority.d.ts.map +1 -0
  40. package/dist/project-registration/http.d.ts +29 -0
  41. package/dist/project-registration/http.d.ts.map +1 -0
  42. package/dist/project-registration/index.d.ts +8 -0
  43. package/dist/project-registration/index.d.ts.map +1 -0
  44. package/dist/project-registration/project-references.d.ts +71 -0
  45. package/dist/project-registration/project-references.d.ts.map +1 -0
  46. package/dist/project-registration/schema.d.ts +5 -0
  47. package/dist/project-registration/schema.d.ts.map +1 -0
  48. package/dist/project-registration/types.d.ts +168 -0
  49. package/dist/project-registration/types.d.ts.map +1 -0
  50. package/dist/project-registration.d.ts +2 -0
  51. package/dist/project-registration.d.ts.map +1 -0
  52. package/dist/project-registration.js +1496 -0
  53. package/dist/sdk/index.d.ts +127 -0
  54. package/dist/sdk/index.d.ts.map +1 -1
  55. package/dist/sdk/index.js +74 -0
  56. package/dist/server/index.d.ts +1 -0
  57. package/dist/server/index.d.ts.map +1 -1
  58. package/dist/server/index.js +2667 -149
  59. package/dist/server/routes/memories.d.ts +1 -0
  60. package/dist/server/routes/memories.d.ts.map +1 -1
  61. package/dist/server/routes/memory-project-link.d.ts +2 -0
  62. package/dist/server/routes/memory-project-link.d.ts.map +1 -0
  63. package/dist/server/routes/project-registration.d.ts +2 -0
  64. package/dist/server/routes/project-registration.d.ts.map +1 -0
  65. package/dist/types/index.d.ts +119 -0
  66. package/dist/types/index.d.ts.map +1 -1
  67. package/package.json +9 -3
  68. package/dashboard/bun.lock +0 -519
package/dist/index.js CHANGED
@@ -1178,6 +1178,429 @@ var init_api_mode = __esm(() => {
1178
1178
  };
1179
1179
  });
1180
1180
 
1181
+ // src/project-registration/schema.ts
1182
+ function sqliteMementosProjectRegistrationSchemaSql() {
1183
+ return `
1184
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_receipts (
1185
+ receipt_id TEXT PRIMARY KEY,
1186
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
1187
+ route TEXT NOT NULL,
1188
+ package_version TEXT NOT NULL,
1189
+ authority_id TEXT NOT NULL,
1190
+ tenant_id TEXT NOT NULL,
1191
+ corpus_id TEXT NOT NULL,
1192
+ operation_id TEXT NOT NULL,
1193
+ step_id TEXT NOT NULL,
1194
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
1195
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'inverse')),
1196
+ target_selector TEXT NOT NULL,
1197
+ idempotency_key TEXT NOT NULL,
1198
+ request_digest TEXT NOT NULL,
1199
+ precondition_digest TEXT NOT NULL,
1200
+ normalized_call_digest TEXT NOT NULL,
1201
+ outcome TEXT NOT NULL CHECK(outcome IN (
1202
+ 'accepted', 'duplicate_of_accepted', 'terminal_nonacceptance'
1203
+ )),
1204
+ reason TEXT,
1205
+ target_id TEXT,
1206
+ result_revision TEXT,
1207
+ result_digest TEXT,
1208
+ duplicate_of_receipt_id TEXT,
1209
+ accepted_receipt_id TEXT,
1210
+ created_by_operation INTEGER NOT NULL CHECK(created_by_operation IN (0, 1)),
1211
+ created_at TEXT NOT NULL
1212
+ );
1213
+
1214
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_lookup
1215
+ ON mementos_project_registration_receipts (
1216
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
1217
+ resource_kind, direction, idempotency_key, target_selector
1218
+ );
1219
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_accepted_step
1220
+ ON mementos_project_registration_receipts (
1221
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
1222
+ resource_kind, direction
1223
+ )
1224
+ WHERE outcome = 'accepted';
1225
+
1226
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_bindings (
1227
+ authority_id TEXT NOT NULL,
1228
+ tenant_id TEXT NOT NULL,
1229
+ corpus_id TEXT NOT NULL,
1230
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
1231
+ target_selector TEXT NOT NULL,
1232
+ operation_id TEXT NOT NULL,
1233
+ step_id TEXT NOT NULL,
1234
+ direction TEXT NOT NULL CHECK(direction = 'forward'),
1235
+ idempotency_key TEXT NOT NULL,
1236
+ request_digest TEXT NOT NULL,
1237
+ precondition_digest TEXT NOT NULL,
1238
+ normalized_call_digest TEXT NOT NULL,
1239
+ state TEXT NOT NULL CHECK(state IN (
1240
+ 'pending', 'accepted', 'terminal_nonacceptance', 'removed'
1241
+ )),
1242
+ target_id TEXT,
1243
+ accepted_receipt_id TEXT,
1244
+ result_revision TEXT,
1245
+ result_digest TEXT,
1246
+ removed_receipt_id TEXT,
1247
+ created_at TEXT NOT NULL,
1248
+ updated_at TEXT NOT NULL,
1249
+ PRIMARY KEY(authority_id, tenant_id, corpus_id, resource_kind, target_selector),
1250
+ UNIQUE(accepted_receipt_id)
1251
+ );
1252
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_binding_target
1253
+ ON mementos_project_registration_bindings(
1254
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
1255
+ )
1256
+ WHERE target_id IS NOT NULL;
1257
+
1258
+ CREATE TRIGGER IF NOT EXISTS mementos_project_registration_receipts_immutable_update
1259
+ BEFORE UPDATE ON mementos_project_registration_receipts
1260
+ BEGIN
1261
+ SELECT RAISE(ABORT, 'mementos project registration receipts are immutable');
1262
+ END;
1263
+
1264
+ CREATE TRIGGER IF NOT EXISTS mementos_project_registration_receipts_immutable_delete
1265
+ BEFORE DELETE ON mementos_project_registration_receipts
1266
+ BEGIN
1267
+ SELECT RAISE(ABORT, 'mementos project registration receipts are immutable');
1268
+ END;
1269
+ `;
1270
+ }
1271
+ function postgresMementosProjectRegistrationSchemaSql() {
1272
+ return `
1273
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_receipts (
1274
+ receipt_id TEXT PRIMARY KEY,
1275
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
1276
+ route TEXT NOT NULL,
1277
+ package_version TEXT NOT NULL,
1278
+ authority_id TEXT NOT NULL,
1279
+ tenant_id TEXT NOT NULL,
1280
+ corpus_id TEXT NOT NULL,
1281
+ operation_id TEXT NOT NULL,
1282
+ step_id TEXT NOT NULL,
1283
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
1284
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'inverse')),
1285
+ target_selector TEXT NOT NULL,
1286
+ idempotency_key TEXT NOT NULL,
1287
+ request_digest TEXT NOT NULL,
1288
+ precondition_digest TEXT NOT NULL,
1289
+ normalized_call_digest TEXT NOT NULL,
1290
+ outcome TEXT NOT NULL CHECK(outcome IN (
1291
+ 'accepted', 'duplicate_of_accepted', 'terminal_nonacceptance'
1292
+ )),
1293
+ reason TEXT,
1294
+ target_id TEXT,
1295
+ result_revision TEXT,
1296
+ result_digest TEXT,
1297
+ duplicate_of_receipt_id TEXT,
1298
+ accepted_receipt_id TEXT,
1299
+ created_by_operation BOOLEAN NOT NULL,
1300
+ created_at TIMESTAMPTZ NOT NULL
1301
+ );
1302
+
1303
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_lookup
1304
+ ON mementos_project_registration_receipts (
1305
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
1306
+ resource_kind, direction, idempotency_key, target_selector
1307
+ );
1308
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_accepted_step
1309
+ ON mementos_project_registration_receipts (
1310
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
1311
+ resource_kind, direction
1312
+ )
1313
+ WHERE outcome = 'accepted';
1314
+
1315
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_bindings (
1316
+ authority_id TEXT NOT NULL,
1317
+ tenant_id TEXT NOT NULL,
1318
+ corpus_id TEXT NOT NULL,
1319
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
1320
+ target_selector TEXT NOT NULL,
1321
+ operation_id TEXT NOT NULL,
1322
+ step_id TEXT NOT NULL,
1323
+ direction TEXT NOT NULL CHECK(direction = 'forward'),
1324
+ idempotency_key TEXT NOT NULL,
1325
+ request_digest TEXT NOT NULL,
1326
+ precondition_digest TEXT NOT NULL,
1327
+ normalized_call_digest TEXT NOT NULL,
1328
+ state TEXT NOT NULL CHECK(state IN (
1329
+ 'pending', 'accepted', 'terminal_nonacceptance', 'removed'
1330
+ )),
1331
+ target_id TEXT,
1332
+ accepted_receipt_id TEXT UNIQUE,
1333
+ result_revision TEXT,
1334
+ result_digest TEXT,
1335
+ removed_receipt_id TEXT,
1336
+ created_at TIMESTAMPTZ NOT NULL,
1337
+ updated_at TIMESTAMPTZ NOT NULL,
1338
+ PRIMARY KEY(authority_id, tenant_id, corpus_id, resource_kind, target_selector)
1339
+ );
1340
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_binding_target
1341
+ ON mementos_project_registration_bindings(
1342
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
1343
+ )
1344
+ WHERE target_id IS NOT NULL;
1345
+
1346
+ CREATE OR REPLACE FUNCTION mementos_project_registration_receipts_immutable()
1347
+ RETURNS trigger
1348
+ LANGUAGE plpgsql
1349
+ AS $$
1350
+ BEGIN
1351
+ RAISE EXCEPTION 'mementos project registration receipts are immutable';
1352
+ END;
1353
+ $$;
1354
+ DROP TRIGGER IF EXISTS mementos_project_registration_receipts_immutable
1355
+ ON mementos_project_registration_receipts;
1356
+ CREATE TRIGGER mementos_project_registration_receipts_immutable
1357
+ BEFORE UPDATE OR DELETE ON mementos_project_registration_receipts
1358
+ FOR EACH ROW EXECUTE FUNCTION mementos_project_registration_receipts_immutable();
1359
+ `;
1360
+ }
1361
+ function sqliteMementosProjectGuardedUpdateSchemaSql() {
1362
+ return `
1363
+ CREATE TABLE IF NOT EXISTS mementos_project_update_receipts (
1364
+ receipt_id TEXT PRIMARY KEY,
1365
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
1366
+ route TEXT NOT NULL CHECK(route = 'mementos.project-guarded-update.v1'),
1367
+ package_version TEXT NOT NULL,
1368
+ authority_id TEXT NOT NULL,
1369
+ tenant_id TEXT NOT NULL,
1370
+ corpus_id TEXT NOT NULL,
1371
+ operation_id TEXT NOT NULL,
1372
+ step_id TEXT NOT NULL,
1373
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
1374
+ idempotency_key TEXT NOT NULL,
1375
+ request_digest TEXT NOT NULL,
1376
+ outcome TEXT NOT NULL CHECK(outcome = 'accepted'),
1377
+ target_id TEXT NOT NULL,
1378
+ expected_revision TEXT NOT NULL,
1379
+ result_revision TEXT NOT NULL,
1380
+ result_digest TEXT NOT NULL,
1381
+ accepted_receipt_id TEXT,
1382
+ before_project_json TEXT NOT NULL,
1383
+ after_project_json TEXT NOT NULL,
1384
+ created_at TEXT NOT NULL,
1385
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
1386
+ );
1387
+
1388
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_update_receipts_target
1389
+ ON mementos_project_update_receipts(
1390
+ authority_id, tenant_id, corpus_id, target_id, created_at
1391
+ );
1392
+
1393
+ CREATE TRIGGER IF NOT EXISTS mementos_project_update_receipts_immutable_update
1394
+ BEFORE UPDATE ON mementos_project_update_receipts
1395
+ BEGIN
1396
+ SELECT RAISE(ABORT, 'mementos project update receipts are immutable');
1397
+ END;
1398
+
1399
+ CREATE TRIGGER IF NOT EXISTS mementos_project_update_receipts_immutable_delete
1400
+ BEFORE DELETE ON mementos_project_update_receipts
1401
+ BEGIN
1402
+ SELECT RAISE(ABORT, 'mementos project update receipts are immutable');
1403
+ END;
1404
+ `;
1405
+ }
1406
+ function postgresMementosProjectGuardedUpdateSchemaSql() {
1407
+ return `
1408
+ CREATE TABLE IF NOT EXISTS mementos_project_update_receipts (
1409
+ receipt_id TEXT PRIMARY KEY,
1410
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
1411
+ route TEXT NOT NULL CHECK(route = 'mementos.project-guarded-update.v1'),
1412
+ package_version TEXT NOT NULL,
1413
+ authority_id TEXT NOT NULL,
1414
+ tenant_id TEXT NOT NULL,
1415
+ corpus_id TEXT NOT NULL,
1416
+ operation_id TEXT NOT NULL,
1417
+ step_id TEXT NOT NULL,
1418
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
1419
+ idempotency_key TEXT NOT NULL,
1420
+ request_digest TEXT NOT NULL,
1421
+ outcome TEXT NOT NULL CHECK(outcome = 'accepted'),
1422
+ target_id TEXT NOT NULL,
1423
+ expected_revision TEXT NOT NULL,
1424
+ result_revision TEXT NOT NULL,
1425
+ result_digest TEXT NOT NULL,
1426
+ accepted_receipt_id TEXT,
1427
+ before_project_json JSONB NOT NULL,
1428
+ after_project_json JSONB NOT NULL,
1429
+ created_at TIMESTAMPTZ NOT NULL,
1430
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
1431
+ );
1432
+
1433
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_update_receipts_target
1434
+ ON mementos_project_update_receipts(
1435
+ authority_id, tenant_id, corpus_id, target_id, created_at
1436
+ );
1437
+
1438
+ CREATE OR REPLACE FUNCTION mementos_project_update_receipts_immutable()
1439
+ RETURNS trigger
1440
+ LANGUAGE plpgsql
1441
+ AS $$
1442
+ BEGIN
1443
+ RAISE EXCEPTION 'mementos project update receipts are immutable';
1444
+ END;
1445
+ $$;
1446
+ DROP TRIGGER IF EXISTS mementos_project_update_receipts_immutable
1447
+ ON mementos_project_update_receipts;
1448
+ CREATE TRIGGER mementos_project_update_receipts_immutable
1449
+ BEFORE UPDATE OR DELETE ON mementos_project_update_receipts
1450
+ FOR EACH ROW EXECUTE FUNCTION mementos_project_update_receipts_immutable();
1451
+ `;
1452
+ }
1453
+
1454
+ // src/memory-project-link/schema.ts
1455
+ function sqliteMementosMemoryProjectLinkSchemaSql() {
1456
+ return `
1457
+ CREATE TABLE IF NOT EXISTS mementos_memory_project_link_receipts (
1458
+ receipt_id TEXT PRIMARY KEY,
1459
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
1460
+ route TEXT NOT NULL CHECK(route = 'mementos.memory-project-link.v1'),
1461
+ package_version TEXT NOT NULL,
1462
+ authority_id TEXT NOT NULL,
1463
+ tenant_id TEXT NOT NULL,
1464
+ corpus_id TEXT NOT NULL,
1465
+ operation_id TEXT NOT NULL,
1466
+ step_id TEXT NOT NULL,
1467
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
1468
+ idempotency_key TEXT NOT NULL,
1469
+ request_digest TEXT NOT NULL,
1470
+ outcome TEXT NOT NULL CHECK(outcome IN ('accepted', 'no_change')),
1471
+ target_memory_id TEXT NOT NULL,
1472
+ requested_project_id TEXT NOT NULL,
1473
+ expected_memory_version INTEGER NOT NULL,
1474
+ expected_memory_revision TEXT NOT NULL,
1475
+ expected_project_revision TEXT,
1476
+ result_memory_version INTEGER NOT NULL,
1477
+ result_memory_revision TEXT NOT NULL,
1478
+ result_memory_digest TEXT NOT NULL,
1479
+ result_project_revision TEXT,
1480
+ result_project_digest TEXT,
1481
+ accepted_receipt_id TEXT,
1482
+ before_link_json TEXT NOT NULL,
1483
+ after_link_json TEXT NOT NULL,
1484
+ before_project_revision TEXT,
1485
+ before_project_digest TEXT,
1486
+ after_project_revision TEXT,
1487
+ after_project_digest TEXT,
1488
+ created_at TEXT NOT NULL,
1489
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
1490
+ );
1491
+
1492
+ CREATE INDEX IF NOT EXISTS idx_mementos_memory_project_link_receipts_target
1493
+ ON mementos_memory_project_link_receipts(
1494
+ authority_id, tenant_id, corpus_id, target_memory_id, created_at
1495
+ );
1496
+
1497
+ CREATE TRIGGER IF NOT EXISTS mementos_memory_project_link_receipts_immutable_update
1498
+ BEFORE UPDATE ON mementos_memory_project_link_receipts
1499
+ BEGIN
1500
+ SELECT RAISE(ABORT, 'mementos memory project link receipts are immutable');
1501
+ END;
1502
+
1503
+ CREATE TRIGGER IF NOT EXISTS mementos_memory_project_link_receipts_immutable_delete
1504
+ BEFORE DELETE ON mementos_memory_project_link_receipts
1505
+ BEGIN
1506
+ SELECT RAISE(ABORT, 'mementos memory project link receipts are immutable');
1507
+ END;
1508
+ `;
1509
+ }
1510
+ function postgresMementosMemoryProjectLinkSchemaSql() {
1511
+ return `
1512
+ CREATE TABLE IF NOT EXISTS mementos_memory_project_link_receipts (
1513
+ receipt_id TEXT PRIMARY KEY,
1514
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
1515
+ route TEXT NOT NULL CHECK(route = 'mementos.memory-project-link.v1'),
1516
+ package_version TEXT NOT NULL,
1517
+ authority_id TEXT NOT NULL,
1518
+ tenant_id TEXT NOT NULL,
1519
+ corpus_id TEXT NOT NULL,
1520
+ operation_id TEXT NOT NULL,
1521
+ step_id TEXT NOT NULL,
1522
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
1523
+ idempotency_key TEXT NOT NULL,
1524
+ request_digest TEXT NOT NULL,
1525
+ outcome TEXT NOT NULL CHECK(outcome IN ('accepted', 'no_change')),
1526
+ target_memory_id TEXT NOT NULL,
1527
+ requested_project_id TEXT NOT NULL,
1528
+ expected_memory_version INTEGER NOT NULL,
1529
+ expected_memory_revision TEXT NOT NULL,
1530
+ expected_project_revision TEXT,
1531
+ result_memory_version INTEGER NOT NULL,
1532
+ result_memory_revision TEXT NOT NULL,
1533
+ result_memory_digest TEXT NOT NULL,
1534
+ result_project_revision TEXT,
1535
+ result_project_digest TEXT,
1536
+ accepted_receipt_id TEXT,
1537
+ before_link_json JSONB NOT NULL,
1538
+ after_link_json JSONB NOT NULL,
1539
+ before_project_revision TEXT,
1540
+ before_project_digest TEXT,
1541
+ after_project_revision TEXT,
1542
+ after_project_digest TEXT,
1543
+ created_at TIMESTAMPTZ NOT NULL,
1544
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
1545
+ );
1546
+
1547
+ CREATE INDEX IF NOT EXISTS idx_mementos_memory_project_link_receipts_target
1548
+ ON mementos_memory_project_link_receipts(
1549
+ authority_id, tenant_id, corpus_id, target_memory_id, created_at
1550
+ );
1551
+
1552
+ CREATE OR REPLACE FUNCTION mementos_memory_project_link_receipts_immutable()
1553
+ RETURNS trigger
1554
+ LANGUAGE plpgsql
1555
+ AS $$
1556
+ BEGIN
1557
+ RAISE EXCEPTION 'mementos memory project link receipts are immutable';
1558
+ END;
1559
+ $$;
1560
+ DROP TRIGGER IF EXISTS mementos_memory_project_link_receipts_immutable
1561
+ ON mementos_memory_project_link_receipts;
1562
+ CREATE TRIGGER mementos_memory_project_link_receipts_immutable
1563
+ BEFORE UPDATE OR DELETE ON mementos_memory_project_link_receipts
1564
+ FOR EACH ROW EXECUTE FUNCTION mementos_memory_project_link_receipts_immutable();
1565
+ `;
1566
+ }
1567
+ var MEMENTOS_MEMORY_PROJECT_LINK_ROUTE = "mementos.memory-project-link.v1", MEMORY_PROJECT_LINK_RECEIPT_COLUMNS;
1568
+ var init_schema = __esm(() => {
1569
+ MEMORY_PROJECT_LINK_RECEIPT_COLUMNS = [
1570
+ "receipt_id",
1571
+ "authority",
1572
+ "route",
1573
+ "package_version",
1574
+ "authority_id",
1575
+ "tenant_id",
1576
+ "corpus_id",
1577
+ "operation_id",
1578
+ "step_id",
1579
+ "direction",
1580
+ "idempotency_key",
1581
+ "request_digest",
1582
+ "outcome",
1583
+ "target_memory_id",
1584
+ "requested_project_id",
1585
+ "expected_memory_version",
1586
+ "expected_memory_revision",
1587
+ "expected_project_revision",
1588
+ "result_memory_version",
1589
+ "result_memory_revision",
1590
+ "result_memory_digest",
1591
+ "result_project_revision",
1592
+ "result_project_digest",
1593
+ "accepted_receipt_id",
1594
+ "before_link_json",
1595
+ "after_link_json",
1596
+ "before_project_revision",
1597
+ "before_project_digest",
1598
+ "after_project_revision",
1599
+ "after_project_digest",
1600
+ "created_at"
1601
+ ];
1602
+ });
1603
+
1181
1604
  // src/db/migrations.ts
1182
1605
  var MEMORY_VERSION_SNAPSHOT_TRIGGER = `
1183
1606
  CREATE TRIGGER IF NOT EXISTS memories_version_snapshot
@@ -1198,6 +1621,7 @@ BEGIN
1198
1621
  END;
1199
1622
  `, MIGRATIONS;
1200
1623
  var init_migrations = __esm(() => {
1624
+ init_schema();
1201
1625
  MIGRATIONS = [
1202
1626
  `
1203
1627
  CREATE TABLE IF NOT EXISTS projects (
@@ -2098,6 +2522,51 @@ INSERT OR IGNORE INTO _migrations (id) VALUES (35);
2098
2522
  `
2099
2523
  ${MEMORY_VERSION_SNAPSHOT_TRIGGER}
2100
2524
  INSERT OR IGNORE INTO _migrations (id) VALUES (36);
2525
+ `,
2526
+ `
2527
+ DROP TRIGGER IF EXISTS audit_memory_insert;
2528
+ CREATE TRIGGER audit_memory_insert AFTER INSERT ON memories BEGIN
2529
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
2530
+ VALUES (hex(randomblob(4)), new.id, new.key, 'create', new.agent_id, datetime('now'));
2531
+ END;
2532
+
2533
+ DROP TRIGGER IF EXISTS audit_memory_update;
2534
+ CREATE TRIGGER audit_memory_update AFTER UPDATE ON memories BEGIN
2535
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, changes, created_at)
2536
+ VALUES (hex(randomblob(4)), new.id, new.key, 'update', new.agent_id,
2537
+ json_object('version_from', old.version, 'version_to', new.version, 'importance_from', old.importance, 'importance_to', new.importance),
2538
+ datetime('now'));
2539
+ END;
2540
+
2541
+ DROP TRIGGER IF EXISTS audit_memory_delete;
2542
+ CREATE TRIGGER audit_memory_delete AFTER DELETE ON memories BEGIN
2543
+ INSERT INTO memory_audit_log (id, memory_id, memory_key, operation, agent_id, created_at)
2544
+ VALUES (hex(randomblob(4)), old.id, old.key, 'delete', old.agent_id, datetime('now'));
2545
+ END;
2546
+
2547
+ UPDATE memory_audit_log SET old_value_hash = NULL
2548
+ WHERE old_value_hash IS NOT NULL
2549
+ AND length(old_value_hash) = 32
2550
+ AND old_value_hash NOT GLOB '*[^0-9A-F]*';
2551
+
2552
+ UPDATE memory_audit_log SET new_value_hash = NULL
2553
+ WHERE new_value_hash IS NOT NULL
2554
+ AND length(new_value_hash) = 32
2555
+ AND new_value_hash NOT GLOB '*[^0-9A-F]*';
2556
+
2557
+ INSERT OR IGNORE INTO _migrations (id) VALUES (37);
2558
+ `,
2559
+ `
2560
+ ${sqliteMementosProjectRegistrationSchemaSql()}
2561
+ INSERT OR IGNORE INTO _migrations (id) VALUES (38);
2562
+ `,
2563
+ `
2564
+ ${sqliteMementosProjectGuardedUpdateSchemaSql()}
2565
+ INSERT OR IGNORE INTO _migrations (id) VALUES (39);
2566
+ `,
2567
+ `
2568
+ ${sqliteMementosMemoryProjectLinkSchemaSql()}
2569
+ INSERT OR IGNORE INTO _migrations (id) VALUES (40);
2101
2570
  `
2102
2571
  ];
2103
2572
  });
@@ -2468,8 +2937,8 @@ var init_util = __esm(() => {
2468
2937
 
2469
2938
  // node_modules/zod/v3/ZodError.js
2470
2939
  var ZodIssueCode, quotelessJson = (obj) => {
2471
- const json = JSON.stringify(obj, null, 2);
2472
- return json.replace(/"([^"]+)":/g, "$1:");
2940
+ const json2 = JSON.stringify(obj, null, 2);
2941
+ return json2.replace(/"([^"]+)":/g, "$1:");
2473
2942
  }, ZodError;
2474
2943
  var init_ZodError = __esm(() => {
2475
2944
  init_util();
@@ -15661,24 +16130,24 @@ class JSONSchemaGenerator {
15661
16130
  const _json = result.schema;
15662
16131
  switch (def.type) {
15663
16132
  case "string": {
15664
- const json = _json;
15665
- json.type = "string";
16133
+ const json2 = _json;
16134
+ json2.type = "string";
15666
16135
  const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag;
15667
16136
  if (typeof minimum === "number")
15668
- json.minLength = minimum;
16137
+ json2.minLength = minimum;
15669
16138
  if (typeof maximum === "number")
15670
- json.maxLength = maximum;
16139
+ json2.maxLength = maximum;
15671
16140
  if (format) {
15672
- json.format = formatMap[format] ?? format;
15673
- if (json.format === "")
15674
- delete json.format;
16141
+ json2.format = formatMap[format] ?? format;
16142
+ if (json2.format === "")
16143
+ delete json2.format;
15675
16144
  }
15676
16145
  if (contentEncoding)
15677
- json.contentEncoding = contentEncoding;
16146
+ json2.contentEncoding = contentEncoding;
15678
16147
  if (patterns && patterns.size > 0) {
15679
16148
  const regexes = [...patterns];
15680
16149
  if (regexes.length === 1)
15681
- json.pattern = regexes[0].source;
16150
+ json2.pattern = regexes[0].source;
15682
16151
  else if (regexes.length > 1) {
15683
16152
  result.schema.allOf = [
15684
16153
  ...regexes.map((regex) => ({
@@ -15691,41 +16160,41 @@ class JSONSchemaGenerator {
15691
16160
  break;
15692
16161
  }
15693
16162
  case "number": {
15694
- const json = _json;
16163
+ const json2 = _json;
15695
16164
  const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
15696
16165
  if (typeof format === "string" && format.includes("int"))
15697
- json.type = "integer";
16166
+ json2.type = "integer";
15698
16167
  else
15699
- json.type = "number";
16168
+ json2.type = "number";
15700
16169
  if (typeof exclusiveMinimum === "number")
15701
- json.exclusiveMinimum = exclusiveMinimum;
16170
+ json2.exclusiveMinimum = exclusiveMinimum;
15702
16171
  if (typeof minimum === "number") {
15703
- json.minimum = minimum;
16172
+ json2.minimum = minimum;
15704
16173
  if (typeof exclusiveMinimum === "number") {
15705
16174
  if (exclusiveMinimum >= minimum)
15706
- delete json.minimum;
16175
+ delete json2.minimum;
15707
16176
  else
15708
- delete json.exclusiveMinimum;
16177
+ delete json2.exclusiveMinimum;
15709
16178
  }
15710
16179
  }
15711
16180
  if (typeof exclusiveMaximum === "number")
15712
- json.exclusiveMaximum = exclusiveMaximum;
16181
+ json2.exclusiveMaximum = exclusiveMaximum;
15713
16182
  if (typeof maximum === "number") {
15714
- json.maximum = maximum;
16183
+ json2.maximum = maximum;
15715
16184
  if (typeof exclusiveMaximum === "number") {
15716
16185
  if (exclusiveMaximum <= maximum)
15717
- delete json.maximum;
16186
+ delete json2.maximum;
15718
16187
  else
15719
- delete json.exclusiveMaximum;
16188
+ delete json2.exclusiveMaximum;
15720
16189
  }
15721
16190
  }
15722
16191
  if (typeof multipleOf === "number")
15723
- json.multipleOf = multipleOf;
16192
+ json2.multipleOf = multipleOf;
15724
16193
  break;
15725
16194
  }
15726
16195
  case "boolean": {
15727
- const json = _json;
15728
- json.type = "boolean";
16196
+ const json2 = _json;
16197
+ json2.type = "boolean";
15729
16198
  break;
15730
16199
  }
15731
16200
  case "bigint": {
@@ -15773,23 +16242,23 @@ class JSONSchemaGenerator {
15773
16242
  break;
15774
16243
  }
15775
16244
  case "array": {
15776
- const json = _json;
16245
+ const json2 = _json;
15777
16246
  const { minimum, maximum } = schema._zod.bag;
15778
16247
  if (typeof minimum === "number")
15779
- json.minItems = minimum;
16248
+ json2.minItems = minimum;
15780
16249
  if (typeof maximum === "number")
15781
- json.maxItems = maximum;
15782
- json.type = "array";
15783
- json.items = this.process(def.element, { ...params, path: [...params.path, "items"] });
16250
+ json2.maxItems = maximum;
16251
+ json2.type = "array";
16252
+ json2.items = this.process(def.element, { ...params, path: [...params.path, "items"] });
15784
16253
  break;
15785
16254
  }
15786
16255
  case "object": {
15787
- const json = _json;
15788
- json.type = "object";
15789
- json.properties = {};
16256
+ const json2 = _json;
16257
+ json2.type = "object";
16258
+ json2.properties = {};
15790
16259
  const shape = def.shape;
15791
16260
  for (const key in shape) {
15792
- json.properties[key] = this.process(shape[key], {
16261
+ json2.properties[key] = this.process(shape[key], {
15793
16262
  ...params,
15794
16263
  path: [...params.path, "properties", key]
15795
16264
  });
@@ -15804,15 +16273,15 @@ class JSONSchemaGenerator {
15804
16273
  }
15805
16274
  }));
15806
16275
  if (requiredKeys.size > 0) {
15807
- json.required = Array.from(requiredKeys);
16276
+ json2.required = Array.from(requiredKeys);
15808
16277
  }
15809
16278
  if (def.catchall?._zod.def.type === "never") {
15810
- json.additionalProperties = false;
16279
+ json2.additionalProperties = false;
15811
16280
  } else if (!def.catchall) {
15812
16281
  if (this.io === "output")
15813
- json.additionalProperties = false;
16282
+ json2.additionalProperties = false;
15814
16283
  } else if (def.catchall) {
15815
- json.additionalProperties = this.process(def.catchall, {
16284
+ json2.additionalProperties = this.process(def.catchall, {
15816
16285
  ...params,
15817
16286
  path: [...params.path, "additionalProperties"]
15818
16287
  });
@@ -15820,15 +16289,15 @@ class JSONSchemaGenerator {
15820
16289
  break;
15821
16290
  }
15822
16291
  case "union": {
15823
- const json = _json;
15824
- json.anyOf = def.options.map((x, i) => this.process(x, {
16292
+ const json2 = _json;
16293
+ json2.anyOf = def.options.map((x, i) => this.process(x, {
15825
16294
  ...params,
15826
16295
  path: [...params.path, "anyOf", i]
15827
16296
  }));
15828
16297
  break;
15829
16298
  }
15830
16299
  case "intersection": {
15831
- const json = _json;
16300
+ const json2 = _json;
15832
16301
  const a = this.process(def.left, {
15833
16302
  ...params,
15834
16303
  path: [...params.path, "allOf", 0]
@@ -15842,17 +16311,17 @@ class JSONSchemaGenerator {
15842
16311
  ...isSimpleIntersection(a) ? a.allOf : [a],
15843
16312
  ...isSimpleIntersection(b) ? b.allOf : [b]
15844
16313
  ];
15845
- json.allOf = allOf;
16314
+ json2.allOf = allOf;
15846
16315
  break;
15847
16316
  }
15848
16317
  case "tuple": {
15849
- const json = _json;
15850
- json.type = "array";
16318
+ const json2 = _json;
16319
+ json2.type = "array";
15851
16320
  const prefixItems = def.items.map((x, i) => this.process(x, { ...params, path: [...params.path, "prefixItems", i] }));
15852
16321
  if (this.target === "draft-2020-12") {
15853
- json.prefixItems = prefixItems;
16322
+ json2.prefixItems = prefixItems;
15854
16323
  } else {
15855
- json.items = prefixItems;
16324
+ json2.items = prefixItems;
15856
16325
  }
15857
16326
  if (def.rest) {
15858
16327
  const rest = this.process(def.rest, {
@@ -15860,29 +16329,29 @@ class JSONSchemaGenerator {
15860
16329
  path: [...params.path, "items"]
15861
16330
  });
15862
16331
  if (this.target === "draft-2020-12") {
15863
- json.items = rest;
16332
+ json2.items = rest;
15864
16333
  } else {
15865
- json.additionalItems = rest;
16334
+ json2.additionalItems = rest;
15866
16335
  }
15867
16336
  }
15868
16337
  if (def.rest) {
15869
- json.items = this.process(def.rest, {
16338
+ json2.items = this.process(def.rest, {
15870
16339
  ...params,
15871
16340
  path: [...params.path, "items"]
15872
16341
  });
15873
16342
  }
15874
16343
  const { minimum, maximum } = schema._zod.bag;
15875
16344
  if (typeof minimum === "number")
15876
- json.minItems = minimum;
16345
+ json2.minItems = minimum;
15877
16346
  if (typeof maximum === "number")
15878
- json.maxItems = maximum;
16347
+ json2.maxItems = maximum;
15879
16348
  break;
15880
16349
  }
15881
16350
  case "record": {
15882
- const json = _json;
15883
- json.type = "object";
15884
- json.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
15885
- json.additionalProperties = this.process(def.valueType, {
16351
+ const json2 = _json;
16352
+ json2.type = "object";
16353
+ json2.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
16354
+ json2.additionalProperties = this.process(def.valueType, {
15886
16355
  ...params,
15887
16356
  path: [...params.path, "additionalProperties"]
15888
16357
  });
@@ -15901,17 +16370,17 @@ class JSONSchemaGenerator {
15901
16370
  break;
15902
16371
  }
15903
16372
  case "enum": {
15904
- const json = _json;
16373
+ const json2 = _json;
15905
16374
  const values = getEnumValues(def.entries);
15906
16375
  if (values.every((v) => typeof v === "number"))
15907
- json.type = "number";
16376
+ json2.type = "number";
15908
16377
  if (values.every((v) => typeof v === "string"))
15909
- json.type = "string";
15910
- json.enum = values;
16378
+ json2.type = "string";
16379
+ json2.enum = values;
15911
16380
  break;
15912
16381
  }
15913
16382
  case "literal": {
15914
- const json = _json;
16383
+ const json2 = _json;
15915
16384
  const vals = [];
15916
16385
  for (const val of def.values) {
15917
16386
  if (val === undefined) {
@@ -15930,23 +16399,23 @@ class JSONSchemaGenerator {
15930
16399
  }
15931
16400
  if (vals.length === 0) {} else if (vals.length === 1) {
15932
16401
  const val = vals[0];
15933
- json.type = val === null ? "null" : typeof val;
15934
- json.const = val;
16402
+ json2.type = val === null ? "null" : typeof val;
16403
+ json2.const = val;
15935
16404
  } else {
15936
16405
  if (vals.every((v) => typeof v === "number"))
15937
- json.type = "number";
16406
+ json2.type = "number";
15938
16407
  if (vals.every((v) => typeof v === "string"))
15939
- json.type = "string";
16408
+ json2.type = "string";
15940
16409
  if (vals.every((v) => typeof v === "boolean"))
15941
- json.type = "string";
16410
+ json2.type = "string";
15942
16411
  if (vals.every((v) => v === null))
15943
- json.type = "null";
15944
- json.enum = vals;
16412
+ json2.type = "null";
16413
+ json2.enum = vals;
15945
16414
  }
15946
16415
  break;
15947
16416
  }
15948
16417
  case "file": {
15949
- const json = _json;
16418
+ const json2 = _json;
15950
16419
  const file = {
15951
16420
  type: "string",
15952
16421
  format: "binary",
@@ -15960,15 +16429,15 @@ class JSONSchemaGenerator {
15960
16429
  if (mime) {
15961
16430
  if (mime.length === 1) {
15962
16431
  file.contentMediaType = mime[0];
15963
- Object.assign(json, file);
16432
+ Object.assign(json2, file);
15964
16433
  } else {
15965
- json.anyOf = mime.map((m) => {
16434
+ json2.anyOf = mime.map((m) => {
15966
16435
  const mFile = { ...file, contentMediaType: m };
15967
16436
  return mFile;
15968
16437
  });
15969
16438
  }
15970
16439
  } else {
15971
- Object.assign(json, file);
16440
+ Object.assign(json2, file);
15972
16441
  }
15973
16442
  break;
15974
16443
  }
@@ -15989,8 +16458,8 @@ class JSONSchemaGenerator {
15989
16458
  break;
15990
16459
  }
15991
16460
  case "success": {
15992
- const json = _json;
15993
- json.type = "boolean";
16461
+ const json2 = _json;
16462
+ json2.type = "boolean";
15994
16463
  break;
15995
16464
  }
15996
16465
  case "default": {
@@ -16025,12 +16494,12 @@ class JSONSchemaGenerator {
16025
16494
  break;
16026
16495
  }
16027
16496
  case "template_literal": {
16028
- const json = _json;
16497
+ const json2 = _json;
16029
16498
  const pattern = schema._zod.pattern;
16030
16499
  if (!pattern)
16031
16500
  throw new Error("Pattern not found in template literal");
16032
- json.type = "string";
16033
- json.pattern = pattern.source;
16501
+ json2.type = "string";
16502
+ json2.pattern = pattern.source;
16034
16503
  break;
16035
16504
  }
16036
16505
  case "pipe": {
@@ -17123,7 +17592,7 @@ function _instanceof(cls, params = {
17123
17592
  inst._zod.bag.Class = cls;
17124
17593
  return inst;
17125
17594
  }
17126
- function json(params) {
17595
+ function json2(params) {
17127
17596
  const jsonSchema = lazy(() => {
17128
17597
  return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
17129
17598
  });
@@ -17810,7 +18279,7 @@ __export(exports_external2, {
17810
18279
  ksuid: () => ksuid2,
17811
18280
  keyof: () => keyof,
17812
18281
  jwt: () => jwt,
17813
- json: () => json,
18282
+ json: () => json2,
17814
18283
  iso: () => exports_iso,
17815
18284
  ipv6: () => ipv62,
17816
18285
  ipv4: () => ipv42,
@@ -19670,7 +20139,7 @@ function createProviderToolFactoryWithOutputSchema({
19670
20139
  supportsDeferredResults
19671
20140
  });
19672
20141
  }
19673
- async function resolve3(value) {
20142
+ async function resolve4(value) {
19674
20143
  if (typeof value === "function") {
19675
20144
  value = value();
19676
20145
  }
@@ -22492,11 +22961,11 @@ var VERSION2 = "3.0.82", anthropicErrorDataSchema, anthropicFailedResponseHandle
22492
22961
  betas,
22493
22962
  headers
22494
22963
  }) {
22495
- return combineHeaders(await resolve3(this.config.headers), headers, betas.size > 0 ? { "anthropic-beta": Array.from(betas).join(",") } : {});
22964
+ return combineHeaders(await resolve4(this.config.headers), headers, betas.size > 0 ? { "anthropic-beta": Array.from(betas).join(",") } : {});
22496
22965
  }
22497
22966
  async getBetasFromHeaders(requestHeaders) {
22498
22967
  var _a16, _b16;
22499
- const configHeaders = await resolve3(this.config.headers);
22968
+ const configHeaders = await resolve4(this.config.headers);
22500
22969
  const configBetaHeader = (_a16 = configHeaders["anthropic-beta"]) != null ? _a16 : "";
22501
22970
  const requestBetaHeader = (_b16 = requestHeaders == null ? undefined : requestHeaders["anthropic-beta"]) != null ? _b16 : "";
22502
22971
  return new Set([
@@ -33166,24 +33635,24 @@ var require_oauth = __commonJS((exports, module) => {
33166
33635
  });
33167
33636
  }
33168
33637
  async function processTokenResponse(response) {
33169
- const json2 = await response.json();
33638
+ const json3 = await response.json();
33170
33639
  if (!response.ok) {
33171
- const errorMsg = typeof json2 === "object" && json2 && "error" in json2 ? String(json2.error) : "Token refresh failed";
33640
+ const errorMsg = typeof json3 === "object" && json3 && "error" in json3 ? String(json3.error) : "Token refresh failed";
33172
33641
  return [new Error(errorMsg)];
33173
33642
  }
33174
- if (typeof json2 !== "object" || json2 === null) {
33643
+ if (typeof json3 !== "object" || json3 === null) {
33175
33644
  return [new Error("Invalid token response")];
33176
33645
  }
33177
- if (typeof json2.access_token !== "string") {
33646
+ if (typeof json3.access_token !== "string") {
33178
33647
  return [new Error("Missing access_token in response")];
33179
33648
  }
33180
- if (json2.token_type !== "Bearer") {
33649
+ if (json3.token_type !== "Bearer") {
33181
33650
  return [new Error("Invalid token_type in response")];
33182
33651
  }
33183
- if (typeof json2.expires_in !== "number") {
33652
+ if (typeof json3.expires_in !== "number") {
33184
33653
  return [new Error("Missing expires_in in response")];
33185
33654
  }
33186
- return [null, json2];
33655
+ return [null, json3];
33187
33656
  }
33188
33657
  });
33189
33658
 
@@ -33920,7 +34389,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
33920
34389
  try {
33921
34390
  const { value } = await getFromApi({
33922
34391
  url: `${this.config.baseURL}/config`,
33923
- headers: await resolve3(this.config.headers()),
34392
+ headers: await resolve4(this.config.headers()),
33924
34393
  successfulResponseHandler: createJsonResponseHandler(gatewayAvailableModelsResponseSchema),
33925
34394
  failedResponseHandler: createJsonErrorResponseHandler({
33926
34395
  errorSchema: exports_external2.any(),
@@ -33938,7 +34407,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
33938
34407
  const baseUrl = new URL(this.config.baseURL);
33939
34408
  const { value } = await getFromApi({
33940
34409
  url: `${baseUrl.origin}/v1/credits`,
33941
- headers: await resolve3(this.config.headers()),
34410
+ headers: await resolve4(this.config.headers()),
33942
34411
  successfulResponseHandler: createJsonResponseHandler(gatewayCreditsResponseSchema),
33943
34412
  failedResponseHandler: createJsonErrorResponseHandler({
33944
34413
  errorSchema: exports_external2.any(),
@@ -33984,7 +34453,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
33984
34453
  }
33985
34454
  const { value } = await getFromApi({
33986
34455
  url: `${baseUrl.origin}/v1/report?${searchParams.toString()}`,
33987
- headers: await resolve3(this.config.headers()),
34456
+ headers: await resolve4(this.config.headers()),
33988
34457
  successfulResponseHandler: createJsonResponseHandler(gatewaySpendReportResponseSchema),
33989
34458
  failedResponseHandler: createJsonErrorResponseHandler({
33990
34459
  errorSchema: exports_external2.any(),
@@ -34006,7 +34475,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34006
34475
  const baseUrl = new URL(this.config.baseURL);
34007
34476
  const { value } = await getFromApi({
34008
34477
  url: `${baseUrl.origin}/v1/generation?id=${encodeURIComponent(params.id)}`,
34009
- headers: await resolve3(this.config.headers()),
34478
+ headers: await resolve4(this.config.headers()),
34010
34479
  successfulResponseHandler: createJsonResponseHandler(gatewayGenerationInfoResponseSchema),
34011
34480
  failedResponseHandler: createJsonErrorResponseHandler({
34012
34481
  errorSchema: exports_external2.any(),
@@ -34039,7 +34508,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34039
34508
  async doGenerate(options) {
34040
34509
  const { args, warnings } = await this.getArgs(options);
34041
34510
  const { abortSignal } = options;
34042
- const resolvedHeaders = await resolve3(this.config.headers());
34511
+ const resolvedHeaders = await resolve4(this.config.headers());
34043
34512
  try {
34044
34513
  const {
34045
34514
  responseHeaders,
@@ -34047,7 +34516,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34047
34516
  rawValue: rawResponse
34048
34517
  } = await postJsonToApi({
34049
34518
  url: this.getUrl(),
34050
- headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve3(this.config.o11yHeaders)),
34519
+ headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve4(this.config.o11yHeaders)),
34051
34520
  body: args,
34052
34521
  successfulResponseHandler: createJsonResponseHandler(exports_external2.any()),
34053
34522
  failedResponseHandler: createJsonErrorResponseHandler({
@@ -34070,11 +34539,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34070
34539
  async doStream(options) {
34071
34540
  const { args, warnings } = await this.getArgs(options);
34072
34541
  const { abortSignal } = options;
34073
- const resolvedHeaders = await resolve3(this.config.headers());
34542
+ const resolvedHeaders = await resolve4(this.config.headers());
34074
34543
  try {
34075
34544
  const { value: response, responseHeaders } = await postJsonToApi({
34076
34545
  url: this.getUrl(),
34077
- headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve3(this.config.o11yHeaders)),
34546
+ headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve4(this.config.o11yHeaders)),
34078
34547
  body: args,
34079
34548
  successfulResponseHandler: createEventSourceResponseHandler(exports_external2.any()),
34080
34549
  failedResponseHandler: createJsonErrorResponseHandler({
@@ -34159,7 +34628,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34159
34628
  providerOptions
34160
34629
  }) {
34161
34630
  var _a102;
34162
- const resolvedHeaders = await resolve3(this.config.headers());
34631
+ const resolvedHeaders = await resolve4(this.config.headers());
34163
34632
  try {
34164
34633
  const {
34165
34634
  responseHeaders,
@@ -34167,7 +34636,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34167
34636
  rawValue
34168
34637
  } = await postJsonToApi({
34169
34638
  url: this.getUrl(),
34170
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
34639
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
34171
34640
  body: {
34172
34641
  values,
34173
34642
  ...providerOptions ? { providerOptions } : {}
@@ -34223,7 +34692,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34223
34692
  abortSignal
34224
34693
  }) {
34225
34694
  var _a102, _b102, _c, _d;
34226
- const resolvedHeaders = await resolve3(this.config.headers());
34695
+ const resolvedHeaders = await resolve4(this.config.headers());
34227
34696
  try {
34228
34697
  const {
34229
34698
  responseHeaders,
@@ -34231,7 +34700,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34231
34700
  rawValue
34232
34701
  } = await postJsonToApi({
34233
34702
  url: this.getUrl(),
34234
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
34703
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
34235
34704
  body: {
34236
34705
  prompt,
34237
34706
  n,
@@ -34306,11 +34775,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34306
34775
  abortSignal
34307
34776
  }) {
34308
34777
  var _a102;
34309
- const resolvedHeaders = await resolve3(this.config.headers());
34778
+ const resolvedHeaders = await resolve4(this.config.headers());
34310
34779
  try {
34311
34780
  const { responseHeaders, value: responseBody } = await postJsonToApi({
34312
34781
  url: this.getUrl(),
34313
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders), { accept: "text/event-stream" }),
34782
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders), { accept: "text/event-stream" }),
34314
34783
  body: {
34315
34784
  prompt,
34316
34785
  n,
@@ -34433,7 +34902,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34433
34902
  abortSignal,
34434
34903
  providerOptions
34435
34904
  }) {
34436
- const resolvedHeaders = await resolve3(this.config.headers());
34905
+ const resolvedHeaders = await resolve4(this.config.headers());
34437
34906
  try {
34438
34907
  const {
34439
34908
  responseHeaders,
@@ -34441,7 +34910,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
34441
34910
  rawValue
34442
34911
  } = await postJsonToApi({
34443
34912
  url: this.getUrl(),
34444
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
34913
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
34445
34914
  body: {
34446
34915
  documents,
34447
34916
  query,
@@ -44045,7 +44514,7 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
44045
44514
  const schema = asSchema(inputSchema);
44046
44515
  return {
44047
44516
  name: "object",
44048
- responseFormat: resolve3(schema.jsonSchema).then((jsonSchema2) => ({
44517
+ responseFormat: resolve4(schema.jsonSchema).then((jsonSchema2) => ({
44049
44518
  type: "json",
44050
44519
  schema: jsonSchema2,
44051
44520
  ...name21 != null && { name: name21 },
@@ -44106,7 +44575,7 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
44106
44575
  const elementSchema = asSchema(inputElementSchema);
44107
44576
  return {
44108
44577
  name: "array",
44109
- responseFormat: resolve3(elementSchema.jsonSchema).then((jsonSchema2) => {
44578
+ responseFormat: resolve4(elementSchema.jsonSchema).then((jsonSchema2) => {
44110
44579
  const { $schema, ...itemSchema } = jsonSchema2;
44111
44580
  return {
44112
44581
  type: "json",
@@ -44283,7 +44752,7 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
44283
44752
  return;
44284
44753
  }
44285
44754
  };
44286
- }, json2 = ({
44755
+ }, json3 = ({
44287
44756
  name: name21,
44288
44757
  description
44289
44758
  } = {}) => {
@@ -47019,9 +47488,9 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
47019
47488
  ...options
47020
47489
  }) {
47021
47490
  var _a21, _b16, _c, _d, _e;
47022
- const resolvedBody = await resolve3(this.body);
47023
- const resolvedHeaders = await resolve3(this.headers);
47024
- const resolvedCredentials = await resolve3(this.credentials);
47491
+ const resolvedBody = await resolve4(this.body);
47492
+ const resolvedHeaders = await resolve4(this.headers);
47493
+ const resolvedCredentials = await resolve4(this.credentials);
47025
47494
  const baseHeaders = {
47026
47495
  ...normalizeHeaders(resolvedHeaders),
47027
47496
  ...normalizeHeaders(options.headers)
@@ -47069,9 +47538,9 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
47069
47538
  }
47070
47539
  async reconnectToStream(options) {
47071
47540
  var _a21, _b16, _c, _d, _e;
47072
- const resolvedBody = await resolve3(this.body);
47073
- const resolvedHeaders = await resolve3(this.headers);
47074
- const resolvedCredentials = await resolve3(this.credentials);
47541
+ const resolvedBody = await resolve4(this.body);
47542
+ const resolvedHeaders = await resolve4(this.headers);
47543
+ const resolvedCredentials = await resolve4(this.credentials);
47075
47544
  const baseHeaders = {
47076
47545
  ...normalizeHeaders(resolvedHeaders),
47077
47546
  ...normalizeHeaders(options.headers)
@@ -48351,7 +48820,7 @@ var init_dist8 = __esm(() => {
48351
48820
  __export2(output_exports, {
48352
48821
  array: () => array2,
48353
48822
  choice: () => choice,
48354
- json: () => json2,
48823
+ json: () => json3,
48355
48824
  object: () => object2,
48356
48825
  text: () => text
48357
48826
  });
@@ -49991,10 +50460,656 @@ function getMemoryVersions(memoryId, db) {
49991
50460
  return [];
49992
50461
  }
49993
50462
  }
50463
+ // src/db/memory-project-link.ts
50464
+ init_storage();
50465
+ init_api_mode();
50466
+ init_database();
50467
+ import { createHash } from "crypto";
50468
+
50469
+ // src/lib/package-version.ts
50470
+ import { readFileSync as readFileSync2 } from "fs";
50471
+ import { dirname as dirname2, join as join4 } from "path";
50472
+ import { fileURLToPath as fileURLToPath2 } from "url";
50473
+ function getMementosPackageVersion() {
50474
+ const here = dirname2(fileURLToPath2(import.meta.url));
50475
+ for (const candidate of [
50476
+ join4(here, "..", "..", "package.json"),
50477
+ join4(here, "..", "package.json")
50478
+ ]) {
50479
+ try {
50480
+ const parsed = JSON.parse(readFileSync2(candidate, "utf8"));
50481
+ if (typeof parsed.version === "string" && parsed.version.trim())
50482
+ return parsed.version;
50483
+ } catch {}
50484
+ }
50485
+ return "0.0.0";
50486
+ }
50487
+
50488
+ // src/db/memory-project-link.ts
50489
+ init_schema();
50490
+
50491
+ class MemoryProjectLinkError extends Error {
50492
+ code;
50493
+ details;
50494
+ constructor(code, message, details = {}) {
50495
+ super(message);
50496
+ this.code = code;
50497
+ this.details = details;
50498
+ this.name = "MemoryProjectLinkError";
50499
+ }
50500
+ }
50501
+ var LINK_AUTHORITY = {
50502
+ authority_id: "mementos",
50503
+ tenant_id: "default",
50504
+ corpus_id: "default"
50505
+ };
50506
+ var BOUNDED_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
50507
+ function canonicalize(value) {
50508
+ if (Array.isArray(value))
50509
+ return value.map(canonicalize);
50510
+ if (!value || typeof value !== "object")
50511
+ return value;
50512
+ const output = {};
50513
+ for (const key of Object.keys(value).sort()) {
50514
+ const item = value[key];
50515
+ if (item !== undefined)
50516
+ output[key] = canonicalize(item);
50517
+ }
50518
+ return output;
50519
+ }
50520
+ function canonicalJson(value) {
50521
+ return JSON.stringify(canonicalize(value));
50522
+ }
50523
+ function digest(value) {
50524
+ return createHash("sha256").update(canonicalJson(value)).digest("hex");
50525
+ }
50526
+ function timestampString(value) {
50527
+ return value instanceof Date ? value.toISOString() : String(value);
50528
+ }
50529
+ function nullableTimestamp(value) {
50530
+ return value === null || value === undefined ? null : timestampString(value);
50531
+ }
50532
+ function normalizeMemoryTimestamps(memory) {
50533
+ return {
50534
+ ...memory,
50535
+ created_at: timestampString(memory.created_at),
50536
+ updated_at: timestampString(memory.updated_at),
50537
+ accessed_at: nullableTimestamp(memory.accessed_at),
50538
+ expires_at: nullableTimestamp(memory.expires_at),
50539
+ valid_from: nullableTimestamp(memory.valid_from),
50540
+ valid_until: nullableTimestamp(memory.valid_until),
50541
+ ingested_at: nullableTimestamp(memory.ingested_at)
50542
+ };
50543
+ }
50544
+ function parseProjectRow(row) {
50545
+ return {
50546
+ id: String(row["id"]),
50547
+ name: String(row["name"]),
50548
+ path: String(row["path"]),
50549
+ description: row["description"] === null ? null : String(row["description"] ?? "") || null,
50550
+ memory_prefix: row["memory_prefix"] === null ? null : String(row["memory_prefix"] ?? "") || null,
50551
+ created_at: timestampString(row["created_at"]),
50552
+ updated_at: timestampString(row["updated_at"])
50553
+ };
50554
+ }
50555
+ function memoryMutationState(memory) {
50556
+ const { access_count: _accessCount, accessed_at: _accessedAt, ...state } = memory;
50557
+ return state;
50558
+ }
50559
+ function memoryDigest(memory) {
50560
+ return digest(memoryMutationState(memory));
50561
+ }
50562
+ function projectDigest(project) {
50563
+ return digest(project);
50564
+ }
50565
+ function snapshot(memory) {
50566
+ return {
50567
+ memory_id: memory.id,
50568
+ project_id: memory.project_id,
50569
+ memory_version: memory.version,
50570
+ memory_revision: timestampString(memory.updated_at),
50571
+ memory_digest: memoryDigest(memory)
50572
+ };
50573
+ }
50574
+ function parseSnapshot(value) {
50575
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
50576
+ return parsed;
50577
+ }
50578
+ function receiptFromRow(row) {
50579
+ return {
50580
+ receipt_id: String(row["receipt_id"]),
50581
+ authority: "mementos",
50582
+ route: MEMENTOS_MEMORY_PROJECT_LINK_ROUTE,
50583
+ package_version: String(row["package_version"]),
50584
+ authority_id: String(row["authority_id"]),
50585
+ tenant_id: String(row["tenant_id"]),
50586
+ corpus_id: String(row["corpus_id"]),
50587
+ operation_id: String(row["operation_id"]),
50588
+ step_id: String(row["step_id"]),
50589
+ direction: row["direction"],
50590
+ idempotency_key: String(row["idempotency_key"]),
50591
+ request_digest: String(row["request_digest"]),
50592
+ outcome: row["outcome"],
50593
+ target_memory_id: String(row["target_memory_id"]),
50594
+ requested_project_id: String(row["requested_project_id"]),
50595
+ expected_memory_version: Number(row["expected_memory_version"]),
50596
+ expected_memory_revision: timestampString(row["expected_memory_revision"]),
50597
+ expected_project_revision: nullableTimestamp(row["expected_project_revision"]),
50598
+ result_memory_version: Number(row["result_memory_version"]),
50599
+ result_memory_revision: timestampString(row["result_memory_revision"]),
50600
+ result_memory_digest: String(row["result_memory_digest"]),
50601
+ result_project_revision: nullableTimestamp(row["result_project_revision"]),
50602
+ result_project_digest: row["result_project_digest"] === null ? null : String(row["result_project_digest"]),
50603
+ accepted_receipt_id: row["accepted_receipt_id"] === null ? null : String(row["accepted_receipt_id"]),
50604
+ before_link: parseSnapshot(row["before_link_json"]),
50605
+ after_link: parseSnapshot(row["after_link_json"]),
50606
+ before_project_revision: nullableTimestamp(row["before_project_revision"]),
50607
+ before_project_digest: row["before_project_digest"] === null ? null : String(row["before_project_digest"]),
50608
+ after_project_revision: nullableTimestamp(row["after_project_revision"]),
50609
+ after_project_digest: row["after_project_digest"] === null ? null : String(row["after_project_digest"]),
50610
+ created_at: timestampString(row["created_at"])
50611
+ };
50612
+ }
50613
+ function assertIdentity(identity) {
50614
+ if (identity.authority_id !== LINK_AUTHORITY.authority_id || identity.tenant_id !== LINK_AUTHORITY.tenant_id || identity.corpus_id !== LINK_AUTHORITY.corpus_id) {
50615
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_AUTHORITY_MISMATCH", "memory project link does not match this authority, tenant, and corpus");
50616
+ }
50617
+ }
50618
+ function assertBoundedIdentifier(value, field) {
50619
+ if (!BOUNDED_IDENTIFIER.test(value)) {
50620
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", `${field} must be an 8-128 character bounded identifier`);
50621
+ }
50622
+ }
50623
+ function assertMemoryGuard(version, revision) {
50624
+ if (!Number.isSafeInteger(version) || version < 1) {
50625
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", "expected_memory_version must be a positive safe integer");
50626
+ }
50627
+ if (!revision || revision.length > 128) {
50628
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", "expected_memory_revision is required and must be bounded");
50629
+ }
50630
+ }
50631
+ function assertForwardRequest(request) {
50632
+ assertIdentity(request);
50633
+ assertBoundedIdentifier(request.operation_id, "operation_id");
50634
+ assertBoundedIdentifier(request.step_id, "step_id");
50635
+ assertBoundedIdentifier(request.idempotency_key, "idempotency_key");
50636
+ assertBoundedIdentifier(request.target_project_id, "target_project_id");
50637
+ assertMemoryGuard(request.expected_memory_version, request.expected_memory_revision);
50638
+ if (!request.expected_project_revision || request.expected_project_revision.length > 128) {
50639
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", "expected_project_revision is required and must be bounded");
50640
+ }
50641
+ }
50642
+ function assertRollbackRequest(request) {
50643
+ assertIdentity(request);
50644
+ assertBoundedIdentifier(request.operation_id, "operation_id");
50645
+ assertBoundedIdentifier(request.step_id, "step_id");
50646
+ assertBoundedIdentifier(request.idempotency_key, "idempotency_key");
50647
+ assertBoundedIdentifier(request.accepted_receipt_id, "accepted_receipt_id");
50648
+ assertMemoryGuard(request.expected_memory_version, request.expected_memory_revision);
50649
+ }
50650
+ function forwardRequestDigest(memoryId, request) {
50651
+ return digest({
50652
+ authority_id: request.authority_id,
50653
+ tenant_id: request.tenant_id,
50654
+ corpus_id: request.corpus_id,
50655
+ operation_id: request.operation_id,
50656
+ step_id: request.step_id,
50657
+ idempotency_key: request.idempotency_key,
50658
+ expected_memory_version: request.expected_memory_version,
50659
+ expected_memory_revision: request.expected_memory_revision,
50660
+ target_project_id: request.target_project_id,
50661
+ expected_project_revision: request.expected_project_revision,
50662
+ direction: "forward",
50663
+ target_memory_id: memoryId
50664
+ });
50665
+ }
50666
+ function rollbackRequestDigest(memoryId, request) {
50667
+ return digest({
50668
+ authority_id: request.authority_id,
50669
+ tenant_id: request.tenant_id,
50670
+ corpus_id: request.corpus_id,
50671
+ operation_id: request.operation_id,
50672
+ step_id: request.step_id,
50673
+ idempotency_key: request.idempotency_key,
50674
+ expected_memory_version: request.expected_memory_version,
50675
+ expected_memory_revision: request.expected_memory_revision,
50676
+ accepted_receipt_id: request.accepted_receipt_id,
50677
+ direction: "rollback",
50678
+ target_memory_id: memoryId
50679
+ });
50680
+ }
50681
+ function forUpdateSuffix(db, lock) {
50682
+ return lock && db instanceof PgAdapter ? " FOR UPDATE" : "";
50683
+ }
50684
+ function getMemoryByExactId(id, db, lock = false) {
50685
+ const row = db.query(`SELECT * FROM memories WHERE id = ? LIMIT 1${forUpdateSuffix(db, lock)}`).get(id);
50686
+ return row ? normalizeMemoryTimestamps(parseMemoryRow(row)) : null;
50687
+ }
50688
+ function getProjectByExactId(id, db, lock = false) {
50689
+ const row = db.query(`SELECT * FROM projects WHERE id = ? LIMIT 1${forUpdateSuffix(db, lock)}`).get(id);
50690
+ return row ? parseProjectRow(row) : null;
50691
+ }
50692
+ function lockProjects(ids, db) {
50693
+ const projects = new Map;
50694
+ const uniqueIds = [...new Set(ids.filter((id) => Boolean(id)))].sort();
50695
+ for (const id of uniqueIds) {
50696
+ const project = getProjectByExactId(id, db, true);
50697
+ if (!project) {
50698
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_PROJECT_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
50699
+ }
50700
+ projects.set(id, project);
50701
+ }
50702
+ return projects;
50703
+ }
50704
+ function assertMemoryPrecondition(memory, expectedVersion, expectedRevision) {
50705
+ if (memory.version !== expectedVersion || memory.updated_at !== expectedRevision) {
50706
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory changed before the guarded project link", {
50707
+ expected_memory_version: expectedVersion,
50708
+ current_memory_version: memory.version,
50709
+ expected_memory_revision: expectedRevision,
50710
+ current_memory_revision: memory.updated_at
50711
+ });
50712
+ }
50713
+ }
50714
+ function assertProjectPrecondition(project, expectedRevision) {
50715
+ if (project.updated_at !== expectedRevision) {
50716
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_PROJECT", "Project changed before the guarded memory link", {
50717
+ expected_project_revision: expectedRevision,
50718
+ current_project_revision: project.updated_at
50719
+ });
50720
+ }
50721
+ }
50722
+ function assertNoTargetBucketCollision(memory, projectId, db) {
50723
+ const collision = db.query(`
50724
+ SELECT id FROM memories
50725
+ WHERE id != ? AND key = ? AND scope = ?
50726
+ AND COALESCE(agent_id, '') = COALESCE(?, '')
50727
+ AND COALESCE(project_id, '') = COALESCE(?, '')
50728
+ AND COALESCE(session_id, '') = COALESCE(?, '')
50729
+ LIMIT 1
50730
+ `).get(memory.id, memory.key, memory.scope, memory.agent_id, projectId, memory.session_id);
50731
+ if (collision) {
50732
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_COLLISION", "Linking this memory would collide with another memory in the target project bucket", { collision_id: collision.id });
50733
+ }
50734
+ }
50735
+ function nextRevision(previous) {
50736
+ const candidate = now();
50737
+ const previousMs = Date.parse(previous);
50738
+ const candidateMs = Date.parse(candidate);
50739
+ if (Number.isFinite(previousMs) && Number.isFinite(candidateMs) && candidateMs <= previousMs) {
50740
+ return new Date(previousMs + 1).toISOString();
50741
+ }
50742
+ return candidate;
50743
+ }
50744
+ function findReceiptByKey(db, identity, direction, idempotencyKey) {
50745
+ const row = db.query(`
50746
+ SELECT * FROM mementos_memory_project_link_receipts
50747
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
50748
+ AND direction = ? AND idempotency_key = ?
50749
+ LIMIT 1
50750
+ `).get(identity.authority_id, identity.tenant_id, identity.corpus_id, direction, idempotencyKey);
50751
+ return row ? receiptFromRow(row) : null;
50752
+ }
50753
+ function findReceiptById(db, identity, receiptId) {
50754
+ const row = db.query(`
50755
+ SELECT * FROM mementos_memory_project_link_receipts
50756
+ WHERE receipt_id = ? AND authority_id = ? AND tenant_id = ? AND corpus_id = ?
50757
+ LIMIT 1
50758
+ `).get(receiptId, identity.authority_id, identity.tenant_id, identity.corpus_id);
50759
+ return row ? receiptFromRow(row) : null;
50760
+ }
50761
+ function insertReceipt(db, receipt) {
50762
+ db.run(`
50763
+ INSERT INTO mementos_memory_project_link_receipts (
50764
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
50765
+ corpus_id, operation_id, step_id, direction, idempotency_key,
50766
+ request_digest, outcome, target_memory_id, requested_project_id,
50767
+ expected_memory_version, expected_memory_revision,
50768
+ expected_project_revision, result_memory_version,
50769
+ result_memory_revision, result_memory_digest, result_project_revision,
50770
+ result_project_digest, accepted_receipt_id, before_link_json,
50771
+ after_link_json, before_project_revision, before_project_digest,
50772
+ after_project_revision, after_project_digest, created_at
50773
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
50774
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
50775
+ `, [
50776
+ receipt.receipt_id,
50777
+ receipt.authority,
50778
+ receipt.route,
50779
+ receipt.package_version,
50780
+ receipt.authority_id,
50781
+ receipt.tenant_id,
50782
+ receipt.corpus_id,
50783
+ receipt.operation_id,
50784
+ receipt.step_id,
50785
+ receipt.direction,
50786
+ receipt.idempotency_key,
50787
+ receipt.request_digest,
50788
+ receipt.outcome,
50789
+ receipt.target_memory_id,
50790
+ receipt.requested_project_id,
50791
+ receipt.expected_memory_version,
50792
+ receipt.expected_memory_revision,
50793
+ receipt.expected_project_revision,
50794
+ receipt.result_memory_version,
50795
+ receipt.result_memory_revision,
50796
+ receipt.result_memory_digest,
50797
+ receipt.result_project_revision,
50798
+ receipt.result_project_digest,
50799
+ receipt.accepted_receipt_id,
50800
+ canonicalJson(receipt.before_link),
50801
+ canonicalJson(receipt.after_link),
50802
+ receipt.before_project_revision,
50803
+ receipt.before_project_digest,
50804
+ receipt.after_project_revision,
50805
+ receipt.after_project_digest,
50806
+ receipt.created_at
50807
+ ]);
50808
+ }
50809
+ function makeReceipt(input) {
50810
+ const beforeLink = snapshot(input.beforeMemory);
50811
+ const afterLink = snapshot(input.afterMemory);
50812
+ const logical = {
50813
+ authority: "mementos",
50814
+ route: MEMENTOS_MEMORY_PROJECT_LINK_ROUTE,
50815
+ package_version: getMementosPackageVersion(),
50816
+ authority_id: input.request.authority_id,
50817
+ tenant_id: input.request.tenant_id,
50818
+ corpus_id: input.request.corpus_id,
50819
+ operation_id: input.request.operation_id,
50820
+ step_id: input.request.step_id,
50821
+ direction: input.direction,
50822
+ idempotency_key: input.request.idempotency_key,
50823
+ request_digest: input.requestDigest,
50824
+ outcome: input.outcome,
50825
+ target_memory_id: input.targetMemoryId,
50826
+ requested_project_id: input.requestedProjectId,
50827
+ expected_memory_version: input.request.expected_memory_version,
50828
+ expected_memory_revision: input.request.expected_memory_revision,
50829
+ expected_project_revision: input.expectedProjectRevision,
50830
+ result_memory_version: afterLink.memory_version,
50831
+ result_memory_revision: afterLink.memory_revision,
50832
+ result_memory_digest: afterLink.memory_digest,
50833
+ result_project_revision: input.afterProject?.updated_at ?? null,
50834
+ result_project_digest: input.afterProject ? projectDigest(input.afterProject) : null,
50835
+ accepted_receipt_id: input.acceptedReceiptId ?? null,
50836
+ before_link: beforeLink,
50837
+ after_link: afterLink,
50838
+ before_project_revision: input.beforeProject?.updated_at ?? null,
50839
+ before_project_digest: input.beforeProject ? projectDigest(input.beforeProject) : null,
50840
+ after_project_revision: input.afterProject?.updated_at ?? null,
50841
+ after_project_digest: input.afterProject ? projectDigest(input.afterProject) : null
50842
+ };
50843
+ return {
50844
+ receipt_id: `mmpl_${digest(logical).slice(0, 40)}`,
50845
+ ...logical,
50846
+ created_at: now()
50847
+ };
50848
+ }
50849
+ function sameSnapshot(memory, expected) {
50850
+ return canonicalJson(snapshot(memory)) === canonicalJson(expected);
50851
+ }
50852
+ function assertProjectSnapshot(project, revision, expectedDigest) {
50853
+ if (!revision && !expectedDigest) {
50854
+ if (project) {
50855
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "receipt expected no linked project but a project row was resolved");
50856
+ }
50857
+ return;
50858
+ }
50859
+ if (!project || project.updated_at !== revision || projectDigest(project) !== expectedDigest) {
50860
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_PROJECT", "Project no longer matches the immutable memory-link receipt", { expected_project_revision: revision, current_project_revision: project?.updated_at ?? null });
50861
+ }
50862
+ }
50863
+ function replayResult(db, receipt, requestDigest, memoryId) {
50864
+ if (receipt.request_digest !== requestDigest || receipt.target_memory_id !== memoryId) {
50865
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_IDEMPOTENCY_MISMATCH", "caller idempotency key is already bound to a different memory project-link request");
50866
+ }
50867
+ const memory = getMemoryByExactId(memoryId, db);
50868
+ if (!memory || !sameSnapshot(memory, receipt.after_link)) {
50869
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "accepted memory project-link target drifted after its immutable receipt");
50870
+ }
50871
+ const project = receipt.after_link.project_id ? getProjectByExactId(receipt.after_link.project_id, db) : null;
50872
+ assertProjectSnapshot(project, receipt.after_project_revision, receipt.after_project_digest);
50873
+ return {
50874
+ dry_run: false,
50875
+ applied: receipt.outcome === "accepted",
50876
+ no_change: receipt.outcome === "no_change",
50877
+ memory,
50878
+ project,
50879
+ receipt
50880
+ };
50881
+ }
50882
+ function previewMemoryProjectLink(memoryId, request, db) {
50883
+ assertForwardRequest(request);
50884
+ assertBoundedIdentifier(memoryId, "memory_id");
50885
+ if (!db && isApiMode()) {
50886
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/guarded-project-link`, { ...request, dry_run: true });
50887
+ return data;
50888
+ }
50889
+ const d = db || getDatabase();
50890
+ const memory = getMemoryByExactId(memoryId, d);
50891
+ if (!memory) {
50892
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_MEMORY_NOT_FOUND", `Memory not found by exact stable ID: ${memoryId}`);
50893
+ }
50894
+ const project = getProjectByExactId(request.target_project_id, d);
50895
+ if (!project) {
50896
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_PROJECT_NOT_FOUND", `Project not found by exact stable ID: ${request.target_project_id}`);
50897
+ }
50898
+ assertMemoryPrecondition(memory, request.expected_memory_version, request.expected_memory_revision);
50899
+ assertProjectPrecondition(project, request.expected_project_revision);
50900
+ const noChange = memory.project_id === project.id;
50901
+ if (!noChange)
50902
+ assertNoTargetBucketCollision(memory, project.id, d);
50903
+ return {
50904
+ dry_run: true,
50905
+ applied: false,
50906
+ no_change: noChange,
50907
+ memory: noChange ? memory : { ...memory, project_id: project.id },
50908
+ project,
50909
+ receipt: null
50910
+ };
50911
+ }
50912
+ function applyMemoryProjectLink(memoryId, request, db) {
50913
+ assertForwardRequest(request);
50914
+ assertBoundedIdentifier(memoryId, "memory_id");
50915
+ if (!db && isApiMode()) {
50916
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/guarded-project-link`, { ...request, dry_run: false });
50917
+ return data;
50918
+ }
50919
+ const d = db || getDatabase();
50920
+ const requestDigest = forwardRequestDigest(memoryId, request);
50921
+ return d.transaction(() => {
50922
+ const prior = findReceiptByKey(d, request, "forward", request.idempotency_key);
50923
+ if (prior)
50924
+ return replayResult(d, prior, requestDigest, memoryId);
50925
+ const before = getMemoryByExactId(memoryId, d, true);
50926
+ if (!before) {
50927
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_MEMORY_NOT_FOUND", `Memory not found by exact stable ID: ${memoryId}`);
50928
+ }
50929
+ const committedPrior = findReceiptByKey(d, request, "forward", request.idempotency_key);
50930
+ if (committedPrior)
50931
+ return replayResult(d, committedPrior, requestDigest, memoryId);
50932
+ assertMemoryPrecondition(before, request.expected_memory_version, request.expected_memory_revision);
50933
+ const projects = lockProjects([before.project_id, request.target_project_id], d);
50934
+ const targetProject = projects.get(request.target_project_id);
50935
+ const beforeProject = before.project_id ? projects.get(before.project_id) : null;
50936
+ assertProjectPrecondition(targetProject, request.expected_project_revision);
50937
+ const noChange = before.project_id === request.target_project_id;
50938
+ if (noChange) {
50939
+ const receipt2 = makeReceipt({
50940
+ request,
50941
+ direction: "forward",
50942
+ requestDigest,
50943
+ outcome: "no_change",
50944
+ targetMemoryId: memoryId,
50945
+ requestedProjectId: request.target_project_id,
50946
+ expectedProjectRevision: request.expected_project_revision,
50947
+ beforeMemory: before,
50948
+ afterMemory: before,
50949
+ beforeProject,
50950
+ afterProject: targetProject
50951
+ });
50952
+ insertReceipt(d, receipt2);
50953
+ return {
50954
+ dry_run: false,
50955
+ applied: false,
50956
+ no_change: true,
50957
+ memory: before,
50958
+ project: targetProject,
50959
+ receipt: receipt2
50960
+ };
50961
+ }
50962
+ assertNoTargetBucketCollision(before, targetProject.id, d);
50963
+ const revision = nextRevision(before.updated_at);
50964
+ const result = d.run(`
50965
+ UPDATE memories
50966
+ SET project_id = ?, updated_at = ?
50967
+ WHERE id = ? AND version = ? AND updated_at = ?
50968
+ AND COALESCE(project_id, '') = COALESCE(?, '')
50969
+ `, [
50970
+ targetProject.id,
50971
+ revision,
50972
+ memoryId,
50973
+ before.version,
50974
+ before.updated_at,
50975
+ before.project_id
50976
+ ]);
50977
+ if (result.changes === 0) {
50978
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory project-link compare-and-swap did not update exactly one row");
50979
+ }
50980
+ const after = getMemoryByExactId(memoryId, d);
50981
+ const expectedAfter = { ...before, project_id: targetProject.id, updated_at: revision };
50982
+ if (!after || canonicalJson(memoryMutationState(after)) !== canonicalJson(memoryMutationState(expectedAfter))) {
50983
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "Memory project link did not read back exactly under the stable ID");
50984
+ }
50985
+ const receipt = makeReceipt({
50986
+ request,
50987
+ direction: "forward",
50988
+ requestDigest,
50989
+ outcome: "accepted",
50990
+ targetMemoryId: memoryId,
50991
+ requestedProjectId: request.target_project_id,
50992
+ expectedProjectRevision: request.expected_project_revision,
50993
+ beforeMemory: before,
50994
+ afterMemory: after,
50995
+ beforeProject,
50996
+ afterProject: targetProject
50997
+ });
50998
+ insertReceipt(d, receipt);
50999
+ return {
51000
+ dry_run: false,
51001
+ applied: true,
51002
+ no_change: false,
51003
+ memory: after,
51004
+ project: targetProject,
51005
+ receipt
51006
+ };
51007
+ });
51008
+ }
51009
+ function rollbackMemoryProjectLink(memoryId, request, db) {
51010
+ assertRollbackRequest(request);
51011
+ assertBoundedIdentifier(memoryId, "memory_id");
51012
+ if (!db && isApiMode()) {
51013
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/guarded-project-link-rollback`, request);
51014
+ return data;
51015
+ }
51016
+ const d = db || getDatabase();
51017
+ const requestDigest = rollbackRequestDigest(memoryId, request);
51018
+ return d.transaction(() => {
51019
+ const prior = findReceiptByKey(d, request, "rollback", request.idempotency_key);
51020
+ if (prior)
51021
+ return replayResult(d, prior, requestDigest, memoryId);
51022
+ const accepted = findReceiptById(d, request, request.accepted_receipt_id);
51023
+ if (!accepted || accepted.direction !== "forward" || accepted.target_memory_id !== memoryId) {
51024
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_RECEIPT_NOT_FOUND", "accepted forward link receipt was not found for this exact memory");
51025
+ }
51026
+ if (accepted.outcome !== "accepted") {
51027
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_RECEIPT_NOT_ROLLBACKABLE", "a no-change memory project-link receipt has no mutation to roll back");
51028
+ }
51029
+ const current = getMemoryByExactId(memoryId, d, true);
51030
+ if (!current) {
51031
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_MEMORY_NOT_FOUND", `Memory not found by exact stable ID: ${memoryId}`);
51032
+ }
51033
+ const committedPrior = findReceiptByKey(d, request, "rollback", request.idempotency_key);
51034
+ if (committedPrior)
51035
+ return replayResult(d, committedPrior, requestDigest, memoryId);
51036
+ assertMemoryPrecondition(current, request.expected_memory_version, request.expected_memory_revision);
51037
+ if (!sameSnapshot(current, accepted.after_link)) {
51038
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory no longer matches the accepted forward link receipt");
51039
+ }
51040
+ const projects = lockProjects([accepted.after_link.project_id, accepted.before_link.project_id], d);
51041
+ const beforeProject = accepted.after_link.project_id ? projects.get(accepted.after_link.project_id) : null;
51042
+ const afterProject = accepted.before_link.project_id ? projects.get(accepted.before_link.project_id) : null;
51043
+ assertProjectSnapshot(beforeProject, accepted.after_project_revision, accepted.after_project_digest);
51044
+ assertProjectSnapshot(afterProject, accepted.before_project_revision, accepted.before_project_digest);
51045
+ assertNoTargetBucketCollision(current, accepted.before_link.project_id, d);
51046
+ const result = d.run(`
51047
+ UPDATE memories
51048
+ SET project_id = ?, updated_at = ?
51049
+ WHERE id = ? AND version = ? AND updated_at = ?
51050
+ AND COALESCE(project_id, '') = COALESCE(?, '')
51051
+ `, [
51052
+ accepted.before_link.project_id,
51053
+ accepted.before_link.memory_revision,
51054
+ memoryId,
51055
+ current.version,
51056
+ current.updated_at,
51057
+ current.project_id
51058
+ ]);
51059
+ if (result.changes === 0) {
51060
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory project-link rollback compare-and-swap did not update exactly one row");
51061
+ }
51062
+ const restored = getMemoryByExactId(memoryId, d);
51063
+ if (!restored || !sameSnapshot(restored, accepted.before_link)) {
51064
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "Memory project-link rollback did not restore the exact prior linkage");
51065
+ }
51066
+ const receipt = makeReceipt({
51067
+ request,
51068
+ direction: "rollback",
51069
+ requestDigest,
51070
+ outcome: "accepted",
51071
+ targetMemoryId: memoryId,
51072
+ requestedProjectId: accepted.requested_project_id,
51073
+ expectedProjectRevision: null,
51074
+ beforeMemory: current,
51075
+ afterMemory: restored,
51076
+ beforeProject,
51077
+ afterProject,
51078
+ acceptedReceiptId: accepted.receipt_id
51079
+ });
51080
+ insertReceipt(d, receipt);
51081
+ return {
51082
+ dry_run: false,
51083
+ applied: true,
51084
+ no_change: false,
51085
+ memory: restored,
51086
+ project: afterProject,
51087
+ receipt
51088
+ };
51089
+ });
51090
+ }
51091
+ function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = LINK_AUTHORITY, db) {
51092
+ assertIdentity(identity);
51093
+ assertBoundedIdentifier(memoryId, "memory_id");
51094
+ assertBoundedIdentifier(receiptId, "receipt_id");
51095
+ if (!db && isApiMode()) {
51096
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/project-link-receipts/lookup`, { ...identity, receipt_id: receiptId });
51097
+ return data;
51098
+ }
51099
+ const d = db || getDatabase();
51100
+ const receipt = findReceiptById(d, identity, receiptId);
51101
+ if (!receipt || receipt.target_memory_id !== memoryId) {
51102
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_RECEIPT_NOT_FOUND", "immutable memory project-link receipt was not found for this exact memory");
51103
+ }
51104
+ return receipt;
51105
+ }
51106
+ // src/memory-project-link/index.ts
51107
+ init_schema();
49994
51108
  // src/db/agents.ts
49995
51109
  init_database();
49996
51110
  init_api_mode();
49997
51111
  var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
51112
+ var UNBOUNDED_AGENT_LIST_LIMIT = Number.MAX_SAFE_INTEGER;
49998
51113
  function parseAgentRow(row) {
49999
51114
  return {
50000
51115
  id: row["id"],
@@ -50086,13 +51201,46 @@ function getAgent(idOrName, db) {
50086
51201
  return parseAgentRow(rows[0]);
50087
51202
  return null;
50088
51203
  }
50089
- function listAgents(db) {
50090
- if (!db && isApiMode()) {
50091
- const { data } = apiJson("GET", "/agents");
51204
+ function normalizedAgentListFilter(filter) {
51205
+ const limit = Number.isFinite(filter.limit) ? Math.max(0, Math.floor(filter.limit)) : undefined;
51206
+ const offset = Number.isFinite(filter.offset) ? Math.max(0, Math.floor(filter.offset)) : undefined;
51207
+ return { limit, offset };
51208
+ }
51209
+ function isDatabaseAdapter(value) {
51210
+ return typeof value === "object" && value !== null && "query" in value && typeof value.query === "function";
51211
+ }
51212
+ function paginatedAgentQuery(baseSql, baseParams, filter) {
51213
+ const { limit, offset } = normalizedAgentListFilter(filter);
51214
+ let sql = `${baseSql} ORDER BY created_at ASC, id ASC`;
51215
+ const params = [...baseParams];
51216
+ if (limit !== undefined) {
51217
+ sql += " LIMIT ?";
51218
+ params.push(limit);
51219
+ } else if ((offset ?? 0) > 0) {
51220
+ sql += " LIMIT ?";
51221
+ params.push(UNBOUNDED_AGENT_LIST_LIMIT);
51222
+ }
51223
+ if ((offset ?? 0) > 0) {
51224
+ sql += " OFFSET ?";
51225
+ params.push(offset);
51226
+ }
51227
+ return { sql, params };
51228
+ }
51229
+ function listAgents(filterOrDb = {}, db) {
51230
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
51231
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
51232
+ const normalized = normalizedAgentListFilter(filter);
51233
+ if (!explicitDb && isApiMode()) {
51234
+ const q = toQuery({
51235
+ limit: normalized.limit,
51236
+ offset: normalized.offset
51237
+ });
51238
+ const { data } = apiJson("GET", `/agents${q}`);
50092
51239
  return data?.agents ?? [];
50093
51240
  }
50094
- const d = db || getDatabase();
50095
- const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
51241
+ const d = explicitDb || getDatabase();
51242
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents", [], normalized);
51243
+ const rows = d.query(sql).all(...params);
50096
51244
  return rows.map(parseAgentRow);
50097
51245
  }
50098
51246
  function touchAgent(idOrName, db) {
@@ -50109,15 +51257,19 @@ function touchAgent(idOrName, db) {
50109
51257
  return;
50110
51258
  d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
50111
51259
  }
50112
- function listAgentsByProject(projectId, db) {
50113
- if (!db && isApiMode()) {
50114
- const q = toQuery({ project_id: projectId });
51260
+ function listAgentsByProject(projectId, filterOrDb = {}, db) {
51261
+ const explicitDb = isDatabaseAdapter(filterOrDb) ? filterOrDb : db;
51262
+ const filter = isDatabaseAdapter(filterOrDb) ? {} : filterOrDb;
51263
+ const normalized = normalizedAgentListFilter(filter);
51264
+ if (!explicitDb && isApiMode()) {
51265
+ const q = toQuery({ project_id: projectId, ...normalized });
50115
51266
  const { data } = apiJson("GET", `/agents${q}`);
50116
51267
  return data?.agents ?? [];
50117
51268
  }
50118
- const d = db || getDatabase();
51269
+ const d = explicitDb || getDatabase();
50119
51270
  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);
51271
+ const { sql, params } = paginatedAgentQuery("SELECT * FROM agents WHERE active_project_id = ?", [resolvedId], normalized);
51272
+ const rows = d.query(sql).all(...params);
50121
51273
  return rows.map(parseAgentRow);
50122
51274
  }
50123
51275
  function updateAgent(id, updates, db) {
@@ -50400,7 +51552,27 @@ function focusFilterSQL(agentId, projectId) {
50400
51552
  // src/db/projects.ts
50401
51553
  init_database();
50402
51554
  init_api_mode();
50403
- function parseProjectRow(row) {
51555
+ import { createHash as createHash2 } from "crypto";
51556
+
51557
+ // src/project-registration/types.ts
51558
+ var MEMENTOS_PROJECT_REGISTRATION_ROUTE = "mementos.project-registration.v1";
51559
+ var MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE = "projects.full-registration.v1";
51560
+ var MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION = 1;
51561
+ var MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE = "mementos.project-guarded-update.v1";
51562
+
51563
+ class MementosProjectRegistrationError extends Error {
51564
+ code;
51565
+ details;
51566
+ constructor(code, message, details = {}) {
51567
+ super(message);
51568
+ this.code = code;
51569
+ this.details = details;
51570
+ this.name = "MementosProjectRegistrationError";
51571
+ }
51572
+ }
51573
+
51574
+ // src/db/projects.ts
51575
+ function parseProjectRow2(row) {
50404
51576
  return {
50405
51577
  id: row["id"],
50406
51578
  name: row["name"],
@@ -50411,6 +51583,241 @@ function parseProjectRow(row) {
50411
51583
  updated_at: row["updated_at"]
50412
51584
  };
50413
51585
  }
51586
+
51587
+ class ProjectCollisionError extends Error {
51588
+ field;
51589
+ value;
51590
+ constructor(field, value) {
51591
+ super(`Project ${field} already exists: ${value}`);
51592
+ this.field = field;
51593
+ this.value = value;
51594
+ this.name = "ProjectCollisionError";
51595
+ }
51596
+ }
51597
+
51598
+ class ProjectGuardedUpdateError extends Error {
51599
+ code;
51600
+ details;
51601
+ constructor(code, message, details = {}) {
51602
+ super(message);
51603
+ this.code = code;
51604
+ this.details = details;
51605
+ this.name = "ProjectGuardedUpdateError";
51606
+ }
51607
+ }
51608
+ var PROJECT_UPDATE_AUTHORITY = {
51609
+ authority_id: "mementos",
51610
+ tenant_id: "default",
51611
+ corpus_id: "default"
51612
+ };
51613
+ var BOUNDED_IDENTIFIER2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
51614
+ function canonicalizeProjectUpdateValue(value) {
51615
+ if (Array.isArray(value))
51616
+ return value.map(canonicalizeProjectUpdateValue);
51617
+ if (!value || typeof value !== "object")
51618
+ return value;
51619
+ const output = {};
51620
+ for (const key of Object.keys(value).sort()) {
51621
+ const item = value[key];
51622
+ if (item !== undefined)
51623
+ output[key] = canonicalizeProjectUpdateValue(item);
51624
+ }
51625
+ return output;
51626
+ }
51627
+ function canonicalProjectUpdateJson(value) {
51628
+ return JSON.stringify(canonicalizeProjectUpdateValue(value));
51629
+ }
51630
+ function digestProjectUpdateValue(value) {
51631
+ return createHash2("sha256").update(canonicalProjectUpdateJson(value)).digest("hex");
51632
+ }
51633
+ function timestampString2(value) {
51634
+ return value instanceof Date ? value.toISOString() : String(value);
51635
+ }
51636
+ function parseProjectJson(value) {
51637
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
51638
+ return parsed;
51639
+ }
51640
+ function projectUpdateReceiptFromRow(row) {
51641
+ return {
51642
+ receipt_id: String(row["receipt_id"]),
51643
+ authority: "mementos",
51644
+ route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
51645
+ package_version: String(row["package_version"]),
51646
+ authority_id: String(row["authority_id"]),
51647
+ tenant_id: String(row["tenant_id"]),
51648
+ corpus_id: String(row["corpus_id"]),
51649
+ operation_id: String(row["operation_id"]),
51650
+ step_id: String(row["step_id"]),
51651
+ direction: row["direction"],
51652
+ idempotency_key: String(row["idempotency_key"]),
51653
+ request_digest: String(row["request_digest"]),
51654
+ outcome: "accepted",
51655
+ target_id: String(row["target_id"]),
51656
+ expected_revision: timestampString2(row["expected_revision"]),
51657
+ result_revision: timestampString2(row["result_revision"]),
51658
+ result_digest: String(row["result_digest"]),
51659
+ accepted_receipt_id: row["accepted_receipt_id"] === null ? null : String(row["accepted_receipt_id"]),
51660
+ before_project: parseProjectJson(row["before_project_json"]),
51661
+ after_project: parseProjectJson(row["after_project_json"]),
51662
+ created_at: timestampString2(row["created_at"])
51663
+ };
51664
+ }
51665
+ function normalizeProjectUpdateInput(input) {
51666
+ const normalized = {};
51667
+ if (input.name !== undefined)
51668
+ normalized.name = input.name.trim();
51669
+ if (input.path !== undefined)
51670
+ normalized.path = input.path.trim();
51671
+ if (input.description !== undefined)
51672
+ normalized.description = input.description;
51673
+ if (input.memory_prefix !== undefined)
51674
+ normalized.memory_prefix = input.memory_prefix;
51675
+ if (Object.keys(normalized).length === 0) {
51676
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "At least one project field must be provided");
51677
+ }
51678
+ if (normalized.name !== undefined && normalized.name.length === 0) {
51679
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "Project name cannot be empty");
51680
+ }
51681
+ if (normalized.path !== undefined && normalized.path.length === 0) {
51682
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "Project path cannot be empty");
51683
+ }
51684
+ return normalized;
51685
+ }
51686
+ function assertProjectUpdateIdentity(identity) {
51687
+ if (identity.authority_id !== PROJECT_UPDATE_AUTHORITY.authority_id || identity.tenant_id !== PROJECT_UPDATE_AUTHORITY.tenant_id || identity.corpus_id !== PROJECT_UPDATE_AUTHORITY.corpus_id) {
51688
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_AUTHORITY_MISMATCH", "guarded project update does not match this authority, tenant, and corpus");
51689
+ }
51690
+ }
51691
+ function assertBoundedIdentifier2(value, field) {
51692
+ if (!BOUNDED_IDENTIFIER2.test(value)) {
51693
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", `${field} must be an 8-128 character bounded identifier`);
51694
+ }
51695
+ }
51696
+ function assertProjectUpdateRequest(request) {
51697
+ assertProjectUpdateIdentity(request);
51698
+ assertBoundedIdentifier2(request.operation_id, "operation_id");
51699
+ assertBoundedIdentifier2(request.step_id, "step_id");
51700
+ assertBoundedIdentifier2(request.idempotency_key, "idempotency_key");
51701
+ if (!request.expected_revision || request.expected_revision.length > 128) {
51702
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "expected_revision is required and must be bounded");
51703
+ }
51704
+ }
51705
+ function getProjectByExactId2(id, db) {
51706
+ const row = db.query("SELECT * FROM projects WHERE id = ? LIMIT 1").get(id);
51707
+ return row ? parseProjectRow2(row) : null;
51708
+ }
51709
+ function assertNoProjectCollision(id, input, db) {
51710
+ if (input.name !== undefined) {
51711
+ const collision = db.query("SELECT id FROM projects WHERE LOWER(name) = LOWER(?) AND id != ? LIMIT 1").get(input.name, id);
51712
+ if (collision) {
51713
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_COLLISION", `Project name already exists: ${input.name}`, { field: "name" });
51714
+ }
51715
+ }
51716
+ if (input.path !== undefined) {
51717
+ const collision = db.query("SELECT id FROM projects WHERE path = ? AND id != ? LIMIT 1").get(input.path, id);
51718
+ if (collision) {
51719
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_COLLISION", `Project path already exists: ${input.path}`, { field: "path" });
51720
+ }
51721
+ }
51722
+ }
51723
+ function nextProjectRevision(previous) {
51724
+ const candidate = now();
51725
+ const previousMs = Date.parse(previous);
51726
+ const candidateMs = Date.parse(candidate);
51727
+ if (Number.isFinite(previousMs) && Number.isFinite(candidateMs) && candidateMs <= previousMs) {
51728
+ return new Date(previousMs + 1).toISOString();
51729
+ }
51730
+ return candidate;
51731
+ }
51732
+ function projectWithUpdates(project, updates, revision) {
51733
+ return {
51734
+ ...project,
51735
+ ...updates.name !== undefined ? { name: updates.name } : {},
51736
+ ...updates.path !== undefined ? { path: updates.path } : {},
51737
+ ...updates.description !== undefined ? { description: updates.description } : {},
51738
+ ...updates.memory_prefix !== undefined ? { memory_prefix: updates.memory_prefix } : {},
51739
+ updated_at: revision
51740
+ };
51741
+ }
51742
+ function findProjectUpdateReceiptByKey(db, identity, direction, idempotencyKey) {
51743
+ const row = db.query(`
51744
+ SELECT * FROM mementos_project_update_receipts
51745
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
51746
+ AND direction = ? AND idempotency_key = ?
51747
+ LIMIT 1
51748
+ `).get(identity.authority_id, identity.tenant_id, identity.corpus_id, direction, idempotencyKey);
51749
+ return row ? projectUpdateReceiptFromRow(row) : null;
51750
+ }
51751
+ function findProjectUpdateReceiptById(db, identity, receiptId) {
51752
+ const row = db.query(`
51753
+ SELECT * FROM mementos_project_update_receipts
51754
+ WHERE receipt_id = ? AND authority_id = ? AND tenant_id = ? AND corpus_id = ?
51755
+ LIMIT 1
51756
+ `).get(receiptId, identity.authority_id, identity.tenant_id, identity.corpus_id);
51757
+ return row ? projectUpdateReceiptFromRow(row) : null;
51758
+ }
51759
+ function insertProjectUpdateReceipt(db, receipt) {
51760
+ db.run(`
51761
+ INSERT INTO mementos_project_update_receipts (
51762
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
51763
+ corpus_id, operation_id, step_id, direction, idempotency_key,
51764
+ request_digest, outcome, target_id, expected_revision, result_revision,
51765
+ result_digest, accepted_receipt_id, before_project_json,
51766
+ after_project_json, created_at
51767
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
51768
+ `, [
51769
+ receipt.receipt_id,
51770
+ receipt.authority,
51771
+ receipt.route,
51772
+ receipt.package_version,
51773
+ receipt.authority_id,
51774
+ receipt.tenant_id,
51775
+ receipt.corpus_id,
51776
+ receipt.operation_id,
51777
+ receipt.step_id,
51778
+ receipt.direction,
51779
+ receipt.idempotency_key,
51780
+ receipt.request_digest,
51781
+ receipt.outcome,
51782
+ receipt.target_id,
51783
+ receipt.expected_revision,
51784
+ receipt.result_revision,
51785
+ receipt.result_digest,
51786
+ receipt.accepted_receipt_id,
51787
+ canonicalProjectUpdateJson(receipt.before_project),
51788
+ canonicalProjectUpdateJson(receipt.after_project),
51789
+ receipt.created_at
51790
+ ]);
51791
+ }
51792
+ function makeProjectUpdateReceipt(input) {
51793
+ const createdAt = now();
51794
+ const logical = {
51795
+ authority: "mementos",
51796
+ route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
51797
+ package_version: getMementosPackageVersion(),
51798
+ authority_id: input.request.authority_id,
51799
+ tenant_id: input.request.tenant_id,
51800
+ corpus_id: input.request.corpus_id,
51801
+ operation_id: input.request.operation_id,
51802
+ step_id: input.request.step_id,
51803
+ direction: input.direction,
51804
+ idempotency_key: input.request.idempotency_key,
51805
+ request_digest: input.request_digest,
51806
+ outcome: "accepted",
51807
+ target_id: input.target_id,
51808
+ expected_revision: input.request.expected_revision,
51809
+ result_revision: input.after_project.updated_at,
51810
+ result_digest: digestProjectUpdateValue(input.after_project),
51811
+ accepted_receipt_id: input.accepted_receipt_id ?? null,
51812
+ before_project: input.before_project,
51813
+ after_project: input.after_project,
51814
+ created_at: createdAt
51815
+ };
51816
+ return {
51817
+ receipt_id: `mpur_${digestProjectUpdateValue(logical).slice(0, 40)}`,
51818
+ ...logical
51819
+ };
51820
+ }
50414
51821
  function registerProject(name, path, description, memoryPrefix, db) {
50415
51822
  if (!db && isApiMode()) {
50416
51823
  const { data } = apiJson("POST", "/projects", {
@@ -50430,7 +51837,7 @@ function registerProject(name, path, description, memoryPrefix, db) {
50430
51837
  timestamp,
50431
51838
  existingId
50432
51839
  ]);
50433
- return parseProjectRow(existing);
51840
+ return parseProjectRow2(existing);
50434
51841
  }
50435
51842
  const id = uuid();
50436
51843
  d.run("INSERT INTO projects (id, name, path, description, memory_prefix, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)", [id, name, path, description || null, memoryPrefix || null, timestamp, timestamp]);
@@ -50446,13 +51853,13 @@ function getProject(idOrPath, db) {
50446
51853
  const d = db || getDatabase();
50447
51854
  let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
50448
51855
  if (row)
50449
- return parseProjectRow(row);
51856
+ return parseProjectRow2(row);
50450
51857
  row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
50451
51858
  if (row)
50452
- return parseProjectRow(row);
51859
+ return parseProjectRow2(row);
50453
51860
  row = d.query("SELECT * FROM projects WHERE LOWER(name) = ?").get(idOrPath.toLowerCase());
50454
51861
  if (row)
50455
- return parseProjectRow(row);
51862
+ return parseProjectRow2(row);
50456
51863
  return null;
50457
51864
  }
50458
51865
  function listProjects(db) {
@@ -50462,7 +51869,1300 @@ function listProjects(db) {
50462
51869
  }
50463
51870
  const d = db || getDatabase();
50464
51871
  const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
50465
- return rows.map(parseProjectRow);
51872
+ return rows.map(parseProjectRow2);
51873
+ }
51874
+ function previewProjectUpdate(id, request, db) {
51875
+ assertProjectUpdateRequest(request);
51876
+ const normalized = normalizeProjectUpdateInput(request.updates);
51877
+ if (!db && isApiMode()) {
51878
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-update`, { ...request, updates: normalized, dry_run: true });
51879
+ return data;
51880
+ }
51881
+ const d = db || getDatabase();
51882
+ const project = getProjectByExactId2(id, d);
51883
+ if (!project) {
51884
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
51885
+ }
51886
+ if (project.updated_at !== request.expected_revision) {
51887
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project revision changed before the guarded update dry run", { expected_revision: request.expected_revision, current_revision: project.updated_at });
51888
+ }
51889
+ assertNoProjectCollision(id, normalized, d);
51890
+ return {
51891
+ dry_run: true,
51892
+ applied: false,
51893
+ project: projectWithUpdates(project, normalized, project.updated_at),
51894
+ receipt: null
51895
+ };
51896
+ }
51897
+ function applyProjectUpdate(id, request, db) {
51898
+ assertProjectUpdateRequest(request);
51899
+ const normalized = normalizeProjectUpdateInput(request.updates);
51900
+ if (!db && isApiMode()) {
51901
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-update`, { ...request, updates: normalized, dry_run: false });
51902
+ return data;
51903
+ }
51904
+ const d = db || getDatabase();
51905
+ const requestDigest = digestProjectUpdateValue({
51906
+ ...request,
51907
+ target_id: id,
51908
+ direction: "forward",
51909
+ updates: normalized
51910
+ });
51911
+ return d.transaction(() => {
51912
+ const prior = findProjectUpdateReceiptByKey(d, request, "forward", request.idempotency_key);
51913
+ if (prior) {
51914
+ if (prior.request_digest !== requestDigest || prior.target_id !== id) {
51915
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_IDEMPOTENCY_MISMATCH", "caller idempotency key is already bound to a different request");
51916
+ }
51917
+ const current = getProjectByExactId2(id, d);
51918
+ if (!current || canonicalProjectUpdateJson(current) !== canonicalProjectUpdateJson(prior.after_project)) {
51919
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "accepted guarded update target drifted after its immutable receipt");
51920
+ }
51921
+ return { dry_run: false, applied: true, project: prior.after_project, receipt: prior };
51922
+ }
51923
+ const before = getProjectByExactId2(id, d);
51924
+ if (!before) {
51925
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
51926
+ }
51927
+ if (before.updated_at !== request.expected_revision) {
51928
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project revision changed before the guarded update", { expected_revision: request.expected_revision, current_revision: before.updated_at });
51929
+ }
51930
+ assertNoProjectCollision(id, normalized, d);
51931
+ const revision = nextProjectRevision(before.updated_at);
51932
+ const after = projectWithUpdates(before, normalized, revision);
51933
+ const result = d.run(`
51934
+ UPDATE projects
51935
+ SET name = ?, path = ?, description = ?, memory_prefix = ?, updated_at = ?
51936
+ WHERE id = ? AND updated_at = ?
51937
+ `, [
51938
+ after.name,
51939
+ after.path,
51940
+ after.description,
51941
+ after.memory_prefix,
51942
+ after.updated_at,
51943
+ id,
51944
+ request.expected_revision
51945
+ ]);
51946
+ if (result.changes !== 1) {
51947
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project compare-and-swap did not update exactly one row");
51948
+ }
51949
+ const readback = getProjectByExactId2(id, d);
51950
+ if (!readback || canonicalProjectUpdateJson(readback) !== canonicalProjectUpdateJson(after)) {
51951
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "Project guarded update did not read back exactly under the stable ID");
51952
+ }
51953
+ const receipt = makeProjectUpdateReceipt({
51954
+ request: { ...request, updates: normalized },
51955
+ direction: "forward",
51956
+ request_digest: requestDigest,
51957
+ target_id: id,
51958
+ before_project: before,
51959
+ after_project: readback
51960
+ });
51961
+ insertProjectUpdateReceipt(d, receipt);
51962
+ return { dry_run: false, applied: true, project: readback, receipt };
51963
+ });
51964
+ }
51965
+ function rollbackProjectUpdate(id, request, db) {
51966
+ assertProjectUpdateRequest(request);
51967
+ assertBoundedIdentifier2(request.accepted_receipt_id, "accepted_receipt_id");
51968
+ if (!db && isApiMode()) {
51969
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-rollback`, request);
51970
+ return data;
51971
+ }
51972
+ const d = db || getDatabase();
51973
+ const requestDigest = digestProjectUpdateValue({
51974
+ ...request,
51975
+ target_id: id,
51976
+ direction: "rollback"
51977
+ });
51978
+ return d.transaction(() => {
51979
+ const prior = findProjectUpdateReceiptByKey(d, request, "rollback", request.idempotency_key);
51980
+ if (prior) {
51981
+ if (prior.request_digest !== requestDigest || prior.target_id !== id) {
51982
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_IDEMPOTENCY_MISMATCH", "caller idempotency key is already bound to a different rollback request");
51983
+ }
51984
+ const current2 = getProjectByExactId2(id, d);
51985
+ if (!current2 || canonicalProjectUpdateJson(current2) !== canonicalProjectUpdateJson(prior.after_project)) {
51986
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "accepted rollback target drifted after its immutable receipt");
51987
+ }
51988
+ return { dry_run: false, applied: true, project: prior.after_project, receipt: prior };
51989
+ }
51990
+ const accepted = findProjectUpdateReceiptById(d, request, request.accepted_receipt_id);
51991
+ if (!accepted || accepted.direction !== "forward" || accepted.target_id !== id) {
51992
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_RECEIPT_NOT_FOUND", "accepted forward update receipt was not found for this exact project");
51993
+ }
51994
+ const current = getProjectByExactId2(id, d);
51995
+ if (!current) {
51996
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
51997
+ }
51998
+ if (current.updated_at !== request.expected_revision || canonicalProjectUpdateJson(current) !== canonicalProjectUpdateJson(accepted.after_project)) {
51999
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project no longer matches the accepted forward receipt", { expected_revision: request.expected_revision, current_revision: current.updated_at });
52000
+ }
52001
+ assertNoProjectCollision(id, accepted.before_project, d);
52002
+ const restored = accepted.before_project;
52003
+ const result = d.run(`
52004
+ UPDATE projects
52005
+ SET name = ?, path = ?, description = ?, memory_prefix = ?,
52006
+ created_at = ?, updated_at = ?
52007
+ WHERE id = ? AND updated_at = ?
52008
+ `, [
52009
+ restored.name,
52010
+ restored.path,
52011
+ restored.description,
52012
+ restored.memory_prefix,
52013
+ restored.created_at,
52014
+ restored.updated_at,
52015
+ id,
52016
+ request.expected_revision
52017
+ ]);
52018
+ if (result.changes !== 1) {
52019
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project rollback compare-and-swap did not update exactly one row");
52020
+ }
52021
+ const readback = getProjectByExactId2(id, d);
52022
+ if (!readback || canonicalProjectUpdateJson(readback) !== canonicalProjectUpdateJson(restored)) {
52023
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "Project rollback did not restore the exact prior row");
52024
+ }
52025
+ const receipt = makeProjectUpdateReceipt({
52026
+ request,
52027
+ direction: "rollback",
52028
+ request_digest: requestDigest,
52029
+ target_id: id,
52030
+ before_project: current,
52031
+ after_project: readback,
52032
+ accepted_receipt_id: accepted.receipt_id
52033
+ });
52034
+ insertProjectUpdateReceipt(d, receipt);
52035
+ return { dry_run: false, applied: true, project: readback, receipt };
52036
+ });
52037
+ }
52038
+ function getProjectUpdateReceipt(id, receiptId, identity = PROJECT_UPDATE_AUTHORITY, db) {
52039
+ assertProjectUpdateIdentity(identity);
52040
+ if (!db && isApiMode()) {
52041
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/update-receipts/lookup`, { ...identity, receipt_id: receiptId });
52042
+ return data;
52043
+ }
52044
+ const d = db || getDatabase();
52045
+ const receipt = findProjectUpdateReceiptById(d, identity, receiptId);
52046
+ if (!receipt || receipt.target_id !== id) {
52047
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_RECEIPT_NOT_FOUND", "immutable project update receipt was not found for this exact project");
52048
+ }
52049
+ return receipt;
52050
+ }
52051
+ // src/project-registration/authority.ts
52052
+ import { createHash as createHash3 } from "crypto";
52053
+ import { resolve as resolve2 } from "path";
52054
+
52055
+ // src/project-registration/project-references.ts
52056
+ var MEMENTOS_PROJECT_REFERENCE_SURFACES = [
52057
+ { key: "memories", table: "memories", column: "project_id" },
52058
+ { key: "sessions", table: "sessions", column: "project_id" },
52059
+ { key: "entities", table: "entities", column: "project_id" },
52060
+ { key: "agents", table: "agents", column: "active_project_id" },
52061
+ { key: "tool_events", table: "tool_events", column: "project_id" },
52062
+ { key: "tasks", table: "tasks", column: "project_id" },
52063
+ {
52064
+ key: "memory_consolidation_runs",
52065
+ table: "memory_consolidation_runs",
52066
+ column: "project_id"
52067
+ },
52068
+ {
52069
+ key: "memory_reflection_runs",
52070
+ table: "memory_reflection_runs",
52071
+ column: "project_id"
52072
+ },
52073
+ { key: "memory_acl", table: "memory_acl", column: "project_id" },
52074
+ { key: "search_history", table: "search_history", column: "project_id" },
52075
+ {
52076
+ key: "session_memory_jobs",
52077
+ table: "session_memory_jobs",
52078
+ column: "project_id"
52079
+ },
52080
+ { key: "synthesis_events", table: "synthesis_events", column: "project_id" },
52081
+ { key: "synthesis_runs", table: "synthesis_runs", column: "project_id" },
52082
+ { key: "webhook_hooks", table: "webhook_hooks", column: "project_id" }
52083
+ ];
52084
+ function quotedIdentifier(value) {
52085
+ if (!/^[a-z_][a-z0-9_]*$/.test(value)) {
52086
+ throw new Error(`unsafe project-reference identifier: ${value}`);
52087
+ }
52088
+ return `"${value}"`;
52089
+ }
52090
+ function mementosProjectReferenceCounts(db, projectId) {
52091
+ const projections = MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ key, table, column }) => `(SELECT COUNT(*) FROM ${quotedIdentifier(table)} ` + `WHERE ${quotedIdentifier(column)} = ?) AS ${quotedIdentifier(key)}`).join(`,
52092
+ `);
52093
+ const row = db.get(`SELECT
52094
+ ${projections}`, ...MEMENTOS_PROJECT_REFERENCE_SURFACES.map(() => projectId));
52095
+ return Object.fromEntries(MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ key }) => [key, Number(row[key] ?? 0)]));
52096
+ }
52097
+ function hasMementosProjectReferences(counts) {
52098
+ return MEMENTOS_PROJECT_REFERENCE_SURFACES.some(({ key }) => counts[key] > 0);
52099
+ }
52100
+ function deleteMementosProjectIfUnreferenced(db, projectId) {
52101
+ const guards = MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ table, column }) => `NOT EXISTS (SELECT 1 FROM ${quotedIdentifier(table)} ` + `WHERE ${quotedIdentifier(column)} = ?)`).join(`
52102
+ AND `);
52103
+ const result = db.run(`DELETE FROM projects
52104
+ WHERE id = ?
52105
+ AND ${guards}`, projectId, ...MEMENTOS_PROJECT_REFERENCE_SURFACES.map(() => projectId));
52106
+ return result.changes;
52107
+ }
52108
+
52109
+ // src/project-registration/authority.ts
52110
+ var WORKSPACE_ID_PATTERN = /^wks_[A-Za-z0-9][A-Za-z0-9_-]{11,}$/;
52111
+ var OPERATION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
52112
+ var STEP_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/;
52113
+ var SHA256_PATTERN = /^[0-9a-f]{64}$/;
52114
+ var IDEMPOTENCY_PATTERN = /^prk_[0-9a-f]{48}$/;
52115
+ var PROJECT_ID_PATTERN = /^mm_project_[0-9a-f]{40}$/;
52116
+
52117
+ class WriteBoundaryError extends Error {
52118
+ point;
52119
+ cause;
52120
+ constructor(point, cause) {
52121
+ super(`Mementos project registration failed at ${point}`);
52122
+ this.point = point;
52123
+ this.cause = cause;
52124
+ }
52125
+ }
52126
+ function canonicalMementosProjectRegistrationJson(value) {
52127
+ return JSON.stringify(canonicalize2(value));
52128
+ }
52129
+ function canonicalize2(value) {
52130
+ if (Array.isArray(value))
52131
+ return value.map(canonicalize2);
52132
+ if (!value || typeof value !== "object")
52133
+ return value;
52134
+ const out = {};
52135
+ for (const key of Object.keys(value).sort()) {
52136
+ const item = value[key];
52137
+ if (item !== undefined)
52138
+ out[key] = canonicalize2(item);
52139
+ }
52140
+ return out;
52141
+ }
52142
+ function digestMementosProjectRegistrationValue(value) {
52143
+ return createHash3("sha256").update(canonicalMementosProjectRegistrationJson(value)).digest("hex");
52144
+ }
52145
+ function digestOwnedPath(path) {
52146
+ return createHash3("sha256").update(path).digest("hex");
52147
+ }
52148
+ function deriveMementosProjectRegistrationIdempotencyKey(input) {
52149
+ return `prk_${digestMementosProjectRegistrationValue({
52150
+ route: MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE,
52151
+ operation_id: input.operation_id,
52152
+ step_id: input.step_id,
52153
+ direction: input.direction,
52154
+ target_selector: input.target_selector,
52155
+ request_digest: input.request_digest,
52156
+ precondition_digest: input.precondition_digest
52157
+ }).slice(0, 48)}`;
52158
+ }
52159
+ function responseBytes(value) {
52160
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
52161
+ }
52162
+ function assertBounds(bounds) {
52163
+ if (!Number.isSafeInteger(bounds.response_byte_limit) || bounds.response_byte_limit <= 0) {
52164
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "response_byte_limit must be a positive integer");
52165
+ }
52166
+ if (!Number.isSafeInteger(bounds.time_budget_ms) || bounds.time_budget_ms <= 0) {
52167
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "time_budget_ms must be a positive integer");
52168
+ }
52169
+ }
52170
+ function assertWithinBounds(value, bounds, startedAt) {
52171
+ const bytes = responseBytes(value);
52172
+ if (bytes > bounds.response_byte_limit) {
52173
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RESPONSE_TOO_LARGE", `registration response requires ${bytes} bytes but the bound is ${bounds.response_byte_limit}`, { response_bytes: bytes, response_byte_limit: bounds.response_byte_limit });
52174
+ }
52175
+ const elapsed = Date.now() - startedAt;
52176
+ if (elapsed > bounds.time_budget_ms) {
52177
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_TIME_BUDGET_EXCEEDED", `registration call took ${elapsed}ms but the bound is ${bounds.time_budget_ms}ms`, { elapsed_ms: elapsed, time_budget_ms: bounds.time_budget_ms });
52178
+ }
52179
+ return { response_bytes: bytes, elapsed_ms: elapsed };
52180
+ }
52181
+ function withResponseControl(receipt, bounds, startedAt) {
52182
+ const result = {
52183
+ receipt,
52184
+ response_control: {
52185
+ response_byte_limit: bounds.response_byte_limit,
52186
+ time_budget_ms: bounds.time_budget_ms,
52187
+ response_bytes: 0,
52188
+ elapsed_ms: 0,
52189
+ complete: true,
52190
+ truncated: false
52191
+ }
52192
+ };
52193
+ for (let attempt = 0;attempt < 8; attempt += 1) {
52194
+ const measured2 = assertWithinBounds(result, bounds, startedAt);
52195
+ const stable = result.response_control.response_bytes === measured2.response_bytes && result.response_control.elapsed_ms === measured2.elapsed_ms;
52196
+ result.response_control.response_bytes = measured2.response_bytes;
52197
+ result.response_control.elapsed_ms = measured2.elapsed_ms;
52198
+ if (stable)
52199
+ break;
52200
+ }
52201
+ const measured = assertWithinBounds(result, bounds, startedAt);
52202
+ result.response_control.response_bytes = measured.response_bytes;
52203
+ result.response_control.elapsed_ms = measured.elapsed_ms;
52204
+ return result;
52205
+ }
52206
+ function requireString(value, field, options = {}) {
52207
+ const min = options.min ?? 1;
52208
+ const max = options.max ?? 512;
52209
+ if (typeof value !== "string" || value.length < min || value.length > max || /[\u0000-\u001f]/.test(value) || options.pattern && !options.pattern.test(value)) {
52210
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} is not a valid bounded registration identifier`);
52211
+ }
52212
+ return value;
52213
+ }
52214
+ function exactKeys(value, expected, field) {
52215
+ const actual = Object.keys(value).sort();
52216
+ const wanted = [...expected].sort();
52217
+ if (actual.length !== wanted.length || actual.some((key, i) => key !== wanted[i])) {
52218
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} must contain exactly: ${wanted.join(", ")}`);
52219
+ }
52220
+ }
52221
+ function ownedPath(target) {
52222
+ if (!target || typeof target.withOwnedPath !== "function") {
52223
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target must be a package-owned path handle");
52224
+ }
52225
+ const path = target.withOwnedPath((value) => value);
52226
+ requireString(path, "target path", { max: 4096 });
52227
+ if (path !== resolve2(path)) {
52228
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target path must already be canonical and absolute");
52229
+ }
52230
+ return path;
52231
+ }
52232
+ function normalizedCallDigest(request) {
52233
+ return digestMementosProjectRegistrationValue({
52234
+ authority_route: request.authority_route,
52235
+ package_version: request.package_version,
52236
+ authority_id: request.authority_id,
52237
+ tenant_id: request.tenant_id,
52238
+ corpus_id: request.corpus_id,
52239
+ operation_id: request.operation_id,
52240
+ step_id: request.step_id,
52241
+ resource_kind: request.resource_kind,
52242
+ direction: request.direction,
52243
+ target_selector: request.target_selector,
52244
+ idempotency_key: request.idempotency_key,
52245
+ request_digest: request.request_digest,
52246
+ precondition_digest: request.precondition_digest,
52247
+ project_id: request.project_id,
52248
+ project_slug: request.project_slug,
52249
+ project_name: request.project_name,
52250
+ desired: request.desired,
52251
+ accepted_receipt_id: request.accepted_receipt?.receipt_id ?? null
52252
+ });
52253
+ }
52254
+ function publicReceipt(row) {
52255
+ const { target_selector: _selector, normalized_call_digest: _digest, ...receipt } = row;
52256
+ return receipt;
52257
+ }
52258
+ function timestampString3(value) {
52259
+ return value instanceof Date ? value.toISOString() : String(value);
52260
+ }
52261
+ function receiptFromRow2(row) {
52262
+ return {
52263
+ ...row,
52264
+ authority: "mementos",
52265
+ resource_kind: "project",
52266
+ created_by_operation: row["created_by_operation"] === true || Number(row["created_by_operation"]) === 1,
52267
+ created_at: timestampString3(row["created_at"])
52268
+ };
52269
+ }
52270
+ function bindingFromRow(row) {
52271
+ return {
52272
+ ...row,
52273
+ created_at: timestampString3(row["created_at"]),
52274
+ updated_at: timestampString3(row["updated_at"])
52275
+ };
52276
+ }
52277
+ function getStoredReceipt(db, receiptId) {
52278
+ const row = db.get("SELECT * FROM mementos_project_registration_receipts WHERE receipt_id = ? LIMIT 1", receiptId);
52279
+ return row ? receiptFromRow2(row) : null;
52280
+ }
52281
+ function getAcceptedReceipt(db, request) {
52282
+ const row = db.get(`
52283
+ SELECT * FROM mementos_project_registration_receipts
52284
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
52285
+ AND operation_id = ? AND step_id = ? AND resource_kind = 'project'
52286
+ AND direction = ? AND outcome = 'accepted'
52287
+ ORDER BY created_at ASC, receipt_id ASC
52288
+ LIMIT 1
52289
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.operation_id, request.step_id, request.direction);
52290
+ return row ? receiptFromRow2(row) : null;
52291
+ }
52292
+ function getReceiptForLookup(db, request) {
52293
+ const row = db.get(`
52294
+ SELECT * FROM mementos_project_registration_receipts
52295
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
52296
+ AND operation_id = ? AND step_id = ? AND resource_kind = 'project'
52297
+ AND direction = ? AND idempotency_key = ? AND target_selector = ?
52298
+ ORDER BY CASE outcome
52299
+ WHEN 'terminal_nonacceptance' THEN 0
52300
+ WHEN 'duplicate_of_accepted' THEN 1
52301
+ ELSE 2
52302
+ END, created_at DESC, receipt_id DESC
52303
+ LIMIT 1
52304
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.operation_id, request.step_id, request.direction, request.idempotency_key, request.target_selector);
52305
+ return row ? receiptFromRow2(row) : null;
52306
+ }
52307
+ function getBinding(db, capability, targetSelector) {
52308
+ const row = db.get(`
52309
+ SELECT * FROM mementos_project_registration_bindings
52310
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
52311
+ AND resource_kind = 'project' AND target_selector = ?
52312
+ LIMIT 1
52313
+ `, capability.authority_id, capability.tenant_id, capability.corpus_id, targetSelector);
52314
+ return row ? bindingFromRow(row) : null;
52315
+ }
52316
+ function getProjectByExactId3(db, id) {
52317
+ const row = db.get("SELECT * FROM projects WHERE id = ? LIMIT 1", id);
52318
+ if (!row)
52319
+ return null;
52320
+ return {
52321
+ id: String(row["id"]),
52322
+ name: String(row["name"]),
52323
+ path: String(row["path"]),
52324
+ description: row["description"] === null ? null : String(row["description"]),
52325
+ memory_prefix: row["memory_prefix"] === null ? null : String(row["memory_prefix"]),
52326
+ created_at: timestampString3(row["created_at"]),
52327
+ updated_at: timestampString3(row["updated_at"])
52328
+ };
52329
+ }
52330
+ function getProjectByPath(db, path) {
52331
+ const row = db.get("SELECT * FROM projects WHERE path = ? LIMIT 1", path);
52332
+ return row ? getProjectByExactId3(db, String(row["id"])) : null;
52333
+ }
52334
+ function projectRecord(db, project, references = mementosProjectReferenceCounts(db, project.id)) {
52335
+ return {
52336
+ target_id: project.id,
52337
+ revision: project.updated_at,
52338
+ digest: digestMementosProjectRegistrationValue({
52339
+ id: project.id,
52340
+ name: project.name,
52341
+ path: project.path,
52342
+ description: project.description,
52343
+ memory_prefix: project.memory_prefix,
52344
+ created_at: project.created_at,
52345
+ updated_at: project.updated_at,
52346
+ references
52347
+ })
52348
+ };
52349
+ }
52350
+ function targetId(capability, selector) {
52351
+ return `mm_project_${digestMementosProjectRegistrationValue({
52352
+ authority_id: capability.authority_id,
52353
+ tenant_id: capability.tenant_id,
52354
+ corpus_id: capability.corpus_id,
52355
+ selector
52356
+ }).slice(0, 40)}`;
52357
+ }
52358
+ function receiptId(input) {
52359
+ return `mmpr_${digestMementosProjectRegistrationValue(input).slice(0, 40)}`;
52360
+ }
52361
+ function makeReceipt2(request, capability, callDigest, now2, values) {
52362
+ const withoutId = {
52363
+ authority: "mementos",
52364
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
52365
+ package_version: capability.package_version,
52366
+ authority_id: capability.authority_id,
52367
+ tenant_id: capability.tenant_id,
52368
+ corpus_id: capability.corpus_id,
52369
+ operation_id: request.operation_id,
52370
+ step_id: request.step_id,
52371
+ resource_kind: "project",
52372
+ direction: request.direction,
52373
+ target_selector: values.target_selector ?? request.target_selector,
52374
+ idempotency_key: request.idempotency_key,
52375
+ request_digest: request.request_digest,
52376
+ precondition_digest: request.precondition_digest,
52377
+ normalized_call_digest: callDigest,
52378
+ outcome: values.outcome,
52379
+ reason: values.reason ?? null,
52380
+ target_id: values.target_id ?? null,
52381
+ result_revision: values.result_revision ?? null,
52382
+ result_digest: values.result_digest ?? null,
52383
+ duplicate_of_receipt_id: values.duplicate_of_receipt_id ?? null,
52384
+ accepted_receipt_id: values.accepted_receipt_id ?? null,
52385
+ created_by_operation: values.created_by_operation ?? false
52386
+ };
52387
+ return {
52388
+ receipt_id: receiptId(withoutId),
52389
+ ...withoutId,
52390
+ created_at: now2
52391
+ };
52392
+ }
52393
+ function insertReceipt2(db, receipt) {
52394
+ db.run(`
52395
+ INSERT INTO mementos_project_registration_receipts (
52396
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
52397
+ corpus_id, operation_id, step_id, resource_kind, direction,
52398
+ target_selector, idempotency_key, request_digest, precondition_digest,
52399
+ normalized_call_digest, outcome, reason, target_id, result_revision,
52400
+ result_digest, duplicate_of_receipt_id, accepted_receipt_id,
52401
+ created_by_operation, created_at
52402
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
52403
+ ON CONFLICT DO NOTHING
52404
+ `, receipt.receipt_id, receipt.authority, receipt.route, receipt.package_version, receipt.authority_id, receipt.tenant_id, receipt.corpus_id, receipt.operation_id, receipt.step_id, receipt.resource_kind, receipt.direction, receipt.target_selector, receipt.idempotency_key, receipt.request_digest, receipt.precondition_digest, receipt.normalized_call_digest, receipt.outcome, receipt.reason, receipt.target_id, receipt.result_revision, receipt.result_digest, receipt.duplicate_of_receipt_id, receipt.accepted_receipt_id, receipt.created_by_operation, receipt.created_at);
52405
+ const stored = getStoredReceipt(db, receipt.receipt_id);
52406
+ const withoutCreatedAt = (value) => {
52407
+ const { created_at: _createdAt, ...logical } = value;
52408
+ return logical;
52409
+ };
52410
+ if (!stored || canonicalMementosProjectRegistrationJson(withoutCreatedAt(stored)) !== canonicalMementosProjectRegistrationJson(withoutCreatedAt(receipt))) {
52411
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "immutable receipt id resolved to different content");
52412
+ }
52413
+ return stored;
52414
+ }
52415
+ function insertBinding(db, request, callDigest, now2) {
52416
+ const result = db.run(`
52417
+ INSERT INTO mementos_project_registration_bindings (
52418
+ authority_id, tenant_id, corpus_id, resource_kind, target_selector,
52419
+ operation_id, step_id, direction, idempotency_key, request_digest,
52420
+ precondition_digest, normalized_call_digest, state, target_id,
52421
+ accepted_receipt_id, result_revision, result_digest, removed_receipt_id,
52422
+ created_at, updated_at
52423
+ ) VALUES (?, ?, ?, 'project', ?, ?, ?, 'forward', ?, ?, ?, ?, 'pending',
52424
+ NULL, NULL, NULL, NULL, NULL, ?, ?)
52425
+ ON CONFLICT DO NOTHING
52426
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.target_selector, request.operation_id, request.step_id, request.idempotency_key, request.request_digest, request.precondition_digest, callDigest, now2, now2);
52427
+ return result.changes === 1;
52428
+ }
52429
+ function lockBinding(db, capability, targetSelector) {
52430
+ const result = db.run(`
52431
+ UPDATE mementos_project_registration_bindings
52432
+ SET updated_at = updated_at
52433
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
52434
+ AND resource_kind = 'project' AND target_selector = ?
52435
+ `, capability.authority_id, capability.tenant_id, capability.corpus_id, targetSelector);
52436
+ return result.changes === 1;
52437
+ }
52438
+ function setBindingAccepted(db, request, receipt, now2) {
52439
+ const result = db.run(`
52440
+ UPDATE mementos_project_registration_bindings
52441
+ SET state = 'accepted', target_id = ?, accepted_receipt_id = ?,
52442
+ result_revision = ?, result_digest = ?, updated_at = ?
52443
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
52444
+ AND resource_kind = 'project' AND target_selector = ? AND state = 'pending'
52445
+ `, receipt.target_id, receipt.receipt_id, receipt.result_revision, receipt.result_digest, now2, request.authority_id, request.tenant_id, request.corpus_id, request.target_selector);
52446
+ if (result.changes !== 1) {
52447
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding was not pending at acceptance");
52448
+ }
52449
+ }
52450
+ function setBindingRemoved(db, accepted, inverseReceiptId, now2) {
52451
+ const result = db.run(`
52452
+ UPDATE mementos_project_registration_bindings
52453
+ SET state = 'removed', removed_receipt_id = ?, updated_at = ?
52454
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
52455
+ AND resource_kind = 'project' AND target_selector = ?
52456
+ AND state = 'accepted' AND accepted_receipt_id = ?
52457
+ `, inverseReceiptId, now2, accepted.authority_id, accepted.tenant_id, accepted.corpus_id, accepted.target_selector, accepted.receipt_id);
52458
+ if (result.changes !== 1) {
52459
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding was not accepted at inverse");
52460
+ }
52461
+ }
52462
+ function capabilityMatches(request, capability) {
52463
+ return request.authority_route === capability.route && request.package_version === capability.package_version && request.authority_id === capability.authority_id && request.tenant_id === capability.tenant_id && request.corpus_id === capability.corpus_id;
52464
+ }
52465
+ function assertCommonRequest(request, capability) {
52466
+ assertBounds(request);
52467
+ if (request.resource_kind !== "project") {
52468
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project");
52469
+ }
52470
+ if (request.direction !== "forward" && request.direction !== "inverse") {
52471
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "direction must be forward or inverse");
52472
+ }
52473
+ if (!capabilityMatches(request, capability)) {
52474
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "registration request does not match this authority capability identity");
52475
+ }
52476
+ requireString(request.operation_id, "operation_id", {
52477
+ min: 8,
52478
+ max: 128,
52479
+ pattern: OPERATION_PATTERN
52480
+ });
52481
+ requireString(request.step_id, "step_id", { min: 3, max: 128, pattern: STEP_PATTERN });
52482
+ requireString(request.target_selector, "target_selector", { max: 512 });
52483
+ requireString(request.project_id, "project_id", {
52484
+ min: 16,
52485
+ max: 128,
52486
+ pattern: WORKSPACE_ID_PATTERN
52487
+ });
52488
+ requireString(request.project_name, "project_name", { max: 256 });
52489
+ requireString(request.project_slug, "project_slug", { max: 128 });
52490
+ requireString(request.request_digest, "request_digest", {
52491
+ min: 64,
52492
+ max: 64,
52493
+ pattern: SHA256_PATTERN
52494
+ });
52495
+ requireString(request.precondition_digest, "precondition_digest", {
52496
+ min: 64,
52497
+ max: 64,
52498
+ pattern: SHA256_PATTERN
52499
+ });
52500
+ requireString(request.idempotency_key, "idempotency_key", {
52501
+ min: 52,
52502
+ max: 52,
52503
+ pattern: IDEMPOTENCY_PATTERN
52504
+ });
52505
+ if (!request.desired || typeof request.desired !== "object" || Array.isArray(request.desired)) {
52506
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "desired must be a JSON object");
52507
+ }
52508
+ if (digestMementosProjectRegistrationValue(request.desired) !== request.request_digest) {
52509
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "request_digest does not match the canonical desired payload");
52510
+ }
52511
+ const expectedKey = deriveMementosProjectRegistrationIdempotencyKey(request);
52512
+ if (request.idempotency_key !== expectedKey) {
52513
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "idempotency_key does not match the deterministic request", { expected: expectedKey });
52514
+ }
52515
+ }
52516
+ function assertForwardRequest2(request, capability) {
52517
+ assertCommonRequest(request, capability);
52518
+ if (request.direction !== "forward" || request.accepted_receipt !== undefined) {
52519
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional create requires a forward request without an accepted receipt");
52520
+ }
52521
+ exactKeys(request.desired, [
52522
+ "name",
52523
+ "source_project_id",
52524
+ "source_project_slug",
52525
+ "target_path_digest"
52526
+ ], "forward desired");
52527
+ if (request.target_selector !== request.project_id || request.desired["source_project_id"] !== request.project_id || request.desired["source_project_slug"] !== request.project_slug || request.desired["name"] !== request.project_name) {
52528
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "forward project identity fields disagree");
52529
+ }
52530
+ const expectedPrecondition = digestMementosProjectRegistrationValue({
52531
+ target_selector: request.target_selector,
52532
+ expected: "absent"
52533
+ });
52534
+ if (request.precondition_digest !== expectedPrecondition) {
52535
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "forward precondition must bind expected absence");
52536
+ }
52537
+ const path = ownedPath(request.target);
52538
+ if (request.desired["target_path_digest"] !== digestOwnedPath(path)) {
52539
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "target path does not match the package-owned digest");
52540
+ }
52541
+ return path;
52542
+ }
52543
+ function sameReceipt(left, right) {
52544
+ return canonicalMementosProjectRegistrationJson(publicReceipt(left)) === canonicalMementosProjectRegistrationJson(right);
52545
+ }
52546
+ function assertInverseRequest(request, capability) {
52547
+ assertCommonRequest(request, capability);
52548
+ if (request.direction !== "inverse" || !request.accepted_receipt) {
52549
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional inverse requires the accepted forward receipt");
52550
+ }
52551
+ const accepted = request.accepted_receipt;
52552
+ if (accepted.authority !== "mementos" || accepted.route !== capability.route || accepted.package_version !== capability.package_version || accepted.authority_id !== capability.authority_id || accepted.tenant_id !== capability.tenant_id || accepted.corpus_id !== capability.corpus_id || accepted.operation_id !== request.operation_id || accepted.step_id !== request.step_id || accepted.resource_kind !== "project" || accepted.direction !== "forward" || accepted.outcome !== "accepted" || !accepted.created_by_operation || !accepted.target_id || !accepted.result_revision || !accepted.result_digest || request.target_selector !== accepted.target_id) {
52553
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "accepted receipt is not an exact attempt-created forward receipt");
52554
+ }
52555
+ exactKeys(request.desired, ["accepted_receipt_id", "target_id"], "inverse desired");
52556
+ if (request.desired["accepted_receipt_id"] !== accepted.receipt_id || request.desired["target_id"] !== accepted.target_id) {
52557
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "inverse desired payload does not bind the accepted receipt");
52558
+ }
52559
+ const expectedPrecondition = digestMementosProjectRegistrationValue({
52560
+ expected_revision: accepted.result_revision,
52561
+ expected_digest: accepted.result_digest
52562
+ });
52563
+ if (request.precondition_digest !== expectedPrecondition) {
52564
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "inverse precondition does not bind the accepted object state");
52565
+ }
52566
+ return accepted;
52567
+ }
52568
+ function assertLookup(request, capability) {
52569
+ assertBounds(request);
52570
+ if (request.max_items !== 1) {
52571
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "terminal receipt lookup requires max_items exactly 1");
52572
+ }
52573
+ if (request.authority !== "mementos" || request.resource_kind !== "project" || request.authority_route !== capability.route || request.package_version !== capability.package_version || request.authority_id !== capability.authority_id || request.tenant_id !== capability.tenant_id || request.corpus_id !== capability.corpus_id) {
52574
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "receipt lookup does not match this authority capability identity");
52575
+ }
52576
+ requireString(request.operation_id, "operation_id", { pattern: OPERATION_PATTERN });
52577
+ requireString(request.step_id, "step_id", { pattern: STEP_PATTERN });
52578
+ requireString(request.target_selector, "target_selector");
52579
+ requireString(request.idempotency_key, "idempotency_key", {
52580
+ min: 52,
52581
+ max: 52,
52582
+ pattern: IDEMPOTENCY_PATTERN
52583
+ });
52584
+ if (request.target_id !== undefined) {
52585
+ requireString(request.target_id, "target_id", {
52586
+ min: 51,
52587
+ max: 51,
52588
+ pattern: PROJECT_ID_PATTERN
52589
+ });
52590
+ }
52591
+ }
52592
+
52593
+ class PackageOwnedMementosProjectRegistrationAuthority {
52594
+ db;
52595
+ authority = "mementos";
52596
+ capabilityValue;
52597
+ now;
52598
+ faultInjector;
52599
+ constructor(db, options = {}) {
52600
+ this.db = db;
52601
+ this.now = options.now ?? (() => new Date().toISOString());
52602
+ this.faultInjector = options.faultInjector;
52603
+ this.capabilityValue = {
52604
+ authority: "mementos",
52605
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
52606
+ package_version: options.packageVersion ?? getMementosPackageVersion(),
52607
+ authority_id: options.authorityId ?? "mementos",
52608
+ tenant_id: options.tenantId ?? "default",
52609
+ corpus_id: options.corpusId ?? "default",
52610
+ supported_resources: ["project"],
52611
+ conditional_create: true,
52612
+ immutable_receipts: true,
52613
+ exact_terminal_lookup: true,
52614
+ exact_readback: true,
52615
+ conditional_inverse: true,
52616
+ ambiguous_outcome_reconciliation: true,
52617
+ guarded_update: true,
52618
+ no_write_dry_run: true,
52619
+ expected_revision_compare_and_swap: true,
52620
+ caller_idempotency: true,
52621
+ exact_inverse_rollback: true
52622
+ };
52623
+ }
52624
+ fault(point, request) {
52625
+ this.faultInjector?.(point, {
52626
+ operation_id: request.operation_id,
52627
+ step_id: request.step_id,
52628
+ resource_kind: "project",
52629
+ direction: request.direction
52630
+ });
52631
+ }
52632
+ writeBoundary(point, request) {
52633
+ try {
52634
+ this.fault(point, request);
52635
+ } catch (cause) {
52636
+ throw new WriteBoundaryError(point, cause);
52637
+ }
52638
+ }
52639
+ terminal(request, callDigest, reason, values = {}) {
52640
+ return insertReceipt2(this.db, makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
52641
+ outcome: "terminal_nonacceptance",
52642
+ reason,
52643
+ target_id: values.target_id,
52644
+ accepted_receipt_id: values.accepted_receipt_id,
52645
+ target_selector: values.target_selector
52646
+ }));
52647
+ }
52648
+ duplicateForwardOrTerminal(request, callDigest, path, accepted) {
52649
+ if (accepted.normalized_call_digest !== callDigest) {
52650
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "accepted operation/step identity is bound to a different request");
52651
+ }
52652
+ const project = accepted.target_id ? getProjectByExactId3(this.db, accepted.target_id) : null;
52653
+ if (!project || project.path !== path) {
52654
+ return this.terminal(request, callDigest, "accepted_target_missing_or_replaced", {
52655
+ target_id: accepted.target_id
52656
+ });
52657
+ }
52658
+ const record = projectRecord(this.db, project);
52659
+ if (record.revision !== accepted.result_revision || record.digest !== accepted.result_digest) {
52660
+ return this.terminal(request, callDigest, "accepted_target_drifted", {
52661
+ target_id: accepted.target_id
52662
+ });
52663
+ }
52664
+ return insertReceipt2(this.db, makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
52665
+ outcome: "duplicate_of_accepted",
52666
+ target_id: accepted.target_id,
52667
+ result_revision: accepted.result_revision,
52668
+ result_digest: accepted.result_digest,
52669
+ duplicate_of_receipt_id: accepted.receipt_id,
52670
+ created_by_operation: false
52671
+ }));
52672
+ }
52673
+ async capability() {
52674
+ return {
52675
+ ...this.capabilityValue,
52676
+ supported_resources: ["project"]
52677
+ };
52678
+ }
52679
+ async create(request) {
52680
+ const startedAt = Date.now();
52681
+ const path = assertForwardRequest2(request, this.capabilityValue);
52682
+ const callDigest = normalizedCallDigest(request);
52683
+ let stored;
52684
+ try {
52685
+ stored = this.db.transaction(() => {
52686
+ const exact = getReceiptForLookup(this.db, {
52687
+ ...request,
52688
+ authority: "mementos",
52689
+ max_items: 1
52690
+ });
52691
+ if (exact?.outcome === "terminal_nonacceptance")
52692
+ return exact;
52693
+ const accepted = getAcceptedReceipt(this.db, request);
52694
+ if (accepted) {
52695
+ return this.duplicateForwardOrTerminal(request, callDigest, path, accepted);
52696
+ }
52697
+ const existing = getProjectByPath(this.db, path) ?? getProjectByExactId3(this.db, targetId(this.capabilityValue, request.target_selector));
52698
+ if (existing) {
52699
+ return this.terminal(request, callDigest, "target_preexists", {
52700
+ target_id: existing.id
52701
+ });
52702
+ }
52703
+ const now2 = this.now();
52704
+ const insertedBinding = insertBinding(this.db, request, callDigest, now2);
52705
+ if (!lockBinding(this.db, this.capabilityValue, request.target_selector)) {
52706
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding disappeared before it could be locked");
52707
+ }
52708
+ const ownedBinding = getBinding(this.db, this.capabilityValue, request.target_selector);
52709
+ if (!ownedBinding || ownedBinding.normalized_call_digest !== callDigest) {
52710
+ return this.terminal(request, callDigest, "target_selector_preexists", {
52711
+ target_id: ownedBinding?.target_id
52712
+ });
52713
+ }
52714
+ const acceptedAfterBinding = getAcceptedReceipt(this.db, request);
52715
+ if (acceptedAfterBinding) {
52716
+ return this.duplicateForwardOrTerminal(request, callDigest, path, acceptedAfterBinding);
52717
+ }
52718
+ if (!insertedBinding || ownedBinding.state !== "pending") {
52719
+ return this.terminal(request, callDigest, "target_selector_preexists", {
52720
+ target_id: ownedBinding.target_id
52721
+ });
52722
+ }
52723
+ const id = targetId(this.capabilityValue, request.target_selector);
52724
+ this.writeBoundary("before_object_write", request);
52725
+ try {
52726
+ this.db.run(`
52727
+ INSERT INTO projects (
52728
+ id, name, path, description, memory_prefix, created_at, updated_at
52729
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
52730
+ `, id, request.project_name, path, null, request.project_slug, now2, now2);
52731
+ } catch (cause) {
52732
+ throw new WriteBoundaryError("before_object_write", cause);
52733
+ }
52734
+ this.writeBoundary("after_object_write", request);
52735
+ const project = getProjectByExactId3(this.db, id);
52736
+ if (!project || project.path !== path) {
52737
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "project write did not read back by its full id");
52738
+ }
52739
+ const record = projectRecord(this.db, project);
52740
+ const acceptedReceipt = makeReceipt2(request, this.capabilityValue, callDigest, now2, {
52741
+ outcome: "accepted",
52742
+ target_id: record.target_id,
52743
+ result_revision: record.revision,
52744
+ result_digest: record.digest,
52745
+ created_by_operation: true
52746
+ });
52747
+ this.writeBoundary("before_receipt_write", request);
52748
+ let inserted;
52749
+ try {
52750
+ inserted = insertReceipt2(this.db, acceptedReceipt);
52751
+ } catch (cause) {
52752
+ throw new WriteBoundaryError("before_receipt_write", cause);
52753
+ }
52754
+ this.writeBoundary("after_receipt_write", request);
52755
+ setBindingAccepted(this.db, request, inserted, now2);
52756
+ return inserted;
52757
+ });
52758
+ } catch (error) {
52759
+ if (!(error instanceof WriteBoundaryError))
52760
+ throw error;
52761
+ stored = this.db.transaction(() => this.terminal(request, callDigest, `write_failed:${error.point}`));
52762
+ }
52763
+ this.fault("after_commit", request);
52764
+ const receipt = publicReceipt(stored);
52765
+ assertWithinBounds(receipt, request, startedAt);
52766
+ return receipt;
52767
+ }
52768
+ async readExact(request) {
52769
+ const startedAt = Date.now();
52770
+ assertBounds(request);
52771
+ if (request.resource_kind !== "project") {
52772
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project");
52773
+ }
52774
+ requireString(request.target_id, "target_id", {
52775
+ min: 51,
52776
+ max: 51,
52777
+ pattern: PROJECT_ID_PATTERN
52778
+ });
52779
+ const path = ownedPath(request.target);
52780
+ const project = getProjectByExactId3(this.db, request.target_id);
52781
+ if (!project || project.path !== path) {
52782
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND", "exact full-id project readback did not resolve the owned path");
52783
+ }
52784
+ const record = projectRecord(this.db, project);
52785
+ assertWithinBounds(record, request, startedAt);
52786
+ return record;
52787
+ }
52788
+ async lookupReceipt(request) {
52789
+ const startedAt = Date.now();
52790
+ assertLookup(request, this.capabilityValue);
52791
+ const receipt = getReceiptForLookup(this.db, request);
52792
+ if (!receipt || request.target_id !== undefined && receipt.target_id !== request.target_id) {
52793
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "exact immutable terminal receipt was not found");
52794
+ }
52795
+ return withResponseControl(publicReceipt(receipt), request, startedAt);
52796
+ }
52797
+ async compensate(request) {
52798
+ const startedAt = Date.now();
52799
+ const acceptedPublic = assertInverseRequest(request, this.capabilityValue);
52800
+ const callDigest = normalizedCallDigest(request);
52801
+ const storedAccepted = getStoredReceipt(this.db, acceptedPublic.receipt_id);
52802
+ if (!storedAccepted || !sameReceipt(storedAccepted, acceptedPublic)) {
52803
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted forward receipt is not stored immutably in this authority");
52804
+ }
52805
+ const path = ownedPath(request.target);
52806
+ let stored;
52807
+ try {
52808
+ stored = this.db.transaction(() => {
52809
+ const exact = getReceiptForLookup(this.db, {
52810
+ ...request,
52811
+ authority: "mementos",
52812
+ max_items: 1
52813
+ });
52814
+ if (exact?.outcome === "terminal_nonacceptance")
52815
+ return exact;
52816
+ if (!lockBinding(this.db, this.capabilityValue, storedAccepted.target_selector)) {
52817
+ return this.terminal(request, callDigest, "accepted_binding_missing", {
52818
+ target_id: storedAccepted.target_id,
52819
+ accepted_receipt_id: storedAccepted.receipt_id
52820
+ });
52821
+ }
52822
+ const binding = getBinding(this.db, this.capabilityValue, storedAccepted.target_selector);
52823
+ if (!binding || binding.accepted_receipt_id !== storedAccepted.receipt_id) {
52824
+ return this.terminal(request, callDigest, "accepted_binding_replaced", {
52825
+ target_id: storedAccepted.target_id,
52826
+ accepted_receipt_id: storedAccepted.receipt_id
52827
+ });
52828
+ }
52829
+ const prior = getAcceptedReceipt(this.db, request);
52830
+ if (prior) {
52831
+ if (prior.normalized_call_digest !== callDigest || prior.accepted_receipt_id !== storedAccepted.receipt_id) {
52832
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "inverse operation/step identity is bound to a different request");
52833
+ }
52834
+ return insertReceipt2(this.db, makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
52835
+ outcome: "duplicate_of_accepted",
52836
+ target_id: prior.target_id,
52837
+ result_revision: prior.result_revision,
52838
+ result_digest: prior.result_digest,
52839
+ duplicate_of_receipt_id: prior.receipt_id,
52840
+ accepted_receipt_id: storedAccepted.receipt_id
52841
+ }));
52842
+ }
52843
+ if (binding.state !== "accepted") {
52844
+ return this.terminal(request, callDigest, "accepted_binding_not_active", {
52845
+ target_id: storedAccepted.target_id,
52846
+ accepted_receipt_id: storedAccepted.receipt_id
52847
+ });
52848
+ }
52849
+ const project = getProjectByExactId3(this.db, storedAccepted.target_id);
52850
+ if (!project) {
52851
+ return this.terminal(request, callDigest, "target_missing", {
52852
+ target_id: storedAccepted.target_id,
52853
+ accepted_receipt_id: storedAccepted.receipt_id
52854
+ });
52855
+ }
52856
+ if (project.path !== path) {
52857
+ return this.terminal(request, callDigest, "target_drifted", {
52858
+ target_id: storedAccepted.target_id,
52859
+ accepted_receipt_id: storedAccepted.receipt_id
52860
+ });
52861
+ }
52862
+ const references = mementosProjectReferenceCounts(this.db, project.id);
52863
+ if (hasMementosProjectReferences(references)) {
52864
+ return this.terminal(request, callDigest, "target_has_dependents", {
52865
+ target_id: storedAccepted.target_id,
52866
+ accepted_receipt_id: storedAccepted.receipt_id
52867
+ });
52868
+ }
52869
+ const current = projectRecord(this.db, project, references);
52870
+ if (current.revision !== storedAccepted.result_revision || current.digest !== storedAccepted.result_digest) {
52871
+ return this.terminal(request, callDigest, "target_drifted", {
52872
+ target_id: storedAccepted.target_id,
52873
+ accepted_receipt_id: storedAccepted.receipt_id
52874
+ });
52875
+ }
52876
+ this.writeBoundary("before_object_write", request);
52877
+ let deleted;
52878
+ try {
52879
+ deleted = deleteMementosProjectIfUnreferenced(this.db, storedAccepted.target_id);
52880
+ } catch (cause) {
52881
+ throw new WriteBoundaryError("before_object_write", cause);
52882
+ }
52883
+ if (deleted !== 1) {
52884
+ const remaining = getProjectByExactId3(this.db, storedAccepted.target_id);
52885
+ if (remaining && hasMementosProjectReferences(mementosProjectReferenceCounts(this.db, storedAccepted.target_id))) {
52886
+ return this.terminal(request, callDigest, "target_has_dependents", {
52887
+ target_id: storedAccepted.target_id,
52888
+ accepted_receipt_id: storedAccepted.receipt_id
52889
+ });
52890
+ }
52891
+ throw new WriteBoundaryError("before_object_write", new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "conditional inverse did not delete exactly one unreferenced project"));
52892
+ }
52893
+ this.writeBoundary("after_object_write", request);
52894
+ const inverseRecord = {
52895
+ target_id: storedAccepted.target_id,
52896
+ revision: "absent",
52897
+ digest: digestMementosProjectRegistrationValue({
52898
+ target_id: storedAccepted.target_id,
52899
+ accepted_receipt_id: storedAccepted.receipt_id,
52900
+ absent: true
52901
+ })
52902
+ };
52903
+ const inverseReceipt = makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
52904
+ outcome: "accepted",
52905
+ target_id: inverseRecord.target_id,
52906
+ result_revision: inverseRecord.revision,
52907
+ result_digest: inverseRecord.digest,
52908
+ accepted_receipt_id: storedAccepted.receipt_id,
52909
+ created_by_operation: false
52910
+ });
52911
+ this.writeBoundary("before_receipt_write", request);
52912
+ let inserted;
52913
+ try {
52914
+ inserted = insertReceipt2(this.db, inverseReceipt);
52915
+ } catch (cause) {
52916
+ throw new WriteBoundaryError("before_receipt_write", cause);
52917
+ }
52918
+ this.writeBoundary("after_receipt_write", request);
52919
+ setBindingRemoved(this.db, storedAccepted, inserted.receipt_id, this.now());
52920
+ return inserted;
52921
+ });
52922
+ } catch (error) {
52923
+ if (!(error instanceof WriteBoundaryError))
52924
+ throw error;
52925
+ stored = this.db.transaction(() => this.terminal(request, callDigest, `write_failed:${error.point}`, {
52926
+ target_id: storedAccepted.target_id,
52927
+ accepted_receipt_id: storedAccepted.receipt_id
52928
+ }));
52929
+ }
52930
+ this.fault("after_commit", request);
52931
+ const receipt = publicReceipt(stored);
52932
+ assertWithinBounds(receipt, request, startedAt);
52933
+ return receipt;
52934
+ }
52935
+ async verifyInverse(request) {
52936
+ const startedAt = Date.now();
52937
+ const accepted = assertInverseRequest(request, this.capabilityValue);
52938
+ const storedAccepted = getStoredReceipt(this.db, accepted.receipt_id);
52939
+ if (!storedAccepted || !sameReceipt(storedAccepted, accepted)) {
52940
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted forward receipt is not stored immutably in this authority");
52941
+ }
52942
+ const inverse = getAcceptedReceipt(this.db, request);
52943
+ if (!inverse || inverse.outcome !== "accepted" || inverse.accepted_receipt_id !== accepted.receipt_id || inverse.result_revision !== "absent" || !inverse.result_digest) {
52944
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "accepted conditional inverse receipt was not found");
52945
+ }
52946
+ if (getProjectByExactId3(this.db, accepted.target_id)) {
52947
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification found the accepted target still present");
52948
+ }
52949
+ const verification = {
52950
+ target_id: accepted.target_id,
52951
+ accepted_receipt_id: accepted.receipt_id,
52952
+ absent: true,
52953
+ digest: digestMementosProjectRegistrationValue({
52954
+ target_id: accepted.target_id,
52955
+ accepted_receipt_id: accepted.receipt_id,
52956
+ absent: true
52957
+ })
52958
+ };
52959
+ assertWithinBounds(verification, request, startedAt);
52960
+ if (verification.digest !== inverse.result_digest) {
52961
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification digest does not match its immutable receipt");
52962
+ }
52963
+ return verification;
52964
+ }
52965
+ }
52966
+ function createMementosProjectRegistrationAuthority(db, options = {}) {
52967
+ return new PackageOwnedMementosProjectRegistrationAuthority(db, options);
52968
+ }
52969
+ var createLocalMementosProjectRegistrationAuthority = createMementosProjectRegistrationAuthority;
52970
+ // src/project-registration/http.ts
52971
+ var JSON_HEADERS = { "Content-Type": "application/json" };
52972
+
52973
+ class WirePathHandle {
52974
+ absolutePath;
52975
+ constructor(absolutePath) {
52976
+ this.absolutePath = absolutePath;
52977
+ }
52978
+ withOwnedPath(consumer) {
52979
+ return consumer(this.absolutePath);
52980
+ }
52981
+ }
52982
+ function json(body, status = 200) {
52983
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
52984
+ }
52985
+ function errorStatus(error) {
52986
+ switch (error.code) {
52987
+ case "MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT":
52988
+ case "MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS":
52989
+ case "MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH":
52990
+ case "MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH":
52991
+ case "MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH":
52992
+ case "MEMENTOS_PROJECT_REGISTRATION_EXACT_ID_REQUIRED":
52993
+ return 400;
52994
+ case "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND":
52995
+ case "MEMENTOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND":
52996
+ case "MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND":
52997
+ return 404;
52998
+ case "MEMENTOS_PROJECT_REGISTRATION_RESPONSE_TOO_LARGE":
52999
+ return 413;
53000
+ case "MEMENTOS_PROJECT_REGISTRATION_TIME_BUDGET_EXCEEDED":
53001
+ return 408;
53002
+ case "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE":
53003
+ return 503;
53004
+ default:
53005
+ return 409;
53006
+ }
53007
+ }
53008
+ async function readJson(request) {
53009
+ try {
53010
+ const value = await request.json();
53011
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
53012
+ } catch {
53013
+ return null;
53014
+ }
53015
+ }
53016
+ function fromWireRequest(body) {
53017
+ const { canonical_path: canonicalPath, ...request } = body;
53018
+ if (typeof canonicalPath !== "string") {
53019
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "canonical_path is required on the private registration transport");
53020
+ }
53021
+ return {
53022
+ ...request,
53023
+ target: new WirePathHandle(canonicalPath)
53024
+ };
53025
+ }
53026
+ function fromWireReadRequest(body) {
53027
+ const { canonical_path: canonicalPath, ...request } = body;
53028
+ if (typeof canonicalPath !== "string") {
53029
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "canonical_path is required on the private exact-read transport");
53030
+ }
53031
+ return {
53032
+ ...request,
53033
+ target: new WirePathHandle(canonicalPath)
53034
+ };
53035
+ }
53036
+ async function handleMementosProjectRegistrationHttpRequest(request, url, authority, basePath = "/v1/project-registration") {
53037
+ const path = url.pathname;
53038
+ if (path !== basePath && !path.startsWith(`${basePath}/`))
53039
+ return null;
53040
+ const action = path.slice(basePath.length).split("/").filter(Boolean).join("/");
53041
+ const method = request.method.toUpperCase();
53042
+ try {
53043
+ if ((action === "" || action === "capability") && method === "GET") {
53044
+ return json({ capability: await authority.capability() });
53045
+ }
53046
+ if (method !== "POST")
53047
+ return json({ error: "method not allowed" }, 405);
53048
+ const body = await readJson(request);
53049
+ if (!body) {
53050
+ return json({
53051
+ error: "invalid JSON body",
53052
+ code: "MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT"
53053
+ }, 400);
53054
+ }
53055
+ if (action === "create") {
53056
+ return json({ receipt: await authority.create(fromWireRequest(body)) }, 201);
53057
+ }
53058
+ if (action === "receipts/lookup") {
53059
+ return json(await authority.lookupReceipt(body));
53060
+ }
53061
+ if (action === "read-exact") {
53062
+ return json({ record: await authority.readExact(fromWireReadRequest(body)) });
53063
+ }
53064
+ if (action === "compensate") {
53065
+ return json({ receipt: await authority.compensate(fromWireRequest(body)) }, 201);
53066
+ }
53067
+ if (action === "verify-inverse") {
53068
+ return json({ verification: await authority.verifyInverse(fromWireRequest(body)) });
53069
+ }
53070
+ return json({
53071
+ error: "unknown Mementos project-registration route",
53072
+ code: "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND"
53073
+ }, 404);
53074
+ } catch (cause) {
53075
+ if (cause instanceof MementosProjectRegistrationError) {
53076
+ return json({
53077
+ error: cause.message,
53078
+ code: cause.code,
53079
+ details: cause.details,
53080
+ authoritative: true
53081
+ }, errorStatus(cause));
53082
+ }
53083
+ return json({
53084
+ error: cause instanceof Error ? cause.message : "internal registration error",
53085
+ code: "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE"
53086
+ }, 500);
53087
+ }
53088
+ }
53089
+ function extractPath(target) {
53090
+ if (!target || typeof target.withOwnedPath !== "function") {
53091
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target must be a package-owned path handle");
53092
+ }
53093
+ return target.withOwnedPath((absolutePath) => absolutePath);
53094
+ }
53095
+ function toWireRequest(request) {
53096
+ const { target, ...serializable } = request;
53097
+ return {
53098
+ ...serializable,
53099
+ canonical_path: extractPath(target)
53100
+ };
53101
+ }
53102
+
53103
+ class MementosProjectRegistrationHttpClient {
53104
+ authority = "mementos";
53105
+ baseUrl;
53106
+ fetchImpl;
53107
+ headers;
53108
+ constructor(options) {
53109
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
53110
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
53111
+ this.headers = {
53112
+ ...options.headers,
53113
+ ...options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {},
53114
+ "Content-Type": "application/json"
53115
+ };
53116
+ }
53117
+ async request(action, init = {}) {
53118
+ const response = await this.fetchImpl(`${this.baseUrl}/v1/project-registration${action}`, {
53119
+ ...init,
53120
+ headers: { ...this.headers, ...init.headers ?? {} }
53121
+ });
53122
+ let body;
53123
+ try {
53124
+ body = await response.json();
53125
+ } catch {
53126
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", `Mementos project registration HTTP ${response.status} returned non-JSON`);
53127
+ }
53128
+ if (!response.ok) {
53129
+ throw new MementosProjectRegistrationError(typeof body["code"] === "string" ? body["code"] : "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", typeof body["error"] === "string" ? body["error"] : `Mementos project registration HTTP ${response.status}`, body["details"] && typeof body["details"] === "object" ? body["details"] : {});
53130
+ }
53131
+ return body;
53132
+ }
53133
+ async capability() {
53134
+ const body = await this.request("/capability");
53135
+ return body.capability;
53136
+ }
53137
+ async create(request) {
53138
+ const body = await this.request("/create", { method: "POST", body: JSON.stringify(toWireRequest(request)) });
53139
+ return body.receipt;
53140
+ }
53141
+ async readExact(request) {
53142
+ const { target, ...serializable } = request;
53143
+ const body = await this.request("/read-exact", {
53144
+ method: "POST",
53145
+ body: JSON.stringify({ ...serializable, canonical_path: extractPath(target) })
53146
+ });
53147
+ return body.record;
53148
+ }
53149
+ async lookupReceipt(request) {
53150
+ return this.request("/receipts/lookup", { method: "POST", body: JSON.stringify(request) });
53151
+ }
53152
+ async compensate(request) {
53153
+ const body = await this.request("/compensate", { method: "POST", body: JSON.stringify(toWireRequest(request)) });
53154
+ return body.receipt;
53155
+ }
53156
+ async verifyInverse(request) {
53157
+ const body = await this.request("/verify-inverse", {
53158
+ method: "POST",
53159
+ body: JSON.stringify(toWireRequest(request))
53160
+ });
53161
+ return body.verification;
53162
+ }
53163
+ }
53164
+ function createMementosProjectRegistrationHttpClient(options) {
53165
+ return new MementosProjectRegistrationHttpClient(options);
50466
53166
  }
50467
53167
  // src/db/machines.ts
50468
53168
  init_database();
@@ -50757,17 +53457,17 @@ function deleteEntity(id, db) {
50757
53457
  if (result.changes === 0)
50758
53458
  throw new EntityNotFoundError(id);
50759
53459
  }
50760
- function mergeEntities(sourceId, targetId, db) {
53460
+ function mergeEntities(sourceId, targetId2, db) {
50761
53461
  if (!db && isApiMode()) {
50762
53462
  const { data } = apiJson("POST", "/entities/merge", {
50763
53463
  source_id: sourceId,
50764
- target_id: targetId
53464
+ target_id: targetId2
50765
53465
  });
50766
53466
  return data;
50767
53467
  }
50768
53468
  const d = db || getDatabase();
50769
53469
  const src = getEntity(sourceId, d).id;
50770
- const tgt = getEntity(targetId, d).id;
53470
+ const tgt = getEntity(targetId2, d).id;
50771
53471
  d.run(`DELETE FROM relations
50772
53472
  WHERE source_entity_id = ?
50773
53473
  AND EXISTS (
@@ -51591,9 +54291,9 @@ function formatMementosProjectPanel(panel) {
51591
54291
  `);
51592
54292
  }
51593
54293
  // src/lib/config.ts
51594
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, cpSync as cpSync2 } from "fs";
54294
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, cpSync as cpSync2 } from "fs";
51595
54295
  import { homedir as homedir2 } from "os";
51596
- import { basename, dirname as dirname2, join as join4, resolve as resolve2 } from "path";
54296
+ import { basename, dirname as dirname3, join as join5, resolve as resolve3 } from "path";
51597
54297
  function isInMemoryDb2(path) {
51598
54298
  return path === ":memory:" || path.startsWith("file::memory:");
51599
54299
  }
@@ -51656,11 +54356,11 @@ function isValidCategory(value) {
51656
54356
  return VALID_CATEGORIES.includes(value);
51657
54357
  }
51658
54358
  function loadConfig() {
51659
- const configPath = join4(homeDir(), ".hasna", "mementos", "config.json");
54359
+ const configPath = join5(homeDir(), ".hasna", "mementos", "config.json");
51660
54360
  let fileConfig = {};
51661
54361
  if (existsSync3(configPath)) {
51662
54362
  try {
51663
- const raw = readFileSync2(configPath, "utf-8");
54363
+ const raw = readFileSync3(configPath, "utf-8");
51664
54364
  fileConfig = JSON.parse(raw);
51665
54365
  } catch {}
51666
54366
  }
@@ -51684,13 +54384,13 @@ function loadConfig() {
51684
54384
  }
51685
54385
  function findFileWalkingUp(filename) {
51686
54386
  let dir = process.cwd();
51687
- const legacyHomeMementosDb = resolve2(homeDir(), ".mementos", "mementos.db");
54387
+ const legacyHomeMementosDb = resolve3(homeDir(), ".mementos", "mementos.db");
51688
54388
  while (true) {
51689
- const candidate = join4(dir, filename);
51690
- if (existsSync3(candidate) && resolve2(candidate) !== legacyHomeMementosDb) {
54389
+ const candidate = join5(dir, filename);
54390
+ if (existsSync3(candidate) && resolve3(candidate) !== legacyHomeMementosDb) {
51691
54391
  return candidate;
51692
54392
  }
51693
- const parent = dirname2(dir);
54393
+ const parent = dirname3(dir);
51694
54394
  if (parent === dir) {
51695
54395
  return null;
51696
54396
  }
@@ -51700,10 +54400,10 @@ function findFileWalkingUp(filename) {
51700
54400
  function findGitRoot2() {
51701
54401
  let dir = process.cwd();
51702
54402
  while (true) {
51703
- if (existsSync3(join4(dir, ".git"))) {
54403
+ if (existsSync3(join5(dir, ".git"))) {
51704
54404
  return dir;
51705
54405
  }
51706
- const parent = dirname2(dir);
54406
+ const parent = dirname3(dir);
51707
54407
  if (parent === dir) {
51708
54408
  return null;
51709
54409
  }
@@ -51711,17 +54411,17 @@ function findGitRoot2() {
51711
54411
  }
51712
54412
  }
51713
54413
  function profilesDir() {
51714
- return join4(homeDir(), ".hasna", "mementos", "profiles");
54414
+ return join5(homeDir(), ".hasna", "mementos", "profiles");
51715
54415
  }
51716
54416
  function globalConfigPath() {
51717
- return join4(homeDir(), ".hasna", "mementos", "config.json");
54417
+ return join5(homeDir(), ".hasna", "mementos", "config.json");
51718
54418
  }
51719
54419
  function readGlobalConfig() {
51720
54420
  const p = globalConfigPath();
51721
54421
  if (!existsSync3(p))
51722
54422
  return {};
51723
54423
  try {
51724
- return JSON.parse(readFileSync2(p, "utf-8"));
54424
+ return JSON.parse(readFileSync3(p, "utf-8"));
51725
54425
  } catch {
51726
54426
  return {};
51727
54427
  }
@@ -51731,7 +54431,7 @@ function readGlobalConfigForWrite() {
51731
54431
  if (!existsSync3(p))
51732
54432
  return {};
51733
54433
  try {
51734
- const data = JSON.parse(readFileSync2(p, "utf-8"));
54434
+ const data = JSON.parse(readFileSync3(p, "utf-8"));
51735
54435
  if (data === null || typeof data !== "object" || Array.isArray(data)) {
51736
54436
  throw new Error("expected a JSON object");
51737
54437
  }
@@ -51743,7 +54443,7 @@ function readGlobalConfigForWrite() {
51743
54443
  }
51744
54444
  function writeGlobalConfig(data) {
51745
54445
  const p = globalConfigPath();
51746
- ensureDir2(dirname2(p));
54446
+ ensureDir2(dirname3(p));
51747
54447
  writeFileSync3(p, JSON.stringify(data, null, 2), "utf-8");
51748
54448
  }
51749
54449
  function getActiveProfile() {
@@ -51769,7 +54469,7 @@ function listProfiles() {
51769
54469
  return readdirSync(dir).filter((f) => f.endsWith(".db")).map((f) => basename(f, ".db")).sort();
51770
54470
  }
51771
54471
  function deleteProfile(name) {
51772
- const dbPath = join4(profilesDir(), `${name}.db`);
54472
+ const dbPath = join5(profilesDir(), `${name}.db`);
51773
54473
  if (!existsSync3(dbPath))
51774
54474
  return false;
51775
54475
  unlinkSync2(dbPath);
@@ -51779,10 +54479,10 @@ function deleteProfile(name) {
51779
54479
  }
51780
54480
  function getDbPath2() {
51781
54481
  const _home = homeDir();
51782
- const _newDir = join4(_home, ".hasna", "mementos");
51783
- const _oldDir = join4(_home, ".mementos");
54482
+ const _newDir = join5(_home, ".hasna", "mementos");
54483
+ const _oldDir = join5(_home, ".mementos");
51784
54484
  if (!existsSync3(_newDir) && existsSync3(_oldDir)) {
51785
- mkdirSync3(join4(_home, ".hasna"), { recursive: true });
54485
+ mkdirSync3(join5(_home, ".hasna"), { recursive: true });
51786
54486
  cpSync2(_oldDir, _newDir, { recursive: true });
51787
54487
  }
51788
54488
  const envDbPath = process.env["HASNA_MEMENTOS_DB_PATH"] ?? process.env["MEMENTOS_DB_PATH"];
@@ -51790,31 +54490,31 @@ function getDbPath2() {
51790
54490
  if (isInMemoryDb2(envDbPath)) {
51791
54491
  return envDbPath;
51792
54492
  }
51793
- const resolved = resolve2(envDbPath);
51794
- ensureDir2(dirname2(resolved));
54493
+ const resolved = resolve3(envDbPath);
54494
+ ensureDir2(dirname3(resolved));
51795
54495
  return resolved;
51796
54496
  }
51797
54497
  const profile = getActiveProfile();
51798
54498
  if (profile) {
51799
- const profilePath = join4(profilesDir(), `${profile}.db`);
51800
- ensureDir2(dirname2(profilePath));
54499
+ const profilePath = join5(profilesDir(), `${profile}.db`);
54500
+ ensureDir2(dirname3(profilePath));
51801
54501
  return profilePath;
51802
54502
  }
51803
54503
  const dbScope = process.env["MEMENTOS_DB_SCOPE"];
51804
54504
  if (dbScope === "project") {
51805
54505
  const gitRoot = findGitRoot2();
51806
54506
  if (gitRoot) {
51807
- const dbPath = join4(gitRoot, ".mementos", "mementos.db");
51808
- ensureDir2(dirname2(dbPath));
54507
+ const dbPath = join5(gitRoot, ".mementos", "mementos.db");
54508
+ ensureDir2(dirname3(dbPath));
51809
54509
  return dbPath;
51810
54510
  }
51811
54511
  }
51812
- const found = findFileWalkingUp(join4(".mementos", "mementos.db"));
54512
+ const found = findFileWalkingUp(join5(".mementos", "mementos.db"));
51813
54513
  if (found) {
51814
54514
  return found;
51815
54515
  }
51816
- const fallback = join4(homeDir(), ".hasna", "mementos", "mementos.db");
51817
- ensureDir2(dirname2(fallback));
54516
+ const fallback = join5(homeDir(), ".hasna", "mementos", "mementos.db");
54517
+ ensureDir2(dirname3(fallback));
51818
54518
  return fallback;
51819
54519
  }
51820
54520
  function ensureDir2(dir) {
@@ -52199,18 +54899,18 @@ function runCleanup(config, db) {
52199
54899
  return { expired, evicted, archived, unused_archived, deprioritized };
52200
54900
  }
52201
54901
  // src/lib/sync.ts
52202
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
54902
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
52203
54903
  import { homedir as homedir3 } from "os";
52204
- import { join as join5 } from "path";
54904
+ import { join as join6 } from "path";
52205
54905
  function getAgentSyncDir(agentName) {
52206
- const dir = join5(homedir3(), ".hasna", "mementos", "agents", agentName);
54906
+ const dir = join6(homedir3(), ".hasna", "mementos", "agents", agentName);
52207
54907
  if (!existsSync4(dir)) {
52208
54908
  mkdirSync4(dir, { recursive: true });
52209
54909
  }
52210
54910
  return dir;
52211
54911
  }
52212
54912
  function setHighWaterMark(agentDir, timestamp) {
52213
- const markFile = join5(agentDir, ".highwatermark");
54913
+ const markFile = join6(agentDir, ".highwatermark");
52214
54914
  writeFileSync4(markFile, timestamp, "utf-8");
52215
54915
  }
52216
54916
  function resolveConflict(local, remote, resolution) {
@@ -52231,7 +54931,7 @@ function pushMemories(agentName, agentId, projectId, db) {
52231
54931
  status: "active",
52232
54932
  limit: 1e4
52233
54933
  }, db);
52234
- const outFile = join5(agentDir, "memories.json");
54934
+ const outFile = join6(agentDir, "memories.json");
52235
54935
  writeFileSync4(outFile, JSON.stringify(memories, null, 2), "utf-8");
52236
54936
  if (memories.length > 0) {
52237
54937
  const latest = memories.reduce((a, b) => new Date(a.updated_at).getTime() > new Date(b.updated_at).getTime() ? a : b);
@@ -52241,11 +54941,11 @@ function pushMemories(agentName, agentId, projectId, db) {
52241
54941
  }
52242
54942
  function pullMemories(agentName, conflictResolution = "prefer-newer", db) {
52243
54943
  const agentDir = getAgentSyncDir(agentName);
52244
- const inFile = join5(agentDir, "memories.json");
54944
+ const inFile = join6(agentDir, "memories.json");
52245
54945
  if (!existsSync4(inFile)) {
52246
54946
  return { pulled: 0, conflicts: 0 };
52247
54947
  }
52248
- const raw = readFileSync3(inFile, "utf-8");
54948
+ const raw = readFileSync4(inFile, "utf-8");
52249
54949
  let remoteMemories;
52250
54950
  try {
52251
54951
  remoteMemories = JSON.parse(raw);
@@ -54802,17 +57502,17 @@ var gatherTrainingData = async (options = {}) => {
54802
57502
  };
54803
57503
  };
54804
57504
  // src/lib/model-config.ts
54805
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
57505
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
54806
57506
  import { homedir as homedir4 } from "os";
54807
- import { join as join6 } from "path";
57507
+ import { join as join7 } from "path";
54808
57508
  var DEFAULT_MODEL = "gpt-4o-mini";
54809
- var CONFIG_DIR = join6(homedir4(), ".hasna", "mementos");
54810
- var CONFIG_PATH = join6(CONFIG_DIR, "config.json");
57509
+ var CONFIG_DIR = join7(homedir4(), ".hasna", "mementos");
57510
+ var CONFIG_PATH = join7(CONFIG_DIR, "config.json");
54811
57511
  function readConfig() {
54812
57512
  if (!existsSync5(CONFIG_PATH))
54813
57513
  return {};
54814
57514
  try {
54815
- const raw = readFileSync4(CONFIG_PATH, "utf-8");
57515
+ const raw = readFileSync5(CONFIG_PATH, "utf-8");
54816
57516
  return JSON.parse(raw);
54817
57517
  } catch {
54818
57518
  return {};
@@ -55266,6 +57966,9 @@ export {
55266
57966
  touchAgent,
55267
57967
  syncMemories,
55268
57968
  startTaskRunner,
57969
+ sqliteMementosProjectRegistrationSchemaSql,
57970
+ sqliteMementosProjectGuardedUpdateSchemaSql,
57971
+ sqliteMementosMemoryProjectLinkSchemaSql,
55269
57972
  shortUuid,
55270
57973
  setPrimaryMachine,
55271
57974
  setFocus,
@@ -55276,6 +57979,8 @@ export {
55276
57979
  saveStorageConfig,
55277
57980
  runConsolidation,
55278
57981
  runCleanup,
57982
+ rollbackProjectUpdate,
57983
+ rollbackMemoryProjectLink,
55279
57984
  resolveProjectId,
55280
57985
  resolvePartialId,
55281
57986
  resetDatabase,
@@ -55294,11 +57999,17 @@ export {
55294
57999
  pullStorageChanges,
55295
58000
  providerRegistry,
55296
58001
  processConversationTurn,
58002
+ previewProjectUpdate,
58003
+ previewMemoryProjectLink,
58004
+ postgresMementosProjectRegistrationSchemaSql,
58005
+ postgresMementosProjectGuardedUpdateSchemaSql,
58006
+ postgresMementosMemoryProjectLinkSchemaSql,
55297
58007
  parseRelationRow,
55298
58008
  parseEntityRow2 as parseEntityRow,
55299
58009
  now,
55300
58010
  mergeEntities,
55301
58011
  memoryLockId,
58012
+ mementosProjectReferenceCounts,
55302
58013
  loadConfig,
55303
58014
  listTasks,
55304
58015
  listTaskComments,
@@ -55315,6 +58026,8 @@ export {
55315
58026
  linkEntityToMemory,
55316
58027
  incrementRecallCount,
55317
58028
  heuristicReflectionCritic,
58029
+ hasMementosProjectReferences,
58030
+ handleMementosProjectRegistrationHttpRequest,
55318
58031
  getTaskStats,
55319
58032
  getTaskRunnerStats,
55320
58033
  getTask,
@@ -55328,11 +58041,13 @@ export {
55328
58041
  getStorageConfig,
55329
58042
  getRelation,
55330
58043
  getRelatedEntities,
58044
+ getProjectUpdateReceipt,
55331
58045
  getProject,
55332
58046
  getPrimaryMachineStartupWarning,
55333
58047
  getPrimaryMachineCandidate,
55334
58048
  getPrimaryMachine,
55335
58049
  getMemoryVersions,
58050
+ getMemoryProjectLinkReceipt,
55336
58051
  getMemoryLinks,
55337
58052
  getMemoryByKey,
55338
58053
  getMemoryBacklinks,
@@ -55361,6 +58076,8 @@ export {
55361
58076
  focusFilterSQL,
55362
58077
  findPath,
55363
58078
  enforceQuotas,
58079
+ digestMementosProjectRegistrationValue,
58080
+ deriveMementosProjectRegistrationIdempotencyKey,
55364
58081
  deprioritizeStale,
55365
58082
  deleteTaskComment,
55366
58083
  deleteTask,
@@ -55375,7 +58092,10 @@ export {
55375
58092
  createRelation,
55376
58093
  createMemoryLink,
55377
58094
  createMemory,
58095
+ createMementosProjectRegistrationHttpClient,
58096
+ createMementosProjectRegistrationAuthority,
55378
58097
  createMementosProjectPanel,
58098
+ createLocalMementosProjectRegistrationAuthority,
55379
58099
  createEntity,
55380
58100
  createAISDKReflectionCritic,
55381
58101
  containsSecrets,
@@ -55386,11 +58106,14 @@ export {
55386
58106
  cleanExpiredLocks,
55387
58107
  checkMemoryWriteLock,
55388
58108
  checkLock,
58109
+ canonicalMementosProjectRegistrationJson,
55389
58110
  bulkLinkEntities,
55390
58111
  bulkDeleteMemories,
55391
58112
  buildFocusFilter,
55392
58113
  archiveUnused,
55393
58114
  archiveStale,
58115
+ applyProjectUpdate,
58116
+ applyMemoryProjectLink,
55394
58117
  agentHoldsLock,
55395
58118
  addTaskComment,
55396
58119
  acquireMemoryWriteLock,
@@ -55398,15 +58121,28 @@ export {
55398
58121
  VersionConflictError,
55399
58122
  SqliteAdapter,
55400
58123
  STORAGE_TABLES,
58124
+ ProjectGuardedUpdateError,
58125
+ ProjectCollisionError,
55401
58126
  PgAdapterAsync,
55402
58127
  PgAdapter,
58128
+ PackageOwnedMementosProjectRegistrationAuthority,
58129
+ MemoryProjectLinkError,
55403
58130
  MemoryNotFoundError,
55404
58131
  MemoryLockConflictError,
55405
58132
  MemoryInjector,
55406
58133
  MemoryExpiredError,
58134
+ MementosProjectRegistrationHttpClient,
58135
+ MementosProjectRegistrationError,
58136
+ MEMORY_PROJECT_LINK_RECEIPT_COLUMNS,
55407
58137
  MEMENTOS_STORAGE_TABLES,
55408
58138
  MEMENTOS_STORAGE_FALLBACK_ENV,
55409
58139
  MEMENTOS_STORAGE_ENV,
58140
+ MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION,
58141
+ MEMENTOS_PROJECT_REGISTRATION_ROUTE,
58142
+ MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE,
58143
+ MEMENTOS_PROJECT_REFERENCE_SURFACES,
58144
+ MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
58145
+ MEMENTOS_MEMORY_PROJECT_LINK_ROUTE,
55410
58146
  InvalidScopeError,
55411
58147
  EntityNotFoundError,
55412
58148
  DuplicateMemoryError,