@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/cli/index.js CHANGED
@@ -6359,8 +6359,86 @@ function getMementosPackageVersion() {
6359
6359
  var init_package_version = () => {};
6360
6360
 
6361
6361
  // src/project-registration/types.ts
6362
- var MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE = "mementos.project-guarded-update.v1";
6363
- var init_types2 = () => {};
6362
+ var MEMENTOS_PROJECT_REGISTRATION_ROUTE = "mementos.project-registration.v1", MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE = "mementos.project-guarded-update.v1", MEMENTOS_PROJECT_RESOURCE_ROUTE = "mementos.project-resources.v1", MEMENTOS_PROJECT_RESOURCE_KINDS;
6363
+ var init_types2 = __esm(() => {
6364
+ MEMENTOS_PROJECT_RESOURCE_KINDS = [
6365
+ "project",
6366
+ "knowledge",
6367
+ "memory",
6368
+ "session"
6369
+ ];
6370
+ });
6371
+
6372
+ // src/project-registration/identity.ts
6373
+ function configuredValue(override, envKey) {
6374
+ return override?.trim() || process.env[envKey]?.trim() || null;
6375
+ }
6376
+ function resolveMementosProjectAuthorityIdentity(options = {}) {
6377
+ const authorityId = configuredValue(options.authorityId, MEMENTOS_PROJECT_AUTHORITY_ENV.authorityId);
6378
+ const tenantId = configuredValue(options.tenantId, MEMENTOS_PROJECT_AUTHORITY_ENV.tenantId);
6379
+ const corpusId = configuredValue(options.corpusId, MEMENTOS_PROJECT_AUTHORITY_ENV.corpusId);
6380
+ if (!authorityId || !tenantId || !corpusId) {
6381
+ const missingEnv = [];
6382
+ if (!authorityId)
6383
+ missingEnv.push(MEMENTOS_PROJECT_AUTHORITY_ENV.authorityId);
6384
+ if (!tenantId)
6385
+ missingEnv.push(MEMENTOS_PROJECT_AUTHORITY_ENV.tenantId);
6386
+ if (!corpusId)
6387
+ missingEnv.push(MEMENTOS_PROJECT_AUTHORITY_ENV.corpusId);
6388
+ throw new MementosProjectAuthorityIdentityError(missingEnv);
6389
+ }
6390
+ return {
6391
+ authority_id: authorityId,
6392
+ tenant_id: tenantId,
6393
+ corpus_id: corpusId
6394
+ };
6395
+ }
6396
+ function buildMementosProjectRegistrationCapability(options = {}) {
6397
+ const identity = resolveMementosProjectAuthorityIdentity(options);
6398
+ return {
6399
+ authority: "mementos",
6400
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
6401
+ package_version: options.packageVersion ?? getMementosPackageVersion(),
6402
+ ...identity,
6403
+ supported_resources: ["project"],
6404
+ conditional_create: true,
6405
+ immutable_receipts: true,
6406
+ exact_terminal_lookup: true,
6407
+ exact_readback: true,
6408
+ conditional_inverse: true,
6409
+ ambiguous_outcome_reconciliation: true,
6410
+ guarded_update: true,
6411
+ guarded_update_route: MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE,
6412
+ no_write_dry_run: true,
6413
+ expected_revision_compare_and_swap: true,
6414
+ caller_idempotency: true,
6415
+ exact_inverse_rollback: true,
6416
+ project_resource_enumeration: true,
6417
+ project_resource_route: MEMENTOS_PROJECT_RESOURCE_ROUTE,
6418
+ project_resource_kinds: ["project", "knowledge", "memory", "session"],
6419
+ stable_keyset_pagination: true,
6420
+ explicit_membership_only: true
6421
+ };
6422
+ }
6423
+ var MEMENTOS_PROJECT_AUTHORITY_ENV, MementosProjectAuthorityIdentityError;
6424
+ var init_identity = __esm(() => {
6425
+ init_package_version();
6426
+ init_types2();
6427
+ MEMENTOS_PROJECT_AUTHORITY_ENV = {
6428
+ authorityId: "MEMENTOS_PROJECT_AUTHORITY_ID",
6429
+ tenantId: "MEMENTOS_PROJECT_TENANT_ID",
6430
+ corpusId: "MEMENTOS_PROJECT_CORPUS_ID"
6431
+ };
6432
+ MementosProjectAuthorityIdentityError = class MementosProjectAuthorityIdentityError extends Error {
6433
+ missing_env;
6434
+ code = "MEMENTOS_PROJECT_AUTHORITY_UNCONFIGURED";
6435
+ constructor(missing_env) {
6436
+ super("Mementos project authority identity is not configured; set " + missing_env.join(", "));
6437
+ this.missing_env = missing_env;
6438
+ this.name = "MementosProjectAuthorityIdentityError";
6439
+ }
6440
+ };
6441
+ });
6364
6442
 
6365
6443
  // src/db/projects.ts
6366
6444
  var exports_projects = {};
@@ -6388,6 +6466,16 @@ function parseProjectRow(row) {
6388
6466
  updated_at: row["updated_at"]
6389
6467
  };
6390
6468
  }
6469
+ function projectUpdateAuthority() {
6470
+ try {
6471
+ return resolveMementosProjectAuthorityIdentity();
6472
+ } catch (error) {
6473
+ if (error instanceof MementosProjectAuthorityIdentityError) {
6474
+ throw new ProjectGuardedUpdateError("PROJECT_UPDATE_AUTHORITY_MISMATCH", error.message, { authority_code: error.code, missing_env: error.missing_env });
6475
+ }
6476
+ throw error;
6477
+ }
6478
+ }
6391
6479
  function canonicalizeProjectUpdateValue(value) {
6392
6480
  if (Array.isArray(value))
6393
6481
  return value.map(canonicalizeProjectUpdateValue);
@@ -6460,7 +6548,7 @@ function normalizeProjectUpdateInput(input) {
6460
6548
  }
6461
6549
  return normalized;
6462
6550
  }
