@hasna/mementos 0.14.81 → 0.14.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50488,6 +50488,98 @@ function getMementosPackageVersion() {
50488
50488
  // src/db/memory-project-link.ts
50489
50489
  init_schema();
50490
50490
 
50491
+ // src/project-registration/types.ts
50492
+ var MEMENTOS_PROJECT_REGISTRATION_ROUTE = "mementos.project-registration.v1";
50493
+ var MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE = "projects.full-registration.v1";
50494
+ var MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION = 1;
50495
+ var MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE = "mementos.project-guarded-update.v1";
50496
+ var MEMENTOS_PROJECT_RESOURCE_ROUTE = "mementos.project-resources.v1";
50497
+ var MEMENTOS_PROJECT_RESOURCE_KINDS = [
50498
+ "project",
50499
+ "knowledge",
50500
+ "memory",
50501
+ "session"
50502
+ ];
50503
+
50504
+ class MementosProjectRegistrationError extends Error {
50505
+ code;
50506
+ details;
50507
+ constructor(code, message, details = {}) {
50508
+ super(message);
50509
+ this.code = code;
50510
+ this.details = details;
50511
+ this.name = "MementosProjectRegistrationError";
50512
+ }
50513
+ }
50514
+
50515
+ // src/project-registration/identity.ts
50516
+ var MEMENTOS_PROJECT_AUTHORITY_ENV = {
50517
+ authorityId: "MEMENTOS_PROJECT_AUTHORITY_ID",
50518
+ tenantId: "MEMENTOS_PROJECT_TENANT_ID",
50519
+ corpusId: "MEMENTOS_PROJECT_CORPUS_ID"
50520
+ };
50521
+ function configuredValue(override, envKey) {
50522
+ return override?.trim() || process.env[envKey]?.trim() || null;
50523
+ }
50524
+
50525
+ class MementosProjectAuthorityIdentityError extends Error {
50526
+ missing_env;
50527
+ code = "MEMENTOS_PROJECT_AUTHORITY_UNCONFIGURED";
50528
+ constructor(missing_env) {
50529
+ super("Mementos project authority identity is not configured; set " + missing_env.join(", "));
50530
+ this.missing_env = missing_env;
50531
+ this.name = "MementosProjectAuthorityIdentityError";
50532
+ }
50533
+ }
50534
+ function resolveMementosProjectAuthorityIdentity(options = {}) {
50535
+ const authorityId = configuredValue(options.authorityId, MEMENTOS_PROJECT_AUTHORITY_ENV.authorityId);
50536
+ const tenantId = configuredValue(options.tenantId, MEMENTOS_PROJECT_AUTHORITY_ENV.tenantId);
50537
+ const corpusId = configuredValue(options.corpusId, MEMENTOS_PROJECT_AUTHORITY_ENV.corpusId);
50538
+ if (!authorityId || !tenantId || !corpusId) {
50539
+ const missingEnv = [];
50540
+ if (!authorityId)
50541
+ missingEnv.push(MEMENTOS_PROJECT_AUTHORITY_ENV.authorityId);
50542
+ if (!tenantId)
50543
+ missingEnv.push(MEMENTOS_PROJECT_AUTHORITY_ENV.tenantId);
50544
+ if (!corpusId)
50545
+ missingEnv.push(MEMENTOS_PROJECT_AUTHORITY_ENV.corpusId);
50546
+ throw new MementosProjectAuthorityIdentityError(missingEnv);
50547
+ }
50548
+ return {
50549
+ authority_id: authorityId,
50550
+ tenant_id: tenantId,
50551
+ corpus_id: corpusId
50552
+ };
50553
+ }
50554
+ function buildMementosProjectRegistrationCapability(options = {}) {
50555
+ const identity = resolveMementosProjectAuthorityIdentity(options);
50556
+ return {
50557
+ authority: "mementos",
50558
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
50559
+ package_version: options.packageVersion ?? getMementosPackageVersion(),
50560
+ ...identity,
50561
+ supported_resources: ["project"],
50562
+ conditional_create: true,
50563
+ immutable_receipts: true,
50564
+ exact_terminal_lookup: true,
50565
+ exact_readback: true,
50566
+ conditional_inverse: true,
50567
+ ambiguous_outcome_reconciliation: true,
50568
+ guarded_update: true,
50569
+ guarded_update_route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
50570
+ no_write_dry_run: true,
50571
+ expected_revision_compare_and_swap: true,
50572
+ caller_idempotency: true,
50573
+ exact_inverse_rollback: true,
50574
+ project_resource_enumeration: true,
50575
+ project_resource_route: MEMENTOS_PROJECT_RESOURCE_ROUTE,
50576
+ project_resource_kinds: ["project", "knowledge", "memory", "session"],
50577
+ stable_keyset_pagination: true,
50578
+ explicit_membership_only: true
50579
+ };
50580
+ }
50581
+
50582
+ // src/db/memory-project-link.ts
50491
50583
  class MemoryProjectLinkError extends Error {
50492
50584
  code;
50493
50585
  details;
@@ -50498,12 +50590,17 @@ class MemoryProjectLinkError extends Error {
50498
50590
  this.name = "MemoryProjectLinkError";
50499
50591
  }
50500
50592
  }
50501
- var LINK_AUTHORITY = {
50502
- authority_id: "mementos",
50503
- tenant_id: "default",
50504
- corpus_id: "default"
50505
- };
50506
50593
  var BOUNDED_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
50594
+ function linkAuthority() {
50595
+ try {
50596
+ return resolveMementosProjectAuthorityIdentity();
50597
+ } catch (error) {
50598
+ if (error instanceof MementosProjectAuthorityIdentityError) {
50599
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_AUTHORITY_MISMATCH", error.message, { authority_code: error.code, missing_env: error.missing_env });
50600
+ }
50601
+ throw error;
50602
+ }
50603
+ }
50507
50604
  function canonicalize(value) {
50508
50605
  if (Array.isArray(value))
50509
50606
  return value.map(canonicalize);
@@ -50611,7 +50708,8 @@ function receiptFromRow(row) {
50611
50708
  };
50612
50709
  }
50613
50710
  function assertIdentity(identity) {
50614
- if (identity.authority_id !== LINK_AUTHORITY.authority_id || identity.tenant_id !== LINK_AUTHORITY.tenant_id || identity.corpus_id !== LINK_AUTHORITY.corpus_id) {
50711
+ const expectedIdentity = linkAuthority();
50712
+ if (identity.authority_id !== expectedIdentity.authority_id || identity.tenant_id !== expectedIdentity.tenant_id || identity.corpus_id !== expectedIdentity.corpus_id) {
50615
50713
  throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_AUTHORITY_MISMATCH", "memory project link does not match this authority, tenant, and corpus");
50616
50714
  }
50617
50715
  }
@@ -51088,7 +51186,7 @@ function rollbackMemoryProjectLink(memoryId, request, db) {
51088
51186
  };
51089
51187
  });
