@hasna/mementos 0.14.80 → 0.14.81

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.
@@ -57624,8 +57624,8 @@ function normalizeProjectUpdateInput(input) {
57624
57624
  }
57625
57625
  return normalized;
57626
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) {
57627
+ function assertProjectUpdateIdentity(identity, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
57628
+ if (identity.authority_id !== expectedIdentity.authority_id || identity.tenant_id !== expectedIdentity.tenant_id || identity.corpus_id !== expectedIdentity.corpus_id) {
57629
57629
  throw new ProjectGuardedUpdateError("PROJECT_UPDATE_AUTHORITY_MISMATCH", "guarded project update does not match this authority, tenant, and corpus");
57630
57630
  }
57631
57631
  }
@@ -57634,8 +57634,8 @@ function assertBoundedIdentifier2(value, field) {
57634
57634
  throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", `${field} must be an 8-128 character bounded identifier`);
57635
57635
  }
57636
57636
  }
57637
- function assertProjectUpdateRequest(request) {
57638
- assertProjectUpdateIdentity(request);
57637
+ function assertProjectUpdateRequest(request, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
57638
+ assertProjectUpdateIdentity(request, expectedIdentity);
57639
57639
  assertBoundedIdentifier2(request.operation_id, "operation_id");
57640
57640
  assertBoundedIdentifier2(request.step_id, "step_id");
57641
57641
  assertBoundedIdentifier2(request.idempotency_key, "idempotency_key");
@@ -57748,7 +57748,7 @@ function makeProjectUpdateReceipt(input) {
57748
57748
  target_id: input.target_id,
57749
57749
  expected_revision: input.request.expected_revision,
57750
57750
  result_revision: input.after_project.updated_at,
57751
- result_digest: digestProjectUpdateValue(input.after_project),
57751
+ result_digest: input.result_digest ?? digestProjectUpdateValue(input.after_project),
57752
57752
  accepted_receipt_id: input.accepted_receipt_id ?? null,
57753
57753
  before_project: input.before_project,
57754
57754
  after_project: input.after_project,
@@ -57812,8 +57812,8 @@ function listProjects(db) {
57812
57812
  const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
57813
57813
  return rows.map(parseProjectRow2);
57814
57814
  }
57815
- function previewProjectUpdate(id, request, db) {
57816
- assertProjectUpdateRequest(request);
57815
+ function previewProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
57816
+ assertProjectUpdateRequest(request, expectedIdentity);
57817
57817
  const normalized = normalizeProjectUpdateInput(request.updates);
57818
57818
  if (!db && isApiMode()) {
57819
57819
  const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-update`, { ...request, updates: normalized, dry_run: true });
@@ -57835,8 +57835,8 @@ function previewProjectUpdate(id, request, db) {
57835
57835
  receipt: null
57836
57836
  };
57837
57837
  }
57838
- function applyProjectUpdate(id, request, db) {
57839
- assertProjectUpdateRequest(request);
57838
+ function applyProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY, resultDigestForProject) {
57839
+ assertProjectUpdateRequest(request, expectedIdentity);
57840
57840
  const normalized = normalizeProjectUpdateInput(request.updates);
57841
57841
  if (!db && isApiMode()) {
57842
57842
  const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-update`, { ...request, updates: normalized, dry_run: false });
@@ -57897,14 +57897,15 @@ function applyProjectUpdate(id, request, db) {
57897
57897
  request_digest: requestDigest,
57898
57898
  target_id: id,
57899
57899
  before_project: before,
57900
- after_project: readback
57900
+ after_project: readback,
57901
+ result_digest: resultDigestForProject?.(readback)
57901
57902
  });
57902
57903
  insertProjectUpdateReceipt(d, receipt);
57903
57904
  return { dry_run: false, applied: true, project: readback, receipt };
57904
57905
  });
57905
57906
  }