6463
- function assertProjectUpdateIdentity(identity, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
6551
+ function assertProjectUpdateIdentity(identity, expectedIdentity = projectUpdateAuthority()) {
6464
6552
  if (identity.authority_id !== expectedIdentity.authority_id || identity.tenant_id !== expectedIdentity.tenant_id || identity.corpus_id !== expectedIdentity.corpus_id) {
6465
6553
  throw new ProjectGuardedUpdateError("PROJECT_UPDATE_AUTHORITY_MISMATCH", "guarded project update does not match this authority, tenant, and corpus");
6466
6554
  }
@@ -6470,7 +6558,7 @@ function assertBoundedIdentifier(value, field) {
6470
6558
  throw new ProjectGuardedUpdateError("PROJECT_UPDATE_INVALID_INPUT", `${field} must be an 8-128 character bounded identifier`);
6471
6559
  }
6472
6560
  }
6473
- function assertProjectUpdateRequest(request, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
6561
+ function assertProjectUpdateRequest(request, expectedIdentity = projectUpdateAuthority()) {
6474
6562
  assertProjectUpdateIdentity(request, expectedIdentity);
6475
6563
  assertBoundedIdentifier(request.operation_id, "operation_id");
6476
6564
  assertBoundedIdentifier(request.step_id, "step_id");
@@ -6648,7 +6736,7 @@ function listProjects(db) {
6648
6736
  const rows = d.query("SELECT * FROM projects ORDER BY updated_at DESC").all();
6649
6737
  return rows.map(parseProjectRow);
6650
6738
  }
6651
- function previewProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
6739
+ function previewProjectUpdate(id, request, db, expectedIdentity = projectUpdateAuthority()) {
6652
6740
  assertProjectUpdateRequest(request, expectedIdentity);
6653
6741
  const normalized = normalizeProjectUpdateInput(request.updates);
6654
6742
  if (!db && isApiMode()) {
@@ -6671,7 +6759,7 @@ function previewProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE
6671
6759
  receipt: null
6672
6760
  };
6673
6761
  }
6674
- function applyProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY, resultDigestForProject) {
6762
+ function applyProjectUpdate(id, request, db, expectedIdentity = projectUpdateAuthority(), resultDigestForProject) {
6675
6763
  assertProjectUpdateRequest(request, expectedIdentity);
6676
6764
  const normalized = normalizeProjectUpdateInput(request.updates);
6677
6765
  if (!db && isApiMode()) {
@@ -6740,7 +6828,7 @@ function applyProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_A
6740
6828
  return { dry_run: false, applied: true, project: readback, receipt };
6741
6829
  });
6742
6830
  }
6743
- function rollbackProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY, resultDigestForProject) {
6831
+ function rollbackProjectUpdate(id, request, db, expectedIdentity = projectUpdateAuthority(), resultDigestForProject) {
6744
6832
  assertProjectUpdateRequest(request, expectedIdentity);
6745
6833
  assertBoundedIdentifier(request.accepted_receipt_id, "accepted_receipt_id");
6746
6834
  if (!db && isApiMode()) {
@@ -6814,7 +6902,7 @@ function rollbackProjectUpdate(id, request, db, expectedIdentity = PROJECT_UPDAT
6814
6902
  return { dry_run: false, applied: true, project: readback, receipt };
6815
6903
  });
6816
6904
  }
6817
- function getProjectUpdateReceipt(id, receiptId, identity = PROJECT_UPDATE_AUTHORITY, db, expectedIdentity = PROJECT_UPDATE_AUTHORITY) {
6905
+ function getProjectUpdateReceipt(id, receiptId, identity = projectUpdateAuthority(), db, expectedIdentity = projectUpdateAuthority()) {
6818
6906
  assertProjectUpdateIdentity(identity, expectedIdentity);
6819
6907
  if (!db && isApiMode()) {
6820
6908
  const { data } = apiJson("POST", `/projects/${encodeURIComponent(id)}/update-receipts/lookup`, { ...identity, receipt_id: receiptId });
@@ -6892,12 +6980,13 @@ function updateProject(id, input, db) {
6892
6980
  return updated ? parseProjectRow(updated) : null;
6893
6981
  });
6894
6982
  }
6895
- var ProjectCollisionError, ProjectGuardedUpdateError, PROJECT_UPDATE_AUTHORITY, BOUNDED_IDENTIFIER;
6983
+ var ProjectCollisionError, ProjectGuardedUpdateError, BOUNDED_IDENTIFIER;
6896
6984
  var init_projects = __esm(() => {
6897
6985
  init_database();
6898
6986
  init_api_mode();
6899
6987
  init_package_version();
6900
6988
  init_types2();
6989
+ init_identity();
6901
6990
  ProjectCollisionError = class ProjectCollisionError extends Error {
6902
6991
  field;
6903
6992
  value;
@@ -6918,11 +7007,6 @@ var init_projects = __esm(() => {
6918
7007
  this.name = "ProjectGuardedUpdateError";
6919
7008
  }
6920
7009
  };
6921
- PROJECT_UPDATE_AUTHORITY = {
6922
- authority_id: "mementos",
6923
- tenant_id: "default",
6924
- corpus_id: "default"
6925
- };
6926
7010
  BOUNDED_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
6927
7011
  });
6928
7012
 
@@ -8564,13 +8648,13 @@ function createRelation(input, db) {
8564
8648
  }
8565
8649
  const d = db || getDatabase();
8566
8650
  const id = shortUuid();
8567
- const timestamp = now();
8651
+ const timestamp2 = now();
8568
8652
  const weight = input.weight ?? 1;
8569
8653
  const metadata = JSON.stringify(input.metadata ?? {});
8570
8654
  d.run(`INSERT INTO relations (id, source_entity_id, target_entity_id, relation_type, weight, metadata, created_at)
8571
8655
  VALUES (?, ?, ?, ?, ?, ?, ?)
8572
8656
  ON CONFLICT(source_entity_id, target_entity_id, relation_type)
8573
- DO UPDATE SET weight = excluded.weight, metadata = excluded.metadata`, [id, input.source_entity_id, input.target_entity_id, input.relation_type, weight, metadata, timestamp]);
8657
+ DO UPDATE SET weight = excluded.weight, metadata = excluded.metadata`, [id, input.source_entity_id, input.target_entity_id, input.relation_type, weight, metadata, timestamp2]);
8574
8658
  const row = d.query(`SELECT * FROM relations
8575
8659
  WHERE source_entity_id = ? AND target_entity_id = ? AND relation_type = ?`).get(input.source_entity_id, input.target_entity_id, input.relation_type);
8576
8660
  const relation = parseRelationRow(row);
@@ -9562,7 +9646,7 @@ function createWebhookHook(input, db) {
9562
9646
  }
9563
9647
  const d = db || getDatabase();
9564
9648
  const id = shortUuid();
9565
- const timestamp = now();
9649
+ const timestamp2 = now();
9566
9650
  d.run(`INSERT INTO webhook_hooks
9567
9651
  (id, type, handler_url, priority, blocking, agent_id, project_id, description, enabled, created_at, invocation_count, failure_count)
9568
9652
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, 0, 0)`, [
@@ -9574,7 +9658,7 @@ function createWebhookHook(input, db) {
9574
9658
  input.agentId ?? null,
9575
9659
  input.projectId ?? null,
9576
9660
  input.description ?? null,
9577
- timestamp
9661
+ timestamp2
9578
9662
  ]);
9579
9663
  return getWebhookHook(id, d);
9580
9664
  }
@@ -9745,7 +9829,7 @@ function parseEventRow(row) {
9745
9829
  function createSynthesisRun(input, db) {
9746
9830
  const d = db || getDatabase();
9747
9831
  const id = shortUuid();
9748
- const timestamp = now();
9832
+ const timestamp2 = now();
9749
9833
  d.run(`INSERT INTO synthesis_runs (id, triggered_by, project_id, agent_id, corpus_size, proposals_generated, proposals_accepted, proposals_rejected, status, started_at)
9750
9834
  VALUES (?, ?, ?, ?, ?, 0, 0, 0, 'pending', ?)`, [
9751
9835
  id,
@@ -9753,7 +9837,7 @@ function createSynthesisRun(input, db) {
9753
9837
  input.project_id ?? null,
9754
9838
  input.agent_id ?? null,
9755
9839
  input.corpus_size ?? 0,
9756
- timestamp
9840
+ timestamp2
9757
9841
  ]);
9758
9842
  return getSynthesisRun(id, d);
9759
9843
  }
@@ -9846,7 +9930,7 @@ function updateSynthesisRun(id, updates, db) {
9846
9930
  function createProposal(input, db) {
9847
9931
  const d = db || getDatabase();
9848
9932
  const id = shortUuid();
9849
- const timestamp = now();
9933
+ const timestamp2 = now();
9850
9934
  d.run(`INSERT INTO synthesis_proposals (id, run_id, proposal_type, memory_ids, target_memory_id, proposed_changes, reasoning, confidence, status, created_at)
9851
9935
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?)`, [
9852
9936
  id,
@@ -9857,7 +9941,7 @@ function createProposal(input, db) {
9857
9941
  JSON.stringify(input.proposed_changes),
9858
9942
  input.reasoning ?? null,
9859
9943
  input.confidence,
9860
- timestamp
9944
+ timestamp2
9861
9945
  ]);
9862
9946
  return getProposal(id, d);
9863
9947
  }
@@ -9905,10 +9989,10 @@ function updateProposal(id, updates, db) {
9905
9989
  function createMetric(input, db) {
9906
9990
  const d = db || getDatabase();
9907
9991
  const id = shortUuid();
9908
- const timestamp = now();
9992
+ const timestamp2 = now();
9909
9993
  d.run(`INSERT INTO synthesis_metrics (id, run_id, metric_type, value, baseline, created_at)
9910
- VALUES (?, ?, ?, ?, ?, ?)`, [id, input.run_id, input.metric_type, input.value, input.baseline ?? null, timestamp]);
9911
- return { id, run_id: input.run_id, metric_type: input.metric_type, value: input.value, baseline: input.baseline ?? null, created_at: timestamp };
9994
+ VALUES (?, ?, ?, ?, ?, ?)`, [id, input.run_id, input.metric_type, input.value, input.baseline ?? null, timestamp2]);
9995
+ return { id, run_id: input.run_id, metric_type: input.metric_type, value: input.value, baseline: input.baseline ?? null, created_at: timestamp2 };
9912
9996
  }
