@hasna/mementos 0.14.81 → 0.14.82

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,311 @@ 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
+ const resources = [];
53689
+ const seen = new Set;
53690
+ do {
53691
+ const page = readMementosProjectResourcePage(projectId, {
53692
+ limit: pageSize,
53693
+ cursor,
53694
+ resource_kinds: options.resource_kinds
53695
+ }, db, authorityOptions);
53696
+ if (!first)
53697
+ first = page;
53698
+ if (page.collection_revision !== first.collection_revision || page.total !== first.total || JSON.stringify(page.resource_kinds) !== JSON.stringify(first.resource_kinds)) {
53699
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_COLLECTION_CHANGED", "Mementos project resource collection changed during complete traversal");
53700
+ }
53701
+ for (const resource of page.resources) {
53702
+ const key = resourceKey(resource);
53703
+ if (seen.has(key)) {
53704
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", `Mementos project resource traversal returned duplicate stable ID: ${key}`);
53705
+ }
53706
+ seen.add(key);
53707
+ resources.push(resource);
53708
+ }
53709
+ if (page.has_more && !page.next_cursor) {
53710
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource page claimed more results without a continuation cursor");
53711
+ }
53712
+ cursor = page.next_cursor;
53713
+ } while (cursor);
53714
+ if (!first) {
53715
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource traversal returned no first page");
53716
+ }
53717
+ if (resources.length !== first.total) {
53718
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", `Mementos project resource traversal returned ${resources.length} of ${first.total} resources`);
53719
+ }
53720
+ return {
53721
+ ...first,
53722
+ resources,
53723
+ count: resources.length,
53724
+ total: resources.length,
53725
+ limit: pageSize,
53726
+ cursor: null,
53727
+ next_cursor: null,
53728
+ has_more: false,
53729
+ complete: true,
53730
+ truncated: false
53731
+ };
53732
+ }
53733
+ function getMementosProjectResourceExact(projectId, resourceKind, stableId, db, authorityOptions = {}) {
53734
+ if (!MEMENTOS_PROJECT_RESOURCE_KINDS.includes(resourceKind)) {
53735
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", `Unsupported Mementos project resource kind: ${resourceKind}`);
53736
+ }
53737
+ if (!db && isApiMode()) {
53738
+ const { data } = apiJson("GET", `/projects/${encodeURIComponent(projectId)}/resources/${encodeURIComponent(resourceKind)}/${encodeURIComponent(stableId)}`);
53739
+ return data;
53740
+ }
53741
+ const d = db ?? getDatabase();
53742
+ const { project, resources, collectionRevision } = localPopulation(projectId, d, [
53743
+ resourceKind
53744
+ ]);
53745
+ const resource = resources.find((candidate) => candidate.stable_id === stableId);
53746
+ if (!resource) {
53747
+ 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 });
53748
+ }
53749
+ const capability = buildMementosProjectRegistrationCapability(authorityOptions);
53750
+ return {
53751
+ schema: "mementos.project-resource.v1",
53752
+ authority: {
53753
+ authority: capability.authority,
53754
+ authority_id: capability.authority_id,
53755
+ tenant_id: capability.tenant_id,
53756
+ corpus_id: capability.corpus_id,
53757
+ package_version: capability.package_version
53758
+ },
53759
+ project_id: projectId,
53760
+ project_revision: timestamp(project.updated_at),
53761
+ collection_revision: collectionRevision,
53762
+ resource,
53763
+ complete: true,
53764
+ truncated: false
53765
+ };
53766
+ }
53398
53767
  // src/db/machines.ts
53399
53768
  init_database();
53400
53769
  import { hostname, platform } from "os";
@@ -53519,13 +53888,13 @@ function createEntity(input, db) {
53519
53888
  return data;
53520
53889
  }
53521
53890
  const d = db || getDatabase();
53522
- const timestamp = now();
53891
+ const timestamp2 = now();
53523
53892
  const metadataJson = JSON.stringify(input.metadata || {});