57906
- function rollbackProjectUpdate(id, request, db) {
57907
- assertProjectUpdateRequest(request);
57907
+ function rollbackProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY, resultDigestForProject) {
57908
+ assertProjectUpdateRequest(request, expectedIdentity);
57908
57909
  assertBoundedIdentifier2(request.accepted_receipt_id, "accepted_receipt_id");
57909
57910
  if (!db && isApiMode()) {
57910
57911
  const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/guarded-rollback`, request);
@@ -57970,14 +57971,15 @@ function rollbackProjectUpdate(id, request, db) {
57970
57971
  target_id: id,
57971
57972
  before_project: current,
57972
57973
  after_project: readback,
57974
+ result_digest: resultDigestForProject?.(readback),
57973
57975
  accepted_receipt_id: accepted.receipt_id
57974
57976
  });
57975
57977
  insertProjectUpdateReceipt(d, receipt);
57976
57978
  return { dry_run: false, applied: true, project: readback, receipt };
57977
57979
  });
57978
57980
  }
57979
- function getProjectUpdateReceipt(id, receiptId, identity = PROJECT_UPDATE_AUTHORITY, db) {
57980
- assertProjectUpdateIdentity(identity);
57981
+ function getProjectUpdateReceipt(id, receiptId, identity = PROJECT_UPDATE_AUTHORITY, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
57982
+ assertProjectUpdateIdentity(identity, expectedIdentity);
57981
57983
  if (!db && isApiMode()) {
57982
57984
  const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/update-receipts/lookup`, { ...identity, receipt_id: receiptId });
57983
57985
  return data;
@@ -58205,9 +58207,9 @@ function assertWithinBounds(value, bounds, startedAt) {
58205
58207
  }
58206
58208
  return { response_bytes: bytes, elapsed_ms: elapsed };
58207
58209
  }
