@hraness/oh 0.2.3

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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +598 -0
  3. package/dist/canonical.d.ts +32 -0
  4. package/dist/canonical.d.ts.map +1 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +3419 -0
  8. package/dist/contract.d.ts +33 -0
  9. package/dist/contract.d.ts.map +1 -0
  10. package/dist/graph.d.ts +67 -0
  11. package/dist/graph.d.ts.map +1 -0
  12. package/dist/index.d.ts +9 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +1988 -0
  15. package/dist/libsql.d.ts +57 -0
  16. package/dist/libsql.d.ts.map +1 -0
  17. package/dist/libsql.js +2662 -0
  18. package/dist/memory.d.ts +366 -0
  19. package/dist/memory.d.ts.map +1 -0
  20. package/dist/memory.js +3650 -0
  21. package/dist/ontology.d.ts +242 -0
  22. package/dist/ontology.d.ts.map +1 -0
  23. package/dist/operation.d.ts +24 -0
  24. package/dist/operation.d.ts.map +1 -0
  25. package/dist/projection-public.d.ts +59 -0
  26. package/dist/projection-public.d.ts.map +1 -0
  27. package/dist/projection-public.js +1682 -0
  28. package/dist/projection-suss.d.ts +17 -0
  29. package/dist/projection-suss.d.ts.map +1 -0
  30. package/dist/projection-suss.js +1721 -0
  31. package/dist/projection.d.ts +315 -0
  32. package/dist/projection.d.ts.map +1 -0
  33. package/dist/schema.d.ts +45 -0
  34. package/dist/schema.d.ts.map +1 -0
  35. package/dist/sdk.d.ts +51 -0
  36. package/dist/sdk.d.ts.map +1 -0
  37. package/dist/sdk.js +3072 -0
  38. package/dist/search.d.ts +34 -0
  39. package/dist/search.d.ts.map +1 -0
  40. package/dist/semantic.d.ts +83 -0
  41. package/dist/semantic.d.ts.map +1 -0
  42. package/dist/semantic.js +706 -0
  43. package/dist/sqlite/driver.d.ts +6 -0
  44. package/dist/sqlite/driver.d.ts.map +1 -0
  45. package/dist/sqlite/index.d.ts +5 -0
  46. package/dist/sqlite/index.d.ts.map +1 -0
  47. package/dist/sqlite/index.js +2840 -0
  48. package/dist/sqlite/migrations.d.ts +10 -0
  49. package/dist/sqlite/migrations.d.ts.map +1 -0
  50. package/dist/sqlite/port.d.ts +40 -0
  51. package/dist/sqlite/port.d.ts.map +1 -0
  52. package/dist/sqlite/runtime.d.ts +23 -0
  53. package/dist/sqlite/runtime.d.ts.map +1 -0
  54. package/dist/sqlite/store.d.ts +85 -0
  55. package/dist/sqlite/store.d.ts.map +1 -0
  56. package/dist/store.d.ts +236 -0
  57. package/dist/store.d.ts.map +1 -0
  58. package/dist/store.js +996 -0
  59. package/dist/sync.d.ts +57 -0
  60. package/dist/sync.d.ts.map +1 -0
  61. package/dist/sync.js +1328 -0
  62. package/package.json +121 -0
  63. package/skills/oh/SKILL.md +206 -0
  64. package/skills/oh/agents/openai.yaml +4 -0
  65. package/spec/README.md +74 -0
  66. package/spec/manifest.json +41 -0
  67. package/spec/v1/canonical-json.md +59 -0
  68. package/spec/v1/contract.json +28 -0
  69. package/spec/v1/contract.schema.json +58 -0
  70. package/spec/v1/embedding-profile.json +11 -0
  71. package/spec/v1/embedding.md +56 -0
  72. package/spec/v1/graph.md +87 -0
  73. package/spec/v1/memory.md +193 -0
  74. package/spec/v1/migration.md +92 -0
  75. package/spec/v1/ontology.json +55 -0
  76. package/spec/v1/ontology.md +80 -0
  77. package/spec/v1/operation.schema.json +138 -0
  78. package/spec/v1/projection-identity.schema.json +58 -0
  79. package/spec/v1/projection-query.schema.json +60 -0
  80. package/spec/v1/projection-result.schema.json +452 -0
  81. package/spec/v1/projection-rule-pack.schema.json +182 -0
  82. package/spec/v1/projection.md +165 -0
  83. package/spec/v1/record.schema.json +95 -0
  84. package/spec/v1/schema-evolution.md +51 -0
  85. package/spec/v1/schema-revision.schema.json +178 -0
  86. package/spec/v1/storage.md +88 -0
  87. package/spec/v1/store.md +131 -0
  88. package/spec/v1/sync-bundle.schema.json +51 -0
  89. package/spec/v1/sync.md +67 -0
  90. package/src/canonical.test.ts +46 -0
  91. package/src/canonical.ts +203 -0
  92. package/src/cli.test.ts +103 -0
  93. package/src/cli.ts +308 -0
  94. package/src/contract.ts +87 -0
  95. package/src/contracts.test.ts +147 -0
  96. package/src/graph.ts +248 -0
  97. package/src/index.ts +8 -0
  98. package/src/libsql.test.ts +657 -0
  99. package/src/libsql.ts +1687 -0
  100. package/src/memory.test.ts +783 -0
  101. package/src/memory.ts +1684 -0
  102. package/src/ontology.ts +573 -0
  103. package/src/operation.ts +80 -0
  104. package/src/projection-public.ts +53 -0
  105. package/src/projection-suss.ts +129 -0
  106. package/src/projection.test.ts +418 -0
  107. package/src/projection.ts +1457 -0
  108. package/src/schema.ts +156 -0
  109. package/src/sdk.ts +96 -0
  110. package/src/search.ts +66 -0
  111. package/src/semantic.test.ts +480 -0
  112. package/src/semantic.ts +333 -0
  113. package/src/sqlite/driver.ts +47 -0
  114. package/src/sqlite/index.ts +4 -0
  115. package/src/sqlite/migrations.test.ts +44 -0
  116. package/src/sqlite/migrations.ts +178 -0
  117. package/src/sqlite/port.test.ts +127 -0
  118. package/src/sqlite/port.ts +120 -0
  119. package/src/sqlite/runtime.test.ts +68 -0
  120. package/src/sqlite/runtime.ts +53 -0
  121. package/src/sqlite/store.test.ts +295 -0
  122. package/src/sqlite/store.ts +988 -0
  123. package/src/store.test.ts +121 -0
  124. package/src/store.ts +701 -0
  125. package/src/sync.test.ts +117 -0
  126. package/src/sync.ts +227 -0