9913
9997
  function listMetrics(run_id, db) {
9914
9998
  const d = db || getDatabase();
@@ -9919,7 +10003,7 @@ function recordSynthesisEvent(input, db) {
9919
10003
  try {
9920
10004
  const d = db || getDatabase();
9921
10005
  const id = shortUuid();
9922
- const timestamp = now();
10006
+ const timestamp2 = now();
9923
10007
  d.run(`INSERT INTO synthesis_events (id, event_type, memory_id, agent_id, project_id, session_id, query, importance_at_time, metadata, created_at)
9924
10008
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
9925
10009
  id,
@@ -9931,7 +10015,7 @@ function recordSynthesisEvent(input, db) {
9931
10015
  input.query ?? null,
9932
10016
  input.importance_at_time ?? null,
9933
10017
  JSON.stringify(input.metadata ?? {}),
9934
- timestamp
10018
+ timestamp2
9935
10019
  ]);
9936
10020
  } catch {}
9937
10021
  }
@@ -10173,20 +10257,20 @@ __export(exports_contradiction, {
10173
10257
  });
10174
10258
  function invalidateFact(oldMemoryId, newMemoryId, db) {
10175
10259
  const d = db || getDatabase();
10176
- const timestamp = now();
10177
- d.run("UPDATE memories SET valid_until = ?, updated_at = ? WHERE id = ?", [timestamp, timestamp, oldMemoryId]);
10260
+ const timestamp2 = now();
10261
+ d.run("UPDATE memories SET valid_until = ?, updated_at = ? WHERE id = ?", [timestamp2, timestamp2, oldMemoryId]);
10178
10262
  if (newMemoryId) {
10179
10263
  const row = d.query("SELECT metadata FROM memories WHERE id = ?").get(newMemoryId);
10180
10264
  if (row) {
10181
10265
  const metadata = JSON.parse(row.metadata || "{}");
10182
10266
  metadata.supersedes_id = oldMemoryId;
10183
- d.run("UPDATE memories SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), timestamp, newMemoryId]);
10267
+ d.run("UPDATE memories SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), timestamp2, newMemoryId]);
10184
10268
  }
10185
10269
  }
10186
10270
  return {
10187
10271
  invalidated_memory_id: oldMemoryId,
10188
10272
  new_memory_id: newMemoryId || null,
10189
- valid_until: timestamp,
10273
+ valid_until: timestamp2,
10190
10274
  supersedes_id: oldMemoryId
10191
10275
  };
10192
10276
  }
@@ -11456,7 +11540,7 @@ function parseJobRow(row) {
11456
11540
  function createSessionJob(input, db) {
11457
11541
  const d = db || getDatabase();
11458
11542
  const id = uuid();
11459
- const timestamp = now();
11543
+ const timestamp2 = now();
11460
11544
  const source = input.source ?? "manual";
11461
11545
  const metadata = JSON.stringify(input.metadata ?? {});
11462
11546
  d.run(`INSERT INTO session_memory_jobs
@@ -11469,7 +11553,7 @@ function createSessionJob(input, db) {
11469
11553
  source,
11470
11554
  input.transcript,
11471
11555
  metadata,
11472
- timestamp
11556
+ timestamp2
11473
11557
  ]);
11474
11558
  return getSessionJob(id, d);
11475
11559
  }
@@ -11605,7 +11689,7 @@ function saveToolEvent(input, db) {
11605
11689
  }
11606
11690
  const d = db || getDatabase();
11607
11691
  const id = uuid();
11608
- const timestamp = now();
11692
+ const timestamp2 = now();
11609
11693
  const metadataJson = JSON.stringify(input.metadata || {});
11610
11694
  d.run(`INSERT INTO tool_events (id, tool_name, action, success, error_type, error_message, tokens_used, latency_ms, context, lesson, when_to_use, agent_id, project_id, session_id, metadata, created_at)
11611
11695
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
@@ -11624,7 +11708,7 @@ function saveToolEvent(input, db) {
11624
11708
  input.project_id || null,
11625
11709
  input.session_id || null,
11626
11710
  metadataJson,
11627
- timestamp
11711
+ timestamp2
11628
11712
  ]);
11629
11713
  return getToolEvent(id, d);
11630
11714
  }
@@ -12347,7 +12431,7 @@ function registerSession(opts) {
12347
12431
  const pid = process.pid;
12348
12432
  const cwd = opts.cwd || process.cwd();
12349
12433
  const id = generateId();
12350
- const timestamp = now3();
12434
+ const timestamp2 = now3();
12351
12435
  const existing = db.query("SELECT id FROM sessions WHERE pid = ? AND mcp_server = ?").get(pid, opts.mcp_server);
12352
12436
  if (existing) {
12353
12437
  db.run(`UPDATE sessions SET agent_name = ?, project_name = ?, cwd = ?,
@@ -12358,7 +12442,7 @@ function registerSession(opts) {
12358
12442
  opts.git_root || null,
12359
12443
  opts.tty || null,
12360
12444
  JSON.stringify(opts.metadata || {}),
12361
- timestamp,
12445
+ timestamp2,
12362
12446
  existing.id
12363
12447
  ]);
12364
12448
  return getSession(existing.id);
@@ -12374,8 +12458,8 @@ function registerSession(opts) {
12374
12458
  opts.tty || null,
12375
12459
  opts.mcp_server,
12376
12460
  JSON.stringify(opts.metadata || {}),
12377
- timestamp,
12378
- timestamp
12461
+ timestamp2,
12462
+ timestamp2
12379
12463
  ]);
12380
12464
  return getSession(id);
12381
12465
  }