58208
- function withResponseControl(receipt, bounds, startedAt) {
58210
+ function withBoundedResponseControl(payload, bounds, startedAt) {
58209
58211
  const result = {
58210
- receipt,
58212
+ ...payload,
58211
58213
  response_control: {
58212
58214
  response_byte_limit: bounds.response_byte_limit,
58213
58215
  time_budget_ms: bounds.time_budget_ms,
@@ -58230,6 +58232,9 @@ function withResponseControl(receipt, bounds, startedAt) {
58230
58232
  result.response_control.elapsed_ms = measured.elapsed_ms;
58231
58233
  return result;
58232
58234
  }
58235
+ function withResponseControl(receipt, bounds, startedAt) {
58236
+ return withBoundedResponseControl({ receipt }, bounds, startedAt);
58237
+ }
58233
58238
  function requireString(value, field, options = {}) {
58234
58239
  const min = options.min ?? 1;
58235
58240
  const max = options.max ?? 512;
@@ -58256,6 +58261,80 @@ function ownedPath(target) {
58256
58261
  }
58257
58262
  return path;
58258
58263
  }
58264
+ function guardedAuthorityIdentity(capability) {
58265
+ return {
58266
+ authority_id: capability.authority_id,
58267
+ tenant_id: capability.tenant_id,
58268
+ corpus_id: capability.corpus_id
58269
+ };
58270
+ }
58271
+ function assertGuardedAuthorityRequest(targetId, request, capability) {
58272
+ assertBounds(request);
58273
+ requireString(targetId, "target_id", { min: 8, max: 128, pattern: OPERATION_PATTERN });
58274
+ if (request.authority !== "mementos" || request.authority_route !== MEMENTOS_PROJECT_GUARDED_UPDATE_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) {
58275
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "guarded project request does not match this authority capability identity");
58276
+ }
58277
+ return guardedAuthorityIdentity(capability);
58278
+ }
58279
+ function assertGuardedOperationFields(request) {
58280
+ requireString(request.operation_id, "operation_id", {
58281
+ min: 8,
58282
+ max: 128,
58283
+ pattern: OPERATION_PATTERN
58284
+ });
58285
+ requireString(request.step_id, "step_id", {
58286
+ min: 8,
58287
+ max: 128,
58288
+ pattern: OPERATION_PATTERN
58289
+ });
58290
+ requireString(request.idempotency_key, "idempotency_key", {
58291
+ min: 8,
58292
+ max: 128,
58293
+ pattern: OPERATION_PATTERN
58294
+ });
58295
+ requireString(request.expected_revision, "expected_revision", { max: 128 });
58296
+ }
58297
+ function assertGuardedUpdateRequest(targetId, request, capability) {
58298
+ const identity = assertGuardedAuthorityRequest(targetId, request, capability);
58299
+ assertGuardedOperationFields(request);
58300
+ if (!request.updates || typeof request.updates !== "object") {
58301
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "guarded project updates must contain exactly one private path handle");
58302
+ }
58303
+ exactKeys(request.updates, ["path"], "updates");
58304
+ return { identity, path: ownedPath(request.updates.path) };
58305
+ }
58306
+ function publicGuardedUpdateReceipt(receipt, capability) {
58307
+ return {
58308
+ receipt_id: receipt.receipt_id,
58309
+ authority: "mementos",
58310
+ route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
58311
+ package_version: capability.package_version,
58312
+ authority_id: capability.authority_id,
58313
+ tenant_id: capability.tenant_id,
58314
+ corpus_id: capability.corpus_id,
58315
+ operation_id: receipt.operation_id,
58316
+ step_id: receipt.step_id,
58317
+ direction: receipt.direction,
58318
+ idempotency_key: receipt.idempotency_key,
58319
+ request_digest: receipt.request_digest,
58320
+ outcome: "accepted",
58321
+ target_id: receipt.target_id,
58322
+ expected_revision: receipt.expected_revision,
58323
+ result_revision: receipt.result_revision,
58324
+ result_digest: receipt.result_digest,
58325
+ accepted_receipt_id: receipt.accepted_receipt_id,
58326
+ created_at: receipt.created_at
58327
+ };
58328
+ }
58329
+ function guardedProjectError(cause) {
58330
+ if (cause instanceof MementosProjectRegistrationError)
58331
+ throw cause;
58332
+ if (cause instanceof ProjectGuardedUpdateError) {
58333
+ const code = cause.code === "PROJECT_UPDATE_INVALID_INPUT" ? "MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT" : cause.code === "PROJECT_UPDATE_AUTHORITY_MISMATCH" ? "MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH" : cause.code === "PROJECT_UPDATE_NOT_FOUND" ? "MEMENTOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND" : cause.code === "PROJECT_UPDATE_RECEIPT_NOT_FOUND" ? "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND" : "MEMENTOS_PROJECT_REGISTRATION_CONFLICT";
58334
+ throw new MementosProjectRegistrationError(code, "guarded project operation was rejected without exposing private project data", { project_update_code: cause.code });
58335
+ }
58336
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", "guarded project operation failed before a bounded public result was available");
58337
+ }
58259
58338
  function normalizedCallDigest(request) {
58260
58339
  return digestMementosProjectRegistrationValue({
58261
58340
  authority_route: request.authority_route,
@@ -58642,6 +58721,7 @@ class PackageOwnedMementosProjectRegistrationAuthority {
58642
58721
  conditional_inverse: true,
58643
58722
  ambiguous_outcome_reconciliation: true,
58644
58723
  guarded_update: true,
58724
+ guarded_update_route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
58645
58725
  no_write_dry_run: true,
58646
58726
  expected_revision_compare_and_swap: true,
58647
58727
  caller_idempotency: true,
@@ -58703,6 +58783,90 @@ class PackageOwnedMementosProjectRegistrationAuthority {
58703
58783
  supported_resources: ["project"]
58704
58784
  };
58705
58785
  }
58786
+ boundedGuardedUpdateResult(targetId2, project, receipt, bounds, startedAt) {
58787
+ if (!receipt || project.id !== targetId2 || receipt.target_id !== targetId2) {
58788
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", "guarded project operation did not return its exact immutable receipt");
58789
+ }
58790
+ if (project.updated_at !== receipt.result_revision) {
58791
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", "guarded project result revision did not match its immutable receipt");
58792
+ }
58793
+ const record = {
58794
+ target_id: targetId2,
58795
+ revision: receipt.result_revision,
58796
+ digest: receipt.result_digest
58797
+ };
58798
+ return withBoundedResponseControl({
58799
+ dry_run: false,
58800
+ applied: true,
58801
+ record,
58802
+ receipt: publicGuardedUpdateReceipt(receipt, this.capabilityValue)
58803
+ }, bounds, startedAt);
58804
+ }
58805
+ async guardedUpdateProject(targetId2, request) {
58806
+ const startedAt = Date.now();
58807
+ const { identity, path } = assertGuardedUpdateRequest(targetId2, request, this.capabilityValue);
58808
+ try {
58809
+ const result = applyProjectUpdate(targetId2, {
58810
+ ...identity,
58811
+ operation_id: request.operation_id,
58812
+ step_id: request.step_id,
58813
+ idempotency_key: request.idempotency_key,
58814
+ expected_revision: request.expected_revision,
58815
+ updates: { path }
58816
+ }, this.db, identity, (project) => projectRecord(this.db, project).digest);
58817
+ return this.boundedGuardedUpdateResult(targetId2, result.project, result.receipt, request, startedAt);
58818
+ } catch (cause) {
58819
+ return guardedProjectError(cause);
58820
+ }
58821
+ }
58822
+ async getGuardedProjectUpdateReceipt(targetId2, receiptId2, request) {
58823
+ const startedAt = Date.now();
58824
+ const identity = assertGuardedAuthorityRequest(targetId2, request, this.capabilityValue);
58825
+ requireString(receiptId2, "receipt_id", {
58826
+ min: 8,
58827
+ max: 128,
58828
+ pattern: OPERATION_PATTERN
58829
+ });
58830
+ try {
58831
+ const receipt = getProjectUpdateReceipt(targetId2, receiptId2, identity, this.db, identity);
58832
+ return withBoundedResponseControl({
58833
+ receipt: publicGuardedUpdateReceipt(receipt, this.capabilityValue)
58834
+ }, request, startedAt);
58835
+ } catch (cause) {
58836
+ return guardedProjectError(cause);
58837
+ }
58838
+ }
58839
+ async rollbackGuardedProjectUpdate(targetId2, request) {
58840
+ const startedAt = Date.now();
58841
+ const identity = assertGuardedAuthorityRequest(targetId2, request, this.capabilityValue);
58842
+ assertGuardedOperationFields(request);
58843
+ if (!request.accepted_receipt || typeof request.accepted_receipt !== "object") {
58844
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional rollback requires the exact sanitized accepted receipt");
58845
+ }
58846
+ requireString(request.accepted_receipt.receipt_id, "accepted_receipt.receipt_id", {
58847
+ min: 8,
58848
+ max: 128,
58849
+ pattern: OPERATION_PATTERN
58850
+ });
58851
+ try {
58852
+ const internalAccepted = getProjectUpdateReceipt(targetId2, request.accepted_receipt.receipt_id, identity, this.db, identity);
58853
+ const accepted = publicGuardedUpdateReceipt(internalAccepted, this.capabilityValue);
58854
+ if (accepted.direction !== "forward" || accepted.target_id !== targetId2 || request.expected_revision !== accepted.result_revision || canonicalMementosProjectRegistrationJson(request.accepted_receipt) !== canonicalMementosProjectRegistrationJson(accepted)) {
58855
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional rollback receipt does not exactly match the accepted forward update");
58856
+ }
58857
+ const result = rollbackProjectUpdate(targetId2, {
58858
+ ...identity,
58859
+ operation_id: request.operation_id,
58860
+ step_id: request.step_id,
58861
+ idempotency_key: request.idempotency_key,
58862
+ expected_revision: request.expected_revision,
58863
+ accepted_receipt_id: accepted.receipt_id
58864
+ }, this.db, identity, (project) => projectRecord(this.db, project).digest);
58865
+ return this.boundedGuardedUpdateResult(targetId2, result.project, result.receipt, request, startedAt);
58866
+ } catch (cause) {
58867
+ return guardedProjectError(cause);
58868
+ }
58869
+ }
58706
58870
  async create(request) {
58707
58871
  const startedAt = Date.now();
58708
58872
  const path = assertForwardRequest2(request, this.capabilityValue);
@@ -58799,9 +58963,9 @@ class PackageOwnedMementosProjectRegistrationAuthority {
58799
58963
  throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project");
58800
58964
  }
58801
58965
  requireString(request.target_id, "target_id", {
58802
- min: 51,
58803
- max: 51,
58804
- pattern: PROJECT_ID_PATTERN
58966
+ min: 8,
58967
+ max: 128,
58968
+ pattern: OPERATION_PATTERN
58805
58969
  });
58806
58970
  const path = ownedPath(request.target);
58807
58971
  const project = getProjectByExactId3(this.db, request.target_id);
@@ -59059,6 +59223,31 @@ function fromWireReadRequest(body) {
59059
59223
  target: new WirePathHandle(canonicalPath)
59060
59224
  };
59061
59225
  }
59226
+ function fromWireGuardedUpdateRequest(body) {
59227
+ const { target_id: targetId2, updates, ...request } = body;
59228
+ if (typeof targetId2 !== "string") {
59229
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target_id is required on the private guarded-update transport");
59230
+ }
59231
+ if (!updates || typeof updates !== "object" || Array.isArray(updates) || typeof updates["path"] !== "string") {
59232
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "updates.path is required on the private guarded-update transport");
59233
+ }
59234
+ return {
59235
+ targetId: targetId2,
59236
+ request: {
59237
+ ...request,
59238
+ updates: {
59239
+ path: new WirePathHandle(String(updates["path"]))
59240
+ }
59241
+ }
59242
+ };
59243
+ }
59244
+ function exactWireIdentifier(body, field) {
59245
+ const value = body[field];
59246
+ if (typeof value !== "string") {
59247
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} is required on the guarded-update transport`);
59248
+ }
59249
+ return value;
59250
+ }
59062
59251
  async function handleMementosProjectRegistrationHttpRequest(request, url, authority, basePath = "/v1/project-registration") {
59063
59252
  const path = url.pathname;
59064
59253
  if (path !== basePath && !path.startsWith(`${basePath}/`))
@@ -59093,6 +59282,21 @@ async function handleMementosProjectRegistrationHttpRequest(request, url, author
59093
59282
  if (action === "verify-inverse") {
59094
59283
  return json2({ verification: await authority.verifyInverse(fromWireRequest(body)) });
59095
59284
  }
59285
+ if (action === "projects/guarded-update") {
59286
+ const guarded = fromWireGuardedUpdateRequest(body);
59287
+ return json2(await authority.guardedUpdateProject(guarded.targetId, guarded.request));
59288
+ }
59289
+ if (action === "projects/update-receipts/lookup") {
59290
+ const targetId2 = exactWireIdentifier(body, "target_id");
59291
+ const receiptId2 = exactWireIdentifier(body, "receipt_id");
59292
+ const { target_id: _targetId, receipt_id: _receiptId, ...lookup } = body;
59293
+ return json2(await authority.getGuardedProjectUpdateReceipt(targetId2, receiptId2, lookup));
59294
+ }
59295
+ if (action === "projects/guarded-rollback") {
59296
+ const targetId2 = exactWireIdentifier(body, "target_id");
59297
+ const { target_id: _targetId, ...rollback } = body;
59298
+ return json2(await authority.rollbackGuardedProjectUpdate(targetId2, rollback));
59299
+ }
59096
59300
  return json2({
59097
59301
  error: "unknown Mementos project-registration route",
59098
59302
  code: "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND"
@@ -59129,6 +59333,9 @@ addRoute("POST", "/api/project-registration/receipts/lookup", handle);
59129
59333
  addRoute("POST", "/api/project-registration/read-exact", handle);
59130
59334
  addRoute("POST", "/api/project-registration/compensate", handle);
59131
59335
  addRoute("POST", "/api/project-registration/verify-inverse", handle);
59336
+ addRoute("POST", "/api/project-registration/projects/guarded-update", handle);
59337
+ addRoute("POST", "/api/project-registration/projects/update-receipts/lookup", handle);
59338
+ addRoute("POST", "/api/project-registration/projects/guarded-rollback", handle);
59132
59339
 
59133
59340
  // src/server/routes/entities.ts
59134
59341
  init_entities();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/mementos",
3
- "version": "0.14.80",
3
+ "version": "0.14.81",
4
4
  "description": "Universal memory system for AI agents - CLI + MCP server + library API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -108,5 +108,5 @@
108
108
  "sigstore": "5.0.0",
109
109
  "typescript": "^5.7.3"
110
110
  },
111
- "gitHead": "a45d90c4a854aae12d57168ca31897213dbfca58"
111
+ "gitHead": "cb2c4c46e64f71e28018307fe00b7083e2e0a8aa"
112
112
  }