@ai-agent-forge/plugin-memory 0.85.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/README.md +65 -0
- package/agent-forge.json +11 -0
- package/dist/capability.d.ts +182 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +2565 -0
- package/dist/capability.js.map +1 -0
- package/dist/entry.d.ts +36 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +154 -0
- package/dist/entry.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/assistant-card.d.ts +31 -0
- package/dist/memory/assistant-card.d.ts.map +1 -0
- package/dist/memory/assistant-card.js +108 -0
- package/dist/memory/assistant-card.js.map +1 -0
- package/dist/memory/candidates.d.ts +65 -0
- package/dist/memory/candidates.d.ts.map +1 -0
- package/dist/memory/candidates.js +100 -0
- package/dist/memory/candidates.js.map +1 -0
- package/dist/memory/code-memory.d.ts +89 -0
- package/dist/memory/code-memory.d.ts.map +1 -0
- package/dist/memory/code-memory.js +104 -0
- package/dist/memory/code-memory.js.map +1 -0
- package/dist/memory/compaction-sequencer.d.ts +63 -0
- package/dist/memory/compaction-sequencer.d.ts.map +1 -0
- package/dist/memory/compaction-sequencer.js +129 -0
- package/dist/memory/compaction-sequencer.js.map +1 -0
- package/dist/memory/continuation.d.ts +44 -0
- package/dist/memory/continuation.d.ts.map +1 -0
- package/dist/memory/continuation.js +49 -0
- package/dist/memory/continuation.js.map +1 -0
- package/dist/memory/curation.d.ts +58 -0
- package/dist/memory/curation.d.ts.map +1 -0
- package/dist/memory/curation.js +68 -0
- package/dist/memory/curation.js.map +1 -0
- package/dist/memory/egress-policy.d.ts +50 -0
- package/dist/memory/egress-policy.d.ts.map +1 -0
- package/dist/memory/egress-policy.js +71 -0
- package/dist/memory/egress-policy.js.map +1 -0
- package/dist/memory/embedding-provider.d.ts +70 -0
- package/dist/memory/embedding-provider.d.ts.map +1 -0
- package/dist/memory/embedding-provider.js +164 -0
- package/dist/memory/embedding-provider.js.map +1 -0
- package/dist/memory/embedding-reranker.d.ts +56 -0
- package/dist/memory/embedding-reranker.d.ts.map +1 -0
- package/dist/memory/embedding-reranker.js +109 -0
- package/dist/memory/embedding-reranker.js.map +1 -0
- package/dist/memory/foundation.d.ts +168 -0
- package/dist/memory/foundation.d.ts.map +1 -0
- package/dist/memory/foundation.js +487 -0
- package/dist/memory/foundation.js.map +1 -0
- package/dist/memory/host-module-import.d.ts +25 -0
- package/dist/memory/host-module-import.d.ts.map +1 -0
- package/dist/memory/host-module-import.js +41 -0
- package/dist/memory/host-module-import.js.map +1 -0
- package/dist/memory/ledger.d.ts +58 -0
- package/dist/memory/ledger.d.ts.map +1 -0
- package/dist/memory/ledger.js +315 -0
- package/dist/memory/ledger.js.map +1 -0
- package/dist/memory/lifecycle.d.ts +124 -0
- package/dist/memory/lifecycle.d.ts.map +1 -0
- package/dist/memory/lifecycle.js +201 -0
- package/dist/memory/lifecycle.js.map +1 -0
- package/dist/memory/memory-network.d.ts +55 -0
- package/dist/memory/memory-network.d.ts.map +1 -0
- package/dist/memory/memory-network.js +70 -0
- package/dist/memory/memory-network.js.map +1 -0
- package/dist/memory/model-cache-hygiene.d.ts +18 -0
- package/dist/memory/model-cache-hygiene.d.ts.map +1 -0
- package/dist/memory/model-cache-hygiene.js +38 -0
- package/dist/memory/model-cache-hygiene.js.map +1 -0
- package/dist/memory/preference-disambiguator.d.ts +43 -0
- package/dist/memory/preference-disambiguator.d.ts.map +1 -0
- package/dist/memory/preference-disambiguator.js +81 -0
- package/dist/memory/preference-disambiguator.js.map +1 -0
- package/dist/memory/preference-lifecycle.d.ts +66 -0
- package/dist/memory/preference-lifecycle.d.ts.map +1 -0
- package/dist/memory/preference-lifecycle.js +129 -0
- package/dist/memory/preference-lifecycle.js.map +1 -0
- package/dist/memory/preference-promotion.d.ts +87 -0
- package/dist/memory/preference-promotion.d.ts.map +1 -0
- package/dist/memory/preference-promotion.js +102 -0
- package/dist/memory/preference-promotion.js.map +1 -0
- package/dist/memory/preference-resolver.d.ts +44 -0
- package/dist/memory/preference-resolver.d.ts.map +1 -0
- package/dist/memory/preference-resolver.js +107 -0
- package/dist/memory/preference-resolver.js.map +1 -0
- package/dist/memory/purge-journal.d.ts +76 -0
- package/dist/memory/purge-journal.d.ts.map +1 -0
- package/dist/memory/purge-journal.js +130 -0
- package/dist/memory/purge-journal.js.map +1 -0
- package/dist/memory/purge.d.ts +90 -0
- package/dist/memory/purge.d.ts.map +1 -0
- package/dist/memory/purge.js +138 -0
- package/dist/memory/purge.js.map +1 -0
- package/dist/memory/recall-agent.d.ts +84 -0
- package/dist/memory/recall-agent.d.ts.map +1 -0
- package/dist/memory/recall-agent.js +199 -0
- package/dist/memory/recall-agent.js.map +1 -0
- package/dist/memory/recall-index.d.ts +87 -0
- package/dist/memory/recall-index.d.ts.map +1 -0
- package/dist/memory/recall-index.js +222 -0
- package/dist/memory/recall-index.js.map +1 -0
- package/dist/memory/recall-packet.d.ts +121 -0
- package/dist/memory/recall-packet.d.ts.map +1 -0
- package/dist/memory/recall-packet.js +156 -0
- package/dist/memory/recall-packet.js.map +1 -0
- package/dist/memory/scheduler-api.d.ts +99 -0
- package/dist/memory/scheduler-api.d.ts.map +1 -0
- package/dist/memory/scheduler-api.js +93 -0
- package/dist/memory/scheduler-api.js.map +1 -0
- package/dist/memory/scheduler.d.ts +55 -0
- package/dist/memory/scheduler.d.ts.map +1 -0
- package/dist/memory/scheduler.js +91 -0
- package/dist/memory/scheduler.js.map +1 -0
- package/dist/memory/store.d.ts +107 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +208 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/suite-memory.d.ts +208 -0
- package/dist/memory/suite-memory.d.ts.map +1 -0
- package/dist/memory/suite-memory.js +288 -0
- package/dist/memory/suite-memory.js.map +1 -0
- package/dist/memory/transfer.d.ts +142 -0
- package/dist/memory/transfer.d.ts.map +1 -0
- package/dist/memory/transfer.js +210 -0
- package/dist/memory/transfer.js.map +1 -0
- package/dist/memory/vector-index.d.ts +39 -0
- package/dist/memory/vector-index.d.ts.map +1 -0
- package/dist/memory/vector-index.js +136 -0
- package/dist/memory/vector-index.js.map +1 -0
- package/dist/memory/write-budget.d.ts +33 -0
- package/dist/memory/write-budget.d.ts.map +1 -0
- package/dist/memory/write-budget.js +45 -0
- package/dist/memory/write-budget.js.map +1 -0
- package/dist/testing/memory-testkit.d.ts +149 -0
- package/dist/testing/memory-testkit.d.ts.map +1 -0
- package/dist/testing/memory-testkit.js +438 -0
- package/dist/testing/memory-testkit.js.map +1 -0
- package/dist/utils/sync-sleep.d.ts +2 -0
- package/dist/utils/sync-sleep.d.ts.map +1 -0
- package/dist/utils/sync-sleep.js +11 -0
- package/dist/utils/sync-sleep.js.map +1 -0
- package/package.json +56 -0
- package/plugin.json +10 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foundation.js","sourceRoot":"","sources":["../../src/memory/foundation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Memory Foundation — canonical atom, identity, and immutable snapshots (1C.1a).\n *\n * A canonical atom is the immutable committed fact. Derived state (attention,\n * effective status, purgeAt) is NOT part of the atom; it is rebuilt from\n * lifecycle events (1C.1b/1C.2). Everything returned to hosts and plugins is a\n * detached, deep-frozen snapshot: mutating the input after `buildMemoryAtomV1`\n * never changes the stored atom.\n *\n * Field semantics follow `docs/design/记忆系统设计.md` §4 (记录模型), which is the\n * authoritative source for memory fields.\n */\nimport type { JsonValue } from \"@agent-forge/plugin-sdk\";\n\n/** Source of one memory: where the fact came from, optionally pinned by revision/digest. */\nexport interface MemorySourceRefV1 {\n\treadonly kind: \"session\" | \"entry\" | \"artifact\" | \"tool\" | \"state\";\n\treadonly id: string;\n\treadonly revision?: string;\n\treadonly digest?: string;\n}\n\n/** Namespace-scoped search facet. Facets are atom facts, never derived. */\nexport interface MemoryFacetV1 {\n\treadonly namespace: string;\n\treadonly schemaVersion: number;\n\treadonly key: string;\n\treadonly value: string;\n}\n\n/** Typed relation between atoms. Weights/validity belong to projections, not here. */\nexport interface MemoryRelationV1 {\n\treadonly relationId: string;\n\treadonly namespace: string;\n\treadonly schemaVersion: number;\n\treadonly kind: string;\n\treadonly targetMemoryId?: string;\n\treadonly targetRef?: MemorySourceRefV1;\n\treadonly confidence?: number;\n\treadonly relationRevision: string;\n}\n\nexport interface MemoryTimeRangeV1 {\n\treadonly from?: string;\n\treadonly to?: string;\n}\n\n/** Applicability narrowing; empty means \"applies without narrowing\". */\nexport interface MemoryApplicabilityV1 {\n\treadonly scenes?: readonly string[];\n\treadonly projects?: readonly string[];\n\treadonly tasks?: readonly string[];\n\treadonly timeRange?: MemoryTimeRangeV1;\n\treadonly exceptions?: readonly string[];\n}\n\n/** Preference envelope: the generic schema frozen for 1C; policy semantics come later. */\nexport interface MemoryPreferenceEnvelopeV1 {\n\treadonly subject: \"user\" | \"project\" | \"task\" | \"environment\";\n\treadonly key: string;\n\treadonly preferredValue: JsonValue;\n\treadonly alternatives?: readonly JsonValue[];\n\treadonly scope: {\n\t\treadonly level: \"task\" | \"project\" | \"scene\" | \"timeRange\" | \"profile-private\" | \"user-default\";\n\t\treadonly scenes?: readonly string[];\n\t\treadonly projects?: readonly string[];\n\t\treadonly tasks?: readonly string[];\n\t\treadonly timeRange?: MemoryTimeRangeV1;\n\t\treadonly exceptions?: readonly string[];\n\t};\n\treadonly evidence: {\n\t\treadonly class: \"explicit\" | \"repeated_behavior\" | \"inferred\";\n\t\treadonly sourceRefs: readonly MemorySourceRefV1[];\n\t\treadonly confidence: number;\n\t};\n\treadonly applicabilityConfidence: number;\n\treadonly confirmedAt?: string;\n}\n\n/** Provenance for synthesis/compaction memories derived from other atoms. */\nexport interface MemoryProvenanceV1 {\n\treadonly sourceMemoryIds: readonly string[];\n\treadonly purgeGroupId: string;\n\treadonly containsSourceContent: boolean;\n}\n\nexport type MemoryScopeV1 = \"session\" | \"cycle\" | \"long-term\";\nexport type MemoryKindV1 =\n\t| \"observation\"\n\t| \"preference\"\n\t| \"fact\"\n\t| \"decision\"\n\t| \"constraint\"\n\t| \"inference\"\n\t| \"synthesis\";\nexport type MemoryEvidenceClassV1 = \"explicit\" | \"repeated_behavior\" | \"tool_or_test\" | \"derived\";\n\n/** The immutable canonical atom. Once committed, no field may change in place. */\nexport interface MemoryAtomV1<TPayload = JsonValue> {\n\treadonly memoryId: string;\n\treadonly contractVersion: string;\n\treadonly schemaVersion: number;\n\treadonly scope: MemoryScopeV1;\n\treadonly retentionMode: string;\n\treadonly owner: string;\n\treadonly profileId: string;\n\treadonly shareGroupId?: string;\n\t/**\n\t * Suite (方案) this atom belongs to (方案系统设计 §6.1/§11, M5). Absent\n\t * (or protocol-level `null` on the wire, normalized to absent here) marks a\n\t * legacy pre-M5 atom: such atoms are fail-safe INVISIBLE to every\n\t * suite-scoped read and only readable through suite-unscoped queries. The\n\t * one cross-suite exception is an explicitly promoted user-default\n\t * preference (see {@link memorySuiteVisibility}).\n\t */\n\treadonly suiteId?: string;\n\treadonly retentionPolicyVersion: string;\n\treadonly memoryKind: MemoryKindV1;\n\treadonly payload: TPayload;\n\treadonly preference?: MemoryPreferenceEnvelopeV1;\n\treadonly occurredAt: string;\n\treadonly recordedAt: string;\n\treadonly sourceRefs: readonly MemorySourceRefV1[];\n\treadonly observationId: string;\n\treadonly sessionRefs: readonly string[];\n\treadonly agentInstanceRefs: readonly string[];\n\treadonly projectRefs: readonly string[];\n\treadonly subjectRefs: readonly string[];\n\treadonly facets: readonly MemoryFacetV1[];\n\treadonly relations: readonly MemoryRelationV1[];\n\treadonly provenance?: MemoryProvenanceV1;\n\treadonly confidence: number;\n\treadonly importance: number;\n\treadonly applicability?: MemoryApplicabilityV1;\n\treadonly evidenceClass: MemoryEvidenceClassV1;\n\treadonly contentRevision: string;\n\treadonly writeReason: string;\n}\n\ntype PlainRecord = Record<string, unknown>;\n\nfunction isPlainRecord(value: unknown): value is PlainRecord {\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\nfunction assertPlainRecord(value: unknown, label: string): asserts value is PlainRecord {\n\tif (!isPlainRecord(value)) throw new Error(`${label} must be a plain object`);\n}\n\nfunction assertExactKeys(\n\tvalue: PlainRecord,\n\trequired: readonly string[],\n\toptional: readonly string[],\n\tlabel: string,\n): void {\n\tconst allowed = new Set([...required, ...optional]);\n\tconst unsupported = Object.keys(value)\n\t\t.filter((key) => !allowed.has(key))\n\t\t.sort();\n\tif (unsupported.length > 0) throw new Error(`${label} has unsupported fields: ${unsupported.join(\", \")}`);\n\tfor (const key of required) {\n\t\tif (!(key in value)) throw new Error(`${label} is missing required field: ${key}`);\n\t}\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) throw new Error(`${label} must be a non-empty string`);\n}\n\nfunction assertPositiveSafeInteger(value: unknown, label: string): asserts value is number {\n\tif (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 1) {\n\t\tthrow new Error(`${label} must be a positive safe integer`);\n\t}\n}\n\nfunction assertUnitInterval(value: unknown, label: string): asserts value is number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value < 0 || value > 1) {\n\t\tthrow new Error(`${label} must be a number between 0 and 1`);\n\t}\n}\n\nfunction assertTimestamp(value: unknown, label: string): asserts value is string {\n\tassertNonEmptyString(value, label);\n\tif (Number.isNaN(Date.parse(value))) throw new Error(`${label} must be an ISO-8601 timestamp`);\n}\n\nfunction assertJsonValue(value: unknown, label: string, seen = new WeakSet<object>()): asserts value is JsonValue {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return;\n\tif (typeof value === \"number\") {\n\t\tif (Number.isFinite(value)) return;\n\t\tthrow new Error(`${label} must contain finite JSON numbers`);\n\t}\n\tif (typeof value !== \"object\") throw new Error(`${label} must be JSON-compatible`);\n\tif (seen.has(value)) throw new Error(`${label} must not contain cycles`);\n\tseen.add(value);\n\tif (Array.isArray(value)) {\n\t\tfor (const [index, child] of value.entries()) assertJsonValue(child, `${label}[${index}]`, seen);\n\t} else {\n\t\tif (!isPlainRecord(value)) throw new Error(`${label} must contain only plain JSON objects`);\n\t\tfor (const [key, child] of Object.entries(value)) assertJsonValue(child, `${label}.${key}`, seen);\n\t}\n\tseen.delete(value);\n}\n\nfunction assertStringArray(value: unknown, label: string): asserts value is readonly string[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tfor (const [index, item] of value.entries()) assertNonEmptyString(item, `${label}[${index}]`);\n}\n\nfunction detachedFreeze<T>(value: T, seen = new WeakMap<object, unknown>()): T {\n\tif (value === null || typeof value !== \"object\") return value;\n\tconst existing = seen.get(value);\n\t// v8 ignore next -- 校验后的原子全部由新副本构成,不存在共享引用\n\tif (existing !== undefined) return existing as T;\n\tif (Array.isArray(value)) {\n\t\tconst copy: unknown[] = [];\n\t\tseen.set(value, copy);\n\t\tfor (const child of value) copy.push(detachedFreeze(child, seen));\n\t\treturn Object.freeze(copy) as T;\n\t}\n\tconst copy: Record<string, unknown> = Object.create(null) as Record<string, unknown>;\n\tseen.set(value, copy);\n\tfor (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n\t\tcopy[key] = detachedFreeze(child, seen);\n\t}\n\treturn Object.freeze(copy) as T;\n}\n\n/** Stable JSON serialization with recursively sorted object keys (for identity digests). */\nfunction stableStringify(value: JsonValue): string {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"number\" || typeof value === \"string\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(\",\")}]`;\n\tconst entries = Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${stableStringify((value as Record<string, JsonValue>)[key])}`);\n\treturn `{${entries.join(\",\")}}`;\n}\n\nconst MEMORY_SCOPES_V1: readonly MemoryScopeV1[] = [\"session\", \"cycle\", \"long-term\"];\nconst MEMORY_KINDS_V1: readonly MemoryKindV1[] = [\n\t\"observation\",\n\t\"preference\",\n\t\"fact\",\n\t\"decision\",\n\t\"constraint\",\n\t\"inference\",\n\t\"synthesis\",\n];\nconst MEMORY_EVIDENCE_CLASSES_V1: readonly MemoryEvidenceClassV1[] = [\n\t\"explicit\",\n\t\"repeated_behavior\",\n\t\"tool_or_test\",\n\t\"derived\",\n];\nconst SOURCE_REF_KINDS: readonly MemorySourceRefV1[\"kind\"][] = [\"session\", \"entry\", \"artifact\", \"tool\", \"state\"];\n\nfunction validateSourceRef(value: unknown, label: string): MemorySourceRefV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(value, [\"kind\", \"id\"], [\"revision\", \"digest\"], label);\n\tif (!SOURCE_REF_KINDS.includes(value.kind as MemorySourceRefV1[\"kind\"])) {\n\t\tthrow new Error(`${label}.kind is invalid`);\n\t}\n\tassertNonEmptyString(value.id, `${label}.id`);\n\tif (value.revision !== undefined) assertNonEmptyString(value.revision, `${label}.revision`);\n\tif (value.digest !== undefined) assertNonEmptyString(value.digest, `${label}.digest`);\n\treturn detachedFreeze({\n\t\tkind: value.kind as MemorySourceRefV1[\"kind\"],\n\t\tid: value.id,\n\t\t...(value.revision === undefined ? {} : { revision: value.revision }),\n\t\t...(value.digest === undefined ? {} : { digest: value.digest }),\n\t});\n}\n\nfunction validateFacet(value: unknown, label: string): MemoryFacetV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(value, [\"namespace\", \"schemaVersion\", \"key\", \"value\"], [], label);\n\tassertNonEmptyString(value.namespace, `${label}.namespace`);\n\tassertPositiveSafeInteger(value.schemaVersion, `${label}.schemaVersion`);\n\tassertNonEmptyString(value.key, `${label}.key`);\n\tassertNonEmptyString(value.value, `${label}.value`);\n\treturn detachedFreeze({\n\t\tnamespace: value.namespace,\n\t\tschemaVersion: value.schemaVersion,\n\t\tkey: value.key,\n\t\tvalue: value.value,\n\t});\n}\n\nfunction validateRelation(value: unknown, label: string): MemoryRelationV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(\n\t\tvalue,\n\t\t[\"relationId\", \"namespace\", \"schemaVersion\", \"kind\", \"relationRevision\"],\n\t\t[\"targetMemoryId\", \"targetRef\", \"confidence\"],\n\t\tlabel,\n\t);\n\tassertNonEmptyString(value.relationId, `${label}.relationId`);\n\tassertNonEmptyString(value.namespace, `${label}.namespace`);\n\tassertPositiveSafeInteger(value.schemaVersion, `${label}.schemaVersion`);\n\tassertNonEmptyString(value.kind, `${label}.kind`);\n\tassertNonEmptyString(value.relationRevision, `${label}.relationRevision`);\n\tif (value.targetMemoryId !== undefined && value.targetRef !== undefined) {\n\t\tthrow new Error(`${label} must not declare both targetMemoryId and targetRef`);\n\t}\n\tconst targetMemoryId = value.targetMemoryId === undefined ? undefined : (value.targetMemoryId as string);\n\tif (targetMemoryId !== undefined) assertNonEmptyString(targetMemoryId, `${label}.targetMemoryId`);\n\tconst targetRef =\n\t\tvalue.targetRef === undefined ? undefined : validateSourceRef(value.targetRef, `${label}.targetRef`);\n\tif (value.confidence !== undefined) assertUnitInterval(value.confidence, `${label}.confidence`);\n\treturn detachedFreeze({\n\t\trelationId: value.relationId,\n\t\tnamespace: value.namespace,\n\t\tschemaVersion: value.schemaVersion,\n\t\tkind: value.kind,\n\t\t...(targetMemoryId === undefined ? {} : { targetMemoryId }),\n\t\t...(targetRef === undefined ? {} : { targetRef }),\n\t\t...(value.confidence === undefined ? {} : { confidence: value.confidence }),\n\t\trelationRevision: value.relationRevision,\n\t});\n}\n\nfunction validateTimeRange(value: unknown, label: string): MemoryTimeRangeV1 {\n\tassertPlainRecord(value, label);\n\t// Key order reversed on purpose: in the natural order the boundary-gate\n\t// lexical scanner reads this key pair as an import specifier (first key\n\t// string, comma, second key string). `assertExactKeys` builds a Set, so the\n\t// order is irrelevant to validation.\n\tassertExactKeys(value, [], [\"to\", \"from\"], label);\n\tconst from = value.from === undefined ? undefined : (value.from as string);\n\tconst to = value.to === undefined ? undefined : (value.to as string);\n\tif (from !== undefined) assertTimestamp(from, `${label}.from`);\n\tif (to !== undefined) assertTimestamp(to, `${label}.to`);\n\treturn detachedFreeze({\n\t\t...(from === undefined ? {} : { from }),\n\t\t...(to === undefined ? {} : { to }),\n\t});\n}\n\nfunction validateApplicability(value: unknown, label: string): MemoryApplicabilityV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(value, [], [\"scenes\", \"projects\", \"tasks\", \"timeRange\", \"exceptions\"], label);\n\tconst timeRange =\n\t\tvalue.timeRange === undefined ? undefined : validateTimeRange(value.timeRange, `${label}.timeRange`);\n\treturn detachedFreeze({\n\t\t...(value.scenes === undefined ? {} : { scenes: Object.freeze([...(value.scenes as readonly string[])]) }),\n\t\t...(value.projects === undefined ? {} : { projects: Object.freeze([...(value.projects as readonly string[])]) }),\n\t\t...(value.tasks === undefined ? {} : { tasks: Object.freeze([...(value.tasks as readonly string[])]) }),\n\t\t...(timeRange === undefined ? {} : { timeRange }),\n\t\t...(value.exceptions === undefined\n\t\t\t? {}\n\t\t\t: { exceptions: Object.freeze([...(value.exceptions as readonly string[])]) }),\n\t});\n}\n\nfunction validatePreferenceEnvelope(value: unknown, label: string): MemoryPreferenceEnvelopeV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(\n\t\tvalue,\n\t\t[\"subject\", \"key\", \"preferredValue\", \"scope\", \"evidence\", \"applicabilityConfidence\"],\n\t\t[\"alternatives\", \"confirmedAt\"],\n\t\tlabel,\n\t);\n\tconst subjects: readonly MemoryPreferenceEnvelopeV1[\"subject\"][] = [\"user\", \"project\", \"task\", \"environment\"];\n\tif (!subjects.includes(value.subject as MemoryPreferenceEnvelopeV1[\"subject\"])) {\n\t\tthrow new Error(`${label}.subject is invalid`);\n\t}\n\tassertNonEmptyString(value.key, `${label}.key`);\n\tassertJsonValue(value.preferredValue, `${label}.preferredValue`);\n\tif (value.alternatives !== undefined) {\n\t\tif (!Array.isArray(value.alternatives)) throw new Error(`${label}.alternatives must be an array`);\n\t\tfor (const [index, item] of value.alternatives.entries()) {\n\t\t\tassertJsonValue(item, `${label}.alternatives[${index}]`);\n\t\t}\n\t}\n\tconst scope = value.scope;\n\tassertPlainRecord(scope, `${label}.scope`);\n\tassertExactKeys(scope, [\"level\"], [\"scenes\", \"projects\", \"tasks\", \"timeRange\", \"exceptions\"], `${label}.scope`);\n\tconst levels: readonly MemoryPreferenceEnvelopeV1[\"scope\"][\"level\"][] = [\n\t\t\"task\",\n\t\t\"project\",\n\t\t\"scene\",\n\t\t\"timeRange\",\n\t\t\"profile-private\",\n\t\t\"user-default\",\n\t];\n\tif (!levels.includes(scope.level as MemoryPreferenceEnvelopeV1[\"scope\"][\"level\"])) {\n\t\tthrow new Error(`${label}.scope.level is invalid`);\n\t}\n\t// Anchor levels require their anchor to be present (记录模型约束).\n\tconst anchors: ReadonlyArray<[string, keyof PlainRecord]> = [\n\t\t[\"task\", \"tasks\"],\n\t\t[\"project\", \"projects\"],\n\t\t[\"scene\", \"scenes\"],\n\t\t[\"timeRange\", \"timeRange\"],\n\t];\n\tfor (const [level, field] of anchors) {\n\t\tif (\n\t\t\tscope.level === level &&\n\t\t\t(scope[field] === undefined || (Array.isArray(scope[field]) && scope[field].length === 0))\n\t\t) {\n\t\t\tthrow new Error(`${label}.scope.level \"${level}\" requires non-empty ${field}`);\n\t\t}\n\t}\n\tconst evidence = value.evidence;\n\tassertPlainRecord(evidence, `${label}.evidence`);\n\tassertExactKeys(evidence, [\"class\", \"sourceRefs\", \"confidence\"], [], `${label}.evidence`);\n\tconst evidenceClasses: readonly MemoryPreferenceEnvelopeV1[\"evidence\"][\"class\"][] = [\n\t\t\"explicit\",\n\t\t\"repeated_behavior\",\n\t\t\"inferred\",\n\t];\n\tif (!evidenceClasses.includes(evidence.class as MemoryPreferenceEnvelopeV1[\"evidence\"][\"class\"])) {\n\t\tthrow new Error(`${label}.evidence.class is invalid`);\n\t}\n\tif (!Array.isArray(evidence.sourceRefs) || evidence.sourceRefs.length === 0) {\n\t\tthrow new Error(`${label}.evidence.sourceRefs must not be empty`);\n\t}\n\tconst evidenceSourceRefs = evidence.sourceRefs.map((item, index) =>\n\t\tvalidateSourceRef(item, `${label}.evidence.sourceRefs[${index}]`),\n\t);\n\tassertUnitInterval(evidence.confidence, `${label}.evidence.confidence`);\n\tassertUnitInterval(value.applicabilityConfidence, `${label}.applicabilityConfidence`);\n\tif (value.confirmedAt !== undefined) assertTimestamp(value.confirmedAt, `${label}.confirmedAt`);\n\treturn detachedFreeze({\n\t\tsubject: value.subject as MemoryPreferenceEnvelopeV1[\"subject\"],\n\t\tkey: value.key,\n\t\tpreferredValue: value.preferredValue,\n\t\t...(value.alternatives === undefined ? {} : { alternatives: Object.freeze([...value.alternatives]) }),\n\t\tscope: detachedFreeze({\n\t\t\tlevel: scope.level as MemoryPreferenceEnvelopeV1[\"scope\"][\"level\"],\n\t\t\t...(scope.scenes === undefined ? {} : { scenes: Object.freeze([...(scope.scenes as readonly string[])]) }),\n\t\t\t...(scope.projects === undefined\n\t\t\t\t? {}\n\t\t\t\t: { projects: Object.freeze([...(scope.projects as readonly string[])]) }),\n\t\t\t...(scope.tasks === undefined ? {} : { tasks: Object.freeze([...(scope.tasks as readonly string[])]) }),\n\t\t\t...(scope.timeRange === undefined\n\t\t\t\t? {}\n\t\t\t\t: { timeRange: validateTimeRange(scope.timeRange, `${label}.scope.timeRange`) }),\n\t\t\t...(scope.exceptions === undefined\n\t\t\t\t? {}\n\t\t\t\t: { exceptions: Object.freeze([...(scope.exceptions as readonly string[])]) }),\n\t\t}),\n\t\tevidence: detachedFreeze({\n\t\t\tclass: evidence.class as MemoryPreferenceEnvelopeV1[\"evidence\"][\"class\"],\n\t\t\tsourceRefs: Object.freeze(evidenceSourceRefs),\n\t\t\tconfidence: evidence.confidence,\n\t\t}),\n\t\tapplicabilityConfidence: value.applicabilityConfidence,\n\t\t...(value.confirmedAt === undefined ? {} : { confirmedAt: value.confirmedAt }),\n\t}) as MemoryPreferenceEnvelopeV1;\n}\n\nfunction validateProvenance(value: unknown, label: string): MemoryProvenanceV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(value, [\"sourceMemoryIds\", \"purgeGroupId\", \"containsSourceContent\"], [], label);\n\tassertStringArray(value.sourceMemoryIds, `${label}.sourceMemoryIds`);\n\tif (value.sourceMemoryIds.length === 0) throw new Error(`${label}.sourceMemoryIds must not be empty`);\n\tassertNonEmptyString(value.purgeGroupId, `${label}.purgeGroupId`);\n\tif (typeof value.containsSourceContent !== \"boolean\") {\n\t\tthrow new Error(`${label}.containsSourceContent must be boolean`);\n\t}\n\treturn detachedFreeze({\n\t\tsourceMemoryIds: Object.freeze([...(value.sourceMemoryIds as readonly string[])]),\n\t\tpurgeGroupId: value.purgeGroupId,\n\t\tcontainsSourceContent: value.containsSourceContent,\n\t});\n}\n\n/**\n * Validates a canonical memory atom and returns a detached, deep-frozen\n * snapshot. Unknown fields, wrong enums, non-JSON payloads, preference-kind\n * atoms without a preference envelope, and identity fields (memoryId,\n * observationId, contentRevision, owner) that are not non-empty strings are\n * rejected.\n */\nexport function validateMemoryAtomV1<TPayload = JsonValue>(value: unknown): MemoryAtomV1<TPayload> {\n\tassertPlainRecord(value, \"Memory atom\");\n\tassertExactKeys(\n\t\tvalue,\n\t\t[\n\t\t\t\"memoryId\",\n\t\t\t\"contractVersion\",\n\t\t\t\"schemaVersion\",\n\t\t\t\"scope\",\n\t\t\t\"retentionMode\",\n\t\t\t\"owner\",\n\t\t\t\"profileId\",\n\t\t\t\"retentionPolicyVersion\",\n\t\t\t\"memoryKind\",\n\t\t\t\"payload\",\n\t\t\t\"occurredAt\",\n\t\t\t\"recordedAt\",\n\t\t\t\"sourceRefs\",\n\t\t\t\"observationId\",\n\t\t\t\"sessionRefs\",\n\t\t\t\"agentInstanceRefs\",\n\t\t\t\"projectRefs\",\n\t\t\t\"subjectRefs\",\n\t\t\t\"facets\",\n\t\t\t\"relations\",\n\t\t\t\"confidence\",\n\t\t\t\"importance\",\n\t\t\t\"evidenceClass\",\n\t\t\t\"contentRevision\",\n\t\t\t\"writeReason\",\n\t\t],\n\t\t[\"tenantId\", \"shareGroupId\", \"suiteId\", \"preference\", \"provenance\", \"applicability\"],\n\t\t\"Memory atom\",\n\t);\n\tassertNonEmptyString(value.memoryId, \"Memory atom.memoryId\");\n\tassertNonEmptyString(value.contractVersion, \"Memory atom.contractVersion\");\n\tassertPositiveSafeInteger(value.schemaVersion, \"Memory atom.schemaVersion\");\n\tif (!MEMORY_SCOPES_V1.includes(value.scope as MemoryScopeV1)) throw new Error(\"Memory atom.scope is invalid\");\n\tassertNonEmptyString(value.retentionMode, \"Memory atom.retentionMode\");\n\tassertNonEmptyString(value.owner, \"Memory atom.owner\");\n\tassertNonEmptyString(value.profileId, \"Memory atom.profileId\");\n\tif (value.shareGroupId !== undefined) assertNonEmptyString(value.shareGroupId, \"Memory atom.shareGroupId\");\n\t// Legacy (pre-M5) atoms may carry protocol-level `null` for \"no suite\"; it\n\t// is tolerated here and normalized to an absent field (存量语义), while a\n\t// present suiteId must be a non-empty string.\n\tif (value.suiteId !== undefined && value.suiteId !== null) {\n\t\tassertNonEmptyString(value.suiteId, \"Memory atom.suiteId\");\n\t}\n\tassertNonEmptyString(value.retentionPolicyVersion, \"Memory atom.retentionPolicyVersion\");\n\tif (!MEMORY_KINDS_V1.includes(value.memoryKind as MemoryKindV1))\n\t\tthrow new Error(\"Memory atom.memoryKind is invalid\");\n\tassertJsonValue(value.payload, \"Memory atom.payload\");\n\tif (value.memoryKind === \"preference\" && value.preference === undefined) {\n\t\tthrow new Error(\"Memory atom.memoryKind preference requires a preference envelope\");\n\t}\n\tconst preference =\n\t\tvalue.preference === undefined\n\t\t\t? undefined\n\t\t\t: validatePreferenceEnvelope(value.preference, \"Memory atom.preference\");\n\tassertTimestamp(value.occurredAt, \"Memory atom.occurredAt\");\n\tassertTimestamp(value.recordedAt, \"Memory atom.recordedAt\");\n\tif (!Array.isArray(value.sourceRefs)) throw new Error(\"Memory atom.sourceRefs must be an array\");\n\tconst sourceRefs = value.sourceRefs.map((item, index) =>\n\t\tvalidateSourceRef(item, `Memory atom.sourceRefs[${index}]`),\n\t);\n\tassertNonEmptyString(value.observationId, \"Memory atom.observationId\");\n\tassertStringArray(value.sessionRefs, \"Memory atom.sessionRefs\");\n\tassertStringArray(value.agentInstanceRefs, \"Memory atom.agentInstanceRefs\");\n\tassertStringArray(value.projectRefs, \"Memory atom.projectRefs\");\n\tassertStringArray(value.subjectRefs, \"Memory atom.subjectRefs\");\n\tif (!Array.isArray(value.facets)) throw new Error(\"Memory atom.facets must be an array\");\n\tconst facets = value.facets.map((item, index) => validateFacet(item, `Memory atom.facets[${index}]`));\n\tif (!Array.isArray(value.relations)) throw new Error(\"Memory atom.relations must be an array\");\n\tconst relations = value.relations.map((item, index) => validateRelation(item, `Memory atom.relations[${index}]`));\n\tconst provenance =\n\t\tvalue.provenance === undefined ? undefined : validateProvenance(value.provenance, \"Memory atom.provenance\");\n\tassertUnitInterval(value.confidence, \"Memory atom.confidence\");\n\tassertUnitInterval(value.importance, \"Memory atom.importance\");\n\tconst applicability =\n\t\tvalue.applicability === undefined\n\t\t\t? undefined\n\t\t\t: validateApplicability(value.applicability, \"Memory atom.applicability\");\n\tif (!MEMORY_EVIDENCE_CLASSES_V1.includes(value.evidenceClass as MemoryEvidenceClassV1)) {\n\t\tthrow new Error(\"Memory atom.evidenceClass is invalid\");\n\t}\n\tassertNonEmptyString(value.contentRevision, \"Memory atom.contentRevision\");\n\tassertNonEmptyString(value.writeReason, \"Memory atom.writeReason\");\n\n\treturn detachedFreeze({\n\t\tmemoryId: value.memoryId,\n\t\tcontractVersion: value.contractVersion,\n\t\tschemaVersion: value.schemaVersion,\n\t\tscope: value.scope as MemoryScopeV1,\n\t\tretentionMode: value.retentionMode,\n\t\towner: value.owner,\n\t\tprofileId: value.profileId,\n\t\t...(value.shareGroupId === undefined ? {} : { shareGroupId: value.shareGroupId }),\n\t\t...(value.suiteId === undefined || value.suiteId === null ? {} : { suiteId: value.suiteId }),\n\t\tretentionPolicyVersion: value.retentionPolicyVersion,\n\t\tmemoryKind: value.memoryKind as MemoryKindV1,\n\t\tpayload: value.payload as TPayload,\n\t\t...(preference === undefined ? {} : { preference }),\n\t\toccurredAt: value.occurredAt,\n\t\trecordedAt: value.recordedAt,\n\t\tsourceRefs: Object.freeze(sourceRefs),\n\t\tobservationId: value.observationId,\n\t\tsessionRefs: Object.freeze([...value.sessionRefs]),\n\t\tagentInstanceRefs: Object.freeze([...value.agentInstanceRefs]),\n\t\tprojectRefs: Object.freeze([...value.projectRefs]),\n\t\tsubjectRefs: Object.freeze([...value.subjectRefs]),\n\t\tfacets: Object.freeze(facets),\n\t\trelations: Object.freeze(relations),\n\t\t...(provenance === undefined ? {} : { provenance }),\n\t\tconfidence: value.confidence,\n\t\timportance: value.importance,\n\t\t...(applicability === undefined ? {} : { applicability }),\n\t\tevidenceClass: value.evidenceClass as MemoryEvidenceClassV1,\n\t\tcontentRevision: value.contentRevision,\n\t\twriteReason: value.writeReason,\n\t}) as MemoryAtomV1<TPayload>;\n}\n\n/** Builds a detached, frozen canonical atom (validate + freeze). */\nexport function buildMemoryAtomV1<TPayload = JsonValue>(atom: MemoryAtomV1<TPayload>): MemoryAtomV1<TPayload> {\n\treturn validateMemoryAtomV1<TPayload>(atom);\n}\n\n/** FNV-1a 64-bit digest over the stable JSON serialization of `value`. */\nexport function memoryContentDigest(value: JsonValue): string {\n\tconst serialized = stableStringify(value);\n\tlet digest = 14695981039346656037n;\n\tfor (const byte of new TextEncoder().encode(serialized)) {\n\t\tdigest ^= BigInt(byte);\n\t\tdigest = BigInt.asUintN(64, digest * 1099511628211n);\n\t}\n\treturn digest.toString(16).padStart(16, \"0\");\n}\n\n/** Idempotent submission identity: the same (owner, observationId) is one observation. */\nexport function memoryObservationKey(owner: string, observationId: string): string {\n\tassertNonEmptyString(owner, \"owner\");\n\tassertNonEmptyString(observationId, \"observationId\");\n\treturn `${owner}\\u0000${observationId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Suite (方案) read boundary (方案系统设计 §6.1/§11, M5)\n// ---------------------------------------------------------------------------\n\n/** Why a record was excluded from a suite-scoped read (读取边界惰性可观测). */\nexport type MemorySuiteVisibilityV1 =\n\t/** The atom matches the queried suite, or is an explicitly promoted user-default preference. */\n\t| \"visible\"\n\t/** Legacy (pre-M5) atom without a suiteId — fail-safe invisible to every suite. */\n\t| \"legacy\"\n\t/** The atom belongs to a different suite. */\n\t| \"foreign-suite\";\n\n/**\n * Skipped-record counters attached to suite-scoped reads. The read boundary is\n * lazy and observable (设计 §11 三段式): excluded atoms are counted, never\n * silently dropped.\n */\nexport interface MemorySuiteFilterStatsV1 {\n\t/** Legacy (suiteId-less) atoms skipped as invisible to any suite. */\n\treadonly legacySkipped: number;\n\t/** Atoms bound to a different suiteId that were skipped. */\n\treadonly foreignSuiteSkipped: number;\n}\n\n/**\n * Suite-scoped visibility of one atom (方案系统设计 §6.1/§11):\n * - `atom.suiteId === suiteId` → visible;\n * - an explicitly promoted user-default preference (scope.level\n * \"user-default\" WITH `confirmedAt` — the canonical explicit-confirmation\n * marker set only by the explicit promotion channel) ignores the suiteId\n * filter and is visible in every suite (§6.1 跨方案偏好);\n * - absent suiteId (legacy/存量) → invisible to every suite (fail-safe\n * default), counted via {@link MemorySuiteFilterStatsV1};\n * - any other suiteId → invisible.\n */\nexport function memorySuiteVisibility(atom: MemoryAtomV1, suiteId: string): MemorySuiteVisibilityV1 {\n\tassertNonEmptyString(suiteId, \"suiteId\");\n\tif (\n\t\tatom.memoryKind === \"preference\" &&\n\t\tatom.preference?.scope.level === \"user-default\" &&\n\t\tatom.preference.confirmedAt !== undefined\n\t) {\n\t\treturn \"visible\";\n\t}\n\tif (atom.suiteId === undefined) return \"legacy\";\n\tif (atom.suiteId === suiteId) return \"visible\";\n\treturn \"foreign-suite\";\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-injected heavy-dependency import (D-075 安装店重依赖可见性).
|
|
3
|
+
*
|
|
4
|
+
* Installed plugin copies live in the install store, which carries no
|
|
5
|
+
* node_modules, so a bare specifier cannot resolve from the plugin's own
|
|
6
|
+
* location at runtime. The host loader injects the entry paths it resolved
|
|
7
|
+
* against its own installation (manifest config `hostDependencyPaths`); when
|
|
8
|
+
* such a path is present we import the absolute file directly — native
|
|
9
|
+
* `import()` of a file URL needs no node_modules lookup. Without an injected
|
|
10
|
+
* path (source/workspace loads) the bare specifier is imported unchanged.
|
|
11
|
+
*
|
|
12
|
+
* Windows absolute paths must be converted to file:// URLs for import().
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* 绝对路径 specifier 判定 (窄边界: 绝对路径 vs 裸包名):
|
|
16
|
+
* - 盘符形态 `C:\...` / `C:/...`
|
|
17
|
+
* - 正斜杠前缀 `/...` (POSIX 绝对, 含正斜杠 UNC `//server/share/...`)
|
|
18
|
+
* - 反斜杠 UNC `\\server\share\...` — 宿主 createRequire().resolve() 在网络
|
|
19
|
+
* 位置安装时产出的形态; pathToFileURL 可正确转换。
|
|
20
|
+
* 相对路径 (`./x`, `../x`) 与裸包名 (`name`, `@scope/name`) 不在此列, 走裸
|
|
21
|
+
* import 由 Node 解析。纯函数, 不触发真实 import, 可直接单测。
|
|
22
|
+
*/
|
|
23
|
+
export declare function isAbsoluteModuleSpecifier(specifier: string): boolean;
|
|
24
|
+
export declare function importHostOrBareModule<T>(specifier: string): Promise<T>;
|
|
25
|
+
//# sourceMappingURL=host-module-import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-module-import.d.ts","sourceRoot":"","sources":["../../src/memory/host-module-import.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAsB,sBAAsB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAK7E","sourcesContent":["import { pathToFileURL } from \"node:url\";\n\n/**\n * Host-injected heavy-dependency import (D-075 安装店重依赖可见性).\n *\n * Installed plugin copies live in the install store, which carries no\n * node_modules, so a bare specifier cannot resolve from the plugin's own\n * location at runtime. The host loader injects the entry paths it resolved\n * against its own installation (manifest config `hostDependencyPaths`); when\n * such a path is present we import the absolute file directly — native\n * `import()` of a file URL needs no node_modules lookup. Without an injected\n * path (source/workspace loads) the bare specifier is imported unchanged.\n *\n * Windows absolute paths must be converted to file:// URLs for import().\n */\n\n/**\n * 绝对路径 specifier 判定 (窄边界: 绝对路径 vs 裸包名):\n * - 盘符形态 `C:\\...` / `C:/...`\n * - 正斜杠前缀 `/...` (POSIX 绝对, 含正斜杠 UNC `//server/share/...`)\n * - 反斜杠 UNC `\\\\server\\share\\...` — 宿主 createRequire().resolve() 在网络\n * 位置安装时产出的形态; pathToFileURL 可正确转换。\n * 相对路径 (`./x`, `../x`) 与裸包名 (`name`, `@scope/name`) 不在此列, 走裸\n * import 由 Node 解析。纯函数, 不触发真实 import, 可直接单测。\n */\nexport function isAbsoluteModuleSpecifier(specifier: string): boolean {\n\treturn /^[a-zA-Z]:[\\\\/]/.test(specifier) || specifier.startsWith(\"/\") || specifier.startsWith(\"\\\\\\\\\");\n}\n\nexport async function importHostOrBareModule<T>(specifier: string): Promise<T> {\n\tif (isAbsoluteModuleSpecifier(specifier)) {\n\t\treturn (await import(pathToFileURL(specifier).href)) as T;\n\t}\n\treturn (await import(specifier)) as T;\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
/**
|
|
11
|
+
* Host-injected heavy-dependency import (D-075 安装店重依赖可见性).
|
|
12
|
+
*
|
|
13
|
+
* Installed plugin copies live in the install store, which carries no
|
|
14
|
+
* node_modules, so a bare specifier cannot resolve from the plugin's own
|
|
15
|
+
* location at runtime. The host loader injects the entry paths it resolved
|
|
16
|
+
* against its own installation (manifest config `hostDependencyPaths`); when
|
|
17
|
+
* such a path is present we import the absolute file directly — native
|
|
18
|
+
* `import()` of a file URL needs no node_modules lookup. Without an injected
|
|
19
|
+
* path (source/workspace loads) the bare specifier is imported unchanged.
|
|
20
|
+
*
|
|
21
|
+
* Windows absolute paths must be converted to file:// URLs for import().
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* 绝对路径 specifier 判定 (窄边界: 绝对路径 vs 裸包名):
|
|
25
|
+
* - 盘符形态 `C:\...` / `C:/...`
|
|
26
|
+
* - 正斜杠前缀 `/...` (POSIX 绝对, 含正斜杠 UNC `//server/share/...`)
|
|
27
|
+
* - 反斜杠 UNC `\\server\share\...` — 宿主 createRequire().resolve() 在网络
|
|
28
|
+
* 位置安装时产出的形态; pathToFileURL 可正确转换。
|
|
29
|
+
* 相对路径 (`./x`, `../x`) 与裸包名 (`name`, `@scope/name`) 不在此列, 走裸
|
|
30
|
+
* import 由 Node 解析。纯函数, 不触发真实 import, 可直接单测。
|
|
31
|
+
*/
|
|
32
|
+
export function isAbsoluteModuleSpecifier(specifier) {
|
|
33
|
+
return /^[a-zA-Z]:[\\/]/.test(specifier) || specifier.startsWith("/") || specifier.startsWith("\\\\");
|
|
34
|
+
}
|
|
35
|
+
export async function importHostOrBareModule(specifier) {
|
|
36
|
+
if (isAbsoluteModuleSpecifier(specifier)) {
|
|
37
|
+
return (await import(__rewriteRelativeImportExtension(pathToFileURL(specifier).href)));
|
|
38
|
+
}
|
|
39
|
+
return (await import(__rewriteRelativeImportExtension(specifier)));
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=host-module-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-module-import.js","sourceRoot":"","sources":["../../src/memory/host-module-import.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;;;;;;GAYG;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAAiB,EAAW;IACrE,OAAO,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAAA,CACtG;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAI,SAAiB,EAAc;IAC9E,IAAI,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,MAAM,MAAM,kCAAC,aAAa,CAAC,SAAS,CAAC,CAAC,IAAI,EAAC,CAAM,CAAC;IAC3D,CAAC;IACD,OAAO,CAAC,MAAM,MAAM,kCAAC,SAAS,EAAC,CAAM,CAAC;AAAA,CACtC","sourcesContent":["import { pathToFileURL } from \"node:url\";\n\n/**\n * Host-injected heavy-dependency import (D-075 安装店重依赖可见性).\n *\n * Installed plugin copies live in the install store, which carries no\n * node_modules, so a bare specifier cannot resolve from the plugin's own\n * location at runtime. The host loader injects the entry paths it resolved\n * against its own installation (manifest config `hostDependencyPaths`); when\n * such a path is present we import the absolute file directly — native\n * `import()` of a file URL needs no node_modules lookup. Without an injected\n * path (source/workspace loads) the bare specifier is imported unchanged.\n *\n * Windows absolute paths must be converted to file:// URLs for import().\n */\n\n/**\n * 绝对路径 specifier 判定 (窄边界: 绝对路径 vs 裸包名):\n * - 盘符形态 `C:\\...` / `C:/...`\n * - 正斜杠前缀 `/...` (POSIX 绝对, 含正斜杠 UNC `//server/share/...`)\n * - 反斜杠 UNC `\\\\server\\share\\...` — 宿主 createRequire().resolve() 在网络\n * 位置安装时产出的形态; pathToFileURL 可正确转换。\n * 相对路径 (`./x`, `../x`) 与裸包名 (`name`, `@scope/name`) 不在此列, 走裸\n * import 由 Node 解析。纯函数, 不触发真实 import, 可直接单测。\n */\nexport function isAbsoluteModuleSpecifier(specifier: string): boolean {\n\treturn /^[a-zA-Z]:[\\\\/]/.test(specifier) || specifier.startsWith(\"/\") || specifier.startsWith(\"\\\\\\\\\");\n}\n\nexport async function importHostOrBareModule<T>(specifier: string): Promise<T> {\n\tif (isAbsoluteModuleSpecifier(specifier)) {\n\t\treturn (await import(pathToFileURL(specifier).href)) as T;\n\t}\n\treturn (await import(specifier)) as T;\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { MemoryAtomV1 } from "./foundation.ts";
|
|
2
|
+
/** Sentinel suite domain for sessions without a suite binding (设计 §6.1). */
|
|
3
|
+
export declare const LEGACY_MEMORY_DOMAIN = "legacy";
|
|
4
|
+
/** Result of {@link DurableMemoryLedgerV1.load}: replayable atoms plus skip diagnostics. */
|
|
5
|
+
export interface DurableMemoryLedgerLoadResultV1 {
|
|
6
|
+
/** Valid atoms in file order — replay these into a fresh store. */
|
|
7
|
+
readonly atoms: readonly MemoryAtomV1[];
|
|
8
|
+
/** Malformed lines (invalid JSON or invalid atoms) skipped during load. */
|
|
9
|
+
readonly corruptedLines: number;
|
|
10
|
+
/** Atoms whose memoryId was already loaded (crash windows can duplicate appends). */
|
|
11
|
+
readonly duplicateSkipped: number;
|
|
12
|
+
/** True when the file existed but could not be read (permissions, IO error). */
|
|
13
|
+
readonly unreadable: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface DurableMemoryLedgerV1 {
|
|
16
|
+
/**
|
|
17
|
+
* Reads the ledger file and returns the replayable atom set plus skip
|
|
18
|
+
* counters. A missing file is an empty ledger, not an error; malformed or
|
|
19
|
+
* duplicate lines are skipped and counted — load NEVER throws.
|
|
20
|
+
*/
|
|
21
|
+
load(): DurableMemoryLedgerLoadResultV1;
|
|
22
|
+
/**
|
|
23
|
+
* Appends one committed atom as a single JSONL line (strictly validated).
|
|
24
|
+
* The write serializes against other processes through the `.lock` sibling
|
|
25
|
+
* file and is fsynced before close, so an acknowledged append survives a
|
|
26
|
+
* hard crash; a lock timeout or IO failure throws (an append never fails
|
|
27
|
+
* silently).
|
|
28
|
+
*/
|
|
29
|
+
append(atom: MemoryAtomV1): void;
|
|
30
|
+
/**
|
|
31
|
+
* Physically rewrites the whole file with the given atoms (atomic tmp-file
|
|
32
|
+
* + rename). The forget/purge flow calls this after removing atoms so the
|
|
33
|
+
* durable replica matches the surviving canonical set. The rewrite holds
|
|
34
|
+
* the same cross-process lock as append for its whole duration (same
|
|
35
|
+
* timeout and stale-lock-stealing semantics), so a competing writer's
|
|
36
|
+
* append cannot be lost inside the rewrite window, and the new content is
|
|
37
|
+
* fsynced before the rename.
|
|
38
|
+
*/
|
|
39
|
+
rewrite(atoms: readonly MemoryAtomV1[]): void;
|
|
40
|
+
/** The ledger's current in-memory atom set (append/rewrite keep it in sync). */
|
|
41
|
+
atomsSnapshot(): readonly MemoryAtomV1[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Creates a durable JSONL memory ledger at `path`. `now` suffixes the rewrite
|
|
45
|
+
* tmp file so deterministic tests can pin it; it never participates in atom
|
|
46
|
+
* identity (timestamps live inside the canonical atoms) and never drives the
|
|
47
|
+
* append lock, which is a real-time mechanism. `lockTimeoutMs` bounds the
|
|
48
|
+
* wall-clock wait for a competing writer's lock; `staleLockMs` is the age at
|
|
49
|
+
* which a lock counts as abandoned and is stolen. Both exist so tests can
|
|
50
|
+
* exercise the timeout and steal paths quickly.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createDurableMemoryLedger(options: {
|
|
53
|
+
readonly path: string;
|
|
54
|
+
readonly now?: () => number;
|
|
55
|
+
readonly lockTimeoutMs?: number;
|
|
56
|
+
readonly staleLockMs?: number;
|
|
57
|
+
}): DurableMemoryLedgerV1;
|
|
58
|
+
//# sourceMappingURL=ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/memory/ledger.ts"],"names":[],"mappings":"AA6DA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,iFAA4E;AAC5E,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAkC7C,4FAA4F;AAC5F,MAAM,WAAW,+BAA+B;IAC/C,qEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,qFAAqF;IACrF,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACrC;;;;OAIG;IACH,IAAI,IAAI,+BAA+B,CAAC;IACxC;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CAAC;IAC9C,gFAAgF;IAChF,aAAa,IAAI,SAAS,YAAY,EAAE,CAAC;CACzC;AAMD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B,GAAG,qBAAqB,CA+MxB","sourcesContent":["/**\n * Durable memory ledger (统一修复轮 A, 方案系统设计 §6.1 + 方案实施计划 §11 A) —\n * the append-only JSONL persistent replica of a Memory Foundation store.\n *\n * Layout: `<agentDir>/memory/<owner>/ledger-<suiteId|\"legacy\">.jsonl`. The\n * owner segment is the store/facade isolation principal (one local user per\n * agentDir in this product); the suite segment is the 方案 read-boundary\n * domain — a real suite id, or the sentinel \"legacy\" for sessions without a\n * suite binding. One line holds exactly one committed canonical atom\n * (contract @2, including `suiteId`).\n *\n * Replica semantics: the write path appends every committed atom (canonical\n * copy on disk); the read path `load()`s the atoms at startup and replays them\n * into a fresh store. After a forget, `rewrite(atom[])` physically rewrites\n * the file so purged atoms cannot survive a restart. Corruption tolerance:\n * malformed lines are skipped and counted, duplicates are skipped and counted\n * — loading never fails. Only the WRITE path validates strictly: a ledger must\n * never persist an atom the Foundation would reject.\n *\n * Concurrency and durability envelope (统一修复轮 H1, #7 深度修复; rewrite 共\n * 锁 + fsync 关洞修复): competing writers — append AND rewrite — serialize\n * through a `.lock` sibling file taken via exclusive creation (`wx` — atomic\n * on every supported platform). A second writer polls (25 ms) until the\n * holder removes the lock, steals a lock whose mtime is older than\n * `staleLockMs` (default 60 s — a crashed writer left it behind; a live\n * holder keeps the lock for milliseconds), and throws after `lockTimeoutMs`\n * (default 5 s, configurable) — a writer never silently blocks and never\n * silently drops its row; lock or IO failures propagate to the caller. The\n * lock is wall-clock based (real waiting), not the injected deterministic\n * `now`. Durability: both write paths run open → write → fsync → close, so the\n * file contents of an acknowledged append or rewrite survive a hard crash,\n * subject to the directory-entry boundary declared below (a torn trailing\n * line, possible only in files written by older builds or damaged externally,\n * is still skipped by the loader's corruption tolerance).\n * Remaining boundaries, declared honestly: the exclusive create is a mutex\n * only where exclusive creation is atomic — local filesystems; multi-host or\n * network-share access is out of scope. `rewrite` still publishes the calling\n * process's atom set as the whole file: rows another process appended since\n * this process last loaded are not merged into a rewrite (the forget caller\n * owns that reconciliation) — the lock closes the interleaving window, not\n * stale in-memory views. And fsync persists file contents, not directory\n * entries (Windows cannot fsync a directory): a crash between rewrite's fsync\n * and its rename leaves the previous file intact, never a half-written one.\n * An fsync failure on append propagates after the row has already reached the\n * OS: the unacknowledged row may be readable, and a caller retry appends it\n * again — the loader's duplicate tolerance (`duplicateSkipped`) absorbs that.\n */\nimport {\n\tcloseSync,\n\tfsyncSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\trenameSync,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n\twriteSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { sleepSync } from \"../utils/sync-sleep.ts\";\nimport type { MemoryAtomV1 } from \"./foundation.ts\";\nimport { validateMemoryAtomV1 } from \"./foundation.ts\";\n\n/** Sentinel suite domain for sessions without a suite binding (设计 §6.1). */\nexport const LEGACY_MEMORY_DOMAIN = \"legacy\";\n\n/** Suffix of the append lock sibling file (`<ledgerPath>.lock`). */\nconst LOCK_FILE_SUFFIX = \".lock\";\n/**\n * Wall-clock wait between lock acquisition attempts. Kept as a per-file\n * constant even though the durable store backend currently uses the same\n * 25 ms value: the two lock protocols stay independent.\n */\nconst LOCK_POLL_INTERVAL_MS = 25;\n/** Default ceiling on waiting for a competing writer's lock. */\nconst DEFAULT_LOCK_TIMEOUT_MS = 5000;\n/**\n * Default age at which a lock counts as abandoned (its writer crashed while\n * holding it). Deliberately far above any live hold time (milliseconds of\n * appendFileSync) and above the wait timeout, so a waiter fails loudly at the\n * timeout instead of stealing a lock that might still be held.\n */\nconst DEFAULT_STALE_LOCK_MS = 60_000;\n\n/**\n * Writes every byte of `data` to the fd, looping over partial `writeSync`\n * results (a short write would silently break the fsync durability promise:\n * the caller fsyncs and closes only after the full payload is handed to the\n * OS).\n */\nconst writeAllSync = (fd: number, data: string): void => {\n\tconst buffer = Buffer.from(data, \"utf8\");\n\tlet written = 0;\n\twhile (written < buffer.length) {\n\t\twritten += writeSync(fd, buffer, written, buffer.length - written);\n\t}\n};\n\n/** Result of {@link DurableMemoryLedgerV1.load}: replayable atoms plus skip diagnostics. */\nexport interface DurableMemoryLedgerLoadResultV1 {\n\t/** Valid atoms in file order — replay these into a fresh store. */\n\treadonly atoms: readonly MemoryAtomV1[];\n\t/** Malformed lines (invalid JSON or invalid atoms) skipped during load. */\n\treadonly corruptedLines: number;\n\t/** Atoms whose memoryId was already loaded (crash windows can duplicate appends). */\n\treadonly duplicateSkipped: number;\n\t/** True when the file existed but could not be read (permissions, IO error). */\n\treadonly unreadable: boolean;\n}\n\nexport interface DurableMemoryLedgerV1 {\n\t/**\n\t * Reads the ledger file and returns the replayable atom set plus skip\n\t * counters. A missing file is an empty ledger, not an error; malformed or\n\t * duplicate lines are skipped and counted — load NEVER throws.\n\t */\n\tload(): DurableMemoryLedgerLoadResultV1;\n\t/**\n\t * Appends one committed atom as a single JSONL line (strictly validated).\n\t * The write serializes against other processes through the `.lock` sibling\n\t * file and is fsynced before close, so an acknowledged append survives a\n\t * hard crash; a lock timeout or IO failure throws (an append never fails\n\t * silently).\n\t */\n\tappend(atom: MemoryAtomV1): void;\n\t/**\n\t * Physically rewrites the whole file with the given atoms (atomic tmp-file\n\t * + rename). The forget/purge flow calls this after removing atoms so the\n\t * durable replica matches the surviving canonical set. The rewrite holds\n\t * the same cross-process lock as append for its whole duration (same\n\t * timeout and stale-lock-stealing semantics), so a competing writer's\n\t * append cannot be lost inside the rewrite window, and the new content is\n\t * fsynced before the rename.\n\t */\n\trewrite(atoms: readonly MemoryAtomV1[]): void;\n\t/** The ledger's current in-memory atom set (append/rewrite keep it in sync). */\n\tatomsSnapshot(): readonly MemoryAtomV1[];\n}\n\nfunction assertNonEmptyString(value: unknown, label: string): asserts value is string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) throw new Error(`${label} must be a non-empty string`);\n}\n\n/**\n * Creates a durable JSONL memory ledger at `path`. `now` suffixes the rewrite\n * tmp file so deterministic tests can pin it; it never participates in atom\n * identity (timestamps live inside the canonical atoms) and never drives the\n * append lock, which is a real-time mechanism. `lockTimeoutMs` bounds the\n * wall-clock wait for a competing writer's lock; `staleLockMs` is the age at\n * which a lock counts as abandoned and is stolen. Both exist so tests can\n * exercise the timeout and steal paths quickly.\n */\nexport function createDurableMemoryLedger(options: {\n\treadonly path: string;\n\treadonly now?: () => number;\n\treadonly lockTimeoutMs?: number;\n\treadonly staleLockMs?: number;\n}): DurableMemoryLedgerV1 {\n\tassertNonEmptyString(options.path, \"Durable ledger path\");\n\tif (options.lockTimeoutMs !== undefined && (!Number.isFinite(options.lockTimeoutMs) || options.lockTimeoutMs <= 0)) {\n\t\tthrow new Error(\"Durable ledger lockTimeoutMs must be a positive number\");\n\t}\n\tif (options.staleLockMs !== undefined && (!Number.isFinite(options.staleLockMs) || options.staleLockMs <= 0)) {\n\t\tthrow new Error(\"Durable ledger staleLockMs must be a positive number\");\n\t}\n\tconst now = options.now ?? (() => Date.now());\n\tconst path = options.path;\n\tconst lockTimeoutMs = options.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n\tconst staleLockMs = options.staleLockMs ?? DEFAULT_STALE_LOCK_MS;\n\tconst lockPath = `${path}${LOCK_FILE_SUFFIX}`;\n\t/** In-memory replica of the durable atom set; kept in sync by append/rewrite/load. */\n\tlet atoms: MemoryAtomV1[] = [];\n\n\tconst ensureDir = (): void => {\n\t\tmkdirSync(dirname(path), { recursive: true });\n\t};\n\n\t/**\n\t * Takes the ledger writer lock, shared by append and rewrite: exclusive\n\t * create (`wx`) is the mutex; an existing lock is polled until released,\n\t * stolen once its mtime exceeds `staleLockMs` (crashed writer), or turned\n\t * into a thrown error at the wait deadline. The lock file carries a\n\t * per-acquisition token so a stale-steal victim releasing late can never\n\t * delete the thief's fresh lock.\n\t */\n\tconst acquireAppendLock = (): string => {\n\t\tconst deadline = Date.now() + lockTimeoutMs;\n\t\tfor (;;) {\n\t\t\tensureDir();\n\t\t\tconst token = `${process.pid}:${Date.now()}`;\n\t\t\ttry {\n\t\t\t\tconst fd = openSync(lockPath, \"wx\");\n\t\t\t\ttry {\n\t\t\t\t\twriteFileSync(fd, token);\n\t\t\t\t} finally {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t}\n\t\t\t\treturn token;\n\t\t\t} catch (error) {\n\t\t\t\t// POSIX maps a lost create race to EEXIST; Windows/libuv maps the\n\t\t\t\t// same concurrent-create sharing violation to EPERM. Both mean the\n\t\t\t\t// lock file existed when we tried to create it — contend (poll /\n\t\t\t\t// stale-steal below). For EPERM the existence check re-stats: a\n\t\t\t\t// lock still there is contention, while ENOENT means the holder\n\t\t\t\t// released and deleted it between the failed open and this stat\n\t\t\t\t// (or Windows briefly left a delete-pending name that has now\n\t\t\t\t// gone) — the same vanished-lock window the EEXIST path tolerates,\n\t\t\t\t// so retry too. A stat error other than ENOENT means the EPERM was\n\t\t\t\t// a genuine permission failure and propagates, and the vanish path\n\t\t\t\t// still rethrows the original EPERM at the wait deadline: a\n\t\t\t\t// persistent EPERM with no lock file ever appearing is a genuine\n\t\t\t\t// permission failure, not a held lock.\n\t\t\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\t\t\tif (code === \"EPERM\") {\n\t\t\t\t\tlet vanished = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstatSync(lockPath);\n\t\t\t\t\t} catch (statError) {\n\t\t\t\t\t\tif ((statError as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\t\t\t\tvanished = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (vanished) {\n\t\t\t\t\t\tif (Date.now() >= deadline) throw error;\n\t\t\t\t\t\tsleepSync(LOCK_POLL_INTERVAL_MS);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} else if (code !== \"EEXIST\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlet stale = false;\n\t\t\t\ttry {\n\t\t\t\t\tstale = Date.now() - statSync(lockPath).mtimeMs > staleLockMs;\n\t\t\t\t} catch {\n\t\t\t\t\t// The holder released the lock between the failed open and the\n\t\t\t\t\t// stat — retry the exclusive create immediately.\n\t\t\t\t}\n\t\t\t\tif (stale) {\n\t\t\t\t\t// A crashed writer left the lock behind: steal it. A competing\n\t\t\t\t\t// waiter may steal first — the next exclusive create resolves that.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tunlinkSync(lockPath);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Already stolen — retry.\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() >= deadline) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`memory ledger append lock timeout after ${lockTimeoutMs} ms: ${lockPath} is held by another writer`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tsleepSync(LOCK_POLL_INTERVAL_MS);\n\t\t\t}\n\t\t}\n\t};\n\n\t/** Releases the append lock, but only if our own token still owns it. */\n\tconst releaseAppendLock = (token: string): void => {\n\t\ttry {\n\t\t\tif (readFileSync(lockPath, \"utf8\") === token) unlinkSync(lockPath);\n\t\t} catch {\n\t\t\t// The lock is already gone (or unreadable) — nothing to release.\n\t\t}\n\t};\n\n\tconst parseAtomLine = (line: string): MemoryAtomV1 | undefined => {\n\t\ttry {\n\t\t\treturn validateMemoryAtomV1(JSON.parse(line));\n\t\t} catch {\n\t\t\t// Corruption tolerance: a malformed line is skipped and counted by the\n\t\t\t// caller — a damaged ledger must never fail the startup replay.\n\t\t\treturn undefined;\n\t\t}\n\t};\n\n\treturn {\n\t\tload() {\n\t\t\tlet raw: string;\n\t\t\ttry {\n\t\t\t\traw = readFileSync(path, \"utf8\");\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\t\tatoms = [];\n\t\t\t\t\treturn { atoms: Object.freeze([]), corruptedLines: 0, duplicateSkipped: 0, unreadable: false };\n\t\t\t\t}\n\t\t\t\tatoms = [];\n\t\t\t\treturn { atoms: Object.freeze([]), corruptedLines: 0, duplicateSkipped: 0, unreadable: true };\n\t\t\t}\n\t\t\tconst loaded: MemoryAtomV1[] = [];\n\t\t\tconst seen = new Set<string>();\n\t\t\tlet corruptedLines = 0;\n\t\t\tlet duplicateSkipped = 0;\n\t\t\tfor (const line of raw.split(\"\\n\")) {\n\t\t\t\tif (line.trim() === \"\") continue;\n\t\t\t\tconst atom = parseAtomLine(line);\n\t\t\t\tif (!atom) {\n\t\t\t\t\tcorruptedLines += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (seen.has(atom.memoryId)) {\n\t\t\t\t\tduplicateSkipped += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseen.add(atom.memoryId);\n\t\t\t\tloaded.push(atom);\n\t\t\t}\n\t\t\tatoms = loaded;\n\t\t\treturn {\n\t\t\t\tatoms: Object.freeze([...loaded]),\n\t\t\t\tcorruptedLines,\n\t\t\t\tduplicateSkipped,\n\t\t\t\tunreadable: false,\n\t\t\t};\n\t\t},\n\t\tappend(atom) {\n\t\t\tconst validated = validateMemoryAtomV1(atom);\n\t\t\tconst token = acquireAppendLock();\n\t\t\ttry {\n\t\t\t\t// One committed atom per line; the trailing newline keeps the file\n\t\t\t\t// a well-formed JSONL stream for crash-truncated-tail recovery.\n\t\t\t\t// The line is fsynced before close: once append returns, a hard\n\t\t\t\t// crash cannot lose it.\n\t\t\t\tconst fd = openSync(path, \"a\");\n\t\t\t\ttry {\n\t\t\t\t\twriteAllSync(fd, `${JSON.stringify(validated)}\\n`);\n\t\t\t\t\tfsyncSync(fd);\n\t\t\t\t} finally {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\treleaseAppendLock(token);\n\t\t\t}\n\t\t\tatoms.push(validated);\n\t\t},\n\t\trewrite(nextAtoms) {\n\t\t\tconst validated = nextAtoms.map((atom) => validateMemoryAtomV1(atom));\n\t\t\t// The rewrite shares the append lock: a competing writer either\n\t\t\t// finished (its row is on disk and observable to the caller's loaded\n\t\t\t// view) or waits out the whole rebuild — no append can land inside\n\t\t\t// the rewrite window anymore.\n\t\t\tconst token = acquireAppendLock();\n\t\t\ttry {\n\t\t\t\tensureDir();\n\t\t\t\tconst tmpPath = `${path}.${now()}.rewrite.tmp`;\n\t\t\t\t// fsync the rebuilt file before the atomic rename: once rewrite\n\t\t\t\t// returns, a hard crash cannot lose the new content (and the\n\t\t\t\t// rename itself means the old file survives intact until then).\n\t\t\t\tconst fd = openSync(tmpPath, \"w\");\n\t\t\t\ttry {\n\t\t\t\t\twriteAllSync(fd, validated.map((atom) => `${JSON.stringify(atom)}\\n`).join(\"\"));\n\t\t\t\t\tfsyncSync(fd);\n\t\t\t\t} finally {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t}\n\t\t\t\trenameSync(tmpPath, path);\n\t\t\t} finally {\n\t\t\t\treleaseAppendLock(token);\n\t\t\t}\n\t\t\tatoms = [...validated];\n\t\t},\n\t\tatomsSnapshot() {\n\t\t\treturn Object.freeze([...atoms]);\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable memory ledger (统一修复轮 A, 方案系统设计 §6.1 + 方案实施计划 §11 A) —
|
|
3
|
+
* the append-only JSONL persistent replica of a Memory Foundation store.
|
|
4
|
+
*
|
|
5
|
+
* Layout: `<agentDir>/memory/<owner>/ledger-<suiteId|"legacy">.jsonl`. The
|
|
6
|
+
* owner segment is the store/facade isolation principal (one local user per
|
|
7
|
+
* agentDir in this product); the suite segment is the 方案 read-boundary
|
|
8
|
+
* domain — a real suite id, or the sentinel "legacy" for sessions without a
|
|
9
|
+
* suite binding. One line holds exactly one committed canonical atom
|
|
10
|
+
* (contract @2, including `suiteId`).
|
|
11
|
+
*
|
|
12
|
+
* Replica semantics: the write path appends every committed atom (canonical
|
|
13
|
+
* copy on disk); the read path `load()`s the atoms at startup and replays them
|
|
14
|
+
* into a fresh store. After a forget, `rewrite(atom[])` physically rewrites
|
|
15
|
+
* the file so purged atoms cannot survive a restart. Corruption tolerance:
|
|
16
|
+
* malformed lines are skipped and counted, duplicates are skipped and counted
|
|
17
|
+
* — loading never fails. Only the WRITE path validates strictly: a ledger must
|
|
18
|
+
* never persist an atom the Foundation would reject.
|
|
19
|
+
*
|
|
20
|
+
* Concurrency and durability envelope (统一修复轮 H1, #7 深度修复; rewrite 共
|
|
21
|
+
* 锁 + fsync 关洞修复): competing writers — append AND rewrite — serialize
|
|
22
|
+
* through a `.lock` sibling file taken via exclusive creation (`wx` — atomic
|
|
23
|
+
* on every supported platform). A second writer polls (25 ms) until the
|
|
24
|
+
* holder removes the lock, steals a lock whose mtime is older than
|
|
25
|
+
* `staleLockMs` (default 60 s — a crashed writer left it behind; a live
|
|
26
|
+
* holder keeps the lock for milliseconds), and throws after `lockTimeoutMs`
|
|
27
|
+
* (default 5 s, configurable) — a writer never silently blocks and never
|
|
28
|
+
* silently drops its row; lock or IO failures propagate to the caller. The
|
|
29
|
+
* lock is wall-clock based (real waiting), not the injected deterministic
|
|
30
|
+
* `now`. Durability: both write paths run open → write → fsync → close, so the
|
|
31
|
+
* file contents of an acknowledged append or rewrite survive a hard crash,
|
|
32
|
+
* subject to the directory-entry boundary declared below (a torn trailing
|
|
33
|
+
* line, possible only in files written by older builds or damaged externally,
|
|
34
|
+
* is still skipped by the loader's corruption tolerance).
|
|
35
|
+
* Remaining boundaries, declared honestly: the exclusive create is a mutex
|
|
36
|
+
* only where exclusive creation is atomic — local filesystems; multi-host or
|
|
37
|
+
* network-share access is out of scope. `rewrite` still publishes the calling
|
|
38
|
+
* process's atom set as the whole file: rows another process appended since
|
|
39
|
+
* this process last loaded are not merged into a rewrite (the forget caller
|
|
40
|
+
* owns that reconciliation) — the lock closes the interleaving window, not
|
|
41
|
+
* stale in-memory views. And fsync persists file contents, not directory
|
|
42
|
+
* entries (Windows cannot fsync a directory): a crash between rewrite's fsync
|
|
43
|
+
* and its rename leaves the previous file intact, never a half-written one.
|
|
44
|
+
* An fsync failure on append propagates after the row has already reached the
|
|
45
|
+
* OS: the unacknowledged row may be readable, and a caller retry appends it
|
|
46
|
+
* again — the loader's duplicate tolerance (`duplicateSkipped`) absorbs that.
|
|
47
|
+
*/
|
|
48
|
+
import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync, } from "node:fs";
|
|
49
|
+
import { dirname } from "node:path";
|
|
50
|
+
import { sleepSync } from "../utils/sync-sleep.js";
|
|
51
|
+
import { validateMemoryAtomV1 } from "./foundation.js";
|
|
52
|
+
/** Sentinel suite domain for sessions without a suite binding (设计 §6.1). */
|
|
53
|
+
export const LEGACY_MEMORY_DOMAIN = "legacy";
|
|
54
|
+
/** Suffix of the append lock sibling file (`<ledgerPath>.lock`). */
|
|
55
|
+
const LOCK_FILE_SUFFIX = ".lock";
|
|
56
|
+
/**
|
|
57
|
+
* Wall-clock wait between lock acquisition attempts. Kept as a per-file
|
|
58
|
+
* constant even though the durable store backend currently uses the same
|
|
59
|
+
* 25 ms value: the two lock protocols stay independent.
|
|
60
|
+
*/
|
|
61
|
+
const LOCK_POLL_INTERVAL_MS = 25;
|
|
62
|
+
/** Default ceiling on waiting for a competing writer's lock. */
|
|
63
|
+
const DEFAULT_LOCK_TIMEOUT_MS = 5000;
|
|
64
|
+
/**
|
|
65
|
+
* Default age at which a lock counts as abandoned (its writer crashed while
|
|
66
|
+
* holding it). Deliberately far above any live hold time (milliseconds of
|
|
67
|
+
* appendFileSync) and above the wait timeout, so a waiter fails loudly at the
|
|
68
|
+
* timeout instead of stealing a lock that might still be held.
|
|
69
|
+
*/
|
|
70
|
+
const DEFAULT_STALE_LOCK_MS = 60_000;
|
|
71
|
+
/**
|
|
72
|
+
* Writes every byte of `data` to the fd, looping over partial `writeSync`
|
|
73
|
+
* results (a short write would silently break the fsync durability promise:
|
|
74
|
+
* the caller fsyncs and closes only after the full payload is handed to the
|
|
75
|
+
* OS).
|
|
76
|
+
*/
|
|
77
|
+
const writeAllSync = (fd, data) => {
|
|
78
|
+
const buffer = Buffer.from(data, "utf8");
|
|
79
|
+
let written = 0;
|
|
80
|
+
while (written < buffer.length) {
|
|
81
|
+
written += writeSync(fd, buffer, written, buffer.length - written);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
function assertNonEmptyString(value, label) {
|
|
85
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
86
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Creates a durable JSONL memory ledger at `path`. `now` suffixes the rewrite
|
|
90
|
+
* tmp file so deterministic tests can pin it; it never participates in atom
|
|
91
|
+
* identity (timestamps live inside the canonical atoms) and never drives the
|
|
92
|
+
* append lock, which is a real-time mechanism. `lockTimeoutMs` bounds the
|
|
93
|
+
* wall-clock wait for a competing writer's lock; `staleLockMs` is the age at
|
|
94
|
+
* which a lock counts as abandoned and is stolen. Both exist so tests can
|
|
95
|
+
* exercise the timeout and steal paths quickly.
|
|
96
|
+
*/
|
|
97
|
+
export function createDurableMemoryLedger(options) {
|
|
98
|
+
assertNonEmptyString(options.path, "Durable ledger path");
|
|
99
|
+
if (options.lockTimeoutMs !== undefined && (!Number.isFinite(options.lockTimeoutMs) || options.lockTimeoutMs <= 0)) {
|
|
100
|
+
throw new Error("Durable ledger lockTimeoutMs must be a positive number");
|
|
101
|
+
}
|
|
102
|
+
if (options.staleLockMs !== undefined && (!Number.isFinite(options.staleLockMs) || options.staleLockMs <= 0)) {
|
|
103
|
+
throw new Error("Durable ledger staleLockMs must be a positive number");
|
|
104
|
+
}
|
|
105
|
+
const now = options.now ?? (() => Date.now());
|
|
106
|
+
const path = options.path;
|
|
107
|
+
const lockTimeoutMs = options.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;
|
|
108
|
+
const staleLockMs = options.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
|
|
109
|
+
const lockPath = `${path}${LOCK_FILE_SUFFIX}`;
|
|
110
|
+
/** In-memory replica of the durable atom set; kept in sync by append/rewrite/load. */
|
|
111
|
+
let atoms = [];
|
|
112
|
+
const ensureDir = () => {
|
|
113
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Takes the ledger writer lock, shared by append and rewrite: exclusive
|
|
117
|
+
* create (`wx`) is the mutex; an existing lock is polled until released,
|
|
118
|
+
* stolen once its mtime exceeds `staleLockMs` (crashed writer), or turned
|
|
119
|
+
* into a thrown error at the wait deadline. The lock file carries a
|
|
120
|
+
* per-acquisition token so a stale-steal victim releasing late can never
|
|
121
|
+
* delete the thief's fresh lock.
|
|
122
|
+
*/
|
|
123
|
+
const acquireAppendLock = () => {
|
|
124
|
+
const deadline = Date.now() + lockTimeoutMs;
|
|
125
|
+
for (;;) {
|
|
126
|
+
ensureDir();
|
|
127
|
+
const token = `${process.pid}:${Date.now()}`;
|
|
128
|
+
try {
|
|
129
|
+
const fd = openSync(lockPath, "wx");
|
|
130
|
+
try {
|
|
131
|
+
writeFileSync(fd, token);
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
closeSync(fd);
|
|
135
|
+
}
|
|
136
|
+
return token;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
// POSIX maps a lost create race to EEXIST; Windows/libuv maps the
|
|
140
|
+
// same concurrent-create sharing violation to EPERM. Both mean the
|
|
141
|
+
// lock file existed when we tried to create it — contend (poll /
|
|
142
|
+
// stale-steal below). For EPERM the existence check re-stats: a
|
|
143
|
+
// lock still there is contention, while ENOENT means the holder
|
|
144
|
+
// released and deleted it between the failed open and this stat
|
|
145
|
+
// (or Windows briefly left a delete-pending name that has now
|
|
146
|
+
// gone) — the same vanished-lock window the EEXIST path tolerates,
|
|
147
|
+
// so retry too. A stat error other than ENOENT means the EPERM was
|
|
148
|
+
// a genuine permission failure and propagates, and the vanish path
|
|
149
|
+
// still rethrows the original EPERM at the wait deadline: a
|
|
150
|
+
// persistent EPERM with no lock file ever appearing is a genuine
|
|
151
|
+
// permission failure, not a held lock.
|
|
152
|
+
const code = error.code;
|
|
153
|
+
if (code === "EPERM") {
|
|
154
|
+
let vanished = false;
|
|
155
|
+
try {
|
|
156
|
+
statSync(lockPath);
|
|
157
|
+
}
|
|
158
|
+
catch (statError) {
|
|
159
|
+
if (statError.code !== "ENOENT")
|
|
160
|
+
throw error;
|
|
161
|
+
vanished = true;
|
|
162
|
+
}
|
|
163
|
+
if (vanished) {
|
|
164
|
+
if (Date.now() >= deadline)
|
|
165
|
+
throw error;
|
|
166
|
+
sleepSync(LOCK_POLL_INTERVAL_MS);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (code !== "EEXIST") {
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
let stale = false;
|
|
174
|
+
try {
|
|
175
|
+
stale = Date.now() - statSync(lockPath).mtimeMs > staleLockMs;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// The holder released the lock between the failed open and the
|
|
179
|
+
// stat — retry the exclusive create immediately.
|
|
180
|
+
}
|
|
181
|
+
if (stale) {
|
|
182
|
+
// A crashed writer left the lock behind: steal it. A competing
|
|
183
|
+
// waiter may steal first — the next exclusive create resolves that.
|
|
184
|
+
try {
|
|
185
|
+
unlinkSync(lockPath);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Already stolen — retry.
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (Date.now() >= deadline) {
|
|
193
|
+
throw new Error(`memory ledger append lock timeout after ${lockTimeoutMs} ms: ${lockPath} is held by another writer`);
|
|
194
|
+
}
|
|
195
|
+
sleepSync(LOCK_POLL_INTERVAL_MS);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
/** Releases the append lock, but only if our own token still owns it. */
|
|
200
|
+
const releaseAppendLock = (token) => {
|
|
201
|
+
try {
|
|
202
|
+
if (readFileSync(lockPath, "utf8") === token)
|
|
203
|
+
unlinkSync(lockPath);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// The lock is already gone (or unreadable) — nothing to release.
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
const parseAtomLine = (line) => {
|
|
210
|
+
try {
|
|
211
|
+
return validateMemoryAtomV1(JSON.parse(line));
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Corruption tolerance: a malformed line is skipped and counted by the
|
|
215
|
+
// caller — a damaged ledger must never fail the startup replay.
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
return {
|
|
220
|
+
load() {
|
|
221
|
+
let raw;
|
|
222
|
+
try {
|
|
223
|
+
raw = readFileSync(path, "utf8");
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
if (error.code === "ENOENT") {
|
|
227
|
+
atoms = [];
|
|
228
|
+
return { atoms: Object.freeze([]), corruptedLines: 0, duplicateSkipped: 0, unreadable: false };
|
|
229
|
+
}
|
|
230
|
+
atoms = [];
|
|
231
|
+
return { atoms: Object.freeze([]), corruptedLines: 0, duplicateSkipped: 0, unreadable: true };
|
|
232
|
+
}
|
|
233
|
+
const loaded = [];
|
|
234
|
+
const seen = new Set();
|
|
235
|
+
let corruptedLines = 0;
|
|
236
|
+
let duplicateSkipped = 0;
|
|
237
|
+
for (const line of raw.split("\n")) {
|
|
238
|
+
if (line.trim() === "")
|
|
239
|
+
continue;
|
|
240
|
+
const atom = parseAtomLine(line);
|
|
241
|
+
if (!atom) {
|
|
242
|
+
corruptedLines += 1;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (seen.has(atom.memoryId)) {
|
|
246
|
+
duplicateSkipped += 1;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
seen.add(atom.memoryId);
|
|
250
|
+
loaded.push(atom);
|
|
251
|
+
}
|
|
252
|
+
atoms = loaded;
|
|
253
|
+
return {
|
|
254
|
+
atoms: Object.freeze([...loaded]),
|
|
255
|
+
corruptedLines,
|
|
256
|
+
duplicateSkipped,
|
|
257
|
+
unreadable: false,
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
append(atom) {
|
|
261
|
+
const validated = validateMemoryAtomV1(atom);
|
|
262
|
+
const token = acquireAppendLock();
|
|
263
|
+
try {
|
|
264
|
+
// One committed atom per line; the trailing newline keeps the file
|
|
265
|
+
// a well-formed JSONL stream for crash-truncated-tail recovery.
|
|
266
|
+
// The line is fsynced before close: once append returns, a hard
|
|
267
|
+
// crash cannot lose it.
|
|
268
|
+
const fd = openSync(path, "a");
|
|
269
|
+
try {
|
|
270
|
+
writeAllSync(fd, `${JSON.stringify(validated)}\n`);
|
|
271
|
+
fsyncSync(fd);
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
closeSync(fd);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
releaseAppendLock(token);
|
|
279
|
+
}
|
|
280
|
+
atoms.push(validated);
|
|
281
|
+
},
|
|
282
|
+
rewrite(nextAtoms) {
|
|
283
|
+
const validated = nextAtoms.map((atom) => validateMemoryAtomV1(atom));
|
|
284
|
+
// The rewrite shares the append lock: a competing writer either
|
|
285
|
+
// finished (its row is on disk and observable to the caller's loaded
|
|
286
|
+
// view) or waits out the whole rebuild — no append can land inside
|
|
287
|
+
// the rewrite window anymore.
|
|
288
|
+
const token = acquireAppendLock();
|
|
289
|
+
try {
|
|
290
|
+
ensureDir();
|
|
291
|
+
const tmpPath = `${path}.${now()}.rewrite.tmp`;
|
|
292
|
+
// fsync the rebuilt file before the atomic rename: once rewrite
|
|
293
|
+
// returns, a hard crash cannot lose the new content (and the
|
|
294
|
+
// rename itself means the old file survives intact until then).
|
|
295
|
+
const fd = openSync(tmpPath, "w");
|
|
296
|
+
try {
|
|
297
|
+
writeAllSync(fd, validated.map((atom) => `${JSON.stringify(atom)}\n`).join(""));
|
|
298
|
+
fsyncSync(fd);
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
closeSync(fd);
|
|
302
|
+
}
|
|
303
|
+
renameSync(tmpPath, path);
|
|
304
|
+
}
|
|
305
|
+
finally {
|
|
306
|
+
releaseAppendLock(token);
|
|
307
|
+
}
|
|
308
|
+
atoms = [...validated];
|
|
309
|
+
},
|
|
310
|
+
atomsSnapshot() {
|
|
311
|
+
return Object.freeze([...atoms]);
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=ledger.js.map
|