@@ -60779,6 +60863,7 @@ init_database();
60779
60863
  init_memories();
60780
60864
  init_package_version();
60781
60865
  init_schema();
60866
+ init_identity();
60782
60867
  import { createHash as createHash2 } from "crypto";
60783
60868
 
60784
60869
  class MemoryProjectLinkError extends Error {
@@ -60791,12 +60876,17 @@ class MemoryProjectLinkError extends Error {
60791
60876
  this.name = "MemoryProjectLinkError";
60792
60877
  }
60793
60878
  }
60794
- var LINK_AUTHORITY = {
60795
- authority_id: "mementos",
60796
- tenant_id: "default",
60797
- corpus_id: "default"
60798
- };
60799
60879
  var BOUNDED_IDENTIFIER2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
60880
+ function linkAuthority() {
60881
+ try {
60882
+ return resolveMementosProjectAuthorityIdentity();
60883
+ } catch (error) {
60884
+ if (error instanceof MementosProjectAuthorityIdentityError) {
60885
+ throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_AUTHORITY_MISMATCH", error.message, { authority_code: error.code, missing_env: error.missing_env });
60886
+ }
60887
+ throw error;
60888
+ }
60889
+ }
60800
60890
  function canonicalize(value) {
60801
60891
  if (Array.isArray(value))
60802
60892
  return value.map(canonicalize);
@@ -60904,7 +60994,8 @@ function receiptFromRow(row) {
60904
60994
  };
60905
60995
  }
60906
60996
  function assertIdentity(identity) {
60907
- if (identity.authority_id !== LINK_AUTHORITY.authority_id || identity.tenant_id !== LINK_AUTHORITY.tenant_id || identity.corpus_id !== LINK_AUTHORITY.corpus_id) {
60997
+ const expectedIdentity = linkAuthority();
60998
+ if (identity.authority_id !== expectedIdentity.authority_id || identity.tenant_id !== expectedIdentity.tenant_id || identity.corpus_id !== expectedIdentity.corpus_id) {
60908
60999
  throw new MemoryProjectLinkError("MEMORY_PROJECT_LINK_AUTHORITY_MISMATCH", "memory project link does not match this authority, tenant, and corpus");
60909
61000
  }
60910
61001
  }
@@ -61381,7 +61472,7 @@ function rollbackMemoryProjectLink(memoryId, request, db) {
61381
61472
  };
61382
61473
  });
61383
61474
  }
61384
- function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = LINK_AUTHORITY, db) {
61475
+ function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = linkAuthority(), db) {
61385
61476
  assertIdentity(identity);
61386
61477
  assertBoundedIdentifier2(memoryId, "memory_id");
61387
61478
  assertBoundedIdentifier2(receiptId, "receipt_id");
@@ -61397,6 +61488,348 @@ function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = LINK_AUTHOR
61397
61488
  return receipt;
61398
61489
  }
61399
61490
 
