@hraness/oh 0.2.7 → 0.3.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 +116 -12
- package/dist/canonical.d.ts.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +91 -19
- package/dist/cloudflare-embedding.d.ts +104 -0
- package/dist/cloudflare-embedding.d.ts.map +1 -0
- package/dist/graph.d.ts.map +1 -1
- package/dist/index.js +90 -18
- package/dist/libsql-semantic.d.ts +111 -0
- package/dist/libsql-semantic.d.ts.map +1 -0
- package/dist/libsql.js +105 -18
- package/dist/memory-page.d.ts +2 -0
- package/dist/memory-page.d.ts.map +1 -0
- package/dist/memory-page.js +725 -0
- package/dist/memory-pages.d.ts +76 -0
- package/dist/memory-pages.d.ts.map +1 -0
- package/dist/memory.d.ts +1 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +478 -18
- package/dist/projection-public.js +105 -18
- package/dist/projection-suss.js +105 -18
- package/dist/sdk.js +90 -18
- package/dist/semantic-cloud.d.ts +3 -0
- package/dist/semantic-cloud.d.ts.map +1 -0
- package/dist/semantic-cloud.js +1843 -0
- package/dist/semantic.d.ts.map +1 -1
- package/dist/semantic.js +104 -21
- package/dist/sqlite/index.js +90 -18
- package/dist/store.js +105 -18
- package/dist/sync.js +90 -18
- package/package.json +10 -2
- package/skills/oh/SKILL.md +28 -2
- package/spec/README.md +11 -3
- package/spec/manifest.json +9 -1
- package/spec/v1/cloudflare-embedding-profile.json +13 -0
- package/spec/v1/cloudflare-embedding-renderer.json +8 -0
- package/spec/v1/memory-page.md +153 -0
- package/spec/v1/memory-page.schema.json +154 -0
- package/spec/v1/memory.md +18 -0
- package/spec/v1/migration.md +13 -0
- package/spec/v1/semantic-cloud.md +87 -0
- package/src/canonical.ts +28 -13
- package/src/cli.ts +1 -1
- package/src/cloudflare-embedding.test.ts +306 -0
- package/src/cloudflare-embedding.ts +385 -0
- package/src/contracts.test.ts +20 -0
- package/src/graph.ts +63 -6
- package/src/libsql-semantic.test.ts +478 -0
- package/src/libsql-semantic.ts +1117 -0
- package/src/memory-page.ts +1 -0
- package/src/memory-pages.test.ts +277 -0
- package/src/memory-pages.ts +440 -0
- package/src/memory.ts +2 -0
- package/src/semantic-cloud.ts +2 -0
- package/src/semantic.ts +14 -3
package/dist/memory.js
CHANGED
|
@@ -60,17 +60,27 @@ function encodeCanonical(value, path, ancestors) {
|
|
|
60
60
|
ancestors.add(value);
|
|
61
61
|
try {
|
|
62
62
|
if (Array.isArray(value)) {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
encoded.push(encodeCanonical(value[index], `${path}[${index}]`, ancestors));
|
|
63
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
64
|
+
const length = lengthDescriptor?.value;
|
|
65
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
|
|
66
|
+
throw new OhValidationError("non-json-property", path, "array has an invalid length descriptor");
|
|
69
67
|
}
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
68
|
+
const ownKeys2 = Reflect.ownKeys(value);
|
|
69
|
+
if (!ownKeys2.includes("length") || ownKeys2.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length))) {
|
|
72
70
|
throw new OhValidationError("non-json-property", path, "array has non-index properties");
|
|
73
71
|
}
|
|
72
|
+
const elements = [];
|
|
73
|
+
for (let index = 0;index < length; index += 1) {
|
|
74
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
75
|
+
if (descriptor === undefined) {
|
|
76
|
+
throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
|
|
77
|
+
}
|
|
78
|
+
if (!descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
|
|
79
|
+
throw new OhValidationError("non-json-property", `${path}[${index}]`, "must be an enumerable data property");
|
|
80
|
+
}
|
|
81
|
+
elements.push(descriptor.value);
|
|
82
|
+
}
|
|
83
|
+
const encoded = elements.map((element, index) => encodeCanonical(element, `${path}[${index}]`, ancestors));
|
|
74
84
|
return `[${encoded.join(",")}]`;
|
|
75
85
|
}
|
|
76
86
|
if (!isPlainRecord(value)) {
|
|
@@ -80,19 +90,21 @@ function encodeCanonical(value, path, ancestors) {
|
|
|
80
90
|
if (ownKeys.some((key) => typeof key !== "string")) {
|
|
81
91
|
throw new OhValidationError("non-json-property", path, "object has a symbol property");
|
|
82
92
|
}
|
|
93
|
+
const entries = [];
|
|
83
94
|
const keys = ownKeys;
|
|
84
95
|
for (const key of keys) {
|
|
85
96
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
86
97
|
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
|
|
87
98
|
throw new OhValidationError("non-json-property", `${path}.${key}`, "must be an enumerable data property");
|
|
88
99
|
}
|
|
100
|
+
entries.push([key, descriptor.value]);
|
|
89
101
|
}
|
|
90
|
-
|
|
91
|
-
const
|
|
102
|
+
entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
103
|
+
const encodedEntries = entries.map(([key, entryValue]) => {
|
|
92
104
|
assertUnicodeScalarString(key, `${path}.<key>`);
|
|
93
|
-
return `${JSON.stringify(key)}:${encodeCanonical(
|
|
105
|
+
return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
|
|
94
106
|
});
|
|
95
|
-
return `{${
|
|
107
|
+
return `{${encodedEntries.join(",")}}`;
|
|
96
108
|
} finally {
|
|
97
109
|
ancestors.delete(value);
|
|
98
110
|
}
|
|
@@ -140,6 +152,21 @@ function canonicalNow() {
|
|
|
140
152
|
function safeCode(value, maximumLength = 128) {
|
|
141
153
|
return typeof value === "string" && value.length <= maximumLength && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
|
|
142
154
|
}
|
|
155
|
+
function boundedText(value, maximumBytes = 64 * 1024) {
|
|
156
|
+
if (typeof value !== "string" || value.length === 0 || value.normalize("NFC") !== value || utf8ByteLength(value) > maximumBytes)
|
|
157
|
+
return null;
|
|
158
|
+
try {
|
|
159
|
+
assertUnicodeScalarString(value, "$text");
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
for (const character of value) {
|
|
164
|
+
const code = character.codePointAt(0) ?? 0;
|
|
165
|
+
if (code <= 8 || code >= 11 && code <= 12 || code >= 14 && code <= 31 || code >= 127 && code <= 159)
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
143
170
|
function orderedUnique(values, key) {
|
|
144
171
|
return values.every((value, index) => index === 0 || key(values[index - 1]) < key(value));
|
|
145
172
|
}
|
|
@@ -183,17 +210,70 @@ var OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
|
|
|
183
210
|
"view",
|
|
184
211
|
"vocabulary"
|
|
185
212
|
];
|
|
213
|
+
var KNOWLEDGE_GRAPH_RECORD_KEYS_V1 = [
|
|
214
|
+
"dependencies",
|
|
215
|
+
"key",
|
|
216
|
+
"kind",
|
|
217
|
+
"recordSha256",
|
|
218
|
+
"v",
|
|
219
|
+
"value"
|
|
220
|
+
];
|
|
221
|
+
function exactKnowledgeGraphRecordEnvelopeV1(value) {
|
|
222
|
+
try {
|
|
223
|
+
if (!isPlainRecord(value))
|
|
224
|
+
return null;
|
|
225
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
226
|
+
if (ownKeys.length !== KNOWLEDGE_GRAPH_RECORD_KEYS_V1.length || ownKeys.some((key) => typeof key !== "string") || KNOWLEDGE_GRAPH_RECORD_KEYS_V1.some((key) => !ownKeys.includes(key)))
|
|
227
|
+
return null;
|
|
228
|
+
const detached = {};
|
|
229
|
+
for (const key of KNOWLEDGE_GRAPH_RECORD_KEYS_V1) {
|
|
230
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
231
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
232
|
+
return null;
|
|
233
|
+
detached[key] = descriptor.value;
|
|
234
|
+
}
|
|
235
|
+
return detached;
|
|
236
|
+
} catch {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function exactGraphDependenciesV1(value) {
|
|
241
|
+
try {
|
|
242
|
+
if (!Array.isArray(value))
|
|
243
|
+
return null;
|
|
244
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
245
|
+
const length = lengthDescriptor?.value;
|
|
246
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
|
|
247
|
+
return null;
|
|
248
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
249
|
+
if (ownKeys.length !== length + 1 || !ownKeys.includes("length") || ownKeys.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length)))
|
|
250
|
+
return null;
|
|
251
|
+
const detached = [];
|
|
252
|
+
for (let index = 0;index < length; index += 1) {
|
|
253
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
254
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
255
|
+
return null;
|
|
256
|
+
detached.push(descriptor.value);
|
|
257
|
+
}
|
|
258
|
+
return detached;
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
186
263
|
function recordKey(value) {
|
|
187
264
|
return typeof value === "string" && value.length <= 512 && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
|
|
188
265
|
}
|
|
189
266
|
function createKnowledgeGraphRecordV1(input) {
|
|
190
|
-
if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1
|
|
267
|
+
if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1)
|
|
191
268
|
throw new TypeError("Invalid graph record input.");
|
|
269
|
+
const dependencyInput = exactGraphDependenciesV1(input.dependencies);
|
|
270
|
+
if (dependencyInput === null)
|
|
271
|
+
throw new TypeError("Invalid graph record dependencies.");
|
|
192
272
|
const key = recordKey(input.key);
|
|
193
273
|
const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
|
|
194
|
-
if (key === null || kind === undefined
|
|
274
|
+
if (key === null || kind === undefined)
|
|
195
275
|
throw new TypeError("Invalid graph record identity.");
|
|
196
|
-
const dependencies =
|
|
276
|
+
const dependencies = dependencyInput.map(recordKey);
|
|
197
277
|
if (dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key)) {
|
|
198
278
|
throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
|
|
199
279
|
}
|
|
@@ -205,10 +285,17 @@ function createKnowledgeGraphRecordV1(input) {
|
|
|
205
285
|
return { ...payload, recordSha256: canonicalSha256(payload) };
|
|
206
286
|
}
|
|
207
287
|
function parseKnowledgeGraphRecordV1(value) {
|
|
208
|
-
|
|
288
|
+
const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
|
|
289
|
+
if (envelope === null)
|
|
209
290
|
return null;
|
|
210
|
-
const recordSha256 = parseSha256Hex(
|
|
211
|
-
const
|
|
291
|
+
const recordSha256 = parseSha256Hex(envelope.recordSha256);
|
|
292
|
+
const input = {
|
|
293
|
+
dependencies: envelope.dependencies,
|
|
294
|
+
key: envelope.key,
|
|
295
|
+
kind: envelope.kind,
|
|
296
|
+
v: envelope.v,
|
|
297
|
+
value: envelope.value
|
|
298
|
+
};
|
|
212
299
|
try {
|
|
213
300
|
const created = createKnowledgeGraphRecordV1(input);
|
|
214
301
|
return recordSha256 !== null && created.recordSha256 === recordSha256 ? { ...created, recordSha256 } : null;
|
|
@@ -971,6 +1058,369 @@ class OhSemanticBundleIngressV1 {
|
|
|
971
1058
|
// src/memory.ts
|
|
972
1059
|
import { createHmac, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
973
1060
|
|
|
1061
|
+
// src/memory-pages.ts
|
|
1062
|
+
var OH_MEMORY_PAGE_FORMAT_V1 = "oh.memory-page.v1";
|
|
1063
|
+
var OH_MEMORY_PAGE_MARKDOWN_EXTENSION_V1 = ".oh.md";
|
|
1064
|
+
var OH_MEMORY_PAGE_LIMITS_V1 = Object.freeze({
|
|
1065
|
+
bodyBytes: 512 * 1024,
|
|
1066
|
+
fileBytes: 1024 * 1024,
|
|
1067
|
+
frontmatterLines: 18 + OH_GRAPH_LIMITS_V1.dependenciesPerRecord + 5 * 128,
|
|
1068
|
+
languageBytes: 255,
|
|
1069
|
+
sourceTitleBytes: 1024,
|
|
1070
|
+
sourceUrlBytes: 4096,
|
|
1071
|
+
sources: 128,
|
|
1072
|
+
summaryBytes: 8192,
|
|
1073
|
+
titleBytes: 512,
|
|
1074
|
+
valueBytes: 768 * 1024
|
|
1075
|
+
});
|
|
1076
|
+
function singleLineText(value, maximumBytes) {
|
|
1077
|
+
const parsed = boundedText(value, maximumBytes);
|
|
1078
|
+
return parsed !== null && !/[\r\n\u0085\u2028\u2029]/u.test(parsed) ? parsed : null;
|
|
1079
|
+
}
|
|
1080
|
+
function exactDataRecord(value, keys) {
|
|
1081
|
+
try {
|
|
1082
|
+
if (!isPlainRecord(value))
|
|
1083
|
+
return null;
|
|
1084
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
1085
|
+
if (ownKeys.length !== keys.length || ownKeys.some((key) => typeof key !== "string") || keys.some((key) => !ownKeys.includes(key)))
|
|
1086
|
+
return null;
|
|
1087
|
+
const detached = {};
|
|
1088
|
+
for (const key of keys) {
|
|
1089
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1090
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
1091
|
+
return null;
|
|
1092
|
+
detached[key] = descriptor.value;
|
|
1093
|
+
}
|
|
1094
|
+
return detached;
|
|
1095
|
+
} catch {
|
|
1096
|
+
return null;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
function exactDataArray(value, maximumLength) {
|
|
1100
|
+
try {
|
|
1101
|
+
if (!Array.isArray(value))
|
|
1102
|
+
return null;
|
|
1103
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
1104
|
+
const length = lengthDescriptor?.value;
|
|
1105
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > maximumLength)
|
|
1106
|
+
return null;
|
|
1107
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
1108
|
+
if (ownKeys.length !== length + 1 || ownKeys.some((key) => typeof key !== "string") || !ownKeys.includes("length"))
|
|
1109
|
+
return null;
|
|
1110
|
+
const detached = [];
|
|
1111
|
+
for (let index = 0;index < length; index += 1) {
|
|
1112
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
1113
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
1114
|
+
return null;
|
|
1115
|
+
detached.push(descriptor.value);
|
|
1116
|
+
}
|
|
1117
|
+
return detached;
|
|
1118
|
+
} catch {
|
|
1119
|
+
return null;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
function parseLanguage(value) {
|
|
1123
|
+
if (value === null)
|
|
1124
|
+
return null;
|
|
1125
|
+
return typeof value === "string" && utf8ByteLength(value) <= OH_MEMORY_PAGE_LIMITS_V1.languageBytes && /^(?:und|[a-z]{2,3}(?:-[a-z0-9]{2,8})*)$/u.test(value) ? value : undefined;
|
|
1126
|
+
}
|
|
1127
|
+
function parseCanonicalSourceUrl(value) {
|
|
1128
|
+
if (typeof value !== "string" || value.normalize("NFC") !== value || utf8ByteLength(value) > OH_MEMORY_PAGE_LIMITS_V1.sourceUrlBytes)
|
|
1129
|
+
return null;
|
|
1130
|
+
try {
|
|
1131
|
+
const url = new URL(value);
|
|
1132
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.username !== "" || url.password !== "" || url.href !== value)
|
|
1133
|
+
return null;
|
|
1134
|
+
for (let index = value.indexOf("%");index >= 0; index = value.indexOf("%", index + 3)) {
|
|
1135
|
+
const encoded = value.slice(index + 1, index + 3);
|
|
1136
|
+
if (!/^[0-9A-F]{2}$/u.test(encoded))
|
|
1137
|
+
return null;
|
|
1138
|
+
const decoded = String.fromCharCode(Number.parseInt(encoded, 16));
|
|
1139
|
+
if (/^[A-Za-z0-9._~-]$/u.test(decoded))
|
|
1140
|
+
return null;
|
|
1141
|
+
}
|
|
1142
|
+
return value;
|
|
1143
|
+
} catch {
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
function parseSource(value) {
|
|
1148
|
+
const source = exactDataRecord(value, ["contentSha256", "observedAt", "title", "url", "v"]);
|
|
1149
|
+
if (source === null || source.v !== 1)
|
|
1150
|
+
return null;
|
|
1151
|
+
const contentSha256 = parseSha256Hex(source.contentSha256);
|
|
1152
|
+
const observedAt = parseCanonicalInstantV1(source.observedAt);
|
|
1153
|
+
const title = singleLineText(source.title, OH_MEMORY_PAGE_LIMITS_V1.sourceTitleBytes);
|
|
1154
|
+
const url = parseCanonicalSourceUrl(source.url);
|
|
1155
|
+
return contentSha256 !== null && observedAt !== null && title !== null && url !== null ? { contentSha256, observedAt, title, url, v: 1 } : null;
|
|
1156
|
+
}
|
|
1157
|
+
function parseProvenance(value) {
|
|
1158
|
+
const provenance = exactDataRecord(value, ["actorId", "attestationSha256", "attestedAt", "kind", "v"]);
|
|
1159
|
+
if (provenance === null || provenance.kind !== "host-attested" || provenance.v !== 1)
|
|
1160
|
+
return null;
|
|
1161
|
+
const actorId = safeCode(provenance.actorId);
|
|
1162
|
+
const attestationSha256 = parseSha256Hex(provenance.attestationSha256);
|
|
1163
|
+
const attestedAt = parseCanonicalInstantV1(provenance.attestedAt);
|
|
1164
|
+
return actorId !== null && attestationSha256 !== null && attestedAt !== null ? { actorId, attestationSha256, attestedAt, kind: "host-attested", v: 1 } : null;
|
|
1165
|
+
}
|
|
1166
|
+
function parseOhMemoryPageValueV1(value) {
|
|
1167
|
+
const page = exactDataRecord(value, [
|
|
1168
|
+
"body",
|
|
1169
|
+
"createdAt",
|
|
1170
|
+
"format",
|
|
1171
|
+
"language",
|
|
1172
|
+
"provenance",
|
|
1173
|
+
"sources",
|
|
1174
|
+
"summary",
|
|
1175
|
+
"title",
|
|
1176
|
+
"updatedAt",
|
|
1177
|
+
"v"
|
|
1178
|
+
]);
|
|
1179
|
+
if (page === null || page.format !== OH_MEMORY_PAGE_FORMAT_V1 || page.v !== 1)
|
|
1180
|
+
return null;
|
|
1181
|
+
const sourceValues = exactDataArray(page.sources, OH_MEMORY_PAGE_LIMITS_V1.sources);
|
|
1182
|
+
if (sourceValues === null)
|
|
1183
|
+
return null;
|
|
1184
|
+
const body = boundedText(page.body, OH_MEMORY_PAGE_LIMITS_V1.bodyBytes);
|
|
1185
|
+
const createdAt = parseCanonicalInstantV1(page.createdAt);
|
|
1186
|
+
const language = parseLanguage(page.language);
|
|
1187
|
+
const provenance = parseProvenance(page.provenance);
|
|
1188
|
+
const sources = sourceValues.map(parseSource);
|
|
1189
|
+
const summary = boundedText(page.summary, OH_MEMORY_PAGE_LIMITS_V1.summaryBytes);
|
|
1190
|
+
const title = singleLineText(page.title, OH_MEMORY_PAGE_LIMITS_V1.titleBytes);
|
|
1191
|
+
const updatedAt = parseCanonicalInstantV1(page.updatedAt);
|
|
1192
|
+
if (body === null || createdAt === null || language === undefined || provenance === null || sources.some((source) => source === null) || summary === null || title === null || updatedAt === null) {
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
const parsedSources = sources;
|
|
1196
|
+
if (!orderedUnique(parsedSources, (source) => source.url) || Date.parse(createdAt) > Date.parse(updatedAt) || Date.parse(updatedAt) > Date.parse(provenance.attestedAt) || parsedSources.some((source) => Date.parse(source.observedAt) > Date.parse(updatedAt)))
|
|
1197
|
+
return null;
|
|
1198
|
+
const parsed = {
|
|
1199
|
+
body,
|
|
1200
|
+
createdAt,
|
|
1201
|
+
format: OH_MEMORY_PAGE_FORMAT_V1,
|
|
1202
|
+
language,
|
|
1203
|
+
provenance,
|
|
1204
|
+
sources: parsedSources,
|
|
1205
|
+
summary,
|
|
1206
|
+
title,
|
|
1207
|
+
updatedAt,
|
|
1208
|
+
v: 1
|
|
1209
|
+
};
|
|
1210
|
+
return utf8ByteLength(canonicalJson(parsed)) <= OH_MEMORY_PAGE_LIMITS_V1.valueBytes ? parsed : null;
|
|
1211
|
+
}
|
|
1212
|
+
function createOhMemoryPageValueV1(value) {
|
|
1213
|
+
const parsed = parseOhMemoryPageValueV1(value);
|
|
1214
|
+
if (parsed === null)
|
|
1215
|
+
throw new TypeError("Invalid Oh memory page value.");
|
|
1216
|
+
return parsed;
|
|
1217
|
+
}
|
|
1218
|
+
function createOhMemoryPageRecordV1(input) {
|
|
1219
|
+
const parsedInput = exactDataRecord(input, ["dependencies", "key", "value"]);
|
|
1220
|
+
if (parsedInput === null) {
|
|
1221
|
+
throw new TypeError("Invalid Oh memory page record input.");
|
|
1222
|
+
}
|
|
1223
|
+
const value = createOhMemoryPageValueV1(parsedInput.value);
|
|
1224
|
+
const record = createKnowledgeGraphRecordV1({
|
|
1225
|
+
dependencies: parsedInput.dependencies,
|
|
1226
|
+
key: parsedInput.key,
|
|
1227
|
+
kind: "edition",
|
|
1228
|
+
v: 1,
|
|
1229
|
+
value
|
|
1230
|
+
});
|
|
1231
|
+
return { ...record, kind: "edition", value };
|
|
1232
|
+
}
|
|
1233
|
+
function parseOhMemoryPageRecordV1(value) {
|
|
1234
|
+
const envelope = exactDataRecord(value, [
|
|
1235
|
+
"dependencies",
|
|
1236
|
+
"key",
|
|
1237
|
+
"kind",
|
|
1238
|
+
"recordSha256",
|
|
1239
|
+
"v",
|
|
1240
|
+
"value"
|
|
1241
|
+
]);
|
|
1242
|
+
if (envelope === null)
|
|
1243
|
+
return null;
|
|
1244
|
+
const record = parseKnowledgeGraphRecordV1(envelope);
|
|
1245
|
+
if (record === null || record.kind !== "edition")
|
|
1246
|
+
return null;
|
|
1247
|
+
const page = parseOhMemoryPageValueV1(record.value);
|
|
1248
|
+
return page === null ? null : { ...record, kind: "edition", value: page };
|
|
1249
|
+
}
|
|
1250
|
+
var OH_MEMORY_PAGE_RECORD_CODEC_V1 = Object.freeze({
|
|
1251
|
+
kind: "edition",
|
|
1252
|
+
parse(value) {
|
|
1253
|
+
return parseOhMemoryPageValueV1(value);
|
|
1254
|
+
}
|
|
1255
|
+
});
|
|
1256
|
+
function scalar(value) {
|
|
1257
|
+
return JSON.stringify(value).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
|
|
1258
|
+
}
|
|
1259
|
+
function dependencyPrefix(index) {
|
|
1260
|
+
return `dependency-${index.toString().padStart(4, "0")}`;
|
|
1261
|
+
}
|
|
1262
|
+
function sourcePrefix(index) {
|
|
1263
|
+
return `source-${index.toString().padStart(3, "0")}`;
|
|
1264
|
+
}
|
|
1265
|
+
function markdownEntries(record) {
|
|
1266
|
+
const page = record.value;
|
|
1267
|
+
const entries = [
|
|
1268
|
+
["format", page.format],
|
|
1269
|
+
["record-v", record.v],
|
|
1270
|
+
["record-kind", record.kind],
|
|
1271
|
+
["record-key", record.key],
|
|
1272
|
+
["record-sha256", record.recordSha256],
|
|
1273
|
+
["dependency-count", record.dependencies.length]
|
|
1274
|
+
];
|
|
1275
|
+
record.dependencies.forEach((dependency, index) => {
|
|
1276
|
+
entries.push([`${dependencyPrefix(index)}-key`, dependency]);
|
|
1277
|
+
});
|
|
1278
|
+
entries.push(["page-v", page.v], ["title", page.title], ["summary", page.summary], ["language", page.language], ["created-at", page.createdAt], ["updated-at", page.updatedAt], ["provenance-kind", page.provenance.kind], ["provenance-v", page.provenance.v], ["provenance-actor-id", page.provenance.actorId], ["provenance-attested-at", page.provenance.attestedAt], ["provenance-attestation-sha256", page.provenance.attestationSha256], ["source-count", page.sources.length]);
|
|
1279
|
+
page.sources.forEach((source, index) => {
|
|
1280
|
+
const prefix = sourcePrefix(index);
|
|
1281
|
+
entries.push([`${prefix}-v`, source.v], [`${prefix}-url`, source.url], [`${prefix}-title`, source.title], [`${prefix}-observed-at`, source.observedAt], [`${prefix}-content-sha256`, source.contentSha256]);
|
|
1282
|
+
});
|
|
1283
|
+
return entries;
|
|
1284
|
+
}
|
|
1285
|
+
function renderOhMemoryPageMarkdownV1(value) {
|
|
1286
|
+
const record = parseOhMemoryPageRecordV1(value);
|
|
1287
|
+
if (record === null)
|
|
1288
|
+
throw new TypeError("Invalid Oh memory page record.");
|
|
1289
|
+
const frontmatter = markdownEntries(record).map(([key, item]) => `${key}: ${scalar(item)}`).join(`
|
|
1290
|
+
`);
|
|
1291
|
+
const rendered = `---
|
|
1292
|
+
${frontmatter}
|
|
1293
|
+
---
|
|
1294
|
+
${record.value.body}`;
|
|
1295
|
+
if (utf8ByteLength(rendered) > OH_MEMORY_PAGE_LIMITS_V1.fileBytes) {
|
|
1296
|
+
throw new RangeError("Oh memory page Markdown exceeds its byte limit.");
|
|
1297
|
+
}
|
|
1298
|
+
return rendered;
|
|
1299
|
+
}
|
|
1300
|
+
function parseFrontmatterLine(line) {
|
|
1301
|
+
const separator = line.indexOf(": ");
|
|
1302
|
+
if (separator < 1 || !/^[a-z][a-z0-9-]*$/u.test(line.slice(0, separator)))
|
|
1303
|
+
return null;
|
|
1304
|
+
const key = line.slice(0, separator);
|
|
1305
|
+
const encoded = line.slice(separator + 2);
|
|
1306
|
+
let value;
|
|
1307
|
+
try {
|
|
1308
|
+
value = JSON.parse(encoded);
|
|
1309
|
+
} catch {
|
|
1310
|
+
return null;
|
|
1311
|
+
}
|
|
1312
|
+
if (value !== null && typeof value !== "string" && typeof value !== "number" || typeof value === "number" && !Number.isFinite(value) || scalar(value) !== encoded)
|
|
1313
|
+
return null;
|
|
1314
|
+
return [key, value];
|
|
1315
|
+
}
|
|
1316
|
+
function parseOhMemoryPageMarkdownV1(text) {
|
|
1317
|
+
if (typeof text !== "string" || utf8ByteLength(text) > OH_MEMORY_PAGE_LIMITS_V1.fileBytes || !text.startsWith(`---
|
|
1318
|
+
`))
|
|
1319
|
+
return null;
|
|
1320
|
+
const closing = text.indexOf(`
|
|
1321
|
+
---
|
|
1322
|
+
`, 4);
|
|
1323
|
+
if (closing < 0)
|
|
1324
|
+
return null;
|
|
1325
|
+
const frontmatter = text.slice(4, closing);
|
|
1326
|
+
let frontmatterLines = 1;
|
|
1327
|
+
for (let index = frontmatter.indexOf(`
|
|
1328
|
+
`);index >= 0; index = frontmatter.indexOf(`
|
|
1329
|
+
`, index + 1)) {
|
|
1330
|
+
frontmatterLines += 1;
|
|
1331
|
+
if (frontmatterLines > OH_MEMORY_PAGE_LIMITS_V1.frontmatterLines)
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
const lines = frontmatter.split(`
|
|
1335
|
+
`);
|
|
1336
|
+
const entries = lines.map(parseFrontmatterLine);
|
|
1337
|
+
if (entries.some((entry) => entry === null) || entries.length < 18)
|
|
1338
|
+
return null;
|
|
1339
|
+
const parsedEntries = entries;
|
|
1340
|
+
const dependencyCount = parsedEntries[5]?.[1];
|
|
1341
|
+
if (!Number.isSafeInteger(dependencyCount) || dependencyCount < 0 || dependencyCount > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
|
|
1342
|
+
return null;
|
|
1343
|
+
const pageOffset = 6 + dependencyCount;
|
|
1344
|
+
const sourceCount = parsedEntries[pageOffset + 11]?.[1];
|
|
1345
|
+
if (!Number.isSafeInteger(sourceCount) || sourceCount < 0 || sourceCount > OH_MEMORY_PAGE_LIMITS_V1.sources)
|
|
1346
|
+
return null;
|
|
1347
|
+
const expectedKeys = [
|
|
1348
|
+
"format",
|
|
1349
|
+
"record-v",
|
|
1350
|
+
"record-kind",
|
|
1351
|
+
"record-key",
|
|
1352
|
+
"record-sha256",
|
|
1353
|
+
"dependency-count",
|
|
1354
|
+
...Array.from({ length: dependencyCount }, (_, index) => `${dependencyPrefix(index)}-key`),
|
|
1355
|
+
"page-v",
|
|
1356
|
+
"title",
|
|
1357
|
+
"summary",
|
|
1358
|
+
"language",
|
|
1359
|
+
"created-at",
|
|
1360
|
+
"updated-at",
|
|
1361
|
+
"provenance-kind",
|
|
1362
|
+
"provenance-v",
|
|
1363
|
+
"provenance-actor-id",
|
|
1364
|
+
"provenance-attested-at",
|
|
1365
|
+
"provenance-attestation-sha256",
|
|
1366
|
+
"source-count",
|
|
1367
|
+
...Array.from({ length: sourceCount }, (_, index) => {
|
|
1368
|
+
const prefix = sourcePrefix(index);
|
|
1369
|
+
return [
|
|
1370
|
+
`${prefix}-v`,
|
|
1371
|
+
`${prefix}-url`,
|
|
1372
|
+
`${prefix}-title`,
|
|
1373
|
+
`${prefix}-observed-at`,
|
|
1374
|
+
`${prefix}-content-sha256`
|
|
1375
|
+
];
|
|
1376
|
+
}).flat()
|
|
1377
|
+
];
|
|
1378
|
+
if (parsedEntries.length !== expectedKeys.length || parsedEntries.some(([key], index) => key !== expectedKeys[index]))
|
|
1379
|
+
return null;
|
|
1380
|
+
const dependencies = Array.from({ length: dependencyCount }, (_, index) => parsedEntries[6 + index]?.[1]);
|
|
1381
|
+
const sources = [];
|
|
1382
|
+
for (let index = 0;index < sourceCount; index += 1) {
|
|
1383
|
+
const offset = pageOffset + 12 + index * 5;
|
|
1384
|
+
sources.push({
|
|
1385
|
+
v: parsedEntries[offset]?.[1],
|
|
1386
|
+
url: parsedEntries[offset + 1]?.[1],
|
|
1387
|
+
title: parsedEntries[offset + 2]?.[1],
|
|
1388
|
+
observedAt: parsedEntries[offset + 3]?.[1],
|
|
1389
|
+
contentSha256: parsedEntries[offset + 4]?.[1]
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
const page = parseOhMemoryPageValueV1({
|
|
1393
|
+
body: text.slice(closing + 5),
|
|
1394
|
+
createdAt: parsedEntries[pageOffset + 4]?.[1],
|
|
1395
|
+
format: parsedEntries[0]?.[1],
|
|
1396
|
+
language: parsedEntries[pageOffset + 3]?.[1],
|
|
1397
|
+
provenance: {
|
|
1398
|
+
actorId: parsedEntries[pageOffset + 8]?.[1],
|
|
1399
|
+
attestationSha256: parsedEntries[pageOffset + 10]?.[1],
|
|
1400
|
+
attestedAt: parsedEntries[pageOffset + 9]?.[1],
|
|
1401
|
+
kind: parsedEntries[pageOffset + 6]?.[1],
|
|
1402
|
+
v: parsedEntries[pageOffset + 7]?.[1]
|
|
1403
|
+
},
|
|
1404
|
+
sources,
|
|
1405
|
+
summary: parsedEntries[pageOffset + 2]?.[1],
|
|
1406
|
+
title: parsedEntries[pageOffset + 1]?.[1],
|
|
1407
|
+
updatedAt: parsedEntries[pageOffset + 5]?.[1],
|
|
1408
|
+
v: parsedEntries[pageOffset]?.[1]
|
|
1409
|
+
});
|
|
1410
|
+
if (page === null || parsedEntries[1]?.[1] !== 1 || parsedEntries[2]?.[1] !== "edition" || typeof parsedEntries[3]?.[1] !== "string" || typeof parsedEntries[4]?.[1] !== "string" || dependencies.some((dependency) => typeof dependency !== "string"))
|
|
1411
|
+
return null;
|
|
1412
|
+
let record;
|
|
1413
|
+
try {
|
|
1414
|
+
record = createOhMemoryPageRecordV1({
|
|
1415
|
+
dependencies,
|
|
1416
|
+
key: parsedEntries[3][1],
|
|
1417
|
+
value: page
|
|
1418
|
+
});
|
|
1419
|
+
} catch {
|
|
1420
|
+
return null;
|
|
1421
|
+
}
|
|
1422
|
+
return record.recordSha256 === parsedEntries[4][1] && renderOhMemoryPageMarkdownV1(record) === text ? record : null;
|
|
1423
|
+
}
|
|
974
1424
|
// src/projection.ts
|
|
975
1425
|
var OH_PROJECTION_FORMAT_VERSION_V1 = 1;
|
|
976
1426
|
var OH_PROJECTION_SEMANTICS_V1 = "oh.projection.positive-datalog.v1";
|
|
@@ -3640,9 +4090,19 @@ async function createOhMemoryAgentV2(options) {
|
|
|
3640
4090
|
return Object.freeze({ explain, nominate, query, remember });
|
|
3641
4091
|
}
|
|
3642
4092
|
export {
|
|
4093
|
+
renderOhMemoryPageMarkdownV1,
|
|
4094
|
+
parseOhMemoryPageValueV1,
|
|
4095
|
+
parseOhMemoryPageRecordV1,
|
|
4096
|
+
parseOhMemoryPageMarkdownV1,
|
|
4097
|
+
createOhMemoryPageValueV1,
|
|
4098
|
+
createOhMemoryPageRecordV1,
|
|
3643
4099
|
createOhMemoryAgentV2,
|
|
3644
4100
|
createOhMemoryAgentV1,
|
|
3645
4101
|
OH_MEMORY_QUERY_LIMITS_V2,
|
|
4102
|
+
OH_MEMORY_PAGE_RECORD_CODEC_V1,
|
|
4103
|
+
OH_MEMORY_PAGE_MARKDOWN_EXTENSION_V1,
|
|
4104
|
+
OH_MEMORY_PAGE_LIMITS_V1,
|
|
4105
|
+
OH_MEMORY_PAGE_FORMAT_V1,
|
|
3646
4106
|
OH_MEMORY_LIMITS_V1,
|
|
3647
4107
|
OH_MEMORY_FORMAT_VERSION_V1,
|
|
3648
4108
|
OH_MEMORY_CONFLICT_POLICY_V1,
|