53524
53893
  const existing = d.query(`SELECT * FROM entities
53525
53894
  WHERE name = ? AND type = ? AND COALESCE(project_id, '') = ?`).get(input.name, input.type, input.project_id || "");
53526
53895
  if (existing) {
53527
53896
  const sets = ["updated_at = ?"];
53528
- const params = [timestamp];
53897
+ const params = [timestamp2];
53529
53898
  if (input.description !== undefined) {
53530
53899
  sets.push("description = ?");
53531
53900
  params.push(input.description);
@@ -53548,8 +53917,8 @@ function createEntity(input, db) {
53548
53917
  input.description || null,
53549
53918
  metadataJson,
53550
53919
  input.project_id || null,
53551
- timestamp,
53552
- timestamp
53920
+ timestamp2,
53921
+ timestamp2
53553
53922
  ]);
53554
53923
  hookRegistry.runHooks("PostEntityCreate", {
53555
53924
  entityId: id,
@@ -54377,7 +54746,7 @@ function memoryResource(memory) {
54377
54746
  tags: memory.tags
54378
54747
  };
54379
54748
  }
54380
- function projectResource(projectId, name, externalId) {
54749
+ function projectResource2(projectId, name, externalId) {
54381
54750
  return {
54382
54751
  kind: "project",
54383
54752
  id: projectId,
@@ -54483,7 +54852,7 @@ function createMementosProjectPanel(projectRef, options = {}) {
54483
54852
  actionResource("mementos:save", "Save project memory")
54484
54853
  ],
54485
54854
  resourceRefs: [
54486
- projectResource(projectId, project?.name ?? projectRef, project?.id ?? projectRef),
54855
+ projectResource2(projectId, project?.name ?? projectRef, project?.id ?? projectRef),
54487
54856
  ...memories.slice(0, limit).map(memoryResource)
54488
54857
  ],
54489
54858
  renderFragment: {
@@ -55085,29 +55454,29 @@ function enforceQuotas(config, db) {
55085
55454
  }
55086
55455
  function archiveStale(staleDays, db) {
55087
55456
  const d = db || getDatabase();
55088
- const timestamp = now();
55457
+ const timestamp2 = now();
55089
55458
  const cutoff = new Date(Date.now() - staleDays * 24 * 60 * 60 * 1000).toISOString();
55090
55459
  const archiveWhere = `status = 'active' AND pinned = 0 AND COALESCE(accessed_at, created_at) < ?`;
55091
55460
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${archiveWhere}`).get(cutoff).c;
55092
55461
  if (count > 0) {
55093
- d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${archiveWhere}`, [timestamp, cutoff]);
55462
+ d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${archiveWhere}`, [timestamp2, cutoff]);
55094
55463
  }
55095
55464
  return count;
55096
55465
  }
55097
55466
  function archiveUnused(days, db) {
55098
55467
  const d = db || getDatabase();
55099
- const timestamp = now();
55468
+ const timestamp2 = now();
55100
55469
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
55101
55470
  const unusedWhere = `status = 'active' AND pinned = 0 AND access_count = 0 AND created_at < ?`;
55102
55471
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${unusedWhere}`).get(cutoff).c;
55103
55472
  if (count > 0) {
55104
- d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${unusedWhere}`, [timestamp, cutoff]);
55473
+ d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${unusedWhere}`, [timestamp2, cutoff]);
55105
55474
  }
55106
55475
  return count;
55107
55476
  }
55108
55477
  function deprioritizeStale(days, db) {
55109
55478
  const d = db || getDatabase();
55110
- const timestamp = now();
55479
+ const timestamp2 = now();
55111
55480
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
55112
55481
  const deprioWhere = `status = 'active' AND pinned = 0 AND importance > 1 AND COALESCE(accessed_at, updated_at) < ?`;
55113
55482
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${deprioWhere}`).get(cutoff).c;
@@ -55116,7 +55485,7 @@ function deprioritizeStale(days, db) {
55116
55485
  SET importance = importance - 1,
55117
55486
  version = version + 1,
55118
55487
  updated_at = ?
55119
- WHERE ${deprioWhere}`, [timestamp, cutoff]);
55488
+ WHERE ${deprioWhere}`, [timestamp2, cutoff]);
55120
55489
  }
55121
55490
  return count;
55122
55491
  }
@@ -55140,9 +55509,9 @@ function getAgentSyncDir(agentName) {
55140
55509
  }
55141
55510
  return dir;
55142
55511
  }
55143
- function setHighWaterMark(agentDir, timestamp) {
55512
+ function setHighWaterMark(agentDir, timestamp2) {
55144
55513
  const markFile = join6(agentDir, ".highwatermark");
55145
- writeFileSync4(markFile, timestamp, "utf-8");
55514
+ writeFileSync4(markFile, timestamp2, "utf-8");
55146
55515
  }
55147
55516
  function resolveConflict(local, remote, resolution) {
55148
55517
  switch (resolution) {
@@ -55462,13 +55831,13 @@ function buildConflictKey(key, sourceMachine, updatedAt) {
55462
55831
  return `${key}__conflict__${machineSegment}__${timestampSegment || "0"}`;
55463
55832
  }
55464
55833
  function buildConflictClone(loser, sourceMachine, winnerId) {
55465
- const timestamp = new Date().toISOString();
55834
+ const timestamp2 = new Date().toISOString();
55466
55835
  const tags = new Set(ensureArrayValue(loser["tags"]));
55467
55836
  tags.add("sync-conflict");
55468
55837
  tags.add(`source_machine:${sourceMachine}`);
55469
55838
  const metadata = ensureObjectValue(loser["metadata"]);
55470
55839
  metadata["sync_conflict"] = true;
55471
- metadata["conflict_detected_at"] = timestamp;
55840
+ metadata["conflict_detected_at"] = timestamp2;
55472
55841
  metadata["conflict_original_id"] = loser["id"];
55473
55842
  metadata["conflict_winner_id"] = winnerId;
55474
55843
  metadata["conflict_source_machine"] = sourceMachine;
@@ -55480,10 +55849,10 @@ function buildConflictClone(loser, sourceMachine, winnerId) {
55480
55849
  metadata: JSON.stringify(metadata),
55481
55850
  access_count: 0,
55482
55851
  version: 1,
55483
- created_at: timestamp,
55484
- updated_at: timestamp,
55852
+ created_at: timestamp2,
55853
+ updated_at: timestamp2,
55485
55854
  ..."accessed_at" in loser ? { accessed_at: null } : {},
55486
- ..."ingested_at" in loser ? { ingested_at: timestamp } : {}
55855
+ ..."ingested_at" in loser ? { ingested_at: timestamp2 } : {}
55487
55856
  }, sourceMachine);
55488
55857
  }
55489
55858
  function insertConflictCloneIfMissing(db, clone) {
@@ -55733,13 +56102,13 @@ function createRelation(input, db) {
55733
56102
  }
55734
56103
  const d = db || getDatabase();
55735
56104
  const id = shortUuid();
55736
- const timestamp = now();
56105
+ const timestamp2 = now();
55737
56106
  const weight = input.weight ?? 1;
55738
56107
  const metadata = JSON.stringify(input.metadata ?? {});
55739
56108
  d.run(`INSERT INTO relations (id, source_entity_id, target_entity_id, relation_type, weight, metadata, created_at)
55740
56109
  VALUES (?, ?, ?, ?, ?, ?, ?)
55741
56110
  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]);
56111
+ 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
56112
  const row = d.query(`SELECT * FROM relations
55744
56113
  WHERE source_entity_id = ? AND target_entity_id = ? AND relation_type = ?`).get(input.source_entity_id, input.target_entity_id, input.relation_type);
55745
56114
  const relation = parseRelationRow(row);
@@ -55916,7 +56285,7 @@ function parseMemoryLink(row) {
55916
56285
  function createMemoryLink(input, db) {
55917
56286
  const d = db || getDatabase();
55918
56287
  const id = shortUuid();
55919
- const timestamp = now();
56288
+ const timestamp2 = now();
55920
56289
  d.run(`INSERT OR IGNORE INTO memory_links (id, source_memory_id, target_memory_id, relation_type, run_id, metadata, created_at)
55921
56290
  VALUES (?, ?, ?, ?, ?, ?, ?)`, [
55922
56291
  id,
@@ -55925,7 +56294,7 @@ function createMemoryLink(input, db) {
55925
56294
  input.relation_type,
55926
56295
  input.run_id ?? null,
55927
56296
  JSON.stringify(input.metadata ?? {}),
55928
- timestamp
56297
+ timestamp2
55929
56298
  ]);
55930
56299
  const row = d.query(`SELECT * FROM memory_links
55931
56300
  WHERE source_memory_id = ? AND target_memory_id = ? AND relation_type = ? AND COALESCE(run_id, '') = ?
@@ -58214,6 +58583,7 @@ export {
58214
58583
  rollbackMemoryProjectLink,
58215
58584
  resolveProjectId,
58216
58585
  resolvePartialId,
58586
+ resolveMementosProjectAuthorityIdentity,
58217
58587
  resetDatabase,
58218
58588
  renameMachine,
58219
58589
  releaseResourceLocks,
@@ -58226,6 +58596,8 @@ export {
58226
58596
  registerAgent,
58227
58597
  reflectOnTrajectory,
58228
58598
  redactSecrets,
58599
+ readMementosProjectResourcePage,
58600
+ readAllMementosProjectResources,
58229
58601
  pushStorageChanges,
58230
58602
  pullStorageChanges,
58231
58603
  providerRegistry,
@@ -58286,6 +58658,7 @@ export {
58286
58658
  getMemoriesForEntity,
58287
58659
  getMemoriesByKey,
58288
58660
  getMementosStorageStatus,
58661
+ getMementosProjectResourceExact,
58289
58662
  getMachine,
58290
58663
  getFocus,
58291
58664
  getFallbackSyncTargetMachine,
@@ -58340,6 +58713,7 @@ export {
58340
58713
  canonicalMementosProjectRegistrationJson,
58341
58714
  bulkLinkEntities,
58342
58715
  bulkDeleteMemories,
58716
+ buildMementosProjectRegistrationCapability,
58343
58717
  buildFocusFilter,
58344
58718
  archiveUnused,
58345
58719
  archiveStale,
@@ -58362,17 +58736,22 @@ export {
58362
58736
  MemoryLockConflictError,
58363
58737
  MemoryInjector,
58364
58738
  MemoryExpiredError,
58739
+ MementosProjectResourceError,
58365
58740
  MementosProjectRegistrationHttpClient,
58366
58741
  MementosProjectRegistrationError,
58742
+ MementosProjectAuthorityIdentityError,
58367
58743
  MEMORY_PROJECT_LINK_RECEIPT_COLUMNS,
58368
58744
  MEMENTOS_STORAGE_TABLES,
58369
58745
  MEMENTOS_STORAGE_FALLBACK_ENV,
58370
58746
  MEMENTOS_STORAGE_ENV,
58747
+ MEMENTOS_PROJECT_RESOURCE_ROUTE,
58748
+ MEMENTOS_PROJECT_RESOURCE_KINDS,
58371
58749
  MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION,
58372
58750
  MEMENTOS_PROJECT_REGISTRATION_ROUTE,
58373
58751
  MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE,
58374
58752
  MEMENTOS_PROJECT_REFERENCE_SURFACES,
58375
58753
  MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
58754
+ MEMENTOS_PROJECT_AUTHORITY_ENV,
58376
58755
  MEMENTOS_MEMORY_PROJECT_LINK_ROUTE,
58377
58756
  InvalidScopeError,
58378
58757
  EntityNotFoundError,