61491
+ // src/project-registration/authority.ts
61492
+ import { createHash as createHash3 } from "crypto";
61493
+ init_projects();
61494
+ init_types2();
61495
+ init_identity();
61496
+ function canonicalMementosProjectRegistrationJson(value) {
61497
+ return JSON.stringify(canonicalize2(value));
61498
+ }
61499
+ function canonicalize2(value) {
61500
+ if (Array.isArray(value))
61501
+ return value.map(canonicalize2);
61502
+ if (!value || typeof value !== "object")
61503
+ return value;
61504
+ const out = {};
61505
+ for (const key of Object.keys(value).sort()) {
61506
+ const item = value[key];
61507
+ if (item !== undefined)
61508
+ out[key] = canonicalize2(item);
61509
+ }
61510
+ return out;
61511
+ }
61512
+ function digestMementosProjectRegistrationValue(value) {
61513
+ return createHash3("sha256").update(canonicalMementosProjectRegistrationJson(value)).digest("hex");
61514
+ }
61515
+ // src/project-registration/http.ts
61516
+ init_types2();
61517
+
61518
+ // src/project-registration/index.ts
61519
+ init_identity();
61520
+
61521
+ // src/project-registration/project-resources.ts
61522
+ init_api_mode();
61523
+ init_database();
61524
+ init_memories();
61525
+ init_identity();
61526
+ init_types2();
61527
+ var DEFAULT_PAGE_LIMIT = 100;
61528
+ var MAX_PAGE_LIMIT = 1000;
61529
+ var CURSOR_SCHEMA = "mementos.project-resources.cursor.v1";
61530
+
61531
+ class MementosProjectResourceError extends Error {
61532
+ code;
61533
+ details;
61534
+ constructor(code, message, details = {}) {
61535
+ super(message);
61536
+ this.code = code;
61537
+ this.details = details;
61538
+ this.name = "MementosProjectResourceError";
61539
+ }
61540
+ }
61541
+ function timestamp(value) {
61542
+ return value instanceof Date ? value.toISOString() : String(value);
61543
+ }
61544
+ function normalizeSqlValue(value) {
61545
+ if (value instanceof Date)
61546
+ return value.toISOString();
61547
+ if (Array.isArray(value))
61548
+ return value.map(normalizeSqlValue);
61549
+ if (!value || typeof value !== "object")
61550
+ return value;
61551
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalizeSqlValue(item)]));
61552
+ }
61553
+ function exactProject(db, projectId) {
61554
+ const row = db.get("SELECT id, name, path, description, memory_prefix, created_at, updated_at FROM projects WHERE id = ? LIMIT 1", projectId);
61555
+ if (!row) {
61556
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_PROJECT_NOT_FOUND", `Mementos project not found: ${projectId}`, { project_id: projectId });
61557
+ }
61558
+ return row;
61559
+ }
61560
+ function resourceKey(resource) {
61561
+ const rank = MEMENTOS_PROJECT_RESOURCE_KINDS.indexOf(resource.resource_kind);
61562
+ return `${String(rank).padStart(2, "0")}:${resource.stable_id}`;
61563
+ }
61564
+ function projectResource(project) {
61565
+ const normalized = normalizeSqlValue(project);
61566
+ return {
61567
+ authority: "mementos",
61568
+ source_package: "@hasna/mementos",
61569
+ project_id: project.id,
61570
+ resource_kind: "project",
61571
+ stable_id: project.id,
61572
+ revision: timestamp(project.updated_at),
61573
+ digest: digestMementosProjectRegistrationValue(normalized),
61574
+ membership: "project_aggregate"
61575
+ };
61576
+ }
61577
+ function memoryResources(db, projectId) {
61578
+ const rows = db.all("SELECT * FROM memories WHERE project_id = ? ORDER BY id ASC", projectId);
61579
+ return rows.map((row) => {
61580
+ const memory = normalizeSqlValue(parseMemoryRow(row));
61581
+ return {
61582
+ authority: "mementos",
61583
+ source_package: "@hasna/mementos",
61584
+ project_id: projectId,
61585
+ resource_kind: row["category"] === "knowledge" ? "knowledge" : "memory",
61586
+ stable_id: String(row["id"]),
61587
+ revision: timestamp(row["updated_at"]),
61588
+ digest: digestMementosProjectRegistrationValue(memory),
61589
+ membership: "explicit_project_id_or_focus"
61590
+ };
61591
+ });
61592
+ }
61593
+ function sessionResources(db, projectId) {
61594
+ const rows = db.all("SELECT * FROM session_memory_jobs WHERE project_id = ? ORDER BY id ASC", projectId);
61595
+ return rows.map((row) => {
61596
+ const normalized = {
61597
+ id: String(row["id"]),
61598
+ session_id: String(row["session_id"]),
61599
+ agent_id: row["agent_id"] === null ? null : String(row["agent_id"] ?? "") || null,
61600
+ project_id: row["project_id"] === null ? null : String(row["project_id"] ?? "") || null,
61601
+ source: String(row["source"]),
61602
+ status: String(row["status"]),
61603
+ transcript: String(row["transcript"]),
61604
+ chunk_count: Number(row["chunk_count"]),
61605
+ memories_extracted: Number(row["memories_extracted"]),
61606
+ error: row["error"] === null ? null : String(row["error"] ?? "") || null,
61607
+ metadata: typeof row["metadata"] === "string" ? JSON.parse(row["metadata"] || "{}") : normalizeSqlValue(row["metadata"] ?? {}),
61608
+ created_at: timestamp(row["created_at"]),
61609
+ started_at: row["started_at"] === null ? null : timestamp(row["started_at"]),
61610
+ completed_at: row["completed_at"] === null ? null : timestamp(row["completed_at"])
61611
+ };
61612
+ return {
61613
+ authority: "mementos",
61614
+ source_package: "@hasna/mementos",
61615
+ project_id: projectId,
61616
+ resource_kind: "session",
61617
+ stable_id: String(row["id"]),
61618
+ revision: timestamp(row["completed_at"] ?? row["started_at"] ?? row["created_at"]),
61619
+ digest: digestMementosProjectRegistrationValue(normalized),
61620
+ membership: "explicit_project_id_or_focus"
61621
+ };
61622
+ });
61623
+ }
61624
+ function normalizeResourceKinds(value) {
61625
+ if (!value)
61626
+ return [...MEMENTOS_PROJECT_RESOURCE_KINDS];
61627
+ if (value.length === 0) {
61628
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", "resource_kinds must contain at least one supported resource kind");
61629
+ }
61630
+ const requested = new Set(value);
61631
+ for (const kind of requested) {
61632
+ if (!MEMENTOS_PROJECT_RESOURCE_KINDS.includes(kind)) {
61633
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", `Unsupported Mementos project resource kind: ${kind}`);
61634
+ }
61635
+ }
61636
+ return MEMENTOS_PROJECT_RESOURCE_KINDS.filter((kind) => requested.has(kind));
61637
+ }
61638
+ function normalizeLimit(value) {
61639
+ const limit = value ?? DEFAULT_PAGE_LIMIT;
61640
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_PAGE_LIMIT) {
61641
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", `limit must be an integer between 1 and ${MAX_PAGE_LIMIT}`);
61642
+ }
61643
+ return limit;
61644
+ }
61645
+ function encodeCursor(cursor) {
61646
+ return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
61647
+ }
61648
+ function decodeCursor(raw) {
61649
+ try {
61650
+ const parsed = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
61651
+ 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") {
61652
+ throw new Error("invalid cursor shape");
61653
+ }
61654
+ return parsed;
61655
+ } catch {
61656
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", "cursor is not a valid Mementos project-resource cursor");
61657
+ }
61658
+ }
61659
+ function localPopulation(projectId, db, resourceKinds) {
61660
+ const project = exactProject(db, projectId);
61661
+ const selected = new Set(resourceKinds);
61662
+ const resources = [
61663
+ ...selected.has("project") ? [projectResource(project)] : [],
61664
+ ...memoryResources(db, projectId).filter((resource) => selected.has(resource.resource_kind)),
61665
+ ...selected.has("session") ? sessionResources(db, projectId) : []
61666
+ ].sort((left, right) => resourceKey(left).localeCompare(resourceKey(right)));
61667
+ const collectionRevision = digestMementosProjectRegistrationValue({
61668
+ schema: MEMENTOS_PROJECT_RESOURCE_ROUTE,
61669
+ project_id: projectId,
61670
+ project_revision: timestamp(project.updated_at),
61671
+ resource_kinds: resourceKinds,
61672
+ resources: resources.map((resource) => ({
61673
+ resource_kind: resource.resource_kind,
61674
+ stable_id: resource.stable_id,
61675
+ revision: resource.revision,
61676
+ digest: resource.digest
61677
+ }))
61678
+ });
61679
+ return { project, resources, collectionRevision };
61680
+ }
61681
+ function readMementosProjectResourcePage(projectId, options = {}, db, authorityOptions = {}) {
61682
+ const resourceKinds = normalizeResourceKinds(options.resource_kinds);
61683
+ const limit = normalizeLimit(options.limit);
61684
+ if (!db && isApiMode()) {
61685
+ const { data } = apiJson("GET", `/projects/${encodeURIComponent(projectId)}/resources${toQuery({
61686
+ limit,
61687
+ cursor: options.cursor ?? undefined,
61688
+ resource_kinds: resourceKinds.join(",")
61689
+ })}`);
61690
+ return data;
61691
+ }
61692
+ const d = db ?? getDatabase();
61693
+ const { project, resources, collectionRevision } = localPopulation(projectId, d, resourceKinds);
61694
+ let start = 0;
61695
+ if (options.cursor) {
61696
+ const cursor = decodeCursor(options.cursor);
61697
+ if (cursor.project_id !== projectId || JSON.stringify(cursor.resource_kinds) !== JSON.stringify(resourceKinds)) {
61698
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", "cursor does not belong to this project and resource-kind selection");
61699
+ }
61700
+ if (cursor.collection_revision !== collectionRevision) {
61701
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_COLLECTION_CHANGED", "Mementos project resource collection changed; restart from the first page", {
61702
+ cursor_collection_revision: cursor.collection_revision,
61703
+ current_collection_revision: collectionRevision
61704
+ });
61705
+ }
61706
+ const afterIndex = resources.findIndex((resource) => resourceKey(resource) === cursor.after_key);
61707
+ if (afterIndex < 0) {
61708
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_COLLECTION_CHANGED", "Mementos project resource cursor no longer names a member; restart from the first page");
61709
+ }
61710
+ start = afterIndex + 1;
61711
+ }
61712
+ const pageResources = resources.slice(start, start + limit);
61713
+ const hasMore = start + pageResources.length < resources.length;
61714
+ const nextCursor = hasMore && pageResources.length > 0 ? encodeCursor({
61715
+ schema: CURSOR_SCHEMA,
61716
+ project_id: projectId,
61717
+ collection_revision: collectionRevision,
61718
+ resource_kinds: resourceKinds,
61719
+ after_key: resourceKey(pageResources[pageResources.length - 1])
61720
+ }) : null;
61721
+ const capability = buildMementosProjectRegistrationCapability(authorityOptions);
61722
+ return {
61723
+ schema: "mementos.project-resources.v1",
61724
+ authority: {
61725
+ authority: capability.authority,
61726
+ authority_id: capability.authority_id,
61727
+ tenant_id: capability.tenant_id,
61728
+ corpus_id: capability.corpus_id,
61729
+ package_version: capability.package_version
61730
+ },
61731
+ project_id: projectId,
61732
+ project_revision: timestamp(project.updated_at),
61733
+ collection_revision: collectionRevision,
61734
+ resource_kinds: resourceKinds,
61735
+ resources: pageResources,
61736
+ count: pageResources.length,
61737
+ total: resources.length,
61738
+ limit,
61739
+ cursor: options.cursor ?? null,
61740
+ next_cursor: nextCursor,
61741
+ has_more: hasMore,
61742
+ complete: true,
61743
+ truncated: false
61744
+ };
61745
+ }
61746
+ function readAllMementosProjectResources(projectId, options = {}, db, authorityOptions = {}) {
61747
+ const pageSize = normalizeLimit(options.page_size);
61748
+ let cursor = null;
61749
+ let first = null;
61750
+ const resources = [];
61751
+ const seen = new Set;
61752
+ do {
61753
+ const page = readMementosProjectResourcePage(projectId, {
61754
+ limit: pageSize,
61755
+ cursor,
61756
+ resource_kinds: options.resource_kinds
61757
+ }, db, authorityOptions);
61758
+ if (!first)
61759
+ first = page;
61760
+ if (page.collection_revision !== first.collection_revision || page.total !== first.total || JSON.stringify(page.resource_kinds) !== JSON.stringify(first.resource_kinds)) {
61761
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_COLLECTION_CHANGED", "Mementos project resource collection changed during complete traversal");
61762
+ }
61763
+ for (const resource of page.resources) {
61764
+ const key = resourceKey(resource);
61765
+ if (seen.has(key)) {
61766
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", `Mementos project resource traversal returned duplicate stable ID: ${key}`);
61767
+ }
61768
+ seen.add(key);
61769
+ resources.push(resource);
61770
+ }
61771
+ if (page.has_more && !page.next_cursor) {
61772
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource page claimed more results without a continuation cursor");
61773
+ }
61774
+ cursor = page.next_cursor;
61775
+ } while (cursor);
61776
+ if (!first) {
61777
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", "Mementos project resource traversal returned no first page");
61778
+ }
61779
+ if (resources.length !== first.total) {
61780
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INCOMPLETE", `Mementos project resource traversal returned ${resources.length} of ${first.total} resources`);
61781
+ }
61782
+ return {
61783
+ ...first,
61784
+ resources,
61785
+ count: resources.length,
61786
+ total: resources.length,
61787
+ limit: pageSize,
61788
+ cursor: null,
61789
+ next_cursor: null,
61790
+ has_more: false,
61791
+ complete: true,
61792
+ truncated: false
61793
+ };
61794
+ }
61795
+ function getMementosProjectResourceExact(projectId, resourceKind, stableId, db, authorityOptions = {}) {
61796
+ if (!MEMENTOS_PROJECT_RESOURCE_KINDS.includes(resourceKind)) {
61797
+ throw new MementosProjectResourceError("MEMENTOS_PROJECT_RESOURCE_INVALID_INPUT", `Unsupported Mementos project resource kind: ${resourceKind}`);
61798
+ }
61799
+ if (!db && isApiMode()) {
61800
+ const { data } = apiJson("GET", `/projects/${encodeURIComponent(projectId)}/resources/${encodeURIComponent(resourceKind)}/${encodeURIComponent(stableId)}`);
61801
+ return data;
61802
+ }
61803
+ const d = db ?? getDatabase();
61804
+ const { project, resources, collectionRevision } = localPopulation(projectId, d, [
61805
+ resourceKind
61806
+ ]);
61807
+ const resource = resources.find((candidate) => candidate.stable_id === stableId);
61808
+ if (!resource) {
61809
+ 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 });
61810
+ }
61811
+ const capability = buildMementosProjectRegistrationCapability(authorityOptions);
61812
+ return {
61813
+ schema: "mementos.project-resource.v1",
61814
+ authority: {
61815
+ authority: capability.authority,
61816
+ authority_id: capability.authority_id,
61817
+ tenant_id: capability.tenant_id,
61818
+ corpus_id: capability.corpus_id,
61819
+ package_version: capability.package_version
61820
+ },
61821
+ project_id: projectId,
61822
+ project_revision: timestamp(project.updated_at),
61823
+ collection_revision: collectionRevision,
61824
+ resource,
61825
+ complete: true,
61826
+ truncated: false
61827
+ };
61828
+ }
61829
+
61830
+ // src/project-registration/index.ts
61831
+ init_types2();
61832
+
61400
61833
  // src/cli/commands/memory-cmd-crud.ts