package/src/graph.ts ADDED
@@ -0,0 +1,248 @@
1
+ import {
2
+ canonicalJson,
3
+ canonicalSha256,
4
+ hasExactKeys,
5
+ isPlainRecord,
6
+ orderedUnique,
7
+ parseSha256Hex,
8
+ safeCode,
9
+ sortUnique,
10
+ type JsonValue,
11
+ type Sha256Hex,
12
+ } from "./canonical";
13
+
14
+ export const OH_GRAPH_FORMAT_VERSION_V1 = 1 as const;
15
+ export const OH_GRAPH_LIMITS_V1 = Object.freeze({
16
+ changesPerOperation: 8_192,
17
+ dependenciesPerRecord: 4_096,
18
+ recordBytes: 1024 * 1024,
19
+ recordsPerSnapshot: 65_536,
20
+ });
21
+
22
+ export const OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
23
+ "activity", "assertion", "context", "dependency-manifest", "edition", "entity",
24
+ "evidence", "identity-operation", "inquiry", "inquiry-event", "review-decision",
25
+ "rights-decision", "schema", "shape", "statement", "type-membership", "view",
26
+ "vocabulary",
27
+ ] as const;
28
+ export type KnowledgeGraphRecordKindV1 = (typeof OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1)[number];
29
+
30
+ export type KnowledgeGraphRecordV1 = Readonly<{
31
+ dependencies: readonly string[];
32
+ key: string;
33
+ kind: KnowledgeGraphRecordKindV1;
34
+ recordSha256: Sha256Hex;
35
+ v: 1;
36
+ value: JsonValue;
37
+ }>;
38
+ export type KnowledgeGraphRecordInputV1 = Omit<KnowledgeGraphRecordV1, "recordSha256">;
39
+
40
+ export type KnowledgeGraphRecordRefV1 = Readonly<{
41
+ dependencies: readonly string[];
42
+ key: string;
43
+ kind: KnowledgeGraphRecordKindV1;
44
+ sha256: Sha256Hex;
45
+ v: 1;
46
+ }>;
47
+
48
+ function recordKey(value: unknown): string | null {
49
+ return typeof value === "string" && value.length <= 512
50
+ && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value)
51
+ ? value : null;
52
+ }
53
+
54
+ export function createKnowledgeGraphRecordV1(input: KnowledgeGraphRecordInputV1): KnowledgeGraphRecordV1 {
55
+ if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"])
56
+ || input.v !== 1 || !Array.isArray(input.dependencies)) throw new TypeError("Invalid graph record input.");
57
+ const key = recordKey(input.key);
58
+ const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
59
+ if (key === null || kind === undefined || input.dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord) throw new TypeError("Invalid graph record identity.");
60
+ const dependencies = input.dependencies.map(recordKey);
61
+ if (dependencies.some((dependency) => dependency === null)
62
+ || !orderedUnique(dependencies as string[], String) || dependencies.includes(key)) {
63
+ throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
64
+ }
65
+ const valueJson = canonicalJson(input.value);
66
+ if (Buffer.byteLength(valueJson, "utf8") > OH_GRAPH_LIMITS_V1.recordBytes) {
67
+ throw new RangeError("Graph record value exceeds its canonical byte limit.");
68
+ }
69
+ const payload = { dependencies: dependencies as string[], key, kind, v: 1 as const, value: input.value };
70
+ return { ...payload, recordSha256: canonicalSha256(payload) };
71
+ }
72
+
73
+ export function parseKnowledgeGraphRecordV1(value: unknown): KnowledgeGraphRecordV1 | null {
74
+ if (!isPlainRecord(value) || !Object.hasOwn(value, "recordSha256")) return null;
75
+ const recordSha256 = parseSha256Hex(value.recordSha256);
76
+ const { recordSha256: _digest, ...input } = value;
77
+ try {
78
+ const created = createKnowledgeGraphRecordV1(input as unknown as KnowledgeGraphRecordInputV1);
79
+ return recordSha256 !== null && created.recordSha256 === recordSha256
80
+ ? { ...created, recordSha256 } : null;
81
+ } catch { return null; }
82
+ }
83
+
84
+ export function knowledgeGraphRecordRefV1(record: KnowledgeGraphRecordV1): KnowledgeGraphRecordRefV1 {
85
+ return { dependencies: record.dependencies, key: record.key, kind: record.kind,
86
+ sha256: record.recordSha256, v: 1 };
87
+ }
88
+
89
+ export type KnowledgeGraphChangeV1 =
90
+ | Readonly<{ kind: "put"; record: KnowledgeGraphRecordV1; v: 1 }>
91
+ | Readonly<{ key: string; kind: "tombstone"; priorSha256: Sha256Hex; v: 1 }>;
92
+
93
+ export type KnowledgeGraphRevisionV1 = Readonly<{
94
+ changes: readonly KnowledgeGraphChangeV1[];
95
+ graphRevisionSha256: Sha256Hex;
96
+ operationId: string;
97
+ parentGraphRevisionSha256: Sha256Hex | null;
98
+ recordRefs: readonly KnowledgeGraphRecordRefV1[];
99
+ recordsSha256: Sha256Hex;
100
+ revision: number;
101
+ v: 1;
102
+ }>;
103
+
104
+ function changeKey(change: KnowledgeGraphChangeV1): string {
105
+ return change.kind === "put" ? change.record.key : change.key;
106
+ }
107
+
108
+ export function canonicalKnowledgeGraphChangesV1(changes: readonly KnowledgeGraphChangeV1[]): readonly KnowledgeGraphChangeV1[] {
109
+ const normalized: KnowledgeGraphChangeV1[] = [];
110
+ for (const change of changes) {
111
+ if (!isPlainRecord(change) || change.v !== 1) throw new TypeError("Invalid graph change.");
112
+ if (change.kind === "put") {
113
+ const record = parseKnowledgeGraphRecordV1(change.record);
114
+ if (record === null) throw new TypeError("Invalid graph record in change.");
115
+ normalized.push({ kind: "put", record, v: 1 });
116
+ } else if (change.kind === "tombstone") {
117
+ const key = recordKey(change.key);
118
+ const priorSha256 = parseSha256Hex(change.priorSha256);
119
+ if (key === null || priorSha256 === null) throw new TypeError("Invalid graph tombstone.");
120
+ normalized.push({ key, kind: "tombstone", priorSha256, v: 1 });
121
+ } else throw new TypeError("Unknown graph change kind.");
122
+ }
123
+ return sortUnique(normalized, changeKey);
124
+ }
125
+
126
+ /** Hashes the canonical graph transition envelope used by snapshots and operations. */
127
+ export function graphRevisionSha256V1(input: Readonly<{
128
+ changes: readonly KnowledgeGraphChangeV1[];
129
+ operationId: string;
130
+ parentGraphRevisionSha256: Sha256Hex | null;
131
+ recordsSha256: Sha256Hex;
132
+ revision: number;
133
+ }>): Sha256Hex {
134
+ const changes = canonicalKnowledgeGraphChangesV1(input.changes);
135
+ const operationId = safeCode(input.operationId);
136
+ const parentGraphRevisionSha256 = input.parentGraphRevisionSha256 === null
137
+ ? null : parseSha256Hex(input.parentGraphRevisionSha256);
138
+ const recordsSha256 = parseSha256Hex(input.recordsSha256);
139
+ const revision = Number.isSafeInteger(input.revision) && input.revision > 0 ? input.revision : null;
140
+ if (changes.length === 0 || changes.length > OH_GRAPH_LIMITS_V1.changesPerOperation
141
+ || operationId === null || recordsSha256 === null || revision === null
142
+ || (input.parentGraphRevisionSha256 !== null && parentGraphRevisionSha256 === null)
143
+ || ((revision === 1) !== (parentGraphRevisionSha256 === null))) {
144
+ throw new TypeError("Invalid graph revision digest input.");
145
+ }
146
+ return canonicalSha256({ changes, operationId, parentGraphRevisionSha256, recordsSha256, revision, v: 1 });
147
+ }
148
+
149
+ export function createKnowledgeGraphRevisionV1(input: Readonly<{
150
+ changes: readonly KnowledgeGraphChangeV1[];
151
+ operationId: string;
152
+ parent: KnowledgeGraphRevisionV1 | null;
153
+ }>): KnowledgeGraphRevisionV1 {
154
+ if (input.parent !== null && parseKnowledgeGraphRevisionV1(input.parent) === null) {
155
+ throw new TypeError("Invalid parent graph revision.");
156
+ }
157
+ const operationId = safeCode(input.operationId);
158
+ const changes = canonicalKnowledgeGraphChangesV1(input.changes);
159
+ if (operationId === null || changes.length === 0 || changes.length > OH_GRAPH_LIMITS_V1.changesPerOperation) throw new TypeError("Invalid graph revision.");
160
+ const byKey = new Map((input.parent?.recordRefs ?? []).map((ref) => [ref.key, ref]));
161
+ for (const change of changes) {
162
+ if (change.kind === "put") {
163
+ for (const dependency of change.record.dependencies) {
164
+ if (!byKey.has(dependency) && !changes.some((candidate) => candidate.kind === "put" && candidate.record.key === dependency)) {
165
+ throw new TypeError(`Missing graph dependency: ${dependency}`);
166
+ }
167
+ }
168
+ byKey.set(change.record.key, knowledgeGraphRecordRefV1(change.record));
169
+ } else {
170
+ const prior = byKey.get(change.key);
171
+ if (prior === undefined || prior.sha256 !== change.priorSha256) throw new TypeError("Tombstone prior digest does not match.");
172
+ byKey.delete(change.key);
173
+ }
174
+ }
175
+ if (byKey.size > OH_GRAPH_LIMITS_V1.recordsPerSnapshot) {
176
+ throw new RangeError("Graph revision exceeds its record snapshot limit.");
177
+ }
178
+ for (const ref of byKey.values()) {
179
+ if (ref.dependencies.some((dependency) => !byKey.has(dependency))) {
180
+ throw new TypeError(`Missing graph dependency after revision: ${ref.key}`);
181
+ }
182
+ }
183
+ const recordRefs = [...byKey.values()].sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0);
184
+ const recordsSha256 = canonicalSha256(recordRefs);
185
+ const payload = { changes, operationId, parentGraphRevisionSha256: input.parent?.graphRevisionSha256 ?? null,
186
+ recordRefs, recordsSha256, revision: (input.parent?.revision ?? 0) + 1, v: 1 as const };
187
+ return { ...payload, graphRevisionSha256: graphRevisionSha256V1(payload) };
188
+ }
189
+
190
+ export function parseKnowledgeGraphRevisionV1(value: unknown): KnowledgeGraphRevisionV1 | null {
191
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["changes", "graphRevisionSha256", "operationId",
192
+ "parentGraphRevisionSha256", "recordRefs", "recordsSha256", "revision", "v"]) || value.v !== 1
193
+ || !Array.isArray(value.changes) || !Array.isArray(value.recordRefs)
194
+ || value.recordRefs.length > OH_GRAPH_LIMITS_V1.recordsPerSnapshot) return null;
195
+ const graphRevisionSha256 = parseSha256Hex(value.graphRevisionSha256);
196
+ const recordsSha256 = parseSha256Hex(value.recordsSha256);
197
+ const parentGraphRevisionSha256 = value.parentGraphRevisionSha256 === null
198
+ ? null : parseSha256Hex(value.parentGraphRevisionSha256);
199
+ const operationId = safeCode(value.operationId);
200
+ const revision = Number.isSafeInteger(value.revision) && (value.revision as number) > 0 ? value.revision as number : null;
201
+ let changes: readonly KnowledgeGraphChangeV1[];
202
+ try { changes = canonicalKnowledgeGraphChangesV1(value.changes as KnowledgeGraphChangeV1[]); } catch { return null; }
203
+ const refs: KnowledgeGraphRecordRefV1[] = [];
204
+ for (const item of value.recordRefs) {
205
+ if (!isPlainRecord(item) || !hasExactKeys(item, ["dependencies", "key", "kind", "sha256", "v"])
206
+ || item.v !== 1 || !Array.isArray(item.dependencies)) return null;
207
+ const dependencies = item.dependencies.map(recordKey);
208
+ const key = recordKey(item.key);
209
+ const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === item.kind);
210
+ const sha256 = parseSha256Hex(item.sha256);
211
+ if (key === null || kind === undefined || sha256 === null
212
+ || dependencies.some((dependency) => dependency === null)
213
+ || dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord
214
+ || !orderedUnique(dependencies as string[], String) || dependencies.includes(key)) return null;
215
+ refs.push({ dependencies: dependencies as string[], key, kind, sha256, v: 1 });
216
+ }
217
+ if (graphRevisionSha256 === null || recordsSha256 === null || operationId === null || revision === null
218
+ || (value.parentGraphRevisionSha256 !== null && parentGraphRevisionSha256 === null)
219
+ || !orderedUnique(refs, (ref) => ref.key) || canonicalSha256(refs) !== recordsSha256) return null;
220
+ const keys = new Set(refs.map((ref) => ref.key));
221
+ if (refs.some((ref) => ref.dependencies.some((dependency) => !keys.has(dependency)))) return null;
222
+ const payload = { changes, operationId, parentGraphRevisionSha256, recordRefs: refs,
223
+ recordsSha256, revision, v: 1 as const };
224
+ try {
225
+ return graphRevisionSha256V1(payload) === graphRevisionSha256 ? { ...payload, graphRevisionSha256 } : null;
226
+ } catch { return null; }
227
+ }
228
+
229
+ /** Deterministically reduces a revision chain and rejects gaps, forks, and false snapshots. */
230
+ export function reduceKnowledgeGraphRevisionsV1(revisions: readonly KnowledgeGraphRevisionV1[]): KnowledgeGraphRevisionV1 | null {
231
+ if (revisions.length === 0 || revisions.length > 65_536) return null;
232
+ const ordered = [...revisions].sort((left, right) => left.revision - right.revision);
233
+ let parent: KnowledgeGraphRevisionV1 | null = null;
234
+ const operationIds = new Set<string>();
235
+ for (const candidate of ordered) {
236
+ const current = parseKnowledgeGraphRevisionV1(candidate);
237
+ if (current === null || current.revision !== (parent?.revision ?? 0) + 1
238
+ || current.parentGraphRevisionSha256 !== (parent?.graphRevisionSha256 ?? null)
239
+ || operationIds.has(current.operationId)) return null;
240
+ try {
241
+ const rebuilt = createKnowledgeGraphRevisionV1({ changes: current.changes, operationId: current.operationId, parent });
242
+ if (rebuilt.graphRevisionSha256 !== current.graphRevisionSha256) return null;
243
+ } catch { return null; }
244
+ operationIds.add(current.operationId);
245
+ parent = current;
246
+ }
247
+ return parent;
248
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./canonical";
2
+ export * from "./contract";
3
+ export * from "./graph";
4
+ export * from "./ontology";
5
+ export * from "./operation";
6
+ export * from "./schema";
7
+ export * from "./store";
8
+ export * from "./sync";