@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.
Files changed (55) hide show
  1. package/README.md +116 -12
  2. package/dist/canonical.d.ts.map +1 -1
  3. package/dist/cli.d.ts +1 -1
  4. package/dist/cli.js +91 -19
  5. package/dist/cloudflare-embedding.d.ts +104 -0
  6. package/dist/cloudflare-embedding.d.ts.map +1 -0
  7. package/dist/graph.d.ts.map +1 -1
  8. package/dist/index.js +90 -18
  9. package/dist/libsql-semantic.d.ts +111 -0
  10. package/dist/libsql-semantic.d.ts.map +1 -0
  11. package/dist/libsql.js +105 -18
  12. package/dist/memory-page.d.ts +2 -0
  13. package/dist/memory-page.d.ts.map +1 -0
  14. package/dist/memory-page.js +725 -0
  15. package/dist/memory-pages.d.ts +76 -0
  16. package/dist/memory-pages.d.ts.map +1 -0
  17. package/dist/memory.d.ts +1 -0
  18. package/dist/memory.d.ts.map +1 -1
  19. package/dist/memory.js +478 -18
  20. package/dist/projection-public.js +105 -18
  21. package/dist/projection-suss.js +105 -18
  22. package/dist/sdk.js +90 -18
  23. package/dist/semantic-cloud.d.ts +3 -0
  24. package/dist/semantic-cloud.d.ts.map +1 -0
  25. package/dist/semantic-cloud.js +1843 -0
  26. package/dist/semantic.d.ts.map +1 -1
  27. package/dist/semantic.js +104 -21
  28. package/dist/sqlite/index.js +90 -18
  29. package/dist/store.js +105 -18
  30. package/dist/sync.js +90 -18
  31. package/package.json +10 -2
  32. package/skills/oh/SKILL.md +28 -2
  33. package/spec/README.md +11 -3
  34. package/spec/manifest.json +9 -1
  35. package/spec/v1/cloudflare-embedding-profile.json +13 -0
  36. package/spec/v1/cloudflare-embedding-renderer.json +8 -0
  37. package/spec/v1/memory-page.md +153 -0
  38. package/spec/v1/memory-page.schema.json +154 -0
  39. package/spec/v1/memory.md +18 -0
  40. package/spec/v1/migration.md +13 -0
  41. package/spec/v1/semantic-cloud.md +87 -0
  42. package/src/canonical.ts +28 -13
  43. package/src/cli.ts +1 -1
  44. package/src/cloudflare-embedding.test.ts +306 -0
  45. package/src/cloudflare-embedding.ts +385 -0
  46. package/src/contracts.test.ts +20 -0
  47. package/src/graph.ts +63 -6
  48. package/src/libsql-semantic.test.ts +478 -0
  49. package/src/libsql-semantic.ts +1117 -0
  50. package/src/memory-page.ts +1 -0
  51. package/src/memory-pages.test.ts +277 -0
  52. package/src/memory-pages.ts +440 -0
  53. package/src/memory.ts +2 -0
  54. package/src/semantic-cloud.ts +2 -0
  55. 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 encoded = [];
64
- for (let index = 0;index < value.length; index += 1) {
65
- if (!Object.hasOwn(value, index)) {
66
- throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
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 extraKeys = Reflect.ownKeys(value).filter((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= value.length));
71
- if (extraKeys.length > 0) {
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
- keys.sort();
91
- const entries = keys.map((key) => {
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(value[key], `${path}.${key}`, ancestors)}`;
105
+ return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
94
106
  });
95
- return `{${entries.join(",")}}`;
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 || !Array.isArray(input.dependencies))
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 || input.dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
274
+ if (key === null || kind === undefined)
195
275
  throw new TypeError("Invalid graph record identity.");
196
- const dependencies = input.dependencies.map(recordKey);
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
- if (!isPlainRecord(value) || !Object.hasOwn(value, "recordSha256"))
288
+ const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
289
+ if (envelope === null)
209
290
  return null;
210
- const recordSha256 = parseSha256Hex(value.recordSha256);
211
- const { recordSha256: _digest, ...input } = value;
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,