61401
61834
  init_projects();
61402
61835
  init_agents();
@@ -61464,9 +61897,7 @@ function registerCrudCommands(program2) {
61464
61897
  throw new Error("Receipt lookup cannot be combined with link, rollback, or dry-run options");
61465
61898
  }
61466
61899
  const receipt = getMemoryProjectLinkReceipt(memoryId, lookupReceipt, {
61467
- authority_id: "mementos",
61468
- tenant_id: "default",
61469
- corpus_id: "default"
61900
+ ...resolveMementosProjectAuthorityIdentity()
61470
61901
  });
61471
61902
  if (globalOpts.json)
61472
61903
  outputJson(receipt);
@@ -61492,9 +61923,7 @@ function registerCrudCommands(program2) {
61492
61923
  throw new Error("--project-id and --rollback-receipt cannot be used together");
61493
61924
  }
61494
61925
  const common = {
61495
- authority_id: "mementos",
61496
- tenant_id: "default",
61497
- corpus_id: "default",
61926
+ ...resolveMementosProjectAuthorityIdentity(),
61498
61927
  operation_id: opts.operationId ?? idempotencyKey,
61499
61928
  step_id: opts.stepId ?? (rollbackReceipt ? "mementos_memory_project_link_rollback" : "mementos_memory_project_link"),
61500
61929
  idempotency_key: idempotencyKey,
@@ -63402,29 +63831,29 @@ function enforceQuotas(config, db) {
63402
63831
  }
63403
63832
  function archiveStale(staleDays, db) {
63404
63833
  const d = db || getDatabase();
63405
- const timestamp = now();
63834
+ const timestamp2 = now();
63406
63835
  const cutoff = new Date(Date.now() - staleDays * 24 * 60 * 60 * 1000).toISOString();
63407
63836
  const archiveWhere = `status = 'active' AND pinned = 0 AND COALESCE(accessed_at, created_at) < ?`;
63408
63837
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${archiveWhere}`).get(cutoff).c;
63409
63838
  if (count > 0) {
63410
- d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${archiveWhere}`, [timestamp, cutoff]);
63839
+ d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${archiveWhere}`, [timestamp2, cutoff]);
63411
63840
  }
63412
63841
  return count;
63413
63842
  }
63414
63843
  function archiveUnused(days, db) {
63415
63844
  const d = db || getDatabase();
63416
- const timestamp = now();
63845
+ const timestamp2 = now();
63417
63846
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
63418
63847
  const unusedWhere = `status = 'active' AND pinned = 0 AND access_count = 0 AND created_at < ?`;
63419
63848
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${unusedWhere}`).get(cutoff).c;
63420
63849
  if (count > 0) {
63421
- d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${unusedWhere}`, [timestamp, cutoff]);
63850
+ d.run(`UPDATE memories SET status = 'archived', updated_at = ? WHERE ${unusedWhere}`, [timestamp2, cutoff]);
63422
63851
  }
63423
63852
  return count;
63424
63853
  }
63425
63854
  function deprioritizeStale(days, db) {
63426
63855
  const d = db || getDatabase();
63427
- const timestamp = now();
63856
+ const timestamp2 = now();
63428
63857
  const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
63429
63858
  const deprioWhere = `status = 'active' AND pinned = 0 AND importance > 1 AND COALESCE(accessed_at, updated_at) < ?`;
63430
63859
  const count = d.query(`SELECT COUNT(*) as c FROM memories WHERE ${deprioWhere}`).get(cutoff).c;
@@ -63433,7 +63862,7 @@ function deprioritizeStale(days, db) {
63433
63862
  SET importance = importance - 1,
63434
63863
  version = version + 1,
63435
63864
  updated_at = ?
63436
- WHERE ${deprioWhere}`, [timestamp, cutoff]);
63865
+ WHERE ${deprioWhere}`, [timestamp2, cutoff]);
63437
63866
  }
63438
63867
  return count;
63439
63868
  }
@@ -63969,9 +64398,7 @@ function registerProjectCommands(program2) {
63969
64398
  process.exit(1);
63970
64399
  }
63971
64400
  const common = {
63972
- authority_id: "mementos",
63973
- tenant_id: "default",
63974
- corpus_id: "default",
64401
+ ...resolveMementosProjectAuthorityIdentity(),
63975
64402
  operation_id: opts.operationId ?? idempotencyKey,
63976
64403
  step_id: opts.stepId ?? (rollbackReceipt ? "mementos_project_rollback" : "mementos_project_update"),
63977
64404
  idempotency_key: idempotencyKey,
@@ -64027,6 +64454,54 @@ function registerProjectCommands(program2) {
64027
64454
  handleError(e);
64028
64455
  }
64029
64456
  });
64457
+ program2.command("project-resources <project-id>").description("Enumerate the complete project-owned Mementos resource population").option("--limit <n>", "Bounded page size (1-1000)", parseInt).option("--cursor <cursor>", "Opaque revision-bound continuation cursor").option("--kinds <kinds>", "Comma-separated resource kinds: project,knowledge,memory,session").option("--all", "Traverse every page and verify complete unique coverage").option("--resource-kind <kind>", "Read one exact resource kind").option("--resource-id <id>", "Read one exact stable resource ID").action((projectId, opts) => {
64458
+ try {
64459
+ const globalOpts = program2.opts();
64460
+ const resourceKind = opts.resourceKind;
64461
+ const resourceId = opts.resourceId;
64462
+ if (Boolean(resourceKind) !== Boolean(resourceId)) {
64463
+ throw new Error("--resource-kind and --resource-id must be provided together");
64464
+ }
64465
+ const resourceKinds = opts.kinds === undefined ? undefined : String(opts.kinds).split(",").map((value) => value.trim()).filter(Boolean);
64466
+ if (resourceKinds) {
64467
+ for (const kind of resourceKinds) {
64468
+ if (!MEMENTOS_PROJECT_RESOURCE_KINDS.includes(kind)) {
64469
+ throw new Error(`Unsupported project resource kind: ${kind}`);
64470
+ }
64471
+ }
64472
+ }
64473
+ const result = resourceKind && resourceId ? getMementosProjectResourceExact(projectId, resourceKind, resourceId) : opts.all ? readAllMementosProjectResources(projectId, {
64474
+ page_size: opts.limit,
64475
+ resource_kinds: resourceKinds
64476
+ }) : readMementosProjectResourcePage(projectId, {
64477
+ limit: opts.limit,
64478
+ cursor: opts.cursor,
64479
+ resource_kinds: resourceKinds
64480
+ });
64481
+ if (globalOpts.json) {
64482
+ outputJson(result);
64483
+ return;
64484
+ }
64485
+ if ("resource" in result) {
64486
+ console.log(chalk23.green("Project resource:"));
64487
+ console.log(` ${chalk23.bold("Project:")} ${result.project_id}`);
64488
+ console.log(` ${chalk23.bold("Kind:")} ${result.resource.resource_kind}`);
64489
+ console.log(` ${chalk23.bold("Stable ID:")} ${result.resource.stable_id}`);
64490
+ console.log(` ${chalk23.bold("Revision:")} ${result.resource.revision}`);
64491
+ return;
64492
+ }
64493
+ console.log(chalk23.bold(`${result.count} of ${result.total} project resource${result.total === 1 ? "" : "s"}:`));
64494
+ for (const resource of result.resources) {
64495
+ console.log(` ${chalk23.dim(resource.resource_kind.padEnd(9))} ${resource.stable_id}`);
64496
+ }
64497
+ console.log(` ${chalk23.bold("Complete:")} ${result.complete} ` + `${chalk23.bold("Has more:")} ${result.has_more}`);
64498
+ if (result.next_cursor) {
64499
+ console.log(chalk23.dim(`Next: mementos project-resources ${projectId} --cursor ${result.next_cursor}`));
64500
+ }
64501
+ } catch (error) {
64502
+ handleError(error);
64503
+ }
64504
+ });
64030
64505
  program2.command("inject").description("Output injection context for agent system prompts").option("--agent <name>", "Agent ID for scope filtering").option("--project <path>", "Project path for scope filtering").option("--session <id>", "Session ID for scope filtering").option("--machine <id>", "Machine ID for machine-local memory visibility").option("--max-tokens <n>", "Max approximate token budget", parseInt).option("--categories <cats>", "Comma-separated categories to include").option("--format <fmt>", "Output format: xml (default), compact, markdown, json").action((opts) => {
64031
64506
  try {
64032
64507
  const globalOpts = program2.opts();
@@ -64216,7 +64691,7 @@ function memoryResource(memory) {
64216
64691
  tags: memory.tags
64217
64692
  };
64218
64693
  }
64219
- function projectResource(projectId, name, externalId) {
64694
+ function projectResource2(projectId, name, externalId) {
64220
64695
  return {
64221
64696
  kind: "project",
64222
64697
  id: projectId,
@@ -64322,7 +64797,7 @@ function createMementosProjectPanel(projectRef, options = {}) {
64322
64797
  actionResource("mementos:save", "Save project memory")
64323
64798
  ],
64324
64799
  resourceRefs: [
64325
- projectResource(projectId, project?.name ?? projectRef, project?.id ?? projectRef),
64800
+ projectResource2(projectId, project?.name ?? projectRef, project?.id ?? projectRef),
64326
64801
  ...memories.slice(0, limit).map(memoryResource)
64327
64802
  ],
64328
64803
  renderFragment: {
@@ -66619,13 +67094,13 @@ function buildConflictKey(key, sourceMachine, updatedAt) {
66619
67094
  return `${key}__conflict__${machineSegment}__${timestampSegment || "0"}`;
66620
67095
  }
66621
67096
  function buildConflictClone(loser, sourceMachine, winnerId) {
66622
- const timestamp = new Date().toISOString();
67097
+ const timestamp2 = new Date().toISOString();
66623
67098
  const tags = new Set(ensureArrayValue(loser["tags"]));
66624
67099
  tags.add("sync-conflict");
66625
67100
  tags.add(`source_machine:${sourceMachine}`);
66626
67101
  const metadata = ensureObjectValue(loser["metadata"]);
66627
67102
  metadata["sync_conflict"] = true;
66628
- metadata["conflict_detected_at"] = timestamp;
67103
+ metadata["conflict_detected_at"] = timestamp2;
66629
67104
  metadata["conflict_original_id"] = loser["id"];
66630
67105
  metadata["conflict_winner_id"] = winnerId;
66631
67106
  metadata["conflict_source_machine"] = sourceMachine;
@@ -66637,10 +67112,10 @@ function buildConflictClone(loser, sourceMachine, winnerId) {
66637
67112
  metadata: JSON.stringify(metadata),
66638
67113
  access_count: 0,
66639
67114
  version: 1,
66640
- created_at: timestamp,
66641
- updated_at: timestamp,
67115
+ created_at: timestamp2,
67116
+ updated_at: timestamp2,
66642
67117
  ..."accessed_at" in loser ? { accessed_at: null } : {},
66643
- ..."ingested_at" in loser ? { ingested_at: timestamp } : {}
67118
+ ..."ingested_at" in loser ? { ingested_at: timestamp2 } : {}
66644
67119
  }, sourceMachine);
66645
67120
  }
66646
67121
  function insertConflictCloneIfMissing(db, clone) {
@@ -67404,7 +67879,7 @@ function parseMemoryLink(row) {
67404
67879
  function createMemoryLink(input, db) {
67405
67880
  const d = db || getDatabase();
67406
67881
  const id = shortUuid();
67407
- const timestamp = now();
67882
+ const timestamp2 = now();
67408
67883
  d.run(`INSERT OR IGNORE INTO memory_links (id, source_memory_id, target_memory_id, relation_type, run_id, metadata, created_at)
