@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.
- package/bun.lock +93 -0
- package/dist/cli/commands/agent.d.ts.map +1 -1
- package/dist/cli/commands/info-context.d.ts.map +1 -1
- package/dist/cli/commands/info-stale.d.ts.map +1 -1
- package/dist/cli/commands/io-export.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-list.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-recall.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-search.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-tail.d.ts.map +1 -1
- package/dist/cli/commands/project.d.ts.map +1 -1
- package/dist/cli/commands/system-watch.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +38 -0
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +2077 -299
- package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +1 -1
- package/dist/db/agents.d.ts +6 -0
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/audit.d.ts +14 -0
- package/dist/db/audit.d.ts.map +1 -1
- package/dist/db/memory-project-link.d.ts +13 -0
- package/dist/db/memory-project-link.d.ts.map +1 -0
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/pg-migrations.d.ts +0 -6
- package/dist/db/pg-migrations.d.ts.map +1 -1
- package/dist/db/projects.d.ts +22 -1
- package/dist/db/projects.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2909 -173
- package/dist/lib/package-version.d.ts +3 -0
- package/dist/lib/package-version.d.ts.map +1 -0
- package/dist/mcp/index.js +524 -10
- package/dist/memory-project-link/index.d.ts +2 -0
- package/dist/memory-project-link/index.d.ts.map +1 -0
- package/dist/memory-project-link/schema.d.ts +5 -0
- package/dist/memory-project-link/schema.d.ts.map +1 -0
- package/dist/project-registration/authority.d.ts +39 -0
- package/dist/project-registration/authority.d.ts.map +1 -0
- package/dist/project-registration/http.d.ts +29 -0
- package/dist/project-registration/http.d.ts.map +1 -0
- package/dist/project-registration/index.d.ts +8 -0
- package/dist/project-registration/index.d.ts.map +1 -0
- package/dist/project-registration/project-references.d.ts +71 -0
- package/dist/project-registration/project-references.d.ts.map +1 -0
- package/dist/project-registration/schema.d.ts +5 -0
- package/dist/project-registration/schema.d.ts.map +1 -0
- package/dist/project-registration/types.d.ts +168 -0
- package/dist/project-registration/types.d.ts.map +1 -0
- package/dist/project-registration.d.ts +2 -0
- package/dist/project-registration.d.ts.map +1 -0
- package/dist/project-registration.js +1496 -0
- package/dist/sdk/index.d.ts +127 -0
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +74 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2667 -149
- package/dist/server/routes/memories.d.ts +1 -0
- package/dist/server/routes/memories.d.ts.map +1 -1
- package/dist/server/routes/memory-project-link.d.ts +2 -0
- package/dist/server/routes/memory-project-link.d.ts.map +1 -0
- package/dist/server/routes/project-registration.d.ts +2 -0
- package/dist/server/routes/project-registration.d.ts.map +1 -0
- package/dist/types/index.d.ts +119 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +9 -3
- 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
|
|
2472
|
-
return
|
|
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
|
|
15665
|
-
|
|
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
|
-
|
|
16137
|
+
json2.minLength = minimum;
|
|
15669
16138
|
if (typeof maximum === "number")
|
|
15670
|
-
|
|
16139
|
+
json2.maxLength = maximum;
|
|
15671
16140
|
if (format) {
|
|
15672
|
-
|
|
15673
|
-
if (
|
|
15674
|
-
delete
|
|
16141
|
+
json2.format = formatMap[format] ?? format;
|
|
16142
|
+
if (json2.format === "")
|
|
16143
|
+
delete json2.format;
|
|
15675
16144
|
}
|
|
15676
16145
|
if (contentEncoding)
|
|
15677
|
-
|
|
16146
|
+
json2.contentEncoding = contentEncoding;
|
|
15678
16147
|
if (patterns && patterns.size > 0) {
|
|
15679
16148
|
const regexes = [...patterns];
|
|
15680
16149
|
if (regexes.length === 1)
|
|
15681
|
-
|
|
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
|
|
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
|
-
|
|
16166
|
+
json2.type = "integer";
|
|
15698
16167
|
else
|
|
15699
|
-
|
|
16168
|
+
json2.type = "number";
|
|
15700
16169
|
if (typeof exclusiveMinimum === "number")
|
|
15701
|
-
|
|
16170
|
+
json2.exclusiveMinimum = exclusiveMinimum;
|
|
15702
16171
|
if (typeof minimum === "number") {
|
|
15703
|
-
|
|
16172
|
+
json2.minimum = minimum;
|
|
15704
16173
|
if (typeof exclusiveMinimum === "number") {
|
|
15705
16174
|
if (exclusiveMinimum >= minimum)
|
|
15706
|
-
delete
|
|
16175
|
+
delete json2.minimum;
|
|
15707
16176
|
else
|
|
15708
|
-
delete
|
|
16177
|
+
delete json2.exclusiveMinimum;
|
|
15709
16178
|
}
|
|
15710
16179
|
}
|
|
15711
16180
|
if (typeof exclusiveMaximum === "number")
|
|
15712
|
-
|
|
16181
|
+
json2.exclusiveMaximum = exclusiveMaximum;
|
|
15713
16182
|
if (typeof maximum === "number") {
|
|
15714
|
-
|
|
16183
|
+
json2.maximum = maximum;
|
|
15715
16184
|
if (typeof exclusiveMaximum === "number") {
|
|
15716
16185
|
if (exclusiveMaximum <= maximum)
|
|
15717
|
-
delete
|
|
16186
|
+
delete json2.maximum;
|
|
15718
16187
|
else
|
|
15719
|
-
delete
|
|
16188
|
+
delete json2.exclusiveMaximum;
|
|
15720
16189
|
}
|
|
15721
16190
|
}
|
|
15722
16191
|
if (typeof multipleOf === "number")
|
|
15723
|
-
|
|
16192
|
+
json2.multipleOf = multipleOf;
|
|
15724
16193
|
break;
|
|
15725
16194
|
}
|
|
15726
16195
|
case "boolean": {
|
|
15727
|
-
const
|
|
15728
|
-
|
|
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
|
|
16245
|
+
const json2 = _json;
|
|
15777
16246
|
const { minimum, maximum } = schema._zod.bag;
|
|
15778
16247
|
if (typeof minimum === "number")
|
|
15779
|
-
|
|
16248
|
+
json2.minItems = minimum;
|
|
15780
16249
|
if (typeof maximum === "number")
|
|
15781
|
-
|
|
15782
|
-
|
|
15783
|
-
|
|
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
|
|
15788
|
-
|
|
15789
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16276
|
+
json2.required = Array.from(requiredKeys);
|
|
15808
16277
|
}
|
|
15809
16278
|
if (def.catchall?._zod.def.type === "never") {
|
|
15810
|
-
|
|
16279
|
+
json2.additionalProperties = false;
|
|
15811
16280
|
} else if (!def.catchall) {
|
|
15812
16281
|
if (this.io === "output")
|
|
15813
|
-
|
|
16282
|
+
json2.additionalProperties = false;
|
|
15814
16283
|
} else if (def.catchall) {
|
|
15815
|
-
|
|
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
|
|
15824
|
-
|
|
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
|
|
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
|
-
|
|
16314
|
+
json2.allOf = allOf;
|
|
15846
16315
|
break;
|
|
15847
16316
|
}
|
|
15848
16317
|
case "tuple": {
|
|
15849
|
-
const
|
|
15850
|
-
|
|
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
|
-
|
|
16322
|
+
json2.prefixItems = prefixItems;
|
|
15854
16323
|
} else {
|
|
15855
|
-
|
|
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
|
-
|
|
16332
|
+
json2.items = rest;
|
|
15864
16333
|
} else {
|
|
15865
|
-
|
|
16334
|
+
json2.additionalItems = rest;
|
|
15866
16335
|
}
|
|
15867
16336
|
}
|
|
15868
16337
|
if (def.rest) {
|
|
15869
|
-
|
|
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
|
-
|
|
16345
|
+
json2.minItems = minimum;
|
|
15877
16346
|
if (typeof maximum === "number")
|
|
15878
|
-
|
|
16347
|
+
json2.maxItems = maximum;
|
|
15879
16348
|
break;
|
|
15880
16349
|
}
|
|
15881
16350
|
case "record": {
|
|
15882
|
-
const
|
|
15883
|
-
|
|
15884
|
-
|
|
15885
|
-
|
|
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
|
|
16373
|
+
const json2 = _json;
|
|
15905
16374
|
const values = getEnumValues(def.entries);
|
|
15906
16375
|
if (values.every((v) => typeof v === "number"))
|
|
15907
|
-
|
|
16376
|
+
json2.type = "number";
|
|
15908
16377
|
if (values.every((v) => typeof v === "string"))
|
|
15909
|
-
|
|
15910
|
-
|
|
16378
|
+
json2.type = "string";
|
|
16379
|
+
json2.enum = values;
|
|
15911
16380
|
break;
|
|
15912
16381
|
}
|
|
15913
16382
|
case "literal": {
|
|
15914
|
-
const
|
|
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
|
-
|
|
15934
|
-
|
|
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
|
-
|
|
16406
|
+
json2.type = "number";
|
|
15938
16407
|
if (vals.every((v) => typeof v === "string"))
|
|
15939
|
-
|
|
16408
|
+
json2.type = "string";
|
|
15940
16409
|
if (vals.every((v) => typeof v === "boolean"))
|
|
15941
|
-
|
|
16410
|
+
json2.type = "string";
|
|
15942
16411
|
if (vals.every((v) => v === null))
|
|
15943
|
-
|
|
15944
|
-
|
|
16412
|
+
json2.type = "null";
|
|
16413
|
+
json2.enum = vals;
|
|
15945
16414
|
}
|
|
15946
16415
|
break;
|
|
15947
16416
|
}
|
|
15948
16417
|
case "file": {
|
|
15949
|
-
const
|
|
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(
|
|
16432
|
+
Object.assign(json2, file);
|
|
15964
16433
|
} else {
|
|
15965
|
-
|
|
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(
|
|
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
|
|
15993
|
-
|
|
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
|
|
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
|
-
|
|
16033
|
-
|
|
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
|
|
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: () =>
|
|
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
|
|
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
|
|
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
|
|
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
|
|
33638
|
+
const json3 = await response.json();
|
|
33170
33639
|
if (!response.ok) {
|
|
33171
|
-
const errorMsg = typeof
|
|
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
|
|
33643
|
+
if (typeof json3 !== "object" || json3 === null) {
|
|
33175
33644
|
return [new Error("Invalid token response")];
|
|
33176
33645
|
}
|
|
33177
|
-
if (typeof
|
|
33646
|
+
if (typeof json3.access_token !== "string") {
|
|
33178
33647
|
return [new Error("Missing access_token in response")];
|
|
33179
33648
|
}
|
|
33180
|
-
if (
|
|
33649
|
+
if (json3.token_type !== "Bearer") {
|
|
33181
33650
|
return [new Error("Invalid token_type in response")];
|
|
33182
33651
|
}
|
|
33183
|
-
if (typeof
|
|
33652
|
+
if (typeof json3.expires_in !== "number") {
|
|
33184
33653
|
return [new Error("Missing expires_in in response")];
|
|
33185
33654
|
}
|
|
33186
|
-
return [null,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
},
|
|
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
|
|
47023
|
-
const resolvedHeaders = await
|
|
47024
|
-
const resolvedCredentials = await
|
|
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
|
|
47073
|
-
const resolvedHeaders = await
|
|
47074
|
-
const resolvedCredentials = await
|
|
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: () =>
|
|
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
|
|
50090
|
-
|
|
50091
|
-
|
|
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 =
|
|
50095
|
-
const
|
|
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
|
-
|
|
50114
|
-
|
|
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 =
|
|
51269
|
+
const d = explicitDb || getDatabase();
|
|
50119
51270
|
const resolvedId = resolvePartialId(d, "projects", projectId) || projectId;
|
|
50120
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
|
51856
|
+
return parseProjectRow2(row);
|
|
50450
51857
|
row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
|
|
50451
51858
|
if (row)
|
|
50452
|
-
return
|
|
51859
|
+
return parseProjectRow2(row);
|
|
50453
51860
|
row = d.query("SELECT * FROM projects WHERE LOWER(name) = ?").get(idOrPath.toLowerCase());
|
|
50454
51861
|
if (row)
|
|
50455
|
-
return
|
|
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(
|
|
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,
|
|
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:
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
54359
|
+
const configPath = join5(homeDir(), ".hasna", "mementos", "config.json");
|
|
51660
54360
|
let fileConfig = {};
|
|
51661
54361
|
if (existsSync3(configPath)) {
|
|
51662
54362
|
try {
|
|
51663
|
-
const raw =
|
|
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 =
|
|
54387
|
+
const legacyHomeMementosDb = resolve3(homeDir(), ".mementos", "mementos.db");
|
|
51688
54388
|
while (true) {
|
|
51689
|
-
const candidate =
|
|
51690
|
-
if (existsSync3(candidate) &&
|
|
54389
|
+
const candidate = join5(dir, filename);
|
|
54390
|
+
if (existsSync3(candidate) && resolve3(candidate) !== legacyHomeMementosDb) {
|
|
51691
54391
|
return candidate;
|
|
51692
54392
|
}
|
|
51693
|
-
const parent =
|
|
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(
|
|
54403
|
+
if (existsSync3(join5(dir, ".git"))) {
|
|
51704
54404
|
return dir;
|
|
51705
54405
|
}
|
|
51706
|
-
const parent =
|
|
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
|
|
54414
|
+
return join5(homeDir(), ".hasna", "mementos", "profiles");
|
|
51715
54415
|
}
|
|
51716
54416
|
function globalConfigPath() {
|
|
51717
|
-
return
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
51783
|
-
const _oldDir =
|
|
54482
|
+
const _newDir = join5(_home, ".hasna", "mementos");
|
|
54483
|
+
const _oldDir = join5(_home, ".mementos");
|
|
51784
54484
|
if (!existsSync3(_newDir) && existsSync3(_oldDir)) {
|
|
51785
|
-
mkdirSync3(
|
|
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 =
|
|
51794
|
-
ensureDir2(
|
|
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 =
|
|
51800
|
-
ensureDir2(
|
|
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 =
|
|
51808
|
-
ensureDir2(
|
|
54507
|
+
const dbPath = join5(gitRoot, ".mementos", "mementos.db");
|
|
54508
|
+
ensureDir2(dirname3(dbPath));
|
|
51809
54509
|
return dbPath;
|
|
51810
54510
|
}
|
|
51811
54511
|
}
|
|
51812
|
-
const found = findFileWalkingUp(
|
|
54512
|
+
const found = findFileWalkingUp(join5(".mementos", "mementos.db"));
|
|
51813
54513
|
if (found) {
|
|
51814
54514
|
return found;
|
|
51815
54515
|
}
|
|
51816
|
-
const fallback =
|
|
51817
|
-
ensureDir2(
|
|
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
|
|
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
|
|
54904
|
+
import { join as join6 } from "path";
|
|
52205
54905
|
function getAgentSyncDir(agentName) {
|
|
52206
|
-
const dir =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
54944
|
+
const inFile = join6(agentDir, "memories.json");
|
|
52245
54945
|
if (!existsSync4(inFile)) {
|
|
52246
54946
|
return { pulled: 0, conflicts: 0 };
|
|
52247
54947
|
}
|
|
52248
|
-
const raw =
|
|
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
|
|
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
|
|
57507
|
+
import { join as join7 } from "path";
|
|
54808
57508
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
54809
|
-
var CONFIG_DIR =
|
|
54810
|
-
var CONFIG_PATH =
|
|
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 =
|
|
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,
|