@hasna/mementos 0.14.75 → 0.14.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/bun.lock +93 -0
  2. package/dist/cli/commands/info-context.d.ts.map +1 -1
  3. package/dist/cli/commands/info-stale.d.ts.map +1 -1
  4. package/dist/cli/commands/io-export.d.ts.map +1 -1
  5. package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
  6. package/dist/cli/commands/memory-cmd-list.d.ts.map +1 -1
  7. package/dist/cli/commands/memory-cmd-recall.d.ts.map +1 -1
  8. package/dist/cli/commands/memory-cmd-search.d.ts.map +1 -1
  9. package/dist/cli/commands/memory-cmd-tail.d.ts.map +1 -1
  10. package/dist/cli/commands/project.d.ts.map +1 -1
  11. package/dist/cli/commands/system-watch.d.ts.map +1 -1
  12. package/dist/cli/helpers.d.ts +38 -0
  13. package/dist/cli/helpers.d.ts.map +1 -1
  14. package/dist/cli/index.js +2009 -334
  15. package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +1 -1
  16. package/dist/db/memory-project-link.d.ts +13 -0
  17. package/dist/db/memory-project-link.d.ts.map +1 -0
  18. package/dist/db/migrations.d.ts.map +1 -1
  19. package/dist/db/pg-migrations.d.ts +0 -6
  20. package/dist/db/pg-migrations.d.ts.map +1 -1
  21. package/dist/db/projects.d.ts +22 -1
  22. package/dist/db/projects.d.ts.map +1 -1
  23. package/dist/index.d.ts +5 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2828 -163
  26. package/dist/lib/package-version.d.ts +3 -0
  27. package/dist/lib/package-version.d.ts.map +1 -0
  28. package/dist/mcp/index.js +414 -0
  29. package/dist/memory-project-link/index.d.ts +2 -0
  30. package/dist/memory-project-link/index.d.ts.map +1 -0
  31. package/dist/memory-project-link/schema.d.ts +5 -0
  32. package/dist/memory-project-link/schema.d.ts.map +1 -0
  33. package/dist/project-registration/authority.d.ts +39 -0
  34. package/dist/project-registration/authority.d.ts.map +1 -0
  35. package/dist/project-registration/http.d.ts +29 -0
  36. package/dist/project-registration/http.d.ts.map +1 -0
  37. package/dist/project-registration/index.d.ts +8 -0
  38. package/dist/project-registration/index.d.ts.map +1 -0
  39. package/dist/project-registration/project-references.d.ts +71 -0
  40. package/dist/project-registration/project-references.d.ts.map +1 -0
  41. package/dist/project-registration/schema.d.ts +5 -0
  42. package/dist/project-registration/schema.d.ts.map +1 -0
  43. package/dist/project-registration/types.d.ts +168 -0
  44. package/dist/project-registration/types.d.ts.map +1 -0
  45. package/dist/project-registration.d.ts +2 -0
  46. package/dist/project-registration.d.ts.map +1 -0
  47. package/dist/project-registration.js +1496 -0
  48. package/dist/sdk/index.d.ts +127 -0
  49. package/dist/sdk/index.d.ts.map +1 -1
  50. package/dist/sdk/index.js +74 -0
  51. package/dist/server/index.d.ts +1 -0
  52. package/dist/server/index.d.ts.map +1 -1
  53. package/dist/server/index.js +2573 -138
  54. package/dist/server/routes/memories.d.ts +1 -0
  55. package/dist/server/routes/memories.d.ts.map +1 -1
  56. package/dist/server/routes/memory-project-link.d.ts +2 -0
  57. package/dist/server/routes/memory-project-link.d.ts.map +1 -0
  58. package/dist/server/routes/project-registration.d.ts +2 -0
  59. package/dist/server/routes/project-registration.d.ts.map +1 -0
  60. package/dist/types/index.d.ts +119 -0
  61. package/dist/types/index.d.ts.map +1 -1
  62. package/package.json +9 -3
  63. package/dashboard/bun.lock +0 -519
@@ -802,6 +802,201 @@ var init_api_mode = __esm(() => {
802
802
  };
803
803
  });
804
804
 
805
+ // src/project-registration/schema.ts
806
+ function sqliteMementosProjectRegistrationSchemaSql() {
807
+ return `
808
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_receipts (
809
+ receipt_id TEXT PRIMARY KEY,
810
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
811
+ route TEXT NOT NULL,
812
+ package_version TEXT NOT NULL,
813
+ authority_id TEXT NOT NULL,
814
+ tenant_id TEXT NOT NULL,
815
+ corpus_id TEXT NOT NULL,
816
+ operation_id TEXT NOT NULL,
817
+ step_id TEXT NOT NULL,
818
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
819
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'inverse')),
820
+ target_selector TEXT NOT NULL,
821
+ idempotency_key TEXT NOT NULL,
822
+ request_digest TEXT NOT NULL,
823
+ precondition_digest TEXT NOT NULL,
824
+ normalized_call_digest TEXT NOT NULL,
825
+ outcome TEXT NOT NULL CHECK(outcome IN (
826
+ 'accepted', 'duplicate_of_accepted', 'terminal_nonacceptance'
827
+ )),
828
+ reason TEXT,
829
+ target_id TEXT,
830
+ result_revision TEXT,
831
+ result_digest TEXT,
832
+ duplicate_of_receipt_id TEXT,
833
+ accepted_receipt_id TEXT,
834
+ created_by_operation INTEGER NOT NULL CHECK(created_by_operation IN (0, 1)),
835
+ created_at TEXT NOT NULL
836
+ );
837
+
838
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_lookup
839
+ ON mementos_project_registration_receipts (
840
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
841
+ resource_kind, direction, idempotency_key, target_selector
842
+ );
843
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_accepted_step
844
+ ON mementos_project_registration_receipts (
845
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
846
+ resource_kind, direction
847
+ )
848
+ WHERE outcome = 'accepted';
849
+
850
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_bindings (
851
+ authority_id TEXT NOT NULL,
852
+ tenant_id TEXT NOT NULL,
853
+ corpus_id TEXT NOT NULL,
854
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
855
+ target_selector TEXT NOT NULL,
856
+ operation_id TEXT NOT NULL,
857
+ step_id TEXT NOT NULL,
858
+ direction TEXT NOT NULL CHECK(direction = 'forward'),
859
+ idempotency_key TEXT NOT NULL,
860
+ request_digest TEXT NOT NULL,
861
+ precondition_digest TEXT NOT NULL,
862
+ normalized_call_digest TEXT NOT NULL,
863
+ state TEXT NOT NULL CHECK(state IN (
864
+ 'pending', 'accepted', 'terminal_nonacceptance', 'removed'
865
+ )),
866
+ target_id TEXT,
867
+ accepted_receipt_id TEXT,
868
+ result_revision TEXT,
869
+ result_digest TEXT,
870
+ removed_receipt_id TEXT,
871
+ created_at TEXT NOT NULL,
872
+ updated_at TEXT NOT NULL,
873
+ PRIMARY KEY(authority_id, tenant_id, corpus_id, resource_kind, target_selector),
874
+ UNIQUE(accepted_receipt_id)
875
+ );
876
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_binding_target
877
+ ON mementos_project_registration_bindings(
878
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
879
+ )
880
+ WHERE target_id IS NOT NULL;
881
+
882
+ CREATE TRIGGER IF NOT EXISTS mementos_project_registration_receipts_immutable_update
883
+ BEFORE UPDATE ON mementos_project_registration_receipts
884
+ BEGIN
885
+ SELECT RAISE(ABORT, 'mementos project registration receipts are immutable');
886
+ END;
887
+
888
+ CREATE TRIGGER IF NOT EXISTS mementos_project_registration_receipts_immutable_delete
889
+ BEFORE DELETE ON mementos_project_registration_receipts
890
+ BEGIN
891
+ SELECT RAISE(ABORT, 'mementos project registration receipts are immutable');
892
+ END;
893
+ `;
894
+ }
895
+ function sqliteMementosProjectGuardedUpdateSchemaSql() {
896
+ return `
897
+ CREATE TABLE IF NOT EXISTS mementos_project_update_receipts (
898
+ receipt_id TEXT PRIMARY KEY,
899
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
900
+ route TEXT NOT NULL CHECK(route = 'mementos.project-guarded-update.v1'),
901
+ package_version TEXT NOT NULL,
902
+ authority_id TEXT NOT NULL,
903
+ tenant_id TEXT NOT NULL,
904
+ corpus_id TEXT NOT NULL,
905
+ operation_id TEXT NOT NULL,
906
+ step_id TEXT NOT NULL,
907
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
908
+ idempotency_key TEXT NOT NULL,
909
+ request_digest TEXT NOT NULL,
910
+ outcome TEXT NOT NULL CHECK(outcome = 'accepted'),
911
+ target_id TEXT NOT NULL,
912
+ expected_revision TEXT NOT NULL,
913
+ result_revision TEXT NOT NULL,
914
+ result_digest TEXT NOT NULL,
915
+ accepted_receipt_id TEXT,
916
+ before_project_json TEXT NOT NULL,
917
+ after_project_json TEXT NOT NULL,
918
+ created_at TEXT NOT NULL,
919
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
920
+ );
921
+
922
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_update_receipts_target
923
+ ON mementos_project_update_receipts(
924
+ authority_id, tenant_id, corpus_id, target_id, created_at
925
+ );
926
+
927
+ CREATE TRIGGER IF NOT EXISTS mementos_project_update_receipts_immutable_update
928
+ BEFORE UPDATE ON mementos_project_update_receipts
929
+ BEGIN
930
+ SELECT RAISE(ABORT, 'mementos project update receipts are immutable');
931
+ END;
932
+
933
+ CREATE TRIGGER IF NOT EXISTS mementos_project_update_receipts_immutable_delete
934
+ BEFORE DELETE ON mementos_project_update_receipts
935
+ BEGIN
936
+ SELECT RAISE(ABORT, 'mementos project update receipts are immutable');
937
+ END;
938
+ `;
939
+ }
940
+
941
+ // src/memory-project-link/schema.ts
942
+ function sqliteMementosMemoryProjectLinkSchemaSql() {
943
+ return `
944
+ CREATE TABLE IF NOT EXISTS mementos_memory_project_link_receipts (
945
+ receipt_id TEXT PRIMARY KEY,
946
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
947
+ route TEXT NOT NULL CHECK(route = 'mementos.memory-project-link.v1'),
948
+ package_version TEXT NOT NULL,
949
+ authority_id TEXT NOT NULL,
950
+ tenant_id TEXT NOT NULL,
951
+ corpus_id TEXT NOT NULL,
952
+ operation_id TEXT NOT NULL,
953
+ step_id TEXT NOT NULL,
954
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
955
+ idempotency_key TEXT NOT NULL,
956
+ request_digest TEXT NOT NULL,
957
+ outcome TEXT NOT NULL CHECK(outcome IN ('accepted', 'no_change')),
958
+ target_memory_id TEXT NOT NULL,
959
+ requested_project_id TEXT NOT NULL,
960
+ expected_memory_version INTEGER NOT NULL,
961
+ expected_memory_revision TEXT NOT NULL,
962
+ expected_project_revision TEXT,
963
+ result_memory_version INTEGER NOT NULL,
964
+ result_memory_revision TEXT NOT NULL,
965
+ result_memory_digest TEXT NOT NULL,
966
+ result_project_revision TEXT,
967
+ result_project_digest TEXT,
968
+ accepted_receipt_id TEXT,
969
+ before_link_json TEXT NOT NULL,
970
+ after_link_json TEXT NOT NULL,
971
+ before_project_revision TEXT,
972
+ before_project_digest TEXT,
973
+ after_project_revision TEXT,
974
+ after_project_digest TEXT,
975
+ created_at TEXT NOT NULL,
976
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
977
+ );
978
+
979
+ CREATE INDEX IF NOT EXISTS idx_mementos_memory_project_link_receipts_target
980
+ ON mementos_memory_project_link_receipts(
981
+ authority_id, tenant_id, corpus_id, target_memory_id, created_at
982
+ );
983
+
984
+ CREATE TRIGGER IF NOT EXISTS mementos_memory_project_link_receipts_immutable_update
985
+ BEFORE UPDATE ON mementos_memory_project_link_receipts
986
+ BEGIN
987
+ SELECT RAISE(ABORT, 'mementos memory project link receipts are immutable');
988
+ END;
989
+
990
+ CREATE TRIGGER IF NOT EXISTS mementos_memory_project_link_receipts_immutable_delete
991
+ BEFORE DELETE ON mementos_memory_project_link_receipts
992
+ BEGIN
993
+ SELECT RAISE(ABORT, 'mementos memory project link receipts are immutable');
994
+ END;
995
+ `;
996
+ }
997
+ var MEMENTOS_MEMORY_PROJECT_LINK_ROUTE = "mementos.memory-project-link.v1";
998
+ var init_schema = () => {};
999
+
805
1000
  // src/db/migrations.ts
806
1001
  var MEMORY_VERSION_SNAPSHOT_TRIGGER = `
807
1002
  CREATE TRIGGER IF NOT EXISTS memories_version_snapshot
@@ -822,6 +1017,7 @@ BEGIN
822
1017
  END;
823
1018
  `, MIGRATIONS;
824
1019
  var init_migrations = __esm(() => {
1020
+ init_schema();
825
1021
  MIGRATIONS = [
826
1022
  `
827
1023
  CREATE TABLE IF NOT EXISTS projects (
@@ -1755,6 +1951,18 @@ UPDATE memory_audit_log SET new_value_hash = NULL
1755
1951
  AND new_value_hash NOT GLOB '*[^0-9A-F]*';
1756
1952
 
1757
1953
  INSERT OR IGNORE INTO _migrations (id) VALUES (37);
1954
+ `,
1955
+ `
1956
+ ${sqliteMementosProjectRegistrationSchemaSql()}
1957
+ INSERT OR IGNORE INTO _migrations (id) VALUES (38);
1958
+ `,
1959
+ `
1960
+ ${sqliteMementosProjectGuardedUpdateSchemaSql()}
1961
+ INSERT OR IGNORE INTO _migrations (id) VALUES (39);
1962
+ `,
1963
+ `
1964
+ ${sqliteMementosMemoryProjectLinkSchemaSql()}
1965
+ INSERT OR IGNORE INTO _migrations (id) VALUES (40);
1758
1966
  `
1759
1967
  ];
1760
1968
  });
@@ -6192,8 +6400,8 @@ var init_util = __esm(() => {
6192
6400
 
6193
6401
  // node_modules/zod/v3/ZodError.js
6194
6402
  var ZodIssueCode, quotelessJson = (obj) => {
6195
- const json2 = JSON.stringify(obj, null, 2);
6196
- return json2.replace(/"([^"]+)":/g, "$1:");
6403
+ const json3 = JSON.stringify(obj, null, 2);
6404
+ return json3.replace(/"([^"]+)":/g, "$1:");
6197
6405
  }, ZodError;