67409
67884
  VALUES (?, ?, ?, ?, ?, ?, ?)`, [
67410
67885
  id,
@@ -67413,7 +67888,7 @@ function createMemoryLink(input, db) {
67413
67888
  input.relation_type,
67414
67889
  input.run_id ?? null,
67415
67890
  JSON.stringify(input.metadata ?? {}),
67416
- timestamp
67891
+ timestamp2
67417
67892
  ]);
67418
67893
  const row = d.query(`SELECT * FROM memory_links
67419
67894
  WHERE source_memory_id = ? AND target_memory_id = ? AND relation_type = ? AND COALESCE(run_id, '') = ?
@@ -68612,8 +69087,8 @@ function makeBrainsCommand() {
68612
69087
  if (!existsSync11(outputDir)) {
68613
69088
  mkdirSync8(outputDir, { recursive: true });
68614
69089
  }
68615
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
68616
- const outputPath = join12(outputDir, `mementos-training-${timestamp}.jsonl`);
69090
+ const timestamp2 = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
69091
+ const outputPath = join12(outputDir, `mementos-training-${timestamp2}.jsonl`);
68617
69092
  const jsonl = result.examples.map((ex) => JSON.stringify(ex)).join(`
68618
69093
  `);
68619
69094
  writeFileSync6(outputPath, jsonl + `