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.cjs CHANGED
@@ -2998,6 +2998,7 @@ var init_policy_store = __esm({
2998
2998
  if (entry.target !== void 0) built.target = entry.target;
2999
2999
  if (entry.reason !== void 0) built.reason = entry.reason;
3000
3000
  if (entry.detail !== void 0) built.detail = entry.detail;
3001
+ if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
3001
3002
  return built;
3002
3003
  }, "validatePolicyAuditEntry");
3003
3004
  validatePolicyDocument = /* @__PURE__ */ __name((value) => {
@@ -3075,6 +3076,7 @@ var init_policy_store = __esm({
3075
3076
  if (entry.target !== void 0) built.target = entry.target;
3076
3077
  if (entry.reason !== void 0) built.reason = entry.reason;
3077
3078
  if (entry.detail !== void 0) built.detail = entry.detail;
3079
+ if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
3078
3080
  return built;
3079
3081
  }, "buildAuditEntry");
3080
3082
  boundedAudit = /* @__PURE__ */ __name((entries2, overrides) => {
@@ -3087,9 +3089,10 @@ var init_policy_store = __esm({
3087
3089
  for (let index = entries2.length - 1; index >= 0; index--) {
3088
3090
  const entry = entries2[index];
3089
3091
  const activeLinked = entry.target !== void 0 && activeOverrideIds.has(entry.target) && (entry.action === "override" || entry.action === "revoke");
3090
- if (activeLinked || budget > 0) {
3092
+ const mandatory = entry.mandatory === true;
3093
+ if (activeLinked || mandatory || budget > 0) {
3091
3094
  kept.unshift(entry);
3092
- if (!activeLinked) budget--;
3095
+ if (!activeLinked && !mandatory) budget--;
3093
3096
  }
3094
3097
  }
3095
3098
  return kept;
@@ -3130,6 +3133,12 @@ var init_policy_store = __esm({
3130
3133
  try {
3131
3134
  await fs5.writeFile(temporary, `${JSON.stringify(document, null, 2)}
3132
3135
  `, "utf8");
3136
+ const handle = await fs5.open(temporary, "r+");
3137
+ try {
3138
+ await handle.sync();
3139
+ } finally {
3140
+ await handle.close();
3141
+ }
3133
3142
  await fs5.rename(temporary, filePath);
3134
3143
  } catch (error) {
3135
3144
  await fs5.rm(temporary, { force: true }).catch(() => void 0);
@@ -5803,6 +5812,109 @@ ${contentId}
5803
5812
  }
5804
5813
  });
5805
5814
 
5815
+ // core/storage-coordinator/index.ts
5816
+ var nodeFs4, withStorageLock, readStorageGeneration, writeStorageGeneration;
5817
+ var init_storage_coordinator = __esm({
5818
+ "core/storage-coordinator/index.ts"() {
5819
+ nodeFs4 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
5820
+ withStorageLock = /* @__PURE__ */ __name(async (directoryPath, operationOrOptions, operation, _options = {}) => {
5821
+ const opts = typeof operationOrOptions === "function" ? operation ?? {} : operationOrOptions ?? {};
5822
+ const op = typeof operationOrOptions === "function" ? operationOrOptions : operation;
5823
+ const timeoutMs = opts.timeoutMs ?? 3e4;
5824
+ const staleMs = opts.staleMs ?? 6e4;
5825
+ const lockName = opts.lockName ?? ".storage.lock";
5826
+ const lockPath = `${directoryPath}/${lockName}`;
5827
+ const fs5 = await nodeFs4();
5828
+ const started = Date.now();
5829
+ await fs5.mkdir(directoryPath, { recursive: true });
5830
+ while (true) {
5831
+ try {
5832
+ await fs5.mkdir(lockPath);
5833
+ const token2 = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
5834
+ await fs5.writeFile(`${lockPath}/owner.json`, JSON.stringify({ token: token2, pid: process.pid, createdAt: Date.now() }), { flag: "wx" });
5835
+ break;
5836
+ } catch (error) {
5837
+ const code = error.code;
5838
+ if (code !== "EEXIST" && code !== "ENOENT" && code !== "EPERM" && code !== "ENOTEMPTY") throw error;
5839
+ let stale = false;
5840
+ let processDead = false;
5841
+ try {
5842
+ const raw = await fs5.readFile(`${lockPath}/owner.json`, "utf8");
5843
+ const parsed = JSON.parse(raw);
5844
+ const ageStale = typeof parsed.createdAt === "number" && Date.now() - parsed.createdAt > staleMs;
5845
+ if (typeof parsed.pid === "number") {
5846
+ try {
5847
+ process.kill(parsed.pid, 0);
5848
+ } catch {
5849
+ processDead = true;
5850
+ }
5851
+ } else {
5852
+ processDead = true;
5853
+ }
5854
+ if (ageStale || processDead) stale = true;
5855
+ } catch {
5856
+ stale = true;
5857
+ }
5858
+ if (stale) {
5859
+ if (processDead) {
5860
+ try {
5861
+ await fs5.rm(lockPath, { recursive: true, force: true });
5862
+ continue;
5863
+ } catch {
5864
+ }
5865
+ }
5866
+ try {
5867
+ await fs5.rmdir(lockPath);
5868
+ continue;
5869
+ } catch {
5870
+ }
5871
+ }
5872
+ if (Date.now() - started > timeoutMs) {
5873
+ throw new Error(`Storage lock timeout for ${directoryPath}`);
5874
+ }
5875
+ await new Promise((resolve) => setTimeout(resolve, 25));
5876
+ }
5877
+ }
5878
+ let token = "";
5879
+ try {
5880
+ const raw = await fs5.readFile(`${lockPath}/owner.json`, "utf8");
5881
+ token = JSON.parse(raw).token ?? "";
5882
+ } catch {
5883
+ token = "";
5884
+ }
5885
+ try {
5886
+ return await op();
5887
+ } finally {
5888
+ try {
5889
+ const raw = await fs5.readFile(`${lockPath}/owner.json`, "utf8");
5890
+ const parsed = JSON.parse(raw);
5891
+ if (parsed.token === token && token) {
5892
+ await fs5.rm(lockPath, { recursive: true, force: true });
5893
+ }
5894
+ } catch {
5895
+ }
5896
+ }
5897
+ }, "withStorageLock");
5898
+ readStorageGeneration = /* @__PURE__ */ __name(async (directoryPath) => {
5899
+ const fs5 = await nodeFs4();
5900
+ try {
5901
+ const raw = await fs5.readFile(`${directoryPath}/.storage.generation`, "utf8");
5902
+ const value = Number(raw.trim());
5903
+ return Number.isFinite(value) && value >= 0 ? value : 0;
5904
+ } catch {
5905
+ return 0;
5906
+ }
5907
+ }, "readStorageGeneration");
5908
+ writeStorageGeneration = /* @__PURE__ */ __name(async (directoryPath, value) => {
5909
+ const fs5 = await nodeFs4();
5910
+ await fs5.mkdir(directoryPath, { recursive: true });
5911
+ const temp = `${directoryPath}/.storage.generation.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
5912
+ await fs5.writeFile(temp, String(value));
5913
+ await fs5.rename(temp, `${directoryPath}/.storage.generation`);
5914
+ }, "writeStorageGeneration");
5915
+ }
5916
+ });
5917
+
5806
5918
  // core/knowledge/mem-repository.ts
5807
5919
  function resolveDuplicate(existing, existingSource, current, currentSource) {
5808
5920
  const existingRetracts = existing.revision?.kind === "retracts";
@@ -5843,19 +5955,20 @@ async function parseMemFile(value, sourceId) {
5843
5955
  throw new Error(`${message} in ${sourceId}`);
5844
5956
  }
5845
5957
  }
5846
- var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE, directoryLocks, nodeFs4, separator2, normalizePath, isAbsolutePath, resolvePath, joinPath2, parentPath, uniqueSuffix, withDirectoryLock, isMissingDirectory, sourcePriority, memFilePath, MemDirectoryKnowledgeRepository;
5958
+ var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE, nodeFs5, repositoryLoadCaches, separator2, normalizePath, isAbsolutePath, resolvePath, joinPath2, parentPath, uniqueSuffix, isMissingDirectory, sourcePriority, memFilePath, MemDirectoryKnowledgeRepository;
5847
5959
  var init_mem_repository = __esm({
5848
5960
  "core/knowledge/mem-repository.ts"() {
5849
5961
  init_mem_codec();
5850
5962
  init_mem_package();
5851
5963
  init_state();
5852
5964
  init_acquisition_limits();
5965
+ init_storage_coordinator();
5853
5966
  DOTMEM_DIRECTORY = ".dotmem";
5854
5967
  DEFAULT_MEM_FILE = "project.mem";
5855
- directoryLocks = /* @__PURE__ */ new Map();
5856
- nodeFs4 = /* @__PURE__ */ __name(async () => {
5968
+ nodeFs5 = /* @__PURE__ */ __name(async () => {
5857
5969
  return import('fs/promises');
5858
5970
  }, "nodeFs");
5971
+ repositoryLoadCaches = /* @__PURE__ */ new Map();
5859
5972
  separator2 = /* @__PURE__ */ __name(() => process.platform === "win32" ? "\\" : "/", "separator");
5860
5973
  normalizePath = /* @__PURE__ */ __name((value) => value.replace(/[\\/]+/g, separator2()), "normalizePath");
5861
5974
  isAbsolutePath = /* @__PURE__ */ __name((value) => /^(?:[A-Za-z]:[\\/]|[\\/])/.test(value), "isAbsolutePath");
@@ -5867,25 +5980,8 @@ var init_mem_repository = __esm({
5867
5980
  return index <= 0 ? "." : normalized.slice(0, index);
5868
5981
  }, "parentPath");
5869
5982
  uniqueSuffix = /* @__PURE__ */ __name(() => globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, "uniqueSuffix");
5870
- withDirectoryLock = /* @__PURE__ */ __name(async (directoryPath, operation) => {
5871
- const previous = directoryLocks.get(directoryPath) ?? Promise.resolve();
5872
- let release = /* @__PURE__ */ __name(() => void 0, "release");
5873
- const gate = new Promise((resolveGate) => {
5874
- release = resolveGate;
5875
- });
5876
- const queued = previous.then(() => gate);
5877
- directoryLocks.set(directoryPath, queued);
5878
- await previous;
5879
- try {
5880
- return await operation();
5881
- } finally {
5882
- release();
5883
- if (directoryLocks.get(directoryPath) === queued) directoryLocks.delete(directoryPath);
5884
- }
5885
- }, "withDirectoryLock");
5886
5983
  isMissingDirectory = /* @__PURE__ */ __name((error) => error.code === "ENOENT", "isMissingDirectory");
5887
5984
  sourcePriority = /* @__PURE__ */ __name((sourceId, scope) => {
5888
- if (sourceId.endsWith(".mem") && sourceId !== "project.mem" && sourceId !== "user.mem" && sourceId !== "agent.mem") return 0;
5889
5985
  if (scope === "project" || sourceId === "project.mem") return 4;
5890
5986
  if (scope === "user" || sourceId === "user.mem") return 3;
5891
5987
  if (sourceId === "agent.mem" || sourceId === "agent") return 2;
@@ -5913,7 +6009,7 @@ var init_mem_repository = __esm({
5913
6009
  this.loadWritableOnly = loadWritableOnly;
5914
6010
  }
5915
6011
  async discover() {
5916
- const { readdir } = await nodeFs4();
6012
+ const { readdir } = await nodeFs5();
5917
6013
  try {
5918
6014
  const entries2 = await readdir(this.directoryPath, { withFileTypes: true });
5919
6015
  return entries2.filter((entry) => entry.isFile() && entry.name.endsWith(".mem") && entry.name !== "@brain.mem").map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
@@ -5928,7 +6024,7 @@ var init_mem_repository = __esm({
5928
6024
  * receive a clear failure instead of discovering corruption on first use.
5929
6025
  */
5930
6026
  async initialize() {
5931
- const { mkdir } = await nodeFs4();
6027
+ const { mkdir } = await nodeFs5();
5932
6028
  await mkdir(this.directoryPath, { recursive: true });
5933
6029
  await this.load();
5934
6030
  }
@@ -5943,7 +6039,7 @@ var init_mem_repository = __esm({
5943
6039
  * that does not delete is not a delete.
5944
6040
  */
5945
6041
  async clearWritable() {
5946
- const { rm, readdir } = await nodeFs4();
6042
+ const { rm, readdir } = await nodeFs5();
5947
6043
  const filePath = memFilePath(this.directoryPath, this.writableSourceId);
5948
6044
  await rm(filePath, { force: true });
5949
6045
  await rm(`${filePath}.bak`, { force: true });
@@ -5960,15 +6056,28 @@ var init_mem_repository = __esm({
5960
6056
  }
5961
6057
  }
5962
6058
  async load() {
5963
- const loaded = this.loadWritableOnly ? await this.loadSources([this.writableSourceId]) : await this.loadSources();
5964
- return this.combine(loaded);
6059
+ const cacheKey = `${this.directoryPath}:${this.writableSourceId}:${this.loadWritableOnly}`;
6060
+ for (let attempt = 0; attempt < 5; attempt += 1) {
6061
+ const generationBefore = await readStorageGeneration(this.directoryPath);
6062
+ const cached = repositoryLoadCaches.get(cacheKey);
6063
+ if (cached && cached.signature === String(generationBefore)) {
6064
+ return structuredClone(cached.state);
6065
+ }
6066
+ const loaded = this.loadWritableOnly ? await this.loadSources([this.writableSourceId]) : await this.loadSources();
6067
+ const state2 = this.combine(loaded);
6068
+ const generationAfter = await readStorageGeneration(this.directoryPath);
6069
+ if (generationBefore !== generationAfter && attempt < 4) continue;
6070
+ repositoryLoadCaches.set(cacheKey, { signature: String(generationAfter), state: structuredClone(state2) });
6071
+ return state2;
6072
+ }
6073
+ throw new Error("Unable to load stable repository state");
5965
6074
  }
5966
- async save(state2, expectedVersion) {
6075
+ async save(state2, expectedVersion, locked = false) {
5967
6076
  const validated = validateKnowledgeState(state2);
5968
6077
  if (!Number.isInteger(expectedVersion) || expectedVersion < 0 || validated.version !== expectedVersion + 1) {
5969
6078
  throw new Error("Invalid expected persisted version");
5970
6079
  }
5971
- await withDirectoryLock(this.directoryPath, async () => {
6080
+ const doWrite = /* @__PURE__ */ __name(async () => {
5972
6081
  const loaded = await this.loadSources();
5973
6082
  const writable = loaded.find((source) => source.id === this.writableSourceId) ?? {
5974
6083
  id: this.writableSourceId,
@@ -5990,10 +6099,16 @@ var init_mem_repository = __esm({
5990
6099
  claims: [...writable.state.claims, ...newClaims]
5991
6100
  };
5992
6101
  await this.writeSource(writable.filePath, nextWritable, writable.preserved, writable.package);
5993
- });
6102
+ await writeStorageGeneration(this.directoryPath, await readStorageGeneration(this.directoryPath) + 1);
6103
+ }, "doWrite");
6104
+ if (locked) {
6105
+ await doWrite();
6106
+ } else {
6107
+ await withStorageLock(this.directoryPath, doWrite);
6108
+ }
5994
6109
  }
5995
6110
  async loadSources(sourceIds) {
5996
- const { readFile: readFile8 } = await nodeFs4();
6111
+ const { readFile: readFile8 } = await nodeFs5();
5997
6112
  const ids = sourceIds ?? await this.discover();
5998
6113
  const sources = [];
5999
6114
  for (const sourceId of ids) {
@@ -6083,7 +6198,7 @@ var init_mem_repository = __esm({
6083
6198
  * valid package, so the backup is always a version this library can read.
6084
6199
  */
6085
6200
  async keepLastValidVersion(filePath) {
6086
- const { readFile: readFile8, copyFile, rename, rm } = await nodeFs4();
6201
+ const { readFile: readFile8, copyFile, rename, rm } = await nodeFs5();
6087
6202
  let current;
6088
6203
  try {
6089
6204
  current = await readFile8(filePath);
@@ -6107,7 +6222,7 @@ var init_mem_repository = __esm({
6107
6222
  }
6108
6223
  /** Best-effort directory sync: the rename is durable once the directory is. */
6109
6224
  async syncDirectory(directoryPath) {
6110
- const { open } = await nodeFs4();
6225
+ const { open } = await nodeFs5();
6111
6226
  try {
6112
6227
  const handle = await open(directoryPath, "r");
6113
6228
  try {
@@ -6119,7 +6234,7 @@ var init_mem_repository = __esm({
6119
6234
  }
6120
6235
  }
6121
6236
  async writeSource(filePath, state2, preserved, preservedPackage) {
6122
- const { mkdir, rename, rm, open, readFile: readFile8 } = await nodeFs4();
6237
+ const { mkdir, rename, rm, open, readFile: readFile8 } = await nodeFs5();
6123
6238
  if (!Number.isInteger(state2.version) || state2.version < 0) throw new Error("Invalid writable .mem knowledge version");
6124
6239
  for (const claim of state2.claims) validateClaim(claim);
6125
6240
  await mkdir(parentPath(filePath), { recursive: true });
@@ -6989,7 +7104,7 @@ var init_project_identity = __esm({
6989
7104
  });
6990
7105
 
6991
7106
  // core/knowledge/staging.ts
6992
- var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor, nodeFs5, normalizeClaimIds, revisionFromOptions, writeStageFile, readStageFile, validateStageFile, commitStageFile, cleanupStageFile;
7107
+ var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor, nodeFs6, normalizeClaimIds, revisionFromOptions, writeStageFile, readStageFile, validateStageFile, commitStageFile, cleanupStageFile;
6993
7108
  var init_staging = __esm({
6994
7109
  "core/knowledge/staging.ts"() {
6995
7110
  init_claim_record();
@@ -7024,7 +7139,7 @@ var init_staging = __esm({
7024
7139
  return `${base}/dotmem`;
7025
7140
  }, "resolveStagingDirectory");
7026
7141
  stagePathFor = /* @__PURE__ */ __name((requestId) => `${resolveStagingDirectory()}/${requestId}.stage`, "stagePathFor");
7027
- nodeFs5 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
7142
+ nodeFs6 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
7028
7143
  normalizeClaimIds = /* @__PURE__ */ __name((value) => {
7029
7144
  if (!value) return [];
7030
7145
  return Array.isArray(value) ? value : [value];
@@ -7049,7 +7164,7 @@ var init_staging = __esm({
7049
7164
  };
7050
7165
  const requestId = generateRequestId();
7051
7166
  const stagePath = stagePathFor(requestId);
7052
- const fs5 = await nodeFs5();
7167
+ const fs5 = await nodeFs6();
7053
7168
  await fs5.mkdir(resolveStagingDirectory(), { recursive: true });
7054
7169
  const stageFile = {
7055
7170
  format: DOTMEM_STAGE_FORMAT,
@@ -7069,7 +7184,7 @@ var init_staging = __esm({
7069
7184
  }, "writeStageFile");
7070
7185
  readStageFile = /* @__PURE__ */ __name(async (requestId) => {
7071
7186
  const stagePath = stagePathFor(requestId);
7072
- const fs5 = await nodeFs5();
7187
+ const fs5 = await nodeFs6();
7073
7188
  try {
7074
7189
  const content = await fs5.readFile(stagePath, "utf8");
7075
7190
  return JSON.parse(content);
@@ -7180,7 +7295,7 @@ var init_staging = __esm({
7180
7295
  }, "commitStageFile");
7181
7296
  cleanupStageFile = /* @__PURE__ */ __name(async (requestId) => {
7182
7297
  const stagePath = stagePathFor(requestId);
7183
- const fs5 = await nodeFs5();
7298
+ const fs5 = await nodeFs6();
7184
7299
  try {
7185
7300
  await fs5.unlink(stagePath);
7186
7301
  } catch (error) {
@@ -10240,10 +10355,12 @@ var init_acquire = __esm({
10240
10355
  init_project_anchor();
10241
10356
  init_acquisition_boundary();
10242
10357
  init_claim_record();
10358
+ init_state();
10243
10359
  init_router();
10244
10360
  init_store_queue();
10245
10361
  init_staging();
10246
10362
  init_user_paths();
10363
+ init_storage_coordinator();
10247
10364
  init_mem_repository();
10248
10365
  init_knowledge_runtime();
10249
10366
  __name(normalizeClaimIds3, "normalizeClaimIds");
@@ -10279,23 +10396,27 @@ var init_acquire = __esm({
10279
10396
  );
10280
10397
  }
10281
10398
  const revision = revisionFromAcquire2(opts);
10399
+ const repository = target.repository;
10282
10400
  await withStoreQueue(acquisitionStoreKey(kind, scope, sourceProject), async () => {
10283
- const state2 = await target.runtime.select([]);
10284
- await target.runtime.update({
10285
- basedOnVersion: state2.context.knowledgeVersion,
10286
- claims: [buildClaimRecord({
10287
- id: opts.id,
10288
- knowledge: boundary.knowledge,
10289
- applicability: opts.applicability,
10290
- evidence: opts.evidence,
10291
- epistemicType: opts.epistemicType,
10292
- provenance: opts.provenance,
10293
- acquiredAt: opts.acquiredAt,
10294
- revision,
10295
- scope,
10296
- sourceProject,
10297
- kind
10298
- })]
10401
+ await withStorageLock(repository.directoryPath, async () => {
10402
+ const state2 = await repository.load();
10403
+ const next = updateKnowledge(state2, {
10404
+ basedOnVersion: state2.version,
10405
+ claims: [buildClaimRecord({
10406
+ id: opts.id,
10407
+ knowledge: boundary.knowledge,
10408
+ applicability: opts.applicability,
10409
+ evidence: opts.evidence,
10410
+ epistemicType: opts.epistemicType,
10411
+ provenance: opts.provenance,
10412
+ acquiredAt: opts.acquiredAt,
10413
+ revision,
10414
+ scope,
10415
+ sourceProject,
10416
+ kind
10417
+ })]
10418
+ });
10419
+ await repository.save(next, state2.version, true);
10299
10420
  });
10300
10421
  });
10301
10422
  const verification = await target.repository.load();
@@ -12965,6 +13086,10 @@ var saveContinuityDocument = /* @__PURE__ */ __name(async (path3, document) => {
12965
13086
  }
12966
13087
  }, "saveContinuityDocument");
12967
13088
 
13089
+ // core/continuity-ledger/index.ts
13090
+ init_memory();
13091
+ init_memory();
13092
+
12968
13093
  // core/continuity/index.ts
12969
13094
  var continuity = {
12970
13095
  async context(options) {