dotmem 0.0.8 → 0.0.9

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/cli.js CHANGED
@@ -2989,6 +2989,7 @@ var init_policy_store = __esm({
2989
2989
  if (entry.target !== void 0) built.target = entry.target;
2990
2990
  if (entry.reason !== void 0) built.reason = entry.reason;
2991
2991
  if (entry.detail !== void 0) built.detail = entry.detail;
2992
+ if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
2992
2993
  return built;
2993
2994
  }, "validatePolicyAuditEntry");
2994
2995
  validatePolicyDocument = /* @__PURE__ */ __name((value) => {
@@ -3066,6 +3067,7 @@ var init_policy_store = __esm({
3066
3067
  if (entry.target !== void 0) built.target = entry.target;
3067
3068
  if (entry.reason !== void 0) built.reason = entry.reason;
3068
3069
  if (entry.detail !== void 0) built.detail = entry.detail;
3070
+ if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
3069
3071
  return built;
3070
3072
  }, "buildAuditEntry");
3071
3073
  boundedAudit = /* @__PURE__ */ __name((entries2, overrides) => {
@@ -3078,9 +3080,10 @@ var init_policy_store = __esm({
3078
3080
  for (let index = entries2.length - 1; index >= 0; index--) {
3079
3081
  const entry = entries2[index];
3080
3082
  const activeLinked = entry.target !== void 0 && activeOverrideIds.has(entry.target) && (entry.action === "override" || entry.action === "revoke");
3081
- if (activeLinked || budget > 0) {
3083
+ const mandatory = entry.mandatory === true;
3084
+ if (activeLinked || mandatory || budget > 0) {
3082
3085
  kept.unshift(entry);
3083
- if (!activeLinked) budget--;
3086
+ if (!activeLinked && !mandatory) budget--;
3084
3087
  }
3085
3088
  }
3086
3089
  return kept;
@@ -3121,6 +3124,12 @@ var init_policy_store = __esm({
3121
3124
  try {
3122
3125
  await fs5.writeFile(temporary, `${JSON.stringify(document, null, 2)}
3123
3126
  `, "utf8");
3127
+ const handle = await fs5.open(temporary, "r+");
3128
+ try {
3129
+ await handle.sync();
3130
+ } finally {
3131
+ await handle.close();
3132
+ }
3124
3133
  await fs5.rename(temporary, filePath);
3125
3134
  } catch (error) {
3126
3135
  await fs5.rm(temporary, { force: true }).catch(() => void 0);
@@ -5794,6 +5803,109 @@ ${contentId}
5794
5803
  }
5795
5804
  });
5796
5805
 
5806
+ // core/storage-coordinator/index.ts
5807
+ var nodeFs4, withStorageLock, readStorageGeneration, writeStorageGeneration;
5808
+ var init_storage_coordinator = __esm({
5809
+ "core/storage-coordinator/index.ts"() {
5810
+ nodeFs4 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
5811
+ withStorageLock = /* @__PURE__ */ __name(async (directoryPath, operationOrOptions, operation, _options = {}) => {
5812
+ const opts = typeof operationOrOptions === "function" ? operation ?? {} : operationOrOptions ?? {};
5813
+ const op = typeof operationOrOptions === "function" ? operationOrOptions : operation;
5814
+ const timeoutMs = opts.timeoutMs ?? 3e4;
5815
+ const staleMs = opts.staleMs ?? 6e4;
5816
+ const lockName = opts.lockName ?? ".storage.lock";
5817
+ const lockPath = `${directoryPath}/${lockName}`;
5818
+ const fs5 = await nodeFs4();
5819
+ const started = Date.now();
5820
+ await fs5.mkdir(directoryPath, { recursive: true });
5821
+ while (true) {
5822
+ try {
5823
+ await fs5.mkdir(lockPath);
5824
+ const token2 = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
5825
+ await fs5.writeFile(`${lockPath}/owner.json`, JSON.stringify({ token: token2, pid: process.pid, createdAt: Date.now() }), { flag: "wx" });
5826
+ break;
5827
+ } catch (error) {
5828
+ const code = error.code;
5829
+ if (code !== "EEXIST" && code !== "ENOENT" && code !== "EPERM" && code !== "ENOTEMPTY") throw error;
5830
+ let stale = false;
5831
+ let processDead = false;
5832
+ try {
5833
+ const raw = await fs5.readFile(`${lockPath}/owner.json`, "utf8");
5834
+ const parsed = JSON.parse(raw);
5835
+ const ageStale = typeof parsed.createdAt === "number" && Date.now() - parsed.createdAt > staleMs;
5836
+ if (typeof parsed.pid === "number") {
5837
+ try {
5838
+ process.kill(parsed.pid, 0);
5839
+ } catch {
5840
+ processDead = true;
5841
+ }
5842
+ } else {
5843
+ processDead = true;
5844
+ }
5845
+ if (ageStale || processDead) stale = true;
5846
+ } catch {
5847
+ stale = true;
5848
+ }
5849
+ if (stale) {
5850
+ if (processDead) {
5851
+ try {
5852
+ await fs5.rm(lockPath, { recursive: true, force: true });
5853
+ continue;
5854
+ } catch {
5855
+ }
5856
+ }
5857
+ try {
5858
+ await fs5.rmdir(lockPath);
5859
+ continue;
5860
+ } catch {
5861
+ }
5862
+ }
5863
+ if (Date.now() - started > timeoutMs) {
5864
+ throw new Error(`Storage lock timeout for ${directoryPath}`);
5865
+ }
5866
+ await new Promise((resolve) => setTimeout(resolve, 25));
5867
+ }
5868
+ }
5869
+ let token = "";
5870
+ try {
5871
+ const raw = await fs5.readFile(`${lockPath}/owner.json`, "utf8");
5872
+ token = JSON.parse(raw).token ?? "";
5873
+ } catch {
5874
+ token = "";
5875
+ }
5876
+ try {
5877
+ return await op();
5878
+ } finally {
5879
+ try {
5880
+ const raw = await fs5.readFile(`${lockPath}/owner.json`, "utf8");
5881
+ const parsed = JSON.parse(raw);
5882
+ if (parsed.token === token && token) {
5883
+ await fs5.rm(lockPath, { recursive: true, force: true });
5884
+ }
5885
+ } catch {
5886
+ }
5887
+ }
5888
+ }, "withStorageLock");
5889
+ readStorageGeneration = /* @__PURE__ */ __name(async (directoryPath) => {
5890
+ const fs5 = await nodeFs4();
5891
+ try {
5892
+ const raw = await fs5.readFile(`${directoryPath}/.storage.generation`, "utf8");
5893
+ const value = Number(raw.trim());
5894
+ return Number.isFinite(value) && value >= 0 ? value : 0;
5895
+ } catch {
5896
+ return 0;
5897
+ }
5898
+ }, "readStorageGeneration");
5899
+ writeStorageGeneration = /* @__PURE__ */ __name(async (directoryPath, value) => {
5900
+ const fs5 = await nodeFs4();
5901
+ await fs5.mkdir(directoryPath, { recursive: true });
5902
+ const temp = `${directoryPath}/.storage.generation.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
5903
+ await fs5.writeFile(temp, String(value));
5904
+ await fs5.rename(temp, `${directoryPath}/.storage.generation`);
5905
+ }, "writeStorageGeneration");
5906
+ }
5907
+ });
5908
+
5797
5909
  // core/knowledge/mem-repository.ts
5798
5910
  function resolveDuplicate(existing, existingSource, current, currentSource) {
5799
5911
  const existingRetracts = existing.revision?.kind === "retracts";
@@ -5834,19 +5946,20 @@ async function parseMemFile(value, sourceId) {
5834
5946
  throw new Error(`${message} in ${sourceId}`);
5835
5947
  }
5836
5948
  }
5837
- var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE, directoryLocks, nodeFs4, separator2, normalizePath, isAbsolutePath, resolvePath, joinPath2, parentPath, uniqueSuffix, withDirectoryLock, isMissingDirectory, sourcePriority, memFilePath, MemDirectoryKnowledgeRepository;
5949
+ var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE, nodeFs5, repositoryLoadCaches, separator2, normalizePath, isAbsolutePath, resolvePath, joinPath2, parentPath, uniqueSuffix, isMissingDirectory, sourcePriority, memFilePath, MemDirectoryKnowledgeRepository;
5838
5950
  var init_mem_repository = __esm({
5839
5951
  "core/knowledge/mem-repository.ts"() {
5840
5952
  init_mem_codec();
5841
5953
  init_mem_package();
5842
5954
  init_state();
5843
5955
  init_acquisition_limits();
5956
+ init_storage_coordinator();
5844
5957
  DOTMEM_DIRECTORY = ".dotmem";
5845
5958
  DEFAULT_MEM_FILE = "project.mem";
5846
- directoryLocks = /* @__PURE__ */ new Map();
5847
- nodeFs4 = /* @__PURE__ */ __name(async () => {
5959
+ nodeFs5 = /* @__PURE__ */ __name(async () => {
5848
5960
  return import('fs/promises');
5849
5961
  }, "nodeFs");
5962
+ repositoryLoadCaches = /* @__PURE__ */ new Map();
5850
5963
  separator2 = /* @__PURE__ */ __name(() => process.platform === "win32" ? "\\" : "/", "separator");
5851
5964
  normalizePath = /* @__PURE__ */ __name((value) => value.replace(/[\\/]+/g, separator2()), "normalizePath");
5852
5965
  isAbsolutePath = /* @__PURE__ */ __name((value) => /^(?:[A-Za-z]:[\\/]|[\\/])/.test(value), "isAbsolutePath");
@@ -5858,25 +5971,8 @@ var init_mem_repository = __esm({
5858
5971
  return index <= 0 ? "." : normalized.slice(0, index);
5859
5972
  }, "parentPath");
5860
5973
  uniqueSuffix = /* @__PURE__ */ __name(() => globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, "uniqueSuffix");
5861
- withDirectoryLock = /* @__PURE__ */ __name(async (directoryPath, operation) => {
5862
- const previous = directoryLocks.get(directoryPath) ?? Promise.resolve();
5863
- let release = /* @__PURE__ */ __name(() => void 0, "release");
5864
- const gate = new Promise((resolveGate) => {
5865
- release = resolveGate;
5866
- });
5867
- const queued = previous.then(() => gate);
5868
- directoryLocks.set(directoryPath, queued);
5869
- await previous;
5870
- try {
5871
- return await operation();
5872
- } finally {
5873
- release();
5874
- if (directoryLocks.get(directoryPath) === queued) directoryLocks.delete(directoryPath);
5875
- }
5876
- }, "withDirectoryLock");
5877
5974
  isMissingDirectory = /* @__PURE__ */ __name((error) => error.code === "ENOENT", "isMissingDirectory");
5878
5975
  sourcePriority = /* @__PURE__ */ __name((sourceId, scope) => {
5879
- if (sourceId.endsWith(".mem") && sourceId !== "project.mem" && sourceId !== "user.mem" && sourceId !== "agent.mem") return 0;
5880
5976
  if (scope === "project" || sourceId === "project.mem") return 4;
5881
5977
  if (scope === "user" || sourceId === "user.mem") return 3;
5882
5978
  if (sourceId === "agent.mem" || sourceId === "agent") return 2;
@@ -5904,7 +6000,7 @@ var init_mem_repository = __esm({
5904
6000
  this.loadWritableOnly = loadWritableOnly;
5905
6001
  }
5906
6002
  async discover() {
5907
- const { readdir } = await nodeFs4();
6003
+ const { readdir } = await nodeFs5();
5908
6004
  try {
5909
6005
  const entries2 = await readdir(this.directoryPath, { withFileTypes: true });
5910
6006
  return entries2.filter((entry) => entry.isFile() && entry.name.endsWith(".mem") && entry.name !== "@brain.mem").map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
@@ -5919,7 +6015,7 @@ var init_mem_repository = __esm({
5919
6015
  * receive a clear failure instead of discovering corruption on first use.
5920
6016
  */
5921
6017
  async initialize() {
5922
- const { mkdir } = await nodeFs4();
6018
+ const { mkdir } = await nodeFs5();
5923
6019
  await mkdir(this.directoryPath, { recursive: true });
5924
6020
  await this.load();
5925
6021
  }
@@ -5934,7 +6030,7 @@ var init_mem_repository = __esm({
5934
6030
  * that does not delete is not a delete.
5935
6031
  */
5936
6032
  async clearWritable() {
5937
- const { rm, readdir } = await nodeFs4();
6033
+ const { rm, readdir } = await nodeFs5();
5938
6034
  const filePath = memFilePath(this.directoryPath, this.writableSourceId);
5939
6035
  await rm(filePath, { force: true });
5940
6036
  await rm(`${filePath}.bak`, { force: true });
@@ -5951,15 +6047,28 @@ var init_mem_repository = __esm({
5951
6047
  }
5952
6048
  }
5953
6049
  async load() {
5954
- const loaded = this.loadWritableOnly ? await this.loadSources([this.writableSourceId]) : await this.loadSources();
5955
- return this.combine(loaded);
6050
+ const cacheKey = `${this.directoryPath}:${this.writableSourceId}:${this.loadWritableOnly}`;
6051
+ for (let attempt = 0; attempt < 5; attempt += 1) {
6052
+ const generationBefore = await readStorageGeneration(this.directoryPath);
6053
+ const cached = repositoryLoadCaches.get(cacheKey);
6054
+ if (cached && cached.signature === String(generationBefore)) {
6055
+ return structuredClone(cached.state);
6056
+ }
6057
+ const loaded = this.loadWritableOnly ? await this.loadSources([this.writableSourceId]) : await this.loadSources();
6058
+ const state2 = this.combine(loaded);
6059
+ const generationAfter = await readStorageGeneration(this.directoryPath);
6060
+ if (generationBefore !== generationAfter && attempt < 4) continue;
6061
+ repositoryLoadCaches.set(cacheKey, { signature: String(generationAfter), state: structuredClone(state2) });
6062
+ return state2;
6063
+ }
6064
+ throw new Error("Unable to load stable repository state");
5956
6065
  }
5957
- async save(state2, expectedVersion) {
6066
+ async save(state2, expectedVersion, locked = false) {
5958
6067
  const validated = validateKnowledgeState(state2);
5959
6068
  if (!Number.isInteger(expectedVersion) || expectedVersion < 0 || validated.version !== expectedVersion + 1) {
5960
6069
  throw new Error("Invalid expected persisted version");
5961
6070
  }
5962
- await withDirectoryLock(this.directoryPath, async () => {
6071
+ const doWrite = /* @__PURE__ */ __name(async () => {
5963
6072
  const loaded = await this.loadSources();
5964
6073
  const writable = loaded.find((source) => source.id === this.writableSourceId) ?? {
5965
6074
  id: this.writableSourceId,
@@ -5981,10 +6090,16 @@ var init_mem_repository = __esm({
5981
6090
  claims: [...writable.state.claims, ...newClaims]
5982
6091
  };
5983
6092
  await this.writeSource(writable.filePath, nextWritable, writable.preserved, writable.package);
5984
- });
6093
+ await writeStorageGeneration(this.directoryPath, await readStorageGeneration(this.directoryPath) + 1);
6094
+ }, "doWrite");
6095
+ if (locked) {
6096
+ await doWrite();
6097
+ } else {
6098
+ await withStorageLock(this.directoryPath, doWrite);
6099
+ }
5985
6100
  }
5986
6101
  async loadSources(sourceIds) {
5987
- const { readFile: readFile8 } = await nodeFs4();
6102
+ const { readFile: readFile8 } = await nodeFs5();
5988
6103
  const ids = sourceIds ?? await this.discover();
5989
6104
  const sources = [];
5990
6105
  for (const sourceId of ids) {
@@ -6074,7 +6189,7 @@ var init_mem_repository = __esm({
6074
6189
  * valid package, so the backup is always a version this library can read.
6075
6190
  */
6076
6191
  async keepLastValidVersion(filePath) {
6077
- const { readFile: readFile8, copyFile, rename, rm } = await nodeFs4();
6192
+ const { readFile: readFile8, copyFile, rename, rm } = await nodeFs5();
6078
6193
  let current;
6079
6194
  try {
6080
6195
  current = await readFile8(filePath);
@@ -6098,7 +6213,7 @@ var init_mem_repository = __esm({
6098
6213
  }
6099
6214
  /** Best-effort directory sync: the rename is durable once the directory is. */
6100
6215
  async syncDirectory(directoryPath) {
6101
- const { open } = await nodeFs4();
6216
+ const { open } = await nodeFs5();
6102
6217
  try {
6103
6218
  const handle = await open(directoryPath, "r");
6104
6219
  try {
@@ -6110,7 +6225,7 @@ var init_mem_repository = __esm({
6110
6225
  }
6111
6226
  }
6112
6227
  async writeSource(filePath, state2, preserved, preservedPackage) {
6113
- const { mkdir, rename, rm, open, readFile: readFile8 } = await nodeFs4();
6228
+ const { mkdir, rename, rm, open, readFile: readFile8 } = await nodeFs5();
6114
6229
  if (!Number.isInteger(state2.version) || state2.version < 0) throw new Error("Invalid writable .mem knowledge version");
6115
6230
  for (const claim of state2.claims) validateClaim(claim);
6116
6231
  await mkdir(parentPath(filePath), { recursive: true });
@@ -6980,7 +7095,7 @@ var init_project_identity = __esm({
6980
7095
  });
6981
7096
 
6982
7097
  // core/knowledge/staging.ts
6983
- var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor, nodeFs5, normalizeClaimIds, revisionFromOptions, writeStageFile, readStageFile, validateStageFile, commitStageFile, cleanupStageFile;
7098
+ var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor, nodeFs6, normalizeClaimIds, revisionFromOptions, writeStageFile, readStageFile, validateStageFile, commitStageFile, cleanupStageFile;
6984
7099
  var init_staging = __esm({
6985
7100
  "core/knowledge/staging.ts"() {
6986
7101
  init_claim_record();
@@ -7015,7 +7130,7 @@ var init_staging = __esm({
7015
7130
  return `${base}/dotmem`;
7016
7131
  }, "resolveStagingDirectory");
7017
7132
  stagePathFor = /* @__PURE__ */ __name((requestId) => `${resolveStagingDirectory()}/${requestId}.stage`, "stagePathFor");
7018
- nodeFs5 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
7133
+ nodeFs6 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
7019
7134
  normalizeClaimIds = /* @__PURE__ */ __name((value) => {
7020
7135
  if (!value) return [];
7021
7136
  return Array.isArray(value) ? value : [value];
@@ -7040,7 +7155,7 @@ var init_staging = __esm({
7040
7155
  };
7041
7156
  const requestId = generateRequestId();
7042
7157
  const stagePath = stagePathFor(requestId);
7043
- const fs5 = await nodeFs5();
7158
+ const fs5 = await nodeFs6();
7044
7159
  await fs5.mkdir(resolveStagingDirectory(), { recursive: true });
7045
7160
  const stageFile = {
7046
7161
  format: DOTMEM_STAGE_FORMAT,
@@ -7060,7 +7175,7 @@ var init_staging = __esm({
7060
7175
  }, "writeStageFile");
7061
7176
  readStageFile = /* @__PURE__ */ __name(async (requestId) => {
7062
7177
  const stagePath = stagePathFor(requestId);
7063
- const fs5 = await nodeFs5();
7178
+ const fs5 = await nodeFs6();
7064
7179
  try {
7065
7180
  const content = await fs5.readFile(stagePath, "utf8");
7066
7181
  return JSON.parse(content);
@@ -7171,7 +7286,7 @@ var init_staging = __esm({
7171
7286
  }, "commitStageFile");
7172
7287
  cleanupStageFile = /* @__PURE__ */ __name(async (requestId) => {
7173
7288
  const stagePath = stagePathFor(requestId);
7174
- const fs5 = await nodeFs5();
7289
+ const fs5 = await nodeFs6();
7175
7290
  try {
7176
7291
  await fs5.unlink(stagePath);
7177
7292
  } catch (error) {
@@ -10231,10 +10346,12 @@ var init_acquire = __esm({
10231
10346
  init_project_anchor();
10232
10347
  init_acquisition_boundary();
10233
10348
  init_claim_record();
10349
+ init_state();
10234
10350
  init_router();
10235
10351
  init_store_queue();
10236
10352
  init_staging();
10237
10353
  init_user_paths();
10354
+ init_storage_coordinator();
10238
10355
  init_mem_repository();
10239
10356
  init_knowledge_runtime();
10240
10357
  __name(normalizeClaimIds3, "normalizeClaimIds");
@@ -10270,23 +10387,27 @@ var init_acquire = __esm({
10270
10387
  );
10271
10388
  }
10272
10389
  const revision = revisionFromAcquire2(opts);
10390
+ const repository = target.repository;
10273
10391
  await withStoreQueue(acquisitionStoreKey(kind, scope, sourceProject), async () => {
10274
- const state2 = await target.runtime.select([]);
10275
- await target.runtime.update({
10276
- basedOnVersion: state2.context.knowledgeVersion,
10277
- claims: [buildClaimRecord({
10278
- id: opts.id,
10279
- knowledge: boundary.knowledge,
10280
- applicability: opts.applicability,
10281
- evidence: opts.evidence,
10282
- epistemicType: opts.epistemicType,
10283
- provenance: opts.provenance,
10284
- acquiredAt: opts.acquiredAt,
10285
- revision,
10286
- scope,
10287
- sourceProject,
10288
- kind
10289
- })]
10392
+ await withStorageLock(repository.directoryPath, async () => {
10393
+ const state2 = await repository.load();
10394
+ const next = updateKnowledge(state2, {
10395
+ basedOnVersion: state2.version,
10396
+ claims: [buildClaimRecord({
10397
+ id: opts.id,
10398
+ knowledge: boundary.knowledge,
10399
+ applicability: opts.applicability,
10400
+ evidence: opts.evidence,
10401
+ epistemicType: opts.epistemicType,
10402
+ provenance: opts.provenance,
10403
+ acquiredAt: opts.acquiredAt,
10404
+ revision,
10405
+ scope,
10406
+ sourceProject,
10407
+ kind
10408
+ })]
10409
+ });
10410
+ await repository.save(next, state2.version, true);
10290
10411
  });
10291
10412
  });
10292
10413
  const verification = await target.repository.load();
@@ -12956,6 +13077,10 @@ var saveContinuityDocument = /* @__PURE__ */ __name(async (path3, document) => {
12956
13077
  }
12957
13078
  }, "saveContinuityDocument");
12958
13079
 
13080
+ // core/continuity-ledger/index.ts
13081
+ init_memory();
13082
+ init_memory();
13083
+
12959
13084
  // core/continuity/index.ts
12960
13085
  var continuity = {
12961
13086
  async context(options) {