51090
51188
  }
51091
- function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = LINK_AUTHORITY, db) {
51189
+ function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = linkAuthority(), db) {
51092
51190
  assertIdentity(identity);
51093
51191
  assertBoundedIdentifier(memoryId, "memory_id");
51094
51192
  assertBoundedIdentifier(receiptId, "receipt_id");
@@ -51553,25 +51651,6 @@ function focusFilterSQL(agentId, projectId) {
51553
51651
  init_database();
51554
51652
  init_api_mode();
51555
51653
  import { createHash as createHash2 } from "crypto";
51556
-
51557
- // src/project-registration/types.ts
51558
- var MEMENTOS_PROJECT_REGISTRATION_ROUTE = "mementos.project-registration.v1";
51559
- var MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE = "projects.full-registration.v1";
51560
- var MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION = 1;
51561
- var MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE = "mementos.project-guarded-update.v1";
51562
-
51563
- class MementosProjectRegistrationError extends Error {
51564
- code;
51565
- details;
51566
- constructor(code, message, details = {}) {
51567
- super(message);
51568
- this.code = code;
51569
- this.details = details;
51570
- this.name = "MementosProjectRegistrationError";
51571
- }
51572
- }
51573
-
51574
- // src/db/projects.ts
51575
51654
  function parseProjectRow2(row) {
51576
51655
  return {
51577
51656
  id: row["id"],
@@ -51605,12 +51684,17 @@ class ProjectGuardedUpdateError extends Error {
51605
51684
  this.name = "ProjectGuardedUpdateError";
51606
51685
  }
51607
51686
  }
51608
- var PROJECT_UPDATE_AUTHORITY = {
51609
- authority_id: "mementos",
51610
- tenant_id: "default",
51611
- corpus_id: "default"
51612
- };
51613
51687
  var BOUNDED_IDENTIFIER2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
51688
+ function projectUpdateAuthority() {
51689
+ try {
51690
+ return resolveMementosProjectAuthorityIdentity();
51691
+ } catch (error) {
51692
+ if (error instanceof MementosProjectAuthorityIdentityError) {
51693
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_AUTHORITY_MISMATCH", error.message, { authority_code: error.code, missing_env: error.missing_env });
51694
+ }
51695
+ throw error;
51696
+ }
51697
+ }
51614
51698
  function canonicalizeProjectUpdateValue(value) {
51615
51699
  if (Array.isArray(value))
51616
51700
  return value.map(canonicalizeProjectUpdateValue);
@@ -51683,7 +51767,7 @@ function normalizeProjectUpdateInput(input) {
51683
51767
  }
51684
51768
  return normalized;
51685
51769
  }
51686
- function assertProjectUpdateIdentity(identity, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
51770
+ function assertProjectUpdateIdentity(identity, expectedIdentity = projectUpdateAuthority()) {
51687
51771
  if (identity.authority_id !== expectedIdentity.authority_id || identity.tenant_id !== expectedIdentity.tenant_id || identity.corpus_id !== expectedIdentity.corpus_id) {
51688
51772
  throw new ProjectGuardedUpdateError("PROJECT_UPDATE_AUTHORITY_MISMATCH", "guarded project update does not match this authority, tenant, and corpus");
51689
51773
  }
@@ -51693,7 +51777,7 @@ function assertBoundedIdentifier2(value, field) {
51693
51777
  throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", `${field} must be an 8-128 character bounded identifier`);
51694
51778
  }
51695
51779
  }
51696
- function assertProjectUpdateRequest(request, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
51780
+ function assertProjectUpdateRequest(request, expectedIdentity = projectUpdateAuthority()) {
51697
51781
  assertProjectUpdateIdentity(request, expectedIdentity);
51698
51782
  assertBoundedIdentifier2(request.operation_id, "operation_id");
51699
51783
  assertBoundedIdentifier2(request.step_id, "step_id");
@@ -51871,7 +51955,7 @@ function listProjects(db) {
51871
51955
  const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
51872
51956
  return rows.map(parseProjectRow2);
51873
51957
  }
51874
- function previewProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
51958
+ function previewProjectUpdate(id, request, db, expectedIdentity = projectUpdateAuthority()) {
51875
51959
  assertProjectUpdateRequest(request, expectedIdentity);
51876
51960
  const normalized = normalizeProjectUpdateInput(request.updates);
51877
51961
  if (!db && isApiMode()) {
@@ -51894,7 +51978,7 @@ function previewProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE
51894
51978
  receipt: null
51895
51979
  };
51896
51980
  }
51897
- function applyProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY, resultDigestForProject) {
51981
+ function applyProjectUpdate(id, request, db, expectedIdentity = projectUpdateAuthority(), resultDigestForProject) {
51898
51982
  assertProjectUpdateRequest(request, expectedIdentity);
51899
51983
  const normalized = normalizeProjectUpdateInput(request.updates);
51900
51984
  if (!db && isApiMode()) {
@@ -51963,7 +52047,7 @@ function applyProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_A
51963
52047
  return { dry_run: false, applied: true, project: readback, receipt };
51964
52048
  });
51965
52049
  }
51966
- function rollbackProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY, resultDigestForProject) {
52050
+ function rollbackProjectUpdate(id, request, db, expectedIdentity = projectUpdateAuthority(), resultDigestForProject) {
51967
52051
  assertProjectUpdateRequest(request, expectedIdentity);
51968
52052
  assertBoundedIdentifier2(request.accepted_receipt_id, "accepted_receipt_id");
51969
52053
  if (!db && isApiMode()) {
@@ -52037,7 +52121,7 @@ function rollbackProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDAT
52037
52121
  return { dry_run: false, applied: true, project: readback, receipt };
52038
52122
  });
52039
52123
  }
52040
- function getProjectUpdateReceipt(id, receiptId, identity = PROJECT_UPDATE_AUTHORITY, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
52124
+ function getProjectUpdateReceipt(id, receiptId, identity = projectUpdateAuthority(), db, expectedIdentity = projectUpdateAuthority()) {
52041
52125
  assertProjectUpdateIdentity(identity, expectedIdentity);
52042
52126
  if (!db && isApiMode()) {
52043
52127
  const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/update-receipts/lookup`, { ...identity, receipt_id: receiptId });
@@ -52679,27 +52763,7 @@ class PackageOwnedMementosProjectRegistrationAuthority {
52679
52763
  this.db = db;
52680
52764
  this.now = options.now ?? (() => new Date().toISOString());
52681
52765
  this.faultInjector = options.faultInjector;
52682
- this.capabilityValue = {
52683
- authority: "mementos",
52684
- route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
52685
- package_version: options.packageVersion ?? getMementosPackageVersion(),
52686
- authority_id: options.authorityId ?? "mementos",
52687
- tenant_id: options.tenantId ?? "default",
52688
- corpus_id: options.corpusId ?? "default",
52689
- supported_resources: ["project"],
52690
- conditional_create: true,
52691
- immutable_receipts: true,
52692
- exact_terminal_lookup: true,
52693
- exact_readback: true,
52694
- conditional_inverse: true,
52695
- ambiguous_outcome_reconciliation: true,
52696
- guarded_update: true,
52697
- guarded_update_route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
52698
- no_write_dry_run: true,
52699
- expected_revision_compare_and_swap: true,
52700
- caller_idempotency: true,
52701
- exact_inverse_rollback: true
52702
- };
52766
+ this.capabilityValue = buildMementosProjectRegistrationCapability(options);
52703
52767
  }
52704
52768
  fault(point, request) {
52705
52769
  this.faultInjector?.(point, {
@@ -53395,6 +53459,331 @@ class MementosProjectRegistrationHttpClient {
53395
53459
  function createMementosProjectRegistrationHttpClient(options) {
53396
53460
  return new MementosProjectRegistrationHttpClient(options);
53397
53461
  }
53462
+ // src/project-registration/project-resources.ts
53463
+ init_api_mode();
53464
+ init_database();
53465
+ var DEFAULT_PAGE_LIMIT = 100;
53466
+ var MAX_PAGE_LIMIT = 1000;
53467
+ var CURSOR_SCHEMA = "mementos.project-resources.cursor.v1";
53468
+
53469
+ class MementosProjectResourceError extends Error {
53470
+ code;
53471
+ details;
53472
+ constructor(code, message, details = {}) {
53473
+ super(message);
53474
+ this.code = code;
53475
+ this.details = details;
53476
+ this.name = "MementosProjectResourceError";
53477
+ }
53478
+ }
53479
+ function timestamp(value) {
53480
+ return value instanceof Date ? value.toISOString() : String(value);
53481
+ }
53482
+ function normalizeSqlValue(value) {
53483
+ if (value instanceof Date)
53484
+ return value.toISOString();
53485
+ if (Array.isArray(value))
53486
+ return value.map(normalizeSqlValue);
53487
+ if (!value || typeof value !== "object")
53488
+ return value;
53489
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalizeSqlValue(item)]));
53490
+ }
53491
+ function exactProject(db, projectId) {
53492
+ const row = db.get("SELECT id, name, path, description, memory_prefix, created_at, updated_at FROM projects WHERE id = ? LIMIT 1", projectId);
53493
+ if (!row) {
53494
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_PROJECT_NOT_FOUND", `Mementos project not found: ${projectId}`, { project_id: projectId });
53495
+ }
53496
+ return row;
53497
+ }
53498
+ function resourceKey(resource) {
53499
+ const rank = MEMENTOS_PROJECT_RESOURCE_KINDS.indexOf(resource.resource_kind);
53500
+ return `${String(rank).padStart(2, "0")}:${resource.stable_id}`;
53501
+ }
53502
+ function projectResource(project) {
53503
+ const normalized = normalizeSqlValue(project);
53504
+ return {
53505
+ authority: "mementos",
53506
+ source_package: "@hasna/mementos",
53507
+ project_id: project.id,
53508
+ resource_kind: "project",
53509
+ stable_id: project.id,
53510
+ revision: timestamp(project.updated_at),
53511
+ digest: digestMementosProjectRegistrationValue(normalized),
53512
+ membership: "project_aggregate"
53513
+ };
53514
+ }
53515
+ function memoryResources(db, projectId) {
53516
+ const rows = db.all("SELECT * FROM memories WHERE project_id = ? ORDER BY id ASC", projectId);
53517
+ return rows.map((row) => {
53518
+ const memory = normalizeSqlValue(parseMemoryRow(row));
53519
+ return {
53520
+ authority: "mementos",
53521
+ source_package: "@hasna/mementos",
53522
+ project_id: projectId,
53523
+ resource_kind: row["category"] === "knowledge" ? "knowledge" : "memory",
53524
+ stable_id: String(row["id"]),
53525
+ revision: timestamp(row["updated_at"]),
53526
+ digest: digestMementosProjectRegistrationValue(memory),
53527
+ membership: "explicit_project_id_or_focus"
53528
+ };
53529
+ });
53530
+ }
53531
+ function sessionResources(db, projectId) {
53532
+ const rows = db.all("SELECT * FROM session_memory_jobs WHERE project_id = ? ORDER BY id ASC", projectId);
53533
+ return rows.map((row) => {
53534
+ const normalized = {
53535
+ id: String(row["id"]),
53536
+ session_id: String(row["session_id"]),
53537
+ agent_id: row["agent_id"] === null ? null : String(row["agent_id"] ?? "") || null,
53538
+ project_id: row["project_id"] === null ? null : String(row["project_id"] ?? "") || null,
53539
+ source: String(row["source"]),
53540
+ status: String(row["status"]),
53541
+ transcript: String(row["transcript"]),
53542
+ chunk_count: Number(row["chunk_count"]),
53543
+ memories_extracted: Number(row["memories_extracted"]),
53544
+ error: row["error"] === null ? null : String(row["error"] ?? "") || null,
53545
+ metadata: typeof row["metadata"] === "string" ? JSON.parse(row["metadata"] || "{}") : normalizeSqlValue(row["metadata"] ?? {}),
53546
+ created_at: timestamp(row["created_at"]),
53547
+ started_at: row["started_at"] === null ? null : timestamp(row["started_at"]),
53548
+ completed_at: row["completed_at"] === null ? null : timestamp(row["completed_at"])
53549
+ };
53550
+ return {
53551
+ authority: "mementos",
53552
+ source_package: "@hasna/mementos",
53553
+ project_id: projectId,
53554
+ resource_kind: "session",
53555
+ stable_id: String(row["id"]),
53556
+ revision: timestamp(row["completed_at"] ?? row["started_at"] ?? row["created_at"]),
53557
+ digest: digestMementosProjectRegistrationValue(normalized),
53558
+ membership: "explicit_project_id_or_focus"
53559
+ };
53560
+ });
53561
+ }
53562
+ function normalizeResourceKinds(value) {
53563
+ if (!value)
53564
+ return [...MEMENTOS_PROJECT_RESOURCE_KINDS];
53565
+ if (value.length === 0) {
53566
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", "resource_kinds must contain at least one supported resource kind");
53567
+ }
53568
+ const requested = new Set(value);
53569
+ for (const kind of requested) {
53570
+ if (!MEMENTOS_PROJECT_RESOURCE_KINDS.includes(kind)) {
53571
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", `Unsupported Mementos project resource kind: ${kind}`);
53572
+ }
53573
+ }
53574
+ return MEMENTOS_PROJECT_RESOURCE_KINDS.filter((kind) => requested.has(kind));
53575
+ }
53576
+ function normalizeLimit(value) {
53577
+ const limit = value ?? DEFAULT_PAGE_LIMIT;
53578
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_PAGE_LIMIT) {
53579
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", `limit must be an integer between 1 and ${MAX_PAGE_LIMIT}`);
53580
+ }
53581
+ return limit;
53582
+ }
53583
+ function encodeCursor(cursor) {
53584
+ return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
53585
+ }
53586
+ function decodeCursor(raw) {
53587
+ try {
53588
+ const parsed = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
53589
+ if (parsed.schema !== CURSOR_SCHEMA || typeof parsed.project_id !== "string" || typeof parsed.collection_revision !== "string" || !Array.isArray(parsed.resource_kinds) || typeof parsed.after_key !== "string") {
53590
+ throw new Error("invalid cursor shape");
53591
+ }
53592
+ return parsed;
53593
+ } catch {
53594
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", "cursor is not a valid Mementos project-resource cursor");
53595
+ }
53596
+ }
53597
+ function localPopulation(projectId, db, resourceKinds) {
53598
+ const project = exactProject(db, projectId);
53599
+ const selected = new Set(resourceKinds);
53600
+ const resources = [
53601
+ ...selected.has("project") ? [projectResource(project)] : [],
53602
+ ...memoryResources(db, projectId).filter((resource) => selected.has(resource.resource_kind)),
53603
+ ...selected.has("session") ? sessionResources(db, projectId) : []
53604
+ ].sort((left, right) => resourceKey(left).localeCompare(resourceKey(right)));
53605
+ const collectionRevision = digestMementosProjectRegistrationValue({
53606
+ schema: MEMENTOS_PROJECT_RESOURCE_ROUTE,
53607
+ project_id: projectId,
53608
+ project_revision: timestamp(project.updated_at),
53609
+ resource_kinds: resourceKinds,
53610
+ resources: resources.map((resource) => ({
53611
+ resource_kind: resource.resource_kind,
53612
+ stable_id: resource.stable_id,
53613
+ revision: resource.revision,
53614
+ digest: resource.digest
53615
+ }))
53616
+ });
53617
+ return { project, resources, collectionRevision };
53618
+ }
53619
+ function readMementosProjectResourcePage(projectId, options = {}, db, authorityOptions = {}) {
53620
+ const resourceKinds = normalizeResourceKinds(options.resource_kinds);
53621
+ const limit = normalizeLimit(options.limit);
53622
+ if (!db && isApiMode()) {
53623
+ const { data } = apiJson("GET", `/projects/${encodeURIComponent(projectId)}/resources${toQuery({
53624
+ limit,
53625
+ cursor: options.cursor ?? undefined,
53626
+ resource_kinds: resourceKinds.join(",")
53627
+ })}`);
53628
+ return data;
53629
+ }
53630
+ const d = db ?? getDatabase();
53631
+ const { project, resources, collectionRevision } = localPopulation(projectId, d, resourceKinds);
53632
+ let start = 0;
53633
+ if (options.cursor) {
53634
+ const cursor = decodeCursor(options.cursor);
53635
+ if (cursor.project_id !== projectId || JSON.stringify(cursor.resource_kinds) !== JSON.stringify(resourceKinds)) {
53636
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", "cursor does not belong to this project and resource-kind selection");
53637
+ }
53638
+ if (cursor.collection_revision !== collectionRevision) {
53639
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_COLLECTION_CHANGED", "Mementos project resource collection changed; restart from the first page", {
53640
+ cursor_collection_revision: cursor.collection_revision,
53641
+ current_collection_revision: collectionRevision
53642
+ });
53643
+ }
53644
+ const afterIndex = resources.findIndex((resource) => resourceKey(resource) === cursor.after_key);
53645
+ if (afterIndex < 0) {
53646
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_COLLECTION_CHANGED", "Mementos project resource cursor no longer names a member; restart from the first page");
53647
+ }
53648
+ start = afterIndex + 1;
53649
+ }
53650
+ const pageResources = resources.slice(start, start + limit);
53651
+ const hasMore = start + pageResources.length < resources.length;
53652
+ const nextCursor = hasMore && pageResources.length > 0 ? encodeCursor({
53653
+ schema: CURSOR_SCHEMA,
53654
+ project_id: projectId,
53655
+ collection_revision: collectionRevision,
53656
+ resource_kinds: resourceKinds,
53657
+ after_key: resourceKey(pageResources[pageResources.length - 1])
53658
+ }) : null;
53659
+ const capability = buildMementosProjectRegistrationCapability(authorityOptions);
53660
+ return {
53661
+ schema: "mementos.project-resources.v1",
53662
+ authority: {
53663
+ authority: capability.authority,
53664
+ authority_id: capability.authority_id,
53665
+ tenant_id: capability.tenant_id,
53666
+ corpus_id: capability.corpus_id,
53667
+ package_version: capability.package_version
53668
+ },
53669
+ project_id: projectId,
53670
+ project_revision: timestamp(project.updated_at),
53671
+ collection_revision: collectionRevision,
53672
+ resource_kinds: resourceKinds,
53673
+ resources: pageResources,
53674
+ count: pageResources.length,
53675
+ total: resources.length,
53676
+ limit,
53677
+ cursor: options.cursor ?? null,
53678
+ next_cursor: nextCursor,
53679
+ has_more: hasMore,
53680
+ complete: true,
53681
+ truncated: false
53682
+ };
53683
+ }
53684
+ function readAllMementosProjectResources(projectId, options = {}, db, authorityOptions = {}) {
53685
+ const pageSize = normalizeLimit(options.page_size);
53686
+ let cursor = null;
53687
+ let first = null;
53688
+ let pageCount = 0;
53689
+ let maxPageCount = 1;
53690
+ const resources = [];
53691
+ const seen = new Set;
53692
+ const seenCursors = new Set;
53693
+ do {
53694
+ const page = readMementosProjectResourcePage(projectId, {
53695
+ limit: pageSize,
53696
+ cursor,
53697
+ resource_kinds: options.resource_kinds
53698
+ }, db, authorityOptions);
53699
+ pageCount += 1;
53700
+ if (!first) {
53701
+ first = page;
53702
+ if (!Number.isSafeInteger(first.total) || first.total < 0) {
53703
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource traversal returned an invalid total");
53704
+ }
53705
+ maxPageCount = Math.max(1, Math.ceil(first.total / pageSize));
53706
+ }
53707
+ if (page.project_id !== projectId || page.collection_revision !== first.collection_revision || page.total !== first.total || JSON.stringify(page.resource_kinds) !== JSON.stringify(first.resource_kinds)) {
53708
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_COLLECTION_CHANGED", "Mementos project resource collection changed during complete traversal");
53709
+ }
53710
+ for (const resource of page.resources) {
53711
+ const key = resourceKey(resource);
53712
+ if (seen.has(key)) {
53713
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", `Mementos project resource traversal returned duplicate stable ID: ${key}`);
53714
+ }
53715
+ seen.add(key);
53716
+ resources.push(resource);
53717
+ }
53718
+ if (page.has_more && !page.next_cursor) {
53719
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource page claimed more results without a continuation cursor");
53720
+ }
53721
+ if (!page.has_more && page.next_cursor) {
53722
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource page returned a continuation cursor while claiming no more results");
53723
+ }
53724
+ if (page.next_cursor && seenCursors.has(page.next_cursor)) {
53725
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource traversal repeated a continuation cursor");
53726
+ }
53727
+ if (page.has_more && pageCount >= maxPageCount) {
53728
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", `Mementos project resource traversal exceeded its bounded ${maxPageCount}-page population`);
53729
+ }
53730
+ if (page.next_cursor)
53731
+ seenCursors.add(page.next_cursor);
53732
+ cursor = page.next_cursor;
53733
+ } while (cursor);
53734
+ if (!first) {
53735
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource traversal returned no first page");
53736
+ }
53737
+ if (resources.length !== first.total) {
53738
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", `Mementos project resource traversal returned ${resources.length} of ${first.total} resources`);
53739
+ }
53740
+ return {
53741
+ ...first,
53742
+ resources,
53743
+ count: resources.length,
53744
+ total: resources.length,
53745
+ limit: pageSize,
53746
+ cursor: null,
53747
+ next_cursor: null,
53748
+ has_more: false,
53749
+ complete: true,
53750
+ truncated: false
53751
+ };
53752
+ }
53753
+ function getMementosProjectResourceExact(projectId, resourceKind, stableId, db, authorityOptions = {}) {
53754
+ if (!MEMENTOS_PROJECT_RESOURCE_KINDS.includes(resourceKind)) {
53755
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", `Unsupported Mementos project resource kind: ${resourceKind}`);
53756
+ }
53757
+ if (!db && isApiMode()) {
53758
+ const { data } = apiJson("GET", `/projects/${encodeURIComponent(projectId)}/resources/${encodeURIComponent(resourceKind)}/${encodeURIComponent(stableId)}`);
53759
+ return data;
53760
+ }
53761
+ const d = db ?? getDatabase();
53762
+ const { project, resources, collectionRevision } = localPopulation(projectId, d, [
53763
+ resourceKind
53764
+ ]);
53765
+ const resource = resources.find((candidate) => candidate.stable_id === stableId);
53766
+ if (!resource) {
53767
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_NOT_FOUND", `Mementos ${resourceKind} resource not found in project ${projectId}: ${stableId}`, { project_id: projectId, resource_kind: resourceKind, stable_id: stableId });
53768
+ }
53769
+ const capability = buildMementosProjectRegistrationCapability(authorityOptions);
53770
+ return {
53771
+ schema: "mementos.project-resource.v1",
53772
+ authority: {
53773
+ authority: capability.authority,
53774
+ authority_id: capability.authority_id,
53775
+ tenant_id: capability.tenant_id,
53776
+ corpus_id: capability.corpus_id,
53777
+ package_version: capability.package_version
53778
+ },
53779
+ project_id: projectId,
53780
+ project_revision: timestamp(project.updated_at),
53781
+ collection_revision: collectionRevision,
53782
+ resource,
53783
+ complete: true,
53784
+ truncated: false
53785
+ };
53786
+ }
53398
53787
  // src/db/machines.ts
53399
53788
  init_database();
53400
53789
  import { hostname, platform } from "os";
@@ -53519,13 +53908,13 @@ function createEntity(input, db) {
53519
53908
  return data;
53520
53909
  }
53521
53910
  const d = db || getDatabase();
53522
- const timestamp = now();
53911
+ const timestamp2 = now();
53523
53912
  const metadataJson = JSON.stringify(input.metadata || {});
53524
53913
  const existing = d.query(`SELECT * FROM entities
53525
53914
  WHERE name = ? AND type = ? AND COALESCE(project_id, '') = ?`).get(input.name, input.type, input.project_id || "");
53526
53915
  if (existing) {
53527
53916
  const sets = ["updated_at = ?"];
53528
- const params = [timestamp];
53917
+ const params = [timestamp2];
53529
53918
  if (input.description !== undefined) {
53530
53919
  sets.push("description = ?");
53531
53920
  params.push(input.description);
@@ -53548,8 +53937,8 @@ function createEntity(input, db) {
53548
53937
  input.description || null,
53549
53938
  metadataJson,
53550
53939
  input.project_id || null,
53551
- timestamp,
53552
- timestamp
53940
+ timestamp2,
53941
+ timestamp2
53553
53942
  ]);
53554
53943
  hookRegistry.runHooks("PostEntityCreate", {
53555
53944
  entityId: id,
@@ -54377,7 +54766,7 @@ function memoryResource(memory) {
54377
54766
  tags: memory.tags
54378
54767
  };
54379
54768
  }
54380
- function projectResource(projectId, name, externalId) {
54769
+ function projectResource2(projectId, name, externalId) {
54381
54770
  return {
54382
54771
  kind: "project",
54383
54772
  id: projectId,
@@ -54483,7 +54872,7 @@ function createMementosProjectPanel(projectRef, options = {}) {
54483
54872
  actionResource("mementos:save", "Save project memory")
54484
54873
  ],
54485
54874
  resourceRefs: [
54486
- projectResource(projectId, project?.name ?? projectRef, project?.id ?? projectRef),
54875
+ projectResource2(projectId, project?.name ?? projectRef, project?.id ?? projectRef),
54487
54876
  ...memories.slice(0, limit).map(memoryResource)
54488
54877
  ],
54489
54878
  renderFragment: {
@@ -55085,29 +55474,29 @@ function enforceQuotas(config, db) {
55085
55474
  }
55086
55475
  function archiveStale(staleDays, db) {
55087
55476
  const d = db || getDatabase();
55088
- const timestamp = now();
55477
+ const timestamp2 = now();
55089
55478
  const cutoff = new Date(Date.now() - staleDays * 24 * 60 * 60 * 1000).toISOString();
55090
55479
  const archiveWhere = `status = 'active' AND pinned = 0 AND COALESCE(accessed_at, created_at) < ?`;
55091
55480
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${archiveWhere}`).get(cutoff).c;
55092
55481
  if (count > 0) {
55093
- d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${archiveWhere}`, [timestamp, cutoff]);
55482
+ d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${archiveWhere}`, [timestamp2, cutoff]);
55094
55483
  }
55095
55484
  return count;
55096
55485
  }
55097
55486
  function archiveUnused(days, db) {
55098
55487
  const d = db || getDatabase();
55099
- const timestamp = now();
55488
+ const timestamp2 = now();
55100
55489
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
55101
55490
  const unusedWhere = `status = 'active' AND pinned = 0 AND access_count = 0 AND created_at < ?`;
55102
55491
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${unusedWhere}`).get(cutoff).c;
55103
55492
  if (count > 0) {
55104
- d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${unusedWhere}`, [timestamp, cutoff]);
55493
+ d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${unusedWhere}`, [timestamp2, cutoff]);
55105
55494
  }
55106
55495
  return count;
55107
55496
  }
55108
55497
  function deprioritizeStale(days, db) {
55109
55498
  const d = db || getDatabase();
55110
- const timestamp = now();
55499
+ const timestamp2 = now();
55111
55500
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
55112
55501
  const deprioWhere = `status = 'active' AND pinned = 0 AND importance > 1 AND COALESCE(accessed_at, updated_at) < ?`;
55113
55502
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${deprioWhere}`).get(cutoff).c;
@@ -55116,7 +55505,7 @@ function deprioritizeStale(days, db) {
55116
55505
  SET importance = importance - 1,
55117
55506
  version = version + 1,
55118
55507
  updated_at = ?
55119
- WHERE ${deprioWhere}`, [timestamp, cutoff]);
55508
+ WHERE ${deprioWhere}`, [timestamp2, cutoff]);
55120
55509
  }
55121
55510
  return count;
55122
55511
  }
@@ -55140,9 +55529,9 @@ function getAgentSyncDir(agentName) {
55140
55529
  }
55141
55530
  return dir;
55142
55531
  }