6198
6406
  var init_ZodError = __esm(() => {
6199
6407
  init_util();
@@ -19385,24 +19593,24 @@ class JSONSchemaGenerator {
19385
19593
  const _json = result.schema;
19386
19594
  switch (def.type) {
19387
19595
  case "string": {
19388
- const json2 = _json;
19389
- json2.type = "string";
19596
+ const json3 = _json;
19597
+ json3.type = "string";
19390
19598
  const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag;
19391
19599
  if (typeof minimum === "number")
19392
- json2.minLength = minimum;
19600
+ json3.minLength = minimum;
19393
19601
  if (typeof maximum === "number")
19394
- json2.maxLength = maximum;
19602
+ json3.maxLength = maximum;
19395
19603
  if (format) {
19396
- json2.format = formatMap[format] ?? format;
19397
- if (json2.format === "")
19398
- delete json2.format;
19604
+ json3.format = formatMap[format] ?? format;
19605
+ if (json3.format === "")
19606
+ delete json3.format;
19399
19607
  }
19400
19608
  if (contentEncoding)
19401
- json2.contentEncoding = contentEncoding;
19609
+ json3.contentEncoding = contentEncoding;
19402
19610
  if (patterns && patterns.size > 0) {
19403
19611
  const regexes = [...patterns];
19404
19612
  if (regexes.length === 1)
19405
- json2.pattern = regexes[0].source;
19613
+ json3.pattern = regexes[0].source;
19406
19614
  else if (regexes.length > 1) {
19407
19615
  result.schema.allOf = [
19408
19616
  ...regexes.map((regex) => ({
@@ -19415,41 +19623,41 @@ class JSONSchemaGenerator {
19415
19623
  break;
19416
19624
  }
19417
19625
  case "number": {
19418
- const json2 = _json;
19626
+ const json3 = _json;
19419
19627
  const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
19420
19628
  if (typeof format === "string" && format.includes("int"))
19421
- json2.type = "integer";
19629
+ json3.type = "integer";
19422
19630
  else
19423
- json2.type = "number";
19631
+ json3.type = "number";
19424
19632
  if (typeof exclusiveMinimum === "number")
19425
- json2.exclusiveMinimum = exclusiveMinimum;
19633
+ json3.exclusiveMinimum = exclusiveMinimum;
19426
19634
  if (typeof minimum === "number") {
19427
- json2.minimum = minimum;
19635
+ json3.minimum = minimum;
19428
19636
  if (typeof exclusiveMinimum === "number") {
19429
19637
  if (exclusiveMinimum >= minimum)
19430
- delete json2.minimum;
19638
+ delete json3.minimum;
19431
19639
  else
19432
- delete json2.exclusiveMinimum;
19640
+ delete json3.exclusiveMinimum;
19433
19641
  }
19434
19642
  }
19435
19643
  if (typeof exclusiveMaximum === "number")
19436
- json2.exclusiveMaximum = exclusiveMaximum;
19644
+ json3.exclusiveMaximum = exclusiveMaximum;
19437
19645
  if (typeof maximum === "number") {
19438
- json2.maximum = maximum;
19646
+ json3.maximum = maximum;
19439
19647
  if (typeof exclusiveMaximum === "number") {
19440
19648
  if (exclusiveMaximum <= maximum)
19441
- delete json2.maximum;
19649
+ delete json3.maximum;
19442
19650
  else
19443
- delete json2.exclusiveMaximum;
19651
+ delete json3.exclusiveMaximum;
19444
19652
  }
19445
19653
  }
19446
19654
  if (typeof multipleOf === "number")
19447
- json2.multipleOf = multipleOf;
19655
+ json3.multipleOf = multipleOf;
19448
19656
  break;
19449
19657
  }
19450
19658
  case "boolean": {
19451
- const json2 = _json;
19452
- json2.type = "boolean";
19659
+ const json3 = _json;
19660
+ json3.type = "boolean";
19453
19661
  break;
19454
19662
  }
19455
19663
  case "bigint": {
@@ -19497,23 +19705,23 @@ class JSONSchemaGenerator {
19497
19705
  break;
19498
19706
  }
19499
19707
  case "array": {
19500
- const json2 = _json;
19708
+ const json3 = _json;
19501
19709
  const { minimum, maximum } = schema._zod.bag;
19502
19710
  if (typeof minimum === "number")
19503
- json2.minItems = minimum;
19711
+ json3.minItems = minimum;
19504
19712
  if (typeof maximum === "number")
19505
- json2.maxItems = maximum;
19506
- json2.type = "array";
19507
- json2.items = this.process(def.element, { ...params, path: [...params.path, "items"] });
19713
+ json3.maxItems = maximum;
19714
+ json3.type = "array";
19715
+ json3.items = this.process(def.element, { ...params, path: [...params.path, "items"] });
19508
19716
  break;
19509
19717
  }
19510
19718
  case "object": {
19511
- const json2 = _json;
19512
- json2.type = "object";
19513
- json2.properties = {};
19719
+ const json3 = _json;
19720
+ json3.type = "object";
19721
+ json3.properties = {};
19514
19722
  const shape = def.shape;
19515
19723
  for (const key in shape) {
19516
- json2.properties[key] = this.process(shape[key], {
19724
+ json3.properties[key] = this.process(shape[key], {
19517
19725
  ...params,
19518
19726
  path: [...params.path, "properties", key]
19519
19727
  });
@@ -19528,15 +19736,15 @@ class JSONSchemaGenerator {
19528
19736
  }
19529
19737
  }));
19530
19738
  if (requiredKeys.size > 0) {
19531
- json2.required = Array.from(requiredKeys);
19739
+ json3.required = Array.from(requiredKeys);
19532
19740
  }
19533
19741
  if (def.catchall?._zod.def.type === "never") {
19534
- json2.additionalProperties = false;
19742
+ json3.additionalProperties = false;
19535
19743
  } else if (!def.catchall) {
19536
19744
  if (this.io === "output")
19537
- json2.additionalProperties = false;
19745
+ json3.additionalProperties = false;
19538
19746
  } else if (def.catchall) {
19539
- json2.additionalProperties = this.process(def.catchall, {
19747
+ json3.additionalProperties = this.process(def.catchall, {
19540
19748
  ...params,
19541
19749
  path: [...params.path, "additionalProperties"]
19542
19750
  });
@@ -19544,15 +19752,15 @@ class JSONSchemaGenerator {
19544
19752
  break;
19545
19753
  }
19546
19754
  case "union": {
19547
- const json2 = _json;
19548
- json2.anyOf = def.options.map((x, i) => this.process(x, {
19755
+ const json3 = _json;
19756
+ json3.anyOf = def.options.map((x, i) => this.process(x, {
19549
19757
  ...params,
19550
19758
  path: [...params.path, "anyOf", i]
19551
19759
  }));
19552
19760
  break;
19553
19761
  }
19554
19762
  case "intersection": {
19555
- const json2 = _json;
19763
+ const json3 = _json;
19556
19764
  const a = this.process(def.left, {
19557
19765
  ...params,
19558
19766
  path: [...params.path, "allOf", 0]
@@ -19566,17 +19774,17 @@ class JSONSchemaGenerator {
19566
19774
  ...isSimpleIntersection(a) ? a.allOf : [a],
19567
19775
  ...isSimpleIntersection(b) ? b.allOf : [b]
19568
19776
  ];
19569
- json2.allOf = allOf;
19777
+ json3.allOf = allOf;
19570
19778
  break;
19571
19779
  }
19572
19780
  case "tuple": {
19573
- const json2 = _json;
19574
- json2.type = "array";
19781
+ const json3 = _json;
19782
+ json3.type = "array";
19575
19783
  const prefixItems = def.items.map((x, i) => this.process(x, { ...params, path: [...params.path, "prefixItems", i] }));
19576
19784
  if (this.target === "draft-2020-12") {
19577
- json2.prefixItems = prefixItems;
19785
+ json3.prefixItems = prefixItems;
19578
19786
  } else {
19579
- json2.items = prefixItems;
19787
+ json3.items = prefixItems;
19580
19788
  }
19581
19789
  if (def.rest) {
19582
19790
  const rest = this.process(def.rest, {
@@ -19584,29 +19792,29 @@ class JSONSchemaGenerator {
19584
19792
  path: [...params.path, "items"]
19585
19793
  });
19586
19794
  if (this.target === "draft-2020-12") {
19587
- json2.items = rest;
19795
+ json3.items = rest;
19588
19796
  } else {
19589
- json2.additionalItems = rest;
19797
+ json3.additionalItems = rest;
19590
19798
  }
19591
19799
  }
19592
19800
  if (def.rest) {
19593
- json2.items = this.process(def.rest, {
19801
+ json3.items = this.process(def.rest, {
19594
19802
  ...params,
19595
19803
  path: [...params.path, "items"]
19596
19804
  });
19597
19805
  }
19598
19806
  const { minimum, maximum } = schema._zod.bag;
19599
19807
  if (typeof minimum === "number")
19600
- json2.minItems = minimum;
19808
+ json3.minItems = minimum;
19601
19809
  if (typeof maximum === "number")
19602
- json2.maxItems = maximum;
19810
+ json3.maxItems = maximum;
19603
19811
  break;
19604
19812
  }
19605
19813
  case "record": {
19606
- const json2 = _json;
19607
- json2.type = "object";
19608
- json2.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
19609
- json2.additionalProperties = this.process(def.valueType, {
19814
+ const json3 = _json;
19815
+ json3.type = "object";
19816
+ json3.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
19817
+ json3.additionalProperties = this.process(def.valueType, {
19610
19818
  ...params,
19611
19819
  path: [...params.path, "additionalProperties"]
19612
19820
  });
@@ -19625,17 +19833,17 @@ class JSONSchemaGenerator {
19625
19833
  break;
19626
19834
  }
19627
19835
  case "enum": {
19628
- const json2 = _json;
19836
+ const json3 = _json;
19629
19837
  const values = getEnumValues(def.entries);
19630
19838
  if (values.every((v) => typeof v === "number"))
19631
- json2.type = "number";
19839
+ json3.type = "number";
19632
19840
  if (values.every((v) => typeof v === "string"))
19633
- json2.type = "string";
19634
- json2.enum = values;
19841
+ json3.type = "string";
19842
+ json3.enum = values;
19635
19843
  break;
19636
19844
  }
19637
19845
  case "literal": {
19638
- const json2 = _json;
19846
+ const json3 = _json;
19639
19847
  const vals = [];
19640
19848
  for (const val of def.values) {
19641
19849
  if (val === undefined) {
@@ -19654,23 +19862,23 @@ class JSONSchemaGenerator {
19654
19862
  }
19655
19863
  if (vals.length === 0) {} else if (vals.length === 1) {
19656
19864
  const val = vals[0];
19657
- json2.type = val === null ? "null" : typeof val;
19658
- json2.const = val;
19865
+ json3.type = val === null ? "null" : typeof val;
19866
+ json3.const = val;
19659
19867
  } else {
19660
19868
  if (vals.every((v) => typeof v === "number"))
19661
- json2.type = "number";
19869
+ json3.type = "number";
19662
19870
  if (vals.every((v) => typeof v === "string"))
19663
- json2.type = "string";
19871
+ json3.type = "string";
19664
19872
  if (vals.every((v) => typeof v === "boolean"))
19665
- json2.type = "string";
19873
+ json3.type = "string";
19666
19874
  if (vals.every((v) => v === null))
19667
- json2.type = "null";
19668
- json2.enum = vals;
19875
+ json3.type = "null";
19876
+ json3.enum = vals;
19669
19877
  }
19670
19878
  break;
19671
19879
  }
19672
19880
  case "file": {
19673
- const json2 = _json;
19881
+ const json3 = _json;
19674
19882
  const file = {
19675
19883
  type: "string",
19676
19884
  format: "binary",
@@ -19684,15 +19892,15 @@ class JSONSchemaGenerator {
19684
19892
  if (mime) {
19685
19893
  if (mime.length === 1) {
19686
19894
  file.contentMediaType = mime[0];
19687
- Object.assign(json2, file);
19895
+ Object.assign(json3, file);
19688
19896
  } else {
19689
- json2.anyOf = mime.map((m) => {
19897
+ json3.anyOf = mime.map((m) => {
19690
19898
  const mFile = { ...file, contentMediaType: m };
19691
19899
  return mFile;
19692
19900
  });
19693
19901
  }
19694
19902
  } else {
19695
- Object.assign(json2, file);
19903
+ Object.assign(json3, file);
19696
19904
  }
19697
19905
  break;
19698
19906
  }
@@ -19713,8 +19921,8 @@ class JSONSchemaGenerator {
19713
19921
  break;
19714
19922
  }
19715
19923
  case "success": {
19716
- const json2 = _json;
19717
- json2.type = "boolean";
19924
+ const json3 = _json;
19925
+ json3.type = "boolean";
19718
19926
  break;
19719
19927
  }
19720
19928
  case "default": {
@@ -19749,12 +19957,12 @@ class JSONSchemaGenerator {
19749
19957
  break;
19750
19958
  }
19751
19959
  case "template_literal": {
19752
- const json2 = _json;
19960
+ const json3 = _json;
19753
19961
  const pattern = schema._zod.pattern;
19754
19962
  if (!pattern)
19755
19963
  throw new Error("Pattern not found in template literal");
19756
- json2.type = "string";
19757
- json2.pattern = pattern.source;
19964
+ json3.type = "string";
19965
+ json3.pattern = pattern.source;
19758
19966
  break;
19759
19967
  }
19760
19968
  case "pipe": {
@@ -20847,7 +21055,7 @@ function _instanceof(cls, params = {
20847
21055
  inst._zod.bag.Class = cls;
20848
21056
  return inst;
20849
21057
  }
20850
- function json2(params) {
21058
+ function json3(params) {
20851
21059
  const jsonSchema = lazy(() => {
20852
21060
  return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
20853
21061
  });
@@ -21534,7 +21742,7 @@ __export(exports_external2, {
21534
21742
  ksuid: () => ksuid2,
21535
21743
  keyof: () => keyof,
21536
21744
  jwt: () => jwt,
21537
- json: () => json2,
21745
+ json: () => json3,
21538
21746
  iso: () => exports_iso,
21539
21747
  ipv6: () => ipv62,
21540
21748
  ipv4: () => ipv42,
@@ -23394,7 +23602,7 @@ function createProviderToolFactoryWithOutputSchema({
23394
23602
  supportsDeferredResults
23395
23603
  });
23396
23604
  }
23397
- async function resolve3(value) {
23605
+ async function resolve4(value) {
23398
23606
  if (typeof value === "function") {
23399
23607
  value = value();
23400
23608
  }
@@ -26216,11 +26424,11 @@ var VERSION2 = "3.0.82", anthropicErrorDataSchema, anthropicFailedResponseHandle
26216
26424
  betas,
26217
26425
  headers
26218
26426
  }) {
26219
- return combineHeaders(await resolve3(this.config.headers), headers, betas.size > 0 ? { "anthropic-beta": Array.from(betas).join(",") } : {});
26427
+ return combineHeaders(await resolve4(this.config.headers), headers, betas.size > 0 ? { "anthropic-beta": Array.from(betas).join(",") } : {});
26220
26428
  }
26221
26429
  async getBetasFromHeaders(requestHeaders) {
26222
26430
  var _a16, _b16;
26223
- const configHeaders = await resolve3(this.config.headers);
26431
+ const configHeaders = await resolve4(this.config.headers);
26224
26432
  const configBetaHeader = (_a16 = configHeaders["anthropic-beta"]) != null ? _a16 : "";
26225
26433
  const requestBetaHeader = (_b16 = requestHeaders == null ? undefined : requestHeaders["anthropic-beta"]) != null ? _b16 : "";
26226
26434
  return new Set([
@@ -36890,24 +37098,24 @@ var require_oauth = __commonJS((exports, module) => {
36890
37098
  });
36891
37099
  }
36892
37100
  async function processTokenResponse(response) {
36893
- const json3 = await response.json();
37101
+ const json4 = await response.json();
36894
37102
  if (!response.ok) {
36895
- const errorMsg = typeof json3 === "object" && json3 && "error" in json3 ? String(json3.error) : "Token refresh failed";
37103
+ const errorMsg = typeof json4 === "object" && json4 && "error" in json4 ? String(json4.error) : "Token refresh failed";
36896
37104
  return [new Error(errorMsg)];
36897
37105
  }
36898
- if (typeof json3 !== "object" || json3 === null) {
37106
+ if (typeof json4 !== "object" || json4 === null) {
36899
37107
  return [new Error("Invalid token response")];
36900
37108
  }
36901
- if (typeof json3.access_token !== "string") {
37109
+ if (typeof json4.access_token !== "string") {
36902
37110
  return [new Error("Missing access_token in response")];
36903
37111
  }
36904
- if (json3.token_type !== "Bearer") {
37112
+ if (json4.token_type !== "Bearer") {
36905
37113
  return [new Error("Invalid token_type in response")];
36906
37114
  }
36907
- if (typeof json3.expires_in !== "number") {
37115
+ if (typeof json4.expires_in !== "number") {
36908
37116
  return [new Error("Missing expires_in in response")];
36909
37117
  }
36910
- return [null, json3];
37118
+ return [null, json4];
36911
37119
  }
36912
37120
  });
36913
37121
 
@@ -37644,7 +37852,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37644
37852
  try {
37645
37853
  const { value } = await getFromApi({
37646
37854
  url: `${this.config.baseURL}/config`,
37647
- headers: await resolve3(this.config.headers()),
37855
+ headers: await resolve4(this.config.headers()),
37648
37856
  successfulResponseHandler: createJsonResponseHandler(gatewayAvailableModelsResponseSchema),
37649
37857
  failedResponseHandler: createJsonErrorResponseHandler({
37650
37858
  errorSchema: exports_external2.any(),
@@ -37662,7 +37870,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37662
37870
  const baseUrl = new URL(this.config.baseURL);
37663
37871
  const { value } = await getFromApi({
37664
37872
  url: `${baseUrl.origin}/v1/credits`,
37665
- headers: await resolve3(this.config.headers()),
37873
+ headers: await resolve4(this.config.headers()),
37666
37874
  successfulResponseHandler: createJsonResponseHandler(gatewayCreditsResponseSchema),
37667
37875
  failedResponseHandler: createJsonErrorResponseHandler({
37668
37876
  errorSchema: exports_external2.any(),
@@ -37708,7 +37916,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37708
37916
  }
37709
37917
  const { value } = await getFromApi({
37710
37918
  url: `${baseUrl.origin}/v1/report?${searchParams.toString()}`,
37711
- headers: await resolve3(this.config.headers()),
37919
+ headers: await resolve4(this.config.headers()),
37712
37920
  successfulResponseHandler: createJsonResponseHandler(gatewaySpendReportResponseSchema),
37713
37921
  failedResponseHandler: createJsonErrorResponseHandler({
37714
37922
  errorSchema: exports_external2.any(),
@@ -37730,7 +37938,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37730
37938
  const baseUrl = new URL(this.config.baseURL);
37731
37939
  const { value } = await getFromApi({
37732
37940
  url: `${baseUrl.origin}/v1/generation?id=${encodeURIComponent(params.id)}`,
37733
- headers: await resolve3(this.config.headers()),
37941
+ headers: await resolve4(this.config.headers()),
37734
37942
  successfulResponseHandler: createJsonResponseHandler(gatewayGenerationInfoResponseSchema),
37735
37943
  failedResponseHandler: createJsonErrorResponseHandler({
37736
37944
  errorSchema: exports_external2.any(),
@@ -37763,7 +37971,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37763
37971
  async doGenerate(options) {
37764
37972
  const { args, warnings } = await this.getArgs(options);
37765
37973
  const { abortSignal } = options;
37766
- const resolvedHeaders = await resolve3(this.config.headers());
37974
+ const resolvedHeaders = await resolve4(this.config.headers());
37767
37975
  try {
37768
37976
  const {
37769
37977
  responseHeaders,
@@ -37771,7 +37979,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37771
37979
  rawValue: rawResponse
37772
37980
  } = await postJsonToApi({
37773
37981
  url: this.getUrl(),
37774
- headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve3(this.config.o11yHeaders)),
37982
+ headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve4(this.config.o11yHeaders)),
37775
37983
  body: args,
37776
37984
  successfulResponseHandler: createJsonResponseHandler(exports_external2.any()),
37777
37985
  failedResponseHandler: createJsonErrorResponseHandler({
@@ -37794,11 +38002,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37794
38002
  async doStream(options) {
37795
38003
  const { args, warnings } = await this.getArgs(options);
37796
38004
  const { abortSignal } = options;
37797
- const resolvedHeaders = await resolve3(this.config.headers());
38005
+ const resolvedHeaders = await resolve4(this.config.headers());
37798
38006
  try {
37799
38007
  const { value: response, responseHeaders } = await postJsonToApi({
37800
38008
  url: this.getUrl(),
37801
- headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve3(this.config.o11yHeaders)),
38009
+ headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve4(this.config.o11yHeaders)),
37802
38010
  body: args,
37803
38011
  successfulResponseHandler: createEventSourceResponseHandler(exports_external2.any()),
37804
38012
  failedResponseHandler: createJsonErrorResponseHandler({
@@ -37883,7 +38091,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37883
38091
  providerOptions
37884
38092
  }) {
37885
38093
  var _a102;
37886
- const resolvedHeaders = await resolve3(this.config.headers());
38094
+ const resolvedHeaders = await resolve4(this.config.headers());
37887
38095
  try {
37888
38096
  const {
37889
38097
  responseHeaders,
@@ -37891,7 +38099,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37891
38099
  rawValue
37892
38100
  } = await postJsonToApi({
37893
38101
  url: this.getUrl(),
37894
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
38102
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
37895
38103
  body: {
37896
38104
  values,
37897
38105
  ...providerOptions ? { providerOptions } : {}
@@ -37947,7 +38155,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37947
38155
  abortSignal
37948
38156
  }) {
37949
38157
  var _a102, _b102, _c, _d;
37950
- const resolvedHeaders = await resolve3(this.config.headers());
38158
+ const resolvedHeaders = await resolve4(this.config.headers());
37951
38159
  try {
37952
38160
  const {
37953
38161
  responseHeaders,
@@ -37955,7 +38163,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
37955
38163
  rawValue
37956
38164
  } = await postJsonToApi({
37957
38165
  url: this.getUrl(),
37958
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
38166
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
37959
38167
  body: {
37960
38168
  prompt,
37961
38169
  n,
@@ -38030,11 +38238,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
38030
38238
  abortSignal
38031
38239
  }) {
38032
38240
  var _a102;
38033
- const resolvedHeaders = await resolve3(this.config.headers());
38241
+ const resolvedHeaders = await resolve4(this.config.headers());
38034
38242
  try {
38035
38243
  const { responseHeaders, value: responseBody } = await postJsonToApi({
38036
38244
  url: this.getUrl(),
38037
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders), { accept: "text/event-stream" }),
38245
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders), { accept: "text/event-stream" }),
38038
38246
  body: {
38039
38247
  prompt,
38040
38248
  n,
@@ -38157,7 +38365,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
38157
38365
  abortSignal,
38158
38366
  providerOptions
38159
38367
  }) {
38160
- const resolvedHeaders = await resolve3(this.config.headers());
38368
+ const resolvedHeaders = await resolve4(this.config.headers());
38161
38369
  try {
38162
38370
  const {
38163
38371
  responseHeaders,
@@ -38165,7 +38373,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
38165
38373
  rawValue
38166
38374
  } = await postJsonToApi({
38167
38375
  url: this.getUrl(),
38168
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
38376
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
38169
38377
  body: {
38170
38378
  documents,
38171
38379
  query,
@@ -47769,7 +47977,7 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
47769
47977
  const schema = asSchema(inputSchema);
47770
47978
  return {
47771
47979
  name: "object",
47772
- responseFormat: resolve3(schema.jsonSchema).then((jsonSchema2) => ({
47980
+ responseFormat: resolve4(schema.jsonSchema).then((jsonSchema2) => ({
47773
47981
  type: "json",
47774
47982
  schema: jsonSchema2,
47775
47983
  ...name21 != null && { name: name21 },
@@ -47830,7 +48038,7 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
47830
48038
  const elementSchema = asSchema(inputElementSchema);
47831
48039
  return {
47832
48040
  name: "array",
47833
- responseFormat: resolve3(elementSchema.jsonSchema).then((jsonSchema2) => {
48041
+ responseFormat: resolve4(elementSchema.jsonSchema).then((jsonSchema2) => {
47834
48042
  const { $schema, ...itemSchema } = jsonSchema2;
47835
48043
  return {
47836
48044
  type: "json",
@@ -48007,7 +48215,7 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
48007
48215
  return;
48008
48216
  }
48009
48217
  };
48010
- }, json3 = ({
48218
+ }, json4 = ({
48011
48219
  name: name21,
48012
48220
  description
48013
48221
  } = {}) => {
@@ -50743,9 +50951,9 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
50743
50951
  ...options
50744
50952
  }) {
50745
50953
  var _a21, _b16, _c, _d, _e;
50746
- const resolvedBody = await resolve3(this.body);
50747
- const resolvedHeaders = await resolve3(this.headers);
50748
- const resolvedCredentials = await resolve3(this.credentials);
50954
+ const resolvedBody = await resolve4(this.body);
50955
+ const resolvedHeaders = await resolve4(this.headers);
50956
+ const resolvedCredentials = await resolve4(this.credentials);
50749
50957
  const baseHeaders = {
50750
50958
  ...normalizeHeaders(resolvedHeaders),
50751
50959
  ...normalizeHeaders(options.headers)
@@ -50793,9 +51001,9 @@ var import_api2, import_api3, __defProp2, __export2 = (target, all) => {
50793
51001
  }
50794
51002
  async reconnectToStream(options) {
50795
51003
  var _a21, _b16, _c, _d, _e;
50796
- const resolvedBody = await resolve3(this.body);
50797
- const resolvedHeaders = await resolve3(this.headers);
50798
- const resolvedCredentials = await resolve3(this.credentials);
51004
+ const resolvedBody = await resolve4(this.body);
51005
+ const resolvedHeaders = await resolve4(this.headers);
51006
+ const resolvedCredentials = await resolve4(this.credentials);
50799
51007
  const baseHeaders = {
50800
51008
  ...normalizeHeaders(resolvedHeaders),
50801
51009
  ...normalizeHeaders(options.headers)
@@ -52075,7 +52283,7 @@ var init_dist8 = __esm(() => {
52075
52283
  __export2(output_exports, {
52076
52284
  array: () => array2,
52077
52285
  choice: () => choice,
52078
- json: () => json3,
52286
+ json: () => json4,
52079
52287
  object: () => object2,
52080
52288
  text: () => text
52081
52289
  });
@@ -52738,7 +52946,7 @@ var init_openapi = __esm(() => {
52738
52946
  // src/server/index.ts
52739
52947
  import { existsSync as existsSync5 } from "fs";
52740
52948
  import { createRequire } from "module";
52741
- import { join as join6, resolve as resolve4, sep } from "path";
52949
+ import { join as join7, resolve as resolve5, sep } from "path";
52742
52950
 
52743
52951
  // src/lib/config.ts
52744
52952
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, unlinkSync, cpSync } from "fs";
@@ -54828,6 +55036,9 @@ addRoute("PATCH", "/api/memories/:id", async (req, _url, params) => {
54828
55036
  if (!body) {
54829
55037
  return errorResponse("Invalid JSON body", 400);
54830
55038
  }
55039
+ if (Object.prototype.hasOwnProperty.call(body, "project_id")) {
55040
+ return errorResponse("project_id changes require the guarded existing-memory project-link route", 428, { code: "MEMORY_PROJECT_LINK_GUARD_REQUIRED" });
55041
+ }
54831
55042
  const patchViolation = validateMemoryEnums(body);
54832
55043
  if (patchViolation) {
54833
55044
  return errorResponse(formatEnumViolation(patchViolation), 400, {
@@ -55710,6 +55921,10 @@ addRoute("POST", "/api/memories/search", async (req) => {
55710
55921
  filter.category = body["category"];
55711
55922
  if (body["tags"])
55712
55923
  filter.tags = body["tags"];
55924
+ if (body["agent_id"])
55925
+ filter.agent_id = body["agent_id"];
55926
+ if (body["project_id"])
55927
+ filter.project_id = body["project_id"];
55713
55928
  if (body["session_id"])
55714
55929
  filter.session_id = body["session_id"];
55715
55930
  if (body["namespace"])
@@ -56175,6 +56390,701 @@ ${lines.join(`
56175
56390
  return json({ context, memories_count: lines.length });
56176
56391
  });
56177
56392
 
56393
+ // src/db/memory-project-link.ts
56394
+ init_storage();
56395
+ init_api_mode();
56396
+ init_database();
56397
+ init_memories();
56398
+ import { createHash } from "crypto";
56399
+
56400
+ // src/lib/package-version.ts
56401
+ import { readFileSync as readFileSync3 } from "fs";
56402
+ import { dirname as dirname4, join as join6 } from "path";
56403
+ import { fileURLToPath as fileURLToPath3 } from "url";
56404
+ function getMementosPackageVersion() {
56405
+ const here = dirname4(fileURLToPath3(import.meta.url));
56406
+ for (const candidate of [
56407
+ join6(here, "..", "..", "package.json"),
56408
+ join6(here, "..", "package.json")
56409
+ ]) {
56410
+ try {
56411
+ const parsed = JSON.parse(readFileSync3(candidate, "utf8"));
56412
+ if (typeof parsed.version === "string" && parsed.version.trim())
56413
+ return parsed.version;
56414
+ } catch {}
56415
+ }
56416
+ return "0.0.0";
56417
+ }
56418
+
56419
+ // src/db/memory-project-link.ts
56420
+ init_schema();
56421
+
56422
+ class MemoryProjectLinkError extends Error {
56423
+ code;
56424
+ details;
56425
+ constructor(code, message, details = {}) {
56426
+ super(message);
56427
+ this.code = code;
56428
+ this.details = details;
56429
+ this.name = "MemoryProjectLinkError";
56430
+ }
56431
+ }
56432
+ var LINK_AUTHORITY = {
56433
+ authority_id: "mementos",
56434
+ tenant_id: "default",
56435
+ corpus_id: "default"
56436
+ };
56437
+ var BOUNDED_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
56438
+ function canonicalize(value) {
56439
+ if (Array.isArray(value))
56440
+ return value.map(canonicalize);
56441
+ if (!value || typeof value !== "object")
56442
+ return value;
56443
+ const output = {};
56444
+ for (const key of Object.keys(value).sort()) {
56445
+ const item = value[key];
56446
+ if (item !== undefined)
56447
+ output[key] = canonicalize(item);
56448
+ }
56449
+ return output;
56450
+ }
56451
+ function canonicalJson(value) {
56452
+ return JSON.stringify(canonicalize(value));
56453
+ }
56454
+ function digest(value) {
56455
+ return createHash("sha256").update(canonicalJson(value)).digest("hex");
56456
+ }
56457
+ function timestampString(value) {
56458
+ return value instanceof Date ? value.toISOString() : String(value);
56459
+ }
56460
+ function nullableTimestamp(value) {
56461
+ return value === null || value === undefined ? null : timestampString(value);
56462
+ }
56463
+ function normalizeMemoryTimestamps(memory) {
56464
+ return {
56465
+ ...memory,
56466
+ created_at: timestampString(memory.created_at),
56467
+ updated_at: timestampString(memory.updated_at),
56468
+ accessed_at: nullableTimestamp(memory.accessed_at),
56469
+ expires_at: nullableTimestamp(memory.expires_at),
56470
+ valid_from: nullableTimestamp(memory.valid_from),
56471
+ valid_until: nullableTimestamp(memory.valid_until),
56472
+ ingested_at: nullableTimestamp(memory.ingested_at)
56473
+ };
56474
+ }
56475
+ function parseProjectRow(row) {
56476
+ return {
56477
+ id: String(row["id"]),
56478
+ name: String(row["name"]),
56479
+ path: String(row["path"]),
56480
+ description: row["description"] === null ? null : String(row["description"] ?? "") || null,
56481
+ memory_prefix: row["memory_prefix"] === null ? null : String(row["memory_prefix"] ?? "") || null,
56482
+ created_at: timestampString(row["created_at"]),
56483
+ updated_at: timestampString(row["updated_at"])
56484
+ };
56485
+ }
56486
+ function memoryMutationState(memory) {
56487
+ const { access_count: _accessCount, accessed_at: _accessedAt, ...state } = memory;
56488
+ return state;
56489
+ }
56490
+ function memoryDigest(memory) {
56491
+ return digest(memoryMutationState(memory));
56492
+ }
56493
+ function projectDigest(project) {
56494
+ return digest(project);
56495
+ }
56496
+ function snapshot(memory) {
56497
+ return {
56498
+ memory_id: memory.id,
56499
+ project_id: memory.project_id,
56500
+ memory_version: memory.version,
56501
+ memory_revision: timestampString(memory.updated_at),
56502
+ memory_digest: memoryDigest(memory)
56503
+ };
56504
+ }
56505
+ function parseSnapshot(value) {
56506
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
56507
+ return parsed;
56508
+ }
56509
+ function receiptFromRow(row) {
56510
+ return {
56511
+ receipt_id: String(row["receipt_id"]),
56512
+ authority: "mementos",
56513
+ route: MEMENTOS_MEMORY_PROJECT_LINK_ROUTE,
56514
+ package_version: String(row["package_version"]),
56515
+ authority_id: String(row["authority_id"]),
56516
+ tenant_id: String(row["tenant_id"]),
56517
+ corpus_id: String(row["corpus_id"]),
56518
+ operation_id: String(row["operation_id"]),
56519
+ step_id: String(row["step_id"]),
56520
+ direction: row["direction"],
56521
+ idempotency_key: String(row["idempotency_key"]),
56522
+ request_digest: String(row["request_digest"]),
56523
+ outcome: row["outcome"],
56524
+ target_memory_id: String(row["target_memory_id"]),
56525
+ requested_project_id: String(row["requested_project_id"]),
56526
+ expected_memory_version: Number(row["expected_memory_version"]),
56527
+ expected_memory_revision: timestampString(row["expected_memory_revision"]),
56528
+ expected_project_revision: nullableTimestamp(row["expected_project_revision"]),
56529
+ result_memory_version: Number(row["result_memory_version"]),
56530
+ result_memory_revision: timestampString(row["result_memory_revision"]),
56531
+ result_memory_digest: String(row["result_memory_digest"]),
56532
+ result_project_revision: nullableTimestamp(row["result_project_revision"]),
56533
+ result_project_digest: row["result_project_digest"] === null ? null : String(row["result_project_digest"]),
56534
+ accepted_receipt_id: row["accepted_receipt_id"] === null ? null : String(row["accepted_receipt_id"]),
56535
+ before_link: parseSnapshot(row["before_link_json"]),
56536
+ after_link: parseSnapshot(row["after_link_json"]),
56537
+ before_project_revision: nullableTimestamp(row["before_project_revision"]),
56538
+ before_project_digest: row["before_project_digest"] === null ? null : String(row["before_project_digest"]),
56539
+ after_project_revision: nullableTimestamp(row["after_project_revision"]),
56540
+ after_project_digest: row["after_project_digest"] === null ? null : String(row["after_project_digest"]),
56541
+ created_at: timestampString(row["created_at"])
56542
+ };
56543
+ }
56544
+ function assertIdentity(identity) {
56545
+ if (identity.authority_id !== LINK_AUTHORITY.authority_id || identity.tenant_id !== LINK_AUTHORITY.tenant_id || identity.corpus_id !== LINK_AUTHORITY.corpus_id) {
56546
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_AUTHORITY_MISMATCH", "memory project link does not match this authority, tenant, and corpus");
56547
+ }
56548
+ }
56549
+ function assertBoundedIdentifier(value, field) {
56550
+ if (!BOUNDED_IDENTIFIER.test(value)) {
56551
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", `${field} must be an 8-128 character bounded identifier`);
56552
+ }
56553
+ }
56554
+ function assertMemoryGuard(version, revision) {
56555
+ if (!Number.isSafeInteger(version) || version < 1) {
56556
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", "expected_memory_version must be a positive safe integer");
56557
+ }
56558
+ if (!revision || revision.length > 128) {
56559
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", "expected_memory_revision is required and must be bounded");
56560
+ }
56561
+ }
56562
+ function assertForwardRequest(request) {
56563
+ assertIdentity(request);
56564
+ assertBoundedIdentifier(request.operation_id, "operation_id");
56565
+ assertBoundedIdentifier(request.step_id, "step_id");
56566
+ assertBoundedIdentifier(request.idempotency_key, "idempotency_key");
56567
+ assertBoundedIdentifier(request.target_project_id, "target_project_id");
56568
+ assertMemoryGuard(request.expected_memory_version, request.expected_memory_revision);
56569
+ if (!request.expected_project_revision || request.expected_project_revision.length > 128) {
56570
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_INVALID_INPUT", "expected_project_revision is required and must be bounded");
56571
+ }
56572
+ }
56573
+ function assertRollbackRequest(request) {
56574
+ assertIdentity(request);
56575
+ assertBoundedIdentifier(request.operation_id, "operation_id");
56576
+ assertBoundedIdentifier(request.step_id, "step_id");
56577
+ assertBoundedIdentifier(request.idempotency_key, "idempotency_key");
56578
+ assertBoundedIdentifier(request.accepted_receipt_id, "accepted_receipt_id");
56579
+ assertMemoryGuard(request.expected_memory_version, request.expected_memory_revision);
56580
+ }
56581
+ function forwardRequestDigest(memoryId, request) {
56582
+ return digest({
56583
+ authority_id: request.authority_id,
56584
+ tenant_id: request.tenant_id,
56585
+ corpus_id: request.corpus_id,
56586
+ operation_id: request.operation_id,
56587
+ step_id: request.step_id,
56588
+ idempotency_key: request.idempotency_key,
56589
+ expected_memory_version: request.expected_memory_version,
56590
+ expected_memory_revision: request.expected_memory_revision,
56591
+ target_project_id: request.target_project_id,
56592
+ expected_project_revision: request.expected_project_revision,
56593
+ direction: "forward",
56594
+ target_memory_id: memoryId
56595
+ });
56596
+ }
56597
+ function rollbackRequestDigest(memoryId, request) {
56598
+ return digest({
56599
+ authority_id: request.authority_id,
56600
+ tenant_id: request.tenant_id,
56601
+ corpus_id: request.corpus_id,
56602
+ operation_id: request.operation_id,
56603
+ step_id: request.step_id,
56604
+ idempotency_key: request.idempotency_key,
56605
+ expected_memory_version: request.expected_memory_version,
56606
+ expected_memory_revision: request.expected_memory_revision,
56607
+ accepted_receipt_id: request.accepted_receipt_id,
56608
+ direction: "rollback",
56609
+ target_memory_id: memoryId
56610
+ });
56611
+ }
56612
+ function forUpdateSuffix(db, lock) {
56613
+ return lock && db instanceof PgAdapter ? " FOR UPDATE" : "";
56614
+ }
56615
+ function getMemoryByExactId(id, db, lock = false) {
56616
+ const row = db.query(`SELECT * FROM memories WHERE id = ? LIMIT 1${forUpdateSuffix(db, lock)}`).get(id);
56617
+ return row ? normalizeMemoryTimestamps(parseMemoryRow(row)) : null;
56618
+ }
56619
+ function getProjectByExactId(id, db, lock = false) {
56620
+ const row = db.query(`SELECT * FROM projects WHERE id = ? LIMIT 1${forUpdateSuffix(db, lock)}`).get(id);
56621
+ return row ? parseProjectRow(row) : null;
56622
+ }
56623
+ function lockProjects(ids, db) {
56624
+ const projects = new Map;
56625
+ const uniqueIds = [...new Set(ids.filter((id) => Boolean(id)))].sort();
56626
+ for (const id of uniqueIds) {
56627
+ const project = getProjectByExactId(id, db, true);
56628
+ if (!project) {
56629
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_PROJECT_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
56630
+ }
56631
+ projects.set(id, project);
56632
+ }
56633
+ return projects;
56634
+ }
56635
+ function assertMemoryPrecondition(memory, expectedVersion, expectedRevision) {
56636
+ if (memory.version !== expectedVersion || memory.updated_at !== expectedRevision) {
56637
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory changed before the guarded project link", {
56638
+ expected_memory_version: expectedVersion,
56639
+ current_memory_version: memory.version,
56640
+ expected_memory_revision: expectedRevision,
56641
+ current_memory_revision: memory.updated_at
56642
+ });
56643
+ }
56644
+ }
56645
+ function assertProjectPrecondition(project, expectedRevision) {
56646
+ if (project.updated_at !== expectedRevision) {
56647
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_PROJECT", "Project changed before the guarded memory link", {
56648
+ expected_project_revision: expectedRevision,
56649
+ current_project_revision: project.updated_at
56650
+ });
56651
+ }
56652
+ }
56653
+ function assertNoTargetBucketCollision(memory, projectId, db) {
56654
+ const collision = db.query(`
56655
+ SELECT id FROM memories
56656
+ WHERE id != ? AND key = ? AND scope = ?
56657
+ AND COALESCE(agent_id, '') = COALESCE(?, '')
56658
+ AND COALESCE(project_id, '') = COALESCE(?, '')
56659
+ AND COALESCE(session_id, '') = COALESCE(?, '')
56660
+ LIMIT 1
56661
+ `).get(memory.id, memory.key, memory.scope, memory.agent_id, projectId, memory.session_id);
56662
+ if (collision) {
56663
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_COLLISION", "Linking this memory would collide with another memory in the target project bucket", { collision_id: collision.id });
56664
+ }
56665
+ }
56666
+ function nextRevision(previous) {
56667
+ const candidate = now();
56668
+ const previousMs = Date.parse(previous);
56669
+ const candidateMs = Date.parse(candidate);
56670
+ if (Number.isFinite(previousMs) && Number.isFinite(candidateMs) && candidateMs <= previousMs) {
56671
+ return new Date(previousMs + 1).toISOString();
56672
+ }
56673
+ return candidate;
56674
+ }
56675
+ function findReceiptByKey(db, identity, direction, idempotencyKey) {
56676
+ const row = db.query(`
56677
+ SELECT * FROM mementos_memory_project_link_receipts
56678
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
56679
+ AND direction = ? AND idempotency_key = ?
56680
+ LIMIT 1
56681
+ `).get(identity.authority_id, identity.tenant_id, identity.corpus_id, direction, idempotencyKey);
56682
+ return row ? receiptFromRow(row) : null;
56683
+ }
56684
+ function findReceiptById(db, identity, receiptId) {
56685
+ const row = db.query(`
56686
+ SELECT * FROM mementos_memory_project_link_receipts
56687
+ WHERE receipt_id = ? AND authority_id = ? AND tenant_id = ? AND corpus_id = ?
56688
+ LIMIT 1
56689
+ `).get(receiptId, identity.authority_id, identity.tenant_id, identity.corpus_id);
56690
+ return row ? receiptFromRow(row) : null;
56691
+ }
56692
+ function insertReceipt(db, receipt) {
56693
+ db.run(`
56694
+ INSERT INTO mementos_memory_project_link_receipts (
56695
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
56696
+ corpus_id, operation_id, step_id, direction, idempotency_key,
56697
+ request_digest, outcome, target_memory_id, requested_project_id,
56698
+ expected_memory_version, expected_memory_revision,
56699
+ expected_project_revision, result_memory_version,
56700
+ result_memory_revision, result_memory_digest, result_project_revision,
56701
+ result_project_digest, accepted_receipt_id, before_link_json,
56702
+ after_link_json, before_project_revision, before_project_digest,
56703
+ after_project_revision, after_project_digest, created_at
56704
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
56705
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
56706
+ `, [
56707
+ receipt.receipt_id,
56708
+ receipt.authority,
56709
+ receipt.route,
56710
+ receipt.package_version,
56711
+ receipt.authority_id,
56712
+ receipt.tenant_id,
56713
+ receipt.corpus_id,
56714
+ receipt.operation_id,
56715
+ receipt.step_id,
56716
+ receipt.direction,
56717
+ receipt.idempotency_key,
56718
+ receipt.request_digest,
56719
+ receipt.outcome,
56720
+ receipt.target_memory_id,
56721
+ receipt.requested_project_id,
56722
+ receipt.expected_memory_version,
56723
+ receipt.expected_memory_revision,
56724
+ receipt.expected_project_revision,
56725
+ receipt.result_memory_version,
56726
+ receipt.result_memory_revision,
56727
+ receipt.result_memory_digest,
56728
+ receipt.result_project_revision,
56729
+ receipt.result_project_digest,
56730
+ receipt.accepted_receipt_id,
56731
+ canonicalJson(receipt.before_link),
56732
+ canonicalJson(receipt.after_link),
56733
+ receipt.before_project_revision,
56734
+ receipt.before_project_digest,
56735
+ receipt.after_project_revision,
56736
+ receipt.after_project_digest,
56737
+ receipt.created_at
56738
+ ]);
56739
+ }
56740
+ function makeReceipt(input) {
56741
+ const beforeLink = snapshot(input.beforeMemory);
56742
+ const afterLink = snapshot(input.afterMemory);
56743
+ const logical = {
56744
+ authority: "mementos",
56745
+ route: MEMENTOS_MEMORY_PROJECT_LINK_ROUTE,
56746
+ package_version: getMementosPackageVersion(),
56747
+ authority_id: input.request.authority_id,
56748
+ tenant_id: input.request.tenant_id,
56749
+ corpus_id: input.request.corpus_id,
56750
+ operation_id: input.request.operation_id,
56751
+ step_id: input.request.step_id,
56752
+ direction: input.direction,
56753
+ idempotency_key: input.request.idempotency_key,
56754
+ request_digest: input.requestDigest,
56755
+ outcome: input.outcome,
56756
+ target_memory_id: input.targetMemoryId,
56757
+ requested_project_id: input.requestedProjectId,
56758
+ expected_memory_version: input.request.expected_memory_version,
56759
+ expected_memory_revision: input.request.expected_memory_revision,
56760
+ expected_project_revision: input.expectedProjectRevision,
56761
+ result_memory_version: afterLink.memory_version,
56762
+ result_memory_revision: afterLink.memory_revision,
56763
+ result_memory_digest: afterLink.memory_digest,
56764
+ result_project_revision: input.afterProject?.updated_at ?? null,
56765
+ result_project_digest: input.afterProject ? projectDigest(input.afterProject) : null,
56766
+ accepted_receipt_id: input.acceptedReceiptId ?? null,
56767
+ before_link: beforeLink,
56768
+ after_link: afterLink,
56769
+ before_project_revision: input.beforeProject?.updated_at ?? null,
56770
+ before_project_digest: input.beforeProject ? projectDigest(input.beforeProject) : null,
56771
+ after_project_revision: input.afterProject?.updated_at ?? null,
56772
+ after_project_digest: input.afterProject ? projectDigest(input.afterProject) : null
56773
+ };
56774
+ return {
56775
+ receipt_id: `mmpl_${digest(logical).slice(0, 40)}`,
56776
+ ...logical,
56777
+ created_at: now()
56778
+ };
56779
+ }
56780
+ function sameSnapshot(memory, expected) {
56781
+ return canonicalJson(snapshot(memory)) === canonicalJson(expected);
56782
+ }
56783
+ function assertProjectSnapshot(project, revision, expectedDigest) {
56784
+ if (!revision && !expectedDigest) {
56785
+ if (project) {
56786
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "receipt expected no linked project but a project row was resolved");
56787
+ }
56788
+ return;
56789
+ }
56790
+ if (!project || project.updated_at !== revision || projectDigest(project) !== expectedDigest) {
56791
+ 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 });
56792
+ }
56793
+ }
56794
+ function replayResult(db, receipt, requestDigest, memoryId) {
56795
+ if (receipt.request_digest !== requestDigest || receipt.target_memory_id !== memoryId) {
56796
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_IDEMPOTENCY_MISMATCH", "caller idempotency key is already bound to a different memory project-link request");
56797
+ }
56798
+ const memory = getMemoryByExactId(memoryId, db);
56799
+ if (!memory || !sameSnapshot(memory, receipt.after_link)) {
56800
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "accepted memory project-link target drifted after its immutable receipt");
56801
+ }
56802
+ const project = receipt.after_link.project_id ? getProjectByExactId(receipt.after_link.project_id, db) : null;
56803
+ assertProjectSnapshot(project, receipt.after_project_revision, receipt.after_project_digest);
56804
+ return {
56805
+ dry_run: false,
56806
+ applied: receipt.outcome === "accepted",
56807
+ no_change: receipt.outcome === "no_change",
56808
+ memory,
56809
+ project,
56810
+ receipt
56811
+ };
56812
+ }
56813
+ function previewMemoryProjectLink(memoryId, request, db) {
56814
+ assertForwardRequest(request);
56815
+ assertBoundedIdentifier(memoryId, "memory_id");
56816
+ if (!db && isApiMode()) {
56817
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/guarded-project-link`, { ...request, dry_run: true });
56818
+ return data;
56819
+ }
56820
+ const d = db || getDatabase();
56821
+ const memory = getMemoryByExactId(memoryId, d);
56822
+ if (!memory) {
56823
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_MEMORY_NOT_FOUND", `Memory not found by exact stable ID: ${memoryId}`);
56824
+ }
56825
+ const project = getProjectByExactId(request.target_project_id, d);
56826
+ if (!project) {
56827
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_PROJECT_NOT_FOUND", `Project not found by exact stable ID: ${request.target_project_id}`);
56828
+ }
56829
+ assertMemoryPrecondition(memory, request.expected_memory_version, request.expected_memory_revision);
56830
+ assertProjectPrecondition(project, request.expected_project_revision);
56831
+ const noChange = memory.project_id === project.id;
56832
+ if (!noChange)
56833
+ assertNoTargetBucketCollision(memory, project.id, d);
56834
+ return {
56835
+ dry_run: true,
56836
+ applied: false,
56837
+ no_change: noChange,
56838
+ memory: noChange ? memory : { ...memory, project_id: project.id },
56839
+ project,
56840
+ receipt: null
56841
+ };
56842
+ }
56843
+ function applyMemoryProjectLink(memoryId, request, db) {
56844
+ assertForwardRequest(request);
56845
+ assertBoundedIdentifier(memoryId, "memory_id");
56846
+ if (!db && isApiMode()) {
56847
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/guarded-project-link`, { ...request, dry_run: false });
56848
+ return data;
56849
+ }
56850
+ const d = db || getDatabase();
56851
+ const requestDigest = forwardRequestDigest(memoryId, request);
56852
+ return d.transaction(() => {
56853
+ const prior = findReceiptByKey(d, request, "forward", request.idempotency_key);
56854
+ if (prior)
56855
+ return replayResult(d, prior, requestDigest, memoryId);
56856
+ const before = getMemoryByExactId(memoryId, d, true);
56857
+ if (!before) {
56858
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_MEMORY_NOT_FOUND", `Memory not found by exact stable ID: ${memoryId}`);
56859
+ }
56860
+ const committedPrior = findReceiptByKey(d, request, "forward", request.idempotency_key);
56861
+ if (committedPrior)
56862
+ return replayResult(d, committedPrior, requestDigest, memoryId);
56863
+ assertMemoryPrecondition(before, request.expected_memory_version, request.expected_memory_revision);
56864
+ const projects = lockProjects([before.project_id, request.target_project_id], d);
56865
+ const targetProject = projects.get(request.target_project_id);
56866
+ const beforeProject = before.project_id ? projects.get(before.project_id) : null;
56867
+ assertProjectPrecondition(targetProject, request.expected_project_revision);
56868
+ const noChange = before.project_id === request.target_project_id;
56869
+ if (noChange) {
56870
+ const receipt2 = makeReceipt({
56871
+ request,
56872
+ direction: "forward",
56873
+ requestDigest,
56874
+ outcome: "no_change",
56875
+ targetMemoryId: memoryId,
56876
+ requestedProjectId: request.target_project_id,
56877
+ expectedProjectRevision: request.expected_project_revision,
56878
+ beforeMemory: before,
56879
+ afterMemory: before,
56880
+ beforeProject,
56881
+ afterProject: targetProject
56882
+ });
56883
+ insertReceipt(d, receipt2);
56884
+ return {
56885
+ dry_run: false,
56886
+ applied: false,
56887
+ no_change: true,
56888
+ memory: before,
56889
+ project: targetProject,
56890
+ receipt: receipt2
56891
+ };
56892
+ }
56893
+ assertNoTargetBucketCollision(before, targetProject.id, d);
56894
+ const revision = nextRevision(before.updated_at);
56895
+ const result = d.run(`
56896
+ UPDATE memories
56897
+ SET project_id = ?, updated_at = ?
56898
+ WHERE id = ? AND version = ? AND updated_at = ?
56899
+ AND COALESCE(project_id, '') = COALESCE(?, '')
56900
+ `, [
56901
+ targetProject.id,
56902
+ revision,
56903
+ memoryId,
56904
+ before.version,
56905
+ before.updated_at,
56906
+ before.project_id
56907
+ ]);
56908
+ if (result.changes === 0) {
56909
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory project-link compare-and-swap did not update exactly one row");
56910
+ }
56911
+ const after = getMemoryByExactId(memoryId, d);
56912
+ const expectedAfter = { ...before, project_id: targetProject.id, updated_at: revision };
56913
+ if (!after || canonicalJson(memoryMutationState(after)) !== canonicalJson(memoryMutationState(expectedAfter))) {
56914
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "Memory project link did not read back exactly under the stable ID");
56915
+ }
56916
+ const receipt = makeReceipt({
56917
+ request,
56918
+ direction: "forward",
56919
+ requestDigest,
56920
+ outcome: "accepted",
56921
+ targetMemoryId: memoryId,
56922
+ requestedProjectId: request.target_project_id,
56923
+ expectedProjectRevision: request.expected_project_revision,
56924
+ beforeMemory: before,
56925
+ afterMemory: after,
56926
+ beforeProject,
56927
+ afterProject: targetProject
56928
+ });
56929
+ insertReceipt(d, receipt);
56930
+ return {
56931
+ dry_run: false,
56932
+ applied: true,
56933
+ no_change: false,
56934
+ memory: after,
56935
+ project: targetProject,
56936
+ receipt
56937
+ };
56938
+ });
56939
+ }
56940
+ function rollbackMemoryProjectLink(memoryId, request, db) {
56941
+ assertRollbackRequest(request);
56942
+ assertBoundedIdentifier(memoryId, "memory_id");
56943
+ if (!db && isApiMode()) {
56944
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/guarded-project-link-rollback`, request);
56945
+ return data;
56946
+ }
56947
+ const d = db || getDatabase();
56948
+ const requestDigest = rollbackRequestDigest(memoryId, request);
56949
+ return d.transaction(() => {
56950
+ const prior = findReceiptByKey(d, request, "rollback", request.idempotency_key);
56951
+ if (prior)
56952
+ return replayResult(d, prior, requestDigest, memoryId);
56953
+ const accepted = findReceiptById(d, request, request.accepted_receipt_id);
56954
+ if (!accepted || accepted.direction !== "forward" || accepted.target_memory_id !== memoryId) {
56955
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_RECEIPT_NOT_FOUND", "accepted forward link receipt was not found for this exact memory");
56956
+ }
56957
+ if (accepted.outcome !== "accepted") {
56958
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_RECEIPT_NOT_ROLLBACKABLE", "a no-change memory project-link receipt has no mutation to roll back");
56959
+ }
56960
+ const current = getMemoryByExactId(memoryId, d, true);
56961
+ if (!current) {
56962
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_MEMORY_NOT_FOUND", `Memory not found by exact stable ID: ${memoryId}`);
56963
+ }
56964
+ const committedPrior = findReceiptByKey(d, request, "rollback", request.idempotency_key);
56965
+ if (committedPrior)
56966
+ return replayResult(d, committedPrior, requestDigest, memoryId);
56967
+ assertMemoryPrecondition(current, request.expected_memory_version, request.expected_memory_revision);
56968
+ if (!sameSnapshot(current, accepted.after_link)) {
56969
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory no longer matches the accepted forward link receipt");
56970
+ }
56971
+ const projects = lockProjects([accepted.after_link.project_id, accepted.before_link.project_id], d);
56972
+ const beforeProject = accepted.after_link.project_id ? projects.get(accepted.after_link.project_id) : null;
56973
+ const afterProject = accepted.before_link.project_id ? projects.get(accepted.before_link.project_id) : null;
56974
+ assertProjectSnapshot(beforeProject, accepted.after_project_revision, accepted.after_project_digest);
56975
+ assertProjectSnapshot(afterProject, accepted.before_project_revision, accepted.before_project_digest);
56976
+ assertNoTargetBucketCollision(current, accepted.before_link.project_id, d);
56977
+ const result = d.run(`
56978
+ UPDATE memories
56979
+ SET project_id = ?, updated_at = ?
56980
+ WHERE id = ? AND version = ? AND updated_at = ?
56981
+ AND COALESCE(project_id, '') = COALESCE(?, '')
56982
+ `, [
56983
+ accepted.before_link.project_id,
56984
+ accepted.before_link.memory_revision,
56985
+ memoryId,
56986
+ current.version,
56987
+ current.updated_at,
56988
+ current.project_id
56989
+ ]);
56990
+ if (result.changes === 0) {
56991
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_STALE_MEMORY", "Memory project-link rollback compare-and-swap did not update exactly one row");
56992
+ }
56993
+ const restored = getMemoryByExactId(memoryId, d);
56994
+ if (!restored || !sameSnapshot(restored, accepted.before_link)) {
56995
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_ACCEPTED_TARGET_DRIFTED", "Memory project-link rollback did not restore the exact prior linkage");
56996
+ }
56997
+ const receipt = makeReceipt({
56998
+ request,
56999
+ direction: "rollback",
57000
+ requestDigest,
57001
+ outcome: "accepted",
57002
+ targetMemoryId: memoryId,
57003
+ requestedProjectId: accepted.requested_project_id,
57004
+ expectedProjectRevision: null,
57005
+ beforeMemory: current,
57006
+ afterMemory: restored,
57007
+ beforeProject,
57008
+ afterProject,
57009
+ acceptedReceiptId: accepted.receipt_id
57010
+ });
57011
+ insertReceipt(d, receipt);
57012
+ return {
57013
+ dry_run: false,
57014
+ applied: true,
57015
+ no_change: false,
57016
+ memory: restored,
57017
+ project: afterProject,
57018
+ receipt
57019
+ };
57020
+ });
57021
+ }
57022
+ function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = LINK_AUTHORITY, db) {
57023
+ assertIdentity(identity);
57024
+ assertBoundedIdentifier(memoryId, "memory_id");
57025
+ assertBoundedIdentifier(receiptId, "receipt_id");
57026
+ if (!db && isApiMode()) {
57027
+ const { data } = apiJson("POST", `/memories/${encodeURIComponent(memoryId)}/project-link-receipts/lookup`, { ...identity, receipt_id: receiptId });
57028
+ return data;
57029
+ }
57030
+ const d = db || getDatabase();
57031
+ const receipt = findReceiptById(d, identity, receiptId);
57032
+ if (!receipt || receipt.target_memory_id !== memoryId) {
57033
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_RECEIPT_NOT_FOUND", "immutable memory project-link receipt was not found for this exact memory");
57034
+ }
57035
+ return receipt;
57036
+ }
57037
+
57038
+ // src/server/routes/memory-project-link.ts
57039
+ init_router();
57040
+ function linkError(error) {
57041
+ const status = error.code === "MEMORY_PROJECT_LINK_AUTHORITY_MISMATCH" ? 403 : error.code === "MEMORY_PROJECT_LINK_MEMORY_NOT_FOUND" || error.code === "MEMORY_PROJECT_LINK_PROJECT_NOT_FOUND" || error.code === "MEMORY_PROJECT_LINK_RECEIPT_NOT_FOUND" ? 404 : error.code === "MEMORY_PROJECT_LINK_INVALID_INPUT" ? 400 : 409;
57042
+ return errorResponse(error.message, status, { code: error.code, ...error.details });
57043
+ }
57044
+ addRoute("POST", "/api/memories/:id/guarded-project-link", async (req, _url, params) => {
57045
+ const body = await readJson(req);
57046
+ if (!body)
57047
+ return errorResponse("Invalid JSON body", 400);
57048
+ try {
57049
+ const request = body;
57050
+ return json(request.dry_run ? previewMemoryProjectLink(params["id"], request) : applyMemoryProjectLink(params["id"], request));
57051
+ } catch (error) {
57052
+ if (error instanceof MemoryProjectLinkError)
57053
+ return linkError(error);
57054
+ throw error;
57055
+ }
57056
+ });
57057
+ addRoute("POST", "/api/memories/:id/guarded-project-link-rollback", async (req, _url, params) => {
57058
+ const body = await readJson(req);
57059
+ if (!body)
57060
+ return errorResponse("Invalid JSON body", 400);
57061
+ try {
57062
+ return json(rollbackMemoryProjectLink(params["id"], body));
57063
+ } catch (error) {
57064
+ if (error instanceof MemoryProjectLinkError)
57065
+ return linkError(error);
57066
+ throw error;
57067
+ }
57068
+ });
57069
+ addRoute("POST", "/api/memories/:id/project-link-receipts/lookup", async (req, _url, params) => {
57070
+ const body = await readJson(req);
57071
+ if (!body || typeof body["receipt_id"] !== "string") {
57072
+ return errorResponse("receipt_id is required", 400);
57073
+ }
57074
+ try {
57075
+ const identity = {
57076
+ authority_id: String(body["authority_id"] ?? ""),
57077
+ tenant_id: String(body["tenant_id"] ?? ""),
57078
+ corpus_id: String(body["corpus_id"] ?? "")
57079
+ };
57080
+ return json(getMemoryProjectLinkReceipt(params["id"], body["receipt_id"], identity));
57081
+ } catch (error) {
57082
+ if (error instanceof MemoryProjectLinkError)
57083
+ return linkError(error);
57084
+ throw error;
57085
+ }
57086
+ });
57087
+
56178
57088
  // src/db/agents.ts
56179
57089
  init_types();
56180
57090
  init_database();
@@ -56596,7 +57506,26 @@ addRoute("POST", "/api/locks/clean", () => {
56596
57506
  // src/db/projects.ts
56597
57507
  init_database();
56598
57508
  init_api_mode();
56599
- function parseProjectRow(row) {
57509
+ import { createHash as createHash2 } from "crypto";
57510
+
57511
+ // src/project-registration/types.ts
57512
+ var MEMENTOS_PROJECT_REGISTRATION_ROUTE = "mementos.project-registration.v1";
57513
+ var MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE = "projects.full-registration.v1";
57514
+ var MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE = "mementos.project-guarded-update.v1";
57515
+
57516
+ class MementosProjectRegistrationError extends Error {
57517
+ code;
57518
+ details;
57519
+ constructor(code, message, details = {}) {
57520
+ super(message);
57521
+ this.code = code;
57522
+ this.details = details;
57523
+ this.name = "MementosProjectRegistrationError";
57524
+ }
57525
+ }
57526
+
57527
+ // src/db/projects.ts
57528
+ function parseProjectRow2(row) {
56600
57529
  return {
56601
57530
  id: row["id"],
56602
57531
  name: row["name"],
@@ -56607,6 +57536,229 @@ function parseProjectRow(row) {
56607
57536
  updated_at: row["updated_at"]
56608
57537
  };
56609
57538
  }
57539
+ class ProjectGuardedUpdateError extends Error {
57540
+ code;
57541
+ details;
57542
+ constructor(code, message, details = {}) {
57543
+ super(message);
57544
+ this.code = code;
57545
+ this.details = details;
57546
+ this.name = "ProjectGuardedUpdateError";
57547
+ }
57548
+ }
57549
+ var PROJECT_UPDATE_AUTHORITY = {
57550
+ authority_id: "mementos",
57551
+ tenant_id: "default",
57552
+ corpus_id: "default"
57553
+ };
57554
+ var BOUNDED_IDENTIFIER2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
57555
+ function canonicalizeProjectUpdateValue(value) {
57556
+ if (Array.isArray(value))
57557
+ return value.map(canonicalizeProjectUpdateValue);
57558
+ if (!value || typeof value !== "object")
57559
+ return value;
57560
+ const output = {};
57561
+ for (const key of Object.keys(value).sort()) {
57562
+ const item = value[key];
57563
+ if (item !== undefined)
57564
+ output[key] = canonicalizeProjectUpdateValue(item);
57565
+ }
57566
+ return output;
57567
+ }
57568
+ function canonicalProjectUpdateJson(value) {
57569
+ return JSON.stringify(canonicalizeProjectUpdateValue(value));
57570
+ }
57571
+ function digestProjectUpdateValue(value) {
57572
+ return createHash2("sha256").update(canonicalProjectUpdateJson(value)).digest("hex");
57573
+ }
57574
+ function timestampString2(value) {
57575
+ return value instanceof Date ? value.toISOString() : String(value);
57576
+ }
57577
+ function parseProjectJson(value) {
57578
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
57579
+ return parsed;
57580
+ }
57581
+ function projectUpdateReceiptFromRow(row) {
57582
+ return {
57583
+ receipt_id: String(row["receipt_id"]),
57584
+ authority: "mementos",
57585
+ route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
57586
+ package_version: String(row["package_version"]),
57587
+ authority_id: String(row["authority_id"]),
57588
+ tenant_id: String(row["tenant_id"]),
57589
+ corpus_id: String(row["corpus_id"]),
57590
+ operation_id: String(row["operation_id"]),
57591
+ step_id: String(row["step_id"]),
57592
+ direction: row["direction"],
57593
+ idempotency_key: String(row["idempotency_key"]),
57594
+ request_digest: String(row["request_digest"]),
57595
+ outcome: "accepted",
57596
+ target_id: String(row["target_id"]),
57597
+ expected_revision: timestampString2(row["expected_revision"]),
57598
+ result_revision: timestampString2(row["result_revision"]),
57599
+ result_digest: String(row["result_digest"]),
57600
+ accepted_receipt_id: row["accepted_receipt_id"] === null ? null : String(row["accepted_receipt_id"]),
57601
+ before_project: parseProjectJson(row["before_project_json"]),
57602
+ after_project: parseProjectJson(row["after_project_json"]),
57603
+ created_at: timestampString2(row["created_at"])
57604
+ };
57605
+ }
57606
+ function normalizeProjectUpdateInput(input) {
57607
+ const normalized = {};
57608
+ if (input.name !== undefined)
57609
+ normalized.name = input.name.trim();
57610
+ if (input.path !== undefined)
57611
+ normalized.path = input.path.trim();
57612
+ if (input.description !== undefined)
57613
+ normalized.description = input.description;
57614
+ if (input.memory_prefix !== undefined)
57615
+ normalized.memory_prefix = input.memory_prefix;
57616
+ if (Object.keys(normalized).length === 0) {
57617
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "At least one project field must be provided");
57618
+ }
57619
+ if (normalized.name !== undefined && normalized.name.length === 0) {
57620
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "Project name cannot be empty");
57621
+ }
57622
+ if (normalized.path !== undefined && normalized.path.length === 0) {
57623
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "Project path cannot be empty");
57624
+ }
57625
+ return normalized;
57626
+ }
57627
+ function assertProjectUpdateIdentity(identity) {
57628
+ 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) {
57629
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_AUTHORITY_MISMATCH", "guarded project update does not match this authority, tenant, and corpus");
57630
+ }
57631
+ }
57632
+ function assertBoundedIdentifier2(value, field) {
57633
+ if (!BOUNDED_IDENTIFIER2.test(value)) {
57634
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", `${field} must be an 8-128 character bounded identifier`);
57635
+ }
57636
+ }
57637
+ function assertProjectUpdateRequest(request) {
57638
+ assertProjectUpdateIdentity(request);
57639
+ assertBoundedIdentifier2(request.operation_id, "operation_id");
57640
+ assertBoundedIdentifier2(request.step_id, "step_id");
57641
+ assertBoundedIdentifier2(request.idempotency_key, "idempotency_key");
57642
+ if (!request.expected_revision || request.expected_revision.length > 128) {
57643
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", "expected_revision is required and must be bounded");
57644
+ }
57645
+ }
57646
+ function getProjectByExactId2(id, db) {
57647
+ const row = db.query("SELECT * FROM projects WHERE id = ? LIMIT 1").get(id);
57648
+ return row ? parseProjectRow2(row) : null;
57649
+ }
57650
+ function assertNoProjectCollision(id, input, db) {
57651
+ if (input.name !== undefined) {
57652
+ const collision = db.query("SELECT id FROM projects WHERE LOWER(name) = LOWER(?) AND id != ? LIMIT 1").get(input.name, id);
57653
+ if (collision) {
57654
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_COLLISION", `Project name already exists: ${input.name}`, { field: "name" });
57655
+ }
57656
+ }
57657
+ if (input.path !== undefined) {
57658
+ const collision = db.query("SELECT id FROM projects WHERE path = ? AND id != ? LIMIT 1").get(input.path, id);
57659
+ if (collision) {
57660
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_COLLISION", `Project path already exists: ${input.path}`, { field: "path" });
57661
+ }
57662
+ }
57663
+ }
57664
+ function nextProjectRevision(previous) {
57665
+ const candidate = now();
57666
+ const previousMs = Date.parse(previous);
57667
+ const candidateMs = Date.parse(candidate);
57668
+ if (Number.isFinite(previousMs) && Number.isFinite(candidateMs) && candidateMs <= previousMs) {
57669
+ return new Date(previousMs + 1).toISOString();
57670
+ }
57671
+ return candidate;
57672
+ }
57673
+ function projectWithUpdates(project, updates, revision) {
57674
+ return {
57675
+ ...project,
57676
+ ...updates.name !== undefined ? { name: updates.name } : {},
57677
+ ...updates.path !== undefined ? { path: updates.path } : {},
57678
+ ...updates.description !== undefined ? { description: updates.description } : {},
57679
+ ...updates.memory_prefix !== undefined ? { memory_prefix: updates.memory_prefix } : {},
57680
+ updated_at: revision
57681
+ };
57682
+ }
57683
+ function findProjectUpdateReceiptByKey(db, identity, direction, idempotencyKey) {
57684
+ const row = db.query(`
57685
+ SELECT * FROM mementos_project_update_receipts
57686
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
57687
+ AND direction = ? AND idempotency_key = ?
57688
+ LIMIT 1
57689
+ `).get(identity.authority_id, identity.tenant_id, identity.corpus_id, direction, idempotencyKey);
57690
+ return row ? projectUpdateReceiptFromRow(row) : null;
57691
+ }
57692
+ function findProjectUpdateReceiptById(db, identity, receiptId) {
57693
+ const row = db.query(`
57694
+ SELECT * FROM mementos_project_update_receipts
57695
+ WHERE receipt_id = ? AND authority_id = ? AND tenant_id = ? AND corpus_id = ?
57696
+ LIMIT 1
57697
+ `).get(receiptId, identity.authority_id, identity.tenant_id, identity.corpus_id);
57698
+ return row ? projectUpdateReceiptFromRow(row) : null;
57699
+ }
57700
+ function insertProjectUpdateReceipt(db, receipt) {
57701
+ db.run(`
57702
+ INSERT INTO mementos_project_update_receipts (
57703
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
57704
+ corpus_id, operation_id, step_id, direction, idempotency_key,
57705
+ request_digest, outcome, target_id, expected_revision, result_revision,
57706
+ result_digest, accepted_receipt_id, before_project_json,
57707
+ after_project_json, created_at
57708
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
57709
+ `, [
57710
+ receipt.receipt_id,
57711
+ receipt.authority,
57712
+ receipt.route,
57713
+ receipt.package_version,
57714
+ receipt.authority_id,
57715
+ receipt.tenant_id,
57716
+ receipt.corpus_id,
57717
+ receipt.operation_id,
57718
+ receipt.step_id,
57719
+ receipt.direction,
57720
+ receipt.idempotency_key,
57721
+ receipt.request_digest,
57722
+ receipt.outcome,
57723
+ receipt.target_id,
57724
+ receipt.expected_revision,
57725
+ receipt.result_revision,
57726
+ receipt.result_digest,
57727
+ receipt.accepted_receipt_id,
57728
+ canonicalProjectUpdateJson(receipt.before_project),
57729
+ canonicalProjectUpdateJson(receipt.after_project),
57730
+ receipt.created_at
57731
+ ]);
57732
+ }
57733
+ function makeProjectUpdateReceipt(input) {
57734
+ const createdAt = now();
57735
+ const logical = {
57736
+ authority: "mementos",
57737
+ route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
57738
+ package_version: getMementosPackageVersion(),
57739
+ authority_id: input.request.authority_id,
57740
+ tenant_id: input.request.tenant_id,
57741
+ corpus_id: input.request.corpus_id,
57742
+ operation_id: input.request.operation_id,
57743
+ step_id: input.request.step_id,
57744
+ direction: input.direction,
57745
+ idempotency_key: input.request.idempotency_key,
57746
+ request_digest: input.request_digest,
57747
+ outcome: "accepted",
57748
+ target_id: input.target_id,
57749
+ expected_revision: input.request.expected_revision,
57750
+ result_revision: input.after_project.updated_at,
57751
+ result_digest: digestProjectUpdateValue(input.after_project),
57752
+ accepted_receipt_id: input.accepted_receipt_id ?? null,
57753
+ before_project: input.before_project,
57754
+ after_project: input.after_project,
57755
+ created_at: createdAt
57756
+ };
57757
+ return {
57758
+ receipt_id: `mpur_${digestProjectUpdateValue(logical).slice(0, 40)}`,
57759
+ ...logical
57760
+ };
57761
+ }
56610
57762
  function registerProject(name, path, description, memoryPrefix, db) {
56611
57763
  if (!db && isApiMode()) {
56612
57764
  const { data } = apiJson("POST", "/projects", {
@@ -56626,7 +57778,7 @@ function registerProject(name, path, description, memoryPrefix, db) {
56626
57778
  timestamp,
56627
57779
  existingId
56628
57780
  ]);
56629
- return parseProjectRow(existing);
57781
+ return parseProjectRow2(existing);
56630
57782
  }
56631
57783
  const id = uuid();
56632
57784
  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]);
@@ -56642,13 +57794,13 @@ function getProject(idOrPath, db) {
56642
57794
  const d = db || getDatabase();
56643
57795
  let row = d.query("SELECT * FROM projects WHERE id = ?").get(idOrPath);
56644
57796
  if (row)
56645
- return parseProjectRow(row);
57797
+ return parseProjectRow2(row);
56646
57798
  row = d.query("SELECT * FROM projects WHERE path = ?").get(idOrPath);
56647
57799
  if (row)
56648
- return parseProjectRow(row);
57800
+ return parseProjectRow2(row);
56649
57801
  row = d.query("SELECT * FROM projects WHERE LOWER(name) = ?").get(idOrPath.toLowerCase());
56650
57802
  if (row)
56651
- return parseProjectRow(row);
57803
+ return parseProjectRow2(row);
56652
57804
  return null;
56653
57805
  }
56654
57806
  function listProjects(db) {
@@ -56658,7 +57810,184 @@ function listProjects(db) {
56658
57810
  }
56659
57811
  const d = db || getDatabase();
56660
57812
  const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
56661
- return rows.map(parseProjectRow);
57813
+ return rows.map(parseProjectRow2);
57814
+ }
57815
+ function previewProjectUpdate(id, request, db) {
57816
+ assertProjectUpdateRequest(request);
57817
+ const normalized = normalizeProjectUpdateInput(request.updates);
57818
+ if (!db && isApiMode()) {
57819
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-update`, { ...request, updates: normalized, dry_run: true });
57820
+ return data;
57821
+ }
57822
+ const d = db || getDatabase();
57823
+ const project = getProjectByExactId2(id, d);
57824
+ if (!project) {
57825
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
57826
+ }
57827
+ if (project.updated_at !== request.expected_revision) {
57828
+ 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 });
57829
+ }
57830
+ assertNoProjectCollision(id, normalized, d);
57831
+ return {
57832
+ dry_run: true,
57833
+ applied: false,
57834
+ project: projectWithUpdates(project, normalized, project.updated_at),
57835
+ receipt: null
57836
+ };
57837
+ }
57838
+ function applyProjectUpdate(id, request, db) {
57839
+ assertProjectUpdateRequest(request);
57840
+ const normalized = normalizeProjectUpdateInput(request.updates);
57841
+ if (!db && isApiMode()) {
57842
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-update`, { ...request, updates: normalized, dry_run: false });
57843
+ return data;
57844
+ }
57845
+ const d = db || getDatabase();
57846
+ const requestDigest = digestProjectUpdateValue({
57847
+ ...request,
57848
+ target_id: id,
57849
+ direction: "forward",
57850
+ updates: normalized
57851
+ });
57852
+ return d.transaction(() => {
57853
+ const prior = findProjectUpdateReceiptByKey(d, request, "forward", request.idempotency_key);
57854
+ if (prior) {
57855
+ if (prior.request_digest !== requestDigest || prior.target_id !== id) {
57856
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_IDEMPOTENCY_MISMATCH", "caller idempotency key is already bound to a different request");
57857
+ }
57858
+ const current = getProjectByExactId2(id, d);
57859
+ if (!current || canonicalProjectUpdateJson(current) !== canonicalProjectUpdateJson(prior.after_project)) {
57860
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "accepted guarded update target drifted after its immutable receipt");
57861
+ }
57862
+ return { dry_run: false, applied: true, project: prior.after_project, receipt: prior };
57863
+ }
57864
+ const before = getProjectByExactId2(id, d);
57865
+ if (!before) {
57866
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
57867
+ }
57868
+ if (before.updated_at !== request.expected_revision) {
57869
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project revision changed before the guarded update", { expected_revision: request.expected_revision, current_revision: before.updated_at });
57870
+ }
57871
+ assertNoProjectCollision(id, normalized, d);
57872
+ const revision = nextProjectRevision(before.updated_at);
57873
+ const after = projectWithUpdates(before, normalized, revision);
57874
+ const result = d.run(`
57875
+ UPDATE projects
57876
+ SET name = ?, path = ?, description = ?, memory_prefix = ?, updated_at = ?
57877
+ WHERE id = ? AND updated_at = ?
57878
+ `, [
57879
+ after.name,
57880
+ after.path,
57881
+ after.description,
57882
+ after.memory_prefix,
57883
+ after.updated_at,
57884
+ id,
57885
+ request.expected_revision
57886
+ ]);
57887
+ if (result.changes !== 1) {
57888
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project compare-and-swap did not update exactly one row");
57889
+ }
57890
+ const readback = getProjectByExactId2(id, d);
57891
+ if (!readback || canonicalProjectUpdateJson(readback) !== canonicalProjectUpdateJson(after)) {
57892
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "Project guarded update did not read back exactly under the stable ID");
57893
+ }
57894
+ const receipt = makeProjectUpdateReceipt({
57895
+ request: { ...request, updates: normalized },
57896
+ direction: "forward",
57897
+ request_digest: requestDigest,
57898
+ target_id: id,
57899
+ before_project: before,
57900
+ after_project: readback
57901
+ });
57902
+ insertProjectUpdateReceipt(d, receipt);
57903
+ return { dry_run: false, applied: true, project: readback, receipt };
57904
+ });
57905
+ }
57906
+ function rollbackProjectUpdate(id, request, db) {
57907
+ assertProjectUpdateRequest(request);
57908
+ assertBoundedIdentifier2(request.accepted_receipt_id, "accepted_receipt_id");
57909
+ if (!db && isApiMode()) {
57910
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-rollback`, request);
57911
+ return data;
57912
+ }
57913
+ const d = db || getDatabase();
57914
+ const requestDigest = digestProjectUpdateValue({
57915
+ ...request,
57916
+ target_id: id,
57917
+ direction: "rollback"
57918
+ });
57919
+ return d.transaction(() => {
57920
+ const prior = findProjectUpdateReceiptByKey(d, request, "rollback", request.idempotency_key);
57921
+ if (prior) {
57922
+ if (prior.request_digest !== requestDigest || prior.target_id !== id) {
57923
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_IDEMPOTENCY_MISMATCH", "caller idempotency key is already bound to a different rollback request");
57924
+ }
57925
+ const current2 = getProjectByExactId2(id, d);
57926
+ if (!current2 || canonicalProjectUpdateJson(current2) !== canonicalProjectUpdateJson(prior.after_project)) {
57927
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "accepted rollback target drifted after its immutable receipt");
57928
+ }
57929
+ return { dry_run: false, applied: true, project: prior.after_project, receipt: prior };
57930
+ }
57931
+ const accepted = findProjectUpdateReceiptById(d, request, request.accepted_receipt_id);
57932
+ if (!accepted || accepted.direction !== "forward" || accepted.target_id !== id) {
57933
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_RECEIPT_NOT_FOUND", "accepted forward update receipt was not found for this exact project");
57934
+ }
57935
+ const current = getProjectByExactId2(id, d);
57936
+ if (!current) {
57937
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_NOT_FOUND", `Project not found by exact stable ID: ${id}`);
57938
+ }
57939
+ if (current.updated_at !== request.expected_revision || canonicalProjectUpdateJson(current) !== canonicalProjectUpdateJson(accepted.after_project)) {
57940
+ 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 });
57941
+ }
57942
+ assertNoProjectCollision(id, accepted.before_project, d);
57943
+ const restored = accepted.before_project;
57944
+ const result = d.run(`
57945
+ UPDATE projects
57946
+ SET name = ?, path = ?, description = ?, memory_prefix = ?,
57947
+ created_at = ?, updated_at = ?
57948
+ WHERE id = ? AND updated_at = ?
57949
+ `, [
57950
+ restored.name,
57951
+ restored.path,
57952
+ restored.description,
57953
+ restored.memory_prefix,
57954
+ restored.created_at,
57955
+ restored.updated_at,
57956
+ id,
57957
+ request.expected_revision
57958
+ ]);
57959
+ if (result.changes !== 1) {
57960
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_STALE_REVISION", "Project rollback compare-and-swap did not update exactly one row");
57961
+ }
57962
+ const readback = getProjectByExactId2(id, d);
57963
+ if (!readback || canonicalProjectUpdateJson(readback) !== canonicalProjectUpdateJson(restored)) {
57964
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_ACCEPTED_TARGET_DRIFTED", "Project rollback did not restore the exact prior row");
57965
+ }
57966
+ const receipt = makeProjectUpdateReceipt({
57967
+ request,
57968
+ direction: "rollback",
57969
+ request_digest: requestDigest,
57970
+ target_id: id,
57971
+ before_project: current,
57972
+ after_project: readback,
57973
+ accepted_receipt_id: accepted.receipt_id
57974
+ });
57975
+ insertProjectUpdateReceipt(d, receipt);
57976
+ return { dry_run: false, applied: true, project: readback, receipt };
57977
+ });
57978
+ }
57979
+ function getProjectUpdateReceipt(id, receiptId, identity = PROJECT_UPDATE_AUTHORITY, db) {
57980
+ assertProjectUpdateIdentity(identity);
57981
+ if (!db && isApiMode()) {
57982
+ const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/update-receipts/lookup`, { ...identity, receipt_id: receiptId });
57983
+ return data;
57984
+ }
57985
+ const d = db || getDatabase();
57986
+ const receipt = findProjectUpdateReceiptById(d, identity, receiptId);
57987
+ if (!receipt || receipt.target_id !== id) {
57988
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_RECEIPT_NOT_FOUND", "immutable project update receipt was not found for this exact project");
57989
+ }
57990
+ return receipt;
56662
57991
  }
56663
57992
 
56664
57993
  // src/server/routes/projects.ts
@@ -56687,6 +58016,54 @@ addRoute("GET", "/api/projects/:id", (_req, _url, params) => {
56687
58016
  return errorResponse("Project not found", 404);
56688
58017
  return json(project);
56689
58018
  });
58019
+ function guardedUpdateError(error) {
58020
+ const status = error.code === "PROJECT_UPDATE_AUTHORITY_MISMATCH" ? 403 : error.code === "PROJECT_UPDATE_NOT_FOUND" || error.code === "PROJECT_UPDATE_RECEIPT_NOT_FOUND" ? 404 : error.code === "PROJECT_UPDATE_INVALID_INPUT" ? 400 : 409;
58021
+ return errorResponse(error.message, status, { code: error.code, ...error.details });
58022
+ }
58023
+ addRoute("PATCH", "/api/projects/:id", () => errorResponse("Unguarded project updates are disabled; use POST /projects/:id/guarded-update", 428));
58024
+ addRoute("POST", "/api/projects/:id/guarded-update", async (req, _url, params) => {
58025
+ const body = await readJson(req);
58026
+ if (!body)
58027
+ return errorResponse("Invalid JSON body", 400);
58028
+ try {
58029
+ const request = body;
58030
+ return json(request.dry_run ? previewProjectUpdate(params["id"], request) : applyProjectUpdate(params["id"], request));
58031
+ } catch (error) {
58032
+ if (error instanceof ProjectGuardedUpdateError)
58033
+ return guardedUpdateError(error);
58034
+ throw error;
58035
+ }
58036
+ });
58037
+ addRoute("POST", "/api/projects/:id/guarded-rollback", async (req, _url, params) => {
58038
+ const body = await readJson(req);
58039
+ if (!body)
58040
+ return errorResponse("Invalid JSON body", 400);
58041
+ try {
58042
+ return json(rollbackProjectUpdate(params["id"], body));
58043
+ } catch (error) {
58044
+ if (error instanceof ProjectGuardedUpdateError)
58045
+ return guardedUpdateError(error);
58046
+ throw error;
58047
+ }
58048
+ });
58049
+ addRoute("POST", "/api/projects/:id/update-receipts/lookup", async (req, _url, params) => {
58050
+ const body = await readJson(req);
58051
+ if (!body || typeof body["receipt_id"] !== "string") {
58052
+ return errorResponse("receipt_id is required", 400);
58053
+ }
58054
+ try {
58055
+ const identity = {
58056
+ authority_id: String(body["authority_id"] ?? ""),
58057
+ tenant_id: String(body["tenant_id"] ?? ""),
58058
+ corpus_id: String(body["corpus_id"] ?? "")
58059
+ };
58060
+ return json(getProjectUpdateReceipt(params["id"], body["receipt_id"], identity));
58061
+ } catch (error) {
58062
+ if (error instanceof ProjectGuardedUpdateError)
58063
+ return guardedUpdateError(error);
58064
+ throw error;
58065
+ }
58066
+ });
56690
58067
  addRoute("GET", "/api/projects/:id/agents", (_req, _url, params) => {
56691
58068
  const project = getProject(params["id"]);
56692
58069
  if (!project)
@@ -56695,6 +58072,1064 @@ addRoute("GET", "/api/projects/:id/agents", (_req, _url, params) => {
56695
58072
  return json({ agents, count: agents.length });
56696
58073
  });
56697
58074
 
58075
+ // src/server/routes/project-registration.ts
58076
+ init_database();
58077
+
58078
+ // src/project-registration/authority.ts
58079
+ import { createHash as createHash3 } from "crypto";
58080
+ import { resolve as resolve3 } from "path";
58081
+
58082
+ // src/project-registration/project-references.ts
58083
+ var MEMENTOS_PROJECT_REFERENCE_SURFACES = [
58084
+ { key: "memories", table: "memories", column: "project_id" },
58085
+ { key: "sessions", table: "sessions", column: "project_id" },
58086
+ { key: "entities", table: "entities", column: "project_id" },
58087
+ { key: "agents", table: "agents", column: "active_project_id" },
58088
+ { key: "tool_events", table: "tool_events", column: "project_id" },
58089
+ { key: "tasks", table: "tasks", column: "project_id" },
58090
+ {
58091
+ key: "memory_consolidation_runs",
58092
+ table: "memory_consolidation_runs",
58093
+ column: "project_id"
58094
+ },
58095
+ {
58096
+ key: "memory_reflection_runs",
58097
+ table: "memory_reflection_runs",
58098
+ column: "project_id"
58099
+ },
58100
+ { key: "memory_acl", table: "memory_acl", column: "project_id" },
58101
+ { key: "search_history", table: "search_history", column: "project_id" },
58102
+ {
58103
+ key: "session_memory_jobs",
58104
+ table: "session_memory_jobs",
58105
+ column: "project_id"
58106
+ },
58107
+ { key: "synthesis_events", table: "synthesis_events", column: "project_id" },
58108
+ { key: "synthesis_runs", table: "synthesis_runs", column: "project_id" },
58109
+ { key: "webhook_hooks", table: "webhook_hooks", column: "project_id" }
58110
+ ];
58111
+ function quotedIdentifier(value) {
58112
+ if (!/^[a-z_][a-z0-9_]*$/.test(value)) {
58113
+ throw new Error(`unsafe project-reference identifier: ${value}`);
58114
+ }
58115
+ return `"${value}"`;
58116
+ }
58117
+ function mementosProjectReferenceCounts(db, projectId) {
58118
+ const projections = MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ key, table, column }) => `(SELECT COUNT(*) FROM ${quotedIdentifier(table)} ` + `WHERE ${quotedIdentifier(column)} = ?) AS ${quotedIdentifier(key)}`).join(`,
58119
+ `);
58120
+ const row = db.get(`SELECT
58121
+ ${projections}`, ...MEMENTOS_PROJECT_REFERENCE_SURFACES.map(() => projectId));
58122
+ return Object.fromEntries(MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ key }) => [key, Number(row[key] ?? 0)]));
58123
+ }
58124
+ function hasMementosProjectReferences(counts) {
58125
+ return MEMENTOS_PROJECT_REFERENCE_SURFACES.some(({ key }) => counts[key] > 0);
58126
+ }
58127
+ function deleteMementosProjectIfUnreferenced(db, projectId) {
58128
+ const guards = MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ table, column }) => `NOT EXISTS (SELECT 1 FROM ${quotedIdentifier(table)} ` + `WHERE ${quotedIdentifier(column)} = ?)`).join(`
58129
+ AND `);
58130
+ const result = db.run(`DELETE FROM projects
58131
+ WHERE id = ?
58132
+ AND ${guards}`, projectId, ...MEMENTOS_PROJECT_REFERENCE_SURFACES.map(() => projectId));
58133
+ return result.changes;
58134
+ }
58135
+
58136
+ // src/project-registration/authority.ts
58137
+ var WORKSPACE_ID_PATTERN = /^wks_[A-Za-z0-9][A-Za-z0-9_-]{11,}$/;
58138
+ var OPERATION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
58139
+ var STEP_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/;
58140
+ var SHA256_PATTERN = /^[0-9a-f]{64}$/;
58141
+ var IDEMPOTENCY_PATTERN = /^prk_[0-9a-f]{48}$/;
58142
+ var PROJECT_ID_PATTERN = /^mm_project_[0-9a-f]{40}$/;
58143
+
58144
+ class WriteBoundaryError extends Error {
58145
+ point;
58146
+ cause;
58147
+ constructor(point, cause) {
58148
+ super(`Mementos project registration failed at ${point}`);
58149
+ this.point = point;
58150
+ this.cause = cause;
58151
+ }
58152
+ }
58153
+ function canonicalMementosProjectRegistrationJson(value) {
58154
+ return JSON.stringify(canonicalize2(value));
58155
+ }
58156
+ function canonicalize2(value) {
58157
+ if (Array.isArray(value))
58158
+ return value.map(canonicalize2);
58159
+ if (!value || typeof value !== "object")
58160
+ return value;
58161
+ const out = {};
58162
+ for (const key of Object.keys(value).sort()) {
58163
+ const item = value[key];
58164
+ if (item !== undefined)
58165
+ out[key] = canonicalize2(item);
58166
+ }
58167
+ return out;
58168
+ }
58169
+ function digestMementosProjectRegistrationValue(value) {
58170
+ return createHash3("sha256").update(canonicalMementosProjectRegistrationJson(value)).digest("hex");
58171
+ }
58172
+ function digestOwnedPath(path) {
58173
+ return createHash3("sha256").update(path).digest("hex");
58174
+ }
58175
+ function deriveMementosProjectRegistrationIdempotencyKey(input) {
58176
+ return `prk_${digestMementosProjectRegistrationValue({
58177
+ route: MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE,
58178
+ operation_id: input.operation_id,
58179
+ step_id: input.step_id,
58180
+ direction: input.direction,
58181
+ target_selector: input.target_selector,
58182
+ request_digest: input.request_digest,
58183
+ precondition_digest: input.precondition_digest
58184
+ }).slice(0, 48)}`;
58185
+ }
58186
+ function responseBytes(value) {
58187
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
58188
+ }
58189
+ function assertBounds(bounds) {
58190
+ if (!Number.isSafeInteger(bounds.response_byte_limit) || bounds.response_byte_limit <= 0) {
58191
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "response_byte_limit must be a positive integer");
58192
+ }
58193
+ if (!Number.isSafeInteger(bounds.time_budget_ms) || bounds.time_budget_ms <= 0) {
58194
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "time_budget_ms must be a positive integer");
58195
+ }
58196
+ }
58197
+ function assertWithinBounds(value, bounds, startedAt) {
58198
+ const bytes = responseBytes(value);
58199
+ if (bytes > bounds.response_byte_limit) {
58200
+ 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 });
58201
+ }
58202
+ const elapsed = Date.now() - startedAt;
58203
+ if (elapsed > bounds.time_budget_ms) {
58204
+ 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 });
58205
+ }
58206
+ return { response_bytes: bytes, elapsed_ms: elapsed };
58207
+ }
58208
+ function withResponseControl(receipt, bounds, startedAt) {
58209
+ const result = {
58210
+ receipt,
58211
+ response_control: {
58212
+ response_byte_limit: bounds.response_byte_limit,
58213
+ time_budget_ms: bounds.time_budget_ms,
58214
+ response_bytes: 0,
58215
+ elapsed_ms: 0,
58216
+ complete: true,
58217
+ truncated: false
58218
+ }
58219
+ };
58220
+ for (let attempt = 0;attempt < 8; attempt += 1) {
58221
+ const measured2 = assertWithinBounds(result, bounds, startedAt);
58222
+ const stable = result.response_control.response_bytes === measured2.response_bytes && result.response_control.elapsed_ms === measured2.elapsed_ms;
58223
+ result.response_control.response_bytes = measured2.response_bytes;
58224
+ result.response_control.elapsed_ms = measured2.elapsed_ms;
58225
+ if (stable)
58226
+ break;
58227
+ }
58228
+ const measured = assertWithinBounds(result, bounds, startedAt);
58229
+ result.response_control.response_bytes = measured.response_bytes;
58230
+ result.response_control.elapsed_ms = measured.elapsed_ms;
58231
+ return result;
58232
+ }
58233
+ function requireString(value, field, options = {}) {
58234
+ const min = options.min ?? 1;
58235
+ const max = options.max ?? 512;
58236
+ if (typeof value !== "string" || value.length < min || value.length > max || /[\u0000-\u001f]/.test(value) || options.pattern && !options.pattern.test(value)) {
58237
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} is not a valid bounded registration identifier`);
58238
+ }
58239
+ return value;
58240
+ }
58241
+ function exactKeys(value, expected, field) {
58242
+ const actual = Object.keys(value).sort();
58243
+ const wanted = [...expected].sort();
58244
+ if (actual.length !== wanted.length || actual.some((key, i) => key !== wanted[i])) {
58245
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} must contain exactly: ${wanted.join(", ")}`);
58246
+ }
58247
+ }
58248
+ function ownedPath(target) {
58249
+ if (!target || typeof target.withOwnedPath !== "function") {
58250
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target must be a package-owned path handle");
58251
+ }
58252
+ const path = target.withOwnedPath((value) => value);
58253
+ requireString(path, "target path", { max: 4096 });
58254
+ if (path !== resolve3(path)) {
58255
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target path must already be canonical and absolute");
58256
+ }
58257
+ return path;
58258
+ }
58259
+ function normalizedCallDigest(request) {
58260
+ return digestMementosProjectRegistrationValue({
58261
+ authority_route: request.authority_route,
58262
+ package_version: request.package_version,
58263
+ authority_id: request.authority_id,
58264
+ tenant_id: request.tenant_id,
58265
+ corpus_id: request.corpus_id,
58266
+ operation_id: request.operation_id,
58267
+ step_id: request.step_id,
58268
+ resource_kind: request.resource_kind,
58269
+ direction: request.direction,
58270
+ target_selector: request.target_selector,
58271
+ idempotency_key: request.idempotency_key,
58272
+ request_digest: request.request_digest,
58273
+ precondition_digest: request.precondition_digest,
58274
+ project_id: request.project_id,
58275
+ project_slug: request.project_slug,
58276
+ project_name: request.project_name,
58277
+ desired: request.desired,
58278
+ accepted_receipt_id: request.accepted_receipt?.receipt_id ?? null
58279
+ });
58280
+ }
58281
+ function publicReceipt(row) {
58282
+ const { target_selector: _selector, normalized_call_digest: _digest, ...receipt } = row;
58283
+ return receipt;
58284
+ }
58285
+ function timestampString3(value) {
58286
+ return value instanceof Date ? value.toISOString() : String(value);
58287
+ }
58288
+ function receiptFromRow2(row) {
58289
+ return {
58290
+ ...row,
58291
+ authority: "mementos",
58292
+ resource_kind: "project",
58293
+ created_by_operation: row["created_by_operation"] === true || Number(row["created_by_operation"]) === 1,
58294
+ created_at: timestampString3(row["created_at"])
58295
+ };
58296
+ }
58297
+ function bindingFromRow(row) {
58298
+ return {
58299
+ ...row,
58300
+ created_at: timestampString3(row["created_at"]),
58301
+ updated_at: timestampString3(row["updated_at"])
58302
+ };
58303
+ }
58304
+ function getStoredReceipt(db, receiptId) {
58305
+ const row = db.get("SELECT * FROM mementos_project_registration_receipts WHERE receipt_id = ? LIMIT 1", receiptId);
58306
+ return row ? receiptFromRow2(row) : null;
58307
+ }
58308
+ function getAcceptedReceipt(db, request) {
58309
+ const row = db.get(`
58310
+ SELECT * FROM mementos_project_registration_receipts
58311
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
58312
+ AND operation_id = ? AND step_id = ? AND resource_kind = 'project'
58313
+ AND direction = ? AND outcome = 'accepted'
58314
+ ORDER BY created_at ASC, receipt_id ASC
58315
+ LIMIT 1
58316
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.operation_id, request.step_id, request.direction);
58317
+ return row ? receiptFromRow2(row) : null;
58318
+ }
58319
+ function getReceiptForLookup(db, request) {
58320
+ const row = db.get(`
58321
+ SELECT * FROM mementos_project_registration_receipts
58322
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
58323
+ AND operation_id = ? AND step_id = ? AND resource_kind = 'project'
58324
+ AND direction = ? AND idempotency_key = ? AND target_selector = ?
58325
+ ORDER BY CASE outcome
58326
+ WHEN 'terminal_nonacceptance' THEN 0
58327
+ WHEN 'duplicate_of_accepted' THEN 1
58328
+ ELSE 2
58329
+ END, created_at DESC, receipt_id DESC
58330
+ LIMIT 1
58331
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.operation_id, request.step_id, request.direction, request.idempotency_key, request.target_selector);
58332
+ return row ? receiptFromRow2(row) : null;
58333
+ }
58334
+ function getBinding(db, capability, targetSelector) {
58335
+ const row = db.get(`
58336
+ SELECT * FROM mementos_project_registration_bindings
58337
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
58338
+ AND resource_kind = 'project' AND target_selector = ?
58339
+ LIMIT 1
58340
+ `, capability.authority_id, capability.tenant_id, capability.corpus_id, targetSelector);
58341
+ return row ? bindingFromRow(row) : null;
58342
+ }
58343
+ function getProjectByExactId3(db, id) {
58344
+ const row = db.get("SELECT * FROM projects WHERE id = ? LIMIT 1", id);
58345
+ if (!row)
58346
+ return null;
58347
+ return {
58348
+ id: String(row["id"]),
58349
+ name: String(row["name"]),
58350
+ path: String(row["path"]),
58351
+ description: row["description"] === null ? null : String(row["description"]),
58352
+ memory_prefix: row["memory_prefix"] === null ? null : String(row["memory_prefix"]),
58353
+ created_at: timestampString3(row["created_at"]),
58354
+ updated_at: timestampString3(row["updated_at"])
58355
+ };
58356
+ }
58357
+ function getProjectByPath(db, path) {
58358
+ const row = db.get("SELECT * FROM projects WHERE path = ? LIMIT 1", path);
58359
+ return row ? getProjectByExactId3(db, String(row["id"])) : null;
58360
+ }
58361
+ function projectRecord(db, project, references = mementosProjectReferenceCounts(db, project.id)) {
58362
+ return {
58363
+ target_id: project.id,
58364
+ revision: project.updated_at,
58365
+ digest: digestMementosProjectRegistrationValue({
58366
+ id: project.id,
58367
+ name: project.name,
58368
+ path: project.path,
58369
+ description: project.description,
58370
+ memory_prefix: project.memory_prefix,
58371
+ created_at: project.created_at,
58372
+ updated_at: project.updated_at,
58373
+ references
58374
+ })
58375
+ };
58376
+ }
58377
+ function targetId(capability, selector) {
58378
+ return `mm_project_${digestMementosProjectRegistrationValue({
58379
+ authority_id: capability.authority_id,
58380
+ tenant_id: capability.tenant_id,
58381
+ corpus_id: capability.corpus_id,
58382
+ selector
58383
+ }).slice(0, 40)}`;
58384
+ }
58385
+ function receiptId(input) {
58386
+ return `mmpr_${digestMementosProjectRegistrationValue(input).slice(0, 40)}`;
58387
+ }
58388
+ function makeReceipt2(request, capability, callDigest, now2, values) {
58389
+ const withoutId = {
58390
+ authority: "mementos",
58391
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
58392
+ package_version: capability.package_version,
58393
+ authority_id: capability.authority_id,
58394
+ tenant_id: capability.tenant_id,
58395
+ corpus_id: capability.corpus_id,
58396
+ operation_id: request.operation_id,
58397
+ step_id: request.step_id,
58398
+ resource_kind: "project",
58399
+ direction: request.direction,
58400
+ target_selector: values.target_selector ?? request.target_selector,
58401
+ idempotency_key: request.idempotency_key,
58402
+ request_digest: request.request_digest,
58403
+ precondition_digest: request.precondition_digest,
58404
+ normalized_call_digest: callDigest,
58405
+ outcome: values.outcome,
58406
+ reason: values.reason ?? null,
58407
+ target_id: values.target_id ?? null,
58408
+ result_revision: values.result_revision ?? null,
58409
+ result_digest: values.result_digest ?? null,
58410
+ duplicate_of_receipt_id: values.duplicate_of_receipt_id ?? null,
58411
+ accepted_receipt_id: values.accepted_receipt_id ?? null,
58412
+ created_by_operation: values.created_by_operation ?? false
58413
+ };
58414
+ return {
58415
+ receipt_id: receiptId(withoutId),
58416
+ ...withoutId,
58417
+ created_at: now2
58418
+ };
58419
+ }
58420
+ function insertReceipt2(db, receipt) {
58421
+ db.run(`
58422
+ INSERT INTO mementos_project_registration_receipts (
58423
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
58424
+ corpus_id, operation_id, step_id, resource_kind, direction,
58425
+ target_selector, idempotency_key, request_digest, precondition_digest,
58426
+ normalized_call_digest, outcome, reason, target_id, result_revision,
58427
+ result_digest, duplicate_of_receipt_id, accepted_receipt_id,
58428
+ created_by_operation, created_at
58429
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
58430
+ ON CONFLICT DO NOTHING
58431
+ `, 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);
58432
+ const stored = getStoredReceipt(db, receipt.receipt_id);
58433
+ const withoutCreatedAt = (value) => {
58434
+ const { created_at: _createdAt, ...logical } = value;
58435
+ return logical;
58436
+ };
58437
+ if (!stored || canonicalMementosProjectRegistrationJson(withoutCreatedAt(stored)) !== canonicalMementosProjectRegistrationJson(withoutCreatedAt(receipt))) {
58438
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "immutable receipt id resolved to different content");
58439
+ }
58440
+ return stored;
58441
+ }
58442
+ function insertBinding(db, request, callDigest, now2) {
58443
+ const result = db.run(`
58444
+ INSERT INTO mementos_project_registration_bindings (
58445
+ authority_id, tenant_id, corpus_id, resource_kind, target_selector,
58446
+ operation_id, step_id, direction, idempotency_key, request_digest,
58447
+ precondition_digest, normalized_call_digest, state, target_id,
58448
+ accepted_receipt_id, result_revision, result_digest, removed_receipt_id,
58449
+ created_at, updated_at
58450
+ ) VALUES (?, ?, ?, 'project', ?, ?, ?, 'forward', ?, ?, ?, ?, 'pending',
58451
+ NULL, NULL, NULL, NULL, NULL, ?, ?)
58452
+ ON CONFLICT DO NOTHING
58453
+ `, 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);
58454
+ return result.changes === 1;
58455
+ }
58456
+ function lockBinding(db, capability, targetSelector) {
58457
+ const result = db.run(`
58458
+ UPDATE mementos_project_registration_bindings
58459
+ SET updated_at = updated_at
58460
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
58461
+ AND resource_kind = 'project' AND target_selector = ?
58462
+ `, capability.authority_id, capability.tenant_id, capability.corpus_id, targetSelector);
58463
+ return result.changes === 1;
58464
+ }
58465
+ function setBindingAccepted(db, request, receipt, now2) {
58466
+ const result = db.run(`
58467
+ UPDATE mementos_project_registration_bindings
58468
+ SET state = 'accepted', target_id = ?, accepted_receipt_id = ?,
58469
+ result_revision = ?, result_digest = ?, updated_at = ?
58470
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
58471
+ AND resource_kind = 'project' AND target_selector = ? AND state = 'pending'
58472
+ `, 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);
58473
+ if (result.changes !== 1) {
58474
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding was not pending at acceptance");
58475
+ }
58476
+ }
58477
+ function setBindingRemoved(db, accepted, inverseReceiptId, now2) {
58478
+ const result = db.run(`
58479
+ UPDATE mementos_project_registration_bindings
58480
+ SET state = 'removed', removed_receipt_id = ?, updated_at = ?
58481
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
58482
+ AND resource_kind = 'project' AND target_selector = ?
58483
+ AND state = 'accepted' AND accepted_receipt_id = ?
58484
+ `, inverseReceiptId, now2, accepted.authority_id, accepted.tenant_id, accepted.corpus_id, accepted.target_selector, accepted.receipt_id);
58485
+ if (result.changes !== 1) {
58486
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding was not accepted at inverse");
58487
+ }
58488
+ }
58489
+ function capabilityMatches(request, capability) {
58490
+ 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;
58491
+ }
58492
+ function assertCommonRequest(request, capability) {
58493
+ assertBounds(request);
58494
+ if (request.resource_kind !== "project") {
58495
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project");
58496
+ }
58497
+ if (request.direction !== "forward" && request.direction !== "inverse") {
58498
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "direction must be forward or inverse");
58499
+ }
58500
+ if (!capabilityMatches(request, capability)) {
58501
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "registration request does not match this authority capability identity");
58502
+ }
58503
+ requireString(request.operation_id, "operation_id", {
58504
+ min: 8,
58505
+ max: 128,
58506
+ pattern: OPERATION_PATTERN
58507
+ });
58508
+ requireString(request.step_id, "step_id", { min: 3, max: 128, pattern: STEP_PATTERN });
58509
+ requireString(request.target_selector, "target_selector", { max: 512 });
58510
+ requireString(request.project_id, "project_id", {
58511
+ min: 16,
58512
+ max: 128,
58513
+ pattern: WORKSPACE_ID_PATTERN
58514
+ });
58515
+ requireString(request.project_name, "project_name", { max: 256 });
58516
+ requireString(request.project_slug, "project_slug", { max: 128 });
58517
+ requireString(request.request_digest, "request_digest", {
58518
+ min: 64,
58519
+ max: 64,
58520
+ pattern: SHA256_PATTERN
58521
+ });
58522
+ requireString(request.precondition_digest, "precondition_digest", {
58523
+ min: 64,
58524
+ max: 64,
58525
+ pattern: SHA256_PATTERN
58526
+ });
58527
+ requireString(request.idempotency_key, "idempotency_key", {
58528
+ min: 52,
58529
+ max: 52,
58530
+ pattern: IDEMPOTENCY_PATTERN
58531
+ });
58532
+ if (!request.desired || typeof request.desired !== "object" || Array.isArray(request.desired)) {
58533
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "desired must be a JSON object");
58534
+ }
58535
+ if (digestMementosProjectRegistrationValue(request.desired) !== request.request_digest) {
58536
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "request_digest does not match the canonical desired payload");
58537
+ }
58538
+ const expectedKey = deriveMementosProjectRegistrationIdempotencyKey(request);
58539
+ if (request.idempotency_key !== expectedKey) {
58540
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "idempotency_key does not match the deterministic request", { expected: expectedKey });
58541
+ }
58542
+ }
58543
+ function assertForwardRequest2(request, capability) {
58544
+ assertCommonRequest(request, capability);
58545
+ if (request.direction !== "forward" || request.accepted_receipt !== undefined) {
58546
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional create requires a forward request without an accepted receipt");
58547
+ }
58548
+ exactKeys(request.desired, [
58549
+ "name",
58550
+ "source_project_id",
58551
+ "source_project_slug",
58552
+ "target_path_digest"
58553
+ ], "forward desired");
58554
+ 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) {
58555
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "forward project identity fields disagree");
58556
+ }
58557
+ const expectedPrecondition = digestMementosProjectRegistrationValue({
58558
+ target_selector: request.target_selector,
58559
+ expected: "absent"
58560
+ });
58561
+ if (request.precondition_digest !== expectedPrecondition) {
58562
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "forward precondition must bind expected absence");
58563
+ }
58564
+ const path = ownedPath(request.target);
58565
+ if (request.desired["target_path_digest"] !== digestOwnedPath(path)) {
58566
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "target path does not match the package-owned digest");
58567
+ }
58568
+ return path;
58569
+ }
58570
+ function sameReceipt(left, right) {
58571
+ return canonicalMementosProjectRegistrationJson(publicReceipt(left)) === canonicalMementosProjectRegistrationJson(right);
58572
+ }
58573
+ function assertInverseRequest(request, capability) {
58574
+ assertCommonRequest(request, capability);
58575
+ if (request.direction !== "inverse" || !request.accepted_receipt) {
58576
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional inverse requires the accepted forward receipt");
58577
+ }
58578
+ const accepted = request.accepted_receipt;
58579
+ 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) {
58580
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "accepted receipt is not an exact attempt-created forward receipt");
58581
+ }
58582
+ exactKeys(request.desired, ["accepted_receipt_id", "target_id"], "inverse desired");
58583
+ if (request.desired["accepted_receipt_id"] !== accepted.receipt_id || request.desired["target_id"] !== accepted.target_id) {
58584
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "inverse desired payload does not bind the accepted receipt");
58585
+ }
58586
+ const expectedPrecondition = digestMementosProjectRegistrationValue({
58587
+ expected_revision: accepted.result_revision,
58588
+ expected_digest: accepted.result_digest
58589
+ });
58590
+ if (request.precondition_digest !== expectedPrecondition) {
58591
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "inverse precondition does not bind the accepted object state");
58592
+ }
58593
+ return accepted;
58594
+ }
58595
+ function assertLookup(request, capability) {
58596
+ assertBounds(request);
58597
+ if (request.max_items !== 1) {
58598
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "terminal receipt lookup requires max_items exactly 1");
58599
+ }
58600
+ 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) {
58601
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "receipt lookup does not match this authority capability identity");
58602
+ }
58603
+ requireString(request.operation_id, "operation_id", { pattern: OPERATION_PATTERN });
58604
+ requireString(request.step_id, "step_id", { pattern: STEP_PATTERN });
58605
+ requireString(request.target_selector, "target_selector");
58606
+ requireString(request.idempotency_key, "idempotency_key", {
58607
+ min: 52,
58608
+ max: 52,
58609
+ pattern: IDEMPOTENCY_PATTERN
58610
+ });
58611
+ if (request.target_id !== undefined) {
58612
+ requireString(request.target_id, "target_id", {
58613
+ min: 51,
58614
+ max: 51,
58615
+ pattern: PROJECT_ID_PATTERN
58616
+ });
58617
+ }
58618
+ }
58619
+
58620
+ class PackageOwnedMementosProjectRegistrationAuthority {
58621
+ db;
58622
+ authority = "mementos";
58623
+ capabilityValue;
58624
+ now;
58625
+ faultInjector;
58626
+ constructor(db, options = {}) {
58627
+ this.db = db;
58628
+ this.now = options.now ?? (() => new Date().toISOString());
58629
+ this.faultInjector = options.faultInjector;
58630
+ this.capabilityValue = {
58631
+ authority: "mementos",
58632
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
58633
+ package_version: options.packageVersion ?? getMementosPackageVersion(),
58634
+ authority_id: options.authorityId ?? "mementos",
58635
+ tenant_id: options.tenantId ?? "default",
58636
+ corpus_id: options.corpusId ?? "default",
58637
+ supported_resources: ["project"],
58638
+ conditional_create: true,
58639
+ immutable_receipts: true,
58640
+ exact_terminal_lookup: true,
58641
+ exact_readback: true,
58642
+ conditional_inverse: true,
58643
+ ambiguous_outcome_reconciliation: true,
58644
+ guarded_update: true,
58645
+ no_write_dry_run: true,
58646
+ expected_revision_compare_and_swap: true,
58647
+ caller_idempotency: true,
58648
+ exact_inverse_rollback: true
58649
+ };
58650
+ }
58651
+ fault(point, request) {
58652
+ this.faultInjector?.(point, {
58653
+ operation_id: request.operation_id,
58654
+ step_id: request.step_id,
58655
+ resource_kind: "project",
58656
+ direction: request.direction
58657
+ });
58658
+ }
58659
+ writeBoundary(point, request) {
58660
+ try {
58661
+ this.fault(point, request);
58662
+ } catch (cause) {
58663
+ throw new WriteBoundaryError(point, cause);
58664
+ }
58665
+ }
58666
+ terminal(request, callDigest, reason, values = {}) {
58667
+ return insertReceipt2(this.db, makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
58668
+ outcome: "terminal_nonacceptance",
58669
+ reason,
58670
+ target_id: values.target_id,
58671
+ accepted_receipt_id: values.accepted_receipt_id,
58672
+ target_selector: values.target_selector
58673
+ }));
58674
+ }
58675
+ duplicateForwardOrTerminal(request, callDigest, path, accepted) {
58676
+ if (accepted.normalized_call_digest !== callDigest) {
58677
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "accepted operation/step identity is bound to a different request");
58678
+ }
58679
+ const project = accepted.target_id ? getProjectByExactId3(this.db, accepted.target_id) : null;
58680
+ if (!project || project.path !== path) {
58681
+ return this.terminal(request, callDigest, "accepted_target_missing_or_replaced", {
58682
+ target_id: accepted.target_id
58683
+ });
58684
+ }
58685
+ const record = projectRecord(this.db, project);
58686
+ if (record.revision !== accepted.result_revision || record.digest !== accepted.result_digest) {
58687
+ return this.terminal(request, callDigest, "accepted_target_drifted", {
58688
+ target_id: accepted.target_id
58689
+ });
58690
+ }
58691
+ return insertReceipt2(this.db, makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
58692
+ outcome: "duplicate_of_accepted",
58693
+ target_id: accepted.target_id,
58694
+ result_revision: accepted.result_revision,
58695
+ result_digest: accepted.result_digest,
58696
+ duplicate_of_receipt_id: accepted.receipt_id,
58697
+ created_by_operation: false
58698
+ }));
58699
+ }
58700
+ async capability() {
58701
+ return {
58702
+ ...this.capabilityValue,
58703
+ supported_resources: ["project"]
58704
+ };
58705
+ }
58706
+ async create(request) {
58707
+ const startedAt = Date.now();
58708
+ const path = assertForwardRequest2(request, this.capabilityValue);
58709
+ const callDigest = normalizedCallDigest(request);
58710
+ let stored;
58711
+ try {
58712
+ stored = this.db.transaction(() => {
58713
+ const exact = getReceiptForLookup(this.db, {
58714
+ ...request,
58715
+ authority: "mementos",
58716
+ max_items: 1
58717
+ });
58718
+ if (exact?.outcome === "terminal_nonacceptance")
58719
+ return exact;
58720
+ const accepted = getAcceptedReceipt(this.db, request);
58721
+ if (accepted) {
58722
+ return this.duplicateForwardOrTerminal(request, callDigest, path, accepted);
58723
+ }
58724
+ const existing = getProjectByPath(this.db, path) ?? getProjectByExactId3(this.db, targetId(this.capabilityValue, request.target_selector));
58725
+ if (existing) {
58726
+ return this.terminal(request, callDigest, "target_preexists", {
58727
+ target_id: existing.id
58728
+ });
58729
+ }
58730
+ const now2 = this.now();
58731
+ const insertedBinding = insertBinding(this.db, request, callDigest, now2);
58732
+ if (!lockBinding(this.db, this.capabilityValue, request.target_selector)) {
58733
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding disappeared before it could be locked");
58734
+ }
58735
+ const ownedBinding = getBinding(this.db, this.capabilityValue, request.target_selector);
58736
+ if (!ownedBinding || ownedBinding.normalized_call_digest !== callDigest) {
58737
+ return this.terminal(request, callDigest, "target_selector_preexists", {
58738
+ target_id: ownedBinding?.target_id
58739
+ });
58740
+ }
58741
+ const acceptedAfterBinding = getAcceptedReceipt(this.db, request);
58742
+ if (acceptedAfterBinding) {
58743
+ return this.duplicateForwardOrTerminal(request, callDigest, path, acceptedAfterBinding);
58744
+ }
58745
+ if (!insertedBinding || ownedBinding.state !== "pending") {
58746
+ return this.terminal(request, callDigest, "target_selector_preexists", {
58747
+ target_id: ownedBinding.target_id
58748
+ });
58749
+ }
58750
+ const id = targetId(this.capabilityValue, request.target_selector);
58751
+ this.writeBoundary("before_object_write", request);
58752
+ try {
58753
+ this.db.run(`
58754
+ INSERT INTO projects (
58755
+ id, name, path, description, memory_prefix, created_at, updated_at
58756
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
58757
+ `, id, request.project_name, path, null, request.project_slug, now2, now2);
58758
+ } catch (cause) {
58759
+ throw new WriteBoundaryError("before_object_write", cause);
58760
+ }
58761
+ this.writeBoundary("after_object_write", request);
58762
+ const project = getProjectByExactId3(this.db, id);
58763
+ if (!project || project.path !== path) {
58764
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "project write did not read back by its full id");
58765
+ }
58766
+ const record = projectRecord(this.db, project);
58767
+ const acceptedReceipt = makeReceipt2(request, this.capabilityValue, callDigest, now2, {
58768
+ outcome: "accepted",
58769
+ target_id: record.target_id,
58770
+ result_revision: record.revision,
58771
+ result_digest: record.digest,
58772
+ created_by_operation: true
58773
+ });
58774
+ this.writeBoundary("before_receipt_write", request);
58775
+ let inserted;
58776
+ try {
58777
+ inserted = insertReceipt2(this.db, acceptedReceipt);
58778
+ } catch (cause) {
58779
+ throw new WriteBoundaryError("before_receipt_write", cause);
58780
+ }
58781
+ this.writeBoundary("after_receipt_write", request);
58782
+ setBindingAccepted(this.db, request, inserted, now2);
58783
+ return inserted;
58784
+ });
58785
+ } catch (error) {
58786
+ if (!(error instanceof WriteBoundaryError))
58787
+ throw error;
58788
+ stored = this.db.transaction(() => this.terminal(request, callDigest, `write_failed:${error.point}`));
58789
+ }
58790
+ this.fault("after_commit", request);
58791
+ const receipt = publicReceipt(stored);
58792
+ assertWithinBounds(receipt, request, startedAt);
58793
+ return receipt;
58794
+ }
58795
+ async readExact(request) {
58796
+ const startedAt = Date.now();
58797
+ assertBounds(request);
58798
+ if (request.resource_kind !== "project") {
58799
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project");
58800
+ }
58801
+ requireString(request.target_id, "target_id", {
58802
+ min: 51,
58803
+ max: 51,
58804
+ pattern: PROJECT_ID_PATTERN
58805
+ });
58806
+ const path = ownedPath(request.target);
58807
+ const project = getProjectByExactId3(this.db, request.target_id);
58808
+ if (!project || project.path !== path) {
58809
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND", "exact full-id project readback did not resolve the owned path");
58810
+ }
58811
+ const record = projectRecord(this.db, project);
58812
+ assertWithinBounds(record, request, startedAt);
58813
+ return record;
58814
+ }
58815
+ async lookupReceipt(request) {
58816
+ const startedAt = Date.now();
58817
+ assertLookup(request, this.capabilityValue);
58818
+ const receipt = getReceiptForLookup(this.db, request);
58819
+ if (!receipt || request.target_id !== undefined && receipt.target_id !== request.target_id) {
58820
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "exact immutable terminal receipt was not found");
58821
+ }
58822
+ return withResponseControl(publicReceipt(receipt), request, startedAt);
58823
+ }
58824
+ async compensate(request) {
58825
+ const startedAt = Date.now();
58826
+ const acceptedPublic = assertInverseRequest(request, this.capabilityValue);
58827
+ const callDigest = normalizedCallDigest(request);
58828
+ const storedAccepted = getStoredReceipt(this.db, acceptedPublic.receipt_id);
58829
+ if (!storedAccepted || !sameReceipt(storedAccepted, acceptedPublic)) {
58830
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted forward receipt is not stored immutably in this authority");
58831
+ }
58832
+ const path = ownedPath(request.target);
58833
+ let stored;
58834
+ try {
58835
+ stored = this.db.transaction(() => {
58836
+ const exact = getReceiptForLookup(this.db, {
58837
+ ...request,
58838
+ authority: "mementos",
58839
+ max_items: 1
58840
+ });
58841
+ if (exact?.outcome === "terminal_nonacceptance")
58842
+ return exact;
58843
+ if (!lockBinding(this.db, this.capabilityValue, storedAccepted.target_selector)) {
58844
+ return this.terminal(request, callDigest, "accepted_binding_missing", {
58845
+ target_id: storedAccepted.target_id,
58846
+ accepted_receipt_id: storedAccepted.receipt_id
58847
+ });
58848
+ }
58849
+ const binding = getBinding(this.db, this.capabilityValue, storedAccepted.target_selector);
58850
+ if (!binding || binding.accepted_receipt_id !== storedAccepted.receipt_id) {
58851
+ return this.terminal(request, callDigest, "accepted_binding_replaced", {
58852
+ target_id: storedAccepted.target_id,
58853
+ accepted_receipt_id: storedAccepted.receipt_id
58854
+ });
58855
+ }
58856
+ const prior = getAcceptedReceipt(this.db, request);
58857
+ if (prior) {
58858
+ if (prior.normalized_call_digest !== callDigest || prior.accepted_receipt_id !== storedAccepted.receipt_id) {
58859
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "inverse operation/step identity is bound to a different request");
58860
+ }
58861
+ return insertReceipt2(this.db, makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
58862
+ outcome: "duplicate_of_accepted",
58863
+ target_id: prior.target_id,
58864
+ result_revision: prior.result_revision,
58865
+ result_digest: prior.result_digest,
58866
+ duplicate_of_receipt_id: prior.receipt_id,
58867
+ accepted_receipt_id: storedAccepted.receipt_id
58868
+ }));
58869
+ }
58870
+ if (binding.state !== "accepted") {
58871
+ return this.terminal(request, callDigest, "accepted_binding_not_active", {
58872
+ target_id: storedAccepted.target_id,
58873
+ accepted_receipt_id: storedAccepted.receipt_id
58874
+ });
58875
+ }
58876
+ const project = getProjectByExactId3(this.db, storedAccepted.target_id);
58877
+ if (!project) {
58878
+ return this.terminal(request, callDigest, "target_missing", {
58879
+ target_id: storedAccepted.target_id,
58880
+ accepted_receipt_id: storedAccepted.receipt_id
58881
+ });
58882
+ }
58883
+ if (project.path !== path) {
58884
+ return this.terminal(request, callDigest, "target_drifted", {
58885
+ target_id: storedAccepted.target_id,
58886
+ accepted_receipt_id: storedAccepted.receipt_id
58887
+ });
58888
+ }
58889
+ const references = mementosProjectReferenceCounts(this.db, project.id);
58890
+ if (hasMementosProjectReferences(references)) {
58891
+ return this.terminal(request, callDigest, "target_has_dependents", {
58892
+ target_id: storedAccepted.target_id,
58893
+ accepted_receipt_id: storedAccepted.receipt_id
58894
+ });
58895
+ }
58896
+ const current = projectRecord(this.db, project, references);
58897
+ if (current.revision !== storedAccepted.result_revision || current.digest !== storedAccepted.result_digest) {
58898
+ return this.terminal(request, callDigest, "target_drifted", {
58899
+ target_id: storedAccepted.target_id,
58900
+ accepted_receipt_id: storedAccepted.receipt_id
58901
+ });
58902
+ }
58903
+ this.writeBoundary("before_object_write", request);
58904
+ let deleted;
58905
+ try {
58906
+ deleted = deleteMementosProjectIfUnreferenced(this.db, storedAccepted.target_id);
58907
+ } catch (cause) {
58908
+ throw new WriteBoundaryError("before_object_write", cause);
58909
+ }
58910
+ if (deleted !== 1) {
58911
+ const remaining = getProjectByExactId3(this.db, storedAccepted.target_id);
58912
+ if (remaining && hasMementosProjectReferences(mementosProjectReferenceCounts(this.db, storedAccepted.target_id))) {
58913
+ return this.terminal(request, callDigest, "target_has_dependents", {
58914
+ target_id: storedAccepted.target_id,
58915
+ accepted_receipt_id: storedAccepted.receipt_id
58916
+ });
58917
+ }
58918
+ throw new WriteBoundaryError("before_object_write", new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "conditional inverse did not delete exactly one unreferenced project"));
58919
+ }
58920
+ this.writeBoundary("after_object_write", request);
58921
+ const inverseRecord = {
58922
+ target_id: storedAccepted.target_id,
58923
+ revision: "absent",
58924
+ digest: digestMementosProjectRegistrationValue({
58925
+ target_id: storedAccepted.target_id,
58926
+ accepted_receipt_id: storedAccepted.receipt_id,
58927
+ absent: true
58928
+ })
58929
+ };
58930
+ const inverseReceipt = makeReceipt2(request, this.capabilityValue, callDigest, this.now(), {
58931
+ outcome: "accepted",
58932
+ target_id: inverseRecord.target_id,
58933
+ result_revision: inverseRecord.revision,
58934
+ result_digest: inverseRecord.digest,
58935
+ accepted_receipt_id: storedAccepted.receipt_id,
58936
+ created_by_operation: false
58937
+ });
58938
+ this.writeBoundary("before_receipt_write", request);
58939
+ let inserted;
58940
+ try {
58941
+ inserted = insertReceipt2(this.db, inverseReceipt);
58942
+ } catch (cause) {
58943
+ throw new WriteBoundaryError("before_receipt_write", cause);
58944
+ }
58945
+ this.writeBoundary("after_receipt_write", request);
58946
+ setBindingRemoved(this.db, storedAccepted, inserted.receipt_id, this.now());
58947
+ return inserted;
58948
+ });
58949
+ } catch (error) {
58950
+ if (!(error instanceof WriteBoundaryError))
58951
+ throw error;
58952
+ stored = this.db.transaction(() => this.terminal(request, callDigest, `write_failed:${error.point}`, {
58953
+ target_id: storedAccepted.target_id,
58954
+ accepted_receipt_id: storedAccepted.receipt_id
58955
+ }));
58956
+ }
58957
+ this.fault("after_commit", request);
58958
+ const receipt = publicReceipt(stored);
58959
+ assertWithinBounds(receipt, request, startedAt);
58960
+ return receipt;
58961
+ }
58962
+ async verifyInverse(request) {
58963
+ const startedAt = Date.now();
58964
+ const accepted = assertInverseRequest(request, this.capabilityValue);
58965
+ const storedAccepted = getStoredReceipt(this.db, accepted.receipt_id);
58966
+ if (!storedAccepted || !sameReceipt(storedAccepted, accepted)) {
58967
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted forward receipt is not stored immutably in this authority");
58968
+ }
58969
+ const inverse = getAcceptedReceipt(this.db, request);
58970
+ if (!inverse || inverse.outcome !== "accepted" || inverse.accepted_receipt_id !== accepted.receipt_id || inverse.result_revision !== "absent" || !inverse.result_digest) {
58971
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "accepted conditional inverse receipt was not found");
58972
+ }
58973
+ if (getProjectByExactId3(this.db, accepted.target_id)) {
58974
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification found the accepted target still present");
58975
+ }
58976
+ const verification = {
58977
+ target_id: accepted.target_id,
58978
+ accepted_receipt_id: accepted.receipt_id,
58979
+ absent: true,
58980
+ digest: digestMementosProjectRegistrationValue({
58981
+ target_id: accepted.target_id,
58982
+ accepted_receipt_id: accepted.receipt_id,
58983
+ absent: true
58984
+ })
58985
+ };
58986
+ assertWithinBounds(verification, request, startedAt);
58987
+ if (verification.digest !== inverse.result_digest) {
58988
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification digest does not match its immutable receipt");
58989
+ }
58990
+ return verification;
58991
+ }
58992
+ }
58993
+ function createMementosProjectRegistrationAuthority(db, options = {}) {
58994
+ return new PackageOwnedMementosProjectRegistrationAuthority(db, options);
58995
+ }
58996
+ // src/project-registration/http.ts
58997
+ var JSON_HEADERS = { "Content-Type": "application/json" };
58998
+
58999
+ class WirePathHandle {
59000
+ absolutePath;
59001
+ constructor(absolutePath) {
59002
+ this.absolutePath = absolutePath;
59003
+ }
59004
+ withOwnedPath(consumer) {
59005
+ return consumer(this.absolutePath);
59006
+ }
59007
+ }
59008
+ function json2(body, status = 200) {
59009
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
59010
+ }
59011
+ function errorStatus(error) {
59012
+ switch (error.code) {
59013
+ case "MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT":
59014
+ case "MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS":
59015
+ case "MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH":
59016
+ case "MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH":
59017
+ case "MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH":
59018
+ case "MEMENTOS_PROJECT_REGISTRATION_EXACT_ID_REQUIRED":
59019
+ return 400;
59020
+ case "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND":
59021
+ case "MEMENTOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND":
59022
+ case "MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND":
59023
+ return 404;
59024
+ case "MEMENTOS_PROJECT_REGISTRATION_RESPONSE_TOO_LARGE":
59025
+ return 413;
59026
+ case "MEMENTOS_PROJECT_REGISTRATION_TIME_BUDGET_EXCEEDED":
59027
+ return 408;
59028
+ case "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE":
59029
+ return 503;
59030
+ default:
59031
+ return 409;
59032
+ }
59033
+ }
59034
+ async function readJson2(request) {
59035
+ try {
59036
+ const value = await request.json();
59037
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
59038
+ } catch {
59039
+ return null;
59040
+ }
59041
+ }
59042
+ function fromWireRequest(body) {
59043
+ const { canonical_path: canonicalPath, ...request } = body;
59044
+ if (typeof canonicalPath !== "string") {
59045
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "canonical_path is required on the private registration transport");
59046
+ }
59047
+ return {
59048
+ ...request,
59049
+ target: new WirePathHandle(canonicalPath)
59050
+ };
59051
+ }
59052
+ function fromWireReadRequest(body) {
59053
+ const { canonical_path: canonicalPath, ...request } = body;
59054
+ if (typeof canonicalPath !== "string") {
59055
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "canonical_path is required on the private exact-read transport");
59056
+ }
59057
+ return {
59058
+ ...request,
59059
+ target: new WirePathHandle(canonicalPath)
59060
+ };
59061
+ }
59062
+ async function handleMementosProjectRegistrationHttpRequest(request, url, authority, basePath = "/v1/project-registration") {
59063
+ const path = url.pathname;
59064
+ if (path !== basePath && !path.startsWith(`${basePath}/`))
59065
+ return null;
59066
+ const action = path.slice(basePath.length).split("/").filter(Boolean).join("/");
59067
+ const method = request.method.toUpperCase();
59068
+ try {
59069
+ if ((action === "" || action === "capability") && method === "GET") {
59070
+ return json2({ capability: await authority.capability() });
59071
+ }
59072
+ if (method !== "POST")
59073
+ return json2({ error: "method not allowed" }, 405);
59074
+ const body = await readJson2(request);
59075
+ if (!body) {
59076
+ return json2({
59077
+ error: "invalid JSON body",
59078
+ code: "MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT"
59079
+ }, 400);
59080
+ }
59081
+ if (action === "create") {
59082
+ return json2({ receipt: await authority.create(fromWireRequest(body)) }, 201);
59083
+ }
59084
+ if (action === "receipts/lookup") {
59085
+ return json2(await authority.lookupReceipt(body));
59086
+ }
59087
+ if (action === "read-exact") {
59088
+ return json2({ record: await authority.readExact(fromWireReadRequest(body)) });
59089
+ }
59090
+ if (action === "compensate") {
59091
+ return json2({ receipt: await authority.compensate(fromWireRequest(body)) }, 201);
59092
+ }
59093
+ if (action === "verify-inverse") {
59094
+ return json2({ verification: await authority.verifyInverse(fromWireRequest(body)) });
59095
+ }
59096
+ return json2({
59097
+ error: "unknown Mementos project-registration route",
59098
+ code: "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND"
59099
+ }, 404);
59100
+ } catch (cause) {
59101
+ if (cause instanceof MementosProjectRegistrationError) {
59102
+ return json2({
59103
+ error: cause.message,
59104
+ code: cause.code,
59105
+ details: cause.details,
59106
+ authoritative: true
59107
+ }, errorStatus(cause));
59108
+ }
59109
+ return json2({
59110
+ error: cause instanceof Error ? cause.message : "internal registration error",
59111
+ code: "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE"
59112
+ }, 500);
59113
+ }
59114
+ }
59115
+ // src/server/routes/project-registration.ts
59116
+ init_router();
59117
+ var handle = async (request, url) => {
59118
+ const basePath = url.pathname.startsWith("/v1/") ? "/v1/project-registration" : "/api/project-registration";
59119
+ const response = await handleMementosProjectRegistrationHttpRequest(request, url, createMementosProjectRegistrationAuthority(getDatabase()), basePath);
59120
+ return response ?? new Response(JSON.stringify({ error: "Not found" }), {
59121
+ status: 404,
59122
+ headers: { "Content-Type": "application/json" }
59123
+ });
59124
+ };
59125
+ addRoute("GET", "/api/project-registration", handle);
59126
+ addRoute("GET", "/api/project-registration/capability", handle);
59127
+ addRoute("POST", "/api/project-registration/create", handle);
59128
+ addRoute("POST", "/api/project-registration/receipts/lookup", handle);
59129
+ addRoute("POST", "/api/project-registration/read-exact", handle);
59130
+ addRoute("POST", "/api/project-registration/compensate", handle);
59131
+ addRoute("POST", "/api/project-registration/verify-inverse", handle);
59132
+
56698
59133
  // src/server/routes/entities.ts
56699
59134
  init_entities();
56700
59135
  init_relations();
@@ -57737,12 +60172,12 @@ function executeAddTag(proposal, d) {
57737
60172
  return { old_tags: rollback };
57738
60173
  }
57739
60174
  function executeMerge(proposal, d) {
57740
- const targetId = proposal.target_memory_id;
57741
- if (!targetId)
60175
+ const targetId2 = proposal.target_memory_id;
60176
+ if (!targetId2)
57742
60177
  throw new Error("merge proposal missing target_memory_id");
57743
- const target = getMemory(targetId, d);
60178
+ const target = getMemory(targetId2, d);
57744
60179
  if (!target)
57745
- throw new Error(`Target memory ${targetId} not found`);
60180
+ throw new Error(`Target memory ${targetId2} not found`);
57746
60181
  const rollback = {
57747
60182
  target_old_value: target.value,
57748
60183
  target_old_version: target.version,
@@ -57750,7 +60185,7 @@ function executeMerge(proposal, d) {
57750
60185
  };
57751
60186
  const sourceValues = [];
57752
60187
  for (const memId of proposal.memory_ids) {
57753
- if (memId === targetId)
60188
+ if (memId === targetId2)
57754
60189
  continue;
57755
60190
  const mem = getMemory(memId, d);
57756
60191
  if (!mem)
@@ -57761,9 +60196,9 @@ function executeMerge(proposal, d) {
57761
60196
  const mergedValue = proposal.proposed_changes["merged_value"] ?? [target.value, ...sourceValues].join(`
57762
60197
  ---
57763
60198
  `);
57764
- d.run("UPDATE memories SET value = ?, version = version + 1, updated_at = ? WHERE id = ?", [mergedValue, now(), targetId]);
60199
+ d.run("UPDATE memories SET value = ?, version = version + 1, updated_at = ? WHERE id = ?", [mergedValue, now(), targetId2]);
57765
60200
  for (const memId of proposal.memory_ids) {
57766
- if (memId === targetId)
60201
+ if (memId === targetId2)
57767
60202
  continue;
57768
60203
  d.run("UPDATE memories SET status = 'archived', updated_at = ? WHERE id = ?", [now(), memId]);
57769
60204
  }
@@ -57853,9 +60288,9 @@ function rollbackProposal(proposal, d) {
57853
60288
  const targetOldValue = rb["target_old_value"];
57854
60289
  const targetOldVersion = rb["target_old_version"];
57855
60290
  const archivedMemories = rb["archived_memories"];
57856
- const targetId = proposal.target_memory_id;
57857
- if (targetId && targetOldValue !== undefined && targetOldVersion !== undefined) {
57858
- d.run("UPDATE memories SET value = ?, version = ?, updated_at = ? WHERE id = ?", [targetOldValue, targetOldVersion, now(), targetId]);
60291
+ const targetId2 = proposal.target_memory_id;
60292
+ if (targetId2 && targetOldValue !== undefined && targetOldVersion !== undefined) {
60293
+ d.run("UPDATE memories SET value = ?, version = ?, updated_at = ? WHERE id = ?", [targetOldValue, targetOldVersion, now(), targetId2]);
57859
60294
  }
57860
60295
  if (archivedMemories) {
57861
60296
  for (const [memId, status] of Object.entries(archivedMemories)) {
@@ -59453,18 +61888,18 @@ function pkgVersion() {
59453
61888
  }
59454
61889
  async function findFreePort(start) {
59455
61890
  const net = await import("net");
59456
- return new Promise((resolve5) => {
61891
+ return new Promise((resolve6) => {
59457
61892
  const server = net.createServer();
59458
61893
  server.unref();
59459
61894
  server.on("error", () => {
59460
- resolve5(findFreePort(start + 1));
61895
+ resolve6(findFreePort(start + 1));
59461
61896
  });
59462
61897
  server.listen(start, () => {
59463
61898
  const address = server.address();
59464
61899
  if (address && typeof address === "object") {
59465
- server.close(() => resolve5(address.port));
61900
+ server.close(() => resolve6(address.port));
59466
61901
  } else {
59467
- resolve5(start);
61902
+ resolve6(start);
59468
61903
  }
59469
61904
  });
59470
61905
  });
@@ -59639,15 +62074,15 @@ function startServer(port) {
59639
62074
  const dashDir = resolveDashboardDir();
59640
62075
  if (existsSync5(dashDir) && (req.method === "GET" || req.method === "HEAD")) {
59641
62076
  if (pathname !== "/") {
59642
- const resolvedDash = resolve4(dashDir) + sep;
59643
- const requestedPath = resolve4(join6(dashDir, pathname));
62077
+ const resolvedDash = resolve5(dashDir) + sep;
62078
+ const requestedPath = resolve5(join7(dashDir, pathname));
59644
62079
  if (requestedPath.startsWith(resolvedDash)) {
59645
62080
  const staticRes = serveStaticFile(requestedPath);
59646
62081
  if (staticRes)
59647
62082
  return staticRes;
59648
62083
  }
59649
62084
  }
59650
- const indexRes = serveStaticFile(join6(dashDir, "index.html"));
62085
+ const indexRes = serveStaticFile(join7(dashDir, "index.html"));
59651
62086
  if (indexRes)
59652
62087
  return indexRes;
59653
62088
  }