dotmem 0.0.8 → 0.1.0
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/AGENTS.md +13 -1
- package/DOTMEM.md +538 -539
- package/JOY.md +55 -0
- package/MEMORY.md +4 -4
- package/PACKAGE.md +1 -1
- package/README.md +251 -417
- package/agent.cjs +13 -0
- package/agent.js +13 -0
- package/bin/cli.cjs +5 -0
- package/bin/cli.js +5 -0
- package/bin/mcp.cjs +5 -0
- package/bin/mcp.js +5 -0
- package/cli.cjs +207 -61
- package/cli.js +207 -61
- package/global.cjs +206 -60
- package/global.js +206 -60
- package/index.cjs +1897 -68
- package/index.js +1869 -61
- package/llms.txt +94 -10
- package/mcp.cjs +207 -61
- package/mcp.js +207 -61
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -18,6 +18,18 @@ var __export = (target, all) => {
|
|
|
18
18
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
// suppress-idb-warning.ts
|
|
22
|
+
var orig;
|
|
23
|
+
var init_suppress_idb_warning = __esm({
|
|
24
|
+
"suppress-idb-warning.ts"() {
|
|
25
|
+
orig = console.warn.bind(console);
|
|
26
|
+
console.warn = (...args) => {
|
|
27
|
+
if (typeof args[0] === "string" && args[0].includes("IndexedDB is not available")) return;
|
|
28
|
+
orig(...args);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
21
33
|
// core/env.ts
|
|
22
34
|
var DEV;
|
|
23
35
|
var init_env = __esm({
|
|
@@ -2410,6 +2422,7 @@ var init_policy_store = __esm({
|
|
|
2410
2422
|
if (entry.target !== void 0) built.target = entry.target;
|
|
2411
2423
|
if (entry.reason !== void 0) built.reason = entry.reason;
|
|
2412
2424
|
if (entry.detail !== void 0) built.detail = entry.detail;
|
|
2425
|
+
if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
|
|
2413
2426
|
return built;
|
|
2414
2427
|
}, "validatePolicyAuditEntry");
|
|
2415
2428
|
validatePolicyDocument = /* @__PURE__ */ __name((value) => {
|
|
@@ -2487,6 +2500,7 @@ var init_policy_store = __esm({
|
|
|
2487
2500
|
if (entry.target !== void 0) built.target = entry.target;
|
|
2488
2501
|
if (entry.reason !== void 0) built.reason = entry.reason;
|
|
2489
2502
|
if (entry.detail !== void 0) built.detail = entry.detail;
|
|
2503
|
+
if (entry.mandatory !== void 0) built.mandatory = entry.mandatory;
|
|
2490
2504
|
return built;
|
|
2491
2505
|
}, "buildAuditEntry");
|
|
2492
2506
|
boundedAudit = /* @__PURE__ */ __name((entries2, overrides) => {
|
|
@@ -2499,9 +2513,10 @@ var init_policy_store = __esm({
|
|
|
2499
2513
|
for (let index = entries2.length - 1; index >= 0; index--) {
|
|
2500
2514
|
const entry = entries2[index];
|
|
2501
2515
|
const activeLinked = entry.target !== void 0 && activeOverrideIds.has(entry.target) && (entry.action === "override" || entry.action === "revoke");
|
|
2502
|
-
|
|
2516
|
+
const mandatory = entry.mandatory === true;
|
|
2517
|
+
if (activeLinked || mandatory || budget > 0) {
|
|
2503
2518
|
kept.unshift(entry);
|
|
2504
|
-
if (!activeLinked) budget--;
|
|
2519
|
+
if (!activeLinked && !mandatory) budget--;
|
|
2505
2520
|
}
|
|
2506
2521
|
}
|
|
2507
2522
|
return kept;
|
|
@@ -2542,6 +2557,12 @@ var init_policy_store = __esm({
|
|
|
2542
2557
|
try {
|
|
2543
2558
|
await fs.writeFile(temporary, `${JSON.stringify(document, null, 2)}
|
|
2544
2559
|
`, "utf8");
|
|
2560
|
+
const handle = await fs.open(temporary, "r+");
|
|
2561
|
+
try {
|
|
2562
|
+
await handle.sync();
|
|
2563
|
+
} finally {
|
|
2564
|
+
await handle.close();
|
|
2565
|
+
}
|
|
2545
2566
|
await fs.rename(temporary, filePath);
|
|
2546
2567
|
} catch (error) {
|
|
2547
2568
|
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
@@ -4460,7 +4481,7 @@ function encodeMemFile(stateValue, preserved) {
|
|
|
4460
4481
|
function decodeMemDocument(value) {
|
|
4461
4482
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid .mem file format");
|
|
4462
4483
|
const file = value;
|
|
4463
|
-
if (file.format !== MEM_FORMAT) throw new Error("Invalid .mem file format");
|
|
4484
|
+
if (file.format !== MEM_FORMAT && file.format !== MEM_FORMAT_LEGACY) throw new Error("Invalid .mem file format");
|
|
4464
4485
|
if (file.version === MEM_FORMAT_VERSION_V1) {
|
|
4465
4486
|
if (!file.knowledge) throw new Error("Invalid .mem file format");
|
|
4466
4487
|
return { state: validateSourceState(file.knowledge) };
|
|
@@ -4542,11 +4563,12 @@ function decodeMemDocument(value) {
|
|
|
4542
4563
|
preserved: { map: structuredClone(map), data: structuredClone(stateRecord) }
|
|
4543
4564
|
};
|
|
4544
4565
|
}
|
|
4545
|
-
var MEM_FORMAT, MEM_FORMAT_VERSION, MEM_FORMAT_VERSION_V1, expectedFields, epistemicTypes, revisionKinds, claimKinds, isJsonValue2, createMemInterpretationMap, validateSourceState, trimRecord, fieldIndexes, validateVocabulary, put, get, optionalGet, requireRecord, putOptional;
|
|
4566
|
+
var MEM_FORMAT, MEM_FORMAT_LEGACY, MEM_FORMAT_VERSION, MEM_FORMAT_VERSION_V1, expectedFields, epistemicTypes, revisionKinds, claimKinds, isJsonValue2, createMemInterpretationMap, validateSourceState, trimRecord, fieldIndexes, validateVocabulary, put, get, optionalGet, requireRecord, putOptional;
|
|
4546
4567
|
var init_mem_codec = __esm({
|
|
4547
4568
|
"core/knowledge/mem-codec.ts"() {
|
|
4548
4569
|
init_state();
|
|
4549
4570
|
MEM_FORMAT = "dotmem.knowledge";
|
|
4571
|
+
MEM_FORMAT_LEGACY = "memorio.knowledge";
|
|
4550
4572
|
MEM_FORMAT_VERSION = 2;
|
|
4551
4573
|
MEM_FORMAT_VERSION_V1 = 1;
|
|
4552
4574
|
expectedFields = {
|
|
@@ -4725,12 +4747,13 @@ async function inspectMem(input) {
|
|
|
4725
4747
|
return { status: artifactError.code, representation: "unknown", message: artifactError.message, parts: [] };
|
|
4726
4748
|
}
|
|
4727
4749
|
}
|
|
4728
|
-
var MEM_PACKAGE_FORMAT, MEM_PACKAGE_VERSION, MEM_PACKAGE_LIMITS, MEM_PACKAGE_WRITE_MARGIN_BYTES, MEM_PACKAGE_WRITE_LIMITS, MANIFEST_PATH, MAP_PATH, DATA_PATH, INTEGRITY_PATH, requiredParts, encoder2, decoder, MemArtifactError, stableValue, jsonBytes, sha256, parseJson, validEntryPath, concatenate, readZipParts, createManifest, digestTableIdentity, signaturePayload, base64UrlEncode, base64UrlDecode, keyIdentity, signaturePath, encodePackage, isZip, decodePackage, verifySignature;
|
|
4750
|
+
var MEM_PACKAGE_FORMAT, MEM_PACKAGE_FORMAT_LEGACY, MEM_PACKAGE_VERSION, MEM_PACKAGE_LIMITS, MEM_PACKAGE_WRITE_MARGIN_BYTES, MEM_PACKAGE_WRITE_LIMITS, MANIFEST_PATH, MAP_PATH, DATA_PATH, INTEGRITY_PATH, requiredParts, encoder2, decoder, MemArtifactError, stableValue, jsonBytes, sha256, parseJson, validEntryPath, concatenate, readZipParts, createManifest, digestTableIdentity, signaturePayload, base64UrlEncode, base64UrlDecode, keyIdentity, signaturePath, encodePackage, isZip, decodePackage, verifySignature;
|
|
4729
4751
|
var init_mem_package = __esm({
|
|
4730
4752
|
"core/knowledge/mem-package.ts"() {
|
|
4731
4753
|
init_browser();
|
|
4732
4754
|
init_mem_codec();
|
|
4733
4755
|
MEM_PACKAGE_FORMAT = "dotmem.package";
|
|
4756
|
+
MEM_PACKAGE_FORMAT_LEGACY = "memorio.package";
|
|
4734
4757
|
MEM_PACKAGE_VERSION = 1;
|
|
4735
4758
|
MEM_PACKAGE_LIMITS = Object.freeze({
|
|
4736
4759
|
maximumPackageBytes: 10 * 1024 * 1024,
|
|
@@ -4921,10 +4944,10 @@ ${contentId}
|
|
|
4921
4944
|
if (!parts[path]) throw new MemArtifactError("invalid", `Missing MEM package part: ${path}`);
|
|
4922
4945
|
}
|
|
4923
4946
|
const manifest = parseJson(parts[MANIFEST_PATH], MANIFEST_PATH);
|
|
4924
|
-
if (manifest.format !== MEM_PACKAGE_FORMAT && manifest.format || manifest.version !== MEM_PACKAGE_VERSION) {
|
|
4947
|
+
if (manifest.format !== MEM_PACKAGE_FORMAT && manifest.format !== MEM_PACKAGE_FORMAT_LEGACY || manifest.version !== MEM_PACKAGE_VERSION) {
|
|
4925
4948
|
throw new MemArtifactError("unsupported", "Unsupported MEM package format or version");
|
|
4926
4949
|
}
|
|
4927
|
-
if (manifest.memory?.format !== MEM_FORMAT && manifest.memory?.format || manifest.memory.version !== MEM_FORMAT_VERSION || manifest.parts?.map !== MAP_PATH || manifest.parts?.data !== DATA_PATH || manifest.parts?.integrity !== INTEGRITY_PATH) {
|
|
4950
|
+
if (manifest.memory?.format !== MEM_FORMAT && manifest.memory?.format !== MEM_FORMAT_LEGACY || manifest.memory.version !== MEM_FORMAT_VERSION || manifest.parts?.map !== MAP_PATH || manifest.parts?.data !== DATA_PATH || manifest.parts?.integrity !== INTEGRITY_PATH) {
|
|
4928
4951
|
throw new MemArtifactError("invalid", "Invalid MEM package manifest");
|
|
4929
4952
|
}
|
|
4930
4953
|
const integrity = parseJson(parts[INTEGRITY_PATH], INTEGRITY_PATH);
|
|
@@ -5036,6 +5059,109 @@ ${contentId}
|
|
|
5036
5059
|
}
|
|
5037
5060
|
});
|
|
5038
5061
|
|
|
5062
|
+
// core/storage-coordinator/index.ts
|
|
5063
|
+
var nodeFs4, withStorageLock, readStorageGeneration, writeStorageGeneration;
|
|
5064
|
+
var init_storage_coordinator = __esm({
|
|
5065
|
+
"core/storage-coordinator/index.ts"() {
|
|
5066
|
+
nodeFs4 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
|
|
5067
|
+
withStorageLock = /* @__PURE__ */ __name(async (directoryPath, operationOrOptions, operation, _options = {}) => {
|
|
5068
|
+
const opts = typeof operationOrOptions === "function" ? operation ?? {} : operationOrOptions ?? {};
|
|
5069
|
+
const op = typeof operationOrOptions === "function" ? operationOrOptions : operation;
|
|
5070
|
+
const timeoutMs = opts.timeoutMs ?? 3e4;
|
|
5071
|
+
const staleMs = opts.staleMs ?? 6e4;
|
|
5072
|
+
const lockName = opts.lockName ?? ".storage.lock";
|
|
5073
|
+
const lockPath = `${directoryPath}/${lockName}`;
|
|
5074
|
+
const fs = await nodeFs4();
|
|
5075
|
+
const started = Date.now();
|
|
5076
|
+
await fs.mkdir(directoryPath, { recursive: true });
|
|
5077
|
+
while (true) {
|
|
5078
|
+
try {
|
|
5079
|
+
await fs.mkdir(lockPath);
|
|
5080
|
+
const token2 = `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
5081
|
+
await fs.writeFile(`${lockPath}/owner.json`, JSON.stringify({ token: token2, pid: process.pid, createdAt: Date.now() }), { flag: "wx" });
|
|
5082
|
+
break;
|
|
5083
|
+
} catch (error) {
|
|
5084
|
+
const code = error.code;
|
|
5085
|
+
if (code !== "EEXIST" && code !== "ENOENT" && code !== "EPERM" && code !== "ENOTEMPTY") throw error;
|
|
5086
|
+
let stale = false;
|
|
5087
|
+
let processDead = false;
|
|
5088
|
+
try {
|
|
5089
|
+
const raw = await fs.readFile(`${lockPath}/owner.json`, "utf8");
|
|
5090
|
+
const parsed = JSON.parse(raw);
|
|
5091
|
+
const ageStale = typeof parsed.createdAt === "number" && Date.now() - parsed.createdAt > staleMs;
|
|
5092
|
+
if (typeof parsed.pid === "number") {
|
|
5093
|
+
try {
|
|
5094
|
+
process.kill(parsed.pid, 0);
|
|
5095
|
+
} catch {
|
|
5096
|
+
processDead = true;
|
|
5097
|
+
}
|
|
5098
|
+
} else {
|
|
5099
|
+
processDead = true;
|
|
5100
|
+
}
|
|
5101
|
+
if (ageStale || processDead) stale = true;
|
|
5102
|
+
} catch {
|
|
5103
|
+
stale = true;
|
|
5104
|
+
}
|
|
5105
|
+
if (stale) {
|
|
5106
|
+
if (processDead) {
|
|
5107
|
+
try {
|
|
5108
|
+
await fs.rm(lockPath, { recursive: true, force: true });
|
|
5109
|
+
continue;
|
|
5110
|
+
} catch {
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
try {
|
|
5114
|
+
await fs.rmdir(lockPath);
|
|
5115
|
+
continue;
|
|
5116
|
+
} catch {
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
if (Date.now() - started > timeoutMs) {
|
|
5120
|
+
throw new Error(`Storage lock timeout for ${directoryPath}`);
|
|
5121
|
+
}
|
|
5122
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
let token = "";
|
|
5126
|
+
try {
|
|
5127
|
+
const raw = await fs.readFile(`${lockPath}/owner.json`, "utf8");
|
|
5128
|
+
token = JSON.parse(raw).token ?? "";
|
|
5129
|
+
} catch {
|
|
5130
|
+
token = "";
|
|
5131
|
+
}
|
|
5132
|
+
try {
|
|
5133
|
+
return await op();
|
|
5134
|
+
} finally {
|
|
5135
|
+
try {
|
|
5136
|
+
const raw = await fs.readFile(`${lockPath}/owner.json`, "utf8");
|
|
5137
|
+
const parsed = JSON.parse(raw);
|
|
5138
|
+
if (parsed.token === token && token) {
|
|
5139
|
+
await fs.rm(lockPath, { recursive: true, force: true });
|
|
5140
|
+
}
|
|
5141
|
+
} catch {
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
}, "withStorageLock");
|
|
5145
|
+
readStorageGeneration = /* @__PURE__ */ __name(async (directoryPath) => {
|
|
5146
|
+
const fs = await nodeFs4();
|
|
5147
|
+
try {
|
|
5148
|
+
const raw = await fs.readFile(`${directoryPath}/.storage.generation`, "utf8");
|
|
5149
|
+
const value = Number(raw.trim());
|
|
5150
|
+
return Number.isFinite(value) && value >= 0 ? value : 0;
|
|
5151
|
+
} catch {
|
|
5152
|
+
return 0;
|
|
5153
|
+
}
|
|
5154
|
+
}, "readStorageGeneration");
|
|
5155
|
+
writeStorageGeneration = /* @__PURE__ */ __name(async (directoryPath, value) => {
|
|
5156
|
+
const fs = await nodeFs4();
|
|
5157
|
+
await fs.mkdir(directoryPath, { recursive: true });
|
|
5158
|
+
const temp = `${directoryPath}/.storage.generation.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
5159
|
+
await fs.writeFile(temp, String(value));
|
|
5160
|
+
await fs.rename(temp, `${directoryPath}/.storage.generation`);
|
|
5161
|
+
}, "writeStorageGeneration");
|
|
5162
|
+
}
|
|
5163
|
+
});
|
|
5164
|
+
|
|
5039
5165
|
// core/knowledge/mem-repository.ts
|
|
5040
5166
|
function resolveDuplicate(existing, existingSource, current, currentSource) {
|
|
5041
5167
|
const existingRetracts = existing.revision?.kind === "retracts";
|
|
@@ -5076,19 +5202,20 @@ async function parseMemFile(value, sourceId) {
|
|
|
5076
5202
|
throw new Error(`${message} in ${sourceId}`);
|
|
5077
5203
|
}
|
|
5078
5204
|
}
|
|
5079
|
-
var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE,
|
|
5205
|
+
var DOTMEM_DIRECTORY, DEFAULT_MEM_FILE, nodeFs5, repositoryLoadCaches, separator2, normalizePath, isAbsolutePath, resolvePath, joinPath2, parentPath, uniqueSuffix, isMissingDirectory, sourcePriority, memFilePath, MemDirectoryKnowledgeRepository;
|
|
5080
5206
|
var init_mem_repository = __esm({
|
|
5081
5207
|
"core/knowledge/mem-repository.ts"() {
|
|
5082
5208
|
init_mem_codec();
|
|
5083
5209
|
init_mem_package();
|
|
5084
5210
|
init_state();
|
|
5085
5211
|
init_acquisition_limits();
|
|
5212
|
+
init_storage_coordinator();
|
|
5086
5213
|
DOTMEM_DIRECTORY = ".dotmem";
|
|
5087
5214
|
DEFAULT_MEM_FILE = "project.mem";
|
|
5088
|
-
|
|
5089
|
-
nodeFs4 = /* @__PURE__ */ __name(async () => {
|
|
5215
|
+
nodeFs5 = /* @__PURE__ */ __name(async () => {
|
|
5090
5216
|
return import('fs/promises');
|
|
5091
5217
|
}, "nodeFs");
|
|
5218
|
+
repositoryLoadCaches = /* @__PURE__ */ new Map();
|
|
5092
5219
|
separator2 = /* @__PURE__ */ __name(() => process.platform === "win32" ? "\\" : "/", "separator");
|
|
5093
5220
|
normalizePath = /* @__PURE__ */ __name((value) => value.replace(/[\\/]+/g, separator2()), "normalizePath");
|
|
5094
5221
|
isAbsolutePath = /* @__PURE__ */ __name((value) => /^(?:[A-Za-z]:[\\/]|[\\/])/.test(value), "isAbsolutePath");
|
|
@@ -5100,25 +5227,8 @@ var init_mem_repository = __esm({
|
|
|
5100
5227
|
return index <= 0 ? "." : normalized.slice(0, index);
|
|
5101
5228
|
}, "parentPath");
|
|
5102
5229
|
uniqueSuffix = /* @__PURE__ */ __name(() => globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`, "uniqueSuffix");
|
|
5103
|
-
withDirectoryLock = /* @__PURE__ */ __name(async (directoryPath, operation) => {
|
|
5104
|
-
const previous = directoryLocks.get(directoryPath) ?? Promise.resolve();
|
|
5105
|
-
let release = /* @__PURE__ */ __name(() => void 0, "release");
|
|
5106
|
-
const gate = new Promise((resolveGate) => {
|
|
5107
|
-
release = resolveGate;
|
|
5108
|
-
});
|
|
5109
|
-
const queued = previous.then(() => gate);
|
|
5110
|
-
directoryLocks.set(directoryPath, queued);
|
|
5111
|
-
await previous;
|
|
5112
|
-
try {
|
|
5113
|
-
return await operation();
|
|
5114
|
-
} finally {
|
|
5115
|
-
release();
|
|
5116
|
-
if (directoryLocks.get(directoryPath) === queued) directoryLocks.delete(directoryPath);
|
|
5117
|
-
}
|
|
5118
|
-
}, "withDirectoryLock");
|
|
5119
5230
|
isMissingDirectory = /* @__PURE__ */ __name((error) => error.code === "ENOENT", "isMissingDirectory");
|
|
5120
5231
|
sourcePriority = /* @__PURE__ */ __name((sourceId, scope) => {
|
|
5121
|
-
if (sourceId.endsWith(".mem") && sourceId !== "project.mem" && sourceId !== "user.mem" && sourceId !== "agent.mem") return 0;
|
|
5122
5232
|
if (scope === "project" || sourceId === "project.mem") return 4;
|
|
5123
5233
|
if (scope === "user" || sourceId === "user.mem") return 3;
|
|
5124
5234
|
if (sourceId === "agent.mem" || sourceId === "agent") return 2;
|
|
@@ -5146,7 +5256,7 @@ var init_mem_repository = __esm({
|
|
|
5146
5256
|
this.loadWritableOnly = loadWritableOnly;
|
|
5147
5257
|
}
|
|
5148
5258
|
async discover() {
|
|
5149
|
-
const { readdir } = await
|
|
5259
|
+
const { readdir } = await nodeFs5();
|
|
5150
5260
|
try {
|
|
5151
5261
|
const entries2 = await readdir(this.directoryPath, { withFileTypes: true });
|
|
5152
5262
|
return entries2.filter((entry) => entry.isFile() && entry.name.endsWith(".mem") && entry.name !== "@brain.mem").map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
@@ -5161,7 +5271,7 @@ var init_mem_repository = __esm({
|
|
|
5161
5271
|
* receive a clear failure instead of discovering corruption on first use.
|
|
5162
5272
|
*/
|
|
5163
5273
|
async initialize() {
|
|
5164
|
-
const { mkdir } = await
|
|
5274
|
+
const { mkdir } = await nodeFs5();
|
|
5165
5275
|
await mkdir(this.directoryPath, { recursive: true });
|
|
5166
5276
|
await this.load();
|
|
5167
5277
|
}
|
|
@@ -5176,7 +5286,7 @@ var init_mem_repository = __esm({
|
|
|
5176
5286
|
* that does not delete is not a delete.
|
|
5177
5287
|
*/
|
|
5178
5288
|
async clearWritable() {
|
|
5179
|
-
const { rm, readdir } = await
|
|
5289
|
+
const { rm, readdir } = await nodeFs5();
|
|
5180
5290
|
const filePath = memFilePath(this.directoryPath, this.writableSourceId);
|
|
5181
5291
|
await rm(filePath, { force: true });
|
|
5182
5292
|
await rm(`${filePath}.bak`, { force: true });
|
|
@@ -5193,15 +5303,28 @@ var init_mem_repository = __esm({
|
|
|
5193
5303
|
}
|
|
5194
5304
|
}
|
|
5195
5305
|
async load() {
|
|
5196
|
-
const
|
|
5197
|
-
|
|
5306
|
+
const cacheKey = `${this.directoryPath}:${this.writableSourceId}:${this.loadWritableOnly}`;
|
|
5307
|
+
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
5308
|
+
const generationBefore = await readStorageGeneration(this.directoryPath);
|
|
5309
|
+
const cached = repositoryLoadCaches.get(cacheKey);
|
|
5310
|
+
if (cached && cached.signature === String(generationBefore)) {
|
|
5311
|
+
return structuredClone(cached.state);
|
|
5312
|
+
}
|
|
5313
|
+
const loaded = this.loadWritableOnly ? await this.loadSources([this.writableSourceId]) : await this.loadSources();
|
|
5314
|
+
const state3 = this.combine(loaded);
|
|
5315
|
+
const generationAfter = await readStorageGeneration(this.directoryPath);
|
|
5316
|
+
if (generationBefore !== generationAfter && attempt < 4) continue;
|
|
5317
|
+
repositoryLoadCaches.set(cacheKey, { signature: String(generationAfter), state: structuredClone(state3) });
|
|
5318
|
+
return state3;
|
|
5319
|
+
}
|
|
5320
|
+
throw new Error("Unable to load stable repository state");
|
|
5198
5321
|
}
|
|
5199
|
-
async save(state3, expectedVersion) {
|
|
5322
|
+
async save(state3, expectedVersion, locked = false) {
|
|
5200
5323
|
const validated = validateKnowledgeState(state3);
|
|
5201
5324
|
if (!Number.isInteger(expectedVersion) || expectedVersion < 0 || validated.version !== expectedVersion + 1) {
|
|
5202
5325
|
throw new Error("Invalid expected persisted version");
|
|
5203
5326
|
}
|
|
5204
|
-
|
|
5327
|
+
const doWrite = /* @__PURE__ */ __name(async () => {
|
|
5205
5328
|
const loaded = await this.loadSources();
|
|
5206
5329
|
const writable = loaded.find((source) => source.id === this.writableSourceId) ?? {
|
|
5207
5330
|
id: this.writableSourceId,
|
|
@@ -5223,10 +5346,16 @@ var init_mem_repository = __esm({
|
|
|
5223
5346
|
claims: [...writable.state.claims, ...newClaims]
|
|
5224
5347
|
};
|
|
5225
5348
|
await this.writeSource(writable.filePath, nextWritable, writable.preserved, writable.package);
|
|
5226
|
-
|
|
5349
|
+
await writeStorageGeneration(this.directoryPath, await readStorageGeneration(this.directoryPath) + 1);
|
|
5350
|
+
}, "doWrite");
|
|
5351
|
+
if (locked) {
|
|
5352
|
+
await doWrite();
|
|
5353
|
+
} else {
|
|
5354
|
+
await withStorageLock(this.directoryPath, doWrite);
|
|
5355
|
+
}
|
|
5227
5356
|
}
|
|
5228
5357
|
async loadSources(sourceIds) {
|
|
5229
|
-
const { readFile: readFile5 } = await
|
|
5358
|
+
const { readFile: readFile5 } = await nodeFs5();
|
|
5230
5359
|
const ids = sourceIds ?? await this.discover();
|
|
5231
5360
|
const sources = [];
|
|
5232
5361
|
for (const sourceId of ids) {
|
|
@@ -5316,7 +5445,7 @@ var init_mem_repository = __esm({
|
|
|
5316
5445
|
* valid package, so the backup is always a version this library can read.
|
|
5317
5446
|
*/
|
|
5318
5447
|
async keepLastValidVersion(filePath) {
|
|
5319
|
-
const { readFile: readFile5, copyFile, rename, rm } = await
|
|
5448
|
+
const { readFile: readFile5, copyFile, rename, rm } = await nodeFs5();
|
|
5320
5449
|
let current;
|
|
5321
5450
|
try {
|
|
5322
5451
|
current = await readFile5(filePath);
|
|
@@ -5340,7 +5469,7 @@ var init_mem_repository = __esm({
|
|
|
5340
5469
|
}
|
|
5341
5470
|
/** Best-effort directory sync: the rename is durable once the directory is. */
|
|
5342
5471
|
async syncDirectory(directoryPath) {
|
|
5343
|
-
const { open } = await
|
|
5472
|
+
const { open } = await nodeFs5();
|
|
5344
5473
|
try {
|
|
5345
5474
|
const handle = await open(directoryPath, "r");
|
|
5346
5475
|
try {
|
|
@@ -5352,7 +5481,7 @@ var init_mem_repository = __esm({
|
|
|
5352
5481
|
}
|
|
5353
5482
|
}
|
|
5354
5483
|
async writeSource(filePath, state3, preserved, preservedPackage) {
|
|
5355
|
-
const { mkdir, rename, rm, open, readFile: readFile5 } = await
|
|
5484
|
+
const { mkdir, rename, rm, open, readFile: readFile5 } = await nodeFs5();
|
|
5356
5485
|
if (!Number.isInteger(state3.version) || state3.version < 0) throw new Error("Invalid writable .mem knowledge version");
|
|
5357
5486
|
for (const claim of state3.claims) validateClaim(claim);
|
|
5358
5487
|
await mkdir(parentPath(filePath), { recursive: true });
|
|
@@ -5880,7 +6009,7 @@ var init_project_identity = __esm({
|
|
|
5880
6009
|
});
|
|
5881
6010
|
|
|
5882
6011
|
// core/knowledge/staging.ts
|
|
5883
|
-
var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor,
|
|
6012
|
+
var DOTMEM_STAGE_FORMAT, DOTMEM_STAGE_VERSION, StageFileError, generateRequestId, nodeCwd, resolveStagingDirectory, stagePathFor, nodeFs6, normalizeClaimIds, revisionFromOptions, writeStageFile, readStageFile, validateStageFile, commitStageFile, cleanupStageFile;
|
|
5884
6013
|
var init_staging = __esm({
|
|
5885
6014
|
"core/knowledge/staging.ts"() {
|
|
5886
6015
|
init_claim_record();
|
|
@@ -5915,7 +6044,7 @@ var init_staging = __esm({
|
|
|
5915
6044
|
return `${base}/dotmem`;
|
|
5916
6045
|
}, "resolveStagingDirectory");
|
|
5917
6046
|
stagePathFor = /* @__PURE__ */ __name((requestId) => `${resolveStagingDirectory()}/${requestId}.stage`, "stagePathFor");
|
|
5918
|
-
|
|
6047
|
+
nodeFs6 = /* @__PURE__ */ __name(async () => import('fs/promises'), "nodeFs");
|
|
5919
6048
|
normalizeClaimIds = /* @__PURE__ */ __name((value) => {
|
|
5920
6049
|
if (!value) return [];
|
|
5921
6050
|
return Array.isArray(value) ? value : [value];
|
|
@@ -5940,7 +6069,7 @@ var init_staging = __esm({
|
|
|
5940
6069
|
};
|
|
5941
6070
|
const requestId = generateRequestId();
|
|
5942
6071
|
const stagePath = stagePathFor(requestId);
|
|
5943
|
-
const fs = await
|
|
6072
|
+
const fs = await nodeFs6();
|
|
5944
6073
|
await fs.mkdir(resolveStagingDirectory(), { recursive: true });
|
|
5945
6074
|
const stageFile = {
|
|
5946
6075
|
format: DOTMEM_STAGE_FORMAT,
|
|
@@ -5960,7 +6089,7 @@ var init_staging = __esm({
|
|
|
5960
6089
|
}, "writeStageFile");
|
|
5961
6090
|
readStageFile = /* @__PURE__ */ __name(async (requestId) => {
|
|
5962
6091
|
const stagePath = stagePathFor(requestId);
|
|
5963
|
-
const fs = await
|
|
6092
|
+
const fs = await nodeFs6();
|
|
5964
6093
|
try {
|
|
5965
6094
|
const content = await fs.readFile(stagePath, "utf8");
|
|
5966
6095
|
return JSON.parse(content);
|
|
@@ -6071,7 +6200,7 @@ var init_staging = __esm({
|
|
|
6071
6200
|
}, "commitStageFile");
|
|
6072
6201
|
cleanupStageFile = /* @__PURE__ */ __name(async (requestId) => {
|
|
6073
6202
|
const stagePath = stagePathFor(requestId);
|
|
6074
|
-
const fs = await
|
|
6203
|
+
const fs = await nodeFs6();
|
|
6075
6204
|
try {
|
|
6076
6205
|
await fs.unlink(stagePath);
|
|
6077
6206
|
} catch (error) {
|
|
@@ -6842,6 +6971,7 @@ function _inferConfidence(claim) {
|
|
|
6842
6971
|
var LOGIC_MEM_FILE, nodeCwd2, cachedRuntime, LogicEngine, logic, logic_default;
|
|
6843
6972
|
var init_logic = __esm({
|
|
6844
6973
|
"core/logic/index.ts"() {
|
|
6974
|
+
init_suppress_idb_warning();
|
|
6845
6975
|
init_knowledge();
|
|
6846
6976
|
init_mem_repository();
|
|
6847
6977
|
init_acquisition_boundary();
|
|
@@ -7109,6 +7239,9 @@ Do not store secrets unless dotmem explicitly supports secure secret storage.
|
|
|
7109
7239
|
}
|
|
7110
7240
|
});
|
|
7111
7241
|
|
|
7242
|
+
// index.ts
|
|
7243
|
+
init_suppress_idb_warning();
|
|
7244
|
+
|
|
7112
7245
|
// core/idb.ts
|
|
7113
7246
|
var idb = {
|
|
7114
7247
|
db: {},
|
|
@@ -7448,7 +7581,11 @@ var security = {
|
|
|
7448
7581
|
createSanitizer
|
|
7449
7582
|
};
|
|
7450
7583
|
|
|
7584
|
+
// core/memory/index.ts
|
|
7585
|
+
init_suppress_idb_warning();
|
|
7586
|
+
|
|
7451
7587
|
// core/memory/journal.ts
|
|
7588
|
+
init_suppress_idb_warning();
|
|
7452
7589
|
init_env();
|
|
7453
7590
|
var g = globalThis;
|
|
7454
7591
|
function namespace() {
|
|
@@ -7655,6 +7792,7 @@ var journal_default = journal;
|
|
|
7655
7792
|
init_brain();
|
|
7656
7793
|
|
|
7657
7794
|
// core/memory/knowledge-runtime.ts
|
|
7795
|
+
init_suppress_idb_warning();
|
|
7658
7796
|
init_knowledge();
|
|
7659
7797
|
init_project_identity();
|
|
7660
7798
|
init_user_paths();
|
|
@@ -8003,6 +8141,7 @@ var policy = {
|
|
|
8003
8141
|
};
|
|
8004
8142
|
|
|
8005
8143
|
// core/memory/entries.ts
|
|
8144
|
+
init_suppress_idb_warning();
|
|
8006
8145
|
init_env();
|
|
8007
8146
|
var MEMORY_INDEX_KEY = "__dotmem_memory_index__";
|
|
8008
8147
|
var SUPERSEDED_PREFIX = "__dotmem_superseded__";
|
|
@@ -8547,15 +8686,18 @@ var context = {
|
|
|
8547
8686
|
};
|
|
8548
8687
|
|
|
8549
8688
|
// core/memory/acquire.ts
|
|
8689
|
+
init_suppress_idb_warning();
|
|
8550
8690
|
init_env();
|
|
8551
8691
|
init_project_identity();
|
|
8552
8692
|
init_project_anchor();
|
|
8553
8693
|
init_acquisition_boundary();
|
|
8554
8694
|
init_claim_record();
|
|
8695
|
+
init_state();
|
|
8555
8696
|
init_router();
|
|
8556
8697
|
init_store_queue();
|
|
8557
8698
|
init_staging();
|
|
8558
8699
|
init_user_paths();
|
|
8700
|
+
init_storage_coordinator();
|
|
8559
8701
|
init_mem_repository();
|
|
8560
8702
|
function normalizeClaimIds3(value) {
|
|
8561
8703
|
if (!value) return [];
|
|
@@ -8633,23 +8775,27 @@ var acquire = {
|
|
|
8633
8775
|
);
|
|
8634
8776
|
}
|
|
8635
8777
|
const revision = revisionFromAcquire2(opts);
|
|
8778
|
+
const repository = target.repository;
|
|
8636
8779
|
await withStoreQueue(acquisitionStoreKey(kind, scope, sourceProject), async () => {
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8780
|
+
await withStorageLock(repository.directoryPath, async () => {
|
|
8781
|
+
const state3 = await repository.load();
|
|
8782
|
+
const next = updateKnowledge(state3, {
|
|
8783
|
+
basedOnVersion: state3.version,
|
|
8784
|
+
claims: [buildClaimRecord({
|
|
8785
|
+
id: opts.id,
|
|
8786
|
+
knowledge: boundary.knowledge,
|
|
8787
|
+
applicability: opts.applicability,
|
|
8788
|
+
evidence: opts.evidence,
|
|
8789
|
+
epistemicType: opts.epistemicType,
|
|
8790
|
+
provenance: opts.provenance,
|
|
8791
|
+
acquiredAt: opts.acquiredAt,
|
|
8792
|
+
revision,
|
|
8793
|
+
scope,
|
|
8794
|
+
sourceProject,
|
|
8795
|
+
kind
|
|
8796
|
+
})]
|
|
8797
|
+
});
|
|
8798
|
+
await repository.save(next, state3.version, true);
|
|
8653
8799
|
});
|
|
8654
8800
|
});
|
|
8655
8801
|
const verification = await target.repository.load();
|
|
@@ -10190,6 +10336,442 @@ var saveContinuityDocument = /* @__PURE__ */ __name(async (path, document) => {
|
|
|
10190
10336
|
}
|
|
10191
10337
|
}, "saveContinuityDocument");
|
|
10192
10338
|
|
|
10339
|
+
// core/continuity-ledger/index.ts
|
|
10340
|
+
var EVENT_ID_PREFIX = "evt.";
|
|
10341
|
+
var LEDGER_APPLICABILITY = { area: "continuity", baseline: false };
|
|
10342
|
+
var EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
10343
|
+
"decision",
|
|
10344
|
+
"discovery",
|
|
10345
|
+
"correction",
|
|
10346
|
+
"conversation",
|
|
10347
|
+
"commitment",
|
|
10348
|
+
"open-loop",
|
|
10349
|
+
"project-change",
|
|
10350
|
+
"activity"
|
|
10351
|
+
]);
|
|
10352
|
+
var MEMORY_DOMAINS = /* @__PURE__ */ new Set(["environmental", "professional", "conversational", "activity"]);
|
|
10353
|
+
var EPITEMIC_STATUSES = /* @__PURE__ */ new Set([
|
|
10354
|
+
"verified-fact",
|
|
10355
|
+
"user-statement",
|
|
10356
|
+
"ai-inference",
|
|
10357
|
+
"hypothesis",
|
|
10358
|
+
"proposal",
|
|
10359
|
+
"decision",
|
|
10360
|
+
"verified-result"
|
|
10361
|
+
]);
|
|
10362
|
+
var CONFIDENCE_VALUES = /* @__PURE__ */ new Set(["high", "medium", "low", "unknown"]);
|
|
10363
|
+
var AUTHORS = /* @__PURE__ */ new Set(["human", "ai", "system"]);
|
|
10364
|
+
var CHANNELS = /* @__PURE__ */ new Set(["conversation", "acquisition", "tool", "migration"]);
|
|
10365
|
+
var SOURCE_KINDS = /* @__PURE__ */ new Set(["claim", "file", "commit", "tool-output", "conversation"]);
|
|
10366
|
+
var RELATIONSHIP_KEYS = [
|
|
10367
|
+
"refines",
|
|
10368
|
+
"supersedes",
|
|
10369
|
+
"retracts",
|
|
10370
|
+
"dependsOn",
|
|
10371
|
+
"resolves",
|
|
10372
|
+
"relatedTo"
|
|
10373
|
+
];
|
|
10374
|
+
var isRecord = /* @__PURE__ */ __name((value) => !!value && typeof value === "object" && !Array.isArray(value), "isRecord");
|
|
10375
|
+
var normalizeRelationships = /* @__PURE__ */ __name((value) => {
|
|
10376
|
+
const result = {
|
|
10377
|
+
refines: [],
|
|
10378
|
+
supersedes: [],
|
|
10379
|
+
retracts: [],
|
|
10380
|
+
dependsOn: [],
|
|
10381
|
+
resolves: [],
|
|
10382
|
+
relatedTo: []
|
|
10383
|
+
};
|
|
10384
|
+
if (!isRecord(value)) return result;
|
|
10385
|
+
for (const key of RELATIONSHIP_KEYS) {
|
|
10386
|
+
const raw = value[key];
|
|
10387
|
+
if (Array.isArray(raw)) {
|
|
10388
|
+
result[key] = raw.filter((item) => typeof item === "string");
|
|
10389
|
+
}
|
|
10390
|
+
}
|
|
10391
|
+
return result;
|
|
10392
|
+
}, "normalizeRelationships");
|
|
10393
|
+
var validateEvent = /* @__PURE__ */ __name((event) => {
|
|
10394
|
+
if (!isRecord(event)) throw new Error("Continuity event must be an object");
|
|
10395
|
+
if (event.schemaVersion !== 1) throw new Error("Unsupported continuity event schemaVersion");
|
|
10396
|
+
if (typeof event.eventId !== "string" || !event.eventId.startsWith(EVENT_ID_PREFIX)) {
|
|
10397
|
+
throw new Error("Continuity event requires a valid eventId");
|
|
10398
|
+
}
|
|
10399
|
+
if (typeof event.eventType !== "string" || !EVENT_TYPES.has(event.eventType)) {
|
|
10400
|
+
throw new Error("Continuity event requires a valid eventType");
|
|
10401
|
+
}
|
|
10402
|
+
if (typeof event.memoryDomain !== "string" || !MEMORY_DOMAINS.has(event.memoryDomain)) {
|
|
10403
|
+
throw new Error("Continuity event requires a valid memoryDomain");
|
|
10404
|
+
}
|
|
10405
|
+
if (typeof event.epistemicStatus !== "string" || !EPITEMIC_STATUSES.has(event.epistemicStatus)) {
|
|
10406
|
+
throw new Error("Continuity event requires a valid epistemicStatus");
|
|
10407
|
+
}
|
|
10408
|
+
if (typeof event.confidence !== "string" || !CONFIDENCE_VALUES.has(event.confidence)) {
|
|
10409
|
+
throw new Error("Continuity event requires a valid confidence");
|
|
10410
|
+
}
|
|
10411
|
+
if (typeof event.createdAt !== "string") throw new Error("Continuity event requires createdAt");
|
|
10412
|
+
if (typeof event.sessionId !== "string") throw new Error("Continuity event requires sessionId");
|
|
10413
|
+
if (event.projectId !== null && typeof event.projectId !== "string") {
|
|
10414
|
+
throw new Error("Continuity event requires projectId to be string or null");
|
|
10415
|
+
}
|
|
10416
|
+
if (typeof event.subject !== "string") throw new Error("Continuity event requires subject");
|
|
10417
|
+
if (!isRecord(event.content)) throw new Error("Continuity event requires content object");
|
|
10418
|
+
if (!Array.isArray(event.sourceRefs)) throw new Error("Continuity event requires sourceRefs array");
|
|
10419
|
+
for (const ref of event.sourceRefs) {
|
|
10420
|
+
if (!isRecord(ref) || typeof ref.kind !== "string" || !SOURCE_KINDS.has(ref.kind) || typeof ref.id !== "string") {
|
|
10421
|
+
throw new Error("Continuity event contains an invalid sourceRef");
|
|
10422
|
+
}
|
|
10423
|
+
}
|
|
10424
|
+
if (!isRecord(event.relationships)) throw new Error("Continuity event requires relationships object");
|
|
10425
|
+
if (!isRecord(event.provenance)) throw new Error("Continuity event requires provenance object");
|
|
10426
|
+
const provenance = event.provenance;
|
|
10427
|
+
if (typeof provenance.author !== "string" || !AUTHORS.has(provenance.author)) {
|
|
10428
|
+
throw new Error("Continuity event requires a valid provenance.author");
|
|
10429
|
+
}
|
|
10430
|
+
if (typeof provenance.origin !== "string") throw new Error("Continuity event requires provenance.origin");
|
|
10431
|
+
if (typeof provenance.channel !== "string" || !CHANNELS.has(provenance.channel)) {
|
|
10432
|
+
throw new Error("Continuity event requires a valid provenance.channel");
|
|
10433
|
+
}
|
|
10434
|
+
if (typeof provenance.recordedAt !== "string") throw new Error("Continuity event requires provenance.recordedAt");
|
|
10435
|
+
return {
|
|
10436
|
+
schemaVersion: 1,
|
|
10437
|
+
eventId: event.eventId,
|
|
10438
|
+
eventType: event.eventType,
|
|
10439
|
+
memoryDomain: event.memoryDomain,
|
|
10440
|
+
epistemicStatus: event.epistemicStatus,
|
|
10441
|
+
confidence: event.confidence,
|
|
10442
|
+
createdAt: event.createdAt,
|
|
10443
|
+
sessionId: event.sessionId,
|
|
10444
|
+
projectId: event.projectId,
|
|
10445
|
+
subject: event.subject,
|
|
10446
|
+
content: event.content,
|
|
10447
|
+
sourceRefs: event.sourceRefs,
|
|
10448
|
+
relationships: normalizeRelationships(event.relationships),
|
|
10449
|
+
provenance
|
|
10450
|
+
};
|
|
10451
|
+
}, "validateEvent");
|
|
10452
|
+
var isLedgerEvent = /* @__PURE__ */ __name((claim) => {
|
|
10453
|
+
return isRecord(claim.knowledge) && claim.knowledge.schemaVersion === 1 && typeof claim.knowledge.eventId === "string";
|
|
10454
|
+
}, "isLedgerEvent");
|
|
10455
|
+
var loadEvents = /* @__PURE__ */ __name(async () => {
|
|
10456
|
+
const state3 = await loadCombinedKnowledgeState();
|
|
10457
|
+
const events = [];
|
|
10458
|
+
for (const claim of state3.claims) {
|
|
10459
|
+
if (isLedgerEvent(claim)) {
|
|
10460
|
+
try {
|
|
10461
|
+
events.push(validateEvent(claim.knowledge));
|
|
10462
|
+
} catch {
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10465
|
+
}
|
|
10466
|
+
return events.sort((left, right) => left.eventId.localeCompare(right.eventId));
|
|
10467
|
+
}, "loadEvents");
|
|
10468
|
+
var findEvent = /* @__PURE__ */ __name((events, eventId) => events.find((event) => event.eventId === eventId), "findEvent");
|
|
10469
|
+
var epistemicTypeFor = /* @__PURE__ */ __name((status) => {
|
|
10470
|
+
if (status === "verified-fact" || status === "verified-result" || status === "decision") return "verified";
|
|
10471
|
+
if (status === "user-statement") return "human-stated";
|
|
10472
|
+
return "inferred";
|
|
10473
|
+
}, "epistemicTypeFor");
|
|
10474
|
+
var EVENT_ID_PATTERN = /^evt\.[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
10475
|
+
var validateEventId = /* @__PURE__ */ __name((eventId) => {
|
|
10476
|
+
if (!EVENT_ID_PATTERN.test(eventId)) throw new Error(`Invalid continuity event ID: ${eventId}`);
|
|
10477
|
+
}, "validateEventId");
|
|
10478
|
+
var hasRelationshipPath = /* @__PURE__ */ __name((events, startId, targetId, relationKey) => {
|
|
10479
|
+
const visited = /* @__PURE__ */ new Set();
|
|
10480
|
+
const stack = [startId];
|
|
10481
|
+
while (stack.length > 0) {
|
|
10482
|
+
const current = stack.pop();
|
|
10483
|
+
if (current === targetId) return true;
|
|
10484
|
+
if (visited.has(current)) continue;
|
|
10485
|
+
visited.add(current);
|
|
10486
|
+
const event = findEvent(events, current);
|
|
10487
|
+
if (!event) continue;
|
|
10488
|
+
for (const next of event.relationships[relationKey]) {
|
|
10489
|
+
if (!visited.has(next)) stack.push(next);
|
|
10490
|
+
}
|
|
10491
|
+
}
|
|
10492
|
+
return false;
|
|
10493
|
+
}, "hasRelationshipPath");
|
|
10494
|
+
var CYCLIC_RELATIONSHIP_KEYS = /* @__PURE__ */ new Set([
|
|
10495
|
+
"refines",
|
|
10496
|
+
"supersedes",
|
|
10497
|
+
"retracts",
|
|
10498
|
+
"dependsOn",
|
|
10499
|
+
"resolves",
|
|
10500
|
+
"relatedTo"
|
|
10501
|
+
]);
|
|
10502
|
+
var validateRelationshipTargets = /* @__PURE__ */ __name(async (event, existing) => {
|
|
10503
|
+
validateEventId(event.eventId);
|
|
10504
|
+
for (const key of RELATIONSHIP_KEYS) {
|
|
10505
|
+
for (const targetId of event.relationships[key]) {
|
|
10506
|
+
validateEventId(targetId);
|
|
10507
|
+
if (targetId === event.eventId) throw new Error(`Continuity event cannot reference itself: ${targetId}`);
|
|
10508
|
+
const target = findEvent(existing, targetId);
|
|
10509
|
+
if (!target) throw new Error(`Continuity relationship references a nonexistent event: ${targetId}`);
|
|
10510
|
+
if (target.projectId !== event.projectId) {
|
|
10511
|
+
throw new Error(`Cross-scope continuity relationship is not allowed: ${event.eventId} -> ${targetId}`);
|
|
10512
|
+
}
|
|
10513
|
+
if (CYCLIC_RELATIONSHIP_KEYS.has(key) && hasRelationshipPath(existing, targetId, event.eventId, key)) {
|
|
10514
|
+
throw new Error(`Continuity relationship would create a cycle: ${event.eventId} -> ${targetId}`);
|
|
10515
|
+
}
|
|
10516
|
+
}
|
|
10517
|
+
}
|
|
10518
|
+
}, "validateRelationshipTargets");
|
|
10519
|
+
var validateResolutionRequest = /* @__PURE__ */ __name(async (resolution, existing) => {
|
|
10520
|
+
validateEventId(resolution.resolutionEventId);
|
|
10521
|
+
if (!Array.isArray(resolution.conflictingEventIds) || resolution.conflictingEventIds.length === 0) {
|
|
10522
|
+
throw new Error("Conflict resolution requires at least one conflicting event");
|
|
10523
|
+
}
|
|
10524
|
+
if (typeof resolution.selectedEventId !== "string" || !resolution.selectedEventId) {
|
|
10525
|
+
throw new Error("Conflict resolution requires a selected event");
|
|
10526
|
+
}
|
|
10527
|
+
if (typeof resolution.reason !== "string" || !resolution.reason.trim()) {
|
|
10528
|
+
throw new Error("Conflict resolution requires a reason");
|
|
10529
|
+
}
|
|
10530
|
+
const conflicting = resolution.conflictingEventIds.map((id) => {
|
|
10531
|
+
validateEventId(id);
|
|
10532
|
+
const event = findEvent(existing, id);
|
|
10533
|
+
if (!event) throw new Error(`Conflict resolution references a nonexistent event: ${id}`);
|
|
10534
|
+
if (event.relationships.supersedes.length !== 1) {
|
|
10535
|
+
throw new Error(`Conflict resolution references a non-supersession event: ${id}`);
|
|
10536
|
+
}
|
|
10537
|
+
return event;
|
|
10538
|
+
});
|
|
10539
|
+
const targets = new Set(conflicting.map((event) => event.relationships.supersedes[0]));
|
|
10540
|
+
if (targets.size !== 1) {
|
|
10541
|
+
throw new Error("Conflict resolution references supersession events targeting different decisions");
|
|
10542
|
+
}
|
|
10543
|
+
const targetId = [...targets][0];
|
|
10544
|
+
const knownCompeting = supersedingEvents(existing, targetId).map((event) => event.eventId).sort();
|
|
10545
|
+
const requested = [...resolution.conflictingEventIds].sort();
|
|
10546
|
+
if (requested.length !== knownCompeting.length || requested.some((id, index) => id !== knownCompeting[index])) {
|
|
10547
|
+
throw new Error("Conflict resolution must include every competing supersession event");
|
|
10548
|
+
}
|
|
10549
|
+
if (!resolution.conflictingEventIds.includes(resolution.selectedEventId)) {
|
|
10550
|
+
throw new Error("Conflict resolution selectedEventId must be one of the conflicting events");
|
|
10551
|
+
}
|
|
10552
|
+
const existingResolutions = existing.filter(
|
|
10553
|
+
(event) => event.relationships.resolves.includes(targetId) || resolution.conflictingEventIds.some((id) => event.relationships.resolves.includes(id))
|
|
10554
|
+
);
|
|
10555
|
+
if (existingResolutions.length > 0) {
|
|
10556
|
+
const selectedOutcomes = new Set(existingResolutions.map((event) => {
|
|
10557
|
+
const content = isRecord(event.content) ? event.content : {};
|
|
10558
|
+
return typeof content.selectedEventId === "string" ? content.selectedEventId : null;
|
|
10559
|
+
}).filter((value) => value !== null));
|
|
10560
|
+
if (existingResolutions.some((event) => event.eventId === resolution.resolutionEventId)) {
|
|
10561
|
+
throw new Error(`Duplicate continuity resolution event: ${resolution.resolutionEventId}`);
|
|
10562
|
+
}
|
|
10563
|
+
if (selectedOutcomes.size > 0 && !selectedOutcomes.has(resolution.selectedEventId)) {
|
|
10564
|
+
throw new Error("Incompatible continuity conflict resolution");
|
|
10565
|
+
}
|
|
10566
|
+
throw new Error("Continuity conflict is already resolved");
|
|
10567
|
+
}
|
|
10568
|
+
const resolutionEventId = resolution.resolutionEventId;
|
|
10569
|
+
for (const conflictingId of resolution.conflictingEventIds) {
|
|
10570
|
+
const event = findEvent(existing, conflictingId);
|
|
10571
|
+
if (event && hasRelationshipPath(existing, conflictingId, resolutionEventId, "resolves")) {
|
|
10572
|
+
throw new Error(`Continuity resolution would create a cycle: ${resolutionEventId}`);
|
|
10573
|
+
}
|
|
10574
|
+
}
|
|
10575
|
+
}, "validateResolutionRequest");
|
|
10576
|
+
var recordEventOnce = /* @__PURE__ */ __name(async (event) => {
|
|
10577
|
+
const validated = validateEvent(event);
|
|
10578
|
+
const existing = await loadEvents();
|
|
10579
|
+
await validateRelationshipTargets(validated, existing);
|
|
10580
|
+
if (existing.some((item) => item.eventId === validated.eventId)) {
|
|
10581
|
+
throw new Error(`Continuity event already exists: ${validated.eventId}`);
|
|
10582
|
+
}
|
|
10583
|
+
const scope = validated.projectId ? "project" : "user";
|
|
10584
|
+
const evidence = validated.sourceRefs.length > 0 ? validated.sourceRefs.map((ref) => ({
|
|
10585
|
+
id: ref.id,
|
|
10586
|
+
source: ref.kind,
|
|
10587
|
+
observedAt: validated.createdAt,
|
|
10588
|
+
supports: validated.eventId
|
|
10589
|
+
})) : [{ id: validated.eventId, source: "continuity-ledger", observedAt: validated.createdAt, supports: validated.eventId }];
|
|
10590
|
+
await memory.acquire({
|
|
10591
|
+
id: validated.eventId,
|
|
10592
|
+
knowledge: validated,
|
|
10593
|
+
applicability: { ...LEDGER_APPLICABILITY, topic: validated.eventType },
|
|
10594
|
+
evidence,
|
|
10595
|
+
epistemicType: epistemicTypeFor(validated.epistemicStatus),
|
|
10596
|
+
provenance: {
|
|
10597
|
+
source: validated.provenance.origin,
|
|
10598
|
+
detail: validated.provenance.channel
|
|
10599
|
+
},
|
|
10600
|
+
scope,
|
|
10601
|
+
sourceProject: validated.projectId ?? void 0,
|
|
10602
|
+
kind: "experience"
|
|
10603
|
+
});
|
|
10604
|
+
}, "recordEventOnce");
|
|
10605
|
+
var recordEvent = /* @__PURE__ */ __name(async (event) => {
|
|
10606
|
+
let lastError;
|
|
10607
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
10608
|
+
try {
|
|
10609
|
+
await recordEventOnce(event);
|
|
10610
|
+
return;
|
|
10611
|
+
} catch (error) {
|
|
10612
|
+
lastError = error;
|
|
10613
|
+
if (error instanceof Error && /Stale update/i.test(error.message) && attempt < 9) {
|
|
10614
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
10615
|
+
continue;
|
|
10616
|
+
}
|
|
10617
|
+
throw error;
|
|
10618
|
+
}
|
|
10619
|
+
}
|
|
10620
|
+
throw lastError instanceof Error ? lastError : new Error("Continuity event recording failed");
|
|
10621
|
+
}, "recordEvent");
|
|
10622
|
+
var getEvent = /* @__PURE__ */ __name(async (eventId) => {
|
|
10623
|
+
const events = await loadEvents();
|
|
10624
|
+
return findEvent(events, eventId) ?? null;
|
|
10625
|
+
}, "getEvent");
|
|
10626
|
+
var listEvents = /* @__PURE__ */ __name(async (filter = {}) => {
|
|
10627
|
+
const events = await loadEvents();
|
|
10628
|
+
return events.filter((event) => {
|
|
10629
|
+
if (filter.eventId && event.eventId !== filter.eventId) return false;
|
|
10630
|
+
if (filter.eventType && event.eventType !== filter.eventType) return false;
|
|
10631
|
+
if (filter.memoryDomain && event.memoryDomain !== filter.memoryDomain) return false;
|
|
10632
|
+
if (filter.sessionId && event.sessionId !== filter.sessionId) return false;
|
|
10633
|
+
if (filter.projectId !== void 0 && event.projectId !== filter.projectId) return false;
|
|
10634
|
+
if (filter.epistemicStatus && event.epistemicStatus !== filter.epistemicStatus) return false;
|
|
10635
|
+
return true;
|
|
10636
|
+
});
|
|
10637
|
+
}, "listEvents");
|
|
10638
|
+
var supersedingEvents = /* @__PURE__ */ __name((events, eventId) => events.filter((event) => event.relationships.supersedes.includes(eventId)), "supersedingEvents");
|
|
10639
|
+
var retractingEvents = /* @__PURE__ */ __name((events, eventId) => events.filter((event) => event.relationships.retracts.includes(eventId)), "retractingEvents");
|
|
10640
|
+
var resolutionEventsFor = /* @__PURE__ */ __name((events, eventId, conflictingIds) => events.filter(
|
|
10641
|
+
(event) => event.relationships.resolves.includes(eventId) || conflictingIds.some((id) => event.relationships.resolves.includes(id))
|
|
10642
|
+
), "resolutionEventsFor");
|
|
10643
|
+
var deriveValidity = /* @__PURE__ */ __name(async (eventId) => {
|
|
10644
|
+
let events = await loadEvents();
|
|
10645
|
+
for (let stabilityAttempt = 0; stabilityAttempt < 5; stabilityAttempt += 1) {
|
|
10646
|
+
const nextEvents = await loadEvents();
|
|
10647
|
+
if (nextEvents.length === events.length) break;
|
|
10648
|
+
events = nextEvents;
|
|
10649
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
10650
|
+
}
|
|
10651
|
+
const supersededBy = supersedingEvents(events, eventId).map((event) => event.eventId);
|
|
10652
|
+
const retractedBy = retractingEvents(events, eventId).map((event) => event.eventId);
|
|
10653
|
+
const contestedBy = supersededBy.length > 1 ? supersededBy : [];
|
|
10654
|
+
const resolvedBy = resolutionEventsFor(events, eventId, contestedBy).map((event) => event.eventId);
|
|
10655
|
+
let state3 = "current";
|
|
10656
|
+
if (retractedBy.length > 0) state3 = "retracted";
|
|
10657
|
+
else if (supersededBy.length > 0) state3 = supersededBy.length > 1 && resolvedBy.length === 0 ? "contested" : "superseded";
|
|
10658
|
+
else if (events.some((event) => event.eventId === eventId && event.eventType === "decision") === false) state3 = "current";
|
|
10659
|
+
if (supersededBy.length > 1 && resolvedBy.length === 0) state3 = "contested";
|
|
10660
|
+
return {
|
|
10661
|
+
eventId,
|
|
10662
|
+
state: state3,
|
|
10663
|
+
supersededBy,
|
|
10664
|
+
retractedBy,
|
|
10665
|
+
contestedBy,
|
|
10666
|
+
resolvedBy
|
|
10667
|
+
};
|
|
10668
|
+
}, "deriveValidity");
|
|
10669
|
+
var listContested = /* @__PURE__ */ __name(async () => {
|
|
10670
|
+
const events = await loadEvents();
|
|
10671
|
+
const targets = /* @__PURE__ */ new Set();
|
|
10672
|
+
for (const event of events) {
|
|
10673
|
+
for (const target of event.relationships.supersedes) targets.add(target);
|
|
10674
|
+
}
|
|
10675
|
+
const contested = [];
|
|
10676
|
+
for (const target of targets) {
|
|
10677
|
+
const supersededBy = supersedingEvents(events, target);
|
|
10678
|
+
if (supersededBy.length <= 1) continue;
|
|
10679
|
+
const conflictingIds = supersededBy.map((event) => event.eventId);
|
|
10680
|
+
const resolvedBy = resolutionEventsFor(events, target, conflictingIds);
|
|
10681
|
+
if (resolvedBy.length === 0) contested.push(target);
|
|
10682
|
+
}
|
|
10683
|
+
return contested.sort();
|
|
10684
|
+
}, "listContested");
|
|
10685
|
+
var resolveConflictOnce = /* @__PURE__ */ __name(async (resolution) => {
|
|
10686
|
+
const existing = await loadEvents();
|
|
10687
|
+
await validateResolutionRequest(resolution, existing);
|
|
10688
|
+
const resolutionEvent = {
|
|
10689
|
+
schemaVersion: 1,
|
|
10690
|
+
eventId: resolution.resolutionEventId,
|
|
10691
|
+
eventType: "correction",
|
|
10692
|
+
memoryDomain: "professional",
|
|
10693
|
+
epistemicStatus: "decision",
|
|
10694
|
+
confidence: "high",
|
|
10695
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10696
|
+
sessionId: "resolution",
|
|
10697
|
+
projectId: null,
|
|
10698
|
+
subject: "Continuity conflict resolution",
|
|
10699
|
+
content: {
|
|
10700
|
+
selectedEventId: resolution.selectedEventId,
|
|
10701
|
+
reason: resolution.reason
|
|
10702
|
+
},
|
|
10703
|
+
sourceRefs: resolution.conflictingEventIds.map((id) => ({ kind: "claim", id })),
|
|
10704
|
+
relationships: {
|
|
10705
|
+
refines: [],
|
|
10706
|
+
supersedes: [],
|
|
10707
|
+
retracts: [],
|
|
10708
|
+
dependsOn: [],
|
|
10709
|
+
resolves: [...resolution.conflictingEventIds],
|
|
10710
|
+
relatedTo: [resolution.selectedEventId]
|
|
10711
|
+
},
|
|
10712
|
+
provenance: {
|
|
10713
|
+
author: "system",
|
|
10714
|
+
origin: "continuity-ledger",
|
|
10715
|
+
channel: "acquisition",
|
|
10716
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10717
|
+
}
|
|
10718
|
+
};
|
|
10719
|
+
await recordEvent(resolutionEvent);
|
|
10720
|
+
}, "resolveConflictOnce");
|
|
10721
|
+
var reconstructValidity = /* @__PURE__ */ __name(async () => {
|
|
10722
|
+
const events = await loadEvents();
|
|
10723
|
+
const result = [];
|
|
10724
|
+
const targets = new Set(events.map((event) => event.eventId));
|
|
10725
|
+
for (const event of events) {
|
|
10726
|
+
for (const target of event.relationships.supersedes) targets.add(target);
|
|
10727
|
+
for (const target of event.relationships.retracts) targets.add(target);
|
|
10728
|
+
}
|
|
10729
|
+
for (const target of targets) {
|
|
10730
|
+
result.push(await deriveValidity(target));
|
|
10731
|
+
}
|
|
10732
|
+
return result.sort((left, right) => left.eventId.localeCompare(right.eventId));
|
|
10733
|
+
}, "reconstructValidity");
|
|
10734
|
+
var resolveConflict = /* @__PURE__ */ __name(async (resolution) => {
|
|
10735
|
+
let lastError;
|
|
10736
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
10737
|
+
try {
|
|
10738
|
+
await resolveConflictOnce(resolution);
|
|
10739
|
+
return;
|
|
10740
|
+
} catch (error) {
|
|
10741
|
+
lastError = error;
|
|
10742
|
+
if (error instanceof Error && /Stale update/i.test(error.message) && attempt < 9) {
|
|
10743
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
10744
|
+
continue;
|
|
10745
|
+
}
|
|
10746
|
+
throw error;
|
|
10747
|
+
}
|
|
10748
|
+
}
|
|
10749
|
+
throw lastError instanceof Error ? lastError : new Error("Continuity conflict resolution failed");
|
|
10750
|
+
}, "resolveConflict");
|
|
10751
|
+
var validateRelationships = validateRelationshipTargets;
|
|
10752
|
+
var continuityLedger = {
|
|
10753
|
+
recordEvent,
|
|
10754
|
+
record: recordEvent,
|
|
10755
|
+
append: recordEvent,
|
|
10756
|
+
add: recordEvent,
|
|
10757
|
+
getEvent,
|
|
10758
|
+
get: getEvent,
|
|
10759
|
+
find: getEvent,
|
|
10760
|
+
listEvents,
|
|
10761
|
+
list: listEvents,
|
|
10762
|
+
query: listEvents,
|
|
10763
|
+
deriveValidity,
|
|
10764
|
+
validity: deriveValidity,
|
|
10765
|
+
listContested,
|
|
10766
|
+
contested: listContested,
|
|
10767
|
+
resolveConflict,
|
|
10768
|
+
resolve: resolveConflict,
|
|
10769
|
+
reconstructValidity,
|
|
10770
|
+
validateRelationships,
|
|
10771
|
+
validateRelationshipTargets,
|
|
10772
|
+
validateResolution: validateResolutionRequest
|
|
10773
|
+
};
|
|
10774
|
+
|
|
10193
10775
|
// core/continuity/index.ts
|
|
10194
10776
|
var continuity = {
|
|
10195
10777
|
async context(options) {
|
|
@@ -12018,6 +12600,1232 @@ var createCompactionEngine = /* @__PURE__ */ __name((options) => new CompactionE
|
|
|
12018
12600
|
// index.ts
|
|
12019
12601
|
init_user_identity();
|
|
12020
12602
|
init_user_paths();
|
|
12603
|
+
|
|
12604
|
+
// core/governance/index.ts
|
|
12605
|
+
init_user_paths();
|
|
12606
|
+
init_policy();
|
|
12607
|
+
init_policy_namespace_lock();
|
|
12608
|
+
init_detection_events();
|
|
12609
|
+
init_policy();
|
|
12610
|
+
var GOVERNANCE_FILE_NAME = "governance.policy.json";
|
|
12611
|
+
var GOVERNANCE_FORMAT = "dotmem.governance-policy";
|
|
12612
|
+
var GOVERNANCE_VERSION = 1;
|
|
12613
|
+
var GOVERNANCE_AUDIT_RETENTION = 200;
|
|
12614
|
+
var GovernancePolicyError = class extends Error {
|
|
12615
|
+
static {
|
|
12616
|
+
__name(this, "GovernancePolicyError");
|
|
12617
|
+
}
|
|
12618
|
+
constructor(message) {
|
|
12619
|
+
super(message);
|
|
12620
|
+
this.name = "GovernancePolicyError";
|
|
12621
|
+
}
|
|
12622
|
+
};
|
|
12623
|
+
var requiredText3 = /* @__PURE__ */ __name((v) => typeof v === "string" && v.trim().length > 0, "requiredText");
|
|
12624
|
+
var nowIso2 = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString(), "nowIso");
|
|
12625
|
+
var joinPath7 = /* @__PURE__ */ __name((base, child) => {
|
|
12626
|
+
const sep = process.platform === "win32" ? "\\" : "/";
|
|
12627
|
+
return `${base.replace(/[\\/]+/g, sep)}${sep}${child}`;
|
|
12628
|
+
}, "joinPath");
|
|
12629
|
+
var resolveGovernanceDir = /* @__PURE__ */ __name((directory) => directory ?? joinPath7(resolveUserDotMemPath(), "policy"), "resolveGovernanceDir");
|
|
12630
|
+
var resolveGovernanceFilePath = /* @__PURE__ */ __name((directory) => joinPath7(resolveGovernanceDir(directory), GOVERNANCE_FILE_NAME), "resolveGovernanceFilePath");
|
|
12631
|
+
var ROLE_CAPABILITIES = {
|
|
12632
|
+
"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"]),
|
|
12633
|
+
"human-approver": /* @__PURE__ */ new Set(["policy.review", "policy.approve", "policy.reject", "governance.read", "governance.audit.read", "guardrail.stop.activate", "guardrail.stop.release"]),
|
|
12634
|
+
"coordinator-jo": /* @__PURE__ */ new Set(["policy.propose", "policy.review", "policy.activate", "governance.read", "governance.audit.read", "agent.execute"]),
|
|
12635
|
+
"specialist-agent": /* @__PURE__ */ new Set(["policy.propose", "governance.read"]),
|
|
12636
|
+
"policy-authority": /* @__PURE__ */ new Set(["policy.propose", "policy.review", "policy.activate", "governance.read", "governance.audit.read", "agent.execute"]),
|
|
12637
|
+
"auditor-reviewer": /* @__PURE__ */ new Set(["governance.read", "governance.audit.read"])
|
|
12638
|
+
};
|
|
12639
|
+
var defaultRoleBindings = /* @__PURE__ */ __name(() => [
|
|
12640
|
+
{ principalId: "hi", role: "human-owner", scope: "*", grantedBy: "system", grantedAt: (/* @__PURE__ */ new Date(0)).toISOString() },
|
|
12641
|
+
{ principalId: "human-owner", role: "human-owner", scope: "*", grantedBy: "system", grantedAt: (/* @__PURE__ */ new Date(0)).toISOString() },
|
|
12642
|
+
{ principalId: "jo", role: "coordinator-jo", scope: "*", grantedBy: "system", grantedAt: (/* @__PURE__ */ new Date(0)).toISOString() }
|
|
12643
|
+
], "defaultRoleBindings");
|
|
12644
|
+
var emptyDocument2 = /* @__PURE__ */ __name(() => ({
|
|
12645
|
+
format: GOVERNANCE_FORMAT,
|
|
12646
|
+
version: GOVERNANCE_VERSION,
|
|
12647
|
+
revision: 0,
|
|
12648
|
+
policies: [],
|
|
12649
|
+
roleBindings: defaultRoleBindings(),
|
|
12650
|
+
audit: []
|
|
12651
|
+
}), "emptyDocument");
|
|
12652
|
+
var validateAuthority = /* @__PURE__ */ __name((value) => {
|
|
12653
|
+
try {
|
|
12654
|
+
return validatePolicyAuthority(value);
|
|
12655
|
+
} catch (err2) {
|
|
12656
|
+
void detectAuthorityEscalationAttempt({ claimed: "invalid governance authority", channel: "governance" }).catch(() => void 0);
|
|
12657
|
+
throw err2 instanceof GovernancePolicyError ? err2 : new GovernancePolicyError(err2.message);
|
|
12658
|
+
}
|
|
12659
|
+
}, "validateAuthority");
|
|
12660
|
+
var buildAudit = /* @__PURE__ */ __name((entry) => ({
|
|
12661
|
+
id: entry.id ?? `gov-audit.${entry.action}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`,
|
|
12662
|
+
action: entry.action,
|
|
12663
|
+
at: entry.at,
|
|
12664
|
+
...entry.actorPrincipal !== void 0 ? { actorPrincipal: entry.actorPrincipal } : {},
|
|
12665
|
+
...entry.actorRole !== void 0 ? { actorRole: entry.actorRole } : {},
|
|
12666
|
+
...entry.targetPolicyId !== void 0 ? { targetPolicyId: entry.targetPolicyId } : {},
|
|
12667
|
+
...entry.targetVersion !== void 0 ? { targetVersion: entry.targetVersion } : {},
|
|
12668
|
+
...entry.scope !== void 0 ? { scope: entry.scope } : {},
|
|
12669
|
+
outcome: entry.outcome,
|
|
12670
|
+
...entry.reason !== void 0 ? { reason: entry.reason } : {},
|
|
12671
|
+
...entry.detail !== void 0 ? { detail: entry.detail } : {},
|
|
12672
|
+
...entry.correlationId !== void 0 ? { correlationId: entry.correlationId } : {},
|
|
12673
|
+
...entry.mandatory !== void 0 ? { mandatory: entry.mandatory } : {}
|
|
12674
|
+
}), "buildAudit");
|
|
12675
|
+
var boundedAudit2 = /* @__PURE__ */ __name((entries2) => {
|
|
12676
|
+
if (entries2.length <= GOVERNANCE_AUDIT_RETENTION) return [...entries2];
|
|
12677
|
+
const kept = [];
|
|
12678
|
+
let budget = GOVERNANCE_AUDIT_RETENTION;
|
|
12679
|
+
for (let i2 = entries2.length - 1; i2 >= 0; i2--) {
|
|
12680
|
+
const e = entries2[i2];
|
|
12681
|
+
const mandatory = e.mandatory === true;
|
|
12682
|
+
if (mandatory || budget > 0) {
|
|
12683
|
+
kept.unshift(e);
|
|
12684
|
+
if (!mandatory) budget--;
|
|
12685
|
+
}
|
|
12686
|
+
}
|
|
12687
|
+
return kept;
|
|
12688
|
+
}, "boundedAudit");
|
|
12689
|
+
var validatePolicy = /* @__PURE__ */ __name((p) => {
|
|
12690
|
+
if (!p || typeof p !== "object" || Array.isArray(p)) throw new GovernancePolicyError("Governance policy must be an object");
|
|
12691
|
+
const r = p;
|
|
12692
|
+
if (!requiredText3(r.policyId)) throw new GovernancePolicyError("policyId is required");
|
|
12693
|
+
if (typeof r.version !== "number" || !Number.isInteger(r.version) || r.version < 1) throw new GovernancePolicyError("version must be a positive integer");
|
|
12694
|
+
if (!requiredText3(r.scope)) throw new GovernancePolicyError("scope is required");
|
|
12695
|
+
if (!requiredText3(r.rationale)) throw new GovernancePolicyError("rationale is required");
|
|
12696
|
+
if (!requiredText3(r.proposedBy)) throw new GovernancePolicyError("proposedBy is required");
|
|
12697
|
+
if (!requiredText3(r.proposedAt)) throw new GovernancePolicyError("proposedAt is required");
|
|
12698
|
+
if (typeof r.status !== "string" || !["proposed", "under_review", "approved", "active", "rejected", "suspended", "retired"].includes(r.status)) {
|
|
12699
|
+
throw new GovernancePolicyError("invalid status");
|
|
12700
|
+
}
|
|
12701
|
+
if (typeof r.revision !== "number" || !Number.isInteger(r.revision) || r.revision < 0) throw new GovernancePolicyError("revision must be a non-negative integer");
|
|
12702
|
+
if (!Array.isArray(r.applicablePrincipals)) throw new GovernancePolicyError("applicablePrincipals must be an array");
|
|
12703
|
+
if (!Array.isArray(r.applicableResources)) throw new GovernancePolicyError("applicableResources must be an array");
|
|
12704
|
+
const authority = validateAuthority(r.ownerAuthority);
|
|
12705
|
+
return {
|
|
12706
|
+
policyId: r.policyId,
|
|
12707
|
+
version: r.version,
|
|
12708
|
+
...requiredText3(r.title) ? { title: r.title } : {},
|
|
12709
|
+
...requiredText3(r.description) ? { description: r.description } : {},
|
|
12710
|
+
scope: r.scope,
|
|
12711
|
+
applicablePrincipals: r.applicablePrincipals,
|
|
12712
|
+
applicableResources: r.applicableResources,
|
|
12713
|
+
...requiredText3(r.effectiveAt) ? { effectiveAt: r.effectiveAt } : {},
|
|
12714
|
+
...requiredText3(r.expiresAt) ? { expiresAt: r.expiresAt } : {},
|
|
12715
|
+
rationale: r.rationale,
|
|
12716
|
+
status: r.status,
|
|
12717
|
+
ownerAuthority: authority,
|
|
12718
|
+
proposedBy: r.proposedBy,
|
|
12719
|
+
proposedAt: r.proposedAt,
|
|
12720
|
+
...requiredText3(r.reviewedBy) ? { reviewedBy: r.reviewedBy } : {},
|
|
12721
|
+
...requiredText3(r.reviewedAt) ? { reviewedAt: r.reviewedAt } : {},
|
|
12722
|
+
...r.approval && typeof r.approval === "object" ? { approval: r.approval } : {},
|
|
12723
|
+
...requiredText3(r.activatedBy) ? { activatedBy: r.activatedBy } : {},
|
|
12724
|
+
...requiredText3(r.activatedAt) ? { activatedAt: r.activatedAt } : {},
|
|
12725
|
+
...requiredText3(r.suspendedBy) ? { suspendedBy: r.suspendedBy } : {},
|
|
12726
|
+
...requiredText3(r.suspendedAt) ? { suspendedAt: r.suspendedAt } : {},
|
|
12727
|
+
...requiredText3(r.retiredBy) ? { retiredBy: r.retiredBy } : {},
|
|
12728
|
+
...requiredText3(r.retiredAt) ? { retiredAt: r.retiredAt } : {},
|
|
12729
|
+
...requiredText3(r.supersedes) ? { supersedes: r.supersedes } : {},
|
|
12730
|
+
...requiredText3(r.supersededBy) ? { supersededBy: r.supersededBy } : {},
|
|
12731
|
+
...requiredText3(r.correlationId) ? { correlationId: r.correlationId } : {},
|
|
12732
|
+
revision: r.revision,
|
|
12733
|
+
...requiredText3(r.fingerprint) ? { fingerprint: r.fingerprint } : {}
|
|
12734
|
+
};
|
|
12735
|
+
}, "validatePolicy");
|
|
12736
|
+
var validateRoleBinding = /* @__PURE__ */ __name((rb) => {
|
|
12737
|
+
if (!rb || typeof rb !== "object" || Array.isArray(rb)) throw new GovernancePolicyError("role binding must be an object");
|
|
12738
|
+
const r = rb;
|
|
12739
|
+
if (!requiredText3(r.principalId)) throw new GovernancePolicyError("principalId is required");
|
|
12740
|
+
if (typeof r.role !== "string" || !["human-owner", "human-approver", "coordinator-jo", "specialist-agent", "policy-authority", "auditor-reviewer"].includes(r.role)) {
|
|
12741
|
+
throw new GovernancePolicyError("invalid role");
|
|
12742
|
+
}
|
|
12743
|
+
if (!requiredText3(r.scope)) throw new GovernancePolicyError("scope is required");
|
|
12744
|
+
return {
|
|
12745
|
+
principalId: r.principalId,
|
|
12746
|
+
role: r.role,
|
|
12747
|
+
scope: r.scope,
|
|
12748
|
+
...requiredText3(r.grantedBy) ? { grantedBy: r.grantedBy } : {},
|
|
12749
|
+
...requiredText3(r.grantedAt) ? { grantedAt: r.grantedAt } : {}
|
|
12750
|
+
};
|
|
12751
|
+
}, "validateRoleBinding");
|
|
12752
|
+
var validateDocument2 = /* @__PURE__ */ __name((value) => {
|
|
12753
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new GovernancePolicyError("document must be an object");
|
|
12754
|
+
const d = value;
|
|
12755
|
+
if (d.format !== GOVERNANCE_FORMAT) throw new GovernancePolicyError("invalid format");
|
|
12756
|
+
if (d.version !== GOVERNANCE_VERSION) throw new GovernancePolicyError("invalid version");
|
|
12757
|
+
if (typeof d.revision !== "number" || !Number.isInteger(d.revision) || d.revision < 0) throw new GovernancePolicyError("invalid revision");
|
|
12758
|
+
if (!Array.isArray(d.policies)) throw new GovernancePolicyError("policies must be an array");
|
|
12759
|
+
if (!Array.isArray(d.roleBindings)) throw new GovernancePolicyError("roleBindings must be an array");
|
|
12760
|
+
if (!Array.isArray(d.audit)) throw new GovernancePolicyError("audit must be an array");
|
|
12761
|
+
const policies = d.policies.map(validatePolicy);
|
|
12762
|
+
const roleBindings = d.roleBindings.map(validateRoleBinding);
|
|
12763
|
+
const audit = d.audit.map((e) => {
|
|
12764
|
+
if (!e || typeof e !== "object" || Array.isArray(e)) throw new GovernancePolicyError("audit entry must be an object");
|
|
12765
|
+
const r = e;
|
|
12766
|
+
if (!requiredText3(r.id)) throw new GovernancePolicyError("audit id required");
|
|
12767
|
+
if (!requiredText3(r.action)) throw new GovernancePolicyError("audit action required");
|
|
12768
|
+
if (!requiredText3(r.at)) throw new GovernancePolicyError("audit at required");
|
|
12769
|
+
if (r.outcome !== "applied" && r.outcome !== "rejected") throw new GovernancePolicyError("audit outcome invalid");
|
|
12770
|
+
return r;
|
|
12771
|
+
});
|
|
12772
|
+
return { format: GOVERNANCE_FORMAT, version: GOVERNANCE_VERSION, revision: d.revision, policies, roleBindings, audit };
|
|
12773
|
+
}, "validateDocument");
|
|
12774
|
+
var scopeCovers = /* @__PURE__ */ __name((bindingScope, targetScope) => {
|
|
12775
|
+
if (bindingScope === "*") return true;
|
|
12776
|
+
if (bindingScope === targetScope) return true;
|
|
12777
|
+
return targetScope.startsWith(bindingScope.endsWith("/") ? bindingScope : bindingScope + "/");
|
|
12778
|
+
}, "scopeCovers");
|
|
12779
|
+
var GovernancePolicyStore = class {
|
|
12780
|
+
constructor(directory) {
|
|
12781
|
+
this.directory = directory;
|
|
12782
|
+
}
|
|
12783
|
+
directory;
|
|
12784
|
+
static {
|
|
12785
|
+
__name(this, "GovernancePolicyStore");
|
|
12786
|
+
}
|
|
12787
|
+
dir() {
|
|
12788
|
+
return resolveGovernanceDir(this.directory);
|
|
12789
|
+
}
|
|
12790
|
+
filePath() {
|
|
12791
|
+
return resolveGovernanceFilePath(this.directory);
|
|
12792
|
+
}
|
|
12793
|
+
async load() {
|
|
12794
|
+
const fs = await import('fs/promises');
|
|
12795
|
+
let raw;
|
|
12796
|
+
try {
|
|
12797
|
+
raw = await fs.readFile(this.filePath(), "utf8");
|
|
12798
|
+
} catch (err2) {
|
|
12799
|
+
if (err2.code === "ENOENT") return emptyDocument2();
|
|
12800
|
+
throw err2;
|
|
12801
|
+
}
|
|
12802
|
+
return validateDocument2(JSON.parse(raw));
|
|
12803
|
+
}
|
|
12804
|
+
async readDoc(fs) {
|
|
12805
|
+
try {
|
|
12806
|
+
const raw = await fs.readFile(this.filePath(), "utf8");
|
|
12807
|
+
return validateDocument2(JSON.parse(raw));
|
|
12808
|
+
} catch (err2) {
|
|
12809
|
+
if (err2.code === "ENOENT") return emptyDocument2();
|
|
12810
|
+
throw err2;
|
|
12811
|
+
}
|
|
12812
|
+
}
|
|
12813
|
+
async writeDoc(fs, doc) {
|
|
12814
|
+
await fs.mkdir(this.dir(), { recursive: true });
|
|
12815
|
+
const fp = this.filePath();
|
|
12816
|
+
const tmp = `${fp}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
12817
|
+
try {
|
|
12818
|
+
await fs.writeFile(tmp, `${JSON.stringify(doc, null, 2)}
|
|
12819
|
+
`, "utf8");
|
|
12820
|
+
const handle = await fs.open(tmp, "r+");
|
|
12821
|
+
try {
|
|
12822
|
+
await handle.sync();
|
|
12823
|
+
} finally {
|
|
12824
|
+
await handle.close();
|
|
12825
|
+
}
|
|
12826
|
+
await fs.rename(tmp, fp);
|
|
12827
|
+
} catch (err2) {
|
|
12828
|
+
await fs.rm(tmp, { force: true }).catch(() => void 0);
|
|
12829
|
+
throw err2;
|
|
12830
|
+
}
|
|
12831
|
+
}
|
|
12832
|
+
async mutate(operation, options = {}) {
|
|
12833
|
+
const directory = this.dir();
|
|
12834
|
+
return withPolicyNamespaceLock(async (session3) => {
|
|
12835
|
+
const fs = await import('fs/promises');
|
|
12836
|
+
const doc = await this.readDoc(fs);
|
|
12837
|
+
if (options.basedOnRevision !== void 0 && options.basedOnRevision !== doc.revision) {
|
|
12838
|
+
throw new GovernancePolicyError(`Stale governance revision: expected ${options.basedOnRevision}, current ${doc.revision}`);
|
|
12839
|
+
}
|
|
12840
|
+
const outcome = operation(doc);
|
|
12841
|
+
let next = outcome.document;
|
|
12842
|
+
validateDocument2(next);
|
|
12843
|
+
await session3.assertOwnership();
|
|
12844
|
+
await this.writeDoc(fs, next);
|
|
12845
|
+
return outcome.result;
|
|
12846
|
+
}, { directory });
|
|
12847
|
+
}
|
|
12848
|
+
// --- role helpers ---
|
|
12849
|
+
findRoleBinding(doc, principalId, scope) {
|
|
12850
|
+
const candidates = doc.roleBindings.filter((rb) => rb.principalId === principalId && scopeCovers(rb.scope, scope));
|
|
12851
|
+
if (candidates.length === 0) return null;
|
|
12852
|
+
return candidates.sort((a, b) => b.scope.length - a.scope.length)[0];
|
|
12853
|
+
}
|
|
12854
|
+
resolveRole(principalId, scope = "*") {
|
|
12855
|
+
return { principalId, role: null, scope, reason: "use resolveRoleAsync" };
|
|
12856
|
+
}
|
|
12857
|
+
async resolveRoleAsync(principalId, scope = "*") {
|
|
12858
|
+
const doc = await this.load();
|
|
12859
|
+
const rb = this.findRoleBinding(doc, principalId, scope);
|
|
12860
|
+
if (!rb) return { principalId, role: null, scope, reason: "no role binding for principal in scope" };
|
|
12861
|
+
return { principalId, role: rb.role, scope, reason: `bound role ${rb.role} (scope ${rb.scope})` };
|
|
12862
|
+
}
|
|
12863
|
+
async hasPermission(principalId, action, scope) {
|
|
12864
|
+
const res = await this.resolveRoleAsync(principalId, scope);
|
|
12865
|
+
if (!res.role) return false;
|
|
12866
|
+
return ROLE_CAPABILITIES[res.role].has(action);
|
|
12867
|
+
}
|
|
12868
|
+
// --- role binding governed ---
|
|
12869
|
+
async bindRole(input, basedOnRevision) {
|
|
12870
|
+
const authority = validateAuthority(input.authority);
|
|
12871
|
+
if (!requiredText3(input.principalId)) throw new GovernancePolicyError("principalId is required");
|
|
12872
|
+
if (!requiredText3(input.grantedBy)) throw new GovernancePolicyError("grantedBy is required");
|
|
12873
|
+
if (input.principalId === input.grantedBy && (input.role === "human-owner" || input.role === "human-approver" || input.role === "policy-authority")) {
|
|
12874
|
+
void detectSelfPromoted({ claim: `self-grant ${input.role}`, channel: "governance.role.bind" }).catch(() => void 0);
|
|
12875
|
+
throw new GovernancePolicyError("self-grant of elevated role denied");
|
|
12876
|
+
}
|
|
12877
|
+
const doc0 = await this.load();
|
|
12878
|
+
const grantedByRole = this.findRoleBinding(doc0, input.grantedBy, input.scope ?? "*");
|
|
12879
|
+
if (!grantedByRole || !ROLE_CAPABILITIES[grantedByRole.role].has("role.bind")) {
|
|
12880
|
+
throw new GovernancePolicyError("grantedBy lacks role.bind authority");
|
|
12881
|
+
}
|
|
12882
|
+
if (authority.kind !== "HI" && authority.kind !== "operator") {
|
|
12883
|
+
throw new GovernancePolicyError("invalid authority for role binding");
|
|
12884
|
+
}
|
|
12885
|
+
const binding = {
|
|
12886
|
+
principalId: input.principalId,
|
|
12887
|
+
role: input.role,
|
|
12888
|
+
scope: input.scope || "*",
|
|
12889
|
+
grantedBy: input.grantedBy,
|
|
12890
|
+
grantedAt: nowIso2()
|
|
12891
|
+
};
|
|
12892
|
+
return this.mutate((doc) => {
|
|
12893
|
+
const nextBindings = [...doc.roleBindings.filter((rb) => !(rb.principalId === binding.principalId && rb.scope === binding.scope)), binding];
|
|
12894
|
+
const revision = doc.revision + 1;
|
|
12895
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
12896
|
+
action: "role.bind",
|
|
12897
|
+
at: binding.grantedAt,
|
|
12898
|
+
actorPrincipal: input.grantedBy,
|
|
12899
|
+
actorRole: grantedByRole.role,
|
|
12900
|
+
scope: binding.scope,
|
|
12901
|
+
outcome: "applied",
|
|
12902
|
+
detail: `bound ${binding.principalId} to ${binding.role} in scope ${binding.scope}`,
|
|
12903
|
+
correlationId: void 0
|
|
12904
|
+
})]);
|
|
12905
|
+
return { document: { ...doc, revision, roleBindings: nextBindings, audit }, result: binding };
|
|
12906
|
+
}, { basedOnRevision });
|
|
12907
|
+
}
|
|
12908
|
+
// --- propose ---
|
|
12909
|
+
async propose(input, basedOnRevision) {
|
|
12910
|
+
if (!input || typeof input !== "object") throw new GovernancePolicyError("proposal input required");
|
|
12911
|
+
if (!requiredText3(input.scope)) throw new GovernancePolicyError("scope is required");
|
|
12912
|
+
if (!requiredText3(input.rationale)) throw new GovernancePolicyError("rationale is required");
|
|
12913
|
+
if (!requiredText3(input.proposedBy)) throw new GovernancePolicyError("proposedBy is required");
|
|
12914
|
+
const authority = validateAuthority(input.authority);
|
|
12915
|
+
const doc0 = await this.load();
|
|
12916
|
+
if (!await this.hasPermission(input.proposedBy, "policy.propose", input.scope)) {
|
|
12917
|
+
void detectAuthorityEscalationAttempt({ claimed: `${input.proposedBy} propose without permission`, channel: "governance.propose" }).catch(() => void 0);
|
|
12918
|
+
await this.recordDenial(doc0, "policy.propose", input.proposedBy, input.scope, "permission denied");
|
|
12919
|
+
throw new GovernancePolicyError("proposer lacks policy.propose permission");
|
|
12920
|
+
}
|
|
12921
|
+
const policyId = requiredText3(input.policyId) ? input.policyId : `gov-policy.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
|
|
12922
|
+
const existing = doc0.policies.filter((p) => p.policyId === policyId);
|
|
12923
|
+
const version = existing.length > 0 ? Math.max(...existing.map((p) => p.version)) + 1 : 1;
|
|
12924
|
+
const supersedes = input.supersedes;
|
|
12925
|
+
const policy2 = {
|
|
12926
|
+
policyId,
|
|
12927
|
+
version,
|
|
12928
|
+
...requiredText3(input.title) ? { title: input.title } : {},
|
|
12929
|
+
...requiredText3(input.description) ? { description: input.description } : {},
|
|
12930
|
+
scope: input.scope,
|
|
12931
|
+
applicablePrincipals: input.applicablePrincipals ?? [],
|
|
12932
|
+
applicableResources: input.applicableResources ?? [],
|
|
12933
|
+
...requiredText3(input.effectiveAt) ? { effectiveAt: input.effectiveAt } : {},
|
|
12934
|
+
...requiredText3(input.expiresAt) ? { expiresAt: input.expiresAt } : {},
|
|
12935
|
+
rationale: input.rationale,
|
|
12936
|
+
status: "proposed",
|
|
12937
|
+
ownerAuthority: authority,
|
|
12938
|
+
proposedBy: input.proposedBy,
|
|
12939
|
+
proposedAt: nowIso2(),
|
|
12940
|
+
...requiredText3(supersedes) ? { supersedes } : {},
|
|
12941
|
+
...requiredText3(input.correlationId) ? { correlationId: input.correlationId } : {},
|
|
12942
|
+
revision: 0
|
|
12943
|
+
};
|
|
12944
|
+
return this.mutate((doc) => {
|
|
12945
|
+
const revision = doc.revision + 1;
|
|
12946
|
+
const fingerprint = `${policy2.policyId}@v${policy2.version}`;
|
|
12947
|
+
const stored = { ...policy2, revision, fingerprint };
|
|
12948
|
+
const policies = [...doc.policies, stored];
|
|
12949
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
12950
|
+
action: "policy.proposed",
|
|
12951
|
+
at: stored.proposedAt,
|
|
12952
|
+
actorPrincipal: input.proposedBy,
|
|
12953
|
+
targetPolicyId: stored.policyId,
|
|
12954
|
+
targetVersion: stored.version,
|
|
12955
|
+
scope: stored.scope,
|
|
12956
|
+
outcome: "applied",
|
|
12957
|
+
detail: `policy ${stored.policyId} v${stored.version} proposed`,
|
|
12958
|
+
correlationId: input.correlationId
|
|
12959
|
+
})]);
|
|
12960
|
+
return { document: { ...doc, revision, policies, audit }, result: stored };
|
|
12961
|
+
}, { basedOnRevision });
|
|
12962
|
+
}
|
|
12963
|
+
async recordDenial(doc, action, actor, scope, reason) {
|
|
12964
|
+
try {
|
|
12965
|
+
await this.mutate((d) => {
|
|
12966
|
+
const revision = d.revision + 1;
|
|
12967
|
+
const audit = boundedAudit2([...d.audit, buildAudit({
|
|
12968
|
+
action: `${action}.denied`,
|
|
12969
|
+
at: nowIso2(),
|
|
12970
|
+
actorPrincipal: actor,
|
|
12971
|
+
scope,
|
|
12972
|
+
outcome: "rejected",
|
|
12973
|
+
reason
|
|
12974
|
+
})]);
|
|
12975
|
+
return { document: { ...d, revision, audit }, result: void 0 };
|
|
12976
|
+
});
|
|
12977
|
+
} catch {
|
|
12978
|
+
}
|
|
12979
|
+
}
|
|
12980
|
+
// --- review ---
|
|
12981
|
+
async review(input) {
|
|
12982
|
+
if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
|
|
12983
|
+
if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
|
|
12984
|
+
if (!requiredText3(input.reviewerPrincipal)) throw new GovernancePolicyError("reviewerPrincipal is required");
|
|
12985
|
+
const doc0 = await this.load();
|
|
12986
|
+
if (!await this.hasPermission(input.reviewerPrincipal, "policy.review", "*")) {
|
|
12987
|
+
const pol = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
|
|
12988
|
+
const scope = pol?.scope ?? "*";
|
|
12989
|
+
if (!await this.hasPermission(input.reviewerPrincipal, "policy.review", scope)) {
|
|
12990
|
+
await this.recordDenial(doc0, "policy.review", input.reviewerPrincipal, scope, "permission denied");
|
|
12991
|
+
throw new GovernancePolicyError("reviewer lacks policy.review permission");
|
|
12992
|
+
}
|
|
12993
|
+
}
|
|
12994
|
+
const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
|
|
12995
|
+
if (!policy2) throw new GovernancePolicyError("policy not found");
|
|
12996
|
+
if (policy2.status !== "proposed") throw new GovernancePolicyError(`cannot review policy in status ${policy2.status}`);
|
|
12997
|
+
return this.mutate((doc) => {
|
|
12998
|
+
const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
|
|
12999
|
+
if (idx < 0) throw new GovernancePolicyError("policy not found");
|
|
13000
|
+
const updated = {
|
|
13001
|
+
...doc.policies[idx],
|
|
13002
|
+
status: "under_review",
|
|
13003
|
+
reviewedBy: input.reviewerPrincipal,
|
|
13004
|
+
reviewedAt: nowIso2()
|
|
13005
|
+
};
|
|
13006
|
+
const policies = [...doc.policies];
|
|
13007
|
+
policies[idx] = updated;
|
|
13008
|
+
const revision = doc.revision + 1;
|
|
13009
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
13010
|
+
action: "policy.reviewed",
|
|
13011
|
+
at: updated.reviewedAt,
|
|
13012
|
+
actorPrincipal: input.reviewerPrincipal,
|
|
13013
|
+
targetPolicyId: updated.policyId,
|
|
13014
|
+
targetVersion: updated.version,
|
|
13015
|
+
scope: updated.scope,
|
|
13016
|
+
outcome: "applied",
|
|
13017
|
+
correlationId: input.correlationId
|
|
13018
|
+
})]);
|
|
13019
|
+
return { document: { ...doc, revision, policies, audit }, result: updated };
|
|
13020
|
+
});
|
|
13021
|
+
}
|
|
13022
|
+
// --- decide (approve/reject) ---
|
|
13023
|
+
async decide(input) {
|
|
13024
|
+
if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
|
|
13025
|
+
if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
|
|
13026
|
+
if (!requiredText3(input.approverPrincipal)) throw new GovernancePolicyError("approverPrincipal is required");
|
|
13027
|
+
if (input.decision !== "approved" && input.decision !== "rejected") throw new GovernancePolicyError("decision must be approved or rejected");
|
|
13028
|
+
const doc0 = await this.load();
|
|
13029
|
+
const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13030
|
+
if (!policy2) throw new GovernancePolicyError("policy not found");
|
|
13031
|
+
const scope = policy2.scope;
|
|
13032
|
+
const action = input.decision === "approved" ? "policy.approve" : "policy.reject";
|
|
13033
|
+
if (!await this.hasPermission(input.approverPrincipal, "policy.approve", scope) && !await this.hasPermission(input.approverPrincipal, "policy.reject", scope)) {
|
|
13034
|
+
if (!await this.hasPermission(input.approverPrincipal, "policy.approve", scope)) {
|
|
13035
|
+
await this.recordDenial(doc0, action, input.approverPrincipal, scope, "permission denied");
|
|
13036
|
+
throw new GovernancePolicyError("approver lacks approval permission");
|
|
13037
|
+
}
|
|
13038
|
+
}
|
|
13039
|
+
if (input.approverPrincipal === policy2.proposedBy) {
|
|
13040
|
+
void detectSelfPromoted({ claim: `self-approval ${policy2.policyId}`, channel: "governance.approve" }).catch(() => void 0);
|
|
13041
|
+
await this.recordDenial(doc0, action, input.approverPrincipal, scope, "self-approval denied");
|
|
13042
|
+
throw new GovernancePolicyError("self-approval denied: proposer cannot be sole approver");
|
|
13043
|
+
}
|
|
13044
|
+
if (policy2.status !== "under_review" && policy2.status !== "proposed") {
|
|
13045
|
+
throw new GovernancePolicyError(`cannot decide policy in status ${policy2.status}`);
|
|
13046
|
+
}
|
|
13047
|
+
return this.mutate((doc) => {
|
|
13048
|
+
const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13049
|
+
if (idx < 0) throw new GovernancePolicyError("policy not found");
|
|
13050
|
+
const current = doc.policies[idx];
|
|
13051
|
+
const newStatus = input.decision === "approved" ? "approved" : "rejected";
|
|
13052
|
+
const updated = {
|
|
13053
|
+
...current,
|
|
13054
|
+
status: newStatus,
|
|
13055
|
+
approval: {
|
|
13056
|
+
approverPrincipal: input.approverPrincipal,
|
|
13057
|
+
decision: input.decision,
|
|
13058
|
+
...requiredText3(input.reason) ? { reason: input.reason } : {},
|
|
13059
|
+
at: nowIso2()
|
|
13060
|
+
}
|
|
13061
|
+
};
|
|
13062
|
+
const policies = [...doc.policies];
|
|
13063
|
+
policies[idx] = updated;
|
|
13064
|
+
const revision = doc.revision + 1;
|
|
13065
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
13066
|
+
action: input.decision === "approved" ? "policy.approved" : "policy.rejected",
|
|
13067
|
+
at: updated.approval.at,
|
|
13068
|
+
actorPrincipal: input.approverPrincipal,
|
|
13069
|
+
targetPolicyId: updated.policyId,
|
|
13070
|
+
targetVersion: updated.version,
|
|
13071
|
+
scope: updated.scope,
|
|
13072
|
+
outcome: "applied",
|
|
13073
|
+
reason: input.reason,
|
|
13074
|
+
correlationId: input.correlationId
|
|
13075
|
+
})]);
|
|
13076
|
+
return { document: { ...doc, revision, policies, audit }, result: updated };
|
|
13077
|
+
});
|
|
13078
|
+
}
|
|
13079
|
+
// --- activate ---
|
|
13080
|
+
async activate(input) {
|
|
13081
|
+
if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
|
|
13082
|
+
if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
|
|
13083
|
+
if (!requiredText3(input.activatorPrincipal)) throw new GovernancePolicyError("activatorPrincipal is required");
|
|
13084
|
+
const doc0 = await this.load();
|
|
13085
|
+
const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13086
|
+
if (!policy2) throw new GovernancePolicyError("policy not found");
|
|
13087
|
+
if (policy2.status !== "approved") throw new GovernancePolicyError(`cannot activate policy in status ${policy2.status}`);
|
|
13088
|
+
const scope = policy2.scope;
|
|
13089
|
+
if (!await this.hasPermission(input.activatorPrincipal, "policy.activate", scope)) {
|
|
13090
|
+
await this.recordDenial(doc0, "policy.activate", input.activatorPrincipal, scope, "permission denied");
|
|
13091
|
+
throw new GovernancePolicyError("activator lacks policy.activate permission");
|
|
13092
|
+
}
|
|
13093
|
+
const now3 = /* @__PURE__ */ new Date();
|
|
13094
|
+
if (policy2.effectiveAt && Date.parse(policy2.effectiveAt) > now3.getTime()) {
|
|
13095
|
+
throw new GovernancePolicyError("policy not yet effective");
|
|
13096
|
+
}
|
|
13097
|
+
if (policy2.expiresAt && Date.parse(policy2.expiresAt) <= now3.getTime()) {
|
|
13098
|
+
throw new GovernancePolicyError("policy expired");
|
|
13099
|
+
}
|
|
13100
|
+
return this.mutate((doc) => {
|
|
13101
|
+
const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13102
|
+
if (idx < 0) throw new GovernancePolicyError("policy not found");
|
|
13103
|
+
const current = doc.policies[idx];
|
|
13104
|
+
if (current.status !== "approved") throw new GovernancePolicyError(`cannot activate policy in status ${current.status}`);
|
|
13105
|
+
const policies = doc.policies.map((p) => {
|
|
13106
|
+
if (p.policyId === current.policyId && p.version !== current.version && p.status === "active") {
|
|
13107
|
+
return { ...p, status: "retired", retiredBy: input.activatorPrincipal, retiredAt: nowIso2(), supersededBy: `${current.policyId}@v${current.version}` };
|
|
13108
|
+
}
|
|
13109
|
+
return p;
|
|
13110
|
+
});
|
|
13111
|
+
const nowStr = nowIso2();
|
|
13112
|
+
policies[idx] = {
|
|
13113
|
+
...current,
|
|
13114
|
+
status: "active",
|
|
13115
|
+
activatedBy: input.activatorPrincipal,
|
|
13116
|
+
activatedAt: nowStr
|
|
13117
|
+
};
|
|
13118
|
+
const updated = policies[idx];
|
|
13119
|
+
const revision = doc.revision + 1;
|
|
13120
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
13121
|
+
action: "policy.activated",
|
|
13122
|
+
at: nowStr,
|
|
13123
|
+
actorPrincipal: input.activatorPrincipal,
|
|
13124
|
+
targetPolicyId: updated.policyId,
|
|
13125
|
+
targetVersion: updated.version,
|
|
13126
|
+
scope: updated.scope,
|
|
13127
|
+
outcome: "applied",
|
|
13128
|
+
correlationId: input.correlationId
|
|
13129
|
+
})]);
|
|
13130
|
+
return { document: { ...doc, revision, policies, audit }, result: updated };
|
|
13131
|
+
});
|
|
13132
|
+
}
|
|
13133
|
+
async suspend(input) {
|
|
13134
|
+
if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
|
|
13135
|
+
if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
|
|
13136
|
+
if (!requiredText3(input.actorPrincipal)) throw new GovernancePolicyError("actorPrincipal is required");
|
|
13137
|
+
const doc0 = await this.load();
|
|
13138
|
+
const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13139
|
+
if (!policy2) throw new GovernancePolicyError("policy not found");
|
|
13140
|
+
const scope = policy2.scope;
|
|
13141
|
+
if (!await this.hasPermission(input.actorPrincipal, "policy.suspend", scope)) {
|
|
13142
|
+
const role = await this.resolveRoleAsync(input.actorPrincipal, scope);
|
|
13143
|
+
if (role.role !== "human-owner" && role.role !== "human-approver") {
|
|
13144
|
+
await this.recordDenial(doc0, "policy.suspend", input.actorPrincipal, scope, "permission denied");
|
|
13145
|
+
throw new GovernancePolicyError("actor lacks suspend permission");
|
|
13146
|
+
}
|
|
13147
|
+
}
|
|
13148
|
+
return this.mutate((doc) => {
|
|
13149
|
+
const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13150
|
+
if (idx < 0) throw new GovernancePolicyError("policy not found");
|
|
13151
|
+
const current = doc.policies[idx];
|
|
13152
|
+
if (current.status !== "active" && current.status !== "approved") throw new GovernancePolicyError(`cannot suspend policy in status ${current.status}`);
|
|
13153
|
+
const updated = {
|
|
13154
|
+
...current,
|
|
13155
|
+
status: "suspended",
|
|
13156
|
+
suspendedBy: input.actorPrincipal,
|
|
13157
|
+
suspendedAt: nowIso2()
|
|
13158
|
+
};
|
|
13159
|
+
const policies = [...doc.policies];
|
|
13160
|
+
policies[idx] = updated;
|
|
13161
|
+
const revision = doc.revision + 1;
|
|
13162
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
13163
|
+
action: "policy.suspended",
|
|
13164
|
+
at: updated.suspendedAt,
|
|
13165
|
+
actorPrincipal: input.actorPrincipal,
|
|
13166
|
+
targetPolicyId: updated.policyId,
|
|
13167
|
+
targetVersion: updated.version,
|
|
13168
|
+
scope: updated.scope,
|
|
13169
|
+
outcome: "applied",
|
|
13170
|
+
reason: input.reason,
|
|
13171
|
+
correlationId: input.correlationId
|
|
13172
|
+
})]);
|
|
13173
|
+
return { document: { ...doc, revision, policies, audit }, result: updated };
|
|
13174
|
+
});
|
|
13175
|
+
}
|
|
13176
|
+
async retire(input) {
|
|
13177
|
+
if (!input || !requiredText3(input.policyId)) throw new GovernancePolicyError("policyId is required");
|
|
13178
|
+
if (typeof input.version !== "number") throw new GovernancePolicyError("version is required");
|
|
13179
|
+
if (!requiredText3(input.actorPrincipal)) throw new GovernancePolicyError("actorPrincipal is required");
|
|
13180
|
+
const doc0 = await this.load();
|
|
13181
|
+
const policy2 = doc0.policies.find((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13182
|
+
if (!policy2) throw new GovernancePolicyError("policy not found");
|
|
13183
|
+
const scope = policy2.scope;
|
|
13184
|
+
const role = await this.resolveRoleAsync(input.actorPrincipal, scope);
|
|
13185
|
+
if (role.role !== "human-owner" && role.role !== "human-approver") {
|
|
13186
|
+
await this.recordDenial(doc0, "policy.retire", input.actorPrincipal, scope, "permission denied");
|
|
13187
|
+
throw new GovernancePolicyError("actor lacks retire permission");
|
|
13188
|
+
}
|
|
13189
|
+
return this.mutate((doc) => {
|
|
13190
|
+
const idx = doc.policies.findIndex((p) => p.policyId === input.policyId && p.version === input.version);
|
|
13191
|
+
if (idx < 0) throw new GovernancePolicyError("policy not found");
|
|
13192
|
+
const current = doc.policies[idx];
|
|
13193
|
+
const updated = {
|
|
13194
|
+
...current,
|
|
13195
|
+
status: "retired",
|
|
13196
|
+
retiredBy: input.actorPrincipal,
|
|
13197
|
+
retiredAt: nowIso2()
|
|
13198
|
+
};
|
|
13199
|
+
const policies = [...doc.policies];
|
|
13200
|
+
policies[idx] = updated;
|
|
13201
|
+
const revision = doc.revision + 1;
|
|
13202
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
13203
|
+
action: "policy.retired",
|
|
13204
|
+
at: updated.retiredAt,
|
|
13205
|
+
actorPrincipal: input.actorPrincipal,
|
|
13206
|
+
targetPolicyId: updated.policyId,
|
|
13207
|
+
targetVersion: updated.version,
|
|
13208
|
+
scope: updated.scope,
|
|
13209
|
+
outcome: "applied",
|
|
13210
|
+
reason: input.reason,
|
|
13211
|
+
correlationId: input.correlationId
|
|
13212
|
+
})]);
|
|
13213
|
+
return { document: { ...doc, revision, policies, audit }, result: updated };
|
|
13214
|
+
});
|
|
13215
|
+
}
|
|
13216
|
+
// --- resolution (fail-closed) ---
|
|
13217
|
+
async resolvePolicy(policyId, version, requestedScope, now3 = /* @__PURE__ */ new Date()) {
|
|
13218
|
+
const doc = await this.load();
|
|
13219
|
+
const policy2 = doc.policies.find((p) => p.policyId === policyId && p.version === version);
|
|
13220
|
+
if (!policy2) {
|
|
13221
|
+
return { outcome: "none", policy: null, reason: "no applicable policy" };
|
|
13222
|
+
}
|
|
13223
|
+
if (!scopeCovers(policy2.scope, requestedScope)) {
|
|
13224
|
+
return { outcome: "insufficient-scope", policy: null, reason: "policy scope does not cover requested scope" };
|
|
13225
|
+
}
|
|
13226
|
+
if (!policy2.ownerAuthority || policy2.ownerAuthority.kind !== "HI" && policy2.ownerAuthority.kind !== "operator") {
|
|
13227
|
+
return { outcome: "unresolved-authority", policy: null, reason: "unresolved or invalid authority" };
|
|
13228
|
+
}
|
|
13229
|
+
if (policy2.effectiveAt && Date.parse(policy2.effectiveAt) > now3.getTime()) {
|
|
13230
|
+
return { outcome: "not-yet-effective", policy: null, reason: "policy not yet effective" };
|
|
13231
|
+
}
|
|
13232
|
+
if (policy2.expiresAt && Date.parse(policy2.expiresAt) <= now3.getTime()) {
|
|
13233
|
+
return { outcome: "expired", policy: null, reason: "policy expired" };
|
|
13234
|
+
}
|
|
13235
|
+
if (policy2.status !== "active") {
|
|
13236
|
+
return { outcome: "none", policy: null, reason: `policy status ${policy2.status} is not active` };
|
|
13237
|
+
}
|
|
13238
|
+
if (policy2.fingerprint && policy2.fingerprint !== `${policy2.policyId}@v${policy2.version}`) {
|
|
13239
|
+
return { outcome: "invalid-provenance", policy: null, reason: "fingerprint mismatch" };
|
|
13240
|
+
}
|
|
13241
|
+
return { outcome: "applicable", policy: policy2, reason: "applicable and valid" };
|
|
13242
|
+
}
|
|
13243
|
+
async resolveActivePolicies(scope, now3 = /* @__PURE__ */ new Date()) {
|
|
13244
|
+
const doc = await this.load();
|
|
13245
|
+
const active = doc.policies.filter((p) => p.status === "active" && scopeCovers(p.scope, scope));
|
|
13246
|
+
return active.filter((p) => {
|
|
13247
|
+
if (p.effectiveAt && Date.parse(p.effectiveAt) > now3.getTime()) return false;
|
|
13248
|
+
if (p.expiresAt && Date.parse(p.expiresAt) <= now3.getTime()) return false;
|
|
13249
|
+
return true;
|
|
13250
|
+
});
|
|
13251
|
+
}
|
|
13252
|
+
async checkConflicts(scope, now3 = /* @__PURE__ */ new Date()) {
|
|
13253
|
+
const actives = await this.resolveActivePolicies(scope, now3);
|
|
13254
|
+
const conflicting = actives.length > 1;
|
|
13255
|
+
return { conflicting, policies: actives };
|
|
13256
|
+
}
|
|
13257
|
+
async failClosedResolve(scope, now3 = /* @__PURE__ */ new Date()) {
|
|
13258
|
+
const actives = await this.resolveActivePolicies(scope, now3);
|
|
13259
|
+
if (actives.length === 0) {
|
|
13260
|
+
return { outcome: "none", policy: null, reason: "no applicable policy" };
|
|
13261
|
+
}
|
|
13262
|
+
if (actives.length > 1) {
|
|
13263
|
+
return { outcome: "conflicting", policy: null, reason: "conflicting active policies" };
|
|
13264
|
+
}
|
|
13265
|
+
const p = actives[0];
|
|
13266
|
+
if (!p.ownerAuthority || p.ownerAuthority.kind !== "HI" && p.ownerAuthority.kind !== "operator") {
|
|
13267
|
+
return { outcome: "unresolved-authority", policy: null, reason: "unresolved authority" };
|
|
13268
|
+
}
|
|
13269
|
+
return { outcome: "applicable", policy: p, reason: "applicable and valid" };
|
|
13270
|
+
}
|
|
13271
|
+
// --- authorization granted/denied audit ---
|
|
13272
|
+
async recordAuthorization(decision) {
|
|
13273
|
+
await this.mutate((doc) => {
|
|
13274
|
+
const revision = doc.revision + 1;
|
|
13275
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
13276
|
+
action: decision.allowed ? "authorization.granted" : "authorization.denied",
|
|
13277
|
+
at: nowIso2(),
|
|
13278
|
+
actorPrincipal: decision.principalId,
|
|
13279
|
+
scope: decision.scope ?? decision.resource,
|
|
13280
|
+
outcome: decision.allowed ? "applied" : "rejected",
|
|
13281
|
+
reason: decision.reason,
|
|
13282
|
+
detail: `${decision.action} on ${decision.resource}`,
|
|
13283
|
+
correlationId: decision.correlationId
|
|
13284
|
+
})]);
|
|
13285
|
+
return { document: { ...doc, revision, audit }, result: void 0 };
|
|
13286
|
+
});
|
|
13287
|
+
}
|
|
13288
|
+
// --- list ---
|
|
13289
|
+
async listPolicies() {
|
|
13290
|
+
const doc = await this.load();
|
|
13291
|
+
return [...doc.policies].sort((a, b) => a.policyId.localeCompare(b.policyId) || a.version - b.version);
|
|
13292
|
+
}
|
|
13293
|
+
async listRoleBindings() {
|
|
13294
|
+
const doc = await this.load();
|
|
13295
|
+
return [...doc.roleBindings];
|
|
13296
|
+
}
|
|
13297
|
+
async auditTrail() {
|
|
13298
|
+
const doc = await this.load();
|
|
13299
|
+
return [...doc.audit];
|
|
13300
|
+
}
|
|
13301
|
+
async recordAuditEvent(input) {
|
|
13302
|
+
await this.mutate((doc) => {
|
|
13303
|
+
const revision = doc.revision + 1;
|
|
13304
|
+
const audit = boundedAudit2([...doc.audit, buildAudit({
|
|
13305
|
+
action: input.action,
|
|
13306
|
+
at: input.at ?? nowIso2(),
|
|
13307
|
+
...input.actorPrincipal !== void 0 ? { actorPrincipal: input.actorPrincipal } : {},
|
|
13308
|
+
...input.actorRole !== void 0 ? { actorRole: input.actorRole } : {},
|
|
13309
|
+
...input.targetPolicyId !== void 0 ? { targetPolicyId: input.targetPolicyId } : {},
|
|
13310
|
+
...input.targetVersion !== void 0 ? { targetVersion: input.targetVersion } : {},
|
|
13311
|
+
...input.scope !== void 0 ? { scope: input.scope } : {},
|
|
13312
|
+
outcome: input.outcome,
|
|
13313
|
+
...input.reason !== void 0 ? { reason: input.reason } : {},
|
|
13314
|
+
...input.detail !== void 0 ? { detail: input.detail } : {},
|
|
13315
|
+
...input.correlationId !== void 0 ? { correlationId: input.correlationId } : {},
|
|
13316
|
+
...input.mandatory !== void 0 ? { mandatory: input.mandatory } : {}
|
|
13317
|
+
})]);
|
|
13318
|
+
return { document: { ...doc, revision, audit }, result: void 0 };
|
|
13319
|
+
});
|
|
13320
|
+
}
|
|
13321
|
+
};
|
|
13322
|
+
var roleHasPermission = /* @__PURE__ */ __name((role, action) => ROLE_CAPABILITIES[role].has(action), "roleHasPermission");
|
|
13323
|
+
var GOVERNANCE_ROLES = [
|
|
13324
|
+
"human-owner",
|
|
13325
|
+
"human-approver",
|
|
13326
|
+
"coordinator-jo",
|
|
13327
|
+
"specialist-agent",
|
|
13328
|
+
"policy-authority",
|
|
13329
|
+
"auditor-reviewer"
|
|
13330
|
+
];
|
|
13331
|
+
var GOVERNANCE_POLICY_STATUSES = [
|
|
13332
|
+
"proposed",
|
|
13333
|
+
"under_review",
|
|
13334
|
+
"approved",
|
|
13335
|
+
"active",
|
|
13336
|
+
"rejected",
|
|
13337
|
+
"suspended",
|
|
13338
|
+
"retired"
|
|
13339
|
+
];
|
|
13340
|
+
|
|
13341
|
+
// core/guardrails/index.ts
|
|
13342
|
+
init_user_paths();
|
|
13343
|
+
init_policy_namespace_lock();
|
|
13344
|
+
var STOP_FILE_NAME = "guardrails.stop.json";
|
|
13345
|
+
var STOP_FORMAT = "dotmem.guardrails-stop";
|
|
13346
|
+
var STOP_VERSION = 1;
|
|
13347
|
+
var GuardrailError = class extends Error {
|
|
13348
|
+
static {
|
|
13349
|
+
__name(this, "GuardrailError");
|
|
13350
|
+
}
|
|
13351
|
+
constructor(message) {
|
|
13352
|
+
super(message);
|
|
13353
|
+
this.name = "GuardrailError";
|
|
13354
|
+
}
|
|
13355
|
+
};
|
|
13356
|
+
var requiredText4 = /* @__PURE__ */ __name((v) => typeof v === "string" && v.trim().length > 0, "requiredText");
|
|
13357
|
+
var nowIso3 = /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString(), "nowIso");
|
|
13358
|
+
var joinPath8 = /* @__PURE__ */ __name((base, child) => {
|
|
13359
|
+
const sep = process.platform === "win32" ? "\\" : "/";
|
|
13360
|
+
return `${base.replace(/[\\/]+/g, sep)}${sep}${child}`;
|
|
13361
|
+
}, "joinPath");
|
|
13362
|
+
var resolveGuardrailDir = /* @__PURE__ */ __name((directory) => directory ?? joinPath8(resolveUserDotMemPath(), "policy"), "resolveGuardrailDir");
|
|
13363
|
+
var resolveStopFilePath = /* @__PURE__ */ __name((directory) => joinPath8(resolveGuardrailDir(directory), STOP_FILE_NAME), "resolveStopFilePath");
|
|
13364
|
+
var ACTIONS_REQUIRING_TRUSTED_IDENTITY = /* @__PURE__ */ new Set([
|
|
13365
|
+
"agent.execute",
|
|
13366
|
+
"guardrail.stop.activate",
|
|
13367
|
+
"guardrail.stop.release"
|
|
13368
|
+
]);
|
|
13369
|
+
var GuardrailService = class {
|
|
13370
|
+
static {
|
|
13371
|
+
__name(this, "GuardrailService");
|
|
13372
|
+
}
|
|
13373
|
+
directory;
|
|
13374
|
+
governance;
|
|
13375
|
+
constructor(directory, governance) {
|
|
13376
|
+
this.directory = directory;
|
|
13377
|
+
this.governance = governance ?? new GovernancePolicyStore(directory);
|
|
13378
|
+
}
|
|
13379
|
+
dir() {
|
|
13380
|
+
return resolveGuardrailDir(this.directory);
|
|
13381
|
+
}
|
|
13382
|
+
// --- stop controller persistence ---
|
|
13383
|
+
async readStopState() {
|
|
13384
|
+
const fs = await import('fs/promises');
|
|
13385
|
+
let raw;
|
|
13386
|
+
try {
|
|
13387
|
+
raw = await fs.readFile(resolveStopFilePath(this.directory), "utf8");
|
|
13388
|
+
} catch (err2) {
|
|
13389
|
+
if (err2.code === "ENOENT") {
|
|
13390
|
+
return { stopped: false, revision: 0 };
|
|
13391
|
+
}
|
|
13392
|
+
throw err2;
|
|
13393
|
+
}
|
|
13394
|
+
const parsed = JSON.parse(raw);
|
|
13395
|
+
if (parsed.format !== STOP_FORMAT || parsed.version !== STOP_VERSION) {
|
|
13396
|
+
throw new GuardrailError("invalid stop state document");
|
|
13397
|
+
}
|
|
13398
|
+
return {
|
|
13399
|
+
stopped: parsed.stopped === true,
|
|
13400
|
+
...requiredText4(parsed.activatedBy) ? { activatedBy: parsed.activatedBy } : {},
|
|
13401
|
+
...requiredText4(parsed.activatedAt) ? { activatedAt: parsed.activatedAt } : {},
|
|
13402
|
+
...requiredText4(parsed.reason) ? { reason: parsed.reason } : {},
|
|
13403
|
+
...requiredText4(parsed.releasedBy) ? { releasedBy: parsed.releasedBy } : {},
|
|
13404
|
+
...requiredText4(parsed.releasedAt) ? { releasedAt: parsed.releasedAt } : {},
|
|
13405
|
+
revision: typeof parsed.revision === "number" ? parsed.revision : 0
|
|
13406
|
+
};
|
|
13407
|
+
}
|
|
13408
|
+
async writeStopState(state3) {
|
|
13409
|
+
const fs = await import('fs/promises');
|
|
13410
|
+
const dir = this.dir();
|
|
13411
|
+
await fs.mkdir(dir, { recursive: true });
|
|
13412
|
+
const fp = resolveStopFilePath(this.directory);
|
|
13413
|
+
const tmp = `${fp}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
13414
|
+
const doc = { format: STOP_FORMAT, version: STOP_VERSION, ...state3 };
|
|
13415
|
+
try {
|
|
13416
|
+
await fs.writeFile(tmp, `${JSON.stringify(doc, null, 2)}
|
|
13417
|
+
`, "utf8");
|
|
13418
|
+
const handle = await fs.open(tmp, "r+");
|
|
13419
|
+
try {
|
|
13420
|
+
await handle.sync();
|
|
13421
|
+
} finally {
|
|
13422
|
+
await handle.close();
|
|
13423
|
+
}
|
|
13424
|
+
await fs.rename(tmp, fp);
|
|
13425
|
+
} catch (err2) {
|
|
13426
|
+
await fs.rm(tmp, { force: true }).catch(() => void 0);
|
|
13427
|
+
throw err2;
|
|
13428
|
+
}
|
|
13429
|
+
}
|
|
13430
|
+
async getStopState() {
|
|
13431
|
+
return this.readStopState();
|
|
13432
|
+
}
|
|
13433
|
+
async isStopped() {
|
|
13434
|
+
const s = await this.readStopState();
|
|
13435
|
+
return s.stopped;
|
|
13436
|
+
}
|
|
13437
|
+
async activateStop(input) {
|
|
13438
|
+
if (!requiredText4(input.actorPrincipal)) throw new GuardrailError("actorPrincipal is required");
|
|
13439
|
+
const role = await this.governance.resolveRoleAsync(input.actorPrincipal, "*");
|
|
13440
|
+
if (role.role !== "human-owner" && role.role !== "human-approver") {
|
|
13441
|
+
await this.governance.recordAuditEvent({
|
|
13442
|
+
action: "guardrail.stop.activate.denied",
|
|
13443
|
+
actorPrincipal: input.actorPrincipal,
|
|
13444
|
+
actorRole: role.role ?? void 0,
|
|
13445
|
+
outcome: "rejected",
|
|
13446
|
+
reason: "insufficient authority to activate stop",
|
|
13447
|
+
correlationId: input.correlationId
|
|
13448
|
+
}).catch(() => void 0);
|
|
13449
|
+
throw new GuardrailError("unauthorized stop activation");
|
|
13450
|
+
}
|
|
13451
|
+
const current = await this.readStopState();
|
|
13452
|
+
if (current.stopped) {
|
|
13453
|
+
await this.governance.recordAuditEvent({
|
|
13454
|
+
action: "guardrail.stop.activate",
|
|
13455
|
+
actorPrincipal: input.actorPrincipal,
|
|
13456
|
+
actorRole: role.role ?? void 0,
|
|
13457
|
+
outcome: "applied",
|
|
13458
|
+
reason: "already stopped (idempotent)",
|
|
13459
|
+
correlationId: input.correlationId
|
|
13460
|
+
}).catch(() => void 0);
|
|
13461
|
+
return current;
|
|
13462
|
+
}
|
|
13463
|
+
const now3 = nowIso3();
|
|
13464
|
+
const next = {
|
|
13465
|
+
stopped: true,
|
|
13466
|
+
activatedBy: input.actorPrincipal,
|
|
13467
|
+
activatedAt: now3,
|
|
13468
|
+
...requiredText4(input.reason) ? { reason: input.reason } : {},
|
|
13469
|
+
revision: current.revision + 1
|
|
13470
|
+
};
|
|
13471
|
+
await this.governance.recordAuditEvent({
|
|
13472
|
+
action: "guardrail.stop.activated",
|
|
13473
|
+
actorPrincipal: input.actorPrincipal,
|
|
13474
|
+
actorRole: role.role ?? void 0,
|
|
13475
|
+
outcome: "applied",
|
|
13476
|
+
reason: input.reason,
|
|
13477
|
+
correlationId: input.correlationId
|
|
13478
|
+
});
|
|
13479
|
+
await withPolicyNamespaceLock(async (session3) => {
|
|
13480
|
+
await this.writeStopState(next);
|
|
13481
|
+
await session3.assertOwnership();
|
|
13482
|
+
}, { directory: this.dir() });
|
|
13483
|
+
return next;
|
|
13484
|
+
}
|
|
13485
|
+
async releaseStop(input) {
|
|
13486
|
+
if (!requiredText4(input.actorPrincipal)) throw new GuardrailError("actorPrincipal is required");
|
|
13487
|
+
const role = await this.governance.resolveRoleAsync(input.actorPrincipal, "*");
|
|
13488
|
+
if (role.role !== "human-owner" && role.role !== "human-approver") {
|
|
13489
|
+
await this.governance.recordAuditEvent({
|
|
13490
|
+
action: "guardrail.stop.release.denied",
|
|
13491
|
+
actorPrincipal: input.actorPrincipal,
|
|
13492
|
+
actorRole: role.role ?? void 0,
|
|
13493
|
+
outcome: "rejected",
|
|
13494
|
+
reason: "insufficient authority to release stop",
|
|
13495
|
+
correlationId: input.correlationId
|
|
13496
|
+
}).catch(() => void 0);
|
|
13497
|
+
throw new GuardrailError("unauthorized stop release");
|
|
13498
|
+
}
|
|
13499
|
+
const current = await this.readStopState();
|
|
13500
|
+
if (!current.stopped) {
|
|
13501
|
+
await this.governance.recordAuditEvent({
|
|
13502
|
+
action: "guardrail.stop.release",
|
|
13503
|
+
actorPrincipal: input.actorPrincipal,
|
|
13504
|
+
actorRole: role.role ?? void 0,
|
|
13505
|
+
outcome: "applied",
|
|
13506
|
+
reason: "already running (idempotent)",
|
|
13507
|
+
correlationId: input.correlationId
|
|
13508
|
+
}).catch(() => void 0);
|
|
13509
|
+
return current;
|
|
13510
|
+
}
|
|
13511
|
+
const now3 = nowIso3();
|
|
13512
|
+
const next = {
|
|
13513
|
+
stopped: false,
|
|
13514
|
+
activatedBy: current.activatedBy,
|
|
13515
|
+
activatedAt: current.activatedAt,
|
|
13516
|
+
reason: current.reason,
|
|
13517
|
+
releasedBy: input.actorPrincipal,
|
|
13518
|
+
releasedAt: now3,
|
|
13519
|
+
revision: current.revision + 1
|
|
13520
|
+
};
|
|
13521
|
+
await this.governance.recordAuditEvent({
|
|
13522
|
+
action: "guardrail.stop.released",
|
|
13523
|
+
actorPrincipal: input.actorPrincipal,
|
|
13524
|
+
actorRole: role.role ?? void 0,
|
|
13525
|
+
outcome: "applied",
|
|
13526
|
+
reason: input.reason,
|
|
13527
|
+
correlationId: input.correlationId
|
|
13528
|
+
});
|
|
13529
|
+
await withPolicyNamespaceLock(async (session3) => {
|
|
13530
|
+
await this.writeStopState(next);
|
|
13531
|
+
await session3.assertOwnership();
|
|
13532
|
+
}, { directory: this.dir() });
|
|
13533
|
+
return next;
|
|
13534
|
+
}
|
|
13535
|
+
auditModes = {
|
|
13536
|
+
"agent.execute": "mandatory",
|
|
13537
|
+
"guardrail.stop.activate": "mandatory",
|
|
13538
|
+
"guardrail.stop.release": "mandatory"
|
|
13539
|
+
};
|
|
13540
|
+
auditModeFor(action) {
|
|
13541
|
+
return this.auditModes[action] ?? "best_effort";
|
|
13542
|
+
}
|
|
13543
|
+
async recordAudit(decision, auditMode) {
|
|
13544
|
+
const persisted = await this.auditDecision(decision);
|
|
13545
|
+
decision.auditPersisted = persisted;
|
|
13546
|
+
decision.auditMode = auditMode;
|
|
13547
|
+
if (!persisted && auditMode === "mandatory" && decision.allowed) {
|
|
13548
|
+
decision.outcome = "audit_failure";
|
|
13549
|
+
decision.allowed = false;
|
|
13550
|
+
decision.reason = "mandatory audit persistence failed";
|
|
13551
|
+
}
|
|
13552
|
+
return persisted;
|
|
13553
|
+
}
|
|
13554
|
+
// --- evaluation ---
|
|
13555
|
+
async evaluate(input) {
|
|
13556
|
+
const evaluatedAt = nowIso3();
|
|
13557
|
+
const auditMode = this.auditModeFor(input.action);
|
|
13558
|
+
const principalId = input.principalContext.trusted ? input.principalContext.trustedPrincipalId : input.principalContext.assertedPrincipalId;
|
|
13559
|
+
const stopState = await this.readStopState().catch(() => ({ stopped: false, revision: 0 }));
|
|
13560
|
+
const base = {
|
|
13561
|
+
outcome: "deny",
|
|
13562
|
+
allowed: false,
|
|
13563
|
+
reason: "",
|
|
13564
|
+
evaluatedAt,
|
|
13565
|
+
principalId,
|
|
13566
|
+
action: input.action,
|
|
13567
|
+
resource: input.resource,
|
|
13568
|
+
scope: input.scope,
|
|
13569
|
+
phase: input.phase,
|
|
13570
|
+
stopState: stopState.stopped ? "stopped" : "running",
|
|
13571
|
+
correlationId: input.correlationId,
|
|
13572
|
+
auditMode,
|
|
13573
|
+
auditPersisted: false
|
|
13574
|
+
};
|
|
13575
|
+
if (stopState.stopped && input.action !== "guardrail.stop.release") {
|
|
13576
|
+
const decision2 = { ...base, outcome: "stop_active", reason: "global stop active" };
|
|
13577
|
+
await this.recordAudit(decision2, auditMode);
|
|
13578
|
+
return decision2;
|
|
13579
|
+
}
|
|
13580
|
+
const requiresTrusted = ACTIONS_REQUIRING_TRUSTED_IDENTITY.has(input.action);
|
|
13581
|
+
if (requiresTrusted && !input.principalContext.trusted) {
|
|
13582
|
+
const decision2 = { ...base, outcome: "identity_unverified", reason: "trusted identity required but not verified" };
|
|
13583
|
+
await this.recordAudit(decision2, auditMode);
|
|
13584
|
+
return decision2;
|
|
13585
|
+
}
|
|
13586
|
+
if (!requiredText4(input.action) || !requiredText4(input.resource) || !requiredText4(input.scope)) {
|
|
13587
|
+
const decision2 = { ...base, outcome: "invalid_context", reason: "missing action/resource/scope" };
|
|
13588
|
+
await this.recordAudit(decision2, auditMode);
|
|
13589
|
+
return decision2;
|
|
13590
|
+
}
|
|
13591
|
+
let policyId;
|
|
13592
|
+
let policyVersion;
|
|
13593
|
+
try {
|
|
13594
|
+
const resolution = await this.governance.failClosedResolve(input.scope, input.now ?? /* @__PURE__ */ new Date());
|
|
13595
|
+
if (resolution.outcome !== "applicable" || !resolution.policy) {
|
|
13596
|
+
const decision2 = {
|
|
13597
|
+
...base,
|
|
13598
|
+
outcome: resolution.outcome === "conflicting" ? "deny" : "policy_unavailable",
|
|
13599
|
+
reason: `policy resolution: ${resolution.outcome} - ${resolution.reason}`
|
|
13600
|
+
};
|
|
13601
|
+
await this.recordAudit(decision2, auditMode);
|
|
13602
|
+
return decision2;
|
|
13603
|
+
}
|
|
13604
|
+
policyId = resolution.policy.policyId;
|
|
13605
|
+
policyVersion = resolution.policy.version;
|
|
13606
|
+
const actorForPermission = principalId ?? "";
|
|
13607
|
+
const allowed = await this.governance.hasPermission(actorForPermission, input.action, input.scope);
|
|
13608
|
+
if (!allowed) {
|
|
13609
|
+
const decision2 = {
|
|
13610
|
+
...base,
|
|
13611
|
+
outcome: "deny",
|
|
13612
|
+
reason: "permission denied by governance role binding",
|
|
13613
|
+
policyId,
|
|
13614
|
+
policyVersion
|
|
13615
|
+
};
|
|
13616
|
+
await this.recordAudit(decision2, auditMode);
|
|
13617
|
+
return decision2;
|
|
13618
|
+
}
|
|
13619
|
+
} catch (err2) {
|
|
13620
|
+
const decision2 = {
|
|
13621
|
+
...base,
|
|
13622
|
+
outcome: "policy_unavailable",
|
|
13623
|
+
reason: `governance unavailable: ${err2.message}`
|
|
13624
|
+
};
|
|
13625
|
+
await this.recordAudit(decision2, auditMode);
|
|
13626
|
+
return decision2;
|
|
13627
|
+
}
|
|
13628
|
+
const decision = {
|
|
13629
|
+
...base,
|
|
13630
|
+
outcome: "allow",
|
|
13631
|
+
allowed: true,
|
|
13632
|
+
reason: "allowed by governance policy",
|
|
13633
|
+
policyId,
|
|
13634
|
+
policyVersion
|
|
13635
|
+
};
|
|
13636
|
+
await this.recordAudit(decision, auditMode);
|
|
13637
|
+
return decision;
|
|
13638
|
+
}
|
|
13639
|
+
async auditDecision(decision) {
|
|
13640
|
+
try {
|
|
13641
|
+
await this.governance.recordAuditEvent({
|
|
13642
|
+
action: decision.allowed ? "guardrail.evaluation.allowed" : "guardrail.evaluation.denied",
|
|
13643
|
+
actorPrincipal: decision.principalId,
|
|
13644
|
+
scope: decision.scope,
|
|
13645
|
+
outcome: decision.allowed ? "applied" : "rejected",
|
|
13646
|
+
reason: decision.reason,
|
|
13647
|
+
detail: `${decision.action} on ${decision.resource} (${decision.outcome})`,
|
|
13648
|
+
correlationId: decision.correlationId,
|
|
13649
|
+
mandatory: decision.auditMode === "mandatory"
|
|
13650
|
+
});
|
|
13651
|
+
return true;
|
|
13652
|
+
} catch {
|
|
13653
|
+
return false;
|
|
13654
|
+
}
|
|
13655
|
+
}
|
|
13656
|
+
async execute(input, operation) {
|
|
13657
|
+
const decision = await this.evaluate(input);
|
|
13658
|
+
if (!decision.allowed) {
|
|
13659
|
+
return { decision, executed: false, error: decision.reason };
|
|
13660
|
+
}
|
|
13661
|
+
await this.governance.recordAuditEvent({
|
|
13662
|
+
action: "guardrail.operation.started",
|
|
13663
|
+
actorPrincipal: decision.principalId,
|
|
13664
|
+
scope: decision.scope,
|
|
13665
|
+
outcome: "applied",
|
|
13666
|
+
detail: `${decision.action} on ${decision.resource}`,
|
|
13667
|
+
correlationId: decision.correlationId
|
|
13668
|
+
}).catch(() => void 0);
|
|
13669
|
+
let result;
|
|
13670
|
+
try {
|
|
13671
|
+
result = await operation();
|
|
13672
|
+
} catch (err2) {
|
|
13673
|
+
await this.governance.recordAuditEvent({
|
|
13674
|
+
action: "guardrail.operation.failed",
|
|
13675
|
+
actorPrincipal: decision.principalId,
|
|
13676
|
+
scope: decision.scope,
|
|
13677
|
+
outcome: "rejected",
|
|
13678
|
+
reason: err2.message,
|
|
13679
|
+
detail: `${decision.action} on ${decision.resource}`,
|
|
13680
|
+
correlationId: decision.correlationId
|
|
13681
|
+
}).catch(() => void 0);
|
|
13682
|
+
return { decision, executed: true, error: err2.message };
|
|
13683
|
+
}
|
|
13684
|
+
await this.governance.recordAuditEvent({
|
|
13685
|
+
action: "guardrail.operation.completed",
|
|
13686
|
+
actorPrincipal: decision.principalId,
|
|
13687
|
+
scope: decision.scope,
|
|
13688
|
+
outcome: "applied",
|
|
13689
|
+
detail: `${decision.action} on ${decision.resource}`,
|
|
13690
|
+
correlationId: decision.correlationId
|
|
13691
|
+
}).catch(() => void 0);
|
|
13692
|
+
return { decision, executed: true, result };
|
|
13693
|
+
}
|
|
13694
|
+
};
|
|
13695
|
+
function createGuardrailService(directory, governance) {
|
|
13696
|
+
return new GuardrailService(directory, governance);
|
|
13697
|
+
}
|
|
13698
|
+
__name(createGuardrailService, "createGuardrailService");
|
|
13699
|
+
|
|
13700
|
+
// core/memory-anthology/index.ts
|
|
13701
|
+
var VALID_TRANSITIONS = {
|
|
13702
|
+
unassessed: ["asserted", "corroborated", "reviewed", "disputed", "refuted", "superseded"],
|
|
13703
|
+
asserted: ["corroborated", "reviewed", "disputed", "refuted", "superseded"],
|
|
13704
|
+
corroborated: ["reviewed", "disputed", "refuted", "superseded"],
|
|
13705
|
+
reviewed: ["disputed", "refuted", "superseded"],
|
|
13706
|
+
disputed: ["corroborated", "reviewed", "refuted", "superseded"],
|
|
13707
|
+
refuted: ["superseded"],
|
|
13708
|
+
superseded: []
|
|
13709
|
+
};
|
|
13710
|
+
function canTransitionEpistemic(from, to) {
|
|
13711
|
+
if (from === to) return true;
|
|
13712
|
+
return (VALID_TRANSITIONS[from] ?? []).includes(to);
|
|
13713
|
+
}
|
|
13714
|
+
__name(canTransitionEpistemic, "canTransitionEpistemic");
|
|
13715
|
+
function validateEpistemicTransition(from, to, reason) {
|
|
13716
|
+
if (from === to) return { allowed: true, reason: "no change" };
|
|
13717
|
+
if (!canTransitionEpistemic(from, to)) {
|
|
13718
|
+
return { allowed: false, reason: `invalid transition ${from} -> ${to}` };
|
|
13719
|
+
}
|
|
13720
|
+
if (to === "corroborated" && !reason) {
|
|
13721
|
+
return { allowed: false, reason: "corroborated requires explanation of independent sources" };
|
|
13722
|
+
}
|
|
13723
|
+
if (to === "reviewed" && !reason) {
|
|
13724
|
+
return { allowed: false, reason: "reviewed requires reviewer identity and scope" };
|
|
13725
|
+
}
|
|
13726
|
+
return { allowed: true, reason: reason ?? "ok" };
|
|
13727
|
+
}
|
|
13728
|
+
__name(validateEpistemicTransition, "validateEpistemicTransition");
|
|
13729
|
+
function validateProvenance(p) {
|
|
13730
|
+
const errors = [];
|
|
13731
|
+
if (!p || typeof p !== "object") {
|
|
13732
|
+
errors.push("provenance must be an object");
|
|
13733
|
+
return { valid: false, errors };
|
|
13734
|
+
}
|
|
13735
|
+
const kinds = ["user-statement", "system-observation", "imported-document", "external-source", "agent-inference", "model-summary", "human-correction", "system-event", "unknown"];
|
|
13736
|
+
if (!kinds.includes(p.kind)) errors.push("invalid provenance kind");
|
|
13737
|
+
if (p.observedAt !== void 0 && (typeof p.observedAt !== "string" || isNaN(Date.parse(p.observedAt)))) {
|
|
13738
|
+
errors.push("observedAt must be valid ISO timestamp if provided");
|
|
13739
|
+
}
|
|
13740
|
+
return { valid: errors.length === 0, errors };
|
|
13741
|
+
}
|
|
13742
|
+
__name(validateProvenance, "validateProvenance");
|
|
13743
|
+
function validateMemoryItem(item) {
|
|
13744
|
+
const errors = [];
|
|
13745
|
+
if (!item || typeof item !== "object") {
|
|
13746
|
+
errors.push("item must be an object");
|
|
13747
|
+
return { valid: false, errors };
|
|
13748
|
+
}
|
|
13749
|
+
if (typeof item.itemId !== "string" || !item.itemId.trim()) errors.push("itemId is required");
|
|
13750
|
+
if (typeof item.schemaVersion !== "number" || !Number.isInteger(item.schemaVersion) || item.schemaVersion < 1) errors.push("schemaVersion must be positive integer");
|
|
13751
|
+
if (typeof item.scope !== "string" || !item.scope.trim()) errors.push("scope is required");
|
|
13752
|
+
if (!item.temporal || typeof item.temporal !== "object") errors.push("temporal is required");
|
|
13753
|
+
else if (typeof item.temporal.recordedAt !== "string" || isNaN(Date.parse(item.temporal.recordedAt))) errors.push("recordedAt must be valid ISO timestamp");
|
|
13754
|
+
if (!item.provenance) errors.push("provenance is required");
|
|
13755
|
+
else {
|
|
13756
|
+
const pv = validateProvenance(item.provenance);
|
|
13757
|
+
if (!pv.valid) errors.push(...pv.errors);
|
|
13758
|
+
}
|
|
13759
|
+
if (!item.revision || typeof item.revision !== "object") errors.push("revision is required");
|
|
13760
|
+
else if (typeof item.revision.revision !== "number" || item.revision.revision < 0) errors.push("revision.revision must be non-negative integer");
|
|
13761
|
+
if (item.category === "derived-summary") {
|
|
13762
|
+
if (!item.derivedLineage) errors.push("derived summary requires lineage");
|
|
13763
|
+
}
|
|
13764
|
+
return { valid: errors.length === 0, errors };
|
|
13765
|
+
}
|
|
13766
|
+
__name(validateMemoryItem, "validateMemoryItem");
|
|
13767
|
+
function validateDerivedLineage(lineage, _sourceStatuses) {
|
|
13768
|
+
const errors = [];
|
|
13769
|
+
if (!lineage || typeof lineage !== "object") {
|
|
13770
|
+
errors.push("lineage required");
|
|
13771
|
+
return { valid: false, errors };
|
|
13772
|
+
}
|
|
13773
|
+
if (!Array.isArray(lineage.sourceItemIds) || lineage.sourceItemIds.length === 0) errors.push("sourceItemIds required");
|
|
13774
|
+
if (!Array.isArray(lineage.sourceRevisions) || lineage.sourceRevisions.length !== lineage.sourceItemIds.length) errors.push("sourceRevisions must match sourceItemIds length");
|
|
13775
|
+
if (typeof lineage.derivedAt !== "string" || isNaN(Date.parse(lineage.derivedAt))) errors.push("derivedAt invalid");
|
|
13776
|
+
if (typeof lineage.producingComponent !== "string" || !lineage.producingComponent.trim()) errors.push("producingComponent required");
|
|
13777
|
+
return { valid: errors.length === 0, errors };
|
|
13778
|
+
}
|
|
13779
|
+
__name(validateDerivedLineage, "validateDerivedLineage");
|
|
13780
|
+
function markDerivedStale(lineage) {
|
|
13781
|
+
return { ...lineage, stale: true };
|
|
13782
|
+
}
|
|
13783
|
+
__name(markDerivedStale, "markDerivedStale");
|
|
13784
|
+
function isLegacyRecord(item) {
|
|
13785
|
+
return !item || typeof item !== "object" || !item.provenance || !item.temporal || !item.revision;
|
|
13786
|
+
}
|
|
13787
|
+
__name(isLegacyRecord, "isLegacyRecord");
|
|
13788
|
+
function normalizeLegacyRecord(item) {
|
|
13789
|
+
return {
|
|
13790
|
+
itemId: item?.id ?? item?.itemId ?? `legacy-${Math.random().toString(36).slice(2, 10)}`,
|
|
13791
|
+
schemaVersion: 0,
|
|
13792
|
+
category: "legacy",
|
|
13793
|
+
payload: item,
|
|
13794
|
+
scope: item?.scope ?? "unknown",
|
|
13795
|
+
provenance: { kind: "unknown" },
|
|
13796
|
+
epistemicStatus: "unassessed",
|
|
13797
|
+
sensitivity: "unknown",
|
|
13798
|
+
temporal: { recordedAt: item?.createdAt ? new Date(item.createdAt).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString() },
|
|
13799
|
+
revision: { revision: 0 }
|
|
13800
|
+
};
|
|
13801
|
+
}
|
|
13802
|
+
__name(normalizeLegacyRecord, "normalizeLegacyRecord");
|
|
13803
|
+
var MEMORY_CATEGORIES = [
|
|
13804
|
+
"observation",
|
|
13805
|
+
"claim",
|
|
13806
|
+
"fact-candidate",
|
|
13807
|
+
"hypothesis",
|
|
13808
|
+
"decision",
|
|
13809
|
+
"preference",
|
|
13810
|
+
"instruction",
|
|
13811
|
+
"experience",
|
|
13812
|
+
"goal",
|
|
13813
|
+
"relationship",
|
|
13814
|
+
"derived-summary",
|
|
13815
|
+
"unknown-disputed",
|
|
13816
|
+
"legacy"
|
|
13817
|
+
];
|
|
13818
|
+
var EPITEMIC_STATUSES2 = [
|
|
13819
|
+
"unassessed",
|
|
13820
|
+
"asserted",
|
|
13821
|
+
"corroborated",
|
|
13822
|
+
"reviewed",
|
|
13823
|
+
"disputed",
|
|
13824
|
+
"refuted",
|
|
13825
|
+
"superseded"
|
|
13826
|
+
];
|
|
13827
|
+
|
|
13828
|
+
// index.ts
|
|
12021
13829
|
var dotmem = {
|
|
12022
13830
|
version: "0.0.8",
|
|
12023
13831
|
isBrowser,
|
|
@@ -12029,4 +13837,4 @@ var dotmem = {
|
|
|
12029
13837
|
};
|
|
12030
13838
|
var index_default = dotmem;
|
|
12031
13839
|
|
|
12032
|
-
export { CompactionEngine, ContextCompiler, ContextLifecycleOrchestrator, RecoveryService, addDecision, addOpenLoopRef, addRejectedApproach, addUnresolvedQuestion, appendReasoningStep, clearUserIdentity, closeConversationalState, compileContext, continuity, createCompactionEngine, createConversationalState, createNoOpHost, createRecoveryService, index_default as default, deleteConversationalState, dotmem, encryption, getCapabilities, getPlatform, idb, inspectMem, isBrowser, isBun, isDeno, isEdge, isNode, listUnfinishedConversations, loadAllConversationalStates, loadConversationalStateById, memory, recoverConversation, resolveUserAgentMemoryPath, resolveUserBrainPath, resolveUserContractPath, resolveUserDotMemPath, resolveUserExperiencePath, resolveUserIdentity, resolveUserProjectMemoryPath, resolveUserReadmePath, saveConversationalState, security, setUserIdentity, signMemPackage, updateConversationalState };
|
|
13840
|
+
export { CompactionEngine, ContextCompiler, ContextLifecycleOrchestrator, EPITEMIC_STATUSES2 as EPITEMIC_STATUSES, GOVERNANCE_POLICY_STATUSES, GOVERNANCE_ROLES, GovernancePolicyError, GovernancePolicyStore, GuardrailError, GuardrailService, MEMORY_CATEGORIES, RecoveryService, addDecision, addOpenLoopRef, addRejectedApproach, addUnresolvedQuestion, appendReasoningStep, canTransitionEpistemic, clearUserIdentity, closeConversationalState, compileContext, continuity, continuityLedger, createCompactionEngine, createConversationalState, createGuardrailService, createNoOpHost, createRecoveryService, index_default as default, deleteConversationalState, dotmem, encryption, getCapabilities, getPlatform, idb, inspectMem, isBrowser, isBun, isDeno, isEdge, isLegacyRecord, isNode, listUnfinishedConversations, loadAllConversationalStates, loadConversationalStateById, markDerivedStale, memory, normalizeLegacyRecord, recoverConversation, resolveUserAgentMemoryPath, resolveUserBrainPath, resolveUserContractPath, resolveUserDotMemPath, resolveUserExperiencePath, resolveUserIdentity, resolveUserProjectMemoryPath, resolveUserReadmePath, roleHasPermission, saveConversationalState, security, setUserIdentity, signMemPackage, updateConversationalState, validateDerivedLineage, validateEpistemicTransition, validatePolicyAuthority as validateGovernanceAuthority, validateMemoryItem, validateProvenance, validateRelationshipTargets, validateResolutionRequest };
|