55143
- function setHighWaterMark(agentDir, timestamp) {
55532
+ function setHighWaterMark(agentDir, timestamp2) {
55144
55533
  const markFile = join6(agentDir, ".highwatermark");
55145
- writeFileSync4(markFile, timestamp, "utf-8");
55534
+ writeFileSync4(markFile, timestamp2, "utf-8");
55146
55535
  }
55147
55536
  function resolveConflict(local, remote, resolution) {
55148
55537
  switch (resolution) {
@@ -55462,13 +55851,13 @@ function buildConflictKey(key, sourceMachine, updatedAt) {
55462
55851
  return `${key}__conflict__${machineSegment}__${timestampSegment || "0"}`;
55463
55852
  }
55464
55853
  function buildConflictClone(loser, sourceMachine, winnerId) {
55465
- const timestamp = new Date().toISOString();
55854
+ const timestamp2 = new Date().toISOString();
55466
55855
  const tags = new Set(ensureArrayValue(loser["tags"]));
55467
55856
  tags.add("sync-conflict");
55468
55857
  tags.add(`source_machine:${sourceMachine}`);
55469
55858
  const metadata = ensureObjectValue(loser["metadata"]);
55470
55859
  metadata["sync_conflict"] = true;
55471
- metadata["conflict_detected_at"] = timestamp;
55860
+ metadata["conflict_detected_at"] = timestamp2;
55472
55861
  metadata["conflict_original_id"] = loser["id"];
55473
55862
  metadata["conflict_winner_id"] = winnerId;
55474
55863
  metadata["conflict_source_machine"] = sourceMachine;
@@ -55480,10 +55869,10 @@ function buildConflictClone(loser, sourceMachine, winnerId) {
55480
55869
  metadata: JSON.stringify(metadata),
55481
55870
  access_count: 0,
55482
55871
  version: 1,
55483
- created_at: timestamp,
55484
- updated_at: timestamp,
55872
+ created_at: timestamp2,
55873
+ updated_at: timestamp2,
55485
55874
  ..."accessed_at" in loser ? { accessed_at: null } : {},
55486
- ..."ingested_at" in loser ? { ingested_at: timestamp } : {}
55875
+ ..."ingested_at" in loser ? { ingested_at: timestamp2 } : {}
55487
55876
  }, sourceMachine);
55488
55877
  }
55489
55878
  function insertConflictCloneIfMissing(db, clone) {
@@ -55733,13 +56122,13 @@ function createRelation(input, db) {
55733
56122
  }
55734
56123
  const d = db || getDatabase();
55735
56124
  const id = shortUuid();
55736
- const timestamp = now();
56125
+ const timestamp2 = now();
55737
56126
  const weight = input.weight ?? 1;
55738
56127
  const metadata = JSON.stringify(input.metadata ?? {});
55739
56128
  d.run(`INSERT INTO relations (id, source_entity_id, target_entity_id, relation_type, weight, metadata, created_at)
55740
56129
  VALUES (?, ?, ?, ?, ?, ?, ?)
55741
56130
  ON CONFLICT(source_entity_id, target_entity_id, relation_type)
55742
- DO UPDATE SET weight = excluded.weight, metadata = excluded.metadata`, [id, input.source_entity_id, input.target_entity_id, input.relation_type, weight, metadata, timestamp]);
56131
+ DO UPDATE SET weight = excluded.weight, metadata = excluded.metadata`, [id, input.source_entity_id, input.target_entity_id, input.relation_type, weight, metadata, timestamp2]);
55743
56132
  const row = d.query(`SELECT * FROM relations
55744
56133
  WHERE source_entity_id = ? AND target_entity_id = ? AND relation_type = ?`).get(input.source_entity_id, input.target_entity_id, input.relation_type);
55745
56134
  const relation = parseRelationRow(row);
@@ -55916,7 +56305,7 @@ function parseMemoryLink(row) {
55916
56305
  function createMemoryLink(input, db) {
55917
56306
  const d = db || getDatabase();
55918
56307
  const id = shortUuid();
55919
- const timestamp = now();
56308
+ const timestamp2 = now();
55920
56309
  d.run(`INSERT OR IGNORE INTO memory_links (id, source_memory_id, target_memory_id, relation_type, run_id, metadata, created_at)
55921
56310
  VALUES (?, ?, ?, ?, ?, ?, ?)`, [
55922
56311
  id,
@@ -55925,7 +56314,7 @@ function createMemoryLink(input, db) {
55925
56314
  input.relation_type,
55926
56315
  input.run_id ?? null,
55927
56316
  JSON.stringify(input.metadata ?? {}),
55928
- timestamp
56317
+ timestamp2
55929
56318
  ]);
55930
56319
  const row = d.query(`SELECT * FROM memory_links
55931
56320
  WHERE source_memory_id = ? AND target_memory_id = ? AND relation_type = ? AND COALESCE(run_id, '') = ?
@@ -58214,6 +58603,7 @@ export {
58214
58603
  rollbackMemoryProjectLink,
58215
58604
  resolveProjectId,
58216
58605
  resolvePartialId,
58606
+ resolveMementosProjectAuthorityIdentity,
58217
58607
  resetDatabase,
58218
58608
  renameMachine,
58219
58609
  releaseResourceLocks,
@@ -58226,6 +58616,8 @@ export {
58226
58616
  registerAgent,
58227
58617
  reflectOnTrajectory,
58228
58618
  redactSecrets,
58619
+ readMementosProjectResourcePage,
58620
+ readAllMementosProjectResources,
58229
58621
  pushStorageChanges,
58230
58622
  pullStorageChanges,
58231
58623
  providerRegistry,
@@ -58286,6 +58678,7 @@ export {
58286
58678
  getMemoriesForEntity,
58287
58679
  getMemoriesByKey,
58288
58680
  getMementosStorageStatus,
58681
+ getMementosProjectResourceExact,
58289
58682
  getMachine,
58290
58683
  getFocus,
58291
58684
  getFallbackSyncTargetMachine,
@@ -58340,6 +58733,7 @@ export {
58340
58733
  canonicalMementosProjectRegistrationJson,
58341
58734
  bulkLinkEntities,
58342
58735
  bulkDeleteMemories,
58736
+ buildMementosProjectRegistrationCapability,
58343
58737
  buildFocusFilter,
58344
58738
  archiveUnused,
58345
58739
  archiveStale,
@@ -58362,17 +58756,22 @@ export {
58362
58756
  MemoryLockConflictError,
58363
58757
  MemoryInjector,
58364
58758
  MemoryExpiredError,
58759
+ MementosProjectResourceError,
58365
58760
  MementosProjectRegistrationHttpClient,
58366
58761
  MementosProjectRegistrationError,
58762
+ MementosProjectAuthorityIdentityError,
58367
58763
  MEMORY_PROJECT_LINK_RECEIPT_COLUMNS,
58368
58764
  MEMENTOS_STORAGE_TABLES,
58369
58765
  MEMENTOS_STORAGE_FALLBACK_ENV,
58370
58766
  MEMENTOS_STORAGE_ENV,
58767
+ MEMENTOS_PROJECT_RESOURCE_ROUTE,
58768
+ MEMENTOS_PROJECT_RESOURCE_KINDS,
58371
58769
  MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION,
58372
58770
  MEMENTOS_PROJECT_REGISTRATION_ROUTE,
58373
58771
  MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE,
58374
58772
  MEMENTOS_PROJECT_REFERENCE_SURFACES,
58375
58773
  MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
58774
+ MEMENTOS_PROJECT_AUTHORITY_ENV,
58376
58775
  MEMENTOS_MEMORY_PROJECT_LINK_ROUTE,
58377
58776
  InvalidScopeError,
58378
58777
  EntityNotFoundError,