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/index.cjs CHANGED
@@ -674,7 +674,7 @@ var init_detection_events = __esm({
674
674
  });
675
675
 
676
676
  // core/knowledge/policy.ts
677
- var ENFORCEMENT_TIERS, POLICY_DIR_NAME, PolicyAuthorityError, validatePolicyAuthority, TIER_ENFORCEMENT, TIER_ACTION, CONVERSATIONAL_OVERRIDE_KEYS, ConversationalOverrideRejectedError, PolicyPreconditionViolationError, assertNoConversationalOverride, findPolicyBypassKey, canonicalEvent, findFieldValue, evaluateCondition, validatePolicyAuditAuthority, authorityRelaxationPermitted, authorityRevocationPermitted, canonicalPolicyFingerprint, resolveOverrideState, resolveEffectivePolicies, evaluatePolicies, applyOverrideOverlay, stripNonAuthoritativeTierFields;
677
+ var ENFORCEMENT_TIERS, POLICY_DIR_NAME, PolicyAuthorityError; exports.validateGovernanceAuthority = void 0; var TIER_ENFORCEMENT, TIER_ACTION, CONVERSATIONAL_OVERRIDE_KEYS, ConversationalOverrideRejectedError, PolicyPreconditionViolationError, assertNoConversationalOverride, findPolicyBypassKey, canonicalEvent, findFieldValue, evaluateCondition, validatePolicyAuditAuthority, authorityRelaxationPermitted, authorityRevocationPermitted, canonicalPolicyFingerprint, resolveOverrideState, resolveEffectivePolicies, evaluatePolicies, applyOverrideOverlay, stripNonAuthoritativeTierFields;
678
678
  var init_policy = __esm({
679
679
  "core/knowledge/policy.ts"() {
680
680
  init_canonical_hash();
@@ -690,7 +690,7 @@ var init_policy = __esm({
690
690
  this.name = "PolicyAuthorityError";
691
691
  }
692
692
  };
693
- validatePolicyAuthority = /* @__PURE__ */ __name((value) => {
693
+ exports.validateGovernanceAuthority = /* @__PURE__ */ __name((value) => {
694
694
  if (!value || typeof value !== "object" || Array.isArray(value)) {
695
695
  throw new PolicyAuthorityError("Policy authority requires an authority object");
696
696
  }
@@ -825,17 +825,17 @@ var init_policy = __esm({
825
825
  if (candidate.kind === "system") {
826
826
  return { kind: "system", source: candidate.source.trim() };
827
827
  }
828
- return validatePolicyAuthority(candidate);
828
+ return exports.validateGovernanceAuthority(candidate);
829
829
  }, "validatePolicyAuditAuthority");
830
830
  authorityRelaxationPermitted = /* @__PURE__ */ __name((targetAuthority, actorAuthority) => {
831
- validatePolicyAuthority(targetAuthority);
832
- validatePolicyAuthority(actorAuthority);
831
+ exports.validateGovernanceAuthority(targetAuthority);
832
+ exports.validateGovernanceAuthority(actorAuthority);
833
833
  if (targetAuthority.kind === "HI") return actorAuthority.kind === "HI";
834
834
  return actorAuthority.kind === "HI" || actorAuthority.kind === "operator";
835
835
  }, "authorityRelaxationPermitted");
836
836
  authorityRevocationPermitted = /* @__PURE__ */ __name((overrideAuthority, actorAuthority) => {
837
- validatePolicyAuthority(overrideAuthority);
838
- validatePolicyAuthority(actorAuthority);
837
+ exports.validateGovernanceAuthority(overrideAuthority);
838
+ exports.validateGovernanceAuthority(actorAuthority);
839
839
  if (actorAuthority.kind === "HI") return true;
840
840
  return overrideAuthority.kind === "operator";
841
841
  }, "authorityRevocationPermitted");
@@ -2238,7 +2238,7 @@ var init_policy_store = __esm({
2238
2238
  resolveUserPolicyFilePath = /* @__PURE__ */ __name((namespace2) => joinPath(resolveUserPolicyDir2(), `${namespace2}.policy.json`), "resolveUserPolicyFilePath");
2239
2239
  validatePolicyAuthority2 = /* @__PURE__ */ __name((value) => {
2240
2240
  try {
2241
- return validatePolicyAuthority(value);
2241
+ return exports.validateGovernanceAuthority(value);
2242
2242
  } catch (error) {
2243
2243
  emitPolicyAuthorityDetection(value);
2244
2244
  throw error instanceof PolicyStoreError ? error : new PolicyStoreError(error.message);
@@ -2414,6 +2414,7 @@ var init_policy_store = __esm({
2414
2414
  if (entry.target !== void 0) built.target = entry.target;
2415
2415
  if (entry.reason !== void 0) built.reason = entry.reason;
2416
2416
  if (entry.detail !== void 0) built.detail = entry.detail;
2417
+ if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
2417
2418
  return built;
2418
2419
  }, "validatePolicyAuditEntry");
2419
2420
  validatePolicyDocument = /* @__PURE__ */ __name((value) => {
@@ -2491,6 +2492,7 @@ var init_policy_store = __esm({
2491
2492
  if (entry.target !== void 0) built.target = entry.target;
2492
2493
  if (entry.reason !== void 0) built.reason = entry.reason;
2493
2494
  if (entry.detail !== void 0) built.detail = entry.detail;
2495
+ if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
2494
2496
  return built;
2495
2497
  }, "buildAuditEntry");
2496
2498
  boundedAudit = /* @__PURE__ */ __name((entries2, overrides) => {
@@ -2503,9 +2505,10 @@ var init_policy_store = __esm({
2503
2505
  for (let index = entries2.length - 1; index >= 0; index--) {
2504
2506
  const entry = entries2[index];
2505
2507
  const activeLinked = entry.target !== void 0 && activeOverrideIds.has(entry.target) && (entry.action === "override" || entry.action === "revoke");
2506
- if (activeLinked || budget > 0) {
2508
+ const mandatory = entry.mandatory === true;
2509
+ if (activeLinked || mandatory || budget > 0) {
2507
2510
  kept.unshift(entry);
2508
- if (!activeLinked) budget--;
2511
+ if (!activeLinked && !mandatory) budget--;
2509
2512
  }
2510
2513
  }
2511
2514
  return kept;
@@ -2546,6 +2549,12 @@ var init_policy_store = __esm({
2546
2549
  try {
2547
2550
  await fs.writeFile(temporary, `${JSON.stringify(document, null, 2)}
2548
2551
  `, "utf8");
2552
+ const handle = await fs.open(temporary, "r+");
2553
+ try {
2554
+ await handle.sync();
2555
+ } finally {
2556
+ await handle.close();
2557
+ }
2549
2558
  await fs.rename(temporary, filePath);
2550
2559
  } catch (error) {
2551
2560
  await fs.rm(temporary, { force: true }).catch(() => void 0);
@@ -5040,6 +5049,109 @@ ${contentId}
5040
5049
  }
5041
5050
  });
5042
5051
 
5052
+ // core/storage-coordinator/index.ts
5053
+ var nodeFs4, withStorageLock, readStorageGeneration, writeStorageGeneration;
5054
+ var init_storage_coordinator = __esm({
5055
+ "core/storage-coordinator/index.ts"() {
5056
+ nodeFs4 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
5057
+ withStorageLock = /* @__PURE__ */ __name(async (directoryPath, operationOrOptions, operation, _options = {}) => {
5058
+ const opts = typeof operationOrOptions === "function" ? operation ?? {} : operationOrOptions ?? {};
5059
+ const op = typeof operationOrOptions === "function" ? operationOrOptions : operation;
5060
+ const timeoutMs = opts.timeoutMs ?? 3e4;
5061
+ const staleMs = opts.staleMs ?? 6e4;
5062
+ const lockName = opts.lockName ?? ".storage.lock";
5063
+ const lockPath = `${directoryPath}/${lockName}`;
5064
+ const fs = await nodeFs4();
5065
+ const started = Date.now();
5066
+ await fs.mkdir(directoryPath, { recursive: true });
5067
+ while (true) {
5068
+ try {
5069
+ await fs.mkdir(lockPath);
5070
+ const token2 = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
5071
+ await fs.writeFile(`${lockPath}/owner.json`, JSON.stringify({ token: token2, pid: process.pid, createdAt: Date.now() }), { flag: "wx" });
5072
+ break;
5073
+ } catch (error) {
5074
+ const code = error.code;
5075
+ if (code !== "EEXIST" && code !== "ENOENT" && code !== "EPERM" && code !== "ENOTEMPTY") throw error;
5076
+ let stale = false;
5077
+ let processDead = false;
5078
+ try {
5079
+ const raw = await fs.readFile(`${lockPath}/owner.json`, "utf8");
5080
+ const parsed = JSON.parse(raw);
5081
+ const ageStale = typeof parsed.createdAt === "number" && Date.now() - parsed.createdAt > staleMs;
5082
+ if (typeof parsed.pid === "number") {
5083
+ try {
5084
+ process.kill(parsed.pid, 0);
5085
+ } catch {
5086
+ processDead = true;
5087
+ }
5088
+ } else {
5089
+ processDead = true;
5090
+ }
5091
+ if (ageStale || processDead) stale = true;
5092
+ } catch {
5093
+ stale = true;
5094
+ }
5095
+ if (stale) {
5096
+ if (processDead) {
5097
+ try {
5098
+ await fs.rm(lockPath, { recursive: true, force: true });
5099
+ continue;
5100
+ } catch {
5101
+ }
5102
+ }
5103
+ try {
5104
+ await fs.rmdir(lockPath);
5105
+ continue;
5106
+ } catch {
5107
+ }
5108
+ }
5109
+ if (Date.now() - started > timeoutMs) {
5110
+ throw new Error(`Storage lock timeout for ${directoryPath}`);
5111
+ }
5112
+ await new Promise((resolve) => setTimeout(resolve, 25));
5113
+ }
5114
+ }
5115
+ let token = "";
5116
+ try {
5117
+ const raw = await fs.readFile(`${lockPath}/owner.json`, "utf8");
5118
+ token = JSON.parse(raw).token ?? "";
5119
+ } catch {
5120
+ token = "";
5121
+ }
5122
+ try {
5123
+ return await op();
5124
+ } finally {
5125
+ try {
5126
+ const raw = await fs.readFile(`${lockPath}/owner.json`, "utf8");
5127
+ const parsed = JSON.parse(raw);
5128
+ if (parsed.token === token && token) {
5129
+ await fs.rm(lockPath, { recursive: true, force: true });
5130
+ }
5131
+ } catch {
5132
+ }
5133
+ }
5134
+ }, "withStorageLock");
5135
+ readStorageGeneration = /* @__PURE__ */ __name(async (directoryPath) => {
5136
+ const fs = await nodeFs4();
5137
+ try {
5138
+ const raw = await fs.readFile(`${directoryPath}/.storage.generation`, "utf8");
5139
+ const value = Number(raw.trim());
5140
+ return Number.isFinite(value) && value >= 0 ? value : 0;
5141
+ } catch {
5142
+ return 0;
5143
+ }
5144
+ }, "readStorageGeneration");
5145
+ writeStorageGeneration = /* @__PURE__ */ __name(async (directoryPath, value) => {
5146
+ const fs = await nodeFs4();
5147
+ await fs.mkdir(directoryPath, { recursive: true });
5148
+ const temp = `${directoryPath}/.storage.generation.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
5149
+ await fs.writeFile(temp, String(value));
5150
+ await fs.rename(temp, `${directoryPath}/.storage.generation`);
5151
+ }, "writeStorageGeneration");
5152
+ }
5153
+ });
5154
+
5043
5155
  // core/knowledge/mem-repository.ts
5044
5156
  function resolveDuplicate(existing, existingSource, current, currentSource) {
5045
5157
  const existingRetracts = existing.revision?.kind === "retracts";
@@ -5080,19 +5192,20 @@ async function parseMemFile(value, sourceId) {
5080
5192
  throw new Error(`${message} in ${sourceId}`);
5081
5193
  }
5082
5194
  }
5083
- var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE, directoryLocks, nodeFs4, separator2, normalizePath, isAbsolutePath, resolvePath, joinPath2, parentPath, uniqueSuffix, withDirectoryLock, isMissingDirectory, sourcePriority, memFilePath, MemDirectoryKnowledgeRepository;
5195
+ var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE, nodeFs5, repositoryLoadCaches, separator2, normalizePath, isAbsolutePath, resolvePath, joinPath2, parentPath, uniqueSuffix, isMissingDirectory, sourcePriority, memFilePath, MemDirectoryKnowledgeRepository;
5084
5196
  var init_mem_repository = __esm({
5085
5197
  "core/knowledge/mem-repository.ts"() {
5086
5198
  init_mem_codec();
5087
5199
  init_mem_package();
5088
5200
  init_state();
5089
5201
  init_acquisition_limits();
5202
+ init_storage_coordinator();
5090
5203
  DOTMEM_DIRECTORY = ".dotmem";
5091
5204
  DEFAULT_MEM_FILE = "project.mem";
5092
- directoryLocks = /* @__PURE__ */ new Map();
5093
- nodeFs4 = /* @__PURE__ */ __name(async () => {
5205
+ nodeFs5 = /* @__PURE__ */ __name(async () => {
5094
5206
  return import('fs/promises');
5095
5207
  }, "nodeFs");
5208
+ repositoryLoadCaches = /* @__PURE__ */ new Map();
5096
5209
  separator2 = /* @__PURE__ */ __name(() => process.platform === "win32" ? "\\" : "/", "separator");
5097
5210
  normalizePath = /* @__PURE__ */ __name((value) => value.replace(/[\\/]+/g, separator2()), "normalizePath");
5098
5211
  isAbsolutePath = /* @__PURE__ */ __name((value) => /^(?:[A-Za-z]:[\\/]|[\\/])/.test(value), "isAbsolutePath");
@@ -5104,25 +5217,8 @@ var init_mem_repository = __esm({
5104
5217
  return index <= 0 ? "." : normalized.slice(0, index);
5105
5218
  }, "parentPath");
5106
5219
  uniqueSuffix = /* @__PURE__ */ __name(() => globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, "uniqueSuffix");
5107
- withDirectoryLock = /* @__PURE__ */ __name(async (directoryPath, operation) => {
5108
- const previous = directoryLocks.get(directoryPath) ?? Promise.resolve();
5109
- let release = /* @__PURE__ */ __name(() => void 0, "release");
5110
- const gate = new Promise((resolveGate) => {
5111
- release = resolveGate;
5112
- });
5113
- const queued = previous.then(() => gate);
5114
- directoryLocks.set(directoryPath, queued);
5115
- await previous;
5116
- try {
5117
- return await operation();
5118
- } finally {
5119
- release();
5120
- if (directoryLocks.get(directoryPath) === queued) directoryLocks.delete(directoryPath);
5121
- }
5122
- }, "withDirectoryLock");
5123
5220
  isMissingDirectory = /* @__PURE__ */ __name((error) => error.code === "ENOENT", "isMissingDirectory");
5124
5221
  sourcePriority = /* @__PURE__ */ __name((sourceId, scope) => {
5125
- if (sourceId.endsWith(".mem") && sourceId !== "project.mem" && sourceId !== "user.mem" && sourceId !== "agent.mem") return 0;
5126
5222
  if (scope === "project" || sourceId === "project.mem") return 4;
5127
5223
  if (scope === "user" || sourceId === "user.mem") return 3;
5128
5224
  if (sourceId === "agent.mem" || sourceId === "agent") return 2;
@@ -5150,7 +5246,7 @@ var init_mem_repository = __esm({
5150
5246
  this.loadWritableOnly = loadWritableOnly;
5151
5247
  }
5152
5248
  async discover() {
5153
- const { readdir } = await nodeFs4();
5249
+ const { readdir } = await nodeFs5();
5154
5250
  try {
5155
5251
  const entries2 = await readdir(this.directoryPath, { withFileTypes: true });
5156
5252
  return entries2.filter((entry) => entry.isFile() && entry.name.endsWith(".mem") && entry.name !== "@brain.mem").map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
@@ -5165,7 +5261,7 @@ var init_mem_repository = __esm({
5165
5261
  * receive a clear failure instead of discovering corruption on first use.
5166
5262
  */
5167
5263
  async initialize() {
5168
- const { mkdir } = await nodeFs4();
5264
+ const { mkdir } = await nodeFs5();
5169
5265
  await mkdir(this.directoryPath, { recursive: true });
5170
5266
  await this.load();
5171
5267
  }
@@ -5180,7 +5276,7 @@ var init_mem_repository = __esm({
5180
5276
  * that does not delete is not a delete.
5181
5277
  */
5182
5278
  async clearWritable() {
5183
- const { rm, readdir } = await nodeFs4();
5279
+ const { rm, readdir } = await nodeFs5();
5184
5280
  const filePath = memFilePath(this.directoryPath, this.writableSourceId);
5185
5281
  await rm(filePath, { force: true });
5186
5282
  await rm(`${filePath}.bak`, { force: true });
@@ -5197,15 +5293,28 @@ var init_mem_repository = __esm({
5197
5293
  }
5198
5294
  }
5199
5295
  async load() {
5200
- const loaded = this.loadWritableOnly ? await this.loadSources([this.writableSourceId]) : await this.loadSources();
5201
- return this.combine(loaded);
5296
+ const cacheKey = `${this.directoryPath}:${this.writableSourceId}:${this.loadWritableOnly}`;
5297
+ for (let attempt = 0; attempt < 5; attempt += 1) {
5298
+ const generationBefore = await readStorageGeneration(this.directoryPath);
5299
+ const cached = repositoryLoadCaches.get(cacheKey);
5300
+ if (cached && cached.signature === String(generationBefore)) {
5301
+ return structuredClone(cached.state);
5302
+ }
5303
+ const loaded = this.loadWritableOnly ? await this.loadSources([this.writableSourceId]) : await this.loadSources();
5304
+ const state3 = this.combine(loaded);
5305
+ const generationAfter = await readStorageGeneration(this.directoryPath);
5306
+ if (generationBefore !== generationAfter && attempt < 4) continue;
5307
+ repositoryLoadCaches.set(cacheKey, { signature: String(generationAfter), state: structuredClone(state3) });
5308
+ return state3;
5309
+ }
5310
+ throw new Error("Unable to load stable repository state");
5202
5311
  }
5203
- async save(state3, expectedVersion) {
5312
+ async save(state3, expectedVersion, locked = false) {
5204
5313
  const validated = validateKnowledgeState(state3);
5205
5314
  if (!Number.isInteger(expectedVersion) || expectedVersion < 0 || validated.version !== expectedVersion + 1) {
5206
5315
  throw new Error("Invalid expected persisted version");
5207
5316
  }
5208
- await withDirectoryLock(this.directoryPath, async () => {
5317
+ const doWrite = /* @__PURE__ */ __name(async () => {
5209
5318
  const loaded = await this.loadSources();
5210
5319
  const writable = loaded.find((source) => source.id === this.writableSourceId) ?? {
5211
5320
  id: this.writableSourceId,
@@ -5227,10 +5336,16 @@ var init_mem_repository = __esm({
5227
5336
  claims: [...writable.state.claims, ...newClaims]
5228
5337
  };
5229
5338
  await this.writeSource(writable.filePath, nextWritable, writable.preserved, writable.package);
5230
- });
5339
+ await writeStorageGeneration(this.directoryPath, await readStorageGeneration(this.directoryPath) + 1);
5340
+ }, "doWrite");
5341
+ if (locked) {
5342
+ await doWrite();
5343
+ } else {
5344
+ await withStorageLock(this.directoryPath, doWrite);
5345
+ }
5231
5346
  }
5232
5347
  async loadSources(sourceIds) {
5233
- const { readFile: readFile5 } = await nodeFs4();
5348
+ const { readFile: readFile5 } = await nodeFs5();
5234
5349
  const ids = sourceIds ?? await this.discover();
5235
5350
  const sources = [];
5236
5351
  for (const sourceId of ids) {
@@ -5320,7 +5435,7 @@ var init_mem_repository = __esm({
5320
5435
  * valid package, so the backup is always a version this library can read.
5321
5436
  */
5322
5437
  async keepLastValidVersion(filePath) {
5323
- const { readFile: readFile5, copyFile, rename, rm } = await nodeFs4();
5438
+ const { readFile: readFile5, copyFile, rename, rm } = await nodeFs5();
5324
5439
  let current;
5325
5440
  try {
5326
5441
  current = await readFile5(filePath);
@@ -5344,7 +5459,7 @@ var init_mem_repository = __esm({
5344
5459
  }
5345
5460
  /** Best-effort directory sync: the rename is durable once the directory is. */
5346
5461
  async syncDirectory(directoryPath) {
5347
- const { open } = await nodeFs4();
5462
+ const { open } = await nodeFs5();
5348
5463
  try {
5349
5464
  const handle = await open(directoryPath, "r");
5350
5465
  try {
@@ -5356,7 +5471,7 @@ var init_mem_repository = __esm({
5356
5471
  }
5357
5472
  }
5358
5473
  async writeSource(filePath, state3, preserved, preservedPackage) {
5359
- const { mkdir, rename, rm, open, readFile: readFile5 } = await nodeFs4();
5474
+ const { mkdir, rename, rm, open, readFile: readFile5 } = await nodeFs5();
5360
5475
  if (!Number.isInteger(state3.version) || state3.version < 0) throw new Error("Invalid writable .mem knowledge version");
5361
5476
  for (const claim of state3.claims) validateClaim(claim);
5362
5477
  await mkdir(parentPath(filePath), { recursive: true });
@@ -5884,7 +5999,7 @@ var init_project_identity = __esm({
5884
5999
  });
5885
6000
 
5886
6001
  // core/knowledge/staging.ts
5887
- var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor, nodeFs5, normalizeClaimIds, revisionFromOptions, writeStageFile, readStageFile, validateStageFile, commitStageFile, cleanupStageFile;
6002
+ var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor, nodeFs6, normalizeClaimIds, revisionFromOptions, writeStageFile, readStageFile, validateStageFile, commitStageFile, cleanupStageFile;
5888
6003
  var init_staging = __esm({
5889
6004
  "core/knowledge/staging.ts"() {
5890
6005
  init_claim_record();
@@ -5919,7 +6034,7 @@ var init_staging = __esm({
5919
6034
  return `${base}/dotmem`;
5920
6035
  }, "resolveStagingDirectory");
5921
6036
  stagePathFor = /* @__PURE__ */ __name((requestId) => `${resolveStagingDirectory()}/${requestId}.stage`, "stagePathFor");
5922
- nodeFs5 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
6037
+ nodeFs6 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
5923
6038
  normalizeClaimIds = /* @__PURE__ */ __name((value) => {
5924
6039
  if (!value) return [];
5925
6040
  return Array.isArray(value) ? value : [value];
@@ -5944,7 +6059,7 @@ var init_staging = __esm({
5944
6059
  };
5945
6060
  const requestId = generateRequestId();
5946
6061
  const stagePath = stagePathFor(requestId);
5947
- const fs = await nodeFs5();
6062
+ const fs = await nodeFs6();
5948
6063
  await fs.mkdir(resolveStagingDirectory(), { recursive: true });
5949
6064
  const stageFile = {
5950
6065
  format: DOTMEM_STAGE_FORMAT,
@@ -5964,7 +6079,7 @@ var init_staging = __esm({
5964
6079
  }, "writeStageFile");
5965
6080
  readStageFile = /* @__PURE__ */ __name(async (requestId) => {
5966
6081
  const stagePath = stagePathFor(requestId);
5967
- const fs = await nodeFs5();
6082
+ const fs = await nodeFs6();
5968
6083
  try {
5969
6084
  const content = await fs.readFile(stagePath, "utf8");
5970
6085
  return JSON.parse(content);
@@ -6075,7 +6190,7 @@ var init_staging = __esm({
6075
6190
  }, "commitStageFile");
6076
6191
  cleanupStageFile = /* @__PURE__ */ __name(async (requestId) => {
6077
6192
  const stagePath = stagePathFor(requestId);
6078
- const fs = await nodeFs5();
6193
+ const fs = await nodeFs6();
6079
6194
  try {
6080
6195
  await fs.unlink(stagePath);
6081
6196
  } catch (error) {
@@ -8556,10 +8671,12 @@ init_project_identity();
8556
8671
  init_project_anchor();
8557
8672
  init_acquisition_boundary();
8558
8673
  init_claim_record();
8674
+ init_state();
8559
8675
  init_router();
8560
8676
  init_store_queue();
8561
8677
  init_staging();
8562
8678
  init_user_paths();
8679
+ init_storage_coordinator();
8563
8680
  init_mem_repository();
8564
8681
  function normalizeClaimIds3(value) {
8565
8682
  if (!value) return [];
@@ -8637,23 +8754,27 @@ var acquire = {
8637
8754
  );
8638
8755
  }
8639
8756
  const revision = revisionFromAcquire2(opts);
8757
+ const repository = target.repository;
8640
8758
  await withStoreQueue(acquisitionStoreKey(kind, scope, sourceProject), async () => {
8641
- const state3 = await target.runtime.select([]);
8642
- await target.runtime.update({
8643
- basedOnVersion: state3.context.knowledgeVersion,
8644
- claims: [buildClaimRecord({
8645
- id: opts.id,
8646
- knowledge: boundary.knowledge,
8647
- applicability: opts.applicability,
8648
- evidence: opts.evidence,
8649
- epistemicType: opts.epistemicType,
8650
- provenance: opts.provenance,
8651
- acquiredAt: opts.acquiredAt,
8652
- revision,
8653
- scope,
8654
- sourceProject,
8655
- kind
8656
- })]
8759
+ await withStorageLock(repository.directoryPath, async () => {
8760
+ const state3 = await repository.load();
8761
+ const next = updateKnowledge(state3, {
8762
+ basedOnVersion: state3.version,
8763
+ claims: [buildClaimRecord({
8764
+ id: opts.id,
8765
+ knowledge: boundary.knowledge,
8766
+ applicability: opts.applicability,
8767
+ evidence: opts.evidence,
8768
+ epistemicType: opts.epistemicType,
8769
+ provenance: opts.provenance,
8770
+ acquiredAt: opts.acquiredAt,
8771
+ revision,
8772
+ scope,
8773
+ sourceProject,
8774
+ kind
8775
+ })]
8776
+ });
8777
+ await repository.save(next, state3.version, true);
8657
8778
  });
8658
8779
  });
8659
8780
  const verification = await target.repository.load();
@@ -10194,6 +10315,442 @@ var saveContinuityDocument = /* @__PURE__ */ __name(async (path, document) => {
10194
10315
  }
10195
10316
  }, "saveContinuityDocument");
10196
10317
 
10318
+ // core/continuity-ledger/index.ts
10319
+ var EVENT_ID_PREFIX = "evt.";
10320
+ var LEDGER_APPLICABILITY = { area: "continuity", baseline: false };
10321
+ var EVENT_TYPES = /* @__PURE__ */ new Set([
10322
+ "decision",
10323
+ "discovery",
10324
+ "correction",
10325
+ "conversation",
10326
+ "commitment",
10327
+ "open-loop",
10328
+ "project-change",
10329
+ "activity"
10330
+ ]);
10331
+ var MEMORY_DOMAINS = /* @__PURE__ */ new Set(["environmental", "professional", "conversational", "activity"]);
10332
+ var EPITEMIC_STATUSES = /* @__PURE__ */ new Set([
10333
+ "verified-fact",
10334
+ "user-statement",
10335
+ "ai-inference",
10336
+ "hypothesis",
10337
+ "proposal",
10338
+ "decision",
10339
+ "verified-result"
10340
+ ]);
10341
+ var CONFIDENCE_VALUES = /* @__PURE__ */ new Set(["high", "medium", "low", "unknown"]);
10342
+ var AUTHORS = /* @__PURE__ */ new Set(["human", "ai", "system"]);
10343
+ var CHANNELS = /* @__PURE__ */ new Set(["conversation", "acquisition", "tool", "migration"]);
10344
+ var SOURCE_KINDS = /* @__PURE__ */ new Set(["claim", "file", "commit", "tool-output", "conversation"]);
10345
+ var RELATIONSHIP_KEYS = [
10346
+ "refines",
10347
+ "supersedes",
10348
+ "retracts",
10349
+ "dependsOn",
10350
+ "resolves",
10351
+ "relatedTo"
10352
+ ];
10353
+ var isRecord = /* @__PURE__ */ __name((value) => !!value && typeof value === "object" && !Array.isArray(value), "isRecord");
10354
+ var normalizeRelationships = /* @__PURE__ */ __name((value) => {
10355
+ const result = {
10356
+ refines: [],
10357
+ supersedes: [],
10358
+ retracts: [],
10359
+ dependsOn: [],
10360
+ resolves: [],
10361
+ relatedTo: []
10362
+ };
10363
+ if (!isRecord(value)) return result;
10364
+ for (const key of RELATIONSHIP_KEYS) {
10365
+ const raw = value[key];
10366
+ if (Array.isArray(raw)) {
10367
+ result[key] = raw.filter((item) => typeof item === "string");
10368
+ }
10369
+ }
10370
+ return result;
10371
+ }, "normalizeRelationships");
10372
+ var validateEvent = /* @__PURE__ */ __name((event) => {
10373
+ if (!isRecord(event)) throw new Error("Continuity event must be an object");
10374
+ if (event.schemaVersion !== 1) throw new Error("Unsupported continuity event schemaVersion");
10375
+ if (typeof event.eventId !== "string" || !event.eventId.startsWith(EVENT_ID_PREFIX)) {
10376
+ throw new Error("Continuity event requires a valid eventId");
10377
+ }
10378
+ if (typeof event.eventType !== "string" || !EVENT_TYPES.has(event.eventType)) {
10379
+ throw new Error("Continuity event requires a valid eventType");
10380
+ }
10381
+ if (typeof event.memoryDomain !== "string" || !MEMORY_DOMAINS.has(event.memoryDomain)) {
10382
+ throw new Error("Continuity event requires a valid memoryDomain");
10383
+ }
10384
+ if (typeof event.epistemicStatus !== "string" || !EPITEMIC_STATUSES.has(event.epistemicStatus)) {
10385
+ throw new Error("Continuity event requires a valid epistemicStatus");
10386
+ }
10387
+ if (typeof event.confidence !== "string" || !CONFIDENCE_VALUES.has(event.confidence)) {
10388
+ throw new Error("Continuity event requires a valid confidence");
10389
+ }
10390
+ if (typeof event.createdAt !== "string") throw new Error("Continuity event requires createdAt");
10391
+ if (typeof event.sessionId !== "string") throw new Error("Continuity event requires sessionId");
10392
+ if (event.projectId !== null && typeof event.projectId !== "string") {
10393
+ throw new Error("Continuity event requires projectId to be string or null");
10394
+ }
10395
+ if (typeof event.subject !== "string") throw new Error("Continuity event requires subject");
10396
+ if (!isRecord(event.content)) throw new Error("Continuity event requires content object");
10397
+ if (!Array.isArray(event.sourceRefs)) throw new Error("Continuity event requires sourceRefs array");
10398
+ for (const ref of event.sourceRefs) {
10399
+ if (!isRecord(ref) || typeof ref.kind !== "string" || !SOURCE_KINDS.has(ref.kind) || typeof ref.id !== "string") {
10400
+ throw new Error("Continuity event contains an invalid sourceRef");
10401
+ }
10402
+ }
10403
+ if (!isRecord(event.relationships)) throw new Error("Continuity event requires relationships object");
10404
+ if (!isRecord(event.provenance)) throw new Error("Continuity event requires provenance object");
10405
+ const provenance = event.provenance;
10406
+ if (typeof provenance.author !== "string" || !AUTHORS.has(provenance.author)) {
10407
+ throw new Error("Continuity event requires a valid provenance.author");
10408
+ }
10409
+ if (typeof provenance.origin !== "string") throw new Error("Continuity event requires provenance.origin");
10410
+ if (typeof provenance.channel !== "string" || !CHANNELS.has(provenance.channel)) {
10411
+ throw new Error("Continuity event requires a valid provenance.channel");
10412
+ }
10413
+ if (typeof provenance.recordedAt !== "string") throw new Error("Continuity event requires provenance.recordedAt");
10414
+ return {
10415
+ schemaVersion: 1,
10416
+ eventId: event.eventId,
10417
+ eventType: event.eventType,
10418
+ memoryDomain: event.memoryDomain,
10419
+ epistemicStatus: event.epistemicStatus,
10420
+ confidence: event.confidence,
10421
+ createdAt: event.createdAt,
10422
+ sessionId: event.sessionId,
10423
+ projectId: event.projectId,
10424
+ subject: event.subject,
10425
+ content: event.content,
10426
+ sourceRefs: event.sourceRefs,
10427
+ relationships: normalizeRelationships(event.relationships),
10428
+ provenance
10429
+ };
10430
+ }, "validateEvent");
10431
+ var isLedgerEvent = /* @__PURE__ */ __name((claim) => {
10432
+ return isRecord(claim.knowledge) && claim.knowledge.schemaVersion === 1 && typeof claim.knowledge.eventId === "string";
10433
+ }, "isLedgerEvent");
10434
+ var loadEvents = /* @__PURE__ */ __name(async () => {
10435
+ const state3 = await loadCombinedKnowledgeState();
10436
+ const events = [];
10437
+ for (const claim of state3.claims) {
10438
+ if (isLedgerEvent(claim)) {
10439
+ try {
10440
+ events.push(validateEvent(claim.knowledge));
10441
+ } catch {
10442
+ }
10443
+ }
10444
+ }
10445
+ return events.sort((left, right) => left.eventId.localeCompare(right.eventId));
10446
+ }, "loadEvents");
10447
+ var findEvent = /* @__PURE__ */ __name((events, eventId) => events.find((event) => event.eventId === eventId), "findEvent");
10448
+ var epistemicTypeFor = /* @__PURE__ */ __name((status) => {
10449
+ if (status === "verified-fact" || status === "verified-result" || status === "decision") return "verified";
10450
+ if (status === "user-statement") return "human-stated";
10451
+ return "inferred";
10452
+ }, "epistemicTypeFor");
10453
+ var EVENT_ID_PATTERN = /^evt\.[A-Za-z0-9][A-Za-z0-9._-]*$/;
10454
+ var validateEventId = /* @__PURE__ */ __name((eventId) => {
10455
+ if (!EVENT_ID_PATTERN.test(eventId)) throw new Error(`Invalid continuity event ID: ${eventId}`);
10456
+ }, "validateEventId");
10457
+ var hasRelationshipPath = /* @__PURE__ */ __name((events, startId, targetId, relationKey) => {
10458
+ const visited = /* @__PURE__ */ new Set();
10459
+ const stack = [startId];
10460
+ while (stack.length > 0) {
10461
+ const current = stack.pop();
10462
+ if (current === targetId) return true;
10463
+ if (visited.has(current)) continue;
10464
+ visited.add(current);
10465
+ const event = findEvent(events, current);
10466
+ if (!event) continue;
10467
+ for (const next of event.relationships[relationKey]) {
10468
+ if (!visited.has(next)) stack.push(next);
10469
+ }
10470
+ }
10471
+ return false;
10472
+ }, "hasRelationshipPath");
10473
+ var CYCLIC_RELATIONSHIP_KEYS = /* @__PURE__ */ new Set([
10474
+ "refines",
10475
+ "supersedes",
10476
+ "retracts",
10477
+ "dependsOn",
10478
+ "resolves",
10479
+ "relatedTo"
10480
+ ]);
10481
+ var validateRelationshipTargets = /* @__PURE__ */ __name(async (event, existing) => {
10482
+ validateEventId(event.eventId);
10483
+ for (const key of RELATIONSHIP_KEYS) {
10484
+ for (const targetId of event.relationships[key]) {
10485
+ validateEventId(targetId);
10486
+ if (targetId === event.eventId) throw new Error(`Continuity event cannot reference itself: ${targetId}`);
10487
+ const target = findEvent(existing, targetId);
10488
+ if (!target) throw new Error(`Continuity relationship references a nonexistent event: ${targetId}`);
10489
+ if (target.projectId !== event.projectId) {
10490
+ throw new Error(`Cross-scope continuity relationship is not allowed: ${event.eventId} -> ${targetId}`);
10491
+ }
10492
+ if (CYCLIC_RELATIONSHIP_KEYS.has(key) && hasRelationshipPath(existing, targetId, event.eventId, key)) {
10493
+ throw new Error(`Continuity relationship would create a cycle: ${event.eventId} -> ${targetId}`);
10494
+ }
10495
+ }
10496
+ }
10497
+ }, "validateRelationshipTargets");
10498
+ var validateResolutionRequest = /* @__PURE__ */ __name(async (resolution, existing) => {
10499
+ validateEventId(resolution.resolutionEventId);
10500
+ if (!Array.isArray(resolution.conflictingEventIds) || resolution.conflictingEventIds.length === 0) {
10501
+ throw new Error("Conflict resolution requires at least one conflicting event");
10502
+ }
10503
+ if (typeof resolution.selectedEventId !== "string" || !resolution.selectedEventId) {
10504
+ throw new Error("Conflict resolution requires a selected event");
10505
+ }
10506
+ if (typeof resolution.reason !== "string" || !resolution.reason.trim()) {
10507
+ throw new Error("Conflict resolution requires a reason");
10508
+ }
10509
+ const conflicting = resolution.conflictingEventIds.map((id) => {
10510
+ validateEventId(id);
10511
+ const event = findEvent(existing, id);
10512
+ if (!event) throw new Error(`Conflict resolution references a nonexistent event: ${id}`);
10513
+ if (event.relationships.supersedes.length !== 1) {
10514
+ throw new Error(`Conflict resolution references a non-supersession event: ${id}`);
10515
+ }
10516
+ return event;
10517
+ });
10518
+ const targets = new Set(conflicting.map((event) => event.relationships.supersedes[0]));
10519
+ if (targets.size !== 1) {
10520
+ throw new Error("Conflict resolution references supersession events targeting different decisions");
10521
+ }
10522
+ const targetId = [...targets][0];
10523
+ const knownCompeting = supersedingEvents(existing, targetId).map((event) => event.eventId).sort();
10524
+ const requested = [...resolution.conflictingEventIds].sort();
10525
+ if (requested.length !== knownCompeting.length || requested.some((id, index) => id !== knownCompeting[index])) {
10526
+ throw new Error("Conflict resolution must include every competing supersession event");
10527
+ }
10528
+ if (!resolution.conflictingEventIds.includes(resolution.selectedEventId)) {
10529
+ throw new Error("Conflict resolution selectedEventId must be one of the conflicting events");
10530
+ }
10531
+ const existingResolutions = existing.filter(
10532
+ (event) => event.relationships.resolves.includes(targetId) || resolution.conflictingEventIds.some((id) => event.relationships.resolves.includes(id))
10533
+ );
10534
+ if (existingResolutions.length > 0) {
10535
+ const selectedOutcomes = new Set(existingResolutions.map((event) => {
10536
+ const content = isRecord(event.content) ? event.content : {};
10537
+ return typeof content.selectedEventId === "string" ? content.selectedEventId : null;
10538
+ }).filter((value) => value !== null));
10539
+ if (existingResolutions.some((event) => event.eventId === resolution.resolutionEventId)) {
10540
+ throw new Error(`Duplicate continuity resolution event: ${resolution.resolutionEventId}`);
10541
+ }
10542
+ if (selectedOutcomes.size > 0 && !selectedOutcomes.has(resolution.selectedEventId)) {
10543
+ throw new Error("Incompatible continuity conflict resolution");
10544
+ }
10545
+ throw new Error("Continuity conflict is already resolved");
10546
+ }
10547
+ const resolutionEventId = resolution.resolutionEventId;
10548
+ for (const conflictingId of resolution.conflictingEventIds) {
10549
+ const event = findEvent(existing, conflictingId);
10550
+ if (event && hasRelationshipPath(existing, conflictingId, resolutionEventId, "resolves")) {
10551
+ throw new Error(`Continuity resolution would create a cycle: ${resolutionEventId}`);
10552
+ }
10553
+ }
10554
+ }, "validateResolutionRequest");
10555
+ var recordEventOnce = /* @__PURE__ */ __name(async (event) => {
10556
+ const validated = validateEvent(event);
10557
+ const existing = await loadEvents();
10558
+ await validateRelationshipTargets(validated, existing);
10559
+ if (existing.some((item) => item.eventId === validated.eventId)) {
10560
+ throw new Error(`Continuity event already exists: ${validated.eventId}`);
10561
+ }
10562
+ const scope = validated.projectId ? "project" : "user";
10563
+ const evidence = validated.sourceRefs.length > 0 ? validated.sourceRefs.map((ref) => ({
10564
+ id: ref.id,
10565
+ source: ref.kind,
10566
+ observedAt: validated.createdAt,
10567
+ supports: validated.eventId
10568
+ })) : [{ id: validated.eventId, source: "continuity-ledger", observedAt: validated.createdAt, supports: validated.eventId }];
10569
+ await memory.acquire({
10570
+ id: validated.eventId,
10571
+ knowledge: validated,
10572
+ applicability: { ...LEDGER_APPLICABILITY, topic: validated.eventType },
10573
+ evidence,
10574
+ epistemicType: epistemicTypeFor(validated.epistemicStatus),
10575
+ provenance: {
10576
+ source: validated.provenance.origin,
10577
+ detail: validated.provenance.channel
10578
+ },
10579
+ scope,
10580
+ sourceProject: validated.projectId ?? void 0,
10581
+ kind: "experience"
10582
+ });
10583
+ }, "recordEventOnce");
10584
+ var recordEvent = /* @__PURE__ */ __name(async (event) => {
10585
+ let lastError;
10586
+ for (let attempt = 0; attempt < 10; attempt += 1) {
10587
+ try {
10588
+ await recordEventOnce(event);
10589
+ return;
10590
+ } catch (error) {
10591
+ lastError = error;
10592
+ if (error instanceof Error && /Stale update/i.test(error.message) && attempt < 9) {
10593
+ await new Promise((resolve) => setTimeout(resolve, 20));
10594
+ continue;
10595
+ }
10596
+ throw error;
10597
+ }
10598
+ }
10599
+ throw lastError instanceof Error ? lastError : new Error("Continuity event recording failed");
10600
+ }, "recordEvent");
10601
+ var getEvent = /* @__PURE__ */ __name(async (eventId) => {
10602
+ const events = await loadEvents();
10603
+ return findEvent(events, eventId) ?? null;
10604
+ }, "getEvent");
10605
+ var listEvents = /* @__PURE__ */ __name(async (filter = {}) => {
10606
+ const events = await loadEvents();
10607
+ return events.filter((event) => {
10608
+ if (filter.eventId && event.eventId !== filter.eventId) return false;
10609
+ if (filter.eventType && event.eventType !== filter.eventType) return false;
10610
+ if (filter.memoryDomain && event.memoryDomain !== filter.memoryDomain) return false;
10611
+ if (filter.sessionId && event.sessionId !== filter.sessionId) return false;
10612
+ if (filter.projectId !== void 0 && event.projectId !== filter.projectId) return false;
10613
+ if (filter.epistemicStatus && event.epistemicStatus !== filter.epistemicStatus) return false;
10614
+ return true;
10615
+ });
10616
+ }, "listEvents");
10617
+ var supersedingEvents = /* @__PURE__ */ __name((events, eventId) => events.filter((event) => event.relationships.supersedes.includes(eventId)), "supersedingEvents");
10618
+ var retractingEvents = /* @__PURE__ */ __name((events, eventId) => events.filter((event) => event.relationships.retracts.includes(eventId)), "retractingEvents");
10619
+ var resolutionEventsFor = /* @__PURE__ */ __name((events, eventId, conflictingIds) => events.filter(
10620
+ (event) => event.relationships.resolves.includes(eventId) || conflictingIds.some((id) => event.relationships.resolves.includes(id))
10621
+ ), "resolutionEventsFor");
10622
+ var deriveValidity = /* @__PURE__ */ __name(async (eventId) => {
10623
+ let events = await loadEvents();
10624
+ for (let stabilityAttempt = 0; stabilityAttempt < 5; stabilityAttempt += 1) {
10625
+ const nextEvents = await loadEvents();
10626
+ if (nextEvents.length === events.length) break;
10627
+ events = nextEvents;
10628
+ await new Promise((resolve) => setTimeout(resolve, 25));
10629
+ }
10630
+ const supersededBy = supersedingEvents(events, eventId).map((event) => event.eventId);
10631
+ const retractedBy = retractingEvents(events, eventId).map((event) => event.eventId);
10632
+ const contestedBy = supersededBy.length > 1 ? supersededBy : [];
10633
+ const resolvedBy = resolutionEventsFor(events, eventId, contestedBy).map((event) => event.eventId);
10634
+ let state3 = "current";
10635
+ if (retractedBy.length > 0) state3 = "retracted";
10636
+ else if (supersededBy.length > 0) state3 = supersededBy.length > 1 && resolvedBy.length === 0 ? "contested" : "superseded";
10637
+ else if (events.some((event) => event.eventId === eventId && event.eventType === "decision") === false) state3 = "current";
10638
+ if (supersededBy.length > 1 && resolvedBy.length === 0) state3 = "contested";
10639
+ return {
10640
+ eventId,
10641
+ state: state3,
10642
+ supersededBy,
10643
+ retractedBy,
10644
+ contestedBy,
10645
+ resolvedBy
10646
+ };
10647
+ }, "deriveValidity");
10648
+ var listContested = /* @__PURE__ */ __name(async () => {
10649
+ const events = await loadEvents();
10650
+ const targets = /* @__PURE__ */ new Set();
10651
+ for (const event of events) {
10652
+ for (const target of event.relationships.supersedes) targets.add(target);
10653
+ }
10654
+ const contested = [];
10655
+ for (const target of targets) {
10656
+ const supersededBy = supersedingEvents(events, target);
10657
+ if (supersededBy.length <= 1) continue;
10658
+ const conflictingIds = supersededBy.map((event) => event.eventId);
10659
+ const resolvedBy = resolutionEventsFor(events, target, conflictingIds);
10660
+ if (resolvedBy.length === 0) contested.push(target);
10661
+ }
10662
+ return contested.sort();
10663
+ }, "listContested");
10664
+ var resolveConflictOnce = /* @__PURE__ */ __name(async (resolution) => {
10665
+ const existing = await loadEvents();
10666
+ await validateResolutionRequest(resolution, existing);
10667
+ const resolutionEvent = {
10668
+ schemaVersion: 1,
10669
+ eventId: resolution.resolutionEventId,
10670
+ eventType: "correction",
10671
+ memoryDomain: "professional",
10672
+ epistemicStatus: "decision",
10673
+ confidence: "high",
10674
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
10675
+ sessionId: "resolution",
10676
+ projectId: null,
10677
+ subject: "Continuity conflict resolution",
10678
+ content: {
10679
+ selectedEventId: resolution.selectedEventId,
10680
+ reason: resolution.reason
10681
+ },
10682
+ sourceRefs: resolution.conflictingEventIds.map((id) => ({ kind: "claim", id })),
10683
+ relationships: {
10684
+ refines: [],
10685
+ supersedes: [],
10686
+ retracts: [],
10687
+ dependsOn: [],
10688
+ resolves: [...resolution.conflictingEventIds],
10689
+ relatedTo: [resolution.selectedEventId]
10690
+ },
10691
+ provenance: {
10692
+ author: "system",
10693
+ origin: "continuity-ledger",
10694
+ channel: "acquisition",
10695
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString()
10696
+ }
10697
+ };
10698
+ await recordEvent(resolutionEvent);
10699
+ }, "resolveConflictOnce");
10700
+ var reconstructValidity = /* @__PURE__ */ __name(async () => {
10701
+ const events = await loadEvents();
10702
+ const result = [];
10703
+ const targets = new Set(events.map((event) => event.eventId));
10704
+ for (const event of events) {
10705
+ for (const target of event.relationships.supersedes) targets.add(target);
10706
+ for (const target of event.relationships.retracts) targets.add(target);
10707
+ }
10708
+ for (const target of targets) {
10709
+ result.push(await deriveValidity(target));
10710
+ }
10711
+ return result.sort((left, right) => left.eventId.localeCompare(right.eventId));
10712
+ }, "reconstructValidity");
10713
+ var resolveConflict = /* @__PURE__ */ __name(async (resolution) => {
10714
+ let lastError;
10715
+ for (let attempt = 0; attempt < 3; attempt += 1) {
10716
+ try {
10717
+ await resolveConflictOnce(resolution);
10718
+ return;
10719
+ } catch (error) {
10720
+ lastError = error;
10721
+ if (error instanceof Error && /Stale update/i.test(error.message) && attempt < 9) {
10722
+ await new Promise((resolve) => setTimeout(resolve, 20));
10723
+ continue;
10724
+ }
10725
+ throw error;
10726
+ }
10727
+ }
10728
+ throw lastError instanceof Error ? lastError : new Error("Continuity conflict resolution failed");
10729
+ }, "resolveConflict");
10730
+ var validateRelationships = validateRelationshipTargets;
10731
+ var continuityLedger = {
10732
+ recordEvent,
10733
+ record: recordEvent,
10734
+ append: recordEvent,
10735
+ add: recordEvent,
10736
+ getEvent,
10737
+ get: getEvent,
10738
+ find: getEvent,
10739
+ listEvents,
10740
+ list: listEvents,
10741
+ query: listEvents,
10742
+ deriveValidity,
10743
+ validity: deriveValidity,
10744
+ listContested,
10745
+ contested: listContested,
10746
+ resolveConflict,
10747
+ resolve: resolveConflict,
10748
+ reconstructValidity,
10749
+ validateRelationships,
10750
+ validateRelationshipTargets,
10751
+ validateResolution: validateResolutionRequest
10752
+ };
10753
+
10197
10754
  // core/continuity/index.ts
10198
10755
  var continuity = {
10199
10756
  async context(options) {
@@ -12022,6 +12579,1232 @@ var createCompactionEngine = /* @__PURE__ */ __name((options) => new CompactionE
12022
12579
  // index.ts
12023
12580
  init_user_identity();
12024
12581
  init_user_paths();
12582
+
12583
+ // core/governance/index.ts
12584
+ init_user_paths();
12585
+ init_policy();
12586
+ init_policy_namespace_lock();
12587
+ init_detection_events();
12588
+ init_policy();
12589
+ var GOVERNANCE_FILE_NAME = "governance.policy.json";
12590
+ var GOVERNANCE_FORMAT = "dotmem.governance-policy";
12591
+ var GOVERNANCE_VERSION = 1;
12592
+ var GOVERNANCE_AUDIT_RETENTION = 200;
12593
+ var GovernancePolicyError = class extends Error {
12594
+ static {
12595
+ __name(this, "GovernancePolicyError");
12596
+ }
12597
+ constructor(message) {
12598
+ super(message);
12599
+ this.name = "GovernancePolicyError";
12600
+ }
12601
+ };
12602
+ var requiredText3 = /* @__PURE__ */ __name((v) => typeof v === "string" && v.trim().length > 0, "requiredText");
12603
+ var nowIso2 = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString(), "nowIso");
12604
+ var joinPath7 = /* @__PURE__ */ __name((base, child) => {
12605
+ const sep = process.platform === "win32" ? "\\" : "/";
12606
+ return `${base.replace(/[\\/]+/g, sep)}${sep}${child}`;
12607
+ }, "joinPath");
12608
+ var resolveGovernanceDir = /* @__PURE__ */ __name((directory) => directory ?? joinPath7(resolveUserDotMemPath(), "policy"), "resolveGovernanceDir");
12609
+ var resolveGovernanceFilePath = /* @__PURE__ */ __name((directory) => joinPath7(resolveGovernanceDir(directory), GOVERNANCE_FILE_NAME), "resolveGovernanceFilePath");
12610
+ var ROLE_CAPABILITIES = {
12611
+ "human-owner": /* @__PURE__ */ new Set(["policy.propose", "policy.review", "policy.approve", "policy.reject", "policy.activate", "policy.suspend", "policy.retire", "role.bind", "governance.read", "governance.audit.read", "agent.execute", "guardrail.stop.activate", "guardrail.stop.release"]),
12612
+ "human-approver": /* @__PURE__ */ new Set(["policy.review", "policy.approve", "policy.reject", "governance.read", "governance.audit.read", "guardrail.stop.activate", "guardrail.stop.release"]),
12613
+ "coordinator-jo": /* @__PURE__ */ new Set(["policy.propose", "policy.review", "policy.activate", "governance.read", "governance.audit.read", "agent.execute"]),
12614
+ "specialist-agent": /* @__PURE__ */ new Set(["policy.propose", "governance.read"]),
12615
+ "policy-authority": /* @__PURE__ */ new Set(["policy.propose", "policy.review", "policy.activate", "governance.read", "governance.audit.read", "agent.execute"]),
12616
+ "auditor-reviewer": /* @__PURE__ */ new Set(["governance.read", "governance.audit.read"])
12617
+ };
12618
+ var defaultRoleBindings = /* @__PURE__ */ __name(() => [
12619
+ { principalId: "hi", role: "human-owner", scope: "*", grantedBy: "system", grantedAt: (/* @__PURE__ */ new Date(0)).toISOString() },
12620
+ { principalId: "human-owner", role: "human-owner", scope: "*", grantedBy: "system", grantedAt: (/* @__PURE__ */ new Date(0)).toISOString() },
12621
+ { principalId: "jo", role: "coordinator-jo", scope: "*", grantedBy: "system", grantedAt: (/* @__PURE__ */ new Date(0)).toISOString() }
12622
+ ], "defaultRoleBindings");
12623
+ var emptyDocument2 = /* @__PURE__ */ __name(() => ({
12624
+ format: GOVERNANCE_FORMAT,
12625
+ version: GOVERNANCE_VERSION,
12626
+ revision: 0,
12627
+ policies: [],
12628
+ roleBindings: defaultRoleBindings(),
12629
+ audit: []
12630
+ }), "emptyDocument");
12631
+ var validateAuthority = /* @__PURE__ */ __name((value) => {
12632
+ try {
12633
+ return exports.validateGovernanceAuthority(value);
12634
+ } catch (err2) {
12635
+ void detectAuthorityEscalationAttempt({ claimed: "invalid governance authority", channel: "governance" }).catch(() => void 0);
12636
+ throw err2 instanceof GovernancePolicyError ? err2 : new GovernancePolicyError(err2.message);
12637
+ }
12638
+ }, "validateAuthority");
12639
+ var buildAudit = /* @__PURE__ */ __name((entry) => ({
12640
+ id: entry.id ?? `gov-audit.${entry.action}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`,
12641
+ action: entry.action,
12642
+ at: entry.at,
12643
+ ...entry.actorPrincipal !== void 0 ? { actorPrincipal: entry.actorPrincipal } : {},
12644
+ ...entry.actorRole !== void 0 ? { actorRole: entry.actorRole } : {},
12645
+ ...entry.targetPolicyId !== void 0 ? { targetPolicyId: entry.targetPolicyId } : {},
12646
+ ...entry.targetVersion !== void 0 ? { targetVersion: entry.targetVersion } : {},
12647
+ ...entry.scope !== void 0 ? { scope: entry.scope } : {},
12648
+ outcome: entry.outcome,
12649
+ ...entry.reason !== void 0 ? { reason: entry.reason } : {},
12650
+ ...entry.detail !== void 0 ? { detail: entry.detail } : {},
12651
+ ...entry.correlationId !== void 0 ? { correlationId: entry.correlationId } : {},
12652
+ ...entry.mandatory !== void 0 ? { mandatory: entry.mandatory } : {}
12653
+ }), "buildAudit");
12654
+ var boundedAudit2 = /* @__PURE__ */ __name((entries2) => {
12655
+ if (entries2.length <= GOVERNANCE_AUDIT_RETENTION) return [...entries2];
12656
+ const kept = [];
12657
+ let budget = GOVERNANCE_AUDIT_RETENTION;
12658
+ for (let i2 = entries2.length - 1; i2 >= 0; i2--) {
12659
+ const e = entries2[i2];
12660
+ const mandatory = e.mandatory === true;
12661
+ if (mandatory || budget > 0) {
12662
+ kept.unshift(e);
12663
+ if (!mandatory) budget--;
12664
+ }
12665
+ }
12666
+ return kept;
12667
+ }, "boundedAudit");
12668
+ var validatePolicy = /* @__PURE__ */ __name((p) => {
12669
+ if (!p || typeof p !== "object" || Array.isArray(p)) throw new GovernancePolicyError("Governance policy must be an object");
12670
+ const r = p;
12671
+ if (!requiredText3(r.policyId)) throw new GovernancePolicyError("policyId is required");
12672
+ if (typeof r.version !== "number" || !Number.isInteger(r.version) || r.version < 1) throw new GovernancePolicyError("version must be a positive integer");
12673
+ if (!requiredText3(r.scope)) throw new GovernancePolicyError("scope is required");
12674
+ if (!requiredText3(r.rationale)) throw new GovernancePolicyError("rationale is required");
12675
+ if (!requiredText3(r.proposedBy)) throw new GovernancePolicyError("proposedBy is required");
12676
+ if (!requiredText3(r.proposedAt)) throw new GovernancePolicyError("proposedAt is required");
12677
+ if (typeof r.status !== "string" || !["proposed", "under_review", "approved", "active", "rejected", "suspended", "retired"].includes(r.status)) {
12678
+ throw new GovernancePolicyError("invalid status");
12679
+ }
12680
+ if (typeof r.revision !== "number" || !Number.isInteger(r.revision) || r.revision < 0) throw new GovernancePolicyError("revision must be a non-negative integer");
12681
+ if (!Array.isArray(r.applicablePrincipals)) throw new GovernancePolicyError("applicablePrincipals must be an array");
12682
+ if (!Array.isArray(r.applicableResources)) throw new GovernancePolicyError("applicableResources must be an array");
12683
+ const authority = validateAuthority(r.ownerAuthority);
12684
+ return {
12685
+ policyId: r.policyId,
12686
+ version: r.version,
12687
+ ...requiredText3(r.title) ? { title: r.title } : {},
12688
+ ...requiredText3(r.description) ? { description: r.description } : {},
12689
+ scope: r.scope,
12690
+ applicablePrincipals: r.applicablePrincipals,
12691
+ applicableResources: r.applicableResources,
12692
+ ...requiredText3(r.effectiveAt) ? { effectiveAt: r.effectiveAt } : {},
12693
+ ...requiredText3(r.expiresAt) ? { expiresAt: r.expiresAt } : {},
12694
+ rationale: r.rationale,
12695
+ status: r.status,
12696
+ ownerAuthority: authority,
12697
+ proposedBy: r.proposedBy,
12698
+ proposedAt: r.proposedAt,
12699
+ ...requiredText3(r.reviewedBy) ? { reviewedBy: r.reviewedBy } : {},
12700
+ ...requiredText3(r.reviewedAt) ? { reviewedAt: r.reviewedAt } : {},
12701
+ ...r.approval && typeof r.approval === "object" ? { approval: r.approval } : {},
12702
+ ...requiredText3(r.activatedBy) ? { activatedBy: r.activatedBy } : {},
12703
+ ...requiredText3(r.activatedAt) ? { activatedAt: r.activatedAt } : {},
12704
+ ...requiredText3(r.suspendedBy) ? { suspendedBy: r.suspendedBy } : {},
12705
+ ...requiredText3(r.suspendedAt) ? { suspendedAt: r.suspendedAt } : {},
12706
+ ...requiredText3(r.retiredBy) ? { retiredBy: r.retiredBy } : {},
12707
+ ...requiredText3(r.retiredAt) ? { retiredAt: r.retiredAt } : {},
12708
+ ...requiredText3(r.supersedes) ? { supersedes: r.supersedes } : {},
12709
+ ...requiredText3(r.supersededBy) ? { supersededBy: r.supersededBy } : {},
12710
+ ...requiredText3(r.correlationId) ? { correlationId: r.correlationId } : {},
12711
+ revision: r.revision,
12712
+ ...requiredText3(r.fingerprint) ? { fingerprint: r.fingerprint } : {}
12713
+ };
12714
+ }, "validatePolicy");
12715
+ var validateRoleBinding = /* @__PURE__ */ __name((rb) => {
12716
+ if (!rb || typeof rb !== "object" || Array.isArray(rb)) throw new GovernancePolicyError("role binding must be an object");
12717
+ const r = rb;
12718
+ if (!requiredText3(r.principalId)) throw new GovernancePolicyError("principalId is required");
12719
+ if (typeof r.role !== "string" || !["human-owner", "human-approver", "coordinator-jo", "specialist-agent", "policy-authority", "auditor-reviewer"].includes(r.role)) {
12720
+ throw new GovernancePolicyError("invalid role");
12721
+ }
12722
+ if (!requiredText3(r.scope)) throw new GovernancePolicyError("scope is required");
12723
+ return {
12724
+ principalId: r.principalId,
12725
+ role: r.role,
12726
+ scope: r.scope,
12727
+ ...requiredText3(r.grantedBy) ? { grantedBy: r.grantedBy } : {},
12728
+ ...requiredText3(r.grantedAt) ? { grantedAt: r.grantedAt } : {}
12729
+ };
12730
+ }, "validateRoleBinding");
12731
+ var validateDocument2 = /* @__PURE__ */ __name((value) => {
12732
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new GovernancePolicyError("document must be an object");
12733
+ const d = value;
12734
+ if (d.format !== GOVERNANCE_FORMAT) throw new GovernancePolicyError("invalid format");
12735
+ if (d.version !== GOVERNANCE_VERSION) throw new GovernancePolicyError("invalid version");
12736
+ if (typeof d.revision !== "number" || !Number.isInteger(d.revision) || d.revision < 0) throw new GovernancePolicyError("invalid revision");
12737
+ if (!Array.isArray(d.policies)) throw new GovernancePolicyError("policies must be an array");
12738
+ if (!Array.isArray(d.roleBindings)) throw new GovernancePolicyError("roleBindings must be an array");
12739
+ if (!Array.isArray(d.audit)) throw new GovernancePolicyError("audit must be an array");
12740
+ const policies = d.policies.map(validatePolicy);
12741
+ const roleBindings = d.roleBindings.map(validateRoleBinding);
12742
+ const audit = d.audit.map((e) => {
12743
+ if (!e || typeof e !== "object" || Array.isArray(e)) throw new GovernancePolicyError("audit entry must be an object");
12744
+ const r = e;
12745
+ if (!requiredText3(r.id)) throw new GovernancePolicyError("audit id required");
12746
+ if (!requiredText3(r.action)) throw new GovernancePolicyError("audit action required");
12747
+ if (!requiredText3(r.at)) throw new GovernancePolicyError("audit at required");
12748
+ if (r.outcome !== "applied" && r.outcome !== "rejected") throw new GovernancePolicyError("audit outcome invalid");
12749
+ return r;
12750
+ });
12751
+ return { format: GOVERNANCE_FORMAT, version: GOVERNANCE_VERSION, revision: d.revision, policies, roleBindings, audit };
12752
+ }, "validateDocument");
12753
+ var scopeCovers = /* @__PURE__ */ __name((bindingScope, targetScope) => {
12754
+ if (bindingScope === "*") return true;
12755
+ if (bindingScope === targetScope) return true;
12756
+ return targetScope.startsWith(bindingScope.endsWith("/") ? bindingScope : bindingScope + "/");
12757
+ }, "scopeCovers");
12758
+ var GovernancePolicyStore = class {
12759
+ constructor(directory) {
12760
+ this.directory = directory;
12761
+ }
12762
+ directory;
12763
+ static {
12764
+ __name(this, "GovernancePolicyStore");
12765
+ }
12766
+ dir() {
12767
+ return resolveGovernanceDir(this.directory);
12768
+ }
12769
+ filePath() {
12770
+ return resolveGovernanceFilePath(this.directory);
12771
+ }
12772
+ async load() {
12773
+ const fs = await import('fs/promises');
12774
+ let raw;
12775
+ try {
12776
+ raw = await fs.readFile(this.filePath(), "utf8");
12777
+ } catch (err2) {
12778
+ if (err2.code === "ENOENT") return emptyDocument2();
12779
+ throw err2;
12780
+ }
12781
+ return validateDocument2(JSON.parse(raw));
12782
+ }
12783
+ async readDoc(fs) {
12784
+ try {
12785
+ const raw = await fs.readFile(this.filePath(), "utf8");
12786
+ return validateDocument2(JSON.parse(raw));
12787
+ } catch (err2) {
12788
+ if (err2.code === "ENOENT") return emptyDocument2();
12789
+ throw err2;
12790
+ }
12791
+ }
12792
+ async writeDoc(fs, doc) {
12793
+ await fs.mkdir(this.dir(), { recursive: true });
12794
+ const fp = this.filePath();
12795
+ const tmp = `${fp}.${process.pid}.${Date.now().toString(36)}.tmp`;
12796
+ try {
12797
+ await fs.writeFile(tmp, `${JSON.stringify(doc, null, 2)}
12798
+ `, "utf8");
12799
+ const handle = await fs.open(tmp, "r+");
12800
+ try {
12801
+ await handle.sync();
12802
+ } finally {
12803
+ await handle.close();
12804
+ }
12805
+ await fs.rename(tmp, fp);
12806
+ } catch (err2) {
12807
+ await fs.rm(tmp, { force: true }).catch(() => void 0);
12808
+ throw err2;
12809
+ }
12810
+ }
12811
+ async mutate(operation, options = {}) {
12812
+ const directory = this.dir();
12813
+ return withPolicyNamespaceLock(async (session3) => {
12814
+ const fs = await import('fs/promises');
12815
+ const doc = await this.readDoc(fs);
12816
+ if (options.basedOnRevision !== void 0 && options.basedOnRevision !== doc.revision) {
12817
+ throw new GovernancePolicyError(`Stale governance revision: expected ${options.basedOnRevision}, current ${doc.revision}`);
12818
+ }
12819
+ const outcome = operation(doc);
12820
+ let next = outcome.document;
12821
+ validateDocument2(next);
12822
+ await session3.assertOwnership();
12823
+ await this.writeDoc(fs, next);
12824
+ return outcome.result;
12825
+ }, { directory });
12826
+ }
12827
+ // --- role helpers ---
12828
+ findRoleBinding(doc, principalId, scope) {
12829
+ const candidates = doc.roleBindings.filter((rb) => rb.principalId === principalId && scopeCovers(rb.scope, scope));
12830
+ if (candidates.length === 0) return null;
12831
+ return candidates.sort((a, b) => b.scope.length - a.scope.length)[0];
12832
+ }
12833
+ resolveRole(principalId, scope = "*") {
12834
+ return { principalId, role: null, scope, reason: "use resolveRoleAsync" };
12835
+ }
12836
+ async resolveRoleAsync(principalId, scope = "*") {
12837
+ const doc = await this.load();
12838
+ const rb = this.findRoleBinding(doc, principalId, scope);
12839
+ if (!rb) return { principalId, role: null, scope, reason: "no role binding for principal in scope" };
12840
+ return { principalId, role: rb.role, scope, reason: `bound role ${rb.role} (scope ${rb.scope})` };
12841
+ }
12842
+ async hasPermission(principalId, action, scope) {
12843
+ const res = await this.resolveRoleAsync(principalId, scope);
12844
+ if (!res.role) return false;
12845
+ return ROLE_CAPABILITIES[res.role].has(action);
12846
+ }
12847
+ // --- role binding governed ---
12848
+ async bindRole(input, basedOnRevision) {
12849
+ const authority = validateAuthority(input.authority);
12850
+ if (!requiredText3(input.principalId)) throw new GovernancePolicyError("principalId is required");
12851
+ if (!requiredText3(input.grantedBy)) throw new GovernancePolicyError("grantedBy is required");
12852
+ if (input.principalId === input.grantedBy && (input.role === "human-owner" || input.role === "human-approver" || input.role === "policy-authority")) {
12853
+ void detectSelfPromoted({ claim: `self-grant ${input.role}`, channel: "governance.role.bind" }).catch(() => void 0);
12854
+ throw new GovernancePolicyError("self-grant of elevated role denied");
12855
+ }
12856
+ const doc0 = await this.load();
12857
+ const grantedByRole = this.findRoleBinding(doc0, input.grantedBy, input.scope ?? "*");
12858
+ if (!grantedByRole || !ROLE_CAPABILITIES[grantedByRole.role].has("role.bind")) {
12859
+ throw new GovernancePolicyError("grantedBy lacks role.bind authority");
12860
+ }
12861
+ if (authority.kind !== "HI" && authority.kind !== "operator") {
12862
+ throw new GovernancePolicyError("invalid authority for role binding");
12863
+ }
12864
+ const binding = {
12865
+ principalId: input.principalId,
12866
+ role: input.role,
12867
+ scope: input.scope || "*",
12868
+ grantedBy: input.grantedBy,
12869
+ grantedAt: nowIso2()
12870
+ };
12871
+ return this.mutate((doc) => {
12872
+ const nextBindings = [...doc.roleBindings.filter((rb) => !(rb.principalId === binding.principalId && rb.scope === binding.scope)), binding];
12873
+ const revision = doc.revision + 1;
12874
+ const audit = boundedAudit2([...doc.audit, buildAudit({
12875
+ action: "role.bind",
12876
+ at: binding.grantedAt,
12877
+ actorPrincipal: input.grantedBy,
12878
+ actorRole: grantedByRole.role,
12879
+ scope: binding.scope,
12880
+ outcome: "applied",
12881
+ detail: `bound ${binding.principalId} to ${binding.role} in scope ${binding.scope}`,
12882
+ correlationId: void 0
12883
+ })]);
12884
+ return { document: { ...doc, revision, roleBindings: nextBindings, audit }, result: binding };
12885
+ }, { basedOnRevision });
12886
+ }
12887
+ // --- propose ---
12888
+ async propose(input, basedOnRevision) {
12889
+ if (!input || typeof input !== "object") throw new GovernancePolicyError("proposal input required");
12890
+ if (!requiredText3(input.scope)) throw new GovernancePolicyError("scope is required");
12891
+ if (!requiredText3(input.rationale)) throw new GovernancePolicyError("rationale is required");
12892
+ if (!requiredText3(input.proposedBy)) throw new GovernancePolicyError("proposedBy is required");
12893
+ const authority = validateAuthority(input.authority);
12894
+ const doc0 = await this.load();
12895
+ if (!await this.hasPermission(input.proposedBy, "policy.propose", input.scope)) {
12896
+ void detectAuthorityEscalationAttempt({ claimed: `${input.proposedBy} propose without permission`, channel: "governance.propose" }).catch(() => void 0);
12897
+ await this.recordDenial(doc0, "policy.propose", input.proposedBy, input.scope, "permission denied");
12898
+ throw new GovernancePolicyError("proposer lacks policy.propose permission");
12899
+ }
12900
+ const policyId = requiredText3(input.policyId) ? input.policyId : `gov-policy.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
12901
+ const existing = doc0.policies.filter((p) => p.policyId === policyId);
12902
+ const version = existing.length > 0 ? Math.max(...existing.map((p) => p.version)) + 1 : 1;
12903
+ const supersedes = input.supersedes;
12904
+ const policy2 = {
12905
+ policyId,
12906
+ version,
12907
+ ...requiredText3(input.title) ? { title: input.title } : {},
12908
+ ...requiredText3(input.description) ? { description: input.description } : {},
12909
+ scope: input.scope,
12910
+ applicablePrincipals: input.applicablePrincipals ?? [],
12911
+ applicableResources: input.applicableResources ?? [],
12912
+ ...requiredText3(input.effectiveAt) ? { effectiveAt: input.effectiveAt } : {},
12913
+ ...requiredText3(input.expiresAt) ? { expiresAt: input.expiresAt } : {},
12914
+ rationale: input.rationale,
12915
+ status: "proposed",
12916
+ ownerAuthority: authority,
12917
+ proposedBy: input.proposedBy,
12918
+ proposedAt: nowIso2(),
12919
+ ...requiredText3(supersedes) ? { supersedes } : {},
12920
+ ...requiredText3(input.correlationId) ? { correlationId: input.correlationId } : {},
12921
+ revision: 0
12922
+ };
12923
+ return this.mutate((doc) => {
12924
+ const revision = doc.revision + 1;
12925
+ const fingerprint = `${policy2.policyId}@v${policy2.version}`;
12926
+ const stored = { ...policy2, revision, fingerprint };
12927
+ const policies = [...doc.policies, stored];
12928
+ const audit = boundedAudit2([...doc.audit, buildAudit({
12929
+ action: "policy.proposed",
12930
+ at: stored.proposedAt,
12931
+ actorPrincipal: input.proposedBy,
12932
+ targetPolicyId: stored.policyId,
12933
+ targetVersion: stored.version,
12934
+ scope: stored.scope,
12935
+ outcome: "applied",
12936
+ detail: `policy ${stored.policyId} v${stored.version} proposed`,
12937
+ correlationId: input.correlationId
12938
+ })]);
12939
+ return { document: { ...doc, revision, policies, audit }, result: stored };
12940
+ }, { basedOnRevision });
12941
+ }
12942
+ async recordDenial(doc, action, actor, scope, reason) {
12943
+ try {
12944
+ await this.mutate((d) => {
12945
+ const revision = d.revision + 1;
12946
+ const audit = boundedAudit2([...d.audit, buildAudit({
12947
+ action: `${action}.denied`,
12948
+ at: nowIso2(),
12949
+ actorPrincipal: actor,
12950
+ scope,
12951
+ outcome: "rejected",
12952
+ reason
12953
+ })]);
12954
+ return { document: { ...d, revision, audit }, result: void 0 };
12955
+ });
12956
+ } catch {
12957
+ }
12958
+ }
12959
+ // --- review ---
12960
+ async review(input) {
12961
+ if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
12962
+ if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
12963
+ if (!requiredText3(input.reviewerPrincipal)) throw new GovernancePolicyError("reviewerPrincipal is required");
12964
+ const doc0 = await this.load();
12965
+ if (!await this.hasPermission(input.reviewerPrincipal, "policy.review", "*")) {
12966
+ const pol = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
12967
+ const scope = pol?.scope ?? "*";
12968
+ if (!await this.hasPermission(input.reviewerPrincipal, "policy.review", scope)) {
12969
+ await this.recordDenial(doc0, "policy.review", input.reviewerPrincipal, scope, "permission denied");
12970
+ throw new GovernancePolicyError("reviewer lacks policy.review permission");
12971
+ }
12972
+ }
12973
+ const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
12974
+ if (!policy2) throw new GovernancePolicyError("policy not found");
12975
+ if (policy2.status !== "proposed") throw new GovernancePolicyError(`cannot review policy in status ${policy2.status}`);
12976
+ return this.mutate((doc) => {
12977
+ const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
12978
+ if (idx < 0) throw new GovernancePolicyError("policy not found");
12979
+ const updated = {
12980
+ ...doc.policies[idx],
12981
+ status: "under_review",
12982
+ reviewedBy: input.reviewerPrincipal,
12983
+ reviewedAt: nowIso2()
12984
+ };
12985
+ const policies = [...doc.policies];
12986
+ policies[idx] = updated;
12987
+ const revision = doc.revision + 1;
12988
+ const audit = boundedAudit2([...doc.audit, buildAudit({
12989
+ action: "policy.reviewed",
12990
+ at: updated.reviewedAt,
12991
+ actorPrincipal: input.reviewerPrincipal,
12992
+ targetPolicyId: updated.policyId,
12993
+ targetVersion: updated.version,
12994
+ scope: updated.scope,
12995
+ outcome: "applied",
12996
+ correlationId: input.correlationId
12997
+ })]);
12998
+ return { document: { ...doc, revision, policies, audit }, result: updated };
12999
+ });
13000
+ }
13001
+ // --- decide (approve/reject) ---
13002
+ async decide(input) {
13003
+ if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
13004
+ if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
13005
+ if (!requiredText3(input.approverPrincipal)) throw new GovernancePolicyError("approverPrincipal is required");
13006
+ if (input.decision !== "approved" && input.decision !== "rejected") throw new GovernancePolicyError("decision must be approved or rejected");
13007
+ const doc0 = await this.load();
13008
+ const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
13009
+ if (!policy2) throw new GovernancePolicyError("policy not found");
13010
+ const scope = policy2.scope;
13011
+ const action = input.decision === "approved" ? "policy.approve" : "policy.reject";
13012
+ if (!await this.hasPermission(input.approverPrincipal, "policy.approve", scope) && !await this.hasPermission(input.approverPrincipal, "policy.reject", scope)) {
13013
+ if (!await this.hasPermission(input.approverPrincipal, "policy.approve", scope)) {
13014
+ await this.recordDenial(doc0, action, input.approverPrincipal, scope, "permission denied");
13015
+ throw new GovernancePolicyError("approver lacks approval permission");
13016
+ }
13017
+ }
13018
+ if (input.approverPrincipal === policy2.proposedBy) {
13019
+ void detectSelfPromoted({ claim: `self-approval ${policy2.policyId}`, channel: "governance.approve" }).catch(() => void 0);
13020
+ await this.recordDenial(doc0, action, input.approverPrincipal, scope, "self-approval denied");
13021
+ throw new GovernancePolicyError("self-approval denied: proposer cannot be sole approver");
13022
+ }
13023
+ if (policy2.status !== "under_review" && policy2.status !== "proposed") {
13024
+ throw new GovernancePolicyError(`cannot decide policy in status ${policy2.status}`);
13025
+ }
13026
+ return this.mutate((doc) => {
13027
+ const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
13028
+ if (idx < 0) throw new GovernancePolicyError("policy not found");
13029
+ const current = doc.policies[idx];
13030
+ const newStatus = input.decision === "approved" ? "approved" : "rejected";
13031
+ const updated = {
13032
+ ...current,
13033
+ status: newStatus,
13034
+ approval: {
13035
+ approverPrincipal: input.approverPrincipal,
13036
+ decision: input.decision,
13037
+ ...requiredText3(input.reason) ? { reason: input.reason } : {},
13038
+ at: nowIso2()
13039
+ }
13040
+ };
13041
+ const policies = [...doc.policies];
13042
+ policies[idx] = updated;
13043
+ const revision = doc.revision + 1;
13044
+ const audit = boundedAudit2([...doc.audit, buildAudit({
13045
+ action: input.decision === "approved" ? "policy.approved" : "policy.rejected",
13046
+ at: updated.approval.at,
13047
+ actorPrincipal: input.approverPrincipal,
13048
+ targetPolicyId: updated.policyId,
13049
+ targetVersion: updated.version,
13050
+ scope: updated.scope,
13051
+ outcome: "applied",
13052
+ reason: input.reason,
13053
+ correlationId: input.correlationId
13054
+ })]);
13055
+ return { document: { ...doc, revision, policies, audit }, result: updated };
13056
+ });
13057
+ }
13058
+ // --- activate ---
13059
+ async activate(input) {
13060
+ if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
13061
+ if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
13062
+ if (!requiredText3(input.activatorPrincipal)) throw new GovernancePolicyError("activatorPrincipal is required");
13063
+ const doc0 = await this.load();
13064
+ const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
13065
+ if (!policy2) throw new GovernancePolicyError("policy not found");
13066
+ if (policy2.status !== "approved") throw new GovernancePolicyError(`cannot activate policy in status ${policy2.status}`);
13067
+ const scope = policy2.scope;
13068
+ if (!await this.hasPermission(input.activatorPrincipal, "policy.activate", scope)) {
13069
+ await this.recordDenial(doc0, "policy.activate", input.activatorPrincipal, scope, "permission denied");
13070
+ throw new GovernancePolicyError("activator lacks policy.activate permission");
13071
+ }
13072
+ const now3 = /* @__PURE__ */ new Date();
13073
+ if (policy2.effectiveAt && Date.parse(policy2.effectiveAt) > now3.getTime()) {
13074
+ throw new GovernancePolicyError("policy not yet effective");
13075
+ }
13076
+ if (policy2.expiresAt && Date.parse(policy2.expiresAt) <= now3.getTime()) {
13077
+ throw new GovernancePolicyError("policy expired");
13078
+ }
13079
+ return this.mutate((doc) => {
13080
+ const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
13081
+ if (idx < 0) throw new GovernancePolicyError("policy not found");
13082
+ const current = doc.policies[idx];
13083
+ if (current.status !== "approved") throw new GovernancePolicyError(`cannot activate policy in status ${current.status}`);
13084
+ const policies = doc.policies.map((p) => {
13085
+ if (p.policyId === current.policyId && p.version !== current.version && p.status === "active") {
13086
+ return { ...p, status: "retired", retiredBy: input.activatorPrincipal, retiredAt: nowIso2(), supersededBy: `${current.policyId}@v${current.version}` };
13087
+ }
13088
+ return p;
13089
+ });
13090
+ const nowStr = nowIso2();
13091
+ policies[idx] = {
13092
+ ...current,
13093
+ status: "active",
13094
+ activatedBy: input.activatorPrincipal,
13095
+ activatedAt: nowStr
13096
+ };
13097
+ const updated = policies[idx];
13098
+ const revision = doc.revision + 1;
13099
+ const audit = boundedAudit2([...doc.audit, buildAudit({
13100
+ action: "policy.activated",
13101
+ at: nowStr,
13102
+ actorPrincipal: input.activatorPrincipal,
13103
+ targetPolicyId: updated.policyId,
13104
+ targetVersion: updated.version,
13105
+ scope: updated.scope,
13106
+ outcome: "applied",
13107
+ correlationId: input.correlationId
13108
+ })]);
13109
+ return { document: { ...doc, revision, policies, audit }, result: updated };
13110
+ });
13111
+ }
13112
+ async suspend(input) {
13113
+ if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
13114
+ if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
13115
+ if (!requiredText3(input.actorPrincipal)) throw new GovernancePolicyError("actorPrincipal is required");
13116
+ const doc0 = await this.load();
13117
+ const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
13118
+ if (!policy2) throw new GovernancePolicyError("policy not found");
13119
+ const scope = policy2.scope;
13120
+ if (!await this.hasPermission(input.actorPrincipal, "policy.suspend", scope)) {
13121
+ const role = await this.resolveRoleAsync(input.actorPrincipal, scope);
13122
+ if (role.role !== "human-owner" && role.role !== "human-approver") {
13123
+ await this.recordDenial(doc0, "policy.suspend", input.actorPrincipal, scope, "permission denied");
13124
+ throw new GovernancePolicyError("actor lacks suspend permission");
13125
+ }
13126
+ }
13127
+ return this.mutate((doc) => {
13128
+ const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
13129
+ if (idx < 0) throw new GovernancePolicyError("policy not found");
13130
+ const current = doc.policies[idx];
13131
+ if (current.status !== "active" && current.status !== "approved") throw new GovernancePolicyError(`cannot suspend policy in status ${current.status}`);
13132
+ const updated = {
13133
+ ...current,
13134
+ status: "suspended",
13135
+ suspendedBy: input.actorPrincipal,
13136
+ suspendedAt: nowIso2()
13137
+ };
13138
+ const policies = [...doc.policies];
13139
+ policies[idx] = updated;
13140
+ const revision = doc.revision + 1;
13141
+ const audit = boundedAudit2([...doc.audit, buildAudit({
13142
+ action: "policy.suspended",
13143
+ at: updated.suspendedAt,
13144
+ actorPrincipal: input.actorPrincipal,
13145
+ targetPolicyId: updated.policyId,
13146
+ targetVersion: updated.version,
13147
+ scope: updated.scope,
13148
+ outcome: "applied",
13149
+ reason: input.reason,
13150
+ correlationId: input.correlationId
13151
+ })]);
13152
+ return { document: { ...doc, revision, policies, audit }, result: updated };
13153
+ });
13154
+ }
13155
+ async retire(input) {
13156
+ if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
13157
+ if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
13158
+ if (!requiredText3(input.actorPrincipal)) throw new GovernancePolicyError("actorPrincipal is required");
13159
+ const doc0 = await this.load();
13160
+ const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
13161
+ if (!policy2) throw new GovernancePolicyError("policy not found");
13162
+ const scope = policy2.scope;
13163
+ const role = await this.resolveRoleAsync(input.actorPrincipal, scope);
13164
+ if (role.role !== "human-owner" && role.role !== "human-approver") {
13165
+ await this.recordDenial(doc0, "policy.retire", input.actorPrincipal, scope, "permission denied");
13166
+ throw new GovernancePolicyError("actor lacks retire permission");
13167
+ }
13168
+ return this.mutate((doc) => {
13169
+ const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
13170
+ if (idx < 0) throw new GovernancePolicyError("policy not found");
13171
+ const current = doc.policies[idx];
13172
+ const updated = {
13173
+ ...current,
13174
+ status: "retired",
13175
+ retiredBy: input.actorPrincipal,
13176
+ retiredAt: nowIso2()
13177
+ };
13178
+ const policies = [...doc.policies];
13179
+ policies[idx] = updated;
13180
+ const revision = doc.revision + 1;
13181
+ const audit = boundedAudit2([...doc.audit, buildAudit({
13182
+ action: "policy.retired",
13183
+ at: updated.retiredAt,
13184
+ actorPrincipal: input.actorPrincipal,
13185
+ targetPolicyId: updated.policyId,
13186
+ targetVersion: updated.version,
13187
+ scope: updated.scope,
13188
+ outcome: "applied",
13189
+ reason: input.reason,
13190
+ correlationId: input.correlationId
13191
+ })]);
13192
+ return { document: { ...doc, revision, policies, audit }, result: updated };
13193
+ });
13194
+ }
13195
+ // --- resolution (fail-closed) ---
13196
+ async resolvePolicy(policyId, version, requestedScope, now3 = /* @__PURE__ */ new Date()) {
13197
+ const doc = await this.load();
13198
+ const policy2 = doc.policies.find((p) => p.policyId === policyId && p.version === version);
13199
+ if (!policy2) {
13200
+ return { outcome: "none", policy: null, reason: "no applicable policy" };
13201
+ }
13202
+ if (!scopeCovers(policy2.scope, requestedScope)) {
13203
+ return { outcome: "insufficient-scope", policy: null, reason: "policy scope does not cover requested scope" };
13204
+ }
13205
+ if (!policy2.ownerAuthority || policy2.ownerAuthority.kind !== "HI" && policy2.ownerAuthority.kind !== "operator") {
13206
+ return { outcome: "unresolved-authority", policy: null, reason: "unresolved or invalid authority" };
13207
+ }
13208
+ if (policy2.effectiveAt && Date.parse(policy2.effectiveAt) > now3.getTime()) {
13209
+ return { outcome: "not-yet-effective", policy: null, reason: "policy not yet effective" };
13210
+ }
13211
+ if (policy2.expiresAt && Date.parse(policy2.expiresAt) <= now3.getTime()) {
13212
+ return { outcome: "expired", policy: null, reason: "policy expired" };
13213
+ }
13214
+ if (policy2.status !== "active") {
13215
+ return { outcome: "none", policy: null, reason: `policy status ${policy2.status} is not active` };
13216
+ }
13217
+ if (policy2.fingerprint && policy2.fingerprint !== `${policy2.policyId}@v${policy2.version}`) {
13218
+ return { outcome: "invalid-provenance", policy: null, reason: "fingerprint mismatch" };
13219
+ }
13220
+ return { outcome: "applicable", policy: policy2, reason: "applicable and valid" };
13221
+ }
13222
+ async resolveActivePolicies(scope, now3 = /* @__PURE__ */ new Date()) {
13223
+ const doc = await this.load();
13224
+ const active = doc.policies.filter((p) => p.status === "active" && scopeCovers(p.scope, scope));
13225
+ return active.filter((p) => {
13226
+ if (p.effectiveAt && Date.parse(p.effectiveAt) > now3.getTime()) return false;
13227
+ if (p.expiresAt && Date.parse(p.expiresAt) <= now3.getTime()) return false;
13228
+ return true;
13229
+ });
13230
+ }
13231
+ async checkConflicts(scope, now3 = /* @__PURE__ */ new Date()) {
13232
+ const actives = await this.resolveActivePolicies(scope, now3);
13233
+ const conflicting = actives.length > 1;
13234
+ return { conflicting, policies: actives };
13235
+ }
13236
+ async failClosedResolve(scope, now3 = /* @__PURE__ */ new Date()) {
13237
+ const actives = await this.resolveActivePolicies(scope, now3);
13238
+ if (actives.length === 0) {
13239
+ return { outcome: "none", policy: null, reason: "no applicable policy" };
13240
+ }
13241
+ if (actives.length > 1) {
13242
+ return { outcome: "conflicting", policy: null, reason: "conflicting active policies" };
13243
+ }
13244
+ const p = actives[0];
13245
+ if (!p.ownerAuthority || p.ownerAuthority.kind !== "HI" && p.ownerAuthority.kind !== "operator") {
13246
+ return { outcome: "unresolved-authority", policy: null, reason: "unresolved authority" };
13247
+ }
13248
+ return { outcome: "applicable", policy: p, reason: "applicable and valid" };
13249
+ }
13250
+ // --- authorization granted/denied audit ---
13251
+ async recordAuthorization(decision) {
13252
+ await this.mutate((doc) => {
13253
+ const revision = doc.revision + 1;
13254
+ const audit = boundedAudit2([...doc.audit, buildAudit({
13255
+ action: decision.allowed ? "authorization.granted" : "authorization.denied",
13256
+ at: nowIso2(),
13257
+ actorPrincipal: decision.principalId,
13258
+ scope: decision.scope ?? decision.resource,
13259
+ outcome: decision.allowed ? "applied" : "rejected",
13260
+ reason: decision.reason,
13261
+ detail: `${decision.action} on ${decision.resource}`,
13262
+ correlationId: decision.correlationId
13263
+ })]);
13264
+ return { document: { ...doc, revision, audit }, result: void 0 };
13265
+ });
13266
+ }
13267
+ // --- list ---
13268
+ async listPolicies() {
13269
+ const doc = await this.load();
13270
+ return [...doc.policies].sort((a, b) => a.policyId.localeCompare(b.policyId) || a.version - b.version);
13271
+ }
13272
+ async listRoleBindings() {
13273
+ const doc = await this.load();
13274
+ return [...doc.roleBindings];
13275
+ }
13276
+ async auditTrail() {
13277
+ const doc = await this.load();
13278
+ return [...doc.audit];
13279
+ }
13280
+ async recordAuditEvent(input) {
13281
+ await this.mutate((doc) => {
13282
+ const revision = doc.revision + 1;
13283
+ const audit = boundedAudit2([...doc.audit, buildAudit({
13284
+ action: input.action,
13285
+ at: input.at ?? nowIso2(),
13286
+ ...input.actorPrincipal !== void 0 ? { actorPrincipal: input.actorPrincipal } : {},
13287
+ ...input.actorRole !== void 0 ? { actorRole: input.actorRole } : {},
13288
+ ...input.targetPolicyId !== void 0 ? { targetPolicyId: input.targetPolicyId } : {},
13289
+ ...input.targetVersion !== void 0 ? { targetVersion: input.targetVersion } : {},
13290
+ ...input.scope !== void 0 ? { scope: input.scope } : {},
13291
+ outcome: input.outcome,
13292
+ ...input.reason !== void 0 ? { reason: input.reason } : {},
13293
+ ...input.detail !== void 0 ? { detail: input.detail } : {},
13294
+ ...input.correlationId !== void 0 ? { correlationId: input.correlationId } : {},
13295
+ ...input.mandatory !== void 0 ? { mandatory: input.mandatory } : {}
13296
+ })]);
13297
+ return { document: { ...doc, revision, audit }, result: void 0 };
13298
+ });
13299
+ }
13300
+ };
13301
+ var roleHasPermission = /* @__PURE__ */ __name((role, action) => ROLE_CAPABILITIES[role].has(action), "roleHasPermission");
13302
+ var GOVERNANCE_ROLES = [
13303
+ "human-owner",
13304
+ "human-approver",
13305
+ "coordinator-jo",
13306
+ "specialist-agent",
13307
+ "policy-authority",
13308
+ "auditor-reviewer"
13309
+ ];
13310
+ var GOVERNANCE_POLICY_STATUSES = [
13311
+ "proposed",
13312
+ "under_review",
13313
+ "approved",
13314
+ "active",
13315
+ "rejected",
13316
+ "suspended",
13317
+ "retired"
13318
+ ];
13319
+
13320
+ // core/guardrails/index.ts
13321
+ init_user_paths();
13322
+ init_policy_namespace_lock();
13323
+ var STOP_FILE_NAME = "guardrails.stop.json";
13324
+ var STOP_FORMAT = "dotmem.guardrails-stop";
13325
+ var STOP_VERSION = 1;
13326
+ var GuardrailError = class extends Error {
13327
+ static {
13328
+ __name(this, "GuardrailError");
13329
+ }
13330
+ constructor(message) {
13331
+ super(message);
13332
+ this.name = "GuardrailError";
13333
+ }
13334
+ };
13335
+ var requiredText4 = /* @__PURE__ */ __name((v) => typeof v === "string" && v.trim().length > 0, "requiredText");
13336
+ var nowIso3 = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString(), "nowIso");
13337
+ var joinPath8 = /* @__PURE__ */ __name((base, child) => {
13338
+ const sep = process.platform === "win32" ? "\\" : "/";
13339
+ return `${base.replace(/[\\/]+/g, sep)}${sep}${child}`;
13340
+ }, "joinPath");
13341
+ var resolveGuardrailDir = /* @__PURE__ */ __name((directory) => directory ?? joinPath8(resolveUserDotMemPath(), "policy"), "resolveGuardrailDir");
13342
+ var resolveStopFilePath = /* @__PURE__ */ __name((directory) => joinPath8(resolveGuardrailDir(directory), STOP_FILE_NAME), "resolveStopFilePath");
13343
+ var ACTIONS_REQUIRING_TRUSTED_IDENTITY = /* @__PURE__ */ new Set([
13344
+ "agent.execute",
13345
+ "guardrail.stop.activate",
13346
+ "guardrail.stop.release"
13347
+ ]);
13348
+ var GuardrailService = class {
13349
+ static {
13350
+ __name(this, "GuardrailService");
13351
+ }
13352
+ directory;
13353
+ governance;
13354
+ constructor(directory, governance) {
13355
+ this.directory = directory;
13356
+ this.governance = governance ?? new GovernancePolicyStore(directory);
13357
+ }
13358
+ dir() {
13359
+ return resolveGuardrailDir(this.directory);
13360
+ }
13361
+ // --- stop controller persistence ---
13362
+ async readStopState() {
13363
+ const fs = await import('fs/promises');
13364
+ let raw;
13365
+ try {
13366
+ raw = await fs.readFile(resolveStopFilePath(this.directory), "utf8");
13367
+ } catch (err2) {
13368
+ if (err2.code === "ENOENT") {
13369
+ return { stopped: false, revision: 0 };
13370
+ }
13371
+ throw err2;
13372
+ }
13373
+ const parsed = JSON.parse(raw);
13374
+ if (parsed.format !== STOP_FORMAT || parsed.version !== STOP_VERSION) {
13375
+ throw new GuardrailError("invalid stop state document");
13376
+ }
13377
+ return {
13378
+ stopped: parsed.stopped === true,
13379
+ ...requiredText4(parsed.activatedBy) ? { activatedBy: parsed.activatedBy } : {},
13380
+ ...requiredText4(parsed.activatedAt) ? { activatedAt: parsed.activatedAt } : {},
13381
+ ...requiredText4(parsed.reason) ? { reason: parsed.reason } : {},
13382
+ ...requiredText4(parsed.releasedBy) ? { releasedBy: parsed.releasedBy } : {},
13383
+ ...requiredText4(parsed.releasedAt) ? { releasedAt: parsed.releasedAt } : {},
13384
+ revision: typeof parsed.revision === "number" ? parsed.revision : 0
13385
+ };
13386
+ }
13387
+ async writeStopState(state3) {
13388
+ const fs = await import('fs/promises');
13389
+ const dir = this.dir();
13390
+ await fs.mkdir(dir, { recursive: true });
13391
+ const fp = resolveStopFilePath(this.directory);
13392
+ const tmp = `${fp}.${process.pid}.${Date.now().toString(36)}.tmp`;
13393
+ const doc = { format: STOP_FORMAT, version: STOP_VERSION, ...state3 };
13394
+ try {
13395
+ await fs.writeFile(tmp, `${JSON.stringify(doc, null, 2)}
13396
+ `, "utf8");
13397
+ const handle = await fs.open(tmp, "r+");
13398
+ try {
13399
+ await handle.sync();
13400
+ } finally {
13401
+ await handle.close();
13402
+ }
13403
+ await fs.rename(tmp, fp);
13404
+ } catch (err2) {
13405
+ await fs.rm(tmp, { force: true }).catch(() => void 0);
13406
+ throw err2;
13407
+ }
13408
+ }
13409
+ async getStopState() {
13410
+ return this.readStopState();
13411
+ }
13412
+ async isStopped() {
13413
+ const s = await this.readStopState();
13414
+ return s.stopped;
13415
+ }
13416
+ async activateStop(input) {
13417
+ if (!requiredText4(input.actorPrincipal)) throw new GuardrailError("actorPrincipal is required");
13418
+ const role = await this.governance.resolveRoleAsync(input.actorPrincipal, "*");
13419
+ if (role.role !== "human-owner" && role.role !== "human-approver") {
13420
+ await this.governance.recordAuditEvent({
13421
+ action: "guardrail.stop.activate.denied",
13422
+ actorPrincipal: input.actorPrincipal,
13423
+ actorRole: role.role ?? void 0,
13424
+ outcome: "rejected",
13425
+ reason: "insufficient authority to activate stop",
13426
+ correlationId: input.correlationId
13427
+ }).catch(() => void 0);
13428
+ throw new GuardrailError("unauthorized stop activation");
13429
+ }
13430
+ const current = await this.readStopState();
13431
+ if (current.stopped) {
13432
+ await this.governance.recordAuditEvent({
13433
+ action: "guardrail.stop.activate",
13434
+ actorPrincipal: input.actorPrincipal,
13435
+ actorRole: role.role ?? void 0,
13436
+ outcome: "applied",
13437
+ reason: "already stopped (idempotent)",
13438
+ correlationId: input.correlationId
13439
+ }).catch(() => void 0);
13440
+ return current;
13441
+ }
13442
+ const now3 = nowIso3();
13443
+ const next = {
13444
+ stopped: true,
13445
+ activatedBy: input.actorPrincipal,
13446
+ activatedAt: now3,
13447
+ ...requiredText4(input.reason) ? { reason: input.reason } : {},
13448
+ revision: current.revision + 1
13449
+ };
13450
+ await this.governance.recordAuditEvent({
13451
+ action: "guardrail.stop.activated",
13452
+ actorPrincipal: input.actorPrincipal,
13453
+ actorRole: role.role ?? void 0,
13454
+ outcome: "applied",
13455
+ reason: input.reason,
13456
+ correlationId: input.correlationId
13457
+ });
13458
+ await withPolicyNamespaceLock(async (session3) => {
13459
+ await this.writeStopState(next);
13460
+ await session3.assertOwnership();
13461
+ }, { directory: this.dir() });
13462
+ return next;
13463
+ }
13464
+ async releaseStop(input) {
13465
+ if (!requiredText4(input.actorPrincipal)) throw new GuardrailError("actorPrincipal is required");
13466
+ const role = await this.governance.resolveRoleAsync(input.actorPrincipal, "*");
13467
+ if (role.role !== "human-owner" && role.role !== "human-approver") {
13468
+ await this.governance.recordAuditEvent({
13469
+ action: "guardrail.stop.release.denied",
13470
+ actorPrincipal: input.actorPrincipal,
13471
+ actorRole: role.role ?? void 0,
13472
+ outcome: "rejected",
13473
+ reason: "insufficient authority to release stop",
13474
+ correlationId: input.correlationId
13475
+ }).catch(() => void 0);
13476
+ throw new GuardrailError("unauthorized stop release");
13477
+ }
13478
+ const current = await this.readStopState();
13479
+ if (!current.stopped) {
13480
+ await this.governance.recordAuditEvent({
13481
+ action: "guardrail.stop.release",
13482
+ actorPrincipal: input.actorPrincipal,
13483
+ actorRole: role.role ?? void 0,
13484
+ outcome: "applied",
13485
+ reason: "already running (idempotent)",
13486
+ correlationId: input.correlationId
13487
+ }).catch(() => void 0);
13488
+ return current;
13489
+ }
13490
+ const now3 = nowIso3();
13491
+ const next = {
13492
+ stopped: false,
13493
+ activatedBy: current.activatedBy,
13494
+ activatedAt: current.activatedAt,
13495
+ reason: current.reason,
13496
+ releasedBy: input.actorPrincipal,
13497
+ releasedAt: now3,
13498
+ revision: current.revision + 1
13499
+ };
13500
+ await this.governance.recordAuditEvent({
13501
+ action: "guardrail.stop.released",
13502
+ actorPrincipal: input.actorPrincipal,
13503
+ actorRole: role.role ?? void 0,
13504
+ outcome: "applied",
13505
+ reason: input.reason,
13506
+ correlationId: input.correlationId
13507
+ });
13508
+ await withPolicyNamespaceLock(async (session3) => {
13509
+ await this.writeStopState(next);
13510
+ await session3.assertOwnership();
13511
+ }, { directory: this.dir() });
13512
+ return next;
13513
+ }
13514
+ auditModes = {
13515
+ "agent.execute": "mandatory",
13516
+ "guardrail.stop.activate": "mandatory",
13517
+ "guardrail.stop.release": "mandatory"
13518
+ };
13519
+ auditModeFor(action) {
13520
+ return this.auditModes[action] ?? "best_effort";
13521
+ }
13522
+ async recordAudit(decision, auditMode) {
13523
+ const persisted = await this.auditDecision(decision);
13524
+ decision.auditPersisted = persisted;
13525
+ decision.auditMode = auditMode;
13526
+ if (!persisted && auditMode === "mandatory" && decision.allowed) {
13527
+ decision.outcome = "audit_failure";
13528
+ decision.allowed = false;
13529
+ decision.reason = "mandatory audit persistence failed";
13530
+ }
13531
+ return persisted;
13532
+ }
13533
+ // --- evaluation ---
13534
+ async evaluate(input) {
13535
+ const evaluatedAt = nowIso3();
13536
+ const auditMode = this.auditModeFor(input.action);
13537
+ const principalId = input.principalContext.trusted ? input.principalContext.trustedPrincipalId : input.principalContext.assertedPrincipalId;
13538
+ const stopState = await this.readStopState().catch(() => ({ stopped: false, revision: 0 }));
13539
+ const base = {
13540
+ outcome: "deny",
13541
+ allowed: false,
13542
+ reason: "",
13543
+ evaluatedAt,
13544
+ principalId,
13545
+ action: input.action,
13546
+ resource: input.resource,
13547
+ scope: input.scope,
13548
+ phase: input.phase,
13549
+ stopState: stopState.stopped ? "stopped" : "running",
13550
+ correlationId: input.correlationId,
13551
+ auditMode,
13552
+ auditPersisted: false
13553
+ };
13554
+ if (stopState.stopped && input.action !== "guardrail.stop.release") {
13555
+ const decision2 = { ...base, outcome: "stop_active", reason: "global stop active" };
13556
+ await this.recordAudit(decision2, auditMode);
13557
+ return decision2;
13558
+ }
13559
+ const requiresTrusted = ACTIONS_REQUIRING_TRUSTED_IDENTITY.has(input.action);
13560
+ if (requiresTrusted && !input.principalContext.trusted) {
13561
+ const decision2 = { ...base, outcome: "identity_unverified", reason: "trusted identity required but not verified" };
13562
+ await this.recordAudit(decision2, auditMode);
13563
+ return decision2;
13564
+ }
13565
+ if (!requiredText4(input.action) || !requiredText4(input.resource) || !requiredText4(input.scope)) {
13566
+ const decision2 = { ...base, outcome: "invalid_context", reason: "missing action/resource/scope" };
13567
+ await this.recordAudit(decision2, auditMode);
13568
+ return decision2;
13569
+ }
13570
+ let policyId;
13571
+ let policyVersion;
13572
+ try {
13573
+ const resolution = await this.governance.failClosedResolve(input.scope, input.now ?? /* @__PURE__ */ new Date());
13574
+ if (resolution.outcome !== "applicable" || !resolution.policy) {
13575
+ const decision2 = {
13576
+ ...base,
13577
+ outcome: resolution.outcome === "conflicting" ? "deny" : "policy_unavailable",
13578
+ reason: `policy resolution: ${resolution.outcome} - ${resolution.reason}`
13579
+ };
13580
+ await this.recordAudit(decision2, auditMode);
13581
+ return decision2;
13582
+ }
13583
+ policyId = resolution.policy.policyId;
13584
+ policyVersion = resolution.policy.version;
13585
+ const actorForPermission = principalId ?? "";
13586
+ const allowed = await this.governance.hasPermission(actorForPermission, input.action, input.scope);
13587
+ if (!allowed) {
13588
+ const decision2 = {
13589
+ ...base,
13590
+ outcome: "deny",
13591
+ reason: "permission denied by governance role binding",
13592
+ policyId,
13593
+ policyVersion
13594
+ };
13595
+ await this.recordAudit(decision2, auditMode);
13596
+ return decision2;
13597
+ }
13598
+ } catch (err2) {
13599
+ const decision2 = {
13600
+ ...base,
13601
+ outcome: "policy_unavailable",
13602
+ reason: `governance unavailable: ${err2.message}`
13603
+ };
13604
+ await this.recordAudit(decision2, auditMode);
13605
+ return decision2;
13606
+ }
13607
+ const decision = {
13608
+ ...base,
13609
+ outcome: "allow",
13610
+ allowed: true,
13611
+ reason: "allowed by governance policy",
13612
+ policyId,
13613
+ policyVersion
13614
+ };
13615
+ await this.recordAudit(decision, auditMode);
13616
+ return decision;
13617
+ }
13618
+ async auditDecision(decision) {
13619
+ try {
13620
+ await this.governance.recordAuditEvent({
13621
+ action: decision.allowed ? "guardrail.evaluation.allowed" : "guardrail.evaluation.denied",
13622
+ actorPrincipal: decision.principalId,
13623
+ scope: decision.scope,
13624
+ outcome: decision.allowed ? "applied" : "rejected",
13625
+ reason: decision.reason,
13626
+ detail: `${decision.action} on ${decision.resource} (${decision.outcome})`,
13627
+ correlationId: decision.correlationId,
13628
+ mandatory: decision.auditMode === "mandatory"
13629
+ });
13630
+ return true;
13631
+ } catch {
13632
+ return false;
13633
+ }
13634
+ }
13635
+ async execute(input, operation) {
13636
+ const decision = await this.evaluate(input);
13637
+ if (!decision.allowed) {
13638
+ return { decision, executed: false, error: decision.reason };
13639
+ }
13640
+ await this.governance.recordAuditEvent({
13641
+ action: "guardrail.operation.started",
13642
+ actorPrincipal: decision.principalId,
13643
+ scope: decision.scope,
13644
+ outcome: "applied",
13645
+ detail: `${decision.action} on ${decision.resource}`,
13646
+ correlationId: decision.correlationId
13647
+ }).catch(() => void 0);
13648
+ let result;
13649
+ try {
13650
+ result = await operation();
13651
+ } catch (err2) {
13652
+ await this.governance.recordAuditEvent({
13653
+ action: "guardrail.operation.failed",
13654
+ actorPrincipal: decision.principalId,
13655
+ scope: decision.scope,
13656
+ outcome: "rejected",
13657
+ reason: err2.message,
13658
+ detail: `${decision.action} on ${decision.resource}`,
13659
+ correlationId: decision.correlationId
13660
+ }).catch(() => void 0);
13661
+ return { decision, executed: true, error: err2.message };
13662
+ }
13663
+ await this.governance.recordAuditEvent({
13664
+ action: "guardrail.operation.completed",
13665
+ actorPrincipal: decision.principalId,
13666
+ scope: decision.scope,
13667
+ outcome: "applied",
13668
+ detail: `${decision.action} on ${decision.resource}`,
13669
+ correlationId: decision.correlationId
13670
+ }).catch(() => void 0);
13671
+ return { decision, executed: true, result };
13672
+ }
13673
+ };
13674
+ function createGuardrailService(directory, governance) {
13675
+ return new GuardrailService(directory, governance);
13676
+ }
13677
+ __name(createGuardrailService, "createGuardrailService");
13678
+
13679
+ // core/memory-anthology/index.ts
13680
+ var VALID_TRANSITIONS = {
13681
+ unassessed: ["asserted", "corroborated", "reviewed", "disputed", "refuted", "superseded"],
13682
+ asserted: ["corroborated", "reviewed", "disputed", "refuted", "superseded"],
13683
+ corroborated: ["reviewed", "disputed", "refuted", "superseded"],
13684
+ reviewed: ["disputed", "refuted", "superseded"],
13685
+ disputed: ["corroborated", "reviewed", "refuted", "superseded"],
13686
+ refuted: ["superseded"],
13687
+ superseded: []
13688
+ };
13689
+ function canTransitionEpistemic(from, to) {
13690
+ if (from === to) return true;
13691
+ return (VALID_TRANSITIONS[from] ?? []).includes(to);
13692
+ }
13693
+ __name(canTransitionEpistemic, "canTransitionEpistemic");
13694
+ function validateEpistemicTransition(from, to, reason) {
13695
+ if (from === to) return { allowed: true, reason: "no change" };
13696
+ if (!canTransitionEpistemic(from, to)) {
13697
+ return { allowed: false, reason: `invalid transition ${from} -> ${to}` };
13698
+ }
13699
+ if (to === "corroborated" && !reason) {
13700
+ return { allowed: false, reason: "corroborated requires explanation of independent sources" };
13701
+ }
13702
+ if (to === "reviewed" && !reason) {
13703
+ return { allowed: false, reason: "reviewed requires reviewer identity and scope" };
13704
+ }
13705
+ return { allowed: true, reason: reason ?? "ok" };
13706
+ }
13707
+ __name(validateEpistemicTransition, "validateEpistemicTransition");
13708
+ function validateProvenance(p) {
13709
+ const errors = [];
13710
+ if (!p || typeof p !== "object") {
13711
+ errors.push("provenance must be an object");
13712
+ return { valid: false, errors };
13713
+ }
13714
+ const kinds = ["user-statement", "system-observation", "imported-document", "external-source", "agent-inference", "model-summary", "human-correction", "system-event", "unknown"];
13715
+ if (!kinds.includes(p.kind)) errors.push("invalid provenance kind");
13716
+ if (p.observedAt !== void 0 && (typeof p.observedAt !== "string" || isNaN(Date.parse(p.observedAt)))) {
13717
+ errors.push("observedAt must be valid ISO timestamp if provided");
13718
+ }
13719
+ return { valid: errors.length === 0, errors };
13720
+ }
13721
+ __name(validateProvenance, "validateProvenance");
13722
+ function validateMemoryItem(item) {
13723
+ const errors = [];
13724
+ if (!item || typeof item !== "object") {
13725
+ errors.push("item must be an object");
13726
+ return { valid: false, errors };
13727
+ }
13728
+ if (typeof item.itemId !== "string" || !item.itemId.trim()) errors.push("itemId is required");
13729
+ if (typeof item.schemaVersion !== "number" || !Number.isInteger(item.schemaVersion) || item.schemaVersion < 1) errors.push("schemaVersion must be positive integer");
13730
+ if (typeof item.scope !== "string" || !item.scope.trim()) errors.push("scope is required");
13731
+ if (!item.temporal || typeof item.temporal !== "object") errors.push("temporal is required");
13732
+ else if (typeof item.temporal.recordedAt !== "string" || isNaN(Date.parse(item.temporal.recordedAt))) errors.push("recordedAt must be valid ISO timestamp");
13733
+ if (!item.provenance) errors.push("provenance is required");
13734
+ else {
13735
+ const pv = validateProvenance(item.provenance);
13736
+ if (!pv.valid) errors.push(...pv.errors);
13737
+ }
13738
+ if (!item.revision || typeof item.revision !== "object") errors.push("revision is required");
13739
+ else if (typeof item.revision.revision !== "number" || item.revision.revision < 0) errors.push("revision.revision must be non-negative integer");
13740
+ if (item.category === "derived-summary") {
13741
+ if (!item.derivedLineage) errors.push("derived summary requires lineage");
13742
+ }
13743
+ return { valid: errors.length === 0, errors };
13744
+ }
13745
+ __name(validateMemoryItem, "validateMemoryItem");
13746
+ function validateDerivedLineage(lineage, _sourceStatuses) {
13747
+ const errors = [];
13748
+ if (!lineage || typeof lineage !== "object") {
13749
+ errors.push("lineage required");
13750
+ return { valid: false, errors };
13751
+ }
13752
+ if (!Array.isArray(lineage.sourceItemIds) || lineage.sourceItemIds.length === 0) errors.push("sourceItemIds required");
13753
+ if (!Array.isArray(lineage.sourceRevisions) || lineage.sourceRevisions.length !== lineage.sourceItemIds.length) errors.push("sourceRevisions must match sourceItemIds length");
13754
+ if (typeof lineage.derivedAt !== "string" || isNaN(Date.parse(lineage.derivedAt))) errors.push("derivedAt invalid");
13755
+ if (typeof lineage.producingComponent !== "string" || !lineage.producingComponent.trim()) errors.push("producingComponent required");
13756
+ return { valid: errors.length === 0, errors };
13757
+ }
13758
+ __name(validateDerivedLineage, "validateDerivedLineage");
13759
+ function markDerivedStale(lineage) {
13760
+ return { ...lineage, stale: true };
13761
+ }
13762
+ __name(markDerivedStale, "markDerivedStale");
13763
+ function isLegacyRecord(item) {
13764
+ return !item || typeof item !== "object" || !item.provenance || !item.temporal || !item.revision;
13765
+ }
13766
+ __name(isLegacyRecord, "isLegacyRecord");
13767
+ function normalizeLegacyRecord(item) {
13768
+ return {
13769
+ itemId: item?.id ?? item?.itemId ?? `legacy-${Math.random().toString(36).slice(2, 10)}`,
13770
+ schemaVersion: 0,
13771
+ category: "legacy",
13772
+ payload: item,
13773
+ scope: item?.scope ?? "unknown",
13774
+ provenance: { kind: "unknown" },
13775
+ epistemicStatus: "unassessed",
13776
+ sensitivity: "unknown",
13777
+ temporal: { recordedAt: item?.createdAt ? new Date(item.createdAt).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString() },
13778
+ revision: { revision: 0 }
13779
+ };
13780
+ }
13781
+ __name(normalizeLegacyRecord, "normalizeLegacyRecord");
13782
+ var MEMORY_CATEGORIES = [
13783
+ "observation",
13784
+ "claim",
13785
+ "fact-candidate",
13786
+ "hypothesis",
13787
+ "decision",
13788
+ "preference",
13789
+ "instruction",
13790
+ "experience",
13791
+ "goal",
13792
+ "relationship",
13793
+ "derived-summary",
13794
+ "unknown-disputed",
13795
+ "legacy"
13796
+ ];
13797
+ var EPITEMIC_STATUSES2 = [
13798
+ "unassessed",
13799
+ "asserted",
13800
+ "corroborated",
13801
+ "reviewed",
13802
+ "disputed",
13803
+ "refuted",
13804
+ "superseded"
13805
+ ];
13806
+
13807
+ // index.ts
12025
13808
  var dotmem = {
12026
13809
  version: "0.0.8",
12027
13810
  isBrowser,
@@ -12056,18 +13839,29 @@ Object.defineProperty(exports, "store", {
12056
13839
  exports.CompactionEngine = CompactionEngine;
12057
13840
  exports.ContextCompiler = ContextCompiler;
12058
13841
  exports.ContextLifecycleOrchestrator = ContextLifecycleOrchestrator;
13842
+ exports.EPITEMIC_STATUSES = EPITEMIC_STATUSES2;
13843
+ exports.GOVERNANCE_POLICY_STATUSES = GOVERNANCE_POLICY_STATUSES;
13844
+ exports.GOVERNANCE_ROLES = GOVERNANCE_ROLES;
13845
+ exports.GovernancePolicyError = GovernancePolicyError;
13846
+ exports.GovernancePolicyStore = GovernancePolicyStore;
13847
+ exports.GuardrailError = GuardrailError;
13848
+ exports.GuardrailService = GuardrailService;
13849
+ exports.MEMORY_CATEGORIES = MEMORY_CATEGORIES;
12059
13850
  exports.RecoveryService = RecoveryService;
12060
13851
  exports.addDecision = addDecision;
12061
13852
  exports.addOpenLoopRef = addOpenLoopRef;
12062
13853
  exports.addRejectedApproach = addRejectedApproach;
12063
13854
  exports.addUnresolvedQuestion = addUnresolvedQuestion;
12064
13855
  exports.appendReasoningStep = appendReasoningStep;
13856
+ exports.canTransitionEpistemic = canTransitionEpistemic;
12065
13857
  exports.clearUserIdentity = clearUserIdentity;
12066
13858
  exports.closeConversationalState = closeConversationalState;
12067
13859
  exports.compileContext = compileContext;
12068
13860
  exports.continuity = continuity;
13861
+ exports.continuityLedger = continuityLedger;
12069
13862
  exports.createCompactionEngine = createCompactionEngine;
12070
13863
  exports.createConversationalState = createConversationalState;
13864
+ exports.createGuardrailService = createGuardrailService;
12071
13865
  exports.createNoOpHost = createNoOpHost;
12072
13866
  exports.createRecoveryService = createRecoveryService;
12073
13867
  exports.default = index_default;
@@ -12082,11 +13876,14 @@ exports.isBrowser = isBrowser;
12082
13876
  exports.isBun = isBun;
12083
13877
  exports.isDeno = isDeno;
12084
13878
  exports.isEdge = isEdge;
13879
+ exports.isLegacyRecord = isLegacyRecord;
12085
13880
  exports.isNode = isNode;
12086
13881
  exports.listUnfinishedConversations = listUnfinishedConversations;
12087
13882
  exports.loadAllConversationalStates = loadAllConversationalStates;
12088
13883
  exports.loadConversationalStateById = loadConversationalStateById;
13884
+ exports.markDerivedStale = markDerivedStale;
12089
13885
  exports.memory = memory;
13886
+ exports.normalizeLegacyRecord = normalizeLegacyRecord;
12090
13887
  exports.recoverConversation = recoverConversation;
12091
13888
  exports.resolveUserAgentMemoryPath = resolveUserAgentMemoryPath;
12092
13889
  exports.resolveUserBrainPath = resolveUserBrainPath;
@@ -12096,8 +13893,15 @@ exports.resolveUserExperiencePath = resolveUserExperiencePath;
12096
13893
  exports.resolveUserIdentity = resolveUserIdentity;
12097
13894
  exports.resolveUserProjectMemoryPath = resolveUserProjectMemoryPath;
12098
13895
  exports.resolveUserReadmePath = resolveUserReadmePath;
13896
+ exports.roleHasPermission = roleHasPermission;
12099
13897
  exports.saveConversationalState = saveConversationalState;
12100
13898
  exports.security = security;
12101
13899
  exports.setUserIdentity = setUserIdentity;
12102
13900
  exports.signMemPackage = signMemPackage;
12103
13901
  exports.updateConversationalState = updateConversationalState;
13902
+ exports.validateDerivedLineage = validateDerivedLineage;
13903
+ exports.validateEpistemicTransition = validateEpistemicTransition;
13904
+ exports.validateMemoryItem = validateMemoryItem;
13905
+ exports.validateProvenance = validateProvenance;
13906
+ exports.validateRelationshipTargets = validateRelationshipTargets;
13907
+ exports.validateResolutionRequest = validateResolutionRequest;